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