diff --git a/apps/meteor/ee/app/livechat-enterprise/server/lib/restrictQuery.ts b/apps/meteor/ee/app/livechat-enterprise/server/lib/restrictQuery.ts index 2fb08bd2571..b3516b400fb 100644 --- a/apps/meteor/ee/app/livechat-enterprise/server/lib/restrictQuery.ts +++ b/apps/meteor/ee/app/livechat-enterprise/server/lib/restrictQuery.ts @@ -31,16 +31,22 @@ export const restrictQuery = async ({ // IF user is trying to filter by a unit he doens't have access to, apply empty filter (no matches) userUnits = [...userUnit.intersection(filteredUnits)]; } - // TODO: units is meant to include units and departments, however, here were only using them as units - // We have to change the filter to something like { $or: [{ ancestors: {$in: units }}, {_id: {$in: units}}] } - const departments = await LivechatDepartment.find({ ancestors: { $in: userUnits } }, { projection: { _id: 1 } }).toArray(); + + const departments = await LivechatDepartment.find( + { $or: [{ ancestors: { $in: userUnits } }, { _id: { $in: userUnits } }] }, + { projection: { _id: 1 } }, + ).toArray(); const expressions = query.$and || []; const condition = { - $or: [{ departmentAncestors: { $in: userUnits } }, { departmentId: { $in: departments.map(({ _id }) => _id) } }], + $or: [ + { departmentAncestors: { $in: userUnits } }, + { departmentId: { $in: departments.map(({ _id }) => _id) } }, + { departmentId: { $exists: false } }, + ], }; query.$and = [condition, ...expressions]; - cbLogger.debug({ msg: 'Applying room query restrictions', userUnits }); + cbLogger.debug({ msg: 'Applying room query restrictions', userUnits, query }); return query; }; diff --git a/apps/meteor/ee/tests/unit/apps/livechat-enterprise/lib/restrictQuery.tests.ts b/apps/meteor/ee/tests/unit/apps/livechat-enterprise/lib/restrictQuery.tests.ts new file mode 100644 index 00000000000..e77a2b0380b --- /dev/null +++ b/apps/meteor/ee/tests/unit/apps/livechat-enterprise/lib/restrictQuery.tests.ts @@ -0,0 +1,301 @@ +import { expect } from 'chai'; +import proxyquire from 'proxyquire'; +import sinon from 'sinon'; + +type RestrictQueryParams = { + originalQuery?: Record; + unitsFilter?: string[]; + userId?: string; +}; + +describe('restrictQuery', () => { + const modulePath = '../../../../../app/livechat-enterprise/server/lib/restrictQuery'; + + // Helper to require the SUT with injected stubs + const loadSut = ({ + getUnitsFromUserResult, + departmentsToReturn = [], + captureFindArgs = false, + }: { + getUnitsFromUserResult: any; + departmentsToReturn?: Array<{ _id: string }>; + captureFindArgs?: boolean; + }) => { + const getUnitsFromUserStub = sinon.stub().callsFake(async () => getUnitsFromUserResult); + + const findArgs: any[] = []; + const findStub = sinon.stub().callsFake((query: any, projection: any) => { + if (captureFindArgs) { + findArgs.push({ query, projection }); + } + return { + toArray: async () => departmentsToReturn, + }; + }); + + const debugStub = sinon.stub(); + + const { restrictQuery } = proxyquire.noCallThru().load(modulePath, { + '@rocket.chat/models': { + LivechatDepartment: { find: findStub }, + }, + '../methods/getUnitsFromUserRoles': { + getUnitsFromUser: getUnitsFromUserStub, + }, + './logger': { + cbLogger: { debug: debugStub }, + }, + }); + + return { restrictQuery, getUnitsFromUserStub, findStub, debugStub, findArgs }; + }; + + afterEach(() => { + sinon.restore(); + }); + + it('returns the original query untouched when user has no units and no unitsFilter is provided', async () => { + const { restrictQuery, getUnitsFromUserStub, findStub, debugStub } = loadSut({ + getUnitsFromUserResult: null, + }); + + const originalQuery = { status: 'open' }; + const result = await (restrictQuery as (p: RestrictQueryParams) => Promise)({ + originalQuery, + userId: 'user-id', + }); + + expect(getUnitsFromUserStub.calledOnce).to.equal(true); + expect(getUnitsFromUserStub.firstCall.args).to.deep.equal(['user-id']); + expect(findStub.called).to.equal(false); + expect(debugStub.called).to.equal(false); + + // It must be a shallow copy but equivalent content + expect(result).to.deep.equal(originalQuery); + // Ensure original wasn't mutated + expect(originalQuery).to.not.have.property('$and'); + }); + + it('returns the original query untouched when user has no units and no unitsFilter is provided', async () => { + const { restrictQuery, getUnitsFromUserStub, findStub, debugStub } = loadSut({ + getUnitsFromUserResult: null, + }); + + const result = await (restrictQuery as (p: RestrictQueryParams) => Promise)({ + originalQuery: undefined, + userId: 'user-id', + }); + + expect(getUnitsFromUserStub.calledOnce).to.equal(true); + expect(getUnitsFromUserStub.firstCall.args).to.deep.equal(['user-id']); + expect(findStub.called).to.equal(false); + expect(debugStub.called).to.equal(false); + + // It must be a shallow copy but equivalent content + expect(result).to.deep.equal({}); + }); + + it('applies departmentAncestors filter directly when user has no units but a unitsFilter is provided', async () => { + const { restrictQuery, getUnitsFromUserStub, findStub, debugStub } = loadSut({ + getUnitsFromUserResult: undefined, + }); + + const originalQuery = { status: { $ne: 'closed' } }; + const unitsFilter = ['unit-a', 'unit-b']; + const result = await (restrictQuery as (p: RestrictQueryParams) => Promise)({ + originalQuery, + unitsFilter, + }); + + expect(getUnitsFromUserStub.calledOnce).to.equal(true); + expect(findStub.called).to.equal(false); + expect(debugStub.called).to.equal(false); + + expect(result).to.deep.equal({ + status: { $ne: 'closed' }, + departmentAncestors: { $in: unitsFilter }, + }); + // Ensure original wasn't mutated + expect(originalQuery).to.not.have.property('departmentAncestors'); + }); + + it('adds $and restriction when user has units and no unitsFilter is provided', async () => { + const { restrictQuery, findArgs, debugStub } = loadSut({ + getUnitsFromUserResult: ['unit-1', 'unit-2'], + departmentsToReturn: [{ _id: 'dep-1' }, { _id: 'dep-2' }], + captureFindArgs: true, + }); + + const originalQuery = { status: 'open' }; + const result = await (restrictQuery as (p: RestrictQueryParams) => Promise)({ + originalQuery, + userId: 'u', + }); + + // Verify LivechatDepartment.find was called with the proper query and projection + expect(findArgs).to.have.length(1); + expect(findArgs[0].query).to.deep.equal({ + $or: [{ ancestors: { $in: ['unit-1', 'unit-2'] } }, { _id: { $in: ['unit-1', 'unit-2'] } }], + }); + expect(findArgs[0].projection).to.deep.equal({ projection: { _id: 1 } }); + + // Verify the resulting query + expect(result).to.have.property('$and').that.is.an('array').with.lengthOf(1); + expect(result.$and[0]).to.deep.equal({ + $or: [ + { departmentAncestors: { $in: ['unit-1', 'unit-2'] } }, + { departmentId: { $in: ['dep-1', 'dep-2'] } }, + { departmentId: { $exists: false } }, + ], + }); + expect(result.status).to.equal('open'); + + // Logger should be called with the final query and computed userUnits + expect(debugStub.callCount).to.equal(1); + const debugArg = debugStub.firstCall.args[0]; + expect(debugArg).to.include({ msg: 'Applying room query restrictions' }); + expect(debugArg.userUnits).to.deep.equal(['unit-1', 'unit-2']); + expect(debugArg.query).to.deep.equal(result); + }); + + it('intersects user units with unitsFilter and applies restrictions accordingly', async () => { + const { restrictQuery, findArgs, debugStub } = loadSut({ + getUnitsFromUserResult: ['unit-1', 'unit-2'], + departmentsToReturn: [{ _id: 'dep-3' }], + captureFindArgs: true, + }); + + const result = await (restrictQuery as (p: RestrictQueryParams) => Promise)({ + originalQuery: { priority: 'high' }, + unitsFilter: ['unit-2', 'unit-3'], // intersection => ['unit-2'] + userId: 'u', + }); + + // find must be called using the intersected set ['unit-2'] + expect(findArgs).to.have.length(1); + expect(findArgs[0].query).to.deep.equal({ + $or: [{ ancestors: { $in: ['unit-2'] } }, { _id: { $in: ['unit-2'] } }], + }); + + // Resulting $and must use the intersected units as well + expect(result.$and).to.have.length(1); + expect(result.$and[0]).to.deep.equal({ + $or: [{ departmentAncestors: { $in: ['unit-2'] } }, { departmentId: { $in: ['dep-3'] } }, { departmentId: { $exists: false } }], + }); + expect(result.priority).to.equal('high'); + + expect(debugStub.callCount).to.equal(1); + const debugArg = debugStub.firstCall.args[0]; + expect(debugArg.userUnits).to.deep.equal(['unit-2']); + }); + + it('places the new restriction before existing $and expressions and preserves other fields', async () => { + const { restrictQuery } = loadSut({ + getUnitsFromUserResult: ['u-a'], + departmentsToReturn: [{ _id: 'dep-a' }], + }); + + const originalQuery = { $and: [{ foo: 1 }], bar: 2 }; + const result = await (restrictQuery as (p: RestrictQueryParams) => Promise)({ + originalQuery, + }); + + expect(result).to.have.property('bar', 2); + expect(result).to.have.property('$and').that.is.an('array').with.lengthOf(2); + + // First element is the newly added condition + expect(result.$and[0]).to.deep.equal({ + $or: [{ departmentAncestors: { $in: ['u-a'] } }, { departmentId: { $in: ['dep-a'] } }, { departmentId: { $exists: false } }], + }); + // Then the existing ones + expect(result.$and[1]).to.deep.equal({ foo: 1 }); + + // Ensure original $and was not mutated by reference insertion (since we recompose it) + expect(originalQuery.$and).to.deep.equal([{ foo: 1 }]); + }); + + it('handles empty intersections by producing restrictive filters while still allowing rooms without department', async () => { + const { restrictQuery, findArgs } = loadSut({ + getUnitsFromUserResult: ['unit-x'], + departmentsToReturn: [], // none will match + captureFindArgs: true, + }); + + const result = await (restrictQuery as (p: RestrictQueryParams) => Promise)({ + originalQuery: {}, + unitsFilter: ['unit-y'], // intersection => [] + }); + + // find called with empty $in arrays + expect(findArgs).to.have.length(1); + expect(findArgs[0].query).to.deep.equal({ + $or: [{ ancestors: { $in: [] } }, { _id: { $in: [] } }], + }); + + // Resulting condition must reflect empty unit and department lists, + // but still allow rooms where departmentId does not exist + expect(result.$and).to.have.length(1); + expect(result.$and[0]).to.deep.equal({ + $or: [{ departmentAncestors: { $in: [] } }, { departmentId: { $in: [] } }, { departmentId: { $exists: false } }], + }); + }); + + // New test: Validate the exact parameters passed to LivechatDepartment.find + it('calls LivechatDepartment.find with correct query and projection parameters', async () => { + const userUnits = ['unit-10', 'unit-20', 'unit-30']; + const { restrictQuery, findArgs, findStub } = loadSut({ + getUnitsFromUserResult: userUnits, + departmentsToReturn: [{ _id: 'dep-10' }], + captureFindArgs: true, + }); + + const originalQuery = { $and: [{ state: { $ne: 'archived' } }], custom: 'x' }; + const result = await (restrictQuery as (p: RestrictQueryParams) => Promise)({ + originalQuery, + userId: 'abc', + }); + + // Ensure find has been called exactly once + expect(findStub.callCount).to.equal(1); + expect(findArgs).to.have.length(1); + + // Validate query argument + expect(findArgs[0].query).to.deep.equal({ + $or: [{ ancestors: { $in: userUnits } }, { _id: { $in: userUnits } }], + }); + + // Validate projection argument is exactly as expected + expect(findArgs[0].projection).to.deep.equal({ projection: { _id: 1 } }); + + // And the resulting query contains our original bits + expect(result).to.have.property('custom', 'x'); + expect(result.$and[1]).to.deep.equal({ state: { $ne: 'archived' } }); + }); + + // New test: Ensure departments returned by find are added to the query's departmentId $in clause + it('uses departments returned by find to build departmentId $in condition', async () => { + const userUnits = ['unit-a']; + const departments = [{ _id: 'dep-a' }, { _id: 'dep-b' }, { _id: 'dep-c' }]; + const { restrictQuery } = loadSut({ + getUnitsFromUserResult: userUnits, + departmentsToReturn: departments, + captureFindArgs: true, + }); + + const result = await (restrictQuery as (p: RestrictQueryParams) => Promise)({ + originalQuery: { tag: 'vip' }, + userId: 'some-user', + }); + + // Validate that all department ids are present in the $in list + expect(result).to.have.property('$and'); + expect(result.$and).to.be.an('array').with.lengthOf(1); + const orCondition = result.$and[0].$or; + expect(orCondition).to.be.an('array').with.lengthOf(3); + + // Extract the departmentId condition + const deptIdCond = orCondition.find((c: any) => Object.keys(c)[0] === 'departmentId'); + expect(deptIdCond).to.deep.equal({ departmentId: { $in: ['dep-a', 'dep-b', 'dep-c'] } }); + expect(result.tag).to.equal('vip'); + }); +}); diff --git a/apps/meteor/tests/data/livechat/department.ts b/apps/meteor/tests/data/livechat/department.ts index 3a1599ef400..ada1eae906a 100644 --- a/apps/meteor/tests/data/livechat/department.ts +++ b/apps/meteor/tests/data/livechat/department.ts @@ -97,7 +97,7 @@ export const createDepartmentWithMethod = ({ }); }); -type OnlineAgent = { +export type OnlineAgent = { user: WithRequiredProperty; credentials: Credentials; }; diff --git a/apps/meteor/tests/e2e/omnichannel/omnichannel-monitor-role.spec.ts b/apps/meteor/tests/e2e/omnichannel/omnichannel-monitor-role.spec.ts index 25a5336742a..d21af6e6570 100644 --- a/apps/meteor/tests/e2e/omnichannel/omnichannel-monitor-role.spec.ts +++ b/apps/meteor/tests/e2e/omnichannel/omnichannel-monitor-role.spec.ts @@ -36,7 +36,7 @@ test.describe('OC - Monitor Role', () => { data: { roles: ['user'] }, userId: 'user3', }); - await expect(res.status()).toBe(200); + expect(res.status()).toBe(200); }); // Allow manual on hold @@ -44,6 +44,9 @@ test.describe('OC - Monitor Role', () => { const responses = await Promise.all([ api.post('/settings/Livechat_allow_manual_on_hold', { value: true }), api.post('/settings/Livechat_allow_manual_on_hold_upon_agent_engagement_only', { value: false }), + api.post('/settings/Omnichannel_enable_department_removal', { value: true }), + // This is required now we're sending a chat into a department with no agents and no default agent + api.post('/settings/Livechat_accept_chats_with_no_agents', { value: true }), ]); responses.forEach((res) => expect(res.status()).toBe(200)); }); @@ -64,7 +67,7 @@ test.describe('OC - Monitor Role', () => { // Create conversations test.beforeAll(async ({ api }) => { - const [departmentA, departmentB] = departments.map(({ data }) => data); + const [departmentA, departmentB, departmentC] = departments.map(({ data }) => data); conversations = await Promise.all([ createConversation(api, { @@ -84,6 +87,7 @@ test.describe('OC - Monitor Role', () => { }), createConversation(api, { visitorName: ROOM_D, + departmentId: departmentC._id, }), ]); }); @@ -120,6 +124,8 @@ test.describe('OC - Monitor Role', () => { // Reset setting api.post('/settings/Livechat_allow_manual_on_hold', { value: false }), api.post('/settings/Livechat_allow_manual_on_hold_upon_agent_engagement_only', { value: true }), + api.post('/settings/Omnichannel_enable_department_removal', { value: false }), + api.post('/settings/Livechat_accept_chats_with_no_agents', { value: false }), ]); }); @@ -248,28 +254,30 @@ test.describe('OC - Monitor Role', () => { await test.step('expect not to be able to see conversations once unit is removed', async () => { const res = await unitA.delete(); - await expect(res.status()).toBe(200); + expect(res.status()).toBe(200); await page.reload(); - await expect(page.locator('text="No chats yet"')).toBeVisible(); + await expect(poOmnichannel.currentChats.findRowByName(ROOM_B)).not.toBeVisible(); + await expect(poOmnichannel.currentChats.findRowByName(ROOM_C)).not.toBeVisible(); + await expect(poOmnichannel.currentChats.findRowByName(ROOM_D)).not.toBeVisible(); }); await test.step('expect to be able to see all conversations once all units are removed', async () => { const res = await unitB.delete(); - await expect(res.status()).toBe(200); + expect(res.status()).toBe(200); await page.reload(); await expect(poOmnichannel.currentChats.findRowByName(ROOM_D)).toBeVisible(); }); await test.step('expect not to be able to see current chats once role is removed', async () => { const res = await monitor.delete(); - await expect(res.status()).toBe(200); + expect(res.status()).toBe(200); await page.reload(); await expect(page.locator('p >> text="You are not authorized to view this page."')).toBeVisible(); }); await test.step('expect monitor to be automaticaly removed from unit once monitor is removed', async () => { const { data: monitors } = await fetchUnitMonitors(api, unitA.data._id); - await expect(monitors).toHaveLength(0); + expect(monitors).toHaveLength(0); }); }); }); diff --git a/apps/meteor/tests/end-to-end/api/livechat/00-rooms.ts b/apps/meteor/tests/end-to-end/api/livechat/00-rooms.ts index 875fb59ac17..9d358494631 100644 --- a/apps/meteor/tests/end-to-end/api/livechat/00-rooms.ts +++ b/apps/meteor/tests/end-to-end/api/livechat/00-rooms.ts @@ -22,6 +22,7 @@ import type { SuccessResult } from '../../../../app/api/server/definition'; import { getCredentials, api, request, credentials, methodCall } from '../../../data/api-data'; import { apps, APP_URL } from '../../../data/apps/apps-data'; import { createCustomField } from '../../../data/livechat/custom-fields'; +import type { OnlineAgent } from '../../../data/livechat/department'; import { createDepartmentWith2OnlineAgents, createDepartmentWithAnAwayAgent, @@ -737,8 +738,7 @@ describe('LIVECHAT - rooms', () => { .set(userCreds) .expect(200) .expect((res) => { - expect(res.body.rooms.length).to.be.equal(1); - expect(res.body.rooms[0]._id).to.be.equal(room1._id); + expect(res.body.rooms.some((r: IOmnichannelRoom) => r._id === room1._id)).to.be.true; }); }); it('should return a valid list of rooms for monitor 2', () => { @@ -747,23 +747,129 @@ describe('LIVECHAT - rooms', () => { .set(user2Creds) .expect(200) .expect((res) => { - expect(res.body.rooms.length).to.be.equal(2); + expect(res.body.rooms.some((r: IOmnichannelRoom) => r._id === room2._id)).to.be.true; + expect(res.body.rooms.some((r: IOmnichannelRoom) => r._id === room1._id)).to.be.true; }); }); it('should allow monitor 1 to filter by units', async () => { const { body } = await request.get(api('livechat/rooms')).set(userCreds).query({ 'units[]': unit._id }).expect(200); - expect(body.rooms.length).to.be.equal(1); - expect(body.rooms[0]._id).to.be.equal(room1._id); + expect(body.rooms.some((room: IOmnichannelRoom) => room._id === room1._id)).to.be.true; + expect(body.rooms.find((r: IOmnichannelRoom) => r._id === room2._id)).to.be.undefined; + expect(body.rooms.some((r: IOmnichannelRoom) => r.departmentAncestors?.includes(unit._id))).to.be.true; + expect(body.rooms.every((r: IOmnichannelRoom) => !r.departmentAncestors?.includes(unit2._id))).to.be.true; }); it('should not allow monitor 1 to filter by a unit hes not part of', async () => { const { body } = await request.get(api('livechat/rooms')).set(userCreds).query({ 'units[]': unit2._id }).expect(200); - expect(body.rooms.length).to.be.equal(0); + expect(body.rooms.every((r: IOmnichannelRoom) => !r.departmentAncestors?.includes(unit2._id))).to.be.true; }); it('should allow monitor 2 to filter by only one unit', async () => { const { body } = await request.get(api('livechat/rooms')).set(user2Creds).query({ 'units[]': unit2._id }).expect(200); + expect(body.rooms.every((r: IOmnichannelRoom) => !r.departmentAncestors?.includes(unit._id))).to.be.true; + expect(body.rooms.some((r: IOmnichannelRoom) => r.departmentAncestors?.includes(unit2._id))).to.be.true; + expect(body.rooms.find((r: IOmnichannelRoom) => r._id === room2._id)).to.be.not.undefined; + }); + }); + (IS_EE ? describe : describe.skip)('units & room restrictions for agents & monitors', () => { + let user: IUser; + let agent: OnlineAgent; + let agent2: OnlineAgent; + let userCreds: Credentials; + let department: ILivechatDepartment; + let department2: ILivechatDepartment; + let unit: IOmnichannelBusinessUnit; + let unit2: IOmnichannelBusinessUnit; + let room1: IOmnichannelRoom; + let room2: IOmnichannelRoom; + + before(async () => { + user = await createUser(); + userCreds = await login(user.username!, password); + + await createMonitor(user.username!); + const { department: dep1, agent: agent1 } = await createDepartmentWithAnOnlineAgent(); + const { department: dep2, agent: agen2 } = await createDepartmentWithAnOnlineAgent(); + + department = dep1; + agent = agent1; + department2 = dep2; + agent2 = agen2; + unit = await createUnit(user._id, user.username!, [department._id]); + unit2 = await createUnit(user._id, user.username!, [department2._id]); + }); + before(async () => { + const { room: localOpenRoom } = await startANewLivechatRoomAndTakeIt({ + departmentId: department._id, + agent: agent.credentials, + }); + const { room: localOpenRoom2 } = await startANewLivechatRoomAndTakeIt({ + departmentId: department2._id, + agent: agent2.credentials, + }); + room1 = localOpenRoom; + room2 = localOpenRoom2; + }); + before(async () => { + await restorePermissionToRoles('view-livechat-rooms'); + }); + after(async () => { + await Promise.all([ + deleteUser(user), + deleteUser(agent.user), + deleteUnit(unit), + deleteDepartment(department._id), + deleteUnit(unit2), + deleteDepartment(department2._id), + deleteUser(agent2.user), + ]); + }); + + it('should show the room to the agent', async () => { + const { body } = await request.get(api('livechat/rooms')).query({ 'agents[]': agent.user._id }).set(agent.credentials).expect(200); + expect(body.rooms.length).to.be.equal(1); + expect(body.rooms[0]._id).to.be.equal(room1._id); + }); + it('should show the closed room to the agent', async () => { + await closeOmnichannelRoom(room1._id); + const { body } = await request + .get(api('livechat/rooms')) + .query({ 'agents[]': agent.user._id, 'open': false }) + .set(agent.credentials) + .expect(200); + expect(body.rooms.length).to.be.equal(1); + expect(body.rooms[0]._id).to.be.equal(room1._id); + }); + it('should not show the agent a room taken by other agent', async () => { + const { body } = await request + .get(api('livechat/rooms')) + .query({ 'agents[]': agent.user._id, 'open': true }) + .set(agent.credentials) + .expect(200); + expect(body.rooms.length).to.be.equal(0); + }); + it('should not allow the agent to filter by another agent', async () => { + await request.get(api('livechat/rooms')).query({ 'agents[]': agent.user._id, 'open': true }).set(agent2.credentials).expect(403); + }); + it('should allow the agent to filter its own closed room', async () => { + await closeOmnichannelRoom(room2._id); + const { body } = await request + .get(api('livechat/rooms')) + .query({ 'agents[]': agent2.user._id, 'open': false }) + .set(agent2.credentials) + .expect(200); expect(body.rooms.length).to.be.equal(1); expect(body.rooms[0]._id).to.be.equal(room2._id); }); + it('should allow monitor to see rooms from his units', async () => { + const { body } = await request.get(api('livechat/rooms')).set(userCreds).query({ 'units[]': unit._id }).expect(200); + expect(body.rooms.find((r: IOmnichannelRoom) => r._id === room1._id)).to.be.not.undefined; + expect(body.rooms.find((r: IOmnichannelRoom) => r._id === room2._id)).to.be.undefined; + }); + it('should show the monitor both rooms when no unit filter is applied', async () => { + const { body } = await request.get(api('livechat/rooms')).set(userCreds).expect(200); + + expect(body.rooms.some((room: IOmnichannelRoom) => room._id === room1._id)).to.be.true; + expect(body.rooms.some((room: IOmnichannelRoom) => room._id === room2._id)).to.be.true; + }); }); }); diff --git a/apps/meteor/tests/end-to-end/api/livechat/21-reports.ts b/apps/meteor/tests/end-to-end/api/livechat/21-reports.ts index ead15a3bfef..7f006ba8c2f 100644 --- a/apps/meteor/tests/end-to-end/api/livechat/21-reports.ts +++ b/apps/meteor/tests/end-to-end/api/livechat/21-reports.ts @@ -1,5 +1,5 @@ import type { Credentials } from '@rocket.chat/api-client'; -import type { IUser } from '@rocket.chat/core-typings'; +import type { IUser, IOmnichannelRoom } from '@rocket.chat/core-typings'; import { expect } from 'chai'; import { after, before, describe, it } from 'mocha'; @@ -148,7 +148,7 @@ import { IS_EE } from '../../../e2e/config/constants'; expect(body.total).to.be.equal(0); expect(body.success).to.be.true; }); - it('should return empty set for a monitor with no units', async () => { + it('should return only public rooms for a monitor with no units', async () => { const oneHourAgo = new Date(Date.now() - 60 * 60 * 1000).toISOString(); const now = new Date().toISOString(); const { body } = await request @@ -158,8 +158,7 @@ import { IS_EE } from '../../../e2e/config/constants'; .expect(200); expect(body).to.have.property('data').and.to.be.an('array'); - expect(body.data).to.have.lengthOf(0); - expect(body.success).to.be.true; + expect(body.data.every((room: IOmnichannelRoom) => !room.departmentAncestors && !room.departmentId)).to.be.true; }); it('should return only the data from the unit the monitor belongs to', async () => { const oneHourAgo = new Date(Date.now() - 60 * 60 * 1000).toISOString(); @@ -258,7 +257,7 @@ import { IS_EE } from '../../../e2e/config/constants'; expect(body.data).to.have.lengthOf(0); expect(body.success).to.be.true; }); - it('should return empty set for a monitor with no units', async () => { + it('should return public rooms for a monitor with no units', async () => { const oneHourAgo = new Date(Date.now() - 60 * 60 * 1000).toISOString(); const now = new Date().toISOString(); const { body } = await request @@ -268,8 +267,7 @@ import { IS_EE } from '../../../e2e/config/constants'; .expect(200); expect(body).to.have.property('data').and.to.be.an('array'); - expect(body.data).to.have.lengthOf(0); - expect(body.success).to.be.true; + expect(body.data.every((room: IOmnichannelRoom) => !room.departmentAncestors && !room.departmentId)).to.be.true; }); it('should return the proper data when login as a manager', async () => { const oneHourAgo = new Date(Date.now() - 60 * 60 * 1000).toISOString(); @@ -360,7 +358,7 @@ import { IS_EE } from '../../../e2e/config/constants'; expect(body.data).to.have.lengthOf(0); expect(body.success).to.be.true; }); - it('should return empty set for a monitor with no units', async () => { + it('should return public rooms for a monitor with no units', async () => { const oneHourAgo = new Date(Date.now() - 60 * 60 * 1000).toISOString(); const now = new Date().toISOString(); const { body } = await request @@ -370,8 +368,7 @@ import { IS_EE } from '../../../e2e/config/constants'; .expect(200); expect(body).to.have.property('data').and.to.be.an('array'); - expect(body.data).to.have.lengthOf(0); - expect(body.success).to.be.true; + expect(body.data.every((room: IOmnichannelRoom) => !room.departmentAncestors && !room.departmentId)).to.be.true; }); it('should return the proper data when login as a manager', async () => { const oneHourAgo = new Date(Date.now() - 60 * 60 * 1000).toISOString(); @@ -454,7 +451,7 @@ import { IS_EE } from '../../../e2e/config/constants'; expect(body.data).to.have.lengthOf(0); expect(body.success).to.be.true; }); - it('should return empty set for a monitor with no units', async () => { + it('should return public rooms for a monitor with no units', async () => { const oneHourAgo = new Date(Date.now() - 60 * 60 * 1000).toISOString(); const now = new Date().toISOString(); const { body } = await request @@ -464,8 +461,7 @@ import { IS_EE } from '../../../e2e/config/constants'; .expect(200); expect(body).to.have.property('data').and.to.be.an('array'); - expect(body.data).to.have.lengthOf(0); - expect(body.success).to.be.true; + expect(body.data.every((room: IOmnichannelRoom) => !room.departmentAncestors && !room.departmentId)).to.be.true; }); it('should return the proper data when login as a manager', async () => { const oneHourAgo = new Date(Date.now() - 60 * 60 * 1000).toISOString(); @@ -547,7 +543,7 @@ import { IS_EE } from '../../../e2e/config/constants'; expect(body).to.have.property('data').and.to.be.an('array'); expect(body.data).to.have.lengthOf(0); }); - it('should return empty set for a monitor with no units', async () => { + it('should return public rooms for a monitor with no units', async () => { const oneHourAgo = new Date(Date.now() - 60 * 60 * 1000).toISOString(); const now = new Date().toISOString(); const { body } = await request @@ -557,8 +553,7 @@ import { IS_EE } from '../../../e2e/config/constants'; .expect(200); expect(body).to.have.property('data').and.to.be.an('array'); - expect(body.data).to.have.lengthOf(0); - expect(body.success).to.be.true; + expect(body.data.every((room: IOmnichannelRoom) => !room.departmentAncestors && !room.departmentId)).to.be.true; }); it('should return the proper data when login as a manager', async () => { const oneHourAgo = new Date(Date.now() - 60 * 60 * 1000).toISOString();