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); });