User validations email uniqueness Rspec FactoryGirl - ruby-on-rails

i have been even trying pending on this but it still gives error.
Userfactory
FactoryGirl.define do
sequence :email do |n|
"email#{n}#evercam.io"
end
factory :user, class: :EvercamUser do
sequence(:firstname) { |n| "firstname#{n}" }
sequence(:lastname) { |n| "lastname#{n}" }
sequence(:username) { |n| "username#{n}" }
sequence(:password) { |n| "password#{n}" }
email
sequence(:api_id) {|n| SecureRandom.hex(10)}
sequence(:api_key) {|n| SecureRandom.hex(16)}
# is_admin false
country do
country = Country.where(iso3166_a2: 'ie').first
country || create(:ireland)
end
end
end
UserSpec
require 'rails_helper'
RSpec.describe User, type: :model do
describe 'validations' do
it { should validate_presence_of :email }
it { should validate_presence_of :firstname }
it { should validate_presence_of :lastname }
it { should validate_presence_of :username }
it { should validate_presence_of :encrypted_password }
describe 'email uniqueness' do
before { create :user, email: 'foo#bar.com' }
let(:user) { build :user, email: 'foo#bar.com' }
it do
user.valid?
expect(user.errors[:email]).to be == ['has already been taken']
end
end
end
describe 'associations' do
it { should belong_to(:country) }
# it { should have_many(:camera_shares) }
end
it 'has a valid factory' do
expect(build(:user)).to be_valid
end
end
and here is my UserModel which is Devise
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
belongs_to :country
validates :firstname, presence: true
validates :lastname, presence: true
validates :username, presence: true
validates :encrypted_password, presence: true
def fullname
"#{firstname} #{lastname}"
end
def self.created_months_ago(number)
given_date = number.months.ago
User.where(created_at: given_date.beginning_of_month..given_date.end_of_month)
end
end
I have even tried to give "pending" to all blocks but it still giving me error i dont know where i am wrong on this
Any help will be appreciated thanks

it { should validate_presence_of :email }
If it fails on the line above, it is because the model does not in fact have a validation for :email:
validates :firstname, presence: true
validates :lastname, presence: true
validates :username, presence: true
validates :encrypted_password, presence: true
# validates :email, presence: true # <---- missing in action

Related

How to update a character varying array field on Postgres with Rails 5.2?

My application defines a list of user's preferred activities as an array of strings stored in a Postgres character varying array field.
The field is defined in Postgres DDL as:
preferred_activities character varying[] COLLATE pg_catalog."default" DEFAULT '{}'::character varying[]
The field is initialised by an API which receives a comma separated list of values (userActivities ) such as "CLIMB,RUN,212" with the following instruction:
#user.preferred_activities = userActivities.split(',')
This results in the expected list stored as {CLIMB,RUN,212} in Postgres field and displayed as expected in the view: CLIMB,RUN,212
As the field can also be manually edited, it also appears in user's form where it is initialised with a consistent format:
<%= f.text_field :preferred_activities, value: f.object.preferred_activities.join(',') %>
which provides the expected field value attribute "CLIMB,RUN,212" (displayed as CLIMB,RUN,212).
And then formatted before user's record update:
#user.preferred_activities = user_params[:preferred_activities].split(',')
which produces the following array: ["CLIM", "RUN", "212"]
This update should be straight forward, but for a reason I don't understand, the following SQL instruction is generated:
User Update (0.9ms) UPDATE "users" SET "preferred_activities" = $1, "updated_at" = $2 WHERE "users"."id" = $3
[["preferred_activities", "{LIM,\"RUN\"}"], ["updated_at", "2022-03-17 12:26:25.195321"], ["id", 1]]
The first letter and the last word have disappeared!
I did try other syntaxes for the instruction, which lead to the same result:
#user.preferred_activities = user_params[:preferred_activities]
#user.preferred_activities = "{#{user_params[:preferred_activities]}}"
Do you see any explanation for this behaviour and any solution to properly update this character varying array field?
User model:
class User < ApplicationRecord
extend CsvHelper
# Audit trail setup
audited except: [:encrypted_password, :reset_password_token, :reset_password_sent_at, :remember_created_at,
:sign_in_count, :current_sign_in_at, :last_sign_in_at, :current_sign_in_ip, :last_sign_in_ip,
:confirmation_token, :confirmed_at, :confirmation_sent_at, :unconfirmed_email,
:failed_attempts, :unlock_token, :locked_at]
#validates_with EmailAddress::ActiveRecordValidator, field: :email
# Virtual attribute for authenticating by either username or email
# This is in addition to a real persisted field like 'username'
attr_accessor :login
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :recoverable, :rememberable,
:trackable, :confirmable, :lockable, :password_archivable, :registerable,
:omniauthable, omniauth_providers: %i[keycloakopenid]#, :secure_validatable
before_save :email_format
before_save :name_update
### validations
# validates :current_playground_id, presence: true
validates :email, :presence => true, uniqueness: {scope: :playground_id, case_sensitive: false}, length: { maximum: 100 }
#validates_format_of :email, with: /\A(\S+)#(.+)\.(\S+)\z/
validate :password_confirmed
validate :password_complexity
validates :first_name, presence: true, length: { maximum: 100 }
validates :last_name, presence: true, length: { maximum: 100 }
validates :user_name, presence: true, uniqueness: {scope: :playground_id, case_sensitive: false}, length: { maximum: 100 }
validates :external_directory_id, length: { maximum: 100 }
validates :created_by, presence: true, length: { maximum: 30 }
validates :updated_by, presence: true, length: { maximum: 30 }
#validate :member_of_Everyone_group
validates :organisation, presence: true
belongs_to :organisation
belongs_to :parent, :class_name => "Playground", :foreign_key => "playground_id"
belongs_to :owner, :class_name => "User", :foreign_key => "owner_id"
# Relations
has_and_belongs_to_many :groups
has_many :groups_users
### Translation support
mattr_accessor :translated_fields, default: ['description']
has_many :translations, as: :document
has_many :description_translations, -> { where(field_name: 'description') }, class_name: 'Translation', as: :document
accepts_nested_attributes_for :translations, :reject_if => :all_blank, :allow_destroy => true
accepts_nested_attributes_for :description_translations, :reject_if => :all_blank, :allow_destroy => true
### Public functions
def activity_status
if self.is_active
if not self.locked_at.nil?
"locked"
else
if self.confirmed_at.nil?
"Unconfirmed"
else
if self.sign_in_count == 0
"Confirmed"
else
"Active"
end
end
end
else
"Inactive"
end
end
def principal_group
self.groups_users.find_by(is_principal: true).group_id
end
def password_complexity
# Regexp extracted from https://stackoverflow.com/questions/19605150/regex-for-password-must-contain-at-least-eight-characters-at-least-one-number-a
return if password.blank? || password =~ /^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[#?!#$%^&*-]).{8,70}$/
errors.add :password, 'Complexity requirement not met. Length should be 8-70 characters and include: 1 uppercase, 1 lowercase, 1 digit and 1 special character'
end
def password_confirmed
return if password == password_confirmation
errors.add :password, 'Password and confirmation do not match'
end
### full-text local search
pg_search_scope :search_by_user_name, against: [:user_name, :name, :description],
using: { tsearch: { prefix: true, negation: true } }
def self.search(criteria)
if criteria.present?
search_by_user_name(criteria)
else
# No query? Return all records, sorted by hierarchy.
order( :updated_at )
end
end
# Allow user creation when a new one comes through OmniAuth
def self.from_omniauth(auth)
where(provider: auth.provider, email: auth.info.email).first_or_create do |user|
user.login = auth.info.email
user.uuid = auth.uid
user.provider = auth.provider
user.email = auth.info.email
user.password = "Odq!1#{Devise.friendly_token[0, 20]}"
user.password_confirmation = user.password
user.first_name = auth.info.first_name
user.last_name = auth.info.last_name
user.name = auth.info.name # assuming the user model has a name
user.user_name = auth.info.email[0, 32]
user.playground_id = 0
user.current_playground_id = 0
user.organisation_id = Parameter.find_by_code("ORGANISATION_ID").property.to_i || 0
user.language = 'fr_OFS'
user.description = 'Created through OmniAuth'
user.active_from = Time.now
user.active_to = Time.now + 3.years
user.is_admin = false
user.is_active = true
user.owner_id = 1
user.created_by = 'OmniAuth'
user.created_at = Time.now
user.updated_by = 'OmniAuth'
user.updated_at = Time.now
user.confirmed_at = Time.now
# If you are using confirmable and the provider(s) you use validate emails,
# uncomment the line below to skip the confirmation emails.
user.skip_confirmation!
end
end
### private functions definitions
private
### before filters
def email_format
self.email = email.downcase
end
def name_update
self.name = "#{first_name} #{last_name}"
end
def self.find_for_database_authentication(warden_conditions)
conditions = warden_conditions.dup
if login = conditions.delete(:login)
where(conditions.to_h).where(["lower(user_name) = :value OR lower(email) = :value", { :value => login.downcase }]).first
elsif conditions.has_key?(:user_name) || conditions.has_key?(:email)
where(conditions.to_h).first
end
end
def member_of_Everyone_group
errors.add(:base, :EveryoneMembershipMissing) unless group_ids.include? 0
end
end

Rails testing with rspec

I have a problem with my testing in rails with rspec. All in all I have a structure between 4 models, but for the moment I try to solve my testing for two of them. I'm using faker and FactoryGirl and have the following factories:
require 'faker'
FactoryGirl.define do
factory :user do |f|
f.name { Faker::Name}
f.email { Faker::Internet.email}
f.password {Faker::Internet.password}
f.role {"Kindergarten"}
end
end
require 'faker'
FactoryGirl.define do
factory :child do |f|
f.name { Faker::Name}
f.city { Faker::Address.city}
f.postalcode {Faker::Number.between(30000,35000)}
f.streed {Faker::Address.street_name}
f.add_number {Faker::Address.secondary_address}
f.disability { Faker::Boolean.boolean}
f.halal { Faker::Boolean.boolean}
f.koscha {Faker::Boolean.boolean}
f.vegetarian {Faker::Boolean.boolean}
f.vegan {Faker::Boolean.boolean}
f.allday { Faker::Boolean.boolean}
f.gender { Faker::Number.between(0,1)}
f.user_id {Faker::Number.between(1,10)}
end
end
and my controller test looks like that
require 'rails_helper'
require 'factory_girl_rails'
describe UsersController do
before do
3.times { FactoryGirl.create(:child)}
end
describe "GET #show" do
let(:user) { FactoryGirl.create(:user) }
before { get :show, id: user.id}
it "assigns the requested user to #user" do
assigns(:user).should eq user
end
it "renders the :show template"
end
describe "GET #new" do
let(:user) { FactoryGirl.create(:user) }
it "assigns a new User to #user" do
get :new, id: user.id
assigns(:user).should be_a_new(User)
end
it "renders the :new template"
end
end
When I try to run the test, I got this error message
UsersController GET #new assigns a new User to #user
Failure/Error: 3.times { FactoryGirl.create(:child)}
ActiveRecord::RecordInvalid:
Validation failed: User can't be blank
My relations and validations in the models are as follow
class Child < ApplicationRecord
has_many :relations, :dependent => :destroy
accepts_nested_attributes_for :relations
belongs_to :user
validates :user, presence: true
validates :name, presence: true, length: { maximum: 50 }
validates :city, presence: true, :on => :create
validates :postalcode, presence: true, numericality: true
validates :streed, presence: true
validates :add_number, presence: true
validates :disability, inclusion: { in: [true, false] }
validates :halal, inclusion: { in: [true, false] }
validates :koscha, inclusion: { in: [true, false] }
validates :vegetarian, inclusion: { in: [true, false] }
validates :vegan, inclusion: { in: [true, false] }
validates :allday, inclusion: { in: [true, false] }
validates :gender, presence: true
end
class User < ApplicationRecord
attr_accessor :remember_token, :activation_token, :reset_token
has_many :children, dependent: :destroy
has_many :kindergartens, dependent: :destroy
has_many :relations, dependent: :destroy
before_save :downcase_email
before_create :create_activation_digest
validates :name, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true, length: { maximum: 255 },
format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
validates :role, presence: true
has_secure_password
validates :password, presence: true, length: { minimum: 6 }, allow_nil: true
end
My problem is, that I'm not sure if there is a fault in my rspec code or if I have made a mistake in my validations and relations in my normal models. Can someone help me?
I think that it would be better to post the whole factory for you. And I think you don't need to require faker if you have it in Gemfile.
FactoryGirl.define do
factory :child do
name { Faker::Name}
city { Faker::Address.city}
postalcode {Faker::Number.between(30000,35000)}
streed {Faker::Address.street_name}
add_number {Faker::Address.secondary_address}
disability { Faker::Boolean.boolean}
halal { Faker::Boolean.boolean}
koscha {Faker::Boolean.boolean}
vegetarian {Faker::Boolean.boolean}
vegan {Faker::Boolean.boolean}
allday { Faker::Boolean.boolean}
gender { Faker::Number.between(0,1)}
user
end
end
Inside specs when you need to create a child for the particular user:
let(:current_user) { create :user }
let(:child) { create :child, user: current_user }
Try to change your controller spec:
describe UsersController do
let(:user) { create(:user) }
describe "GET #show" do
before { get :show, params: { id: user.id } }
it "assigns the requested user to #user" do
assigns(:user).should eq user
end
it "renders the :show template"
it { expect(response).to have_http_status 200 }
end
describe "GET #new" do
before { get :new }
it "assigns a new User to #user" do
assigns(:user).should be_a_new(User)
end
it "renders the :new template"
end
end

validate only on some method rails

Based on rails validation docs. I need to validate fullname field only on update
# encoding: utf-8
class User < ActiveRecord::Base
GENDER_MALE = true
GENDER_FEMALE = false
attr_accessor :password_confirm,
:term,
:year, :month, :day,
:captcha
validates :username, presence: {message: "Bạn phải nhập tài khoản"},
uniqueness: {message: 'Tài khoản đã tồn tại'}, :on => :update
# validates :password, presence: {message: "Bạn phải nhập mật khẩu"},
# confirmation: {message: 'Mật khẩu không chính xác'}
# validates :password_confirmation, presence: {message: "Bạn phải nhập xác nhận mật khẩu"}
# validates :fullname, presence: {message: "Bạn phải nhập họ tên"}
# validates :email, presence: {message: "Bạn phải nhập email"},
# uniqueness: {message: "Email đã tồn tại"}
# validates :email, format: {with: /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i, message: "Email không đúng định dạng"},
# unless: "email.blank?"
# validates :term, acceptance: {message: "Bạn phải đồng ý điều khoản"}
# # validates :gender, acceptance: {accept: [0,1], message: "Giới tính không hợp lệ"}
# validate :_birthday_validator
# validate :_captcha_validator
#
# before_save :_encrypt_password
def signup
self.birthday = "#{year.to_s}-#{month.to_s}-#{day.to_s}"
self.save
end
def self.human_attribute_name(attr, option = {})
"" || super
end
protected
def _encrypt_password
self.password = Digest::MD5::hexdigest(password)
end
private
def _birthday_validator
unless year.present? && month.present? && day.present?
errors.add(:birthday, 'Bạn phải nhập ngày sinh')
else
errors.add(:birthday, 'Ngày sinh không hợp lệ') unless Date.valid_date?(year.to_i, month.to_i, day.to_i)
end
end
def _captcha_validator
if !(captcha.nil?)
errors.add(:captcha, "Mã xác nhận không hợp lệ") if captcha == false
end
end
end
As understand, this validation rule only run when I call update method, but I have no idea why this rule run all the time
Can anyone tell me why or I missed somethings?
Ps: Can Rails validates only for user defined method, somethings like
validates :username, presence: true, only: [:my_func]
One way would be to set a virtual attribute which you'll only populate in the signup method:
#app/models/user.rb
class User < ActiveRecord::Base
attr_accessor :should_validate
validates :fullname, presence: true, on: :update, if: "should_validate.present?"
end
This way, you can then assign a value to should_validate only when you use signup:
def signup
self.birthday = "#{year.to_s}-#{month.to_s}-#{day.to_s}"
self.should_validate = true
self.save
end
you can use method like
validate :fullname , on: :update
def fullname
if self.fullname.present?
true
else
false
end
end

Failing user validation with nested attributes and polymorphic association

This simple validation test is failing:
require 'test_helper'
class UserTest < ActiveSupport::TestCase
def setup
#user = User.new(name: "Example User",
email: "user#example.com",
character_attributes: {callsign: "example"},
password: "foobar",
password_confirmation: "foobar"
)
end
test "should be valid" do
assert #user.valid?, "#{#user.errors.messages}"
end
end
...with this message: character.sociable_id"=>["can't be blank"]
I don't understand why the user creation in UserTest is failing to make a valid User.
Each User has_one :character and each Character belongs_to a User.
The User model:
User.rb:
class User < ActiveRecord::Base
attr_accessor :remember_token, :activation_token, :reset_token
has_one :character, as: :sociable, dependent: :destroy
accepts_nested_attributes_for :character
has_secure_password
before_validation do
self.create_character unless character
end
before_save do
self.email.downcase!
end
before_create :create_activation_digest
validates :name, presence: true,
length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-]+(?:\.[a-z\d\-]+)*\.[a-z]+\z/i
validates :email, presence: true,
format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
validates :password, length: { minimum: 6 }, allow_blank: true
validates :character, presence: true
.
.
end
The Character model:
Character.rb:
class Character < ActiveRecord::Base
belongs_to :sociable, polymorphic: true
has_many :posts, dependent: :destroy
before_save do
self.callsign.downcase!
end
validates :sociable_id, presence: true
VALID_CALLSIGN_REGEX = /\A[a-z\d\-.\_]+\z/i
validates :callsign, presence: true,
length: { maximum: 20 },
format: { with: VALID_CALLSIGN_REGEX },
uniqueness: { case_sensitive: false }
end
It should be:-
test "should be valid" do
assert #user.valid? , "#{#user.errors.messages}"
end

Rails 3.1, Factory girl bug

Fixed. There was a bug in Rails. See https://github.com/rails/rails/issues/2333
I have a problem with Factory Girl Rails and Rails 3.1.0.rc5
When I do more than once user = FactoryGirl.create(:user) I have an error.
Failure/Error: user = FactoryGirl.create(:user)
NameError:
uninitialized constant User::User
# ./app/models/user.rb:17:in `generate_token'
# ./app/models/user.rb:4:in `block in <class:User>'
# ./spec/requests/users_spec.rb:20:in `block (3 levels) in <top (required)>'
I can create as many user as I want using Factory but only in rails console.
Tests:
require 'spec_helper'
describe "Users" do
describe "signin" do
it "should sign in a user" do
visit root_path
user = FactoryGirl.create(:user)
within("div#sign_in_form") do
fill_in "Name", with: user.name
fill_in "Password", with: user.password
end
click_button "Sign in"
current_path.should eq(user_path(user))
page.should have_content("signed in")
end
it "should not show new user form on /" do
user = FactoryGirl.create(:user)
visit root_path
page.should_not have_css("div#new_user_form")
end
end
end
factories.rb
FactoryGirl.define do
factory :user do |f|
f.sequence(:name) { |n| "john#{n}" }
f.fullname 'Doe'
f.sequence(:email) { |n| "test#{n}#example.com" }
f.password 'foobar'
end
end
model/user.rb
class User < ActiveRecord::Base
has_secure_password
attr_accessible :name, :fullname, :email, :password
before_create { generate_token(:auth_token) }
email_regex = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :name, presence: true, length: { maximum: 20 },
uniqueness: { case_sensitive: false }
validates :fullname, presence: true, length: { maximum: 30 }
validates :email, format: { with: email_regex },
uniqueness: { case_sensitive: false }, length: { maximum: 30 }
validates :password, length: { in: 5..25 }
def generate_token(column)
begin
self[column] = SecureRandom.urlsafe_base64
end while User.exists?(column => self[column])
end
end
User.exists?(column => self[column]) causes the problem.
Somehow the class is not properly looked up, and I am not sure how this is happenning but could you try accessing it differently:
def generate_token(column)
begin
self[column] = SecureRandom.urlsafe_base64
end while self.class.exists?(column => self[column])
end
You've got an extra line i your factories.rb, it should read like this:
FactoryGirl.define :user do |f|
f.sequence(:name) { |n| "john#{n}" }
f.fullname 'Doe'
f.sequence(:email) { |n| "test#{n}#example.com" }
f.password 'foobar'
end
This should work:
FactoryGirl.define do
factory :user do
sequence(:name) { |n| "john#{n}" }
fullname 'Doe'
sequence(:email) { |n| "test#{n}#example.com" }
password 'foobar'
end
end

Resources