|
|
@ -23,16 +23,28 @@ class ExceptionCapture: |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app = FastAPI() |
|
|
app = FastAPI() |
|
|
|
|
|
sub_app = FastAPI() |
|
|
captured_exception = ExceptionCapture() |
|
|
captured_exception = ExceptionCapture() |
|
|
|
|
|
|
|
|
|
|
|
app.mount(path="/sub", app=sub_app) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@app.exception_handler(RequestValidationError) |
|
|
@app.exception_handler(RequestValidationError) |
|
|
|
|
|
@sub_app.exception_handler(RequestValidationError) |
|
|
async def request_validation_handler(request: Request, exc: RequestValidationError): |
|
|
async def request_validation_handler(request: Request, exc: RequestValidationError): |
|
|
captured_exception.capture(exc) |
|
|
captured_exception.capture(exc) |
|
|
raise exc |
|
|
raise exc |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@app.exception_handler(ResponseValidationError) |
|
|
|
|
|
@sub_app.exception_handler(ResponseValidationError) |
|
|
|
|
|
async def response_validation_handler(_: Request, exc: ResponseValidationError): |
|
|
|
|
|
captured_exception.capture(exc) |
|
|
|
|
|
raise exc |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@app.exception_handler(WebSocketRequestValidationError) |
|
|
@app.exception_handler(WebSocketRequestValidationError) |
|
|
|
|
|
@sub_app.exception_handler(WebSocketRequestValidationError) |
|
|
async def websocket_validation_handler( |
|
|
async def websocket_validation_handler( |
|
|
websocket: WebSocket, exc: WebSocketRequestValidationError |
|
|
websocket: WebSocket, exc: WebSocketRequestValidationError |
|
|
): |
|
|
): |
|
|
@ -50,6 +62,11 @@ def get_item(): |
|
|
return {"name": "Widget"} |
|
|
return {"name": "Widget"} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@sub_app.get("/items/", response_model=Item) |
|
|
|
|
|
def get_sub_item(): |
|
|
|
|
|
return {"name": "Widget"} # pragma: no cover |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@app.websocket("/ws/{item_id}") |
|
|
@app.websocket("/ws/{item_id}") |
|
|
async def websocket_endpoint(websocket: WebSocket, item_id: int): |
|
|
async def websocket_endpoint(websocket: WebSocket, item_id: int): |
|
|
await websocket.accept() # pragma: no cover |
|
|
await websocket.accept() # pragma: no cover |
|
|
@ -57,6 +74,13 @@ async def websocket_endpoint(websocket: WebSocket, item_id: int): |
|
|
await websocket.close() # pragma: no cover |
|
|
await websocket.close() # pragma: no cover |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@sub_app.websocket("/ws/{item_id}") |
|
|
|
|
|
async def subapp_websocket_endpoint(websocket: WebSocket, item_id: int): |
|
|
|
|
|
await websocket.accept() # pragma: no cover |
|
|
|
|
|
await websocket.send_text(f"Item: {item_id}") # pragma: no cover |
|
|
|
|
|
await websocket.close() # pragma: no cover |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
client = TestClient(app) |
|
|
client = TestClient(app) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -95,6 +119,33 @@ def test_websocket_validation_error_includes_endpoint_context(): |
|
|
assert "/ws/" in error_str |
|
|
assert "/ws/" in error_str |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_subapp_request_validation_error_includes_endpoint_context(): |
|
|
|
|
|
captured_exception.exception = None |
|
|
|
|
|
try: |
|
|
|
|
|
client.get("/sub/items/") |
|
|
|
|
|
except Exception: |
|
|
|
|
|
pass |
|
|
|
|
|
|
|
|
|
|
|
assert captured_exception.exception is not None |
|
|
|
|
|
error_str = str(captured_exception.exception) |
|
|
|
|
|
assert "get_sub_item" in error_str |
|
|
|
|
|
assert "/sub/items/" in error_str |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_subapp_websocket_validation_error_includes_endpoint_context(): |
|
|
|
|
|
captured_exception.exception = None |
|
|
|
|
|
try: |
|
|
|
|
|
with client.websocket_connect("/sub/ws/invalid"): |
|
|
|
|
|
pass # pragma: no cover |
|
|
|
|
|
except Exception: |
|
|
|
|
|
pass |
|
|
|
|
|
|
|
|
|
|
|
assert captured_exception.exception is not None |
|
|
|
|
|
error_str = str(captured_exception.exception) |
|
|
|
|
|
assert "subapp_websocket_endpoint" in error_str |
|
|
|
|
|
assert "/sub/ws/" in error_str |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_validation_error_with_only_path(): |
|
|
def test_validation_error_with_only_path(): |
|
|
errors = [{"type": "missing", "loc": ("body", "name"), "msg": "Field required"}] |
|
|
errors = [{"type": "missing", "loc": ("body", "name"), "msg": "Field required"}] |
|
|
exc = RequestValidationError(errors, endpoint_ctx={"path": "GET /api/test"}) |
|
|
exc = RequestValidationError(errors, endpoint_ctx={"path": "GET /api/test"}) |
|
|
|