Make single row moves atomic

This commit is contained in:
2026-07-23 17:25:28 +02:00
parent 9cc3b7c08c
commit eb945a9622
3 changed files with 94 additions and 110 deletions
+72 -50
View File
@@ -299,24 +299,24 @@ describe("circuit write service rules", () => {
});
it("moving a device row to another circuit preserves row and toggles reserve flags", async () => {
const updatedReserve: Array<{ id: string; isReserve: boolean }> = [];
const movedCalls: Array<{ rowId: string; targetCircuitId: string; sortOrder: number }> = [];
let transactionalMove:
| {
rows: Array<{ id: string; expectedCircuitId: string }>;
targetCircuitId?: string;
createTargetCircuit?: unknown;
}
| undefined;
const service = new CircuitWriteService({
deviceRowRepository: {
async findById() {
return { id: "r1", circuitId: "c1" } as never;
},
async countByCircuit(circuitId: string) {
if (circuitId === "c2") {
return 2;
}
if (circuitId === "c1") {
return 0;
}
return 0;
},
async moveToCircuit(rowId: string, targetCircuitId: string, sortOrder: number) {
movedCalls.push({ rowId, targetCircuitId, sortOrder });
moveRowsTransactional(input: typeof transactionalMove) {
transactionalMove = input;
return {
movedRowIds: input!.rows.map((row) => row.id),
targetCircuitId: input!.targetCircuitId!,
};
},
} as never,
circuitRepository: {
@@ -340,35 +340,39 @@ describe("circuit write service rules", () => {
isReserve: 1,
} as never;
},
async update(id: string, payload: { isReserve: boolean }) {
updatedReserve.push({ id, isReserve: payload.isReserve });
},
} as never,
});
await service.moveDeviceRow("r1", { targetCircuitId: "c2" });
assert.deepEqual(movedCalls, [{ rowId: "r1", targetCircuitId: "c2", sortOrder: 30 }]);
assert.deepEqual(updatedReserve, [
{ id: "c1", isReserve: true },
{ id: "c2", isReserve: false },
]);
assert.deepEqual(transactionalMove, {
rows: [{ id: "r1", expectedCircuitId: "c1" }],
targetCircuitId: "c2",
createTargetCircuit: undefined,
});
});
it("moving a device row to placeholder creates a new circuit in target section", async () => {
let createdCircuitPayload: { sectionId: string; equipmentIdentifier: string; isReserve?: boolean } | null = null;
let preparedTarget:
| {
circuitListId: string;
sectionId: string;
equipmentIdentifier: string;
displayName: string;
sortOrder: number;
}
| undefined;
const service = new CircuitWriteService({
deviceRowRepository: {
async findById() {
return { id: "r1", circuitId: "c1" } as never;
},
async countByCircuit(circuitId: string) {
if (circuitId === "c-new") {
return 0;
}
return 1;
},
async moveToCircuit() {
return;
moveRowsTransactional(input: { rows: Array<{ id: string }>; createTargetCircuit?: typeof preparedTarget }) {
preparedTarget = input.createTargetCircuit;
return {
movedRowIds: input.rows.map((row) => row.id),
targetCircuitId: "c-new",
createdCircuitId: "c-new",
};
},
} as never,
circuitRepository: {
@@ -383,28 +387,11 @@ describe("circuit write service rules", () => {
isReserve: 0,
} as never;
}
if (circuitId === "c-new") {
return {
id: "c-new",
sectionId: "s2",
circuitListId: "l1",
equipmentIdentifier: "-2F8",
sortOrder: 50,
isReserve: 0,
} as never;
}
return null as never;
},
async listBySection() {
return [{ sortOrder: 40 }] as never[];
},
async create(payload: { sectionId: string; equipmentIdentifier: string; isReserve?: boolean }) {
createdCircuitPayload = payload;
return "c-new";
},
async update() {
return;
},
} as never,
circuitSectionRepository: {
async findById() {
@@ -419,9 +406,44 @@ describe("circuit write service rules", () => {
});
await service.moveDeviceRow("r1", { targetSectionId: "s2", createNewCircuit: true });
assert.equal(createdCircuitPayload?.sectionId, "s2");
assert.equal(createdCircuitPayload?.equipmentIdentifier, "-2F8");
assert.equal(createdCircuitPayload?.isReserve, false);
assert.deepEqual(preparedTarget, {
circuitListId: "l1",
sectionId: "s2",
equipmentIdentifier: "-2F8",
displayName: "Neuer Stromkreis",
sortOrder: 50,
});
});
it("moving a device row to its current circuit remains a no-op", async () => {
let transactionalMoveCount = 0;
const service = new CircuitWriteService({
deviceRowRepository: {
async findById() {
return { id: "r1", circuitId: "c1" } as never;
},
moveRowsTransactional() {
transactionalMoveCount += 1;
throw new Error("same-circuit move must not write");
},
} as never,
circuitRepository: {
async findById() {
return {
id: "c1",
sectionId: "s1",
circuitListId: "l1",
equipmentIdentifier: "-1F1",
sortOrder: 10,
isReserve: 0,
} as never;
},
} as never,
});
const result = await service.moveDeviceRow("r1", { targetCircuitId: "c1" });
assert.equal(transactionalMoveCount, 0);
assert.equal(result?.id, "r1");
});
it("moving multiple device rows to a circuit preserves input order and toggles reserve", async () => {