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

This commit is contained in:
gsxdsm
2026-05-20 02:47:34 -07:00
parent c9fd41ef26
commit b1c0a91752
4 changed files with 136 additions and 6 deletions

View File

@@ -0,0 +1,82 @@
import { afterEach, beforeEach, describe, expect, it } from "vitest";
import { AgentStore } from "../agent-store.js";
import { TaskStore } from "../store.js";
import { createTaskStoreTestHarness } from "./store-test-helpers.js";
describe("TaskStore soft delete of checked-out tasks", () => {
const harness = createTaskStoreTestHarness();
beforeEach(async () => {
await harness.beforeEach();
});
afterEach(async () => {
await harness.afterEach();
});
it("clears agents.taskId when deleting a checked-out task", async () => {
harness.store().close();
const store = new TaskStore(harness.rootDir(), harness.globalDir());
await store.init();
const agentStore = new AgentStore({ rootDir: store.getFusionDir(), taskStore: store });
await agentStore.init();
try {
const task = await store.createTask({ description: "checked-out delete target" });
const agent = await agentStore.createAgent({ name: "Lease Holder", role: "executor" });
await store.updateTask(task.id, { assignedAgentId: agent.id });
expect((await agentStore.getAgent(agent.id))?.taskId).toBe(task.id);
await agentStore.checkoutTask(agent.id, task.id);
expect((await store.getTask(task.id)).checkedOutBy).toBe(agent.id);
const deletedEvents: string[] = [];
store.on("task:deleted", (event) => deletedEvents.push(event.id));
await store.deleteTask(task.id);
expect((await agentStore.getAgent(agent.id))?.taskId).toBeUndefined();
const row = (store as any).db.prepare(
"SELECT taskId, json_extract(data, '$.taskId') AS jsonTaskId FROM agents WHERE id = ?",
).get(agent.id) as { taskId: string | null; jsonTaskId: string | null };
expect(row.taskId).toBeNull();
expect(row.jsonTaskId).toBeNull();
expect(deletedEvents).toEqual([task.id]);
} finally {
agentStore.close();
store.close();
}
});
it("keeps checked-out soft-deleted rows invisible to live readers", async () => {
harness.store().close();
const store = new TaskStore(harness.rootDir(), harness.globalDir());
await store.init();
const agentStore = new AgentStore({ rootDir: store.getFusionDir(), taskStore: store });
await agentStore.init();
try {
const task = await store.createTask({ description: "checked-out invisible row" });
const agent = await agentStore.createAgent({ name: "Deleted Lease Holder", role: "executor" });
await store.updateTask(task.id, { assignedAgentId: agent.id });
await agentStore.checkoutTask(agent.id, task.id);
await store.deleteTask(task.id);
await expect(store.getTask(task.id)).rejects.toThrow(`Task ${task.id} not found`);
expect((await store.listTasks()).map((entry) => entry.id)).not.toContain(task.id);
const row = (store as any).db.prepare(
"SELECT checkedOutBy, deletedAt FROM tasks WHERE id = ?",
).get(task.id) as { checkedOutBy: string | null; deletedAt: string | null };
expect(row.checkedOutBy).toBe(agent.id);
expect(typeof row.deletedAt).toBe("string");
} finally {
agentStore.close();
store.close();
}
});
});