backend: Add bulk delete recordings tests and enhance test utilities
This commit is contained in:
parent
32c0c9d242
commit
f8a176b4fa
@ -0,0 +1,187 @@
|
|||||||
|
import { afterAll, beforeAll, describe, expect, it } from '@jest/globals';
|
||||||
|
import {
|
||||||
|
bulkDeleteRecordings,
|
||||||
|
deleteAllRecordings,
|
||||||
|
deleteAllRooms,
|
||||||
|
startTestServer,
|
||||||
|
stopRecording,
|
||||||
|
stopTestServer
|
||||||
|
} from '../../../utils/helpers';
|
||||||
|
import { setupMultiRecordingsTestContext } from '../../../utils/test-scenarios';
|
||||||
|
|
||||||
|
describe('Recording API Tests', () => {
|
||||||
|
beforeAll(async () => {
|
||||||
|
await startTestServer();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
await deleteAllRooms();
|
||||||
|
await deleteAllRecordings();
|
||||||
|
await stopTestServer();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Bulk Delete Recording Tests', () => {
|
||||||
|
it('"should return 200 when mixed valid and non-existent IDs are provided', async () => {
|
||||||
|
const testContext = await setupMultiRecordingsTestContext(3, 3, 3, '0s');
|
||||||
|
const recordingIds = testContext.rooms.map((room) => room.recordingId);
|
||||||
|
const nonExistentIds = ['nonExistent--EG_000--1234', 'nonExistent--EG_111--5678'];
|
||||||
|
const mixedIds = [...recordingIds, ...nonExistentIds];
|
||||||
|
|
||||||
|
const deleteResponse = await bulkDeleteRecordings(mixedIds);
|
||||||
|
|
||||||
|
expect(deleteResponse.status).toBe(200);
|
||||||
|
expect(deleteResponse.body).toEqual({
|
||||||
|
deleted: expect.arrayContaining(recordingIds),
|
||||||
|
notDeleted: expect.arrayContaining(
|
||||||
|
nonExistentIds.map((id) => ({
|
||||||
|
recordingId: id,
|
||||||
|
error: expect.stringContaining(`Recording '${id}' not found`)
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return 200 with mixed results when some recordings are in active state', async () => {
|
||||||
|
const testContext = await setupMultiRecordingsTestContext(3, 3, 2, '0s');
|
||||||
|
const activeRecordingRoom = testContext.getLastRoom();
|
||||||
|
const recordingIds = testContext.rooms
|
||||||
|
.map((room) => room.recordingId)
|
||||||
|
.filter((id) => id !== activeRecordingRoom!.recordingId);
|
||||||
|
|
||||||
|
const activeRecordingId = activeRecordingRoom?.recordingId;
|
||||||
|
let deleteResponse = await bulkDeleteRecordings([...recordingIds, activeRecordingId]);
|
||||||
|
|
||||||
|
expect(deleteResponse.status).toBe(200);
|
||||||
|
expect(deleteResponse.body).toEqual({
|
||||||
|
deleted: expect.arrayContaining(recordingIds),
|
||||||
|
notDeleted: [
|
||||||
|
{
|
||||||
|
recordingId: activeRecordingId,
|
||||||
|
error: expect.stringContaining(`Recording '${activeRecordingId}' is not stopped yet`)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
await stopRecording(activeRecordingId!, activeRecordingRoom!.moderatorCookie);
|
||||||
|
|
||||||
|
deleteResponse = await bulkDeleteRecordings([activeRecordingId]);
|
||||||
|
|
||||||
|
expect(deleteResponse.status).toBe(204);
|
||||||
|
expect(deleteResponse.body).toStrictEqual({});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not delete any recordings and return 200', async () => {
|
||||||
|
const testContext = await setupMultiRecordingsTestContext(2, 2, 0, '0s');
|
||||||
|
const recordingIds = testContext.rooms.map((room) => room.recordingId);
|
||||||
|
const deleteResponse = await bulkDeleteRecordings(recordingIds);
|
||||||
|
expect(deleteResponse.status).toBe(200);
|
||||||
|
expect(deleteResponse.body).toEqual({
|
||||||
|
deleted: [],
|
||||||
|
notDeleted: expect.arrayContaining(
|
||||||
|
recordingIds.map((id) => ({
|
||||||
|
recordingId: id,
|
||||||
|
error: expect.stringContaining(`Recording '${id}' is not stopped yet`)
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
await Promise.all(
|
||||||
|
recordingIds.map((id, index) => {
|
||||||
|
return stopRecording(id!, testContext.getRoomByIndex(index)!.moderatorCookie);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should delete all recordings and return 204 when all operations succeed', async () => {
|
||||||
|
const response = await setupMultiRecordingsTestContext(5, 5, 5, '0s');
|
||||||
|
const recordingIds = response.rooms.map((room) => room.recordingId);
|
||||||
|
const deleteResponse = await bulkDeleteRecordings(recordingIds);
|
||||||
|
|
||||||
|
expect(deleteResponse.status).toBe(204);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle single recording deletion correctly', async () => {
|
||||||
|
const testContext = await setupMultiRecordingsTestContext(1, 1, 1, '0s');
|
||||||
|
const recordingId = testContext.rooms[0].recordingId;
|
||||||
|
const deleteResponse = await bulkDeleteRecordings([recordingId]);
|
||||||
|
|
||||||
|
expect(deleteResponse.status).toBe(204);
|
||||||
|
expect(deleteResponse.body).toStrictEqual({});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle duplicate recording IDs by treating them as a single delete', async () => {
|
||||||
|
const testContext = await setupMultiRecordingsTestContext(1, 1, 1, '0s');
|
||||||
|
const recordingId = testContext.getRoomByIndex(0)!.recordingId;
|
||||||
|
const deleteResponse = await bulkDeleteRecordings([recordingId, recordingId]);
|
||||||
|
|
||||||
|
expect(deleteResponse.status).toBe(204);
|
||||||
|
expect(deleteResponse.body).toStrictEqual({});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Bulk Delete Recording Validation', () => {
|
||||||
|
it('should handle empty recordingIds array gracefully', async () => {
|
||||||
|
const deleteResponse = await bulkDeleteRecordings([]);
|
||||||
|
|
||||||
|
expect(deleteResponse.status).toBe(422);
|
||||||
|
expect(deleteResponse.body).toEqual({
|
||||||
|
details: [
|
||||||
|
{
|
||||||
|
field: 'recordingIds',
|
||||||
|
message: 'recordingIds must contain at least one item'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
error: 'Unprocessable Entity',
|
||||||
|
message: 'Invalid request'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should reject a CSV string with invalid format', async () => {
|
||||||
|
const invalidRecordingIds = 'invalid--recording.id,invalid--EG_111--5678';
|
||||||
|
const deleteResponse = await bulkDeleteRecordings([invalidRecordingIds]);
|
||||||
|
|
||||||
|
expect(deleteResponse.status).toBe(422);
|
||||||
|
expect(deleteResponse.body).toMatchObject({
|
||||||
|
details: [
|
||||||
|
{
|
||||||
|
message: 'recordingId does not follow the expected format'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
error: 'Unprocessable Entity',
|
||||||
|
message: 'Invalid request'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should reject an array containing empty strings after sanitization', async () => {
|
||||||
|
const invalidRecordingIds = ['', ' '];
|
||||||
|
const deleteResponse = await bulkDeleteRecordings(invalidRecordingIds);
|
||||||
|
|
||||||
|
expect(deleteResponse.status).toBe(422);
|
||||||
|
expect(deleteResponse.body).toMatchObject({
|
||||||
|
details: [
|
||||||
|
{
|
||||||
|
message: 'recordingIds must contain at least one item'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
error: 'Unprocessable Entity',
|
||||||
|
message: 'Invalid request'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should reject an array with mixed valid and totally invalid IDs', async () => {
|
||||||
|
const invalidRecordingIds = ['valid--EG_111--5678', 'invalid--recording.id'];
|
||||||
|
const deleteResponse = await bulkDeleteRecordings(invalidRecordingIds);
|
||||||
|
|
||||||
|
expect(deleteResponse.status).toBe(422);
|
||||||
|
expect(deleteResponse.body).toMatchObject({
|
||||||
|
details: [
|
||||||
|
{
|
||||||
|
message: 'recordingId does not follow the expected format'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
error: 'Unprocessable Entity',
|
||||||
|
message: 'Invalid request'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -1,5 +1,5 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
import request from 'supertest';
|
import request, { Response } from 'supertest';
|
||||||
import { Express } from 'express';
|
import { Express } from 'express';
|
||||||
import { Server } from 'http';
|
import { Server } from 'http';
|
||||||
import { createApp, registerDependencies } from '../../src/server.js';
|
import { createApp, registerDependencies } from '../../src/server.js';
|
||||||
@ -20,8 +20,8 @@ import INTERNAL_CONFIG from '../../src/config/internal-config.js';
|
|||||||
import { ChildProcess, execSync, spawn } from 'child_process';
|
import { ChildProcess, execSync, spawn } from 'child_process';
|
||||||
import { container } from '../../src/config/dependency-injector.config.js';
|
import { container } from '../../src/config/dependency-injector.config.js';
|
||||||
import { RoomService } from '../../src/services/room.service.js';
|
import { RoomService } from '../../src/services/room.service.js';
|
||||||
import { MeetRoomHelper } from '../../src/helpers/room.helper.js';
|
|
||||||
import { RecordingService } from '../../src/services/recording.service.js';
|
import { RecordingService } from '../../src/services/recording.service.js';
|
||||||
|
import ms, { StringValue } from 'ms';
|
||||||
|
|
||||||
const CREDENTIALS = {
|
const CREDENTIALS = {
|
||||||
user: {
|
user: {
|
||||||
@ -39,8 +39,8 @@ let server: Server;
|
|||||||
|
|
||||||
const fakeParticipantsProcesses = new Map<string, ChildProcess>();
|
const fakeParticipantsProcesses = new Map<string, ChildProcess>();
|
||||||
|
|
||||||
export const sleep = (ms: number) => {
|
export const sleep = (time: StringValue) => {
|
||||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
return new Promise((resolve) => setTimeout(resolve, ms(time)));
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -187,40 +187,6 @@ export const updateRoomPreferences = async (roomId: string, preferences: any) =>
|
|||||||
.send(preferences);
|
.send(preferences);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Asserts that a rooms response matches the expected values for testing purposes.
|
|
||||||
* Validates the room array length and pagination properties.
|
|
||||||
*
|
|
||||||
* @param body - The API response body to validate
|
|
||||||
* @param expectedRoomLength - The expected number of rooms in the response
|
|
||||||
* @param expectedMaxItems - The expected maximum number of items in pagination
|
|
||||||
* @param expectedTruncated - The expected value for pagination.isTruncated flag
|
|
||||||
* @param expectedNextPageToken - The expected presence of pagination.nextPageToken
|
|
||||||
* (if true, expects nextPageToken to be defined;
|
|
||||||
* if false, expects nextPageToken to be undefined)
|
|
||||||
*/
|
|
||||||
export const assertSuccessRoomsResponse = (
|
|
||||||
response: any,
|
|
||||||
expectedRoomLength: number,
|
|
||||||
expectedMaxItems: number,
|
|
||||||
expectedTruncated: boolean,
|
|
||||||
expectedNextPageToken: boolean
|
|
||||||
) => {
|
|
||||||
const { body } = response;
|
|
||||||
expect(response.status).toBe(200);
|
|
||||||
expect(body).toBeDefined();
|
|
||||||
expect(body.rooms).toBeDefined();
|
|
||||||
expect(Array.isArray(body.rooms)).toBe(true);
|
|
||||||
expect(body.rooms.length).toBe(expectedRoomLength);
|
|
||||||
expect(body.pagination).toBeDefined();
|
|
||||||
expect(body.pagination.isTruncated).toBe(expectedTruncated);
|
|
||||||
|
|
||||||
expectedNextPageToken
|
|
||||||
? expect(body.pagination.nextPageToken).toBeDefined()
|
|
||||||
: expect(body.pagination.nextPageToken).toBeUndefined();
|
|
||||||
expect(body.pagination.maxItems).toBe(expectedMaxItems);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves information about a specific room from the API.
|
* Retrieves information about a specific room from the API.
|
||||||
*
|
*
|
||||||
@ -262,16 +228,6 @@ export const bulkDeleteRooms = async (roomIds: any[], force?: any) => {
|
|||||||
.query({ roomIds: roomIds.join(','), force });
|
.query({ roomIds: roomIds.join(','), force });
|
||||||
};
|
};
|
||||||
|
|
||||||
export const assertEmptyRooms = async () => {
|
|
||||||
if (!app) {
|
|
||||||
throw new Error('App instance is not defined');
|
|
||||||
}
|
|
||||||
|
|
||||||
const response = await getRooms();
|
|
||||||
|
|
||||||
assertSuccessRoomsResponse(response, 0, 10, false, false);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Runs the room garbage collector to delete expired rooms.
|
* Runs the room garbage collector to delete expired rooms.
|
||||||
*
|
*
|
||||||
@ -286,8 +242,6 @@ export const runRoomGarbageCollector = async () => {
|
|||||||
|
|
||||||
const roomService = container.get(RoomService);
|
const roomService = container.get(RoomService);
|
||||||
await (roomService as any)['deleteExpiredRooms']();
|
await (roomService as any)['deleteExpiredRooms']();
|
||||||
|
|
||||||
await sleep(1000);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const runReleaseActiveRecordingLock = async (roomId: string) => {
|
export const runReleaseActiveRecordingLock = async (roomId: string) => {
|
||||||
@ -327,8 +281,6 @@ export const deleteAllRooms = async () => {
|
|||||||
.delete(`${INTERNAL_CONFIG.API_BASE_PATH_V1}/rooms`)
|
.delete(`${INTERNAL_CONFIG.API_BASE_PATH_V1}/rooms`)
|
||||||
.query({ roomIds: roomIds.join(','), force: true })
|
.query({ roomIds: roomIds.join(','), force: true })
|
||||||
.set(INTERNAL_CONFIG.API_KEY_HEADER, MEET_API_KEY);
|
.set(INTERNAL_CONFIG.API_KEY_HEADER, MEET_API_KEY);
|
||||||
|
|
||||||
await new Promise((resolve) => setTimeout(resolve, 1000)); // Wait for 1 second
|
|
||||||
} while (nextPageToken);
|
} while (nextPageToken);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -407,7 +359,7 @@ export const joinFakeParticipant = async (roomId: string, participantName: strin
|
|||||||
|
|
||||||
// Store the process to be able to terminate it later
|
// Store the process to be able to terminate it later
|
||||||
fakeParticipantsProcesses.set(participantName, process);
|
fakeParticipantsProcesses.set(participantName, process);
|
||||||
await sleep(1000);
|
await sleep('1s');
|
||||||
};
|
};
|
||||||
|
|
||||||
export const disconnectFakeParticipants = async () => {
|
export const disconnectFakeParticipants = async () => {
|
||||||
@ -417,7 +369,7 @@ export const disconnectFakeParticipants = async () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
fakeParticipantsProcesses.clear();
|
fakeParticipantsProcesses.clear();
|
||||||
await sleep(1000);
|
await sleep('1s');
|
||||||
};
|
};
|
||||||
|
|
||||||
export const startRecording = async (roomId: string, moderatorCookie = '') => {
|
export const startRecording = async (roomId: string, moderatorCookie = '') => {
|
||||||
@ -442,7 +394,7 @@ export const stopRecording = async (recordingId: string, moderatorCookie = '') =
|
|||||||
.post(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/recordings/${recordingId}/stop`)
|
.post(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/recordings/${recordingId}/stop`)
|
||||||
.set('Cookie', moderatorCookie)
|
.set('Cookie', moderatorCookie)
|
||||||
.send();
|
.send();
|
||||||
await sleep(2500);
|
await sleep('2.5s');
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
};
|
};
|
||||||
@ -469,7 +421,7 @@ export const stopAllRecordings = async (moderatorCookie: string) => {
|
|||||||
.send()
|
.send()
|
||||||
);
|
);
|
||||||
await Promise.all(tasks);
|
await Promise.all(tasks);
|
||||||
await sleep(1000); // Wait for 1 second
|
await sleep('1s');
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getAllRecordings = async (query: Record<string, any> = {}) => {
|
export const getAllRecordings = async (query: Record<string, any> = {}) => {
|
||||||
@ -483,6 +435,18 @@ export const getAllRecordings = async (query: Record<string, any> = {}) => {
|
|||||||
.query(query);
|
.query(query);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const bulkDeleteRecordings = async (recordingIds: any[]): Promise<Response> => {
|
||||||
|
if (!app) {
|
||||||
|
throw new Error('App instance is not defined');
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await request(app)
|
||||||
|
.delete(`${INTERNAL_CONFIG.API_BASE_PATH_V1}/recordings`)
|
||||||
|
.query({ recordingIds: recordingIds.join(',') })
|
||||||
|
.set(INTERNAL_CONFIG.API_KEY_HEADER, MEET_API_KEY);
|
||||||
|
return response;
|
||||||
|
};
|
||||||
|
|
||||||
export const deleteAllRecordings = async () => {
|
export const deleteAllRecordings = async () => {
|
||||||
if (!app) {
|
if (!app) {
|
||||||
throw new Error('App instance is not defined');
|
throw new Error('App instance is not defined');
|
||||||
@ -507,14 +471,7 @@ export const deleteAllRecordings = async () => {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`Deleting ${recordingIds.length} recordings...`);
|
await bulkDeleteRecordings(recordingIds);
|
||||||
console.log('Recording IDs:', recordingIds);
|
|
||||||
await request(app)
|
|
||||||
.delete(`${INTERNAL_CONFIG.API_BASE_PATH_V1}/recordings`)
|
|
||||||
.query({ recordingIds: recordingIds.join(',')})
|
|
||||||
.set(INTERNAL_CONFIG.API_KEY_HEADER, MEET_API_KEY);
|
|
||||||
|
|
||||||
await sleep(1000); // Wait for 1 second
|
|
||||||
} while (nextPageToken);
|
} while (nextPageToken);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -1,10 +1,20 @@
|
|||||||
import { MeetRoomHelper } from '../../src/helpers';
|
import { MeetRoomHelper } from '../../src/helpers';
|
||||||
import { createRoom, loginUserAsRole, generateParticipantToken, joinFakeParticipant } from './helpers';
|
import {
|
||||||
|
createRoom,
|
||||||
|
loginUserAsRole,
|
||||||
|
generateParticipantToken,
|
||||||
|
joinFakeParticipant,
|
||||||
|
startRecording,
|
||||||
|
stopRecording,
|
||||||
|
sleep
|
||||||
|
} from './helpers';
|
||||||
|
|
||||||
import { UserRole } from '../../src/typings/ce';
|
import { MeetRoom, UserRole } from '../../src/typings/ce';
|
||||||
|
import ms, { StringValue } from 'ms';
|
||||||
|
import { expectValidStartRecordingResponse } from './assertion-helpers';
|
||||||
|
|
||||||
export interface RoomData {
|
export interface RoomData {
|
||||||
room: any;
|
room: MeetRoom;
|
||||||
moderatorCookie: string;
|
moderatorCookie: string;
|
||||||
moderatorSecret: string;
|
moderatorSecret: string;
|
||||||
recordingId?: string;
|
recordingId?: string;
|
||||||
@ -13,6 +23,7 @@ export interface RoomData {
|
|||||||
export interface TestContext {
|
export interface TestContext {
|
||||||
rooms: RoomData[];
|
rooms: RoomData[];
|
||||||
getRoomByIndex(index: number): RoomData | undefined;
|
getRoomByIndex(index: number): RoomData | undefined;
|
||||||
|
getLastRoom(): RoomData | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -28,18 +39,11 @@ export async function setupMultiRoomTestContext(numRooms: number, withParticipan
|
|||||||
roomIdPrefix: `test-recording-room-${i + 1}`
|
roomIdPrefix: `test-recording-room-${i + 1}`
|
||||||
});
|
});
|
||||||
const { moderatorSecret } = MeetRoomHelper.extractSecretsFromRoom(room);
|
const { moderatorSecret } = MeetRoomHelper.extractSecretsFromRoom(room);
|
||||||
const moderatorCookie = await generateParticipantToken(
|
const [moderatorCookie, _] = await Promise.all([
|
||||||
adminCookie,
|
generateParticipantToken(adminCookie, room.roomId, `Moderator-${i + 1}`, moderatorSecret),
|
||||||
room.roomId,
|
// Join participant (if needed) concurrently with token generation
|
||||||
`Moderator-${i + 1}`,
|
withParticipants ? joinFakeParticipant(room.roomId, `TEST_P-${i + 1}`) : Promise.resolve()
|
||||||
moderatorSecret
|
]);
|
||||||
);
|
|
||||||
|
|
||||||
if (withParticipants) {
|
|
||||||
const participantId = `TEST_P-${i + 1}`;
|
|
||||||
|
|
||||||
await joinFakeParticipant(room.roomId, participantId);
|
|
||||||
}
|
|
||||||
|
|
||||||
rooms.push({
|
rooms.push({
|
||||||
room,
|
room,
|
||||||
@ -56,6 +60,73 @@ export async function setupMultiRoomTestContext(numRooms: number, withParticipan
|
|||||||
}
|
}
|
||||||
|
|
||||||
return rooms[index];
|
return rooms[index];
|
||||||
|
},
|
||||||
|
|
||||||
|
getLastRoom: () => {
|
||||||
|
if (rooms.length === 0) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rooms[rooms.length - 1];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Quickly creates multiple recordings for bulk delete testing.
|
||||||
|
* Allows customizing how many recordings to start and how many to stop after a delay.
|
||||||
|
*
|
||||||
|
* @param numRooms Number of rooms to use.
|
||||||
|
* @param numStarts Number of recordings to start.
|
||||||
|
* @param numStops Number of recordings to stop after the delay.
|
||||||
|
* @param stopDelayMs Delay in milliseconds before stopping recordings.
|
||||||
|
* @returns Test context with created recordings (some stopped, some still running).
|
||||||
|
*/
|
||||||
|
export async function setupMultiRecordingsTestContext(
|
||||||
|
numRooms: number,
|
||||||
|
numStarts: number,
|
||||||
|
numStops: number,
|
||||||
|
stopDelay: StringValue
|
||||||
|
): Promise<TestContext> {
|
||||||
|
// Setup rooms with participants
|
||||||
|
const testContext = await setupMultiRoomTestContext(numRooms, true);
|
||||||
|
|
||||||
|
// Start the specified number of recordings in parallel
|
||||||
|
const startPromises = Array.from({ length: numStarts }).map(async (_, i) => {
|
||||||
|
const roomIndex = i % numRooms;
|
||||||
|
const roomData = testContext.getRoomByIndex(roomIndex);
|
||||||
|
|
||||||
|
if (!roomData) {
|
||||||
|
throw new Error(`Room at index ${roomIndex} not found`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send start recording request
|
||||||
|
const response = await startRecording(roomData.room.roomId, roomData.moderatorCookie);
|
||||||
|
expectValidStartRecordingResponse(response, roomData.room.roomId);
|
||||||
|
|
||||||
|
// Store the recordingId in context
|
||||||
|
roomData.recordingId = response.body.recordingId;
|
||||||
|
return roomData;
|
||||||
|
});
|
||||||
|
const startedRooms = await Promise.all(startPromises);
|
||||||
|
|
||||||
|
// Wait for the configured delay before stopping recordings
|
||||||
|
if (ms(stopDelay) > 0) {
|
||||||
|
await sleep(stopDelay);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop recordings for the first numStops rooms
|
||||||
|
const stopPromises = startedRooms.slice(0, numStops).map(async (roomData) => {
|
||||||
|
if (roomData.recordingId) {
|
||||||
|
await stopRecording(roomData.recordingId, roomData.moderatorCookie);
|
||||||
|
console.log(`Recording stopped for room ${roomData.room.roomId}`);
|
||||||
|
return roomData.recordingId;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
const stoppedIds = (await Promise.all(stopPromises)).filter((id): id is string => Boolean(id));
|
||||||
|
console.log(`Stopped ${stoppedIds.length} recordings after ${stopDelay}ms:`, stoppedIds);
|
||||||
|
|
||||||
|
return testContext;
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user