Files
fusion/packages/dashboard/app/api/__tests__/deleteAiSession.test.ts
gsxdsm dab1569ac3 FN-5892: dedupe planning sessions and persist history deletion
Prevent duplicate planning-history rows and keep deletions visible only after the backend confirms persistence.

- dedupe Planning Mode history by session id across initial loads, live updates, archive toggles, and draft creation
- make deleteAiSession surface backend/content-type errors instead of silently succeeding on failed deletes
- keep failed history deletions visible, refresh the list, and show an error toast when persistence fails
- add dashboard tests covering session deduplication, delete API failures, and persistence-aware Planning Mode deletion behavior
- document the history dedupe/delete semantics and add a patch changeset for @runfusion/fusion

Files changed:
 .changeset/fn-5892-planning-history-fix.md         |   5 +
 docs/dashboard-guide.md                            |   2 +-
 packages/dashboard/app/api/__tests__/deleteAiSession.test.ts      |  54 +++++
 packages/dashboard/app/api/legacy.ts               |  41 +++-
 packages/dashboard/app/components/PlanningModeModal.tsx |  70 ++++---
 packages/dashboard/app/components/__tests__/PlanningModeModal.autosize.test.tsx  |  11 +
 packages/dashboard/app/components/__tests__/PlanningModeModal.dedupeSessionsById.test.ts   |  44 ++++
 packages/dashboard/app/components/__tests__/PlanningModeModal.initial.test.tsx   |  11 +
 packages/dashboard/app/components/__tests__/PlanningModeModal.planning-flow.test.tsx       | 226 ++++++++++++++++++++-
 packages/dashboard/app/components/__tests__/PlanningModeModal.test-helpers.ts    |   1 +
 10 files changed, 439 insertions(+), 26 deletions(-)

Fusion-Task-Id: FN-5892

Fusion-Task-Lineage: 7453bc34-6d57-4d67-ac92-8fb50084fbee
2026-06-02 12:42:04 -07:00

55 lines
1.7 KiB
TypeScript

import { afterEach, describe, expect, it, vi } from "vitest";
import { ApiRequestError, deleteAiSession } from "../legacy";
describe("deleteAiSession", () => {
afterEach(() => {
vi.restoreAllMocks();
});
it("resolves on 200 responses", async () => {
vi.spyOn(globalThis, "fetch").mockResolvedValue(
new Response(JSON.stringify({ ok: true }), {
status: 200,
headers: { "content-type": "application/json" },
}),
);
await expect(deleteAiSession("session-1")).resolves.toBeUndefined();
});
it("treats 404 responses as idempotent success", async () => {
vi.spyOn(globalThis, "fetch").mockResolvedValue(
new Response(JSON.stringify({ error: "Session not found" }), {
status: 404,
headers: { "content-type": "application/json" },
}),
);
await expect(deleteAiSession("missing-session")).resolves.toBeUndefined();
});
it("rejects on non-404 server failures", async () => {
vi.spyOn(globalThis, "fetch").mockResolvedValue(
new Response(JSON.stringify({ error: "Delete failed", details: { retryable: false } }), {
status: 500,
headers: { "content-type": "application/json" },
}),
);
await expect(deleteAiSession("session-1")).rejects.toEqual(
expect.objectContaining<ApiRequestError>({
name: "ApiRequestError",
message: "Delete failed",
status: 500,
details: { retryable: false },
}),
);
});
it("rejects on network failures", async () => {
vi.spyOn(globalThis, "fetch").mockRejectedValue(new TypeError("Failed to fetch"));
await expect(deleteAiSession("session-1")).rejects.toThrow("Failed to fetch");
});
});