From 85354fcb6aae7320f638685d55b8cf4bb7cb0dd2 Mon Sep 17 00:00:00 2001 From: "paseo-bot[bot]" <266920839+paseo-bot[bot]@users.noreply.github.com> Date: Sat, 31 Oct 2026 18:38:48 +0000 Subject: [PATCH] test(terminal): verify cleanup by process identity (#8389) * test(terminal): capture recurring worker publication failures * test(terminal): diagnose the remaining process cleanup wait * test(terminal): verify cleanup by process identity * test(terminal): retain native liveness proof during cleanup checks --------- Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com> --- .../src/terminal/terminal-worker-reaper.test.ts | 7 ++++++- .../src/terminal/worker-terminal-manager.test.ts | 14 +++++++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/packages/server/src/terminal/terminal-worker-reaper.test.ts b/packages/server/src/terminal/terminal-worker-reaper.test.ts index a872325d31..a20d571b7f 100644 --- a/packages/server/src/terminal/terminal-worker-reaper.test.ts +++ b/packages/server/src/terminal/terminal-worker-reaper.test.ts @@ -5,6 +5,7 @@ import { tmpdir } from "node:os"; import path from "node:path"; import { Worker } from "node:worker_threads"; import { isPlatform } from "../test-utils/platform.js"; +import { classifyTerminalProgramProcess } from "./terminal-worker-ownership.js"; import { currentBootId } from "../server/pid-lock.js"; import { createTerminalWorkerProcessInspector, @@ -260,15 +261,19 @@ describe.skipIf(isPlatform("win32"))("terminal worker reaper", () => { it("leaves a program alive when its pid now belongs to a process with another birth time", async () => { const home = makeHome(); const unrelated = spawnIdle(); + const original = { id: "t1", pid: unrelated.pid!, startedAt: "Thu Jan 1 00:00:00 1970" }; const recordPath = writeRecord(home, { pid: await goneProcessPid(), - terminals: [{ id: "t1", pid: unrelated.pid!, startedAt: "Thu Jan 1 00:00:00 1970" }], + terminals: [original], }); await reapTerminalWorkers({ paseoHome: home, inspector, gracefulMs: 0 }); expect(isAlive(unrelated.pid!)).toBe(true); expect(existsSync(recordPath)).toBe(false); + expect( + classifyTerminalProgramProcess(original, await inspector.inspect(original.pid)), + ).toMatchObject({ status: "unrelated" }); }); it("retires a record from an earlier boot without signaling anything it names", async () => { diff --git a/packages/server/src/terminal/worker-terminal-manager.test.ts b/packages/server/src/terminal/worker-terminal-manager.test.ts index 7d91ec180e..2605e62965 100644 --- a/packages/server/src/terminal/worker-terminal-manager.test.ts +++ b/packages/server/src/terminal/worker-terminal-manager.test.ts @@ -50,6 +50,7 @@ import type { WorkerTerminalState, TerminalWorkerToParentMessage, } from "./terminal-worker-protocol.js"; +import { classifyTerminalProgramProcess } from "./terminal-worker-ownership.js"; import { createTerminalWorkerProcessInspector } from "./terminal-worker-processes.js"; import { readUnconfirmedTerminalCleanup, @@ -881,8 +882,19 @@ it.each([false, true])( expect(manager.getTerminal(remaining.id)).toBeUndefined(); expect(await manager.getTerminals(cwd)).toEqual([]); - await waitForCondition(() => !isProcessAlive(remainingPid), 10000); expect(await readUnconfirmedTerminalCleanup(paseoHome)).toEqual([]); + const original = record!.terminals.find((entry) => entry.id === remaining.id)!; + // Startup can launch a new worker after cleanup and Windows can reuse the old PID. + // Require the original process to be gone, not the numeric PID to remain unused. + await expect + .poll( + async () => + !isProcessAlive(remainingPid) || + classifyTerminalProgramProcess(original, await inspector.inspect(remainingPid)).status === + "unrelated", + { timeout: 10000 }, + ) + .toBe(true); }, );