This commit is contained in:
2026-07-09 16:45:17 +03:00
parent 5c7a9710f3
commit da24ed8ba7
23 changed files with 1542 additions and 358 deletions

View File

@@ -21,15 +21,9 @@ from tests.helpers import make_remote_user
class _StubSyncService:
def __init__(
self,
*,
cached_users_by_telegram: dict[int, list] | None = None,
active_promo_by_telegram: dict[int, object] | None = None,
) -> None:
def __init__(self, *, cached_users_by_telegram: dict[int, list] | None = None) -> None:
self.refreshed: list[int] = []
self.cached_users_by_telegram = cached_users_by_telegram or {}
self.active_promo_by_telegram = active_promo_by_telegram or {}
async def get_referral_summary(self, telegram_id: int):
return type(
@@ -50,9 +44,6 @@ class _StubSyncService:
async def get_cached_users_for_telegram(self, telegram_id: int):
return list(self.cached_users_by_telegram.get(telegram_id, []))
async def get_active_promo_for_user(self, telegram_id: int):
return self.active_promo_by_telegram.get(telegram_id)
class _StubRemnawaveClient:
def __init__(self, *, created_user, users_by_telegram_id: dict[int, list] | None = None) -> None:
@@ -62,6 +53,10 @@ class _StubRemnawaveClient:
self.users_by_telegram_id = users_by_telegram_id or {}
async def get_user_by_uuid(self, user_uuid: str):
for users in self.users_by_telegram_id.values():
for user in users:
if str(user.uuid) == user_uuid:
return user
raise RemnawaveApiError(status_code=404, message="not found")
async def get_user_by_username(self, username: str):
@@ -107,9 +102,6 @@ async def _seed_order(
telegram_id: int,
status: str,
order_uuid: str | None = None,
plan_code: str = "30d",
plan_title: str = "OREOL VPN на 30 дней",
amount_stars: int = 250,
) -> str:
resolved_order_uuid = order_uuid or str(uuid4())
@@ -128,12 +120,12 @@ async def _seed_order(
order_uuid=resolved_order_uuid,
telegram_user_id=user.id,
telegram_id=telegram_id,
plan_code=plan_code,
plan_title=plan_title,
plan_code="30d",
plan_title="OREOL VPN на 30 дней",
plan_duration_days=30,
traffic_limit_bytes=0,
traffic_limit_strategy="NO_RESET",
amount_stars=amount_stars,
amount_stars=250,
currency="RUB",
status=status,
invoice_payload=f"payload-{resolved_order_uuid}",
@@ -167,29 +159,7 @@ async def _seed_referral_invite(
def _make_settings() -> Settings:
return Settings.model_construct(
bot_admin_ids_raw="",
payment_product_plans_raw="",
payment_internal_squad_uuids_raw="11111111-1111-1111-1111-111111111111",
payment_vpn_squad_uuids_raw="",
payment_white_squad_uuids_raw="",
payment_user_tag="BOT",
payment_external_squad_uuid="",
payment_username_prefix="Oreol",
payment_plan_traffic_limit_gb=0,
payment_plan_traffic_reset_period="NO_RESET",
referral_discount_percent=5,
referral_bonus_days=7,
)
def _make_product_settings() -> Settings:
return Settings.model_construct(
bot_admin_ids_raw="",
payment_plans_raw="30:999",
payment_product_plans_raw="vpn_white:30:450,white:30:250,vpn:30:200",
payment_transfer_text="Реквизиты",
payment_internal_squad_uuids_raw="",
payment_vpn_squad_uuids_raw="11111111-1111-1111-1111-111111111111",
payment_white_squad_uuids_raw="22222222-2222-2222-2222-222222222222",
payment_user_tag="BOT",
payment_external_squad_uuid="",
payment_username_prefix="Oreol",
@@ -292,37 +262,6 @@ async def test_approve_order_fulfills_and_refreshes_cache(session_factory) -> No
assert order.remnawave_user_uuid == str(remote_user.uuid)
async def test_approve_product_order_sends_product_squads_to_remnawave(session_factory) -> None:
remote_user = make_remote_user(
user_uuid="99999999-9999-9999-9999-999999999999",
user_id=9,
short_uuid="product999",
username="oreol-product",
telegram_id=1001,
)
remnawave_client = _StubRemnawaveClient(created_user=remote_user)
service = PaymentService(
settings=_make_product_settings(),
session_factory=session_factory,
remnawave_client=remnawave_client,
sync_service=_StubSyncService(),
)
order_uuid = await _seed_order(
session_factory,
telegram_id=1001,
status=REVIEW_PAYMENT_STATUS,
plan_code="white",
plan_title="OREOL VPN · Белые списки",
amount_stars=250,
)
await service.approve_order(order_uuid=order_uuid)
assert remnawave_client.created_bodies[0]["activeInternalSquads"] == [
"22222222-2222-2222-2222-222222222222",
]
async def test_approve_order_grants_and_applies_referral_bonus(session_factory) -> None:
buyer_remote_user = make_remote_user(
user_uuid="44444444-4444-4444-4444-444444444444",
@@ -475,16 +414,30 @@ async def test_create_order_generates_unique_provision_username(session_factory)
assert second_order.extends_existing_access is False
async def test_product_plan_uses_matching_squads_and_price(session_factory) -> None:
async def test_create_order_transliterates_russian_first_name_without_username(
session_factory,
) -> None:
service = PaymentService(
settings=_make_product_settings(),
settings=Settings.model_construct(
bot_admin_ids_raw="",
payment_plans_raw="30:250",
payment_transfer_text="Реквизиты",
payment_internal_squad_uuids_raw="11111111-1111-1111-1111-111111111111",
payment_user_tag="BOT",
payment_external_squad_uuid="",
payment_username_prefix="Oreol",
payment_plan_traffic_limit_gb=0,
payment_plan_traffic_reset_period="NO_RESET",
referral_discount_percent=5,
referral_bonus_days=7,
),
session_factory=session_factory,
remnawave_client=_StubRemnawaveClient(
created_user=make_remote_user(
user_uuid="99999999-9999-9999-9999-999999999999",
user_id=9,
short_uuid="buyer999",
username="oreol-product",
username="oreol-user",
telegram_id=1001,
)
),
@@ -493,75 +446,16 @@ async def test_product_plan_uses_matching_squads_and_price(session_factory) -> N
order = await service.create_order(
telegram_id=1001,
username="tester",
first_name="Test",
username=None,
first_name="\u0410\u043b\u0435\u043a\u0441\u0435\u0439",
last_name=None,
language_code="ru",
plan_code="vpn_white",
plan_code="30d",
)
assert order.plan.label == "VPN + Белые списки"
assert order.plan.amount_rub == 450
assert order.plan.internal_squad_uuids == [
"11111111-1111-1111-1111-111111111111",
"22222222-2222-2222-2222-222222222222",
]
async def test_promo_code_discounts_only_matching_plan(session_factory) -> None:
promo = type(
"PromoStub",
(),
{
"code": "WHITE50",
"discount_percent": 50,
"plan_code": "white",
"expires_at": None,
},
)()
service = PaymentService(
settings=_make_product_settings(),
session_factory=session_factory,
remnawave_client=_StubRemnawaveClient(
created_user=make_remote_user(
user_uuid="10101010-1010-1010-1010-101010101010",
user_id=10,
short_uuid="buyer101",
username="oreol-promo",
telegram_id=1001,
)
),
sync_service=_StubSyncService(active_promo_by_telegram={1001: promo}),
)
plans = await service.get_available_plans(telegram_id=1001)
assert [(plan.code, plan.amount_rub, plan.discount_code) for plan in plans] == [
("vpn_white", 450, ""),
("white", 125, "WHITE50"),
("vpn", 200, ""),
]
order = await service.create_order(
telegram_id=1001,
username="tester",
first_name="Test",
last_name=None,
language_code="ru",
plan_code="white",
)
assert order.plan.amount_rub == 125
assert order.plan.discount_source == "promo"
async with session_factory() as session:
stored_order = await session.scalar(
select(PaymentOrder).where(PaymentOrder.order_uuid == order.order_uuid)
)
assert stored_order is not None
assert stored_order.amount_stars == 125
assert stored_order.error_message == "WHITE50"
assert order.provision_username.startswith("Oreol-1001-")
assert order.provision_username.endswith("-Aleksey")
assert order.provision_username.isascii()
async def test_create_order_marks_existing_access_as_renewal(session_factory) -> None:
@@ -605,6 +499,68 @@ async def test_create_order_marks_existing_access_as_renewal(session_factory) ->
assert "продлит текущую подписку" in service.build_transfer_text(order=order)
async def test_create_order_targets_external_subscription_linked_by_telegram_id(
session_factory,
) -> None:
existing_remote_user = make_remote_user(
user_uuid="aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa",
user_id=10,
short_uuid="external10",
username="custom-external",
telegram_id=1001,
)
remnawave_client = _StubRemnawaveClient(
created_user=existing_remote_user,
users_by_telegram_id={1001: [existing_remote_user]},
)
service = PaymentService(
settings=Settings.model_construct(
bot_admin_ids_raw="",
payment_plans_raw="30:250",
payment_transfer_text="Реквизиты",
payment_internal_squad_uuids_raw="11111111-1111-1111-1111-111111111111",
payment_user_tag="BOT",
payment_external_squad_uuid="",
payment_username_prefix="Oreol",
payment_plan_traffic_limit_gb=0,
payment_plan_traffic_reset_period="NO_RESET",
referral_discount_percent=5,
referral_bonus_days=7,
),
session_factory=session_factory,
remnawave_client=remnawave_client,
sync_service=_StubSyncService(),
)
order = await service.create_order(
telegram_id=1001,
username="buyer",
first_name="Buyer",
last_name=None,
language_code="ru",
plan_code="30d",
)
assert order.extends_existing_access is True
assert order.provision_username == "custom-external"
async with session_factory() as session:
stored_order = await session.scalar(
select(PaymentOrder).where(PaymentOrder.order_uuid == order.order_uuid)
)
assert stored_order is not None
assert stored_order.remnawave_user_uuid == str(existing_remote_user.uuid)
await service.mark_order_under_review(order_uuid=order.order_uuid, telegram_id=1001)
access = await service.approve_order(order_uuid=order.order_uuid)
assert access.is_renewal is True
assert access.username == "custom-external"
assert remnawave_client.created_bodies == []
assert remnawave_client.updated_bodies[-1]["username"] == "custom-external"
async def test_approve_order_extends_existing_user_found_by_telegram_id(session_factory) -> None:
existing_remote_user = make_remote_user(
user_uuid="88888888-8888-8888-8888-888888888888",