feat(audit-report): Exec-Summary, Top-N je Modul, Statistik, Gesamtanalyse
User-Feedback umgesetzt: Cookie-Titel-Fix (rendern nicht mehr als nacktes "Befund" — Titel aus cookie/type/vendor), Executive Summary oben, je Modul Statistik (Counts + Severity-Balken + MCs) + nur Top-3 Befunde + Verweis auf "N weitere" mit Frontend-Link (snapshot_id) + Zwischenfazit, Browser-Übersicht, Gesamtanalyse, klarerer "Grenzen"-Satz, Report-Versionsnummer. 6 Tests grün. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -1,52 +1,72 @@
|
||||
"""Audit-Report-Assembler (pur) — Sektionen, 4-Status-/Severity-Zählung,
|
||||
Co-Pilot-Tonalität, kein Normtext."""
|
||||
"""Audit-Report-Assembler (pur) — Sektionen, Cookie-Titel-Fix, Top-N-Deckelung,
|
||||
Severity-Statistik, Co-Pilot-Tonalität, kein Normtext."""
|
||||
|
||||
from compliance.services.audit_report import assemble_report, render_markdown
|
||||
|
||||
META = {"site_label": "BMW", "site_domain": "bmw.de",
|
||||
META = {"snapshot_id": "abc-123", "site_label": "BMW", "site_domain": "bmw.de",
|
||||
"created_at": "2026-06-11T14:15:00", "check_id": "508983ec"}
|
||||
MODULES = {
|
||||
"cookie": {"findings": [
|
||||
{"title": "Cookie als notwendig deklariert, real Marketing",
|
||||
"severity": "HIGH", "legal_ref": "§ 25 TDDDG",
|
||||
"measure": "Als einwilligungspflichtig (§ 25) einstufen."},
|
||||
{"title": "Laufzeit überschreitet Empfehlung", "severity": "LOW"},
|
||||
# Cookie-Befunde OHNE title-Feld (nur cookie/type/remediation) → Titel-Fix.
|
||||
{"cookie": "cto_bmw", "vendor": "Criteo", "type": "tracker_as_necessary",
|
||||
"severity": "HIGH", "remediation": "Als einwilligungspflichtig einstufen."},
|
||||
{"cookie": "_ga", "vendor": "Google", "type": "excessive_lifetime",
|
||||
"severity": "LOW", "remediation": "Laufzeit reduzieren."},
|
||||
{"cookie": "x1", "type": "missing_purpose", "severity": "MEDIUM"},
|
||||
{"cookie": "x2", "type": "missing_purpose", "severity": "MEDIUM"},
|
||||
{"cookie": "x3", "type": "missing_purpose", "severity": "MEDIUM"},
|
||||
]},
|
||||
"impressum": {"findings": [
|
||||
{"title": "Vertretungsberechtigte fehlen", "severity": "MEDIUM",
|
||||
"status": "APPLICABLE", "recommendation": "Vertretungsberechtigte ergänzen."},
|
||||
], "status": "APPLICABLE", "confidence": 0.8},
|
||||
"browser": {"cross_findings": [
|
||||
"status": "fail", "recommendation": "Vertretungsberechtigte ergänzen."},
|
||||
], "mc_coverage": [1, 2, 3, 4]},
|
||||
"browser": {"browser_matrix": {"browser_matrix": [
|
||||
{"label": "Chromium", "score": 47,
|
||||
"summary": {"reject_respected": False, "violations": {"before_consent": 1}}},
|
||||
]}, "cross_findings": [
|
||||
{"title": "Tracking vor der Einwilligung — in allen Browsern",
|
||||
"severity": "HIGH", "detail": "Chrome + Firefox setzen Tracker vor Consent",
|
||||
"measure": "Tracking-Skripte erst nach aktiver Einwilligung laden."},
|
||||
"severity": "HIGH", "measure": "Tracking erst nach Consent laden."},
|
||||
]},
|
||||
}
|
||||
|
||||
|
||||
def test_sections_present():
|
||||
r = assemble_report(META, MODULES)
|
||||
titles = [s["title"] for s in r["sections"]]
|
||||
for t in ["Einleitung", "Testumfang & Methodik", "Management-Summary",
|
||||
"Detailbefunde", "Empfohlene Maßnahmen", "Rechtlicher Hinweis"]:
|
||||
def test_sections_present_incl_exec_and_gesamt():
|
||||
titles = [s["title"] for s in assemble_report(META, MODULES)["sections"]]
|
||||
for t in ["Executive Summary", "Einleitung", "Testumfang & Methodik",
|
||||
"Detailbefunde", "Browser-Übersicht", "Gesamtanalyse",
|
||||
"Empfohlene Maßnahmen", "Rechtlicher Hinweis"]:
|
||||
assert t in titles, f"Sektion fehlt: {t}"
|
||||
|
||||
|
||||
def test_severity_counts():
|
||||
def test_cookie_title_not_befund_fallback():
|
||||
md = render_markdown(assemble_report(META, MODULES))
|
||||
# Positiv: Cookie-Titel korrekt gebaut (Fallback "Befund" käme OHNE cto_bmw).
|
||||
assert "cto_bmw" in md and "Criteo" in md
|
||||
assert "Als notwendig deklariert" in md # Typ-Label statt roher type
|
||||
assert "**[Hoch]** Befund" not in md # kein nackter Fallback-Titel
|
||||
|
||||
|
||||
def test_top_n_cap_and_more_hint():
|
||||
# cookie hat 5 Befunde → nur Top-3 + Verweis auf „weitere".
|
||||
md = render_markdown(assemble_report(META, MODULES))
|
||||
assert "weitere Befund(e)" in md
|
||||
assert "abc-123" in md # Frontend-Link mit snapshot_id
|
||||
|
||||
|
||||
def test_severity_counts_and_bar():
|
||||
r = assemble_report(META, MODULES)
|
||||
c = r["totals"]["by_severity"]
|
||||
assert c["HIGH"] == 2 and c["MEDIUM"] == 1 and c["LOW"] == 1
|
||||
assert r["totals"]["findings"] == 4
|
||||
assert c["HIGH"] == 2 and c["MEDIUM"] == 4 and c["LOW"] == 1
|
||||
md = render_markdown(r)
|
||||
assert "▇" in md # Severity-Balken (Graphik)
|
||||
assert "Anforderungen (MCs) geprüft" in md # Modul-Statistik (Impressum mc_coverage)
|
||||
|
||||
|
||||
def test_markdown_has_header_findings_and_copilot_disclaimer():
|
||||
def test_copilot_disclaimer_and_no_normtext():
|
||||
md = render_markdown(assemble_report(META, MODULES))
|
||||
assert "Compliance-Audit-Bericht — BMW" in md
|
||||
assert "Tracking vor der Einwilligung" in md # Browser-Cross-Finding
|
||||
assert "Vertretungsberechtigte ergänzen" in md # Maßnahme aus recommendation
|
||||
assert "DSB" in md and "Anwalt" in md # Co-Pilot-Disclaimer
|
||||
assert "Wahrscheinlichkeit" in md # keine Garantie
|
||||
assert "BreakPilot" in md
|
||||
assert "DSB" in md and "Anwalt" in md and "Wahrscheinlichkeit" in md
|
||||
assert "§ 25 Abs. 2" in md and "nicht-essentielle" in md.lower()
|
||||
assert "BreakPilot" in md and "Browser-Übersicht" in md
|
||||
|
||||
|
||||
def test_empty_modules_graceful():
|
||||
@@ -54,12 +74,4 @@ def test_empty_modules_graceful():
|
||||
assert r["totals"]["findings"] == 0
|
||||
md = render_markdown(r)
|
||||
assert "keine wesentlichen auffälligkeiten" in md.lower()
|
||||
# Auch ohne Befunde: Disclaimer + Methodik vorhanden.
|
||||
assert "Rechtlicher Hinweis" in md
|
||||
|
||||
|
||||
def test_essential_cookie_framing_in_methodik():
|
||||
# Tonalität/Recht: technisch notwendige Cookies ausgenommen (§ 25 Abs. 2).
|
||||
md = render_markdown(assemble_report(META, MODULES))
|
||||
assert "§ 25 Abs. 2" in md
|
||||
assert "nicht-essentielle" in md.lower()
|
||||
|
||||
Reference in New Issue
Block a user