test(FN-5152): complete Step 6 — add near-duplicate reliability interaction tests
Fusion-Task-Id: FN-5152 Fusion-Task-Lineage: 954570d8-82e1-4519-a739-c52ee790d2db
This commit is contained in:
committed by
gsxdsm
parent
d9a66571d0
commit
8364be83d6
@@ -0,0 +1,151 @@
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import { mkdtemp, rm } 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 * as core from "@fusion/core";
|
||||
import { TriageProcessor } from "../../triage.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-near-dup-"));
|
||||
git(rootDir, "git init -b main");
|
||||
git(rootDir, 'git config user.email "test@example.com"');
|
||||
git(rootDir, 'git config user.name "Test User"');
|
||||
git(rootDir, "git commit --allow-empty -m init");
|
||||
|
||||
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 });
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const basePrompt = `# Task: FN-1 - test\n\n**Size:** S\n\n## Review Level: 1\n\n## File Scope\n- packages/dashboard/src/routes/register-git-github.ts\n`;
|
||||
|
||||
describe("reliability interactions: near-duplicate intake", () => {
|
||||
const fixtures: Array<Awaited<ReturnType<typeof createFixture>>> = [];
|
||||
afterEach(async () => {
|
||||
vi.useRealTimers();
|
||||
vi.restoreAllMocks();
|
||||
while (fixtures.length) await fixtures.pop()!.cleanup();
|
||||
});
|
||||
|
||||
it("archives newer task as near-duplicate and records activity", async () => {
|
||||
const fx = await createFixture();
|
||||
fixtures.push(fx);
|
||||
|
||||
await fx.store.createTask({
|
||||
title: "Create PR routes missing handlers",
|
||||
description: "Missing /api/tasks/:id/pr/options and /api/tasks/:id/pr/preflight and /api/tasks/:id/pr/generate-metadata",
|
||||
column: "todo",
|
||||
});
|
||||
const incoming = await fx.store.createTask({
|
||||
title: "Missing handlers for create PR routes",
|
||||
description: "GET /api/tasks/:id/pr/options and GET /api/tasks/:id/pr/preflight and POST /api/tasks/:id/pr/generate-metadata all fail",
|
||||
});
|
||||
|
||||
await (fx.triage as any).finalizeApprovedTask(incoming, basePrompt, await fx.store.getSettings(), {});
|
||||
|
||||
const updated = await fx.store.getTask(incoming.id);
|
||||
expect(updated.column).toBe("archived");
|
||||
expect(updated.sourceMetadata?.nearDuplicateOf).toBeTruthy();
|
||||
const activity = await fx.store.getActivityLog({ type: "task:auto-archived-near-duplicate", limit: 20 });
|
||||
expect(activity.some((entry) => entry.taskId === incoming.id)).toBe(true);
|
||||
});
|
||||
|
||||
it("does not archive generic file overlap only", async () => {
|
||||
const fx = await createFixture();
|
||||
fixtures.push(fx);
|
||||
|
||||
await fx.store.createTask({
|
||||
title: "Fix PR comments pagination",
|
||||
description: "Touch register-git-github.ts pagination only",
|
||||
column: "todo",
|
||||
});
|
||||
const incoming = await fx.store.createTask({
|
||||
title: "Add PR merge auto-rebase option",
|
||||
description: "Touch register-git-github.ts merge behavior only",
|
||||
});
|
||||
|
||||
await (fx.triage as any).finalizeApprovedTask(incoming, basePrompt, await fx.store.getSettings(), {});
|
||||
const updated = await fx.store.getTask(incoming.id);
|
||||
expect(updated.column).toBe("todo");
|
||||
});
|
||||
|
||||
it("does not archive when candidate is older than window", async () => {
|
||||
vi.useFakeTimers();
|
||||
const fx = await createFixture();
|
||||
fixtures.push(fx);
|
||||
|
||||
vi.setSystemTime(new Date("2026-05-01T00:00:00.000Z"));
|
||||
await fx.store.createTask({
|
||||
title: "Create PR routes missing handlers",
|
||||
description: "Missing /api/tasks/:id/pr/options and /api/tasks/:id/pr/preflight and /api/tasks/:id/pr/generate-metadata",
|
||||
column: "todo",
|
||||
});
|
||||
|
||||
vi.setSystemTime(new Date("2026-05-10T00:00:00.000Z"));
|
||||
const incoming = await fx.store.createTask({
|
||||
title: "Missing handlers for create PR routes",
|
||||
description: "GET /api/tasks/:id/pr/options and GET /api/tasks/:id/pr/preflight and POST /api/tasks/:id/pr/generate-metadata all fail",
|
||||
});
|
||||
await (fx.triage as any).finalizeApprovedTask(incoming, basePrompt, await fx.store.getSettings(), {});
|
||||
const updated = await fx.store.getTask(incoming.id);
|
||||
expect(updated.column).toBe("todo");
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
it("fails open when extractor throws", async () => {
|
||||
const fx = await createFixture();
|
||||
fixtures.push(fx);
|
||||
|
||||
const incoming = await fx.store.createTask({
|
||||
title: "Missing handlers for create PR routes",
|
||||
description: "GET /api/tasks/:id/pr/options and GET /api/tasks/:id/pr/preflight and POST /api/tasks/:id/pr/generate-metadata all fail",
|
||||
});
|
||||
vi.spyOn(core, "extractIntentSignature").mockImplementation(() => {
|
||||
throw new Error("boom");
|
||||
});
|
||||
|
||||
await (fx.triage as any).finalizeApprovedTask(incoming, basePrompt, await fx.store.getSettings(), {});
|
||||
const updated = await fx.store.getTask(incoming.id);
|
||||
expect(updated.column).toBe("todo");
|
||||
});
|
||||
|
||||
it("does not archive older task when newer candidate exists", async () => {
|
||||
const fx = await createFixture();
|
||||
fixtures.push(fx);
|
||||
|
||||
const older = await fx.store.createTask({
|
||||
title: "Create PR routes missing handlers",
|
||||
description: "Missing /api/tasks/:id/pr/options and /api/tasks/:id/pr/preflight and /api/tasks/:id/pr/generate-metadata",
|
||||
});
|
||||
const newer = await fx.store.createTask({
|
||||
title: "Missing handlers for create PR routes",
|
||||
description: "GET /api/tasks/:id/pr/options and GET /api/tasks/:id/pr/preflight and POST /api/tasks/:id/pr/generate-metadata all fail",
|
||||
column: "todo",
|
||||
});
|
||||
|
||||
await (fx.triage as any).finalizeApprovedTask(older, basePrompt, await fx.store.getSettings(), {});
|
||||
|
||||
const updatedOlder = await fx.store.getTask(older.id);
|
||||
expect(updatedOlder.column).not.toBe("archived");
|
||||
expect(updatedOlder.column).toBe("todo");
|
||||
const updatedNewer = await fx.store.getTask(newer.id);
|
||||
expect(updatedNewer.column).toBe("todo");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user