feat(FN-1692): merge fusion/fn-1692

This commit is contained in:
gsxdsm
2026-04-13 16:05:05 -07:00
parent 6aec83fff3
commit eb669a21b4
4 changed files with 107 additions and 6 deletions

View File

@@ -7,6 +7,7 @@ import { AgentStore } from "../agent-store.js";
describe("AgentStore — instructions bundle", () => {
let testDir: string;
let store: AgentStore;
const createdAgentIds: string[] = [];
beforeEach(async () => {
testDir = await mkdtemp(join(tmpdir(), "agent-instructions-bundle-test-"));
@@ -15,7 +16,23 @@ describe("AgentStore — instructions bundle", () => {
});
afterEach(async () => {
await rm(testDir, { recursive: true, force: true });
// Teardown order: entity cleanup first, then filesystem
// Delete all created agents explicitly
for (const agentId of createdAgentIds) {
try {
await store.deleteAgent(agentId);
} catch {
// Ignore cleanup errors for already-removed entities
}
}
createdAgentIds.length = 0;
// Filesystem cleanup last
try {
await rm(testDir, { recursive: true, force: true });
} catch {
// Ignore cleanup errors
}
});
it("persists bundleConfig through create + load roundtrip", async () => {
@@ -28,6 +45,7 @@ describe("AgentStore — instructions bundle", () => {
files: ["AGENTS.md", "STYLE.md"],
},
});
createdAgentIds.push(created.id);
expect(created.bundleConfig).toEqual({
mode: "managed",
@@ -41,11 +59,13 @@ describe("AgentStore — instructions bundle", () => {
it("getInstructionsDir returns the managed bundle directory path", async () => {
const agent = await store.createAgent({ name: "dir-agent", role: "executor" });
createdAgentIds.push(agent.id);
expect(store.getInstructionsDir(agent.id)).toBe(join(testDir, "agents", `${agent.id}-instructions`));
});
it("listBundleFiles returns empty for missing directory and sorted .md files only", async () => {
const agent = await store.createAgent({ name: "list-agent", role: "executor" });
createdAgentIds.push(agent.id);
expect(await store.listBundleFiles(agent.id)).toEqual([]);
@@ -61,6 +81,7 @@ describe("AgentStore — instructions bundle", () => {
it("readBundleFile reads content and rejects missing/traversal paths", async () => {
const agent = await store.createAgent({ name: "read-agent", role: "executor" });
createdAgentIds.push(agent.id);
await store.writeBundleFile(agent.id, "AGENTS.md", "Hello bundle");
await expect(store.readBundleFile(agent.id, "AGENTS.md")).resolves.toBe("Hello bundle");
@@ -71,6 +92,7 @@ describe("AgentStore — instructions bundle", () => {
it("writeBundleFile creates directories, overwrites, validates paths, and enforces max file count", async () => {
const agent = await store.createAgent({ name: "write-agent", role: "executor" });
createdAgentIds.push(agent.id);
const dir = store.getInstructionsDir(agent.id);
await store.writeBundleFile(agent.id, "AGENTS.md", "first");
@@ -93,6 +115,7 @@ describe("AgentStore — instructions bundle", () => {
it("deleteBundleFile removes files and throws when missing", async () => {
const agent = await store.createAgent({ name: "delete-agent", role: "executor" });
createdAgentIds.push(agent.id);
const filePath = join(store.getInstructionsDir(agent.id), "AGENTS.md");
await store.writeBundleFile(agent.id, "AGENTS.md", "to-delete");
@@ -104,6 +127,7 @@ describe("AgentStore — instructions bundle", () => {
it("setBundleConfig validates input and creates managed directory", async () => {
const agent = await store.createAgent({ name: "config-agent", role: "executor" });
createdAgentIds.push(agent.id);
const managed = await store.setBundleConfig(agent.id, {
mode: "managed",
@@ -143,6 +167,7 @@ describe("AgentStore — instructions bundle", () => {
role: "executor",
instructionsText: "Legacy text content",
});
createdAgentIds.push(agent.id);
const migrated = await store.migrateLegacyInstructions(agent.id);
@@ -166,6 +191,7 @@ describe("AgentStore — instructions bundle", () => {
role: "executor",
instructionsPath: sourcePath,
});
createdAgentIds.push(agent.id);
const migrated = await store.migrateLegacyInstructions(agent.id);
@@ -189,6 +215,7 @@ describe("AgentStore — instructions bundle", () => {
instructionsText: "Primary inline content",
instructionsPath: sourcePath,
});
createdAgentIds.push(agent.id);
const migrated = await store.migrateLegacyInstructions(agent.id);
@@ -215,6 +242,7 @@ describe("AgentStore — instructions bundle", () => {
},
instructionsText: "should-stay",
});
createdAgentIds.push(agent.id);
const migrated = await store.migrateLegacyInstructions(agent.id);
@@ -231,6 +259,7 @@ describe("AgentStore — instructions bundle", () => {
name: "no-legacy",
role: "executor",
});
createdAgentIds.push(agent.id);
const migrated = await store.migrateLegacyInstructions(agent.id);

View File

@@ -7,6 +7,7 @@ import { AgentStore } from "../agent-store.js";
describe("AgentStore — instructions fields", () => {
let testDir: string;
let store: AgentStore;
const createdAgentIds: string[] = [];
beforeEach(async () => {
testDir = await mkdtemp(join(tmpdir(), "agent-instructions-test-"));
@@ -15,7 +16,23 @@ describe("AgentStore — instructions fields", () => {
});
afterEach(async () => {
await rm(testDir, { recursive: true, force: true });
// Teardown order: entity cleanup first, then filesystem
// Delete all created agents explicitly
for (const agentId of createdAgentIds) {
try {
await store.deleteAgent(agentId);
} catch {
// Ignore cleanup errors for already-removed entities
}
}
createdAgentIds.length = 0;
// Filesystem cleanup last
try {
await rm(testDir, { recursive: true, force: true });
} catch {
// Ignore cleanup errors
}
});
it("creates an agent with instructionsText", async () => {
@@ -24,6 +41,7 @@ describe("AgentStore — instructions fields", () => {
role: "executor",
instructionsText: "Always use TypeScript strict mode.",
});
createdAgentIds.push(agent.id);
expect(agent.instructionsText).toBe("Always use TypeScript strict mode.");
expect(agent.instructionsPath).toBeUndefined();
@@ -35,6 +53,7 @@ describe("AgentStore — instructions fields", () => {
role: "executor",
instructionsPath: ".fusion/agents/custom.md",
});
createdAgentIds.push(agent.id);
expect(agent.instructionsPath).toBe(".fusion/agents/custom.md");
expect(agent.instructionsText).toBeUndefined();
@@ -47,6 +66,7 @@ describe("AgentStore — instructions fields", () => {
instructionsText: "Check for security issues.",
instructionsPath: ".fusion/agents/reviewer.md",
});
createdAgentIds.push(agent.id);
expect(agent.instructionsText).toBe("Check for security issues.");
expect(agent.instructionsPath).toBe(".fusion/agents/reviewer.md");
@@ -57,6 +77,7 @@ describe("AgentStore — instructions fields", () => {
name: "test-agent",
role: "executor",
});
createdAgentIds.push(agent.id);
expect(agent.instructionsText).toBeUndefined();
expect(agent.instructionsPath).toBeUndefined();
@@ -68,6 +89,7 @@ describe("AgentStore — instructions fields", () => {
role: "executor",
instructionsText: "Always write tests.",
});
createdAgentIds.push(created.id);
const loaded = await store.getAgent(created.id);
expect(loaded).not.toBeNull();
@@ -80,6 +102,7 @@ describe("AgentStore — instructions fields", () => {
role: "executor",
instructionsPath: ".fusion/agents/instructions.md",
});
createdAgentIds.push(created.id);
const loaded = await store.getAgent(created.id);
expect(loaded).not.toBeNull();
@@ -91,6 +114,7 @@ describe("AgentStore — instructions fields", () => {
name: "test-agent",
role: "executor",
});
createdAgentIds.push(agent.id);
const updated = await store.updateAgent(agent.id, {
instructionsText: "Use functional programming patterns.",
@@ -104,6 +128,7 @@ describe("AgentStore — instructions fields", () => {
name: "test-agent",
role: "executor",
});
createdAgentIds.push(agent.id);
const updated = await store.updateAgent(agent.id, {
instructionsPath: ".fusion/agents/new-instructions.md",
@@ -118,6 +143,7 @@ describe("AgentStore — instructions fields", () => {
role: "executor",
instructionsText: "Some instructions",
});
createdAgentIds.push(agent.id);
const updated = await store.updateAgent(agent.id, {
instructionsText: "",
@@ -133,6 +159,7 @@ describe("AgentStore — instructions fields", () => {
role: "executor",
instructionsPath: ".fusion/agents/old.md",
});
createdAgentIds.push(agent.id);
const updated = await store.updateAgent(agent.id, {
instructionsPath: "",
@@ -148,6 +175,7 @@ describe("AgentStore — instructions fields", () => {
instructionsText: "Old text",
instructionsPath: "old.md",
});
createdAgentIds.push(agent.id);
const updated = await store.updateAgent(agent.id, {
instructionsText: "New text",
@@ -170,6 +198,7 @@ describe("AgentStore — instructions fields", () => {
title: "My Executor",
instructionsText: "Initial",
});
createdAgentIds.push(agent.id);
const updated = await store.updateAgent(agent.id, {
instructionsText: "Updated",
@@ -188,6 +217,7 @@ describe("AgentStore — instructions fields", () => {
instructionsText: "Cached instructions",
instructionsPath: ".fusion/cached.md",
});
createdAgentIds.push(agent.id);
const cached = store.getCachedAgent(agent.id);
expect(cached).not.toBeNull();

View File

@@ -30,9 +30,30 @@ describe("CentralCore Integration", () => {
});
afterAll(async () => {
// Cleanup
await central.close();
rmSync(tempDir, { recursive: true, force: true });
// Teardown order: entity cleanup first, then infrastructure, then filesystem
// Unregister all tracked projects first (not just a subset from assertions)
for (const project of projects) {
try {
await central.unregisterProject(project.id);
} catch {
// Ignore cleanup errors for already-removed entities
}
}
projects.length = 0;
// Close CentralCore before filesystem cleanup
try {
await central.close();
} catch {
// Ignore close errors
}
// Filesystem cleanup last
try {
rmSync(tempDir, { recursive: true, force: true });
} catch {
// Ignore cleanup errors
}
});
it("should register multiple projects", async () => {