SPB Git forge

spb/api-ka

Public

API-KA — plateforme centrale : collecte quotidienne des 8 services KA, historisation append-only et API publique sur www.api-ka.com

48commits 1branches 0releases
5.9 MBsize
maindefault branch
19 days agolast push
Python 60.9% HTML 21% TypeScript 7.3% JavaScript 5.2% CSS 4.8% Shell 0.8%
6.0 KB · 173 lines python
Raw Blame History
1# ============================================2# Projet   : API-KA3# Fichier  : tests/test_api.py4# Node     : m3u96b5# Author   : Simon-Pierre Boucher6# Contact  : contact@spboucher.ai7# Date     : 2026-08-168# ============================================9"""Tests des routes de l'API publique : format uniforme, pagination, 404, /health."""1011from __future__ import annotations1213import datetime14import hashlib15import json1617import pytest18from fastapi.testclient import TestClient1920from src.api.main import app21from src.database.db import session_scope22from src.database.models import DATA_MODELS, CollectionRun2324SEED_DATE = datetime.date(2026, 8, 10)252627def _seed_louka() -> None:28    model = DATA_MODELS["louka"]29    now = datetime.datetime.now(tz=datetime.UTC)30    with session_scope() as session:31        for i in range(3):32            payload = {"id": i, "nom": f"louka-{i}"}33            checksum = hashlib.sha256(34                json.dumps(payload, sort_keys=True, separators=(",", ":")).encode()35            ).hexdigest()36            exists = (37                session.query(model)38                .filter_by(source="louka", date_key=SEED_DATE, checksum=checksum)39                .first()40            )41            if not exists:42                session.add(43                    model(44                        payload=payload,45                        source="louka",46                        collected_at=now,47                        date_key=SEED_DATE,48                        checksum=checksum,49                    )50                )51        session.add(52            CollectionRun(53                service="louka",54                date_key=SEED_DATE,55                status="success",56                records_count=3,57                duration_seconds=1.2,58                error_message=None,59                node="m3u96b",60                started_at=now,61                finished_at=now,62            )63        )646566@pytest.fixture(scope="module")67def client():68    """Client de test FastAPI avec cycle de vie (lifespan) actif."""69    _seed_louka()70    with TestClient(app) as test_client:71        yield test_client727374def test_root(client: TestClient) -> None:75    """GET / retourne le statut de la plateforme et la version."""76    response = client.get("/")77    assert response.status_code == 20078    body = response.json()79    assert body["success"] is True80    assert body["data"]["platform"] == "API-KA"81    assert body["data"]["version"]82    assert "node" in body["meta"]838485def test_root_serves_web_platform_to_browsers(client: TestClient) -> None:86    """GET / avec Accept: text/html sert la plateforme web (docs + playground)."""87    response = client.get("/", headers={"Accept": "text/html"})88    assert response.status_code == 20089    assert response.headers["content-type"].startswith("text/html")90    assert "Playground" in response.text91    assert "API-KA" in response.text929394def test_health(client: TestClient) -> None:95    """GET /health expose le node, l'état DB et les dernières collectes."""96    response = client.get("/health")97    assert response.status_code == 20098    data = response.json()["data"]99    assert data["database"] == "ok"100    assert data["required_node"] == "m3u96b"101    assert data["last_collections"]["louka"] is not None102    assert data["last_collections"]["louka"]["records_count"] == 3103104105def test_list_service_data(client: TestClient) -> None:106    """GET /api/v1/louka pagine les données au format uniforme."""107    response = client.get("/api/v1/louka", params={"page": 1, "limit": 2})108    assert response.status_code == 200109    body = response.json()110    assert body["success"] is True111    assert len(body["data"]) == 2112    assert body["meta"]["page"] == 1113    assert body["meta"]["limit"] == 2114    assert body["meta"]["total"] >= 3115116117def test_latest(client: TestClient) -> None:118    """GET /api/v1/louka/latest retourne la collecte la plus récente."""119    response = client.get("/api/v1/louka/latest")120    assert response.status_code == 200121    body = response.json()122    assert body["meta"]["date_key"] == SEED_DATE.isoformat()123    assert len(body["data"]) == 3124125126def test_by_date(client: TestClient) -> None:127    """GET /api/v1/louka/date/{date} filtre par date logique."""128    response = client.get(f"/api/v1/louka/date/{SEED_DATE.isoformat()}")129    assert response.status_code == 200130    assert response.json()["meta"]["total"] == 3131132    empty = client.get("/api/v1/louka/date/2020-01-01")133    assert empty.status_code == 200134    assert empty.json()["meta"]["total"] == 0135136    invalid = client.get("/api/v1/louka/date/pas-une-date")137    assert invalid.status_code == 422138139140def test_stats(client: TestClient) -> None:141    """GET /api/v1/louka/stats agrège par jour et expose la dernière réussite."""142    response = client.get("/api/v1/louka/stats")143    assert response.status_code == 200144    data = response.json()["data"]145    assert data["total_records"] >= 3146    assert any(day["date_key"] == SEED_DATE.isoformat() for day in data["days"])147    assert data["last_success"]["status"] == "success"148149150def test_unknown_service_is_404(client: TestClient) -> None:151    """Tout service hors de la liste officielle → 404."""152    assert client.get("/api/v1/inconnu").status_code == 404153    assert client.get("/api/v1/inconnu/latest").status_code == 404154    assert client.get("/api/v1/inconnu/stats").status_code == 404155156157def test_limit_is_capped_at_500(client: TestClient) -> None:158    """limit > 500 est rejeté (422)."""159    response = client.get("/api/v1/louka", params={"limit": 501})160    assert response.status_code == 422161162163def test_runs_endpoint(client: TestClient) -> None:164    """GET /api/v1/runs liste l'historique, filtrable par service et statut."""165    response = client.get("/api/v1/runs", params={"service": "louka"})166    assert response.status_code == 200167    body = response.json()168    assert body["meta"]["total"] >= 1169    assert all(run["service"] == "louka" for run in body["data"])170171    assert client.get("/api/v1/runs", params={"service": "inconnu"}).status_code == 404172    assert client.get("/api/v1/runs", params={"status": "invalide"}).status_code == 422173