Files
RemnaWaveBOT/tests/test_sync_service.py
2026-05-06 15:41:56 +03:00

322 lines
10 KiB
Python

from __future__ import annotations
from datetime import datetime, timedelta
from sqlalchemy import select
from app.config import Settings
from app.db.base import utcnow
from app.db.models import (
PromoCode,
PromoCodeApplication,
ReferralCode,
ReferralInvite,
RemnawaveUser,
TelegramUser,
)
from app.schemas.remnawave import PaginatedUsers
from app.services.sync_service import SyncService
from tests.helpers import make_remote_user
class _StubRemnawaveClient:
def __init__(self, users) -> None:
self.users = users
async def get_users_by_telegram_id(self, telegram_id: int):
return list(self.users)
async def get_all_users(self, *, start: int = 0, size: int = 100):
items = list(self.users)[start : start + size]
return PaginatedUsers(users=items, total=len(self.users))
async def test_refresh_cached_users_clears_stale_links_and_keeps_profile(
session_factory,
) -> None:
user_one = make_remote_user(
user_uuid="44444444-4444-4444-4444-444444444444",
user_id=4,
short_uuid="user-one",
username="oreol-one",
telegram_id=1001,
)
user_two = make_remote_user(
user_uuid="55555555-5555-5555-5555-555555555555",
user_id=5,
short_uuid="user-two",
username="oreol-two",
telegram_id=1001,
)
client = _StubRemnawaveClient([user_one, user_two])
service = SyncService(
settings=Settings.model_construct(
bot_admin_ids_raw="",
sync_subscription_history=False,
),
session_factory=session_factory,
remnawave_client=client,
)
await service.sync_users_for_telegram(
telegram_id=1001,
username="keeper",
first_name="Keep",
last_name="Me",
language_code="ru",
)
client.users = [user_one]
views = await service.refresh_cached_users_for_telegram(telegram_id=1001)
assert [view.record.rw_uuid for view in views] == [str(user_one.uuid)]
async with session_factory() as session:
telegram_user = await session.scalar(
select(TelegramUser).where(TelegramUser.telegram_id == 1001)
)
stale_user = await session.scalar(
select(RemnawaveUser).where(RemnawaveUser.rw_uuid == str(user_two.uuid))
)
assert telegram_user is not None
assert telegram_user.username == "keeper"
assert stale_user is not None
assert stale_user.telegram_id is None
assert stale_user.owner_telegram_user_id is None
async def test_get_admin_telegram_users_page_returns_paginated_users(
session_factory,
) -> None:
service = SyncService(
settings=Settings.model_construct(
bot_admin_ids_raw="",
sync_subscription_history=False,
),
session_factory=session_factory,
remnawave_client=_StubRemnawaveClient([]),
)
async with session_factory() as session:
session.add_all(
[
TelegramUser(
telegram_id=1001,
username="first",
first_name="First",
last_name="User",
language_code="ru",
last_seen_at=datetime(2026, 4, 21, 10, 0),
created_at=datetime(2026, 4, 1, 10, 0),
updated_at=datetime(2026, 4, 21, 10, 0),
),
TelegramUser(
telegram_id=1002,
username="second",
first_name="Second",
last_name="User",
language_code="ru",
last_seen_at=datetime(2026, 4, 22, 12, 0),
created_at=datetime(2026, 4, 2, 10, 0),
updated_at=datetime(2026, 4, 22, 12, 0),
),
TelegramUser(
telegram_id=1003,
username="third",
first_name="Third",
last_name="User",
language_code="ru",
last_seen_at=datetime(2026, 4, 22, 12, 0),
created_at=datetime(2026, 4, 3, 10, 0),
updated_at=datetime(2026, 4, 22, 12, 0),
),
]
)
await session.commit()
page_one = await service.get_admin_telegram_users_page(page=1, page_size=2)
page_two = await service.get_admin_telegram_users_page(page=2, page_size=2)
assert page_one.total_items == 3
assert page_one.total_pages == 2
assert [item.telegram_id for item in page_one.items] == [1003, 1002]
assert [item.telegram_id for item in page_two.items] == [1001]
async def test_get_admin_telegram_user_profile_returns_referral_and_access_data(
session_factory,
) -> None:
remote_user = make_remote_user(
user_uuid="66666666-6666-6666-6666-666666666666",
user_id=6,
short_uuid="profile-user",
username="oreol-profile",
telegram_id=2001,
)
service = SyncService(
settings=Settings.model_construct(
bot_admin_ids_raw="1001",
sync_subscription_history=False,
),
session_factory=session_factory,
remnawave_client=_StubRemnawaveClient([remote_user]),
)
await service.sync_users_for_telegram(
telegram_id=2001,
username="profile",
first_name="Profile",
last_name="User",
language_code="ru",
)
async with session_factory() as session:
session.add(
TelegramUser(
telegram_id=1001,
username="inviter",
first_name="Inviter",
last_name="Admin",
language_code="ru",
is_admin=True,
last_seen_at=datetime(2026, 4, 22, 11, 0),
created_at=datetime(2026, 4, 1, 8, 0),
updated_at=datetime(2026, 4, 22, 11, 0),
)
)
session.add(
ReferralCode(telegram_id=1001, code="INVITER1001")
)
session.add(
ReferralCode(telegram_id=2001, code="SELF2001")
)
session.add_all(
[
ReferralInvite(
inviter_telegram_id=1001,
invited_telegram_id=2001,
invited_username="profile",
invited_display_name="Profile User",
),
ReferralInvite(
inviter_telegram_id=2001,
invited_telegram_id=3001,
invited_username="child",
invited_display_name="Child User",
),
]
)
await session.commit()
profile = await service.get_admin_telegram_user_profile(telegram_id=2001)
assert profile is not None
assert profile.user.telegram_id == 2001
assert profile.referral_code == "SELF2001"
assert profile.applied_referral_code == "INVITER1001"
assert profile.invited_count == 1
assert profile.recent_invited_names == ["Child User"]
assert len(profile.accesses) == 1
assert profile.accesses[0].record.username == "oreol-profile"
async def test_promo_code_can_be_applied_through_referral_input(session_factory) -> None:
service = SyncService(
settings=Settings.model_construct(
bot_admin_ids_raw="",
sync_subscription_history=False,
),
session_factory=session_factory,
remnawave_client=_StubRemnawaveClient([]),
)
promo = await service.create_promo_code(
code="white50",
plan_code="white",
discount_percent=50,
expires_at=utcnow() + timedelta(days=7),
created_by_telegram_id=1001,
)
applied_code = await service.apply_referral_code(
telegram_id=2001,
username="buyer",
first_name="Buyer",
last_name=None,
language_code="ru",
referral_code="white50",
)
summary = await service.get_referral_summary(2001)
active_promo = await service.get_active_promo_for_user(2001)
assert promo.code == "WHITE50"
assert applied_code == "WHITE50"
assert summary.applied_referral_code == ""
assert summary.applied_promo_code == "WHITE50"
assert summary.applied_promo_discount_percent == 50
assert summary.applied_promo_plan_code == "white"
assert active_promo is not None
assert active_promo.code == "WHITE50"
async with session_factory() as session:
promo_record = await session.scalar(
select(PromoCode).where(PromoCode.code == "WHITE50")
)
application = await session.scalar(
select(PromoCodeApplication).where(PromoCodeApplication.telegram_id == 2001)
)
assert promo_record is not None
assert application is not None
assert application.promo_code_id == promo_record.id
async def test_sync_all_users_does_not_autoflush_half_built_user(
session_factory,
) -> None:
remote_user = make_remote_user(
user_uuid="77777777-7777-7777-7777-777777777777",
user_id=7,
short_uuid="sync-all-user",
username="oreol-sync-all",
telegram_id=4001,
)
service = SyncService(
settings=Settings.model_construct(
bot_admin_ids_raw="",
sync_subscription_history=False,
),
session_factory=session_factory,
remnawave_client=_StubRemnawaveClient([remote_user]),
)
async with session_factory() as session:
session.add(
TelegramUser(
telegram_id=4001,
username="owner",
first_name="Sync",
last_name="Owner",
language_code="ru",
)
)
await session.commit()
total = await service.sync_all_users(batch_size=50)
assert total == 1
async with session_factory() as session:
owner = await session.scalar(
select(TelegramUser).where(TelegramUser.telegram_id == 4001)
)
cached_user = await session.scalar(
select(RemnawaveUser).where(RemnawaveUser.rw_uuid == str(remote_user.uuid))
)
assert owner is not None
assert cached_user is not None
assert cached_user.rw_id == 7
assert cached_user.owner_telegram_user_id == owner.id
assert cached_user.username == "oreol-sync-all"