wardencommunity / warden

General Rack Authentication Framework
MIT License
2.48k stars 204 forks source link

Warden::Test::Helpers unlogin method #206

Open BrianHawley opened 2 years ago

BrianHawley commented 2 years ago
BrianHawley commented 2 years ago

I've used a version of this method for years in my work test suite. Works great for testing our custom session serialization and after_set_user hooks in system/feature/request tests.

I used proxy.instance_variable_get(:@users) because Warden seems to support old Ruby versions, some of which don't have working refinements. I use a refinement like this in my version at work:

using(
  Module.new do
    refine Warden::Proxy do
      attr_reader :users
    end
  end
)

Another option would be to add this code to Warden::Proxy:

# :api: private
attr_reader :users

I figured I'd get feedback before making that change though, because I don't want to break anything.

BrianHawley commented 2 years ago

I got the helper tests for logout to actually use the logout helper. However, I couldn't figure out how to make those tests use the same session for multiple requests, so we could test logging in in one request with login_as then logging out of another request with logout. Instead, the user is not logged in in the second request. These tests instead check that the logout helper overrides what the login_as helper did earlier, if both are applied to the same request.

It works in feature and request tests though, which already have persistent sessions with multiple requests in the same scenario.

BrianHawley commented 2 years ago

Here's an example of this in patch form, suitable for putting in an initializer:

# Accessor for use by the unlogin method.
using(
  Module.new do
    refine Warden::Proxy do
      attr_reader :users
    end
  end
)

if Rails.env.test?
  Warden::Test::Helpers.module_exec do
    # Reset the logins without logging out, so the next request will fetch.
    def unlogin(*scopes)
      Warden.on_next_request do |proxy|
        if scopes.empty?
          proxy.users.clear
        else
          scopes.each { |scope| proxy.users.delete(scope) }
        end
      end
    end
  end
end

Written for Rails and a Ruby version where refinements work.

BrianHawley commented 2 years ago

@jsmestad you merged the last few PRs. Could you take a look at this one?