backend: replace roomIdPrefix with roomName

This commit is contained in:
juancarmore 2025-08-05 16:36:03 +02:00
parent d8e6da5b4b
commit 459537ecfb
18 changed files with 152 additions and 135 deletions

View File

@ -8,7 +8,7 @@ content:
summary: Full room details response summary: Full room details response
value: value:
roomId: 'room-123' roomId: 'room-123'
roomIdPrefix: 'room' roomName: 'room'
creationDate: 1620000000000 creationDate: 1620000000000
autoDeletionDate: 1900000000000 autoDeletionDate: 1900000000000
preferences: preferences:
@ -26,11 +26,11 @@ content:
value: value:
roomId: 'room-123' roomId: 'room-123'
fields=roomId,roomIdPrefix,creationDate,autoDeletionDate,preferences: fields=roomId,roomName,creationDate,autoDeletionDate,preferences:
summary: Room details with roomId, roomIdPrefix, creationDate, autoDeletionDate, and preferences summary: Room details with roomId, roomName, creationDate, autoDeletionDate, and preferences
value: value:
roomId: 'room-123' roomId: 'room-123'
roomIdPrefix: 'room' roomName: 'room'
creationDate: 1620000000000 creationDate: 1620000000000
autoDeletionDate: 1900000000000 autoDeletionDate: 1900000000000
preferences: preferences:

View File

@ -17,7 +17,7 @@ content:
value: value:
rooms: rooms:
- roomId: 'room-123' - roomId: 'room-123'
roomIdPrefix: 'room' roomName: 'room'
creationDate: 1620000000000 creationDate: 1620000000000
autoDeletionDate: 1900000000000 autoDeletionDate: 1900000000000
preferences: preferences:
@ -30,7 +30,7 @@ content:
moderatorURL: 'http://localhost:6080/room/room-123?secret=123456' moderatorURL: 'http://localhost:6080/room/room-123?secret=123456'
publisherURL: 'http://localhost:6080/room/room-123?secret=654321' publisherURL: 'http://localhost:6080/room/room-123?secret=654321'
- roomId: 'room-456' - roomId: 'room-456'
roomIdPrefix: 'room' roomName: 'room'
creationDate: 1620001000000 creationDate: 1620001000000
autoDeletionDate: 1900000000000 autoDeletionDate: 1900000000000
preferences: preferences:
@ -55,12 +55,12 @@ content:
isTruncated: false isTruncated: false
maxItems: 10 maxItems: 10
fields=roomId,roomIdPrefix,creationDate,autoDeletionDate,preferences: fields=roomId,roomName,creationDate,autoDeletionDate,preferences:
summary: Room details including preferences but no URLs summary: Room details including preferences but no URLs
value: value:
rooms: rooms:
- roomId: 'room-123' - roomId: 'room-123'
roomIdPrefix: 'room' roomName: 'room'
creationDate: 1620000000000 creationDate: 1620000000000
autoDeletionDate: 1900000000000 autoDeletionDate: 1900000000000
preferences: preferences:
@ -71,7 +71,7 @@ content:
virtualBackgroundPreferences: virtualBackgroundPreferences:
enabled: true enabled: true
- roomId: 'room-456' - roomId: 'room-456'
roomIdPrefix: 'room' roomName: 'room'
creationDate: 1620001000000 creationDate: 1620001000000
autoDeletionDate: 1900000000000 autoDeletionDate: 1900000000000
preferences: preferences:

View File

@ -1,5 +1,14 @@
type: object type: object
properties: properties:
roomName:
type: [string, 'null']
maxLength: 50
example: 'room'
default: 'Room'
description: |
The display name of the room, used to identify it in a user-friendly way. This value does not need to be unique.
Maximum length: 50 characters. If not provided, the default value "Room" will be used.
autoDeletionDate: autoDeletionDate:
type: [number, 'null'] type: [number, 'null']
example: 1900000000000 example: 1900000000000
@ -11,12 +20,6 @@ properties:
It will be removed after the last participant leaves (graceful deletion). It will be removed after the last participant leaves (graceful deletion).
If not set, the room remains active until manually deleted. If not set, the room remains active until manually deleted.
roomIdPrefix:
type: string
example: 'room'
description: >
A prefix to be used for the room ID. The room ID will be generated by concatenating this prefix with an unique identifier.
The maximum length of the room ID is 50 characters.
# maxParticipants: # maxParticipants:
# type: integer # type: integer
# example: 10 # example: 10

View File

@ -4,12 +4,16 @@ properties:
type: string type: string
example: 'room-123' example: 'room-123'
description: > description: >
The unique identifier of the room. This ID is generated when the room is created. The unique identifier of the room. This ID is generated by combining the room name with a unique identifier.
roomIdPrefix: roomName:
type: string type: [string, 'null']
maxLength: 50
example: 'room' example: 'room'
description: > default: 'Room'
The prefix used for the room ID. This prefix is used to generate the room ID. description: |
The display name of the room, used to identify it in a user-friendly way. This value does not need to be unique.
Maximum length: 50 characters. If not provided, the default value "Room" will be used.
creationDate: creationDate:
type: number type: number
example: 1620000000000 example: 1620000000000

View File

@ -14,10 +14,10 @@ export class MeetRoomHelper {
*/ */
static toOpenViduOptions(room: MeetRoom): MeetRoomOptions { static toOpenViduOptions(room: MeetRoom): MeetRoomOptions {
return { return {
roomName: room.roomName,
autoDeletionDate: room.autoDeletionDate, autoDeletionDate: room.autoDeletionDate,
// maxParticipants: room.maxParticipants, preferences: room.preferences
preferences: room.preferences, // maxParticipants: room.maxParticipants
roomIdPrefix: room.roomIdPrefix
}; };
} }

View File

@ -13,6 +13,25 @@ import { z } from 'zod';
import INTERNAL_CONFIG from '../../config/internal-config.js'; import INTERNAL_CONFIG from '../../config/internal-config.js';
import { rejectUnprocessableRequest } from '../../models/error.model.js'; import { rejectUnprocessableRequest } from '../../models/error.model.js';
/**
* Sanitizes a room name by removing invalid characters and normalizing format.
*
* @param val The string to sanitize
* @returns A sanitized string safe for use as a room name
*/
const sanitizeRoomName = (val: string): string => {
return val
.trim() // Remove leading/trailing spaces
.replace(/[^a-zA-Z0-9_-\s]/g, '') // Allow alphanumeric, underscores, hyphens and spaces
.replace(/\s+/g, ' ') // Replace multiple consecutive spaces with a single space
.replace(/-+/g, '-') // Replace multiple consecutive hyphens with a single hyphen
.replace(/_+/g, '_') // Replace multiple consecutive underscores with a single underscore
.replace(/-+$/, '') // Remove trailing hyphens
.replace(/_+$/, '') // Remove trailing underscores
.replace(/^-+/, '') // Remove leading hyphens
.replace(/^_+/, ''); // Remove leading underscores
};
/** /**
* Sanitizes an identifier by removing/replacing invalid characters * Sanitizes an identifier by removing/replacing invalid characters
* and normalizing format. * and normalizing format.
@ -21,19 +40,7 @@ import { rejectUnprocessableRequest } from '../../models/error.model.js';
* @returns A sanitized string safe for use as an identifier * @returns A sanitized string safe for use as an identifier
*/ */
const sanitizeRoomId = (val: string): string => { const sanitizeRoomId = (val: string): string => {
let transformed = val return sanitizeRoomName(val).replace(/\s+/g, ''); // Remove all spaces
.trim() // Remove leading/trailing spaces
.replace(/\s+/g, '') // Remove all spaces
.replace(/[^a-zA-Z0-9_-]/g, '') // Allow alphanumeric, underscores and hyphens
.replace(/-+/g, '-') // Replace multiple consecutive hyphens
.replace(/-+$/, ''); // Remove trailing hyphens
// Remove leading hyphens
if (transformed.startsWith('-')) {
transformed = transformed.substring(1);
}
return transformed;
}; };
export const nonEmptySanitizedRoomId = (fieldName: string) => export const nonEmptySanitizedRoomId = (fieldName: string) =>
@ -94,6 +101,12 @@ const RoomPreferencesSchema: z.ZodType<MeetRoomPreferences> = z.object({
}); });
const RoomRequestOptionsSchema: z.ZodType<MeetRoomOptions> = z.object({ const RoomRequestOptionsSchema: z.ZodType<MeetRoomOptions> = z.object({
roomName: z
.string()
.max(50, 'roomName cannot exceed 50 characters')
.transform(sanitizeRoomName)
.optional()
.default('Room'),
autoDeletionDate: z autoDeletionDate: z
.number() .number()
.positive('autoDeletionDate must be a positive integer') .positive('autoDeletionDate must be a positive integer')
@ -102,12 +115,6 @@ const RoomRequestOptionsSchema: z.ZodType<MeetRoomOptions> = z.object({
`autoDeletionDate must be at least ${INTERNAL_CONFIG.MIN_FUTURE_TIME_FOR_ROOM_AUTODELETION_DATE} in the future` `autoDeletionDate must be at least ${INTERNAL_CONFIG.MIN_FUTURE_TIME_FOR_ROOM_AUTODELETION_DATE} in the future`
) )
.optional(), .optional(),
roomIdPrefix: z
.string()
.max(50, 'roomIdPrefix cannot exceed 50 characters')
.transform(sanitizeRoomId)
.optional()
.default(''),
preferences: RoomPreferencesSchema.optional().default({ preferences: RoomPreferencesSchema.optional().default({
recordingPreferences: { enabled: true, allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_PUBLISHER }, recordingPreferences: { enabled: true, allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_PUBLISHER },
chatPreferences: { enabled: true }, chatPreferences: { enabled: true },
@ -185,6 +192,7 @@ export const withValidRoomOptions = (req: Request, res: Response, next: NextFunc
return rejectUnprocessableRequest(res, error); return rejectUnprocessableRequest(res, error);
} }
console.log('VALID ROOM OPTIONS', data);
req.body = data; req.body = data;
next(); next();
}; };

View File

@ -50,7 +50,6 @@ roomRouter.get(
configureRoomAuthorization, configureRoomAuthorization,
roomCtrl.getRoom roomCtrl.getRoom
); );
roomRouter.put( roomRouter.put(
'/:roomId', '/:roomId',
withAuth(apiKeyValidator, tokenAndRoleValidator(UserRole.ADMIN)), withAuth(apiKeyValidator, tokenAndRoleValidator(UserRole.ADMIN)),

View File

@ -69,12 +69,13 @@ export class RoomService {
* *
*/ */
async createMeetRoom(baseUrl: string, roomOptions: MeetRoomOptions): Promise<MeetRoom> { async createMeetRoom(baseUrl: string, roomOptions: MeetRoomOptions): Promise<MeetRoom> {
const { preferences, autoDeletionDate, roomIdPrefix } = roomOptions; const { roomName, autoDeletionDate, preferences } = roomOptions;
const roomId = roomIdPrefix ? `${roomIdPrefix}-${uid(15)}` : uid(15); const roomIdPrefix = roomName!.replace(/\s+/g, ''); // Remove all spaces
const roomId = `${roomIdPrefix}-${uid(15)}`; // Generate a unique room ID based on the room name
const meetRoom: MeetRoom = { const meetRoom: MeetRoom = {
roomId, roomId,
roomIdPrefix, roomName: roomName!,
creationDate: Date.now(), creationDate: Date.now(),
// maxParticipants, // maxParticipants,
autoDeletionDate, autoDeletionDate,

View File

@ -86,12 +86,12 @@ export const expectSuccessRoomsResponse = (
export const expectSuccessRoomResponse = ( export const expectSuccessRoomResponse = (
response: any, response: any,
idPrefix: string, roomName: string,
autoDeletionDate?: number, autoDeletionDate?: number,
preferences?: MeetRoomPreferences preferences?: MeetRoomPreferences
) => { ) => {
expect(response.status).toBe(200); expect(response.status).toBe(200);
expectValidRoom(response.body, idPrefix, autoDeletionDate, preferences); expectValidRoom(response.body, roomName, autoDeletionDate, preferences);
}; };
export const expectSuccessRoomPreferencesResponse = (response: any, preferences: MeetRoomPreferences) => { export const expectSuccessRoomPreferencesResponse = (response: any, preferences: MeetRoomPreferences) => {
@ -102,7 +102,7 @@ export const expectSuccessRoomPreferencesResponse = (response: any, preferences:
export const expectValidRoom = ( export const expectValidRoom = (
room: MeetRoom, room: MeetRoom,
idPrefix: string, name: string,
autoDeletionDate?: number, autoDeletionDate?: number,
preferences?: MeetRoomPreferences, preferences?: MeetRoomPreferences,
markedForDeletion?: boolean markedForDeletion?: boolean
@ -110,10 +110,10 @@ export const expectValidRoom = (
expect(room).toBeDefined(); expect(room).toBeDefined();
expect(room.roomId).toBeDefined(); expect(room.roomId).toBeDefined();
expect(room.roomIdPrefix).toBeDefined(); expect(room.roomName).toBeDefined();
expect(room.roomIdPrefix).toBe(idPrefix); expect(room.roomName).toBe(name);
expect(room.roomId).not.toBe(''); expect(room.roomId).not.toBe('');
expect(room.roomId).toContain(room.roomIdPrefix); expect(room.roomId).toContain(room.roomName.replace(/\s+/g, '')); // Ensure roomId contains the name without spaces
expect(room.creationDate).toBeDefined(); expect(room.creationDate).toBeDefined();
if (autoDeletionDate !== undefined) { if (autoDeletionDate !== undefined) {

View File

@ -34,15 +34,17 @@ export interface TestContext {
* Creates a single room with optional participant. * Creates a single room with optional participant.
* *
* @param withParticipant Whether to join a fake participant in the room. * @param withParticipant Whether to join a fake participant in the room.
* @param roomName Name of the room to create.
* @param preferences Optional room preferences.
* @returns Room data including secrets and cookies. * @returns Room data including secrets and cookies.
*/ */
export const setupSingleRoom = async ( export const setupSingleRoom = async (
withParticipant = false, withParticipant = false,
roomPrefix = 'TEST_ROOM', roomName = 'TEST_ROOM',
preferences?: MeetRoomPreferences preferences?: MeetRoomPreferences
): Promise<RoomData> => { ): Promise<RoomData> => {
const room = await createRoom({ const room = await createRoom({
roomIdPrefix: roomPrefix, roomName,
preferences preferences
}); });

View File

@ -31,7 +31,7 @@ describe('Room API Tests', () => {
}); });
it('should delete room (204) with invalid force parameter when no participants exist', async () => { it('should delete room (204) with invalid force parameter when no participants exist', async () => {
const { roomId } = await createRoom({ roomIdPrefix: 'test-invalid-force' }); const { roomId } = await createRoom({ roomName: 'test-invalid-force' });
const response = await bulkDeleteRooms([roomId]); const response = await bulkDeleteRooms([roomId]);
@ -41,7 +41,7 @@ describe('Room API Tests', () => {
}); });
it('should mark room for deletion (202) with invalid force parameter when participants exist', async () => { it('should mark room for deletion (202) with invalid force parameter when participants exist', async () => {
const { roomId } = await createRoom({ roomIdPrefix: 'test-invalid-force' }); const { roomId } = await createRoom({ roomName: 'test-invalid-force' });
await joinFakeParticipant(roomId, 'test-participant-1'); await joinFakeParticipant(roomId, 'test-participant-1');
@ -53,7 +53,7 @@ describe('Room API Tests', () => {
}); });
it('should delete room (204) with force=true parameter when no participants exist', async () => { it('should delete room (204) with force=true parameter when no participants exist', async () => {
const { roomId } = await createRoom({ roomIdPrefix: 'test-force' }); const { roomId } = await createRoom({ roomName: 'test-force' });
const response = await bulkDeleteRooms([roomId], true); const response = await bulkDeleteRooms([roomId], true);
@ -63,7 +63,7 @@ describe('Room API Tests', () => {
}); });
it('should delete room (204) with force=true parameter when participants exist', async () => { it('should delete room (204) with force=true parameter when participants exist', async () => {
const { roomId } = await createRoom({ roomIdPrefix: 'test-force' }); const { roomId } = await createRoom({ roomName: 'test-force' });
await joinFakeParticipant(roomId, 'test-participant-1'); await joinFakeParticipant(roomId, 'test-participant-1');
@ -75,7 +75,7 @@ describe('Room API Tests', () => {
}); });
it('should successfully delete the room requesting the same roomId multiple times', async () => { it('should successfully delete the room requesting the same roomId multiple times', async () => {
const { roomId } = await createRoom({ roomIdPrefix: 'test-duplicate' }); const { roomId } = await createRoom({ roomName: 'test-duplicate' });
const response = await bulkDeleteRooms([roomId, roomId, roomId], true); const response = await bulkDeleteRooms([roomId, roomId, roomId], true);
@ -84,7 +84,7 @@ describe('Room API Tests', () => {
}); });
it('should successfully delete valid roomIds while ignoring invalid ones', async () => { it('should successfully delete valid roomIds while ignoring invalid ones', async () => {
const { roomId } = await createRoom({ roomIdPrefix: 'test-invalid-force' }); const { roomId } = await createRoom({ roomName: 'test-invalid-force' });
const response = await bulkDeleteRooms([roomId, '!!@##$']); const response = await bulkDeleteRooms([roomId, '!!@##$']);
@ -95,8 +95,8 @@ describe('Room API Tests', () => {
it('should successfully delete multiple rooms with valid roomIds', async () => { it('should successfully delete multiple rooms with valid roomIds', async () => {
// Create test rooms // Create test rooms
const [room1, room2] = await Promise.all([ const [room1, room2] = await Promise.all([
createRoom({ roomIdPrefix: 'test-bulk-1' }), createRoom({ roomName: 'test-bulk-1' }),
createRoom({ roomIdPrefix: 'test-bulk-2' }) createRoom({ roomName: 'test-bulk-2' })
]); ]);
// Delete both rooms // Delete both rooms
@ -117,8 +117,8 @@ describe('Room API Tests', () => {
it('should successfully marked for deletion multiple rooms with valid roomIds', async () => { it('should successfully marked for deletion multiple rooms with valid roomIds', async () => {
// Create test rooms // Create test rooms
const [room1, room2] = await Promise.all([ const [room1, room2] = await Promise.all([
createRoom({ roomIdPrefix: 'test-bulk-1' }), createRoom({ roomName: 'test-bulk-1' }),
createRoom({ roomIdPrefix: 'test-bulk-2' }) createRoom({ roomName: 'test-bulk-2' })
]); ]);
await Promise.all([ await Promise.all([
@ -147,7 +147,7 @@ describe('Room API Tests', () => {
it('should sanitize roomIds before deleting', async () => { it('should sanitize roomIds before deleting', async () => {
// Create a test room // Create a test room
const { roomId } = await createRoom({ roomIdPrefix: 'test-sanitize' }); const { roomId } = await createRoom({ roomName: 'test-sanitize' });
const response = await bulkDeleteRooms([roomId + '!!@##$']); const response = await bulkDeleteRooms([roomId + '!!@##$']);
expect(response.status).toBe(204); expect(response.status).toBe(204);
@ -160,8 +160,8 @@ describe('Room API Tests', () => {
it('should delete rooms when force=true and participants exist', async () => { it('should delete rooms when force=true and participants exist', async () => {
// Create test rooms // Create test rooms
const [room1, room2] = await Promise.all([ const [room1, room2] = await Promise.all([
createRoom({ roomIdPrefix: 'test-bulk-1' }), createRoom({ roomName: 'test-bulk-1' }),
createRoom({ roomIdPrefix: 'test-bulk-2' }) createRoom({ roomName: 'test-bulk-2' })
]); ]);
// Join a participant to the rooms // Join a participant to the rooms
@ -188,8 +188,8 @@ describe('Room API Tests', () => {
it('should return mixed results (200) when some rooms are deleted and others marked for deletion', async () => { it('should return mixed results (200) when some rooms are deleted and others marked for deletion', async () => {
// Create rooms // Create rooms
const room1 = await createRoom({ roomIdPrefix: 'empty-room' }); const room1 = await createRoom({ roomName: 'empty-room' });
const room2 = await createRoom({ roomIdPrefix: 'occupied-room' }); const room2 = await createRoom({ roomName: 'occupied-room' });
// Add participant to only one room // Add participant to only one room
await joinFakeParticipant(room2.roomId, 'test-participant'); await joinFakeParticipant(room2.roomId, 'test-participant');
@ -214,7 +214,7 @@ describe('Room API Tests', () => {
it('should handle a large number of room IDs', async () => { it('should handle a large number of room IDs', async () => {
// Create 20+ rooms and test deletion // Create 20+ rooms and test deletion
const rooms = await Promise.all( const rooms = await Promise.all(
Array.from({ length: 20 }, (_, i) => createRoom({ roomIdPrefix: `bulk-${i}` })) Array.from({ length: 20 }, (_, i) => createRoom({ roomName: `bulk-${i}` }))
); );
const response = await bulkDeleteRooms(rooms.map((r) => r.roomId)); const response = await bulkDeleteRooms(rooms.map((r) => r.roomId));
@ -230,7 +230,7 @@ describe('Room API Tests', () => {
it('should handle a large number of room IDs with mixed valid and invalid IDs', async () => { it('should handle a large number of room IDs with mixed valid and invalid IDs', async () => {
// Create 20+ rooms and test deletion // Create 20+ rooms and test deletion
const rooms = await Promise.all( const rooms = await Promise.all(
Array.from({ length: 20 }, (_, i) => createRoom({ roomIdPrefix: `bulk-${i}` })) Array.from({ length: 20 }, (_, i) => createRoom({ roomName: `bulk-${i}` }))
); );
await joinFakeParticipant(rooms[0].roomId, 'test-participant-1'); await joinFakeParticipant(rooms[0].roomId, 'test-participant-1');

View File

@ -27,7 +27,7 @@ describe('Room API Tests', () => {
describe('Room Creation Tests', () => { describe('Room Creation Tests', () => {
it('Should create a room without autoDeletionDate (default behavior)', async () => { it('Should create a room without autoDeletionDate (default behavior)', async () => {
const room = await createRoom({ const room = await createRoom({
roomIdPrefix: ' Test Room ' roomName: ' Test Room '
}); });
expectValidRoom(room, 'Test Room'); expectValidRoom(room, 'Test Room');
}); });
@ -35,7 +35,7 @@ describe('Room API Tests', () => {
it('Should create a room with a valid autoDeletionDate', async () => { it('Should create a room with a valid autoDeletionDate', async () => {
const room = await createRoom({ const room = await createRoom({
autoDeletionDate: validAutoDeletionDate, autoDeletionDate: validAutoDeletionDate,
roomIdPrefix: ' .,-------}{¡$#<+My Room *123 ' roomName: ' .,-------}{¡$#<+My Room *123 '
}); });
expectValidRoom(room, 'My Room 123', validAutoDeletionDate); expectValidRoom(room, 'My Room 123', validAutoDeletionDate);
@ -43,7 +43,7 @@ describe('Room API Tests', () => {
it('Should create a room when sending full valid payload', async () => { it('Should create a room when sending full valid payload', async () => {
const payload = { const payload = {
roomIdPrefix: ' =Example Room&/ ', roomName: ' =Example Room&/ ',
autoDeletionDate: validAutoDeletionDate, autoDeletionDate: validAutoDeletionDate,
preferences: { preferences: {
recordingPreferences: { recordingPreferences: {
@ -65,7 +65,7 @@ describe('Room API Tests', () => {
it('should fail when autoDeletionDate is negative', async () => { it('should fail when autoDeletionDate is negative', async () => {
const payload = { const payload = {
autoDeletionDate: -5000, autoDeletionDate: -5000,
roomIdPrefix: 'TestRoom' roomName: 'TestRoom'
}; };
const response = await request(app).post(ROOMS_PATH).set('Cookie', adminCookie).send(payload).expect(422); const response = await request(app).post(ROOMS_PATH).set('Cookie', adminCookie).send(payload).expect(422);
@ -78,7 +78,7 @@ describe('Room API Tests', () => {
it('should fail when autoDeletionDate is less than 1 hour in the future', async () => { it('should fail when autoDeletionDate is less than 1 hour in the future', async () => {
const payload = { const payload = {
autoDeletionDate: Date.now() + ms('30m'), autoDeletionDate: Date.now() + ms('30m'),
roomIdPrefix: 'TestRoom' roomName: 'TestRoom'
}; };
const response = await request(app).post(ROOMS_PATH).set('Cookie', adminCookie).send(payload).expect(422); const response = await request(app).post(ROOMS_PATH).set('Cookie', adminCookie).send(payload).expect(422);
@ -92,7 +92,7 @@ describe('Room API Tests', () => {
it('should fail when autoDeletionDate is not a number (string provided)', async () => { it('should fail when autoDeletionDate is not a number (string provided)', async () => {
const payload = { const payload = {
autoDeletionDate: 'not-a-number', autoDeletionDate: 'not-a-number',
roomIdPrefix: 'TestRoom' roomName: 'TestRoom'
}; };
const response = await request(app).post(ROOMS_PATH).set('Cookie', adminCookie).send(payload).expect(422); const response = await request(app).post(ROOMS_PATH).set('Cookie', adminCookie).send(payload).expect(422);
@ -103,7 +103,7 @@ describe('Room API Tests', () => {
it('should fail when autoDeletionDate is a boolean', async () => { it('should fail when autoDeletionDate is a boolean', async () => {
const payload = { const payload = {
autoDeletionDate: true, autoDeletionDate: true,
roomIdPrefix: 'TestRoom' roomName: 'TestRoom'
}; };
const response = await request(app).post(ROOMS_PATH).set('Cookie', adminCookie).send(payload).expect(422); const response = await request(app).post(ROOMS_PATH).set('Cookie', adminCookie).send(payload).expect(422);
@ -114,7 +114,7 @@ describe('Room API Tests', () => {
it('should fail when autoDeletionDate is omitted but provided as null', async () => { it('should fail when autoDeletionDate is omitted but provided as null', async () => {
const payload = { const payload = {
autoDeletionDate: null, autoDeletionDate: null,
roomIdPrefix: 'TestRoom' roomName: 'TestRoom'
}; };
const response = await request(app).post(ROOMS_PATH).set('Cookie', adminCookie).send(payload).expect(422); const response = await request(app).post(ROOMS_PATH).set('Cookie', adminCookie).send(payload).expect(422);
@ -122,9 +122,9 @@ describe('Room API Tests', () => {
expect(JSON.stringify(response.body.details)).toContain('Expected number'); expect(JSON.stringify(response.body.details)).toContain('Expected number');
}); });
it('should fail when roomIdPrefix is not a string (number provided)', async () => { it('should fail when roomName is not a string (number provided)', async () => {
const payload = { const payload = {
roomIdPrefix: 12345, roomName: 12345,
autoDeletionDate: validAutoDeletionDate autoDeletionDate: validAutoDeletionDate
}; };
@ -133,9 +133,9 @@ describe('Room API Tests', () => {
expect(JSON.stringify(response.body.details)).toContain('Expected string'); expect(JSON.stringify(response.body.details)).toContain('Expected string');
}); });
it('should fail when roomIdPrefix is a boolean', async () => { it('should fail when roomName is a boolean', async () => {
const payload = { const payload = {
roomIdPrefix: false, roomName: false,
autoDeletionDate: validAutoDeletionDate autoDeletionDate: validAutoDeletionDate
}; };
@ -146,7 +146,7 @@ describe('Room API Tests', () => {
it('should fail when preferences is not an object (string provided)', async () => { it('should fail when preferences is not an object (string provided)', async () => {
const payload = { const payload = {
roomIdPrefix: 'TestRoom', roomName: 'TestRoom',
autoDeletionDate: validAutoDeletionDate, autoDeletionDate: validAutoDeletionDate,
preferences: 'invalid-preferences' preferences: 'invalid-preferences'
}; };
@ -160,7 +160,7 @@ describe('Room API Tests', () => {
// Assuming preferences expects each sub-property to be an object with a boolean "enabled", // Assuming preferences expects each sub-property to be an object with a boolean "enabled",
// here we deliberately use an invalid structure. // here we deliberately use an invalid structure.
const payload = { const payload = {
roomIdPrefix: 'TestRoom', roomName: 'TestRoom',
autoDeletionDate: validAutoDeletionDate, autoDeletionDate: validAutoDeletionDate,
preferences: { preferences: {
recordingPreferences: { recordingPreferences: {
@ -183,23 +183,23 @@ describe('Room API Tests', () => {
.post(ROOMS_PATH) .post(ROOMS_PATH)
.set('Cookie', adminCookie) .set('Cookie', adminCookie)
.set('Content-Type', 'application/json') .set('Content-Type', 'application/json')
.send('{"roomIdPrefix": "TestRoom",') // invalid JSON syntax .send('{"roomName": "TestRoom",') // invalid JSON syntax
.expect(400); .expect(400);
expect(response.body.error).toContain('Bad Request'); expect(response.body.error).toContain('Bad Request');
expect(response.body.message).toContain('Malformed body'); expect(response.body.message).toContain('Malformed body');
}); });
it('should fail when roomIdPrefix is too long', async () => { it('should fail when roomName is too long', async () => {
const longRoomId = 'a'.repeat(51); const longRoomId = 'a'.repeat(51);
const payload = { const payload = {
roomIdPrefix: longRoomId, roomName: longRoomId,
autoDeletionDate: validAutoDeletionDate autoDeletionDate: validAutoDeletionDate
}; };
const response = await request(app).post(ROOMS_PATH).set('Cookie', adminCookie).send(payload).expect(422); const response = await request(app).post(ROOMS_PATH).set('Cookie', adminCookie).send(payload).expect(422);
expect(JSON.stringify(response.body.details)).toContain('roomIdPrefix cannot exceed 50 characters'); expect(JSON.stringify(response.body.details)).toContain('roomName cannot exceed 50 characters');
}); });
}); });
}); });

View File

@ -36,7 +36,7 @@ describe('Room API Tests', () => {
it('should default to force=false when force parameter is invalid', async () => { it('should default to force=false when force parameter is invalid', async () => {
// Create a room first // Create a room first
const { roomId } = await createRoom({ const { roomId } = await createRoom({
roomIdPrefix: 'test-room' roomName: 'test-room'
}); });
const response = await deleteRoom(roomId, { force: 'not-a-boolean' }); const response = await deleteRoom(roomId, { force: 'not-a-boolean' });
@ -49,7 +49,7 @@ describe('Room API Tests', () => {
it('should mark room for deletion when participants exist and force parameter is invalid', async () => { it('should mark room for deletion when participants exist and force parameter is invalid', async () => {
// Create a room first // Create a room first
const { roomId } = await createRoom({ const { roomId } = await createRoom({
roomIdPrefix: 'test-room' roomName: 'test-room'
}); });
await joinFakeParticipant(roomId, 'test-participant'); await joinFakeParticipant(roomId, 'test-participant');
@ -70,7 +70,7 @@ describe('Room API Tests', () => {
}); });
it('should delete an empty room completely (204)', async () => { it('should delete an empty room completely (204)', async () => {
const { roomId } = await createRoom({ roomIdPrefix: 'test-room' }); const { roomId } = await createRoom({ roomName: 'test-room' });
const response = await deleteRoom(roomId); const response = await deleteRoom(roomId);
@ -84,7 +84,7 @@ describe('Room API Tests', () => {
it('should sanitize roomId with spaces and special characters before deletion', async () => { it('should sanitize roomId with spaces and special characters before deletion', async () => {
// Create a room first // Create a room first
const createdRoom = await createRoom({ const createdRoom = await createRoom({
roomIdPrefix: 'test-mixed' roomName: 'test-mixed'
}); });
// Add some spaces and special chars to the valid roomId // Add some spaces and special chars to the valid roomId
@ -101,7 +101,7 @@ describe('Room API Tests', () => {
it('should handle explicit force=true for room with no participants', async () => { it('should handle explicit force=true for room with no participants', async () => {
const createdRoom = await createRoom({ const createdRoom = await createRoom({
roomIdPrefix: 'test-room' roomName: 'test-room'
}); });
const response = await deleteRoom(createdRoom.roomId, { force: true }); const response = await deleteRoom(createdRoom.roomId, { force: true });
@ -116,7 +116,7 @@ describe('Room API Tests', () => {
it('should mark room for deletion (202) when participants exist and force=false', async () => { it('should mark room for deletion (202) when participants exist and force=false', async () => {
const autoDeletionDate = Date.now() + ms('5h'); const autoDeletionDate = Date.now() + ms('5h');
const { roomId } = await createRoom({ const { roomId } = await createRoom({
roomIdPrefix: 'test-room', roomName: 'test-room',
autoDeletionDate autoDeletionDate
}); });
@ -133,7 +133,7 @@ describe('Room API Tests', () => {
it('should delete a room marked for deletion when the webhook room_finished is received', async () => { it('should delete a room marked for deletion when the webhook room_finished is received', async () => {
const autoDeletionDate = Date.now() + ms('5h'); const autoDeletionDate = Date.now() + ms('5h');
const { roomId } = await createRoom({ const { roomId } = await createRoom({
roomIdPrefix: 'test-room', roomName: 'test-room',
autoDeletionDate autoDeletionDate
}); });
@ -166,7 +166,7 @@ describe('Room API Tests', () => {
it('should force delete (204) room with active participants when force=true', async () => { it('should force delete (204) room with active participants when force=true', async () => {
const { roomId } = await createRoom({ const { roomId } = await createRoom({
roomIdPrefix: 'test-room' roomName: 'test-room'
}); });
await joinFakeParticipant(roomId, 'test-participant'); await joinFakeParticipant(roomId, 'test-participant');
@ -182,7 +182,7 @@ describe('Room API Tests', () => {
}); });
it('should successfully delete a room already marked for deletion', async () => { it('should successfully delete a room already marked for deletion', async () => {
const { roomId } = await createRoom({ roomIdPrefix: 'test-marked' }); const { roomId } = await createRoom({ roomName: 'test-marked' });
// First mark it for deletion // First mark it for deletion
await joinFakeParticipant(roomId, 'test-participant'); await joinFakeParticipant(roomId, 'test-participant');
@ -195,7 +195,7 @@ describe('Room API Tests', () => {
}); });
it('should handle repeated deletion of the same room gracefully', async () => { it('should handle repeated deletion of the same room gracefully', async () => {
const { roomId } = await createRoom({ roomIdPrefix: 'test-idempotent' }); const { roomId } = await createRoom({ roomName: 'test-idempotent' });
// Delete first time // Delete first time
const response1 = await deleteRoom(roomId); const response1 = await deleteRoom(roomId);

View File

@ -31,7 +31,7 @@ describe('Room Garbage Collector Tests', () => {
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 participant is present', async () => {
const createdRoom = await createRoom({ const createdRoom = await createRoom({
roomIdPrefix: 'test-gc', roomName: 'test-gc',
autoDeletionDate: Date.now() + ms('1s') autoDeletionDate: Date.now() + ms('1s')
}); });
@ -50,7 +50,7 @@ describe('Room Garbage Collector Tests', () => {
it('should mark room for deletion but not delete when expiration date has passed and participants exist', async () => { it('should mark room for deletion but not delete when expiration date has passed and participants exist', async () => {
const createdRoom = await createRoom({ const createdRoom = await createRoom({
roomIdPrefix: 'test-gc-participants', roomName: 'test-gc-participants',
autoDeletionDate: Date.now() + ms('1s') autoDeletionDate: Date.now() + ms('1s')
}); });
@ -66,7 +66,7 @@ describe('Room Garbage Collector Tests', () => {
it('should not touch a room with a future auto-deletion date', async () => { it('should not touch a room with a future auto-deletion date', async () => {
const createdRoom = await createRoom({ const createdRoom = await createRoom({
roomIdPrefix: 'test-gc-future', roomName: 'test-gc-future',
autoDeletionDate: Date.now() + ms('1h') autoDeletionDate: Date.now() + ms('1h')
}); });
@ -79,7 +79,7 @@ describe('Room Garbage Collector Tests', () => {
it('should delete a room after the last participant leaves when it was marked for deletion', async () => { it('should delete a room after the last participant leaves when it was marked for deletion', async () => {
const { roomId } = await createRoom({ const { roomId } = await createRoom({
roomIdPrefix: 'test-gc-lifecycle', roomName: 'test-gc-lifecycle',
autoDeletionDate: Date.now() + ms('1s') autoDeletionDate: Date.now() + ms('1s')
}); });
@ -118,7 +118,7 @@ describe('Room Garbage Collector Tests', () => {
it('should never delete a room without an auto-deletion date', async () => { it('should never delete a room without an auto-deletion date', async () => {
const createdRoom = await createRoom({ const createdRoom = await createRoom({
roomIdPrefix: 'test-gc-no-date' roomName: 'test-gc-no-date'
}); });
await runRoomGarbageCollector(); await runRoomGarbageCollector();
@ -135,16 +135,16 @@ describe('Room Garbage Collector Tests', () => {
it('should handle multiple expired rooms in one batch', async () => { it('should handle multiple expired rooms in one batch', async () => {
const rooms = await Promise.all([ const rooms = await Promise.all([
createRoom({ roomIdPrefix: 'test-gc-multi-1', autoDeletionDate: Date.now() + ms('1s') }), createRoom({ roomName: 'test-gc-multi-1', autoDeletionDate: Date.now() + ms('1s') }),
createRoom({ roomIdPrefix: 'test-gc-multi-2', autoDeletionDate: Date.now() + ms('1s') }), createRoom({ roomName: 'test-gc-multi-2', autoDeletionDate: Date.now() + ms('1s') }),
createRoom({ roomIdPrefix: 'test-gc-multi-3', autoDeletionDate: Date.now() + ms('1s') }), createRoom({ roomName: 'test-gc-multi-3', autoDeletionDate: Date.now() + ms('1s') }),
createRoom({ roomIdPrefix: 'test-gc-multi-4', autoDeletionDate: Date.now() + ms('1h') }), createRoom({ roomName: 'test-gc-multi-4', autoDeletionDate: Date.now() + ms('1h') }),
createRoom({ roomIdPrefix: 'test-gc-multi-5', autoDeletionDate: Date.now() + ms('1h') }), createRoom({ roomName: 'test-gc-multi-5', autoDeletionDate: Date.now() + ms('1h') }),
createRoom({ roomIdPrefix: 'test-gc-multi-6', autoDeletionDate: Date.now() + ms('1s') }), createRoom({ roomName: 'test-gc-multi-6', autoDeletionDate: Date.now() + ms('1s') }),
createRoom({ roomIdPrefix: 'test-gc-multi-7', autoDeletionDate: Date.now() + ms('1s') }), createRoom({ roomName: 'test-gc-multi-7', autoDeletionDate: Date.now() + ms('1s') }),
createRoom({ roomIdPrefix: 'test-gc-multi-8', autoDeletionDate: Date.now() + ms('1s') }), createRoom({ roomName: 'test-gc-multi-8', autoDeletionDate: Date.now() + ms('1s') }),
createRoom({ roomIdPrefix: 'test-gc-multi-9', autoDeletionDate: Date.now() + ms('1s') }), createRoom({ roomName: 'test-gc-multi-9', autoDeletionDate: Date.now() + ms('1s') }),
createRoom({ roomIdPrefix: 'test-gc-multi-10', autoDeletionDate: Date.now() + ms('1s') }) createRoom({ roomName: 'test-gc-multi-10', autoDeletionDate: Date.now() + ms('1s') })
]); ]);
// Make sure all rooms are expired // Make sure all rooms are expired

View File

@ -35,7 +35,7 @@ describe('Room API Tests', () => {
it('should retrieve custom room preferences', async () => { it('should retrieve custom room preferences', async () => {
const payload = { const payload = {
roomIdPrefix: 'custom-prefs', roomName: 'custom-prefs',
preferences: { preferences: {
recordingPreferences: { recordingPreferences: {
enabled: true, enabled: true,
@ -46,7 +46,7 @@ describe('Room API Tests', () => {
} }
}; };
const roomData = await setupSingleRoom(false, payload.roomIdPrefix, payload.preferences); const roomData = await setupSingleRoom(false, payload.roomName, payload.preferences);
const roomId = roomData.room.roomId; const roomId = roomData.room.roomId;
const cookie = roomData.moderatorCookie; const cookie = roomData.moderatorCookie;

View File

@ -22,7 +22,7 @@ describe('Room API Tests', () => {
describe('Get Room Tests', () => { describe('Get Room Tests', () => {
it('should successfully retrieve a room by its ID', async () => { it('should successfully retrieve a room by its ID', async () => {
const createdRoom = await createRoom({ const createdRoom = await createRoom({
roomIdPrefix: 'test-room' roomName: 'test-room'
}); });
expectValidRoom(createdRoom, 'test-room'); expectValidRoom(createdRoom, 'test-room');
@ -33,7 +33,7 @@ describe('Room API Tests', () => {
it('should retrieve a room with custom preferences', async () => { it('should retrieve a room with custom preferences', async () => {
const payload = { const payload = {
roomIdPrefix: 'custom-prefs', roomName: 'custom-prefs',
preferences: { preferences: {
recordingPreferences: { recordingPreferences: {
enabled: true, enabled: true,
@ -55,22 +55,22 @@ describe('Room API Tests', () => {
it('should retrieve only specified fields when using fields parameter', async () => { it('should retrieve only specified fields when using fields parameter', async () => {
// Create a room // Create a room
const createdRoom = await createRoom({ const createdRoom = await createRoom({
roomIdPrefix: 'field-filtered' roomName: 'field-filtered'
}); });
// Get the room with field filtering // Get the room with field filtering
const response = await getRoom(createdRoom.roomId, 'roomId,roomIdPrefix'); const response = await getRoom(createdRoom.roomId, 'roomId,roomName');
// Verify that only the requested fields are returned // Verify that only the requested fields are returned
expect(response.status).toBe(200); expect(response.status).toBe(200);
expectValidRoomWithFields(response.body, ['roomId', 'roomIdPrefix']); expectValidRoomWithFields(response.body, ['roomId', 'roomName']);
}); });
it('should handle roomId with characters that need sanitization', async () => { it('should handle roomId with characters that need sanitization', async () => {
// Create a room // Create a room
const createdRoom = await createRoom({ const createdRoom = await createRoom({
roomIdPrefix: 'test-room' roomName: 'test-room'
}); });
const dirtyRoomId = ' ' + createdRoom.roomId + ' '; // Add spaces that should be trimmed const dirtyRoomId = ' ' + createdRoom.roomId + ' '; // Add spaces that should be trimmed
@ -86,7 +86,7 @@ describe('Room API Tests', () => {
// Create a room with autoDeletionDate // Create a room with autoDeletionDate
const createdRoom = await createRoom({ const createdRoom = await createRoom({
roomIdPrefix: 'deletion-date', roomName: 'deletion-date',
autoDeletionDate: validAutoDeletionDate autoDeletionDate: validAutoDeletionDate
}); });

View File

@ -30,7 +30,7 @@ describe('Room API Tests', () => {
it('should return a list of rooms', async () => { it('should return a list of rooms', async () => {
await createRoom({ await createRoom({
roomIdPrefix: 'test-room' roomName: 'test-room'
}); });
const response = await getRooms(); const response = await getRooms();
@ -41,7 +41,7 @@ describe('Room API Tests', () => {
it('should return a list of rooms applying fields filter', async () => { it('should return a list of rooms applying fields filter', async () => {
await createRoom({ await createRoom({
roomIdPrefix: 'test-room', roomName: 'test-room',
autoDeletionDate: validAutoDeletionDate autoDeletionDate: validAutoDeletionDate
}); });
@ -56,7 +56,7 @@ describe('Room API Tests', () => {
it('should return a list of rooms with pagination', async () => { it('should return a list of rooms with pagination', async () => {
const promises = [0, 1, 2, 3, 4, 5].map((i) => { const promises = [0, 1, 2, 3, 4, 5].map((i) => {
return createRoom({ return createRoom({
roomIdPrefix: `test-room-${i}`, roomName: `test-room-${i}`,
autoDeletionDate: validAutoDeletionDate autoDeletionDate: validAutoDeletionDate
}); });
}); });

View File

@ -32,7 +32,7 @@ describe('Room API Tests', () => {
it('should successfully update room preferences', async () => { it('should successfully update room preferences', async () => {
const sendSignalSpy = jest.spyOn(frontendEventService as any, 'sendSignal'); const sendSignalSpy = jest.spyOn(frontendEventService as any, 'sendSignal');
const createdRoom = await createRoom({ const createdRoom = await createRoom({
roomIdPrefix: 'update-test', roomName: 'update-test',
preferences: { preferences: {
recordingPreferences: { recordingPreferences: {
enabled: true, enabled: true,
@ -81,7 +81,7 @@ describe('Room API Tests', () => {
it('should allow partial preference updates', async () => { it('should allow partial preference updates', async () => {
// Create a room first with all preferences enabled // Create a room first with all preferences enabled
const createdRoom = await createRoom({ const createdRoom = await createRoom({
roomIdPrefix: 'partial-update', roomName: 'partial-update',
preferences: { preferences: {
recordingPreferences: { recordingPreferences: {
enabled: true, enabled: true,
@ -118,7 +118,7 @@ describe('Room API Tests', () => {
describe('Update Room Validation failures', () => { describe('Update Room Validation failures', () => {
it('should fail when preferences have incorrect structure', async () => { it('should fail when preferences have incorrect structure', async () => {
const { roomId } = await createRoom({ const { roomId } = await createRoom({
roomIdPrefix: 'validation-test' roomName: 'validation-test'
}); });
// Invalid preferences (missing required fields) // Invalid preferences (missing required fields)
@ -139,7 +139,7 @@ describe('Room API Tests', () => {
it('should fail when preferences have incorrect types', async () => { it('should fail when preferences have incorrect types', async () => {
const createdRoom = await createRoom({ const createdRoom = await createRoom({
roomIdPrefix: 'type-test' roomName: 'type-test'
}); });
// Invalid preferences (wrong types) // Invalid preferences (wrong types)
@ -161,7 +161,7 @@ describe('Room API Tests', () => {
it('should fail when preferences are missing required properties', async () => { it('should fail when preferences are missing required properties', async () => {
const createdRoom = await createRoom({ const createdRoom = await createRoom({
roomIdPrefix: 'missing-props' roomName: 'missing-props'
}); });
const emptyPreferences = {}; const emptyPreferences = {};
@ -174,7 +174,7 @@ describe('Room API Tests', () => {
it('should fail when recording is enabled but allowAccessTo is missing', async () => { it('should fail when recording is enabled but allowAccessTo is missing', async () => {
const createdRoom = await createRoom({ const createdRoom = await createRoom({
roomIdPrefix: 'missing-access' roomName: 'missing-access'
}); });
const invalidPreferences = { const invalidPreferences = {
recordingPreferences: { recordingPreferences: {