Skip to content
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

Docs for Testing-Rails need update for request specs #319

Open
brentgreeff opened this issue Jul 23, 2022 · 2 comments
Open

Docs for Testing-Rails need update for request specs #319

brentgreeff opened this issue Jul 23, 2022 · 2 comments
Labels
documentation Improvements or additions to documentation

Comments

@brentgreeff
Copy link

This page https://github.com/Sorcery/sorcery/wiki/Testing-Rails needs updating.

For request specs (I dont need helpers for system specs - and I dont use controller specs)

I added:

config.include Sorcery::TestHelpers::Rails::Request, type: :request - in rails_helper.rb

-  post 'login' => "user_sessions#create"
+  post 'login' => "user_sessions#create", as: 'user_sessions'

I had to make this change - because I got undefined method user_sessions_url

Then using pry I figured out that the password assumed is secret - which is not a good password since my user model has

  validates :password, length: { minimum: 8 }, if: -> {
    new_record? || changes[:crypted_password]
  }

In my test - I added

User.class_eval do
  clear_validators!
end
  • which I dont really want to do.

Configuration

  • Sorcery Version: 0.16.3
  • Ruby Version: 3.0.4p208
  • Framework: rails-7.0.3.1
  • Platform: OSX
@brentgreeff
Copy link
Author

This messed up my user specs - so I changed it to

before do
    User.class_eval { clear_validators! }
  end
  after do
    Object.send(:remove_const, :User)
    load 'app/models/user.rb'
  end

So I decided to look in the code - https://github.com/Sorcery/sorcery/blob/master/lib/sorcery/test_helpers/rails/request.rb

def login_user(user = nil, password = 'secret', route = nil, http_method = :post)

I see the password param

My call is

login_user( create(:user), password: 'password' )

That doesnt work - using pry in user_sessions controller.

params
=> #<ActionController::Parameters {"email"=>"[email protected]", "password"=>#<ActionController::Parameters {"password"=>"password"} permitted: false>, "controller"=>"user_sessions", "action"=>"create"} permitted: false>

@joshbuker joshbuker added the documentation Improvements or additions to documentation label Aug 26, 2022
@allisonphillips
Copy link

allisonphillips commented Oct 30, 2023

Unfortunately, Sorcery is pretty irksome in that you can't sign in a user until they're active... and if you create a user with an active state sorcery will overwrite the user's activation state and make them an inactive user before creating the record. I suspect you're running into that. You need to ensure activate! is called on your user after creation by declaring it like let(:user) { create :user, &:activate! }, or define a trait that will trigger activation after the record is created:

# spec/factories/users.rb
FactoryBot.define do
  factory :user, class: 'User' do
    password { 'MyDefaultFactoryPassword!' }

    # Alternative:
    # let(:user) { create :user, &:activate! }
    trait :active do
      transient do
        activation_state { :active }
      end
    end

    after :create do |user, evaluator|
      user.activate! if evaluator.activation_state == :active
    end
  end
end

To support before { sign_in user } in your regular specs, add a file with a sign in helper to spec/support (or rely on the rails request one + ensure your rspec config is loading it):

module Sorcery::TestHelpers::Rails
  # Adapted from Sorcery::TestHelpers::Rails::Request
  def sign_in(user, password = 'MyDefaultFactoryPassword!', route = nil, http_method = :post)
    route ||= login_url

    username_attr = user.sorcery_config.username_attribute_names.first
    password_attr = user.sorcery_config.password_attribute_name

    send(
      http_method,
      route,
      params: {
        username_attr => user.send(username_attr),
        password_attr => password
      }
    )
  end
end

But since you're testing actual login here, you would write something like:

describe '/user_sessions' do
  subject { response }
  
  let(:user) { create :user, :active, password: }
  let(:password) { 'P@ssw0rd!' }

  describe 'POST /login' do
    let(:request) { post login_path(params: { email: user.email, password: }) }

    it 'succeeds' do
      request
      should have_http_status(:success)
    end

    it 'logs in user' do
      expect { request }.to change { user.reload.logged_in? }.from(false).to(true)
    end
  end
end

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

No branches or pull requests

3 participants