

























@@ -145,4 +145,59 @@ describe("qa runner model catalog", () => {
145145}
146146},
147147);
148+149+it.runIf(process.platform !== "win32")(
150+"preserves abort grace when catalog descendants exit cleanly",
151+async () => {
152+const repoRoot = await makeTempDir("openclaw-qa-model-catalog-clean-");
153+const readyPath = path.join(repoRoot, "descendant.ready");
154+const cleanupPath = path.join(repoRoot, "descendant.cleanup");
155+const pidPath = path.join(repoRoot, "descendant.pid");
156+let descendantPid: number | undefined;
157+const controller = new AbortController();
158+const childScript = [
159+"const fs = require('node:fs');",
160+`fs.writeFileSync(${JSON.stringify(pidPath)}, String(process.pid));`,
161+"process.on('SIGTERM', () => {",
162+" setTimeout(() => {",
163+` fs.writeFileSync(${JSON.stringify(cleanupPath)}, 'clean');`,
164+" process.exit(0);",
165+" }, 75);",
166+"});",
167+`fs.writeFileSync(${JSON.stringify(readyPath)}, 'ready');`,
168+"setInterval(() => {}, 1000);",
169+].join("\n");
170+const catalogScript = [
171+"const { spawn } = require('node:child_process');",
172+`spawn(process.execPath, ['-e', ${JSON.stringify(childScript)}], { stdio: 'ignore' });`,
173+"process.on('SIGTERM', () => process.exit(0));",
174+"setInterval(() => {}, 1000);",
175+].join("\n");
176+177+try {
178+await fs.mkdir(path.join(repoRoot, "dist"), { recursive: true });
179+await fs.writeFile(path.join(repoRoot, "dist", "index.js"), catalogScript, "utf8");
180+const runPromise = loadQaRunnerModelOptions({
181+ repoRoot,
182+signal: controller.signal,
183+});
184+185+await waitForFile(readyPath, 2_000);
186+descendantPid = Number.parseInt(await fs.readFile(pidPath, "utf8"), 10);
187+expect(Number.isInteger(descendantPid)).toBe(true);
188+const abortStartedAt = Date.now();
189+controller.abort();
190+191+await expect(runPromise).rejects.toThrow("qa model catalog aborted");
192+expect(await fs.readFile(cleanupPath, "utf8")).toBe("clean");
193+expect(Date.now() - abortStartedAt).toBeLessThan(1_700);
194+await waitForDead(descendantPid, 2_000);
195+} finally {
196+if (descendantPid !== undefined && isProcessAlive(descendantPid)) {
197+process.kill(descendantPid, "SIGKILL");
198+}
199+await fs.rm(repoRoot, { force: true, recursive: true });
200+}
201+},
202+);
148203});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。