Добавлена отправка накладных на почту

This commit is contained in:
Раис Юсупалиев
2026-05-24 00:55:09 +03:00
parent 5526f90cb3
commit 50124fb2c9
36 changed files with 1615 additions and 22 deletions
@@ -312,6 +312,11 @@ observability:
service_name: "cdek-adapter-test-service"
otlp_endpoint: "http://collector:4317"
otlp_insecure: true
email:
smtp_host: "smtp.test"
smtp_port: 587
from_address: "no-reply@test"
""".strip(),
encoding="utf-8",
)
@@ -0,0 +1,115 @@
import asyncio
from typing import Any
import httpx
import pytest
from app.adapters.delivery_providers.cdek.client import (
CDEKClient,
CDEKClientError,
CDEKRequestError,
)
class StubAuthClient:
async def get_access_token(self) -> str:
return "test-token"
class SequenceHTTPClient:
def __init__(self, results: list[Any]) -> None:
self._results = results
self.calls: list[dict[str, Any]] = []
async def get(
self,
url: str,
*,
headers: dict[str, str] | None = None,
timeout: float | None = None,
) -> httpx.Response:
self.calls.append(
{"method": "GET", "url": url, "headers": headers, "timeout": timeout}
)
result = self._results[len(self.calls) - 1]
if isinstance(result, Exception):
raise result
return result
def _make_client(http_client: SequenceHTTPClient, **kwargs: Any) -> CDEKClient:
kwargs.setdefault("retry_attempts", 0)
return CDEKClient(
http_client=http_client, # type: ignore[arg-type]
auth_client=StubAuthClient(), # type: ignore[arg-type]
base_url="https://api.cdek.test/v2",
timeout_seconds=7.5,
**kwargs,
)
def test_download_waybill_pdf_returns_response_bytes() -> None:
pdf_bytes = b"%PDF-1.4 mock content"
response = httpx.Response(
200,
content=pdf_bytes,
request=httpx.Request("GET", "https://cdek.test/waybill/1.pdf"),
)
http_client = SequenceHTTPClient([response])
client = _make_client(http_client)
result = asyncio.run(
client.download_waybill_pdf("https://cdek.test/waybill/1.pdf")
)
assert result == pdf_bytes
assert http_client.calls[0]["url"] == "https://cdek.test/waybill/1.pdf"
assert http_client.calls[0]["headers"] == {"Authorization": "Bearer test-token"}
assert http_client.calls[0]["timeout"] == 7.5
def test_download_waybill_pdf_retries_on_5xx_and_succeeds() -> None:
request = httpx.Request("GET", "https://cdek.test/x.pdf")
flaky = httpx.Response(503, content=b"oops", request=request)
success = httpx.Response(200, content=b"%PDF", request=request)
http_client = SequenceHTTPClient([flaky, success])
sleep_calls: list[float] = []
async def fake_sleep(seconds: float) -> None:
sleep_calls.append(seconds)
client = _make_client(
http_client,
retry_attempts=1,
retry_backoff_seconds=0.1,
sleep=fake_sleep,
)
result = asyncio.run(client.download_waybill_pdf("https://cdek.test/x.pdf"))
assert result == b"%PDF"
assert len(http_client.calls) == 2
assert sleep_calls == [0.1]
def test_download_waybill_pdf_raises_request_error_on_4xx() -> None:
response = httpx.Response(
404,
content=b"not found",
request=httpx.Request("GET", "https://cdek.test/missing.pdf"),
)
http_client = SequenceHTTPClient([response])
client = _make_client(http_client)
with pytest.raises(CDEKRequestError, match="status 404"):
asyncio.run(client.download_waybill_pdf("https://cdek.test/missing.pdf"))
def test_download_waybill_pdf_raises_client_error_after_retries_exhausted() -> None:
request = httpx.Request("GET", "https://cdek.test/x.pdf")
response = httpx.Response(500, content=b"oops", request=request)
http_client = SequenceHTTPClient([response])
client = _make_client(http_client, retry_attempts=0)
with pytest.raises(CDEKClientError, match="retriable status 500"):
asyncio.run(client.download_waybill_pdf("https://cdek.test/x.pdf"))