tests: update tests to reflect code changes and add test cases for update room status endpoint

This commit is contained in:
juancarmore 2025-09-05 01:12:17 +02:00
parent 1dd8cc3238
commit 2bea178e76
22 changed files with 921 additions and 502 deletions

View File

@ -3,11 +3,16 @@ import { container } from '../../src/config/dependency-injector.config';
import INTERNAL_CONFIG from '../../src/config/internal-config';
import { TokenService } from '../../src/services';
import {
MeetingEndAction,
MeetRecordingAccess,
MeetRecordingInfo,
MeetRecordingStatus,
MeetRoom,
MeetRoomAutoDeletionPolicy,
MeetRoomDeletionPolicyWithMeeting,
MeetRoomDeletionPolicyWithRecordings,
MeetRoomPreferences,
MeetRoomStatus,
ParticipantPermissions,
ParticipantRole
} from '../../src/typings/ce';
@ -91,7 +96,7 @@ export const expectSuccessRoomResponse = (
preferences?: MeetRoomPreferences
) => {
expect(response.status).toBe(200);
expectValidRoom(response.body, roomName, autoDeletionDate, preferences);
expectValidRoom(response.body, roomName, preferences, autoDeletionDate);
};
export const expectSuccessRoomPreferencesResponse = (response: any, preferences: MeetRoomPreferences) => {
@ -103,9 +108,11 @@ export const expectSuccessRoomPreferencesResponse = (response: any, preferences:
export const expectValidRoom = (
room: MeetRoom,
name: string,
autoDeletionDate?: number,
preferences?: MeetRoomPreferences,
markedForDeletion?: boolean
autoDeletionDate?: number,
autoDeletionPolicy?: MeetRoomAutoDeletionPolicy,
status?: MeetRoomStatus,
meetingEndAction?: MeetingEndAction
) => {
expect(room).toBeDefined();
@ -123,6 +130,16 @@ export const expectValidRoom = (
expect(room.autoDeletionDate).toBeUndefined();
}
if (autoDeletionPolicy !== undefined) {
expect(room.autoDeletionPolicy).toBeDefined();
expect(room.autoDeletionPolicy).toEqual(autoDeletionPolicy);
} else {
expect(room.autoDeletionPolicy).toEqual({
withMeeting: MeetRoomDeletionPolicyWithMeeting.WHEN_MEETING_ENDS,
withRecordings: MeetRoomDeletionPolicyWithRecordings.CLOSE
});
}
expect(room.preferences).toBeDefined();
if (preferences !== undefined) {
@ -143,11 +160,10 @@ export const expectValidRoom = (
expect(room.moderatorUrl).toContain(room.roomId);
expect(room.speakerUrl).toContain(room.roomId);
if (markedForDeletion !== undefined) {
expect(room.autoDeletionDate).toBeDefined();
expect(room.markedForDeletion).toBe(markedForDeletion ?? false);
}
expect(room.status).toBeDefined();
expect(room.status).toEqual(status || MeetRoomStatus.OPEN);
expect(room.meetingEndAction).toBeDefined();
expect(room.meetingEndAction).toEqual(meetingEndAction || MeetingEndAction.NONE);
};
export const expectValidRecording = (

View File

@ -22,6 +22,8 @@ import {
MeetRecordingInfo,
MeetRecordingStatus,
MeetRoom,
MeetRoomDeletionPolicyWithMeeting,
MeetRoomDeletionPolicyWithRecordings,
MeetRoomOptions,
ParticipantRole,
WebhookPreferences
@ -244,20 +246,18 @@ export const getRoom = async (roomId: string, fields?: string, cookie?: string,
export const getRoomPreferences = async (roomId: string) => {
checkAppIsRunning();
const adminCookie = await loginUser();
return await request(app)
.get(`${INTERNAL_CONFIG.API_BASE_PATH_V1}/rooms/${roomId}/preferences`)
.set('Cookie', adminCookie)
.set(INTERNAL_CONFIG.API_KEY_HEADER, MEET_INITIAL_API_KEY)
.send();
};
export const updateRoomPreferences = async (roomId: string, preferences: any) => {
checkAppIsRunning();
const adminCookie = await loginUser();
return await request(app)
.put(`${INTERNAL_CONFIG.API_BASE_PATH_V1}/rooms/${roomId}/preferences`)
.set('Cookie', adminCookie)
.set(INTERNAL_CONFIG.API_KEY_HEADER, MEET_INITIAL_API_KEY)
.send({ preferences });
};
@ -277,6 +277,15 @@ export const updateRecordingAccessPreferencesInRoom = async (roomId: string, rec
expect(response.status).toBe(200);
};
export const updateRoomStatus = async (roomId: string, status: string) => {
checkAppIsRunning();
return await request(app)
.put(`${INTERNAL_CONFIG.API_BASE_PATH_V1}/rooms/${roomId}/status`)
.set(INTERNAL_CONFIG.API_KEY_HEADER, MEET_INITIAL_API_KEY)
.send({ status });
};
export const deleteRoom = async (roomId: string, query: Record<string, any> = {}) => {
checkAppIsRunning();
@ -288,13 +297,17 @@ export const deleteRoom = async (roomId: string, query: Record<string, any> = {}
return result;
};
export const bulkDeleteRooms = async (roomIds: any[], force?: any) => {
export const bulkDeleteRooms = async (
roomIds: any[],
withMeeting?: string,
withRecordings?: string
) => {
checkAppIsRunning();
const result = await request(app)
.delete(`${INTERNAL_CONFIG.API_BASE_PATH_V1}/rooms`)
.set(INTERNAL_CONFIG.API_KEY_HEADER, MEET_INITIAL_API_KEY)
.query({ roomIds: roomIds.join(','), force });
.query({ roomIds: roomIds.join(','), withMeeting, withRecordings });
await sleep('1s');
return result;
};
@ -305,11 +318,8 @@ export const deleteAllRooms = async () => {
let nextPageToken: string | undefined;
do {
const response: any = await request(app)
.get(`${INTERNAL_CONFIG.API_BASE_PATH_V1}/rooms`)
.query({ fields: 'roomId', maxItems: 100, nextPageToken })
.set(INTERNAL_CONFIG.API_KEY_HEADER, MEET_INITIAL_API_KEY)
.expect(200);
const response = await getRooms({ fields: 'roomId', maxItems: 100, nextPageToken });
expect(response.status).toBe(200);
nextPageToken = response.body.pagination?.nextPageToken ?? undefined;
const roomIds = response.body.rooms.map((room: { roomId: string }) => room.roomId);
@ -318,13 +328,12 @@ export const deleteAllRooms = async () => {
break;
}
await request(app)
.delete(`${INTERNAL_CONFIG.API_BASE_PATH_V1}/rooms`)
.query({ roomIds: roomIds.join(','), force: true })
.set(INTERNAL_CONFIG.API_KEY_HEADER, MEET_INITIAL_API_KEY);
await bulkDeleteRooms(
roomIds,
MeetRoomDeletionPolicyWithMeeting.FORCE,
MeetRoomDeletionPolicyWithRecordings.FORCE
);
} while (nextPageToken);
await sleep('1s');
};
/**
@ -339,6 +348,7 @@ export const runRoomGarbageCollector = async () => {
const roomService = container.get(RoomService);
await (roomService as any)['deleteExpiredRooms']();
await sleep('1s');
};
export const runReleaseActiveRecordingLock = async (roomId: string) => {
@ -547,7 +557,7 @@ export const updateParticipant = async (
return response;
};
export const deleteParticipant = async (roomId: string, participantIdentity: string, moderatorCookie: string) => {
export const kickParticipant = async (roomId: string, participantIdentity: string, moderatorCookie: string) => {
checkAppIsRunning();
const response = await request(app)

View File

@ -71,8 +71,8 @@ describe('Meetings API Tests', () => {
it('should fail with 404 if the room does not exist', async () => {
// Delete the room to ensure it does not exist
let response = await deleteRoom(roomData.room.roomId, { force: true });
expect(response.status).toBe(204);
let response = await deleteRoom(roomData.room.roomId, { withMeeting: 'force' });
expect(response.status).toBe(200);
response = await endMeeting(roomData.room.roomId, roomData.moderatorCookie);
expect(response.status).toBe(404);

View File

@ -4,9 +4,9 @@ import { OpenViduMeetError } from '../../../../src/models/error.model.js';
import { LiveKitService } from '../../../../src/services/index.js';
import {
deleteAllRooms,
deleteParticipant,
deleteRoom,
disconnectFakeParticipants,
kickParticipant,
startTestServer
} from '../../../helpers/request-helpers.js';
import { RoomData, setupSingleRoom } from '../../../helpers/test-scenarios.js';
@ -27,19 +27,19 @@ describe('Meetings API Tests', () => {
await deleteAllRooms();
});
describe('Delete Participant Tests', () => {
describe('Kick Participant Tests', () => {
beforeEach(async () => {
roomData = await setupSingleRoom(true);
});
it('should remove participant from LiveKit room', async () => {
it('should kick participant from LiveKit room', async () => {
// Check if participant exists before deletion
const participant = await livekitService.getParticipant(roomData.room.roomId, participantIdentity);
expect(participant).toBeDefined();
expect(participant.identity).toBe(participantIdentity);
// Delete the participant
const response = await deleteParticipant(roomData.room.roomId, participantIdentity, roomData.moderatorCookie);
const response = await kickParticipant(roomData.room.roomId, participantIdentity, roomData.moderatorCookie);
expect(response.status).toBe(200);
// Check if the participant has been removed from LiveKit
@ -51,7 +51,7 @@ describe('Meetings API Tests', () => {
});
it('should fail with 404 if participant does not exist', async () => {
const response = await deleteParticipant(
const response = await kickParticipant(
roomData.room.roomId,
'NON_EXISTENT_PARTICIPANT',
roomData.moderatorCookie
@ -62,10 +62,10 @@ describe('Meetings API Tests', () => {
it('should fail with 404 if room does not exist', async () => {
// Delete the room to ensure it does not exist
let response = await deleteRoom(roomData.room.roomId, { force: true });
expect(response.status).toBe(204);
let response = await deleteRoom(roomData.room.roomId, { withMeeting: 'force' });
expect(response.status).toBe(200);
response = await deleteParticipant(roomData.room.roomId, participantIdentity, roomData.moderatorCookie);
response = await kickParticipant(roomData.room.roomId, participantIdentity, roomData.moderatorCookie);
expect(response.status).toBe(404);
expect(response.body.error).toBe('Room Error');
});

View File

@ -141,8 +141,8 @@ describe('Meetings API Tests', () => {
it('should fail with 404 if room does not exist', async () => {
// Delete the room to ensure it does not exist
let response = await deleteRoom(roomData.room.roomId, { force: true });
expect(response.status).toBe(204);
let response = await deleteRoom(roomData.room.roomId, { withMeeting: 'force' });
expect(response.status).toBe(200);
response = await updateParticipant(
roomData.room.roomId,

View File

@ -4,9 +4,11 @@ import { expectValidationError, expectValidParticipantTokenResponse } from '../.
import {
deleteAllRooms,
disconnectFakeParticipants,
endMeeting,
generateParticipantToken,
generateParticipantTokenCookie,
startTestServer
startTestServer,
updateRoomStatus
} from '../../../helpers/request-helpers.js';
import { RoomData, setupSingleRoom } from '../../../helpers/test-scenarios.js';
@ -125,6 +127,17 @@ describe('Participant API Tests', () => {
roomData = await setupSingleRoom();
});
it('should fail with 409 when room is closed', async () => {
await endMeeting(roomData.room.roomId, roomData.moderatorCookie);
await updateRoomStatus(roomData.room.roomId, 'closed');
const response = await generateParticipantToken({
roomId: roomData.room.roomId,
secret: roomData.moderatorSecret,
participantName
});
expect(response.status).toBe(409);
});
it('should fail with 404 when room does not exist', async () => {
const response = await generateParticipantToken({
roomId: 'non_existent_room',

View File

@ -30,7 +30,7 @@ describe('Recording API Tests', () => {
});
describe('Bulk Delete Recording Tests', () => {
it('should return 200 when mixed valid and non-existent IDs are provided', async () => {
it('should return 400 when mixed valid and non-existent IDs are provided', async () => {
const testContext = await setupMultiRecordingsTestContext(3, 3, 3);
const recordingIds = testContext.rooms.map((room) => room.recordingId);
const nonExistentIds = ['nonExistent--EG_000--1234', 'nonExistent--EG_111--5678'];
@ -38,10 +38,11 @@ describe('Recording API Tests', () => {
const deleteResponse = await bulkDeleteRecordings(mixedIds);
expect(deleteResponse.status).toBe(200);
expect(deleteResponse.status).toBe(400);
expect(deleteResponse.body).toEqual({
message: expect.stringContaining('could not be deleted'),
deleted: expect.arrayContaining(recordingIds),
notDeleted: expect.arrayContaining(
failed: expect.arrayContaining(
nonExistentIds.map((id) => ({
recordingId: id,
error: expect.stringContaining(`Recording '${id}' not found`)
@ -50,7 +51,7 @@ describe('Recording API Tests', () => {
});
});
it('should return 200 with mixed results when some recordings are in active state', async () => {
it('should return 400 with mixed results when some recordings are in active state', async () => {
const testContext = await setupMultiRecordingsTestContext(3, 3, 2);
const activeRecordingRoom = testContext.getLastRoom();
const recordingIds = testContext.rooms
@ -60,10 +61,11 @@ describe('Recording API Tests', () => {
const activeRecordingId = activeRecordingRoom?.recordingId;
let deleteResponse = await bulkDeleteRecordings([...recordingIds, activeRecordingId]);
expect(deleteResponse.status).toBe(200);
expect(deleteResponse.status).toBe(400);
expect(deleteResponse.body).toEqual({
message: expect.stringContaining('could not be deleted'),
deleted: expect.arrayContaining(recordingIds),
notDeleted: [
failed: [
{
recordingId: activeRecordingId,
error: expect.stringContaining(`Recording '${activeRecordingId}' is not stopped yet`)
@ -75,18 +77,22 @@ describe('Recording API Tests', () => {
deleteResponse = await bulkDeleteRecordings([activeRecordingId]);
expect(deleteResponse.status).toBe(204);
expect(deleteResponse.body).toStrictEqual({});
expect(deleteResponse.status).toBe(200);
expect(deleteResponse.body).toEqual({
message: expect.stringContaining('All recordings deleted successfully'),
deleted: expect.arrayContaining([activeRecordingId])
});
});
it('should not delete any recordings and return 200', async () => {
it('should not delete any recordings and return 400', async () => {
const testContext = await setupMultiRecordingsTestContext(2, 2, 0);
const recordingIds = testContext.rooms.map((room) => room.recordingId);
const deleteResponse = await bulkDeleteRecordings(recordingIds);
expect(deleteResponse.status).toBe(200);
expect(deleteResponse.status).toBe(400);
expect(deleteResponse.body).toEqual({
message: expect.stringContaining('could not be deleted'),
deleted: [],
notDeleted: expect.arrayContaining(
failed: expect.arrayContaining(
recordingIds.map((id) => ({
recordingId: id,
error: expect.stringContaining(`Recording '${id}' is not stopped yet`)
@ -101,12 +107,12 @@ describe('Recording API Tests', () => {
);
});
it('should delete all recordings and return 204 when all operations succeed', async () => {
it('should delete all recordings and return 200 when all operations succeed', async () => {
const response = await setupMultiRecordingsTestContext(5, 5, 5);
const recordingIds = response.rooms.map((room) => room.recordingId);
const deleteResponse = await bulkDeleteRecordings(recordingIds);
expect(deleteResponse.status).toBe(204);
expect(deleteResponse.status).toBe(200);
});
it('should only delete recordings belonging to the room when using a recording token', async () => {
@ -125,10 +131,11 @@ describe('Recording API Tests', () => {
// Intenta eliminar ambas grabaciones usando el token de la primera sala
const deleteResponse = await bulkDeleteRecordings([recordingId, otherRecordingId], recordingCookie);
expect(deleteResponse.status).toBe(200);
expect(deleteResponse.status).toBe(400);
expect(deleteResponse.body).toEqual({
message: expect.stringContaining('could not be deleted'),
deleted: [recordingId],
notDeleted: [
failed: [
{
recordingId: otherRecordingId,
error: expect.stringContaining(
@ -144,7 +151,7 @@ describe('Recording API Tests', () => {
const recordingIds = response.rooms.map((room) => room.recordingId);
const deleteResponse = await bulkDeleteRecordings(recordingIds);
expect(deleteResponse.status).toBe(204);
expect(deleteResponse.status).toBe(200);
const storageService = container.get(MeetStorageService);
@ -159,8 +166,7 @@ describe('Recording API Tests', () => {
const recordingId = testContext.rooms[0].recordingId;
const deleteResponse = await bulkDeleteRecordings([recordingId]);
expect(deleteResponse.status).toBe(204);
expect(deleteResponse.body).toStrictEqual({});
expect(deleteResponse.status).toBe(200);
});
it('should handle duplicate recording IDs by treating them as a single delete', async () => {
@ -168,8 +174,7 @@ describe('Recording API Tests', () => {
const recordingId = testContext.getRoomByIndex(0)!.recordingId;
const deleteResponse = await bulkDeleteRecordings([recordingId, recordingId]);
expect(deleteResponse.status).toBe(204);
expect(deleteResponse.body).toStrictEqual({});
expect(deleteResponse.status).toBe(200);
});
it('should delete room metadata when deleting the last recording', async () => {
@ -197,7 +202,7 @@ describe('Recording API Tests', () => {
await stopRecording(secondRecordingId, moderatorCookie);
// Delete first recording - room metadata should remain
const bulkResponse = await bulkDeleteRecordings([firstRecordingId, secondRecordingId]);
expect(bulkResponse.status).toBe(204);
expect(bulkResponse.status).toBe(200);
// // Verify second recording still exists
// const secondRecordingResponse = await getRecording(secondRecordingId);

View File

@ -44,7 +44,7 @@ describe('Recording API Tests', () => {
it('should delete a recording successfully', async () => {
// Delete the recording
const deleteResponse = await deleteRecording(recordingId);
expect(deleteResponse.status).toBe(204);
expect(deleteResponse.status).toBe(200);
// Verify that the recording is deleted
const getResponse = await deleteRecording(recordingId);
@ -61,7 +61,7 @@ describe('Recording API Tests', () => {
// Check that the room metadata still exists after deleteing the first recording
const deleteResponse = await deleteRecording(recordingId!);
expect(deleteResponse.status).toBe(204);
expect(deleteResponse.status).toBe(200);
recSecrets = await storageService.getAccessRecordingSecrets(recordingId);
expect(recSecrets).toBe(null);
@ -85,7 +85,7 @@ describe('Recording API Tests', () => {
// Check that the room metadata still exists after deleteing the first recording
let deleteResponse = await deleteRecording(recordingId!);
expect(deleteResponse.status).toBe(204);
expect(deleteResponse.status).toBe(200);
roomMetadata = await meetStorageService.getArchivedRoomMetadata(room.roomId);
@ -95,7 +95,7 @@ describe('Recording API Tests', () => {
// Delete the second recording
deleteResponse = await deleteRecording(secondRecordingId!);
expect(deleteResponse.status).toBe(204);
expect(deleteResponse.status).toBe(200);
// Verify that the room metadata is deleted after deleting the last recording
roomMetadata = await meetStorageService.getArchivedRoomMetadata(room.roomId);
@ -149,8 +149,7 @@ describe('Recording API Tests', () => {
it('should sanitize recordingId before validation', async () => {
const response = await deleteRecording(` ${recordingId} `);
expect(response.status).toBe(204);
expect(response.body).toStrictEqual({});
expect(response.status).toBe(200);
});
it('should return 409 when attempting to delete an active recording', async () => {
@ -164,7 +163,7 @@ describe('Recording API Tests', () => {
await stopRecording(activeRecordingId, moderatorCookie);
// Attempt to delete the recording again
deleteResponse = await deleteRecording(activeRecordingId);
expect(deleteResponse.status).toBe(204);
expect(deleteResponse.status).toBe(200);
});
});
});

View File

@ -434,7 +434,7 @@ describe('Recording API Race Conditions Tests', () => {
streamResponse.status === 'fulfilled' &&
(streamResponse.value.status === 200 || streamResponse.value.status === 206);
const deleteSuccessful = deleteResponse.status === 'fulfilled' && deleteResponse.value.status === 204;
const deleteSuccessful = deleteResponse.status === 'fulfilled' && deleteResponse.value.status === 200;
console.log(`Stream successful: ${streamSuccessful}, Delete successful: ${deleteSuccessful}`);
@ -485,8 +485,7 @@ describe('Recording API Race Conditions Tests', () => {
// Both operations should complete successfully
const [bulkDeleteResult, newRecordingResult] = await Promise.all([bulkDeletePromise, startNewRecordingPromise]);
expect(bulkDeleteResult.status).toBe(204);
expect(bulkDeleteResult.body).toEqual({});
expect(bulkDeleteResult.status).toBe(200);
// Check that the new recording started successfully
expectValidStartRecordingResponse(newRecordingResult, room3.room.roomId, room3.room.roomName);

View File

@ -1,224 +1,150 @@
import { afterEach, beforeAll, describe, expect, it } from '@jest/globals';
import { afterAll, beforeAll, describe, expect, it } from '@jest/globals';
import {
MeetingEndAction,
MeetRoom,
MeetRoomDeletionErrorCode,
MeetRoomDeletionPolicyWithMeeting,
MeetRoomDeletionPolicyWithRecordings,
MeetRoomDeletionSuccessCode,
MeetRoomStatus
} from '../../../../src/typings/ce/room.js';
import { expectValidRoom } from '../../../helpers/assertion-helpers.js';
import {
bulkDeleteRooms,
createRoom,
deleteAllRecordings,
deleteAllRooms,
disconnectFakeParticipants,
endMeeting,
getRoom,
joinFakeParticipant,
sleep,
startTestServer
} from '../../../helpers/request-helpers.js';
import { setupSingleRoom, setupSingleRoomWithRecording } from '../../../helpers/test-scenarios.js';
describe('Room API Tests', () => {
beforeAll(() => {
startTestServer();
});
afterEach(async () => {
// Remove all rooms created
afterAll(async () => {
await disconnectFakeParticipants();
await deleteAllRooms();
await deleteAllRecordings();
});
describe('Bulk Delete Room Tests', () => {
it('should return 204 when room does not exist (idempotent deletion)', async () => {
// The roomId will be transformed to string
const response = await bulkDeleteRooms([{ invalid: 'format' }], true);
expect(response.status).toBe(204);
expect(response.body).toStrictEqual({});
});
it('should delete room (204) with invalid force parameter when no participants exist', async () => {
const { roomId } = await createRoom({ roomName: 'test-invalid-force' });
it('should return 200 when all rooms are processed for deletion successfully', async () => {
const { roomId } = await createRoom();
const response = await bulkDeleteRooms([roomId]);
//The bulk operation for only one room should be the same as deleting the room
expect(response.status).toBe(204);
expect(response.body).toStrictEqual({});
expect(response.status).toBe(200);
expect(response.body).toEqual({
message: 'All rooms successfully processed for deletion',
successful: expect.arrayContaining([
{
roomId,
successCode: MeetRoomDeletionSuccessCode.ROOM_DELETED,
message: expect.any(String)
}
])
});
});
it('should mark room for deletion (202) with invalid force parameter when participants exist', async () => {
const { roomId } = await createRoom({ roomName: 'test-invalid-force' });
it('should return 400 when some rooms fail to process for deletion', async () => {
const room1 = await createRoom();
const { room: room2 } = await setupSingleRoom(true);
await joinFakeParticipant(roomId, 'test-participant-1');
const response = await bulkDeleteRooms([roomId]);
//The bulk operation for only one room should be the same as deleting the room
expect(response.status).toBe(202);
expect(response.body.message).toContain('marked for deletion');
const response = await bulkDeleteRooms([room1.roomId, room2.roomId]);
expect(response.status).toBe(400);
expect(response.body).toEqual({
message: '1 room(s) failed to process while deleting',
successful: expect.arrayContaining([
{
roomId: room1.roomId,
successCode: MeetRoomDeletionSuccessCode.ROOM_DELETED,
message: expect.any(String)
}
]),
failed: expect.arrayContaining([
{
roomId: room2.roomId,
error: MeetRoomDeletionErrorCode.ROOM_HAS_ACTIVE_MEETING,
message: expect.any(String)
}
])
});
});
it('should delete room (204) with force=true parameter when no participants exist', async () => {
const { roomId } = await createRoom({ roomName: 'test-force' });
it('should return 400 when all rooms fail to process for deletion', async () => {
const { room } = await setupSingleRoom(true);
const response = await bulkDeleteRooms([roomId], true);
//The bulk operation for only one room should be the same as deleting the room
expect(response.status).toBe(204);
expect(response.body).toStrictEqual({});
});
it('should delete room (204) with force=true parameter when participants exist', async () => {
const { roomId } = await createRoom({ roomName: 'test-force' });
await joinFakeParticipant(roomId, 'test-participant-1');
const response = await bulkDeleteRooms([roomId], true);
//The bulk operation for only one room should be the same as deleting the room
expect(response.status).toBe(204);
expect(response.body).toStrictEqual({});
const response = await bulkDeleteRooms([room.roomId]);
expect(response.status).toBe(400);
expect(response.body).toEqual({
message: '1 room(s) failed to process while deleting',
successful: [],
failed: expect.arrayContaining([
{
roomId: room.roomId,
error: MeetRoomDeletionErrorCode.ROOM_HAS_ACTIVE_MEETING,
message: expect.any(String)
}
])
});
});
it('should successfully delete the room requesting the same roomId multiple times', async () => {
const { roomId } = await createRoom({ roomName: 'test-duplicate' });
const { roomId } = await createRoom();
const response = await bulkDeleteRooms([roomId, roomId, roomId], true);
expect(response.status).toBe(204);
expect(response.body).toStrictEqual({});
const response = await bulkDeleteRooms([roomId, roomId, roomId]);
expect(response.status).toBe(200);
expect(response.body).toEqual({
message: 'All rooms successfully processed for deletion',
successful: expect.arrayContaining([
{
roomId,
successCode: MeetRoomDeletionSuccessCode.ROOM_DELETED,
message: expect.any(String)
}
])
});
});
it('should successfully delete valid roomIds while ignoring invalid ones', async () => {
const { roomId } = await createRoom({ roomName: 'test-invalid-force' });
const { roomId } = await createRoom();
const response = await bulkDeleteRooms([roomId, '!!@##$']);
expect(response.status).toBe(204);
expect(response.body).toStrictEqual({});
});
it('should successfully delete multiple rooms with valid roomIds', async () => {
// Create test rooms
const [room1, room2] = await Promise.all([
createRoom({ roomName: 'test-bulk-1' }),
createRoom({ roomName: 'test-bulk-2' })
]);
// Delete both rooms
const response = await bulkDeleteRooms([room1.roomId, room2.roomId]);
expect(response.status).toBe(204);
expect(response.body).toStrictEqual({});
// Verify that the rooms are deleted
const getRoom1 = await getRoom(room1.roomId);
const getRoom2 = await getRoom(room2.roomId);
expect(getRoom1.status).toBe(404);
expect(getRoom2.status).toBe(404);
expect(getRoom1.body.message).toContain(`'${room1.roomId}' does not exist`);
expect(getRoom2.body.message).toContain(`'${room2.roomId}' does not exist`);
});
it('should successfully marked for deletion multiple rooms with valid roomIds', async () => {
// Create test rooms
const [room1, room2] = await Promise.all([
createRoom({ roomName: 'test-bulk-1' }),
createRoom({ roomName: 'test-bulk-2' })
]);
await Promise.all([
joinFakeParticipant(room1.roomId, 'test-participant-1'),
joinFakeParticipant(room2.roomId, 'test-participant-2')
]);
// Delete both rooms
const response = await bulkDeleteRooms([room1.roomId, room2.roomId]);
expect(response.status).toBe(202);
expect(response.body.message).toContain(`Rooms '${room1.roomId}, ${room2.roomId}' marked for deletion`);
expect(response.body.deleted).toBeUndefined();
// Verify that the rooms are marked for deletion
const getRoom1 = await getRoom(room1.roomId);
const getRoom2 = await getRoom(room2.roomId);
expect(getRoom1.status).toBe(200);
expect(getRoom2.status).toBe(200);
expect(getRoom1.body.roomId).toBe(room1.roomId);
expect(getRoom2.body.roomId).toBe(room2.roomId);
expect(getRoom1.body.markedForDeletion).toBe(true);
expect(getRoom2.body.markedForDeletion).toBe(true);
});
it('should sanitize roomIds before deleting', async () => {
// Create a test room
const { roomId } = await createRoom({ roomName: 'test-sanitize' });
const response = await bulkDeleteRooms([roomId + '!!@##$']);
expect(response.status).toBe(204);
expect(response.body).toStrictEqual({});
const deletedRoom = await getRoom(roomId);
expect(deletedRoom.status).toBe(404);
expect(deletedRoom.body.message).toContain(`'${roomId}' does not exist`);
});
it('should delete rooms when force=true and participants exist', async () => {
// Create test rooms
const [room1, room2] = await Promise.all([
createRoom({ roomName: 'test-bulk-1' }),
createRoom({ roomName: 'test-bulk-2' })
]);
// Join a participant to the rooms
await Promise.all([
joinFakeParticipant(room1.roomId, 'test-participant-1'),
joinFakeParticipant(room2.roomId, 'test-participant-2')
]);
// Attempt to delete the rooms with force=false
const response = await bulkDeleteRooms([room1.roomId, room2.roomId], true);
expect(response.status).toBe(204);
expect(response.body).toStrictEqual({});
// Verify that the rooms are deleted
await sleep('1s'); // Wait a bit for the meetings to be closed and the rooms deleted
const deletedRoom1 = await getRoom(room1.roomId);
const deletedRoom2 = await getRoom(room2.roomId);
expect(deletedRoom1.status).toBe(404);
expect(deletedRoom1.body.message).toContain(`'${room1.roomId}' does not exist`);
expect(deletedRoom2.status).toBe(404);
expect(deletedRoom2.body.message).toContain(`'${room2.roomId}' does not exist`);
});
it('should return mixed results (200) when some rooms are deleted and others marked for deletion', async () => {
// Create rooms
const room1 = await createRoom({ roomName: 'empty-room' });
const room2 = await createRoom({ roomName: 'occupied-room' });
// Add participant to only one room
await joinFakeParticipant(room2.roomId, 'test-participant');
// Delete both rooms (without force)
const response = await bulkDeleteRooms([room1.roomId, room2.roomId], false);
// Should return 200 with mixed results
expect(response.status).toBe(200);
expect(response.body.deleted).toHaveLength(1);
expect(response.body.deleted).toContain(room1.roomId);
expect(response.body.markedForDeletion).toHaveLength(1);
expect(response.body.markedForDeletion).toContain(room2.roomId);
// Verify deletion state
const getRoom1 = await getRoom(room1.roomId);
const getRoom2 = await getRoom(room2.roomId);
expect(getRoom1.status).toBe(404);
expect(getRoom2.status).toBe(200);
expect(response.body).toEqual({
message: 'All rooms successfully processed for deletion',
successful: expect.arrayContaining([
{
roomId,
successCode: MeetRoomDeletionSuccessCode.ROOM_DELETED,
message: expect.any(String)
}
])
});
});
it('should handle a large number of room IDs', async () => {
// Create 20+ rooms and test deletion
// Create 20 rooms
const rooms = await Promise.all(
Array.from({ length: 20 }, (_, i) => createRoom({ roomName: `bulk-${i}` }))
);
const response = await bulkDeleteRooms(rooms.map((r) => r.roomId));
expect(response.status).toBe(204);
expect(response.status).toBe(200);
expect(response.body).toEqual({
message: 'All rooms successfully processed for deletion',
successful: expect.arrayContaining(
rooms.map((room) => ({
roomId: room.roomId,
successCode: MeetRoomDeletionSuccessCode.ROOM_DELETED,
message: expect.any(String)
}))
)
});
// Verify all rooms are deleted
for (const room of rooms) {
@ -227,39 +153,98 @@ describe('Room API Tests', () => {
}
});
it('should handle a large number of room IDs with mixed valid and invalid IDs', async () => {
// Create 20+ rooms and test deletion
const rooms = await Promise.all(
Array.from({ length: 20 }, (_, i) => createRoom({ roomName: `bulk-${i}` }))
);
await joinFakeParticipant(rooms[0].roomId, 'test-participant-1');
const response = await bulkDeleteRooms([
...rooms.map((r) => r.roomId),
'!!@##$',
',,,,',
'room-1',
'room-2'
it('should handle deletion when specifying withMeeting and withRecordings parameters', async () => {
const [room1, { room: room2 }, { room: room3 }, { room: room4, moderatorCookie }] = await Promise.all([
createRoom(), // Room without active meeting or recordings
setupSingleRoom(true), // Room with active meeting
setupSingleRoomWithRecording(true), // Room with active meeting and recordings
setupSingleRoomWithRecording(true) // Room with recordings
]);
await endMeeting(room4.roomId, moderatorCookie);
const fakeRoomId = 'fakeRoomId'; // Non-existing room
expect(response.status).toBe(200);
const response = await bulkDeleteRooms(
[room1.roomId, room2.roomId, room3.roomId, room4.roomId, fakeRoomId],
MeetRoomDeletionPolicyWithMeeting.WHEN_MEETING_ENDS,
MeetRoomDeletionPolicyWithRecordings.CLOSE
);
expect(response.status).toBe(400);
expect(response.body).toEqual({
message: '1 room(s) failed to process while deleting',
successful: expect.arrayContaining([
{
roomId: room1.roomId,
successCode: MeetRoomDeletionSuccessCode.ROOM_DELETED,
message: expect.any(String)
},
{
roomId: room2.roomId,
successCode: MeetRoomDeletionSuccessCode.ROOM_WITH_ACTIVE_MEETING_SCHEDULED_TO_BE_DELETED,
message: expect.any(String),
room: expect.any(Object)
},
{
roomId: room3.roomId,
successCode: MeetRoomDeletionSuccessCode.ROOM_WITH_ACTIVE_MEETING_SCHEDULED_TO_BE_CLOSED,
message: expect.any(String),
room: expect.any(Object)
},
{
roomId: room4.roomId,
successCode: MeetRoomDeletionSuccessCode.ROOM_CLOSED,
message: expect.any(String),
room: expect.any(Object)
}
]),
failed: expect.arrayContaining([
{
roomId: fakeRoomId,
error: 'Room Error',
message: expect.stringContaining('does not exist')
}
])
});
// Verify all valid rooms are deleted
for (const room of rooms) {
if (room.roomId === rooms[0].roomId) {
continue; // Skip the room with a participant
}
const getResponse = await getRoom(room.roomId);
expect(getResponse.status).toBe(404);
}
// Verify the room with a participant is marked for deletion
const getResponse = await getRoom(rooms[0].roomId);
expect(getResponse.status).toBe(200);
expect(getResponse.body.markedForDeletion).toBe(true);
expect(getResponse.body.roomId).toBe(rooms[0].roomId);
// Check successful rooms properties
const successfulRoom2 = response.body.successful.find(
(s: { roomId: string; successCode: MeetRoomDeletionSuccessCode; message: string; room?: MeetRoom }) =>
s.room?.roomId === room2.roomId
);
expectValidRoom(
successfulRoom2.room,
successfulRoom2.room.roomName,
undefined,
undefined,
undefined,
MeetRoomStatus.ACTIVE_MEETING,
MeetingEndAction.DELETE
);
const successfulRoom3 = response.body.successful.find(
(r: { roomId: string; successCode: MeetRoomDeletionSuccessCode; message: string; room?: MeetRoom }) =>
r.room?.roomId === room3.roomId
);
expectValidRoom(
successfulRoom3.room,
successfulRoom3.room.roomName,
undefined,
undefined,
undefined,
MeetRoomStatus.ACTIVE_MEETING,
MeetingEndAction.CLOSE
);
const successfulRoom4 = response.body.successful.find(
(r: { roomId: string; successCode: MeetRoomDeletionSuccessCode; message: string; room?: MeetRoom }) =>
r.room?.roomId === room4.roomId
);
expectValidRoom(
successfulRoom4.room,
successfulRoom4.room.roomName,
undefined,
undefined,
undefined,
MeetRoomStatus.CLOSED,
MeetingEndAction.NONE
);
});
});
@ -293,5 +278,21 @@ describe('Room API Tests', () => {
'At least one valid roomId is required after sanitization'
);
});
it('should fail when withMeeting parameter is invalid', async () => {
const response = await bulkDeleteRooms(['testRoom'], 'invalid_value');
expect(response.status).toBe(422);
expect(response.body.error).toContain('Unprocessable Entity');
expect(JSON.stringify(response.body.details)).toContain('Invalid enum value');
});
it('should fail when withRecordings parameter is invalid', async () => {
const response = await bulkDeleteRooms(['testRoom'], 'force', 'invalid_value');
expect(response.status).toBe(422);
expect(response.body.error).toContain('Unprocessable Entity');
expect(JSON.stringify(response.body.details)).toContain('Invalid enum value');
});
});
});

View File

@ -3,7 +3,11 @@ import { Express } from 'express';
import ms from 'ms';
import request from 'supertest';
import INTERNAL_CONFIG from '../../../../src/config/internal-config.js';
import { MeetRecordingAccess } from '../../../../src/typings/ce/index.js';
import {
MeetRecordingAccess,
MeetRoomDeletionPolicyWithMeeting,
MeetRoomDeletionPolicyWithRecordings
} from '../../../../src/typings/ce/index.js';
import { expectValidRoom } from '../../../helpers/assertion-helpers.js';
import { createRoom, deleteAllRooms, loginUser, startTestServer } from '../../../helpers/request-helpers.js';
@ -25,6 +29,11 @@ describe('Room API Tests', () => {
});
describe('Room Creation Tests', () => {
it('Should create a room with default name when roomName is omitted', async () => {
const room = await createRoom();
expectValidRoom(room, 'Room');
});
it('Should create a room without autoDeletionDate (default behavior)', async () => {
const room = await createRoom({
roomName: ' Test Room '
@ -38,13 +47,17 @@ describe('Room API Tests', () => {
roomName: ' .,-------}{¡$#<+My Room *123 '
});
expectValidRoom(room, 'My Room 123', validAutoDeletionDate);
expectValidRoom(room, 'My Room 123', undefined, validAutoDeletionDate);
});
it('Should create a room when sending full valid payload', async () => {
const payload = {
roomName: ' =Example Room&/ ',
autoDeletionDate: validAutoDeletionDate,
autoDeletionPolicy: {
withMeeting: MeetRoomDeletionPolicyWithMeeting.FORCE,
withRecordings: MeetRoomDeletionPolicyWithRecordings.FORCE
},
preferences: {
recordingPreferences: {
enabled: false,
@ -57,7 +70,13 @@ describe('Room API Tests', () => {
const room = await createRoom(payload);
expectValidRoom(room, 'Example Room', validAutoDeletionDate, payload.preferences);
expectValidRoom(
room,
'Example Room',
payload.preferences,
validAutoDeletionDate,
payload.autoDeletionPolicy
);
});
});
@ -122,6 +141,63 @@ describe('Room API Tests', () => {
expect(JSON.stringify(response.body.details)).toContain('Expected number');
});
it('should fail when autoDeletionPolicy is not an object (string provided)', async () => {
const payload = {
roomName: 'TestRoom',
autoDeletionDate: validAutoDeletionDate,
autoDeletionPolicy: 'invalid-policy'
};
const response = await request(app).post(ROOMS_PATH).set('Cookie', adminCookie).send(payload).expect(422);
expect(JSON.stringify(response.body.details)).toContain('Expected object');
});
it('should fail when autoDeletionPolicy has invalid structure', async () => {
const payload = {
roomName: 'TestRoom',
autoDeletionDate: validAutoDeletionDate,
autoDeletionPolicy: {
withMeeting: 'invalid-value',
withRecordings: MeetRoomDeletionPolicyWithRecordings.CLOSE
}
};
const response = await request(app).post(ROOMS_PATH).set('Cookie', adminCookie).send(payload).expect(422);
expect(JSON.stringify(response.body.details)).toContain('Invalid enum value');
});
it('should fail when autoDeletionPolicy.withMeeting has FAIL policy', async () => {
const payload = {
roomName: 'TestRoom',
autoDeletionDate: validAutoDeletionDate,
autoDeletionPolicy: {
withMeeting: MeetRoomDeletionPolicyWithMeeting.FAIL,
withRecordings: MeetRoomDeletionPolicyWithRecordings.CLOSE
}
};
const response = await request(app).post(ROOMS_PATH).set('Cookie', adminCookie).send(payload).expect(422);
expect(JSON.stringify(response.body.details)).toContain('FAIL policy is not allowed for withMeeting auto-deletion policy');
});
it('should fail when autoDeletionPolicy.withRecordings has FAIL policy', async () => {
const payload = {
roomName: 'TestRoom',
autoDeletionDate: validAutoDeletionDate,
autoDeletionPolicy: {
withMeeting: MeetRoomDeletionPolicyWithMeeting.WHEN_MEETING_ENDS,
withRecordings: MeetRoomDeletionPolicyWithRecordings.FAIL
}
};
const response = await request(app).post(ROOMS_PATH).set('Cookie', adminCookie).send(payload).expect(422);
expect(JSON.stringify(response.body.details)).toContain('FAIL policy is not allowed for withRecordings auto-deletion policy');
});
it('should fail when roomName is not a string (number provided)', async () => {
const payload = {
roomName: 12345,

View File

@ -1,209 +1,342 @@
import { afterEach, beforeAll, describe, expect, it } from '@jest/globals';
import ms from 'ms';
import { expectValidRoom } from '../../../helpers/assertion-helpers.js';
import { afterAll, beforeAll, beforeEach, describe, expect, it } from '@jest/globals';
import {
MeetingEndAction,
MeetRoomDeletionErrorCode,
MeetRoomDeletionPolicyWithMeeting,
MeetRoomDeletionPolicyWithRecordings,
MeetRoomDeletionSuccessCode,
MeetRoomStatus
} from '../../../../src/typings/ce/room.js';
import { expectSuccessListRecordingResponse, expectValidRoom } from '../../../helpers/assertion-helpers.js';
import {
createRoom,
deleteAllRecordings,
deleteAllRooms,
deleteRoom,
disconnectFakeParticipants,
endMeeting,
getAllRecordings,
getRoom,
joinFakeParticipant,
sleep,
startTestServer
} from '../../../helpers/request-helpers.js';
import { RoomService } from '../../../../src/services/room.service.js';
import { container } from '../../../../src/config/dependency-injector.config.js';
import { setInternalConfig } from '../../../../src/config/internal-config.js';
import { setupSingleRoom, setupSingleRoomWithRecording } from '../../../helpers/test-scenarios.js';
describe('Room API Tests', () => {
beforeAll(() => {
startTestServer();
});
afterEach(async () => {
afterAll(async () => {
// Remove all rooms created
await disconnectFakeParticipants();
await deleteAllRooms();
await deleteAllRecordings();
});
describe('Delete Room Tests', () => {
it('should return 204 when room does not exist (idempotent deletion)', async () => {
const response = await deleteRoom('non-existent-room-id');
describe('without active meeting or recordings', () => {
it('should return 200 with successCode=room_deleted', async () => {
const { roomId } = await createRoom();
expect(response.status).toBe(204);
});
const response = await deleteRoom(roomId);
expect(response.status).toBe(200);
expect(response.body).toHaveProperty('successCode', MeetRoomDeletionSuccessCode.ROOM_DELETED);
it('should default to force=false when force parameter is invalid', async () => {
// Create a room first
const { roomId } = await createRoom({
roomName: 'test-room'
// Check room is deleted
const getResponse = await getRoom(roomId);
expect(getResponse.status).toBe(404);
});
const response = await deleteRoom(roomId, { force: 'not-a-boolean' });
expect(response.status).toBe(204);
// Verify it's deleted
const getResponse = await getRoom(roomId);
expect(getResponse.status).toBe(404);
});
it('should mark room for deletion when participants exist and force parameter is invalid', async () => {
// Create a room first
const { roomId } = await createRoom({
roomName: 'test-room'
describe('with active meeting but no recordings', () => {
let roomId: string;
let roomName: string;
let moderatorCookie: string;
beforeEach(async () => {
// Create a room with an active meeting
const { room, moderatorCookie: cookie } = await setupSingleRoom(true);
roomId = room.roomId;
roomName = room.roomName;
moderatorCookie = cookie;
});
await joinFakeParticipant(roomId, 'test-participant');
it('should return 200 with successCode=room_with_active_meeting_deleted when withMeeting=force', async () => {
const response = await deleteRoom(roomId, {
withMeeting: MeetRoomDeletionPolicyWithMeeting.FORCE
});
expect(response.status).toBe(200);
expect(response.body).toHaveProperty(
'successCode',
MeetRoomDeletionSuccessCode.ROOM_WITH_ACTIVE_MEETING_DELETED
);
// The force parameter is not a boolean so it should be defined as false
// and the room should be marked for deletion
const response = await deleteRoom(roomId, { force: 'not-a-boolean' });
// Check operation accepted
expect(response.status).toBe(202);
// The room should be marked for deletion
const roomResponse = await getRoom(roomId);
expect(roomResponse.body).toBeDefined();
expect(roomResponse.body.roomId).toBe(roomId);
expect(roomResponse.body.markedForDeletion).toBeDefined();
expect(roomResponse.body.markedForDeletion).toBe(true);
});
it('should delete an empty room completely (204)', async () => {
const { roomId } = await createRoom({ roomName: 'test-room' });
const response = await deleteRoom(roomId);
expect(response.status).toBe(204);
// Try to retrieve the room again
const responseAfterDelete = await getRoom(roomId);
expect(responseAfterDelete.status).toBe(404);
});
it('should sanitize roomId with spaces and special characters before deletion', async () => {
// Create a room first
const createdRoom = await createRoom({
roomName: 'test-mixed'
// Check room is deleted
const getResponse = await getRoom(roomId);
expect(getResponse.status).toBe(404);
});
// Add some spaces and special chars to the valid roomId
const modifiedId = ` ${createdRoom.roomId}!@# `;
const response = await deleteRoom(modifiedId);
it('should return 202 with successCode=room_with_active_meeting_scheduled_to_be_deleted when withMeeting=when_meeting_ends', async () => {
const response = await deleteRoom(roomId, {
withMeeting: MeetRoomDeletionPolicyWithMeeting.WHEN_MEETING_ENDS
});
expect(response.status).toBe(202);
expect(response.body).toHaveProperty(
'successCode',
MeetRoomDeletionSuccessCode.ROOM_WITH_ACTIVE_MEETING_SCHEDULED_TO_BE_DELETED
);
expectValidRoom(
response.body.room,
roomName,
undefined,
undefined,
undefined,
MeetRoomStatus.ACTIVE_MEETING,
MeetingEndAction.DELETE
);
// The validation should sanitize the ID and successfully delete
expect(response.status).toBe(204);
// Verify it's deleted
const getResponse = await getRoom(createdRoom.roomId);
expect(getResponse.status).toBe(404);
});
it('should handle explicit force=true for room with no participants', async () => {
const createdRoom = await createRoom({
roomName: 'test-room'
// End meeting and check the room is deleted
await endMeeting(roomId, moderatorCookie);
const getResponse = await getRoom(roomId);
expect(getResponse.status).toBe(404);
});
const response = await deleteRoom(createdRoom.roomId, { force: true });
expect(response.status).toBe(204);
// Try to retrieve the room again
const responseAfterDelete = await getRoom(createdRoom.roomId);
expect(responseAfterDelete.status).toBe(404);
it('should return 409 with error=room_has_active_meeting when withMeeting=fail', async () => {
const response = await deleteRoom(roomId, { withMeeting: MeetRoomDeletionPolicyWithMeeting.FAIL });
expect(response.status).toBe(409);
expect(response.body).toHaveProperty('error', MeetRoomDeletionErrorCode.ROOM_HAS_ACTIVE_MEETING);
});
});
it('should mark room for deletion (202) when participants exist and force=false', async () => {
const autoDeletionDate = Date.now() + ms('5h');
const { roomId } = await createRoom({
roomName: 'test-room',
autoDeletionDate
describe('with recordings but no active meeting', () => {
let roomId: string;
let roomName: string;
beforeEach(async () => {
// Create a room with recordings and end the meeting
const { room, moderatorCookie } = await setupSingleRoomWithRecording(true);
roomId = room.roomId;
roomName = room.roomName;
await endMeeting(roomId, moderatorCookie);
});
await joinFakeParticipant(roomId, 'test-participant');
it('should return 200 with successCode=room_and_recordings_deleted when withRecording=force', async () => {
const response = await deleteRoom(roomId, {
withRecordings: MeetRoomDeletionPolicyWithRecordings.FORCE
});
expect(response.status).toBe(200);
expect(response.body).toHaveProperty(
'successCode',
MeetRoomDeletionSuccessCode.ROOM_AND_RECORDINGS_DELETED
);
const response = await deleteRoom(roomId, { force: false });
expect(response.status).toBe(202);
const roomResponse = await getRoom(roomId);
expectValidRoom(roomResponse.body, 'test-room', autoDeletionDate, undefined, true);
});
it('should delete a room marked for deletion when the webhook room_finished is received', async () => {
const autoDeletionDate = Date.now() + ms('5h');
const { roomId } = await createRoom({
roomName: 'test-room',
autoDeletionDate
// Check the room and recordings are deleted
const roomResponse = await getRoom(roomId);
expect(roomResponse.status).toBe(404);
const recordingsResponse = await getAllRecordings({ roomId, maxItems: 1 });
expectSuccessListRecordingResponse(recordingsResponse, 0, false, false, 1);
});
const roomService = container.get(RoomService);
// Set MEETING_DEPARTURE_TIMEOUT to 1s to force the room to be closed immediately
setInternalConfig({
MEETING_DEPARTURE_TIMEOUT: '1s'
});
// Create livekit room with custom departure timeout
// This is needed to trigger the room_finished event
await roomService.createLivekitRoom(roomId);
it('should return 200 with successCode=room_closed when withRecording=close', async () => {
const response = await deleteRoom(roomId, {
withRecordings: MeetRoomDeletionPolicyWithRecordings.CLOSE
});
expect(response.status).toBe(200);
expect(response.body).toHaveProperty('successCode', MeetRoomDeletionSuccessCode.ROOM_CLOSED);
// Join a participant to the room
await joinFakeParticipant(roomId, 'test-participant');
// Check that the room is closed and recordings are not deleted
expectValidRoom(
response.body.room,
roomName,
undefined,
undefined,
undefined,
MeetRoomStatus.CLOSED,
MeetingEndAction.NONE
);
const response = await deleteRoom(roomId, { force: false });
expect(response.status).toBe(202);
const roomResponse = await getRoom(roomId);
expectValidRoom(roomResponse.body, 'test-room', autoDeletionDate, undefined, true);
await disconnectFakeParticipants();
// Wait for room deletion
await sleep('2s');
const responseAfterDelete = await getRoom(roomId);
expect(responseAfterDelete.status).toBe(404);
});
it('should force delete (204) room with active participants when force=true', async () => {
const { roomId } = await createRoom({
roomName: 'test-room'
const recordingsResponse = await getAllRecordings({ roomId, maxItems: 1 });
expectSuccessListRecordingResponse(recordingsResponse, 1, false, false, 1);
});
await joinFakeParticipant(roomId, 'test-participant');
const response = await deleteRoom(roomId, { force: true });
expect(response.status).toBe(204);
// Try to retrieve the room again
await sleep('1s'); // Wait a bit for the meeting to be closed and the room deleted
const responseAfterDelete = await getRoom(roomId);
expect(responseAfterDelete.status).toBe(404);
it('should return 409 with error=room_has_recordings when withRecording=fail', async () => {
const response = await deleteRoom(roomId, {
withRecordings: MeetRoomDeletionPolicyWithRecordings.FAIL
});
expect(response.status).toBe(409);
expect(response.body).toHaveProperty('error', MeetRoomDeletionErrorCode.ROOM_HAS_RECORDINGS);
});
});
it('should successfully delete a room already marked for deletion', async () => {
const { roomId } = await createRoom({ roomName: 'test-marked' });
describe('with active meeting and recordings', () => {
let roomId: string;
let roomName: string;
let moderatorCookie: string;
// First mark it for deletion
await joinFakeParticipant(roomId, 'test-participant');
beforeEach(async () => {
// Create a room with recordings, keep the meeting active
const { room, moderatorCookie: cookie } = await setupSingleRoomWithRecording(true);
roomId = room.roomId;
roomName = room.roomName;
moderatorCookie = cookie;
});
await deleteRoom(roomId, { force: false });
it('should return 200 with successCode=room_with_active_meeting_and_recordings_deleted when withMeeting=force and withRecording=force', async () => {
const response = await deleteRoom(roomId, {
withMeeting: MeetRoomDeletionPolicyWithMeeting.FORCE,
withRecordings: MeetRoomDeletionPolicyWithRecordings.FORCE
});
expect(response.status).toBe(200);
expect(response.body).toHaveProperty(
'successCode',
MeetRoomDeletionSuccessCode.ROOM_WITH_ACTIVE_MEETING_AND_RECORDINGS_DELETED
);
// Then try to delete it again
const response = await deleteRoom(roomId, { force: true });
expect(response.status).toBe(204);
});
// Check the room and recordings are deleted
const roomResponse = await getRoom(roomId);
expect(roomResponse.status).toBe(404);
const recordingsResponse = await getAllRecordings({ roomId, maxItems: 1 });
expectSuccessListRecordingResponse(recordingsResponse, 0, false, false, 1);
});
it('should handle repeated deletion of the same room gracefully', async () => {
const { roomId } = await createRoom({ roomName: 'test-idempotent' });
it('should return 200 with successCode=room_with_active_meeting_closed when withMeeting=force and withRecording=close', async () => {
const response = await deleteRoom(roomId, {
withMeeting: MeetRoomDeletionPolicyWithMeeting.FORCE,
withRecordings: MeetRoomDeletionPolicyWithRecordings.CLOSE
});
expect(response.status).toBe(200);
expect(response.body).toHaveProperty(
'successCode',
MeetRoomDeletionSuccessCode.ROOM_WITH_ACTIVE_MEETING_CLOSED
);
expectValidRoom(
response.body.room,
roomName,
undefined,
undefined,
undefined,
MeetRoomStatus.ACTIVE_MEETING,
MeetingEndAction.CLOSE
);
// Delete first time
const response1 = await deleteRoom(roomId);
expect(response1.status).toBe(204);
// Check that the room is closed and recordings are not deleted
const roomResponse = await getRoom(roomId);
expect(roomResponse.status).toBe(200);
expectValidRoom(
roomResponse.body,
roomName,
undefined,
undefined,
undefined,
MeetRoomStatus.CLOSED,
MeetingEndAction.NONE
);
// Delete second time - should still return 204 (no error)
const response2 = await deleteRoom(roomId);
expect(response2.status).toBe(204);
const recordingsResponse = await getAllRecordings({ roomId, maxItems: 1 });
expectSuccessListRecordingResponse(recordingsResponse, 1, false, false, 1);
});
it('should return 409 with error=room_with_active_meeting_has_recordings when withMeeting=force and withRecording=fail', async () => {
const response = await deleteRoom(roomId, {
withMeeting: MeetRoomDeletionPolicyWithMeeting.FORCE,
withRecordings: MeetRoomDeletionPolicyWithRecordings.FAIL
});
expect(response.status).toBe(409);
expect(response.body).toHaveProperty(
'error',
MeetRoomDeletionErrorCode.ROOM_WITH_ACTIVE_MEETING_HAS_RECORDINGS
);
});
it('should return 202 with successCode=room_with_active_meeting_and_recordings_scheduled_to_be_deleted when withMeeting=when_meeting_ends and withRecording=force', async () => {
const response = await deleteRoom(roomId, {
withMeeting: MeetRoomDeletionPolicyWithMeeting.WHEN_MEETING_ENDS,
withRecordings: MeetRoomDeletionPolicyWithRecordings.FORCE
});
expect(response.status).toBe(202);
expect(response.body).toHaveProperty(
'successCode',
MeetRoomDeletionSuccessCode.ROOM_WITH_ACTIVE_MEETING_AND_RECORDINGS_SCHEDULED_TO_BE_DELETED
);
expectValidRoom(
response.body.room,
roomName,
undefined,
undefined,
undefined,
MeetRoomStatus.ACTIVE_MEETING,
MeetingEndAction.DELETE
);
// End meeting and check the room and recordings are deleted
await endMeeting(roomId, moderatorCookie);
const roomResponse = await getRoom(roomId);
expect(roomResponse.status).toBe(404);
const recordingsResponse = await getAllRecordings({ roomId, maxItems: 1 });
expectSuccessListRecordingResponse(recordingsResponse, 0, false, false, 1);
});
it('should return 202 with successCode=room_with_active_meeting_scheduled_to_be_closed when withMeeting=when_meeting_ends and withRecording=close', async () => {
const response = await deleteRoom(roomId, {
withMeeting: MeetRoomDeletionPolicyWithMeeting.WHEN_MEETING_ENDS,
withRecordings: MeetRoomDeletionPolicyWithRecordings.CLOSE
});
expect(response.status).toBe(202);
expect(response.body).toHaveProperty(
'successCode',
MeetRoomDeletionSuccessCode.ROOM_WITH_ACTIVE_MEETING_SCHEDULED_TO_BE_CLOSED
);
expectValidRoom(
response.body.room,
roomName,
undefined,
undefined,
undefined,
MeetRoomStatus.ACTIVE_MEETING,
MeetingEndAction.CLOSE
);
// End meeting and check that the room is closed and recordings are not deleted
await endMeeting(roomId, moderatorCookie);
const roomResponse = await getRoom(roomId);
expect(roomResponse.status).toBe(200);
expectValidRoom(
roomResponse.body,
roomName,
undefined,
undefined,
undefined,
MeetRoomStatus.CLOSED,
MeetingEndAction.NONE
);
const recordingsResponse = await getAllRecordings({ roomId, maxItems: 1 });
expectSuccessListRecordingResponse(recordingsResponse, 1, false, false, 1);
});
it('should return 409 with error=room_with_active_meeting_has_recordings_cannot_schedule_deletion when withMeeting=when_meeting_ends and withRecording=fail', async () => {
const response = await deleteRoom(roomId, {
withMeeting: MeetRoomDeletionPolicyWithMeeting.WHEN_MEETING_ENDS,
withRecordings: MeetRoomDeletionPolicyWithRecordings.FAIL
});
expect(response.status).toBe(409);
expect(response.body).toHaveProperty(
'error',
MeetRoomDeletionErrorCode.ROOM_WITH_ACTIVE_MEETING_HAS_RECORDINGS_CANNOT_SCHEDULE_DELETION
);
});
it('should return 409 with error=room_with_recordings_has_active_meeting when withMeeting=fail', async () => {
const response = await deleteRoom(roomId, {
withMeeting: MeetRoomDeletionPolicyWithMeeting.FAIL,
withRecordings: MeetRoomDeletionPolicyWithRecordings.FORCE
});
expect(response.status).toBe(409);
expect(response.body).toHaveProperty(
'error',
MeetRoomDeletionErrorCode.ROOM_WITH_RECORDINGS_HAS_ACTIVE_MEETING
);
});
});
});
@ -217,12 +350,20 @@ describe('Room API Tests', () => {
expect(JSON.stringify(response.body.details)).toContain('roomId cannot be empty after sanitization');
});
it('should fail when force parameter is a number instead of boolean', async () => {
const response = await deleteRoom('testRoom', { force: { value: 123 } });
it('should fail when withMeeting parameter is invalid', async () => {
const response = await deleteRoom('testRoom', { withMeeting: 'invalid_value' });
expect(response.status).toBe(422);
expect(response.body.error).toContain('Unprocessable Entity');
expect(JSON.stringify(response.body.details)).toContain('Expected boolean, received object');
expect(JSON.stringify(response.body.details)).toContain('Invalid enum value');
});
it('should fail when withRecordings parameter is invalid', async () => {
const response = await deleteRoom('testRoom', { withRecordings: 'invalid_value' });
expect(response.status).toBe(422);
expect(response.body.error).toContain('Unprocessable Entity');
expect(JSON.stringify(response.body.details)).toContain('Invalid enum value');
});
});
});

View File

@ -1,19 +1,25 @@
import { afterEach, beforeAll, describe, expect, it } from '@jest/globals';
import { afterAll, beforeAll, describe, expect, it } from '@jest/globals';
import ms from 'ms';
import { setInternalConfig } from '../../../../src/config/internal-config.js';
import { MeetRoomHelper } from '../../../../src/helpers/room.helper.js';
import {
MeetRoomDeletionPolicyWithMeeting,
MeetRoomDeletionPolicyWithRecordings
} from '../../../../src/typings/ce/room.js';
import {
createRoom,
deleteAllRecordings,
deleteAllRooms,
disconnectFakeParticipants,
endMeeting,
generateParticipantTokenCookie,
getRoom,
getRooms,
joinFakeParticipant,
runRoomGarbageCollector,
sleep,
startRecording,
startTestServer
} from '../../../helpers/request-helpers.js';
import { container } from '../../../../src/config/dependency-injector.config.js';
import { RoomService } from '../../../../src/services/room.service.js';
describe('Room Garbage Collector Tests', () => {
beforeAll(() => {
@ -23,13 +29,14 @@ describe('Room Garbage Collector Tests', () => {
startTestServer();
});
afterEach(async () => {
afterAll(async () => {
// Remove all rooms created
await disconnectFakeParticipants();
await deleteAllRooms();
await deleteAllRecordings();
});
it('should delete a room with a past auto-deletion date if no participant is present', async () => {
it('should delete a room with a past auto-deletion date if no active meeting', async () => {
const createdRoom = await createRoom({
roomName: 'test-gc',
autoDeletionDate: Date.now() + ms('1s')
@ -48,20 +55,20 @@ describe('Room Garbage Collector Tests', () => {
expect(response.status).toBe(404);
});
it('should mark room for deletion but not delete when expiration date has passed and participants exist', async () => {
it('should schedule room to be deleted when expiration date has passed and there is a active meeting', async () => {
const createdRoom = await createRoom({
roomName: 'test-gc-participants',
autoDeletionDate: Date.now() + ms('1s')
});
await joinFakeParticipant(createdRoom.roomId, 'test-participant');
await runRoomGarbageCollector();
// The room should not be deleted but marked for deletion
// The room should not be deleted but scheduled for deletion
const response = await getRoom(createdRoom.roomId);
expect(response.status).toBe(200);
expect(response.body.markedForDeletion).toBe(true);
expect(response.body).toHaveProperty('status', 'active_meeting');
expect(response.body).toHaveProperty('meetingEndAction', 'delete');
});
it('should not touch a room with a future auto-deletion date', async () => {
@ -74,45 +81,32 @@ describe('Room Garbage Collector Tests', () => {
const response = await getRoom(createdRoom.roomId);
expect(response.status).toBe(200);
expect(response.body.markedForDeletion).toBeFalsy();
expect(response.body).toHaveProperty('status', 'open');
expect(response.body).toHaveProperty('meetingEndAction', 'none');
});
it('should delete a room after the last participant leaves when it was marked for deletion', async () => {
const { roomId } = await createRoom({
it('should delete a room scheduled for deletion when the the meeting ends', async () => {
const room = await createRoom({
roomName: 'test-gc-lifecycle',
autoDeletionDate: Date.now() + ms('1s')
});
await joinFakeParticipant(room.roomId, 'test-participant');
const roomService = container.get(RoomService);
// Set MEETING_DEPARTURE_TIMEOUT to 1s to force the room to be closed immediately
setInternalConfig({
MEETING_DEPARTURE_TIMEOUT: '1s'
});
// Create livekit room with custom departure timeout
// This is needed to trigger the room_finished event
await roomService.createLivekitRoom(roomId);
await joinFakeParticipant(roomId, 'test-participant');
// Wait for the auto-deletion date to pass
await sleep('1s');
// Should mark the room for deletion but not delete it yet
await runRoomGarbageCollector();
let response = await getRoom(roomId);
// The room should not be deleted but scheduled for deletion
let response = await getRoom(room.roomId);
expect(response.status).toBe(200);
expect(response.body.markedForDeletion).toBe(true);
expect(response.body.autoDeletionDate).toBeTruthy();
expect(response.body.autoDeletionDate).toBeLessThan(Date.now());
expect(response.body).toHaveProperty('status', 'active_meeting');
expect(response.body).toHaveProperty('meetingEndAction', 'delete');
await disconnectFakeParticipants();
// Wait to receive webhook room_finished
await sleep('1s');
// End the meeting
const { moderatorSecret } = MeetRoomHelper.extractSecretsFromRoom(room);
const moderatorCookie = await generateParticipantTokenCookie(room.roomId, moderatorSecret, 'moderator');
await endMeeting(room.roomId, moderatorCookie);
// Verify that the room is deleted
response = await getRoom(roomId);
response = await getRoom(room.roomId);
expect(response.status).toBe(404);
});
@ -123,14 +117,10 @@ describe('Room Garbage Collector Tests', () => {
await runRoomGarbageCollector();
let response = await getRoom(createdRoom.roomId);
const response = await getRoom(createdRoom.roomId);
expect(response.status).toBe(200);
await runRoomGarbageCollector();
response = await getRoom(createdRoom.roomId);
expect(response.status).toBe(200);
expect(response.body.markedForDeletion).toBeFalsy();
expect(response.body.autoDeletionDate).toBeFalsy();
expect(response.body).toHaveProperty('status', 'open');
expect(response.body).toHaveProperty('meetingEndAction', 'none');
});
it('should handle multiple expired rooms in one batch', async () => {
@ -161,11 +151,48 @@ describe('Room Garbage Collector Tests', () => {
expect(response.status).toBe(200); // Should still exist
}
}
});
const response = await getRooms();
const { body } = response;
it('should handle expired rooms correctly when specifying autoDeletionPolicy', async () => {
// Create both rooms in parallel
const [room1, room2] = await Promise.all([
createRoom({
roomName: 'test-gc-policy-1',
autoDeletionDate: Date.now() + ms('1s'),
autoDeletionPolicy: {
withMeeting: MeetRoomDeletionPolicyWithMeeting.FORCE,
withRecordings: MeetRoomDeletionPolicyWithRecordings.CLOSE
}
}),
createRoom({
roomName: 'test-gc-policy-2',
autoDeletionDate: Date.now() + ms('1s'),
autoDeletionPolicy: {
withMeeting: MeetRoomDeletionPolicyWithMeeting.WHEN_MEETING_ENDS,
withRecordings: MeetRoomDeletionPolicyWithRecordings.FORCE
}
})
]);
// Join participants
await joinFakeParticipant(room1.roomId, 'participant1');
await joinFakeParticipant(room2.roomId, 'participant2');
// Start recording
const { moderatorSecret } = MeetRoomHelper.extractSecretsFromRoom(room1);
const moderatorCookie = await generateParticipantTokenCookie(room1.roomId, moderatorSecret, 'moderator');
await startRecording(room1.roomId, moderatorCookie);
await runRoomGarbageCollector();
const response = await getRoom(room1.roomId);
expect(response.status).toBe(200);
expect(body.rooms.length).toBe(2); // Only 2 rooms should remain
expect(response.body).toHaveProperty('status', 'closed');
expect(response.body).toHaveProperty('meetingEndAction', 'none');
const response2 = await getRoom(room2.roomId);
expect(response2.status).toBe(200);
expect(response2.body).toHaveProperty('status', 'active_meeting');
expect(response2.body).toHaveProperty('meetingEndAction', 'delete');
});
});

View File

@ -82,7 +82,7 @@ describe('Room API Tests', () => {
expectSuccessRoomsResponse(response, 3, 3, true, true);
rooms.forEach((room: MeetRoom, i: number) => {
expectValidRoom(room, `test-room-${i}`, validAutoDeletionDate);
expectValidRoom(room, `test-room-${i}`, undefined, validAutoDeletionDate);
});
const nextPageToken = pagination.nextPageToken;
@ -90,7 +90,7 @@ describe('Room API Tests', () => {
({ pagination, rooms } = response.body);
expectSuccessRoomsResponse(response, 3, 3, false, false);
rooms.forEach((room: MeetRoom, i: number) => {
expectValidRoom(room, `test-room-${i + 3}`, validAutoDeletionDate);
expectValidRoom(room, `test-room-${i + 3}`, undefined, validAutoDeletionDate);
});
});

View File

@ -21,7 +21,7 @@ describe('Room API Tests', () => {
await deleteAllRooms();
});
describe('Update Room Tests', () => {
describe('Update Room Preferences Tests', () => {
let frontendEventService: FrontendEventService;
beforeAll(() => {
@ -113,7 +113,7 @@ describe('Room API Tests', () => {
});
});
describe('Update Room Validation failures', () => {
describe('Update Room Preferences Validation failures', () => {
it('should fail when preferences have incorrect structure', async () => {
const { roomId } = await createRoom({
roomName: 'validation-test'

View File

@ -0,0 +1,103 @@
import { afterEach, beforeAll, describe, expect, it } from '@jest/globals';
import {
createRoom,
deleteAllRooms,
disconnectFakeParticipants,
endMeeting,
getRoom,
startTestServer,
updateRoomStatus
} from '../../../helpers/request-helpers.js';
import { setupSingleRoom } from '../../../helpers/test-scenarios.js';
describe('Room API Tests', () => {
beforeAll(() => {
startTestServer();
});
afterEach(async () => {
await disconnectFakeParticipants();
await deleteAllRooms();
});
describe('Update Room Status Tests', () => {
it('should successfully update room status to open', async () => {
const createdRoom = await createRoom({
roomName: 'update-test'
});
// Update the room status
const response = await updateRoomStatus(createdRoom.roomId, 'open');
expect(response.status).toBe(200);
expect(response.body).toHaveProperty('message');
// Verify with a get request
const getResponse = await getRoom(createdRoom.roomId);
expect(getResponse.status).toBe(200);
expect(getResponse.body.status).toEqual('open');
});
it('should successfully update room status to closed', async () => {
const createdRoom = await createRoom({
roomName: 'update-test'
});
// Update the room status
const response = await updateRoomStatus(createdRoom.roomId, 'closed');
expect(response.status).toBe(200);
expect(response.body).toHaveProperty('message');
// Verify with a get request
const getResponse = await getRoom(createdRoom.roomId);
expect(getResponse.status).toBe(200);
expect(getResponse.body.status).toEqual('closed');
});
it('should schedule room to be closed when meeting ends if there is an active meeting', async () => {
const roomData = await setupSingleRoom(true);
// Update the room status
const response = await updateRoomStatus(roomData.room.roomId, 'closed');
expect(response.status).toBe(202);
expect(response.body).toHaveProperty('message');
// Verify with a get request
let getResponse = await getRoom(roomData.room.roomId);
expect(getResponse.status).toBe(200);
expect(getResponse.body.status).toEqual('active_meeting');
expect(getResponse.body.meetingEndAction).toEqual('close');
// End meeting and verify closed status
await endMeeting(roomData.room.roomId, roomData.moderatorCookie);
getResponse = await getRoom(roomData.room.roomId);
expect(getResponse.status).toBe(200);
expect(getResponse.body.status).toEqual('closed');
expect(getResponse.body.meetingEndAction).toEqual('none');
});
it('should fail with 404 when updating non-existent room', async () => {
const nonExistentRoomId = 'non-existent-room';
const response = await updateRoomStatus(nonExistentRoomId, 'closed');
expect(response.status).toBe(404);
expect(response.body.message).toContain(`'${nonExistentRoomId}' does not exist`);
});
});
describe('Update Room Status Validation failures', () => {
it('should fail when status is invalid', async () => {
const { roomId } = await createRoom({
roomName: 'validation-test'
});
// Invalid status
const response = await updateRoomStatus(roomId, 'invalid_status');
expect(response.status).toBe(422);
expect(response.body.error).toContain('Unprocessable Entity');
expect(JSON.stringify(response.body.details)).toContain('Invalid enum value');
});
});
});

View File

@ -27,7 +27,6 @@ describe('Authentication API Tests', () => {
.expect(200);
expect(response.body).toHaveProperty('message');
expect(response.body.message).toBe('Login succeeded');
// Check for access token and refresh token cookies
expect(response.headers['set-cookie']).toBeDefined();
@ -144,7 +143,6 @@ describe('Authentication API Tests', () => {
.expect(200);
expect(response.body).toHaveProperty('message');
expect(response.body.message).toBe('Token refreshed');
// Check for new access token cookie
const newCookies = response.headers['set-cookie'] as unknown as string[];
@ -235,7 +233,7 @@ describe('Authentication API Tests', () => {
it('should delete all API keys', async () => {
const apiKey = await generateApiKey();
await request(app).delete(`${AUTH_PATH}/api-keys`).set('Cookie', adminCookie).expect(204);
await request(app).delete(`${AUTH_PATH}/api-keys`).set('Cookie', adminCookie).expect(200);
// Confirm deletion
const getResponse = await getApiKeys();

View File

@ -141,7 +141,7 @@ describe('Meeting API Security Tests', () => {
});
});
describe('Delete Participant from Meeting Tests', () => {
describe('Kick Participant from Meeting Tests', () => {
const PARTICIPANT_IDENTITY = 'TEST_PARTICIPANT';
it('should fail when request includes API key', async () => {

View File

@ -428,7 +428,7 @@ describe('Recording API Security Tests', () => {
/*
Use a simulated recording ID matching the API's expected format.
This allows testing the delete endpoint logic without deleting a real recording.
As a result, all successful delete tests will expect a 404 Not Found response.
As a result, all successful delete tests will expect a 400 response with failed recordings.
*/
fakeRecordingId = `${roomData.room.roomId}--EG_xxx--uid`;
});
@ -438,7 +438,7 @@ describe('Recording API Security Tests', () => {
.delete(RECORDINGS_PATH)
.query({ recordingIds: fakeRecordingId })
.set(INTERNAL_CONFIG.API_KEY_HEADER, MEET_INITIAL_API_KEY);
expect(response.status).toBe(200);
expect(response.status).toBe(400);
});
it('should succeed when user is authenticated as admin', async () => {
@ -446,7 +446,7 @@ describe('Recording API Security Tests', () => {
.delete(RECORDINGS_PATH)
.query({ recordingIds: fakeRecordingId })
.set('Cookie', adminCookie);
expect(response.status).toBe(200);
expect(response.status).toBe(400);
});
it('should fail when recording access is admin-moderator-speaker and participant is speaker', async () => {
@ -480,7 +480,7 @@ describe('Recording API Security Tests', () => {
.delete(RECORDINGS_PATH)
.query({ recordingIds: fakeRecordingId })
.set('Cookie', recordingCookie);
expect(response.status).toBe(200);
expect(response.status).toBe(400);
});
it('should fail when recording access is admin-moderator and participant is speaker', async () => {
@ -508,7 +508,7 @@ describe('Recording API Security Tests', () => {
.delete(RECORDINGS_PATH)
.query({ recordingIds: fakeRecordingId })
.set('Cookie', recordingCookie);
expect(response.status).toBe(200);
expect(response.status).toBe(400);
});
});

View File

@ -85,7 +85,7 @@ describe('Room API Security Tests', () => {
.delete(ROOMS_PATH)
.query({ roomIds: roomId })
.set(INTERNAL_CONFIG.API_KEY_HEADER, MEET_INITIAL_API_KEY);
expect(response.status).toBe(204);
expect(response.status).toBe(200);
});
it('should succeed when user is authenticated as admin', async () => {
@ -93,7 +93,7 @@ describe('Room API Security Tests', () => {
.delete(ROOMS_PATH)
.query({ roomIds: roomId })
.set('Cookie', adminCookie);
expect(response.status).toBe(204);
expect(response.status).toBe(200);
});
it('should fail when user is not authenticated', async () => {
@ -165,12 +165,12 @@ describe('Room API Security Tests', () => {
const response = await request(app)
.delete(`${ROOMS_PATH}/${roomId}`)
.set(INTERNAL_CONFIG.API_KEY_HEADER, MEET_INITIAL_API_KEY);
expect(response.status).toBe(204);
expect(response.status).toBe(200);
});
it('should succeed when user is authenticated as admin', async () => {
const response = await request(app).delete(`${ROOMS_PATH}/${roomId}`).set('Cookie', adminCookie);
expect(response.status).toBe(204);
expect(response.status).toBe(200);
});
it('should fail when user is not authenticated', async () => {
@ -283,6 +283,38 @@ describe('Room API Security Tests', () => {
});
});
describe('Update Room Status Tests', () => {
let roomId: string;
beforeAll(async () => {
const room = await createRoom();
roomId = room.roomId;
});
it('should succeed when request includes API key', async () => {
const response = await request(app)
.put(`${ROOMS_PATH}/${roomId}/status`)
.set(INTERNAL_CONFIG.API_KEY_HEADER, MEET_INITIAL_API_KEY)
.send({ status: 'open' });
expect(response.status).toBe(200);
});
it('should succeed when user is authenticated as admin', async () => {
const response = await request(app)
.put(`${ROOMS_PATH}/${roomId}/status`)
.set('Cookie', adminCookie)
.send({ status: 'open' });
expect(response.status).toBe(200);
});
it('should fail when user is not authenticated', async () => {
const response = await request(app)
.put(`${ROOMS_PATH}/${roomId}/status`)
.send({ status: 'open' });
expect(response.status).toBe(401);
});
});
describe('Generate Recording Token Tests', () => {
let roomData: RoomData;

View File

@ -16,7 +16,6 @@ describe('Users API Tests', () => {
const newPassword = 'newpassword123';
const response = await changePassword(MEET_INITIAL_ADMIN_PASSWORD, newPassword, adminCookie);
expect(response.status).toBe(200);
expect(response.body).toHaveProperty('message', 'Password changed successfully');
// Reset password
await changePassword(newPassword, MEET_INITIAL_ADMIN_PASSWORD, adminCookie);

View File

@ -114,7 +114,7 @@ describe('Webhook Integration Tests', () => {
const context = await setupSingleRoom(true);
const roomData = context.room;
// Forcefully delete the room
await deleteRoom(roomData.roomId, { force: true });
await deleteRoom(roomData.roomId, { withMeeting: 'force' });
// Verify 'meetingEnded' webhook is sent
expect(receivedWebhooks.length).toBeGreaterThanOrEqual(1);