feat(FN-5124): add soft-delete QA boundary regression coverage

Adds a regression test for FN-5124 covering soft-delete QA boundary cases, targeting the engine reliability interaction suite. The single new test file exercises edge conditions in the soft-delete path to guard against future regressions.

Fusion-Task-Id: FN-5124

Fusion-Task-Lineage: 7166e252-9c98-4f51-956a-1402dae6f402
This commit is contained in:
Fusion (runfusion.ai)
2026-05-19 05:52:09 -07:00
committed by gsxdsm
parent 0cf65b4269
commit 15bb84f862
3 changed files with 101 additions and 0 deletions

View File

@@ -0,0 +1,81 @@
import { afterEach, beforeEach, describe, expect, it } from "vitest";
import { createTaskStoreTestHarness } from "./store-test-helpers.js";
describe("soft-delete QA boundary audit (FN-5124)", () => {
const harness = createTaskStoreTestHarness();
beforeEach(async () => {
await harness.beforeEach();
});
afterEach(async () => {
await harness.afterEach();
});
it("re-delete is deterministic: second delete fails not-found and does not emit duplicate task:deleted", async () => {
const store = harness.store();
const task = await store.createTask({ column: "todo", description: "redelete target" });
const deletedEvents: string[] = [];
store.on("task:deleted", (event) => deletedEvents.push(event.id));
await store.deleteTask(task.id);
const firstDeletedAt = ((store as any).db.prepare("SELECT deletedAt FROM tasks WHERE id = ?").get(task.id) as { deletedAt: string | null })
.deletedAt;
await expect(store.deleteTask(task.id)).rejects.toThrow(`Task ${task.id} not found`);
const secondDeletedAt = ((store as any).db.prepare("SELECT deletedAt FROM tasks WHERE id = ?").get(task.id) as { deletedAt: string | null })
.deletedAt;
expect(firstDeletedAt).toBeTruthy();
expect(secondDeletedAt).toBe(firstDeletedAt);
expect(deletedEvents).toEqual([task.id]);
});
it("preserves dependents by default and rewrites blockedBy when removeDependencyReferences is true", async () => {
const store = harness.store();
const parent = await store.createTask({ column: "todo", title: "parent", description: "parent task" });
const dependent = await store.createTask({ column: "todo", title: "dependent", description: "dependent task" });
await store.updateTask(dependent.id, { dependencies: [parent.id] });
await expect(store.deleteTask(parent.id)).rejects.toThrow(/depend/i);
await store.deleteTask(parent.id, { removeDependencyReferences: true });
const dependentAfter = await store.getTask(dependent.id);
expect(dependentAfter.dependencies).not.toContain(parent.id);
expect((store as any).findLiveDependents(parent.id)).toEqual([]);
});
it("archiving a soft-deleted task succeeds and hard-removes the row consistently", async () => {
const store = harness.store();
const doneTask = await store.createTask({ column: "done", title: "done task", description: "done task description" });
await store.deleteTask(doneTask.id);
const archived = await store.archiveTask(doneTask.id);
const liveRow = (store as any).db.prepare("SELECT id, deletedAt FROM tasks WHERE id = ?").get(doneTask.id) as
| { id: string; deletedAt: string | null }
| undefined;
expect(archived.column).toBe("archived");
expect(liveRow).toBeUndefined();
expect((store as any).archiveDb.get(doneTask.id)?.id).toBe(doneTask.id);
});
it.each(["todo", "in-progress", "in-review", "done", "triage"])(
"keeps ID reservation after soft-delete (%s)",
async (column) => {
const store = harness.store();
const task = await store.createTask({ column: column as any, title: `reserve-${column}`, description: `reserve ${column}` });
await store.deleteTask(task.id);
expect(() => (store as any).assertTaskIdAvailable(task.id)).toThrow(`Task ID already exists: ${task.id}`);
expect((store as any).taskIdExistsAnywhere(task.id)).toBe(true);
},
);
});