@@ -370,6 +370,37 @@ describe("windows command wrapper behavior", () => {
|
370 | 370 | } |
371 | 371 | }); |
372 | 372 | |
| 373 | +it("kills the Windows process tree when the overall timeout elapses", async () => { |
| 374 | +vi.useFakeTimers(); |
| 375 | +const platformSpy = vi.spyOn(process, "platform", "get").mockReturnValue("win32"); |
| 376 | +const child = createMockChild({ autoClose: false }); |
| 377 | +const taskkillChild = createMockChild(); |
| 378 | + |
| 379 | +spawnMock.mockImplementationOnce(() => child).mockImplementationOnce(() => taskkillChild); |
| 380 | + |
| 381 | +try { |
| 382 | +const resultPromise = runCommandWithTimeout(["node", "idle.js"], { timeoutMs: 80 }); |
| 383 | + |
| 384 | +await vi.advanceTimersByTimeAsync(81); |
| 385 | +expect(child.kill).not.toHaveBeenCalled(); |
| 386 | +expect(spawnMock).toHaveBeenCalledTimes(2); |
| 387 | +expect(spawnMock.mock.calls[1]?.[0]).toBe("taskkill"); |
| 388 | +expect(spawnMock.mock.calls[1]?.[1]).toEqual(["/PID", "1234", "/T", "/F"]); |
| 389 | +expect(spawnMock.mock.calls[1]?.[2]).toMatchObject({ |
| 390 | +stdio: "ignore", |
| 391 | +windowsHide: true, |
| 392 | +}); |
| 393 | + |
| 394 | +child.emit("close", null, "SIGKILL"); |
| 395 | +const result = await resultPromise; |
| 396 | +expect(result.termination).toBe("timeout"); |
| 397 | +expect(result.code).not.toBe(0); |
| 398 | +} finally { |
| 399 | +platformSpy.mockRestore(); |
| 400 | +vi.useRealTimers(); |
| 401 | +} |
| 402 | +}); |
| 403 | + |
373 | 404 | it("decodes GBK stdout and stderr from runExec on Windows", async () => { |
374 | 405 | const platformSpy = vi.spyOn(process, "platform", "get").mockReturnValue("win32"); |
375 | 406 | const stdout = Buffer.from([0xb2, 0xe2, 0xca, 0xd4]); |
|