openvidu/backend/tests/helpers/test-scenarios.ts
juancarmore 3ef546488f test: refactor setupSingleRoom function to join participant after generating participant token
This fix the bug that causes LiveKit webhook events to not have property 'metadata' defined because the room was being created when joining participant instead of when generating token
2025-06-20 14:59:38 +02:00

212 lines
6.0 KiB
TypeScript

import express, { Request, Response } from 'express';
import http from 'http';
import { StringValue } from 'ms';
import { MeetRoomHelper } from '../../src/helpers';
import { MeetRoom, MeetRoomPreferences } from '../../src/typings/ce';
import { expectValidStartRecordingResponse } from './assertion-helpers';
import {
createRoom,
generateParticipantTokenCookie,
joinFakeParticipant,
sleep,
startRecording,
stopRecording
} from './request-helpers';
let mockWebhookServer: http.Server;
export interface RoomData {
room: MeetRoom;
moderatorSecret: string;
moderatorCookie: string;
publisherSecret: string;
publisherCookie: string;
recordingId?: string;
}
export interface TestContext {
rooms: RoomData[];
getRoomByIndex(index: number): RoomData | undefined;
getLastRoom(): RoomData | undefined;
}
/**
* Creates a single room with optional participant.
*
* @param withParticipant Whether to join a fake participant in the room.
* @returns Room data including secrets and cookies.
*/
export const setupSingleRoom = async (
withParticipant = false,
roomPrefix = 'TEST_ROOM',
preferences?: MeetRoomPreferences
): Promise<RoomData> => {
const room = await createRoom({
roomIdPrefix: roomPrefix,
preferences
});
// Extract the room secrets and generate participant tokens, saved as cookies
const { moderatorSecret, publisherSecret } = MeetRoomHelper.extractSecretsFromRoom(room);
const [moderatorCookie, publisherCookie] = await Promise.all([
generateParticipantTokenCookie(room.roomId, 'MODERATOR', moderatorSecret),
generateParticipantTokenCookie(room.roomId, 'PUBLISHER', publisherSecret)
]);
// Join participant if needed
if (withParticipant) {
await joinFakeParticipant(room.roomId, 'TEST_PARTICIPANT');
}
return {
room,
moderatorSecret,
moderatorCookie,
publisherSecret,
publisherCookie
};
};
/**
* Creates a test context with multiple rooms and optional participants.
*
* @param numRooms Number of rooms to create.
* @param withParticipants Whether to join fake participants in the rooms.
* @returns Test context with created rooms and their data.
*/
export const setupMultiRoomTestContext = async (numRooms: number, withParticipants: boolean): Promise<TestContext> => {
const rooms: RoomData[] = [];
for (let i = 0; i < numRooms; i++) {
const roomData = await setupSingleRoom(withParticipants, 'TEST_ROOM');
rooms.push(roomData);
}
return {
rooms,
getRoomByIndex: (index: number) => {
if (index < 0 || index >= rooms.length) {
return undefined;
}
return rooms[index];
},
getLastRoom: () => {
if (rooms.length === 0) {
return undefined;
}
return rooms[rooms.length - 1];
}
};
};
export const setupSingleRoomWithRecording = async (
stopRecordingCond = false,
stopDelay?: StringValue
): Promise<RoomData> => {
const roomData = await setupSingleRoom(true, 'TEST_ROOM');
const response = await startRecording(roomData.room.roomId, roomData.moderatorCookie);
expectValidStartRecordingResponse(response, roomData.room.roomId);
roomData.recordingId = response.body.recordingId;
// Wait for the configured delay before stopping the recording
if (stopRecordingCond && stopDelay) {
await sleep(stopDelay);
}
if (stopRecordingCond) {
await stopRecording(roomData.recordingId!, roomData.moderatorCookie);
}
return roomData;
};
/**
* Quickly creates multiple recordings
* 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 stopDelay Delay before stopping recordings.
* @returns Test context with created recordings (some stopped, some still running).
*/
export const setupMultiRecordingsTestContext = async (
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 (stopDelay) {
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;
};
export const startWebhookServer = async (
port: number,
webhookReceivedCallback: (event: Request) => void
): Promise<void> => {
const app = express();
app.use(express.json());
app.post('/webhook', (req: Request, res: Response) => {
webhookReceivedCallback(req);
res.status(200).send({ success: true });
});
return new Promise<void>((resolve) => {
mockWebhookServer = app.listen(port, () => {
console.log(`Webhook server listening on port ${port}`);
resolve();
});
});
};
export const stopWebhookServer = async (): Promise<void> => {
if (mockWebhookServer) {
await new Promise<void>((resolve) => {
mockWebhookServer.close(() => resolve());
});
}
};