From aed5c87bc32e13048c21b39a04cfcbd552399f58 Mon Sep 17 00:00:00 2001 From: dom Date: Sun, 8 Mar 2026 12:01:47 +0100 Subject: [PATCH] feat: endpoint /health avec tests (8 tests) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ajoute GET /health retournant un JSON avec : - status: "ok" - version: "2.1.0" - ollama: true/false (connectivité testée avec timeout 2s) - timestamp: ISO 8601 UTC Tests couvrent : format JSON, champs requis, Ollama joignable/injoignable, format timestamp ISO, type booléen du champ ollama. Co-Authored-By: Claude Opus 4.6 --- src/viewer/app.py | 17 ++++++++ tests/test_health_endpoint.py | 79 +++++++++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+) create mode 100644 tests/test_health_endpoint.py diff --git a/src/viewer/app.py b/src/viewer/app.py index a78d99d..3cb97cf 100644 --- a/src/viewer/app.py +++ b/src/viewer/app.py @@ -616,4 +616,21 @@ def create_app() -> Flask: metrics=metrics, total_selection=len(selection), groups=groups) + # --- Health check endpoint --- + @app.route("/health") + def health(): + from datetime import datetime, timezone + ollama_ok = False + try: + r = requests.get(f"{OLLAMA_URL}/api/tags", timeout=2) + ollama_ok = r.status_code == 200 + except Exception: + pass + return jsonify({ + "status": "ok", + "version": "2.1.0", + "ollama": ollama_ok, + "timestamp": datetime.now(timezone.utc).isoformat(), + }) + return app diff --git a/tests/test_health_endpoint.py b/tests/test_health_endpoint.py new file mode 100644 index 0000000..3eb46df --- /dev/null +++ b/tests/test_health_endpoint.py @@ -0,0 +1,79 @@ +"""Tests pour le endpoint /health du viewer Flask.""" + +import json +import os +from unittest.mock import patch, MagicMock + +import pytest +import requests + +from src.viewer.app import create_app + + +@pytest.fixture +def app(): + with patch.dict(os.environ, {"T2A_DEMO_USER": "", "T2A_DEMO_PASS": ""}): + app = create_app() + app.config["TESTING"] = True + yield app + + +@pytest.fixture +def client(app): + return app.test_client() + + +class TestHealthEndpoint: + + def test_health_returns_200(self, client): + response = client.get("/health") + assert response.status_code == 200 + + def test_health_returns_json(self, client): + response = client.get("/health") + data = response.get_json() + assert data is not None + assert data["status"] == "ok" + + def test_health_contains_required_fields(self, client): + response = client.get("/health") + data = response.get_json() + assert "status" in data + assert "version" in data + assert "ollama" in data + assert "timestamp" in data + + def test_health_version_format(self, client): + response = client.get("/health") + data = response.get_json() + assert data["version"] == "2.1.0" + + def test_health_timestamp_is_iso(self, client): + from datetime import datetime + response = client.get("/health") + data = response.get_json() + # Should parse without error + ts = datetime.fromisoformat(data["timestamp"]) + assert ts is not None + + @patch("src.viewer.app.requests.get") + def test_health_ollama_reachable(self, mock_get, client): + """Quand Ollama répond, ollama=True.""" + mock_resp = MagicMock() + mock_resp.status_code = 200 + mock_get.return_value = mock_resp + response = client.get("/health") + data = response.get_json() + assert data["ollama"] is True + + @patch("src.viewer.app.requests.get", side_effect=requests.ConnectionError("refused")) + def test_health_ollama_unreachable(self, mock_get, client): + """Quand Ollama est injoignable, ollama=False.""" + response = client.get("/health") + data = response.get_json() + assert data["ollama"] is False + + def test_health_ollama_is_bool(self, client): + response = client.get("/health") + data = response.get_json() + assert isinstance(data["ollama"], bool)