# ----------------------------------------------------------------------------- # Lou-Ka — tests de régression des connecteurs (rejeu des fixtures hors-ligne) # Ré-enregistrer une fixture après un changement de site : # .venv/bin/python run.py record # ----------------------------------------------------------------------------- import json import re import pytest from louka.fixtures import FIXTURES_DIR, recorded_sources, replay_connector, snapshot SOURCES = recorded_sources() pytestmark = pytest.mark.skipif( not SOURCES, reason="aucune fixture enregistrée (run.py record )") @pytest.fixture(scope="module") def parsed(): """Rejoue chaque connecteur une seule fois par session de test.""" cache = {} def _get(source_id): if source_id not in cache: cache[source_id] = replay_connector(source_id).fetch() return cache[source_id] return _get @pytest.mark.parametrize("source_id", SOURCES) def test_parsing_matches_snapshot(source_id, parsed): """Le parsing des fixtures reproduit exactement l'instantané enregistré.""" expected = json.loads( (FIXTURES_DIR / source_id / "expected.json").read_text(encoding="utf-8")) actual = snapshot(parsed(source_id)) assert actual["count"] == expected["count"] assert actual["listings"] == expected["listings"] @pytest.mark.parametrize("source_id", SOURCES) def test_listings_are_valid(source_id, parsed): """Contraintes de schéma : uid uniques, URLs absolues, prix plausibles.""" listings = parsed(source_id) assert listings, "le connecteur ne retourne aucune annonce depuis la fixture" uids = [l.uid for l in listings] assert len(uids) == len(set(uids)), "external_id en double" for l in listings: assert l.source == source_id assert l.external_id assert re.match(r"^https?://", l.url), f"URL non absolue : {l.url!r}" assert l.price is None or 100 <= l.price <= 20000 assert l.area_sqft is None or 80 <= l.area_sqft <= 20000 for img in l.images: assert re.match(r"^https?://", img), f"image non absolue : {img!r}" # jamais de valeur inventée : chaîne vide ou None, pas de « N/A » assert (l.unit_type or "") not in ("N/A", "n/a", "?")