import asyncio from decimal import Decimal from typing import Any import httpx import pytest from app.adapters.delivery_providers.cse import ( CSEClient, CSEProvider, CSERequestError, ) from app.adapters.delivery_providers.cse import order_mapper from app.adapters.delivery_providers.cse.order_mapper import ( CSEOrderRegistrationParams, ) from app.adapters.delivery_providers.cse.soap import ( build_envelope, parse_response, ) from app.schemas.request import DeliveryCalculationRequest, DeliveryEntity from tests.payment_fixtures import make_init_payment_request _CALC_RESPONSE = """ Calc Destination Tariff tariff-guid-1 Total1114.92 CurrencyNameRUR ServiceРоссия доставка MinPeriod3 MaxPeriod5 Tariff tariff-guid-2 Total2000.00 CurrencyNameRUR ServiceЭкспресс MinPeriod1 MaxPeriod2 """ _SAVE_RESPONSE = """ SaveDocuments Order NumberCSE-000123 """ _ERROR_RESPONSE = """ Calc Error Description1001 """ class SequenceHTTPClient: def __init__(self, results: list[Any]) -> None: self._results = results self.calls: list[dict[str, Any]] = [] async def post( self, url: str, *, content: bytes | None = None, headers: dict[str, str] | None = None, timeout: float | None = None, ) -> httpx.Response: self.calls.append({"url": url, "content": content, "headers": headers}) result = self._results[len(self.calls) - 1] if isinstance(result, Exception): raise result return result def _params() -> CSEOrderRegistrationParams: return CSEOrderRegistrationParams( payer="payer-1", payment_method="pm-1", shipping_method="sm-1", urgency="urgency-1", ) def _build_client(results: list[Any]) -> tuple[CSEClient, SequenceHTTPClient]: http_client = SequenceHTTPClient(results) client = CSEClient( http_client=http_client, # type: ignore[arg-type] base_url="http://lk-test.cse.ru/1c/ws/web1c.1cws", login="test", password="2016", registration_params=_params(), retry_attempts=0, ) return client, http_client @pytest.fixture(autouse=True) def _patch_references(monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setattr( order_mapper, "resolve_cse_geography", lambda city_id: f"geo-{city_id}" ) monkeypatch.setattr( order_mapper, "resolve_type_of_cargo", lambda parcel_type: "cargo-guid" ) def _calc_request() -> DeliveryCalculationRequest: return DeliveryCalculationRequest( entity=DeliveryEntity.INDIVIDUAL, from_city=1, to_city=2, weight_kg=1.0, length_cm=10.0, width_cm=10.0, height_cm=10.0, ) def test_build_envelope_contains_credentials_and_payload() -> None: body = order_mapper.build_calc_body_for_calculation(_calc_request()) envelope = build_envelope("Calc", login="test", password="2016", body=body) text = envelope.decode("utf-8") assert "Calc" in text assert "test" in text or ">test<" in text assert "geo-1" in text assert "geo-2" in text def test_parse_calc_response_round_trip() -> None: root = parse_response(_CALC_RESPONSE, "Calc") assert root.key == "Calc" assert root.items[0].items[0].field_value("Total") == "1114.92" def test_provider_get_prices_maps_all_tariffs() -> None: response = httpx.Response(200, text=_CALC_RESPONSE) client, _ = _build_client([response]) provider = CSEProvider(client) prices = asyncio.run(provider.get_prices(_calc_request())) assert [price.provider for price in prices] == ["cse", "cse"] assert [price.tariff_code for price in prices] == ["tariff-guid-1", "tariff-guid-2"] assert prices[0].price == Decimal("1114.92") assert prices[0].currency == "RUB" assert prices[0].delivery_days_min == 3 assert prices[0].delivery_days_max == 5 assert prices[0].service_name == "Россия доставка" def test_provider_get_payment_price_selects_matching_tariff() -> None: response = httpx.Response(200, text=_CALC_RESPONSE) client, _ = _build_client([response]) provider = CSEProvider(client) request = make_init_payment_request( systemData={ "tariff": { "provider": "cse", "serviceName": "Экспресс", "price": 200000, "deliveryDaysMin": 1, "deliveryDaysMax": 2, "tariffCode": "tariff-guid-2", }, "parcelType": "parcel", "weight": "1.0", "dimensions": {"length": "10", "width": "10", "height": "10"}, } ) price = asyncio.run(provider.get_payment_price(request)) assert price is not None assert price.tariff_code == "tariff-guid-2" assert price.price == Decimal("2000.00") def test_provider_register_order_returns_document_number() -> None: response = httpx.Response(200, text=_SAVE_RESPONSE) client, http_client = _build_client([response]) provider = CSEProvider(client) result = asyncio.run( provider.register_order(make_init_payment_request(), "order-uuid-1") ) assert result.order_number == "CSE-000123" assert http_client.calls[0]["url"] == "http://lk-test.cse.ru/1c/ws/web1c.1cws" def test_application_error_in_response_raises_request_error() -> None: response = httpx.Response(200, text=_ERROR_RESPONSE) client, _ = _build_client([response]) provider = CSEProvider(client) with pytest.raises(CSERequestError): asyncio.run(provider.get_prices(_calc_request()))