168 lines
4.5 KiB
TypeScript
168 lines
4.5 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import { describe, it } from "node:test";
|
|
import { ProjectDeviceSyncService } from "../src/domain/services/project-device-sync.service.js";
|
|
|
|
function projectDevice() {
|
|
return {
|
|
id: "pd1",
|
|
projectId: "p1",
|
|
name: "Luminaire",
|
|
displayName: "Office lighting",
|
|
phaseType: "single_phase",
|
|
connectionKind: "fixed",
|
|
costGroup: "440",
|
|
category: "Lighting",
|
|
quantity: 6,
|
|
powerPerUnit: 0.04,
|
|
simultaneityFactor: 0.8,
|
|
totalPower: 0.192,
|
|
cosPhi: 0.95,
|
|
remark: "DALI",
|
|
voltageV: 230,
|
|
};
|
|
}
|
|
|
|
function linkedRow() {
|
|
return {
|
|
id: "row1",
|
|
circuitId: "c1",
|
|
linkedProjectDeviceId: "pd1",
|
|
legacyConsumerId: null,
|
|
sortOrder: 10,
|
|
name: "Old luminaire",
|
|
displayName: "Local display name",
|
|
phaseType: "single_phase",
|
|
connectionKind: null,
|
|
costGroup: null,
|
|
category: "Lighting",
|
|
level: null,
|
|
roomId: null,
|
|
roomNumberSnapshot: "1.01",
|
|
roomNameSnapshot: "Office",
|
|
quantity: 2,
|
|
powerPerUnit: 0.03,
|
|
simultaneityFactor: 1,
|
|
cosPhi: 1,
|
|
remark: null,
|
|
overriddenFields: JSON.stringify(["displayName", "quantity"]),
|
|
equipmentIdentifier: "-1F1",
|
|
circuitDisplayName: "Office",
|
|
circuitListId: "list1",
|
|
circuitListName: "UV-01 Stromkreisliste",
|
|
distributionBoardId: "db1",
|
|
distributionBoardName: "UV-01",
|
|
};
|
|
}
|
|
|
|
function createService() {
|
|
const rows = [linkedRow()];
|
|
const service = new ProjectDeviceSyncService({
|
|
projectDeviceRepository: {
|
|
async findById() {
|
|
return projectDevice();
|
|
},
|
|
},
|
|
deviceRowRepository: {
|
|
async listLinkedByProjectDevice() {
|
|
return rows;
|
|
},
|
|
},
|
|
});
|
|
return { service, rows };
|
|
}
|
|
|
|
describe("project device synchronization", () => {
|
|
it("shows field differences and local overrides without changing rows", async () => {
|
|
const { service, rows } = createService();
|
|
const before = structuredClone(rows);
|
|
const preview = await service.getPreview("p1", "pd1");
|
|
|
|
assert.deepEqual(rows, before);
|
|
assert.equal(preview.rows[0].equipmentIdentifier, "-1F1");
|
|
assert.equal(
|
|
preview.rows[0].differences.some(
|
|
(difference) => difference.field === "quantity"
|
|
),
|
|
true
|
|
);
|
|
assert.equal(
|
|
preview.rows[0].differences.find(
|
|
(difference) => difference.field === "displayName"
|
|
)?.isOverridden,
|
|
true
|
|
);
|
|
});
|
|
|
|
it("creates a deterministic command for selected synchronization fields", async () => {
|
|
const { service, rows } = createService();
|
|
const before = structuredClone(rows);
|
|
const command = await service.createSynchronizeCommand(
|
|
"p1",
|
|
"pd1",
|
|
["row1"],
|
|
["quantity", "powerPerUnit"]
|
|
);
|
|
|
|
assert.deepEqual(rows, before);
|
|
assert.equal(command.payload.operation, "synchronize");
|
|
assert.equal(command.payload.rows[0].expected.quantity, 2);
|
|
assert.equal(command.payload.rows[0].target.quantity, 6);
|
|
assert.equal(command.payload.rows[0].target.powerPerUnit, 0.04);
|
|
assert.equal(
|
|
command.payload.rows[0].target.displayName,
|
|
"Local display name"
|
|
);
|
|
assert.deepEqual(
|
|
JSON.parse(command.payload.rows[0].target.overriddenFields ?? "[]"),
|
|
["displayName"]
|
|
);
|
|
});
|
|
|
|
it("rejects rows that are not linked to the selected project device", async () => {
|
|
const { service } = createService();
|
|
await assert.rejects(
|
|
() =>
|
|
service.createSynchronizeCommand(
|
|
"p1",
|
|
"pd1",
|
|
["other-row"],
|
|
["quantity"]
|
|
),
|
|
/not linked/
|
|
);
|
|
});
|
|
|
|
it("creates a link-only disconnect command", async () => {
|
|
const { service } = createService();
|
|
const command = await service.createDisconnectCommand(
|
|
"p1",
|
|
"pd1",
|
|
["row1"]
|
|
);
|
|
|
|
const assignment = command.payload.rows[0];
|
|
assert.equal(command.payload.operation, "disconnect");
|
|
assert.equal(assignment.expected.linkedProjectDeviceId, "pd1");
|
|
assert.equal(assignment.target.linkedProjectDeviceId, null);
|
|
assert.equal(
|
|
assignment.target.displayName,
|
|
assignment.expected.displayName
|
|
);
|
|
assert.equal(assignment.target.quantity, assignment.expected.quantity);
|
|
});
|
|
|
|
it("does not create no-op synchronization commands", async () => {
|
|
const { service } = createService();
|
|
await assert.rejects(
|
|
() =>
|
|
service.createSynchronizeCommand(
|
|
"p1",
|
|
"pd1",
|
|
["row1"],
|
|
["category"]
|
|
),
|
|
/at least one row/
|
|
);
|
|
});
|
|
});
|