leistungsbilanz-ts/tests/external-model-configuration.test.ts

132 lines
5.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 { 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();
}
});
});