"""Tests for system routes — health, version. These tests use the FastAPI TestClient against the real app. The health and version endpoints do NOT require authentication, so we can test them without setting up the full dependency injection. """ import pytest from fastapi.testclient import TestClient from ledgrab import __version__ from ledgrab.config import get_config def _auth_headers() -> dict[str, str]: """Resolve the configured API key lazily so test ordering doesn't matter. Evaluating ``get_config()`` at module import time produced a stale empty key when other tests mutated the global config before this module ran. """ api_key = next(iter(get_config().auth.api_keys.values()), "") return {"Authorization": f"Bearer {api_key}"} if api_key else {} @pytest.fixture(scope="module") def client(): """Provide a test client for the main app. The app module initializes stores from the default config on import, which is acceptable for read-only endpoints tested here. """ from ledgrab.main import app return TestClient(app, raise_server_exceptions=False) class TestHealthEndpoint: def test_health_returns_200(self, client): resp = client.get("/health") assert resp.status_code == 200 def test_health_response_structure(self, client): data = client.get("/health").json() assert data["status"] == "healthy" assert data["version"] == __version__ assert "timestamp" in data def test_health_no_auth_required(self, client): """Health endpoint should work without Authorization header.""" resp = client.get("/health") assert resp.status_code == 200 class TestVersionEndpoint: def test_version_returns_200(self, client): resp = client.get("/api/v1/version") assert resp.status_code == 200 def test_version_response_fields(self, client): data = client.get("/api/v1/version").json() assert data["version"] == __version__ assert "python_version" in data assert data["api_version"] == "v1" assert "demo_mode" in data class TestOpenAPIEndpoint: def test_openapi_available(self, client): resp = client.get("/openapi.json", headers=_auth_headers()) assert resp.status_code == 200 data = resp.json() assert "info" in data assert data["info"]["version"] == __version__ def test_swagger_ui(self, client): resp = client.get("/docs", headers=_auth_headers()) assert resp.status_code == 200 assert "text/html" in resp.headers["content-type"] def test_openapi_requires_auth(self, client): """Without a valid bearer token, the OpenAPI surface is unreachable.""" resp = client.get("/openapi.json") assert resp.status_code == 401 def test_swagger_requires_auth(self, client): resp = client.get("/docs") assert resp.status_code == 401 class TestRootEndpoint: def test_root_returns_html(self, client): resp = client.get("/") assert resp.status_code == 200 assert "text/html" in resp.headers["content-type"] class TestInstalledAppsEndpoint: def test_requires_auth(self, client): resp = client.get("/api/v1/system/installed-apps") assert resp.status_code == 401 def test_empty_off_android(self, client): """Desktop test host: is_android() is False, so the bridge wrapper short-circuits to an empty list.""" resp = client.get("/api/v1/system/installed-apps", headers=_auth_headers()) assert resp.status_code == 200 assert resp.json() == {"apps": [], "count": 0} def test_returns_apps_when_available(self, client, monkeypatch): from ledgrab.core.automations import platform_detector as pd monkeypatch.setattr( pd, "list_installed_apps", lambda: [{"package": "com.netflix.mediaclient", "label": "Netflix"}], ) resp = client.get("/api/v1/system/installed-apps", headers=_auth_headers()) assert resp.status_code == 200 data = resp.json() assert data["count"] == 1 assert data["apps"][0] == {"package": "com.netflix.mediaclient", "label": "Netflix"} class TestSystemInfoEndpoint: def test_requires_auth(self, client): resp = client.get("/api/v1/system/info") assert resp.status_code == 401 def test_desktop_signal(self, client): resp = client.get("/api/v1/system/info", headers=_auth_headers()) assert resp.status_code == 200 data = resp.json() assert data["is_android"] is False assert data["app_match_kind"] == "process" assert data["usage_access_granted"] is True def test_android_signal(self, client, monkeypatch): import ledgrab.utils.platform as plat from ledgrab.core.automations import platform_detector as pd monkeypatch.setattr(plat, "is_android", lambda: True) monkeypatch.setattr(pd, "has_usage_access", lambda: False) resp = client.get("/api/v1/system/info", headers=_auth_headers()) assert resp.status_code == 200 data = resp.json() assert data["is_android"] is True assert data["app_match_kind"] == "package" assert data["usage_access_granted"] is False