From 42db1b7bb69dfc6d98ac1440988160fdf258ad57 Mon Sep 17 00:00:00 2001 From: juancarmore Date: Wed, 6 Aug 2025 13:33:56 +0200 Subject: [PATCH] backend: update recording handling to include roomName and adjust related logic --- .../src/controllers/recording.controller.ts | 1 - .../helpers/ov-components-adapter.helper.ts | 3 +- backend/src/helpers/recording.helper.ts | 16 +++-- .../src/services/livekit-webhook.service.ts | 2 +- backend/src/services/recording.service.ts | 6 +- backend/tests/helpers/assertion-helpers.ts | 18 ++++- backend/tests/helpers/test-scenarios.ts | 4 +- .../recordings/bulk-delete-recording.test.ts | 3 +- .../api/recordings/delete-recording.test.ts | 2 +- .../api/recordings/get-recording.test.ts | 17 ++++- .../api/recordings/get-recordings.test.ts | 2 + .../api/recordings/race-conditions.test.ts | 66 +++++++++++-------- .../api/recordings/start-recording.test.ts | 38 +++++++---- .../api/recordings/stop-recording.test.ts | 16 ++++- .../api/security/recording-security.test.ts | 2 +- typings/src/recording.model.ts | 1 + 16 files changed, 132 insertions(+), 65 deletions(-) diff --git a/backend/src/controllers/recording.controller.ts b/backend/src/controllers/recording.controller.ts index 1673e2e..bd56bfd 100644 --- a/backend/src/controllers/recording.controller.ts +++ b/backend/src/controllers/recording.controller.ts @@ -141,7 +141,6 @@ export const deleteRecording = async (req: Request, res: Response) => { logger.info(`Deleting recording '${recordingId}'`); try { - // TODO: Check role to determine if the request is from an admin or a participant await recordingService.deleteRecording(recordingId); return res.status(204).send(); } catch (error) { diff --git a/backend/src/helpers/ov-components-adapter.helper.ts b/backend/src/helpers/ov-components-adapter.helper.ts index 339f5b8..d1feb80 100644 --- a/backend/src/helpers/ov-components-adapter.helper.ts +++ b/backend/src/helpers/ov-components-adapter.helper.ts @@ -1,6 +1,5 @@ import { MeetRecordingInfo, MeetRecordingStatus } from '@typings-ce'; -import { EgressInfo, EgressStatus, SendDataOptions } from 'livekit-server-sdk'; -import { RecordingHelper } from './recording.helper.js'; +import { SendDataOptions } from 'livekit-server-sdk'; const enum OpenViduComponentsDataTopic { CHAT = 'chat', diff --git a/backend/src/helpers/recording.helper.ts b/backend/src/helpers/recording.helper.ts index b35cac5..23b8be3 100644 --- a/backend/src/helpers/recording.helper.ts +++ b/backend/src/helpers/recording.helper.ts @@ -2,13 +2,15 @@ import { EgressStatus } from '@livekit/protocol'; import { MeetRecordingInfo, MeetRecordingStatus } from '@typings-ce'; import { EgressInfo } from 'livekit-server-sdk'; import { uid as secureUid } from 'uid/secure'; +import { container } from '../config/index.js'; +import { RoomService } from '../services/index.js'; export class RecordingHelper { private constructor() { // Prevent instantiation of this utility class } - static toRecordingInfo(egressInfo: EgressInfo): MeetRecordingInfo { + static async toRecordingInfo(egressInfo: EgressInfo): Promise { const status = RecordingHelper.extractOpenViduStatus(egressInfo.status); const size = RecordingHelper.extractSize(egressInfo); // const outputMode = RecordingHelper.extractOutputMode(egressInfo); @@ -17,10 +19,15 @@ export class RecordingHelper { const endDateMs = RecordingHelper.extractEndDate(egressInfo); const filename = RecordingHelper.extractFilename(egressInfo); const uid = RecordingHelper.extractUidFromFilename(filename); - const { egressId, roomName, errorCode, error, details } = egressInfo; + const { egressId, roomName: roomId, errorCode, error, details } = egressInfo; + + const roomService = container.get(RoomService); + const { roomName } = await roomService.getMeetRoom(roomId); + return { - recordingId: `${roomName}--${egressId}--${uid}`, - roomId: roomName, + recordingId: `${roomId}--${egressId}--${uid}`, + roomId, + roomName, // outputMode, status, filename, @@ -186,7 +193,6 @@ export class RecordingHelper { return size !== 0 ? size : undefined; } - /** * Builds the secrets for public and private access to recordings. * @returns An object containing public and private access secrets. diff --git a/backend/src/services/livekit-webhook.service.ts b/backend/src/services/livekit-webhook.service.ts index 15828b8..ad25d9f 100644 --- a/backend/src/services/livekit-webhook.service.ts +++ b/backend/src/services/livekit-webhook.service.ts @@ -234,7 +234,7 @@ export class LivekitWebhookService { this.logger.debug(`Processing recording_${webhookAction} webhook for egress: ${egressInfo.egressId}.`); try { - const recordingInfo: MeetRecordingInfo = RecordingHelper.toRecordingInfo(egressInfo); + const recordingInfo: MeetRecordingInfo = await RecordingHelper.toRecordingInfo(egressInfo); const { roomId, recordingId, status } = recordingInfo; this.logger.debug(`Recording '${recordingId}' in room '${roomId}' status: '${status}'`); diff --git a/backend/src/services/recording.service.ts b/backend/src/services/recording.service.ts index 5457d9d..34ab814 100644 --- a/backend/src/services/recording.service.ts +++ b/backend/src/services/recording.service.ts @@ -77,6 +77,7 @@ export class RecordingService { await this.frontendEventService.sendRecordingSignalToOpenViduComponents(roomId, { recordingId: '', roomId, + roomName: roomId, status: MeetRecordingStatus.STARTING }); @@ -121,7 +122,7 @@ export class RecordingService { throw errorRecordingStartTimeout(roomId); } - const recordingInfo = RecordingHelper.toRecordingInfo(egressInfo); + const recordingInfo = await RecordingHelper.toRecordingInfo(egressInfo); recordingId = recordingInfo.recordingId; // If the recording is already active, we can resolve the promise immediately. @@ -199,7 +200,7 @@ export class RecordingService { const egressInfo = await this.livekitService.stopEgress(egressId); this.logger.info(`Recording stopped successfully for room '${roomId}'.`); - return RecordingHelper.toRecordingInfo(egressInfo); + return await RecordingHelper.toRecordingInfo(egressInfo); } catch (error) { this.logger.error(`Error stopping recording '${recordingId}': ${error}`); throw error; @@ -585,6 +586,7 @@ export class RecordingService { const recordingInfo: MeetRecordingInfo = { recordingId, roomId, + roomName: roomId, status: MeetRecordingStatus.FAILED, error: `No egress service was able to register a request. Check your CPU usage or if there's any Media Node with enough CPU. Remember that by default, composite recording uses 4 CPUs for each room.` }; diff --git a/backend/tests/helpers/assertion-helpers.ts b/backend/tests/helpers/assertion-helpers.ts index 9fd0f7e..86dc27b 100644 --- a/backend/tests/helpers/assertion-helpers.ts +++ b/backend/tests/helpers/assertion-helpers.ts @@ -154,13 +154,16 @@ export const expectValidRecording = ( recording: MeetRecordingInfo, recordingId: string, roomId: string, + roomName: string, status: MeetRecordingStatus ) => { expect(recording).toBeDefined(); expect(recording.recordingId).toBeDefined(); expect(recording.roomId).toBeDefined(); + expect(recording.roomName).toBeDefined(); expect(recording.recordingId).toBe(recordingId); expect(recording.roomId).toBe(roomId); + expect(recording.roomName).toBe(roomName); expect(recording.startDate).toBeDefined(); expect(recording.status).toBeDefined(); expect(recording.status).toBe(status); @@ -329,7 +332,7 @@ export const expectSuccessRecordingMediaResponse = ( } }; -export const expectValidStartRecordingResponse = (response: any, roomId: string) => { +export const expectValidStartRecordingResponse = (response: any, roomId: string, roomName: string) => { expect(response.status).toBe(201); expect(response.body).toHaveProperty('recordingId'); @@ -340,6 +343,7 @@ export const expectValidStartRecordingResponse = (response: any, roomId: string) expect(recordingId).toContain(roomId); expect(response.body).toHaveProperty('roomId', roomId); + expect(response.body).toHaveProperty('roomName', roomName); expect(response.body).toHaveProperty('startDate'); expect(response.body).toHaveProperty('status', 'ACTIVE'); expect(response.body).toHaveProperty('filename'); @@ -348,12 +352,18 @@ export const expectValidStartRecordingResponse = (response: any, roomId: string) expect(response.body).not.toHaveProperty('size'); }; -export const expectValidStopRecordingResponse = (response: any, recordingId: string, roomId: string) => { +export const expectValidStopRecordingResponse = ( + response: any, + recordingId: string, + roomId: string, + roomName: string +) => { expect(response.status).toBe(202); expect(response.body).toBeDefined(); expect(response.body).toHaveProperty('recordingId', recordingId); expect([MeetRecordingStatus.COMPLETE, MeetRecordingStatus.ENDING]).toContain(response.body.status); expect(response.body).toHaveProperty('roomId', roomId); + expect(response.body).toHaveProperty('roomName', roomName); expect(response.body).toHaveProperty('filename'); expect(response.body).toHaveProperty('startDate'); expect(response.body).toHaveProperty('duration', expect.any(Number)); @@ -365,6 +375,7 @@ export const expectValidGetRecordingResponse = ( response: any, recordingId: string, roomId: string, + roomName: string, status?: MeetRecordingStatus, maxSecDuration?: number ) => { @@ -372,7 +383,7 @@ export const expectValidGetRecordingResponse = ( expect(response.body).toBeDefined(); const body = response.body; - expect(body).toMatchObject({ recordingId, roomId }); + expect(body).toMatchObject({ recordingId, roomId, roomName }); const isRecFinished = status && @@ -384,6 +395,7 @@ export const expectValidGetRecordingResponse = ( expect.objectContaining({ recordingId: expect.stringMatching(new RegExp(`^${recordingId}$`)), roomId: expect.stringMatching(new RegExp(`^${roomId}$`)), + roomName: expect.stringMatching(new RegExp(`^${roomName}$`)), ...(isRecFinished ? { status: expect.any(String) } : {}), ...(isRecFinished ? { duration: expect.any(Number) } : {}), ...(isRecFinished ? { startDate: expect.any(Number) } : {}), diff --git a/backend/tests/helpers/test-scenarios.ts b/backend/tests/helpers/test-scenarios.ts index c3c645e..6996f1c 100644 --- a/backend/tests/helpers/test-scenarios.ts +++ b/backend/tests/helpers/test-scenarios.ts @@ -110,7 +110,7 @@ export const setupSingleRoomWithRecording = async ( ): Promise => { const roomData = await setupSingleRoom(true, 'TEST_ROOM'); const response = await startRecording(roomData.room.roomId, roomData.moderatorCookie); - expectValidStartRecordingResponse(response, roomData.room.roomId); + expectValidStartRecordingResponse(response, roomData.room.roomId, roomData.room.roomName); roomData.recordingId = response.body.recordingId; // Wait for the configured delay before stopping the recording @@ -155,7 +155,7 @@ export const setupMultiRecordingsTestContext = async ( // Send start recording request const response = await startRecording(roomData.room.roomId, roomData.moderatorCookie); - expectValidStartRecordingResponse(response, roomData.room.roomId); + expectValidStartRecordingResponse(response, roomData.room.roomId, roomData.room.roomName); // Store the recordingId in context roomData.recordingId = response.body.recordingId; diff --git a/backend/tests/integration/api/recordings/bulk-delete-recording.test.ts b/backend/tests/integration/api/recordings/bulk-delete-recording.test.ts index 6103f99..54a34b0 100644 --- a/backend/tests/integration/api/recordings/bulk-delete-recording.test.ts +++ b/backend/tests/integration/api/recordings/bulk-delete-recording.test.ts @@ -191,7 +191,7 @@ describe('Recording API Tests', () => { expect(roomMetadata!.publisherRoomUrl).toContain(room.roomId); const response = await startRecording(room.roomId, moderatorCookie); - expectValidStartRecordingResponse(response, room.roomId); + expectValidStartRecordingResponse(response, room.roomId, room.roomName); const secondRecordingId = response.body.recordingId; await stopRecording(secondRecordingId, moderatorCookie); @@ -207,6 +207,7 @@ describe('Recording API Tests', () => { // secondRecordingResponse, // secondRecordingId, // room.roomId, + // room.roomName, // MeetRecordingStatus.COMPLETE, // 3 // ); diff --git a/backend/tests/integration/api/recordings/delete-recording.test.ts b/backend/tests/integration/api/recordings/delete-recording.test.ts index 2c3e3d9..41c7623 100644 --- a/backend/tests/integration/api/recordings/delete-recording.test.ts +++ b/backend/tests/integration/api/recordings/delete-recording.test.ts @@ -79,7 +79,7 @@ describe('Recording API Tests', () => { // Generate a new recording const response = await startRecording(room.roomId, moderatorCookie); console.log('Start recording response:', response.body); - expectValidStartRecordingResponse(response, room.roomId); + expectValidStartRecordingResponse(response, room.roomId, room.roomName); const secondRecordingId = response.body.recordingId; await stopRecording(secondRecordingId, moderatorCookie); diff --git a/backend/tests/integration/api/recordings/get-recording.test.ts b/backend/tests/integration/api/recordings/get-recording.test.ts index 0ea3c82..430d22a 100644 --- a/backend/tests/integration/api/recordings/get-recording.test.ts +++ b/backend/tests/integration/api/recordings/get-recording.test.ts @@ -35,7 +35,14 @@ describe('Recording API Tests', () => { it('should return 200 when recording exists', async () => { const response = await getRecording(recordingId); - expectValidGetRecordingResponse(response, recordingId, room.roomId, MeetRecordingStatus.COMPLETE, 1); + expectValidGetRecordingResponse( + response, + recordingId, + room.roomId, + room.roomName, + MeetRecordingStatus.COMPLETE, + 1 + ); }); it('should get an ACTIVE recording status', async () => { @@ -47,7 +54,13 @@ describe('Recording API Tests', () => { } = contextAux.getRoomByIndex(0)!; const response = await getRecording(recordingIdAux); - expectValidGetRecordingResponse(response, recordingIdAux, roomAux.roomId, MeetRecordingStatus.ACTIVE); + expectValidGetRecordingResponse( + response, + recordingIdAux, + roomAux.roomId, + roomAux.roomName, + MeetRecordingStatus.ACTIVE + ); await stopAllRecordings(moderatorCookieAux); }); diff --git a/backend/tests/integration/api/recordings/get-recordings.test.ts b/backend/tests/integration/api/recordings/get-recordings.test.ts index 244c772..057eadc 100644 --- a/backend/tests/integration/api/recordings/get-recordings.test.ts +++ b/backend/tests/integration/api/recordings/get-recordings.test.ts @@ -112,6 +112,7 @@ describe('Recordings API Tests', () => { recording, associatedRoom!.recordingId!, associatedRoom!.room.roomId, + associatedRoom!.room.roomName, MeetRecordingStatus.COMPLETE ); }); @@ -127,6 +128,7 @@ describe('Recordings API Tests', () => { recording, associatedRoom!.recordingId!, associatedRoom!.room.roomId, + associatedRoom!.room.roomName, MeetRecordingStatus.COMPLETE ); }); diff --git a/backend/tests/integration/api/recordings/race-conditions.test.ts b/backend/tests/integration/api/recordings/race-conditions.test.ts index d04c59b..dfbbbee 100644 --- a/backend/tests/integration/api/recordings/race-conditions.test.ts +++ b/backend/tests/integration/api/recordings/race-conditions.test.ts @@ -192,17 +192,17 @@ describe('Recording API Race Conditions Tests', () => { // Recording in different room should work normally const rec2 = await startRecording(room2.room.roomId, room2.moderatorCookie); expect(rec2.status).toBe(201); - expectValidStartRecordingResponse(rec2, room2.room.roomId); + expectValidStartRecordingResponse(rec2, room2.room.roomId, room2.room.roomName); let response = await stopRecording(rec2.body.recordingId!, room2.moderatorCookie); - expectValidStopRecordingResponse(response, rec2.body.recordingId!, room2.room.roomId); + expectValidStopRecordingResponse(response, rec2.body.recordingId!, room2.room.roomId, room2.room.roomName); // ✅ EXPECTED BEHAVIOR: After timeout cleanup, room1 should be available again const rec3 = await startRecording(room1.room.roomId, room1.moderatorCookie); expect(rec3.status).toBe(201); - expectValidStartRecordingResponse(rec3, room1.room.roomId); + expectValidStartRecordingResponse(rec3, room1.room.roomId, room1.room.roomName); response = await stopRecording(rec3.body.recordingId!, room1.moderatorCookie); - expectValidStopRecordingResponse(response, rec3.body.recordingId!, room1.room.roomId); + expectValidStopRecordingResponse(response, rec3.body.recordingId!, room1.room.roomId, room1.room.roomName); } finally { startRoomCompositeSpy.mockRestore(); } @@ -246,18 +246,14 @@ describe('Recording API Race Conditions Tests', () => { for (const startResult of retryResults) { expect(startResult.status).toBe(201); - expectValidStartRecordingResponse( - startResult, - rooms.find((r) => r.room.roomId === startResult.body.roomId)!.room.roomId - ); - const stopResult = await stopRecording( - startResult.body.recordingId!, - rooms.find((r) => r.room.roomId === startResult.body.roomId)!.moderatorCookie - ); + const room = rooms.find((r) => r.room.roomId === startResult.body.roomId)!; + expectValidStartRecordingResponse(startResult, room.room.roomId, room.room.roomName); + const stopResult = await stopRecording(startResult.body.recordingId!, room.moderatorCookie); expectValidStopRecordingResponse( stopResult, startResult.body.recordingId!, - rooms.find((r) => r.room.roomId === startResult.body.roomId)!.room.roomId + room.room.roomId, + room.room.roomName ); } } finally { @@ -283,12 +279,12 @@ describe('Recording API Race Conditions Tests', () => { // Step 2: Start recording in roomB (this will complete quickly) const recordingResponseB = await startRecording(roomDataB!.room.roomId, roomDataB!.moderatorCookie); - expectValidStartRecordingResponse(recordingResponseB, roomDataB!.room.roomId); + expectValidStartRecordingResponse(recordingResponseB, roomDataB!.room.roomId, roomDataB!.room.roomName); const recordingIdB = recordingResponseB.body.recordingId; // Step 3: Stop recording in roomB while roomA is still waiting for its event const stopResponseB = await stopRecording(recordingIdB, roomDataB!.moderatorCookie); - expectValidStopRecordingResponse(stopResponseB, recordingIdB, roomDataB!.room.roomId); + expectValidStopRecordingResponse(stopResponseB, recordingIdB, roomDataB!.room.roomId, roomDataB!.room.roomName); eventController.releaseEventsForRoom(roomDataA!.room.roomId); @@ -311,7 +307,11 @@ describe('Recording API Race Conditions Tests', () => { ); startResponses.forEach((response, index) => { - expectValidStartRecordingResponse(response, roomDataList[index].room.roomId); + expectValidStartRecordingResponse( + response, + roomDataList[index].room.roomId, + roomDataList[index].room.roomName + ); }); const recordingIds = startResponses.map((res) => res.body.recordingId); @@ -321,7 +321,12 @@ describe('Recording API Race Conditions Tests', () => { ); stopResponses.forEach((response, index) => { - expectValidStopRecordingResponse(response, recordingIds[index], roomDataList[index].room.roomId); + expectValidStopRecordingResponse( + response, + recordingIds[index], + roomDataList[index].room.roomId, + roomDataList[index].room.roomName + ); }); }); @@ -338,8 +343,8 @@ describe('Recording API Race Conditions Tests', () => { stopRecording(recordingIdA, roomDataA!.moderatorCookie), stopRecording(recordingIdB, roomDataB!.moderatorCookie) ]); - expectValidStopRecordingResponse(stopResponseA, recordingIdA, roomDataA!.room.roomId); - expectValidStopRecordingResponse(stopResponseB, recordingIdB, roomDataB!.room.roomId); + expectValidStopRecordingResponse(stopResponseA, recordingIdA, roomDataA!.room.roomId, roomDataA!.room.roomName); + expectValidStopRecordingResponse(stopResponseB, recordingIdB, roomDataB!.room.roomId, roomDataB!.room.roomName); }); it('should prevent multiple recording starts in the same room', async () => { @@ -361,16 +366,17 @@ describe('Recording API Race Conditions Tests', () => { expect(oneShouldBeFailed).toBe(true); if (firstRecordingResponse.status === 201) { - expectValidStartRecordingResponse(firstRecordingResponse, roomData.room.roomId); + expectValidStartRecordingResponse(firstRecordingResponse, roomData.room.roomId, roomData.room.roomName); // stop the first recording const stopResponse = await stopRecording(firstRecordingResponse.body.recordingId, roomData.moderatorCookie); expectValidStopRecordingResponse( stopResponse, firstRecordingResponse.body.recordingId, - roomData.room.roomId + roomData.room.roomId, + roomData.room.roomName ); } else { - expectValidStartRecordingResponse(secondRecordingResponse, roomData.room.roomId); + expectValidStartRecordingResponse(secondRecordingResponse, roomData.room.roomId, roomData.room.roomName); // stop the second recording const stopResponse = await stopRecording( secondRecordingResponse.body.recordingId, @@ -379,7 +385,8 @@ describe('Recording API Race Conditions Tests', () => { expectValidStopRecordingResponse( stopResponse, secondRecordingResponse.body.recordingId, - roomData.room.roomId + roomData.room.roomId, + roomData.room.roomName ); } }); @@ -391,7 +398,7 @@ describe('Recording API Race Conditions Tests', () => { const gcSpy = jest.spyOn(recordingService as any, 'performRecordingLocksGarbageCollection'); const startResponse = await startRecording(roomData.room.roomId, roomData.moderatorCookie); - expectValidStartRecordingResponse(startResponse, roomData.room.roomId); + expectValidStartRecordingResponse(startResponse, roomData.room.roomId, roomData.room.roomName); const recordingId = startResponse.body.recordingId; // Execute garbage collection while stopping the recording @@ -404,7 +411,7 @@ describe('Recording API Race Conditions Tests', () => { // Check that the recording was stopped successfully const stopResponse = await stopPromise; - expectValidStopRecordingResponse(stopResponse, recordingId, roomData.room.roomId); + expectValidStopRecordingResponse(stopResponse, recordingId, roomData.room.roomId, roomData.room.roomName); // Check that garbage collection was called expect(gcSpy).toHaveBeenCalled(); @@ -482,9 +489,14 @@ describe('Recording API Race Conditions Tests', () => { expect(bulkDeleteResult.body).toEqual({}); // Check that the new recording started successfully - expectValidStartRecordingResponse(newRecordingResult, room3.room.roomId); + expectValidStartRecordingResponse(newRecordingResult, room3.room.roomId, room3.room.roomName); const newStopResponse = await stopRecording(newRecordingResult.body.recordingId, room3.moderatorCookie); - expectValidStopRecordingResponse(newStopResponse, newRecordingResult.body.recordingId, room3.room.roomId); + expectValidStopRecordingResponse( + newStopResponse, + newRecordingResult.body.recordingId, + room3.room.roomId, + room3.room.roomName + ); }); }); diff --git a/backend/tests/integration/api/recordings/start-recording.test.ts b/backend/tests/integration/api/recordings/start-recording.test.ts index b933e11..d075086 100644 --- a/backend/tests/integration/api/recordings/start-recording.test.ts +++ b/backend/tests/integration/api/recordings/start-recording.test.ts @@ -51,16 +51,16 @@ describe('Recording API Tests', () => { it('should return 201 with proper response and location header when recording starts successfully', async () => { const response = await startRecording(room.roomId, moderatorCookie); const recordingId = response.body.recordingId; - expectValidStartRecordingResponse(response, room.roomId); + expectValidStartRecordingResponse(response, room.roomId, room.roomName); const stopResponse = await stopRecording(recordingId, moderatorCookie); - expectValidStopRecordingResponse(stopResponse, recordingId, room.roomId); + expectValidStopRecordingResponse(stopResponse, recordingId, room.roomId, room.roomName); }); it('should secrets and archived room files be created when recording starts', async () => { const response = await startRecording(room.roomId, moderatorCookie); const recordingId = response.body.recordingId; - expectValidStartRecordingResponse(response, room.roomId); + expectValidStartRecordingResponse(response, room.roomId, room.roomName); const storageService = container.get(MeetStorageService); @@ -76,24 +76,24 @@ describe('Recording API Tests', () => { expect(archivedRoom?.preferences).toBeDefined(); const secretsResponse = await stopRecording(recordingId, moderatorCookie); - expectValidStopRecordingResponse(secretsResponse, recordingId, room.roomId); + expectValidStopRecordingResponse(secretsResponse, recordingId, room.roomId, room.roomName); }); it('should successfully start recording, stop it, and start again (sequential operations)', async () => { const firstStartResponse = await startRecording(room.roomId, moderatorCookie); const firstRecordingId = firstStartResponse.body.recordingId; - expectValidStartRecordingResponse(firstStartResponse, room.roomId); + expectValidStartRecordingResponse(firstStartResponse, room.roomId, room.roomName); const firstStopResponse = await stopRecording(firstRecordingId, moderatorCookie); - expectValidStopRecordingResponse(firstStopResponse, firstRecordingId, room.roomId); + expectValidStopRecordingResponse(firstStopResponse, firstRecordingId, room.roomId, room.roomName); const secondStartResponse = await startRecording(room.roomId, moderatorCookie); - expectValidStartRecordingResponse(secondStartResponse, room.roomId); + expectValidStartRecordingResponse(secondStartResponse, room.roomId, room.roomName); const secondRecordingId = secondStartResponse.body.recordingId; const secondStopResponse = await stopRecording(secondRecordingId, moderatorCookie); - expectValidStopRecordingResponse(secondStopResponse, secondRecordingId, room.roomId); + expectValidStopRecordingResponse(secondStopResponse, secondRecordingId, room.roomId, room.roomName); }); it('should handle simultaneous recordings in different rooms correctly', async () => { @@ -105,8 +105,8 @@ describe('Recording API Tests', () => { const firstResponse = await startRecording(roomDataA.room.roomId, roomDataA.moderatorCookie); const secondResponse = await startRecording(roomDataB.room.roomId, roomDataB.moderatorCookie); - expectValidStartRecordingResponse(firstResponse, roomDataA.room.roomId); - expectValidStartRecordingResponse(secondResponse, roomDataB.room.roomId); + expectValidStartRecordingResponse(firstResponse, roomDataA.room.roomId, roomDataA.room.roomName); + expectValidStartRecordingResponse(secondResponse, roomDataB.room.roomId, roomDataB.room.roomName); const firstRecordingId = firstResponse.body.recordingId; const secondRecordingId = secondResponse.body.recordingId; @@ -115,8 +115,18 @@ describe('Recording API Tests', () => { stopRecording(firstRecordingId, roomDataA.moderatorCookie), stopRecording(secondRecordingId, roomDataB.moderatorCookie) ]); - expectValidStopRecordingResponse(firstStopResponse, firstRecordingId, roomDataA.room.roomId); - expectValidStopRecordingResponse(secondStopResponse, secondRecordingId, roomDataB.room.roomId); + expectValidStopRecordingResponse( + firstStopResponse, + firstRecordingId, + roomDataA.room.roomId, + roomDataA.room.roomName + ); + expectValidStopRecordingResponse( + secondStopResponse, + secondRecordingId, + roomDataB.room.roomId, + roomDataB.room.roomName + ); }); }); @@ -180,13 +190,13 @@ describe('Recording API Tests', () => { await joinFakeParticipant(room.roomId, 'fakeParticipantId'); const firstResponse = await startRecording(room.roomId, moderatorCookie); const recordingId = firstResponse.body.recordingId; - expectValidStartRecordingResponse(firstResponse, room.roomId); + expectValidStartRecordingResponse(firstResponse, room.roomId, room.roomName); const secondResponse = await startRecording(room!.roomId, moderatorCookie); expect(secondResponse.status).toBe(409); expect(secondResponse.body.message).toContain('already'); const stopResponse = await stopRecording(recordingId, moderatorCookie); - expectValidStopRecordingResponse(stopResponse, recordingId, room.roomId); + expectValidStopRecordingResponse(stopResponse, recordingId, room.roomId, room.roomName); }); it('should return 503 when recording start times out', async () => { diff --git a/backend/tests/integration/api/recordings/stop-recording.test.ts b/backend/tests/integration/api/recordings/stop-recording.test.ts index 1f41948..e42e8fe 100644 --- a/backend/tests/integration/api/recordings/stop-recording.test.ts +++ b/backend/tests/integration/api/recordings/stop-recording.test.ts @@ -39,7 +39,7 @@ describe('Recording API Tests', () => { it('should stop an active recording and return 202', async () => { const response = await stopRecording(recordingId, moderatorCookie); - expectValidStopRecordingResponse(response, recordingId, room.roomId); + expectValidStopRecordingResponse(response, recordingId, room.roomId, room.roomName); }); it('should stop multiple recordings in parallel', async () => { @@ -51,9 +51,19 @@ describe('Recording API Tests', () => { const recordingIdA = responseA.body.recordingId; const recordingIdB = responseB.body.recordingId; const stopResponseA = await stopRecording(recordingIdA, roomDataA?.moderatorCookie); - expectValidStopRecordingResponse(stopResponseA, recordingIdA, roomDataA!.room.roomId); + expectValidStopRecordingResponse( + stopResponseA, + recordingIdA, + roomDataA!.room.roomId, + roomDataA!.room.roomName + ); const stopResponseB = await stopRecording(recordingIdB, roomDataB?.moderatorCookie); - expectValidStopRecordingResponse(stopResponseB, recordingIdB, roomDataB!.room.roomId); + expectValidStopRecordingResponse( + stopResponseB, + recordingIdB, + roomDataB!.room.roomId, + roomDataB!.room.roomName + ); }); describe('Stop Recording Validation failures', () => { diff --git a/backend/tests/integration/api/security/recording-security.test.ts b/backend/tests/integration/api/security/recording-security.test.ts index 0117c65..e9f219f 100644 --- a/backend/tests/integration/api/security/recording-security.test.ts +++ b/backend/tests/integration/api/security/recording-security.test.ts @@ -70,7 +70,7 @@ describe('Recording API Security Tests', () => { // Stop recording to clean up const recordingId = response.body.recordingId; const stopResponse = await stopRecording(recordingId, roomData.moderatorCookie); - expectValidStopRecordingResponse(stopResponse, recordingId, roomData.room.roomId); + expectValidStopRecordingResponse(stopResponse, recordingId, roomData.room.roomId, roomData.room.roomName); }); it('should fail when participant is moderator of a different room', async () => { diff --git a/typings/src/recording.model.ts b/typings/src/recording.model.ts index 2018c94..7e30fe0 100644 --- a/typings/src/recording.model.ts +++ b/typings/src/recording.model.ts @@ -18,6 +18,7 @@ export const enum MeetRecordingStatus { export interface MeetRecordingInfo { recordingId: string; roomId: string; + roomName: string; // outputMode: MeetRecordingOutputMode; status: MeetRecordingStatus; filename?: string;