FN-8225: migrate goal-tool tests to shared PostgreSQL harness

Migrate goal-tool extension tests to the injected shared PostgreSQL harness.

- Replace temporary embedded PostgreSQL roots with shared harness lifecycle hooks
- Reuse harness API registration and tool helpers across goal retrieval and audit suites
- Preserve retrieval audit assertions while avoiding concurrent database startup locks

Files changed:
 .../__tests__/extension-goal-tools-audit.test.ts   |  72 ++++++-------
 .../cli/src/__tests__/extension-goal-tools.test.ts | 113 ++++++++-------------
 2 files changed, 76 insertions(+), 109 deletions(-)

Fusion-Task-Id: FN-8225

Fusion-Task-Lineage: 424c1bb2-5cb7-4e81-b3fd-8f6a6f29b433

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
This commit is contained in:
gsxdsm
2026-07-17 11:17:18 -07:00
parent a7ab3ccb49
commit cdcc3261f1
2 changed files with 75 additions and 108 deletions

View File

@@ -1,55 +1,49 @@
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { mkdtemp, mkdir, rm } from "node:fs/promises";
import { join } from "node:path";
import { tmpdir } from "node:os";
/**
* FNXC:PostgresCutover 2026-07-17-00:00:
* FN-8225 moves this audit suite from a per-test temporary-root `getStore(cwd)`
* path, which booted embedded PostgreSQL, to the shared injected PostgreSQL
* harness. This prevents FN-8222's concurrent postmaster.pid locks, leaked
* initdb processes, and five-second timeouts while retaining prototype audit spies.
*/
import { afterAll, afterEach, beforeAll, beforeEach, expect, it, vi } from "vitest";
import { TaskStore, collectCitedGoalIdsFromAudit } from "@fusion/core";
import kbExtension, { closeCachedStores } from "../extension.js";
import { GOAL_RETRIEVAL_INVOKED } from "@fusion/engine";
import {
createMockApi,
createPgExtensionHarness,
pgDescribe,
registerExtension,
requireTool,
} from "./pg-extension-harness.js";
interface RegisteredTool {
name: string;
execute: (toolCallId: string, params: any, signal: AbortSignal | undefined, onUpdate: ((update: any) => void) | undefined, ctx: any) => Promise<any>;
}
function createMockAPI() {
const tools = new Map<string, RegisteredTool>();
return {
registerTool(def: RegisteredTool) { tools.set(def.name, def); },
registerCommand() {},
registerShortcut() {},
registerFlag() {},
on() {},
tools,
} as any;
}
describe("extension goal tools retrieval audit", () => {
let tmpDir: string;
beforeEach(async () => {
tmpDir = await mkdtemp(join(tmpdir(), "kb-goal-audit-"));
await mkdir(join(tmpDir, ".fusion"), { recursive: true });
});
pgDescribe("extension goal tools retrieval audit", () => {
const h = createPgExtensionHarness("fn-goal-tools-audit");
beforeAll(h.beforeAll);
beforeEach(h.beforeEach);
afterEach(async () => {
await closeCachedStores();
await rm(tmpDir, { recursive: true, force: true });
vi.restoreAllMocks();
try {
await h.afterEach();
} finally {
vi.restoreAllMocks();
}
});
afterAll(h.afterAll);
it("emits retrieval audit for fn_goal_list and fn_goal_show branches", async () => {
const recordSpy = vi.spyOn(TaskStore.prototype, "recordRunAuditEvent");
const api = createMockAPI();
kbExtension(api);
const api = createMockApi();
registerExtension(api);
const createTool = api.tools.get("fn_goal_create");
const listTool = api.tools.get("fn_goal_list");
const showTool = api.tools.get("fn_goal_show");
const ctx = { cwd: tmpDir, runId: "run-1", agentId: "agent-1", taskId: "FN-1" };
const createTool = requireTool(api, "fn_goal_create");
const listTool = requireTool(api, "fn_goal_list");
const showTool = requireTool(api, "fn_goal_show");
const ctx = { cwd: h.rootDir(), runId: "run-1", agentId: "agent-1", taskId: "FN-1" };
await createTool.execute("c1", { title: "Goal one" }, undefined, undefined, ctx);
const listResult = await listTool.execute("l1", { status: "active" }, undefined, undefined, ctx);
const goalId = listResult.details.goals[0].id as string;
const goalId = (listResult.details!.goals as Array<{ id: string }>)[0].id;
await showTool.execute("s1", { id: goalId }, undefined, undefined, ctx);
await showTool.execute("s2", { id: "G-404" }, undefined, undefined, ctx);

View File

@@ -1,42 +1,18 @@
import { afterEach, beforeEach, describe, expect, it } from "vitest";
import { mkdtemp, mkdir, rm } from "node:fs/promises";
import { join } from "node:path";
import { tmpdir } from "node:os";
import kbExtension, { closeCachedStores } from "../extension.js";
/**
* FNXC:PostgresCutover 2026-07-17-00:00:
* FN-8225 moves these goal-tool tests from a per-test temporary-root
* `getStore(cwd)` path, which booted embedded PostgreSQL, to the shared
* injected PostgreSQL harness. This prevents FN-8222's concurrent
* postmaster.pid locks, leaked initdb processes, and five-second timeouts.
*/
interface RegisteredTool {
name: string;
description: string;
parameters?: {
type: string;
properties?: Record<string, unknown>;
required?: string[];
};
execute: (
toolCallId: string,
params: any,
signal: AbortSignal | undefined,
onUpdate: ((update: any) => void) | undefined,
ctx: any,
) => Promise<any>;
}
function createMockAPI() {
const tools = new Map<string, RegisteredTool>();
const api = {
registerTool(def: RegisteredTool) {
tools.set(def.name, def);
},
registerCommand() {},
registerShortcut() {},
registerFlag() {},
on() {},
tools,
};
return api as any;
}
import { afterAll, afterEach, beforeAll, beforeEach, expect, it } from "vitest";
import {
createMockApi,
createPgExtensionHarness,
pgDescribe,
registerExtension,
} from "./pg-extension-harness.js";
function makeCtx(cwd: string) {
return { cwd } as any;
@@ -47,21 +23,18 @@ function textOf(result: { content: Array<{ type: string; text?: string }> }): st
return first && first.type === "text" ? (first.text ?? "") : "";
}
describe("extension goal retrieval tools", () => {
let tmpDir: string;
let api: ReturnType<typeof createMockAPI>;
pgDescribe("extension goal retrieval tools", () => {
const h = createPgExtensionHarness("fn-goal-tools");
let api: ReturnType<typeof createMockApi>;
beforeAll(h.beforeAll);
beforeEach(async () => {
tmpDir = await mkdtemp(join(tmpdir(), "kb-goal-tools-"));
await mkdir(join(tmpDir, ".fusion"), { recursive: true });
api = createMockAPI();
kbExtension(api);
});
afterEach(async () => {
await closeCachedStores();
await rm(tmpDir, { recursive: true, force: true });
await h.beforeEach();
api = createMockApi();
registerExtension(api);
});
afterEach(h.afterEach);
afterAll(h.afterAll);
it("registers fn_goal_show with expected schema", () => {
const tool = api.tools.get("fn_goal_show");
@@ -92,11 +65,11 @@ describe("extension goal retrieval tools", () => {
{ title: "Improve reliability", description: "Reduce flaky test retries" },
undefined,
undefined,
makeCtx(tmpDir),
makeCtx(h.rootDir()),
);
const goalId = created.details.goalId as string;
const result = await tool!.execute("goal-show-1", { id: goalId }, undefined, undefined, makeCtx(tmpDir));
const result = await tool!.execute("goal-show-1", { id: goalId }, undefined, undefined, makeCtx(h.rootDir()));
expect(result.isError).toBeUndefined();
expect(result.details.goal).toMatchObject({
@@ -118,7 +91,7 @@ describe("extension goal retrieval tools", () => {
const tool = api.tools.get("fn_goal_show");
expect(tool).toBeDefined();
const result = await tool!.execute("goal-show-404", { id: "G-404" }, undefined, undefined, makeCtx(tmpDir));
const result = await tool!.execute("goal-show-404", { id: "G-404" }, undefined, undefined, makeCtx(h.rootDir()));
expect(result.isError).toBe(true);
expect(textOf(result)).toBe("Goal G-404 not found");
@@ -131,7 +104,7 @@ describe("extension goal retrieval tools", () => {
expect(createTool).toBeDefined();
expect(listTool).toBeDefined();
const emptyResult = await listTool!.execute("goal-list-empty", {}, undefined, undefined, makeCtx(tmpDir));
const emptyResult = await listTool!.execute("goal-list-empty", {}, undefined, undefined, makeCtx(h.rootDir()));
expect(textOf(emptyResult)).toBe(["Goals (0) [filter: active]", "Active: 0/5", "", "No goals found."].join("\n"));
expect(emptyResult.details).toEqual({ goals: [], activeCount: 0, softWarning: false, hardLimit: 5 });
@@ -140,10 +113,10 @@ describe("extension goal retrieval tools", () => {
{ title: "Ship slice 2" },
undefined,
undefined,
makeCtx(tmpDir),
makeCtx(h.rootDir()),
);
const goalId = created.details.goalId as string;
const result = await listTool!.execute("goal-list-single", { status: "active" }, undefined, undefined, makeCtx(tmpDir));
const result = await listTool!.execute("goal-list-single", { status: "active" }, undefined, undefined, makeCtx(h.rootDir()));
expect(textOf(result)).toBe([
"Goals (1) [filter: active]",
@@ -170,11 +143,11 @@ describe("extension goal retrieval tools", () => {
},
undefined,
undefined,
makeCtx(tmpDir),
makeCtx(h.rootDir()),
);
const goalId = created.details.goalId as string;
const listResult = await listTool!.execute("goal-list-long", {}, undefined, undefined, makeCtx(tmpDir));
const listResult = await listTool!.execute("goal-list-long", {}, undefined, undefined, makeCtx(h.rootDir()));
const listText = textOf(listResult);
expect(listText).toContain(`- ${goalId} [active] Cite goals by ID — First line with extra spaces`);
@@ -189,7 +162,7 @@ describe("extension goal retrieval tools", () => {
},
]);
const showResult = await showTool!.execute("goal-show-long", { id: goalId }, undefined, undefined, makeCtx(tmpDir));
const showResult = await showTool!.execute("goal-show-long", { id: goalId }, undefined, undefined, makeCtx(h.rootDir()));
expect(textOf(showResult)).toContain("Description: First line with extra spaces");
expect(textOf(showResult)).toContain("Second line must never appear in fn_goal_list.");
expect(showResult.details.goal.description).toContain("Second line must never appear in fn_goal_list.");
@@ -203,14 +176,14 @@ describe("extension goal retrieval tools", () => {
expect(archiveTool).toBeDefined();
expect(listTool).toBeDefined();
const archivedGoal = await createTool!.execute("goal-create-4", { title: "Archive me", description: "one line" }, undefined, undefined, makeCtx(tmpDir));
await archiveTool!.execute("goal-archive-1", { id: archivedGoal.details.goalId }, undefined, undefined, makeCtx(tmpDir));
await createTool!.execute("goal-create-5", { title: "One" }, undefined, undefined, makeCtx(tmpDir));
await createTool!.execute("goal-create-6", { title: "Two" }, undefined, undefined, makeCtx(tmpDir));
await createTool!.execute("goal-create-7", { title: "Three" }, undefined, undefined, makeCtx(tmpDir));
const archivedGoal = await createTool!.execute("goal-create-4", { title: "Archive me", description: "one line" }, undefined, undefined, makeCtx(h.rootDir()));
await archiveTool!.execute("goal-archive-1", { id: archivedGoal.details.goalId }, undefined, undefined, makeCtx(h.rootDir()));
await createTool!.execute("goal-create-5", { title: "One" }, undefined, undefined, makeCtx(h.rootDir()));
await createTool!.execute("goal-create-6", { title: "Two" }, undefined, undefined, makeCtx(h.rootDir()));
await createTool!.execute("goal-create-7", { title: "Three" }, undefined, undefined, makeCtx(h.rootDir()));
const archivedResult = await listTool!.execute("goal-list-archived", { status: "archived" }, undefined, undefined, makeCtx(tmpDir));
const allResult = await listTool!.execute("goal-list-all", { status: "all" }, undefined, undefined, makeCtx(tmpDir));
const archivedResult = await listTool!.execute("goal-list-archived", { status: "archived" }, undefined, undefined, makeCtx(h.rootDir()));
const allResult = await listTool!.execute("goal-list-all", { status: "all" }, undefined, undefined, makeCtx(h.rootDir()));
expect(textOf(archivedResult)).toBe([
"Goals (1) [filter: archived]",
@@ -232,14 +205,14 @@ describe("extension goal retrieval tools", () => {
expect(listTool).toBeDefined();
expect(showTool).toBeDefined();
await createTool!.execute("goal-create-8", { title: "Ship slice 2" }, undefined, undefined, makeCtx(tmpDir));
await createTool!.execute("goal-create-8", { title: "Ship slice 2" }, undefined, undefined, makeCtx(h.rootDir()));
const listResult = await listTool!.execute("goal-list-1", { status: "active" }, undefined, undefined, makeCtx(tmpDir));
const listResult = await listTool!.execute("goal-list-1", { status: "active" }, undefined, undefined, makeCtx(h.rootDir()));
expect(Array.isArray(listResult.details.goals)).toBe(true);
expect(listResult.details.goals.length).toBeGreaterThan(0);
const listedGoal = listResult.details.goals[0] as { id: string };
const showResult = await showTool!.execute("goal-show-2", { id: listedGoal.id }, undefined, undefined, makeCtx(tmpDir));
const showResult = await showTool!.execute("goal-show-2", { id: listedGoal.id }, undefined, undefined, makeCtx(h.rootDir()));
expect(showResult.details.goal.id).toBe(listedGoal.id);
expect(showResult.details.goal).toMatchObject({