Create circuits with rows atomically

This commit is contained in:
2026-07-23 17:36:38 +02:00
parent b0fc2b4fbf
commit 0800f984bb
11 changed files with 450 additions and 93 deletions
+149
View File
@@ -215,6 +215,85 @@ describe("circuit write service rules", () => {
});
});
it("creates a circuit and all first device rows through one repository command", async () => {
let transactionalCreate: {
circuit: { circuitListId: string; sectionId: string; equipmentIdentifier: string };
deviceRows: Array<{ linkedProjectDeviceId?: string; name: string }>;
} | undefined;
const service = new CircuitWriteService({
circuitListRepository: {
async findById() {
return { id: "l1", projectId: "p1" } as never;
},
} as never,
circuitSectionRepository: {
async findById() {
return { id: "s1", circuitListId: "l1" } as never;
},
} as never,
circuitRepository: {
async existsByEquipmentIdentifier() {
return false;
},
async findById() {
return { id: "c-new", circuitListId: "l1", sectionId: "s1" } as never;
},
} as never,
projectDeviceRepository: {
async findById(projectId: string, deviceId: string) {
return projectId === "p1" && deviceId === "pd1" ? ({ id: "pd1" } as never) : null;
},
} as never,
deviceRowRepository: {
createCircuitWithDeviceRowsTransactional(input: typeof transactionalCreate) {
transactionalCreate = input;
return { circuitId: "c-new", rowIds: ["r1", "r2"] };
},
async findById(rowId: string) {
return { id: rowId, circuitId: "c-new" } as never;
},
} as never,
});
const created = await service.createCircuitWithDeviceRows("p1", "l1", {
circuit: {
sectionId: "s1",
equipmentIdentifier: "-2F1",
displayName: "Steckdosen",
sortOrder: 10,
},
deviceRows: [
{
linkedProjectDeviceId: "pd1",
name: "Socket",
displayName: "Steckdose",
quantity: 1,
powerPerUnit: 0.2,
simultaneityFactor: 1,
},
{
name: "Manual",
displayName: "Manuell",
quantity: 2,
powerPerUnit: 0.1,
simultaneityFactor: 0.8,
},
],
});
assert.equal(transactionalCreate?.circuit.circuitListId, "l1");
assert.equal(transactionalCreate?.circuit.sectionId, "s1");
assert.equal(transactionalCreate?.circuit.equipmentIdentifier, "-2F1");
assert.deepEqual(
transactionalCreate?.deviceRows.map((row) => row.name),
["Socket", "Manual"]
);
assert.deepEqual(
created.deviceRows.map((row) => row?.id),
["r1", "r2"]
);
});
it("renumber uses safe bulk identifier update for swapped identifiers", async () => {
let safeUpdatePayload: Array<{ id: string; equipmentIdentifier: string }> = [];
const service = new CircuitWriteService({
@@ -997,6 +1076,76 @@ describe("circuit write service rules", () => {
}
});
it("new circuit and initial device rows share one synchronous transaction", () => {
const repository = new CircuitDeviceRowRepository();
const originalTransaction = (db as unknown as { transaction: unknown }).transaction;
let callbackReturnedPromise = false;
const insertedValues: Array<Record<string, unknown>> = [];
(db as unknown as { transaction: (cb: (tx: unknown) => unknown) => void }).transaction = (cb) => {
const fakeTx = {
insert() {
return {
values(values: Record<string, unknown>) {
insertedValues.push(values);
return {
run() {
return { changes: 1 };
},
};
},
};
},
};
const callbackResult = cb(fakeTx);
callbackReturnedPromise = Boolean(
callbackResult && typeof (callbackResult as Promise<unknown>).then === "function"
);
};
try {
const created = repository.createCircuitWithDeviceRowsTransactional({
circuit: {
circuitListId: "l1",
sectionId: "s1",
equipmentIdentifier: "-2F1",
displayName: "Steckdosen",
sortOrder: 10,
},
deviceRows: [
{
name: "Socket",
displayName: "Steckdose",
quantity: 1,
powerPerUnit: 0.2,
simultaneityFactor: 1,
sortOrder: 15,
},
{
name: "Manual",
displayName: "Manuell",
quantity: 2,
powerPerUnit: 0.1,
simultaneityFactor: 0.8,
},
],
});
assert.equal(callbackReturnedPromise, false);
assert.equal(insertedValues.length, 3);
assert.equal(insertedValues[0].id, created.circuitId);
assert.equal(insertedValues[0].isReserve, 0);
assert.equal(insertedValues[1].id, created.rowIds[0]);
assert.equal(insertedValues[1].circuitId, created.circuitId);
assert.equal(insertedValues[1].sortOrder, 15);
assert.equal(insertedValues[2].id, created.rowIds[1]);
assert.equal(insertedValues[2].circuitId, created.circuitId);
assert.equal(insertedValues[2].sortOrder, 20);
} finally {
(db as unknown as { transaction: unknown }).transaction = originalTransaction;
}
});
it("tracks local edits on linked device rows as overridden fields", async () => {
let savedOverrides: string | undefined;
const current = {