Undo Redo working
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { describe, it } from "node:test";
|
||||
import { CircuitWriteService } from "../src/domain/services/circuit-write.service.js";
|
||||
import { CircuitRepository } from "../src/db/repositories/circuit.repository.js";
|
||||
import { db } from "../src/db/client.js";
|
||||
|
||||
describe("circuit write service rules", () => {
|
||||
it("rejects duplicate equipment identifiers in same circuit list", async () => {
|
||||
@@ -146,8 +148,8 @@ describe("circuit write service rules", () => {
|
||||
assert.equal(reserveFlag, false);
|
||||
});
|
||||
|
||||
it("renumber affects only circuits in selected section and keeps row order untouched", async () => {
|
||||
const updatedIds: string[] = [];
|
||||
it("renumber uses safe bulk identifier update for swapped identifiers", async () => {
|
||||
let safeUpdatePayload: Array<{ id: string; equipmentIdentifier: string }> = [];
|
||||
const service = new CircuitWriteService({
|
||||
circuitSectionRepository: {
|
||||
async findById() {
|
||||
@@ -157,24 +159,94 @@ describe("circuit write service rules", () => {
|
||||
circuitRepository: {
|
||||
async listBySection() {
|
||||
return [
|
||||
{ id: "c1", sectionId: "s1", equipmentIdentifier: "-2F7", sortOrder: 10, isReserve: 0 },
|
||||
{ id: "c2", sectionId: "s1", equipmentIdentifier: "-2F9", sortOrder: 20, isReserve: 1 },
|
||||
{ id: "cB", sectionId: "s1", equipmentIdentifier: "-2F2", sortOrder: 10, isReserve: 0 },
|
||||
{ id: "cA", sectionId: "s1", equipmentIdentifier: "-2F1", sortOrder: 20, isReserve: 1 },
|
||||
] as never[];
|
||||
},
|
||||
async listByCircuitList() {
|
||||
return [{ id: "x1", sectionId: "s2", equipmentIdentifier: "-3F1" }] as never[];
|
||||
},
|
||||
async update(circuitId: string) {
|
||||
updatedIds.push(circuitId);
|
||||
async updateEquipmentIdentifiersSafely(_listId: string, updates: Array<{ id: string; equipmentIdentifier: string }>) {
|
||||
safeUpdatePayload = updates;
|
||||
},
|
||||
} as never,
|
||||
});
|
||||
|
||||
const result = await service.renumberSection("s1");
|
||||
assert.deepEqual(updatedIds, ["c1", "c2"]);
|
||||
assert.deepEqual(safeUpdatePayload, [
|
||||
{ id: "cB", equipmentIdentifier: "-2F1" },
|
||||
{ id: "cA", equipmentIdentifier: "-2F2" },
|
||||
]);
|
||||
assert.equal(result.length, 2);
|
||||
});
|
||||
|
||||
it("renumber shifts forward/backward and respects other sections", async () => {
|
||||
let safeUpdatePayload: Array<{ id: string; equipmentIdentifier: string }> = [];
|
||||
const service = new CircuitWriteService({
|
||||
circuitSectionRepository: {
|
||||
async findById() {
|
||||
return { id: "s1", circuitListId: "l1", prefix: "-1F" } as never;
|
||||
},
|
||||
} as never,
|
||||
circuitRepository: {
|
||||
async listBySection() {
|
||||
return [
|
||||
{ id: "c2", sectionId: "s1", equipmentIdentifier: "-1F5", sortOrder: 10, isReserve: 0 },
|
||||
{ id: "c1", sectionId: "s1", equipmentIdentifier: "-1F1", sortOrder: 20, isReserve: 0 },
|
||||
{ id: "c3", sectionId: "s1", equipmentIdentifier: "-1F9", sortOrder: 30, isReserve: 0 },
|
||||
] as never[];
|
||||
},
|
||||
async listByCircuitList() {
|
||||
return [{ id: "o1", sectionId: "s2", equipmentIdentifier: "-1F2" }] as never[];
|
||||
},
|
||||
async updateEquipmentIdentifiersSafely(_listId: string, updates: Array<{ id: string; equipmentIdentifier: string }>) {
|
||||
safeUpdatePayload = updates;
|
||||
},
|
||||
} as never,
|
||||
});
|
||||
|
||||
await service.renumberSection("s1");
|
||||
assert.deepEqual(safeUpdatePayload, [
|
||||
{ id: "c2", equipmentIdentifier: "-1F1" },
|
||||
{ id: "c1", equipmentIdentifier: "-1F3" },
|
||||
{ id: "c3", equipmentIdentifier: "-1F4" },
|
||||
]);
|
||||
});
|
||||
|
||||
it("renumber handles gaps and keeps device rows untouched by identifier-only update path", async () => {
|
||||
let safeCalled = 0;
|
||||
const service = new CircuitWriteService({
|
||||
circuitSectionRepository: {
|
||||
async findById() {
|
||||
return { id: "s1", circuitListId: "l1", prefix: "-1F" } as never;
|
||||
},
|
||||
} as never,
|
||||
circuitRepository: {
|
||||
async listBySection() {
|
||||
return [
|
||||
{ id: "c1", sectionId: "s1", equipmentIdentifier: "-1F1", sortOrder: 10, isReserve: 0 },
|
||||
{ id: "c2", sectionId: "s1", equipmentIdentifier: "-1F5", sortOrder: 20, isReserve: 0 },
|
||||
{ id: "c3", sectionId: "s1", equipmentIdentifier: "-1F9", sortOrder: 30, isReserve: 0 },
|
||||
] as never[];
|
||||
},
|
||||
async listByCircuitList() {
|
||||
return [] as never[];
|
||||
},
|
||||
async updateEquipmentIdentifiersSafely() {
|
||||
safeCalled += 1;
|
||||
},
|
||||
} as never,
|
||||
deviceRowRepository: {
|
||||
async update() {
|
||||
throw new Error("device rows must not be touched");
|
||||
},
|
||||
} as never,
|
||||
});
|
||||
|
||||
await service.renumberSection("s1");
|
||||
assert.equal(safeCalled, 1);
|
||||
});
|
||||
|
||||
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 }> = [];
|
||||
@@ -330,4 +402,93 @@ describe("circuit write service rules", () => {
|
||||
{ id: "c2", sortOrder: 30, equipmentIdentifier: "-2F9" },
|
||||
]);
|
||||
});
|
||||
|
||||
it("safe section identifier bulk update validates full section set (undo safety)", async () => {
|
||||
let safeCalled = false;
|
||||
const service = new CircuitWriteService({
|
||||
circuitSectionRepository: {
|
||||
async findById() {
|
||||
return { id: "s1", circuitListId: "l1", prefix: "-1F" } as never;
|
||||
},
|
||||
} as never,
|
||||
circuitRepository: {
|
||||
async listBySection() {
|
||||
return [
|
||||
{ id: "c1", sectionId: "s1", equipmentIdentifier: "-1F1", sortOrder: 10, isReserve: 0 },
|
||||
{ id: "c2", sectionId: "s1", equipmentIdentifier: "-1F2", sortOrder: 20, isReserve: 0 },
|
||||
] as never[];
|
||||
},
|
||||
async updateEquipmentIdentifiersSafely() {
|
||||
safeCalled = true;
|
||||
},
|
||||
} as never,
|
||||
});
|
||||
await service.updateSectionEquipmentIdentifiers("s1", {
|
||||
identifiers: [
|
||||
{ circuitId: "c1", equipmentIdentifier: "-1F2" },
|
||||
{ circuitId: "c2", equipmentIdentifier: "-1F1" },
|
||||
],
|
||||
});
|
||||
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;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user