test: add integration tests for generating participant tokens and validate responses

This commit is contained in:
juancarmore 2025-05-13 14:23:00 +02:00
parent 9630fa475d
commit 26103ab52a
4 changed files with 196 additions and 20 deletions

View File

@ -513,6 +513,40 @@ const getPermissions = (roomId: string, role: ParticipantRole) => {
}
};
export const expectValidParticipantTokenResponse = (
response: any,
roomId: string,
participantName: string,
participantRole: ParticipantRole
) => {
expect(response.status).toBe(200);
expect(response.body).toHaveProperty('token');
const token = response.body.token;
const decodedToken = decodeJWTToken(token);
const permissions = getPermissions(roomId, participantRole);
expect(decodedToken).toHaveProperty('sub', participantName);
expect(decodedToken).toHaveProperty('video', permissions.livekit);
expect(decodedToken).toHaveProperty('metadata');
const metadata = JSON.parse(decodedToken.metadata);
expect(metadata).toHaveProperty('role', participantRole);
expect(metadata).toHaveProperty('permissions', permissions.openvidu);
// Check that the token is included in a cookie
expect(response.headers['set-cookie']).toBeDefined();
const cookies = response.headers['set-cookie'] as unknown as string[];
const participantTokenCookie = cookies.find((cookie) =>
cookie.startsWith(`${INTERNAL_CONFIG.PARTICIPANT_TOKEN_COOKIE_NAME}=`)
) as string;
expect(participantTokenCookie).toBeDefined();
expect(participantTokenCookie).toContain(token);
expect(participantTokenCookie).toContain('HttpOnly');
expect(participantTokenCookie).toContain('SameSite=Strict');
expect(participantTokenCookie).toContain('Path=/');
};
export const expectValidRecordingTokenResponse = (
response: any,
roomId: string,
@ -523,7 +557,8 @@ export const expectValidRecordingTokenResponse = (
expect(response.status).toBe(200);
expect(response.body).toHaveProperty('token');
const decodedToken = decodeJWTToken(response.body.token);
const token = response.body.token;
const decodedToken = decodeJWTToken(token);
expect(decodedToken).toHaveProperty('video', {
room: roomId
@ -535,6 +570,18 @@ export const expectValidRecordingTokenResponse = (
canRetrieveRecordings,
canDeleteRecordings
});
// Check that the token is included in a cookie
expect(response.headers['set-cookie']).toBeDefined();
const cookies = response.headers['set-cookie'] as unknown as string[];
const participantTokenCookie = cookies.find((cookie) =>
cookie.startsWith(`${INTERNAL_CONFIG.RECORDING_TOKEN_COOKIE_NAME}=`)
) as string;
expect(participantTokenCookie).toBeDefined();
expect(participantTokenCookie).toContain(token);
expect(participantTokenCookie).toContain('HttpOnly');
expect(participantTokenCookie).toContain('SameSite=Strict');
expect(participantTokenCookie).toContain('Path=/');
};
const decodeJWTToken = (token: string) => {

View File

@ -307,14 +307,7 @@ export const getRoomRoleBySecret = async (roomId: string, secret: string) => {
return response;
};
/**
* Generates a participant token for a room and returns the cookie containing the token
*/
export const generateParticipantToken = async (
roomId: string,
participantName: string,
secret: string
): Promise<string> => {
export const generateParticipantToken = async (participantOptions: any) => {
checkAppIsRunning();
// Disable authentication to generate the token
@ -325,12 +318,25 @@ export const generateParticipantToken = async (
// Generate the participant token
const response = await request(app)
.post(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/participants/token`)
.send({
roomId,
participantName,
secret
})
.expect(200);
.send(participantOptions);
return response;
};
/**
* Generates a participant token for a room and returns the cookie containing the token
*/
export const generateParticipantTokenCookie = async (
roomId: string,
participantName: string,
secret: string
): Promise<string> => {
// Generate the participant token
const response = await generateParticipantToken({
roomId,
participantName,
secret
});
expect(response.status).toBe(200);
// Return the participant token cookie
const cookies = response.headers['set-cookie'] as unknown as string[];
@ -406,8 +412,6 @@ export const generateRecordingToken = async (roomId: string, secret: string) =>
* Generates a token for retrieving/deleting recordings from a room and returns the cookie containing the token
*/
export const generateRecordingTokenCookie = async (roomId: string, secret: string) => {
checkAppIsRunning();
// Generate the recording token
const response = await generateRecordingToken(roomId, secret);
expect(response.status).toBe(200);

View File

@ -6,7 +6,7 @@ import { MeetRoom } from '../../src/typings/ce';
import { expectValidStartRecordingResponse } from './assertion-helpers';
import {
createRoom,
generateParticipantToken,
generateParticipantTokenCookie,
joinFakeParticipant,
sleep,
startRecording,
@ -44,8 +44,8 @@ export const setupSingleRoom = async (withParticipant = false): Promise<RoomData
// Extract the room secrets and generate participant tokens, saved as cookies
const { moderatorSecret, publisherSecret } = MeetRoomHelper.extractSecretsFromRoom(room);
const [moderatorCookie, publisherCookie] = await Promise.all([
generateParticipantToken(room.roomId, 'MODERATOR', moderatorSecret),
generateParticipantToken(room.roomId, 'PUBLISHER', publisherSecret),
generateParticipantTokenCookie(room.roomId, 'MODERATOR', moderatorSecret),
generateParticipantTokenCookie(room.roomId, 'PUBLISHER', publisherSecret),
// Join participant if needed
withParticipant ? joinFakeParticipant(room.roomId, 'TEST_PARTICIPANT') : Promise.resolve()
]);

View File

@ -0,0 +1,125 @@
import { afterAll, beforeAll, describe, expect, it } from '@jest/globals';
import { ParticipantRole } from '../../../../src/typings/ce/participant.js';
import { expectValidationError, expectValidParticipantTokenResponse } from '../../../helpers/assertion-helpers.js';
import { deleteAllRooms, generateParticipantToken, startTestServer } from '../../../helpers/request-helpers.js';
import { RoomData, setupSingleRoom } from '../../../helpers/test-scenarios.js';
const participantName = 'TEST_PARTICIPANT';
describe('Participant API Tests', () => {
let roomData: RoomData;
beforeAll(async () => {
startTestServer();
roomData = await setupSingleRoom();
});
afterAll(async () => {
await deleteAllRooms();
});
describe('Generate Participant Token Tests', () => {
it('should generate a participant token with moderator permissions when using the moderator secret', async () => {
const response = await generateParticipantToken({
roomId: roomData.room.roomId,
participantName,
secret: roomData.moderatorSecret
});
expectValidParticipantTokenResponse(
response,
roomData.room.roomId,
participantName,
ParticipantRole.MODERATOR
);
});
it('should generate a participant token with publisher permissions when using the publisher secret', async () => {
const response = await generateParticipantToken({
roomId: roomData.room.roomId,
participantName,
secret: roomData.publisherSecret
});
expectValidParticipantTokenResponse(
response,
roomData.room.roomId,
participantName,
ParticipantRole.PUBLISHER
);
});
it('should fail with 409 when participant already exists in the room', async () => {
roomData = await setupSingleRoom(true);
const response = await generateParticipantToken({
roomId: roomData.room.roomId,
participantName,
secret: roomData.moderatorSecret
});
expect(response.status).toBe(409);
// Recreate the room without the participant
roomData = await setupSingleRoom();
});
it('should fail with 404 when room does not exist', async () => {
const response = await generateParticipantToken({
roomId: 'non_existent_room',
participantName,
secret: roomData.moderatorSecret
});
expect(response.status).toBe(404);
});
it('should fail with 400 when secret is invalid', async () => {
const response = await generateParticipantToken({
roomId: roomData.room.roomId,
participantName,
secret: 'invalid_secret'
});
expect(response.status).toBe(400);
});
});
describe('Generate Participant Token Validation Tests', () => {
it('should fail when roomId is not provided', async () => {
const response = await generateParticipantToken({
participantName,
secret: roomData.moderatorSecret
});
expectValidationError(response, 'roomId', 'Required');
});
it('should fail when participantName is not provided', async () => {
const response = await generateParticipantToken({
roomId: roomData.room.roomId,
secret: roomData.moderatorSecret
});
expectValidationError(response, 'participantName', 'Required');
});
it('should fail when secret is not provided', async () => {
const response = await generateParticipantToken({
roomId: roomData.room.roomId,
participantName
});
expectValidationError(response, 'secret', 'Required');
});
it('should fail when participantName is empty', async () => {
const response = await generateParticipantToken({
roomId: roomData.room.roomId,
participantName: '',
secret: roomData.moderatorSecret
});
expectValidationError(response, 'participantName', 'Participant name is required');
});
it('should fail when secret is empty', async () => {
const response = await generateParticipantToken({
roomId: roomData.room.roomId,
participantName,
secret: ''
});
expectValidationError(response, 'secret', 'Secret is required');
});
});
});