forked from jappel/leistungsbilanz-ts
Protect referenced feeder structures from deletion
This commit is contained in:
parent
b893c72534
commit
855a04e655
13 changed files with 369 additions and 0 deletions
|
|
@ -14,6 +14,7 @@ import { circuitDeviceRows } from "../src/db/schema/circuit-device-rows.js";
|
|||
import { circuitProtectionDevices } from "../src/db/schema/circuit-protection-devices.js";
|
||||
import { circuitSections } from "../src/db/schema/circuit-sections.js";
|
||||
import { circuits } from "../src/db/schema/circuits.js";
|
||||
import { distributionBoards } from "../src/db/schema/distribution-boards.js";
|
||||
import { projectRevisions } from "../src/db/schema/project-revisions.js";
|
||||
import { projects } from "../src/db/schema/projects.js";
|
||||
import {
|
||||
|
|
@ -611,6 +612,62 @@ describe("circuit device-row move project-command repository", () => {
|
|||
}
|
||||
});
|
||||
|
||||
it("blocks undo from removing a generated circuit used as a feeder", () => {
|
||||
const fixture = createTestDatabase();
|
||||
try {
|
||||
const store = new CircuitDeviceRowMoveProjectCommandRepository(
|
||||
fixture.context.db
|
||||
);
|
||||
const targetCircuit = createMoveTargetSnapshot(fixture);
|
||||
const command = createCircuitDeviceRowMoveWithNewCircuitProjectCommand(
|
||||
"create",
|
||||
targetCircuit,
|
||||
[{
|
||||
rowId: "row-1",
|
||||
expectedCircuitId: "circuit-1",
|
||||
expectedSortOrder: 10,
|
||||
targetCircuitId: targetCircuit.id,
|
||||
targetSortOrder: 10,
|
||||
}]
|
||||
);
|
||||
const moved = store.execute({
|
||||
projectId: "project-1",
|
||||
expectedRevision: 0,
|
||||
source: "user",
|
||||
command,
|
||||
});
|
||||
const downstream = new DistributionBoardFixtureRepository(
|
||||
fixture.context.db
|
||||
).createWithCircuitListAndDefaultSections("project-1", "UV-02");
|
||||
fixture.context.db
|
||||
.update(distributionBoards)
|
||||
.set({
|
||||
feedingDistributionBoardId: fixture.circuitListId,
|
||||
feedingCircuitId: targetCircuit.id,
|
||||
})
|
||||
.where(eq(distributionBoards.id, downstream.id))
|
||||
.run();
|
||||
|
||||
assert.throws(
|
||||
() => store.execute({
|
||||
projectId: "project-1",
|
||||
expectedRevision: 1,
|
||||
source: "undo",
|
||||
historyTargetChangeSetId: moved.revision.changeSetId,
|
||||
command: moved.inverse,
|
||||
}),
|
||||
/Speist UV-02 – erst Einspeisung ändern\./
|
||||
);
|
||||
assert.equal(getRow(fixture.context, "row-1")?.circuitId, targetCircuit.id);
|
||||
assert.ok(
|
||||
fixture.context.db.select().from(circuits)
|
||||
.where(eq(circuits.id, targetCircuit.id)).get()
|
||||
);
|
||||
} finally {
|
||||
fixture.context.close();
|
||||
}
|
||||
});
|
||||
|
||||
it("rejects deletion when the created target changed outside history", () => {
|
||||
const fixture = createTestDatabase();
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import { circuitSections } from "../src/db/schema/circuit-sections.js";
|
|||
import { circuits } from "../src/db/schema/circuits.js";
|
||||
import { distributionBoardComponentProtectionDevices } from "../src/db/schema/distribution-board-component-protection-devices.js";
|
||||
import { distributionBoardComponents } from "../src/db/schema/distribution-board-components.js";
|
||||
import { distributionBoards } from "../src/db/schema/distribution-boards.js";
|
||||
import { projectRevisions } from "../src/db/schema/project-revisions.js";
|
||||
import { projects } from "../src/db/schema/projects.js";
|
||||
import { createCircuitGroupDeleteSubtreeProjectCommand } from "../src/domain/models/circuit-group-subtree-project-command.model.js";
|
||||
|
|
@ -228,6 +229,45 @@ describe("circuit-group subtree project command", () => {
|
|||
}
|
||||
});
|
||||
|
||||
it("blocks deleting a group containing a feeding circuit", () => {
|
||||
const context = createTestDatabase();
|
||||
try {
|
||||
const list = context.db.select().from(circuitLists).get()!;
|
||||
const downstream = new DistributionBoardFixtureRepository(
|
||||
context.db
|
||||
).createWithCircuitListAndDefaultSections("project-1", "Unterverteiler");
|
||||
context.db
|
||||
.update(distributionBoards)
|
||||
.set({
|
||||
feedingDistributionBoardId: list.distributionBoardId,
|
||||
feedingCircuitId: "subtree-circuit",
|
||||
})
|
||||
.where(eq(distributionBoards.id, downstream.id))
|
||||
.run();
|
||||
const repository =
|
||||
new CircuitGroupSubtreeProjectCommandRepository(context.db);
|
||||
const groupId = context.db
|
||||
.select()
|
||||
.from(circuits)
|
||||
.where(eq(circuits.id, "subtree-circuit"))
|
||||
.get()!.sectionId;
|
||||
const snapshot = repository.capture("project-1", groupId);
|
||||
|
||||
assert.throws(
|
||||
() => repository.execute({
|
||||
projectId: "project-1",
|
||||
expectedRevision: 0,
|
||||
source: "user",
|
||||
command: createCircuitGroupDeleteSubtreeProjectCommand(snapshot),
|
||||
}),
|
||||
/Speist Unterverteiler – erst Einspeisung ändern\./
|
||||
);
|
||||
assert.deepEqual(repository.capture("project-1", groupId), snapshot);
|
||||
} finally {
|
||||
context.close();
|
||||
}
|
||||
});
|
||||
|
||||
it("rolls back a cascaded deletion when history persistence fails", () => {
|
||||
const context = createTestDatabase();
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import { circuitDeviceRows } from "../src/db/schema/circuit-device-rows.js";
|
|||
import { circuitProtectionDevices } from "../src/db/schema/circuit-protection-devices.js";
|
||||
import { circuitSections } from "../src/db/schema/circuit-sections.js";
|
||||
import { circuits } from "../src/db/schema/circuits.js";
|
||||
import { distributionBoards } from "../src/db/schema/distribution-boards.js";
|
||||
import { projectDevices } from "../src/db/schema/project-devices.js";
|
||||
import { projectRevisions } from "../src/db/schema/project-revisions.js";
|
||||
import { projects } from "../src/db/schema/projects.js";
|
||||
|
|
@ -442,6 +443,48 @@ describe("circuit structure project-command repository", () => {
|
|||
}
|
||||
});
|
||||
|
||||
it("blocks deleting a circuit that feeds a distribution board", () => {
|
||||
const fixture = createTestDatabase();
|
||||
try {
|
||||
const downstream = new DistributionBoardFixtureRepository(
|
||||
fixture.context.db
|
||||
).createWithCircuitListAndDefaultSections("project-1", "UV Unterverteiler");
|
||||
fixture.context.db
|
||||
.update(distributionBoards)
|
||||
.set({
|
||||
boardNumber: "UV-02",
|
||||
feedingDistributionBoardId: fixture.circuitListId,
|
||||
feedingCircuitId: "circuit-existing",
|
||||
})
|
||||
.where(eq(distributionBoards.id, downstream.id))
|
||||
.run();
|
||||
|
||||
const store = new CircuitStructureProjectCommandRepository(
|
||||
fixture.context.db
|
||||
);
|
||||
assert.throws(
|
||||
() =>
|
||||
store.execute({
|
||||
projectId: "project-1",
|
||||
expectedRevision: 0,
|
||||
source: "user",
|
||||
command: createCircuitDeleteProjectCommand(
|
||||
"circuit-existing",
|
||||
fixture.circuitListId
|
||||
),
|
||||
}),
|
||||
/Speist UV-02 – erst Einspeisung ändern\./
|
||||
);
|
||||
assert.ok(getCircuit(fixture.context, "circuit-existing"));
|
||||
assert.equal(
|
||||
fixture.context.db.select().from(projectRevisions).all().length,
|
||||
0
|
||||
);
|
||||
} finally {
|
||||
fixture.context.close();
|
||||
}
|
||||
});
|
||||
|
||||
it("rolls back insert and delete for late history failures", () => {
|
||||
const fixture = createTestDatabase();
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -219,6 +219,54 @@ describe("distribution-board structure project command", () => {
|
|||
}
|
||||
});
|
||||
|
||||
it("blocks undo from removing an empty board used as a feeder", () => {
|
||||
const context = createTestDatabase();
|
||||
try {
|
||||
const repository =
|
||||
new DistributionBoardStructureProjectCommandRepository(context.db);
|
||||
const upstream = createDistributionBoardStructureSnapshot(
|
||||
"project-1",
|
||||
"NSHV"
|
||||
);
|
||||
const inserted = repository.execute({
|
||||
projectId: "project-1",
|
||||
expectedRevision: 0,
|
||||
source: "user",
|
||||
command: createDistributionBoardInsertProjectCommand(upstream),
|
||||
});
|
||||
const downstream = createDistributionBoardStructureSnapshot(
|
||||
"project-1",
|
||||
"UV-01"
|
||||
);
|
||||
context.db.insert(distributionBoards).values({
|
||||
...downstream.distributionBoard,
|
||||
boardNumber: "UV-01",
|
||||
feedingDistributionBoardId: upstream.distributionBoard.id,
|
||||
}).run();
|
||||
context.db.insert(circuitLists).values(downstream.circuitList).run();
|
||||
context.db.insert(circuitSections).values(downstream.sections).run();
|
||||
context.db.insert(distributionBoardComponents)
|
||||
.values(downstream.components).run();
|
||||
|
||||
assert.throws(
|
||||
() => repository.execute({
|
||||
projectId: "project-1",
|
||||
expectedRevision: 1,
|
||||
source: "undo",
|
||||
historyTargetChangeSetId: inserted.revision.changeSetId,
|
||||
command: inserted.inverse,
|
||||
}),
|
||||
/Speist UV-01 – erst Einspeisung ändern\./
|
||||
);
|
||||
assert.ok(
|
||||
context.db.select().from(distributionBoards)
|
||||
.where(eq(distributionBoards.id, upstream.distributionBoard.id)).get()
|
||||
);
|
||||
} finally {
|
||||
context.close();
|
||||
}
|
||||
});
|
||||
|
||||
it("allows only supply types enabled in the project", () => {
|
||||
const context = createTestDatabase();
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -291,6 +291,43 @@ describe("distribution-board subtree project command", () => {
|
|||
}
|
||||
});
|
||||
|
||||
it("blocks deleting a board subtree that feeds another board", () => {
|
||||
const context = createTestDatabase();
|
||||
try {
|
||||
const upstream = createPopulatedBoard(context);
|
||||
const downstream = new DistributionBoardFixtureRepository(
|
||||
context.db
|
||||
).createWithCircuitListAndDefaultSections("project-1", "Unterverteiler");
|
||||
context.db
|
||||
.update(distributionBoards)
|
||||
.set({
|
||||
boardNumber: "UV-03",
|
||||
feedingDistributionBoardId: upstream.id,
|
||||
})
|
||||
.where(eq(distributionBoards.id, downstream.id))
|
||||
.run();
|
||||
const repository =
|
||||
new DistributionBoardSubtreeProjectCommandRepository(context.db);
|
||||
const snapshot = repository.capture("project-1", upstream.id);
|
||||
|
||||
assert.throws(
|
||||
() =>
|
||||
repository.execute({
|
||||
projectId: "project-1",
|
||||
expectedRevision: 0,
|
||||
source: "user",
|
||||
command:
|
||||
createDistributionBoardDeleteSubtreeProjectCommand(snapshot),
|
||||
}),
|
||||
/Speist UV-03 – erst Einspeisung ändern\./
|
||||
);
|
||||
assert.deepEqual(repository.capture("project-1", upstream.id), snapshot);
|
||||
assert.equal(context.db.select().from(projectRevisions).all().length, 0);
|
||||
} finally {
|
||||
context.close();
|
||||
}
|
||||
});
|
||||
|
||||
it("rolls back a cascaded deletion when history persistence fails", () => {
|
||||
const context = createTestDatabase();
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -533,6 +533,61 @@ describe("external object new-circuit project command", () => {
|
|||
}
|
||||
});
|
||||
|
||||
it("blocks history removal when the generated circuit became a feeder", () => {
|
||||
const context = createFixture(null);
|
||||
try {
|
||||
const object = objectSnapshot(null);
|
||||
const circuit = newExternalCircuit(context.db);
|
||||
const command = createExternalObjectNewCircuitProjectCommand(
|
||||
externalObjectAssignToNewCircuitCommandType,
|
||||
{
|
||||
circuit,
|
||||
objects: [{
|
||||
expected: object,
|
||||
target: { ...object, circuitDeviceRowId: "row-new" },
|
||||
}],
|
||||
confirmedConflictObjectIds: [],
|
||||
}
|
||||
);
|
||||
const repository = new ExternalObjectNewCircuitProjectCommandRepository(context.db);
|
||||
const inserted = repository.execute({
|
||||
projectId: "project-1",
|
||||
expectedRevision: 0,
|
||||
source: "user",
|
||||
command,
|
||||
});
|
||||
context.db.insert(distributionBoards).values({
|
||||
id: "board-2",
|
||||
projectId: "project-1",
|
||||
name: "Unterverteiler",
|
||||
boardNumber: "UV-02",
|
||||
feedingDistributionBoardId: "board-1",
|
||||
feedingCircuitId: circuit.id,
|
||||
}).run();
|
||||
|
||||
assert.throws(
|
||||
() => repository.execute({
|
||||
projectId: "project-1",
|
||||
expectedRevision: 1,
|
||||
source: "undo",
|
||||
historyTargetChangeSetId: inserted.revision.changeSetId,
|
||||
command: inserted.inverse,
|
||||
}),
|
||||
/Speist UV-02 – erst Einspeisung ändern\./
|
||||
);
|
||||
assert.ok(
|
||||
context.db.select().from(circuits)
|
||||
.where(eq(circuits.id, circuit.id)).get()
|
||||
);
|
||||
assert.equal(
|
||||
context.db.select().from(externalModelObjects).get()!.circuitDeviceRowId,
|
||||
"row-new"
|
||||
);
|
||||
} finally {
|
||||
context.close();
|
||||
}
|
||||
});
|
||||
|
||||
it("rolls back the complete circuit subtree when history persistence fails", () => {
|
||||
const context = createFixture(null);
|
||||
try {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue