覆盖网络线累积产物(此前只在工作区、未入版本库):
- 新增 relay 子系统 src/net/relay/**(wire/duplex/server/client/dialer/switcher/directory/identity/keys/placement/network/addr-override/main/index)
- 新增 src/worker/relay-tunnel.ts、src/web/routes/overlay.ts
- 新增观测/演练脚本 overlay-probe、overlay-failover-drill、overlay-keyring、overlay-holepunch、overlay-jitter、overlay-wan、overlay-relaykey-add、relay-mem-calibrate
- 新增测试 12 个(relay / relay-failover / remote-spawner / instance-port / overlay-{network,auth,bootstrap,identity} / remote-user-fs 等)
验收基线:npm test = 162 pass / 0 fail / 1 skip;--scene all = 12 PASS / 0 SKIP / 0 FAIL;overlay-probe = 12/12
73 lines
2.8 KiB
JavaScript
73 lines
2.8 KiB
JavaScript
/**
|
||
* 覆盖网络 S3 · 实例端口区间单测(不连外网、只在本机回环上 bind)。
|
||
* 运行:node --test test/instance-port.test.mjs(已含在 npm test / npm verify 中)
|
||
*
|
||
* ## 这个测试防的是什么
|
||
* `listen(0)` 让**每台 worker 各自**随机取端口,而跨机实例的隧道落点全挤在 Manager 的
|
||
* `127.0.0.1` 上 ⇒ 两台 worker 取到同号就撞号,`ssh -R` 失败还是**静默**的,
|
||
* Manager 会按该端口拨到**别人的实例**(2026-09-16 实测)。
|
||
*
|
||
* 所以这里要钉死两件事:
|
||
* ① 取到的端口**必须落在配置区间内**(区间隔离的前提)
|
||
* ② 起点被占 ⇒ **跳到下一个**,绝不"回落到 `listen(0)`"
|
||
*/
|
||
import { test } from 'node:test'
|
||
import assert from 'node:assert/strict'
|
||
import { createServer } from 'node:net'
|
||
|
||
import { findFreePortInRange, findInstancePort } from '../lib/supervisor/spawn.js'
|
||
|
||
/** 占住一个回环端口,返回 `{ port, release }`。 */
|
||
function holdPort() {
|
||
return new Promise((resolve, reject) => {
|
||
const server = createServer()
|
||
server.once('error', reject)
|
||
server.listen(0, '127.0.0.1', () => {
|
||
const address = server.address()
|
||
const port = typeof address === 'object' && address !== null ? address.port : 0
|
||
resolve({ port, release: () => new Promise((r) => server.close(() => r())) })
|
||
})
|
||
})
|
||
}
|
||
|
||
test('端口区间:取到的端口必须落在区间内', async () => {
|
||
// 用打洞拿到的空闲端口当区间起点 ⇒ 测试不依赖"20000 一定空着"这种环境假设
|
||
const { port, release } = await holdPort()
|
||
try {
|
||
const got = await findFreePortInRange(port, 200)
|
||
assert.ok(got >= port && got < port + 200, `越界:${got} 不在 [${port}, ${port + 200})`)
|
||
} finally {
|
||
await release()
|
||
}
|
||
})
|
||
|
||
test('端口区间:起点被占用 ⇒ 跳过它,不回落到 listen(0)', async () => {
|
||
const { port, release } = await holdPort()
|
||
try {
|
||
const got = await findFreePortInRange(port, 200)
|
||
assert.notEqual(got, port, '起点被占却仍返回了它 ⇒ 会撞号')
|
||
assert.ok(got >= port && got < port + 200)
|
||
} finally {
|
||
await release()
|
||
}
|
||
})
|
||
|
||
test('端口区间:区间耗尽 ⇒ 抛错(禁止静默退回 listen(0))', async () => {
|
||
await assert.rejects(() => findFreePortInRange(20000, 0), /区间已耗尽/)
|
||
})
|
||
|
||
test('findInstancePort:未配区间(base<=0)⇒ 退回旧的 listen(0) 行为', async () => {
|
||
const got = await findInstancePort(0, 1000)
|
||
assert.ok(got > 0 && got < 65536, `应拿到合法端口,实际 ${got}`)
|
||
})
|
||
|
||
test('findInstancePort:配了区间 ⇒ 结果落在区间内', async () => {
|
||
const { port, release } = await holdPort()
|
||
try {
|
||
const got = await findInstancePort(port, 128)
|
||
assert.ok(got >= port && got < port + 128, `越界:${got}`)
|
||
} finally {
|
||
await release()
|
||
}
|
||
})
|