"""Proxy routes: read-only guarantee, upstream failure translation, JSON shape. Nothing here touches a socket — see conftest.FakeUpstream. The point is to pin the contract the browser depends on: what passes through, what becomes JSON, and exactly how an engine outage shows up. """ from __future__ import annotations import json import httpx import pytest import app as app_module from tests.conftest import GUFO_FIXTURE, SLOTS_FIXTURE, VLLM_FIXTURE, response API_PATHS = ["/api/gufo/slots", "/api/gufo/metrics", "/api/vllm/metrics", "/api/config"] GUFO_PATHS = ["/api/gufo/slots", "/api/gufo/metrics"] VLLM_PATHS = ["/api/vllm/metrics"] # --------------------------------------------------------------------------- # # read-only by construction # --------------------------------------------------------------------------- # @pytest.mark.parametrize("method", ["post", "put", "patch", "delete"]) @pytest.mark.parametrize("path", API_PATHS) def test_no_writing_verbs(client, method: str, path: str) -> None: assert getattr(client, method)(path).status_code == 405 def test_only_get_reaches_upstream(client, upstream) -> None: upstream.route("http://gufo.test/slots", response(200, SLOTS_FIXTURE.encode())) client.get("/api/gufo/slots") upstream.route("http://gufo.test/metrics", response(200, GUFO_FIXTURE.encode())) client.get("/api/gufo/metrics") assert upstream.methods == {"GET"} # --------------------------------------------------------------------------- # # /api/gufo/slots — passthrough # --------------------------------------------------------------------------- # def test_slots_pass_through_byte_for_byte(client, upstream) -> None: upstream.route( "http://gufo.test/slots", response(200, SLOTS_FIXTURE.encode(), "application/json") ) r = client.get("/api/gufo/slots") assert r.status_code == 200 assert r.content.decode() == SLOTS_FIXTURE # not re-serialised, not reformatted assert r.headers["content-type"].startswith("application/json") assert "no-store" in r.headers["cache-control"] assert r.json()["slots"][0]["id_task"] == 12 def test_slots_accepts_a_bare_array_payload(client, upstream) -> None: """The deployed gufo returns `[{...}]`, not `{"slots": [...]}`. The proxy must not care — it forwards whatever JSON the engine sent, unchanged.""" bare = '[{"id":0,"is_processing":true,"id_task":7,"next_token":[{"n_decoded":3,"n_remain":9}]}]' upstream.route("http://gufo.test/slots", response(200, bare.encode(), "application/json")) r = client.get("/api/gufo/slots") assert r.status_code == 200 assert r.json() == json.loads(bare) assert isinstance(r.json(), list) def test_upstream_content_type_is_passed_through(client, upstream) -> None: # whatever gufo claims, the browser gets the same thing — no re-typing upstream.route( "http://gufo.test/slots", response(200, SLOTS_FIXTURE.encode(), "application/stream+json"), ) assert client.get("/api/gufo/slots").headers["content-type"] == "application/stream+json" def test_slots_falls_back_to_the_alias(client, upstream) -> None: # /slots 404s (the stub's default for unrouted URLs), the alias answers upstream.route("http://gufo.test/v1/slots", response(200, SLOTS_FIXTURE.encode(), "application/json")) r = client.get("/api/gufo/slots") assert r.status_code == 200 assert upstream.requested_urls == ["http://gufo.test/slots", "http://gufo.test/v1/slots"] def test_slots_reports_missing_endpoints(client, upstream) -> None: r = client.get("/api/gufo/slots") assert r.status_code == 502 assert "/slots" in r.json()["detail"] and "/v1/slots" in r.json()["detail"] def test_slots_does_not_swallow_a_server_error(client, upstream) -> None: upstream.route("http://gufo.test/slots", response(500, b"boom")) r = client.get("/api/gufo/slots") assert r.status_code == 502 # engine is unhealthy: that is a bad gateway, not a fallback assert "HTTP 500" in r.json()["detail"] assert upstream.requested_urls == ["http://gufo.test/slots"] # no pointless alias retry # --------------------------------------------------------------------------- # # /metrics — Prometheus -> JSON # --------------------------------------------------------------------------- # def test_gufo_metrics_endpoint(client, upstream) -> None: upstream.route("http://gufo.test/metrics", response(200, GUFO_FIXTURE.encode())) body = client.get("/api/gufo/metrics").json() assert body["ok"] is True assert body["engine"] == "gufo" assert body["upstream"] == "http://gufo.test/metrics" assert body["series"] == 8 # 7 llamacpp:* series + the legacy llama:* one assert body["unparsed"] == 0 assert body["metrics"]["requests_processing"]["value"] == 2.0 assert body["scraped_at"] > 0 def test_vllm_metrics_endpoint(client, upstream) -> None: upstream.route("http://vllm.test/metrics", response(200, VLLM_FIXTURE.encode())) m = client.get("/api/vllm/metrics").json()["metrics"] assert m["num_requests_running"]["value"] == 3.0 assert m["waiting_capacity"]["value"] == 5.0 assert m["time_to_first_token_seconds"]["mean"] == pytest.approx(1.0) def test_garbage_exposition_is_ok_with_no_series(client, upstream) -> None: upstream.route("http://vllm.test/metrics", response(200, b"not prometheus at all\n")) body = client.get("/api/vllm/metrics").json() assert body["ok"] is True assert body["series"] == 0 and body["unparsed"] == 1 assert body["metrics"]["num_requests_running"]["value"] is None # renders as n/a # --------------------------------------------------------------------------- # # engine outage translation (the per-engine health contract) # --------------------------------------------------------------------------- # def test_unreachable_engine_is_a_502_with_a_readable_detail(client, upstream) -> None: upstream.route("http://vllm.test/metrics", httpx.ConnectError("connection refused")) r = client.get("/api/vllm/metrics") assert r.status_code == 502 assert r.json()["ok"] is False assert "unreachable" in r.json()["detail"] def test_slow_engine_is_a_504(client, upstream) -> None: upstream.route("http://gufo.test/metrics", httpx.TimeoutException("too slow")) r = client.get("/api/gufo/metrics") assert r.status_code == 504 assert "timed out" in r.json()["detail"] def test_one_engine_being_down_does_not_affect_the_other(client, upstream) -> None: upstream.route("http://gufo.test/slots", response(200, SLOTS_FIXTURE.encode(), "application/json")) upstream.route("http://vllm.test/metrics", httpx.ConnectError("connection refused")) assert client.get("/api/vllm/metrics").status_code == 502 assert client.get("/api/gufo/slots").status_code == 200 # --------------------------------------------------------------------------- # # auth + config # --------------------------------------------------------------------------- # def test_no_authorization_header_without_keys(client, upstream) -> None: upstream.route("http://gufo.test/slots", response(200, b"{}", "application/json")) upstream.route("http://vllm.test/metrics", response(200, b"", "text/plain")) client.get("/api/gufo/slots") client.get("/api/vllm/metrics") assert all("authorization" not in headers for _url, headers in upstream.calls) def test_bearer_tokens_are_per_engine(client, upstream, monkeypatch) -> None: monkeypatch.setattr(app_module, "GUFO_API_KEY", "sk-gufo") monkeypatch.setattr(app_module, "VLLM_API_KEY", "sk-vllm") upstream.route("http://gufo.test/slots", response(200, b"{}", "application/json")) upstream.route("http://vllm.test/metrics", response(200, b"", "text/plain")) client.get("/api/gufo/slots") client.get("/api/vllm/metrics") assert upstream.calls[0][1]["Authorization"] == "Bearer sk-gufo" assert upstream.calls[1][1]["Authorization"] == "Bearer sk-vllm" def test_config_never_leaks_keys(client, monkeypatch) -> None: monkeypatch.setattr(app_module, "GUFO_API_KEY", "sk-super-secret") r = client.get("/api/config") assert "sk-super-secret" not in r.text body = r.json() assert body["gufo_auth"] is True and body["vllm_auth"] is False assert body["poll_ms"] == app_module.POLL_MS # --------------------------------------------------------------------------- # # opt-in engines: an unset *_BASE_URL means this deployment does not run that server # --------------------------------------------------------------------------- # @pytest.mark.parametrize( "raw, expected", [ ("http://box:1", "http://box:1"), ("http://box:1/", "http://box:1"), # never a doubled //metrics (" http://box:1 ", "http://box:1"), ("", ""), # set-but-empty is 'not configured', not 'http:///metrics' (" ", ""), ], ) def test_base_url_env_parsing(monkeypatch, raw: str, expected: str) -> None: monkeypatch.setenv("TEST_ENGINE_BASE_URL", raw) assert app_module._base_url_from_env("TEST_ENGINE_BASE_URL") == expected # and unset means the same as blank: no default host is invented monkeypatch.delenv("TEST_ENGINE_BASE_URL") assert app_module._base_url_from_env("TEST_ENGINE_BASE_URL") == "" @pytest.mark.parametrize("path", GUFO_PATHS + VLLM_PATHS) def test_unset_base_url_is_404_not_configured(client, upstream, monkeypatch, path: str) -> None: """A server nobody runs must not look like a server that is down: no upstream call, no 502, and a detail that names the env var to set.""" engine = "gufo" if "gufo" in path else "vllm" monkeypatch.setattr(app_module, f"{engine.upper()}_BASE_URL", "") r = client.get(path) assert r.status_code == 404 body = r.json() assert body["ok"] is False assert body["configured"] is False assert body["engine"] == engine assert f"{engine.upper()}_BASE_URL" in body["detail"] assert "no such route" not in body["detail"] # distinct from an unknown path assert upstream.requested_urls == [] # nothing was dialled def test_one_configured_engine_is_unaffected_by_the_other_being_absent( client, upstream, monkeypatch ) -> None: """The vLLM-only recipient of this repo gets a working dashboard, not a half-broken one.""" monkeypatch.setattr(app_module, "GUFO_BASE_URL", "") upstream.route("http://vllm.test/metrics", response(200, VLLM_FIXTURE.encode())) assert client.get("/api/gufo/metrics").status_code == 404 assert client.get("/api/gufo/slots").status_code == 404 body = client.get("/api/vllm/metrics").json() assert body["ok"] is True assert upstream.requested_urls == ["http://vllm.test/metrics"] def test_config_reports_which_engines_exist(client, monkeypatch) -> None: body = client.get("/api/config").json() assert body["gufo_enabled"] is True and body["vllm_enabled"] is True monkeypatch.setattr(app_module, "GUFO_BASE_URL", "") body = client.get("/api/config").json() assert body["gufo_enabled"] is False assert body["gufo_base_url"] == "" assert body["vllm_enabled"] is True # the other engine keeps its section def test_lifespan_owns_one_shared_client(client) -> None: """Entering lifespan installs exactly one AsyncClient on app.state and closes it on shutdown — the poll loop depends on connection reuse, and a leaked client would keep sockets open after Ctrl-C.""" from fastapi.testclient import TestClient with TestClient(app_module.app) as running: shared = app_module.app.state.client assert isinstance(shared, httpx.AsyncClient) assert not shared.is_closed assert running.get("/api/config").status_code == 200 assert app_module.app.state.client is shared assert shared.is_closed # --------------------------------------------------------------------------- # # page + misc # --------------------------------------------------------------------------- # def test_dashboard_is_served(client) -> None: r = client.get("/") assert r.status_code == 200 assert "text/html" in r.headers["content-type"] assert "LLM Monitor" in r.text def test_dashboard_layout_is_driven_by_the_config_flags(client) -> None: """The page must render from /api/config's *_enabled flags instead of hardcoding two engines: an absent engine loses its panel, its strip card, its polling and its share of the fleet totals, and nothing configured at all shows the setup card. Asserted on the shipped HTML because the layout logic lives in the inline script.""" html = client.get("/").text # one generic engine list, revealed from the backend's flags assert "const ENGINE_IDS" in html and "_enabled`" in html assert "isEnabled(job.engine)" in html # an absent engine is never polled # what gets hidden, and what shows instead when there is nothing to show for marker in ("panel-${engine}", "strip-${engine}", 'id="no-engines"'): assert marker in html def test_dashboard_loads_no_third_party_assets(client) -> None: """The engines live on an internet-disconnected network: no CDN, no webfonts, no build step. Only same-origin requests are allowed from the page.""" import re html = client.get("/").text refs = re.findall(r"""(?:src|href)\s*=\s*["']([^"']+)["']""", html) external = [r for r in refs if r.startswith(("http://", "//"))] assert external == [], f"page references external assets: {external}" def test_unknown_route_is_json(client) -> None: r = client.get("/api/nope") assert r.status_code == 404 assert "no such route" in r.json()["detail"]