import * as fs from 'fs'; import * as os from 'os'; import * as path from 'path'; import { createComposedRecordingTarget, createServerTrackRecordingTarget, deleteServerTrackRecordingFiles, listServerTrackRecordingFiles, sanitizeRecordingPathSegment, updateServerTrackRecordingMetadataSize, writeComposedRecordingMetadata, writeServerTrackRecordingMetadata } from '../src/recording/storage'; describe('recording storage', () => { const originalRecordingDir = process.env.RECORDING_DIR; let tempDir: string; beforeEach(() => { tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'recording-storage-')); process.env.RECORDING_DIR = tempDir; }); afterEach(() => { process.env.RECORDING_DIR = originalRecordingDir; fs.rmSync(tempDir, { recursive: true, force: true }); }); test('sanitizes path segments', () => { expect(sanitizeRecordingPathSegment('../room:name', 'fallback')).toBe('__room_name'); expect(sanitizeRecordingPathSegment('', 'fallback')).toBe('fallback'); }); test('creates server track target and updates metadata size', () => { const target = createServerTrackRecordingTarget({ recordingId: 'recording/1', connectionId: 'room:1', participantId: 'participant-1', kind: 'video', trackId: 'track-1' }); expect(target.meetingId).toBe('room_1'); expect(target.filePath.startsWith(path.join(tempDir, 'room_1'))).toBe(true); expect(target.filename).toContain('recording_1-participant-1-video-track-1.webm'); writeServerTrackRecordingMetadata({ recordingId: 'recording-1', connectionId: 'room-1', participantId: 'participant-1', kind: 'video', trackId: 'track-1', target }); fs.writeFileSync(target.filePath, Buffer.from('webm')); updateServerTrackRecordingMetadataSize(target); const metadata = JSON.parse(fs.readFileSync(target.metadataPath, 'utf8')); expect(metadata).toEqual(expect.objectContaining({ meetingId: 'room_1', filename: target.filename, mimetype: 'video/webm', size: 4, userId: 'server-recorder', recordingSource: 'server', participantId: 'participant-1', trackKind: 'video', recordingStartedAt: expect.any(String), recordingEndedAt: expect.any(String) })); const files = listServerTrackRecordingFiles({ meetingId: 'room_1', recordingId: 'recording-1', trackKind: 'video' }); expect(files).toEqual([ expect.objectContaining({ filename: target.filename, participantId: 'participant-1', trackKind: 'video' }) ]); expect(deleteServerTrackRecordingFiles(files)).toEqual([ target.filename, `${target.filename}.json` ]); expect(fs.existsSync(target.filePath)).toBe(false); expect(fs.existsSync(target.metadataPath)).toBe(false); expect(listServerTrackRecordingFiles({ meetingId: 'room_1', recordingId: 'recording-1', trackKind: 'video' })).toEqual([]); }); test('writes composed recording metadata', () => { const target = createComposedRecordingTarget({ meetingId: 'room-1', recordingId: 'recording-1', format: 'webm' }); fs.writeFileSync(target.filePath, Buffer.from('composed')); writeComposedRecordingMetadata({ target, recordingId: 'recording-1', layout: 'grid', format: 'webm', host: { participantId: 'host-p', userId: 'host-user', id: 'host-user', name: 'Host User', avatar: '/uploads/host.png', role: 'host' }, participants: [ { participantId: 'p1', userId: 'participant-user', id: 'participant-user', name: 'Participant User', avatar: '/uploads/p1.png', role: 'participant' } ], inputs: [ { ...target, filename: 'p1-video.webm', recordingId: 'recording-1', participantId: 'p1', trackId: 'track-1', trackKind: 'video', uploadedAt: '2026-06-01T00:00:00.000Z', metadata: {} } ] }); const metadata = JSON.parse(fs.readFileSync(target.metadataPath, 'utf8')); expect(metadata).toEqual(expect.objectContaining({ meetingId: 'room-1', filename: target.filename, recordingSource: 'server-composed', size: 8, layout: 'grid', inputFiles: ['p1-video.webm'] })); expect(metadata.host).toEqual(expect.objectContaining({ participantId: 'host-p', userId: 'host-user', name: 'Host User', role: 'host' })); expect(metadata.participants).toEqual([ expect.objectContaining({ participantId: 'p1', userId: 'participant-user', name: 'Participant User', role: 'participant' }) ]); }); });