Rails: Integration test data - ruby-on-rails-3

I'm new to Rails testing and I'm confuse with the nature of rails integration test data:
test "should register new user and login" do
..
# create user
post users_path, :user => {:username => "newuser", :email => "newuser#gmail.com",
:password => "secret",
:password_confirmation => "secret"}
assert assigns(:user).valid?
..
end
test "another should register new user and login" do
..
# create user
post users_path, :user => {:username => "newuser", :email => "newuser#gmail.com",
:password => "secret",
:password_confirmation => "secret"}
assert assigns(:user).valid?
..
end
In the User model i have a validation to ensure that :username and :email are unique. But how come the test was able to post both data with no complain/error/invalid? Any help is appreciated, I thought there's a single DB only for the test and the test data should've clashed. Thanks.

Good question. Before the test framework (Test::Unit) runs each test it resets the database to its original state. So before each test you are guaranteed that the DB only has the fixture data and nothing else.
This is helpful. You don't want the data from one test still to be there when you start the next test. If it were, the DB state would be inconsistent if you did something like run a test by itself (instead of the whole suite) or run tests in a different order.
The solution: you can write a test that specifically verifies that the :username and :email are unique. Before I give an example, I should mention that it looks like you wrote a functional test for this validation. This is tempting because you're verifying the behavior the user will see, but the place to test validation is in the unit tests because validation rules in Rails get pushed down to the models. (To keep validation DRY. You don't have to duplicate your validation rules across a bunch of controllers)
So here's one way you could write the unit test:
test "should validate unique email" do
attributes = { :username => "newuser", :email => "newuser#gmail.com", :password => "secret", :password_confirmation => "secret" }
# create user
user1 = User.create(attributes)
assert_nil user1.errors.on(:username)
assert_nil user1.errors.on(:email)
# create another user with the same name and email
user2 = User.create(attributes)
assert_not_nil user2.errors.on(:username)
assert_not_nil user2.errors.on(:email)
end
For a good article about this, see John Nunemaker's blog post from two years ago. Ignore the accidental HTML tags in his code samples. Nunemaker is an awesome Rails blogger.
There are some alternate testing libraries like Shoulda that have built-in functions to validate that your model validates uniqueness. But since you're new to Rails I'd recommend sticking with the default test framework for now. (In fact the folks at 37Signals who invented Rails still use the default too, so that says a lot for it. I also prefer to stick with the default.)
By the way, you can format code in a StackOverflow question by indenting it with four spaces.

Related

Rails 3.2.20 setting up Devise :lockable

I have a Rails 3.2.20 app which uses Devise 2.1.2. Everything works fine, but Id like to add the :lockable feature to my project. After 5 failed attempts lock the account for 2 hours.
I've been reading over the Devise documentation and various StackOverflow questions and answers but I have a different setup than some.
I handle sessions in my own controller and also use the strategy to login via email or username from the Devise WiKi.
Here is my current setup:
routes.rb
devise_for :users, :controllers => { :sessions => "my_sessions" }
my_sesssions_controller.rb
class MySessionsController < Devise::SessionsController
skip_before_filter :check_concurrent_session
def create
super
set_login_token
end
private
def set_login_token
token = Devise.friendly_token
session[:token] = token
current_user.login_token = token
current_user.save(validate: false)
end
end
user.rb
attr_accessor :login
def self.find_first_by_auth_conditions(warden_conditions)
conditions = warden_conditions.dup
if login = conditions.delete(:login)
where(conditions).where(["lower(username) = :value OR lower(email) = :value", { :value => login.downcase }]).first
else
where(conditions).first
end
end
config/initializers/devise.rb (Excerpt)
config.authentication_keys = [ :login ]
config.case_insensitive_keys = [ :username ]
config.strip_whitespace_keys = [ :username ]
So my question is, considering I handle sessions in my own controller and am also passing authentication via either email or username, how can I implement :lockable to lock out the account for 2 hours after 5 failed attempts?
I'm assuming there's a migration that I will need to generate and run, as well as setting the locking and unlocking strategy.
I read this post How to make devise lockable with number of failed attempts but I'm a bit unsure of the unlock_keys and how that works. Also in this post it talks about generating an index for the unlock_token, but I don't think I will need that since I'll be using a :time unlock_strategy.
Basically, I have Devise working well and I don't want to muddy the water or introduce any bugs into my project.
If anyone can help guide me through setting this up, I'd appreciate your assistance. Sorry if the question is redundant or not clear.
Thanks in advance for your help.

validates_inclusion_of Rails 3 with a query not working as expected

I am newish to Rails and i am facing a prblem. I have
Models
Model1
id: integer
account_id: integer
Account
id: integer
I have a validation in the Model1 as follows,
Model Code:
validates :account_id, :inclusion => { :in => Account.find(:all).collect(&:id)}
I use a dropdown for
View Code:
<%= f.select :account_id, #accounts.collect {|acc| [acc.name, acc.id]}, {:prompt => 'Select Account' }, { :selected => #defaultaccount, :class => 'selectwidth' } %>
I am using client side validations to validate the fields before submitting the form. I am using Heroku to deploy my app.
I create an account first and then from the form i try to create a new model1. The drop down is populated with all the accounts that i have. Then i select an account that i created from the dropdown, the client side validations comes into effect and says "is not valid" which is the error message for validates_inclusion_of as if the account never exists. Not sure what ishappening and i checked the database and the account is created which is why the dropdown is loaded with accounts in the first place.
I change the validates_inclusion_of to rails 3 validates :account_id, :inclusion => { :in => {} } syntax and redeploy. Now i try to select that account in the new model1 form, The drop down does not show an error for the account that was previously created.
So then i created a new account and tried to create a new model1 for the new account the validation failure resurfaces.
So its like a cache issue ? Is it a client side validations issue ? I did everything i can to try and find what the issues is but i am out of ideas on what is happening.
So everytime i create an account it expects me redeploy which kills what the application does. How do i get rid of this problem ? Not really sure what is happening.
Config,
Rails 3
Deployed in Heroku
uses Asset pipeline
Gems:
Client Side Validations
Kaminari
It's not working as you want because the :in => Account.find(:all).collect(&:id) it's evaluated just one time in production mode, when Rails loads the class. I confess I don't understand the reason behind such a validation. Anyway, you have to use a custom validator if you really want to achieve that.
Currently, the statement gets executed once the server is started (as you correctly realized). You'll need to wrap it in a lambda, so it gets executed everytime the validation is done.
1.8.7 Syntax:
validates :account_id, :inclusion => { :in => lambda {Account.find(:all).collect(&:id)}}
1.9.2 Syntax:
validates :account_id, :inclusion => { :in => ->{Account.find(:all).collect(&:id)]}

Rails/Rspec: Testing uniqueness validations in models without touching the database

I current test uniqueness validations in rspec using something like the following:
#unique = Unique.create!(:unique_field => 'unique_value')
Unique.new(:unique_field => 'unique_value').should_not be_valid
Ideally, I'd like to get my nose out of the database for uniqueness validations. Any ideas how to go about doing that?
validates_uniqueness_of does not guarantee the absence of duplicate record insertions, because uniqueness checks on the application level are inherently prone to race conditionsmore. To get around this problem you might define unique index on on your unique field or write some workaround to have tests that don't depend on your database however I don't see any point in writing these tests. You'll just have some useless code in your app.
you can change the unique value dynamically by editing your factory to change every time you run test
for example
Factory.define :user do |f|
f.sequence(:username) { |n| "foo#{n}" }
f.password "foobar"
f.password_confirmation { |u| u.password }
f.sequence(:email) { |n| "foo#{n}#example.com" }
end
iHope it helpful , good luck
source : Railscasts Episode #158: Factory Girl

Rails 3: client_side_validations gem and devise password validations

I've got the client_side_validations gem working really great on my forms. I'm using Devise for my users model and when I go to the edit_user_registration form, validations are thrown up for everything except the :current_password, :password, and :password_confirmation fields.
Fort instance is I leave the email blank, the validation pops up right when I tab out of the field. However, if I leave the current_password field blank and tab out of it nothing happen, then when I submit the form I get the error "1 error prohibited this user from being saved: password can't be blank"
Thanks for any help
http://pastie.org/1907464
Currently ClientSideValidations will filter out any conditional validators. Devise sets some of the validators as conditional: https://github.com/plataformatec/devise/blob/master/lib/devise/models/validatable.rb#L24-32
The reason I did this is because there is no good way for the client to determine the true value of that conditional. I could do it at the time the form is generated but what if that conditional relied upon a value that could be changed on the form? So I opted to filter them and let things fall back to the server.
That was the idea but clearly it has imposed unfair limitations on some things. This being the most obvious (and popular).
So I plan on releasing a new version very soon that will allow you to explicitly override the conditional filters. It will work like this:
<%= f.text_field :password, :validate => { :presence => true, :confirmation => true } %>
or
<%= f.text_field :password, :validate => true %>
In the first case you can choose which validators to turn the filter off. In the 2nd case it will turn the filter off for all validators on that attribute. The conditional will be evaluated at the time the form is generated and if it passes it will add the validator to the input element for use on the client.
The master branch now supports this format. Point your Gemfile to it and you should be good
It’s simple! The gem extends the Rails default form builder, and all you have to do is set a :validate => true option on any form_for (or simple_form_for for simple form users) tag that you want the inline validations for. The form builder uses some rails reflections on your model validations to generate some json that gets included in a script tag after your form. The json is then used by the gem’s Javascript framework to perform the validations that need to be performed.
<%= form_for(#user, :url => registration_path(resource_name), :validate => true) do |f| %>

Testing route in controller

I have a Rails 3.0 web app that allow user to create own path to the application.
example : www.my_app.com/user_company_name
So I store a custom path in user DB field. User can changing path throught a input.
I have added this validation in model
validates_presence_of :custom_page
validates_format_of :custom_page, :with => /^([a-z]|[0-9]|\-|_)+$/, :message => "Only letter (small caps), number, underscore and - are authorized"
validates_length_of :custom_page, :minimum => 3
validates_uniqueness_of :custom_page, :case_sensitive => false
But I don't know how I can validate url to check it isn't in conflict with another route in my routing.
For example in my route.rb I have
resources :user
Validation need to don't allow using www.my_app.com/user, how I can do that?
Thanks, vincent
In your routes, you match the company name to a variable
match 'some_path/:company_name.format'
you can then do the lookup using company_name which rails will populate for you.
Validating the uniqueness of the custom_page variable should be enough to ensure there's no overlap. (note that validate uniqueness of doesn't scale -- if this will be big, you need a db constraint as well) as long as users can only specify one field.
If you're letting users specify
'some_path/:custom_path_1/:custom_path_2.format'
then you have to validate across both fields, and now it's getting messy. Hope you're not doing that.
You can try a custom validation to weed out "user"
validate :custom_page_cant_be_user
def custom_page_cant_be_user
errors.add(:custom_page, "can't be `user`") if self.custom_page =~ /^user$/i
end
assuming :custom_page comes in as a basic [a-z], if :custom_page has /user you need to update the regex a bit.