"""Smoke tests for mail-render V2.""" from unittest.mock import MagicMock from compliance.services.mail_render_v2._compose import compose_v2 from compliance.services.mail_render_v2._cookie_inventory import ( build_cookie_inventory, ) from compliance.services.mail_render_v2._style import ( card, chip, kpi_row, page_close, page_open, section, table, ) def _mock_check_result(label, doc_type, url, comp, corr, n_checks, n_fail): r = MagicMock() r.label = label r.doc_type = doc_type r.url = url r.completeness_pct = comp r.correctness_pct = corr r.error = "" r.scenario = "fix" checks = [] for i in range(n_checks): c = MagicMock() c.id = f"mc-{i}" c.label = f"Check {i}" c.passed = i >= n_fail c.severity = "HIGH" if i < n_fail else "LOW" c.skipped = False c.hint = f"Hint {i}" if i < n_fail else "" c.regulation = "DSGVO" c.level = 2 c.matched_text = "" checks.append(c) r.checks = checks return r def _full_state(): return { "check_id": "abc12345", "site_name": "Example AG", "domain": "example.de", "doc_count": 5, "results": [ _mock_check_result("Impressum", "impressum", "https://example.de/impressum", 95, 92, 10, 1), _mock_check_result("Datenschutzerklärung", "dse", "https://example.de/dse", 80, 63, 20, 5), ], "total_findings": 6, "cmp_vendors": [ {"name": "Google", "source": "table_crawled", "cookies": [ {"name": "_ga", "category": "Statistik", "duration": "14 Monate"}, {"name": "_gid", "category": "Statistik", "duration": "24h"}, ]}, {"name": "Meta", "source": "html_table", "cookies": [ {"name": "_fbp", "category": "Marketing", "duration": "3 Monate"}, ]}, ], "banner_result": { "banner_detected": True, "banner_provider": "Cookiebot", "cookies_detailed": [ {"name": "_ga", "domain": "example.de"}, {"name": "_fbp", "domain": "facebook.net"}, {"name": "undocumented_pixel", "domain": "tracker.com"}, ], "banner_checks": {"violations": [{"id": "v1"}]}, }, "cookie_audit": { "declared_count": 3, "browser_count": 3, "undeclared_in_browser": [{"name": "undocumented_pixel"}], "compliant": [{"name": "_ga"}, {"name": "_fbp"}], }, "scorecard": {"totals": {"pct": 72}}, "retention_findings": [ {"matches": False, "cookie_name": "_ga", "vendor_name": "Google", "severity": "HIGH", "severity_reason": "factually_wrong", "mismatch_type": "dsi_under_actual", "dsi_days": 180, "table_days": 420, "actual_days": 420, "diff_days": 240}, {"matches": True, "cookie_name": "_fbp", "severity": None, "severity_reason": None}, ], "retention_theme_summary": { "theme_id": "TH-RETENTION", "total": 2, "passed": 1, "failed": 1, "incomplete": 0, "pct": 50, "by_severity": {"HIGH": 1}, "by_mismatch_type": {}, "top_fails": [], }, "reachability_finding": { "check_id": "COOKIE-CONSENT-UX-001", "passed": False, "severity": "HIGH", "severity_reason": "missing", "notes": ["no consent-manager link in footer"], "reopen_anchor": None, "anchors_total": 0, }, "cookie_evidence_slices": [{"idx": 0}, {"idx": 1}], "cookie_evidence_meta": {"url": "https://example.de/cookie"}, "audit_quality_findings": [ {"severity": "MEDIUM", "title": "Cookie-URL nicht erreichbar", "message": "Auto-Discovery hat keine Alternative gefunden."}, ], } class TestStyleHelpers: def test_section_wraps_with_title(self): out = section("My Section", "

body

") assert "My Section" in out assert "

body

" in out def test_chip_renders_text(self): out = chip("FAIL", "fail") assert "FAIL" in out def test_table_basic(self): out = table(["A", "B"], [["1", "2"], ["3", "4"]]) assert "" in out assert ">1<" in out and ">4<" in out def test_kpi_row_4(self): out = kpi_row([ {"label": "Score", "value": "92%", "sev": "pass"}, {"label": "Findings", "value": "3"}, {"label": "Docs", "value": "5/7"}, {"label": "Vendors", "value": "12"}, ]) assert "Score" in out and "92%" in out def test_card_with_sev(self): out = card("inner", sev="warn") assert "inner" in out def test_page_wraps(self): head = page_open("Foo") tail = page_close("abc123", "deadbee") assert "Foo" not in head # site_name not in open shell assert "abc123" in tail and "deadbee" in tail class TestCookieInventory: def test_merge_declared_and_browser(self): st = _full_state() rows, summary = build_cookie_inventory(st) assert summary["total"] >= 3 names = [r["name"].lower() for r in rows] assert "_ga" in names assert "undocumented_pixel" in names def test_status_undoc_for_browser_only(self): st = _full_state() rows, _ = build_cookie_inventory(st) undoc = next(r for r in rows if r["name"].lower() == "undocumented_pixel") assert undoc["status_code"] == "UNDOC" assert undoc["status_sev"] == "fail" def test_status_ok_for_compliant(self): st = _full_state() rows, _ = build_cookie_inventory(st) ga = next(r for r in rows if r["name"].lower() == "_ga") assert ga["status_code"] == "OK" def test_empty_state(self): rows, summary = build_cookie_inventory({}) assert rows == [] assert summary["total"] == 0 class TestComposeV2: def test_full_render(self): st = _full_state() html = compose_v2(st) # Header assert "Example AG" in html assert "example.de" in html # KPIs assert "72%" in html # Critical assert "1. Kritische Befunde" in html # Per Doc assert "Impressum" in html assert "Datenschutzerklärung" in html # Per Theme assert "Cookie-Inventar" in html assert "UNDOC" in html # Reachability assert "Mobile Reachability" in html # Retention assert "TH-RETENTION" in html # Caveats assert "Cookie-URL nicht erreichbar" in html # Attachments assert "evidence-abc12345" in html def test_no_critical_when_clean(self): st = _full_state() st["results"] = [] st["reachability_finding"] = {"passed": True, "severity": None, "notes": []} st["retention_findings"] = [] html = compose_v2(st) assert "Keine HIGH/CRITICAL-Befunde" in html