Files
remnawave-minishop/tests/test_user_bot_menu.py
T
3252a8 33707b7257 chore: remove terms of service setting
Keep privacy policy and user agreement links as the supported legal documents.

Refresh the admin settings manifest, demo dataset, locales, docs, and bot menu tests.
2026-06-04 00:03:28 +03:00

476 lines
17 KiB
Python

import json
import unittest
from pathlib import Path
from types import SimpleNamespace
from bot.app.web import subscription_webapp
from bot.handlers.user import referral
from bot.handlers.user.subscription.core import (
_format_premium_usage_limit,
_with_subscription_purchase_description,
)
from bot.keyboards.inline.user_keyboards import (
get_bot_interface_inline_keyboard,
get_connect_and_main_keyboard,
get_information_links_keyboard,
get_language_selection_keyboard,
get_main_menu_inline_keyboard,
get_payment_method_keyboard,
get_referral_link_keyboard,
get_subscription_options_keyboard,
get_tariff_catalog_keyboard,
get_tariff_periods_keyboard,
get_yk_autopay_choice_keyboard,
payment_methods_back_callback,
payment_options_back_callback,
)
from bot.middlewares.i18n import LOCALE_KEY_ALIASES
from config.tariffs_config import TariffsConfig
class JsonI18nStub:
def __init__(self):
self.translations = json.loads(Path("locales/en.json").read_text(encoding="utf-8"))
def gettext(self, lang, key, **kwargs):
key = LOCALE_KEY_ALIASES.get(key, key)
text = self.translations[key]
return text.format(**kwargs) if kwargs else text
class UserBotMenuTests(unittest.TestCase):
def setUp(self):
self.i18n = JsonI18nStub()
self.settings = SimpleNamespace(
SUBSCRIPTION_MINI_APP_URL="https://app.example.com/",
SUPPORT_LINK="https://t.me/support",
PRIVACY_POLICY_URL="https://example.com/privacy",
USER_AGREEMENT_URL="https://example.com/agreement",
TRIAL_ENABLED=True,
SERVER_STATUS_URL="",
SUBSCRIPTION_GUIDES_ENABLED=True,
SUBSCRIPTION_GUIDES_BOT_MENU_ENABLED=False,
SUBSCRIPTION_PAGE_CONFIG_PANEL_ENABLED=True,
SUBSCRIPTION_PAGE_CONFIG_JSON_OVERRIDE_ENABLED=False,
SUBSCRIPTION_PAGE_CONFIG_JSON="",
PANEL_API_URL="https://panel.example.com",
PANEL_API_KEY="token",
)
def _callback_data(self, markup):
return [
button.callback_data
for row in markup.inline_keyboard
for button in row
if button.callback_data
]
def test_main_menu_exposes_bot_menu_and_information(self):
markup = get_main_menu_inline_keyboard("en", self.i18n, self.settings)
callbacks = self._callback_data(markup)
self.assertIn("main_action:bot_interface", callbacks)
self.assertIn("main_action:info", callbacks)
def test_main_menu_shows_trial_button_as_mini_app_deeplink_when_available(self):
markup = get_main_menu_inline_keyboard(
"en",
self.i18n,
self.settings,
show_trial_button=True,
)
trial_button = markup.inline_keyboard[0][0]
self.assertEqual(trial_button.text, self.i18n.gettext("en", "menu_activate_trial_button"))
self.assertIsNone(trial_button.callback_data)
self.assertEqual(trial_button.web_app.url, "https://app.example.com/trial")
def test_main_menu_trial_button_falls_back_to_bot_callback_without_mini_app(self):
self.settings.SUBSCRIPTION_MINI_APP_URL = ""
markup = get_main_menu_inline_keyboard(
"en",
self.i18n,
self.settings,
show_trial_button=True,
)
trial_button = markup.inline_keyboard[0][0]
self.assertEqual(trial_button.callback_data, "main_action:request_trial")
self.assertIsNone(trial_button.web_app)
def test_bot_interface_trial_button_uses_mini_app_deeplink_when_available(self):
markup = get_bot_interface_inline_keyboard(
"en",
self.i18n,
self.settings,
show_trial_button=True,
)
trial_button = markup.inline_keyboard[0][0]
self.assertEqual(trial_button.web_app.url, "https://app.example.com/trial")
def test_bot_interface_buttons_return_to_bot_interface(self):
markup = get_bot_interface_inline_keyboard("en", self.i18n, self.settings)
callbacks = self._callback_data(markup)
self.assertIn("main_action:bot_subscribe", callbacks)
self.assertIn("main_action:bot_my_subscription", callbacks)
self.assertIn("main_action:bot_referral", callbacks)
self.assertIn("main_action:bot_info", callbacks)
self.assertIn("main_action:back_to_main", callbacks)
def test_connect_keyboard_uses_subscription_url_when_bot_guides_disabled(self):
markup = get_connect_and_main_keyboard(
"en",
self.i18n,
self.settings,
"https://sb.example.com/user",
connect_button_url=None,
)
self.assertEqual(markup.inline_keyboard[0][0].url, "https://sb.example.com/user")
self.assertIsNone(markup.inline_keyboard[0][0].web_app)
def test_connect_keyboard_opens_install_guide_when_bot_guides_enabled(self):
self.settings.SUBSCRIPTION_GUIDES_BOT_MENU_ENABLED = True
markup = get_connect_and_main_keyboard(
"en",
self.i18n,
self.settings,
"https://sb.example.com/user",
install_share_url="https://app.example.com/s/8f559061460e8fede78ef18dce887236",
)
self.assertIsNone(markup.inline_keyboard[0][0].url)
self.assertEqual(
markup.inline_keyboard[0][0].web_app.url,
"https://app.example.com/install",
)
self.assertEqual(
markup.inline_keyboard[1][0].url,
"https://app.example.com/s/8f559061460e8fede78ef18dce887236",
)
def test_nested_bot_menu_keyboards_can_target_bot_interface_back(self):
subscription_markup = get_subscription_options_keyboard(
{1: 100},
"RUB",
"en",
self.i18n,
back_callback="main_action:bot_interface",
)
referral_markup = get_referral_link_keyboard(
"en",
self.i18n,
back_callback="main_action:bot_interface",
)
info_markup = get_information_links_keyboard(
"en",
self.i18n,
"https://example.com/privacy",
"https://example.com/agreement",
back_callback="main_action:bot_interface",
)
language_markup = get_language_selection_keyboard(
self.i18n,
"en",
back_callback="main_action:bot_interface",
)
self.assertIn("main_action:bot_interface", self._callback_data(subscription_markup))
self.assertIn("main_action:bot_interface", self._callback_data(referral_markup))
self.assertIn("main_action:bot_interface", self._callback_data(info_markup))
self.assertIn("set_lang_ru:bot", self._callback_data(language_markup))
self.assertIn("subscribe_period:1:bot", self._callback_data(subscription_markup))
def test_subscription_purchase_description_is_prepended_before_period_selection(self):
settings = SimpleNamespace(
subscription_purchase_description=lambda language: f"Description {language}"
)
self.assertEqual(
_with_subscription_purchase_description(
"Choose period",
settings,
"en",
include=True,
),
"Description en\n\nChoose period",
)
self.assertEqual(
_with_subscription_purchase_description(
"Choose traffic",
settings,
"en",
include=False,
),
"Choose traffic",
)
def test_premium_usage_limit_uses_byte_fields_when_display_fields_missing(self):
gib = 1024**3
active = {
"premium_used": None,
"premium_limit": None,
"premium_used_bytes": int(1.25 * gib),
"premium_limit_bytes": 26 * gib,
}
text = _format_premium_usage_limit(active)
self.assertEqual(text, "1.25 GB из 26.00 GB")
self.assertNotIn("None", text)
def test_payment_navigation_context_keeps_bot_menu_source(self):
settings = SimpleNamespace(
payment_methods_order=[],
PLATEGA_ENABLED=False,
PLATEGA_SBP_ENABLED=False,
PLATEGA_CRYPTO_ENABLED=False,
)
markup = get_payment_method_keyboard(
1,
100,
None,
"RUB",
"en",
self.i18n,
settings,
sale_mode="subscription|bot",
)
self.assertIn("main_action:bot_subscribe", self._callback_data(markup))
self.assertEqual(
payment_methods_back_callback("1", "subscription|bot"),
"subscribe_period:1:bot",
)
def test_payment_navigation_context_ignores_hwid_renewal_token(self):
self.assertEqual(
payment_options_back_callback("subscription@basic|bot|hwid_renewal"),
"tariff:select:basic:bot",
)
self.assertEqual(
payment_methods_back_callback("1", "subscription@basic|bot|hwid_renewal"),
"tariff:period:basic:1:bot",
)
def test_payment_method_keyboard_adds_hwid_renewal_toggle(self):
settings = SimpleNamespace(payment_methods_order=[])
quote = {"device_count": 2, "price": 50}
selected = get_payment_method_keyboard(
1,
100,
None,
"RUB",
"en",
self.i18n,
settings,
sale_mode="subscription@basic|bot",
hwid_renewal_quote=quote,
hwid_renewal_selected=True,
)
disabled = get_payment_method_keyboard(
1,
100,
None,
"RUB",
"en",
self.i18n,
settings,
sale_mode="subscription@basic|bot",
hwid_renewal_quote=quote,
hwid_renewal_selected=False,
)
self.assertIn("tariff:period:basic:1:bot:no_hwid", self._callback_data(selected))
self.assertIn("tariff:period:basic:1:bot:hwid", self._callback_data(disabled))
def test_yookassa_saved_card_choice_keeps_sale_mode_after_page_token(self):
markup = get_yk_autopay_choice_keyboard(
1,
100,
"en",
self.i18n,
has_saved_cards=True,
sale_mode="subscription@basic|hwid_renewal",
)
self.assertIn(
"pay_yk_saved_list:1:100:0:subscription@basic|hwid_renewal",
self._callback_data(markup),
)
def test_tariff_back_buttons_return_to_previous_level(self):
tariff = SimpleNamespace(
key="basic",
billing_model="period",
enabled_periods=[1],
name=lambda _lang: "Basic",
description=lambda _lang: "Basic plan",
min_period_price_rub=lambda: 100,
period_price=lambda months, currency: (
100 if months == 1 and currency == "rub" else None
),
)
settings = SimpleNamespace(DEFAULT_CURRENCY_SYMBOL="RUB")
catalog = get_tariff_catalog_keyboard(
[tariff],
"en",
self.i18n,
back_callback="main_action:bot_interface",
)
periods = get_tariff_periods_keyboard(
tariff,
"en",
self.i18n,
settings,
back_callback="main_action:bot_subscribe",
callback_context="bot",
)
self.assertIn("tariff:select:basic:bot", self._callback_data(catalog))
self.assertIn("main_action:bot_interface", self._callback_data(catalog))
self.assertIn("tariff:period:basic:1:bot", self._callback_data(periods))
self.assertIn("main_action:bot_subscribe", self._callback_data(periods))
self.assertEqual(
payment_options_back_callback("subscription@basic|bot"),
"tariff:select:basic:bot",
)
self.assertEqual(
payment_methods_back_callback("1", "subscription@basic|bot"),
"tariff:period:basic:1:bot",
)
def test_webapp_referral_link_uses_ref_query_and_is_normalized(self):
link = referral._build_webapp_referral_link(
"https://app.example.com/invite?utm=channel",
"AbC123xYz",
)
self.assertEqual(
link,
"https://app.example.com/invite?utm=channel&ref=uAbC123xYz",
)
self.assertEqual(subscription_webapp._normalize_referral_param("uAbC123xYz"), "ABC123XYZ")
self.assertEqual(
subscription_webapp._normalize_referral_param("ref_uAbC123xYz"), "ABC123XYZ"
)
def test_referral_text_places_web_link_after_telegram_link(self):
text = self.i18n.gettext(
"en",
"referral_program_info_new",
referral_link="https://t.me/bot?start=ref_uABC123XYZ",
webapp_link_section=self.i18n.gettext(
"en",
"referral_webapp_link_line",
webapp_referral_link="https://app.example.com/?ref=uABC123XYZ",
),
bonus_details="bonus",
invited_count=1,
purchased_count=0,
)
self.assertLess(text.index("Telegram link:"), text.index("Web link:"))
self.assertLess(text.index("Web link:"), text.index("Invitation bonuses:"))
def test_single_tariff_referral_text_uses_period_rows_without_tariff_name(self):
settings = SimpleNamespace(
tariffs_config=TariffsConfig.model_validate(
{
"default_tariff": "standard",
"tariffs": [
{
"key": "standard",
"names": {"en": "Standard"},
"descriptions": {},
"squad_uuids": ["standard"],
"billing_model": "period",
"monthly_gb": 100,
"prices_rub": {"2": 400, "4": 800},
"prices_stars": {},
"referral_bonus_days_inviter": {"2": 5, "4": 10},
"referral_bonus_days_referee": {"2": 1, "4": 2},
"enabled_periods": [2, 4],
"enabled": True,
}
],
}
),
subscription_options={},
referral_bonus_inviter={},
referral_bonus_referee={},
)
text = referral._build_referral_bonus_details_text(
settings,
lambda key, **kwargs: self.i18n.gettext("en", key, **kwargs),
"en",
)
self.assertIn("For a friend's 2-month subscription", text)
self.assertIn("For a friend's 4-month subscription", text)
self.assertNotIn("Standard", text)
def test_multiple_tariff_referral_text_uses_tariff_ranges(self):
settings = SimpleNamespace(
tariffs_config=TariffsConfig.model_validate(
{
"default_tariff": "standard",
"tariffs": [
{
"key": "standard",
"names": {"en": "Standard"},
"descriptions": {},
"squad_uuids": ["standard"],
"billing_model": "period",
"monthly_gb": 100,
"prices_rub": {"2": 400, "4": 800},
"prices_stars": {},
"referral_bonus_days_inviter": {"2": 5, "4": 10},
"referral_bonus_days_referee": {"2": 1, "4": 2},
"enabled_periods": [2, 4],
"enabled": True,
},
{
"key": "premium",
"names": {"en": "Premium"},
"descriptions": {},
"squad_uuids": ["premium"],
"billing_model": "period",
"monthly_gb": 500,
"prices_rub": {"1": 700, "3": 1800},
"prices_stars": {},
"referral_bonus_days_inviter": {"1": 8, "3": 24},
"referral_bonus_days_referee": {"1": 3, "3": 9},
"enabled_periods": [1, 3],
"enabled": True,
},
],
}
),
subscription_options={},
referral_bonus_inviter={},
referral_bonus_referee={},
)
text = referral._build_referral_bonus_details_text(
settings,
lambda key, **kwargs: self.i18n.gettext("en", key, **kwargs),
"en",
)
self.assertIn("Standard", text)
self.assertIn("Premium", text)
self.assertIn("from 5 to 10 days", text)
self.assertIn("from 8 to 24 days", text)
self.assertNotIn("2-month subscription", text)