@@ -3,13 +3,14 @@ import { mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
|
3 | 3 | import path from "node:path"; |
4 | 4 | import { setTimeout as sleep } from "node:timers/promises"; |
5 | 5 | import { resolvePreferredOpenClawTmpDir } from "openclaw/plugin-sdk/temp-path"; |
6 | | -import { describe, expect, it } from "vitest"; |
| 6 | +import { describe, expect, it, vi } from "vitest"; |
7 | 7 | import { |
8 | 8 | formatMatrixQaCliCommand, |
9 | 9 | redactMatrixQaCliOutput, |
10 | 10 | resolveMatrixQaOpenClawCliEntryPath, |
11 | 11 | runMatrixQaOpenClawCli, |
12 | 12 | startMatrixQaOpenClawCli, |
| 13 | +testing, |
13 | 14 | } from "./scenario-runtime-cli.js"; |
14 | 15 | |
15 | 16 | function isProcessRunning(pid: number): boolean { |
@@ -60,6 +61,38 @@ describe("Matrix QA CLI runtime", () => {
|
60 | 61 | ).toBe("GET /_matrix/client/v3/sync?access_token=abcdef…ghij"); |
61 | 62 | }); |
62 | 63 | |
| 64 | +it("force-kills Windows CLI process trees when graceful taskkill fails", () => { |
| 65 | +const platformDescriptor = Object.getOwnPropertyDescriptor(process, "platform"); |
| 66 | +Object.defineProperty(process, "platform", { value: "win32", configurable: true }); |
| 67 | +try { |
| 68 | +const killMock = vi.fn(); |
| 69 | +const child = { |
| 70 | +pid: 12345, |
| 71 | +kill: killMock, |
| 72 | +} as unknown as Parameters<typeof testing.killMatrixQaCliChild>[0]; |
| 73 | +const runTaskkill = vi |
| 74 | +.fn() |
| 75 | +.mockReturnValueOnce({ status: 1 }) |
| 76 | +.mockReturnValueOnce({ status: 0 }); |
| 77 | + |
| 78 | +testing.killMatrixQaCliChild(child, "SIGTERM", runTaskkill); |
| 79 | + |
| 80 | +expect(runTaskkill).toHaveBeenNthCalledWith(1, "taskkill", ["/PID", "12345", "/T"], { |
| 81 | +stdio: "ignore", |
| 82 | +windowsHide: true, |
| 83 | +}); |
| 84 | +expect(runTaskkill).toHaveBeenNthCalledWith(2, "taskkill", ["/PID", "12345", "/T", "/F"], { |
| 85 | +stdio: "ignore", |
| 86 | +windowsHide: true, |
| 87 | +}); |
| 88 | +expect(killMock).not.toHaveBeenCalled(); |
| 89 | +} finally { |
| 90 | +if (platformDescriptor) { |
| 91 | +Object.defineProperty(process, "platform", platformDescriptor); |
| 92 | +} |
| 93 | +} |
| 94 | +}); |
| 95 | + |
63 | 96 | it("prefers the ESM OpenClaw CLI entrypoint when present", async () => { |
64 | 97 | const root = await mkdtemp(path.join(resolvePreferredOpenClawTmpDir(), "matrix-qa-cli-entry-")); |
65 | 98 | try { |
|