Files
breakpilot-compliance/backend-compliance/tests/test_widerrufsbelehrung_reachability_check.py
T
Benjamin Admin b9baa8c603 feat(b13): Widerrufsbelehrung-Reachability-Check (GT WIDERRUFSBELEHRUNG-001)
Erkennt B2C-Shop ohne öffentlich erreichbare Widerrufsbelehrung.
Schließt eine der offenen GT-Lücken aus dem Elli-Audit.

Signale:
  - doc_entries[widerruf]: discovery_attempted=True + Text leer
  - kein Footer-Link auf Widerruf/cancellation/rückgabe
  - B2C-Scope: Warenkorb/Kasse/Bestellung/MwSt/Wallbox/Tarif (strong)
    vs Shop/Produkt/Rechnung (weak, ≥2 = likely)
  - B2B-only-Override: "ausschließlich an Unternehmer" etc.

Severity:
  - HIGH bei b2c_strong
  - MEDIUM bei b2c_likely
  - kein Finding bei b2b_only / unknown (False-Positive-Schutz)

Norm: Art. 246a § 1 Abs. 2 Nr. 1 EGBGB i.V.m. § 312d BGB.

Wiring:
  - widerrufsbelehrung_reachability_check.py — Check + Scope-Detection
  - _b13_wiring.py — Render + state-Anschluss
  - _orchestrator.py — run_b13 nach run_b12
  - mail_render_v2/_compose.py — widerruf_reach_html-Block

Tests: 13/13 grün (Scope-Detection 5 + Check-Logik 8).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-07 00:04:41 +02:00

138 lines
4.8 KiB
Python

"""Tests for B13 Widerrufsbelehrung-Reachability-Check."""
from compliance.services.widerrufsbelehrung_reachability_check import (
_detect_b2c_scope,
check_widerrufsbelehrung_reachability,
)
def _state(widerruf_entry=None, home_text="", doc_entries=None,
footer_text=""):
entries = list(doc_entries or [])
if widerruf_entry is not None:
entries.append({"doc_type": "widerruf", **widerruf_entry})
return {
"doc_entries": entries,
"home_text": home_text,
"footer_text": footer_text,
}
class TestDetectB2CScope:
def test_strong_b2c_warenkorb(self):
s = _state(home_text="Legen Sie das Produkt in den Warenkorb.")
scope, hits = _detect_b2c_scope(s)
assert scope == "b2c_strong"
assert any("warenkorb" in h for h in hits)
def test_b2b_only_overrides_b2c(self):
s = _state(home_text="Wir verkaufen ausschließlich an Unternehmer. "
"Warenkorb für Großkunden.")
scope, _ = _detect_b2c_scope(s)
assert scope == "b2b_only"
def test_weak_signals_two_promote_to_likely(self):
s = _state(home_text="Unser Shop bietet Wallbox-Produkte mit "
"Rechnung zur Bestellung.")
scope, _ = _detect_b2c_scope(s)
assert scope == "b2c_likely"
def test_single_weak_signal_stays_unknown(self):
s = _state(home_text="Wir bieten einen Shop.")
scope, _ = _detect_b2c_scope(s)
assert scope == "unknown"
def test_empty_state(self):
s = _state()
scope, _ = _detect_b2c_scope(s)
assert scope == "unknown"
class TestCheck:
def test_no_widerruf_entry_no_finding(self):
out = check_widerrufsbelehrung_reachability(_state())
assert out == []
def test_widerruf_reachable_no_finding(self):
out = check_widerrufsbelehrung_reachability(_state(
widerruf_entry={
"url": "https://shop.de/widerruf",
"text": "Belehrung " * 100,
"discovery_attempted": True,
},
home_text="Warenkorb / zur Kasse.",
))
assert out == []
def test_unreachable_plus_b2c_strong_high_finding(self):
out = check_widerrufsbelehrung_reachability(_state(
widerruf_entry={
"url": "", "text": "",
"discovery_attempted": True,
"rejected_url": "https://shop.de/widerruf",
},
home_text="In den Warenkorb. Lieferzeit 2 Tage. Preis inkl. MwSt.",
))
assert len(out) == 1
f = out[0]
assert f["check_id"] == "WIDERRUF-REACH-001"
assert f["severity"] == "HIGH"
assert f["b2c_scope"] == "b2c_strong"
assert "Art. 246a" in f["norm"]
assert "shop.de/widerruf" in f["evidence"]
def test_unreachable_plus_b2c_likely_medium(self):
out = check_widerrufsbelehrung_reachability(_state(
widerruf_entry={
"url": "", "text": "",
"discovery_attempted": True,
},
home_text="Shop bietet Wallbox und Tarif buchen.",
))
assert len(out) == 1
assert out[0]["severity"] == "MEDIUM"
assert out[0]["b2c_scope"] == "b2c_likely"
def test_unreachable_plus_b2b_only_no_finding(self):
out = check_widerrufsbelehrung_reachability(_state(
widerruf_entry={
"url": "", "text": "",
"discovery_attempted": True,
},
home_text="B2B only — kein Verkauf an Verbraucher.",
))
assert out == []
def test_unreachable_plus_unknown_scope_no_finding(self):
# Pure agency / B2B-services without clear shop signals — silent.
out = check_widerrufsbelehrung_reachability(_state(
widerruf_entry={
"url": "", "text": "",
"discovery_attempted": True,
},
home_text="Wir sind eine Beratungsagentur für Mittelstand.",
))
assert out == []
def test_discovery_not_attempted_no_finding(self):
# Avoid false positives when discovery had no homepage to crawl.
out = check_widerrufsbelehrung_reachability(_state(
widerruf_entry={
"url": "", "text": "",
"discovery_attempted": False,
},
home_text="In den Warenkorb. Preis inkl. MwSt.",
))
assert out == []
def test_footer_widerruf_link_suppresses_finding(self):
out = check_widerrufsbelehrung_reachability(_state(
widerruf_entry={
"url": "", "text": "",
"discovery_attempted": True,
},
home_text="Warenkorb. Lieferzeit. Preis inkl. MwSt.",
footer_text='<a href="/widerruf">Widerruf</a>',
))
assert out == []