SPB Git

spb/job-ka

Public
3.1 KB · 80 lines python
1# =============================================================================2# Job·Ka — Groupe KA3# Auteur  : Simon-Pierre Boucher4# Contact : contact@spboucher.ai5# Fichier : tests/test_connectors.py6# Rôle    : Tests de régression des connecteurs (rejeu des fixtures hors ligne)7#           Ré-enregistrer après un changement de site : run.py record <source>8# Créé    : 2026-08-17   Modifié : 2026-08-179# =============================================================================10import json11import re1213import pytest1415from jobka.fixtures import FIXTURES_DIR, recorded_sources, replay_connector, snapshot1617SOURCES = recorded_sources()1819pytestmark = pytest.mark.skipif(20    not SOURCES, reason="aucune fixture enregistrée (run.py record <source>)")212223@pytest.fixture(scope="module")24def parsed():25    """Rejoue chaque connecteur une seule fois par session de test."""26    cache = {}2728    def _get(source_id):29        if source_id not in cache:30            cache[source_id] = replay_connector(source_id).fetch()31        return cache[source_id]3233    return _get343536@pytest.mark.parametrize("source_id", SOURCES)37def test_parsing_matches_snapshot(source_id, parsed):38    """Le parsing des fixtures reproduit exactement l'instantané enregistré."""39    expected = json.loads(40        (FIXTURES_DIR / source_id / "expected.json").read_text(encoding="utf-8"))41    actual = snapshot(parsed(source_id))42    assert actual["count"] == expected["count"]43    assert actual["jobs"] == expected["jobs"]444546@pytest.mark.parametrize("source_id", SOURCES)47def test_jobs_are_valid(source_id, parsed):48    """Contraintes de schéma : uid uniques, URLs absolues, salaires plausibles."""49    postings = parsed(source_id)50    assert postings, "le connecteur ne retourne aucune offre depuis la fixture"5152    uids = [p.uid for p in postings]53    assert len(uids) == len(set(uids)), "external_id en double"5455    for p in postings:56        assert p.source == source_id57        assert p.external_id58        assert p.title, f"offre sans titre : {p.uid}"59        assert p.employer, f"offre sans employeur : {p.uid}"60        assert re.match(r"^https?://", p.url), f"URL non absolue : {p.url!r}"61        if p.salary_min is not None:62            assert p.salary_unit in ("hour", "day", "week", "biweek", "month", "year")63            assert p.salary_max is None or p.salary_max >= p.salary_min64        # jamais de valeur inventée : chaîne vide ou None, pas de « N/A »65        assert (p.employment_type or "") not in ("N/A", "n/a", "?")666768@pytest.mark.parametrize("source_id", SOURCES)69def test_finalize_is_safe(source_id, parsed):70    """finalize() ne lève pas et produit des valeurs canoniques."""71    for p in parsed(source_id):72        f = p.finalize()73        assert f.work_mode in (None, "presentiel", "hybride", "teletravail")74        assert f.employment_type in (None, "temps_plein", "temps_partiel",75                                     "contractuel", "stage", "saisonnier")76        if f.date_posted is not None:77            assert re.fullmatch(r"\d{4}-\d{2}-\d{2}", f.date_posted)78        if f.lat is not None:79            assert 44.5 <= f.lat <= 63.0 and -80.0 <= f.lng <= -56.080
Files · 5d4b0232a0c7de36bcdf1183569b4bec3d8f612d
Menu

Theme