diff --git a/.changeset/silent-coats-open.md b/.changeset/silent-coats-open.md new file mode 100644 index 0000000000000..e8e88a7966104 --- /dev/null +++ b/.changeset/silent-coats-open.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +Fixes the Chat Limits locking mechanism to allow bot agents to skip the lock as they aren't limited diff --git a/apps/meteor/app/livechat/server/lib/conditionalLockAgent.ts b/apps/meteor/app/livechat/server/lib/conditionalLockAgent.ts index 44f4d416c18a1..89935f3ec6470 100644 --- a/apps/meteor/app/livechat/server/lib/conditionalLockAgent.ts +++ b/apps/meteor/app/livechat/server/lib/conditionalLockAgent.ts @@ -1,5 +1,6 @@ import { Users } from '@rocket.chat/models'; +import { hasRoleAsync } from '../../../authorization/server/functions/hasRole'; import { settings } from '../../../settings/server'; type LockResult = { @@ -9,10 +10,10 @@ type LockResult = { }; export async function conditionalLockAgent(agentId: string): Promise { - // Lock and chats limits enforcement are only required when waiting_queue is enabled + // Lock and chats limits enforcement are only required when waiting_queue is enabled and the agent is not a bot const shouldLock = settings.get('Livechat_waiting_queue'); - if (!shouldLock) { + if (!shouldLock || (await hasRoleAsync(agentId, 'bot'))) { return { acquired: false, required: false, diff --git a/apps/meteor/tests/unit/app/livechat/server/lib/conditionalLockAgent.spec.ts b/apps/meteor/tests/unit/app/livechat/server/lib/conditionalLockAgent.spec.ts index 50c7677db7bf4..4b79d29aa2bb9 100644 --- a/apps/meteor/tests/unit/app/livechat/server/lib/conditionalLockAgent.spec.ts +++ b/apps/meteor/tests/unit/app/livechat/server/lib/conditionalLockAgent.spec.ts @@ -11,8 +11,11 @@ const mockSettings = { get: sinon.stub(), }; +const mockHasRoleAsync = sinon.stub(); + const { conditionalLockAgent } = proxyquire.noCallThru().load('../../../../../../app/livechat/server/lib/conditionalLockAgent', { '@rocket.chat/models': { Users: mockUsers }, + '../../../authorization/server/functions/hasRole': { hasRoleAsync: mockHasRoleAsync }, '../../../settings/server': { settings: mockSettings }, }); @@ -21,11 +24,13 @@ describe('conditionalLockAgent', () => { mockUsers.acquireAgentLock.reset(); mockUsers.releaseAgentLock.reset(); mockSettings.get.reset(); + mockHasRoleAsync.reset(); }); describe('when waiting_queue is enabled', () => { beforeEach(() => { mockSettings.get.withArgs('Livechat_waiting_queue').returns(true); + mockHasRoleAsync.resolves(false); }); it('should return acquired: true when lock is successfully acquired', async () => { @@ -59,6 +64,16 @@ describe('conditionalLockAgent', () => { expect(mockUsers.releaseAgentLock.firstCall.args[0]).to.equal('agent1'); expect(mockUsers.releaseAgentLock.firstCall.args[1]).to.be.instanceOf(Date); }); + + it('should skip lock acquisition for bot agents', async () => { + mockHasRoleAsync.resolves(true); + + const result = await conditionalLockAgent('agent1'); + + expect(result.acquired).to.equal(false); + expect(result.required).to.equal(false); + expect(mockUsers.acquireAgentLock.called).to.equal(false); + }); }); describe('when waiting_queue is disabled', () => { @@ -72,6 +87,7 @@ describe('conditionalLockAgent', () => { expect(result.acquired).to.equal(false); expect(result.required).to.equal(false); expect(mockUsers.acquireAgentLock.called).to.equal(false); + expect(mockHasRoleAsync.called).to.equal(false); }); it('should have a no-op unlock function', async () => {