diff --git a/src/modules/call/call.controller.spec.ts b/src/modules/call/call.controller.spec.ts index fcb4561..3c1ac66 100644 --- a/src/modules/call/call.controller.spec.ts +++ b/src/modules/call/call.controller.spec.ts @@ -316,11 +316,10 @@ describe('Testing Call Endpoints', () => { describe('Leaving a call', () => { it('When leaving a call for authenticated user, then it should leave successfully', async () => { callUseCase.leaveCall.mockResolvedValue(); + const leaveCallDto = new LeaveCallDto(); + leaveCallDto.userId = mockUserPayload.uuid; - const result = await callController.leaveCall( - mockRoomId, - mockUserPayload, - ); + const result = await callController.leaveCall(mockRoomId, leaveCallDto); expect(result).toBeUndefined(); expect(callUseCase.leaveCall).toHaveBeenCalledWith( @@ -336,7 +335,7 @@ describe('Testing Call Endpoints', () => { callUseCase.leaveCall.mockResolvedValue(); - await callController.leaveCall(mockRoomId, null, leaveCallDto); + await callController.leaveCall(mockRoomId, leaveCallDto); expect(callUseCase.leaveCall).toHaveBeenCalledWith( mockRoomId, @@ -350,16 +349,11 @@ describe('Testing Call Endpoints', () => { callUseCase.leaveCall.mockResolvedValue(); - const userToken = createMockUserToken(); - await callController.leaveCall( - mockRoomId, - userToken.payload, - leaveCallDto, - ); + await callController.leaveCall(mockRoomId, leaveCallDto); expect(callUseCase.leaveCall).toHaveBeenCalledWith( mockRoomId, - userToken.payload.uuid, + 'anonymous-user-id', ); }); @@ -367,20 +361,22 @@ describe('Testing Call Endpoints', () => { const emptyDto = new LeaveCallDto(); callUseCase.leaveCall.mockResolvedValue(); - await callController.leaveCall(mockRoomId, null, emptyDto); - - expect(callUseCase.leaveCall).toHaveBeenCalledWith(mockRoomId, undefined); + await expect( + callController.leaveCall(mockRoomId, emptyDto), + ).rejects.toThrow(BadRequestException); }); it('When room is not found, then it should propagate NotFoundException', async () => { - const userToken = createMockUserToken(); + const anonymousUserId = 'anonymous-user-id'; + const leaveCallDto = new LeaveCallDto(); + leaveCallDto.userId = anonymousUserId; callUseCase.leaveCall.mockRejectedValue( new NotFoundException('Specified room not found'), ); await expect( - callController.leaveCall(mockRoomId, userToken.payload), + callController.leaveCall(mockRoomId, leaveCallDto), ).rejects.toThrow(NotFoundException); }); diff --git a/src/modules/call/call.controller.ts b/src/modules/call/call.controller.ts index 4b44e90..32c77e6 100644 --- a/src/modules/call/call.controller.ts +++ b/src/modules/call/call.controller.ts @@ -173,12 +173,14 @@ export class CallController { }) @ApiNotFoundResponse({ description: 'Call/Room not found' }) @ApiInternalServerErrorResponse({ description: 'Internal server error' }) - leaveCall( + async leaveCall( @Param('id') roomId: string, - @User() user: UserTokenData['payload'], @Body() leaveCallDto?: LeaveCallDto, ): Promise { - const { uuid } = user || {}; - return this.callUseCase.leaveCall(roomId, uuid || leaveCallDto?.userId); + if (!leaveCallDto?.userId) { + this.logger.warn('Attempt to leave call without user ID:', leaveCallDto); + throw new BadRequestException('The user id is needed to leave the call'); + } + return this.callUseCase.leaveCall(roomId, leaveCallDto.userId); } } diff --git a/src/modules/call/call.usecase.spec.ts b/src/modules/call/call.usecase.spec.ts index cf31b28..ea3c821 100644 --- a/src/modules/call/call.usecase.spec.ts +++ b/src/modules/call/call.usecase.spec.ts @@ -246,7 +246,7 @@ describe('CallUseCase', () => { expect(result).toEqual({ token: 'test-jwt-token', room: roomId, - userId, + userId: autoGeneratedUUID, appId: 'jitsi-app-id', }); }); @@ -418,7 +418,7 @@ describe('CallUseCase', () => { expect(result).toEqual({ token: 'test-jwt-token', room: roomId, - userId, + userId: autoGeneratedUUID, appId: 'jitsi-app-id', }); }); @@ -490,7 +490,7 @@ describe('CallUseCase', () => { anonymous: false, }, ); - expect(result.userId).toEqual(roomUserMock.userId); + expect(result.userId).toEqual(roomUserMock.id); }); }); diff --git a/src/modules/call/call.usecase.ts b/src/modules/call/call.usecase.ts index 54d88a7..5cc46b8 100644 --- a/src/modules/call/call.usecase.ts +++ b/src/modules/call/call.usecase.ts @@ -182,7 +182,7 @@ export class CallUseCase { return { token: callTokenData, room: roomId, - userId: roomUser.userId, + userId: roomUser.id, appId: this.configService.get('jitsi.appId'), }; } diff --git a/src/modules/call/infrastructure/room-user.repository.spec.ts b/src/modules/call/infrastructure/room-user.repository.spec.ts index 7d742a5..55974e0 100644 --- a/src/modules/call/infrastructure/room-user.repository.spec.ts +++ b/src/modules/call/infrastructure/room-user.repository.spec.ts @@ -225,7 +225,7 @@ describe('SequelizeRoomUserRepository', () => { expect(deleteRoomUserSpy).toHaveBeenCalledWith({ where: { - userId: 'test-user-id', + id: 'test-user-id', roomId: 'test-room-id', }, }); diff --git a/src/modules/call/infrastructure/room-user.repository.ts b/src/modules/call/infrastructure/room-user.repository.ts index b7b90fe..d0a2653 100644 --- a/src/modules/call/infrastructure/room-user.repository.ts +++ b/src/modules/call/infrastructure/room-user.repository.ts @@ -82,7 +82,7 @@ export class SequelizeRoomUserRepository { } async deleteByUserIdAndRoomId(userId: string, roomId: string): Promise { - await this.roomUserModel.destroy({ where: { userId, roomId } }); + await this.roomUserModel.destroy({ where: { id: userId, roomId } }); } async findByParticipantIdAndRoomId( diff --git a/src/modules/call/services/call.service.spec.ts b/src/modules/call/services/call.service.spec.ts index fcbec6d..6e48b0f 100644 --- a/src/modules/call/services/call.service.spec.ts +++ b/src/modules/call/services/call.service.spec.ts @@ -86,4 +86,21 @@ describe('Call service', () => { expect(paymentService.getUserTier).toHaveBeenCalledWith(userPayload.uuid); }); + + it('When the user has meet enabled and isDevelopment, then a call should be created without calling payment service', async () => { + jest.spyOn(configService, 'get').mockReturnValueOnce(false); + const userPayload = mockUserPayload; + (uuid.v4 as jest.Mock).mockReturnValue('test-room-id'); + (jwt.sign as jest.Mock).mockReturnValue('test-jitsi-token'); + + const result = await callService.createCall(userPayload); + + expect(result).toEqual({ + appId: 'jitsi-app-id', + room: 'test-room-id', + paxPerCall: 10, + }); + + expect(paymentService.getUserTier).not.toHaveBeenCalled(); + }); }); diff --git a/src/modules/call/services/call.service.ts b/src/modules/call/services/call.service.ts index 3184e9f..ce7c368 100644 --- a/src/modules/call/services/call.service.ts +++ b/src/modules/call/services/call.service.ts @@ -48,6 +48,13 @@ export class CallService { private async getMeetFeatureConfigForUser( userUuid: string, ): Promise { + const isProduction = this.configService.get('isProduction'); + if (!isProduction) { + return { + enabled: true, + paxPerCall: 10, + }; + } const userFeatures = await this.paymentService .getUserTier(userUuid) .catch((err) => { diff --git a/src/modules/call/services/room.service.spec.ts b/src/modules/call/services/room.service.spec.ts index 94c688e..e05e3b7 100644 --- a/src/modules/call/services/room.service.spec.ts +++ b/src/modules/call/services/room.service.spec.ts @@ -13,6 +13,8 @@ import { createMockRoom, } from '../fixtures'; import { v4 } from 'uuid'; +import { RoomUser } from '../domain/room-user.domain'; +import { Sequelize } from 'sequelize-typescript'; describe('Room Service', () => { let roomService: RoomService; @@ -21,6 +23,7 @@ describe('Room Service', () => { let userRepository: UserRepository; let avatarService: AvatarService; let moduleRef: TestingModule; + let sequelize: Sequelize; beforeEach(async () => { moduleRef = await Test.createTestingModule({ @@ -38,6 +41,7 @@ describe('Room Service', () => { ); userRepository = moduleRef.get(UserRepository); avatarService = moduleRef.get(AvatarService); + sequelize = moduleRef.get(Sequelize); }); describe('Creating a room', () => { @@ -442,4 +446,53 @@ describe('Room Service', () => { expect(roomRepository.delete).toHaveBeenCalledWith(mockRoom.id); }); }); + + describe('handleUserJoined', () => { + it('When new non-anonymous user joins, then it should create new entry in the data base', async () => { + const userUuid = v4(); + const roomId = v4(); + const userData = new RoomUser({ + name: 'John', + lastName: 'Doe', + anonymous: false, + id: v4(), + roomId, + userId: userUuid, + }); + jest + .spyOn(sequelize, 'transaction') + .mockImplementation(async (cb: any) => { + return cb({} as any); + }); + + jest + .spyOn(roomUserRepository, 'findByUserIdAndRoomId') + .mockResolvedValueOnce(null); + jest.spyOn(roomUserRepository, 'create').mockResolvedValueOnce(userData); + + const { roomUser, oldParticipantId } = await roomService.handleUserJoined(userUuid, roomId, { + name: 'John', + lastName: 'Doe', + anonymous: false, + }); + + expect(oldParticipantId).toBeUndefined(); + expect(roomUser).toEqual(userData); + expect(roomUserRepository.findByUserIdAndRoomId).toHaveBeenCalledWith( + userUuid, + roomId, + { transaction: {}, lock: true }, + ); + expect(roomUserRepository.create).toHaveBeenCalledWith( + { + userId: userUuid, + roomId: roomId, + name: 'John', + lastName: 'Doe', + anonymous: false, + }, + {}, + ); + }); + }); }); diff --git a/src/modules/call/services/room.service.ts b/src/modules/call/services/room.service.ts index 4a819bb..abeec66 100644 --- a/src/modules/call/services/room.service.ts +++ b/src/modules/call/services/room.service.ts @@ -157,33 +157,20 @@ export class RoomService { { transaction, lock: true }, ); - if (existingUser) { - if (existingUser.participantId) { - oldParticipantId = existingUser.participantId; - } - - await this.roomUserRepository.update( - existingUser.id, - { - participantId: null, - joinedAt: null, - }, - transaction, - ); - - roomUser = existingUser; - } else { - roomUser = await this.roomUserRepository.create( - { - roomId, - userId, - name: userData.name, - lastName: userData.lastName, - anonymous: Boolean(userData.anonymous), - }, - transaction, - ); + if (existingUser?.participantId) { + oldParticipantId = existingUser.participantId; } + + roomUser = await this.roomUserRepository.create( + { + roomId, + userId, + name: userData.name, + lastName: userData.lastName, + anonymous: Boolean(userData.anonymous), + }, + transaction, + ); }); return { roomUser, oldParticipantId };