import asyncio import httpx from app.controllers.v1.delivery import get_aggregator_service from app.main import create_app from app.schemas.order import OrderCreateRequest, OrderCreateResponse from app.services.aggregator import ( AggregatorServiceError, InvalidOrderCreateRequestError, ) class StubAggregatorService: def __init__(self, *, response: object, error: Exception | None = None) -> None: self._response = response self._error = error self.calls: list[OrderCreateRequest] = [] async def create_order(self, request: OrderCreateRequest) -> object: self.calls.append(request) if self._error is not None: raise self._error return self._response def _install_service_override(app, service: StubAggregatorService) -> None: async def override_service() -> StubAggregatorService: return service app.dependency_overrides[get_aggregator_service] = override_service def _valid_payload() -> dict[str, object]: return { "type": 2, "tariff_code": 535, "comment": "Test order", "sender": { "company": "Romashka LLC", "name": "Petr Petrov", "email": "sender@example.com", "phones": [{"number": "+79009876543"}], }, "recipient": { "name": "Ivan Ivanov", "email": "ivan@example.com", "phones": [{"number": "+79001234567"}], }, "from_location": { "address": "Lenina 1", "city": "Moscow", "country_code": "RU", }, "to_location": { "address": "Pushkina 10", "city": "Novosibirsk", "country_code": "RU", }, "services": [{"code": "INSURANCE", "parameter": "1000"}], "packages": [ { "number": "1", "weight": 1000, "length": 20, "width": 15, "height": 10, "comment": "Package 1", } ], } def test_post_delivery_order_returns_response_and_delegates_to_service() -> None: expected_response = OrderCreateResponse(provider="cdek", order_uuid="order-uuid-1") service = StubAggregatorService(response=expected_response) app = create_app() _install_service_override(app, service) async def run_request() -> httpx.Response: transport = httpx.ASGITransport(app=app) async with httpx.AsyncClient( transport=transport, base_url="http://testserver", ) as client: return await client.post("/api/v1/delivery/order", json=_valid_payload()) response = asyncio.run(run_request()) assert response.status_code == 200 assert response.json() == expected_response.model_dump(mode="json") assert service.calls == [OrderCreateRequest(**_valid_payload())] def test_post_delivery_order_rejects_invalid_payload() -> None: service = StubAggregatorService(response=None) app = create_app() _install_service_override(app, service) invalid_payload = _valid_payload() invalid_payload["tariff_code"] = 136 async def run_request() -> httpx.Response: transport = httpx.ASGITransport(app=app) async with httpx.AsyncClient( transport=transport, base_url="http://testserver", ) as client: return await client.post("/api/v1/delivery/order", json=invalid_payload) response = asyncio.run(run_request()) assert response.status_code == 422 assert service.calls == [] def test_post_delivery_order_maps_invalid_request_to_400() -> None: service = StubAggregatorService( response=None, error=InvalidOrderCreateRequestError("invalid payload"), ) app = create_app() _install_service_override(app, service) async def run_request() -> httpx.Response: transport = httpx.ASGITransport(app=app, raise_app_exceptions=False) async with httpx.AsyncClient( transport=transport, base_url="http://testserver", ) as client: return await client.post("/api/v1/delivery/order", json=_valid_payload()) response = asyncio.run(run_request()) assert response.status_code == 400 assert response.json() == { "detail": { "code": "invalid_order_create_request", "message": "Order request contains invalid or unsupported CDEK data.", } } def test_post_delivery_order_maps_service_exception_to_503() -> None: service = StubAggregatorService( response=None, error=AggregatorServiceError("service unavailable"), ) app = create_app() _install_service_override(app, service) async def run_request() -> httpx.Response: transport = httpx.ASGITransport(app=app, raise_app_exceptions=False) async with httpx.AsyncClient( transport=transport, base_url="http://testserver", ) as client: return await client.post("/api/v1/delivery/order", json=_valid_payload()) response = asyncio.run(run_request()) assert response.status_code == 503 assert response.json() == { "detail": { "code": "order_creation_unavailable", "message": "CDEK order creation is temporarily unavailable.", } }