fix(rpc): resolve taskkill from system32 in script probes

This commit is contained in:
Vincent Koc
2026-06-21 09:10:48 +02:00
parent fc8542b377
commit 0a7b009647
4 changed files with 212 additions and 93 deletions

View File

@@ -62,10 +62,15 @@ import {
validateCliArgs,
waitForGatewayReady,
} from "../../scripts/e2e/kitchen-sink-rpc-walk.mjs";
import { resolveWindowsTaskkillPath } from "../../scripts/lib/windows-taskkill.mjs";
import { cleanupTempDirs, makeTempDir } from "../helpers/temp-dir.js";
const posixIt = process.platform === "win32" ? it.skip : it;
function expectedTaskkillPath(): string {
return resolveWindowsTaskkillPath();
}
afterEach(() => {
vi.restoreAllMocks();
vi.useRealTimers();
@@ -296,9 +301,14 @@ describe("kitchen-sink RPC gateway teardown", () => {
runTaskkill,
}),
).toBe(true);
expect(runTaskkill).toHaveBeenNthCalledWith(1, "taskkill", ["/PID", "12345", "/T"], {
stdio: "ignore",
});
expect(runTaskkill).toHaveBeenNthCalledWith(
1,
expectedTaskkillPath(),
["/PID", "12345", "/T"],
{
stdio: "ignore",
},
);
expect(
signalGateway(child, "SIGKILL", killProcess, {
@@ -306,9 +316,14 @@ describe("kitchen-sink RPC gateway teardown", () => {
runTaskkill,
}),
).toBe(true);
expect(runTaskkill).toHaveBeenNthCalledWith(2, "taskkill", ["/PID", "12345", "/T", "/F"], {
stdio: "ignore",
});
expect(runTaskkill).toHaveBeenNthCalledWith(
2,
expectedTaskkillPath(),
["/PID", "12345", "/T", "/F"],
{
stdio: "ignore",
},
);
expect(killProcess).not.toHaveBeenCalled();
expect(child.kill).not.toHaveBeenCalled();
});
@@ -331,12 +346,22 @@ describe("kitchen-sink RPC gateway teardown", () => {
}),
).toBe(true);
expect(runTaskkill).toHaveBeenNthCalledWith(1, "taskkill", ["/PID", "12345", "/T"], {
stdio: "ignore",
});
expect(runTaskkill).toHaveBeenNthCalledWith(2, "taskkill", ["/PID", "12345", "/T", "/F"], {
stdio: "ignore",
});
expect(runTaskkill).toHaveBeenNthCalledWith(
1,
expectedTaskkillPath(),
["/PID", "12345", "/T"],
{
stdio: "ignore",
},
);
expect(runTaskkill).toHaveBeenNthCalledWith(
2,
expectedTaskkillPath(),
["/PID", "12345", "/T", "/F"],
{
stdio: "ignore",
},
);
expect(killProcess).not.toHaveBeenCalled();
expect(child.kill).not.toHaveBeenCalled();
});
@@ -702,17 +727,27 @@ setInterval(() => {}, 1000);
platform: "win32",
runTaskkill,
});
expect(runTaskkill).toHaveBeenNthCalledWith(1, "taskkill", ["/PID", "12345", "/T"], {
stdio: "ignore",
});
expect(runTaskkill).toHaveBeenNthCalledWith(
1,
expectedTaskkillPath(),
["/PID", "12345", "/T"],
{
stdio: "ignore",
},
);
signalProcessGroup(child, "SIGKILL", {
platform: "win32",
runTaskkill,
});
expect(runTaskkill).toHaveBeenNthCalledWith(2, "taskkill", ["/PID", "12345", "/T", "/F"], {
stdio: "ignore",
});
expect(runTaskkill).toHaveBeenNthCalledWith(
2,
expectedTaskkillPath(),
["/PID", "12345", "/T", "/F"],
{
stdio: "ignore",
},
);
expect(child.kill).not.toHaveBeenCalled();
});
@@ -731,12 +766,22 @@ setInterval(() => {}, 1000);
runTaskkill,
});
expect(runTaskkill).toHaveBeenNthCalledWith(1, "taskkill", ["/PID", "12345", "/T"], {
stdio: "ignore",
});
expect(runTaskkill).toHaveBeenNthCalledWith(2, "taskkill", ["/PID", "12345", "/T", "/F"], {
stdio: "ignore",
});
expect(runTaskkill).toHaveBeenNthCalledWith(
1,
expectedTaskkillPath(),
["/PID", "12345", "/T"],
{
stdio: "ignore",
},
);
expect(runTaskkill).toHaveBeenNthCalledWith(
2,
expectedTaskkillPath(),
["/PID", "12345", "/T", "/F"],
{
stdio: "ignore",
},
);
expect(child.kill).not.toHaveBeenCalled();
});

View File

@@ -2,6 +2,7 @@
import { spawnSync } from "node:child_process";
import { EventEmitter } from "node:events";
import { describe, expect, it, vi } from "vitest";
import { resolveWindowsTaskkillPath } from "../../scripts/lib/windows-taskkill.mjs";
import {
assertRpcSmokeResponse,
cleanupTempRoot,
@@ -16,6 +17,31 @@ import {
waitForGatewayReady,
} from "../../scripts/measure-rpc-rtt.mjs";
function expectedTaskkillPath(): string {
return resolveWindowsTaskkillPath();
}
function restoreEnvValue(key: string, value: string | undefined): void {
if (value === undefined) {
delete process.env[key];
return;
}
process.env[key] = value;
}
function withDefaultWindowsSystemRoot(run: () => void): void {
const originalSystemRoot = process.env.SystemRoot;
const originalWindir = process.env.WINDIR;
try {
process.env.SystemRoot = "C:\\Windows";
delete process.env.WINDIR;
run();
} finally {
restoreEnvValue("SystemRoot", originalSystemRoot);
restoreEnvValue("WINDIR", originalWindir);
}
}
class FakeWebSocket extends EventEmitter {
static OPEN = 1;
static instances: FakeWebSocket[] = [];
@@ -324,7 +350,7 @@ describe("scripts/measure-rpc-rtt.mjs", () => {
expect(signalGatewayProcess(child, "SIGTERM", kill, { runTaskkill })).toBe(true);
if (process.platform === "win32") {
expect(runTaskkill).toHaveBeenCalledWith("taskkill", ["/PID", "12345", "/T"], {
expect(runTaskkill).toHaveBeenCalledWith(expectedTaskkillPath(), ["/PID", "12345", "/T"], {
stdio: "ignore",
});
expect(child.kill).not.toHaveBeenCalled();
@@ -335,66 +361,90 @@ describe("scripts/measure-rpc-rtt.mjs", () => {
});
it("signals Windows gateway process trees with taskkill", () => {
const child = Object.assign(new EventEmitter(), {
exitCode: null,
kill: vi.fn(),
pid: 12345,
signalCode: null,
});
const kill = vi.fn(() => true);
const runTaskkill = vi.fn(() => ({ error: undefined, status: 0 }));
withDefaultWindowsSystemRoot(() => {
const child = Object.assign(new EventEmitter(), {
exitCode: null,
kill: vi.fn(),
pid: 12345,
signalCode: null,
});
const kill = vi.fn(() => true);
const runTaskkill = vi.fn(() => ({ error: undefined, status: 0 }));
expect(
signalGatewayProcess(child, "SIGTERM", kill, {
platform: "win32",
runTaskkill,
}),
).toBe(true);
expect(runTaskkill).toHaveBeenNthCalledWith(1, "taskkill", ["/PID", "12345", "/T"], {
stdio: "ignore",
});
expect(
signalGatewayProcess(child, "SIGTERM", kill, {
platform: "win32",
runTaskkill,
}),
).toBe(true);
expect(runTaskkill).toHaveBeenNthCalledWith(
1,
expectedTaskkillPath(),
["/PID", "12345", "/T"],
{
stdio: "ignore",
},
);
expect(
signalGatewayProcess(child, "SIGKILL", kill, {
platform: "win32",
runTaskkill,
}),
).toBe(true);
expect(runTaskkill).toHaveBeenNthCalledWith(2, "taskkill", ["/PID", "12345", "/T", "/F"], {
stdio: "ignore",
expect(
signalGatewayProcess(child, "SIGKILL", kill, {
platform: "win32",
runTaskkill,
}),
).toBe(true);
expect(runTaskkill).toHaveBeenNthCalledWith(
2,
expectedTaskkillPath(),
["/PID", "12345", "/T", "/F"],
{
stdio: "ignore",
},
);
expect(kill).not.toHaveBeenCalled();
expect(child.kill).not.toHaveBeenCalled();
});
expect(kill).not.toHaveBeenCalled();
expect(child.kill).not.toHaveBeenCalled();
});
it("force-kills Windows gateway process trees when graceful taskkill fails", () => {
const child = Object.assign(new EventEmitter(), {
exitCode: null,
kill: vi.fn(),
pid: 12345,
signalCode: null,
});
const kill = vi.fn(() => true);
const runTaskkill = vi
.fn()
.mockReturnValueOnce({ error: undefined, status: 1 })
.mockReturnValueOnce({ error: undefined, status: 0 });
withDefaultWindowsSystemRoot(() => {
const child = Object.assign(new EventEmitter(), {
exitCode: null,
kill: vi.fn(),
pid: 12345,
signalCode: null,
});
const kill = vi.fn(() => true);
const runTaskkill = vi
.fn()
.mockReturnValueOnce({ error: undefined, status: 1 })
.mockReturnValueOnce({ error: undefined, status: 0 });
expect(
signalGatewayProcess(child, "SIGTERM", kill, {
platform: "win32",
runTaskkill,
}),
).toBe(true);
expect(
signalGatewayProcess(child, "SIGTERM", kill, {
platform: "win32",
runTaskkill,
}),
).toBe(true);
expect(runTaskkill).toHaveBeenNthCalledWith(1, "taskkill", ["/PID", "12345", "/T"], {
stdio: "ignore",
expect(runTaskkill).toHaveBeenNthCalledWith(
1,
expectedTaskkillPath(),
["/PID", "12345", "/T"],
{
stdio: "ignore",
},
);
expect(runTaskkill).toHaveBeenNthCalledWith(
2,
expectedTaskkillPath(),
["/PID", "12345", "/T", "/F"],
{
stdio: "ignore",
},
);
expect(kill).not.toHaveBeenCalled();
expect(child.kill).not.toHaveBeenCalled();
});
expect(runTaskkill).toHaveBeenNthCalledWith(2, "taskkill", ["/PID", "12345", "/T", "/F"], {
stdio: "ignore",
});
expect(kill).not.toHaveBeenCalled();
expect(child.kill).not.toHaveBeenCalled();
});
it("treats missing gateway process groups as already exited", () => {
@@ -442,12 +492,22 @@ describe("scripts/measure-rpc-rtt.mjs", () => {
await stopGateway(child, { killGraceMs: 1, killProcess: kill, runTaskkill });
if (process.platform === "win32") {
expect(runTaskkill).toHaveBeenNthCalledWith(1, "taskkill", ["/PID", "12346", "/T"], {
stdio: "ignore",
});
expect(runTaskkill).toHaveBeenNthCalledWith(2, "taskkill", ["/PID", "12346", "/T", "/F"], {
stdio: "ignore",
});
expect(runTaskkill).toHaveBeenNthCalledWith(
1,
expectedTaskkillPath(),
["/PID", "12346", "/T"],
{
stdio: "ignore",
},
);
expect(runTaskkill).toHaveBeenNthCalledWith(
2,
expectedTaskkillPath(),
["/PID", "12346", "/T", "/F"],
{
stdio: "ignore",
},
);
expect(child.kill).not.toHaveBeenCalled();
} else {
expect(kill).toHaveBeenNthCalledWith(1, -12346, 0);
@@ -489,12 +549,22 @@ describe("scripts/measure-rpc-rtt.mjs", () => {
});
if (process.platform === "win32") {
expect(runTaskkill).toHaveBeenNthCalledWith(1, "taskkill", ["/PID", "12350", "/T"], {
stdio: "ignore",
});
expect(runTaskkill).toHaveBeenNthCalledWith(2, "taskkill", ["/PID", "12350", "/T", "/F"], {
stdio: "ignore",
});
expect(runTaskkill).toHaveBeenNthCalledWith(
1,
expectedTaskkillPath(),
["/PID", "12350", "/T"],
{
stdio: "ignore",
},
);
expect(runTaskkill).toHaveBeenNthCalledWith(
2,
expectedTaskkillPath(),
["/PID", "12350", "/T", "/F"],
{
stdio: "ignore",
},
);
expect(child.kill).not.toHaveBeenCalled();
} else {
expect(kill).toHaveBeenCalledWith(-12350, "SIGKILL");
@@ -549,7 +619,7 @@ describe("scripts/measure-rpc-rtt.mjs", () => {
processLike.emit("SIGTERM");
if (process.platform === "win32") {
expect(runTaskkill).toHaveBeenCalledWith("taskkill", ["/PID", "12348", "/T"], {
expect(runTaskkill).toHaveBeenCalledWith(expectedTaskkillPath(), ["/PID", "12348", "/T"], {
stdio: "ignore",
});
expect(child.kill).not.toHaveBeenCalled();
@@ -595,7 +665,7 @@ describe("scripts/measure-rpc-rtt.mjs", () => {
processLike.emit("exit");
if (process.platform === "win32") {
expect(runTaskkill).toHaveBeenCalledWith("taskkill", ["/PID", "12349", "/T"], {
expect(runTaskkill).toHaveBeenCalledWith(expectedTaskkillPath(), ["/PID", "12349", "/T"], {
stdio: "ignore",
});
expect(child.kill).not.toHaveBeenCalled();