test(FN-4892): add reliability interaction coverage for intake auto-archive

Fusion-Task-Id: FN-4892
Fusion-Task-Lineage: 6f56b468-a827-416d-ba1f-444e1326d613
This commit is contained in:
Fusion (runfusion.ai)
2026-05-18 00:08:33 -07:00
committed by gsxdsm
parent e0f8e4c7e7
commit 619070388b
2 changed files with 192 additions and 0 deletions

View File

@@ -0,0 +1,89 @@
import { afterEach, describe, expect, it, vi } from "vitest";
import { mkdtemp, rm } from "node:fs/promises";
import { join } from "node:path";
import { tmpdir } from "node:os";
import { TaskStore } from "@fusion/core";
async function createStore() {
const rootDir = await mkdtemp(join(tmpdir(), "fusion-duplicate-intake-"));
const store = new TaskStore(rootDir, undefined, { inMemoryDb: true });
await store.init();
return {
store,
cleanup: async () => {
store.close();
await rm(rootDir, { recursive: true, force: true });
},
};
}
describe("reliability interactions: same-agent duplicate intake", () => {
const fixtures: Array<Awaited<ReturnType<typeof createStore>>> = [];
afterEach(async () => {
vi.restoreAllMocks();
while (fixtures.length) await fixtures.pop()!.cleanup();
});
it("archives later near-duplicate from same agent", async () => {
const fx = await createStore();
fixtures.push(fx);
const a = await fx.store.createTask({
title: "fix: secrets sync typecheck",
description: "typecheck error in secrets-sync",
source: { sourceType: "agent", sourceAgentId: "agent-x" },
});
const b = await fx.store.createTask({
title: "fix: secrets sync typecheck regression",
description: "typecheck error in secrets-sync",
source: { sourceType: "agent", sourceAgentId: "agent-x" },
});
expect((await fx.store.getTask(a.id)).column).toBe("triage");
expect((await fx.store.getTask(b.id)).column).toBe("archived");
const activity = await fx.store.getActivityLog({ type: "task:auto-archived-duplicate", limit: 10 });
const entry = activity.find((item) => item.taskId === b.id);
expect(entry).toBeTruthy();
expect((entry?.metadata as { siblingTaskIds?: string[] } | null)?.siblingTaskIds).toEqual([a.id]);
});
it("does not archive unrelated tasks", async () => {
const fx = await createStore();
fixtures.push(fx);
const a = await fx.store.createTask({
title: "fix: api timeout",
description: "network timeout issue",
source: { sourceType: "agent", sourceAgentId: "agent-x" },
});
const b = await fx.store.createTask({
title: "feat: add mission detail panel",
description: "new dashboard ui",
source: { sourceType: "agent", sourceAgentId: "agent-x" },
});
expect((await fx.store.getTask(a.id)).column).toBe("triage");
expect((await fx.store.getTask(b.id)).column).toBe("triage");
});
it("fails open when duplicate detection throws", async () => {
const fx = await createStore();
fixtures.push(fx);
await fx.store.createTask({
title: "fix: baseline",
description: "desc",
source: { sourceType: "agent", sourceAgentId: "agent-x" },
});
vi.spyOn(fx.store, "listTasks").mockRejectedValueOnce(new Error("boom"));
const b = await fx.store.createTask({
title: "fix: baseline clone",
description: "desc",
source: { sourceType: "agent", sourceAgentId: "agent-x" },
});
expect((await fx.store.getTask(b.id)).column).toBe("triage");
});
});

View File

@@ -0,0 +1,103 @@
import { afterEach, describe, expect, it, vi } from "vitest";
import { mkdtemp, mkdir, rm, writeFile } from "node:fs/promises";
import { execSync } from "node:child_process";
import { join } from "node:path";
import { tmpdir } from "node:os";
import { DEFAULT_SETTINGS, TaskStore } from "@fusion/core";
import { TriageProcessor } from "../../triage.js";
import * as triagePreflight from "../../triage-preflight.js";
function git(cwd: string, command: string): string {
return execSync(command, { cwd, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
}
async function createFixture() {
const rootDir = await mkdtemp(join(tmpdir(), "fusion-ghost-preflight-"));
git(rootDir, "git init -b main");
git(rootDir, 'git config user.email "test@example.com"');
git(rootDir, 'git config user.name "Test User"');
await mkdir(join(rootDir, "packages/core/src"), { recursive: true });
await writeFile(join(rootDir, "packages/core/src/secrets-sync.ts"), "export const value = 1;\n", "utf-8");
git(rootDir, "git add .");
git(rootDir, 'git commit -m "chore: init fixture"');
const store = new TaskStore(rootDir, undefined, { inMemoryDb: true });
await store.init();
await store.updateSettings({ ...DEFAULT_SETTINGS, requirePlanApproval: false });
const triage = new TriageProcessor(store, rootDir);
return {
rootDir,
store,
triage,
cleanup: async () => {
store.close();
await rm(rootDir, { recursive: true, force: true });
},
};
}
describe("reliability interactions: ghost-bug preflight", () => {
const fixtures: Array<Awaited<ReturnType<typeof createFixture>>> = [];
afterEach(async () => {
vi.restoreAllMocks();
while (fixtures.length) await fixtures.pop()!.cleanup();
});
const basePrompt = `# Task: FN-1 - Fix issue\n\n**Size:** S\n\n## Review Level: 1\n`;
it("auto-archives when cited construct is missing", async () => {
const fx = await createFixture();
fixtures.push(fx);
const task = await fx.store.createTask({ title: "fix: missing construct", description: "typecheck error", prompt: "draft" });
await (fx.triage as any).finalizeApprovedTask(
task,
`${basePrompt}\nCited identifier: \`DefinitelyMissingSymbol_DoNotExist\`\n`,
await fx.store.getSettings(),
{},
);
const updated = await fx.store.getTask(task.id);
expect(updated.column).toBe("archived");
const activity = await fx.store.getActivityLog({ type: "task:auto-archived-ghost-bug", limit: 10 });
expect(activity.some((entry) => entry.taskId === task.id)).toBe(true);
const audit = fx.store.getRunAuditEvents({ taskId: task.id, limit: 20 });
expect(audit.some((entry) => entry.mutationType === "task:auto-archived-ghost-bug")).toBe(true);
});
it("passes to todo when construct exists", async () => {
const fx = await createFixture();
fixtures.push(fx);
const task = await fx.store.createTask({ title: "fix: existing construct", description: "typecheck error", prompt: "draft" });
await (fx.triage as any).finalizeApprovedTask(
task,
`${basePrompt}\n\`value = 1;\`\n`,
await fx.store.getSettings(),
{},
);
const updated = await fx.store.getTask(task.id);
expect(updated.column).toBe("todo");
});
it("fails open when probe throws", async () => {
const fx = await createFixture();
fixtures.push(fx);
const task = await fx.store.createTask({ title: "fix: throwing probe", description: "typecheck error", prompt: "draft" });
vi.spyOn(triagePreflight, "runGhostBugPreflight").mockRejectedValueOnce(new Error("boom"));
await (fx.triage as any).finalizeApprovedTask(
task,
`${basePrompt}\nCited identifier: \`DefinitelyMissingSymbol_DoNotExist\`\n`,
await fx.store.getSettings(),
{},
);
const updated = await fx.store.getTask(task.id);
expect(updated.column).toBe("todo");
const activity = await fx.store.getActivityLog({ type: "task:auto-archived-ghost-bug", limit: 10 });
expect(activity.some((entry) => entry.taskId === task.id)).toBe(false);
});
});