import assert from "node:assert/strict"; import { describe, it } from "node:test"; import { deserializeProjectStateSnapshot, parseProjectStateSnapshot, serializeProjectStateSnapshot, } from "../src/domain/models/project-state-snapshot.model.js"; import { createNamedProjectSnapshotSchema } from "../src/shared/validation/project-snapshot.schemas.js"; function minimalSnapshot() { return { schemaVersion: 1 as const, project: { id: "project-1", name: "Projekt", singlePhaseVoltageV: 230, threePhaseVoltageV: 400, }, distributionBoards: [], circuitLists: [], circuitSections: [], circuits: [], projectDevices: [], floors: [], rooms: [], }; } describe("project state snapshot model", () => { it("round-trips a versioned logical project state", () => { const snapshot = parseProjectStateSnapshot(minimalSnapshot()); assert.deepEqual( deserializeProjectStateSnapshot( serializeProjectStateSnapshot(snapshot) ), snapshot ); }); it("rejects duplicate ids and cross-project ownership", () => { assert.throws( () => parseProjectStateSnapshot({ ...minimalSnapshot(), distributionBoards: [ { id: "board-1", projectId: "project-1", name: "UV 1", }, { id: "board-1", projectId: "project-1", name: "UV 2", }, ], }), /duplicate distribution board ids/ ); assert.throws( () => parseProjectStateSnapshot({ ...minimalSnapshot(), floors: [ { id: "floor-1", projectId: "project-2", name: "EG", sortOrder: 10, }, ], }), /floor belongs to a different project/ ); }); it("rejects invalid references and inconsistent reserve circuits", () => { assert.throws( () => parseProjectStateSnapshot({ ...minimalSnapshot(), rooms: [ { id: "room-1", projectId: "project-1", floorId: "missing", roomNumber: "001", roomName: "Technik", }, ], }), /room floor reference is invalid/ ); assert.throws( () => parseProjectStateSnapshot({ ...minimalSnapshot(), circuits: [ { id: "circuit-1", circuitListId: "missing", sectionId: "missing", equipmentIdentifier: "-1F1", displayName: null, sortOrder: 10, protectionType: null, protectionRatedCurrent: null, protectionCharacteristic: null, cableType: null, cableCrossSection: null, cableLength: null, rcdAssignment: null, terminalDesignation: null, voltage: null, controlRequirement: null, status: null, isReserve: true, remark: null, deviceRows: [], }, ], }), /circuit list reference is invalid/ ); }); }); describe("named project snapshot request", () => { it("normalizes bounded snapshot metadata", () => { assert.deepEqual( createNamedProjectSnapshotSchema.parse({ expectedRevision: 12, name: " Freigabe ", description: " Stand vor Ausschreibung ", }), { expectedRevision: 12, name: "Freigabe", description: "Stand vor Ausschreibung", } ); }); it("rejects stale-shaped and excessive input", () => { for (const input of [ { expectedRevision: -1, name: "Stand" }, { expectedRevision: 0, name: " " }, { expectedRevision: 0, name: "x".repeat(101) }, { expectedRevision: 0, name: "Stand", description: "x".repeat(501), }, { expectedRevision: 0, name: "Stand", unexpected: true, }, ]) { assert.equal( createNamedProjectSnapshotSchema.safeParse(input).success, false ); } }); });