Basis: Certvia dev@a48c5fb als Fundament für Craftvia
CI / build-and-check (push) Canceled after 0s
CI / audit (push) Canceled after 0s
CI / sbom (push) Canceled after 0s

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>
This commit is contained in:
2026-09-14 11:05:39 +02:00
co-authored by Claude Opus 5
commit c8e6f30a27
720 changed files with 140143 additions and 0 deletions
+93
View File
@@ -0,0 +1,93 @@
// 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); });