backend: Add integration tests for room garbage collector functionality
This commit is contained in:
parent
dbef4f6e21
commit
9bdacf7d0f
165
backend/tests/integration/api/rooms/garbage-collector.test.ts
Normal file
165
backend/tests/integration/api/rooms/garbage-collector.test.ts
Normal file
@ -0,0 +1,165 @@
|
||||
import { describe, it, expect, beforeAll, afterAll, afterEach } from '@jest/globals';
|
||||
import {
|
||||
createRoom,
|
||||
deleteAllRooms,
|
||||
startTestServer,
|
||||
stopTestServer,
|
||||
getRoom,
|
||||
sleep,
|
||||
joinFakeParticipant,
|
||||
runRoomGarbageCollector,
|
||||
disconnectFakeParticipants,
|
||||
getRooms
|
||||
} from '../../../utils/helpers.js';
|
||||
import ms from 'ms';
|
||||
import { setPrivateConfig } from '../../../../src/config/internal-config.js';
|
||||
|
||||
describe('OpenVidu Meet Room Garbage Collector Tests', () => {
|
||||
beforeAll(async () => {
|
||||
setPrivateConfig({
|
||||
MIN_FUTURE_TIME_FOR_ROOM_AUTODELETION_DATE: '0s'
|
||||
});
|
||||
await startTestServer();
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await stopTestServer();
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
// Remove all rooms created
|
||||
await deleteAllRooms();
|
||||
});
|
||||
|
||||
it('should delete a room with a past auto-deletion date if no participant is present', async () => {
|
||||
const createdRoom = await createRoom({
|
||||
roomIdPrefix: 'test-gc',
|
||||
autoDeletionDate: Date.now() + ms('1s')
|
||||
});
|
||||
|
||||
let response = await getRoom(createdRoom.roomId);
|
||||
expect(response.status).toBe(200);
|
||||
|
||||
// Wait for auto-deletion date to pass
|
||||
await sleep(2000);
|
||||
|
||||
// Run garbage collector
|
||||
await runRoomGarbageCollector();
|
||||
|
||||
response = await getRoom(createdRoom.roomId);
|
||||
expect(response.status).toBe(404);
|
||||
});
|
||||
|
||||
it('should mark room for deletion but not delete when expiration date has passed and participants exist', async () => {
|
||||
const createdRoom = await createRoom({
|
||||
roomIdPrefix: 'test-gc-participants',
|
||||
autoDeletionDate: Date.now() + ms('1s')
|
||||
});
|
||||
|
||||
joinFakeParticipant(createdRoom.roomId, 'test-participant');
|
||||
await sleep(2000);
|
||||
|
||||
await runRoomGarbageCollector();
|
||||
|
||||
// The room should not be deleted but marked for deletion
|
||||
const response = await getRoom(createdRoom.roomId);
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.body.markedForDeletion).toBe(true);
|
||||
});
|
||||
|
||||
it('should not touch a room with a future auto-deletion date', async () => {
|
||||
const createdRoom = await createRoom({
|
||||
roomIdPrefix: 'test-gc-future',
|
||||
autoDeletionDate: Date.now() + ms('1h')
|
||||
});
|
||||
|
||||
await runRoomGarbageCollector();
|
||||
|
||||
const response = await getRoom(createdRoom.roomId);
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.body.markedForDeletion).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should delete a room after the last participant leaves when it was marked for deletion', async () => {
|
||||
const createdRoom = await createRoom({
|
||||
roomIdPrefix: 'test-gc-lifecycle',
|
||||
autoDeletionDate: Date.now() + ms('1s')
|
||||
});
|
||||
|
||||
joinFakeParticipant(createdRoom.roomId, 'test-participant');
|
||||
|
||||
// Wait for the auto-deletion date to pass
|
||||
await sleep(1000);
|
||||
|
||||
// Should mark the room for deletion but not delete it yet
|
||||
await runRoomGarbageCollector();
|
||||
|
||||
let response = await getRoom(createdRoom.roomId);
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.body.markedForDeletion).toBe(true);
|
||||
expect(response.body.autoDeletionDate).toBeTruthy();
|
||||
expect(response.body.autoDeletionDate).toBeLessThan(Date.now());
|
||||
|
||||
disconnectFakeParticipants();
|
||||
|
||||
// Wait to receive webhook room_finished
|
||||
await sleep(3000);
|
||||
|
||||
// Verify that the room is deleted
|
||||
response = await getRoom(createdRoom.roomId);
|
||||
expect(response.status).toBe(404);
|
||||
});
|
||||
|
||||
it('should never delete a room without an auto-deletion date', async () => {
|
||||
const createdRoom = await createRoom({
|
||||
roomIdPrefix: 'test-gc-no-date'
|
||||
});
|
||||
|
||||
await runRoomGarbageCollector();
|
||||
|
||||
let response = await getRoom(createdRoom.roomId);
|
||||
expect(response.status).toBe(200);
|
||||
|
||||
await runRoomGarbageCollector();
|
||||
response = await getRoom(createdRoom.roomId);
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.body.markedForDeletion).toBeFalsy();
|
||||
expect(response.body.autoDeletionDate).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should handle multiple expired rooms in one batch', async () => {
|
||||
const rooms = await Promise.all([
|
||||
createRoom({ roomIdPrefix: 'test-gc-multi-1', autoDeletionDate: Date.now() + ms('1s') }),
|
||||
createRoom({ roomIdPrefix: 'test-gc-multi-2', autoDeletionDate: Date.now() + ms('1s') }),
|
||||
createRoom({ roomIdPrefix: 'test-gc-multi-3', autoDeletionDate: Date.now() + ms('1s') }),
|
||||
createRoom({ roomIdPrefix: 'test-gc-multi-4', autoDeletionDate: Date.now() + ms('1h') }),
|
||||
createRoom({ roomIdPrefix: 'test-gc-multi-5', autoDeletionDate: Date.now() + ms('1h') }),
|
||||
createRoom({ roomIdPrefix: 'test-gc-multi-6', autoDeletionDate: Date.now() + ms('1s') }),
|
||||
createRoom({ roomIdPrefix: 'test-gc-multi-7', autoDeletionDate: Date.now() + ms('1s') }),
|
||||
createRoom({ roomIdPrefix: 'test-gc-multi-8', autoDeletionDate: Date.now() + ms('1s') }),
|
||||
createRoom({ roomIdPrefix: 'test-gc-multi-9', autoDeletionDate: Date.now() + ms('1s') }),
|
||||
createRoom({ roomIdPrefix: 'test-gc-multi-10', autoDeletionDate: Date.now() + ms('1s') })
|
||||
]);
|
||||
|
||||
// Make sure all rooms are expired
|
||||
await sleep(2000);
|
||||
|
||||
await runRoomGarbageCollector();
|
||||
|
||||
for (const room of rooms) {
|
||||
const response = await getRoom(room.roomId);
|
||||
|
||||
if (room.autoDeletionDate! < Date.now()) {
|
||||
expect(response.status).toBe(404); // Should be deleted
|
||||
} else {
|
||||
expect(response.status).toBe(200); // Should still exist
|
||||
}
|
||||
}
|
||||
|
||||
const response = await getRooms();
|
||||
const { body } = response;
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(body.rooms.length).toBe(2); // Only 2 rooms should remain
|
||||
});
|
||||
});
|
||||
@ -18,6 +18,8 @@ import { AuthMode, AuthType, MeetRoom, UserRole, MeetRoomOptions } from '../../s
|
||||
import { expect } from '@jest/globals';
|
||||
import INTERNAL_CONFIG from '../../src/config/internal-config.js';
|
||||
import { ChildProcess, execSync, spawn } from 'child_process';
|
||||
import { container } from '../../src/config/dependency-injector.config.js';
|
||||
import { RoomService } from '../../src/services/room.service.js';
|
||||
|
||||
const CREDENTIALS = {
|
||||
user: {
|
||||
@ -35,6 +37,10 @@ let server: Server;
|
||||
|
||||
const fakeParticipantsProcesses = new Map<string, ChildProcess>();
|
||||
|
||||
export const sleep = (ms: number) => {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
};
|
||||
|
||||
/**
|
||||
* Starts the test server
|
||||
*/
|
||||
@ -264,6 +270,24 @@ export const assertEmptyRooms = async () => {
|
||||
assertSuccessRoomsResponse(response, 0, 10, false, false);
|
||||
};
|
||||
|
||||
/**
|
||||
* Runs the room garbage collector to delete expired rooms.
|
||||
*
|
||||
* This function retrieves the RoomService from the dependency injection container
|
||||
* and calls its deleteExpiredRooms method to clean up expired rooms.
|
||||
* It then waits for 1 second before completing.
|
||||
*/
|
||||
export const runRoomGarbageCollector = async () => {
|
||||
if (!app) {
|
||||
throw new Error('App instance is not defined');
|
||||
}
|
||||
|
||||
const roomService = container.get(RoomService);
|
||||
await (roomService as any)['deleteExpiredRooms']();
|
||||
|
||||
await sleep(1000);
|
||||
};
|
||||
|
||||
/**
|
||||
* Deletes all rooms
|
||||
*/
|
||||
@ -383,10 +407,6 @@ export const disconnectFakeParticipants = () => {
|
||||
fakeParticipantsProcesses.clear();
|
||||
};
|
||||
|
||||
export const sleep = (ms: number) => {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
};
|
||||
|
||||
// PRIVATE METHODS
|
||||
|
||||
const runCommandSync = (command: string): string => {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user