From 18426e211106945237630f6a06f75b40b18bae89 Mon Sep 17 00:00:00 2001 From: juancarmore Date: Tue, 3 Feb 2026 17:40:50 +0100 Subject: [PATCH] test: update webhook config references and improve request helpers - Renamed `getWebbhookConfig` and `updateWebbhookConfig` to `getWebhookConfig` and `updateWebhookConfig` respectively for consistency. - Updated integration tests to reflect the new function names. - Refactored request helper methods to use `getFullPath` for API endpoint construction. - Removed unnecessary parameters in `stopRecording` calls across various tests. - Cleaned up test scenarios by removing redundant room deletion logic. - Ensured proper handling of recording states in tests to avoid race conditions. --- meet-ce/backend/jest.integration.config.mjs | 4 +- .../models/mongoose-schemas/room.schema.ts | 25 ++------- .../backend/src/routes/recording.routes.ts | 35 +++++------- .../backend/src/utils/html-injection.utils.ts | 15 ++++- .../backend/tests/helpers/request-helpers.ts | 56 ++++++++++--------- .../backend/tests/helpers/test-scenarios.ts | 4 +- .../api/analytics/get-analytics.test.ts | 2 +- .../api/auth/token-validation.test.ts | 9 +-- .../api/global-config/webhook.test.ts | 26 ++++----- .../api/meetings/end-meeting.test.ts | 7 +-- .../api/meetings/kick-participant.test.ts | 7 +-- .../api/meetings/update-participant.test.ts | 10 +--- .../recordings/bulk-delete-recording.test.ts | 6 +- .../api/recordings/delete-recording.test.ts | 16 +++--- .../recordings/get-media-recording.test.ts | 4 +- .../api/recordings/get-recordings.test.ts | 4 +- .../api/recordings/race-conditions.test.ts | 22 ++------ .../integration/api/rooms/create-room.test.ts | 12 ++-- .../api/rooms/update-room-config.test.ts | 6 +- .../api/security/meeting-security.test.ts | 1 + .../api/security/recording-security.test.ts | 7 ++- .../security/room-members-security.test.ts | 12 +++- .../integration/webhooks/webhook.test.ts | 10 ++-- 23 files changed, 135 insertions(+), 165 deletions(-) diff --git a/meet-ce/backend/jest.integration.config.mjs b/meet-ce/backend/jest.integration.config.mjs index d6ec82b3..228d8b21 100644 --- a/meet-ce/backend/jest.integration.config.mjs +++ b/meet-ce/backend/jest.integration.config.mjs @@ -5,8 +5,8 @@ const integrationConfig = { runInBand: true, forceExit: true, - detectOpenHandles: true, - testMatch: ['**/tests/integration/**/*.(spec|test).ts'], + detectOpenHandles: true, + testMatch: ['**/tests/integration/**/*.(spec|test).ts'] }; export default integrationConfig; diff --git a/meet-ce/backend/src/models/mongoose-schemas/room.schema.ts b/meet-ce/backend/src/models/mongoose-schemas/room.schema.ts index ade2844d..fe5cb62e 100644 --- a/meet-ce/backend/src/models/mongoose-schemas/room.schema.ts +++ b/meet-ce/backend/src/models/mongoose-schemas/room.schema.ts @@ -1,5 +1,4 @@ import { - MeetRecordingEncodingPreset, MeetRecordingLayout, MeetRoom, MeetRoomDeletionPolicyWithMeeting, @@ -54,25 +53,11 @@ const MeetRecordingConfigSchema = new Schema( layout: { type: String, enum: Object.values(MeetRecordingLayout), - required: true, - default: MeetRecordingLayout.GRID + required: true }, encoding: { type: Schema.Types.Mixed, - required: true, - default: MeetRecordingEncodingPreset.H264_720P_30, - validate: { - validator: (value: any) => { - if (!value) return true; - - if (typeof value === 'string') return true; - - if (typeof value === 'object') return value.video || value.audio; - - return false; - }, - message: 'Encoding must be a preset string or options object' - } + required: true } }, { _id: false } @@ -318,8 +303,7 @@ const MeetRoomSchema = new Schema( status: { type: String, enum: Object.values(MeetRoomStatus), - required: true, - default: MeetRoomStatus.OPEN + required: true }, rolesUpdatedAt: { type: Number, @@ -328,8 +312,7 @@ const MeetRoomSchema = new Schema( meetingEndAction: { type: String, enum: Object.values(MeetingEndAction), - required: true, - default: MeetingEndAction.NONE + required: true } }, { diff --git a/meet-ce/backend/src/routes/recording.routes.ts b/meet-ce/backend/src/routes/recording.routes.ts index 7aa80eee..e3ff3fd9 100644 --- a/meet-ce/backend/src/routes/recording.routes.ts +++ b/meet-ce/backend/src/routes/recording.routes.ts @@ -29,6 +29,14 @@ recordingRouter.use(bodyParser.urlencoded({ extended: true })); recordingRouter.use(bodyParser.json()); // Recording Routes +recordingRouter.post( + '/', + withAuth(apiKeyValidator, roomMemberTokenValidator), + validateStartRecordingReq, + withRecordingEnabled, + authorizeRecordingControl, + recordingCtrl.startRecording +); recordingRouter.get( '/', withAuth( @@ -77,6 +85,13 @@ recordingRouter.delete( authorizeRecordingAccess('canDeleteRecordings'), recordingCtrl.deleteRecording ); +recordingRouter.post( + '/:recordingId/stop', + withAuth(apiKeyValidator, roomMemberTokenValidator), + withValidRecordingId, + authorizeRecordingControl, + recordingCtrl.stopRecording +); recordingRouter.get( '/:recordingId/media', validateGetRecordingMediaReq, @@ -95,23 +110,3 @@ recordingRouter.get( authorizeRecordingAccess('canRetrieveRecordings'), recordingCtrl.getRecordingUrl ); -recordingRouter.post( - '/', - withAuth(apiKeyValidator, roomMemberTokenValidator), - validateStartRecordingReq, - withRecordingEnabled, - authorizeRecordingControl, - recordingCtrl.startRecording -); -recordingRouter.post( - '/:recordingId/stop', - withAuth(apiKeyValidator, roomMemberTokenValidator), - withValidRecordingId, - authorizeRecordingControl, - recordingCtrl.stopRecording -); - -// Internal Recording Routes -// export const internalRecordingRouter: Router = Router(); -// internalRecordingRouter.use(bodyParser.urlencoded({ extended: true })); -// internalRecordingRouter.use(bodyParser.json()); diff --git a/meet-ce/backend/src/utils/html-injection.utils.ts b/meet-ce/backend/src/utils/html-injection.utils.ts index 200c1489..4bfd9089 100644 --- a/meet-ce/backend/src/utils/html-injection.utils.ts +++ b/meet-ce/backend/src/utils/html-injection.utils.ts @@ -49,12 +49,21 @@ function validateBasePathConfig(): void { // Check if BASE_URL contains a path (other than just /) if (url.pathname && url.pathname !== '/') { - console.warn(chalk.yellow('⚠️ WARNING: MEET_BASE_URL contains a path segment:'), chalk.cyan(url.pathname)); - console.warn(chalk.yellow(' MEET_BASE_URL should only contain https protocol and host (e.g., https://example.com)')); + console.warn( + chalk.yellow('⚠️ WARNING: MEET_BASE_URL contains a path segment:'), + chalk.cyan(url.pathname) + ); + console.warn( + chalk.yellow( + ' MEET_BASE_URL should only contain https protocol and host (e.g., https://example.com)' + ) + ); console.warn(chalk.yellow(' Use MEET_BASE_PATH for the deployment path (e.g., /meet/)')); if (basePath && basePath !== '/') { - console.warn(chalk.red(` This may cause issues: BASE_URL path "${url.pathname}" + BASE_PATH "${basePath}"`)); + console.warn( + chalk.red(` This may cause issues: BASE_URL path "${url.pathname}" + BASE_PATH "${basePath}"`) + ); } } } catch { diff --git a/meet-ce/backend/tests/helpers/request-helpers.ts b/meet-ce/backend/tests/helpers/request-helpers.ts index b4037e6f..baad91a8 100644 --- a/meet-ce/backend/tests/helpers/request-helpers.ts +++ b/meet-ce/backend/tests/helpers/request-helpers.ts @@ -17,6 +17,7 @@ import { MeetRoomOptions, MeetRoomRolesConfig, MeetRoomStatus, + MeetUserOptions, SecurityConfig, WebhookConfig } from '@openvidu-meet/typings'; @@ -139,7 +140,7 @@ export const updateRoomsAppearanceConfig = async (config: { appearance: MeetAppe return response; }; -export const getWebbhookConfig = async () => { +export const getWebhookConfig = async () => { checkAppIsRunning(); const { accessToken } = await loginRootAdmin(); @@ -150,7 +151,7 @@ export const getWebbhookConfig = async () => { return response; }; -export const updateWebbhookConfig = async (config: WebhookConfig) => { +export const updateWebhookConfig = async (config: WebhookConfig) => { checkAppIsRunning(); const { accessToken } = await loginRootAdmin(); @@ -211,7 +212,9 @@ export const restoreDefaultGlobalConfig = async () => { export const loginReq = async (body: { userId: string; password: string }) => { checkAppIsRunning(); - return await request(app).post(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/auth/login`).send(body); + return await request(app) + .post(getFullPath(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/auth/login`)) + .send(body); }; /** @@ -241,7 +244,7 @@ export const refreshTokenReq = async (refreshToken: string) => { checkAppIsRunning(); return await request(app) - .post(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/auth/refresh`) + .post(getFullPath(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/auth/refresh`)) .set(INTERNAL_CONFIG.REFRESH_TOKEN_HEADER, refreshToken); }; @@ -252,7 +255,7 @@ export const createUser = async (options: MeetUserOptions) => { const { accessToken } = await loginRootAdmin(); return await request(app) - .post(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/users`) + .post(getFullPath(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/users`)) .set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, accessToken) .send(options); }; @@ -262,7 +265,7 @@ export const getUsers = async (query: Record = {}) => { const { accessToken } = await loginRootAdmin(); return await request(app) - .get(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/users`) + .get(getFullPath(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/users`)) .set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, accessToken) .query(query); }; @@ -272,7 +275,7 @@ export const getUser = async (userId: string) => { const { accessToken } = await loginRootAdmin(); return await request(app) - .get(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/users/${userId}`) + .get(getFullPath(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/users/${userId}`)) .set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, accessToken) .send(); }; @@ -281,7 +284,7 @@ export const getMe = async (accessToken: string) => { checkAppIsRunning(); return await request(app) - .get(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/users/me`) + .get(getFullPath(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/users/me`)) .set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, accessToken) .send(); }; @@ -325,7 +328,7 @@ export const resetUserPassword = async (userId: string, newPassword: string, acc const { accessToken: rootAdminAccessToken } = await loginRootAdmin(); return await request(app) - .put(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/users/${userId}/password`) + .put(getFullPath(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/users/${userId}/password`)) .set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, accessToken ?? rootAdminAccessToken) .send({ newPassword }); }; @@ -335,7 +338,7 @@ export const updateUserRole = async (userId: string, role: string, accessToken?: const { accessToken: rootAdminAccessToken } = await loginRootAdmin(); return await request(app) - .put(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/users/${userId}/role`) + .put(getFullPath(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/users/${userId}/role`)) .set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, accessToken ?? rootAdminAccessToken) .send({ role }); }; @@ -345,7 +348,7 @@ export const deleteUser = async (userId: string, accessToken?: string) => { const { accessToken: rootAdminAccessToken } = await loginRootAdmin(); return await request(app) - .delete(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/users/${userId}`) + .delete(getFullPath(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/users/${userId}`)) .set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, accessToken ?? rootAdminAccessToken) .send(); }; @@ -355,7 +358,7 @@ export const bulkDeleteUsers = async (userIds: string[], accessToken?: string) = const { accessToken: rootAdminAccessToken } = await loginRootAdmin(); return await request(app) - .delete(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/users`) + .delete(getFullPath(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/users`)) .set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, accessToken ?? rootAdminAccessToken) .query({ userIds: userIds.join(',') }); }; @@ -387,7 +390,10 @@ export const deleteAllUsers = async () => { export const createRoom = async (options: MeetRoomOptions = {}, accessToken?: string): Promise => { checkAppIsRunning(); - const req = request(app).post(`${INTERNAL_CONFIG.API_BASE_PATH_V1}/rooms`).send(options).expect(201); + const req = request(app) + .post(getFullPath(`${INTERNAL_CONFIG.API_BASE_PATH_V1}/rooms`)) + .send(options) + .expect(201); if (accessToken) { req.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, accessToken); @@ -464,7 +470,7 @@ export const updateRoomRoles = async (roomId: string, rolesConfig: MeetRoomRoles checkAppIsRunning(); return await request(app) - .put(`${INTERNAL_CONFIG.API_BASE_PATH_V1}/rooms/${roomId}/roles`) + .put(getFullPath(`${INTERNAL_CONFIG.API_BASE_PATH_V1}/rooms/${roomId}/roles`)) .set(INTERNAL_CONFIG.API_KEY_HEADER, MEET_ENV.INITIAL_API_KEY) .send({ roles: rolesConfig }); }; @@ -473,7 +479,7 @@ export const updateRoomAnonymousConfig = async (roomId: string, anonymousConfig: checkAppIsRunning(); return await request(app) - .put(`${INTERNAL_CONFIG.API_BASE_PATH_V1}/rooms/${roomId}/anonymous`) + .put(getFullPath(`${INTERNAL_CONFIG.API_BASE_PATH_V1}/rooms/${roomId}/anonymous`)) .set(INTERNAL_CONFIG.API_KEY_HEADER, MEET_ENV.INITIAL_API_KEY) .send({ anonymous: anonymousConfig }); }; @@ -560,7 +566,7 @@ export const createRoomMember = async (roomId: string, memberOptions: MeetRoomMe checkAppIsRunning(); return await request(app) - .post(`${INTERNAL_CONFIG.API_BASE_PATH_V1}/rooms/${roomId}/members`) + .post(getFullPath(`${INTERNAL_CONFIG.API_BASE_PATH_V1}/rooms/${roomId}/members`)) .set(INTERNAL_CONFIG.API_KEY_HEADER, MEET_ENV.INITIAL_API_KEY) .send(memberOptions); }; @@ -569,7 +575,7 @@ export const getRoomMembers = async (roomId: string, query: Record { checkAppIsRunning(); return await request(app) - .get(`${INTERNAL_CONFIG.API_BASE_PATH_V1}/rooms/${roomId}/members/${memberId}`) + .get(getFullPath(`${INTERNAL_CONFIG.API_BASE_PATH_V1}/rooms/${roomId}/members/${memberId}`)) .set(INTERNAL_CONFIG.API_KEY_HEADER, MEET_ENV.INITIAL_API_KEY); }; @@ -586,7 +592,7 @@ export const updateRoomMember = async (roomId: string, memberId: string, updates checkAppIsRunning(); return await request(app) - .put(`${INTERNAL_CONFIG.API_BASE_PATH_V1}/rooms/${roomId}/members/${memberId}`) + .put(getFullPath(`${INTERNAL_CONFIG.API_BASE_PATH_V1}/rooms/${roomId}/members/${memberId}`)) .set(INTERNAL_CONFIG.API_KEY_HEADER, MEET_ENV.INITIAL_API_KEY) .send(updates); }; @@ -595,7 +601,7 @@ export const deleteRoomMember = async (roomId: string, memberId: string) => { checkAppIsRunning(); return await request(app) - .delete(`${INTERNAL_CONFIG.API_BASE_PATH_V1}/rooms/${roomId}/members/${memberId}`) + .delete(getFullPath(`${INTERNAL_CONFIG.API_BASE_PATH_V1}/rooms/${roomId}/members/${memberId}`)) .set(INTERNAL_CONFIG.API_KEY_HEADER, MEET_ENV.INITIAL_API_KEY) .send(); }; @@ -604,7 +610,7 @@ export const bulkDeleteRoomMembers = async (roomId: string, memberIds: string[]) checkAppIsRunning(); return await request(app) - .delete(`${INTERNAL_CONFIG.API_BASE_PATH_V1}/rooms/${roomId}/members`) + .delete(getFullPath(`${INTERNAL_CONFIG.API_BASE_PATH_V1}/rooms/${roomId}/members`)) .set(INTERNAL_CONFIG.API_KEY_HEADER, MEET_ENV.INITIAL_API_KEY) .query({ memberIds: memberIds.join(',') }); }; @@ -617,7 +623,7 @@ export const generateRoomMemberTokenRequest = async ( checkAppIsRunning(); const req = request(app) - .post(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/rooms/${roomId}/members/token`) + .post(getFullPath(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/rooms/${roomId}/members/token`)) .send(tokenOptions); if (accessToken) { @@ -800,7 +806,7 @@ export const getAllRecordings = async (query: Record = {}) => { checkAppIsRunning(); return await request(app) - .get(`${INTERNAL_CONFIG.API_BASE_PATH_V1}/recordings`) + .get(getFullPath(`${INTERNAL_CONFIG.API_BASE_PATH_V1}/recordings`)) .set(INTERNAL_CONFIG.API_KEY_HEADER, MEET_ENV.INITIAL_API_KEY) .query(query); }; @@ -809,7 +815,7 @@ export const getAllRecordingsFromRoom = async (roomMemberToken: string) => { checkAppIsRunning(); return await request(app) - .get(`${INTERNAL_CONFIG.API_BASE_PATH_V1}/recordings`) + .get(getFullPath(`${INTERNAL_CONFIG.API_BASE_PATH_V1}/recordings`)) .set(INTERNAL_CONFIG.ROOM_MEMBER_TOKEN_HEADER, roomMemberToken); }; @@ -821,7 +827,7 @@ export const downloadRecordings = async ( checkAppIsRunning(); const req = request(app) - .get(`${INTERNAL_CONFIG.API_BASE_PATH_V1}/recordings/download`) + .get(getFullPath(`${INTERNAL_CONFIG.API_BASE_PATH_V1}/recordings/download`)) .query({ recordingIds: recordingIds.join(',') }); if (roomMemberToken) { diff --git a/meet-ce/backend/tests/helpers/test-scenarios.ts b/meet-ce/backend/tests/helpers/test-scenarios.ts index 532a0ede..bfcc5fb3 100644 --- a/meet-ce/backend/tests/helpers/test-scenarios.ts +++ b/meet-ce/backend/tests/helpers/test-scenarios.ts @@ -144,7 +144,7 @@ export const setupSingleRoomWithRecording = async ( */ export const setupCompletedRecording = async (roomData: RoomData, stopDelay?: StringValue): Promise => { // Start recording - const response = await startRecording(roomData.room.roomId, roomData.moderatorToken); + const response = await startRecording(roomData.room.roomId); expectValidStartRecordingResponse(response, roomData.room.roomId, roomData.room.roomName); const recordingId = response.body.recordingId; roomData.recordingId = recordingId; @@ -155,7 +155,7 @@ export const setupCompletedRecording = async (roomData: RoomData, stopDelay?: St } // Stop recording - await stopRecording(recordingId, roomData.moderatorToken); + await stopRecording(recordingId); return recordingId; }; diff --git a/meet-ce/backend/tests/integration/api/analytics/get-analytics.test.ts b/meet-ce/backend/tests/integration/api/analytics/get-analytics.test.ts index d45dd9f2..e5c6baf0 100644 --- a/meet-ce/backend/tests/integration/api/analytics/get-analytics.test.ts +++ b/meet-ce/backend/tests/integration/api/analytics/get-analytics.test.ts @@ -90,7 +90,7 @@ describe('Analytics API Tests', () => { expect(analytics.completeRecordings).toBe(1); // Now stop the incomplete recording - await stopRecording(roomToStop.recordingId!, roomToStop.moderatorToken); + await stopRecording(roomToStop.recordingId!); }); }); }); diff --git a/meet-ce/backend/tests/integration/api/auth/token-validation.test.ts b/meet-ce/backend/tests/integration/api/auth/token-validation.test.ts index 1bfe7cda..fa5ef44a 100644 --- a/meet-ce/backend/tests/integration/api/auth/token-validation.test.ts +++ b/meet-ce/backend/tests/integration/api/auth/token-validation.test.ts @@ -10,6 +10,7 @@ import { deleteRoom, deleteRoomMember, deleteUser, + getFullPath, loginUser, resetUserPassword, sleep, @@ -22,8 +23,8 @@ import { import { setupRoomMember, setupSingleRoom, setupTestUsers, setupUser } from '../../../helpers/test-scenarios.js'; import { TestUsers, UserData } from '../../../interfaces/scenarios.js'; -const USERS_PATH = `${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/users`; -const ROOMS_PATH = `${INTERNAL_CONFIG.API_BASE_PATH_V1}/rooms`; +const USERS_PATH = getFullPath(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/users`); +const ROOMS_PATH = getFullPath(`${INTERNAL_CONFIG.API_BASE_PATH_V1}/rooms`); describe('Token Validation Tests', () => { let app: Express; @@ -63,7 +64,7 @@ describe('Token Validation Tests', () => { it('should succeed when admin accesses admin-only endpoint', async () => { const response = await request(app) - .get(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/api-keys`) + .get(getFullPath(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/api-keys`)) .set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, testUsers.admin.accessToken); expect(response.status).toBe(200); }); @@ -150,7 +151,7 @@ describe('Token Validation Tests', () => { it('should fail when USER tries to access ADMIN-only endpoint', async () => { const response = await request(app) - .get(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/api-keys`) + .get(getFullPath(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/api-keys`)) .set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, testUsers.user.accessToken); expect(response.status).toBe(403); expect(response.body).toHaveProperty('message'); diff --git a/meet-ce/backend/tests/integration/api/global-config/webhook.test.ts b/meet-ce/backend/tests/integration/api/global-config/webhook.test.ts index 9cc42ab8..c6c5542e 100644 --- a/meet-ce/backend/tests/integration/api/global-config/webhook.test.ts +++ b/meet-ce/backend/tests/integration/api/global-config/webhook.test.ts @@ -3,11 +3,11 @@ import { Request } from 'express'; import { MEET_ENV } from '../../../../src/environment.js'; import { expectValidationError } from '../../../helpers/assertion-helpers.js'; import { - getWebbhookConfig, + getWebhookConfig, restoreDefaultGlobalConfig, startTestServer, testWebhookUrl, - updateWebbhookConfig + updateWebhookConfig } from '../../../helpers/request-helpers.js'; import { startWebhookServer, stopWebhookServer } from '../../../helpers/test-scenarios.js'; @@ -26,12 +26,12 @@ describe('Webhook Config API Tests', () => { enabled: true, url: 'https://example.com/webhook' }; - let response = await updateWebbhookConfig(validConfig); + let response = await updateWebhookConfig(validConfig); expect(response.status).toBe(200); expect(response.body.message).toBe('Webhooks config updated successfully'); - response = await getWebbhookConfig(); + response = await getWebhookConfig(); expect(response.status).toBe(200); expect(response.body.enabled).toBe(true); expect(response.body.url).toBe(validConfig.url); @@ -39,17 +39,17 @@ describe('Webhook Config API Tests', () => { }); it('should allow disabling webhooks', async () => { - const oldWebhookConfig = await getWebbhookConfig(); + const oldWebhookConfig = await getWebhookConfig(); expect(oldWebhookConfig.status).toBe(200); - let response = await updateWebbhookConfig({ + let response = await updateWebhookConfig({ enabled: false }); expect(response.status).toBe(200); expect(response.body.message).toBe('Webhooks config updated successfully'); - response = await getWebbhookConfig(); + response = await getWebhookConfig(); expect(response.status).toBe(200); expect(response.body.enabled).toBe(false); expect(response.body.url).toBe(oldWebhookConfig.body.url); @@ -60,12 +60,12 @@ describe('Webhook Config API Tests', () => { enabled: false, url: 'https://newurl.com/webhook' }; - const response = await updateWebbhookConfig(config); + const response = await updateWebhookConfig(config); expect(response.status).toBe(200); expect(response.body.message).toBe('Webhooks config updated successfully'); - const configResponse = await getWebbhookConfig(); + const configResponse = await getWebhookConfig(); expect(configResponse.status).toBe(200); expect(configResponse.body.enabled).toBe(config.enabled); expect(configResponse.body.url).toBe(config.url); @@ -74,7 +74,7 @@ describe('Webhook Config API Tests', () => { describe('Update webhook config validation', () => { it('should reject invalid webhook URL', async () => { - const response = await updateWebbhookConfig({ + const response = await updateWebhookConfig({ enabled: true, url: 'invalid-url' }); @@ -84,14 +84,14 @@ describe('Webhook Config API Tests', () => { }); it('should reject missing URL when webhooks are enabled', async () => { - const response = await updateWebbhookConfig({ enabled: true }); + const response = await updateWebhookConfig({ enabled: true }); expect(response.status).toBe(422); expectValidationError(response, 'url', 'URL is required when webhooks are enabled'); }); it('should reject non-http(s) URLs', async () => { - const response = await updateWebbhookConfig({ + const response = await updateWebhookConfig({ enabled: true, url: 'ftp://example.com/webhook' }); @@ -103,7 +103,7 @@ describe('Webhook Config API Tests', () => { describe('Get webhook config', () => { it('should return webhook config when authenticated as admin', async () => { - const response = await getWebbhookConfig(); + const response = await getWebhookConfig(); expect(response.status).toBe(200); expect(response.body).toEqual({ diff --git a/meet-ce/backend/tests/integration/api/meetings/end-meeting.test.ts b/meet-ce/backend/tests/integration/api/meetings/end-meeting.test.ts index 49a2dcfc..0d9e9ed8 100644 --- a/meet-ce/backend/tests/integration/api/meetings/end-meeting.test.ts +++ b/meet-ce/backend/tests/integration/api/meetings/end-meeting.test.ts @@ -4,7 +4,6 @@ import { OpenViduMeetError } from '../../../../src/models/error.model.js'; import { LiveKitService } from '../../../../src/services/livekit.service.js'; import { deleteAllRooms, - deleteRoom, disconnectFakeParticipants, endMeeting, getRoom, @@ -71,11 +70,7 @@ 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, { withMeeting: 'force' }); - expect(response.status).toBe(200); - - response = await endMeeting(roomData.room.roomId, roomData.moderatorToken); + const response = await endMeeting('nonexistent-room-id', roomData.moderatorToken); expect(response.status).toBe(404); }); }); diff --git a/meet-ce/backend/tests/integration/api/meetings/kick-participant.test.ts b/meet-ce/backend/tests/integration/api/meetings/kick-participant.test.ts index 6ef189d6..ece3211c 100644 --- a/meet-ce/backend/tests/integration/api/meetings/kick-participant.test.ts +++ b/meet-ce/backend/tests/integration/api/meetings/kick-participant.test.ts @@ -4,7 +4,6 @@ import { OpenViduMeetError } from '../../../../src/models/error.model.js'; import { LiveKitService } from '../../../../src/services/livekit.service.js'; import { deleteAllRooms, - deleteRoom, disconnectFakeParticipants, kickParticipant, startTestServer @@ -62,11 +61,7 @@ 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, { withMeeting: 'force' }); - expect(response.status).toBe(200); - - response = await kickParticipant(roomData.room.roomId, participantIdentity, roomData.moderatorToken); + const response = await kickParticipant('nonexistent-room-id', participantIdentity, roomData.moderatorToken); expect(response.status).toBe(404); expect(response.body.error).toBe('Room Error'); }); diff --git a/meet-ce/backend/tests/integration/api/meetings/update-participant.test.ts b/meet-ce/backend/tests/integration/api/meetings/update-participant.test.ts index 36bc0fee..dcdaab82 100644 --- a/meet-ce/backend/tests/integration/api/meetings/update-participant.test.ts +++ b/meet-ce/backend/tests/integration/api/meetings/update-participant.test.ts @@ -7,7 +7,6 @@ import { LiveKitService } from '../../../../src/services/livekit.service.js'; import { getPermissions } from '../../../helpers/assertion-helpers.js'; import { deleteAllRooms, - deleteRoom, disconnectFakeParticipants, startTestServer, updateParticipant, @@ -35,6 +34,7 @@ describe('Meetings API Tests', () => { describe('Update Participant Tests', () => { const setParticipantMetadata = async (roomId: string, baseRole: MeetRoomMemberRole) => { const metadata: MeetRoomMemberTokenMetadata = { + iat: Date.now(), livekitUrl: MEET_ENV.LIVEKIT_URL, roomId, baseRole, @@ -141,12 +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, { withMeeting: 'force' }); - expect(response.status).toBe(200); - - response = await updateParticipant( - roomData.room.roomId, + const response = await updateParticipant( + 'nonexistent-room-id', participantIdentity, MeetRoomMemberRole.MODERATOR, roomData.moderatorToken diff --git a/meet-ce/backend/tests/integration/api/recordings/bulk-delete-recording.test.ts b/meet-ce/backend/tests/integration/api/recordings/bulk-delete-recording.test.ts index d8e865ea..7d5f6912 100644 --- a/meet-ce/backend/tests/integration/api/recordings/bulk-delete-recording.test.ts +++ b/meet-ce/backend/tests/integration/api/recordings/bulk-delete-recording.test.ts @@ -70,7 +70,7 @@ describe('Recording API Tests', () => { ] }); - await stopRecording(activeRecordingId!, activeRecordingRoom!.moderatorToken); + await stopRecording(activeRecordingId!); deleteResponse = await bulkDeleteRecordings([activeRecordingId]); @@ -98,8 +98,8 @@ describe('Recording API Tests', () => { }); await Promise.all( - recordingIds.map((id, index) => { - return stopRecording(id!, testContext.getRoomByIndex(index)!.moderatorToken); + recordingIds.map((id) => { + return stopRecording(id); }) ); }); diff --git a/meet-ce/backend/tests/integration/api/recordings/delete-recording.test.ts b/meet-ce/backend/tests/integration/api/recordings/delete-recording.test.ts index 4edef88e..e0f44c20 100644 --- a/meet-ce/backend/tests/integration/api/recordings/delete-recording.test.ts +++ b/meet-ce/backend/tests/integration/api/recordings/delete-recording.test.ts @@ -24,17 +24,17 @@ describe('Recording API Tests', () => { }); describe('Delete Recording Tests', () => { - let recordingId: string, moderatorToken: string; + let recordingId: string; beforeEach(async () => { const testContext = await setupMultiRecordingsTestContext(1, 1, 1); const roomData = testContext.getRoomByIndex(0)!; - ({ recordingId = '', moderatorToken } = roomData); + ({ recordingId = '' } = roomData); }); afterAll(async () => { - await stopAllRecordings(moderatorToken); + await stopAllRecordings(); await Promise.all([deleteAllRecordings(), deleteAllRooms()]); }); @@ -50,17 +50,17 @@ describe('Recording API Tests', () => { }); describe('Delete Recording Validation', () => { - let room: MeetRoom, recordingId: string, moderatorToken: string; + let room: MeetRoom, recordingId: string; beforeAll(async () => { await deleteAllRecordings(); const testContext = await setupMultiRecordingsTestContext(1, 1, 1); const roomData = testContext.getRoomByIndex(0)!; - ({ room, recordingId = '', moderatorToken } = roomData); + ({ room, recordingId = '' } = roomData); }); afterAll(async () => { - await stopAllRecordings(moderatorToken); + await stopAllRecordings(); await Promise.all([deleteAllRecordings(), deleteAllRooms()]); }); @@ -101,13 +101,13 @@ describe('Recording API Tests', () => { it('should return 409 when attempting to delete an active recording', async () => { const testContext = await setupMultiRecordingsTestContext(1, 1, 0); - const { recordingId: activeRecordingId = '', moderatorToken } = testContext.rooms[0]; + const { recordingId: activeRecordingId = '' } = testContext.rooms[0]; // Attempt to delete the active recording let deleteResponse = await deleteRecording(activeRecordingId); expect(deleteResponse.status).toBe(409); - await stopRecording(activeRecordingId, moderatorToken); + await stopRecording(activeRecordingId); // Attempt to delete the recording again deleteResponse = await deleteRecording(activeRecordingId); expect(deleteResponse.status).toBe(200); diff --git a/meet-ce/backend/tests/integration/api/recordings/get-media-recording.test.ts b/meet-ce/backend/tests/integration/api/recordings/get-media-recording.test.ts index c97c9d33..94749de7 100644 --- a/meet-ce/backend/tests/integration/api/recordings/get-media-recording.test.ts +++ b/meet-ce/backend/tests/integration/api/recordings/get-media-recording.test.ts @@ -167,7 +167,7 @@ describe('Recording API Tests', () => { it('should return a 409 when the recording is in progress', async () => { const testContext = await setupMultiRecordingsTestContext(1, 1, 0); - const { recordingId: activeRecordingId = '', moderatorToken } = testContext.rooms[0]; + const { recordingId: activeRecordingId = '' } = testContext.rooms[0]; // Attempt to get the media of an active recording const response = await getRecordingMedia(activeRecordingId); @@ -176,7 +176,7 @@ describe('Recording API Tests', () => { expect(response.body).toHaveProperty('message'); expect(response.body.message).toContain(`Recording '${activeRecordingId}' is not stopped yet`); - await stopRecording(activeRecordingId, moderatorToken); + await stopRecording(activeRecordingId); }); it('should return 404 when recording not found', async () => { diff --git a/meet-ce/backend/tests/integration/api/recordings/get-recordings.test.ts b/meet-ce/backend/tests/integration/api/recordings/get-recordings.test.ts index a6d69866..c7e37eef 100644 --- a/meet-ce/backend/tests/integration/api/recordings/get-recordings.test.ts +++ b/meet-ce/backend/tests/integration/api/recordings/get-recordings.test.ts @@ -93,7 +93,7 @@ describe('Recordings API Tests', () => { expect(recordings[0].status).toBe(MeetRecordingStatus.COMPLETE); // Stop the active recording to clean up - await stopRecording(roomData.recordingId!, roomData.moderatorToken); + await stopRecording(roomData.recordingId!); }); it('should return recordings with fields filter applied', async () => { @@ -180,7 +180,7 @@ describe('Recordings API Tests', () => { afterAll(async () => { // Stop the active recording - await stopRecording(roomDataC.recordingId!, roomDataC.moderatorToken); + await stopRecording(roomDataC.recordingId!); // Disconnect participants and clean up await disconnectFakeParticipants(); diff --git a/meet-ce/backend/tests/integration/api/recordings/race-conditions.test.ts b/meet-ce/backend/tests/integration/api/recordings/race-conditions.test.ts index 01885a94..5a619d4d 100644 --- a/meet-ce/backend/tests/integration/api/recordings/race-conditions.test.ts +++ b/meet-ce/backend/tests/integration/api/recordings/race-conditions.test.ts @@ -39,11 +39,7 @@ describe('Recording API Race Conditions Tests', () => { }); afterEach(async () => { - const moderatorToken = context?.getRoomByIndex(0)?.moderatorToken; - - if (moderatorToken) { - await stopAllRecordings(moderatorToken); - } + await stopAllRecordings(); eventController.reset(); await disconnectFakeParticipants(); @@ -223,9 +219,7 @@ describe('Recording API Race Conditions Tests', () => { try { // Start recordings in all rooms simultaneously (all should timeout) - const results = await Promise.all( - rooms.map((room) => startRecording(room.room.roomId)) - ); + const results = await Promise.all(rooms.map((room) => startRecording(room.room.roomId))); // All should timeout results.forEach((result) => { @@ -238,9 +232,7 @@ describe('Recording API Race Conditions Tests', () => { }); // ✅ EXPECTED BEHAVIOR: After timeouts, all rooms should be available again - const retryResults = await Promise.all( - rooms.map((room) => startRecording(room.room.roomId)) - ); + const retryResults = await Promise.all(rooms.map((room) => startRecording(room.room.roomId))); for (const startResult of retryResults) { expect(startResult.status).toBe(201); @@ -300,9 +292,7 @@ describe('Recording API Race Conditions Tests', () => { const roomDataList = Array.from({ length: 5 }, (_, index) => context!.getRoomByIndex(index)!); - const startResponses = await Promise.all( - roomDataList.map((roomData) => startRecording(roomData.room.roomId)) - ); + const startResponses = await Promise.all(roomDataList.map((roomData) => startRecording(roomData.room.roomId))); startResponses.forEach((response, index) => { expectValidStartRecordingResponse( @@ -314,9 +304,7 @@ describe('Recording API Race Conditions Tests', () => { const recordingIds = startResponses.map((res) => res.body.recordingId); - const stopResponses = await Promise.all( - recordingIds.map((recordingId) => stopRecording(recordingId)) - ); + const stopResponses = await Promise.all(recordingIds.map((recordingId) => stopRecording(recordingId))); stopResponses.forEach((response, index) => { expectValidStopRecordingResponse( diff --git a/meet-ce/backend/tests/integration/api/rooms/create-room.test.ts b/meet-ce/backend/tests/integration/api/rooms/create-room.test.ts index 3d1e3bae..717ba096 100644 --- a/meet-ce/backend/tests/integration/api/rooms/create-room.test.ts +++ b/meet-ce/backend/tests/integration/api/rooms/create-room.test.ts @@ -312,8 +312,7 @@ describe('Room API Tests', () => { recording: { enabled: true, layout: DEFAULT_RECORDING_LAYOUT, - encoding: DEFAULT_RECORDING_ENCODING_PRESET, // Default value - allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER + encoding: DEFAULT_RECORDING_ENCODING_PRESET // Default value }, chat: { enabled: true }, virtualBackground: { enabled: true }, @@ -340,8 +339,7 @@ describe('Room API Tests', () => { recording: { enabled: true, layout: DEFAULT_RECORDING_LAYOUT, - encoding: MeetRecordingEncodingPreset.H264_1080P_30, - allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER + encoding: MeetRecordingEncodingPreset.H264_1080P_30 }, chat: { enabled: true }, virtualBackground: { enabled: true }, @@ -368,8 +366,7 @@ describe('Room API Tests', () => { recording: { enabled: true, layout: DEFAULT_RECORDING_LAYOUT, - encoding: MeetRecordingEncodingPreset.PORTRAIT_H264_720P_30, - allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER + encoding: MeetRecordingEncodingPreset.PORTRAIT_H264_720P_30 }, chat: { enabled: true }, virtualBackground: { enabled: true }, @@ -426,8 +423,7 @@ describe('Room API Tests', () => { bitrate: 192, frequency: 44100 } - }, - allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER + } }, chat: { enabled: true }, virtualBackground: { enabled: true }, diff --git a/meet-ce/backend/tests/integration/api/rooms/update-room-config.test.ts b/meet-ce/backend/tests/integration/api/rooms/update-room-config.test.ts index 37d9b4e8..99e9f808 100644 --- a/meet-ce/backend/tests/integration/api/rooms/update-room-config.test.ts +++ b/meet-ce/backend/tests/integration/api/rooms/update-room-config.test.ts @@ -278,8 +278,7 @@ describe('Room API Tests', () => { recording: { enabled: true, layout: MeetRecordingLayout.SPEAKER, - encoding: MeetRecordingEncodingPreset.H264_720P_30, - allowAccessTo: MeetRecordingAccess.ADMIN + encoding: MeetRecordingEncodingPreset.H264_720P_30 } } }); @@ -306,8 +305,7 @@ describe('Room API Tests', () => { recording: { enabled: true, layout: MeetRecordingLayout.SPEAKER, - encoding: MeetRecordingEncodingPreset.H264_1080P_30, - allowAccessTo: MeetRecordingAccess.ADMIN + encoding: MeetRecordingEncodingPreset.H264_1080P_30 }, chat: { enabled: true }, virtualBackground: { enabled: true }, diff --git a/meet-ce/backend/tests/integration/api/security/meeting-security.test.ts b/meet-ce/backend/tests/integration/api/security/meeting-security.test.ts index c2f53f34..15eb16d6 100644 --- a/meet-ce/backend/tests/integration/api/security/meeting-security.test.ts +++ b/meet-ce/backend/tests/integration/api/security/meeting-security.test.ts @@ -101,6 +101,7 @@ describe('Meeting API Security Tests', () => { const setParticipantMetadata = async () => { const metadata: MeetRoomMemberTokenMetadata = { + iat: Date.now(), livekitUrl: MEET_ENV.LIVEKIT_URL, roomId, baseRole: MeetRoomMemberRole.SPEAKER, diff --git a/meet-ce/backend/tests/integration/api/security/recording-security.test.ts b/meet-ce/backend/tests/integration/api/security/recording-security.test.ts index 30f33354..c0974ec5 100644 --- a/meet-ce/backend/tests/integration/api/security/recording-security.test.ts +++ b/meet-ce/backend/tests/integration/api/security/recording-security.test.ts @@ -12,6 +12,7 @@ import { disconnectFakeParticipants, getFullPath, getRecordingAccessSecret, + sleep, startRecording, startTestServer, stopAllRecordings @@ -137,9 +138,10 @@ describe('Recording API Security Tests', () => { expect(response.status).toBe(202); // Recreate recording for next tests since it was stopped + await sleep('2s'); // Ensure recording is fully stopped before starting a new one const startResponse = await startRecording(roomData.room.roomId); expectValidStartRecordingResponse(startResponse, roomData.room.roomId, roomData.room.roomName); - roomData.recordingId = startResponse.body.recordingId; + recordingId = startResponse.body.recordingId; }); it('should fail when using access token', async () => { @@ -161,9 +163,10 @@ describe('Recording API Security Tests', () => { expect(response.status).toBe(202); // Recreate recording for next tests since it was stopped + await sleep('2s'); // Ensure recording is fully stopped before starting a new one const startResponse = await startRecording(roomData.room.roomId); expectValidStartRecordingResponse(startResponse, roomData.room.roomId, roomData.room.roomName); - roomData.recordingId = startResponse.body.recordingId; + recordingId = startResponse.body.recordingId; }); it('should fail when using room member token without canRecord permission', async () => { diff --git a/meet-ce/backend/tests/integration/api/security/room-members-security.test.ts b/meet-ce/backend/tests/integration/api/security/room-members-security.test.ts index 1f3cc54e..c3d6dd28 100644 --- a/meet-ce/backend/tests/integration/api/security/room-members-security.test.ts +++ b/meet-ce/backend/tests/integration/api/security/room-members-security.test.ts @@ -4,12 +4,18 @@ import { Express } from 'express'; import request from 'supertest'; import { INTERNAL_CONFIG } from '../../../../src/config/internal-config.js'; import { MEET_ENV } from '../../../../src/environment.js'; -import { createRoomMember, deleteAllRooms, deleteAllUsers, startTestServer } from '../../../helpers/request-helpers.js'; +import { + createRoomMember, + deleteAllRooms, + deleteAllUsers, + getFullPath, + startTestServer +} from '../../../helpers/request-helpers.js'; import { setupSingleRoom, setupTestUsers, setupTestUsersForRoom } from '../../../helpers/test-scenarios.js'; import { RoomData, RoomTestUsers, TestUsers } from '../../../interfaces/scenarios.js'; -const ROOMS_PATH = `${INTERNAL_CONFIG.API_BASE_PATH_V1}/rooms`; -const INTERNAL_ROOMS_PATH = `${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/rooms`; +const ROOMS_PATH = getFullPath(`${INTERNAL_CONFIG.API_BASE_PATH_V1}/rooms`); +const INTERNAL_ROOMS_PATH = getFullPath(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/rooms`); describe('Room Members API Security Tests', () => { let app: Express; diff --git a/meet-ce/backend/tests/integration/webhooks/webhook.test.ts b/meet-ce/backend/tests/integration/webhooks/webhook.test.ts index e81938e6..d8ee2556 100644 --- a/meet-ce/backend/tests/integration/webhooks/webhook.test.ts +++ b/meet-ce/backend/tests/integration/webhooks/webhook.test.ts @@ -1,6 +1,5 @@ import { afterAll, beforeAll, beforeEach, describe, expect, it } from '@jest/globals'; import { - MeetRecordingAccess, MeetRecordingEncodingPreset, MeetRecordingInfo, MeetRecordingLayout, @@ -21,7 +20,7 @@ import { restoreDefaultGlobalConfig, sleep, startTestServer, - updateWebbhookConfig + updateWebhookConfig } from '../../helpers/request-helpers.js'; import { setupSingleRoom, @@ -37,8 +36,7 @@ describe('Webhook Integration Tests', () => { recording: { enabled: true, layout: MeetRecordingLayout.GRID, - encoding: MeetRecordingEncodingPreset.H264_720P_30, - allowAccessTo: MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER + encoding: MeetRecordingEncodingPreset.H264_720P_30 }, chat: { enabled: true }, virtualBackground: { enabled: true }, @@ -61,7 +59,7 @@ describe('Webhook Integration Tests', () => { beforeEach(async () => { receivedWebhooks = []; // Enable webhooks in global config - await updateWebbhookConfig({ + await updateWebhookConfig({ enabled: true, url: `http://localhost:5080/webhook` }); @@ -81,7 +79,7 @@ describe('Webhook Integration Tests', () => { }; it('should not send webhooks when disabled', async () => { - await updateWebbhookConfig({ + await updateWebhookConfig({ enabled: false });