eb18994b7d
- Migrate 660+ datetime.utcnow() across 153 files to datetime.now(UTC) - Migrate 30+ datetime.now() without UTC to datetime.now(UTC) - Convert all 170 DateTime columns to DateTime(timezone=True) - Add migrate_datetime_to_timestamptz() in universal_migration with SET LOCAL timezone='UTC' safety - Remove 70+ .replace(tzinfo=None) workarounds - Fix utcfromtimestamp → fromtimestamp(..., tz=UTC) - Fix fromtimestamp() without tz= (system_logs, backup_service, referral_diagnostics) - Fix fromisoformat/isoparse to ensure aware output (platega, yookassa, wata, miniapp, nalogo) - Fix strptime() to add .replace(tzinfo=UTC) (backup_service, referral_diagnostics) - Fix datetime.combine() to include tzinfo=UTC (remnawave_sync, traffic_monitoring) - Fix datetime.max/datetime.min sentinels with .replace(tzinfo=UTC) - Rename panel_datetime_to_naive_utc → panel_datetime_to_utc - Remove DTZ003 from ruff ignore list
182 lines
6.1 KiB
Python
182 lines
6.1 KiB
Python
"""Тесты низкоуровневого сервиса YooKassaService."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import asyncio
|
|
import sys
|
|
from datetime import UTC, datetime
|
|
from pathlib import Path
|
|
from types import SimpleNamespace
|
|
|
|
import pytest
|
|
|
|
|
|
ROOT_DIR = Path(__file__).resolve().parents[2]
|
|
if str(ROOT_DIR) not in sys.path:
|
|
sys.path.insert(0, str(ROOT_DIR))
|
|
|
|
from yookassa import Configuration, Payment as YooKassaPayment # type: ignore
|
|
|
|
from app.config import settings
|
|
from app.services.yookassa_service import YooKassaService
|
|
|
|
|
|
@pytest.fixture
|
|
def anyio_backend() -> str:
|
|
return 'asyncio'
|
|
|
|
|
|
class DummyLoop:
|
|
async def run_in_executor(self, _executor, func):
|
|
return func()
|
|
|
|
|
|
def _prepare_config(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
monkeypatch.setattr(settings, 'YOOKASSA_SHOP_ID', 'shop123', raising=False)
|
|
monkeypatch.setattr(settings, 'YOOKASSA_SECRET_KEY', 'secret123', raising=False)
|
|
monkeypatch.setattr(settings, 'YOOKASSA_RETURN_URL', 'https://example.com/return', raising=False)
|
|
monkeypatch.setattr(settings, 'YOOKASSA_VAT_CODE', 1, raising=False)
|
|
monkeypatch.setattr(settings, 'YOOKASSA_PAYMENT_MODE', 'full_payment', raising=False)
|
|
monkeypatch.setattr(settings, 'YOOKASSA_PAYMENT_SUBJECT', 'service', raising=False)
|
|
|
|
|
|
def test_init_without_credentials(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
monkeypatch.setattr(settings, 'YOOKASSA_SHOP_ID', '', raising=False)
|
|
monkeypatch.setattr(settings, 'YOOKASSA_SECRET_KEY', '', raising=False)
|
|
service = YooKassaService()
|
|
assert service.configured is False
|
|
assert service.return_url == 'https://t.me/'
|
|
|
|
|
|
@pytest.mark.anyio('asyncio')
|
|
async def test_create_payment_success(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
_prepare_config(monkeypatch)
|
|
monkeypatch.setattr(settings, 'YOOKASSA_DEFAULT_RECEIPT_EMAIL', None, raising=False)
|
|
monkeypatch.setattr(asyncio, 'get_running_loop', lambda: DummyLoop(), raising=False)
|
|
|
|
captured_config: dict[str, tuple[str, str]] = {}
|
|
|
|
def fake_configure(shop_id: str, secret_key: str) -> None:
|
|
captured_config['values'] = (shop_id, secret_key)
|
|
|
|
monkeypatch.setattr(Configuration, 'configure', fake_configure, raising=False)
|
|
|
|
response_obj = SimpleNamespace(
|
|
id='yk_1',
|
|
status='pending',
|
|
paid=False,
|
|
confirmation=SimpleNamespace(confirmation_url='https://yk/confirm'),
|
|
metadata={'meta': 'value'},
|
|
amount=SimpleNamespace(value='140.00', currency='RUB'),
|
|
refundable=True,
|
|
created_at=datetime(2024, 1, 1, 12, 0, 0, tzinfo=UTC),
|
|
description='Desc',
|
|
test=False,
|
|
)
|
|
|
|
monkeypatch.setattr(
|
|
YooKassaPayment,
|
|
'create',
|
|
staticmethod(lambda payload, key: response_obj),
|
|
raising=False,
|
|
)
|
|
|
|
service = YooKassaService()
|
|
monkeypatch.setattr(settings, 'YOOKASSA_DEFAULT_RECEIPT_EMAIL', 'fallback@example.com', raising=False)
|
|
|
|
result = await service.create_payment(
|
|
amount=140.0,
|
|
currency='RUB',
|
|
description='Пополнение',
|
|
metadata={'order': '1'},
|
|
receipt_email='user@example.com',
|
|
)
|
|
|
|
assert service.configured is True
|
|
assert captured_config['values'] == ('shop123', 'secret123')
|
|
assert result is not None
|
|
assert result['id'] == 'yk_1'
|
|
assert result['confirmation_url'] == 'https://yk/confirm'
|
|
assert result['amount_value'] == 140.0
|
|
assert result['status'] == 'pending'
|
|
|
|
|
|
@pytest.mark.anyio('asyncio')
|
|
async def test_create_payment_without_contacts(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
_prepare_config(monkeypatch)
|
|
monkeypatch.setattr(settings, 'YOOKASSA_DEFAULT_RECEIPT_EMAIL', None, raising=False)
|
|
monkeypatch.setattr(Configuration, 'configure', lambda *args, **kwargs: None, raising=False)
|
|
monkeypatch.setattr(asyncio, 'get_running_loop', lambda: DummyLoop(), raising=False)
|
|
monkeypatch.setattr(
|
|
YooKassaPayment,
|
|
'create',
|
|
staticmethod(lambda payload, key: SimpleNamespace()),
|
|
raising=False,
|
|
)
|
|
|
|
service = YooKassaService()
|
|
result = await service.create_payment(
|
|
amount=10,
|
|
currency='RUB',
|
|
description='desc',
|
|
metadata={},
|
|
)
|
|
assert result is not None
|
|
assert result.get('error') is True
|
|
|
|
|
|
@pytest.mark.anyio('asyncio')
|
|
async def test_create_payment_returns_none_when_not_configured(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
monkeypatch.setattr(settings, 'YOOKASSA_SHOP_ID', '', raising=False)
|
|
monkeypatch.setattr(settings, 'YOOKASSA_SECRET_KEY', '', raising=False)
|
|
service = YooKassaService()
|
|
result = await service.create_payment(
|
|
amount=10,
|
|
currency='RUB',
|
|
description='desc',
|
|
metadata={},
|
|
)
|
|
assert result is None
|
|
|
|
|
|
@pytest.mark.anyio('asyncio')
|
|
async def test_create_sbp_payment_success(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
_prepare_config(monkeypatch)
|
|
monkeypatch.setattr(asyncio, 'get_running_loop', lambda: DummyLoop(), raising=False)
|
|
monkeypatch.setattr(Configuration, 'configure', lambda *args, **kwargs: None, raising=False)
|
|
monkeypatch.setattr(settings, 'YOOKASSA_DEFAULT_RECEIPT_EMAIL', 'fallback@example.com', raising=False)
|
|
|
|
response_obj = SimpleNamespace(
|
|
id='sbp_001',
|
|
status='pending',
|
|
paid=False,
|
|
confirmation=SimpleNamespace(confirmation_url='https://sbp/confirm'),
|
|
metadata={'meta': 'value'},
|
|
amount=SimpleNamespace(value='200.00', currency='RUB'),
|
|
refundable=False,
|
|
created_at=datetime(2024, 2, 1, 9, 0, 0, tzinfo=UTC),
|
|
description='SBP payment',
|
|
test=True,
|
|
)
|
|
|
|
monkeypatch.setattr(
|
|
YooKassaPayment,
|
|
'create',
|
|
staticmethod(lambda payload, key: response_obj),
|
|
raising=False,
|
|
)
|
|
|
|
service = YooKassaService()
|
|
result = await service.create_sbp_payment(
|
|
amount=200.0,
|
|
currency='rub',
|
|
description='Оплата',
|
|
metadata={'type': 'sbp'},
|
|
receipt_phone='+70000000000',
|
|
)
|
|
|
|
assert result is not None
|
|
assert result['id'] == 'sbp_001'
|
|
assert result['confirmation_url'] == 'https://sbp/confirm'
|
|
assert result['status'] == 'pending'
|