controller's before_save methods aren't called in testing - ruby-on-rails

Following is rails-api only app.
In users_controller.rb
def sign_up
#user = User.new(user_params)
if #user.save
user = {}
user[:name] = #user.name
user[:email] = #user.email
user[:access_token] = #user.auth_token
user[:message] = "Successfully Signed up"
render json: user, status: 200
else
render json: #user.errors, status: :unprocessable_entity
end
end
private
def user_params
params.permit(:name, :email, :password, :password_confirmation)
end
In model/user.rb
attr_accessor :password
before_save :encrypt_password, :generate_token
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true, format: { with: VALID_EMAIL_REGEX } , uniqueness: { case_sensitive:false }
validates :password, :confirmation => true, presence: true, :length => {:within => 6..20}
validates_presence_of :password_salt, :password_hash
validates_presence_of :auth_token
In test/controllers/users_controller_test.rb
test "should create user" do
assert_difference('User.count') do
post :sign_up, {email: 'user_3#example.com', name: 'user_3', password: 12345678, password_confirmation: 12345678}
end
end
user model attributes are:
name email password_hash password_salt auth_token
The test above is failing b'cos #user.errors shows
<ActiveModel::Errors:0x007fd826117140 #base=#<User id: nil, name: "user_3", email: "user_3#example.com", password_hash: nil, password_salt: nil, auth_token: nil, created_at: nil, updated_at: nil>, #messages={:password_salt=>["can't be blank"], :password_hash=>["can't be blank"], :auth_token=>["can't be blank"]}>
On #user.save before_save :encrypt_password, :generate_token aren't called.
Using gem 'bcrypt' to store the password as hash.
How do I correct this?

Instead of before_save :encrypt_password, :generate_token use before_validation :encrypt_password, :generate_token. Code in encrypt_password and generate_token is not triggered because you validate model data first and since data is not valid there is no need to trigger before_save because record would not be saved anyway

Related

Minitest fails on user update test

My user#update method appears to be working as anticipated. When performing this method as requested from my front-end, a successful update occurs. The test for the method, however, fails.
For my test, I'm logging the user in, then attaching the returned auth_token in the request headers.
Here is the test:
test "should successfully update valid email" do
old = #user.auth_token
post api_login_url, params: { session: #credentials }
#user.reload
assert_not_equal old, #user.auth_token
old_user = #user
#request.headers["Authorization"] = #user.auth_token
patch "/api/users/" + #user.id.to_s, params: { email: "new#me.com", password: "testtest" }, headers: #request.headers
assert_response 201
assert_equal "new#me.com", User.find(#user.id).email
end
The error:
Expected: "new#me.com"
Actual: "test#woohoo.com"
In my controller (#user is already set):
before_action :authenticate_with_token!, only: [:update, :destroy]
before_action :set_user, only: [:show, :update, :destroy, :confirm, :posts, :comments]
wrap_parameters :user, include: [:username, :email, :password, :password_confirmation]
def update
if #user.update(user_params)
render json: #user
else
render json: #user.errors, status: :unprocessable_entity
end
end
def user_params
params.require(:user).permit(:username, :email, :password, :password_confirmation, :confirmation_code, :confirmed)
end
The user model:
require 'json_web_token'
class User < ApplicationRecord
before_save { email.downcase! }
before_create :generate_authentication_token!
before_update :reset_confirmed!, :if => :email_changed?
has_secure_password
has_many :posts
has_many :comments
has_many :votes
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i
validates :email, presence: true, length: { maximum: 255 }, format: { with: VALID_EMAIL_REGEX }, uniqueness: { case_sensitive: false }
validates :username, presence: true, length: { maximum: 24 }, uniqueness: { case_sensitive: false }
validates :password, presence: true, length: { minimum: 8 }
validates :auth_token, uniqueness: true
def generate_authentication_token!
begin
self.auth_token = JsonWebToken.encode('id' => self.id, 'username' => self.username)
end while self.class.exists?(auth_token: auth_token)
end
def destroy_token!
self.auth_token = nil
end
def reset_confirmed!
self.confirmed = false
end
end
In case anyone will ask, I've tried removing the before_update :reset_confirmed! call.
I've tried rewriting the tests many different ways. The assert_response 201 does not fail.
Rails version: 5.0.0.beta2
Ruby version: 2.2.3 (x86_64-darwin15)
You need to wrap your params:
patch "/api/users/" + #user.id.to_s,
params: { user: { email: "new#me.com", password: "testtest" }},
headers: #request.headers
I am however quite surprised you are not getting an exception (require(:user) should raise one when params[:user] is missing), so let me know if it is working.

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

Writes to database empty records when I check validation

I have a model with validates:
attr_accessor :name, :username, :email, :password
validates :name, :presence => { allow_blank: false, message: "Имя не может быть пустым" }
validates :username, :presence => { allow_blank: false, message: "Имя пользователя не может быть пустым" }
validates :email, :presence => { allow_blank: false, message: "Эл. адрес не может быть пустым" }
CREATE def:
def create
#user= User.new(user_params)
if #user.save
redirect_to "http://bookworm.az:3000"
else
session[:name] =#user.name
session[:username] =#user.username
session[:email] =#user.email
error =#user.errors.to_a.to_sentence(two_words_connector:',')
delimeter = error.split
error = error.gsub(delimeter[0], '')
show_error = error.split(",")
flash[:notice] = show_error[0]
redirect_to :back
end
end
When I delete ALL validates checking all it saves the user in the database. But with the validates LOG prints:
INSERT INTO "users" DEFAULT VALUES RETURNING "id"
What is the problem?
UPDATE
def user_params
params.require(:user).permit(:name, :username, :email, :password)
end
attr_accessor is overriding the rails attributes. Try removing it and it should work.
Read the answer for similar issue.

Why aren't parameters populating a new instance of a class?

I am submitting a form into a CorporateContactFormsController. I can see the parameters are being passed but they are not being added to the instantiated class.
def create
#contact_form = CorporateContactForm.new(corporate_contact_form_params)
raise
end
private
def corporate_contact_form_params
params.require(:corporate_contact_form).permit(:firstname, :surname, :contact_number, :email, :company_name, :method_of_payment, :number_of_employees, :comments, :contact_type)
end
This is from better-errors gem
>> corporate_contact_form_params
=> {"firstname"=>"qwertyui", "surname"=>"wertyuio", "contact_number"=>"wertyuio", "email"=>"qwdqwd#iugh.com", "company_name"=>"wqedfwefwef", "method_of_payment"=>"Other", "number_of_employees"=>"1-50", "comments"=>"qwdqwdqwdqwdqwdqwdwqd", "contact_type"=>"employee"}
>> #contact_form = CorporateContactForm.new(corporate_contact_form_params)
=> #<CorporateContactForm id: nil, contact_type: nil, firstname: nil, surname: nil, contact_number: nil, email: nil, company_name: nil, company_website: nil, position_in_company: nil, location: nil, number_of_employees: nil, method_of_payment: nil, comments: nil, created_at: nil, updated_at: nil>
Why aren't the parameters populating the new instance of the class?
I am using rails 4.0
update
model
class CorporateContactForm < ActiveRecord::Base
EMPLOYEE = 'employee'
EMPLOYER = 'employer'
CONTACT_TYPE = [EMPLOYEE, EMPLOYER]
ONE_TO_FIFITY = "1-50"
FIFTY_TO_ONEHUNDRED = "50-100"
ONEHUNDRED_OR_MORE = "100+"
EMPLOYEE_COUNT = [ONE_TO_FIFITY, FIFTY_TO_ONEHUNDRED, ONEHUNDRED_OR_MORE]
OTHER = "Other"
COMPANY = "Company funded"
METHOD_OF_PAYMENT = [COMPANY, OTHER]
validates :contact_type, inclusion: CONTACT_TYPE, presence: true
validates :number_of_employees, inclusion: EMPLOYEE_COUNT, presence: true
validates :firstname, presence: :true
validates :email, presence: :true
validates :company_name, presence: :true
validates :email, presence: :true
validate :blanks_to_nils
private
def blanks_to_nils
blank_to_nil :surname
blank_to_nil :contact_number
blank_to_nil :position_in_company
blank_to_nil :location
blank_to_nil :method_of_payment
blank_to_nil :comments
end

Validates doesn't work?

i am new in ruby and like the syntax! But i keep getting this error, can someone tell me why? I did also try to include it with include ActiveModel::Validations without any luck. Getting this "stack level too deep"
class HomeController < ApplicationController
def index
if params[:username]
l = Users.new(:username => params[:username], :password => params[:password], :email => params[:email]).save
z = Users.where(:username => params[:username]).limit(1).last
#debugging = "Howdy" + z[:username] + ""
end
end
end
users model:
class Users < ActiveRecord::Base
validates :username, :presence => true
attr_accessible :email, :password, :username
end
It should be in your model if you are using active record:
validates :username, :presence => true
It should be User < ActiveRecord::Base:
user.rb
class User < ActiveRecord::Base
validates :username, :presence => true #or you can also write as 'validates_presence_of :username'
attr_accessible :email, :password, :username
end
home_controller.rb
class HomeController < ApplicationController
def index
if params[:username]
l = User.new(:username => params[:username], :password => params[:password], :email => params[:email])
if l.save?
flash[:success] = "Valid user"
else
flash[:error] = "Invalid user"
end
z = User.where(:username => params[:username]).limit(1).last
#debugging = "Howdy" + z[:username] + ""
end
end
end
Your model seems ok
Try to change your index action for this:
if params[:username]
user = User.create(:username => params[:username], :password => params[:password], :email => params[:email])
if user.persisted?
#debugging = "Howdy #{user.username}"
else
#debugging = "Error while creating user"
end
end
If the user is not valid User.create will create an User object not persisted in database

Resources