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 { CircuitProtectionProjectCommandRepository } from "../src/db/repositories/circuit-protection-project-command.repository.js"; import { ProjectHistoryRepository } from "../src/db/repositories/project-history.repository.js"; import { circuitLists } from "../src/db/schema/circuit-lists.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 { projects } from "../src/db/schema/projects.js"; import { createCircuitProtectionUpdateProjectCommand, type CircuitProtectionSnapshot, } from "../src/domain/models/circuit-protection-project-command.model.js"; import { DistributionBoardFixtureRepository } from "./support/distribution-board-fixture.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: "Projekt" }, { id: "project-2", name: "Fremdprojekt" }, ]) .run(); new DistributionBoardFixtureRepository( context.db ).createWithCircuitListAndDefaultSections("project-1", "UV-01"); const list = context.db.select().from(circuitLists).get(); assert.ok(list); const section = context.db .select() .from(circuitSections) .where(eq(circuitSections.circuitListId, list.id)) .get(); assert.ok(section); context.db .insert(circuits) .values({ id: "circuit-1", circuitListId: list.id, sectionId: section.id, equipmentIdentifier: "-1F1.1", sortOrder: 10, }) .run(); return context; } const protection: CircuitProtectionSnapshot = { circuitId: "circuit-1", type: "LS", ratedCurrentA: 10, fuseUtilizationCategory: null, tripCharacteristic: "B", rcdType: null, ratedResidualCurrentMa: null, }; describe("circuit-protection project command", () => { it("inserts, updates and restores protection through exact inverse commands", () => { const context = createTestDatabase(); try { const repository = new CircuitProtectionProjectCommandRepository( context.db ); const history = new ProjectHistoryRepository(context.db); const inserted = repository.execute({ projectId: "project-1", expectedRevision: 0, source: "user", command: createCircuitProtectionUpdateProjectCommand( "circuit-1", null, protection ), }); assert.deepEqual( context.db.select().from(circuitProtectionDevices).get(), protection ); const target = { ...protection, ratedCurrentA: 16 }; const updated = repository.execute({ projectId: "project-1", expectedRevision: 1, source: "user", command: createCircuitProtectionUpdateProjectCommand( "circuit-1", protection, target ), }); assert.equal( context.db.select().from(circuitProtectionDevices).get() ?.ratedCurrentA, 16 ); const updateUndo = history.getNextCommand("project-1", "undo"); assert.ok(updateUndo); repository.execute({ projectId: "project-1", expectedRevision: 2, source: "undo", historyTargetChangeSetId: updateUndo.changeSetId, command: updated.inverse, }); assert.deepEqual( context.db.select().from(circuitProtectionDevices).get(), protection ); const insertUndo = history.getNextCommand("project-1", "undo"); assert.ok(insertUndo); repository.execute({ projectId: "project-1", expectedRevision: 3, source: "undo", historyTargetChangeSetId: insertUndo.changeSetId, command: inserted.inverse, }); assert.equal( context.db.select().from(circuitProtectionDevices).get(), undefined ); } finally { context.close(); } }); it("rejects stale state, foreign ownership and user removal", () => { const context = createTestDatabase(); try { const repository = new CircuitProtectionProjectCommandRepository( context.db ); assert.throws( () => repository.execute({ projectId: "project-2", expectedRevision: 0, source: "user", command: createCircuitProtectionUpdateProjectCommand( "circuit-1", null, protection ), }), /does not belong/ ); context.db.insert(circuitProtectionDevices).values(protection).run(); assert.throws( () => repository.execute({ projectId: "project-1", expectedRevision: 0, source: "user", command: createCircuitProtectionUpdateProjectCommand( "circuit-1", null, protection ), }), /changed before/ ); assert.throws( () => repository.execute({ projectId: "project-1", expectedRevision: 0, source: "user", command: createCircuitProtectionUpdateProjectCommand( "circuit-1", protection, null ), }), /cannot remove/ ); } finally { context.close(); } }); });