Files
msolarczekandClaude Opus 5 c8e6f30a27
CI / build-and-check (push) Canceled after 0s
CI / audit (push) Canceled after 0s
CI / sbom (push) Canceled after 0s
Basis: Certvia dev@a48c5fb als Fundament für Craftvia
Unveränderter Stand von certvia/dev (a48c5fb) plus Craftvia-Spezifikation
und Brandbook unter docs/craftvia/. ISMS-Module werden im Folgecommit entfernt.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 11:05:39 +02:00

89 lines
5.1 KiB
TypeScript

import "dotenv/config";
import { join } from "node:path";
import { prisma } from "../src/server/db";
import { importPolicies } from "../prisma/import-policies";
const SEED_DIR = join(process.cwd(), "seed", "isms-vorlagenpaket-v2");
const ok = (c: boolean, m: string) => console.log(`${c ? "✓" : "✗ FEHLER"} ${m}`);
async function main() {
const tenant = await prisma.tenant.findFirst({ where: { slug: "demo" } });
if (!tenant) throw new Error("Demo-Mandant fehlt");
const t = tenant.id;
// Ausgangszustand eines echten Dokuments + einer Variable sichern
const doc0 = await prisma.policyDocument.findFirst({ where: { tenantId: t, code: "R08" } });
const varName = "ORG_NAME";
const var0 = await prisma.policyVariable.findFirst({ where: { tenantId: t, key: varName } });
if (!doc0 || !var0) throw new Error("Erwartetes Dokument R08 / Variable ORG_NAME fehlt");
// 1) Kuratierten Zustand simulieren: Freigabe-Status, TISAX-Override, Einreicher,
// geänderter Titel (Inhalts-Diff) + nutzergepflegter Variablenwert.
await prisma.policyDocument.update({
where: { id: doc0.id },
data: { status: "ENTWURF", protectionOverride: "AL3", submittedBy: "tester", title: "MANUELL GEÄNDERTER TITEL" },
});
await prisma.policyVariable.update({ where: { id: var0.id }, data: { value: "Mustermann Spezial GmbH" } });
// 2) Entfernte Anforderung simulieren (nicht im Paket): muss deaktiviert, nicht gelöscht werden.
const fakeReqId = "ZZ-REMOVED-TEST-1";
await prisma.policyRequirement.deleteMany({ where: { tenantId: t, reqId: fakeReqId } });
await prisma.policyRequirement.create({
data: { tenantId: t, reqId: fakeReqId, policyCode: "R08", control: "0.0.0", obligation: "MUSS", requirement: "Test", implementation: "" },
});
// 3) Dry-Run: Vorschau ohne Schreibzugriff
const preview = await importPolicies(prisma, t, SEED_DIR, { dryRun: true });
console.log("\n— Dry-Run-Report —");
console.log(JSON.stringify(preview.report, null, 0));
ok(preview.report.documents.updated >= 1, "Dry-Run erkennt Titeländerung an R08 (updated ≥ 1)");
ok(preview.report.documents.archived === 0, "Dry-Run deaktiviert KEINE verwalteten Register (CRYPTO/HANDBUCH/…)");
ok(preview.report.requirements.archived === 1, "Dry-Run erkennt 1 zu deaktivierende Anforderung");
const stillDraftAfterDry = await prisma.policyDocument.findUnique({ where: { id: doc0.id } });
ok(stillDraftAfterDry?.title === "MANUELL GEÄNDERTER TITEL", "Dry-Run schreibt NICHT (Titel unverändert)");
// 4) Echter Re-Import
const run1 = await importPolicies(prisma, t, SEED_DIR, { dryRun: false, actorId: null });
console.log("\n— Re-Import-Report —");
console.log(JSON.stringify(run1.report, null, 0));
const docA = await prisma.policyDocument.findUnique({ where: { id: doc0.id } });
ok(docA?.status === "ENTWURF", "Freigabe-Status bleibt erhalten (ENTWURF)");
ok(docA?.protectionOverride === "AL3", "TISAX-Override bleibt erhalten (AL3)");
ok(docA?.submittedBy === "tester", "Einreicher (Freigabe-Workflow) bleibt erhalten");
ok(docA?.title === doc0.title, "Titel wurde aus dem Paket aktualisiert (Inhalt)");
ok(docA?.archivedAt === null, "Dokument bleibt aktiv");
const varA = await prisma.policyVariable.findUnique({ where: { id: var0.id } });
ok(varA?.value === "Mustermann Spezial GmbH", "Nutzergepflegter Variablenwert bleibt erhalten");
const fakeA = await prisma.policyRequirement.findFirst({ where: { tenantId: t, reqId: fakeReqId } });
ok(!!fakeA && fakeA.archivedAt !== null, "Entfernte Anforderung ist deaktiviert (archivedAt gesetzt), NICHT gelöscht");
const auditA = await prisma.auditLog.findFirst({ where: { tenantId: t, entity: "policy_package" }, orderBy: { createdAt: "desc" } });
ok(!!auditA, "Änderungsreport im Audit-Log protokolliert");
// 5) Idempotenz: zweiter Lauf ohne externe Änderung → keine Diffs
const run2 = await importPolicies(prisma, t, SEED_DIR, { dryRun: false });
const r = run2.report;
const noChanges =
r.documents.added + r.documents.updated + r.documents.archived + r.documents.reactivated === 0 &&
r.requirements.added + r.requirements.updated + r.requirements.archived + r.requirements.reactivated === 0 &&
r.variables.added + r.variables.updated === 0 && r.baseline.added + r.baseline.updated === 0 &&
r.evidence.added + r.evidence.updated === 0;
console.log("\n— Idempotenz-Report —");
console.log(JSON.stringify(r, null, 0));
ok(noChanges, "Zweiter Lauf ist idempotent (keine added/updated/archived/reactivated)");
// Aufräumen: Testartefakt entfernen, kuratierten Zustand zurücksetzen
await prisma.policyRequirement.deleteMany({ where: { tenantId: t, reqId: fakeReqId } });
await prisma.policyDocument.update({
where: { id: doc0.id },
data: { status: doc0.status, protectionOverride: doc0.protectionOverride, submittedBy: doc0.submittedBy, title: doc0.title, archivedAt: null },
});
await prisma.policyVariable.update({ where: { id: var0.id }, data: { value: var0.value } });
console.log("\n✓ aufgeräumt (Testartefakte entfernt, R08/ORG_NAME zurückgesetzt)");
}
main().then(() => process.exit(0)).catch((e) => { console.error(e); process.exit(1); });