from __future__ import annotations from datetime import datetime from sqlalchemy import select from app.config import Settings from app.db.models import 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_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"