Skip to content

Devise with Rails 8 and Rspec does not load strategies when run single test #5771

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
Piioo opened this issue Mar 19, 2025 · 6 comments
Open

Comments

@Piioo
Copy link

Piioo commented Mar 19, 2025

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: #5752 (comment)

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)

@CuddlyBunion341
Copy link

CuddlyBunion341 commented Mar 24, 2025

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)

@pungerfreak
Copy link

pungerfreak commented Mar 25, 2025

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.

@olliebennett
Copy link

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 - landgrab/landgrab#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)>'

@Piioo
Copy link
Author

Piioo commented Apr 15, 2025

Hmm, the solutions does not work for us .

@pungerfreak
Copy link

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.

@jasonperrone
Copy link

I don't think this problem is limited to tests. I am seeing this in a production 8.0.2 app. See #5774

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

5 participants