Добавлен заказ накладной
This commit is contained in:
@@ -9,6 +9,7 @@ from app.services.aggregator import (
|
||||
InitPaymentUnavailableError,
|
||||
InvalidInitPaymentRequestError,
|
||||
)
|
||||
from tests.payment_fixtures import make_init_payment_payload
|
||||
|
||||
|
||||
class StubAggregatorService:
|
||||
@@ -31,45 +32,16 @@ def _install_service_override(app, service: StubAggregatorService) -> None:
|
||||
app.dependency_overrides[get_aggregator_service] = override_service
|
||||
|
||||
|
||||
def _valid_payload() -> dict[str, object]:
|
||||
return {
|
||||
"order_uuid": "order-uuid-1",
|
||||
"price": 125000,
|
||||
"type": 2,
|
||||
"tariff_code": 535,
|
||||
"comment": "Test payment",
|
||||
"sender": {
|
||||
"name": "Petr Petrov",
|
||||
"email": "sender@example.com",
|
||||
"phone": {"number": "+79009876543"},
|
||||
},
|
||||
"recipient": {
|
||||
"name": "Ivan Ivanov",
|
||||
"email": "ivan@example.com",
|
||||
"phone": {"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": 1,
|
||||
"length": 20,
|
||||
"width": 15,
|
||||
"height": 10,
|
||||
"comment": "Package 1",
|
||||
}
|
||||
],
|
||||
}
|
||||
def _post(app, payload: dict[str, object]) -> httpx.Response:
|
||||
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=payload)
|
||||
|
||||
return asyncio.run(run_request())
|
||||
|
||||
|
||||
def test_post_init_payment_returns_response_and_delegates_to_service() -> None:
|
||||
@@ -77,44 +49,64 @@ def test_post_init_payment_returns_response_and_delegates_to_service() -> None:
|
||||
service = StubAggregatorService(response=expected_response)
|
||||
app = create_app()
|
||||
_install_service_override(app, service)
|
||||
payload = make_init_payment_payload()
|
||||
|
||||
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())
|
||||
response = _post(app, payload)
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response.json() == expected_response.model_dump(mode="json")
|
||||
assert service.calls == [InitPaymentRequest(**_valid_payload())]
|
||||
assert service.calls == [InitPaymentRequest.model_validate(payload)]
|
||||
|
||||
|
||||
def test_post_init_payment_rejects_invalid_payload() -> None:
|
||||
def test_post_init_payment_rejects_invalid_tariff_code() -> None:
|
||||
service = StubAggregatorService(response=None)
|
||||
app = create_app()
|
||||
_install_service_override(app, service)
|
||||
invalid_payload = _valid_payload()
|
||||
invalid_payload["tariff_code"] = "not-a-number"
|
||||
invalid_payload = make_init_payment_payload()
|
||||
invalid_payload["systemData"]["tariff"]["tariffCode"] = "not-a-number"
|
||||
|
||||
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 = _post(app, invalid_payload)
|
||||
|
||||
response = asyncio.run(run_request())
|
||||
assert response.status_code == 422
|
||||
assert service.calls == []
|
||||
|
||||
|
||||
def test_post_init_payment_rejects_snake_case_top_level_field() -> None:
|
||||
service = StubAggregatorService(response=None)
|
||||
app = create_app()
|
||||
_install_service_override(app, service)
|
||||
invalid_payload = make_init_payment_payload()
|
||||
invalid_payload["order_uuid"] = invalid_payload.pop("orderUuid")
|
||||
|
||||
response = _post(app, invalid_payload)
|
||||
|
||||
assert response.status_code == 422
|
||||
assert service.calls == []
|
||||
|
||||
|
||||
def test_post_init_payment_rejects_snake_case_nested_address_field() -> None:
|
||||
service = StubAggregatorService(response=None)
|
||||
app = create_app()
|
||||
_install_service_override(app, service)
|
||||
invalid_payload = make_init_payment_payload()
|
||||
sender_address = invalid_payload["senderAddress"]
|
||||
sender_address["city_id"] = sender_address.pop("cityId")
|
||||
|
||||
response = _post(app, invalid_payload)
|
||||
|
||||
assert response.status_code == 422
|
||||
assert service.calls == []
|
||||
|
||||
|
||||
def test_post_init_payment_rejects_snake_case_nested_tariff_field() -> None:
|
||||
service = StubAggregatorService(response=None)
|
||||
app = create_app()
|
||||
_install_service_override(app, service)
|
||||
invalid_payload = make_init_payment_payload()
|
||||
tariff = invalid_payload["systemData"]["tariff"]
|
||||
tariff["tariff_code"] = tariff.pop("tariffCode")
|
||||
|
||||
response = _post(app, invalid_payload)
|
||||
|
||||
assert response.status_code == 422
|
||||
assert service.calls == []
|
||||
@@ -124,21 +116,10 @@ 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"]
|
||||
invalid_payload = make_init_payment_payload()
|
||||
del invalid_payload["systemData"]["tariff"]["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/order",
|
||||
json=invalid_payload,
|
||||
)
|
||||
|
||||
response = asyncio.run(run_request())
|
||||
response = _post(app, invalid_payload)
|
||||
|
||||
assert response.status_code == 422
|
||||
assert service.calls == []
|
||||
@@ -148,21 +129,10 @@ 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
|
||||
invalid_payload = make_init_payment_payload()
|
||||
invalid_payload["systemData"]["tariff"]["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/order",
|
||||
json=invalid_payload,
|
||||
)
|
||||
|
||||
response = asyncio.run(run_request())
|
||||
response = _post(app, invalid_payload)
|
||||
|
||||
assert response.status_code == 422
|
||||
assert service.calls == []
|
||||
@@ -172,155 +142,102 @@ 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
|
||||
invalid_payload = make_init_payment_payload()
|
||||
invalid_payload["systemData"]["tariff"]["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/order",
|
||||
json=invalid_payload,
|
||||
)
|
||||
|
||||
response = asyncio.run(run_request())
|
||||
response = _post(app, invalid_payload)
|
||||
|
||||
assert response.status_code == 422
|
||||
assert service.calls == []
|
||||
|
||||
|
||||
def test_post_init_payment_rejects_sender_company_field() -> None:
|
||||
def test_post_init_payment_requires_company_fields_when_is_company_true() -> None:
|
||||
service = StubAggregatorService(response=None)
|
||||
app = create_app()
|
||||
_install_service_override(app, service)
|
||||
invalid_payload = _valid_payload()
|
||||
invalid_payload["sender"] = {
|
||||
**invalid_payload["sender"], # type: ignore[arg-type]
|
||||
"company": "Romashka LLC",
|
||||
}
|
||||
invalid_payload = make_init_payment_payload()
|
||||
invalid_payload["senderContact"]["isCompany"] = True
|
||||
|
||||
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())
|
||||
response = _post(app, invalid_payload)
|
||||
|
||||
assert response.status_code == 422
|
||||
assert service.calls == []
|
||||
|
||||
|
||||
def test_post_init_payment_rejects_sender_phones_field() -> None:
|
||||
service = StubAggregatorService(response=None)
|
||||
app = create_app()
|
||||
_install_service_override(app, service)
|
||||
invalid_payload = _valid_payload()
|
||||
invalid_payload["sender"] = {
|
||||
**invalid_payload["sender"], # type: ignore[arg-type]
|
||||
"phones": [{"number": "+79009876543"}],
|
||||
}
|
||||
|
||||
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_init_payment_rejects_recipient_company_field() -> None:
|
||||
service = StubAggregatorService(response=None)
|
||||
app = create_app()
|
||||
_install_service_override(app, service)
|
||||
invalid_payload = _valid_payload()
|
||||
invalid_payload["recipient"] = {
|
||||
**invalid_payload["recipient"], # type: ignore[arg-type]
|
||||
"company": "Romashka LLC",
|
||||
}
|
||||
|
||||
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_init_payment_rejects_recipient_phones_field() -> None:
|
||||
service = StubAggregatorService(response=None)
|
||||
app = create_app()
|
||||
_install_service_override(app, service)
|
||||
invalid_payload = _valid_payload()
|
||||
invalid_payload["recipient"] = {
|
||||
**invalid_payload["recipient"], # type: ignore[arg-type]
|
||||
"phones": [{"number": "+79001234567"}],
|
||||
}
|
||||
|
||||
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_init_payment_accepts_request_without_services() -> None:
|
||||
def test_post_init_payment_accepts_company_with_full_requisites() -> None:
|
||||
expected_response = InitPaymentResponse(payment_url="https://pay.test/payment/1")
|
||||
service = StubAggregatorService(response=expected_response)
|
||||
app = create_app()
|
||||
_install_service_override(app, service)
|
||||
payload = _valid_payload()
|
||||
del payload["services"]
|
||||
payload = make_init_payment_payload()
|
||||
payload["senderContact"].update(
|
||||
{
|
||||
"isCompany": True,
|
||||
"companyName": "Romashka LLC",
|
||||
"inn": "7707083893",
|
||||
"kpp": "770701001",
|
||||
}
|
||||
)
|
||||
|
||||
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=payload)
|
||||
|
||||
response = asyncio.run(run_request())
|
||||
response = _post(app, payload)
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response.json() == expected_response.model_dump(mode="json")
|
||||
assert service.calls == [InitPaymentRequest(**payload)]
|
||||
assert service.calls == [InitPaymentRequest.model_validate(payload)]
|
||||
|
||||
|
||||
def test_post_init_payment_rejects_non_numeric_weight() -> None:
|
||||
service = StubAggregatorService(response=None)
|
||||
app = create_app()
|
||||
_install_service_override(app, service)
|
||||
invalid_payload = make_init_payment_payload()
|
||||
invalid_payload["systemData"]["weight"] = "string"
|
||||
|
||||
response = _post(app, invalid_payload)
|
||||
|
||||
assert response.status_code == 422
|
||||
assert service.calls == []
|
||||
|
||||
|
||||
def test_post_init_payment_rejects_non_numeric_dimension() -> None:
|
||||
service = StubAggregatorService(response=None)
|
||||
app = create_app()
|
||||
_install_service_override(app, service)
|
||||
invalid_payload = make_init_payment_payload()
|
||||
invalid_payload["systemData"]["dimensions"]["length"] = "string"
|
||||
|
||||
response = _post(app, invalid_payload)
|
||||
|
||||
assert response.status_code == 422
|
||||
assert service.calls == []
|
||||
|
||||
|
||||
def test_post_init_payment_rejects_parcel_without_dimensions() -> None:
|
||||
service = StubAggregatorService(response=None)
|
||||
app = create_app()
|
||||
_install_service_override(app, service)
|
||||
invalid_payload = make_init_payment_payload()
|
||||
invalid_payload["systemData"]["dimensions"] = None
|
||||
|
||||
response = _post(app, invalid_payload)
|
||||
|
||||
assert response.status_code == 422
|
||||
assert service.calls == []
|
||||
|
||||
|
||||
def test_post_init_payment_accepts_doc_without_dimensions() -> None:
|
||||
expected_response = InitPaymentResponse(payment_url="https://pay.test/payment/1")
|
||||
service = StubAggregatorService(response=expected_response)
|
||||
app = create_app()
|
||||
_install_service_override(app, service)
|
||||
payload = make_init_payment_payload()
|
||||
payload["systemData"]["parcelType"] = "doc"
|
||||
payload["systemData"]["docPackaging"] = "envelope"
|
||||
payload["systemData"]["dimensions"] = None
|
||||
|
||||
response = _post(app, payload)
|
||||
|
||||
assert response.status_code == 200
|
||||
assert service.calls == [InitPaymentRequest.model_validate(payload)]
|
||||
|
||||
|
||||
def test_post_init_payment_maps_invalid_request_to_400() -> None:
|
||||
@@ -331,18 +248,7 @@ def test_post_init_payment_maps_invalid_request_to_400() -> None:
|
||||
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())
|
||||
response = _post(app, make_init_payment_payload())
|
||||
|
||||
assert response.status_code == 400
|
||||
assert response.json() == {
|
||||
@@ -353,37 +259,6 @@ def test_post_init_payment_maps_invalid_request_to_400() -> None:
|
||||
}
|
||||
|
||||
|
||||
def test_post_init_payment_maps_price_mismatch_to_400() -> None:
|
||||
service = StubAggregatorService(
|
||||
response=None,
|
||||
error=InvalidInitPaymentRequestError("price mismatch"),
|
||||
)
|
||||
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_init_payment_request",
|
||||
"message": "Payment request contains invalid or unsupported TBank data.",
|
||||
}
|
||||
}
|
||||
assert service.calls == [InitPaymentRequest(**_valid_payload())]
|
||||
|
||||
|
||||
def test_post_init_payment_maps_service_exception_to_503() -> None:
|
||||
service = StubAggregatorService(
|
||||
response=None,
|
||||
@@ -392,18 +267,7 @@ def test_post_init_payment_maps_service_exception_to_503() -> None:
|
||||
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())
|
||||
response = _post(app, make_init_payment_payload())
|
||||
|
||||
assert response.status_code == 503
|
||||
assert response.json() == {
|
||||
@@ -412,53 +276,3 @@ def test_post_init_payment_maps_service_exception_to_503() -> None:
|
||||
"message": "Payment initialization is temporarily unavailable.",
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def test_post_init_payment_maps_temporary_cdek_validation_error_to_503() -> None:
|
||||
service = StubAggregatorService(
|
||||
response=None,
|
||||
error=InitPaymentUnavailableError("CDEK validation 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": "init_payment_unavailable",
|
||||
"message": "Payment initialization is temporarily unavailable.",
|
||||
}
|
||||
}
|
||||
assert service.calls == [InitPaymentRequest(**_valid_payload())]
|
||||
|
||||
|
||||
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