3

I am trying to create a logic where a user gets an invitation from a group. If they signup via that invitation, the group id will be stored in the session and when they signup, they will be automatically added to the group as a member.

I am using Devise for authentication. So I customized it like this:

class Users::RegistrationsController < Devise::RegistrationsController
  def create
    super
    if @user
      # We create a membership to the group if the user had it in the session (it means that it was invited)
      Membership.create(user: @user, group: session[:group]) if session[:group]
      # We remove the existing session
      reset_session
    end
  end
end

Now, I want to test this behaviour. I would like to test 2 scenarios:

  1. User doesn't have the session and signs up just like that
  2. User has the session and signs up -> a group membership is generated

The first one is fine but the second one is the one I am struggling at:

context "when user signs up via invitation" do
 let(:group) {create :group}

 before do
  # We need to create an icon so that we can attach it to the use when created
  create :icon
  
  post user_registration_path,
  params: { "user": { "username": "test", "email": "[email protected]", "password": "mypassword" }},
  session: { "group": group.id }
 end

 it "has a session saved with group id" do
  expect(session[:group]).to eq group.id
 end
end

I found this way here, but it throws the following error:

ArgumentError:
       unknown keyword: :session

If I try making the call like this instead:

params: { user: { "username": "test", "email": "[email protected]", "password": "mypassword" }, session: { "group": group.id}}

It still throws an error:

NoMethodError:
       undefined method `enabled?' for {}:Hash
     
               return unless session.enabled?

I also tried setting it like this:

request.session[:group] = group.id

After I make the post call (with only the params). It does pass the expectation but I cannot fetch it from the controller.

Also, just setting it like this:

session[:group] = group.id

Throws the following error:

NoMethodError:
       undefined method `session' for nil:NilClass
     
             @request.session

Finally, if I try to mock it inside the before {} block:

allow_any_instance_of(ActionDispatch::Request).to receive(:session).and_return( group: group.id )

It gives me the following error:

NoMethodError:
       undefined method `enabled?' for {:group=>1}:Hash
     
               return unless session.enabled?

Any ideas on how can I tackle this?

PD. Using Rails 7 API and ruby 3.1.2

and also in my application.rb I added the following to be able to work with sessions in the app (and when I test manually it does work)

# Configure session storage
config.session_store :cookie_store, key: '_interslice_session'
config.middleware.use ActionDispatch::Cookies
config.middleware.use config.session_store, config.session_options

Thanks!

1 Answer 1

3

I also struggled with this until I followed this approach and included a "session double" support, like this example:

Support:

https://github.com/DFE-Digital/schools-experience/blob/master/spec/support/session_double.rb

Usage:

https://github.com/DFE-Digital/schools-experience/blob/master/spec/controllers/schools/sessions_controller_spec.rb

Your Example

# your test...

RSpec.describe '...', type: :request do
  describe '...' do
    include_context 'session double'

    let(:group) {create :group}
    let(:session_hash) { {} }

    before do
      session_hash[:group] = group.id
    end

    it 'has a session saved with group id' do
      post user_registration_path, params: { "user": { "username": "test", "email": "[email protected]", "password": "mypassword" }}

      expect(session[:group]).to eq group.id
    end
  end
end

Add Session Support:

# spec/support/session_double.rb

shared_context 'session double' do
  let(:session_hash) { {} }

  before do
    session_double = instance_double(ActionDispatch::Request::Session, enabled?: true, loaded?: false)

    allow(session_double).to receive(:[]) do |key|
      session_hash[key]
    end

    allow(session_double).to receive(:[]=) do |key, value|
      session_hash[key] = value
    end

    allow(session_double).to receive(:delete) do |key|
      session_hash.delete(key)
    end

    allow(session_double).to receive(:clear) do |_key|
      session_hash.clear
    end

    allow(session_double).to receive(:fetch) do |key|
      session_hash.fetch(key)
    end

    allow(session_double).to receive(:key?) do |key|
      session_hash.key?(key)
    end

    allow_any_instance_of(ActionDispatch::Request)
      .to receive(:session).and_return(session_double)
  end
end

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.