tests/test_extinfo.py
Rama main · réplica de lectura del repositorio original.
"""Tests info externes v1.8.0 (APIs publiques sans clé).
Le client HTTP est mocké au niveau de la classe (httpx.AsyncClient utilisé
en context manager dans extinfo) : on simule les réponses JSON sans réseau.
"""
from unittest.mock import patch
import pytest
from app import extinfo, tools # noqa: F401 (tools: vérif import côté routeur)
class _FakeResp:
def __init__(self, data, status=200):
self._data = data
self.status_code = status
def json(self):
return self._data
class _FakeAsyncClient:
"""Remplace httpx.AsyncClient : renvoie la réponse donnée (ou lève)."""
def __init__(self, resp=None, exc=None, *args, **kwargs):
self._resp, self._exc = resp, exc
async def __aenter__(self):
return self
async def __aexit__(self, *exc):
return False
async def get(self, url):
if self._exc:
raise self._exc
return self._resp
CF_OK = {"status": {"indicator": "none", "description": "All Systems Operational"}}
CF_MAJOR = {"status": {"indicator": "major", "description": "Elevated HTTP errors"}}
IP_OK = {"ip": "1.2.3.4"}
TIME_OK = {"unixtime": 1790880000, "utc_datetime": "2026-10-01T00:00:00+00:00"}
class TestCloudflare:
async def test_status_ok(self):
with patch_cf(_FakeAsyncClient(_FakeResp(CF_OK))):
out = await extinfo.cloudflare_status()
assert "🟢" in out and "Operational" in out
async def test_status_incident_majeur(self):
with patch_cf(_FakeAsyncClient(_FakeResp(CF_MAJOR))):
out = await extinfo.cloudflare_status()
assert "🟠" in out and "HTTP errors" in out
async def test_status_http_500(self):
with patch_cf(_FakeAsyncClient(_FakeResp({}, status=500))):
out = await extinfo.cloudflare_status()
assert "🔴" in out
async def test_injoignable(self):
with patch_cf(_FakeAsyncClient(exc=RuntimeError("boom"))):
out = await extinfo.cloudflare_status()
assert "🔴" in out
class TestIpPublique:
async def test_ip_ok(self):
with patch_cf(_FakeAsyncClient(_FakeResp(IP_OK))):
out = await extinfo.public_ip()
assert "1.2.3.4" in out
async def test_ip_ko(self):
with patch_cf(_FakeAsyncClient(exc=RuntimeError("x"))):
out = await extinfo.public_ip()
assert "🔴" in out
class TestHorloge:
async def test_derive_ok(self):
import time as _t
fake = {"unixtime": _t.time()}
with patch_cf(_FakeAsyncClient(_FakeResp(fake))):
out = await extinfo.clock_check()
assert "🟢" in out
async def test_derive_grande(self):
fake = {"unixtime": 0} # 1970 : dérive énorme
with patch_cf(_FakeAsyncClient(_FakeResp(fake))):
out = await extinfo.clock_check()
assert "🟠" in out
class TestRapportGlobal:
async def test_rapport_ne_leve_jamais(self):
async def boom(*a, **k):
raise RuntimeError("x")
with patch.object(extinfo, "cloudflare_status", boom), \
patch.object(extinfo, "public_ip", boom), \
patch.object(extinfo, "clock_check", boom):
out = await extinfo.external_report()
assert "Informations externes" in out
async def test_rapport_complet(self):
with patch_cf(_FakeAsyncClient(_FakeResp(CF_OK))):
out = await extinfo.external_report()
assert "Cloudflare" in out
def patch_cf(client):
"""Patch httpx.AsyncClient par une factory qui ignore les args (timeout…)."""
import unittest.mock as m
return m.patch.object(extinfo.httpx, "AsyncClient", lambda *a, **k: client)