package opsdocs import ( "os" "path/filepath" "strings" "testing" ) func readPlan(t *testing.T) string { t.Helper() // Test laeuft aus internal/opsdocs/ heraus, Repo-Root ist zwei Ebenen hoeher. path := filepath.Join("..", "..", IncidentResponsePlanPath) data, err := os.ReadFile(path) if err != nil { t.Fatalf("incident-response-plan nicht lesbar (%s): %v", path, err) } return string(data) } func requireContains(t *testing.T, content, substr, why string) { t.Helper() if !strings.Contains(content, substr) { t.Fatalf("erwartet %q im incident-response-plan (%s), nicht gefunden", substr, why) } } // Akzeptanzkriterium 1: Ablaufplan dokumentiert Erkennung/Eskalation/ // Meldefristen/Verantwortlichkeiten. func TestPlan_DocumentsDetectionEscalationAndResponsibilities(t *testing.T) { content := readPlan(t) requireContains(t, content, "Erkennung", "Abschnitt Erkennung fehlt") requireContains(t, content, "Eskalationskette", "Abschnitt Eskalation fehlt") requireContains(t, content, "Incident Commander", "Verantwortlichkeits-Rolle fehlt") requireContains(t, content, "Datenschutzbeauftragter", "DSB-Rolle fehlt") } // Akzeptanzkriterium 2: DSGVO-72-Stunden-Meldefrist ist als Prozessschritt // mit Verantwortlichem hinterlegt. func TestPlan_Documents72HourGDPRDeadlineWithResponsibleRole(t *testing.T) { content := readPlan(t) requireContains(t, content, "72 Stunden", "72-Stunden-Frist fehlt") requireContains(t, content, "Art. 33", "Verweis auf Art. 33 DSGVO fehlt") requireContains(t, content, "Verantwortlich für die Meldung", "Zuständigkeit für die Meldung fehlt") } // Akzeptanzkriterium 3: Plan verweist konkret auf die Audit-Log-Quellen // (Core AUD-01/AUD-03/AUD-05), die im Vorfall herangezogen werden. func TestPlan_ReferencesConcreteAuditLogSources(t *testing.T) { content := readPlan(t) requireContains(t, content, "AUD-01", "Verweis auf AUD-01 fehlt") requireContains(t, content, "AUD-03", "Verweis auf AUD-03 (Export) fehlt") requireContains(t, content, "internal/audit", "konkreter Code-Pfad zum Audit-Log fehlt") requireContains(t, content, "StreamCSV", "konkrete Export-Funktion fehlt") } // Zusaetzliche Absicherung: die drei geforderten Pruefungen sind im // Dokument tatsaechlich mit einem Ergebnis (PASS/OFFEN) festgehalten, // nicht nur als Vorhaben erwaehnt — verhindert, dass "durchgefuehrt" // behauptet wird, ohne das Ergebnis schriftlich festzuhalten (Ticket- // Vorgabe: "Nicht durchgefuehrte Pruefungen zaehlen als offen"). func TestPlan_RecordsAllThreeRequiredCheckResults(t *testing.T) { content := readPlan(t) requireContains(t, content, "Prüfung 1", "Ergebnis der Tabletop-Übung fehlt") requireContains(t, content, "Prüfung 2", "Ergebnis der Meldefrist-Vollständigkeitsprüfung fehlt") requireContains(t, content, "Prüfung 3", "Ergebnis der Kontaktlisten-Prüfung fehlt") requireContains(t, content, "Status: OFFEN", "ehrlicher Offen-Status fuer die nicht durchfuehrbare Kontaktlisten-Pruefung fehlt") }