Judge / backend /tests /test_auth.py
GonzaViss's picture
feat(backend): exige secreto compartido X-Proxy-Secret en todos los endpoints salvo /health
a722494
Raw
History Blame Contribute Delete
3.71 kB
"""Integration tests for shared-secret auth between the Next proxy and the backend.
When settings.proxy_shared_secret is set, every endpoint except shallow /health
requires the X-Proxy-Secret header. When unset, auth is disabled (local dev).
"""
from unittest.mock import MagicMock, patch
import pytest
from fastapi.testclient import TestClient
from app.rag.retrieval import Chunk
from tests.conftest import FakeEmbedder, FakeLLMProvider, _fake_settings, _reset_limiter
TEST_SECRET = "test-secret"
def _make_chunk() -> Chunk:
return Chunk(
id="chunk-1",
content="Rules content.",
section="Rules",
parent_section=None,
source_type="rulebook",
similarity=0.9,
)
def _auth_client(proxy_shared_secret: str | None):
from app.api.v1.query import get_db_pool, get_embedder, get_llm_provider
from app.main import app
_reset_limiter()
app.dependency_overrides[get_embedder] = lambda: FakeEmbedder()
app.dependency_overrides[get_db_pool] = lambda: MagicMock()
app.dependency_overrides[get_llm_provider] = lambda: FakeLLMProvider()
settings = _fake_settings()
settings.proxy_shared_secret = proxy_shared_secret
with (
patch("app.main.init_pool", return_value=MagicMock()),
patch("app.main.close_pool"),
patch("app.main.Embedder.load", return_value=FakeEmbedder()),
patch("app.main.genai.Client", return_value=MagicMock()),
patch("app.main.get_settings", return_value=settings),
):
with TestClient(app) as c:
yield c
app.dependency_overrides.clear()
@pytest.fixture
def secured_client():
yield from _auth_client(TEST_SECRET)
@pytest.fixture
def open_client():
yield from _auth_client(None)
def _post_query(client: TestClient, headers: dict | None = None):
with (
patch("app.rag.pipeline.hybrid_search", return_value=[_make_chunk()]),
patch("app.rag.pipeline.get_cached", return_value=None),
patch("app.rag.pipeline.set_cached"),
):
return client.post(
"/api/v1/query",
json={"question": "Can a unit attack twice?"},
headers=headers or {},
)
def test_query_without_secret_returns_401(secured_client: TestClient):
resp = _post_query(secured_client)
assert resp.status_code == 401
def test_query_with_wrong_secret_returns_401(secured_client: TestClient):
resp = _post_query(secured_client, headers={"X-Proxy-Secret": "wrong"})
assert resp.status_code == 401
def test_query_with_valid_secret_succeeds(secured_client: TestClient):
resp = _post_query(secured_client, headers={"X-Proxy-Secret": TEST_SECRET})
assert resp.status_code == 200
def test_401_detail_is_generic(secured_client: TestClient):
"""The 401 must not reveal that a proxy secret mechanism exists."""
resp = _post_query(secured_client)
assert resp.json() == {"detail": "Unauthorized"}
def test_shallow_health_is_exempt(secured_client: TestClient):
resp = secured_client.get("/health")
assert resp.status_code == 200
def test_deep_health_requires_secret(secured_client: TestClient):
resp = secured_client.get("/health/deep")
assert resp.status_code == 401
def test_deep_health_with_secret_succeeds(secured_client: TestClient):
resp = secured_client.get("/health/deep", headers={"X-Proxy-Secret": TEST_SECRET})
assert resp.status_code == 200
def test_auth_disabled_when_secret_unset(open_client: TestClient):
"""Local dev mode: no secret configured means no auth enforcement."""
resp = _post_query(open_client)
assert resp.status_code == 200
assert open_client.get("/health/deep").status_code == 200