diff --git a/packages/cli/src/__tests__/project-context.test.ts b/packages/cli/src/__tests__/project-context.test.ts index 2a3c253268..18e8261b36 100644 --- a/packages/cli/src/__tests__/project-context.test.ts +++ b/packages/cli/src/__tests__/project-context.test.ts @@ -24,16 +24,32 @@ import { } from "../../../core/src/__test-utils__/pg-test-harness.js"; import { beforeAll, afterAll } from "vitest"; -describe("project-context", () => { +/* +FNXC:ProjectContextTests 2026-07-16-09:00: +CentralCore tests require PostgreSQL, but booting its embedded postmaster for every test races other forked CLI files under load. Use the shared external pg test harness and skip only CentralCore coverage when PostgreSQL is unavailable; pure formatting coverage remains ungated below. +*/ +pgDescribe("project-context (PostgreSQL-backed detection)", () => { + let h: PgTestHarness; let tempDir: string; let homeDir: string; let central: CentralCore; + let previousDatabaseUrl: string | undefined; const createdProjectIds: string[] = []; + beforeAll(async () => { + h = await createTaskStoreForTest({ prefix: "fusion_cli_project_ctx_detection" }); + }); + + afterAll(async () => { + await h.teardown(); + }); + beforeEach(async () => { + previousDatabaseUrl = process.env.DATABASE_URL; + process.env.DATABASE_URL = h.testUrl; tempDir = mkdtempSync(join(tmpdir(), "kb-test-")); homeDir = mkdtempSync(join(tmpdir(), "kb-home-")); - central = new CentralCore(homeDir); + central = new CentralCore(homeDir, { asyncLayer: h.layer }); await central.init(); }); @@ -56,6 +72,11 @@ describe("project-context", () => { // Ignore close errors } await clearStoreCache(); + if (previousDatabaseUrl === undefined) { + delete process.env.DATABASE_URL; + } else { + process.env.DATABASE_URL = previousDatabaseUrl; + } // Filesystem cleanup last try { @@ -132,45 +153,6 @@ describe("project-context", () => { }); }); - describe("formatProjectLine", () => { - it("should format default project with asterisk", () => { - const project: RegisteredProject = { - id: "proj_123", - name: "my-app", - path: "/path/to/app", - status: "active", - isolationMode: "in-process", - createdAt: "2024-01-01T00:00:00Z", - updatedAt: "2024-01-01T00:00:00Z", - }; - - const line = formatProjectLine(project, true); - - expect(line).toContain("* "); - expect(line).toContain("my-app"); - expect(line).toContain("/path/to/app"); - expect(line).toContain("[active]"); - }); - - it("should format non-default project without asterisk", () => { - const project: RegisteredProject = { - id: "proj_456", - name: "other-app", - path: "/path/to/other", - status: "paused", - isolationMode: "child-process", - createdAt: "2024-01-01T00:00:00Z", - updatedAt: "2024-01-01T00:00:00Z", - }; - - const line = formatProjectLine(project, false); - - expect(line).not.toContain("*"); - expect(line).toContain("other-app"); - expect(line).toContain("[paused]"); - }); - }); - describe("resolveProject", () => { it("should throw for unknown project name", async () => { await expect(resolveProject("unknown-project", tempDir, homeDir)).rejects.toThrow( @@ -195,6 +177,45 @@ describe("project-context", () => { }); }); +describe("formatProjectLine", () => { + it("should format default project with asterisk", () => { + const project: RegisteredProject = { + id: "proj_123", + name: "my-app", + path: "/path/to/app", + status: "active", + isolationMode: "in-process", + createdAt: "2024-01-01T00:00:00Z", + updatedAt: "2024-01-01T00:00:00Z", + }; + + const line = formatProjectLine(project, true); + + expect(line).toContain("* "); + expect(line).toContain("my-app"); + expect(line).toContain("/path/to/app"); + expect(line).toContain("[active]"); + }); + + it("should format non-default project without asterisk", () => { + const project: RegisteredProject = { + id: "proj_456", + name: "other-app", + path: "/path/to/other", + status: "paused", + isolationMode: "child-process", + createdAt: "2024-01-01T00:00:00Z", + updatedAt: "2024-01-01T00:00:00Z", + }; + + const line = formatProjectLine(project, false); + + expect(line).not.toContain("*"); + expect(line).toContain("other-app"); + expect(line).toContain("[paused]"); + }); +}); + /* FNXC:PostgresCutover 2026-07-05-17:30: PostgreSQL-backed CentralCore coverage for project-context. The legacy SQLite diff --git a/packages/cli/vitest.config.ts b/packages/cli/vitest.config.ts index 69d4710a8c..76405e8dbc 100644 --- a/packages/cli/vitest.config.ts +++ b/packages/cli/vitest.config.ts @@ -93,10 +93,9 @@ const quarantinedCliTests: string[] = [ */ "src/__tests__/extension-dist-barrel.test.ts", /* - FNXC:CliTests 2026-07-14-23:05: - project-context.test.ts lost its embedded PostgreSQL postmaster and timed out only in a combined focused lane, then passed 12/12 in isolation. Quarantine the loaded-lane cluster interference on sight instead of widening hooks, retrying, or weakening lifecycle assertions; mirrored in scripts/lib/test-quarantine.json. + FNXC:CliTests 2026-07-16-09:00: + FN-8077 removed project-context.test.ts from this list and the ledger in lockstep. Its CentralCore coverage now uses the external PostgreSQL test harness under pgDescribe rather than launching an embedded postmaster for each test in forked loaded lanes; pure formatting coverage remains ungated. */ - "src/__tests__/project-context.test.ts", ]; export default defineConfig({ diff --git a/packages/dashboard/src/__tests__/routes-system.test.ts b/packages/dashboard/src/__tests__/routes-system.test.ts index c6db2d3263..cea127d976 100644 --- a/packages/dashboard/src/__tests__/routes-system.test.ts +++ b/packages/dashboard/src/__tests__/routes-system.test.ts @@ -429,7 +429,13 @@ describe("GET /api/system-stats", () => { it("returns process/system metrics with task and agent aggregates", async () => { const cpuUsageSpy = vi.spyOn(process, "cpuUsage"); - const dateNowSpy = vi.spyOn(Date, "now"); + /* + FNXC:DashboardCpuSampling 2026-07-16-09:00: + CPU sampling measures the delta between two route-owned Date.now() values. Fake only Date and explicitly advance it between requests so unrelated Express and I/O clock reads cannot inflate the sampling interval under a loaded lane. + */ + vi.useFakeTimers({ toFake: ["Date"] }); + const sampleStart = new Date("2026-07-16T00:00:00.000Z"); + vi.setSystemTime(sampleStart); cpuUsageSpy .mockReturnValueOnce({ user: 1_000_000, system: 500_000 }) .mockImplementation((previousValue?: NodeJS.CpuUsage) => { @@ -438,12 +444,6 @@ describe("GET /api/system-stats", () => { } return { user: 1_200_000, system: 600_000 }; }); - let now = 1_000; - dateNowSpy.mockImplementation(() => { - now += 1_000; - return now; - }); - const store = createMockStore({ listTasks: vi.fn().mockResolvedValue([ { id: "FN-1", column: "triage" }, @@ -495,6 +495,7 @@ describe("GET /api/system-stats", () => { }), ); + vi.setSystemTime(new Date(sampleStart.getTime() + 1_000)); const secondRes = await GET(app, "/api/system-stats"); expect(secondRes.status).toBe(200); expect(secondRes.body.systemStats.cpuPercent).toBe(30); @@ -521,7 +522,7 @@ describe("GET /api/system-stats", () => { expect(res.body.vitestLastAutoKillAt).toBeNull(); cpuUsageSpy.mockRestore(); - dateNowSpy.mockRestore(); + vi.useRealTimers(); mockExecFile.mockClear(); }); diff --git a/packages/dashboard/vitest.config.ts b/packages/dashboard/vitest.config.ts index e8021e965a..744dd72128 100644 --- a/packages/dashboard/vitest.config.ts +++ b/packages/dashboard/vitest.config.ts @@ -328,10 +328,10 @@ deleted per the AGENTS.md deletion ratchet (14 days expired, not rescued). Ledger entries removed from scripts/lib/test-quarantine.json in the same commit. The array stays empty; add new entries here only with a matching ledger row. -FNXC:DashboardTestQuarantine 2026-07-14-18:48: -PostgreSQL maintainability verification observed routes-system.test.ts return a timing-sensitive CPU sample of 10 where the test expected 30. Quarantine the unrelated flake under the deletion ratchet without changing its timeout, retries, or assertion; mirror the entry in scripts/lib/test-quarantine.json. +FNXC:DashboardTestQuarantine 2026-07-16-09:00: +FN-8077 removed routes-system.test.ts from this list and the ledger in lockstep. Its test now explicitly advances a fake Date-only clock between CPU samples, so unrelated route clock reads cannot stretch elapsed time under the loaded API lane; assertions and timeout policy are unchanged. */ -const quarantinedDashboardTests: string[] = ["src/__tests__/routes-system.test.ts"]; +const quarantinedDashboardTests: string[] = []; const qualityApiTests = [ // Critical HTTP/server behavior: auth, task/project/settings mutation, diff --git a/scripts/lib/test-quarantine.json b/scripts/lib/test-quarantine.json index 310ef10edf..ec3501aac6 100644 --- a/scripts/lib/test-quarantine.json +++ b/scripts/lib/test-quarantine.json @@ -150,16 +150,6 @@ "file": "packages/engine/src/__tests__/reliability-interactions/post-done-continuation-no-wedge.test.ts", "reason": "VAL-REMOVAL-005 PG migration: reliability fixture uses PG-backed store but sync APIs (getRunAuditEvents, getDatabase) fail in backend mode. Failing run: https://github.com/Runfusion/Fusion/actions/runs/29344576232. Mirrored in packages/engine/vitest.config.ts.", "quarantinedAt": "2026-07-14" - }, - { - "file": "packages/dashboard/src/__tests__/routes-system.test.ts", - "reason": "PostgreSQL maintainability verification on 2026-07-14 observed the CPU sampling assertion near line 500 receive 10 when 30 was expected during the local file-scoped `pnpm --filter @fusion/dashboard exec vitest run packages/dashboard/src/__tests__/routes-system.test.ts` run. Archived local-run evidence: https://github.com/Runfusion/Fusion/pull/2109#discussion_r3584473782. The unrelated sampling result is timing/load-sensitive, so quarantine the file on sight instead of changing its timeout, retries, or assertion. Mirrored in packages/dashboard/vitest.config.ts quarantinedDashboardTests.", - "quarantinedAt": "2026-07-14" - }, - { - "file": "packages/cli/src/__tests__/project-context.test.ts", - "reason": "PR #2110 feedback verification on 2026-07-14 observed the embedded PostgreSQL postmaster exit unexpectedly in a combined file-scoped CLI run, followed by ECONNREFUSED, 10s hook timeouts, and the subprocess guard; the unchanged suite passed 12/12 immediately in isolation. Archived local-run evidence: https://github.com/Runfusion/Fusion/pull/2110#issuecomment-4977406152. The loaded-lane embedded-cluster interference is timing/resource-sensitive, so quarantine the file on sight instead of widening timeouts, adding retries, or weakening assertions. Mirrored in packages/cli/vitest.config.ts quarantinedCliTests.", - "quarantinedAt": "2026-07-14" } ] }