0

This started out as a problem I could not get past in the rails tutorial, but at this point I would just like to understand whats going on, if I can I should be able to get past my problem in the tutorial.

My problem boils down to this. I am trying to set a cookie in a test, and then check it's value in the controller after doing a put, but I don't see the cookie in the controller.

Here is the code in the test:

describe "as wrong user" do
  let(:user) { FactoryGirl.create(:user) }
  let(:wrong_user) { FactoryGirl.create(:user, email: "wrong@example.com") }
  before do
      puts "setting up test - set remeber_token"
      cookies['remember_token'] = user.remember_token
      puts "cookies['remember_token'] = #{cookies['remember_token']}"
  end
  describe "submitting a PUT request to the Users#update action" do
    before do 
      puts "in test " 
      puts "cookies['remember_token'] = #{cookies['remember_token']}"
      put user_path(wrong_user) 
    end
    specify { response.should redirect_to(root_url) }
  end
end

and here is the code in the controller (I think this is all that is relevant)

before_filter :signed_in_user, only: [:edit, :update]
before_filter :correct_user,   only: [:edit, :update]

    def signed_in_user
      puts "in signed_in_user filter"
      puts "cookies['remember_token'] = #{cookies['remember_token']}"
      redirect_to signin_url, notice: "Please sign in." unless signed_in?
    end

    def correct_user
      @user = User.find(params[:id])
      redirect_to(root_url) unless current_user?(@user)
    end

When I run the tests the output seems to indicate that the cookie is set in the test but is gone in the before filter. The test ultimately fails because because the signed_in? method is supposed to see if a user exists with the remember_token retrieved from the cookies object, which is nil. Can someone explain what is going on here?

Here is the console output:

>bundle exec rspec spec/requests/authentication_pages_spec.rb -e "submitting a PUT request to the Users#update action"
Run options: include {:full_description=>/submitting\ a\ PUT\ request\ to\ the\ Users\#update\ action/}
setting up test - set remeber_token
cookies['remember_token'] = qpcZCCuhaoBirjWR9s5YMw
in test
cookies['remember_token'] = qpcZCCuhaoBirjWR9s5YMw
in signed_in_user filter
cookies['remember_token'] =
F

Failures:

  1) Authentication authorization as wrong user submitting a PUT request to the Users#update action
     Failure/Error: specify { response.should redirect_to(root_url) }
       Expected response to be a redirect to <"http://www.example.com/"> but was a redirect to <"http://www.example.com/signin">
     # ./spec/requests/authentication_pages_spec.rb:84:in `block (5 levels) in <top (required)>'

Finished in 0.34502 seconds
1 example, 1 failure

Failed examples:

rspec ./spec/requests/authentication_pages_spec.rb:84 # Authentication authorization as wrong user submitting a PUT request to the Users#update action

UPDATE: I tried using request.cookies['remember_token'] to set the cookie in the test and retrieve in the before filter, and I am having the same issue.

so basically I have this:

describe "as wrong user" do
  describe "in the Users controller" do
    let(:user) { FactoryGirl.create(:user) }
    let(:wrong_user) { FactoryGirl.create(:user, email: "wrong@example.com") }
    before do 
      puts "setting up test - set remeber_token"
      get root_url
      request.cookies['remember_token'] = user.remember_token
      puts "cookies['remember_token'] = #{request.cookies['remember_token']}" 
    end  

    describe "visiting Users#edit page" do
      before { visit edit_user_path(wrong_user) }
      it { should_not have_selector('title', text: full_title('Edit user')) }
    end

    describe "submitting a PUT request to the Users#update action" do
      before do 
        puts "in test " 
        puts "cookies['remember_token'] = #{request.cookies['remember_token']}"
        put user_path(wrong_user) 
      end
      specify { response.should redirect_to(root_url) }
    end
  end

and in the before_filter :

  def signed_in_user
      puts "in signed_in_user filter"
      puts "cookies[:remember_token] = #{request.cookies[:remember_token]}"
      puts "request = #{request.cookies}"
      p request
      redirect_to signin_url, notice: "Please sign in." unless signed_in?
    end

and the puts "request = #{request.cookies}" returns => request = {}

Update:

I now understand what is going on here. I have too look back through the tutorial to see if I missed something, or find the correct way around this. Here is what is happening.

In the ApplicationController I have this (copied directly from the tutorial)

class ApplicationController < ActionController::Base
  protect_from_forgery
  include SessionsHelper

  # Force signout to prevent CSRF attacks
  def handle_unverified_request
    sign_out
    super
  end   
end

The sessions helper has this sign_out method:

  def sign_out
    puts "in sign out"
    cookies.delete :remember_token
    self.current_user = nil
  end

So what happens is that when I do the put in the test, the user gets signed out and as a result is redirected to the sign in page rather than the root_url. I have to look through the tutorial to see what I might have missed, or maybe it's an oversight?

SOLVED!

It turns out this was a very simple set up issue in my environment. I had set the environment variable RAILS_ENV="development". Removing that allows the first line in the spec_helper.rb to correctly set the RAILS_ENV to 'test' which turns protect_from_forgery off in the test env, which prevents the sign_out when I do the put, and allows the test to pass.

I doubt many others will have this same issue, but if you are going through the rails tutorial and are failing the following test, make sure your tests are being run in the test environment.

Failures:

  1) Authentication authorization as wrong user submitting a PUT request to the Users#update action
     Failure/Error: specify { response.should redirect_to(root_url) }
       Expected response to be a redirect to <"http://www.example.com/"> but was a redirect to <"http://www.example.com/signin">
     # ./spec/requests/authentication_pages_spec.rb:84:in `block (5 levels) in <top (required)>'
nPn
  • 16,254
  • 9
  • 35
  • 58

2 Answers2

2

Try to change cookies['remember_token'] = user.remember_token to request.cookies['remember_token'] = user.remember_token in your before block.

By the way in this post there is a good explanation of using cookies in tests.

Community
  • 1
  • 1
Igor Drozdov
  • 14,690
  • 5
  • 37
  • 53
  • I actually had tried that at one point also. The problem I have when I use request.cookies[] is that I get "undefined method 'cookies' for nil:NilClass". am I missing a require of something? – nPn Jan 18 '14 at 22:07
  • 1
    You should describe the controller. But in your example it seems that you described Authentication class. – Igor Drozdov Jan 18 '14 at 22:41
  • I have this setup the same as in the tutorial, but I did try using a describe UsersController block and got the same result. Anyone have any idea why request would be nil? – nPn Jan 19 '14 at 00:10
  • i managed to get a non-nil request object by doing a get root_url first, so now I can use request.cookies, but I am still having the same problem ... the request.cookies is blank in the before_filter – nPn Jan 19 '14 at 00:26
0

Ultimately the issue was that the tests were being run in the "development" environment rather than the "test" environment. In the development environment, protect_from_forgery is on, which, in this case, triggered a call to sign_out which ultimately deleted the cookie. That's why it is not available in the controller. After fixing an environment variable problem, the tests are now run in the "test" environment, and the cookie shows up as expected in the controller. For more details see the updated section "SOLVED!" in the question.

nPn
  • 16,254
  • 9
  • 35
  • 58