diff --git a/.mocharc.js b/.mocharc.js index 5f18ee8009b..6d7f3c02aa4 100644 --- a/.mocharc.js +++ b/.mocharc.js @@ -23,5 +23,5 @@ Object.assign( module.exports = { ...base, // see https://github.com/mochajs/mocha/issues/3916 exit: true, - spec: ['app/**/*.spec.ts', 'app/**/*.tests.js', 'app/**/*.tests.ts', 'server/**/*.tests.ts'], + spec: ['app/**/*.spec.ts', 'app/**/*.tests.js', 'app/**/*.tests.ts', 'server/**/*.tests.ts', 'ee/**/*.tests.ts'], }; diff --git a/ee/server/services/presence/actions/updateUserPresence.ts b/ee/server/services/presence/actions/updateUserPresence.ts index c3a18dd9193..77e3bcb633c 100755 --- a/ee/server/services/presence/actions/updateUserPresence.ts +++ b/ee/server/services/presence/actions/updateUserPresence.ts @@ -26,8 +26,11 @@ export async function updateUserPresence(uid: string): Promise { } const userSessions = (await UserSession.findOne(query)) || { connections: [] }; + const { statusDefault = UserStatus.OFFLINE } = user; + const { status, statusConnection } = processPresenceAndStatus(userSessions.connections, statusDefault); + const result = await User.updateOne(query, { $set: { status, statusConnection }, }); diff --git a/ee/server/services/presence/lib/processConnectionStatus.tests.ts b/ee/server/services/presence/lib/processConnectionStatus.tests.ts new file mode 100644 index 00000000000..9c51a2fce5f --- /dev/null +++ b/ee/server/services/presence/lib/processConnectionStatus.tests.ts @@ -0,0 +1,241 @@ +/* eslint-disable @typescript-eslint/camelcase */ +import { expect } from 'chai'; + +import { UserStatus } from '../../../../../definition/UserStatus'; +import { processConnectionStatus, processStatus, processPresenceAndStatus } from './processConnectionStatus'; + +describe('Presence micro service', () => { + it('should return connection as online when there is a connection online', () => { + expect(processConnectionStatus(UserStatus.OFFLINE, UserStatus.ONLINE)).to.equal(UserStatus.ONLINE); + expect(processConnectionStatus(UserStatus.ONLINE, UserStatus.ONLINE)).to.equal(UserStatus.ONLINE); + expect(processConnectionStatus(UserStatus.BUSY, UserStatus.ONLINE)).to.equal(UserStatus.ONLINE); + expect(processConnectionStatus(UserStatus.AWAY, UserStatus.ONLINE)).to.equal(UserStatus.ONLINE); + }); + + it('should return the connections status if the other connection is offline', () => { + expect(processConnectionStatus(UserStatus.OFFLINE, UserStatus.OFFLINE)).to.equal(UserStatus.OFFLINE); + expect(processConnectionStatus(UserStatus.ONLINE, UserStatus.OFFLINE)).to.equal(UserStatus.ONLINE); + expect(processConnectionStatus(UserStatus.AWAY, UserStatus.OFFLINE)).to.equal(UserStatus.AWAY); + }); + + it('should return the connection status when the default status is online', () => { + expect(processStatus(UserStatus.ONLINE, UserStatus.ONLINE)).to.equal(UserStatus.ONLINE); + expect(processStatus(UserStatus.AWAY, UserStatus.ONLINE)).to.equal(UserStatus.AWAY); + expect(processStatus(UserStatus.OFFLINE, UserStatus.ONLINE)).to.equal(UserStatus.OFFLINE); + }); + + it('should return status busy when the default status is busy', () => { + expect(processStatus(UserStatus.ONLINE, UserStatus.BUSY)).to.equal(UserStatus.BUSY); + expect(processStatus(UserStatus.AWAY, UserStatus.BUSY)).to.equal(UserStatus.BUSY); + expect(processStatus(UserStatus.OFFLINE, UserStatus.BUSY)).to.equal(UserStatus.OFFLINE); + }); + + it('should return status away when the default status is away', () => { + expect(processStatus(UserStatus.ONLINE, UserStatus.AWAY)).to.equal(UserStatus.AWAY); + expect(processStatus(UserStatus.AWAY, UserStatus.AWAY)).to.equal(UserStatus.AWAY); + expect(processStatus(UserStatus.OFFLINE, UserStatus.AWAY)).to.equal(UserStatus.OFFLINE); + }); + + it('should return status offline when the default status is offline', () => { + expect(processStatus(UserStatus.ONLINE, UserStatus.OFFLINE)).to.equal(UserStatus.OFFLINE); + expect(processStatus(UserStatus.AWAY, UserStatus.OFFLINE)).to.equal(UserStatus.OFFLINE); + expect(processStatus(UserStatus.OFFLINE, UserStatus.OFFLINE)).to.equal(UserStatus.OFFLINE); + }); + + it('should return correct status and statusConnection when connected once', () => { + expect( + processPresenceAndStatus( + [ + { + id: 'random', + instanceId: 'random', + status: UserStatus.ONLINE, + _createdAt: new Date(), + _updatedAt: new Date(), + }, + ], + UserStatus.ONLINE, + ), + ).to.deep.equal({ status: UserStatus.ONLINE, statusConnection: UserStatus.ONLINE }); + + expect( + processPresenceAndStatus( + [ + { + id: 'random', + instanceId: 'random', + status: UserStatus.AWAY, + _createdAt: new Date(), + _updatedAt: new Date(), + }, + ], + UserStatus.ONLINE, + ), + ).to.deep.equal({ status: UserStatus.AWAY, statusConnection: UserStatus.AWAY }); + + expect( + processPresenceAndStatus( + [ + { + id: 'random', + instanceId: 'random', + status: UserStatus.ONLINE, + _createdAt: new Date(), + _updatedAt: new Date(), + }, + ], + UserStatus.BUSY, + ), + ).to.deep.equal({ status: UserStatus.BUSY, statusConnection: UserStatus.ONLINE }); + + expect( + processPresenceAndStatus( + [ + { + id: 'random', + instanceId: 'random', + status: UserStatus.ONLINE, + _createdAt: new Date(), + _updatedAt: new Date(), + }, + ], + UserStatus.AWAY, + ), + ).to.deep.equal({ status: UserStatus.AWAY, statusConnection: UserStatus.ONLINE }); + + expect( + processPresenceAndStatus( + [ + { + id: 'random', + instanceId: 'random', + status: UserStatus.AWAY, + _createdAt: new Date(), + _updatedAt: new Date(), + }, + ], + UserStatus.BUSY, + ), + ).to.deep.equal({ status: UserStatus.BUSY, statusConnection: UserStatus.AWAY }); + + expect( + processPresenceAndStatus( + [ + { + id: 'random', + instanceId: 'random', + status: UserStatus.ONLINE, + _createdAt: new Date(), + _updatedAt: new Date(), + }, + ], + UserStatus.OFFLINE, + ), + ).to.deep.equal({ status: UserStatus.OFFLINE, statusConnection: UserStatus.ONLINE }); + + expect( + processPresenceAndStatus( + [ + { + id: 'random', + instanceId: 'random', + status: UserStatus.AWAY, + _createdAt: new Date(), + _updatedAt: new Date(), + }, + ], + UserStatus.OFFLINE, + ), + ).to.deep.equal({ status: UserStatus.OFFLINE, statusConnection: UserStatus.AWAY }); + }); + + it('should return correct status and statusConnection when connected twice', () => { + expect( + processPresenceAndStatus( + [ + { + id: 'random', + instanceId: 'random', + status: UserStatus.ONLINE, + _createdAt: new Date(), + _updatedAt: new Date(), + }, + { + id: 'random', + instanceId: 'random', + status: UserStatus.AWAY, + _createdAt: new Date(), + _updatedAt: new Date(), + }, + ], + UserStatus.ONLINE, + ), + ).to.deep.equal({ status: UserStatus.ONLINE, statusConnection: UserStatus.ONLINE }); + + expect( + processPresenceAndStatus( + [ + { + id: 'random', + instanceId: 'random', + status: UserStatus.AWAY, + _createdAt: new Date(), + _updatedAt: new Date(), + }, + { + id: 'random', + instanceId: 'random', + status: UserStatus.ONLINE, + _createdAt: new Date(), + _updatedAt: new Date(), + }, + ], + UserStatus.ONLINE, + ), + ).to.deep.equal({ status: UserStatus.ONLINE, statusConnection: UserStatus.ONLINE }); + + expect( + processPresenceAndStatus( + [ + { + id: 'random', + instanceId: 'random', + status: UserStatus.AWAY, + _createdAt: new Date(), + _updatedAt: new Date(), + }, + { + id: 'random', + instanceId: 'random', + status: UserStatus.AWAY, + _createdAt: new Date(), + _updatedAt: new Date(), + }, + ], + UserStatus.ONLINE, + ), + ).to.deep.equal({ status: UserStatus.AWAY, statusConnection: UserStatus.AWAY }); + }); + + it('should return correct status and statusConnection when not connected', () => { + expect(processPresenceAndStatus([], UserStatus.ONLINE)).to.deep.equal({ + status: UserStatus.OFFLINE, + statusConnection: UserStatus.OFFLINE, + }); + + expect(processPresenceAndStatus([], UserStatus.BUSY)).to.deep.equal({ + status: UserStatus.OFFLINE, + statusConnection: UserStatus.OFFLINE, + }); + + expect(processPresenceAndStatus([], UserStatus.AWAY)).to.deep.equal({ + status: UserStatus.OFFLINE, + statusConnection: UserStatus.OFFLINE, + }); + + expect(processPresenceAndStatus([], UserStatus.OFFLINE)).to.deep.equal({ + status: UserStatus.OFFLINE, + statusConnection: UserStatus.OFFLINE, + }); + }); +}); diff --git a/ee/server/services/presence/lib/processConnectionStatus.ts b/ee/server/services/presence/lib/processConnectionStatus.ts index 87e92296311..5f29c63e601 100644 --- a/ee/server/services/presence/lib/processConnectionStatus.ts +++ b/ee/server/services/presence/lib/processConnectionStatus.ts @@ -1,8 +1,11 @@ import { IUserSessionConnection } from '../../../../../definition/IUserSession'; import { UserStatus } from '../../../../../definition/UserStatus'; +/** + * Defines new connection status compared to a previous connection status + */ export const processConnectionStatus = (current: UserStatus, status: UserStatus): UserStatus => { - if (status === UserStatus.ONLINE) { + if (current === UserStatus.ONLINE) { return UserStatus.ONLINE; } if (status !== UserStatus.OFFLINE) { @@ -11,15 +14,32 @@ export const processConnectionStatus = (current: UserStatus, status: UserStatus) return current; }; -export const processStatus = (statusConnection: UserStatus, statusDefault: UserStatus): UserStatus => - statusConnection !== UserStatus.OFFLINE ? statusDefault : statusConnection; +/** + * Defines user's status based on presence and connection status + */ +export const processStatus = (statusConnection: UserStatus, statusDefault: UserStatus): UserStatus => { + if (statusConnection === UserStatus.OFFLINE) { + return statusConnection; + } + if (statusDefault === UserStatus.ONLINE) { + return statusConnection; + } + + return statusDefault; +}; + +/** + * Defines user's status and connection status based on user's connections and default status + */ export const processPresenceAndStatus = ( userSessions: IUserSessionConnection[] = [], statusDefault = UserStatus.ONLINE, ): { status: UserStatus; statusConnection: UserStatus } => { const statusConnection = userSessions.map((s) => s.status).reduce(processConnectionStatus, UserStatus.OFFLINE); + const status = processStatus(statusConnection, statusDefault); + return { status, statusConnection,