Space Vatican

Ramblings of a curious coder

Request Specs and Authlogic

I was writing some rspec request specs the other day and was curious to notice that

1
2
3
4
before(:each) do
  activate_authlogic
  UserSession.create(@user)
end

wasn’t working: the code under test didn’t think the user was logged in at all. The same code works fine for controller specs and the authlogic documentation asserts that functional and integration tests should behave identically in this respect. Everything should just work.

Searching around revealed quite a few people with the same problem, all being told “don’t do this, have your user login via your login form”. While obviously actually testing the login form is important, to do so for every single request spec that involves a logged in user seems over the top. Taken to its extreme, every single request spec involving a logged in user should have the user signing up, editing their preferences and generally replaying the entirety of the history involving in going from a non user of the site to an active user in the desired state. Also, I was curious as to what was going on.

Cookie issues

When you call UserSession.create it validates the credentials provided (which is trivial when the arguments you pass are an actual user) and then stores the user’s persistence token in a cookie (called user_credentials by default). It expects to have access to a controller like object that it can use for accessing cookies, request parameters / headers and so on.

AuthLogic::TestCase expects there to be a controller handing around, which in a controller spec / functional test there is (setup hooks create the controller, request & response ahead of time). In an integration spec on the hand the controller/request do not initially exist – Rails doesn’t know what you’re going to request. Because of this, Authlogic thinks you’re testing from a model spec and so sets its cookies on a mock controller object. The Rails integration stack doesn’t know anything about this mock controller object so when you make a request the authentication cookies never reach your controller(s).

In integration tests a thing called an integration session is in charge of maintaining state between requests, such as cookies, so that’s where Authlogic needs to be setting its cookies.

Solution

I would have liked to be able to just use the integration session as Authlogic’s controller, but that didn’t quite work out. Authlogic expects the controller to have a request from which it can slurp stuff like the remote_ip but at the start of a request spec the request doesn’t exist yet. In the end I did this

1
 cookies['user_credentials'] = "#{@user.persistence_token}::#{@user.send(@user.class.primary_key)}"

The cookies[]= method falls through (via method_missing) to the integration session object, so this cookie is stored in the right place. It’s a bit yucky in that it makes assumptions about what the format of cookie value should be, but I can live with that. You may need to change the name of the cookie – Authlogic sets that based on the name of the user class and that sort of stuff so will vary from app to app. If you’re using capybara rather than webrat to make your requests then you can use Capybara.current_session.driver.browser.set_cookie to set the cookie.