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.
This commit is contained in:
juancarmore 2026-02-03 17:40:50 +01:00
parent 27a6064b61
commit 18426e2111
23 changed files with 135 additions and 165 deletions

View File

@ -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;

View File

@ -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<MeetRoomDocument>(
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<MeetRoomDocument>(
meetingEndAction: {
type: String,
enum: Object.values(MeetingEndAction),
required: true,
default: MeetingEndAction.NONE
required: true
}
},
{

View File

@ -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());

View File

@ -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 {

View File

@ -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<string, unknown> = {}) => {
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<MeetRoom> => {
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<string, unkno
checkAppIsRunning();
return await request(app)
.get(`${INTERNAL_CONFIG.API_BASE_PATH_V1}/rooms/${roomId}/members`)
.get(getFullPath(`${INTERNAL_CONFIG.API_BASE_PATH_V1}/rooms/${roomId}/members`))
.set(INTERNAL_CONFIG.API_KEY_HEADER, MEET_ENV.INITIAL_API_KEY)
.query(query);
};
@ -578,7 +584,7 @@ export const getRoomMember = async (roomId: string, memberId: string) => {
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<string, unknown> = {}) => {
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) {

View File

@ -144,7 +144,7 @@ export const setupSingleRoomWithRecording = async (
*/
export const setupCompletedRecording = async (roomData: RoomData, stopDelay?: StringValue): Promise<string> => {
// 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;
};

View File

@ -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!);
});
});
});

View File

@ -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');

View File

@ -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({

View File

@ -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);
});
});

View File

@ -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');
});

View File

@ -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

View File

@ -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);
})
);
});

View File

@ -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);

View File

@ -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 () => {

View File

@ -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();

View File

@ -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(

View File

@ -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 },

View File

@ -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 },

View File

@ -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,

View File

@ -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 () => {

View File

@ -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;

View File

@ -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
});