// AP1 — Framework-Dimension: Kern-Invarianten (docs/UEBERGABE-framework-iso27001.md §1). // // Prüft gegen die lokale DB (Demo-Mandant): // 1. Backfill: jeder Mandant hat ≥1 TenantFramework; Bestands-Anforderungen sind TISAX; // getTenantFrameworks(demo) beginnt mit TISAX; Fallback ["TISAX"] ohne Zeile. // 2. Beide Mappings parsen: mapping.json (VDA ISA) und mapping-iso.json (ISO) — gemeinsamer // Dokumentensatz, unterschiedliche Anforderungen. // 3. Doppel-Framework-Reconcile: ISO-Import in einen TISAX-Mandanten legt die ISO- // Anforderungen an und archiviert KEINE TISAX-Anforderung (Falle 1.1); Dokumente // werden nicht dupliziert (reconcileShared). // 4. Rück-Reconcile TISAX archiviert die ISO-Anforderungen NICHT. // Räumt die ISO-Testdaten am Ende wieder ab. // // Lauf: npx tsx scripts/test-framework-core.ts import "dotenv/config"; import { join } from "node:path"; import { prisma } from "../src/server/db"; import { parsePackageFiles, reconcilePackage } from "../prisma/import-policies"; import { getTenantFrameworks, getTenantPrimaryFramework } from "../prisma/template-store"; const SEED_DIR = join(process.cwd(), "seed", "isms-vorlagenpaket-v2"); let failures = 0; const ok = (c: boolean, m: string) => { console.log(`${c ? "✓" : "✗ FEHLER"} ${m}`); if (!c) failures++; }; const activeReqs = (tenantId: string, framework: "TISAX" | "ISO_27001") => prisma.policyRequirement.count({ where: { tenantId, framework, archivedAt: null } }); const archivedReqs = (tenantId: string, framework: "TISAX" | "ISO_27001") => prisma.policyRequirement.count({ where: { tenantId, framework, archivedAt: { not: null } } }); async function main() { const tenant = await prisma.tenant.findFirst({ where: { slug: "demo" } }); if (!tenant) throw new Error("Demo-Mandant fehlt (prisma/seed.ts ausführen)."); const t = tenant.id; // ── 1. Backfill / Framework-Zuordnung ─────────────────────────────────────── const allTenants = await prisma.tenant.count(); const tenantsWithFw = await prisma.tenantFramework.groupBy({ by: ["tenantId"] }); ok(tenantsWithFw.length === allTenants, `jeder Mandant hat ≥1 TenantFramework (${tenantsWithFw.length}/${allTenants})`); const demoFw = await getTenantFrameworks(prisma, t); ok(demoFw[0] === "TISAX", "getTenantFrameworks(demo) beginnt mit TISAX"); ok((await getTenantPrimaryFramework(prisma, t)) === "TISAX", "Primär-Framework = TISAX"); ok((await getTenantFrameworks(prisma, "does-not-exist"))[0] === "TISAX", "Fallback ohne Zeile → [\"TISAX\"]"); const isoBaseline = await prisma.policyRequirement.count({ where: { tenantId: t, framework: "ISO_27001" } }); ok(isoBaseline === 0, "Demo-Mandant hat vor dem Test keine ISO-Anforderungen"); // ── 2. Beide Mappings parsen ──────────────────────────────────────────────── const tisaxPkg = parsePackageFiles(SEED_DIR, "mapping.json"); const isoPkg = parsePackageFiles(SEED_DIR, "mapping-iso.json"); ok(tisaxPkg.requirements.length > 300, `mapping.json: ${tisaxPkg.requirements.length} Anforderungen (VDA ISA)`); ok(isoPkg.requirements.length === 120, `mapping-iso.json: ${isoPkg.requirements.length} Anforderungen (erwartet 120)`); ok(tisaxPkg.documents.length === isoPkg.documents.length, "gemeinsamer Dokumentensatz (gleiche Dokumentanzahl)"); // ── 3. Doppel-Framework: ISO in TISAX-Mandant ─────────────────────────────── const tisaxActiveBefore = await activeReqs(t, "TISAX"); const tisaxArchivedBefore = await archivedReqs(t, "TISAX"); const docsBefore = await prisma.policyDocument.count({ where: { tenantId: t } }); const isoRun = await reconcilePackage(prisma, t, isoPkg, { framework: "ISO_27001", reconcileShared: false }); ok(isoRun.report.requirements.added === isoPkg.requirements.length, `ISO-Import legt ${isoRun.report.requirements.added} Anforderungen an (= ${isoPkg.requirements.length})`); ok(isoRun.report.documents.added === 0 && isoRun.report.documents.updated === 0, "ISO-Import fasst die geteilten Dokumente NICHT an (reconcileShared=false)"); const isoActive = await activeReqs(t, "ISO_27001"); ok(isoActive === isoPkg.requirements.length, `ISO-Anforderungen aktiv: ${isoActive}`); const tisaxActiveAfter = await activeReqs(t, "TISAX"); const tisaxArchivedAfter = await archivedReqs(t, "TISAX"); ok(tisaxActiveAfter === tisaxActiveBefore, `KEINE TISAX-Anforderung archiviert (aktiv ${tisaxActiveBefore} → ${tisaxActiveAfter})`); ok(tisaxArchivedAfter === tisaxArchivedBefore, "ISO-Import erzeugt keine neu archivierten TISAX-Anforderungen (Falle 1.1)"); const docsAfter = await prisma.policyDocument.count({ where: { tenantId: t } }); ok(docsAfter === docsBefore, `Dokumente nicht dupliziert (${docsBefore} → ${docsAfter})`); const coexist = tisaxActiveAfter + isoActive; ok(tisaxActiveAfter > 0 && isoActive === 120, `Doppel-Framework: ${tisaxActiveAfter} TISAX + ${isoActive} ISO koexistieren (${coexist})`); // ── 4. Rück-Reconcile TISAX archiviert ISO NICHT ──────────────────────────── await reconcilePackage(prisma, t, tisaxPkg, { framework: "TISAX", reconcileShared: true }); const isoStillActive = await activeReqs(t, "ISO_27001"); ok(isoStillActive === isoPkg.requirements.length, "TISAX-Re-Import archiviert die ISO-Anforderungen NICHT"); // ── Aufräumen: ISO-Testdaten entfernen (Demo-Mandant zurücksetzen) ────────── const removed = await prisma.policyRequirement.deleteMany({ where: { tenantId: t, framework: "ISO_27001" } }); console.log(`\n✓ aufgeräumt (${removed.count} ISO-Test-Anforderungen entfernt)`); } main() .then(() => { console.log(failures === 0 ? "\nAP1-Framework-Kern grün." : `\n${failures} Prüfung(en) fehlgeschlagen.`); process.exit(failures === 0 ? 0 : 1); }) .catch((e) => { console.error(e); process.exit(1); });