


















@@ -371,6 +371,89 @@ process.exit(2);
371371await waitFor(() => !isProcessAlive(mockPid));
372372});
373373374+posixIt("cleans gateway descendants after a failed gateway leader exits", async () => {
375+const root = makeTempDir();
376+const outputDir = makeTempDir();
377+const mockScript = path.join(root, "scripts/e2e/mock-openai-server.mjs");
378+const gatewayScript = path.join(root, "gateway-leader-exits.mjs");
379+const gatewayGrandchildPidPath = path.join(root, "gateway-grandchild.pid");
380+let gatewayGrandchildPid = 0;
381+fs.mkdirSync(path.dirname(mockScript), { recursive: true });
382+writeExecutable(
383+mockScript,
384+`
385+process.stdout.write("mock-openai listening\\n");
386+process.on("SIGTERM", () => process.exit(0));
387+setInterval(() => {}, 1000);
388+`,
389+);
390+writeExecutable(
391+gatewayScript,
392+`
393+import { spawn } from "node:child_process";
394+import fs from "node:fs";
395+396+const grandchild = spawn(process.execPath, [
397+ "-e",
398+ "process.on('SIGTERM', () => process.exit(0)); setInterval(() => {}, 1000);",
399+], { stdio: "ignore" });
400+fs.writeFileSync(${JSON.stringify(gatewayGrandchildPidPath)}, String(grandchild.pid));
401+process.exit(2);
402+`,
403+);
404+405+try {
406+await expect(
407+startLocalSut(
408+{
409+gatewayPort: 19042,
410+groupId: "group",
411+mockPort: 19043,
412+mockResponseText: "ok",
413+ outputDir,
414+repoRoot: root,
415+sutToken: "token",
416+testerId: "tester",
417+},
418+{
419+createGatewaySpawnSpec: () => ({
420+args: [gatewayScript],
421+command: process.execPath,
422+options: { cwd: root, env: process.env },
423+}),
424+drainUpdates: async () => ({
425+drained: 0,
426+webhookUrlSet: false,
427+}),
428+waitForOutputReady: async (child, _pattern, output, label) => {
429+if (label === "mock-openai") {
430+await waitFor(() => output().includes("mock-openai listening"));
431+return;
432+}
433+await waitFor(() => fs.existsSync(gatewayGrandchildPidPath));
434+gatewayGrandchildPid = Number.parseInt(
435+fs.readFileSync(gatewayGrandchildPidPath, "utf8"),
436+10,
437+);
438+if (child.exitCode === null && child.signalCode === null) {
439+await new Promise<void>((resolve) => {
440+child.once("exit", () => resolve());
441+});
442+}
443+throw new Error("gateway exited before ready");
444+},
445+},
446+),
447+).rejects.toThrow("gateway exited before ready");
448+449+await waitFor(() => !isProcessAlive(gatewayGrandchildPid));
450+} finally {
451+if (gatewayGrandchildPid && isProcessAlive(gatewayGrandchildPid)) {
452+process.kill(gatewayGrandchildPid, "SIGKILL");
453+}
454+}
455+});
456+374457posixIt("stops Crabbox recording when the desktop probe fails", async () => {
375458const root = makeTempDir();
376459const recorderPath = path.join(root, "fake-crabbox-recorder.mjs");
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。