























11// OpenClaw SDK tests cover package behavior.
2-import { spawn, type SpawnOptionsWithoutStdio } from "node:child_process";
2+import { spawn, spawnSync, type SpawnOptionsWithoutStdio } from "node:child_process";
33import { createReadStream } from "node:fs";
44import fs from "node:fs/promises";
55import { createServer, type Server } from "node:http";
66import os from "node:os";
77import path from "node:path";
8-import { afterEach, describe, expect, it } from "vitest";
8+import { afterEach, describe, expect, it, vi } from "vitest";
99import { createPnpmRunnerSpawnSpec } from "../../../scripts/pnpm-runner.mjs";
1010import { createNodeEvalArgs } from "../../../src/test-utils/node-process.js";
1111@@ -89,8 +89,35 @@ function runCommand(
8989});
9090}
919192-function signalCommandProcess(child: ReturnType<typeof spawn>, signal: NodeJS.Signals): void {
93-if (process.platform !== "win32" && typeof child.pid === "number") {
92+function signalCommandProcess(
93+child: ReturnType<typeof spawn>,
94+signal: NodeJS.Signals,
95+runTaskkill: typeof spawnSync = spawnSync,
96+): void {
97+if (process.platform === "win32") {
98+if (typeof child.pid === "number") {
99+const args = ["/PID", String(child.pid), "/T"];
100+if (signal === "SIGKILL") {
101+args.push("/F");
102+}
103+const result = runTaskkill("taskkill", args, { stdio: "ignore", windowsHide: true });
104+if (!result.error && result.status === 0) {
105+return;
106+}
107+if (signal !== "SIGKILL") {
108+const forceResult = runTaskkill("taskkill", [...args, "/F"], {
109+stdio: "ignore",
110+windowsHide: true,
111+});
112+if (!forceResult.error && forceResult.status === 0) {
113+return;
114+}
115+}
116+}
117+child.kill(signal);
118+return;
119+}
120+if (typeof child.pid === "number") {
94121try {
95122process.kill(-child.pid, signal);
96123return;
@@ -260,6 +287,38 @@ describe("OpenClaw SDK package e2e", () => {
260287);
261288});
262289290+it("force-kills Windows package command process trees when graceful taskkill fails", () => {
291+const platformDescriptor = Object.getOwnPropertyDescriptor(process, "platform");
292+Object.defineProperty(process, "platform", { value: "win32", configurable: true });
293+try {
294+const killMock = vi.fn();
295+const child = {
296+pid: 12345,
297+kill: killMock,
298+} as unknown as ReturnType<typeof spawn>;
299+const runTaskkill = vi
300+.fn()
301+.mockReturnValueOnce({ status: 1 })
302+.mockReturnValueOnce({ status: 0 });
303+304+signalCommandProcess(child, "SIGTERM", runTaskkill);
305+306+expect(runTaskkill).toHaveBeenNthCalledWith(1, "taskkill", ["/PID", "12345", "/T"], {
307+stdio: "ignore",
308+windowsHide: true,
309+});
310+expect(runTaskkill).toHaveBeenNthCalledWith(2, "taskkill", ["/PID", "12345", "/T", "/F"], {
311+stdio: "ignore",
312+windowsHide: true,
313+});
314+expect(killMock).not.toHaveBeenCalled();
315+} finally {
316+if (platformDescriptor) {
317+Object.defineProperty(process, "platform", platformDescriptor);
318+}
319+}
320+});
321+263322it("packs and imports from an external temp consumer", async () => {
264323const repoRoot = process.cwd();
265324const packageRoots = [
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。