shoulda matcher and conditional validation errors - ruby

Here I use the attribute :new_record to set the condition for validation.
attr_accessor :new_record
validates :email, presence: true, email: true, unless: :new_record?
def new_record?
#new_record || true
end
if the new_record? == true, the email validation will be skipped and new user is valid.
I wrote this test:
it { expect(subject).to validate_presence_of(:email) }
and it returns error:
Failure/Error: it { expect(subject).to validate_presence_of(:email) }
User did not properly validate that :email cannot be empty/falsy.
After setting :email to ‹nil›, the matcher expected the User to be
invalid, but it was valid instead.
Note that this test run perfectly if there is no condition added to the validation. Also, the code work well in production.
I also have tried
before(:each) do
subject.new_record = true
end
or
`before { allow(subject).to receive(:new_record?).and_return(true) }`
Can any one help?

Related

Using find_or_create_by! in before_action filter

I have a weird behaviour when using User.find_or_create_by! in before_action filter as follows:
class ApplicationController < ActionController::API
before_action :authorize_request
attr_reader :current_user
private
def authorize_request
#current_user = (AuthorizeApiRequest.new(request.headers).call)[:user]
end
end
Then in AuthorizeApiRequest I'm checking for existence or creating a new User by name:
class AuthorizeApiRequest
def initialize(headers = {})
#headers = headers
end
def call
{
user: user
}
end
def user
if decoded_auth_token && decoded_auth_token[:sub]
#user ||= User.find_or_create_by!(username: decoded_auth_token[:sub])
Rails.logger.silence do
#user.update_column(:token, http_auth_header)
end
#user
end
rescue ActiveRecord::RecordInvalid => e
raise(
ExceptionHandler::InvalidToken,
("#{Message.invalid_token} #{e.message}")
)
end
end
Example of UsersController:
class UsersController < ApplicationController
def me
if user_info_service.call
json_response current_user, :ok, include: 'shop'
else
raise AuthenticationError
end
end
private
def user_info_service_class
#user_info_service_class ||= ServiceProvider.get(:user_info_service)
end
def user_info_service
#user_info_service ||= user_info_service_class.new(user: current_user)
end
end
What is weird is that sometimes the User is created twice with the same username, sometimes not.
I'm using Ember JS in the front and another call is made to shops right after the authentication with JWT. All the routes are protected. I have the impression that calling current_user is not always in the same thread or sth like that and it results in having 2 identical users:
- the first one with just a username attribute set
- another one with all the others User attributes.
Here is the User model:
class User < ApplicationRecord
validates :username, presence: true, uniqueness: { case_sensitive: false }, on: :create
validates :shop_identifier, numericality: { only_integer: true, greater_than: 0 }, on: :update
validates :first_name, presence: true, on: :update
validates :last_name, presence: true, uniqueness: { case_sensitive: false, scope: :first_name }, on: :update
before_update do |user|
user.first_name = first_name.strip.capitalize
user.last_name = last_name.strip.upcase
end
Any ideas ? Thank you

Ruby - ActiveModel::SecurePassword not working

I have a simple ruby class with ActiveModel::Validations and ActiveModel::SecurePassword included, I provide all required attributes (below) to the new obkject but when I validate it it says it's false.
require 'active_model'
require 'bcrypt'
class User
include ActiveModel::Validations
include ActiveModel::SecurePassword
attr_accessor :name, :email, :password, :password_digest
def initialize(name:, email:, password:)
#name, #email, #password = name, email, password
end
validates :name, :email, presence: true
has_secure_password
end
user = User.new(
name: "TestUser1",
email: "test#gmail.com",
password: "password"
)
puts user.valid? => false
puts user.errors.messages => {:password=>["can't be blank"]}
puts user.password => password
According to the documentation here has_secure_password provides validations on password accessor:
Password should be present.
Password should be equal to its confirmation (provided password_confirmation is passed along).
The maximum length of a password is 72 (required by bcrypt on which ActiveModel::SecurePassword depends)
What am I doing wrong? How's this object false?
EDIT
I have also tried adding password_confirmation attribute but it didn't work either.
user.password_confirmation = "password"
puts user.valid? => false
puts user.errors.messages => {:password=>["can't be blank"]}
Since you need to have password_digested filled by ActiveModel::SecurePassword you have to call User#password= setter method. But it's not happening when you set your password using #password = password in your initializer. To fix it you have set it using self.password = password:
def initialize(name:, email:, password:)
#name, #email = name, email
self.password = password
end
Also you need to remove :password from attr_accessor call because SecurePassword provides it.
I believe you need to set the user.password_confirmation attribute as well.
http://api.rubyonrails.org/classes/ActiveModel/SecurePassword/ClassMethods.html#method-i-has_secure_password

ActiveModel presence validation fails

I have created a model (called PhoneNumber) that is not backed by a database table:
class PhoneNumber
include ActiveModel::Validations
attr_accessor :pnumber
validates :pnumber, presence: true, on: :create #=> { :message => " cannot be blank" }
validates :pnumber, numericality: true, on: :create
end
I am testing it in the console like this:
2.0.0-p451 :001 > example = PhoneNumber.new
=> #<PhoneNumber:0x007f839c90c690>
2.0.0-p451 :002 > example.valid?
=> true
2.0.0-p451 :003 > example.pnumber
=> nil
As you can see, the empty PhoneNumber is considered valid even if the :pnumber attribute is nil, i.e. the validation is not working. How to fix it?
Long story short, on: :create does not work in this context.
Usually validations are not tied to actions anyways, so you're good with just removing it.
Have a good one,
Jan

wrong redirect with capybara

I have problem in my previous question, me helped, but and now I've took new.
I'm make integration tests with rspec and capybara.
this my profiles_controllers.rb :
before_filter :authenticate_user!
def update
#profile = current_user.profile
if #profile.update_attributes(params[:profile])
flash[:success] = "Профиль обновлен!"
redirect_to user_path(current_user)
else
render 'edit'
end
end
it's my test file:
describe "ProfilePages" do
subject { page }
describe "edit" do
let(:user) { FactoryGirl.create(:user) }
let(:profile) { FactoryGirl.create(:profile, user: user) }
before do
login user
visit edit_profile_path(profile)
end
it { should have_selector('h2', text: 'Заполните информацию о себе') }
describe "change information" do
let(:new_city) { "Ulan-Bator" }
let(:new_phone) { 1232442 }
let(:new_gamelevel) { "M2" }
let(:new_aboutme) { "nfsfsdfds" }
let(:submit) { "Сохранить" }
before do
fill_in "Город", with: new_city
fill_in "Телефон", with: new_phone
select new_gamelevel, from: "Уровень игры"
fill_in "О себе", with: new_aboutme
click_button submit
end
specify { profile.reload.city.should == new_city }
specify { profile.reload.phone.should == new_phone }
specify { profile.reload.gamelevel.should == new_gamelevel }
specify { profile.reload.aboutme.should == new_aboutme }
end
describe "submitting to the update action" do
before { put profile_path(profile) }
specify { response.should redirect_to(user_path(user)) }
end
end
end
And I have error:
Failure/Error: specify { response.should redirect_to(user_path(user)) }
Expected response to be a redirect to http://www.example.com/users/1 but was a redirect to http://www.example.com/users/sign_in
I use Devise and have login helper in spec/support:
def login(user)
page.driver.post user_session_path, 'user[email]' => user.email, 'user[password]' => user.password
end
And config.include Devise::TestHelpers, :type => :controller in spec_helper.rb
I tried use warden helper login_as , but have same error. How I understand it's don't start session, I'am right?
This is nothing to do with your app code, but the test code.
response object is for controller integration tests, and there is no such object in Capybara.
Normally you can use page object to check response information. And for path checking, a better approach is current_path or current_url.
So your code will work by:
current_path.should be(user_path(user))

Rails 3.2.9.Testing observer with RSpec(trouble with should_receive)

I have such problem. My test checks whether the Observer called, but does not execute it.
My files:
todo_observer.rb:
class TodoObserver < ActiveRecord::Observer
def after_create(todo)
todo.add_log('creating')
end
end
todo.rb:
class Todo < ActiveRecord::Base
attr_accessible :content, :done, :order
validates :content, :presence => true,
:length => {:minimum => 2}
def add_log(event)
Logdata.start_logging(self.content, event)
end
end
logdata.rb
class Logdata < ActiveRecord::Base
attr_accessible :modification, :event
def self.start_logging(content, event)
Logdata.create!(:modification => content, :event => event)
end
end
todo_observer_spec.rb:
require 'spec_helper'
describe TodoObserver do
before(:each) do
#attr = { :modification => "Example", :event => 'Event' }
#attr_todo = { :content => "Example", :done => :false }
end
describe 'after_create' do
it "should create log about creating task" do
count_log = Logdata.all.size
todo = Todo.new(#attr_todo)
todo.should_receive(:add_log).with('creating')
todo.save!
(Logdata.all.size).should eq(count_log + 1)
end
end
end
When I run test I get such error
Failure/Error: (Logdata.all.size).should eq(count_log + 1)
expected: 1
got: 0
Its mean, that observer called,but doesn't create instance of Logdata. When I comment string(check the call)
todo.should_receive(:add_log).with('creating')
My tests were successful.And accordingly its success when I comment string (Logdata.all.size).should eq(count_log + 1)and uncomment previous string.
How does the function should_receive to create an instance of the class Logdata?
should_receive prevents the actual method from being called.
You should create two separate tests. One to check that the log is added to the todo, and one to check that the log is created.
describe 'after_create' do
it "should add a log to the todo" do
todo = Todo.new(#attr_todo)
todo.should_receive(:add_log).with('creating')
todo.save!
end
it "should create a new logdata" do
todo = Todo.new(#attr_todo)
expect {
todo.save!
}.to change {Logdata.count}.by(1)
end
end

Resources