FN-8075: restore PostgreSQL self-healing test coverage

Restore self-healing coverage for PostgreSQL-backed maintenance.

- Update self-healing mocks and assertions for asynchronous audit APIs and PostgreSQL WAL behavior
- Align git command expectations and transient recovery budget coverage with current implementation
- Remove the repaired self-healing suite from the quarantine ledger and gate exclusion

Files changed:
 packages/engine/src/__tests__/self-healing.test.ts | 70 ++++++++++++----------
 packages/engine/vitest.config.ts                   |  1 -
 scripts/lib/test-quarantine.json                   |  5 --
 3 files changed, 39 insertions(+), 37 deletions(-)

Fusion-Task-Id: FN-8075

Fusion-Task-Lineage: 7dfce9b0-9d10-4d70-8e92-8100f6595fce

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
This commit is contained in:
gsxdsm
2026-07-16 08:11:24 -07:00
parent fc9fb21470
commit 14953f39e2
3 changed files with 39 additions and 37 deletions

View File

@@ -179,8 +179,13 @@ function createMockStore(overrides: Record<string, unknown> = {}): TaskStore & E
peekMergeQueue: vi.fn().mockReturnValue([]),
mergeTask: vi.fn().mockResolvedValue(undefined),
archiveTaskAndCleanup: vi.fn().mockResolvedValue({} as Task),
/*
FNXC:PgMigrationQuarantine 2026-07-16-08:00:
VAL-REMOVAL-005 moved run-audit reads and operational-log retention to the
asynchronous PostgreSQL TaskStore contract. Keep the synchronous audit probe
below because recent-activity recovery still uses it as an optional fallback.
*/
pruneOperationalLogsAsync: vi.fn().mockResolvedValue({ deletedTotal: 0, deletedByTable: {} }),
walCheckpoint: vi.fn().mockReturnValue({ busy: 0, log: 5, checkpointed: 5 }),
listTasks: vi.fn().mockResolvedValue([]),
reconcileActiveTimingForEngineDowntime: vi.fn().mockResolvedValue({ shiftedTaskIds: [], downtimeMs: 0 }),
parseFileScopeFromPrompt: vi.fn().mockResolvedValue([]),
@@ -188,6 +193,7 @@ function createMockStore(overrides: Record<string, unknown> = {}): TaskStore & E
createTask: vi.fn().mockResolvedValue({ id: "FN-RESCUE", lineageId: "lin-rescue" }),
recordRunAuditEvent: vi.fn().mockResolvedValue(undefined),
getRunAuditEvents: vi.fn().mockReturnValue([]),
getRunAuditEventsAsync: vi.fn().mockResolvedValue([]),
getBootstrappedAt: vi.fn().mockReturnValue(null),
getRootDir: vi.fn().mockReturnValue("/tmp/test-project"),
clearStaleExecutionStartBranchReferences: vi.fn().mockReturnValue([]),
@@ -2715,7 +2721,7 @@ describe("SelfHealingManager", () => {
status: "failed",
mergeRetries: 3,
error: "spawn ENOTDIR",
mergeDetails: { transientRecoveryCount: 2 },
mergeDetails: { transientRecoveryCount: 5 },
},
],
});
@@ -2816,7 +2822,7 @@ describe("SelfHealingManager", () => {
mgr.stop();
});
it("parks task as failed once budget is exhausted (transientRecoveryCount >= 2)", async () => {
it("parks task as failed once budget is exhausted (transientRecoveryCount >= 5)", async () => {
const transientStore = setupTransientRecoveryStore({
tasks: [
{
@@ -2826,7 +2832,7 @@ describe("SelfHealingManager", () => {
status: "failed",
mergeRetries: 3,
error: "Merge handoff refused (lease-handoff-failed): target-not-queued",
mergeDetails: { transientRecoveryCount: 2 },
mergeDetails: { transientRecoveryCount: 5 },
},
],
});
@@ -4762,9 +4768,9 @@ describe("SelfHealingManager", () => {
});
mockedExecSync.mockImplementation((command) => {
const cmd = String(command);
if (cmd.includes("rev-parse --verify 'fusion/fn-500'")) return "ok" as any;
if (cmd.includes("rev-parse --verify 'main'")) return "ok" as any;
if (cmd.includes("rev-list --count 'main'..'fusion/fn-500'")) return "0\n" as any;
if (cmd.includes("rev-parse --verify fusion/fn-500")) return "ok" as any;
if (cmd.includes("rev-parse --verify main")) return "ok" as any;
if (cmd.includes("rev-list --count main..fusion/fn-500")) return "0\n" as any;
return "" as any;
});
(store.listTasks as ReturnType<typeof vi.fn>)
@@ -4834,9 +4840,9 @@ describe("SelfHealingManager", () => {
});
mockedExecSync.mockImplementation((command) => {
const cmd = String(command);
if (cmd.includes("rev-parse --verify 'fusion/fn-6461'")) return "ok" as any;
if (cmd.includes("rev-parse --verify 'main'")) return "ok" as any;
if (cmd.includes("rev-list --count 'main'..'fusion/fn-6461'")) return "0\n" as any;
if (cmd.includes("rev-parse --verify fusion/fn-6461")) return "ok" as any;
if (cmd.includes("rev-parse --verify main")) return "ok" as any;
if (cmd.includes("rev-list --count main..fusion/fn-6461")) return "0\n" as any;
return "" as any;
});
(store.listTasks as ReturnType<typeof vi.fn>).mockResolvedValue([
@@ -4892,9 +4898,9 @@ describe("SelfHealingManager", () => {
});
mockedExecSync.mockImplementation((command) => {
const cmd = String(command);
if (cmd.includes("rev-parse --verify 'fusion/fn-6462'")) return "ok" as any;
if (cmd.includes("rev-parse --verify 'main'")) return "ok" as any;
if (cmd.includes("rev-list --count 'main'..'fusion/fn-6462'")) return "0\n" as any;
if (cmd.includes("rev-parse --verify fusion/fn-6462")) return "ok" as any;
if (cmd.includes("rev-parse --verify main")) return "ok" as any;
if (cmd.includes("rev-list --count main..fusion/fn-6462")) return "0\n" as any;
return "" as any;
});
(store.listTasks as ReturnType<typeof vi.fn>).mockResolvedValue([
@@ -4965,9 +4971,9 @@ describe("SelfHealingManager", () => {
} as any);
mockedExecSync.mockImplementation((command) => {
const cmd = String(command);
if (cmd.includes("rev-parse --verify 'fusion/fn-501'")) return "ok" as any;
if (cmd.includes("rev-parse --verify 'main'")) return "ok" as any;
if (cmd.includes("rev-list --count 'main'..'fusion/fn-501'")) return "0\n" as any;
if (cmd.includes("rev-parse --verify fusion/fn-501")) return "ok" as any;
if (cmd.includes("rev-parse --verify main")) return "ok" as any;
if (cmd.includes("rev-list --count main..fusion/fn-501")) return "0\n" as any;
return "" as any;
});
(store.listTasks as ReturnType<typeof vi.fn>).mockResolvedValue([
@@ -5008,9 +5014,9 @@ describe("SelfHealingManager", () => {
});
mockedExecSync.mockImplementation((command) => {
const cmd = String(command);
if (cmd.includes("rev-parse --verify 'fusion/fn-501'")) return "ok" as any;
if (cmd.includes("rev-parse --verify 'main'")) return "ok" as any;
if (cmd.includes("rev-list --count 'main'..'fusion/fn-501'")) return "3\n" as any;
if (cmd.includes("rev-parse --verify fusion/fn-501")) return "ok" as any;
if (cmd.includes("rev-parse --verify main")) return "ok" as any;
if (cmd.includes("rev-list --count main..fusion/fn-501")) return "3\n" as any;
return "" as any;
});
(store.listTasks as ReturnType<typeof vi.fn>).mockResolvedValue([
@@ -5067,9 +5073,9 @@ describe("SelfHealingManager", () => {
});
mockedExecSync.mockImplementation((command) => {
const cmd = String(command);
if (cmd.includes("rev-parse --verify 'fusion/fn-502'")) return "ok" as any;
if (cmd.includes("rev-parse --verify 'main'")) return "ok" as any;
if (cmd.includes("rev-list --count 'main'..'fusion/fn-502'")) {
if (cmd.includes("rev-parse --verify fusion/fn-502")) return "ok" as any;
if (cmd.includes("rev-parse --verify main")) return "ok" as any;
if (cmd.includes("rev-list --count main..fusion/fn-502")) {
throw new Error("git failed");
}
return "" as any;
@@ -5138,10 +5144,10 @@ describe("SelfHealingManager", () => {
it("resolves ahead count via origin fallback", async () => {
mockedExecSync.mockImplementation((command) => {
const cmd = String(command);
if (cmd.includes("rev-parse --verify 'fusion/fn-999'")) return "ok" as any;
if (cmd.includes("rev-parse --verify 'release'")) throw new Error("missing local");
if (cmd.includes("rev-parse --verify 'origin/release'")) return "ok" as any;
if (cmd.includes("rev-list --count 'origin/release'..'fusion/fn-999'")) return "0\n" as any;
if (cmd.includes("rev-parse --verify fusion/fn-999")) return "ok" as any;
if (cmd.includes("rev-parse --verify release")) throw new Error("missing local");
if (cmd.includes("rev-parse --verify origin/release")) return "ok" as any;
if (cmd.includes("rev-list --count origin/release..fusion/fn-999")) return "0\n" as any;
return "" as any;
});
@@ -9707,7 +9713,7 @@ describe("maintenance cycle concurrency", () => {
expect((manager as any).maintenanceRunning).toBe(false);
});
it("uses a passive WAL checkpoint during maintenance", async () => {
it("skips SQLite WAL checkpoints during PostgreSQL maintenance", async () => {
(vi.spyOn(manager as any, "pruneWorktrees").mockResolvedValue(0) as any);
(vi.spyOn(manager as any, "cleanupOrphans").mockResolvedValue(0) as any);
(vi.spyOn(manager as any, "enforceWorktreeCap").mockResolvedValue(0) as any);
@@ -9729,7 +9735,9 @@ describe("maintenance cycle concurrency", () => {
await (manager as any).runMaintenance();
expect(store.walCheckpoint).toHaveBeenCalledWith("PASSIVE");
expect(getSelfHealingLogger().log).toHaveBeenCalledWith(
expect.stringContaining("wal-checkpoint\" skipped — PostgreSQL manages WAL + autovacuum"),
);
});
it("runs batch 1 operations in sequence (with isolation — one failure doesn't block others)", async () => {
@@ -10691,9 +10699,9 @@ describe("FN-5335 triple-proof no-action unit coverage", () => {
} as any);
mockedExecSync.mockImplementation((command) => {
const cmd = String(command);
if (cmd.includes("rev-parse --verify 'fusion/fn-noop'")) return "ok" as any;
if (cmd.includes("rev-parse --verify 'main'")) return "ok" as any;
if (cmd.includes("rev-list --count 'main'..'fusion/fn-noop'")) return "0\n" as any;
if (cmd.includes("rev-parse --verify fusion/fn-noop")) return "ok" as any;
if (cmd.includes("rev-parse --verify main")) return "ok" as any;
if (cmd.includes("rev-list --count main..fusion/fn-noop")) return "0\n" as any;
return "" as any;
});

View File

@@ -305,7 +305,6 @@ export default defineConfig({
"src/__tests__/mission-factory-parity.integration.test.ts",
"src/__tests__/planner-overseer-intervention-wiring.test.ts",
"src/__tests__/project-engine.test.ts",
"src/__tests__/self-healing.test.ts",
"src/__tests__/unlinked-missions-advisory-reporter.test.ts",
"src/__tests__/workflow-graph-task-runner.test.ts",
"src/__tests__/agent-tools-intake-column.test.ts",

View File

@@ -26,11 +26,6 @@
"reason": "VAL-REMOVAL-005 PG migration: fails due to SQLite removal or sync API incompatibility 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/engine/src/__tests__/self-healing.test.ts",
"reason": "VAL-REMOVAL-005 PG migration: fails due to SQLite removal or sync API incompatibility 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/engine/src/__tests__/unlinked-missions-advisory-reporter.test.ts",
"reason": "VAL-REMOVAL-005 PG migration: fails due to SQLite removal or sync API incompatibility in backend mode. Failing run: https://github.com/Runfusion/Fusion/actions/runs/29344576232. Mirrored in packages/engine/vitest.config.ts.",