Files
leistungsbilanz-ts/tests/project-state-snapshot.test.ts
T

336 lines
8.6 KiB
TypeScript

import assert from "node:assert/strict";
import { describe, it } from "node:test";
import {
deserializeProjectStateSnapshot,
parseProjectStateSnapshot,
projectStateSnapshotSchemaVersion,
serializeProjectStateSnapshot,
} from "../src/domain/models/project-state-snapshot.model.js";
import { createNamedProjectSnapshotSchema } from "../src/shared/validation/project-snapshot.schemas.js";
function minimalSnapshot() {
return {
schemaVersion: projectStateSnapshotSchemaVersion,
project: {
id: "project-1",
name: "Projekt",
internalProjectNumber: "INT-1",
externalProjectNumber: null,
buildingOwner: null,
description: null,
singlePhaseVoltageV: 230,
threePhaseVoltageV: 400,
enabledDistributionBoardSupplyTypes: [
"AV",
"SV",
"EV",
"USV",
"MSR",
"SiBe",
],
},
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("upgrades version-one snapshots with empty project metadata", () => {
const legacy = {
...minimalSnapshot(),
schemaVersion: 1 as const,
project: {
id: "project-1",
name: "Altprojekt",
singlePhaseVoltageV: 230,
threePhaseVoltageV: 400,
},
};
const snapshot = parseProjectStateSnapshot(legacy);
assert.equal(snapshot.schemaVersion, projectStateSnapshotSchemaVersion);
assert.deepEqual(snapshot.project, {
...legacy.project,
internalProjectNumber: null,
externalProjectNumber: null,
buildingOwner: null,
description: null,
enabledDistributionBoardSupplyTypes: [
"AV",
"SV",
"EV",
"USV",
"MSR",
"SiBe",
],
});
});
it("upgrades version-two distribution boards without inferred assignments", () => {
const previous = {
...minimalSnapshot(),
schemaVersion: 2 as const,
project: {
...minimalSnapshot().project,
enabledDistributionBoardSupplyTypes: undefined,
},
distributionBoards: [
{
id: "board-1",
projectId: "project-1",
name: "UV 1",
},
],
};
delete previous.project.enabledDistributionBoardSupplyTypes;
const snapshot = parseProjectStateSnapshot(previous);
assert.deepEqual(snapshot.distributionBoards, [
{
...previous.distributionBoards[0],
floorId: null,
supplyType: null,
simultaneityFactor: 1,
},
]);
});
it("upgrades version-three snapshots with all project supply types", () => {
const previous = {
...minimalSnapshot(),
schemaVersion: 3 as const,
project: {
...minimalSnapshot().project,
enabledDistributionBoardSupplyTypes: undefined,
},
};
delete previous.project.enabledDistributionBoardSupplyTypes;
const snapshot = parseProjectStateSnapshot(previous);
assert.deepEqual(snapshot.project.enabledDistributionBoardSupplyTypes, [
"AV",
"SV",
"EV",
"USV",
"MSR",
"SiBe",
]);
});
it("normalizes version-four device voltages to project settings", () => {
const previous = {
...minimalSnapshot(),
schemaVersion: 4 as const,
projectDevices: [
{
id: "device-1",
projectId: "project-1",
name: "Pumpe",
displayName: "Pumpe",
phaseType: "three_phase" as const,
connectionKind: null,
costGroup: null,
category: null,
quantity: 1,
powerPerUnit: 2,
simultaneityFactor: 1,
cosPhi: null,
remark: null,
voltageV: 500,
},
],
};
const snapshot = parseProjectStateSnapshot(previous);
assert.equal(snapshot.schemaVersion, projectStateSnapshotSchemaVersion);
assert.equal(snapshot.projectDevices[0]?.voltageV, 400);
});
it("upgrades version-five distribution boards with a neutral factor", () => {
const previous = {
...minimalSnapshot(),
schemaVersion: 5 as const,
distributionBoards: [
{
id: "board-1",
projectId: "project-1",
name: "UV 1",
floorId: null,
supplyType: "AV" as const,
},
],
};
const snapshot = parseProjectStateSnapshot(previous);
assert.equal(snapshot.schemaVersion, projectStateSnapshotSchemaVersion);
assert.equal(
snapshot.distributionBoards[0]?.simultaneityFactor,
1
);
});
it("rejects duplicate ids and cross-project ownership", () => {
assert.throws(
() =>
parseProjectStateSnapshot({
...minimalSnapshot(),
distributionBoards: [
{
id: "board-1",
projectId: "project-1",
name: "UV 1",
floorId: null,
supplyType: null,
simultaneityFactor: 1,
},
{
id: "board-1",
projectId: "project-1",
name: "UV 2",
floorId: null,
supplyType: null,
simultaneityFactor: 1,
},
],
}),
/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(),
distributionBoards: [
{
id: "board-1",
projectId: "project-1",
name: "UV 1",
floorId: "missing",
supplyType: "AV",
simultaneityFactor: 1,
},
],
}),
/distribution board floor reference is invalid/
);
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
);
}
});
});