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:
committed by
gsxdsm
parent
0cf65b4269
commit
15bb84f862
81
packages/core/src/__tests__/soft-delete-qa-FN-5124.test.ts
Normal file
81
packages/core/src/__tests__/soft-delete-qa-FN-5124.test.ts
Normal 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);
|
||||
},
|
||||
);
|
||||
});
|
||||
Reference in New Issue
Block a user