Files
delphi-database/tests/test_error_handling.py
2025-08-10 21:34:11 -05:00

83 lines
2.7 KiB
Python

import json
from typing import Optional
from fastapi import FastAPI, HTTPException
from fastapi.testclient import TestClient
from pydantic import BaseModel, Field
from app.middleware.logging import LoggingMiddleware
from app.middleware.errors import register_exception_handlers
class Item(BaseModel):
name: str = Field(..., min_length=3)
quantity: int = Field(..., ge=1)
def build_test_app() -> FastAPI:
app = FastAPI()
app.add_middleware(LoggingMiddleware, log_requests=False, log_responses=False)
register_exception_handlers(app)
@app.get("/http-error")
async def http_error():
raise HTTPException(status_code=403, detail="Forbidden action")
@app.post("/validation")
async def validation_endpoint(item: Item): # noqa: F841
return {"ok": True}
@app.get("/crash")
async def crash():
raise RuntimeError("Boom")
return app
def assert_envelope(resp, status: int, code: str, has_details: bool, expected_cid: Optional[str] = None):
assert resp.status_code == status
data = resp.json()
assert data["success"] is False
assert data["error"]["status"] == status
assert data["error"]["code"] == code
assert isinstance(data["error"]["message"], str) and data["error"]["message"]
if has_details:
assert "details" in data["error"]
else:
assert "details" not in data["error"]
# Correlation id in body and header
assert "correlation_id" in data and isinstance(data["correlation_id"], str)
header_cid = resp.headers.get("X-Correlation-ID")
assert header_cid == data["correlation_id"]
if expected_cid is not None:
assert header_cid == expected_cid
def test_http_exception_envelope_and_correlation_id_echo():
app = build_test_app()
client = TestClient(app)
cid = "abc-12345-test"
resp = client.get("/http-error", headers={"X-Correlation-ID": cid})
assert_envelope(resp, 403, "http_error", has_details=False, expected_cid=cid)
def test_validation_exception_envelope_and_correlation_id_echo():
app = build_test_app()
client = TestClient(app)
cid = "cid-validation-67890"
# Missing fields to trigger 422
resp = client.post("/validation", json={"name": "ab"}, headers={"X-Correlation-ID": cid})
assert_envelope(resp, 422, "validation_error", has_details=True, expected_cid=cid)
def test_unhandled_exception_envelope_and_generated_correlation_id():
app = build_test_app()
client = TestClient(app, raise_server_exceptions=False)
resp = client.get("/crash")
# Should have generated a correlation id and not echo None
assert_envelope(resp, 500, "internal_error", has_details=False)
assert resp.headers.get("X-Correlation-ID")