from datetime import datetime, timezone from unittest.mock import Mock, patch import pytest from fastapi import FastAPI, Request from starlette.exceptions import HTTPException from app.common.error_handler import ( ErrorResponse, app_exception_handler, http_exception_handler, register_exception_handlers, ) from app.core.exceptions import AppException class TestErrorResponse: def test_error_response_creation(self) -> None: response = ErrorResponse( status_code=400, message="Bad request", timestamp=datetime.now(timezone.utc).isoformat(), ) assert response.status_code == 400 assert response.message == "Bad request" assert response.details is None def test_error_response_with_details(self) -> None: response = ErrorResponse( status_code=500, message="Internal error", details={"field": "value"}, timestamp=datetime.now(timezone.utc).isoformat(), ) assert response.status_code == 500 assert response.message == "Internal error" assert response.details == {"field": "value"} class TestAppExceptionHandler: @pytest.mark.asyncio async def test_app_exception_handler(self) -> None: request = Mock(spec=Request) exc = AppException(message="Test error", status_code=400) response = await app_exception_handler(request, exc) assert response.status_code == 400 body = bytes(response.body).decode() assert "Test error" in body assert "400" in body @pytest.mark.asyncio async def test_app_exception_handler_content(self) -> None: request = Mock(spec=Request) exc = AppException(message="Validation error", status_code=422) with patch("app.common.error_handler.datetime") as mock_datetime: mock_datetime.now.return_value.isoformat.return_value = ( "2024-01-01T00:00:00" ) response = await app_exception_handler(request, exc) content = bytes(response.body).decode() assert "Validation error" in content assert "422" in content assert "2024-01-01T00:00:00" in content class TestHttpExceptionHandler: @pytest.mark.asyncio async def test_http_exception_handler(self) -> None: request = Mock(spec=Request) exc = HTTPException(status_code=404, detail="Not found") response = await http_exception_handler(request, exc) assert response.status_code == 404 body = bytes(response.body).decode() assert "Not found" in body assert "404" in body @pytest.mark.asyncio async def test_http_exception_handler_content(self) -> None: request = Mock(spec=Request) exc = HTTPException(status_code=503, detail="Service unavailable") with patch("app.common.error_handler.datetime") as mock_datetime: mock_datetime.now.return_value.isoformat.return_value = ( "2024-01-01T12:00:00" ) response = await http_exception_handler(request, exc) content = bytes(response.body).decode() assert "Service unavailable" in content assert "503" in content assert "2024-01-01T12:00:00" in content class TestRegisterExceptionHandlers: def test_register_exception_handlers(self) -> None: app = Mock(spec=FastAPI) register_exception_handlers(app) assert app.add_exception_handler.call_count == 2 app.add_exception_handler.assert_any_call(AppException, app_exception_handler) app.add_exception_handler.assert_any_call(HTTPException, http_exception_handler)