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,91 @@
|
||||
// Unit-Tests (Story A8) für die Gap-Konsolidierung gegen die C8-Beispiele.
|
||||
// Lauf: npx tsx scripts/test-gap-consolidation.ts
|
||||
import { consolidate, gapPriority, isQuickWin, summarize, type RawGap } from "../src/lib/gap-consolidation";
|
||||
|
||||
let failed = 0;
|
||||
function check(name: string, cond: boolean, detail = "") {
|
||||
if (cond) console.log(` ok ${name}`);
|
||||
else {
|
||||
failed++;
|
||||
console.error(`FAIL ${name}${detail ? ` — ${detail}` : ""}`);
|
||||
}
|
||||
}
|
||||
|
||||
function raw(p: Partial<RawGap>): RawGap {
|
||||
return {
|
||||
id: p.id ?? "x",
|
||||
source: p.source ?? "control",
|
||||
title: p.title ?? "",
|
||||
action: p.action ?? "",
|
||||
controls: p.controls ?? [],
|
||||
effort: p.effort ?? "mittel",
|
||||
maturityImpact: p.maturityImpact ?? 1,
|
||||
hasDependency: p.hasDependency ?? false,
|
||||
taskOrigin: p.taskOrigin ?? "wizard:gap",
|
||||
...p,
|
||||
};
|
||||
}
|
||||
|
||||
// C8 §1 Prioritätsregeln + §4 Beispieltabelle.
|
||||
console.log("§1/§4 Priorität:");
|
||||
check(
|
||||
"Kein Patchmanagement (MUSS<2 + Risiko) → Hoch",
|
||||
gapPriority(raw({ requirementType: "MUSS", maturity: 1, riskScore: 16, aboveAcceptance: true })) === "hoch",
|
||||
);
|
||||
check(
|
||||
"Restore-Test fehlt (MUSS-Nachweis) → Hoch",
|
||||
gapPriority(raw({ requirementType: "MUSS", maturity: 1, kind: "nachweis" })) === "hoch",
|
||||
);
|
||||
check("ISB=IT (FT-01 Konflikt) → Hoch", gapPriority(raw({ ftConflict: true, source: "role" })) === "hoch");
|
||||
check(
|
||||
"SOLL Berechtigungs-Review nur Vorlage → Mittel",
|
||||
gapPriority(raw({ requirementType: "SOLL", kind: "verfahren", maturity: 1 })) === "mittel",
|
||||
);
|
||||
check("Zonenbezeichnung redaktionell → Niedrig", gapPriority(raw({ kind: "redaktionell", maturity: 3 })) === "niedrig");
|
||||
check(
|
||||
"MUSS-Control Reifegrad 2 unter Ziel 3 → Mittel",
|
||||
gapPriority(raw({ requirementType: "MUSS", maturity: 2, target: 3, kind: "nachweis" })) === "mittel",
|
||||
);
|
||||
check("HOCH-Zusatzanforderung offen → Hoch", gapPriority(raw({ requirementType: "HOCH", maturity: 2, target: 3 })) === "hoch");
|
||||
|
||||
// C8 §3 Quick-Wins.
|
||||
console.log("§3 Quick-Wins:");
|
||||
check("Restore-Test (organisatorisch, +1) → Quick-Win", isQuickWin(raw({ effort: "gering", maturityImpact: 1 })));
|
||||
check("Patchmanagement (Tool) → kein Quick-Win", !isQuickWin(raw({ effort: "hoch", maturityImpact: 1 })));
|
||||
check("Abhängigkeit → kein Quick-Win", !isQuickWin(raw({ effort: "gering", maturityImpact: 1, hasDependency: true })));
|
||||
check("keine Reifegrad-Wirkung → kein Quick-Win", !isQuickWin(raw({ effort: "gering", maturityImpact: 0 })));
|
||||
|
||||
// C8 §2 Deduplizierung.
|
||||
console.log("§2 Deduplizierung:");
|
||||
const dupSame = consolidate([
|
||||
raw({ id: "control:5.2.3:nachweis", source: "control", controls: ["5.2.3"], kind: "nachweis", requirementType: "MUSS", maturity: 2, target: 3 }),
|
||||
raw({ id: "control:5.2.3:nachweis", source: "control", controls: ["5.2.3"], kind: "nachweis", requirementType: "MUSS", maturity: 2, target: 3 }),
|
||||
]);
|
||||
check("gleiche Teilanforderung → ein Punkt", dupSame.length === 1);
|
||||
|
||||
// Cross-Source-Merge: Risiko-Maßnahme + Control-Gaps derselben Controls (C8 §2, R-OPS-03-Beispiel).
|
||||
const crossMerge = consolidate([
|
||||
raw({ id: "risk:R1", source: "risk", controls: ["5.2.3", "5.2.5"], riskScore: 16, aboveAcceptance: true, title: "Patchmanagement", taskId: "t1" }),
|
||||
raw({ id: "control:5.2.3:nachweis", source: "control", controls: ["5.2.3"], kind: "nachweis", requirementType: "MUSS", maturity: 2, target: 3 }),
|
||||
raw({ id: "control:5.2.5:nachweis", source: "control", controls: ["5.2.5"], kind: "nachweis", requirementType: "MUSS", maturity: 2, target: 3 }),
|
||||
raw({ id: "control:1.1.1:nachweis", source: "control", controls: ["1.1.1"], kind: "nachweis", requirementType: "MUSS", maturity: 2, target: 3 }),
|
||||
]);
|
||||
check("Risiko absorbiert zugehörige Control-Gaps → 2 Punkte (Risiko + 1.1.1)", crossMerge.length === 2);
|
||||
const riskItem = crossMerge.find((i) => i.source === "risk")!;
|
||||
check("gemergter Risiko-Punkt hat Priorität Hoch", riskItem.priority === "hoch");
|
||||
check("gemergter Risiko-Punkt vereint 5.2.3/5.2.5", riskItem.controls.includes("5.2.3") && riskItem.controls.includes("5.2.5"));
|
||||
check("gemergter Risiko-Punkt behält bestehende Aufgabe", riskItem.taskId === "t1");
|
||||
check("Risiko-Punkt vor Control-Punkt sortiert (Priorität + betroffene Controls)", crossMerge[0].source === "risk");
|
||||
|
||||
// Sortierung: Hoch vor Mittel.
|
||||
console.log("Sortierung & Summary:");
|
||||
const sorted = consolidate([
|
||||
raw({ id: "a", source: "control", controls: ["3.1.1"], kind: "redaktionell", maturity: 3 }),
|
||||
raw({ id: "b", source: "control", controls: ["1.4.1"], requirementType: "MUSS", maturity: 1, kind: "verfahren" }),
|
||||
]);
|
||||
check("Hoch (b) vor Niedrig (a)", sorted[0].id === "b" && sorted[1].id === "a");
|
||||
const sum = summarize(sorted);
|
||||
check("Summary zählt korrekt", sum.total === 2 && sum.hoch === 1 && sum.niedrig === 1);
|
||||
|
||||
console.log(failed === 0 ? "\nAlle Gap-Konsolidierungs-Tests grün." : `\n${failed} Test(s) fehlgeschlagen.`);
|
||||
process.exit(failed === 0 ? 0 : 1);
|
||||
Reference in New Issue
Block a user