devise
devise copied to clipboard
Devise with Rails 8 and Rspec does not load strategies when run single test
Hello,
I'm not sure if I'm in the right place, I have a problem with devise strategies and rspec after Rails update to version 8.0.1.
We have a custom strategy which we load into the devise strategies (see below) and it does work fine on development/production and test when I run all test from one file at once. The strategies are all loaded in the manager and are found in proxy.rb to run the correct authenticate! in our custom strategy.
When I run a specific test like rspec spec/requests/foo/create_spec.rb:123, then the manager default_strategies are always empty and the test stops at the authentication.
I have seen others have similar problems when running one test: https://github.com/heartcombo/devise/issues/5752#issuecomment-2665179578
Has someone an Idea?
require 'devise/strategies/cookie_auth'
module Devise
module Models
module CookieAuth
extend ActiveSupport::Concern
end
end
end
module Devise
module Strategies
class CookieAuth < Warden::Strategies::Base
def valid?
!cookies[:access_token].nil?
end
def authenticate!
...
end
def store?
false
end
end
end
end
Warden::Strategies.add(:cookie_auth, Devise::Strategies::CookieAuth)
Devise.add_module(:cookie_auth, {
strategy: true,
controller: :sessions,
model: 'devise/models/cookie_auth',
route: :session
})
devise (4.9.4) rails (8.0.1) (with 8.0.2 it's not better) rspec-core (3.13.3) rspec-rails (7.1.1)
Same problem here.
I have the identical configuration:
- devise (4.9.4)
- rails (8.0.2)
- rspec-core (3.13.3)
- rspec-rails (7.1.1)
I believe I have some insight, rough though it may be. I just ran into a similar pattern today (single spec failing, but not as a suite) and have been a few hours trying to make sense of it.
In my particular scenario, the problem looks to be coming from devise's controller helpers being set up before rails routes are drawn. In my rails_helper.rb I have
RSpec.configure do |config|
config.include Devise::Test::ControllerHelpers, type: :controller
end
and if you look at that helpers code, there is some setup there. One of the methods called is #warden, which builds the proxy based on Devise.warden_config and sets it on the env. However, at that point warden has not been configured with any strategies which may be defined. That happens here, when Rails routes are finalized.
What works for me, for now, is
RSpec.configure do |config|
config.before(:suite) do
Devise.configure_warden!
end
end
Update: I wrote before fully reading the linked thread and I see there's a similar approach and conclusion. Though I do find my approach to be more concise and focused to the problem at hand.
Thanks; the Devise.configure_warden! fix sorted it for me.
For reference, for me the error only manifested locally (not CI), and in the first test.
The exception (locally) when running docker-compose exec -e "RAILS_ENV=test" app bin/rspec is below.
The repo is public in case helpful for reproduction - https://github.com/landgrab/landgrab/pull/580
ArgumentError:
wrong number of arguments (given 1, expected 2)
# /usr/local/bundle/gems/devise-4.9.4/lib/devise/models/authenticatable.rb:241:in `serialize_from_session'
# /usr/local/bundle/gems/devise-4.9.4/lib/devise.rb:496:in `block (2 levels) in configure_warden!'
# /usr/local/bundle/gems/warden-1.2.9/lib/warden/session_serializer.rb:35:in `fetch'
# /usr/local/bundle/gems/warden-1.2.9/lib/warden/proxy.rb:224:in `user'
# /usr/local/bundle/gems/warden-1.2.9/lib/warden/proxy.rb:334:in `_perform_authentication'
# /usr/local/bundle/gems/warden-1.2.9/lib/warden/proxy.rb:133:in `authenticate!'
# /usr/local/bundle/gems/devise-4.9.4/lib/devise/controllers/helpers.rb:120:in `authenticate_user!'
# /usr/local/bundle/gems/devise-4.9.4/lib/devise/test/controller_helpers.rb:35:in `block in process'
# /usr/local/bundle/gems/devise-4.9.4/lib/devise/test/controller_helpers.rb:104:in `catch'
# /usr/local/bundle/gems/devise-4.9.4/lib/devise/test/controller_helpers.rb:104:in `_catch_warden'
# /usr/local/bundle/gems/devise-4.9.4/lib/devise/test/controller_helpers.rb:35:in `process'
# ./spec/controllers/admin/plots_controller_spec.rb:16:in `block (3 levels) in <main>'
# /usr/local/bundle/gems/webmock-3.25.1/lib/webmock/rspec.rb:39:in `block (2 levels) in <top (required)>'
Hmm, the solutions does not work for us .
While I can peck my way around, I lack fluency in devise. I went back to where I had implemented the solution that worked for me, and tried to understand exactly what you're experiencing, @Piioo, but was unlucky.
While I had experienced this in controller specs, I was able to reproduce in request specs. Given I have little context of your scenario, it's hard to assume that I can grasp what's going on in your app, but to get as close as possible I dropped in the example code you posted (tweaked it for some observability), and configured it as such so that it's sure to be the first strategy tried
Devise.setup do |config|
config.warden do |manager|
manager.default_strategies(scope: :user).unshift :cookie_auth
end
end
Given you didn't share any additional implementation details, it's only for me to guess as to how your specific scenario is set up. Here's my example, simplified.
it "confirms the sign in workflow" do
user = create(:user)
post "/sign-in", params: { user: { email: user.email, password: "temp12345678" } }
expect(response.status).to eq(303)
end
This fails without the fix I implemented
expected: 303
got: 422
but when I drop Devise.configure_warden! into my config.before(:suite) block, I can observe that the :cookie_auth strategy is attempted.
Now in the above example, there's an additional step I left out during testing that was causing the example to pass without the addition of Devise.configure_warden!, even when run singly.
it "confirms the sign in workflow" do
user = create(:user)
get "/account" # the additional step
post response.location, params: { user: { email: user.email, password: "temp12345678" } }
expect(response.status).to eq(303)
end
If adding something similar to your failing example causes it to pass, that could provide some additional clues into what is causing the problem.
Hope that provides, at the least, a tiniest grain of insight.
I don't think this problem is limited to tests. I am seeing this in a production 8.0.2 app. See #5774