"""Tests des endpoints : succès, vide, erreur amont, timeout, cache périmé, rate-limit du refresh, santé.""" import time import pytest from fastapi.testclient import TestClient import app as app_module import sources FIRE_OK = { "id": "abc123", "lat": 43.3, "lon": 5.4, "acquired_at": "2026-07-27T01:37:00Z", "source": "NASA FIRMS VIIRS S-NPP (Europe 24 h)", "satellite": "N", "instrument": "VIIRS", "frp": 12.3, "brightness": 312.5, "confidence": "nominal", "daynight": "N", "type": "thermal_anomaly", } def _result(fires, used=None): sts = [sources.SourceStatus(name=u or "src", ok=True, latency_ms=42, last_success="2026-07-27T10:00:00Z") for u in (used or ["src"])] return sources.FetchResult(fires=fires, sources_used=used or ["src"], statuses=sts) @pytest.fixture() def client(monkeypatch): monkeypatch.setattr(app_module, "_cache", {"payload": None, "fetched_at": 0.0}) monkeypatch.setattr(app_module, "_last_forced", 0.0) monkeypatch.setattr(app_module, "_last_statuses", []) return TestClient(app_module.app) def test_fires_succes(client, monkeypatch): monkeypatch.setattr(sources, "fetch_all", lambda: _result([FIRE_OK])) r = client.get("/api/fires") assert r.status_code == 200 body = r.json() assert body["status"] == "ok" assert body["count"] == 1 assert body["fires"][0]["id"] == "abc123" assert body["data_observed_at"] == "2026-07-27T01:37:00Z" assert body["stale"] is False assert body["errors"] == [] assert body["attribution"] def test_fires_vide_honnete(client, monkeypatch): """Amont OK mais zéro feu : état vide honnête, pas de données fabriquées.""" monkeypatch.setattr(sources, "fetch_all", lambda: _result([])) body = client.get("/api/fires").json() assert body["status"] == "ok" assert body["count"] == 0 assert body["fires"] == [] assert body["data_observed_at"] is None def test_erreur_amont_sans_cache(client, monkeypatch): def boom(): raise sources.UpstreamError("toutes les sources ont échoué") monkeypatch.setattr(sources, "fetch_all", boom) body = client.get("/api/fires").json() assert body["status"] == "error" assert body["fires"] == [] assert body["errors"] def test_cache_frais_pas_de_nouvel_appel(client, monkeypatch): appels = [] def fake(): appels.append(1) return _result([FIRE_OK]) monkeypatch.setattr(sources, "fetch_all", fake) client.get("/api/fires") client.get("/api/fires") assert len(appels) == 1 # 2e appel servi par le cache def test_cache_perime_apres_echec(client, monkeypatch): monkeypatch.setattr(sources, "fetch_all", lambda: _result([FIRE_OK])) first = client.get("/api/fires").json() assert first["stale"] is False # Cache expiré + amont en panne -> repli sur cache marqué périmé app_module._cache["fetched_at"] = time.time() - (app_module.TTL_S + 10) def boom(): raise sources.UpstreamError("timeout amont") monkeypatch.setattr(sources, "fetch_all", boom) body = client.get("/api/fires").json() assert body["status"] == "ok" assert body["stale"] is True assert body["count"] == 1 assert body["errors"] def test_timeout_traite_comme_erreur(client, monkeypatch): def boom(): raise sources.UpstreamError("ReadTimeout: délai dépassé") monkeypatch.setattr(sources, "fetch_all", boom) body = client.get("/api/fires").json() assert body["status"] == "error" def test_refresh_force_et_rate_limit(client, monkeypatch): appels = [] def fake(): appels.append(1) return _result([FIRE_OK]) monkeypatch.setattr(sources, "fetch_all", fake) client.get("/api/fires") app_module._cache["fetched_at"] = time.time() - (app_module.TTL_S + 10) r = client.post("/api/refresh") assert r.status_code == 200 assert len(appels) == 2 # Second refresh dans les 30 s : pas de nouvel appel amont app_module._cache["fetched_at"] = time.time() - (app_module.TTL_S + 10) client.post("/api/refresh") assert len(appels) == 2 def test_health_apres_succes(client, monkeypatch): monkeypatch.setattr(sources, "fetch_all", lambda: _result([FIRE_OK], ["NASA FIRMS test"])) client.get("/api/fires") r = client.get("/api/health") assert r.status_code == 200 body = r.json() assert body["status"] in ("ok", "degraded") assert body["version"] assert body["time"].endswith("Z") assert body["sources"][0]["name"] == "NASA FIRMS test" assert body["sources"][0]["ok"] is True assert body["cache"]["ttl_s"] == app_module.TTL_S assert body["cache"]["age_s"] is not None def test_health_sans_donnees(client): body = client.get("/api/health").json() assert body["status"] == "degraded" assert body["cache"]["age_s"] is None