Create clean release database baseline
This commit is contained in:
@@ -42,17 +42,6 @@ function minimalSnapshot() {
|
||||
};
|
||||
}
|
||||
|
||||
function legacyMinimalSnapshot() {
|
||||
const {
|
||||
distributionBoardComponents: _distributionBoardComponents,
|
||||
circuitProtectionDevices: _circuitProtectionDevices,
|
||||
distributionBoardComponentProtectionDevices:
|
||||
_distributionBoardComponentProtectionDevices,
|
||||
...snapshot
|
||||
} = minimalSnapshot();
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
function protectedSnapshot() {
|
||||
return {
|
||||
...minimalSnapshot(),
|
||||
@@ -94,9 +83,6 @@ function protectedSnapshot() {
|
||||
equipmentIdentifier: "-1F1.1",
|
||||
displayName: "Reserve",
|
||||
sortOrder: 10,
|
||||
protectionType: null,
|
||||
protectionRatedCurrent: null,
|
||||
protectionCharacteristic: null,
|
||||
cableType: null,
|
||||
cableCrossSection: null,
|
||||
cableLength: null,
|
||||
@@ -148,234 +134,26 @@ function protectedSnapshot() {
|
||||
}
|
||||
|
||||
describe("project state snapshot model", () => {
|
||||
it("round-trips a versioned logical project state", () => {
|
||||
it("round-trips the current logical project state", () => {
|
||||
const snapshot = parseProjectStateSnapshot(minimalSnapshot());
|
||||
assert.deepEqual(
|
||||
deserializeProjectStateSnapshot(
|
||||
serializeProjectStateSnapshot(snapshot)
|
||||
),
|
||||
deserializeProjectStateSnapshot(serializeProjectStateSnapshot(snapshot)),
|
||||
snapshot
|
||||
);
|
||||
});
|
||||
|
||||
it("upgrades version-one snapshots with empty project metadata", () => {
|
||||
const legacy = {
|
||||
...legacyMinimalSnapshot(),
|
||||
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 = {
|
||||
...legacyMinimalSnapshot(),
|
||||
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 = {
|
||||
...legacyMinimalSnapshot(),
|
||||
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 = {
|
||||
...legacyMinimalSnapshot(),
|
||||
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 = {
|
||||
...legacyMinimalSnapshot(),
|
||||
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 pre-baseline snapshot shapes", () => {
|
||||
const outdated = structuredClone(minimalSnapshot());
|
||||
delete (outdated.project as Record<string, unknown>).description;
|
||||
assert.throws(() => parseProjectStateSnapshot(outdated));
|
||||
assert.throws(() =>
|
||||
parseProjectStateSnapshot({ ...minimalSnapshot(), schemaVersion: 7 })
|
||||
);
|
||||
});
|
||||
|
||||
it("upgrades version-six sections without inventing component data", () => {
|
||||
const previous = {
|
||||
...legacyMinimalSnapshot(),
|
||||
schemaVersion: 6 as const,
|
||||
circuitSections: [
|
||||
{
|
||||
id: "section-lighting",
|
||||
circuitListId: "list-1",
|
||||
key: "lighting",
|
||||
displayName: "Beleuchtung",
|
||||
prefix: "-1F",
|
||||
sortOrder: 10,
|
||||
},
|
||||
{
|
||||
id: "section-unassigned",
|
||||
circuitListId: "list-1",
|
||||
key: "unassigned",
|
||||
displayName: "Nicht zugeordnet",
|
||||
prefix: "-UF",
|
||||
sortOrder: 90,
|
||||
},
|
||||
],
|
||||
distributionBoards: [
|
||||
{
|
||||
id: "board-1",
|
||||
projectId: "project-1",
|
||||
name: "UV 1",
|
||||
floorId: null,
|
||||
supplyType: null,
|
||||
simultaneityFactor: 1,
|
||||
},
|
||||
],
|
||||
circuitLists: [
|
||||
{
|
||||
id: "list-1",
|
||||
projectId: "project-1",
|
||||
distributionBoardId: "board-1",
|
||||
name: "UV 1 Stromkreisliste",
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const snapshot = parseProjectStateSnapshot(previous);
|
||||
|
||||
assert.deepEqual(
|
||||
snapshot.circuitSections.map(
|
||||
({ key, category, groupNumber }) => ({
|
||||
key,
|
||||
category,
|
||||
groupNumber,
|
||||
})
|
||||
),
|
||||
[
|
||||
{
|
||||
key: "lighting",
|
||||
category: "lighting",
|
||||
groupNumber: 1,
|
||||
},
|
||||
{
|
||||
key: "unassigned",
|
||||
category: null,
|
||||
groupNumber: null,
|
||||
},
|
||||
]
|
||||
);
|
||||
assert.deepEqual(snapshot.distributionBoardComponents, []);
|
||||
assert.deepEqual(snapshot.circuitProtectionDevices, []);
|
||||
assert.deepEqual(
|
||||
snapshot.distributionBoardComponentProtectionDevices,
|
||||
[]
|
||||
);
|
||||
});
|
||||
|
||||
it("validates the complete group, component and protection graph", () => {
|
||||
it("validates component, protection and equipment relations", () => {
|
||||
const snapshot = parseProjectStateSnapshot(protectedSnapshot());
|
||||
|
||||
assert.equal(snapshot.circuitSections[0]?.groupNumber, 1);
|
||||
assert.equal(
|
||||
snapshot.distributionBoardComponents[0]?.sectionId,
|
||||
"section-1"
|
||||
);
|
||||
assert.equal(
|
||||
snapshot.circuitProtectionDevices[0]?.tripCharacteristic,
|
||||
"B"
|
||||
);
|
||||
assert.equal(snapshot.circuitProtectionDevices[0]?.tripCharacteristic, "B");
|
||||
assert.equal(
|
||||
snapshot.distributionBoardComponentProtectionDevices[0]
|
||||
?.ratedResidualCurrentMa,
|
||||
@@ -401,113 +179,27 @@ describe("project state snapshot model", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("rejects duplicate ids and cross-project ownership", () => {
|
||||
it("rejects duplicate ids and invalid ownership", () => {
|
||||
const duplicate = protectedSnapshot();
|
||||
duplicate.distributionBoards.push({
|
||||
...duplicate.distributionBoards[0]!,
|
||||
name: "UV 2",
|
||||
});
|
||||
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,
|
||||
},
|
||||
],
|
||||
}),
|
||||
() => parseProjectStateSnapshot(duplicate),
|
||||
/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", () => {
|
||||
const wrongOwner = minimalSnapshot();
|
||||
wrongOwner.floors.push({
|
||||
id: "floor-1",
|
||||
projectId: "project-2",
|
||||
name: "EG",
|
||||
sortOrder: 10,
|
||||
});
|
||||
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/
|
||||
() => parseProjectStateSnapshot(wrongOwner),
|
||||
/floor belongs to a different project/
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -518,36 +210,29 @@ describe("named project snapshot request", () => {
|
||||
createNamedProjectSnapshotSchema.parse({
|
||||
expectedRevision: 12,
|
||||
name: " Freigabe ",
|
||||
description: " Stand vor Ausschreibung ",
|
||||
description: " Stand vor Änderung ",
|
||||
}),
|
||||
{
|
||||
expectedRevision: 12,
|
||||
name: "Freigabe",
|
||||
description: "Stand vor Ausschreibung",
|
||||
description: "Stand vor Änderung",
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
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",
|
||||
assert.throws(() =>
|
||||
createNamedProjectSnapshotSchema.parse({
|
||||
expectedRevision: -1,
|
||||
name: "",
|
||||
unexpected: true,
|
||||
},
|
||||
]) {
|
||||
assert.equal(
|
||||
createNamedProjectSnapshotSchema.safeParse(input).success,
|
||||
false
|
||||
);
|
||||
}
|
||||
})
|
||||
);
|
||||
assert.throws(() =>
|
||||
createNamedProjectSnapshotSchema.parse({
|
||||
expectedRevision: 0,
|
||||
name: "x".repeat(201),
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user