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>
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
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); });
|
||||
Reference in New Issue
Block a user