70 lines
2.2 KiB
TypeScript
70 lines
2.2 KiB
TypeScript
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();
|
|
}
|
|
});
|
|
});
|