feat: HUB-21 — réglages généraux du Hub consommés par ObsiGate (bannière + drapeau recherche_partout) #229
This commit is contained in:
@@ -0,0 +1,213 @@
|
||||
# tests/test_hub_reglages.py
|
||||
# Hub HUB-21 (propagation) — Réglages généraux du Hub consommés par
|
||||
# ObsiGate : client (parsing de la forme figée, cache 60 s, repli
|
||||
# intégral), bannière injectée côté serveur (page principale et page
|
||||
# /recherche, texte échappé) et drapeau ``recherche_partout`` rendu
|
||||
# en attribut de la page /recherche. Patron posé par l'hôte pilote
|
||||
# TikTak 0.61.22. ``domaine_defaut`` n'est pas appliqué : ObsiGate
|
||||
# n'a pas de notion de domaine propre (voir backend/hub_reglages.py).
|
||||
|
||||
import json
|
||||
import time
|
||||
import urllib.error
|
||||
|
||||
import pytest
|
||||
|
||||
from backend import hub_reglages
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def _hub_env(monkeypatch):
|
||||
"""Cache vierge + Hub de test explicite pour chaque test."""
|
||||
hub_reglages.reinitialiser_cache()
|
||||
monkeypatch.setenv("DRACO_HUB_URL", "https://hub-test.dracodev.net")
|
||||
yield
|
||||
hub_reglages.reinitialiser_cache()
|
||||
|
||||
|
||||
def _reponse(corps=None, statut=200):
|
||||
"""Fausse réponse urlopen portant un payload du contrat D6(a)."""
|
||||
if corps is None:
|
||||
corps = {"recherche_partout": True, "banniere": "",
|
||||
"domaine_defaut": None, "ttl_secondes": 60}
|
||||
charge = json.dumps(corps).encode("utf-8")
|
||||
|
||||
class _Resp:
|
||||
status = statut
|
||||
|
||||
def read(self):
|
||||
return charge
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, *exc):
|
||||
return False
|
||||
|
||||
return _Resp()
|
||||
|
||||
|
||||
def _patch_consultation(monkeypatch, reglages, appels=None):
|
||||
"""Remplace la consultation HTTP par des réglages fixes."""
|
||||
def _faux(url):
|
||||
if appels is not None:
|
||||
appels.append(url)
|
||||
return reglages
|
||||
monkeypatch.setattr(hub_reglages, "_consulter_hub", _faux)
|
||||
|
||||
|
||||
# ── Client : parsing du contrat ─────────────────────────────────────
|
||||
|
||||
def test_consultation_parse_le_contrat(monkeypatch):
|
||||
vu = {}
|
||||
|
||||
def _faux_urlopen(requete, timeout=None):
|
||||
vu["url"] = requete.full_url
|
||||
vu["cookie"] = requete.get_header("Cookie")
|
||||
vu["accept"] = requete.get_header("Accept")
|
||||
vu["timeout"] = timeout
|
||||
return _reponse({"recherche_partout": False,
|
||||
"banniere": "Maintenance dimanche",
|
||||
"domaine_defaut": "famille", "ttl_secondes": 60})
|
||||
|
||||
monkeypatch.setattr(hub_reglages.urllib.request, "urlopen", _faux_urlopen)
|
||||
reglages = hub_reglages.reglages_effectifs()
|
||||
assert reglages == {"recherche_partout": False,
|
||||
"banniere": "Maintenance dimanche",
|
||||
"domaine_defaut": "famille"}
|
||||
assert vu["url"] == "https://hub-test.dracodev.net/api/reglages/effectifs"
|
||||
assert vu["cookie"] is None # endpoint public : aucun cookie
|
||||
assert vu["accept"] == "application/json"
|
||||
assert vu["timeout"] == 2 # délai du contrat
|
||||
|
||||
|
||||
@pytest.mark.parametrize("corps", [
|
||||
{"recherche_partout": "oui", "banniere": "", "domaine_defaut": None},
|
||||
{"recherche_partout": True, "banniere": 42, "domaine_defaut": None},
|
||||
{"recherche_partout": True, "banniere": "", "domaine_defaut": "travail"},
|
||||
{"banniere": "", "domaine_defaut": None},
|
||||
["pas", "un", "dict"],
|
||||
])
|
||||
def test_consultation_forme_inattendue_repli(monkeypatch, corps):
|
||||
monkeypatch.setattr(hub_reglages.urllib.request, "urlopen",
|
||||
lambda requete, timeout=None: _reponse(corps))
|
||||
assert hub_reglages.reglages_effectifs() == hub_reglages.DEFAUTS
|
||||
|
||||
|
||||
def test_consultation_erreur_http_repli(monkeypatch):
|
||||
monkeypatch.setattr(hub_reglages.urllib.request, "urlopen",
|
||||
lambda requete, timeout=None: _reponse(statut=500))
|
||||
assert hub_reglages.reglages_effectifs() == hub_reglages.DEFAUTS
|
||||
assert hub_reglages.banniere() == ""
|
||||
assert hub_reglages.recherche_partout() is True
|
||||
|
||||
|
||||
def test_consultation_hub_injoignable_repli(monkeypatch):
|
||||
def _leve(requete, timeout=None):
|
||||
raise urllib.error.URLError("injoignable")
|
||||
monkeypatch.setattr(hub_reglages.urllib.request, "urlopen", _leve)
|
||||
assert hub_reglages.reglages_effectifs() == hub_reglages.DEFAUTS
|
||||
|
||||
|
||||
def test_consultation_json_illisible_repli(monkeypatch):
|
||||
class _Resp:
|
||||
status = 200
|
||||
|
||||
def read(self):
|
||||
return b"pas du json"
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, *exc):
|
||||
return False
|
||||
|
||||
monkeypatch.setattr(hub_reglages.urllib.request, "urlopen",
|
||||
lambda requete, timeout=None: _Resp())
|
||||
assert hub_reglages.reglages_effectifs() == hub_reglages.DEFAUTS
|
||||
|
||||
|
||||
# ── Client : cache 60 s, ancien cache, pas de rafale ────────────────
|
||||
|
||||
def test_cache_60_s(monkeypatch):
|
||||
appels = []
|
||||
_patch_consultation(monkeypatch, {
|
||||
"recherche_partout": False, "banniere": "Annonce A",
|
||||
"domaine_defaut": None}, appels)
|
||||
assert hub_reglages.banniere() == "Annonce A"
|
||||
assert hub_reglages.recherche_partout() is False
|
||||
assert len(appels) == 1 # cache 60 s
|
||||
hub_reglages._cache_at = time.monotonic() - 61
|
||||
assert hub_reglages.banniere() == "Annonce A"
|
||||
assert len(appels) == 2 # expiration → rappel
|
||||
|
||||
|
||||
def test_echec_ancien_cache_sans_rafale(monkeypatch):
|
||||
appels = []
|
||||
_patch_consultation(monkeypatch, {
|
||||
"recherche_partout": True, "banniere": "Annonce A",
|
||||
"domaine_defaut": None}, appels)
|
||||
assert hub_reglages.banniere() == "Annonce A"
|
||||
hub_reglages._cache_at = time.monotonic() - 61
|
||||
_patch_consultation(monkeypatch, None, appels) # Hub tombe
|
||||
assert hub_reglages.banniere() == "Annonce A" # ancien cache
|
||||
assert len(appels) == 2 # pas de rafale
|
||||
assert hub_reglages.banniere() == "Annonce A"
|
||||
assert len(appels) == 2
|
||||
|
||||
|
||||
# ── Intégration : bannière et drapeau dans les pages servies ────────
|
||||
|
||||
@pytest.fixture()
|
||||
def hub_banniere(monkeypatch):
|
||||
etat = {"reglages": None}
|
||||
monkeypatch.setattr(hub_reglages, "_consulter_hub",
|
||||
lambda url: etat["reglages"])
|
||||
hub_reglages.reinitialiser_cache()
|
||||
return etat
|
||||
|
||||
|
||||
def test_banniere_presente_et_echappee_index(client, hub_banniere):
|
||||
hub_banniere["reglages"] = {
|
||||
"recherche_partout": True,
|
||||
"banniere": "Maintenance dimanche <b>gras</b> & cie",
|
||||
"domaine_defaut": None}
|
||||
r = client.get("/")
|
||||
assert r.status_code == 200
|
||||
assert 'id="banniere-hub"' in r.text
|
||||
assert "Maintenance dimanche <b>gras</b> & cie" in r.text
|
||||
assert "obsigate_banniere_masquee" in r.text
|
||||
|
||||
|
||||
def test_banniere_absente_si_vide_ou_hub_muet(client, hub_banniere):
|
||||
hub_banniere["reglages"] = {
|
||||
"recherche_partout": True, "banniere": "", "domaine_defaut": "pro"}
|
||||
r = client.get("/")
|
||||
assert r.status_code == 200
|
||||
assert "banniere-hub" not in r.text
|
||||
hub_banniere["reglages"] = None
|
||||
hub_reglages.reinitialiser_cache()
|
||||
r = client.get("/")
|
||||
assert r.status_code == 200
|
||||
assert "banniere-hub" not in r.text
|
||||
|
||||
|
||||
def test_recherche_banniere_et_drapeau(client, hub_banniere):
|
||||
hub_banniere["reglages"] = {
|
||||
"recherche_partout": False, "banniere": "Annonce recherche",
|
||||
"domaine_defaut": None}
|
||||
r = client.get("/recherche")
|
||||
assert r.status_code == 200
|
||||
assert 'data-recherche-partout="false"' in r.text
|
||||
assert 'id="banniere-hub"' in r.text
|
||||
assert "Annonce recherche" in r.text
|
||||
hub_banniere["reglages"] = {
|
||||
"recherche_partout": True, "banniere": "", "domaine_defaut": None}
|
||||
hub_reglages.reinitialiser_cache()
|
||||
r = client.get("/recherche")
|
||||
assert 'data-recherche-partout="true"' in r.text
|
||||
assert "banniere-hub" not in r.text
|
||||
hub_banniere["reglages"] = None # Hub muet → défaut actif
|
||||
hub_reglages.reinitialiser_cache()
|
||||
r = client.get("/recherche")
|
||||
assert 'data-recherche-partout="true"' in r.text
|
||||
Reference in New Issue
Block a user