diff --git a/scripts/e2e/telegram-user-credential-io.ts b/scripts/e2e/telegram-user-credential-io.ts index 0201d1804eb7..7d142e6a0720 100644 --- a/scripts/e2e/telegram-user-credential-io.ts +++ b/scripts/e2e/telegram-user-credential-io.ts @@ -273,9 +273,15 @@ export function signalChildProcessTree( args.push("/F"); } const result = runTaskkill("taskkill", args, { stdio: "ignore" }); - if (!result.error && result.status === 0) { + if (!result?.error && result?.status === 0) { return; } + if (signal !== "SIGKILL") { + const forceResult = runTaskkill("taskkill", [...args, "/F"], { stdio: "ignore" }); + if (!forceResult?.error && forceResult?.status === 0) { + return; + } + } } child.kill(signal); } diff --git a/test/scripts/telegram-user-credential.test.ts b/test/scripts/telegram-user-credential.test.ts index 1f1974add58c..a0c44006250c 100644 --- a/test/scripts/telegram-user-credential.test.ts +++ b/test/scripts/telegram-user-credential.test.ts @@ -432,6 +432,30 @@ setInterval(() => {}, 1000); expect(child.kill).not.toHaveBeenCalled(); }); + it("force-kills Windows credential helper process trees when graceful taskkill fails", () => { + const child = { + kill: vi.fn(), + pid: 12345, + }; + const runTaskkill = vi + .fn() + .mockReturnValueOnce({ error: undefined, status: 1 }) + .mockReturnValueOnce({ error: undefined, status: 0 }); + + signalChildProcessTree(child, "SIGTERM", { + platform: "win32", + runTaskkill, + }); + + expect(runTaskkill).toHaveBeenNthCalledWith(1, "taskkill", ["/PID", "12345", "/T"], { + stdio: "ignore", + }); + expect(runTaskkill).toHaveBeenNthCalledWith(2, "taskkill", ["/PID", "12345", "/T", "/F"], { + stdio: "ignore", + }); + expect(child.kill).not.toHaveBeenCalled(); + }); + it.runIf(process.platform !== "win32")( "exits promptly after forwarded SIGTERM children exit cleanly", async () => {