import assert from "node:assert/strict"; import { describe, it } from "node:test"; import { loadCircuitEditorSnapshot } from "../src/frontend/utils/circuit-editor-history.js"; import { getProjectHistory, redoProjectCommand, undoProjectCommand, } from "../src/frontend/utils/api.js"; import type { CircuitTreeResponseDto, ProjectHistoryStateDto, } from "../src/frontend/types.js"; function tree(currentRevision: number): CircuitTreeResponseDto { return { id: "list-1", projectId: "project-1", distributionBoardId: "board-1", name: "Liste", currentRevision, sections: [], }; } function history(currentRevision: number): ProjectHistoryStateDto { return { projectId: "project-1", currentRevision, undoDepth: currentRevision, redoDepth: 0, undoChangeSetId: currentRevision > 0 ? `change-${currentRevision}` : null, redoChangeSetId: null, }; } describe("circuit editor history snapshot", () => { it("returns tree and persistent history from the same project revision", async () => { const snapshot = await loadCircuitEditorSnapshot( async () => tree(4), async () => history(4) ); assert.equal(snapshot.tree.currentRevision, 4); assert.equal(snapshot.history.undoDepth, 4); }); it("retries when the project changes between the tree and history reads", async () => { const treeRevisions = [4, 5]; const historyRevisions = [5, 5]; let attempts = 0; const snapshot = await loadCircuitEditorSnapshot( async () => tree(treeRevisions[attempts]), async () => history(historyRevisions[attempts++]) ); assert.equal(attempts, 2); assert.equal(snapshot.tree.currentRevision, 5); assert.equal(snapshot.history.currentRevision, 5); }); it("rejects a snapshot that remains inconsistent", async () => { await assert.rejects( () => loadCircuitEditorSnapshot( async () => tree(4), async () => history(5), 2 ), /während des Ladens geändert/ ); }); it("loads history and sends revision-safe undo/redo requests", async () => { const requests: Array<{ url: string; body?: unknown }> = []; const originalFetch = globalThis.fetch; globalThis.fetch = async (input, init) => { const url = String(input); requests.push({ url, ...(init?.body ? { body: JSON.parse(String(init.body)) as unknown } : {}), }); const currentRevision = requests.length - 1; const state = history(currentRevision); return new Response( JSON.stringify( init?.method === "POST" ? { revision: { revisionId: `revision-${currentRevision}`, changeSetId: `change-${currentRevision}`, projectId: "project-1", revisionNumber: currentRevision, createdAtIso: "2026-07-25T00:00:00.000Z", }, history: state, } : state ), { status: 200, headers: { "Content-Type": "application/json" } } ); }; try { await getProjectHistory("project-1"); await undoProjectCommand("project-1", 4); await redoProjectCommand("project-1", 5); } finally { globalThis.fetch = originalFetch; } assert.deepEqual(requests, [ { url: "/api/projects/project-1/history" }, { url: "/api/projects/project-1/history/undo", body: { expectedRevision: 4 }, }, { url: "/api/projects/project-1/history/redo", body: { expectedRevision: 5 }, }, ]); }); });