From 5e32d7fa554b6071b59fca77cee2d66c06e3533e Mon Sep 17 00:00:00 2001 From: jkooe <62494645+jkooe@users.noreply.github.com> Date: Tue, 27 Oct 2026 08:04:27 +0800 Subject: [PATCH] =?UTF-8?q?test(e2e):=20=E5=AD=90=E8=BF=9B=E7=A8=8B?= =?UTF-8?q?=E7=BB=9F=E4=B8=80=E7=99=BB=E8=AE=B0=20+=20=E9=80=80=E5=87=BA?= =?UTF-8?q?=E5=85=9C=E5=BA=95=20SIGKILL=20=E2=80=94=E2=80=94=20=E4=BF=AE?= =?UTF-8?q?=E8=BF=9E=E8=B7=91=E5=81=B6=E5=8F=91=E5=A4=B1=E8=B4=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 套件有 8 处 spawn,各自 finally 只发 kill()(SIGTERM)就 process.exit(), 被控端可能来不及退出 → 下一轮固定端口仍被占用,表现为「连跑第二次偶发失败、 重跑又绿」这种最难查的形态。 做法:把 spawn 包一层统一登记(同名函数遮蔽,调用点零改动),进程退出时对 仍存活的子进程 SIGKILL。单点收口,将来新增 spawn 也不会漏。 实测:连跑两次 34/34,退出后 pgrep 无残留 agent 进程。 --- tests/e2e/run.mjs | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/tests/e2e/run.mjs b/tests/e2e/run.mjs index 9d76dec..9cfc88e 100644 --- a/tests/e2e/run.mjs +++ b/tests/e2e/run.mjs @@ -6,7 +6,7 @@ * * 运行:node tests/e2e/run.mjs */ -import { spawn } from 'node:child_process'; +import { spawn as rawSpawn } from 'node:child_process'; import { mkdtempSync, rmSync, writeFileSync } from 'node:fs'; import { createHash } from 'node:crypto'; import { createRequire } from 'node:module'; @@ -25,6 +25,34 @@ const RawWebSocket = requireFromClient('ws'); const __dirname = dirname(fileURLToPath(import.meta.url)); const root = join(__dirname, '../..'); +/** + * 子进程登记表 + 兜底清理。 + * + * 为什么需要:本套件有 8 处 spawn(主 agent / TLS / ed25519 / hub / fs / monitor…), + * 各自的 finally 只做 `child.kill()`(SIGTERM),随后立刻 process.exit —— 被控端 + * 可能还没来得及退出就被父进程带走,**下一轮连跑时固定端口仍被占用**,表现为 + * 「连跑第二次偶发失败」(历史真机现象,重跑又绿,最难查)。 + * + * 做法:包一层 spawn,把所有子进程登记下来;进程退出时统一 SIGKILL。 + * 调用点无需改动(同名函数遮蔽),单点收口也不会漏掉将来新增的 spawn。 + */ +const liveChildren = new Set(); +function spawn(...args) { + const child = rawSpawn(...args); + liveChildren.add(child); + child.once('exit', () => liveChildren.delete(child)); + return child; +} +process.on('exit', () => { + for (const child of liveChildren) { + try { + child.kill('SIGKILL'); + } catch { + /* 已退出或不可杀:忽略 */ + } + } +}); + const PORT = 18765; const TLS_PORT = 18772; const ED25519_PORT = 18781;