CanCan abilities working in application but not in test - ruby-on-rails-3

I have the following definition in my ability.rb file:
can :index, Call, :country_id => user.countries
I am trying to only index Calls whose country_id is in current user's countries array.
This seems to be working when I try it: It only shows me calls that have been tagged by countries that the current_user is a member of.
However, I have the following test which I can't seem to get to pass:
before :each do
# Users
#admin = FactoryGirl.create(:user, admin: true)
end
it "should show the call index page with just calls that have the same country tags as them" do
anotheruser1 = FactoryGirl.create(:user)
anotheruserscall1 = FactoryGirl.create(:call, user_id: anotheruser1.id)
anotheruser2 = FactoryGirl.create(:user)
anotheruserscall2 = FactoryGirl.create(:call, user_id: anotheruser2.id)
#admin.countries << anotheruserscall1.country
ability = Ability.new(#admin)
ability.should be_able_to(:index, anotheruserscall1)
ability.should_not be_able_to(:index, anotheruserscall2)
end
But I get a failure:
Failures:
1) Admin ability should show the call index page with just calls that have the same country tags as them
Failure/Error: ability.should be_able_to(:index, anotheruserscall1)
expected to be able to :index #<Call id: 1, subject: "Natus qui fuga ut.", description: "Consequatur aut veritatis earum dolor. Reprehenderi...", from: "2012-10-25 18:43:23", to: "2012-10-25 19:43:23", user_id: 7, status: "Tentative", created_at: "2012-10-29 13:41:07", updated_at: "2012-10-29 13:41:07", expert_id: nil, country_id: 1>
# ./spec/models/ability_spec.rb:291:in `block (2 levels) in <top (required)>'
Any help greatly appreciated.

OK, I fixed this by passing the details in as a block as follows:
can :index, Call, ["country_id IN (?)", user.countries] do |call|
user.countries.include?(call.country)
end

Related

creating a custom path-helper while using FreindlyID

we're trying to get our site to have less scrapeable AND more readable urls
so e.g.
www.loomio.org/discussions/3122
becomes
www.loomio.org/d/3saA4ds9/lets-go-to-the-moon
we only really want the human-readable slug on show-links, so www.loomio.org/d/3saA4ds9/edit should be the url for editing that discussion
the solution so far follows the top answer here:
Ruby on Rails: How to override the 'show' route of a resource?
modify routes.rb:
get '/d/:id/:slug', to: 'discussions#show', as: :discussion
resources :discussions, path: 'd', except: [:edit, :show] do
get :activity_counts, on: :collection
member do
post :update_description
post :add_comment
post :show_description_history
get :new_proposal
post :edit_title
put :move
end
end
install gem FriendlyID; make and populated a :key column on Discussion table; add the following to discussion.rb (model):
KEY_LENGTH = 10
extend FriendlyId
friendly_id :key
write a custom path helper for group_path. in groups_helper.rb:
def group_url(group, options={})
url_for(options.merge(:controller => 'groups', :action => 'show',
:id => group.key, :slug => group.full_name.parameterize))
end
def group_path(group, options={})
group_url(group, options.merge(:only_path => true))
end
rake routes produces:
group GET /g/:id/:slug(.:format) groups#show
and while calling group_path(group) seems to work in some cases, I'm also seeing strange unrelated urls get generated, like :
http://loomio.org/group_requests/TegFOIx4DB/start_new_group?action=show&controller=groups%2Fgroups&slug=19-tory
in console, I'm also getting errors such as:
[5] pry(main)> g = Group.last
[6] pry(main)> app.group_path(g)
ActionController::RoutingError: No route matches {:controller=>"groups", :action=>"show", :id=>#<Group id: 2811, name: "Sylvester Buckridge", created_at: "2013-12-10 06:25:42", updated_at: "2013-12-10 06:25:42", privacy: "public", members_invitable_by: "members", parent_id: nil, email_new_motion: true, hide_members: false, beta_features: false, description: "A description for this group", memberships_count: 1, archived_at: nil, max_size: 300, cannot_contribute: false, distribution_metric: nil, sectors: nil, other_sector: nil, discussions_count: 0, motions_count: 0, country_name: nil, setup_completed_at: "2013-12-10 05:25:01", next_steps_completed: false, full_name: "Sylvester Buckridge", payment_plan: "undetermined", viewable_by_parent_members: false, key: "rkdlTytOin">}
from /home/s01us/.rvm/gems/ruby-2.0.0-p247/gems/actionpack-3.2.16/lib/action_dispatch/routing/route_set.rb:540:in `raise_routing_error'
I've tried putting the group_path and grop_url methods in ApplicationController and ApplicationHelper to no avail.
calling
group_path( group.key, group.fullname.parameterize )
works, but would ideally like to be able to only have to call e.g.
group_path(#group)
as far as i understand the problem, you could use the good old hack with defining the to_param method on your model
class Group < ActiveRecord::Base
def to_param
"#{id}-#{slug}"
end
end
The beauty of this solution is that you won't need to do anything else. Rails will automatically use the to_param method as your record ID when it generates an URL from a record. You can do anything
redirect_to group_path(#group)
redirect_to #grup
# etc
and your Group.find should eat it as it is 123-smth-smth, usually it is smart enough to extract the integer part of the id

While testing Devise with rspec, it acts like I am not logged in

I'm using Rails 3.2.13, devise 2.2.4, and rspec 2.13.2. I have a multi-tenant app in which the default scope only shows current_user's items. Everything seems to work in development, but my tests are having issues.
Problem: When I create a meal in my test, it shows user_id => nil. In my development environment everything works, though. Also, when I go into debug mode, when I check the #controller.current_user, it shows user_id 1 is logged in. Since in my development environment the user_id is being assigned automatically when I create an object, I can't figure out why it's not doing it during tests. Any ideas?
Here's my abbreviated meals_controller_spec file:
describe MealsController do
include Devise::TestHelpers
describe "When logged in as user," do
before(:each) do
#request.env["devise.mapping"] = Devise.mappings[:user]
#email = "someone#test.com"
#password = "password"
#user = User.create(email: #email, password: #password)
sign_in #user
end
describe "DELETE destroy" do
it "destroys the requested meal" do
meal = Meal.create!("name" => "", "servings" => "2", "meal_type" => "dinner")
expect { delete 'destroy', {:id => meal.id} }.to change(Meal, :count).by(-1)
end
end
end
end
Here's part of my model:
class Meal < ActiveRecord::Base
belongs_to :user
default_scope { where(user_id: User.current_id) }
Here's part of my controller:
class MealsController < ApplicationController
before_filter :authenticate_user!
def destroy
#meal = Meal.find(params[:id])
#meal.destroy
redirect_to meals_url, :notice => "Successfully deleted #{#meal.name}."
end
end
And I think my application controller could be relevant:
class ApplicationController < ActionController::Base
protect_from_forgery
around_filter :scope_current_tenant
private
def scope_current_tenant
User.current_id = current_user.id
yield
rescue Exception
yield
ensure
User.current_id = nil
end
end
Here's some output when I debug in the middle of that test:
(rdb:1) #controller.current_user.id
1
(rdb:1) Meal.create!(name: "meal", meal_type: "dinner", servings: 2)
#<Meal id: 2, name: "meal", meal_type: "dinner", servings: 2, user_id: nil, created_at: "2013-06-17 06:07:34", updated_at: "2013-06-17 06:07:34">
(rdb:1) Meal.create!(name: "meal", meal_type: "dinner", servings: 2, user_id: 1)
#<Meal id: 3, name: "meal", meal_type: "dinner", servings: 2, user_id: 1, created_at: "2013-06-17 06:12:18", updated_at: "2013-06-17 06:12:18">
(rdb:1) Meal.find(3).destroy
*** NoMethodError Exception: undefined method `destroy' for nil:NilClass
So I guess I'm wondering why the user in my test appears to be nil, even though I am using the Devise test helpers and signing in, and in my dev environment everything works.
Edit:
Here are some more debugger results:
(rdb:1) Meal.find(4)
*** ActiveRecord::RecordNotFound Exception: Couldn't find Meal with id=4 [WHERE "meals"."user_id" IS NULL]
(rdb:1) Meal.find(4).destroy
*** ActiveRecord::RecordNotFound Exception: Couldn't find Meal with id=4 [WHERE "meals"."user_id" IS NULL]
(rdb:1) Meal.where(:user_id => 1).find(3)
#<Meal id: 3, name: "meal", meal_type: "dinner", servings: 2, user_id: 1, created_at: "2013-06-17 06:12:18", updated_at: "2013-06-17 06:12:18">
It appears that the "current user" is in fact user_id => nil, per the sql snippets. So why would that be if I used the Devise test helper sign_in function?

result should have been changed by 1, but wasn't?

I'm having trouble getting this pass, although things work in both the console and the browser. Thanks for your help!
Here's my spec:
before(:each) do
#attr = {name: "Example Class"}
#create = post :create, {user_id: #user.id, student_group: #attr}
end
it "should create a user" do
lambda do
#create
end.should change {#user.student_groups.count}.by(1)
end
And my new and create actions in the controller:
def new
#student_group = #user.student_groups.new
#title = "Create a new class"
end
...
def create
#student_group = #user.student_groups.create(params[:student_group])
if #student_group.save
# for now redirect to
redirect_to classes_path, flash: { success: "#{#student_group.name} created! Next, add some students" }
# redirect_to new_student_group_student_path
else
#title = "Create a new class"
flash.now[:error] = "Something's gone wrong. Please try again!"
render 'new'
end
end
and the error:
1) StudentGroupsController POST 'create' success should create a user
Failure/Error: lambda do
result should have been changed by 1, but was changed by 0
# ./spec/controllers/student_groups_controller_spec.rb:113:in `block (4 levels) in <top (required)>'
Your lambda does not contain executable code, I think the moving creation code to this lambda will resolve the issue.
it "should create a user" do
lambda do
post :create, {user_id: #user.id, student_group: #attr}
end.should change {#user.student_groups.count}.by(1)
end

Failing test in ruby tutorial (Michael Hartl)

I'm studying Michael Hartl's tutorial. I'm using RSPEC to run the test.
So far so good but it seems that I've hit the wall with the following example.
Here is the test that fails (it should pass):
describe "authenticate method" do
it "should return the user on email/password match" do
matching_user = User.authenticate(#attr[:email], #attr[:password])
matching_user.should == #user
end
end
Just in case.
#user defined as:
before(:each) do
#user = User.create!(#attr)
end
#attr defined as:
before(:each) do
#attr = {
:name => "Example user",
:email => "user#example.com",
:password => "foobar",
:password_confirmation => "foobar"
}
end
Entries in user.rb
before_save :encrypt_password
def has_password?(submitted_password)
encrypted_password == encrypt(submitted_password)
end
def self.authenticate(email, submitted_password)
user = find_by_email(email)
return nil if user.nil?
return user if user.has_password?(submitted_password)
end
private
def encrypt_password
self.salt = make_salt unless has_password?(password)
self.encrypted_password = encrypt(password)
end
def encrypt(string)
secure_hash("#{salt}--#{string}")
end
def make_salt
secure_hash("#{Time.now.utc}--#{password}")
end
def secure_hash(string)
Digest::SHA2.hexdigest(string)
end
Error message displayed when the test is failing
c:\RailsInstaller\work\apptwit>rspec spec/models/user_spec.rb
.................F
Failures:
1) User password validations password encryption authenticate method should return the user on email/password
Failure/Error: matching_user.should == #user
expected: #<User id: 1, name: "Example user", email: "user#example.com", created_at: "2011-12-07 19:08:23
ed_at: "2011-12-07 19:08:23", encrypted_password: "fbdbaf712fa1b6c925c4ab2192e73ac9f9d1bedf67630610d68...">
got: nil (using ==)
# ./spec/models/user_spec.rb:204:in `block (5 levels) in <top (required)>'
Finished in 221.37 seconds
18 examples, 1 failure
Failed examples:
rspec ./spec/models/user_spec.rb:202 # User password validations password encryption authenticate method should
he user on email/password match
I would appreciate any pointers,
Thanks a lot.
If matching_user is nil, then you might want to put some puts email and puts user.inspect statements in self.authenticate to debug it.
It seems as though it's either not able to find the user by email or your password is incorrect for some reason in the authenticate method.

Rails 3 + Rspec 2: Validation failed: Email has already been taken

I have 2 models, User and Bucket. User has_many Buckets and a Bucket belongs_to a User.
In factories.rb, I have:
Factory.define :user do |user|
user.email "teste#test.com"
user.password "foobar"
user.password_confirmation "foobar"
end
Factory.sequence :email do |n|
"person-#{n}#example.com"
end
Factory.define :bucket do |bucket|
bucket.email "user#example.com"
bucket.confirmation false
bucket.association :user
end
and I have a login_user module as follows:
def login_user
before(:each) do
#request.env["devise.mapping"] = Devise.mappings[:user]
#user = Factory.create(:user)
##user.confirm!
sign_in #user
end
end
I am using Spork and Watch and my Buckets_controller_spec.rb is as simple as:
describe "User authenticated: " do
login_user
#bucket = Factory(:bucket)
it "should get index" do
get 'index'
response.should be_success
end
...
end
The error is always the same:
Failures:
1) BucketsController User authenticated: should get index
Failure/Error: Unable to find matching line from backtrace
ActiveRecord::RecordInvalid:
Validation failed: Email has already been taken
# ./lib/controller_macros.rb:12:in `block in login_user'
And it only happens when I have the Factory(:bucket). The login works fine when I don't add the Factory(:bucket).
It's always the same error. I have tried adding :email => Factory.next(:email) to the user, but no success.
Edit:
In rails c test:
ruby-1.9.2-p180 :019 > bucket = Factory(:bucket, :email => "hello#hello.com")
ActiveRecord::RecordInvalid: Validation failed: Email has already been taken
ruby-1.9.2-p180 :018 > Bucket.create(:email => "hello#hello.com")
=> #<Bucket id: 2, email: "hello#hello.com", confirmation: nil, created_at: "2011-04-08 21:59:12", updated_at: "2011-04-08 21:59:12", user_id: nil>
Edit 2:
I found out that the error is in the association, however, I don't know how to fix it.
bucket.association :user
When you define a factory with an association you need to give the factory an object to associate with whenever you use the factory.
This should work:
describe "User authenticated: " do
login_user
#bucket = Factory(:bucket, :user => #user)
it "should get index" do
get 'index'
response.should be_success
end
end
That way factorygirl knows to make a bucket which is associated with #user.
Try this in your user factory:
Factory.define :user do |f|
f.sequence(:email) { |n| "test#{n}#example.com" }
...
end
I think that's probably your problem. When you use f.email = "anyvalue" it's going to use that value every time. I see you were trying to create a sequence in the next block, but I'm not sure that sequence is getting used.
ALSO - be aware that if you get tests interrupted by a crash or something, sometimes bogus test data can get left in your test DB instead of being rolled back.
Very first thing I try if something worked once and then quit working is to reset the test db.
rake db:test:prepare
That will clean everything out.
If this doesn't work let me know and I'll take a second look!
If someone is getting this recently with your views. Try using Database Cleaner.
For more info: RailsTutorial - chapter 8.4.3 - Test database not clearing after adding user in integration test