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 { ExternalModelConfigurationProjectCommandRepository } from "../src/db/repositories/external-model-configuration-project-command.repository.js"; import { ExternalModelConfigurationRepository } from "../src/db/repositories/external-model-configuration.repository.js"; import { ProjectTransferRepository } from "../src/db/repositories/project-transfer.repository.js"; import { externalModelConfigurations } from "../src/db/schema/external-model-configurations.js"; import { projects } from "../src/db/schema/projects.js"; import { createExternalModelConfigurationUpdateProjectCommand } from "../src/domain/models/external-model-configuration-project-command.model.js"; import { assertExternalModelConfiguration, createDefaultExternalModelConfiguration, } from "../src/external-model/configuration/external-model-configuration.js"; const deviceTypeId = "10000000-0000-4000-8000-000000000001"; const ruleId = "20000000-0000-4000-8000-000000000001"; function configuration() { const value = createDefaultExternalModelConfiguration(); value.deviceTypes.push({ id: deviceTypeId, name: "Steckdose", category: "single_phase", defaults: { powerPerUnitW: 200, simultaneityFactor: null, cosPhi: 0.95, costGroup: "440" }, }); value.familyTypeRules.push({ id: ruleId, exactFamilyAndType: "Steckdose: Standard", deviceTypeId, quantityRule: "socketCount", }); return value; } function snapshot(projectId = "project-1") { return { id: "config-1", projectId, configuration: configuration() }; } 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" }).run(); return context; } describe("external model configuration", () => { it("validates the single Worksharing-ID mapping and exact family rules", () => { const value = configuration(); assert.doesNotThrow(() => assertExternalModelConfiguration(value)); assert.equal("idColumn" in value.layout, false); const duplicate = structuredClone(value); duplicate.columns.ifcGuid = duplicate.columns.worksharingId; assert.throws(() => assertExternalModelConfiguration(duplicate), /nicht eindeutig/); const unknownType = structuredClone(value); unknownType.familyTypeRules[0]!.deviceTypeId = "30000000-0000-4000-8000-000000000001"; assert.throws(() => assertExternalModelConfiguration(unknownType), /unbekannte Ger.*teart/); }); it("persists changes without an independent configuration revision", () => { const context = createTestDatabase(); try { const repository = new ExternalModelConfigurationProjectCommandRepository(context.db); const initial = snapshot(); const inserted = repository.execute({ projectId: "project-1", expectedRevision: 0, source: "user", command: createExternalModelConfigurationUpdateProjectCommand(null, initial), }); assert.equal(inserted.revision.revisionNumber, 1); assert.equal("configurationVersion" in initial, false); const target = structuredClone(initial); target.configuration.deviceTypes[0]!.name = "Steckdose allgemein"; const updated = repository.execute({ projectId: "project-1", expectedRevision: 1, source: "user", command: createExternalModelConfigurationUpdateProjectCommand(initial, target), }); assert.equal(updated.revision.revisionNumber, 2); assert.deepEqual(updated.inverse.payload, { expected: target, target: initial }); assert.throws( () => repository.execute({ projectId: "project-1", expectedRevision: 2, source: "user", command: createExternalModelConfigurationUpdateProjectCommand(initial, target), }), /zwischenzeitlich ge.*ndert/ ); } finally { context.close(); } }); it("distinguishes missing projects and remaps configuration identities on duplicate", () => { const context = createTestDatabase(); try { const reader = new ExternalModelConfigurationRepository(context.db); assert.deepEqual(reader.getByProject("project-1"), { projectExists: true, configuration: null }); assert.deepEqual(reader.getByProject("missing"), { projectExists: false, configuration: null }); new ExternalModelConfigurationProjectCommandRepository(context.db).execute({ projectId: "project-1", expectedRevision: 0, source: "user", command: createExternalModelConfigurationUpdateProjectCommand(null, snapshot()), }); const transferRepository = new ProjectTransferRepository(context.db); const transfer = transferRepository.exportProject("project-1"); assert.ok(transfer); const duplicate = transferRepository.importDuplicate(transfer); const copied = context.db.select().from(externalModelConfigurations) .where(eq(externalModelConfigurations.projectId, duplicate.projectId)).get(); assert.ok(copied); assert.notEqual(copied.id, "config-1"); assert.notEqual(copied.configuration.deviceTypes[0]!.id, deviceTypeId); assert.equal( copied.configuration.familyTypeRules[0]!.deviceTypeId, copied.configuration.deviceTypes[0]!.id ); } finally { context.close(); } }); });