Aynkhn's picture
Deploy FaceVision to Hugging Face Spaces
67af2df verified
Raw History Blame Contribute Delete
3.38 kB
"""Error-handling regression tests.
The previous global handler returned ``str(exc)`` in the response body, which for a
SQLAlchemy failure includes the connection URL and its password.
"""
from __future__ import annotations
import pytest
from fastapi.testclient import TestClient
from app.core.config import Settings
from app.db.deps import get_db
from app.main import create_app
LEAKY_MESSAGE = (
"(psycopg2.OperationalError) connection to server at "
"postgresql://admin:hunter2@db.example.com:5432/facevision failed"
)
def _app_with_exploding_db(environment: str) -> TestClient:
settings = Settings(
environment=environment,
database_url="sqlite://",
FACEVISION_INGEST_KEY="ingest",
log_level="CRITICAL",
)
app = create_app(settings)
class ExplodingSession:
def execute(self, *_args, **_kwargs):
raise RuntimeError(LEAKY_MESSAGE)
def close(self) -> None:
pass
def exploding_db():
yield ExplodingSession()
app.dependency_overrides[get_db] = exploding_db
return TestClient(app, raise_server_exceptions=False)
class TestInternalErrorsDoNotLeak:
def test_production_body_omits_the_exception_text(self):
client = _app_with_exploding_db("production")
response = client.get("/api/logs", headers={"X-Session-Id": "error-test-session-01"})
assert response.status_code == 500
body = response.text
assert "hunter2" not in body
assert "postgresql://" not in body
assert "Traceback" not in body
def test_production_body_carries_a_request_id(self):
client = _app_with_exploding_db("production")
body = client.get("/api/logs", headers={"X-Session-Id": "error-test-session-01"}).json()
assert body["message"] == "Internal Server Error"
assert body["request_id"]
def test_development_includes_detail_for_debugging(self):
client = _app_with_exploding_db("development")
body = client.get("/api/logs", headers={"X-Session-Id": "error-test-session-01"}).json()
assert "detail" in body
class TestValidationErrors:
def test_validation_failure_reports_field_errors(self, client, ingest_headers):
response = client.post(
"/api/logs",
json={"dominant_emotion": "Nope", "duration_seconds": "abc"},
headers=ingest_headers,
)
assert response.status_code == 422
body = response.json()
assert body["message"] == "Validation failed"
assert body["errors"]
class TestRequestId:
def test_successful_responses_carry_a_request_id_header(self, client):
response = client.get("/api/health")
assert response.headers.get("X-Request-ID")
class TestSecurityHeaders:
@pytest.mark.parametrize(
("header", "value"),
[
("Cross-Origin-Opener-Policy", "same-origin"),
("Cross-Origin-Embedder-Policy", "require-corp"),
("X-Content-Type-Options", "nosniff"),
],
)
def test_isolation_headers_are_present(self, client, header, value):
"""next.config.ts declares these but they are inert under output: 'export'."""
assert client.get("/api/health").headers.get(header) == value