Isolate circuit section transactions

This commit is contained in:
2026-07-23 19:02:38 +02:00
parent 91baa6c76c
commit 9be94028b3
9 changed files with 377 additions and 214 deletions
+15 -123
View File
@@ -1,9 +1,6 @@
import assert from "node:assert/strict";
import { describe, it } from "node:test";
import { CircuitWriteService } from "../src/domain/services/circuit-write.service.js";
import { CircuitDeviceRowRepository } from "../src/db/repositories/circuit-device-row.repository.js";
import { CircuitRepository } from "../src/db/repositories/circuit.repository.js";
import { db } from "../src/db/client.js";
import { createCircuitSchema } from "../src/shared/validation/circuit.schemas.js";
describe("circuit write service rules", () => {
@@ -320,7 +317,9 @@ describe("circuit write service rules", () => {
async listByCircuitList() {
return [{ id: "x1", sectionId: "s2", equipmentIdentifier: "-3F1" }] as never[];
},
async updateEquipmentIdentifiersSafely(_listId: string, updates: Array<{ id: string; equipmentIdentifier: string }>) {
} as never,
circuitSectionTransactionStore: {
updateEquipmentIdentifiers(_listId: string, updates: Array<{ id: string; equipmentIdentifier: string }>) {
safeUpdatePayload = updates;
},
} as never,
@@ -353,7 +352,9 @@ describe("circuit write service rules", () => {
async listByCircuitList() {
return [{ id: "o1", sectionId: "s2", equipmentIdentifier: "-1F2" }] as never[];
},
async updateEquipmentIdentifiersSafely(_listId: string, updates: Array<{ id: string; equipmentIdentifier: string }>) {
} as never,
circuitSectionTransactionStore: {
updateEquipmentIdentifiers(_listId: string, updates: Array<{ id: string; equipmentIdentifier: string }>) {
safeUpdatePayload = updates;
},
} as never,
@@ -386,7 +387,9 @@ describe("circuit write service rules", () => {
async listByCircuitList() {
return [] as never[];
},
async updateEquipmentIdentifiersSafely() {
} as never,
circuitSectionTransactionStore: {
updateEquipmentIdentifiers() {
safeCalled += 1;
},
} as never,
@@ -692,7 +695,9 @@ describe("circuit write service rules", () => {
{ id: "c3", sectionId: "s1", equipmentIdentifier: "-2F5", sortOrder: 30, isReserve: 1 },
] as never[];
},
updateSortOrdersSafely(sectionId: string, circuitIds: string[]) {
} as never,
circuitSectionTransactionStore: {
updateSortOrders(sectionId: string, circuitIds: string[]) {
safeReorder = { sectionId, circuitIds };
},
} as never,
@@ -720,7 +725,9 @@ describe("circuit write service rules", () => {
{ id: "c2", sectionId: "s1", equipmentIdentifier: "-1F2", sortOrder: 20, isReserve: 0 },
] as never[];
},
async updateEquipmentIdentifiersSafely() {
} as never,
circuitSectionTransactionStore: {
updateEquipmentIdentifiers() {
safeCalled = true;
},
} as never,
@@ -734,121 +741,6 @@ describe("circuit write service rules", () => {
assert.equal(safeCalled, true);
});
it("safe identifier bulk update uses synchronous transaction callback", async () => {
const repository = new CircuitRepository();
const originalTransaction = (db as unknown as { transaction: unknown }).transaction;
let callbackReturnedPromise = false;
(db as unknown as { transaction: (cb: (tx: unknown) => unknown) => void }).transaction = (cb) => {
const fakeTx = {
select() {
return {
from() {
return {
where() {
return {
all() {
return [{ id: "c1" }, { id: "c2" }];
},
};
},
};
},
};
},
update() {
return {
set() {
return {
where() {
return {
run() {
return;
},
};
},
};
},
};
},
};
const result = cb(fakeTx);
callbackReturnedPromise = Boolean(result && typeof (result as Promise<unknown>).then === "function");
if (callbackReturnedPromise) {
throw new Error("Transaction function cannot return a promise");
}
};
try {
await repository.updateEquipmentIdentifiersSafely(
"l1",
[
{ id: "c1", equipmentIdentifier: "-1F1" },
{ id: "c2", equipmentIdentifier: "-1F2" },
],
"s1"
);
assert.equal(callbackReturnedPromise, false);
} finally {
(db as unknown as { transaction: unknown }).transaction = originalTransaction;
}
});
it("safe circuit reorder uses one synchronous transaction callback", () => {
const repository = new CircuitRepository();
const originalTransaction = (db as unknown as { transaction: unknown }).transaction;
let callbackReturnedPromise = false;
const sortOrders: number[] = [];
(db as unknown as { transaction: (cb: (tx: unknown) => unknown) => void }).transaction = (cb) => {
const fakeTx = {
select() {
return {
from() {
return {
where() {
return {
all() {
return [{ id: "c1" }, { id: "c2" }, { id: "c3" }];
},
};
},
};
},
};
},
update() {
return {
set(values: { sortOrder: number }) {
sortOrders.push(values.sortOrder);
return {
where() {
return {
run() {
return { changes: 1 };
},
};
},
};
},
};
},
};
const callbackResult = cb(fakeTx);
callbackReturnedPromise = Boolean(
callbackResult && typeof (callbackResult as Promise<unknown>).then === "function"
);
};
try {
repository.updateSortOrdersSafely("s1", ["c3", "c1", "c2"]);
assert.equal(callbackReturnedPromise, false);
assert.deepEqual(sortOrders, [10, 20, 30]);
} finally {
(db as unknown as { transaction: unknown }).transaction = originalTransaction;
}
});
it("tracks local edits on linked device rows as overridden fields", async () => {
let updatePayload: Record<string, unknown> = {};
const current = {