maboto/tests/test_fetch_abbruch_ort.py
Jeuner 39b8a98bc2 Initiale Implementierung: Angebots-Übersicht (Fetch + Kategorisierung + Web-UI)
Der Schnitt aus CLAUDE.md ist durchgehalten:
- Fetch (deterministisch, kein LLM): marktguru-Adapter mit geprüftem
  Ortsbezug (zipCode), Wochen-Cache, robots.txt-Respekt, ehrlicher Regel-4-
  Abbruch bei fehlendem Beleg statt Krücke.
- Kategorisierung (einziger LLM-Ort): geschlossene Liste + Daten-Integrität
  als Code erzwungen; austauschbar via Protokoll (OpenRouter/Anthropic),
  mit Drosselung/Retry und ehrlichem Abbruch.
- FastAPI-Web-UI als dünne Schicht: Modellauswahl (Liste/Suche/Refresh),
  Live-Fortschritt, gruppierte Ergebnisse mit Filtern, Ergebnis-Cache.
- 36 Tests gegen die Architektur-Regeln (kein Auffüllen, Abbruch, Integrität,
  geschlossene Liste, Unsicherheit, Schnitt) und die Web-Schicht.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 09:29:59 +02:00

41 lines
1.4 KiB
Python

"""Regel 4: Abbruch statt stiller Drift -- mit Schwelle, Ursache, Vorschlag."""
import pytest
from angebote.fehler import AbbruchFehler
from angebote.fetch import aufloesen_ort, hole_angebote
from tests.fakes import FakeQuelle, beispiel_angebot
def test_unaufloesbarer_ort_bricht_ab():
with pytest.raises(AbbruchFehler) as exc:
aufloesen_ort("Hintertupfingen")
e = exc.value
# Der Abbruch ist brauchbar: alle drei Felder sind belegt.
assert e.schwelle and e.ursache and e.vorschlag
assert "PLZ" in e.vorschlag
def test_plz_wird_direkt_aufgeloest():
ort = aufloesen_ort("60487")
assert ort.plz == "60487"
def test_bekannter_ortsname_wird_aufgeloest():
ort = aufloesen_ort("Frankfurt")
assert ort.plz and ort.plz.isdigit()
def test_keine_quelle_deckt_ort_ab_bricht_ab():
with pytest.raises(AbbruchFehler) as exc:
hole_angebote("60487", [FakeQuelle("woanders", [], deckt=False)])
assert exc.value.schwelle == "Ortsabdeckung"
def test_adapter_abbruch_propagiert_nicht_kaschiert():
# Eine Quelle, die ihren Ortsbezug nicht herstellen kann, bricht ab --
# der Orchestrator schluckt das NICHT zu einem leeren Ergebnis.
fehler = AbbruchFehler("Quelle X", "Ortsbezug nicht verifiziert", "andere Quelle")
quelle = FakeQuelle("kaputt", [beispiel_angebot()], fehler=fehler)
with pytest.raises(AbbruchFehler):
hole_angebote("60487", [quelle])