Добавлен адаптер к tbank и формирование ссылки на оплату
This commit is contained in:
@@ -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 == []
|
||||
Reference in New Issue
Block a user