waiting-for-dev/devise-jwt

Using Rspec to test that Authorization header is set on a sign up

toomanyjoes opened this issue · 3 comments

I am currently unable to get a JWT token in the response headers of a rspec test after a successful sign_up.

devise (4.7.3)
devise-jwt (0.8.0)
warden (1.2.9)
warden-jwt_auth (0.5.0)

I think my problem might be that I'm using a controller test in order to validate this. Is it even possible to test that an Authorization header is set on an rspec controller type test? The controller test needs to use the action #create instead of the url path. Does this cause jwt dispatcher not to recognize that a jwt should be added to the header on this request?

on the other hand if I use a type: :request I no longer have access to the request object to set request.env['devise.mapping'] = Devise.mappings[:api_v1_user]

What would be the recommended method for testing this? This is what my test looks like:

RSpec.describe Api::V1::RegistrationsController, type: :controller do
  before :each do
    request.env['devise.mapping'] = Devise.mappings[:api_v1_user]
  end
  describe '#create' do
    let!(:valid_attributes) {
      {
        api_v1_user: {
          first_name: 'Natalia',
          last_name: 'Stanton',
          email: 'natalia.stanton@gmail.com',
          password: 'bogus1',
          password_confirmation: 'bogus1'
        }
      }
    }

    it 'should create a new user' do
      expect {
        post :create, format: 'json', params: valid_attributes
      }.to change(User, :count).by(1)

      expect(response.headers).to include('Authorization')
    end
end

routes.rb

Rails.application.routes.draw do
  namespace :api do
    namespace :v1 do
      devise_for :users, defaults: { format: :json }
    end
  end
end

devise.rb:

Devise.setup do |config|
.
.
.
  config.jwt do |jwt|
    jwt.secret = ENV.fetch('DEVISE_JWT_SECRET_KEY')
    jwt.dispatch_requests = [
      ['POST', %r{api/v1/users}],
      ['POST', %r{api/v1/registrations}],
    ]
    jwt.request_formats = {
      user: [:json],
    }
  end
end

Expected
Authorization {JWT} header in response in rspec test

Actual
No authorization header is set

The token is added in a rack middleware. Controller tests don't go through them so, basically, you have no way. Acknowledging that everything is opinionated, I think it makes no sense to test in your application something that is already tested on warden-jwt_auth & devise-jwt. What I'd do is to add an integration test with your... well... integration, to check that all pieces are correctly glued.

Closing for now but feel free to add more comments if needed.

@toomanyjoes - @waiting-for-dev is right. You have to use Request level specs to test this. Here's one such spec from an app of mine.

require 'rails_helper'

RSpec.describe "API::V1::Sessions", type: :request do
  describe "POST /api/v1/authenticate" do
    it "returns a JWT token upon successful login" do
      known_password = 'p@ssw0rd'
      customer = create(:customer, password: known_password)
      post '/api/v1/authenticate', as: :json, params: { customer: { email: customer.email, password: known_password } }

      aggregate_failures do
        expect(response).to have_http_status(:success)
        expect(response.content_type).to eq("application/json; charset=utf-8")
        expect(response.headers).to have_key('Authorization')
        expect(response.headers['Authorization']).to be_starts_with('Bearer')
      end
    end
  end
end