fix: stabilize conversation FAQ lock spec (#15236)

## Description

Backend CI no longer flakes when the conversation FAQ grouping-lock spec
runs after Rails has reloaded application constants.

The job already raises the intended lock-acquisition error and prevents
concurrent suggestion generation. This updates the assertion to compare
the error class name, preserving that behavior without depending on a
reload-sensitive Ruby `Class` object.

The failure was reproduced in [CircleCI backend job
171299](https://app.circleci.com/pipelines/github/chatwoot/chatwoot/116560/workflows/efcee2f1-5af8-4121-82c3-920002dc420b/jobs/171299).

## Type of change

- [x] Bug fix (non-breaking change which fixes an issue)

## How Has This Been Tested?

The isolated job spec passes, the exact 18-way CircleCI shard selection
passes all 442 examples, and the changed spec passes Ruby lint.

## Checklist:

- [x] My code follows the style guidelines of this project
- [x] I have performed a self-review of my code
- [x] My changes generate no new warnings
- [x] New and existing unit tests pass locally with my changes
This commit is contained in:
Sony Mathew
2026-07-29 17:06:31 +05:30
committed by GitHub
parent 8fcd32f442
commit bc9839ed38
2 changed files with 17 additions and 16 deletions

View File

@@ -165,9 +165,8 @@ RSpec.describe DeviseOverrides::SessionsController, type: :controller do
end
describe 'session limit enforcement' do
before { stub_const('DeviseOverrides::SessionsController::MAX_SESSIONS', 5) }
let(:user) { create(:user, password: 'Test@123456') }
let(:session_limit) { described_class::MAX_SESSIONS }
let(:browser_ua) { 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.2.1 Safari/605.1.15' }
let(:mobile_ua) { 'okhttp/4.9.3' }
@@ -195,9 +194,9 @@ RSpec.describe DeviseOverrides::SessionsController, type: :controller do
it 'does not count expired tokens toward the cap' do
request.env['HTTP_USER_AGENT'] = browser_ua
# 3 expired + 2 active = 5 raw entries, but only 2 active
# Raw token count reaches the cap, but three tokens are expired
3.times { |i| seed_token("expired#{i}", expiry_offset_days: -1, with_session: false) }
2.times { |i| seed_token("active#{i}", expiry_offset_days: 30) }
(session_limit - 3).times { |i| seed_token("active#{i}", expiry_offset_days: 30) }
post :create, params: login_params
@@ -208,7 +207,7 @@ RSpec.describe DeviseOverrides::SessionsController, type: :controller do
context 'when at the limit from a browser with full tracking' do
before do
request.env['HTTP_USER_AGENT'] = browser_ua
5.times { |i| seed_token("c#{i}", expiry_offset_days: 30) }
session_limit.times { |i| seed_token("c#{i}", expiry_offset_days: 30) }
end
it 'returns 409 with the session list (picker)' do
@@ -217,7 +216,7 @@ RSpec.describe DeviseOverrides::SessionsController, type: :controller do
expect(response).to have_http_status(:conflict)
body = response.parsed_body
expect(body['sessions_limit_reached']).to be true
expect(body['sessions'].size).to eq(5)
expect(body['sessions'].size).to eq(session_limit)
end
it 'does not create a new session row' do
@@ -228,7 +227,7 @@ RSpec.describe DeviseOverrides::SessionsController, type: :controller do
context 'when at the limit from a non-browser client' do
before do
request.env['HTTP_USER_AGENT'] = mobile_ua
5.times { |i| seed_token("c#{i}", expiry_offset_days: 30 + i, with_session: false) }
session_limit.times { |i| seed_token("c#{i}", expiry_offset_days: 30 + i, with_session: false) }
end
it 'silently evicts the oldest token and lets login proceed' do
@@ -242,9 +241,9 @@ RSpec.describe DeviseOverrides::SessionsController, type: :controller do
context 'when at the limit but tracking is partial (legacy tokens present)' do
before do
request.env['HTTP_USER_AGENT'] = browser_ua
# one tracked, four legacy (no user_session rows)
# One tracked session, with the remaining tokens having no user_session rows
seed_token('tracked', expiry_offset_days: 60, with_session: true)
4.times { |i| seed_token("legacy#{i}", expiry_offset_days: 10 + i, with_session: false) }
(session_limit - 1).times { |i| seed_token("legacy#{i}", expiry_offset_days: 10 + i, with_session: false) }
end
it 'silent-evicts instead of showing a partial picker' do
@@ -266,10 +265,10 @@ RSpec.describe DeviseOverrides::SessionsController, type: :controller do
context 'when at the limit with full tracking (no legacy gap)' do
before do
request.env['HTTP_USER_AGENT'] = mobile_ua
# Five tracked sessions, varying activity timestamps
5.times do |i|
# Tracked sessions with varying activity timestamps
session_limit.times do |i|
seed_token("tracked#{i}", expiry_offset_days: 30)
user.user_sessions.find_by(client_id: "tracked#{i}").update!(last_activity_at: (5 - i).days.ago)
user.user_sessions.find_by(client_id: "tracked#{i}").update!(last_activity_at: (session_limit - i).days.ago)
end
end
@@ -277,7 +276,7 @@ RSpec.describe DeviseOverrides::SessionsController, type: :controller do
post :create, params: login_params
expect(response).to have_http_status(:success)
# tracked0 had the oldest last_activity_at (5 days ago)
# tracked0 has the oldest last_activity_at
expect(user.reload.tokens.keys).not_to include('tracked0')
expect(user.user_sessions.exists?(client_id: 'tracked0')).to be false
end
@@ -286,7 +285,7 @@ RSpec.describe DeviseOverrides::SessionsController, type: :controller do
context 'with revoke_session_id during login' do
before do
request.env['HTTP_USER_AGENT'] = browser_ua
5.times { |i| seed_token("c#{i}", expiry_offset_days: 30) }
session_limit.times { |i| seed_token("c#{i}", expiry_offset_days: 30) }
end
it 'revokes the chosen session and proceeds with login' do
@@ -303,7 +302,7 @@ RSpec.describe DeviseOverrides::SessionsController, type: :controller do
context 'with revoke_all_sessions during login' do
before do
request.env['HTTP_USER_AGENT'] = browser_ua
5.times { |i| seed_token("c#{i}", expiry_offset_days: 30) }
session_limit.times { |i| seed_token("c#{i}", expiry_offset_days: 30) }
end
it 'wipes all sessions and tokens, then proceeds with login' do

View File

@@ -50,7 +50,9 @@ RSpec.describe Captain::Llm::ConversationFaqJob, type: :job do
expect do
described_class.new.perform(conversation, assistant)
end.to raise_error(MutexApplicationJob::LockAcquisitionError)
end.to(raise_error do |error|
expect(error.class.name).to eq('MutexApplicationJob::LockAcquisitionError')
end)
end
end
end