from datetime import datetime, timedelta from types import SimpleNamespace from app.bot.ui.panel import ( PANEL_SECTION_ADMIN, PANEL_SECTION_HOME, PANEL_SECTION_REFERRAL, PANEL_SECTION_SUBSCRIPTION, PANEL_SECTION_SUPPORT, PANEL_SECTION_TERMS, PanelContext, build_panel_keyboard, render_panel_caption, ) from app.services.sync_service import CachedUserView def _button_texts(markup) -> list[str]: return [button.text for row in markup.inline_keyboard for button in row] def _cached_user_view(*, telegram_id: int = 123456789) -> CachedUserView: return CachedUserView( record=SimpleNamespace( username="oreol-renew", status="ACTIVE", expire_at=datetime.now() + timedelta(days=10), used_traffic_bytes=0, traffic_limit_bytes=0, short_uuid="renew999", subscription_url="https://example.com/sub/renew999", telegram_id=telegram_id, ), internal_squads=["VPN"], ) def test_home_panel_caption_without_subscription() -> None: context = PanelContext( telegram_id=123456789, display_name="Evgeniy Spirin", username="evgeniy", language_code="ru", is_admin=False, users=[], api_error=None, ) caption = render_panel_caption( section=PANEL_SECTION_HOME, context=context, brand_name="OREOL VPN", ) assert "Добро пожаловать в OREOL VPN" in caption assert "Профиль" not in caption assert "Подписка" in caption assert "Статус: Не активна" in caption assert "Срок: " in caption assert "Трафик: " in caption def test_home_panel_keyboard_has_expected_buttons() -> None: context = PanelContext( telegram_id=123456789, display_name="Evgeniy Spirin", username="evgeniy", language_code="ru", is_admin=False, users=[], api_error=None, ) markup = build_panel_keyboard(section=PANEL_SECTION_HOME, context=context) button_texts = _button_texts(markup) assert "Профиль" not in button_texts assert "💳 Подписка" in button_texts assert "🆘 Поддержка" in button_texts assert "👥 Реферальная система" in button_texts assert "📜 Условия использования" in button_texts assert len(markup.inline_keyboard[0]) == 1 assert markup.inline_keyboard[0][0].text == "💳 Подписка" assert markup.inline_keyboard[0][0].callback_data == "payment:menu" def test_admin_home_panel_keyboard_has_admin_button() -> None: context = PanelContext( telegram_id=123456789, display_name="Evgeniy Spirin", username="evgeniy", language_code="ru", is_admin=True, users=[], api_error=None, ) markup = build_panel_keyboard(section=PANEL_SECTION_HOME, context=context) button_texts = _button_texts(markup) assert "🛠 Админ-панель" in button_texts assert len(markup.inline_keyboard[1]) == 1 assert markup.inline_keyboard[1][0].text == "🛠 Админ-панель" assert markup.inline_keyboard[1][0].callback_data == "panel:admin" def test_admin_panel_caption_contains_dashboard_stats() -> None: context = PanelContext( telegram_id=123456789, display_name="Evgeniy Spirin", username="evgeniy", language_code="ru", is_admin=True, users=[], admin_total_telegram_users=120, admin_total_cached_users=80, admin_active_cached_users=67, admin_pending_orders=9, admin_review_orders=4, admin_open_tickets=3, admin_pending_referral_bonuses=2, api_error=None, ) caption = render_panel_caption( section=PANEL_SECTION_ADMIN, context=context, brand_name="OREOL VPN", ) assert "🛠 Админ-панель OREOL VPN" in caption assert "Пользователей в боте: 120" in caption assert "Чеков на проверке: 4" in caption assert "Открытых тикетов: 3" in caption assert "Бонусов в ожидании: 2" in caption def test_admin_panel_keyboard_has_expected_buttons() -> None: context = PanelContext( telegram_id=123456789, display_name="Evgeniy Spirin", username="evgeniy", language_code="ru", is_admin=True, users=[], api_error=None, ) markup = build_panel_keyboard( section=PANEL_SECTION_ADMIN, context=context, payment_review_url="https://t.me/c/123/56/57", support_ticket_url="https://t.me/c/123/2/3", ) button_texts = _button_texts(markup) assert "🔍 Найти пользователя" in button_texts assert "👤 Пользователи" in button_texts assert "🔄 Полная синхронизация" in button_texts assert "💸 Очередь оплат" in button_texts assert "🎫 Тикеты" in button_texts assert "↻ Обновить" in button_texts assert "🏠 Главное меню" in button_texts def test_subscription_caption_without_subscription_but_with_payment() -> None: context = PanelContext( telegram_id=123456789, display_name="Evgeniy Spirin", username="evgeniy", language_code="ru", is_admin=False, users=[], payment_enabled=True, payment_plan_title="OREOL VPN на 30 дней", payment_plan_description="Доступ к VPN на 30 дней", payment_plan_price_stars=250, payment_plan_duration_days=30, payment_plan_traffic_limit_bytes=0, api_error=None, ) caption = render_panel_caption( section=PANEL_SECTION_SUBSCRIPTION, context=context, brand_name="OREOL VPN", ) assert "У вас пока нет активного доступа." in caption assert "OREOL VPN на 30 дней" in caption assert "Цена: 250 ₽" in caption assert "После выбора тарифа бот покажет реквизиты" in caption def test_subscription_caption_without_payment_uses_ui_instructions() -> None: context = PanelContext( telegram_id=123456789, display_name="Evgeniy Spirin", username="evgeniy", language_code="ru", is_admin=False, users=[], payment_enabled=False, api_error=None, ) caption = render_panel_caption( section=PANEL_SECTION_SUBSCRIPTION, context=context, brand_name="OREOL VPN", ) assert "откройте поддержку" in caption assert "short UUID" in caption assert "/link" not in caption def test_subscription_keyboard_has_buy_button() -> None: context = PanelContext( telegram_id=123456789, display_name="Evgeniy Spirin", username="evgeniy", language_code="ru", is_admin=False, users=[], payment_enabled=True, api_error=None, ) markup = build_panel_keyboard(section=PANEL_SECTION_SUBSCRIPTION, context=context) button_texts = _button_texts(markup) assert "💳 Купить подписку" in button_texts assert "🏠 Главное меню" in button_texts def test_subscription_keyboard_has_renew_button_when_access_exists() -> None: context = PanelContext( telegram_id=123456789, display_name="Evgeniy Spirin", username="evgeniy", language_code="ru", is_admin=False, users=[_cached_user_view()], payment_enabled=True, api_error=None, ) markup = build_panel_keyboard(section=PANEL_SECTION_SUBSCRIPTION, context=context) button_texts = _button_texts(markup) assert "💳 Продлить подписку" in button_texts assert "💳 Купить подписку" not in button_texts def test_subscription_caption_with_existing_access_mentions_renewal() -> None: context = PanelContext( telegram_id=123456789, display_name="Evgeniy Spirin", username="evgeniy", language_code="ru", is_admin=False, users=[_cached_user_view()], payment_enabled=True, api_error=None, ) caption = render_panel_caption( section=PANEL_SECTION_SUBSCRIPTION, context=context, brand_name="OREOL VPN", ) assert "Продлить подписку" in caption assert "автоматически добавит выбранный срок" in caption def test_terms_panel_caption() -> None: context = PanelContext( telegram_id=123456789, display_name="Evgeniy Spirin", username="evgeniy", language_code="ru", is_admin=False, users=[], api_error=None, ) caption = render_panel_caption( section=PANEL_SECTION_TERMS, context=context, brand_name="OREOL VPN", ) assert "📜 Правила VPN" in caption assert "использовать VPN только в законных целях" in caption assert "не гарантирует полной анонимности" in caption def test_support_panel_has_create_ticket_button() -> None: context = PanelContext( telegram_id=123456789, display_name="Evgeniy Spirin", username="evgeniy", language_code="ru", is_admin=False, users=[], api_error=None, ) markup = build_panel_keyboard(section=PANEL_SECTION_SUPPORT, context=context) button_texts = _button_texts(markup) assert "🎫 Создать тикет" in button_texts assert "🏠 Главное меню" in button_texts def test_support_panel_has_support_url_button() -> None: context = PanelContext( telegram_id=123456789, display_name="Evgeniy Spirin", username="evgeniy", language_code="ru", is_admin=False, users=[], api_error=None, ) markup = build_panel_keyboard( section=PANEL_SECTION_SUPPORT, context=context, support_url="https://t.me/support_account", ) button_texts = _button_texts(markup) assert "Написать в поддержку" in button_texts def test_support_panel_caption() -> None: context = PanelContext( telegram_id=123456789, display_name="Evgeniy Spirin", username="evgeniy", language_code="ru", is_admin=False, users=[], api_error=None, ) caption = render_panel_caption( section=PANEL_SECTION_SUPPORT, context=context, brand_name="OREOL VPN", ) assert "🆘 Помощь и сопровождение" in caption assert "откройте тикет через кнопку ниже" in caption def test_referral_panel_caption_with_link() -> None: context = PanelContext( telegram_id=123456789, display_name="Evgeniy Spirin", username="evgeniy", language_code="ru", is_admin=False, users=[], referral_code="ABCD1234", referral_count=3, referral_link="https://t.me/oreol_vpn_bot?start=ref_ABCD1234", referral_share_url="https://t.me/share/url?url=test", referral_recent_names=["Alex", "Maria"], referral_discount_percent=12, referral_bonus_days=7, api_error=None, ) caption = render_panel_caption( section=PANEL_SECTION_REFERRAL, context=context, brand_name="OREOL VPN", ) assert "👥 Реферальная система" in caption assert "Ваш реферальный код: ABCD1234" in caption assert "Приглашено пользователей: 3" in caption assert "+7 дней" in caption assert "Чтобы получить скидку 12%" in caption assert "https://t.me/oreol_vpn_bot?start=ref_ABCD1234" in caption assert "Alex" in caption assert "Maria" in caption def test_referral_caption_uses_runtime_discount_for_applied_code() -> None: context = PanelContext( telegram_id=123456789, display_name="Evgeniy Spirin", username="evgeniy", language_code="ru", is_admin=False, users=[], referral_code="ABCD1234", referral_count=1, applied_referral_code="PROMO777", referral_discount_percent=17, api_error=None, ) caption = render_panel_caption( section=PANEL_SECTION_REFERRAL, context=context, brand_name="OREOL VPN", ) assert "Код: PROMO777" in caption assert "Скидка перед оплатой: 17%" in caption def test_admin_panel_keyboard_has_settings_button() -> None: context = PanelContext( telegram_id=123456789, display_name="Evgeniy Spirin", username="evgeniy", language_code="ru", is_admin=True, users=[], api_error=None, ) markup = build_panel_keyboard(section=PANEL_SECTION_ADMIN, context=context) button_texts = _button_texts(markup) assert any("Настрой" in text for text in button_texts) def test_home_panel_hides_referral_when_disabled() -> None: context = PanelContext( telegram_id=123456789, display_name="Evgeniy Spirin", username="evgeniy", language_code="ru", is_admin=False, users=[], referral_enabled=False, api_error=None, ) markup = build_panel_keyboard(section=PANEL_SECTION_HOME, context=context) button_texts = _button_texts(markup) assert all("Реферал" not in text for text in button_texts) def test_referral_panel_caption_when_disabled() -> None: context = PanelContext( telegram_id=123456789, display_name="Evgeniy Spirin", username="evgeniy", language_code="ru", is_admin=False, users=[], referral_enabled=False, api_error=None, ) caption = render_panel_caption( section=PANEL_SECTION_REFERRAL, context=context, brand_name="OREOL VPN", ) assert "отключена" in caption