视频会议保存

This commit is contained in:
duoaohui
2026-05-18 19:42:55 +08:00
parent 315884685f
commit ac8ec4c903
41 changed files with 5069 additions and 39 deletions

View File

@@ -0,0 +1,69 @@
import { describe, it, expect, beforeEach } from 'vitest';
import dgram from 'node:dgram';
import {
allocatePortPair,
releasePortPair,
getPortPoolStats,
_clearPortPool,
} from '../../src/utils/port-pool.js';
describe('utils/port-pool', () => {
beforeEach(() => {
_clearPortPool();
});
it('allocates a pair where rtcp = rtp + 1 and rtp is even', async () => {
const { rtp, rtcp } = await allocatePortPair();
expect(rtcp).toBe(rtp + 1);
expect(rtp % 2).toBe(0);
expect(rtp).toBeGreaterThanOrEqual(50000);
expect(rtcp).toBeLessThanOrEqual(59999);
});
it('does not return the same pair twice while still reserved', async () => {
const seen = new Set<number>();
const pairs = await Promise.all(Array.from({ length: 8 }, () => allocatePortPair()));
for (const p of pairs) {
expect(seen.has(p.rtp)).toBe(false);
expect(seen.has(p.rtcp)).toBe(false);
seen.add(p.rtp);
seen.add(p.rtcp);
}
expect(getPortPoolStats().reserved).toBe(16);
});
it('release allows the pair to be re-issued', async () => {
const a = await allocatePortPair();
releasePortPair(a.rtp, a.rtcp);
expect(getPortPoolStats().reserved).toBe(0);
// 不一定立刻拿到完全相同的端口(随机分配),但能继续分配且不再含已释放的端口
const b = await allocatePortPair();
expect(b.rtp).not.toBe(undefined);
});
it('release is idempotent', () => {
releasePortPair(50000, 50001); // 未被分配过,应静默
expect(getPortPoolStats().reserved).toBe(0);
});
it('skips ports already bound by another socket', async () => {
// 强制占用一个偶数端口,确认 probe 会跳过它并最终返回别的端口
const occupiedRtp = 50100;
const blocker = dgram.createSocket('udp4');
await new Promise<void>((resolve, reject) => {
blocker.once('error', reject);
blocker.bind(occupiedRtp, '0.0.0.0', () => resolve());
});
try {
// 多次分配,确保命中冲突路径
const results = await Promise.all(Array.from({ length: 6 }, () => allocatePortPair()));
for (const r of results) {
expect(r.rtp).not.toBe(occupiedRtp);
}
} finally {
blocker.close();
}
});
});