backend: update recording handling to include roomName and adjust related logic
This commit is contained in:
parent
bdc3f599f0
commit
42db1b7bb6
@ -141,7 +141,6 @@ export const deleteRecording = async (req: Request, res: Response) => {
|
|||||||
logger.info(`Deleting recording '${recordingId}'`);
|
logger.info(`Deleting recording '${recordingId}'`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// TODO: Check role to determine if the request is from an admin or a participant
|
|
||||||
await recordingService.deleteRecording(recordingId);
|
await recordingService.deleteRecording(recordingId);
|
||||||
return res.status(204).send();
|
return res.status(204).send();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
import { MeetRecordingInfo, MeetRecordingStatus } from '@typings-ce';
|
import { MeetRecordingInfo, MeetRecordingStatus } from '@typings-ce';
|
||||||
import { EgressInfo, EgressStatus, SendDataOptions } from 'livekit-server-sdk';
|
import { SendDataOptions } from 'livekit-server-sdk';
|
||||||
import { RecordingHelper } from './recording.helper.js';
|
|
||||||
|
|
||||||
const enum OpenViduComponentsDataTopic {
|
const enum OpenViduComponentsDataTopic {
|
||||||
CHAT = 'chat',
|
CHAT = 'chat',
|
||||||
|
|||||||
@ -2,13 +2,15 @@ import { EgressStatus } from '@livekit/protocol';
|
|||||||
import { MeetRecordingInfo, MeetRecordingStatus } from '@typings-ce';
|
import { MeetRecordingInfo, MeetRecordingStatus } from '@typings-ce';
|
||||||
import { EgressInfo } from 'livekit-server-sdk';
|
import { EgressInfo } from 'livekit-server-sdk';
|
||||||
import { uid as secureUid } from 'uid/secure';
|
import { uid as secureUid } from 'uid/secure';
|
||||||
|
import { container } from '../config/index.js';
|
||||||
|
import { RoomService } from '../services/index.js';
|
||||||
|
|
||||||
export class RecordingHelper {
|
export class RecordingHelper {
|
||||||
private constructor() {
|
private constructor() {
|
||||||
// Prevent instantiation of this utility class
|
// Prevent instantiation of this utility class
|
||||||
}
|
}
|
||||||
|
|
||||||
static toRecordingInfo(egressInfo: EgressInfo): MeetRecordingInfo {
|
static async toRecordingInfo(egressInfo: EgressInfo): Promise<MeetRecordingInfo> {
|
||||||
const status = RecordingHelper.extractOpenViduStatus(egressInfo.status);
|
const status = RecordingHelper.extractOpenViduStatus(egressInfo.status);
|
||||||
const size = RecordingHelper.extractSize(egressInfo);
|
const size = RecordingHelper.extractSize(egressInfo);
|
||||||
// const outputMode = RecordingHelper.extractOutputMode(egressInfo);
|
// const outputMode = RecordingHelper.extractOutputMode(egressInfo);
|
||||||
@ -17,10 +19,15 @@ export class RecordingHelper {
|
|||||||
const endDateMs = RecordingHelper.extractEndDate(egressInfo);
|
const endDateMs = RecordingHelper.extractEndDate(egressInfo);
|
||||||
const filename = RecordingHelper.extractFilename(egressInfo);
|
const filename = RecordingHelper.extractFilename(egressInfo);
|
||||||
const uid = RecordingHelper.extractUidFromFilename(filename);
|
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 {
|
return {
|
||||||
recordingId: `${roomName}--${egressId}--${uid}`,
|
recordingId: `${roomId}--${egressId}--${uid}`,
|
||||||
roomId: roomName,
|
roomId,
|
||||||
|
roomName,
|
||||||
// outputMode,
|
// outputMode,
|
||||||
status,
|
status,
|
||||||
filename,
|
filename,
|
||||||
@ -186,7 +193,6 @@ export class RecordingHelper {
|
|||||||
return size !== 0 ? size : undefined;
|
return size !== 0 ? size : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds the secrets for public and private access to recordings.
|
* Builds the secrets for public and private access to recordings.
|
||||||
* @returns An object containing public and private access secrets.
|
* @returns An object containing public and private access secrets.
|
||||||
|
|||||||
@ -234,7 +234,7 @@ export class LivekitWebhookService {
|
|||||||
this.logger.debug(`Processing recording_${webhookAction} webhook for egress: ${egressInfo.egressId}.`);
|
this.logger.debug(`Processing recording_${webhookAction} webhook for egress: ${egressInfo.egressId}.`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const recordingInfo: MeetRecordingInfo = RecordingHelper.toRecordingInfo(egressInfo);
|
const recordingInfo: MeetRecordingInfo = await RecordingHelper.toRecordingInfo(egressInfo);
|
||||||
const { roomId, recordingId, status } = recordingInfo;
|
const { roomId, recordingId, status } = recordingInfo;
|
||||||
|
|
||||||
this.logger.debug(`Recording '${recordingId}' in room '${roomId}' status: '${status}'`);
|
this.logger.debug(`Recording '${recordingId}' in room '${roomId}' status: '${status}'`);
|
||||||
|
|||||||
@ -77,6 +77,7 @@ export class RecordingService {
|
|||||||
await this.frontendEventService.sendRecordingSignalToOpenViduComponents(roomId, {
|
await this.frontendEventService.sendRecordingSignalToOpenViduComponents(roomId, {
|
||||||
recordingId: '',
|
recordingId: '',
|
||||||
roomId,
|
roomId,
|
||||||
|
roomName: roomId,
|
||||||
status: MeetRecordingStatus.STARTING
|
status: MeetRecordingStatus.STARTING
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -121,7 +122,7 @@ export class RecordingService {
|
|||||||
throw errorRecordingStartTimeout(roomId);
|
throw errorRecordingStartTimeout(roomId);
|
||||||
}
|
}
|
||||||
|
|
||||||
const recordingInfo = RecordingHelper.toRecordingInfo(egressInfo);
|
const recordingInfo = await RecordingHelper.toRecordingInfo(egressInfo);
|
||||||
recordingId = recordingInfo.recordingId;
|
recordingId = recordingInfo.recordingId;
|
||||||
|
|
||||||
// If the recording is already active, we can resolve the promise immediately.
|
// 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);
|
const egressInfo = await this.livekitService.stopEgress(egressId);
|
||||||
|
|
||||||
this.logger.info(`Recording stopped successfully for room '${roomId}'.`);
|
this.logger.info(`Recording stopped successfully for room '${roomId}'.`);
|
||||||
return RecordingHelper.toRecordingInfo(egressInfo);
|
return await RecordingHelper.toRecordingInfo(egressInfo);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logger.error(`Error stopping recording '${recordingId}': ${error}`);
|
this.logger.error(`Error stopping recording '${recordingId}': ${error}`);
|
||||||
throw error;
|
throw error;
|
||||||
@ -585,6 +586,7 @@ export class RecordingService {
|
|||||||
const recordingInfo: MeetRecordingInfo = {
|
const recordingInfo: MeetRecordingInfo = {
|
||||||
recordingId,
|
recordingId,
|
||||||
roomId,
|
roomId,
|
||||||
|
roomName: roomId,
|
||||||
status: MeetRecordingStatus.FAILED,
|
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.`
|
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.`
|
||||||
};
|
};
|
||||||
|
|||||||
@ -154,13 +154,16 @@ export const expectValidRecording = (
|
|||||||
recording: MeetRecordingInfo,
|
recording: MeetRecordingInfo,
|
||||||
recordingId: string,
|
recordingId: string,
|
||||||
roomId: string,
|
roomId: string,
|
||||||
|
roomName: string,
|
||||||
status: MeetRecordingStatus
|
status: MeetRecordingStatus
|
||||||
) => {
|
) => {
|
||||||
expect(recording).toBeDefined();
|
expect(recording).toBeDefined();
|
||||||
expect(recording.recordingId).toBeDefined();
|
expect(recording.recordingId).toBeDefined();
|
||||||
expect(recording.roomId).toBeDefined();
|
expect(recording.roomId).toBeDefined();
|
||||||
|
expect(recording.roomName).toBeDefined();
|
||||||
expect(recording.recordingId).toBe(recordingId);
|
expect(recording.recordingId).toBe(recordingId);
|
||||||
expect(recording.roomId).toBe(roomId);
|
expect(recording.roomId).toBe(roomId);
|
||||||
|
expect(recording.roomName).toBe(roomName);
|
||||||
expect(recording.startDate).toBeDefined();
|
expect(recording.startDate).toBeDefined();
|
||||||
expect(recording.status).toBeDefined();
|
expect(recording.status).toBeDefined();
|
||||||
expect(recording.status).toBe(status);
|
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.status).toBe(201);
|
||||||
expect(response.body).toHaveProperty('recordingId');
|
expect(response.body).toHaveProperty('recordingId');
|
||||||
|
|
||||||
@ -340,6 +343,7 @@ export const expectValidStartRecordingResponse = (response: any, roomId: string)
|
|||||||
|
|
||||||
expect(recordingId).toContain(roomId);
|
expect(recordingId).toContain(roomId);
|
||||||
expect(response.body).toHaveProperty('roomId', roomId);
|
expect(response.body).toHaveProperty('roomId', roomId);
|
||||||
|
expect(response.body).toHaveProperty('roomName', roomName);
|
||||||
expect(response.body).toHaveProperty('startDate');
|
expect(response.body).toHaveProperty('startDate');
|
||||||
expect(response.body).toHaveProperty('status', 'ACTIVE');
|
expect(response.body).toHaveProperty('status', 'ACTIVE');
|
||||||
expect(response.body).toHaveProperty('filename');
|
expect(response.body).toHaveProperty('filename');
|
||||||
@ -348,12 +352,18 @@ export const expectValidStartRecordingResponse = (response: any, roomId: string)
|
|||||||
expect(response.body).not.toHaveProperty('size');
|
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.status).toBe(202);
|
||||||
expect(response.body).toBeDefined();
|
expect(response.body).toBeDefined();
|
||||||
expect(response.body).toHaveProperty('recordingId', recordingId);
|
expect(response.body).toHaveProperty('recordingId', recordingId);
|
||||||
expect([MeetRecordingStatus.COMPLETE, MeetRecordingStatus.ENDING]).toContain(response.body.status);
|
expect([MeetRecordingStatus.COMPLETE, MeetRecordingStatus.ENDING]).toContain(response.body.status);
|
||||||
expect(response.body).toHaveProperty('roomId', roomId);
|
expect(response.body).toHaveProperty('roomId', roomId);
|
||||||
|
expect(response.body).toHaveProperty('roomName', roomName);
|
||||||
expect(response.body).toHaveProperty('filename');
|
expect(response.body).toHaveProperty('filename');
|
||||||
expect(response.body).toHaveProperty('startDate');
|
expect(response.body).toHaveProperty('startDate');
|
||||||
expect(response.body).toHaveProperty('duration', expect.any(Number));
|
expect(response.body).toHaveProperty('duration', expect.any(Number));
|
||||||
@ -365,6 +375,7 @@ export const expectValidGetRecordingResponse = (
|
|||||||
response: any,
|
response: any,
|
||||||
recordingId: string,
|
recordingId: string,
|
||||||
roomId: string,
|
roomId: string,
|
||||||
|
roomName: string,
|
||||||
status?: MeetRecordingStatus,
|
status?: MeetRecordingStatus,
|
||||||
maxSecDuration?: number
|
maxSecDuration?: number
|
||||||
) => {
|
) => {
|
||||||
@ -372,7 +383,7 @@ export const expectValidGetRecordingResponse = (
|
|||||||
expect(response.body).toBeDefined();
|
expect(response.body).toBeDefined();
|
||||||
const body = response.body;
|
const body = response.body;
|
||||||
|
|
||||||
expect(body).toMatchObject({ recordingId, roomId });
|
expect(body).toMatchObject({ recordingId, roomId, roomName });
|
||||||
|
|
||||||
const isRecFinished =
|
const isRecFinished =
|
||||||
status &&
|
status &&
|
||||||
@ -384,6 +395,7 @@ export const expectValidGetRecordingResponse = (
|
|||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
recordingId: expect.stringMatching(new RegExp(`^${recordingId}$`)),
|
recordingId: expect.stringMatching(new RegExp(`^${recordingId}$`)),
|
||||||
roomId: expect.stringMatching(new RegExp(`^${roomId}$`)),
|
roomId: expect.stringMatching(new RegExp(`^${roomId}$`)),
|
||||||
|
roomName: expect.stringMatching(new RegExp(`^${roomName}$`)),
|
||||||
...(isRecFinished ? { status: expect.any(String) } : {}),
|
...(isRecFinished ? { status: expect.any(String) } : {}),
|
||||||
...(isRecFinished ? { duration: expect.any(Number) } : {}),
|
...(isRecFinished ? { duration: expect.any(Number) } : {}),
|
||||||
...(isRecFinished ? { startDate: expect.any(Number) } : {}),
|
...(isRecFinished ? { startDate: expect.any(Number) } : {}),
|
||||||
|
|||||||
@ -110,7 +110,7 @@ export const setupSingleRoomWithRecording = async (
|
|||||||
): Promise<RoomData> => {
|
): Promise<RoomData> => {
|
||||||
const roomData = await setupSingleRoom(true, 'TEST_ROOM');
|
const roomData = await setupSingleRoom(true, 'TEST_ROOM');
|
||||||
const response = await startRecording(roomData.room.roomId, roomData.moderatorCookie);
|
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;
|
roomData.recordingId = response.body.recordingId;
|
||||||
|
|
||||||
// Wait for the configured delay before stopping the recording
|
// Wait for the configured delay before stopping the recording
|
||||||
@ -155,7 +155,7 @@ export const setupMultiRecordingsTestContext = async (
|
|||||||
|
|
||||||
// Send start recording request
|
// Send start recording request
|
||||||
const response = await startRecording(roomData.room.roomId, roomData.moderatorCookie);
|
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
|
// Store the recordingId in context
|
||||||
roomData.recordingId = response.body.recordingId;
|
roomData.recordingId = response.body.recordingId;
|
||||||
|
|||||||
@ -191,7 +191,7 @@ describe('Recording API Tests', () => {
|
|||||||
expect(roomMetadata!.publisherRoomUrl).toContain(room.roomId);
|
expect(roomMetadata!.publisherRoomUrl).toContain(room.roomId);
|
||||||
|
|
||||||
const response = await startRecording(room.roomId, moderatorCookie);
|
const response = await startRecording(room.roomId, moderatorCookie);
|
||||||
expectValidStartRecordingResponse(response, room.roomId);
|
expectValidStartRecordingResponse(response, room.roomId, room.roomName);
|
||||||
const secondRecordingId = response.body.recordingId;
|
const secondRecordingId = response.body.recordingId;
|
||||||
|
|
||||||
await stopRecording(secondRecordingId, moderatorCookie);
|
await stopRecording(secondRecordingId, moderatorCookie);
|
||||||
@ -207,6 +207,7 @@ describe('Recording API Tests', () => {
|
|||||||
// secondRecordingResponse,
|
// secondRecordingResponse,
|
||||||
// secondRecordingId,
|
// secondRecordingId,
|
||||||
// room.roomId,
|
// room.roomId,
|
||||||
|
// room.roomName,
|
||||||
// MeetRecordingStatus.COMPLETE,
|
// MeetRecordingStatus.COMPLETE,
|
||||||
// 3
|
// 3
|
||||||
// );
|
// );
|
||||||
|
|||||||
@ -79,7 +79,7 @@ describe('Recording API Tests', () => {
|
|||||||
// Generate a new recording
|
// Generate a new recording
|
||||||
const response = await startRecording(room.roomId, moderatorCookie);
|
const response = await startRecording(room.roomId, moderatorCookie);
|
||||||
console.log('Start recording response:', response.body);
|
console.log('Start recording response:', response.body);
|
||||||
expectValidStartRecordingResponse(response, room.roomId);
|
expectValidStartRecordingResponse(response, room.roomId, room.roomName);
|
||||||
const secondRecordingId = response.body.recordingId;
|
const secondRecordingId = response.body.recordingId;
|
||||||
await stopRecording(secondRecordingId, moderatorCookie);
|
await stopRecording(secondRecordingId, moderatorCookie);
|
||||||
|
|
||||||
|
|||||||
@ -35,7 +35,14 @@ describe('Recording API Tests', () => {
|
|||||||
it('should return 200 when recording exists', async () => {
|
it('should return 200 when recording exists', async () => {
|
||||||
const response = await getRecording(recordingId);
|
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 () => {
|
it('should get an ACTIVE recording status', async () => {
|
||||||
@ -47,7 +54,13 @@ describe('Recording API Tests', () => {
|
|||||||
} = contextAux.getRoomByIndex(0)!;
|
} = contextAux.getRoomByIndex(0)!;
|
||||||
const response = await getRecording(recordingIdAux);
|
const response = await getRecording(recordingIdAux);
|
||||||
|
|
||||||
expectValidGetRecordingResponse(response, recordingIdAux, roomAux.roomId, MeetRecordingStatus.ACTIVE);
|
expectValidGetRecordingResponse(
|
||||||
|
response,
|
||||||
|
recordingIdAux,
|
||||||
|
roomAux.roomId,
|
||||||
|
roomAux.roomName,
|
||||||
|
MeetRecordingStatus.ACTIVE
|
||||||
|
);
|
||||||
|
|
||||||
await stopAllRecordings(moderatorCookieAux);
|
await stopAllRecordings(moderatorCookieAux);
|
||||||
});
|
});
|
||||||
|
|||||||
@ -112,6 +112,7 @@ describe('Recordings API Tests', () => {
|
|||||||
recording,
|
recording,
|
||||||
associatedRoom!.recordingId!,
|
associatedRoom!.recordingId!,
|
||||||
associatedRoom!.room.roomId,
|
associatedRoom!.room.roomId,
|
||||||
|
associatedRoom!.room.roomName,
|
||||||
MeetRecordingStatus.COMPLETE
|
MeetRecordingStatus.COMPLETE
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -127,6 +128,7 @@ describe('Recordings API Tests', () => {
|
|||||||
recording,
|
recording,
|
||||||
associatedRoom!.recordingId!,
|
associatedRoom!.recordingId!,
|
||||||
associatedRoom!.room.roomId,
|
associatedRoom!.room.roomId,
|
||||||
|
associatedRoom!.room.roomName,
|
||||||
MeetRecordingStatus.COMPLETE
|
MeetRecordingStatus.COMPLETE
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@ -192,17 +192,17 @@ describe('Recording API Race Conditions Tests', () => {
|
|||||||
// Recording in different room should work normally
|
// Recording in different room should work normally
|
||||||
const rec2 = await startRecording(room2.room.roomId, room2.moderatorCookie);
|
const rec2 = await startRecording(room2.room.roomId, room2.moderatorCookie);
|
||||||
expect(rec2.status).toBe(201);
|
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);
|
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
|
// ✅ EXPECTED BEHAVIOR: After timeout cleanup, room1 should be available again
|
||||||
const rec3 = await startRecording(room1.room.roomId, room1.moderatorCookie);
|
const rec3 = await startRecording(room1.room.roomId, room1.moderatorCookie);
|
||||||
expect(rec3.status).toBe(201);
|
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);
|
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 {
|
} finally {
|
||||||
startRoomCompositeSpy.mockRestore();
|
startRoomCompositeSpy.mockRestore();
|
||||||
}
|
}
|
||||||
@ -246,18 +246,14 @@ describe('Recording API Race Conditions Tests', () => {
|
|||||||
|
|
||||||
for (const startResult of retryResults) {
|
for (const startResult of retryResults) {
|
||||||
expect(startResult.status).toBe(201);
|
expect(startResult.status).toBe(201);
|
||||||
expectValidStartRecordingResponse(
|
const room = rooms.find((r) => r.room.roomId === startResult.body.roomId)!;
|
||||||
startResult,
|
expectValidStartRecordingResponse(startResult, room.room.roomId, room.room.roomName);
|
||||||
rooms.find((r) => r.room.roomId === startResult.body.roomId)!.room.roomId
|
const stopResult = await stopRecording(startResult.body.recordingId!, room.moderatorCookie);
|
||||||
);
|
|
||||||
const stopResult = await stopRecording(
|
|
||||||
startResult.body.recordingId!,
|
|
||||||
rooms.find((r) => r.room.roomId === startResult.body.roomId)!.moderatorCookie
|
|
||||||
);
|
|
||||||
expectValidStopRecordingResponse(
|
expectValidStopRecordingResponse(
|
||||||
stopResult,
|
stopResult,
|
||||||
startResult.body.recordingId!,
|
startResult.body.recordingId!,
|
||||||
rooms.find((r) => r.room.roomId === startResult.body.roomId)!.room.roomId
|
room.room.roomId,
|
||||||
|
room.room.roomName
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
@ -283,12 +279,12 @@ describe('Recording API Race Conditions Tests', () => {
|
|||||||
|
|
||||||
// Step 2: Start recording in roomB (this will complete quickly)
|
// Step 2: Start recording in roomB (this will complete quickly)
|
||||||
const recordingResponseB = await startRecording(roomDataB!.room.roomId, roomDataB!.moderatorCookie);
|
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;
|
const recordingIdB = recordingResponseB.body.recordingId;
|
||||||
|
|
||||||
// Step 3: Stop recording in roomB while roomA is still waiting for its event
|
// Step 3: Stop recording in roomB while roomA is still waiting for its event
|
||||||
const stopResponseB = await stopRecording(recordingIdB, roomDataB!.moderatorCookie);
|
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);
|
eventController.releaseEventsForRoom(roomDataA!.room.roomId);
|
||||||
|
|
||||||
@ -311,7 +307,11 @@ describe('Recording API Race Conditions Tests', () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
startResponses.forEach((response, index) => {
|
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);
|
const recordingIds = startResponses.map((res) => res.body.recordingId);
|
||||||
@ -321,7 +321,12 @@ describe('Recording API Race Conditions Tests', () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
stopResponses.forEach((response, index) => {
|
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(recordingIdA, roomDataA!.moderatorCookie),
|
||||||
stopRecording(recordingIdB, roomDataB!.moderatorCookie)
|
stopRecording(recordingIdB, roomDataB!.moderatorCookie)
|
||||||
]);
|
]);
|
||||||
expectValidStopRecordingResponse(stopResponseA, recordingIdA, roomDataA!.room.roomId);
|
expectValidStopRecordingResponse(stopResponseA, recordingIdA, roomDataA!.room.roomId, roomDataA!.room.roomName);
|
||||||
expectValidStopRecordingResponse(stopResponseB, recordingIdB, roomDataB!.room.roomId);
|
expectValidStopRecordingResponse(stopResponseB, recordingIdB, roomDataB!.room.roomId, roomDataB!.room.roomName);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should prevent multiple recording starts in the same room', async () => {
|
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);
|
expect(oneShouldBeFailed).toBe(true);
|
||||||
|
|
||||||
if (firstRecordingResponse.status === 201) {
|
if (firstRecordingResponse.status === 201) {
|
||||||
expectValidStartRecordingResponse(firstRecordingResponse, roomData.room.roomId);
|
expectValidStartRecordingResponse(firstRecordingResponse, roomData.room.roomId, roomData.room.roomName);
|
||||||
// stop the first recording
|
// stop the first recording
|
||||||
const stopResponse = await stopRecording(firstRecordingResponse.body.recordingId, roomData.moderatorCookie);
|
const stopResponse = await stopRecording(firstRecordingResponse.body.recordingId, roomData.moderatorCookie);
|
||||||
expectValidStopRecordingResponse(
|
expectValidStopRecordingResponse(
|
||||||
stopResponse,
|
stopResponse,
|
||||||
firstRecordingResponse.body.recordingId,
|
firstRecordingResponse.body.recordingId,
|
||||||
roomData.room.roomId
|
roomData.room.roomId,
|
||||||
|
roomData.room.roomName
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
expectValidStartRecordingResponse(secondRecordingResponse, roomData.room.roomId);
|
expectValidStartRecordingResponse(secondRecordingResponse, roomData.room.roomId, roomData.room.roomName);
|
||||||
// stop the second recording
|
// stop the second recording
|
||||||
const stopResponse = await stopRecording(
|
const stopResponse = await stopRecording(
|
||||||
secondRecordingResponse.body.recordingId,
|
secondRecordingResponse.body.recordingId,
|
||||||
@ -379,7 +385,8 @@ describe('Recording API Race Conditions Tests', () => {
|
|||||||
expectValidStopRecordingResponse(
|
expectValidStopRecordingResponse(
|
||||||
stopResponse,
|
stopResponse,
|
||||||
secondRecordingResponse.body.recordingId,
|
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 gcSpy = jest.spyOn(recordingService as any, 'performRecordingLocksGarbageCollection');
|
||||||
|
|
||||||
const startResponse = await startRecording(roomData.room.roomId, roomData.moderatorCookie);
|
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;
|
const recordingId = startResponse.body.recordingId;
|
||||||
|
|
||||||
// Execute garbage collection while stopping the recording
|
// Execute garbage collection while stopping the recording
|
||||||
@ -404,7 +411,7 @@ describe('Recording API Race Conditions Tests', () => {
|
|||||||
|
|
||||||
// Check that the recording was stopped successfully
|
// Check that the recording was stopped successfully
|
||||||
const stopResponse = await stopPromise;
|
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
|
// Check that garbage collection was called
|
||||||
expect(gcSpy).toHaveBeenCalled();
|
expect(gcSpy).toHaveBeenCalled();
|
||||||
@ -482,9 +489,14 @@ describe('Recording API Race Conditions Tests', () => {
|
|||||||
expect(bulkDeleteResult.body).toEqual({});
|
expect(bulkDeleteResult.body).toEqual({});
|
||||||
|
|
||||||
// Check that the new recording started successfully
|
// 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);
|
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
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -51,16 +51,16 @@ describe('Recording API Tests', () => {
|
|||||||
it('should return 201 with proper response and location header when recording starts successfully', async () => {
|
it('should return 201 with proper response and location header when recording starts successfully', async () => {
|
||||||
const response = await startRecording(room.roomId, moderatorCookie);
|
const response = await startRecording(room.roomId, moderatorCookie);
|
||||||
const recordingId = response.body.recordingId;
|
const recordingId = response.body.recordingId;
|
||||||
expectValidStartRecordingResponse(response, room.roomId);
|
expectValidStartRecordingResponse(response, room.roomId, room.roomName);
|
||||||
|
|
||||||
const stopResponse = await stopRecording(recordingId, moderatorCookie);
|
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 () => {
|
it('should secrets and archived room files be created when recording starts', async () => {
|
||||||
const response = await startRecording(room.roomId, moderatorCookie);
|
const response = await startRecording(room.roomId, moderatorCookie);
|
||||||
const recordingId = response.body.recordingId;
|
const recordingId = response.body.recordingId;
|
||||||
expectValidStartRecordingResponse(response, room.roomId);
|
expectValidStartRecordingResponse(response, room.roomId, room.roomName);
|
||||||
|
|
||||||
const storageService = container.get(MeetStorageService);
|
const storageService = container.get(MeetStorageService);
|
||||||
|
|
||||||
@ -76,24 +76,24 @@ describe('Recording API Tests', () => {
|
|||||||
expect(archivedRoom?.preferences).toBeDefined();
|
expect(archivedRoom?.preferences).toBeDefined();
|
||||||
|
|
||||||
const secretsResponse = await stopRecording(recordingId, moderatorCookie);
|
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 () => {
|
it('should successfully start recording, stop it, and start again (sequential operations)', async () => {
|
||||||
const firstStartResponse = await startRecording(room.roomId, moderatorCookie);
|
const firstStartResponse = await startRecording(room.roomId, moderatorCookie);
|
||||||
const firstRecordingId = firstStartResponse.body.recordingId;
|
const firstRecordingId = firstStartResponse.body.recordingId;
|
||||||
|
|
||||||
expectValidStartRecordingResponse(firstStartResponse, room.roomId);
|
expectValidStartRecordingResponse(firstStartResponse, room.roomId, room.roomName);
|
||||||
|
|
||||||
const firstStopResponse = await stopRecording(firstRecordingId, moderatorCookie);
|
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);
|
const secondStartResponse = await startRecording(room.roomId, moderatorCookie);
|
||||||
expectValidStartRecordingResponse(secondStartResponse, room.roomId);
|
expectValidStartRecordingResponse(secondStartResponse, room.roomId, room.roomName);
|
||||||
const secondRecordingId = secondStartResponse.body.recordingId;
|
const secondRecordingId = secondStartResponse.body.recordingId;
|
||||||
|
|
||||||
const secondStopResponse = await stopRecording(secondRecordingId, moderatorCookie);
|
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 () => {
|
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 firstResponse = await startRecording(roomDataA.room.roomId, roomDataA.moderatorCookie);
|
||||||
const secondResponse = await startRecording(roomDataB.room.roomId, roomDataB.moderatorCookie);
|
const secondResponse = await startRecording(roomDataB.room.roomId, roomDataB.moderatorCookie);
|
||||||
|
|
||||||
expectValidStartRecordingResponse(firstResponse, roomDataA.room.roomId);
|
expectValidStartRecordingResponse(firstResponse, roomDataA.room.roomId, roomDataA.room.roomName);
|
||||||
expectValidStartRecordingResponse(secondResponse, roomDataB.room.roomId);
|
expectValidStartRecordingResponse(secondResponse, roomDataB.room.roomId, roomDataB.room.roomName);
|
||||||
|
|
||||||
const firstRecordingId = firstResponse.body.recordingId;
|
const firstRecordingId = firstResponse.body.recordingId;
|
||||||
const secondRecordingId = secondResponse.body.recordingId;
|
const secondRecordingId = secondResponse.body.recordingId;
|
||||||
@ -115,8 +115,18 @@ describe('Recording API Tests', () => {
|
|||||||
stopRecording(firstRecordingId, roomDataA.moderatorCookie),
|
stopRecording(firstRecordingId, roomDataA.moderatorCookie),
|
||||||
stopRecording(secondRecordingId, roomDataB.moderatorCookie)
|
stopRecording(secondRecordingId, roomDataB.moderatorCookie)
|
||||||
]);
|
]);
|
||||||
expectValidStopRecordingResponse(firstStopResponse, firstRecordingId, roomDataA.room.roomId);
|
expectValidStopRecordingResponse(
|
||||||
expectValidStopRecordingResponse(secondStopResponse, secondRecordingId, roomDataB.room.roomId);
|
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');
|
await joinFakeParticipant(room.roomId, 'fakeParticipantId');
|
||||||
const firstResponse = await startRecording(room.roomId, moderatorCookie);
|
const firstResponse = await startRecording(room.roomId, moderatorCookie);
|
||||||
const recordingId = firstResponse.body.recordingId;
|
const recordingId = firstResponse.body.recordingId;
|
||||||
expectValidStartRecordingResponse(firstResponse, room.roomId);
|
expectValidStartRecordingResponse(firstResponse, room.roomId, room.roomName);
|
||||||
|
|
||||||
const secondResponse = await startRecording(room!.roomId, moderatorCookie);
|
const secondResponse = await startRecording(room!.roomId, moderatorCookie);
|
||||||
expect(secondResponse.status).toBe(409);
|
expect(secondResponse.status).toBe(409);
|
||||||
expect(secondResponse.body.message).toContain('already');
|
expect(secondResponse.body.message).toContain('already');
|
||||||
const stopResponse = await stopRecording(recordingId, moderatorCookie);
|
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 () => {
|
it('should return 503 when recording start times out', async () => {
|
||||||
|
|||||||
@ -39,7 +39,7 @@ describe('Recording API Tests', () => {
|
|||||||
|
|
||||||
it('should stop an active recording and return 202', async () => {
|
it('should stop an active recording and return 202', async () => {
|
||||||
const response = await stopRecording(recordingId, moderatorCookie);
|
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 () => {
|
it('should stop multiple recordings in parallel', async () => {
|
||||||
@ -51,9 +51,19 @@ describe('Recording API Tests', () => {
|
|||||||
const recordingIdA = responseA.body.recordingId;
|
const recordingIdA = responseA.body.recordingId;
|
||||||
const recordingIdB = responseB.body.recordingId;
|
const recordingIdB = responseB.body.recordingId;
|
||||||
const stopResponseA = await stopRecording(recordingIdA, roomDataA?.moderatorCookie);
|
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);
|
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', () => {
|
describe('Stop Recording Validation failures', () => {
|
||||||
|
|||||||
@ -70,7 +70,7 @@ describe('Recording API Security Tests', () => {
|
|||||||
// Stop recording to clean up
|
// Stop recording to clean up
|
||||||
const recordingId = response.body.recordingId;
|
const recordingId = response.body.recordingId;
|
||||||
const stopResponse = await stopRecording(recordingId, roomData.moderatorCookie);
|
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 () => {
|
it('should fail when participant is moderator of a different room', async () => {
|
||||||
|
|||||||
@ -18,6 +18,7 @@ export const enum MeetRecordingStatus {
|
|||||||
export interface MeetRecordingInfo {
|
export interface MeetRecordingInfo {
|
||||||
recordingId: string;
|
recordingId: string;
|
||||||
roomId: string;
|
roomId: string;
|
||||||
|
roomName: string;
|
||||||
// outputMode: MeetRecordingOutputMode;
|
// outputMode: MeetRecordingOutputMode;
|
||||||
status: MeetRecordingStatus;
|
status: MeetRecordingStatus;
|
||||||
filename?: string;
|
filename?: string;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user