# Instruktion: Teststrategie Block C **Repo:** `/Users/benjaminadmin/Projekte/breakpilot-core/` **Verzeichnis:** `control-pipeline/tests/` **Erstellt:** 2026-05-01 **Geschaetzter Aufwand:** 2-3 Tage ## Ausgangslage - 221 bestehende Tests in 7 Dateien (NICHT aendern!) - 40 Golden Test Cases (golden_controls.yaml) - 24 Demo Cases (demo_cases.yaml) - Alle Tests sind pure Python, kein DB noetig - Pipeline v1 abgeschlossen: 151.675 unique Controls, 15.291 Dependencies ## Aufgabe 1: Real-World Benchmarks (C1) ### Was zu tun ist 10 echte deutsche E-Commerce Websites manuell pruefen und Ground Truth YAML erstellen. ### Verzeichnis ``` control-pipeline/tests/benchmarks/ ├── amazon_de.yaml ├── zalando_de.yaml ├── otto_de.yaml ├── lidl_de.yaml ├── check24_de.yaml ├── booking_de.yaml ├── thomann_de.yaml ├── aboutyou_de.yaml ├── mytheresa_com.yaml └── kleiner_shop.yaml ``` ### Format pro Website ```yaml website: amazon.de url: https://www.amazon.de checked_at: "2026-05-XX" checked_by: "Name" ground_truth: impressum: present: true/false complete: true/false # Name, Adresse, Email, HR-Nummer, USt-ID within_2_clicks: true/false missing_fields: [] # z.B. ["USt-ID", "Handelsregister"] datenschutzerklaerung: present: true/false art13_complete: true/false missing_art13_fields: [] # z.B. ["Speicherdauer", "Empfaenger"] rechtsgrundlagen_korrekt: true/false wrong_legal_bases: [] # z.B. ["Analytics auf lit. f statt lit. a"] cookie_banner: present: true/false reject_equally_easy: true/false # CNIL: Ablehnen = gleich prominent cookies_before_consent: true/false # Planet49: Cookies VOR Consent? dark_patterns: [] # z.B. ["Ablehnen-Button kleiner", "Ablehnen hinter Einstellungen"] widerrufsbelehrung: present: true/false matches_legal_template: true/false # Gesetzliches Muster agb: present: true/false checkout_button_text: "..." # z.B. "Jetzt kaufen" (korrekt) vs "Weiter" (falsch) google_fonts_external: true/false google_analytics: true/false third_party_services: - name: "Google Analytics" detected: true consent_required: true consent_obtained_before_load: false - name: "Facebook Pixel" detected: true consent_required: true consent_obtained_before_load: false expected_findings: - "Cookie-Banner: Ablehnen nicht gleichwertig" - "Google Analytics ohne vorherige Einwilligung" - "DSE: Rechtsgrundlage fuer Analytics falsch" expected_no_findings: - "Impressum fehlt" # Ist vorhanden, darf nicht geflagt werden ``` ### Test-Runner ```python # control-pipeline/tests/test_benchmarks.py """ Real-World Benchmark Tests — vergleicht Agent-Findings mit manueller Ground Truth. Erfordert: Compliance Agent muss laufen (https://macmini:3007/sdk/agent) """ import yaml import pytest import os BENCHMARK_DIR = os.path.join(os.path.dirname(__file__), "benchmarks") def load_benchmarks(): cases = [] for f in sorted(os.listdir(BENCHMARK_DIR)): if f.endswith(".yaml"): with open(os.path.join(BENCHMARK_DIR, f)) as fh: cases.append(yaml.safe_load(fh)) return cases class TestBenchmarks: """Precision/Recall gegen Ground Truth messen.""" @pytest.mark.parametrize("case", load_benchmarks(), ids=lambda c: c["website"]) def test_benchmark(self, case): # TODO: Agent gegen Website laufen lassen # TODO: Findings mit expected_findings vergleichen # TODO: Precision + Recall berechnen pass ``` ### Wie die Ground Truth erstellt wird 1. Website im Browser oeffnen 2. Impressum pruefen (alle Pflichtfelder nach § 5 DDG) 3. Datenschutzerklaerung lesen (Art. 13 DSGVO Checkliste) 4. Cookie-Banner testen (Ablehnen gleich einfach? Cookies vor Consent?) 5. Widerrufsbelehrung gegen gesetzliches Muster pruefen 6. Browser DevTools: Netzwerk-Tab → externe Requests vor Consent? 7. Alles in YAML dokumentieren **Ziel-Metriken:** - Precision > 80% (wenige False Positives) - Recall > 70% (findet die meisten echten Probleme) --- ## Aufgabe 2: Adversarial Tests (C2) ### Was zu tun ist 30 tricky Test Cases erstellen die den Agent/Controls herausfordern. ### Datei `control-pipeline/tests/adversarial_cases.yaml` ### Kategorien **A. Falsche Rechtsgrundlage (8 Cases):** - Analytics auf lit. f statt lit. a - Marketing-Emails auf lit. b statt lit. a - Mitarbeiter-Tracking auf lit. f statt Betriebsvereinbarung - Biometrische Daten auf lit. f statt Art. 9 - Profiling auf lit. f statt Art. 22 - Newsletter auf lit. b statt lit. a - Social Login auf lit. b statt lit. a - Kreditscoring auf lit. f statt lit. a + Art. 22 **B. Dark Patterns (6 Cases):** - Ablehnen-Button existiert aber 3px gross + grau - "Alle akzeptieren" prominent, "Einstellungen" statt "Ablehnen" - Cookie-Wall: Inhalt erst nach Zustimmung sichtbar - Vorausgefuellte Checkboxen (Planet49) - Confirm-Shaming: "Nein, ich moechte keine sichere Verbindung" - Ablehnen erfordert 3 Klicks, Akzeptieren nur 1 **C. Fast-vollstaendige Dokumente (6 Cases):** - Impressum komplett bis auf USt-ID - DSE ohne Speicherdauer - DSE ohne DSB-Kontakt - Widerrufsbelehrung mit falschem Fristbeginn - AGB ohne Gerichtsstand - Cookie-Policy ohne Auflistung aller Cookies **D. Semantisch aehnlich aber verschieden (5 Cases):** - "Admin-MFA" vs "User-MFA" (verschiedene Scopes!) - "Daten loeschen nach Kuendigung" vs "Daten loeschen nach Aufbewahrungsfrist" - "Rate Limiting API" vs "Rate Limiting Login" - "Verschluesselung at rest" vs "Verschluesselung in transit" - "Incident Response Plan" vs "Business Continuity Plan" **E. Semantisch verschieden aber gleich klingend (5 Cases):** - "Einwilligung" (DSGVO) vs "Einwilligung" (Werbung) - "Verarbeitung" (Daten) vs "Verarbeitung" (Lebensmittel) - "Risikobewertung" (DSGVO DSFA) vs "Risikobewertung" (Finanzrisiko) - "Audit" (Datenschutz) vs "Audit" (Finanzen) - "Zertifizierung" (ISO 27001) vs "Zertifizierung" (CE-Marking) ### Format ```yaml - id: ADV-LIT-001 category: wrong_legal_basis input: "Wir verarbeiten Ihre Daten fuer Webanalyse auf Grundlage unseres berechtigten Interesses (Art. 6 Abs. 1 lit. f DSGVO)." context: "DSE-Abschnitt ueber Google Analytics" expected: finding: true finding_type: "wrong_legal_basis" correct_basis: "Art. 6 Abs. 1 lit. a (Einwilligung)" reason: "Analytics erfordert Einwilligung, nicht berechtigtes Interesse (EuGH C-673/17 Planet49)" difficulty: medium # easy / medium / hard ``` --- ## Aufgabe 3: Regression-Harness (C3) ### Was zu tun ist 1. `conftest.py` mit shared Fixtures 2. `test_regression.py` mit Snapshot-Tests 3. CI/CD Quality Gate ### conftest.py ```python # control-pipeline/tests/conftest.py import os import pytest @pytest.fixture(scope="session") def db_session(): """DB session for integration tests — skip if no DATABASE_URL.""" url = os.getenv("DATABASE_URL") if not url: pytest.skip("DATABASE_URL not set") from db.session import SessionLocal db = SessionLocal() yield db db.close() @pytest.fixture def sample_controls(db_session): """Load 100 random draft controls for regression testing.""" from sqlalchemy import text rows = db_session.execute(text(""" SELECT control_id, title, category, severity, generation_metadata->>'assertion' as assertion FROM compliance.canonical_controls WHERE release_state = 'draft' AND decomposition_method = 'pass0b' ORDER BY random() LIMIT 100 """)).fetchall() return [dict(r._mapping) for r in rows] ``` ### test_regression.py ```python # control-pipeline/tests/test_regression.py """ Regression Tests — pruefen ob Pipeline-Updates bestehende Controls veraendern. Erfordert: DATABASE_URL Umgebungsvariable """ class TestControlStability: def test_draft_count_stable(self, db_session): """Draft count darf nicht um >5% abweichen.""" from sqlalchemy import text count = db_session.execute(text( "SELECT COUNT(*) FROM compliance.canonical_controls " "WHERE release_state = 'draft' AND decomposition_method = 'pass0b'" )).scalar() assert count > 140000, f"Draft count too low: {count}" assert count < 200000, f"Draft count too high: {count}" def test_no_null_assertions(self, db_session): """Alle draft Controls muessen eine assertion haben.""" from sqlalchemy import text null_count = db_session.execute(text( "SELECT COUNT(*) FROM compliance.canonical_controls " "WHERE release_state = 'draft' AND decomposition_method = 'pass0b' " "AND (generation_metadata->>'assertion' IS NULL OR generation_metadata->>'assertion' = '')" )).scalar() assert null_count < 1000, f"Too many controls without assertion: {null_count}" def test_dependency_graph_valid(self, db_session): """Keine Zyklen im Dependency-Graph.""" from sqlalchemy import text cycle_count = db_session.execute(text( "SELECT COUNT(*) FROM compliance.control_dependencies WHERE is_active = true" )).scalar() assert cycle_count > 10000, f"Too few dependencies: {cycle_count}" class TestQualityGates: def test_duplicate_rate(self, db_session): pass # Implementieren: duplicate_rate < 5% def test_evidence_leak_rate(self, db_session): pass # Implementieren: evidence_leak < 2% ``` ### CI/CD Quality Gate ```yaml # .gitea/workflows/quality-gate.yml name: Control Pipeline Quality Gate on: push: paths: - 'control-pipeline/**' jobs: quality-gate: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Run Tests run: | cd control-pipeline pip install -r requirements.txt pytest pyyaml PYTHONPATH=. pytest tests/ -v --tb=short -x - name: Quality Metrics run: | # Nur wenn Container laeuft curl -sf http://127.0.0.1:8098/v1/canonical/generate/quality-metrics || echo "Pipeline not running, skip metrics" ``` --- ## WICHTIG - Bestehende 221 Tests NICHT aendern - NICHT deployen (Container nicht neustarten) - Alle neuen Tests muessen ohne DB laufen (ausser test_regression.py mit skip-Marker) - Ground Truth YAML manuell erstellen (kein LLM fuer die Referenzdaten!) - Bei Fragen: Memory lesen unter `/Users/benjaminadmin/.claude/projects/-Users-benjaminadmin-Projekte-breakpilot-core/memory/`