Files
craftvia/scripts/test-readiness.ts
T
msolarczekandClaude Opus 5 c8e6f30a27
CI / build-and-check (push) Canceled after 0s
CI / audit (push) Canceled after 0s
CI / sbom (push) Canceled after 0s
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>
2026-09-14 11:05:39 +02:00

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);