forked from jappel/leistungsbilanz-ts
tsx runs tests without type checking and tsconfig.json covers only src, so 71 type errors had accumulated unnoticed. tsconfig.tests.json now checks every test and helper, and npm run typecheck includes it. Fixtures gain the fields the DTOs have grown since they were written (manualQuantity, circuit apparent power/cos phi, downstream board loads, section totals, building, cover and board info fields). Repository lookups that return "row ?? null" now declare the null in their type, which their callers already handle. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
241 lines
7.6 KiB
TypeScript
241 lines
7.6 KiB
TypeScript
import path from "node:path";
|
|
import assert from "node:assert/strict";
|
|
import { describe, it } from "node:test";
|
|
import { eq } from "drizzle-orm";
|
|
import { migrate } from "drizzle-orm/better-sqlite3/migrator";
|
|
import {
|
|
createDatabaseContext,
|
|
type DatabaseContext,
|
|
} from "../src/db/database-context.js";
|
|
import { DistributionBoardStructureProjectCommandRepository } from "../src/db/repositories/distribution-board-structure-project-command.repository.js";
|
|
import { ProjectHistoryRepository } from "../src/db/repositories/project-history.repository.js";
|
|
import { circuits } from "../src/db/schema/circuits.js";
|
|
import { distributionBoards } from "../src/db/schema/distribution-boards.js";
|
|
import { projects } from "../src/db/schema/projects.js";
|
|
import {
|
|
assertDistributionBoardFeederCircuitProjectCommand,
|
|
createDistributionBoardCreateFeederCircuitProjectCommand,
|
|
} from "../src/domain/models/distribution-board-feeder-circuit-project-command.model.js";
|
|
import { createDistributionBoardUpdateProjectCommand } from "../src/domain/models/distribution-board-project-command.model.js";
|
|
import {
|
|
createDistributionBoardInsertProjectCommand,
|
|
createDistributionBoardStructureSnapshot,
|
|
} from "../src/domain/models/distribution-board-structure-project-command.model.js";
|
|
import { buildCircuitInsertSnapshot } from "../src/frontend/utils/circuit-structure-command.js";
|
|
|
|
function createTestDatabase(): DatabaseContext {
|
|
const context = createDatabaseContext(":memory:");
|
|
migrate(context.db, {
|
|
migrationsFolder: path.resolve("src", "db", "migrations"),
|
|
});
|
|
context.db
|
|
.insert(projects)
|
|
.values({ id: "project-1", name: "Test project" })
|
|
.run();
|
|
return context;
|
|
}
|
|
|
|
function setupBoards(
|
|
context: DatabaseContext,
|
|
repository: DistributionBoardStructureProjectCommandRepository
|
|
) {
|
|
const source = createDistributionBoardStructureSnapshot(
|
|
"project-1",
|
|
"NSHV"
|
|
);
|
|
const downstream = createDistributionBoardStructureSnapshot(
|
|
"project-1",
|
|
"UV EG"
|
|
);
|
|
repository.execute({
|
|
projectId: "project-1",
|
|
expectedRevision: 0,
|
|
source: "user",
|
|
command: createDistributionBoardInsertProjectCommand(source),
|
|
});
|
|
repository.execute({
|
|
projectId: "project-1",
|
|
expectedRevision: 1,
|
|
source: "user",
|
|
command: createDistributionBoardInsertProjectCommand(downstream),
|
|
});
|
|
const section = source.sections.find(
|
|
(candidate) => candidate.key === "three_phase"
|
|
)!;
|
|
const circuit = {
|
|
...buildCircuitInsertSnapshot({
|
|
id: "feeder-circuit",
|
|
circuitListId: source.circuitList.id,
|
|
category: "three_phase",
|
|
deviceRows: [],
|
|
values: {
|
|
sectionId: section.id,
|
|
equipmentIdentifier: `${section.prefix}1`,
|
|
displayName: undefined,
|
|
sortOrder: 10,
|
|
voltage: 400,
|
|
isReserve: true,
|
|
},
|
|
}),
|
|
protectionDevice: null,
|
|
};
|
|
const boardUpdate = createDistributionBoardUpdateProjectCommand(
|
|
downstream.distributionBoard.id,
|
|
{
|
|
feedingDistributionBoardId: source.distributionBoard.id,
|
|
feedingCircuitId: circuit.id,
|
|
}
|
|
);
|
|
const command =
|
|
createDistributionBoardCreateFeederCircuitProjectCommand(
|
|
circuit,
|
|
boardUpdate
|
|
);
|
|
return { source, downstream, circuit, command };
|
|
}
|
|
|
|
describe("distribution-board feeder-circuit project command", () => {
|
|
it("creates and links one feeder atomically and supports undo and redo", () => {
|
|
const context = createTestDatabase();
|
|
try {
|
|
const repository =
|
|
new DistributionBoardStructureProjectCommandRepository(context.db);
|
|
const history = new ProjectHistoryRepository(context.db);
|
|
const { source, downstream, circuit, command } = setupBoards(
|
|
context,
|
|
repository
|
|
);
|
|
|
|
repository.executeFeederCircuit({
|
|
projectId: "project-1",
|
|
expectedRevision: 2,
|
|
source: "user",
|
|
command,
|
|
});
|
|
assert.ok(
|
|
context.db
|
|
.select()
|
|
.from(circuits)
|
|
.where(eq(circuits.id, circuit.id))
|
|
.get()
|
|
);
|
|
assert.deepEqual(
|
|
context.db
|
|
.select({
|
|
boardId: distributionBoards.feedingDistributionBoardId,
|
|
circuitId: distributionBoards.feedingCircuitId,
|
|
})
|
|
.from(distributionBoards)
|
|
.where(eq(distributionBoards.id, downstream.distributionBoard.id))
|
|
.get(),
|
|
{
|
|
boardId: source.distributionBoard.id,
|
|
circuitId: circuit.id,
|
|
}
|
|
);
|
|
|
|
const undo = history.getNextCommand("project-1", "undo");
|
|
assert.ok(undo);
|
|
assertDistributionBoardFeederCircuitProjectCommand(undo.command);
|
|
repository.executeFeederCircuit({
|
|
projectId: "project-1",
|
|
expectedRevision: 3,
|
|
source: "undo",
|
|
historyTargetChangeSetId: undo.changeSetId,
|
|
command: undo.command,
|
|
});
|
|
assert.equal(
|
|
context.db
|
|
.select()
|
|
.from(circuits)
|
|
.where(eq(circuits.id, circuit.id))
|
|
.get(),
|
|
undefined
|
|
);
|
|
assert.deepEqual(
|
|
context.db
|
|
.select({
|
|
boardId: distributionBoards.feedingDistributionBoardId,
|
|
circuitId: distributionBoards.feedingCircuitId,
|
|
})
|
|
.from(distributionBoards)
|
|
.where(eq(distributionBoards.id, downstream.distributionBoard.id))
|
|
.get(),
|
|
{ boardId: null, circuitId: null }
|
|
);
|
|
|
|
const redo = history.getNextCommand("project-1", "redo");
|
|
assert.ok(redo);
|
|
assertDistributionBoardFeederCircuitProjectCommand(redo.command);
|
|
repository.executeFeederCircuit({
|
|
projectId: "project-1",
|
|
expectedRevision: 4,
|
|
source: "redo",
|
|
historyTargetChangeSetId: redo.changeSetId,
|
|
command: redo.command,
|
|
});
|
|
assert.equal(
|
|
context.db
|
|
.select({ equipmentIdentifier: circuits.equipmentIdentifier })
|
|
.from(circuits)
|
|
.where(eq(circuits.id, circuit.id))
|
|
.get()?.equipmentIdentifier,
|
|
circuit.equipmentIdentifier
|
|
);
|
|
} finally {
|
|
context.close();
|
|
}
|
|
});
|
|
|
|
it("rolls back unlinking when the generated circuit changed before undo", () => {
|
|
const context = createTestDatabase();
|
|
try {
|
|
const repository =
|
|
new DistributionBoardStructureProjectCommandRepository(context.db);
|
|
const history = new ProjectHistoryRepository(context.db);
|
|
const { downstream, circuit, command } = setupBoards(context, repository);
|
|
repository.executeFeederCircuit({
|
|
projectId: "project-1",
|
|
expectedRevision: 2,
|
|
source: "user",
|
|
command,
|
|
});
|
|
context.db
|
|
.update(circuits)
|
|
.set({ displayName: "Außerhalb der Historie geändert" })
|
|
.where(eq(circuits.id, circuit.id))
|
|
.run();
|
|
|
|
const undo = history.getNextCommand("project-1", "undo");
|
|
assert.ok(undo);
|
|
const undoCommand = undo.command;
|
|
assertDistributionBoardFeederCircuitProjectCommand(undoCommand);
|
|
assert.throws(
|
|
() =>
|
|
repository.executeFeederCircuit({
|
|
projectId: "project-1",
|
|
expectedRevision: 3,
|
|
source: "undo",
|
|
historyTargetChangeSetId: undo.changeSetId,
|
|
command: undoCommand,
|
|
}),
|
|
/changed before history removal/
|
|
);
|
|
const board = context.db
|
|
.select()
|
|
.from(distributionBoards)
|
|
.where(eq(distributionBoards.id, downstream.distributionBoard.id))
|
|
.get();
|
|
assert.equal(board?.feedingCircuitId, circuit.id);
|
|
assert.ok(
|
|
context.db
|
|
.select()
|
|
.from(circuits)
|
|
.where(eq(circuits.id, circuit.id))
|
|
.get()
|
|
);
|
|
} finally {
|
|
context.close();
|
|
}
|
|
});
|
|
});
|