473 lines
14 KiB
Python
473 lines
14 KiB
Python
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 "Добро пожаловать в <b>OREOL VPN</b>" in caption
|
||
assert "<b>Профиль</b>" not in caption
|
||
assert "<b>Подписка</b>" in caption
|
||
assert "Статус: <b>Не активна</b>" in caption
|
||
assert "Срок: <b>—</b>" in caption
|
||
assert "Трафик: <b>—</b>" 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 "<b>🛠 Админ-панель OREOL VPN</b>" in caption
|
||
assert "Пользователей в боте: <b>120</b>" in caption
|
||
assert "Чеков на проверке: <b>4</b>" in caption
|
||
assert "Открытых тикетов: <b>3</b>" in caption
|
||
assert "Бонусов в ожидании: <b>2</b>" 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 "Цена: <b>250 ₽</b>" 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 "<b>📜 Правила VPN</b>" 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 "<b>🆘 Помощь и сопровождение</b>" 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 "<b>👥 Реферальная система</b>" in caption
|
||
assert "Ваш реферальный код: <code>ABCD1234</code>" in caption
|
||
assert "Приглашено пользователей: <b>3</b>" 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 "Код: <code>PROMO777</code>" in caption
|
||
assert "Скидка перед оплатой: <b>17%</b>" 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
|