Commit c3738de

mo <mo@mokhan.ca>
2018-10-19 01:00:55
try rubocop-rspec
1 parent 03b2b0a
spec/factories/authorization.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 FactoryBot.define do
   factory :authorization do
     user
spec/factories/client.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 FactoryBot.define do
   factory :client do
     uuid { SecureRandom.uuid }
spec/factories/scim.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 FactoryBot.define do
   factory :scim_user, class: SCIM::User do
     schemas { ["urn:ietf:params:scim:schemas:core:2.0:User"] }
spec/factories/token.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 FactoryBot.define do
   factory :token do
     uuid { SecureRandom.uuid }
spec/factories/user.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 FactoryBot.define do
   factory :user do
     email { FFaker::Internet.email }
spec/factories/user_session.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 FactoryBot.define do
   factory :user_session do
     user
spec/models/scim/user_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe SCIM::User do
@@ -51,55 +53,58 @@ RSpec.describe SCIM::User do
   describe "#save!" do
     context "when the user is new" do
       let(:current_user) { create(:user) }
+
       before { allow(Current).to receive(:user).and_return(current_user) }
 
       context "when creating a user" do
         subject { build(:scim_user) }
 
-        before { @result = subject.save! }
-
-        specify { expect(@result).to be_persisted }
-        specify { expect(@result.uuid).to be_present }
-        specify { expect(@result.email).to eql(subject.userName) }
-        specify { expect(@result.locale).to eql(subject.locale) }
-        specify { expect(@result.timezone).to eql(subject.timezone) }
-        specify { expect(@result.password_digest).to be_present }
+        specify { expect(subject.save!).to be_persisted }
+        specify { expect(subject.save!.uuid).to be_present }
+        specify { expect(subject.save!.email).to eql(subject.userName) }
+        specify { expect(subject.save!.locale).to eql(subject.locale) }
+        specify { expect(subject.save!.timezone).to eql(subject.timezone) }
+        specify { expect(subject.save!.password_digest).to be_present }
       end
     end
 
     context "when one user is updating another user" do
       subject { build(:scim_user, id: other_user.to_param) }
+
       let(:current_user) { create(:user) }
       let(:other_user) { create(:user) }
 
       before { allow(Current).to receive(:user).and_return(current_user) }
-      before { @result = subject.save! }
 
-      specify { expect(@result.uuid).to eql(other_user.uuid) }
-      specify { expect(@result.email).to eql(subject.userName) }
-      specify { expect(@result.locale).to eql(subject.locale) }
-      specify { expect(@result.timezone).to eql(subject.timezone) }
+      specify { expect(subject.save!.uuid).to eql(other_user.uuid) }
+      specify { expect(subject.save!.email).to eql(subject.userName) }
+      specify { expect(subject.save!.locale).to eql(subject.locale) }
+      specify { expect(subject.save!.timezone).to eql(subject.timezone) }
     end
 
     context "when one user attempts to change the password of another user" do
       subject { build(:scim_user, id: other_user.to_param, password: generate(:password)) }
+
       let(:current_user) { create(:user) }
       let(:other_user) { create(:user) }
 
       before { allow(Current).to receive(:user).and_return(current_user) }
+
       specify { expect { subject.save! }.to raise_error(StandardError) }
     end
 
     context "when a user changes their own password" do
       subject { build(:scim_user, id: current_user.to_param, password: password) }
+
       let!(:current_user) { create(:user) }
       let(:password) { generate(:password) }
 
-      before { freeze_time }
-      before { allow(Current).to receive(:user).and_return(current_user) }
-      before { @result = subject.save! }
+      before do
+        freeze_time
+        allow(Current).to receive(:user).and_return(current_user)
+      end
 
-      specify { expect(@result.authenticate(password)).to be_truthy }
+      specify { expect(subject.save!.authenticate(password)).to be_truthy }
     end
   end
 
spec/models/authorization_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Authorization, type: :model do
@@ -23,6 +25,7 @@ RSpec.describe Authorization, type: :model do
 
   describe ".active, .revoked, .expired" do
     subject { described_class }
+
     let!(:active) { create(:authorization) }
     let!(:expired) { create(:authorization, expired_at: 1.second.ago) }
     let!(:revoked) { create(:authorization, revoked_at: 1.second.ago) }
spec/models/client_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Client do
@@ -13,6 +15,7 @@ RSpec.describe Client do
 
   describe "#redirect_url" do
     subject { build(:client) }
+
     let(:code) { SecureRandom.uuid }
     let(:redirect_uri) { subject.redirect_uri }
 
spec/models/token_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Token, type: :model do
@@ -5,8 +7,10 @@ RSpec.describe Token, type: :model do
     subject { create(:access_token) }
 
     context "when the token has not been revoked yet" do
-      before { freeze_time }
-      before { subject.revoke! }
+      before do
+        freeze_time
+        subject.revoke!
+      end
 
       specify { expect(subject.reload.revoked_at.to_i).to eql(DateTime.now.to_i) }
     end
@@ -28,6 +32,7 @@ RSpec.describe Token, type: :model do
 
   describe ".claims_for" do
     subject { described_class }
+
     let(:access_token) { build_stubbed(:access_token).to_jwt }
     let(:refresh_token) { build_stubbed(:refresh_token).to_jwt }
 
spec/models/user_session_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe UserSession do
@@ -11,18 +13,22 @@ RSpec.describe UserSession do
 
   describe "#access" do
     subject { create(:user_session) }
+
     let!(:original_key) { subject.key }
-    let(:request) { double(ip: "192.168.1.1", user_agent: "blah") }
+    let(:request) { instance_double(ActionDispatch::Request, ip: "192.168.1.1", user_agent: "blah") }
+    let(:result) { subject.access(request) }
 
-    before { freeze_time }
-    before { @result = subject.access(request) }
+    before do
+      freeze_time
+      result
+    end
 
     specify { expect(subject.accessed_at).to eql(Time.now) }
     specify { expect(subject.ip).to eql(request.ip) }
     specify { expect(subject.user_agent).to eql(request.user_agent) }
     specify { expect(subject).to be_persisted }
     specify { expect(subject.key).not_to eql(original_key) }
-    specify { expect(@result).to eql(subject.key) }
+    specify { expect(result).to eql(subject.key) }
   end
 
   describe ".active" do
@@ -31,11 +37,11 @@ RSpec.describe UserSession do
     let!(:expired_session) { create(:user_session, :absolute_timeout_expired) }
     let!(:revoked_session) { create(:user_session, :revoked) }
 
-    specify { expect(UserSession.active).to match_array([active_session]) }
-    specify { expect(UserSession.revoked).to match_array([revoked_session]) }
-    specify { expect(UserSession.expired).to match_array([inactive_session, expired_session]) }
-    specify { expect(UserSession.idle_timeout).to match_array([inactive_session]) }
-    specify { expect(UserSession.absolute_timeout).to match_array([expired_session]) }
+    specify { expect(described_class.active).to match_array([active_session]) }
+    specify { expect(described_class.revoked).to match_array([revoked_session]) }
+    specify { expect(described_class.expired).to match_array([inactive_session, expired_session]) }
+    specify { expect(described_class.idle_timeout).to match_array([inactive_session]) }
+    specify { expect(described_class.absolute_timeout).to match_array([expired_session]) }
   end
 
   describe ".authenticate" do
@@ -44,13 +50,13 @@ RSpec.describe UserSession do
     let!(:expired_session) { create(:user_session, :absolute_timeout_expired) }
     let!(:revoked_session) { create(:user_session, :revoked) }
 
-    specify { expect(UserSession.authenticate(active_session.key)).to eql(active_session) }
-    specify { expect(UserSession.authenticate("blah")).to be_nil }
-    specify { expect(UserSession.authenticate(inactive_session.key)).to be_nil }
-    specify { expect(UserSession.authenticate(expired_session.key)).to be_nil }
-    specify { expect(UserSession.authenticate(revoked_session.key)).to be_nil }
-    specify { expect(UserSession.authenticate(nil)).to be_nil }
-    specify { expect(UserSession.authenticate("")).to be_nil }
+    specify { expect(described_class.authenticate(active_session.key)).to eql(active_session) }
+    specify { expect(described_class.authenticate("blah")).to be_nil }
+    specify { expect(described_class.authenticate(inactive_session.key)).to be_nil }
+    specify { expect(described_class.authenticate(expired_session.key)).to be_nil }
+    specify { expect(described_class.authenticate(revoked_session.key)).to be_nil }
+    specify { expect(described_class.authenticate(nil)).to be_nil }
+    specify { expect(described_class.authenticate("")).to be_nil }
   end
 
   describe ".sudo?" do
spec/models/user_spec.rb
@@ -1,8 +1,11 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe User do
   describe "#sessions" do
     subject { create(:user) }
+
     let!(:user_session) { create(:user_session, user: subject) }
 
     specify { expect(subject.sessions).to match_array([user_session]) }
spec/requests/my/clients_spec.rb
@@ -1,8 +1,11 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe '/my/clients' do
   context "when logged in" do
     let(:current_user) { create(:user) }
+
     before { http_login(current_user) }
 
     describe "GET /my/clients" do
@@ -27,7 +30,7 @@ RSpec.describe '/my/clients' do
 
         specify { expect(response).to redirect_to(my_clients_path) }
         specify { expect(flash[:notice]).to include('success') }
-        specify { expect(Client.count).to eql(1) }
+        specify { expect(Client.count).to be(1) }
       end
     end
   end
spec/requests/my/dashboard_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe "/my/dashboard" do
spec/requests/my/mfas_spec.rb
@@ -1,8 +1,11 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe '/my/mfa' do
   context "when logged in" do
     let(:current_user) { create(:user) }
+
     before { http_login(current_user) }
 
     describe "GET /my/mfa" do
@@ -10,11 +13,13 @@ RSpec.describe '/my/mfa' do
         let(:current_user) { create(:user, :mfa_configured) }
 
         before { get '/my/mfa' }
+
         specify { expect(response).to redirect_to(edit_my_mfa_path) }
       end
 
       context "when MFA is not set up" do
         before { get '/my/mfa' }
+
         specify { expect(response).to redirect_to(new_my_mfa_path) }
       end
     end
@@ -29,6 +34,7 @@ RSpec.describe '/my/mfa' do
 
       context "when mfa has been set up" do
         let(:current_user) { create(:user, :mfa_configured) }
+
         before { get '/my/mfa/new' }
 
         specify { expect(response).to redirect_to(edit_my_mfa_path) }
@@ -38,6 +44,7 @@ RSpec.describe '/my/mfa' do
     describe "POST /my/mfa" do
       context "when the secret is valid" do
         let(:secret) { SecureRandom.hex(20) }
+
         before { post '/my/mfa', params: { user: { mfa_secret: secret } } }
 
         specify { expect(current_user.reload.mfa_secret).to eql(secret) }
spec/requests/my/sessions_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe "/my/sessions" do
spec/requests/scim/v2/bulk_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe "/api/scim/v2/Bulk" do
@@ -13,6 +15,7 @@ RSpec.describe "/api/scim/v2/Bulk" do
 
   describe "POST /scim/v2/Bulk" do
     before { post '/scim/v2/Bulk' }
+
     specify { expect(response).to have_http_status(:not_implemented) }
   end
 end
spec/requests/scim/v2/groups_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 describe "/scim/v2/groups" do
@@ -15,10 +17,12 @@ describe "/scim/v2/groups" do
     describe "GET /scim/v2/groups" do
       before { get '/scim/v2/groups', headers: headers }
 
+      let(:json) { JSON.parse(response.body, symbolize_names: true) }
+
       specify { expect(response).to have_http_status(:ok) }
       specify { expect(response.headers['Content-Type']).to eql('application/scim+json') }
       specify { expect(response.body).to be_present }
-      let(:json) { JSON.parse(response.body, symbolize_names: true) }
+
       specify { expect(json[:schemas]).to match_array([Scim::Shady::Messages::LIST_RESPONSE]) }
       specify { expect(json[:totalResults]).to be_kind_of(Numeric) }
       specify { expect(json[:Resources]).to match_array([id: user.uuid, userName: user.email]) }
@@ -33,6 +37,7 @@ describe "/scim/v2/groups" do
         'Content-Type' => 'application/scim+json',
       }
     end
+
     before { get '/scim/v2/groups', headers: bad_headers }
 
     specify { expect(response).to have_http_status(:unauthorized) }
spec/requests/scim/v2/me_spec.rb
@@ -1,8 +1,11 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe '/scim/v2/Me' do
   describe "GET /scim/v2/Me" do
     before { get '/scim/v2/Me' }
+
     specify { expect(response).to have_http_status(:not_implemented) }
   end
 end
spec/requests/scim/v2/resource_types_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe "/scim/v2/ResourceTypes" do
@@ -13,10 +15,11 @@ RSpec.describe "/scim/v2/ResourceTypes" do
 
   describe "GET /scim/v2/ResourceTypes" do
     before { get "/scim/v2/ResourceTypes", headers: headers }
+
     let(:json) { JSON.parse(response.body, symbolize_names: true) }
 
     specify { expect(response).to have_http_status(:ok) }
-    specify { expect(json.count).to eql(2) }
+    specify { expect(json.count).to be(2) }
     specify { expect(json[0][:schemas]).to match_array(["urn:ietf:params:scim:schemas:core:2.0:ResourceType"]) }
     specify { expect(json[0][:id]).to eql('User') }
     specify { expect(json[0][:name]).to eql('User') }
@@ -36,6 +39,7 @@ RSpec.describe "/scim/v2/ResourceTypes" do
 
   describe "GET /scim/v2/ResourceTypes/User" do
     before { get "/scim/v2/ResourceTypes/User", headers: headers }
+
     let(:json) { JSON.parse(response.body, symbolize_names: true) }
 
     specify { expect(response).to have_http_status(:ok) }
@@ -49,6 +53,7 @@ RSpec.describe "/scim/v2/ResourceTypes" do
 
   describe "GET /scim/v2/ResourceTypes/Group" do
     before { get "/scim/v2/ResourceTypes/Group", headers: headers }
+
     let(:json) { JSON.parse(response.body, symbolize_names: true) }
 
     specify { expect(response).to have_http_status(:ok) }
spec/requests/scim/v2/schemas_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe "/scim/v2/Schemas" do
@@ -12,34 +14,31 @@ RSpec.describe "/scim/v2/Schemas" do
   end
 
   describe "GET scim/v2/Schemas" do
-    before :each do
-      get "/scim/v2/schemas", headers: headers
-      @json = JSON.parse(response.body, symbolize_names: true)
-    end
+    let(:json) { JSON.parse(response.body, symbolize_names: true) }
+
+    before { get "/scim/v2/schemas", headers: headers }
 
     specify { expect(response).to have_http_status(:ok) }
-    specify { expect(@json.count).to eql(2) }
-    specify { expect(@json[0][:id]).to eql('urn:ietf:params:scim:schemas:core:2.0:User') }
-    specify { expect(@json[1][:id]).to eql('urn:ietf:params:scim:schemas:core:2.0:Group') }
+    specify { expect(json.count).to be(2) }
+    specify { expect(json[0][:id]).to eql('urn:ietf:params:scim:schemas:core:2.0:User') }
+    specify { expect(json[1][:id]).to eql('urn:ietf:params:scim:schemas:core:2.0:Group') }
   end
 
   describe "GET /Schemas/urn:ietf:params:scim:schemas:core:2.0:User" do
-    before :each do
-      get "/scim/v2/schemas/urn:ietf:params:scim:schemas:core:2.0:User", headers: headers
-    end
     let(:json) { JSON.parse(response.body, symbolize_names: true) }
 
+    before { get "/scim/v2/schemas/urn:ietf:params:scim:schemas:core:2.0:User", headers: headers }
+
     specify { expect(response).to have_http_status(:ok) }
     specify { expect(json[:id]).to eql('urn:ietf:params:scim:schemas:core:2.0:User') }
     specify { expect(json[:meta][:location]).to eql(scim_v2_schema_url(id: 'urn:ietf:params:scim:schemas:core:2.0:User')) }
   end
 
   describe "GET /Schemas/urn:ietf:params:scim:schemas:core:2.0:Group" do
-    before :each do
-      get "/scim/v2/schemas/urn:ietf:params:scim:schemas:core:2.0:Group", headers: headers
-    end
     let(:json) { JSON.parse(response.body, symbolize_names: true) }
 
+    before { get "/scim/v2/schemas/urn:ietf:params:scim:schemas:core:2.0:Group", headers: headers }
+
     specify { expect(response).to have_http_status(:ok) }
     specify { expect(json[:id]).to eql('urn:ietf:params:scim:schemas:core:2.0:Group') }
     specify { expect(json[:meta][:location]).to eql(scim_v2_schema_url(id: 'urn:ietf:params:scim:schemas:core:2.0:Group')) }
spec/requests/scim/v2/search_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 describe '/scim/v1/.search' do
@@ -12,26 +14,26 @@ describe '/scim/v1/.search' do
   end
 
   describe "POST /scim/v2/.search" do
-    it 'returns an empty set of results' do
-      body = {
+    let(:request_body) do
+      let(:json) { JSON.parse(response.body, symbolize_names: true) }
+      {
         "schemas": [Scim::Shady::Messages::SEARCH_REQUEST],
-        "attributes": ["displayName", "userName"],
+        "attributes": %w[displayName userName],
         "filter": "displayName sw \"smith\"",
         "startIndex": 1,
         "count": 10
       }
-      post "/scim/v2/.search", headers: headers, params: body.to_json
+    end
 
-      expect(response).to have_http_status(:ok)
-      expect(response.headers['Content-Type']).to eql('application/scim+json')
-      expect(response.body).to be_present
+    before { post "/scim/v2/.search", headers: headers, params: request_body.to_json }
 
-      json = JSON.parse(response.body, symbolize_names: true)
-      expect(json[:schemas]).to match_array([Scim::Shady::Messages::LIST_RESPONSE])
-      expect(json[:totalResults]).to be_zero
-      expect(json[:itemsPerPage]).to be_zero
-      expect(json[:startIndex]).to eql(1)
-      expect(json[:Resources]).to be_empty
-    end
+    specify { expect(response).to have_http_status(:ok) }
+    specify { expect(response.headers['Content-Type']).to eql('application/scim+json') }
+    specify { expect(response.body).to be_present }
+    specify { expect(json[:schemas]).to match_array([Scim::Shady::Messages::LIST_RESPONSE]) }
+    specify { expect(json[:totalResults]).to be_zero }
+    specify { expect(json[:itemsPerPage]).to be_zero }
+    specify { expect(json[:startIndex]).to be(1) }
+    specify { expect(json[:Resources]).to be_empty }
   end
 end
spec/requests/scim/v2/service_provider_config_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 describe "/ServiceProviderConfig" do
@@ -11,33 +13,26 @@ describe "/ServiceProviderConfig" do
     }
   end
 
-  it 'returns a 200' do
-    get '/scim/v2/ServiceProviderConfig', headers: headers
+  context "when loading the service provider configuration" do
+    let(:json) { JSON.parse(response.body, symbolize_names: true) }
 
-    expect(response).to have_http_status(:ok)
-    expect(response.body).to be_present
+    before { get '/scim/v2/ServiceProviderConfig', headers: headers }
 
-    json = JSON.parse(response.body, symbolize_names: true)
-    expect(json[:schemas]).to match_array([Scim::Shady::Schemas::SERVICE_PROVIDER_CONFIG])
-    expect(json[:documentationUri]).to be_blank
-    expect(json[:patch][:supported]).to be(false)
-    expect(json[:bulk][:supported]).to be(false)
-    expect(json[:filter][:supported]).to be(false)
-    expect(json[:changePassword][:supported]).to be(false)
-    expect(json[:sort][:supported]).to be(false)
-    expect(json[:etag][:supported]).to be(false)
-    expect(json[:authenticationSchemes]).to match_array([
-      name: 'OAuth Bearer Token',
-      description: 'Authentication scheme using the OAuth Bearer Token Standard',
-      specUri: 'http://www.rfc-editor.org/info/rfc6750',
-      documentationUri: 'http://example.com/help/oauth.html',
-      type: 'oauthbearertoken',
-      primary: true,
-    ])
-    expect(json[:meta][:location]).to eql(scim_v2_ServiceProviderConfig_url)
-    expect(json[:meta][:resourceType]).to eql('ServiceProviderConfig')
-    expect(json[:meta][:created]).to be_present
-    expect(json[:meta][:lastModified]).to be_present
-    expect(json[:meta][:version]).to be_present
+    specify { expect(response).to have_http_status(:ok) }
+    specify { expect(response.body).to be_present }
+    specify { expect(json[:schemas]).to match_array([Scim::Shady::Schemas::SERVICE_PROVIDER_CONFIG]) }
+    specify { expect(json[:documentationUri]).to be_blank }
+    specify { expect(json[:patch][:supported]).to be(false) }
+    specify { expect(json[:bulk][:supported]).to be(false) }
+    specify { expect(json[:filter][:supported]).to be(false) }
+    specify { expect(json[:changePassword][:supported]).to be(false) }
+    specify { expect(json[:sort][:supported]).to be(false) }
+    specify { expect(json[:etag][:supported]).to be(false) }
+    specify { expect(json[:authenticationSchemes]).to match_array([name: 'OAuth Bearer Token', description: 'Authentication scheme using the OAuth Bearer Token Standard', specUri: 'http://www.rfc-editor.org/info/rfc6750', documentationUri: 'http://example.com/help/oauth.html', type: 'oauthbearertoken', primary: true]) }
+    specify { expect(json[:meta][:location]).to eql(scim_v2_ServiceProviderConfig_url) }
+    specify { expect(json[:meta][:resourceType]).to eql('ServiceProviderConfig') }
+    specify { expect(json[:meta][:created]).to be_present }
+    specify { expect(json[:meta][:lastModified]).to be_present }
+    specify { expect(json[:meta][:version]).to be_present }
   end
 end
spec/requests/scim/v2/users_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 describe '/scim/v2/users' do
@@ -17,6 +19,7 @@ describe '/scim/v2/users' do
       let(:locale) { 'en' }
       let(:timezone) { 'Etc/UTC' }
       let(:body) { { schemas: [Scim::Shady::Schemas::USER], userName: email, locale: locale, timezone: timezone } }
+      let(:json) { JSON.parse(response.body, symbolize_names: true) }
 
       before { post '/scim/v2/users', params: body.to_json, headers: headers }
 
@@ -24,7 +27,6 @@ describe '/scim/v2/users' do
       specify { expect(response.headers['Content-Type']).to eql('application/scim+json') }
       specify { expect(response.headers['Location']).to be_present }
       specify { expect(response.body).to be_present }
-      let(:json) { JSON.parse(response.body, symbolize_names: true) }
       specify { expect(json[:schemas]).to match_array([Scim::Shady::Schemas::USER]) }
       specify { expect(json[:id]).to be_present }
       specify { expect(json[:userName]).to eql(email) }
@@ -40,11 +42,11 @@ describe '/scim/v2/users' do
     context "when a duplicate email is specified" do
       let(:other_user) { create(:user) }
       let(:request_body) { attributes_for(:scim_user, userName: other_user.email) }
+      let(:json) { JSON.parse(response.body, symbolize_names: true) }
 
       before { post '/scim/v2/users', params: request_body.to_json, headers: headers }
 
       specify { expect(response).to have_http_status(:bad_request) }
-      let(:json) { JSON.parse(response.body, symbolize_names: true) }
       specify { expect(json[:schemas]).to match_array(['urn:ietf:params:scim:api:messages:2.0:Error']) }
       specify { expect(json[:scimType]).to eql('uniqueness') }
       specify { expect(json[:detail]).to be_instance_of(String) }
@@ -58,13 +60,14 @@ describe '/scim/v2/users' do
     context "when the resource is available" do
       before { get "/scim/v2/users/#{user.uuid}", headers: headers }
 
+      let(:json) { JSON.parse(response.body, symbolize_names: true) }
+
       specify { expect(response).to have_http_status(:ok) }
       specify { expect(response.headers['Content-Type']).to eql('application/scim+json') }
       specify { expect(response.headers['Location']).to eql(scim_v2_user_url(user)) }
       specify { expect(response.headers['ETag']).to be_present }
       specify { expect(response.body).to be_present }
 
-      let(:json) { JSON.parse(response.body, symbolize_names: true) }
       specify { expect(json[:schemas]).to match_array([Scim::Shady::Schemas::USER]) }
       specify { expect(json[:id]).to eql(user.uuid) }
       specify { expect(json[:userName]).to eql(user.email) }
@@ -79,10 +82,11 @@ describe '/scim/v2/users' do
     end
 
     context "when the resource does not exist" do
+      let(:json) { JSON.parse(response.body, symbolize_names: true) }
+
       before { get "/scim/v2/users/#{SecureRandom.uuid}", headers: headers }
 
       specify { expect(response).to have_http_status(:not_found) }
-      let(:json) { JSON.parse(response.body, symbolize_names: true) }
       specify { expect(json[:schemas]).to match_array(['urn:ietf:params:scim:api:messages:2.0:Error']) }
       specify { expect(json[:detail]).to be_present }
       specify { expect(json[:status]).to eql('404') }
@@ -104,12 +108,13 @@ describe '/scim/v2/users' do
   end
 
   describe "GET /scim/v2/users" do
+    let(:json) { JSON.parse(response.body, symbolize_names: true) }
+
     before { get "/scim/v2/users?attributes=userName", headers: headers }
 
     specify { expect(response).to have_http_status(:ok) }
     specify { expect(response.headers['Content-Type']).to eql('application/scim+json') }
     specify { expect(response.body).to be_present }
-    let(:json) { JSON.parse(response.body, symbolize_names: true) }
     specify { expect(json[:schemas]).to match_array([Scim::Shady::Messages::LIST_RESPONSE]) }
     specify { expect(json[:totalResults]).to be_zero }
     specify { expect(json[:Resources]).to be_empty }
@@ -121,6 +126,7 @@ describe '/scim/v2/users' do
     let(:locale) { 'ja' }
     let(:timezone) { 'America/Denver' }
     let(:body) { { schemas: [Scim::Shady::Schemas::USER], userName: new_email, locale: locale, timezone: timezone } }
+    let(:json) { JSON.parse(response.body, symbolize_names: true) }
 
     before { put "/scim/v2/users/#{user.uuid}", headers: headers, params: body.to_json }
 
@@ -128,7 +134,6 @@ describe '/scim/v2/users' do
     specify { expect(response.headers['Content-Type']).to eql('application/scim+json') }
     specify { expect(response.headers['Location']).to eql(scim_v2_user_url(user)) }
     specify { expect(response.body).to be_present }
-    let(:json) { JSON.parse(response.body, symbolize_names: true) }
     specify { expect(json[:schemas]).to match_array([Scim::Shady::Schemas::USER]) }
     specify { expect(json[:id]).to be_present }
     specify { expect(json[:userName]).to eql(new_email) }
@@ -145,17 +150,14 @@ describe '/scim/v2/users' do
   describe "DELETE /scim/v2/users/:id" do
     let(:other_user) { create(:user) }
 
-    it 'deletes the user' do
-      delete "/scim/v2/users/#{other_user.uuid}", headers: headers
-      expect(response).to have_http_status(:no_content)
-
-      get "/scim/v2/users/#{other_user.uuid}", headers: headers
-      expect(response).to have_http_status(:not_found)
-      expect(response.body).to be_present
-      json = JSON.parse(response.body, symbolize_names: true)
-      expect(json[:schemas]).to match_array([Scim::Shady::Messages::ERROR])
-      expect(json[:detail]).to eql("Resource #{other_user.uuid} not found")
-      expect(json[:status]).to eql("404")
+    context "when the user can be deleted" do
+      before { delete "/scim/v2/users/#{other_user.uuid}", headers: headers }
+
+      specify { expect(response).to have_http_status(:no_content) }
+      specify do
+        get "/scim/v2/users/#{other_user.uuid}", headers: headers
+        expect(response).to have_http_status(:not_found)
+      end
     end
   end
 end
spec/requests/metadata_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 describe '/metadata' do
@@ -6,7 +8,7 @@ describe '/metadata' do
 
     specify { expect(Saml::Kit::Metadata.from(response.body)).to be_valid }
     specify { expect(Saml::Kit::Metadata.from(response.body).entity_id).to eql(Saml::Kit.configuration.entity_id) }
-    specify { expect(response).to have_http_status(:ok)  }
+    specify { expect(response).to have_http_status(:ok) }
     specify { expect(response.content_type).to eq("application/samlmetadata+xml") }
   end
 end
spec/requests/mfas_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe "/mfa" do
@@ -15,6 +17,7 @@ RSpec.describe "/mfa" do
     describe "POST /mfa" do
       context "when the code is correct" do
         let(:correct_code) { current_user.mfa.current_totp }
+
         before { post '/mfa', params: { mfa: { code: correct_code } } }
 
         specify { expect(response).to redirect_to(response_path) }
@@ -23,6 +26,7 @@ RSpec.describe "/mfa" do
 
       context "when the code is incorrect" do
         let(:incorrect_code) { rand(1_000) }
+
         before { post '/mfa', params: { mfa: { code: incorrect_code } } }
 
         specify { expect(response).to redirect_to(new_mfa_path) }
spec/requests/oauth_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe '/oauth' do
@@ -7,19 +9,21 @@ RSpec.describe '/oauth' do
     before { http_login(current_user) }
 
     describe "GET /oauth" do
-      let(:state) { SecureRandom.uuid  }
+      let(:state) { SecureRandom.uuid }
 
       context "when the client id is known" do
         let(:client) { create(:client) }
 
         context "when requesting an authorization code" do
           before { get "/oauth", params: { client_id: client.to_param, response_type: 'code', state: state, redirect_uri: client.redirect_uri } }
+
           specify { expect(response).to have_http_status(:ok) }
           specify { expect(response.body).to include(CGI.escapeHTML(client.name)) }
         end
 
         context "when requesting an access token" do
           before { get "/oauth", params: { client_id: client.to_param, response_type: 'token', state: state, redirect_uri: client.redirect_uri } }
+
           specify { expect(response).to have_http_status(:ok) }
           specify { expect(response.body).to include(CGI.escapeHTML(client.name)) }
         end
@@ -39,10 +43,11 @@ RSpec.describe '/oauth' do
     end
 
     describe "GET /oauth/authorize" do
-      let(:state) { SecureRandom.uuid  }
+      let(:state) { SecureRandom.uuid }
 
       context "when the client id is known" do
         let(:client) { create(:client) }
+
         before { get "/oauth/authorize", params: { client_id: client.to_param, response_type: 'code', state: state, redirect_uri: client.redirect_uri } }
 
         specify { expect(response).to have_http_status(:ok) }
@@ -56,7 +61,7 @@ RSpec.describe '/oauth' do
         let(:state) { SecureRandom.uuid }
 
         context "when the client requested an authorization code" do
-          before :each do
+          before do
             get "/oauth", params: { client_id: client.to_param, response_type: 'code', state: state, redirect_uri: client.redirect_uri }
             post "/oauth"
           end
@@ -68,7 +73,7 @@ RSpec.describe '/oauth' do
           let(:token) { Token.access.active.last&.to_jwt }
           let(:scope) { "admin" }
 
-          before :each do
+          before do
             get "/oauth", params: { client_id: client.to_param, response_type: 'token', state: state, redirect_uri: client.redirect_uri }
             post "/oauth"
           end
@@ -81,7 +86,7 @@ RSpec.describe '/oauth' do
           let(:code_verifier) { SecureRandom.hex(128) }
           let(:code_challenge) { Base64.urlsafe_encode64(Digest::SHA256.hexdigest(code_verifier)) }
 
-          before :each do
+          before do
             get "/oauth", params: {
               client_id: client.to_param,
               response_type: 'code',
@@ -102,7 +107,7 @@ RSpec.describe '/oauth' do
           let(:token) { Token.access.active.last&.to_jwt }
           let(:code_verifier) { SecureRandom.hex(128) }
 
-          before :each do
+          before do
             get "/oauth", params: {
               client_id: client.to_param,
               response_type: 'code',
@@ -123,7 +128,7 @@ RSpec.describe '/oauth' do
           let(:token) { Token.access.active.last&.to_jwt }
           let(:code_verifier) { SecureRandom.hex(128) }
 
-          before :each do
+          before do
             get "/oauth", params: {
               client_id: client.to_param,
               response_type: 'code',
@@ -148,7 +153,7 @@ RSpec.describe '/oauth' do
         context "when the state parameter looks malicious" do
           let(:state) { "<script>alert('hi');</script>" }
 
-          before :each do
+          before do
             get "/oauth", params: { client_id: client.to_param, response_type: 'token', state: state, redirect_uri: client.redirect_uri }
             post "/oauth"
           end
spec/requests/registrations_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe '/registrations' do
@@ -11,10 +13,11 @@ RSpec.describe '/registrations' do
   describe "POST /registrations" do
     context "when the new registration data is valid" do
       let(:email) { FFaker::Internet.email }
+
       before { post "/registrations", params: { user: { email: email, password: "password" } } }
 
       specify { expect(response).to redirect_to(new_session_url) }
-      specify { expect(User.count).to eql(1) }
+      specify { expect(User.count).to be(1) }
       specify { expect(User.last.email).to eql(email) }
     end
 
spec/requests/response_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe "/response" do
@@ -25,7 +27,7 @@ RSpec.describe "/response" do
           end
         end
 
-        before :each do
+        before do
           Saml::Kit.configuration.registry = registry
           allow(registry).to receive(:metadata_for).with(issuer).and_return(sp_metadata)
           get redirect_binding.serialize(Saml::Kit::AuthenticationRequest.builder, relay_state: relay_state)[0]
@@ -42,8 +44,10 @@ RSpec.describe "/response" do
         end
 
         context "when the SAML request is no longer valid" do
-          before { allow_any_instance_of(Saml::Kit::AuthenticationRequest).to receive(:valid?).and_return(false) }
-          before { get '/response' }
+          before do
+            allow_any_instance_of(Saml::Kit::AuthenticationRequest).to receive(:valid?).and_return(false)
+            get '/response'
+          end
 
           specify { expect(response).to have_http_status(:forbidden) }
         end
spec/requests/sessions_spec.rb
@@ -1,6 +1,8 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
-describe SessionsController do
+describe "/sessions" do
   let(:registry) { Saml::Kit::DefaultRegistry.new }
   let(:issuer) { Saml::Kit.configuration.entity_id }
   let(:sp_metadata) do
@@ -15,6 +17,7 @@ describe SessionsController do
   def session_id_from(response)
     cookies = response.headers['Set-Cookie']
     return if cookies.nil?
+
     cookies.split("\;")[0].split("=")[1]
   end
 
@@ -23,11 +26,12 @@ describe SessionsController do
 
     context "when the user is already logged in" do
       let(:user) { create(:user) }
+
       before { http_login(user) }
 
       context "when a registered SAML request is provided" do
-        before { allow(registry).to receive(:metadata_for).with(issuer).and_return(sp_metadata) }
-        before :each do
+        before do
+          allow(registry).to receive(:metadata_for).with(issuer).and_return(sp_metadata)
           url, saml_params = post_binding.serialize(Saml::Kit::AuthenticationRequest.builder)
           post url, params: saml_params
           follow_redirect!
@@ -38,7 +42,7 @@ describe SessionsController do
       end
 
       context "when an unregistered SAML request is provided" do
-        before :each do
+        before do
           url, saml_params = post_binding.serialize(Saml::Kit::AuthenticationRequest.builder)
           post url, params: saml_params
         end
@@ -55,8 +59,8 @@ describe SessionsController do
 
     context "when the user is not logged in" do
       context "when a registered SAML request is provided" do
-        before { allow(registry).to receive(:metadata_for).with(issuer).and_return(sp_metadata) }
-        before :each do
+        before do
+          allow(registry).to receive(:metadata_for).with(issuer).and_return(sp_metadata)
           url, saml_params = post_binding.serialize(Saml::Kit::AuthenticationRequest.builder)
           post url, params: saml_params
         end
@@ -68,7 +72,7 @@ describe SessionsController do
       end
 
       context "when an unregistered SAML request is provided" do
-        before :each do
+        before do
           url, saml_params = post_binding.serialize(Saml::Kit::AuthenticationRequest.builder)
           post url, params: saml_params
         end
@@ -92,9 +96,11 @@ describe SessionsController do
       before { http_login(create(:user)) }
 
       context "when a registered SAML request is provided" do
-        before { allow(registry).to receive(:metadata_for).with(issuer).and_return(sp_metadata) }
-        before { get redirect_binding.serialize(Saml::Kit::AuthenticationRequest.builder)[0] }
-        before { follow_redirect! }
+        before do
+          allow(registry).to receive(:metadata_for).with(issuer).and_return(sp_metadata)
+          get redirect_binding.serialize(Saml::Kit::AuthenticationRequest.builder)[0]
+          follow_redirect!
+        end
 
         specify { expect(response).to have_http_status(:ok) }
         specify { expect(response.body).to include("Sending Response to Service Provider") }
@@ -115,8 +121,10 @@ describe SessionsController do
 
     context "when the user is not logged in" do
       context "when a registered SAML request is provided" do
-        before { allow(registry).to receive(:metadata_for).with(issuer).and_return(sp_metadata) }
-        before { get redirect_binding.serialize(Saml::Kit::AuthenticationRequest.builder)[0] }
+        before do
+          allow(registry).to receive(:metadata_for).with(issuer).and_return(sp_metadata)
+          get redirect_binding.serialize(Saml::Kit::AuthenticationRequest.builder)[0]
+        end
 
         specify { expect(response).to have_http_status(:ok) }
         specify { expect(session[:saml]).to be_present }
@@ -146,6 +154,7 @@ describe SessionsController do
     context "when a SAMLRequest is not present" do
       context "when the credentials are correct" do
         before { post '/session', params: { user: { email: user.email, password: password } } }
+
         specify { expect(response).to redirect_to(response_path) }
       end
 
@@ -161,7 +170,7 @@ describe SessionsController do
       let(:redirect_binding) { Saml::Kit::Bindings::HttpRedirect.new(location: new_session_url) }
       let(:relay_state) { SecureRandom.uuid }
 
-      before :each do
+      before do
         allow(registry).to receive(:metadata_for).with(issuer).and_return(sp_metadata)
         get redirect_binding.serialize(Saml::Kit::AuthenticationRequest.builder, relay_state: relay_state)[0]
       end
@@ -188,9 +197,11 @@ describe SessionsController do
     let(:user) { create(:user) }
 
     context "when receiving a logout request" do
-      before :each do
+      let(:session_id) { session_id_from(response) }
+
+      before do
         http_login(user)
-        @session_id = session_id_from(response)
+        session_id
 
         allow(registry).to receive(:metadata_for).with(issuer).and_return(sp_metadata)
         builder = Saml::Kit::LogoutRequest.builder(user) do |x|
@@ -206,11 +217,11 @@ describe SessionsController do
       specify { expect(response.body).to include("SAMLResponse") }
       specify { expect(response.body).to include(sp_metadata.single_logout_service_for(binding: :http_post).location) }
       specify { expect(session_id_from(response)).to be_present }
-      specify { expect(session_id_from(response)).not_to eql(@session_id) }
+      specify { expect(session_id_from(response)).not_to eql(session_id) }
     end
 
     context "when receiving a logout response" do
-      before :each do
+      before do
         allow(registry).to receive(:metadata_for).with(issuer).and_return(sp_metadata)
         builder = Saml::Kit::LogoutResponse.builder(Saml::Kit::AuthenticationRequest.build) do |x|
           x.issuer = issuer
@@ -225,14 +236,15 @@ describe SessionsController do
 
     context "when logging out of the IDP only" do
       let(:user) { create(:user) }
+      let(:session_id) { session_id_from(response) }
 
-      before :each do
+      before do
         http_login(user)
-        @session_id = session_id_from(response)
+        session_id
         delete session_path
       end
 
-      specify { expect(session_id_from(response)).not_to eql(@session_id) }
+      specify { expect(session_id_from(response)).not_to eql(session_id) }
       specify { expect(session_id_from(response)).to be_present }
       specify { expect(response).to redirect_to(new_session_path) }
     end
spec/requests/tokens_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe '/tokens' do
@@ -9,6 +11,7 @@ RSpec.describe '/tokens' do
     context "when using the authorization_code grant" do
       context "when the code is still valid" do
         let(:authorization) { create(:authorization, client: client) }
+        let(:json) { JSON.parse(response.body, symbolize_names: true) }
 
         before { post '/oauth/token', params: { grant_type: 'authorization_code', code: authorization.code }, headers: headers }
 
@@ -16,8 +19,6 @@ RSpec.describe '/tokens' do
         specify { expect(response.headers['Content-Type']).to include('application/json') }
         specify { expect(response.headers['Cache-Control']).to include('no-store') }
         specify { expect(response.headers['Pragma']).to eql('no-cache') }
-
-        let(:json) { JSON.parse(response.body, symbolize_names: true) }
         specify { expect(json[:access_token]).to be_present }
         specify { expect(json[:token_type]).to eql('Bearer') }
         specify { expect(json[:expires_in]).to eql(1.hour.to_i) }
@@ -27,6 +28,7 @@ RSpec.describe '/tokens' do
 
       context "when the code is expired" do
         let(:authorization) { create(:authorization, client: client, expired_at: 1.second.ago) }
+        let(:json) { JSON.parse(response.body, symbolize_names: true) }
 
         before { post '/oauth/token', params: { grant_type: 'authorization_code', code: authorization.code }, headers: headers }
 
@@ -34,37 +36,36 @@ RSpec.describe '/tokens' do
         specify { expect(response.headers['Content-Type']).to include('application/json') }
         specify { expect(response.headers['Cache-Control']).to include('no-store') }
         specify { expect(response.headers['Pragma']).to eql('no-cache') }
-
-        let(:json) { JSON.parse(response.body, symbolize_names: true) }
         specify { expect(json[:error]).to eql('invalid_request') }
       end
 
       context "when the code is not known" do
         before { post '/oauth/token', params: { grant_type: 'authorization_code', code: SecureRandom.hex(20) }, headers: headers }
 
+        let(:json) { JSON.parse(response.body, symbolize_names: true) }
+
         specify { expect(response).to have_http_status(:bad_request) }
         specify { expect(response.headers['Content-Type']).to include('application/json') }
         specify { expect(response.headers['Cache-Control']).to include('no-store') }
         specify { expect(response.headers['Pragma']).to eql('no-cache') }
 
-        let(:json) { JSON.parse(response.body, symbolize_names: true) }
         specify { expect(json[:error]).to eql('invalid_request') }
       end
 
       context "when the authorization was created with the code_challenge_method of SHA256" do
         let(:code_verifier) { SecureRandom.hex(128) }
-        before { post '/oauth/token', params: { grant_type: 'authorization_code', code: SecureRandom.hex(20) }, headers: headers }
-
-        let(:authorization) { create(:authorization, client: client, challenge: Base64.urlsafe_encode64(Digest::SHA256.hexdigest(code_verifier)) , challenge_method: :sha256) }
+        let(:authorization) { create(:authorization, client: client, challenge: Base64.urlsafe_encode64(Digest::SHA256.hexdigest(code_verifier)), challenge_method: :sha256) }
+        let(:json) { JSON.parse(response.body, symbolize_names: true) }
 
-        before { post '/oauth/token', params: { grant_type: 'authorization_code', code: authorization.code, code_verifier: code_verifier }, headers: headers }
+        before do
+          post '/oauth/token', params: { grant_type: 'authorization_code', code: SecureRandom.hex(20) }, headers: headers
+          post '/oauth/token', params: { grant_type: 'authorization_code', code: authorization.code, code_verifier: code_verifier }, headers: headers
+        end
 
         specify { expect(response).to have_http_status(:ok) }
         specify { expect(response.headers['Content-Type']).to include('application/json') }
         specify { expect(response.headers['Cache-Control']).to include('no-store') }
         specify { expect(response.headers['Pragma']).to eql('no-cache') }
-
-        let(:json) { JSON.parse(response.body, symbolize_names: true) }
         specify { expect(json[:access_token]).to be_present }
         specify { expect(json[:token_type]).to eql('Bearer') }
         specify { expect(json[:expires_in]).to eql(1.hour.to_i) }
@@ -74,18 +75,18 @@ RSpec.describe '/tokens' do
 
       context "when the authorization was created with the code_challenge_method of plain" do
         let(:code_verifier) { SecureRandom.hex(128) }
-        before { post '/oauth/token', params: { grant_type: 'authorization_code', code: SecureRandom.hex(20) }, headers: headers }
-
-        let(:authorization) { create(:authorization, client: client, challenge: code_verifier , challenge_method: :plain) }
+        let(:authorization) { create(:authorization, client: client, challenge: code_verifier, challenge_method: :plain) }
+        let(:json) { JSON.parse(response.body, symbolize_names: true) }
 
-        before { post '/oauth/token', params: { grant_type: 'authorization_code', code: authorization.code, code_verifier: code_verifier }, headers: headers }
+        before do
+          post '/oauth/token', params: { grant_type: 'authorization_code', code: SecureRandom.hex(20) }, headers: headers
+          post '/oauth/token', params: { grant_type: 'authorization_code', code: authorization.code, code_verifier: code_verifier }, headers: headers
+        end
 
         specify { expect(response).to have_http_status(:ok) }
         specify { expect(response.headers['Content-Type']).to include('application/json') }
         specify { expect(response.headers['Cache-Control']).to include('no-store') }
         specify { expect(response.headers['Pragma']).to eql('no-cache') }
-
-        let(:json) { JSON.parse(response.body, symbolize_names: true) }
         specify { expect(json[:access_token]).to be_present }
         specify { expect(json[:token_type]).to eql('Bearer') }
         specify { expect(json[:expires_in]).to eql(1.hour.to_i) }
@@ -95,49 +96,51 @@ RSpec.describe '/tokens' do
 
       context "when the SHA256 challenge is invalid" do
         let(:code_verifier) { SecureRandom.hex(128) }
-        before { post '/oauth/token', params: { grant_type: 'authorization_code', code: SecureRandom.hex(20) }, headers: headers }
-
-        let(:authorization) { create(:authorization, client: client, challenge: Base64.urlsafe_encode64(Digest::SHA256.hexdigest(code_verifier)) , challenge_method: :sha256) }
+        let(:authorization) { create(:authorization, client: client, challenge: Base64.urlsafe_encode64(Digest::SHA256.hexdigest(code_verifier)), challenge_method: :sha256) }
+        let(:json) { JSON.parse(response.body, symbolize_names: true) }
 
-        before { post '/oauth/token', params: { grant_type: 'authorization_code', code: authorization.code, code_verifier: 'invalid' }, headers: headers }
+        before do
+          post '/oauth/token', params: { grant_type: 'authorization_code', code: SecureRandom.hex(20) }, headers: headers
+          post '/oauth/token', params: { grant_type: 'authorization_code', code: authorization.code, code_verifier: 'invalid' }, headers: headers
+        end
 
         specify { expect(response).to have_http_status(:bad_request) }
         specify { expect(response.headers['Content-Type']).to include('application/json') }
         specify { expect(response.headers['Cache-Control']).to include('no-store') }
         specify { expect(response.headers['Pragma']).to eql('no-cache') }
 
-        let(:json) { JSON.parse(response.body, symbolize_names: true) }
         specify { expect(json[:error]).to eql('invalid_request') }
       end
 
       context "when the plain challenge is invalid" do
         let(:code_verifier) { SecureRandom.hex(128) }
-        before { post '/oauth/token', params: { grant_type: 'authorization_code', code: SecureRandom.hex(20) }, headers: headers }
-
         let(:authorization) { create(:authorization, client: client, challenge: code_verifier, challenge_method: :plain) }
+        let(:json) { JSON.parse(response.body, symbolize_names: true) }
 
-        before { post '/oauth/token', params: { grant_type: 'authorization_code', code: authorization.code, code_verifier: 'invalid' }, headers: headers }
+        before do
+          post '/oauth/token', params: { grant_type: 'authorization_code', code: SecureRandom.hex(20) }, headers: headers
+          post '/oauth/token', params: { grant_type: 'authorization_code', code: authorization.code, code_verifier: 'invalid' }, headers: headers
+        end
 
         specify { expect(response).to have_http_status(:bad_request) }
         specify { expect(response.headers['Content-Type']).to include('application/json') }
         specify { expect(response.headers['Cache-Control']).to include('no-store') }
         specify { expect(response.headers['Pragma']).to eql('no-cache') }
 
-        let(:json) { JSON.parse(response.body, symbolize_names: true) }
         specify { expect(json[:error]).to eql('invalid_request') }
       end
     end
 
     context "when requesting a token using the client_credentials grant" do
       context "when the client credentials are valid" do
+        let(:json) { JSON.parse(response.body, symbolize_names: true) }
+
         before { post '/oauth/token', params: { grant_type: 'client_credentials' }, headers: headers }
 
         specify { expect(response).to have_http_status(:ok) }
         specify { expect(response.headers['Content-Type']).to include('application/json') }
         specify { expect(response.headers['Cache-Control']).to include('no-store') }
         specify { expect(response.headers['Pragma']).to eql('no-cache') }
-
-        let(:json) { JSON.parse(response.body, symbolize_names: true) }
         specify { expect(json[:access_token]).to be_present }
         specify { expect(json[:token_type]).to eql('Bearer') }
         specify { expect(json[:expires_in]).to eql(1.hour.to_i) }
@@ -146,10 +149,11 @@ RSpec.describe '/tokens' do
 
       context "when the credentials are unknown" do
         let(:headers) { { 'Authorization' => 'invalid' } }
+        let(:json) { JSON.parse(response.body, symbolize_names: true) }
+
         before { post '/oauth/token', params: { grant_type: 'client_credentials' }, headers: headers }
 
         specify { expect(response).to have_http_status(:unauthorized) }
-        let(:json) { JSON.parse(response.body, symbolize_names: true) }
         specify { expect(json[:error]).to eql('invalid_client') }
       end
     end
@@ -157,14 +161,14 @@ RSpec.describe '/tokens' do
     context "when requesting tokens using the resource owner password credentials grant" do
       context "when the credentials are valid" do
         let(:user) { create(:user) }
+        let(:json) { JSON.parse(response.body, symbolize_names: true) }
+
         before { post '/oauth/token', params: { grant_type: 'password', username: user.email, password: user.password }, headers: headers }
 
         specify { expect(response).to have_http_status(:ok) }
         specify { expect(response.headers['Content-Type']).to include('application/json') }
         specify { expect(response.headers['Cache-Control']).to include('no-store') }
         specify { expect(response.headers['Pragma']).to eql('no-cache') }
-
-        let(:json) { JSON.parse(response.body, symbolize_names: true) }
         specify { expect(json[:access_token]).to be_present }
         specify { expect(json[:token_type]).to eql('Bearer') }
         specify { expect(json[:expires_in]).to eql(1.hour.to_i) }
@@ -172,10 +176,11 @@ RSpec.describe '/tokens' do
       end
 
       context "when the credentials are invalid" do
+        let(:json) { JSON.parse(response.body, symbolize_names: true) }
+
         before { post '/oauth/token', params: { grant_type: 'password', username: generate(:email), password: generate(:password) }, headers: headers }
 
         specify { expect(response).to have_http_status(:bad_request) }
-        let(:json) { JSON.parse(response.body, symbolize_names: true) }
         specify { expect(json[:error]).to eql('invalid_request') }
       end
     end
@@ -183,6 +188,7 @@ RSpec.describe '/tokens' do
     context "when exchanging a refresh token for a new access token" do
       context "when the refresh token is still active" do
         let(:refresh_token) { create(:refresh_token) }
+        let(:json) { JSON.parse(response.body, symbolize_names: true) }
 
         before { post '/oauth/token', params: { grant_type: 'refresh_token', refresh_token: refresh_token.to_jwt }, headers: headers }
 
@@ -190,8 +196,6 @@ RSpec.describe '/tokens' do
         specify { expect(response.headers['Content-Type']).to include('application/json') }
         specify { expect(response.headers['Cache-Control']).to include('no-store') }
         specify { expect(response.headers['Pragma']).to eql('no-cache') }
-
-        let(:json) { JSON.parse(response.body, symbolize_names: true) }
         specify { expect(json[:access_token]).to be_present }
         specify { expect(json[:token_type]).to eql('Bearer') }
         specify { expect(json[:expires_in]).to eql(1.hour.to_i) }
@@ -203,11 +207,12 @@ RSpec.describe '/tokens' do
     context "when exchanging a SAML 2.0 assertion grant for tokens" do
       context "when the assertion contains a valid email address" do
         let(:user) { create(:user) }
-        let(:saml_request) { double(id: Xml::Kit::Id.generate, issuer: Saml::Kit.configuration.entity_id, trusted?: true) }
+        let(:saml_request) { instance_double(Saml::Kit::AuthenticationRequest, id: Xml::Kit::Id.generate, issuer: Saml::Kit.configuration.entity_id, trusted?: true) }
         let(:saml) { Saml::Kit::Assertion.build_xml(user, saml_request) }
         let(:metadata) { Saml::Kit::Metadata.build(&:build_identity_provider) }
+        let(:json) { JSON.parse(response.body, symbolize_names: true) }
 
-        before :each do
+        before do
           allow(Saml::Kit.configuration.registry).to receive(:metadata_for).and_return(metadata)
           post '/oauth/token', params: {
             grant_type: 'urn:ietf:params:oauth:grant-type:saml2-bearer',
@@ -219,8 +224,6 @@ RSpec.describe '/tokens' do
         specify { expect(response.headers['Content-Type']).to include('application/json') }
         specify { expect(response.headers['Cache-Control']).to include('no-store') }
         specify { expect(response.headers['Pragma']).to eql('no-cache') }
-
-        let(:json) { JSON.parse(response.body, symbolize_names: true) }
         specify { expect(json[:access_token]).to be_present }
         specify { expect(json[:token_type]).to eql('Bearer') }
         specify { expect(json[:expires_in]).to eql(1.hour.to_i) }
@@ -229,11 +232,12 @@ RSpec.describe '/tokens' do
 
       context "when the assertion contains a valid uuid" do
         let(:user) { create(:user) }
-        let(:saml_request) { double(id: Xml::Kit::Id.generate, issuer: Saml::Kit.configuration.entity_id, trusted?: true, name_id_format: Saml::Kit::Namespaces::PERSISTENT) }
+        let(:saml_request) { instance_double(Saml::Kit::AuthenticationRequest, id: Xml::Kit::Id.generate, issuer: Saml::Kit.configuration.entity_id, trusted?: true, name_id_format: Saml::Kit::Namespaces::PERSISTENT) }
         let(:saml) { Saml::Kit::Assertion.build_xml(user, saml_request) }
         let(:metadata) { Saml::Kit::Metadata.build(&:build_identity_provider) }
+        let(:json) { JSON.parse(response.body, symbolize_names: true) }
 
-        before :each do
+        before do
           allow(Saml::Kit.configuration.registry).to receive(:metadata_for).and_return(metadata)
           post '/oauth/token', params: {
             grant_type: 'urn:ietf:params:oauth:grant-type:saml2-bearer',
@@ -245,8 +249,6 @@ RSpec.describe '/tokens' do
         specify { expect(response.headers['Content-Type']).to include('application/json') }
         specify { expect(response.headers['Cache-Control']).to include('no-store') }
         specify { expect(response.headers['Pragma']).to eql('no-cache') }
-
-        let(:json) { JSON.parse(response.body, symbolize_names: true) }
         specify { expect(json[:access_token]).to be_present }
         specify { expect(json[:token_type]).to eql('Bearer') }
         specify { expect(json[:expires_in]).to eql(1.hour.to_i) }
@@ -256,11 +258,12 @@ RSpec.describe '/tokens' do
 
     context "when the assertion is not a valid document" do
       let(:user) { create(:user) }
-      let(:saml_request) { double(id: Xml::Kit::Id.generate, issuer: Saml::Kit.configuration.entity_id) }
+      let(:saml_request) { instance_double(Saml::Kit::AuthenticationRequest, id: Xml::Kit::Id.generate, issuer: Saml::Kit.configuration.entity_id) }
       let(:saml) { 'invalid' }
       let(:metadata) { Saml::Kit::Metadata.build(&:build_identity_provider) }
+      let(:json) { JSON.parse(response.body, symbolize_names: true) }
 
-      before :each do
+      before do
         allow(Saml::Kit.configuration.registry).to receive(:metadata_for).and_return(metadata)
         post '/oauth/token', params: {
           grant_type: 'urn:ietf:params:oauth:grant-type:saml2-bearer',
@@ -272,19 +275,18 @@ RSpec.describe '/tokens' do
       specify { expect(response.headers['Content-Type']).to include('application/json') }
       specify { expect(response.headers['Cache-Control']).to include('no-store') }
       specify { expect(response.headers['Pragma']).to eql('no-cache') }
-
-      let(:json) { JSON.parse(response.body, symbolize_names: true) }
       specify { expect(json[:error]).to eql('invalid_request') }
     end
 
     context "when the assertion has an invalid signature" do
       let(:user) { create(:user) }
-      let(:saml_request) { double(id: Xml::Kit::Id.generate, issuer: Saml::Kit.configuration.entity_id, trusted?: false) }
+      let(:saml_request) { instance_double(Saml::Kit::AuthenticationRequest, id: Xml::Kit::Id.generate, issuer: Saml::Kit.configuration.entity_id, trusted?: false) }
       let(:key_pair) { Xml::Kit::KeyPair.generate(use: :signing) }
       let(:saml) { Saml::Kit::Assertion.build_xml(user, saml_request) { |x| x.sign_with(key_pair) } }
       let(:metadata) { Saml::Kit::Metadata.build(&:build_identity_provider) }
+      let(:json) { JSON.parse(response.body, symbolize_names: true) }
 
-      before :each do
+      before do
         allow(Saml::Kit.configuration.registry).to receive(:metadata_for).and_return(metadata)
         post '/oauth/token', params: {
           grant_type: 'urn:ietf:params:oauth:grant-type:saml2-bearer',
@@ -297,7 +299,6 @@ RSpec.describe '/tokens' do
       specify { expect(response.headers['Cache-Control']).to include('no-store') }
       specify { expect(response.headers['Pragma']).to eql('no-cache') }
 
-      let(:json) { JSON.parse(response.body, symbolize_names: true) }
       specify { expect(json[:error]).to eql('invalid_request') }
     end
   end
@@ -305,13 +306,13 @@ RSpec.describe '/tokens' do
   describe "POST /tokens/introspect" do
     context "when the access_token is valid" do
       let(:token) { create(:access_token) }
+      let(:json) { JSON.parse(response.body, symbolize_names: true) }
 
       before { post '/tokens/introspect', params: { token: token.to_jwt }, headers: headers }
 
       specify { expect(response).to have_http_status(:ok) }
       specify { expect(response['Content-Type']).to include('application/json') }
-      let(:json) { JSON.parse(response.body, symbolize_names: true) }
-      specify { expect(json[:active]).to eql(true) }
+      specify { expect(json[:active]).to be(true) }
       specify { expect(json[:sub]).to eql(token.claims[:sub]) }
       specify { expect(json[:aud]).to eql(token.claims[:aud]) }
       specify { expect(json[:iss]).to eql(token.claims[:iss]) }
@@ -321,13 +322,13 @@ RSpec.describe '/tokens' do
 
     context "when the refresh_token is valid" do
       let(:token) { create(:refresh_token) }
+      let(:json) { JSON.parse(response.body, symbolize_names: true) }
 
       before { post '/tokens/introspect', params: { token: token.to_jwt }, headers: headers }
 
       specify { expect(response).to have_http_status(:ok) }
       specify { expect(response['Content-Type']).to include('application/json') }
-      let(:json) { JSON.parse(response.body, symbolize_names: true) }
-      specify { expect(json[:active]).to eql(true) }
+      specify { expect(json[:active]).to be(true) }
       specify { expect(json[:sub]).to eql(token.claims[:sub]) }
       specify { expect(json[:aud]).to eql(token.claims[:aud]) }
       specify { expect(json[:iss]).to eql(token.claims[:iss]) }
@@ -337,24 +338,24 @@ RSpec.describe '/tokens' do
 
     context "when the token is revoked" do
       let(:token) { create(:access_token, :revoked) }
+      let(:json) { JSON.parse(response.body, symbolize_names: true) }
 
       before { post '/tokens/introspect', params: { token: token.to_jwt }, headers: headers }
 
       specify { expect(response).to have_http_status(:ok) }
       specify { expect(response['Content-Type']).to include('application/json') }
-      let(:json) { JSON.parse(response.body, symbolize_names: true) }
-      specify { expect(json[:active]).to eql(false) }
+      specify { expect(json[:active]).to be(false) }
     end
 
     context "when the token is expired" do
       let(:token) { create(:access_token, :expired) }
+      let(:json) { JSON.parse(response.body, symbolize_names: true) }
 
       before { post '/tokens/introspect', params: { token: token.to_jwt }, headers: headers }
 
       specify { expect(response).to have_http_status(:ok) }
       specify { expect(response['Content-Type']).to include('application/json') }
-      let(:json) { JSON.parse(response.body, symbolize_names: true) }
-      specify { expect(json[:active]).to eql(false) }
+      specify { expect(json[:active]).to be(false) }
     end
   end
 
spec/routing/scim_spec.rb
@@ -1,22 +1,25 @@
+# frozen_string_literal: true
+
 require "rails_helper"
 
 describe "/scim" do
   let(:id) { SecureRandom.uuid }
+
   it { expect(get: "scim/v2/users/#{id}").to route_to(controller: "scim/v2/users", action: "show", id: id, format: :scim) }
   it { expect(post: "scim/v2/users").to route_to(controller: "scim/v2/users", action: "create", format: :scim) }
   it { expect(put: "scim/v2/users/#{id}").to route_to(controller: "scim/v2/users", action: "update", id: id, format: :scim) }
   it { expect(patch: "scim/v2/users/#{id}").to route_to(controller: "scim/v2/users", action: "update", id: id, format: :scim) }
   it { expect(delete: "scim/v2/users/#{id}").to route_to(controller: "scim/v2/users", action: "destroy", id: id, format: :scim) }
 
-  #it { expect(get: "scim/v2/groups/#{id}").to route_to(controller: "scim/v2/groups", action: "show", id: id, format: :scim) }
-  #it { expect(post: "scim/v2/groups").to route_to(controller: "scim/v2/groups", action: "create", format: :scim) }
-  #it { expect(put: "scim/v2/groups/#{id}").to route_to(controller: "scim/v2/groups", action: "update", id: id, format: :scim) }
-  #it { expect(patch: "scim/v2/groups/#{id}").to route_to(controller: "scim/v2/groups", action: "update", id: id, format: :scim) }
-  #it { expect(delete: "scim/v2/groups/#{id}").to route_to(controller: "scim/v2/groups", action: "destroy", id: id, format: :scim) }
+  # it { expect(get: "scim/v2/groups/#{id}").to route_to(controller: "scim/v2/groups", action: "show", id: id, format: :scim) }
+  # it { expect(post: "scim/v2/groups").to route_to(controller: "scim/v2/groups", action: "create", format: :scim) }
+  # it { expect(put: "scim/v2/groups/#{id}").to route_to(controller: "scim/v2/groups", action: "update", id: id, format: :scim) }
+  # it { expect(patch: "scim/v2/groups/#{id}").to route_to(controller: "scim/v2/groups", action: "update", id: id, format: :scim) }
+  # it { expect(delete: "scim/v2/groups/#{id}").to route_to(controller: "scim/v2/groups", action: "destroy", id: id, format: :scim) }
 
-  #it { expect(get: "/scim/v2/me").to route_to(controller: 'hi') }
+  # it { expect(get: "/scim/v2/me").to route_to(controller: 'hi') }
   it { expect(get: "scim/v2/ServiceProviderConfig").to route_to(controller: "scim/v2/service_providers", action: "show", format: :scim) }
   it { expect(get: "scim/v2/ResourceTypes").to route_to(controller: "scim/v2/resource_types", action: "index", format: :scim) }
   it { expect(get: "scim/v2/schemas").to route_to(controller: "scim/v2/schemas", action: "index", format: :scim) }
-  #it { expect(post: "scim/v2/bulk").to route_to(controller: "scim/v2/bulk", action: "update", format: :scim) }
+  # it { expect(post: "scim/v2/bulk").to route_to(controller: "scim/v2/bulk", action: "update", format: :scim) }
 end
spec/support/active_support.rb
@@ -1,6 +1,8 @@
+# frozen_string_literal: true
+
 RSpec.configure do |config|
   config.include ActiveSupport::Testing::TimeHelpers
-  config.after :each do |example|
+  config.after do |_example|
     travel_back
   end
   config.include(Module.new do
spec/support/factory_bot.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 RSpec.configure do |config|
   config.include FactoryBot::Syntax::Methods
 end
spec/support/request.rb
@@ -1,8 +1,11 @@
+# frozen_string_literal: true
+
 RSpec.configure do |config|
   config.include(Module.new do
     def http_login(user, skip_mfa: false)
       post '/session', params: { user: { email: user.email, password: user.password } }
       return if skip_mfa
+
       mfa_login(user) if user.mfa.setup?
     end
 
spec/support/system.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'capybara/rails'
 require 'capybara-screenshot/rspec'
 
spec/system/direct_login_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 describe "when logging in directly in to the application", js: true do
spec/factories.rb
@@ -1,4 +1,6 @@
+# frozen_string_literal: true
+
 FactoryBot.define do
-  sequence(:email) { |n| FFaker::Internet.email }
-  sequence(:password) { |n| FFaker::Internet.password }
+  sequence(:email) { |_n| FFaker::Internet.email }
+  sequence(:password) { |_n| FFaker::Internet.password }
 end
spec/rails_helper.rb
@@ -1,7 +1,9 @@
+# frozen_string_literal: true
+
 # This file is copied to spec/ when you run 'rails generate rspec:install'
 require 'spec_helper'
 ENV['RAILS_ENV'] ||= 'test'
-require File.expand_path('../../config/environment', __FILE__)
+require File.expand_path('../config/environment', __dir__)
 # Prevent database truncation if the environment is production
 abort("The Rails environment is running in production mode!") if Rails.env.production?
 require 'rspec/rails'
spec/spec_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 # This file was generated by the `rails generate rspec:install` command. Conventionally, all
 # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
 # The generated `.rspec` file contains `--require spec_helper` which will cause
@@ -44,53 +46,51 @@ RSpec.configure do |config|
   # triggering implicit auto-inclusion in groups with matching metadata.
   config.shared_context_metadata_behavior = :apply_to_host_groups
 
-# The settings below are suggested to provide a good initial experience
-# with RSpec, but feel free to customize to your heart's content.
-=begin
-  # This allows you to limit a spec run to individual examples or groups
-  # you care about by tagging them with `:focus` metadata. When nothing
-  # is tagged with `:focus`, all examples get run. RSpec also provides
-  # aliases for `it`, `describe`, and `context` that include `:focus`
-  # metadata: `fit`, `fdescribe` and `fcontext`, respectively.
-  config.filter_run_when_matching :focus
-
-  # Allows RSpec to persist some state between runs in order to support
-  # the `--only-failures` and `--next-failure` CLI options. We recommend
-  # you configure your source control system to ignore this file.
-  config.example_status_persistence_file_path = "spec/examples.txt"
-
-  # Limits the available syntax to the non-monkey patched syntax that is
-  # recommended. For more details, see:
-  #   - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/
-  #   - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
-  #   - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode
-  config.disable_monkey_patching!
-
-  # Many RSpec users commonly either run the entire suite or an individual
-  # file, and it's useful to allow more verbose output when running an
-  # individual spec file.
-  if config.files_to_run.one?
-    # Use the documentation formatter for detailed output,
-    # unless a formatter has already been configured
-    # (e.g. via a command-line flag).
-    config.default_formatter = "doc"
-  end
-
-  # Print the 10 slowest examples and example groups at the
-  # end of the spec run, to help surface which specs are running
-  # particularly slow.
-  config.profile_examples = 10
-
-  # Run specs in random order to surface order dependencies. If you find an
-  # order dependency and want to debug it, you can fix the order by providing
-  # the seed, which is printed after each run.
-  #     --seed 1234
-  config.order = :random
-
-  # Seed global randomization in this process using the `--seed` CLI option.
-  # Setting this allows you to use `--seed` to deterministically reproduce
-  # test failures related to randomization by passing the same `--seed` value
-  # as the one that triggered the failure.
-  Kernel.srand config.seed
-=end
+  # The settings below are suggested to provide a good initial experience
+  # with RSpec, but feel free to customize to your heart's content.
+  #   # This allows you to limit a spec run to individual examples or groups
+  #   # you care about by tagging them with `:focus` metadata. When nothing
+  #   # is tagged with `:focus`, all examples get run. RSpec also provides
+  #   # aliases for `it`, `describe`, and `context` that include `:focus`
+  #   # metadata: `fit`, `fdescribe` and `fcontext`, respectively.
+  #   config.filter_run_when_matching :focus
+  #
+  #   # Allows RSpec to persist some state between runs in order to support
+  #   # the `--only-failures` and `--next-failure` CLI options. We recommend
+  #   # you configure your source control system to ignore this file.
+  #   config.example_status_persistence_file_path = "spec/examples.txt"
+  #
+  #   # Limits the available syntax to the non-monkey patched syntax that is
+  #   # recommended. For more details, see:
+  #   #   - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/
+  #   #   - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
+  #   #   - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode
+  #   config.disable_monkey_patching!
+  #
+  #   # Many RSpec users commonly either run the entire suite or an individual
+  #   # file, and it's useful to allow more verbose output when running an
+  #   # individual spec file.
+  #   if config.files_to_run.one?
+  #     # Use the documentation formatter for detailed output,
+  #     # unless a formatter has already been configured
+  #     # (e.g. via a command-line flag).
+  #     config.default_formatter = "doc"
+  #   end
+  #
+  #   # Print the 10 slowest examples and example groups at the
+  #   # end of the spec run, to help surface which specs are running
+  #   # particularly slow.
+  #   config.profile_examples = 10
+  #
+  #   # Run specs in random order to surface order dependencies. If you find an
+  #   # order dependency and want to debug it, you can fix the order by providing
+  #   # the seed, which is printed after each run.
+  #   #     --seed 1234
+  #   config.order = :random
+  #
+  #   # Seed global randomization in this process using the `--seed` CLI option.
+  #   # Setting this allows you to use `--seed` to deterministically reproduce
+  #   # test failures related to randomization by passing the same `--seed` value
+  #   # as the one that triggered the failure.
+  #   Kernel.srand config.seed
 end
.rubocop.yml
@@ -1,3 +1,4 @@
+require: rubocop-rspec
 # For a list of available cops see:
 # https://github.com/bbatsov/rubocop/blob/master/config/default.yml
 AllCops:
@@ -11,20 +12,45 @@ AllCops:
     - 'db/seeds.rb'
     - 'node_modules/**/*'
     - 'pkg/**/*'
-    - 'spec/**/*'
-    - 'test/**/*'
     - 'tmp/**/*'
     - 'vendor/**/*'
 
 Metrics/AbcSize:
   Enabled: false
 
+Metrics/BlockLength:
+  Exclude:
+    - 'spec/**/*'
+
+Metrics/LineLength:
+  Exclude:
+    - 'spec/**/*'
+
 Metrics/MethodLength:
   Enabled: false
 
 Metrics/PerceivedComplexity:
   Enabled: false
 
+RSpec/DescribeClass:
+  Enabled: false
+
+RSpec/ExampleLength:
+  Exclude:
+    - 'spec/system/**/*'
+
+RSpec/LetSetup:
+  Enabled: false
+
+RSpec/MultipleExpectations:
+  Max: 2
+
+RSpec/NamedSubject:
+  Enabled: false
+
+RSpec/NestedGroups:
+  Max: 5
+
 Style/Documentation:
   Enabled: false
 
Gemfile
@@ -34,19 +34,22 @@ group :development do
   gem 'brakeman', '~> 4.3'
   gem 'bundler-audit', '~> 0.6'
   gem 'listen', '>= 3.0.5', '< 3.2'
-  gem 'rubocop', '~> 0.58'
+  gem 'rubocop', '~> 0.59', require: false
   gem 'web-console', '>= 3.3.0'
 end
 group :development, :test do
   gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
+  gem 'i18n-tasks', '~> 0.9.24'
+  gem 'rspec-rails', '~> 3.8'
+  gem 'sqlite3'
+end
+group :test do
   gem 'capybara', '~> 3.6'
   gem 'capybara-screenshot', '~> 1.0'
   gem 'factory_bot_rails', '~> 4.11'
   gem 'ffaker', '~> 2.10'
-  gem 'i18n-tasks', '~> 0.9.24'
-  gem 'rspec-rails', '~> 3.7'
+  gem 'rubocop-rspec', '~> 1.30'
   gem 'selenium-webdriver', '~> 3.14'
-  gem 'sqlite3'
   gem 'webmock', '~> 3.4'
 end
 group :production do
Gemfile.lock
@@ -64,7 +64,7 @@ GEM
       bundler (~> 1.2)
       thor (~> 0.18)
     byebug (10.0.2)
-    capybara (3.8.1)
+    capybara (3.9.0)
       addressable
       mini_mime (>= 0.1.3)
       nokogiri (~> 1.8)
@@ -144,13 +144,13 @@ GEM
       sass (~> 3.4)
     jekyll-seo-tag (2.5.0)
       jekyll (~> 3.3)
-    jekyll-watch (2.0.0)
+    jekyll-watch (2.1.2)
       listen (~> 3.0)
     jwt (2.1.0)
     kramdown (1.17.0)
     launchy (2.4.3)
       addressable (~> 2.3)
-    liquid (4.0.0)
+    liquid (4.0.1)
     listen (3.1.5)
       rb-fsevent (~> 0.9, >= 0.9.4)
       rb-inotify (~> 0.9, >= 0.9.7)
@@ -159,7 +159,7 @@ GEM
     loofah (2.2.2)
       crass (~> 1.0.2)
       nokogiri (>= 1.5.9)
-    mail (2.7.0)
+    mail (2.7.1)
       mini_mime (>= 0.1.1)
     marcel (0.3.3)
       mimemagic (~> 0.3.2)
@@ -228,10 +228,10 @@ GEM
     rb-inotify (0.9.10)
       ffi (>= 0.5.0, < 2)
     rotp (3.3.1)
-    rouge (3.2.1)
+    rouge (3.3.0)
     rspec-core (3.8.0)
       rspec-support (~> 3.8.0)
-    rspec-expectations (3.8.1)
+    rspec-expectations (3.8.2)
       diff-lcs (>= 1.2.0, < 2.0)
       rspec-support (~> 3.8.0)
     rspec-mocks (3.8.0)
@@ -254,11 +254,13 @@ GEM
       rainbow (>= 2.2.2, < 4.0)
       ruby-progressbar (~> 1.7)
       unicode-display_width (~> 1.0, >= 1.0.1)
+    rubocop-rspec (1.30.0)
+      rubocop (>= 0.58.0)
     ruby-progressbar (1.10.0)
     ruby_dep (1.5.0)
     rubyzip (1.2.2)
     safe_yaml (1.0.4)
-    saml-kit (1.0.24)
+    saml-kit (1.0.25)
       activemodel (>= 4.2.0)
       net-hippie (~> 0.1.8)
       xml-kit (>= 0.1.13, <= 1.0.0)
@@ -269,9 +271,9 @@ GEM
       rb-inotify (~> 0.9, >= 0.9.7)
     scim-shady (0.2.1)
       activesupport (>= 4.2.0)
-    selenium-webdriver (3.14.0)
+    selenium-webdriver (3.14.1)
       childprocess (~> 0.5)
-      rubyzip (~> 1.2)
+      rubyzip (~> 1.2, >= 1.2.2)
     spank (1.0.1441140881)
     sprockets (3.7.2)
       concurrent-ruby (~> 1.0)
@@ -318,7 +320,7 @@ GEM
       xmldsig (~> 0.6)
     xmldsig (0.6.6)
       nokogiri (>= 1.6.8, < 2.0.0)
-    xpath (3.1.0)
+    xpath (3.2.0)
       nokogiri (~> 1.8)
 
 PLATFORMS
@@ -353,8 +355,9 @@ DEPENDENCIES
   rails (~> 5.2.0)
   rails_12factor (~> 0.0)
   rotp (~> 3.3)
-  rspec-rails (~> 3.7)
-  rubocop (~> 0.58)
+  rspec-rails (~> 3.8)
+  rubocop (~> 0.59)
+  rubocop-rspec (~> 1.30)
   saml-kit (~> 1.0)
   scim-shady (~> 0.2)
   selenium-webdriver (~> 3.14)