Spaces:
Running
Running
tudragon154203
fix: use get_authorization_scheme_param for Bearer auth + skip /visualize and /dashboard
ab357e0 | """Tests for proxy-level API key authentication.""" | |
| import pytest | |
| from fastapi.testclient import TestClient | |
| from headroom.proxy.models import ProxyConfig | |
| from headroom.proxy.server import _proxy_config_from_env, create_app | |
| def api_client(): | |
| """Create a TestClient with API key auth enabled.""" | |
| config = ProxyConfig( | |
| proxy_api_key="test-secret-key", | |
| require_proxy_api_key=True, | |
| cache_enabled=False, | |
| rate_limit_enabled=False, | |
| log_requests=False, | |
| ) | |
| with TestClient(create_app(config)) as client: | |
| yield client | |
| def open_client(): | |
| """Create a TestClient with API key auth disabled.""" | |
| config = ProxyConfig( | |
| cache_enabled=False, | |
| rate_limit_enabled=False, | |
| log_requests=False, | |
| ) | |
| with TestClient(create_app(config)) as client: | |
| yield client | |
| class TestHealthEndpointsAlwaysAccessible: | |
| """Health endpoints must remain accessible without API key.""" | |
| def test_livez_no_key(self, api_client): | |
| resp = api_client.get("/livez") | |
| assert resp.status_code == 200 | |
| def test_healthz_no_key(self, api_client): | |
| resp = api_client.get("/healthz") | |
| assert resp.status_code == 200 | |
| def test_readyz_no_key(self, api_client): | |
| resp = api_client.get("/readyz") | |
| assert resp.status_code == 200 | |
| def test_health_no_key(self, api_client): | |
| resp = api_client.get("/health") | |
| assert resp.status_code == 200 | |
| def test_visualize_no_key(self, api_client): | |
| resp = api_client.get("/visualize") | |
| assert resp.status_code == 200 | |
| def test_dashboard_no_key(self, api_client): | |
| resp = api_client.get("/dashboard") | |
| assert resp.status_code == 200 | |
| class TestProtectedEndpointsRejectWithoutKey: | |
| """Functional endpoints must reject requests without a valid key.""" | |
| def test_stats_no_key(self, api_client): | |
| resp = api_client.get("/stats") | |
| assert resp.status_code == 401 | |
| def test_stats_wrong_key(self, api_client): | |
| resp = api_client.get("/stats", headers={"Authorization": "Bearer wrong"}) | |
| assert resp.status_code == 401 | |
| def test_cache_clear_no_key(self, api_client): | |
| resp = api_client.post("/cache/clear") | |
| assert resp.status_code == 401 | |
| def test_quota_no_key(self, api_client): | |
| resp = api_client.get("/quota") | |
| assert resp.status_code == 401 | |
| class TestProtectedEndpointsAcceptValidKey: | |
| """Functional endpoints must accept requests with the correct key.""" | |
| def test_stats_with_key(self, api_client): | |
| resp = api_client.get("/stats", headers={"Authorization": "Bearer test-secret-key"}) | |
| assert resp.status_code == 200 | |
| def test_quota_with_key(self, api_client): | |
| resp = api_client.get("/quota", headers={"Authorization": "Bearer test-secret-key"}) | |
| assert resp.status_code == 200 | |
| def test_cache_clear_with_key(self, api_client): | |
| resp = api_client.post("/cache/clear", headers={"Authorization": "Bearer test-secret-key"}) | |
| assert resp.status_code == 200 | |
| class TestAuthDisabledByDefault: | |
| """When no API key is configured, all endpoints are open.""" | |
| def test_stats_open(self, open_client): | |
| resp = open_client.get("/stats") | |
| assert resp.status_code == 200 | |
| def test_cache_clear_open(self, open_client): | |
| resp = open_client.post("/cache/clear") | |
| assert resp.status_code == 200 | |
| class TestEnvConfig: | |
| """The proxy should pick up API key auth from the environment.""" | |
| def test_env_enables_proxy_key(self, monkeypatch): | |
| monkeypatch.setenv("HEADROOM_PROXY_API_KEY", "env-secret-key") | |
| config = _proxy_config_from_env() | |
| assert config.proxy_api_key == "env-secret-key" | |
| assert config.require_proxy_api_key is True | |
| class TestStartupValidation: | |
| """Proxy must fail fast if misconfigured.""" | |
| def test_raises_when_key_missing(self): | |
| config = ProxyConfig(require_proxy_api_key=True, proxy_api_key=None) | |
| with pytest.raises(ValueError, match="proxy_api_key is required"): | |
| create_app(config) | |