From 21df96cfbbac6578037a16843de3d8836ba9ac03 Mon Sep 17 00:00:00 2001 From: gsxdsm Date: Sun, 21 Jun 2026 10:53:08 -0700 Subject: [PATCH] fix(FN-5048): address PR feedback on test verification Fusion-Task-Id: FN-5048 --- .github/workflows/full-suite.yml | 20 +++++++++ .../run-verification-command.test.ts | 41 +++++++++++-------- scripts/run-script-tests.mjs | 13 +++++- 3 files changed, 56 insertions(+), 18 deletions(-) diff --git a/.github/workflows/full-suite.yml b/.github/workflows/full-suite.yml index ccfc63704d..7a3b00f91d 100644 --- a/.github/workflows/full-suite.yml +++ b/.github/workflows/full-suite.yml @@ -166,6 +166,26 @@ jobs: - name: Assert every dashboard test file is gated or skip-listed run: node scripts/check-test-inventory.mjs --dashboard-curated + line-count-audit: + name: Line-count audit + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node.js and pnpm + uses: ./.github/actions/setup-node-pnpm + with: + skip-install: "true" + + # FNXC:TestInfrastructure 2026-06-21-10:26: + # Keep line-count drift visible in automated post-merge signal without restoring it to the blocking PR gate. + # The guard is intentionally absent from pretest/pr-checks so task verification and merge progress are not blocked by file-size cleanup work. + - name: Run line-count audit + continue-on-error: true + run: pnpm check:line-count + # The engine-slow tier (src/**/*.slow.test.ts) runs here with a non-empty # execution assertion, so a glob/config drift that silently empties the tier # fails this workflow instead of passing vacuously. Engine slow tests do real diff --git a/packages/engine/src/__tests__/run-verification-command.test.ts b/packages/engine/src/__tests__/run-verification-command.test.ts index e08c43be9d..e178111bb0 100644 --- a/packages/engine/src/__tests__/run-verification-command.test.ts +++ b/packages/engine/src/__tests__/run-verification-command.test.ts @@ -9,6 +9,7 @@ import { detectMarathonVerification, normalizeVerificationCommand, runVerificationCommand, + __testOnlyReapVerificationProcessGroup, type RunVerificationOptions, } from "../run-verification-tool.js"; @@ -19,19 +20,6 @@ import { const onPosix = process.platform !== "win32"; const itPosix = onPosix ? it : it.skip; -function isProcessAlive(pid: number): boolean { - try { - process.kill(pid, 0); - return true; - } catch { - return false; - } -} - -function sleep(ms: number): Promise { - return new Promise((resolve) => setTimeout(resolve, ms)); -} - /** * Tests for runVerificationCommand - the core verification execution logic. * These tests validate basic command execution, output capture, and error handling. @@ -404,11 +392,32 @@ describe("runVerificationCommand", { timeout: 30000 }, () => { expect(result.success).toBe(true); const leakedPid = Number.parseInt(result.stdout.trim(), 10); expect(Number.isFinite(leakedPid)).toBe(true); + expect(result.timedOut).toBe(false); + }); - for (let i = 0; i < 15 && isProcessAlive(leakedPid); i++) { - await sleep(100); + it("escalates non-timeout process-group reaping with fake timers", () => { + /* + * FNXC:Verification 2026-06-21-10:26: + * Keep timer assertions on a narrow seam with fake timers so the integration test above never polls wall-clock time while still pinning SIGTERM -> SIGKILL escalation. + */ + vi.useFakeTimers(); + const kill = vi.fn(); + const supervised = { kill } as unknown as Parameters[0]; + + try { + __testOnlyReapVerificationProcessGroup(supervised); + expect(kill).toHaveBeenCalledTimes(1); + expect(kill).toHaveBeenCalledWith("SIGTERM"); + + vi.advanceTimersByTime(499); + expect(kill).toHaveBeenCalledTimes(1); + + vi.advanceTimersByTime(1); + expect(kill).toHaveBeenCalledTimes(2); + expect(kill).toHaveBeenLastCalledWith("SIGKILL"); + } finally { + vi.useRealTimers(); } - expect(isProcessAlive(leakedPid)).toBe(false); }); }); diff --git a/scripts/run-script-tests.mjs b/scripts/run-script-tests.mjs index b057234296..1a9f3d9607 100644 --- a/scripts/run-script-tests.mjs +++ b/scripts/run-script-tests.mjs @@ -2,6 +2,8 @@ import { globSync } from "node:fs"; import { spawn } from "node:child_process"; +import { resolve } from "node:path"; +import { fileURLToPath, URL } from "node:url"; /* FNXC:TestInfrastructure 2026-06-21-10:00: @@ -10,12 +12,19 @@ The old package script always expanded scripts/__tests__/*.test.mjs before forwa */ const forwarded = process.argv.slice(2).filter((arg) => arg !== "--"); +const repoRoot = fileURLToPath(new URL("..", import.meta.url)); const testFiles = forwarded.length > 0 - ? forwarded - : globSync("scripts/__tests__/*.test.mjs").sort(); + ? forwarded.map((file) => resolve(repoRoot, file)) + : globSync("scripts/__tests__/*.test.mjs", { cwd: repoRoot }).sort().map((file) => resolve(repoRoot, file)); + +if (testFiles.length === 0) { + console.error("[run-script-tests] no script test files matched"); + process.exit(1); +} const child = spawn(process.execPath, ["--test", ...testFiles], { stdio: "inherit", + cwd: repoRoot, }); child.on("exit", (code, signal) => {