Persist circuit structure edits

This commit is contained in:
2026-07-25 20:49:34 +02:00
parent 76d8d59391
commit 2eba4ea75e
19 changed files with 809 additions and 964 deletions
+207
View File
@@ -0,0 +1,207 @@
import assert from "node:assert/strict";
import { describe, it } from "node:test";
import {
buildCircuitDeviceRowInsertSnapshot,
buildCircuitInsertSnapshot,
} from "../src/frontend/utils/circuit-structure-command.js";
import {
deleteCircuitCommand,
deleteCircuitDeviceRowCommand,
insertCircuitCommand,
insertCircuitDeviceRowCommand,
} from "../src/frontend/utils/api.js";
describe("circuit structure command adapters", () => {
it("builds a complete row snapshot without legacy identities", () => {
const row = buildCircuitDeviceRowInsertSnapshot({
id: "row-1",
circuitId: "circuit-1",
defaultSortOrder: 20,
values: {
linkedProjectDeviceId: "device-1",
name: "Steckdose",
displayName: "Steckdose Büro",
quantity: 2,
powerPerUnit: 0.4,
simultaneityFactor: 0.8,
},
});
assert.deepEqual(row, {
id: "row-1",
circuitId: "circuit-1",
linkedProjectDeviceId: "device-1",
legacyConsumerId: null,
sortOrder: 20,
name: "Steckdose",
displayName: "Steckdose Büro",
phaseType: null,
connectionKind: null,
costGroup: null,
category: null,
level: null,
roomId: null,
roomNumberSnapshot: null,
roomNameSnapshot: null,
quantity: 2,
powerPerUnit: 0.4,
simultaneityFactor: 0.8,
cosPhi: null,
remark: null,
overriddenFields: null,
});
});
it("derives reserve state from the complete device-row snapshot", () => {
const reserve = buildCircuitInsertSnapshot({
id: "circuit-1",
circuitListId: "list-1",
values: {
sectionId: "section-1",
equipmentIdentifier: "-2F1",
sortOrder: 10,
isReserve: false,
voltage: 230,
controlRequirement: "DALI",
},
deviceRows: [],
});
assert.equal(reserve.isReserve, true);
assert.equal(reserve.displayName, null);
assert.equal(reserve.voltage, 230);
assert.equal(reserve.controlRequirement, "DALI");
const row = buildCircuitDeviceRowInsertSnapshot({
id: "row-1",
circuitId: reserve.id,
defaultSortOrder: 10,
values: {
name: "Gerät",
displayName: "Gerät",
quantity: 1,
powerPerUnit: 0,
simultaneityFactor: 1,
cosPhi: 1,
},
});
const populated = buildCircuitInsertSnapshot({
id: reserve.id,
circuitListId: reserve.circuitListId,
values: {
sectionId: reserve.sectionId,
equipmentIdentifier: reserve.equipmentIdentifier,
sortOrder: reserve.sortOrder,
isReserve: true,
},
deviceRows: [row],
});
assert.equal(populated.isReserve, false);
assert.deepEqual(populated.deviceRows, [row]);
});
it("sends structure envelopes through the project command route", async () => {
const requests: Array<{ url: string; body: Record<string, unknown> }> = [];
const originalFetch = globalThis.fetch;
globalThis.fetch = async (input, init) => {
requests.push({
url: String(input),
body: JSON.parse(String(init?.body)) as Record<string, unknown>,
});
return new Response(
JSON.stringify({
revision: {
revisionId: "revision-1",
changeSetId: "change-1",
projectId: "project-1",
revisionNumber: requests.length,
createdAtIso: "2026-07-25T00:00:00.000Z",
},
history: {
projectId: "project-1",
currentRevision: requests.length,
undoDepth: requests.length,
redoDepth: 0,
undoChangeSetId: "change-1",
redoChangeSetId: null,
},
}),
{ status: 200, headers: { "Content-Type": "application/json" } }
);
};
try {
const row = buildCircuitDeviceRowInsertSnapshot({
id: "row-1",
circuitId: "circuit-1",
defaultSortOrder: 10,
values: {
name: "Gerät",
displayName: "Gerät",
quantity: 1,
powerPerUnit: 0,
simultaneityFactor: 1,
},
});
const circuit = buildCircuitInsertSnapshot({
id: "circuit-1",
circuitListId: "list-1",
values: {
sectionId: "section-1",
equipmentIdentifier: "-2F1",
sortOrder: 10,
},
deviceRows: [row],
});
await insertCircuitCommand("project-1", 0, circuit, "insert circuit");
await deleteCircuitCommand(
"project-1",
1,
circuit.id,
circuit.circuitListId,
"delete circuit"
);
await insertCircuitDeviceRowCommand(
"project-1",
2,
row,
"insert row"
);
await deleteCircuitDeviceRowCommand(
"project-1",
3,
row.id,
row.circuitId,
"delete row"
);
} finally {
globalThis.fetch = originalFetch;
}
assert.deepEqual(
requests.map((request) => request.url),
Array(4).fill("/api/projects/project-1/commands")
);
assert.deepEqual(
requests.map((request) => {
const command = request.body.command as {
schemaVersion: number;
type: string;
};
return [
request.body.expectedRevision,
command.schemaVersion,
command.type,
];
}),
[
[0, 1, "circuit.insert"],
[1, 1, "circuit.delete"],
[2, 1, "circuit-device-row.insert"],
[3, 1, "circuit-device-row.delete"],
]
);
});
});