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() as never; }, }, deviceRowRepository: { async listLinkedByProjectDevice() { return rows as never; }, }, }); 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/ ); }); });