import { type EventEmitter, once } from 'node:events'; import process from 'node:process'; import { describe, test, expect } from 'vitest'; import { SSRCMap, type VoiceUserData } from '../src/receive/SSRCMap'; async function onceOrThrow(target: Emitter, event: string, after: number) { return new Promise((resolve, reject) => { target.on(event, resolve); setTimeout(() => reject(new Error('Time up')), after); }); } describe('SSRCMap', () => { test('update persists data and emits correctly', async () => { const fixture1: VoiceUserData = { audioSSRC: 1, userId: '123', }; const fixture2: VoiceUserData = { ...fixture1, videoSSRC: 2, }; const map = new SSRCMap(); process.nextTick(() => map.update(fixture1)); let [oldData, newData] = await once(map, 'update'); expect(oldData).toBeUndefined(); expect(newData).toMatchObject(fixture1); expect(map.get(fixture1.audioSSRC)).toMatchObject(fixture1); process.nextTick(() => map.update(fixture2)); [oldData, newData] = await once(map, 'update'); expect(oldData).toMatchObject(fixture1); expect(newData).toMatchObject(fixture2); expect(map.get(fixture1.userId)).toMatchObject(fixture2); }); test('delete removes data and emits correctly', async () => { const fixture1: VoiceUserData = { audioSSRC: 1, userId: '123', }; const map = new SSRCMap(); map.delete(fixture1.audioSSRC); await expect(onceOrThrow(map, 'delete', 5)).rejects.toThrow(); map.update(fixture1); process.nextTick(() => map.delete(fixture1.audioSSRC)); await expect(once(map, 'delete')).resolves.toMatchObject([fixture1]); map.delete(fixture1.audioSSRC); await expect(onceOrThrow(map, 'delete', 5)).rejects.toThrow(); map.update(fixture1); process.nextTick(() => map.delete(fixture1.userId)); await expect(once(map, 'delete')).resolves.toMatchObject([fixture1]); expect(map.get(fixture1.audioSSRC)).toBeUndefined(); }); });