test: add integration tests for generating participant tokens and validate responses
This commit is contained in:
parent
9630fa475d
commit
26103ab52a
@ -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 = (
|
export const expectValidRecordingTokenResponse = (
|
||||||
response: any,
|
response: any,
|
||||||
roomId: string,
|
roomId: string,
|
||||||
@ -523,7 +557,8 @@ export const expectValidRecordingTokenResponse = (
|
|||||||
expect(response.status).toBe(200);
|
expect(response.status).toBe(200);
|
||||||
expect(response.body).toHaveProperty('token');
|
expect(response.body).toHaveProperty('token');
|
||||||
|
|
||||||
const decodedToken = decodeJWTToken(response.body.token);
|
const token = response.body.token;
|
||||||
|
const decodedToken = decodeJWTToken(token);
|
||||||
|
|
||||||
expect(decodedToken).toHaveProperty('video', {
|
expect(decodedToken).toHaveProperty('video', {
|
||||||
room: roomId
|
room: roomId
|
||||||
@ -535,6 +570,18 @@ export const expectValidRecordingTokenResponse = (
|
|||||||
canRetrieveRecordings,
|
canRetrieveRecordings,
|
||||||
canDeleteRecordings
|
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) => {
|
const decodeJWTToken = (token: string) => {
|
||||||
|
|||||||
@ -307,14 +307,7 @@ export const getRoomRoleBySecret = async (roomId: string, secret: string) => {
|
|||||||
return response;
|
return response;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
export const generateParticipantToken = async (participantOptions: any) => {
|
||||||
* 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> => {
|
|
||||||
checkAppIsRunning();
|
checkAppIsRunning();
|
||||||
|
|
||||||
// Disable authentication to generate the token
|
// Disable authentication to generate the token
|
||||||
@ -325,12 +318,25 @@ export const generateParticipantToken = async (
|
|||||||
// Generate the participant token
|
// Generate the participant token
|
||||||
const response = await request(app)
|
const response = await request(app)
|
||||||
.post(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/participants/token`)
|
.post(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/participants/token`)
|
||||||
.send({
|
.send(participantOptions);
|
||||||
roomId,
|
return response;
|
||||||
participantName,
|
};
|
||||||
secret
|
|
||||||
})
|
/**
|
||||||
.expect(200);
|
* 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
|
// Return the participant token cookie
|
||||||
const cookies = response.headers['set-cookie'] as unknown as string[];
|
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
|
* 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) => {
|
export const generateRecordingTokenCookie = async (roomId: string, secret: string) => {
|
||||||
checkAppIsRunning();
|
|
||||||
|
|
||||||
// Generate the recording token
|
// Generate the recording token
|
||||||
const response = await generateRecordingToken(roomId, secret);
|
const response = await generateRecordingToken(roomId, secret);
|
||||||
expect(response.status).toBe(200);
|
expect(response.status).toBe(200);
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import { MeetRoom } from '../../src/typings/ce';
|
|||||||
import { expectValidStartRecordingResponse } from './assertion-helpers';
|
import { expectValidStartRecordingResponse } from './assertion-helpers';
|
||||||
import {
|
import {
|
||||||
createRoom,
|
createRoom,
|
||||||
generateParticipantToken,
|
generateParticipantTokenCookie,
|
||||||
joinFakeParticipant,
|
joinFakeParticipant,
|
||||||
sleep,
|
sleep,
|
||||||
startRecording,
|
startRecording,
|
||||||
@ -44,8 +44,8 @@ export const setupSingleRoom = async (withParticipant = false): Promise<RoomData
|
|||||||
// Extract the room secrets and generate participant tokens, saved as cookies
|
// Extract the room secrets and generate participant tokens, saved as cookies
|
||||||
const { moderatorSecret, publisherSecret } = MeetRoomHelper.extractSecretsFromRoom(room);
|
const { moderatorSecret, publisherSecret } = MeetRoomHelper.extractSecretsFromRoom(room);
|
||||||
const [moderatorCookie, publisherCookie] = await Promise.all([
|
const [moderatorCookie, publisherCookie] = await Promise.all([
|
||||||
generateParticipantToken(room.roomId, 'MODERATOR', moderatorSecret),
|
generateParticipantTokenCookie(room.roomId, 'MODERATOR', moderatorSecret),
|
||||||
generateParticipantToken(room.roomId, 'PUBLISHER', publisherSecret),
|
generateParticipantTokenCookie(room.roomId, 'PUBLISHER', publisherSecret),
|
||||||
// Join participant if needed
|
// Join participant if needed
|
||||||
withParticipant ? joinFakeParticipant(room.roomId, 'TEST_PARTICIPANT') : Promise.resolve()
|
withParticipant ? joinFakeParticipant(room.roomId, 'TEST_PARTICIPANT') : Promise.resolve()
|
||||||
]);
|
]);
|
||||||
|
|||||||
@ -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');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
Loading…
x
Reference in New Issue
Block a user