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 { CircuitDeviceRowTransactionRepository } from "../src/db/repositories/circuit-device-row-transaction.repository.js"; import { DistributionBoardRepository } from "../src/db/repositories/distribution-board.repository.js"; import { circuitDeviceRows } from "../src/db/schema/circuit-device-rows.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"; 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(); const board = new DistributionBoardRepository( context.db ).createWithCircuitListAndDefaultSections( "project-1", "UV-01" ); const [section] = context.db .select() .from(circuitSections) .where(eq(circuitSections.circuitListId, board.id)) .limit(1) .all(); context.db .insert(circuits) .values({ id: "circuit-1", circuitListId: board.id, sectionId: section.id, equipmentIdentifier: "-1F1", displayName: "Reserve", sortOrder: 10, isReserve: 1, }) .run(); return context; } function insertDeviceRow(context: DatabaseContext) { context.db .insert(circuitDeviceRows) .values({ id: "row-1", circuitId: "circuit-1", sortOrder: 10, name: "Leuchte", displayName: "Leuchte", quantity: 1, powerPerUnit: 0.1, simultaneityFactor: 1, }) .run(); context.db .update(circuits) .set({ isReserve: 0 }) .where(eq(circuits.id, "circuit-1")) .run(); } describe("circuit device-row transaction repository", () => { it("commits a new device row and clears the circuit reserve status together", () => { const context = createTestDatabase(); try { const repository = new CircuitDeviceRowTransactionRepository(context.db); const rowId = repository.createInCircuit({ circuitId: "circuit-1", name: "Steckdose", displayName: "Steckdose", quantity: 1, powerPerUnit: 0.2, simultaneityFactor: 1, }); const [row] = context.db .select() .from(circuitDeviceRows) .where(eq(circuitDeviceRows.id, rowId)) .all(); const [circuit] = context.db .select() .from(circuits) .where(eq(circuits.id, "circuit-1")) .all(); assert.equal(row.circuitId, "circuit-1"); assert.equal(row.sortOrder, 10); assert.equal(circuit.isReserve, 0); } finally { context.close(); } }); it("rolls back the row insert when clearing the reserve status fails", () => { const context = createTestDatabase(); try { context.sqlite.exec(` CREATE TRIGGER fail_reserve_clear BEFORE UPDATE OF is_reserve ON circuits WHEN NEW.is_reserve = 0 BEGIN SELECT RAISE(ABORT, 'forced reserve clear failure'); END; `); const repository = new CircuitDeviceRowTransactionRepository(context.db); assert.throws( () => repository.createInCircuit({ circuitId: "circuit-1", name: "Steckdose", displayName: "Steckdose", quantity: 1, powerPerUnit: 0.2, simultaneityFactor: 1, }), /forced reserve clear failure/ ); assert.equal(context.db.select().from(circuitDeviceRows).all().length, 0); assert.equal(context.db.select().from(circuits).all()[0].isReserve, 1); } finally { context.close(); } }); it("commits the last-row deletion and activates the circuit reserve status together", () => { const context = createTestDatabase(); try { insertDeviceRow(context); const repository = new CircuitDeviceRowTransactionRepository(context.db); repository.deleteFromCircuit("row-1", "circuit-1"); assert.equal(context.db.select().from(circuitDeviceRows).all().length, 0); assert.equal(context.db.select().from(circuits).all()[0].isReserve, 1); } finally { context.close(); } }); it("rolls back the row deletion when activating the reserve status fails", () => { const context = createTestDatabase(); try { insertDeviceRow(context); context.sqlite.exec(` CREATE TRIGGER fail_reserve_activation BEFORE UPDATE OF is_reserve ON circuits WHEN NEW.is_reserve = 1 BEGIN SELECT RAISE(ABORT, 'forced reserve activation failure'); END; `); const repository = new CircuitDeviceRowTransactionRepository(context.db); assert.throws( () => repository.deleteFromCircuit("row-1", "circuit-1"), /forced reserve activation failure/ ); assert.equal(context.db.select().from(circuitDeviceRows).all().length, 1); assert.equal(context.db.select().from(circuits).all()[0].isReserve, 0); } finally { context.close(); } }); });