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>
57 lines
2.9 KiB
TypeScript
57 lines
2.9 KiB
TypeScript
// Akzeptanztest der Readiness-Logik (Story B7-1, C9 §1/§2). Reine Logik, kein DB-Zugriff.
|
|
// Lauf: npx tsx scripts/test-readiness.ts (Exit 1 bei Fehler).
|
|
|
|
import { interpretationBand, averageReifegrad, buildNextSteps, computeReadiness, type ControlAssessment } from "../src/lib/readiness";
|
|
|
|
let failures = 0;
|
|
const ok = (cond: boolean, msg: string) => {
|
|
console.log(`${cond ? "✓" : "✗ FEHLER"} ${msg}`);
|
|
if (!cond) failures++;
|
|
};
|
|
|
|
// — C9 §1: Textbänder je Ø-Reifegrad —
|
|
ok(interpretationBand(0).band === "Aufbau", "Ø 0 → Aufbau");
|
|
ok(interpretationBand(1.49).band === "Aufbau", "Ø 1,49 → Aufbau");
|
|
ok(interpretationBand(1.5).band === "Etabliert im Aufbau", "Ø 1,5 → Etabliert im Aufbau");
|
|
ok(interpretationBand(2.49).band === "Etabliert im Aufbau", "Ø 2,49 → Etabliert im Aufbau");
|
|
ok(interpretationBand(2.5).band === "Assessment-nah", "Ø 2,5 → Assessment-nah");
|
|
ok(interpretationBand(2.99).band === "Assessment-nah", "Ø 2,99 → Assessment-nah");
|
|
ok(interpretationBand(3.0).band === "Assessment-reif", "Ø 3,0 → Assessment-reif");
|
|
|
|
// — Ø-Reifegrad: „unbestätigt zählt nicht als erfüllt" (geht mit 0 ein) —
|
|
{
|
|
const a: ControlAssessment[] = [
|
|
{ control: "1.1.1", chapter: "1", reifegrad: 3, bestaetigt: true },
|
|
{ control: "1.1.2", chapter: "1", reifegrad: 3, bestaetigt: false }, // zählt als 0
|
|
];
|
|
ok(averageReifegrad(a) === 1.5, "unbestätigt zählt als 0 → Ø(3,0) = 1,5");
|
|
ok(averageReifegrad([]) === null, "keine Assessments → null (A7 ausstehend)");
|
|
}
|
|
|
|
// — C9 §2: dynamische nächste Schritte —
|
|
{
|
|
const all = buildNextSteps({ openHighCount: 2, unvalidatedCount: 5, prototypeGap: true, evidenceUploadPending: true });
|
|
ok(all.some((s) => s.includes("2 Hoch-Punkte")), "Hoch-Punkte → Schritt mit Anzahl");
|
|
ok(all.some((s) => s.includes("5 Objekte")), "unvalidierte Objekte → Schritt mit Anzahl");
|
|
ok(all.some((s) => s.includes("Prototypen")), "Prototyp-Gap → Prototyp-Schritt");
|
|
ok(all.length === 4, "alle vier Regeln greifen");
|
|
const none = buildNextSteps({ openHighCount: 0, unvalidatedCount: 0, prototypeGap: false, evidenceUploadPending: false });
|
|
ok(none.length === 0, "keine Bedingung → keine Schritte");
|
|
}
|
|
|
|
// — computeReadiness: assessmentPending ohne A7-Daten —
|
|
{
|
|
const r = computeReadiness({
|
|
assessments: [], bestaetigtAnteil: 0,
|
|
offenJePrioritaet: { hoch: 1, mittel: 2, niedrig: 0 },
|
|
pruefzielAbdeckung: [{ pruefziel: "informationssicherheit", anforderungen: 300 }],
|
|
zielReifegrad: 3,
|
|
nextSteps: { openHighCount: 1, unvalidatedCount: 0, prototypeGap: false, evidenceUploadPending: false },
|
|
});
|
|
ok(r.assessmentPending === true && r.avgReifegrad === null && r.band === null, "ohne Assessments → pending, kein Reifegrad/Band");
|
|
ok(r.nextSteps.length === 1, "Kennzahlen-basierte Schritte trotzdem berechnet");
|
|
}
|
|
|
|
console.log(failures === 0 ? "\nOK — alle Readiness-Tests grün" : `\nPRUEFEN — ${failures} Fehler`);
|
|
process.exit(failures === 0 ? 0 : 1);
|