Добавлен адаптер к tbank и формирование ссылки на оплату

This commit is contained in:
Раис Юсупалиев
2026-04-12 18:56:41 +03:00
parent ab0b66e1c2
commit 2b201a08be
34 changed files with 1322 additions and 268 deletions
@@ -4,10 +4,10 @@ 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.schemas.payment import InitPaymentRequest, InitPaymentResponse
from app.services.aggregator import (
AggregatorServiceError,
InvalidOrderCreateRequestError,
InitPaymentUnavailableError,
InvalidInitPaymentRequestError,
)
@@ -15,9 +15,9 @@ class StubAggregatorService:
def __init__(self, *, response: object, error: Exception | None = None) -> None:
self._response = response
self._error = error
self.calls: list[OrderCreateRequest] = []
self.calls: list[InitPaymentRequest] = []
async def create_order(self, request: OrderCreateRequest) -> object:
async def init_payment(self, request: InitPaymentRequest) -> object:
self.calls.append(request)
if self._error is not None:
raise self._error
@@ -33,9 +33,11 @@ def _install_service_override(app, service: StubAggregatorService) -> None:
def _valid_payload() -> dict[str, object]:
return {
"order_uuid": "order-uuid-1",
"price": 125000,
"type": 2,
"tariff_code": 535,
"comment": "Test order",
"comment": "Test payment",
"sender": {
"name": "Petr Petrov",
"email": "sender@example.com",
@@ -70,8 +72,8 @@ def _valid_payload() -> dict[str, object]:
}
def test_post_delivery_order_returns_response_and_delegates_to_service() -> None:
expected_response = OrderCreateResponse(provider="cdek", order_uuid="order-uuid-1")
def test_post_init_payment_returns_response_and_delegates_to_service() -> None:
expected_response = InitPaymentResponse(payment_url="https://pay.test/payment/1")
service = StubAggregatorService(response=expected_response)
app = create_app()
_install_service_override(app, service)
@@ -82,16 +84,19 @@ def test_post_delivery_order_returns_response_and_delegates_to_service() -> None
transport=transport,
base_url="http://testserver",
) as client:
return await client.post("/api/v1/delivery/order", json=_valid_payload())
return await client.post(
"/api/v1/delivery/init-payment",
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())]
assert service.calls == [InitPaymentRequest(**_valid_payload())]
def test_post_delivery_order_rejects_invalid_payload() -> None:
def test_post_init_payment_rejects_invalid_payload() -> None:
service = StubAggregatorService(response=None)
app = create_app()
_install_service_override(app, service)
@@ -104,7 +109,10 @@ def test_post_delivery_order_rejects_invalid_payload() -> None:
transport=transport,
base_url="http://testserver",
) as client:
return await client.post("/api/v1/delivery/order", json=invalid_payload)
return await client.post(
"/api/v1/delivery/init-payment",
json=invalid_payload,
)
response = asyncio.run(run_request())
@@ -112,7 +120,79 @@ def test_post_delivery_order_rejects_invalid_payload() -> None:
assert service.calls == []
def test_post_delivery_order_rejects_sender_company_field() -> None:
def test_post_init_payment_rejects_missing_price() -> None:
service = StubAggregatorService(response=None)
app = create_app()
_install_service_override(app, service)
invalid_payload = _valid_payload()
del invalid_payload["price"]
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/init-payment",
json=invalid_payload,
)
response = asyncio.run(run_request())
assert response.status_code == 422
assert service.calls == []
def test_post_init_payment_rejects_non_positive_price() -> None:
service = StubAggregatorService(response=None)
app = create_app()
_install_service_override(app, service)
invalid_payload = _valid_payload()
invalid_payload["price"] = 0
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/init-payment",
json=invalid_payload,
)
response = asyncio.run(run_request())
assert response.status_code == 422
assert service.calls == []
def test_post_init_payment_rejects_non_integer_price() -> None:
service = StubAggregatorService(response=None)
app = create_app()
_install_service_override(app, service)
invalid_payload = _valid_payload()
invalid_payload["price"] = 125000.5
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/init-payment",
json=invalid_payload,
)
response = asyncio.run(run_request())
assert response.status_code == 422
assert service.calls == []
def test_post_init_payment_rejects_sender_company_field() -> None:
service = StubAggregatorService(response=None)
app = create_app()
_install_service_override(app, service)
@@ -128,7 +208,10 @@ def test_post_delivery_order_rejects_sender_company_field() -> None:
transport=transport,
base_url="http://testserver",
) as client:
return await client.post("/api/v1/delivery/order", json=invalid_payload)
return await client.post(
"/api/v1/delivery/init-payment",
json=invalid_payload,
)
response = asyncio.run(run_request())
@@ -136,7 +219,7 @@ def test_post_delivery_order_rejects_sender_company_field() -> None:
assert service.calls == []
def test_post_delivery_order_rejects_sender_phones_field() -> None:
def test_post_init_payment_rejects_sender_phones_field() -> None:
service = StubAggregatorService(response=None)
app = create_app()
_install_service_override(app, service)
@@ -152,7 +235,10 @@ def test_post_delivery_order_rejects_sender_phones_field() -> None:
transport=transport,
base_url="http://testserver",
) as client:
return await client.post("/api/v1/delivery/order", json=invalid_payload)
return await client.post(
"/api/v1/delivery/init-payment",
json=invalid_payload,
)
response = asyncio.run(run_request())
@@ -160,7 +246,7 @@ def test_post_delivery_order_rejects_sender_phones_field() -> None:
assert service.calls == []
def test_post_delivery_order_rejects_recipient_company_field() -> None:
def test_post_init_payment_rejects_recipient_company_field() -> None:
service = StubAggregatorService(response=None)
app = create_app()
_install_service_override(app, service)
@@ -176,7 +262,10 @@ def test_post_delivery_order_rejects_recipient_company_field() -> None:
transport=transport,
base_url="http://testserver",
) as client:
return await client.post("/api/v1/delivery/order", json=invalid_payload)
return await client.post(
"/api/v1/delivery/init-payment",
json=invalid_payload,
)
response = asyncio.run(run_request())
@@ -184,7 +273,7 @@ def test_post_delivery_order_rejects_recipient_company_field() -> None:
assert service.calls == []
def test_post_delivery_order_rejects_recipient_phones_field() -> None:
def test_post_init_payment_rejects_recipient_phones_field() -> None:
service = StubAggregatorService(response=None)
app = create_app()
_install_service_override(app, service)
@@ -200,7 +289,10 @@ def test_post_delivery_order_rejects_recipient_phones_field() -> None:
transport=transport,
base_url="http://testserver",
) as client:
return await client.post("/api/v1/delivery/order", json=invalid_payload)
return await client.post(
"/api/v1/delivery/init-payment",
json=invalid_payload,
)
response = asyncio.run(run_request())
@@ -208,8 +300,8 @@ def test_post_delivery_order_rejects_recipient_phones_field() -> None:
assert service.calls == []
def test_post_delivery_order_accepts_request_without_services() -> None:
expected_response = OrderCreateResponse(provider="cdek", order_uuid="order-uuid-1")
def test_post_init_payment_accepts_request_without_services() -> None:
expected_response = InitPaymentResponse(payment_url="https://pay.test/payment/1")
service = StubAggregatorService(response=expected_response)
app = create_app()
_install_service_override(app, service)
@@ -222,19 +314,19 @@ def test_post_delivery_order_accepts_request_without_services() -> None:
transport=transport,
base_url="http://testserver",
) as client:
return await client.post("/api/v1/delivery/order", json=payload)
return await client.post("/api/v1/delivery/init-payment", json=payload)
response = asyncio.run(run_request())
assert response.status_code == 200
assert response.json() == expected_response.model_dump(mode="json")
assert service.calls == [OrderCreateRequest(**payload)]
assert service.calls == [InitPaymentRequest(**payload)]
def test_post_delivery_order_maps_invalid_request_to_400() -> None:
def test_post_init_payment_maps_invalid_request_to_400() -> None:
service = StubAggregatorService(
response=None,
error=InvalidOrderCreateRequestError("invalid payload"),
error=InvalidInitPaymentRequestError("invalid payload"),
)
app = create_app()
_install_service_override(app, service)
@@ -245,23 +337,26 @@ def test_post_delivery_order_maps_invalid_request_to_400() -> None:
transport=transport,
base_url="http://testserver",
) as client:
return await client.post("/api/v1/delivery/order", json=_valid_payload())
return await client.post(
"/api/v1/delivery/init-payment",
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.",
"code": "invalid_init_payment_request",
"message": "Payment request contains invalid or unsupported TBank data.",
}
}
def test_post_delivery_order_maps_service_exception_to_503() -> None:
def test_post_init_payment_maps_service_exception_to_503() -> None:
service = StubAggregatorService(
response=None,
error=AggregatorServiceError("service unavailable"),
error=InitPaymentUnavailableError("service unavailable"),
)
app = create_app()
_install_service_override(app, service)
@@ -272,14 +367,36 @@ def test_post_delivery_order_maps_service_exception_to_503() -> None:
transport=transport,
base_url="http://testserver",
) as client:
return await client.post("/api/v1/delivery/order", json=_valid_payload())
return await client.post(
"/api/v1/delivery/init-payment",
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.",
"code": "init_payment_unavailable",
"message": "Payment initialization is temporarily unavailable.",
}
}
def test_delivery_order_endpoint_is_removed() -> None:
service = StubAggregatorService(response=None)
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 == 404
assert service.calls == []