Closed mauriciopasquier closed 9 years ago
Well, it seems on_request
is more permanent than I thought! I had to clear the hooks accessing Warden::Manager._on_request
directly. Maybe a less hackish way is good to have, something like the stuff from Warden.test_reset!
I'm also :+1: I ended up monkey patching the TestHelpers
module in order to use on_request
for my integration tests.
You should test_reset! in your after/teardown phase. The login_as method should be logging you into the session so you should only have to call it once to get it into the cookie jar.
You can see in the code that it just sets the user (which should trigger whatever serialization you have setup) https://github.com/hassox/warden/blob/master/lib/warden/test/helpers.rb#L20
Curious to get a better understanding of the problems you faced.
When using capybara to test my Sinatra app I have the something like this to login the user:
before { login_as mock_user }
Then I issue several visit 'url'
or click_button 'button_name'
and the user is only authenticated for the first request (in this case visit or link/button click).
What does your mock and serializers look like? If you can't serialize the mock_user into and out of the session it's going to seem that you're not being logged in for more than one request.
I double checked and my user object is being properly serialized. I'm user the standard user.id
for serialization and deserialization.
I'm trying to use login_as in Capybara tests:
user = create :user
login_as user, scope: :user
visit '/'
screenshot_and_save_page
visit '/'
screenshot_and_save_page
The first visit
is authenticated, but the second isn't. So, it may work fine for a single HTTP request, but in my case I've got several (I'm using AJAX).
Is there a reason to be using login_as
for a single request, given that you request teardown with test_reset!
?
The login_as can be thought of as visiting your login page. You only should need to do it once, and after that your session should keep track of what's going on. Please confirm that you session is working correctly, and also that your mock_user is handled by the session serializers.
Well, the user is serialized under session['warden.user.user.key']
and it looks like this [[2], "$2a$10$12TdWyIwWthkSvpiUV1h9e"]
(fields being id and encrypted password). How do I check that session is working correctly?
There's nothing in the session['warden.user.user.key']
on the second visit
though.
That looks like a devise session. If it is, this might help: https://github.com/plataformatec/devise/wiki/How-To:-Test-controllers-with-Rails-3-and-4-%28and-RSpec%29
Devise does some clever things with scopes and objects. If it's devise, I'd try using their helper methods, and failing that, don't use a mock for it, use a real object (just to nail down the issue)
There's a Devise howto about Warden and Capybara, so I'm doing it by the book: https://github.com/plataformatec/devise/wiki/How-To:-Test-with-Capybara
@gotjosh can you please share what you did? I've tried using on_request and it did nothing for me. Thanks.
@ledestin Basically what I did was create a new function called login_permanently_as
that does the same thing that Warden does to auth but on test. I'm almost certain this is a bad practice but here you go:
module Warden
module Test
module Helpers
def login_permanently_as(user, opts = {})
Warden::Manager.on_request do |proxy|
opts[:event] || :authentication
proxy.set_user(user, opts)
end
end
end
end
end
@gotjosh thanks! May be a bad practice, but it's 3x faster! I've updated it a bit: https://isntabook.mooo.com/faster-feature-spec-logins/
@gotjosh Thanks for that - saved my life!
@gotjosh you have to add following code in this case:
config.after :each, type: :system do
Warden::Manager._on_request.clear
end
I'm running into the same issue where login_as only provides authentication for one request and I'm confused about why the solution from @gotjosh is needed. I printed the session in the controller handling the requests after calling login_as and it includes the user key but for both requests but the the second is handled as if the user is not authenticated.
Session contents from request made immediately after calling login_as:
session_id: d3a68c592ec2df872c2255a6575ecdc3
warden.user.user.key: [[1], "$2a$04$JfjJsKxuWHEd3vFI6bqF8."]
That first request passes the authentication check and behaves as if the user is signed in.
Then I make the same request again and see this in the session:
session_id: 8d5a8e993050134583753440e4343812
warden.user.user.key: [[1], "$2a$04$JfjJsKxuWHEd3vFI6bqF8."]
That second request does not pass the authentication check and behaves as if the user is not signed in.
I can use the workaround for now but I am unclear about why login_as does not work as intended. This seems to be a somewhat common problem as evidenced by this issue and https://stackoverflow.com/questions/41084378/capybara-resets-devise-session-after-each-request
UPDATE The problem I encountered turned out to be caused by an oversight in my test setup code. I was setting the password attribute after creating the user model and I think Devise computes a new encrypted password when the password attribute is set. So the encrypted password of the user instance in the test differed from what was actually in the database. This caused Warden to set an incorrect salt value based on what was on the user instance, not actually in the database. The mismatch was causing the authentication failure. After fixing that the login_as method works as intended. Thanks!
Here are some other similar issues. Many seem to be related to storing the user in the database.
https://stackoverflow.com/questions/35609694/capybara-not-holding-user-session-after-one-page-visit-rails-4-2 https://stackoverflow.com/questions/49832310/current-user-set-to-nil-on-redirect-session-lost https://stackoverflow.com/questions/18623661/why-is-capybara-discarding-my-session-after-one-event/18846813
@gotjosh thank you very much
Lost an afternoon to this issue while trying to get Devise/Warden working with system tests. Incredible that @gotjosh's solution is relevant 8 years later.
Given "system tests" are a relatively modern recommendation, most solutions on the web are junk. Devise makes no mention and neither does Warden. Best one gets is misleading documentation on controller or feature specs.
Rails engineers truly are aging out 💀
Sorry if this is really obvious, but I need to confirm that I'm not doing something wrong in my setup. I'm confused about the test helper
login_as
andon_next_request
. Is it intended that the user dissapears (i.e. is logged out?) after the next request?Are these helpers not intended for features/integrations test, where you normally click around and make several requests in a row? In this case, do you see something wrong with using
on_request
instead ofon_next_request
?I don't see the rationale of making it dissapear after one request when you could simply use
logout
after each test.Would you accept a patch adding a
login_permanently_as
helper usingon_request
? Or maybe a clarification in the README..