Files
g2s-aggregator/tests/repositories/order/test_repository.py
T
2026-05-23 19:27:32 +03:00

282 lines
9.5 KiB
Python

import asyncio
from collections.abc import Awaitable, Callable
from typing import Any
import pytest
from sqlalchemy import select
from sqlalchemy.exc import IntegrityError
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker
from sqlalchemy.ext.asyncio import create_async_engine
from app.repositories.order import OrderData, OrderRepository
from app.repositories.order.models import Base, Order
from tests.payment_fixtures import make_init_payment_payload
def _make_order_data(**overrides: object) -> OrderData:
payload: dict[str, Any] = {
"order_uuid": "order-uuid-1",
"payment_url": "https://pay.test/payment/1",
"price": 125000,
"tariff_code": 535,
"account_email": "client@example.com",
"payload": make_init_payment_payload(),
}
payload.update(overrides)
return OrderData(**payload)
async def _with_repository(
test_fn: Callable[
[OrderRepository, async_sessionmaker[AsyncSession]],
Awaitable[None],
],
) -> None:
engine = create_async_engine("sqlite+aiosqlite:///:memory:")
try:
async with engine.begin() as connection:
await connection.run_sync(Base.metadata.create_all)
session_factory = async_sessionmaker(engine, expire_on_commit=False)
await test_fn(OrderRepository(session_factory=session_factory), session_factory)
finally:
await engine.dispose()
def test_create_order_persists_all_required_fields() -> None:
async def run(
repository: OrderRepository,
session_factory: async_sessionmaker[AsyncSession],
) -> None:
order_data = _make_order_data()
async with repository.session() as session:
order = await repository.create_order(session, order_data)
async with session_factory() as session:
result = await session.execute(
select(Order).where(Order.order_uuid == "order-uuid-1")
)
persisted_order = result.scalar_one()
assert order.id == persisted_order.id
assert persisted_order.order_uuid == "order-uuid-1"
assert persisted_order.payment_url == "https://pay.test/payment/1"
assert persisted_order.price == 125000
assert persisted_order.tariff_code == 535
assert persisted_order.account_email == "client@example.com"
assert persisted_order.payload == order_data.payload
assert persisted_order.payment_status is None
assert persisted_order.tbank_payment_id is None
assert persisted_order.cdek_order_uuid is None
assert persisted_order.cdek_waybill_uuid is None
assert persisted_order.cdek_waybill_url is None
assert persisted_order.created_at is not None
assert persisted_order.updated_at is not None
asyncio.run(_with_repository(run))
def test_create_order_rejects_duplicate_order_uuid() -> None:
async def run(
repository: OrderRepository,
_session_factory: async_sessionmaker[AsyncSession],
) -> None:
order_data = _make_order_data()
async with repository.session() as session:
await repository.create_order(session, order_data)
with pytest.raises(IntegrityError):
async with repository.session() as session:
await repository.create_order(session, order_data)
asyncio.run(_with_repository(run))
def test_get_order_by_order_uuid_returns_persisted_order() -> None:
async def run(
repository: OrderRepository,
_session_factory: async_sessionmaker[AsyncSession],
) -> None:
order_data = _make_order_data()
async with repository.session() as session:
await repository.create_order(session, order_data)
async with repository.session() as session:
order = await repository.get_order_by_order_uuid(session, "order-uuid-1")
assert order is not None
assert order.order_uuid == "order-uuid-1"
assert order.payment_url == "https://pay.test/payment/1"
asyncio.run(_with_repository(run))
def test_get_order_by_order_uuid_returns_none_for_missing_order() -> None:
async def run(
repository: OrderRepository,
_session_factory: async_sessionmaker[AsyncSession],
) -> None:
async with repository.session() as session:
order = await repository.get_order_by_order_uuid(session, "missing-order")
assert order is None
asyncio.run(_with_repository(run))
def test_mark_payment_status_persists_status_and_payment_id() -> None:
async def run(
repository: OrderRepository,
session_factory: async_sessionmaker[AsyncSession],
) -> None:
async with repository.session() as session:
await repository.create_order(session, _make_order_data())
async with repository.session() as session:
order = await repository.mark_payment_status(
session,
"order-uuid-1",
"CONFIRMED",
8347568144,
)
async with session_factory() as session:
result = await session.execute(
select(Order).where(Order.order_uuid == "order-uuid-1")
)
persisted_order = result.scalar_one()
assert order is not None
assert persisted_order.payment_status == "CONFIRMED"
assert persisted_order.tbank_payment_id == 8347568144
asyncio.run(_with_repository(run))
def test_mark_payment_status_returns_none_for_missing_order() -> None:
async def run(
repository: OrderRepository,
_session_factory: async_sessionmaker[AsyncSession],
) -> None:
async with repository.session() as session:
order = await repository.mark_payment_status(
session,
"missing-order",
"CONFIRMED",
8347568144,
)
assert order is None
asyncio.run(_with_repository(run))
def test_mark_cdek_order_registered_persists_cdek_order_and_waybill() -> None:
async def run(
repository: OrderRepository,
session_factory: async_sessionmaker[AsyncSession],
) -> None:
async with repository.session() as session:
await repository.create_order(session, _make_order_data())
async with repository.session() as session:
order = await repository.mark_cdek_order_registered(
session,
"order-uuid-1",
"cdek-order-uuid-1",
"waybill-uuid-1",
"https://cdek.test/waybill/1.pdf",
)
async with session_factory() as session:
result = await session.execute(
select(Order).where(Order.order_uuid == "order-uuid-1")
)
persisted_order = result.scalar_one()
assert order is not None
assert persisted_order.cdek_order_uuid == "cdek-order-uuid-1"
assert persisted_order.cdek_waybill_uuid == "waybill-uuid-1"
assert persisted_order.cdek_waybill_url == "https://cdek.test/waybill/1.pdf"
asyncio.run(_with_repository(run))
def test_mark_cdek_order_registered_allows_missing_waybill_fields() -> None:
async def run(
repository: OrderRepository,
session_factory: async_sessionmaker[AsyncSession],
) -> None:
async with repository.session() as session:
await repository.create_order(session, _make_order_data())
async with repository.session() as session:
await repository.mark_cdek_order_registered(
session,
"order-uuid-1",
"cdek-order-uuid-1",
)
async with session_factory() as session:
result = await session.execute(
select(Order).where(Order.order_uuid == "order-uuid-1")
)
persisted_order = result.scalar_one()
assert persisted_order.cdek_order_uuid == "cdek-order-uuid-1"
assert persisted_order.cdek_waybill_uuid is None
assert persisted_order.cdek_waybill_url is None
asyncio.run(_with_repository(run))
def test_mark_cdek_order_registered_is_idempotent_for_same_uuid() -> None:
async def run(
repository: OrderRepository,
session_factory: async_sessionmaker[AsyncSession],
) -> None:
async with repository.session() as session:
await repository.create_order(session, _make_order_data())
async with repository.session() as session:
await repository.mark_cdek_order_registered(
session,
"order-uuid-1",
"cdek-order-uuid-1",
)
async with repository.session() as session:
await repository.mark_cdek_order_registered(
session,
"order-uuid-1",
"cdek-order-uuid-1",
)
async with session_factory() as session:
result = await session.execute(select(Order))
orders = result.scalars().all()
assert len(orders) == 1
assert orders[0].cdek_order_uuid == "cdek-order-uuid-1"
asyncio.run(_with_repository(run))
def test_mark_cdek_order_registered_returns_none_for_missing_order() -> None:
async def run(
repository: OrderRepository,
_session_factory: async_sessionmaker[AsyncSession],
) -> None:
async with repository.session() as session:
order = await repository.mark_cdek_order_registered(
session,
"missing-order",
"cdek-order-uuid-1",
)
assert order is None
asyncio.run(_with_repository(run))