From 4958512d9e113beda04138ae672e11bbbe9bbe31 Mon Sep 17 00:00:00 2001 From: adil Date: Sun, 12 Apr 2026 10:51:16 +0530 Subject: [PATCH] test(cli): add coverage for port scan fallback and c8 ignore for signal handlers Adds test for stopDashboard finding an orphaned dashboard on a reassigned port via the port-range scan fallback. Marks signal handler body with c8 ignore since it only fires on process termination. Co-Authored-By: Claude Opus 4.6 (1M context) --- packages/cli/__tests__/commands/start.test.ts | 22 +++++++++++++++++++ packages/cli/src/commands/start.ts | 2 ++ 2 files changed, 24 insertions(+) diff --git a/packages/cli/__tests__/commands/start.test.ts b/packages/cli/__tests__/commands/start.test.ts index de8937782..ba68afa88 100644 --- a/packages/cli/__tests__/commands/start.test.ts +++ b/packages/cli/__tests__/commands/start.test.ts @@ -1059,6 +1059,7 @@ describe("stop command", () => { mockConfigRef.current = makeConfig({ "my-app": makeProject() }); mockSessionManager.get.mockResolvedValue({ id: "app-orchestrator", status: "running" }); mockSessionManager.kill.mockResolvedValue(undefined); + mockExec.mockResolvedValue({ stdout: "12345", stderr: "" }); await program.parseAsync(["node", "test", "stop", "--purge-session"]); @@ -1066,6 +1067,27 @@ describe("stop command", () => { purgeOpenCode: true, }); }); + + it("finds orphaned dashboard on a reassigned port via port scan", async () => { + mockConfigRef.current = makeConfig({ "my-app": makeProject() }); + mockSessionManager.get.mockResolvedValue({ id: "app-orchestrator", status: "running" }); + mockSessionManager.kill.mockResolvedValue(undefined); + // Port 3000 has nothing, but port 3001 has the orphaned dashboard + mockExec.mockImplementation(async (cmd: string, args: string[] = []) => { + if (cmd === "kill") return { stdout: "", stderr: "" }; + const portArg = args.find((a) => a.startsWith(":")); + if (portArg === ":3001") return { stdout: "99999", stderr: "" }; + throw new Error("no process"); + }); + + await program.parseAsync(["node", "test", "stop"]); + + const output = vi + .mocked(console.log) + .mock.calls.map((c) => c.join(" ")) + .join("\n"); + expect(output).toContain("was on port 3001"); + }); }); // --------------------------------------------------------------------------- diff --git a/packages/cli/src/commands/start.ts b/packages/cli/src/commands/start.ts index b73d18b76..4614f05ee 100644 --- a/packages/cli/src/commands/start.ts +++ b/packages/cli/src/commands/start.ts @@ -1155,6 +1155,7 @@ async function runStartup( // Ensure the dashboard child is killed when the parent exits (e.g. Ctrl+C). // Node.js does not guarantee signal propagation to child processes. + /* c8 ignore start -- signal handlers only fire on process termination */ const killDashboard = (): void => { try { dashboardProcess?.kill("SIGTERM"); @@ -1162,6 +1163,7 @@ async function runStartup( // already dead } }; + /* c8 ignore stop */ process.on("SIGINT", killDashboard); process.on("SIGTERM", killDashboard); process.on("exit", killDashboard);