"""Тесты для MenuLayoutService.""" from unittest.mock import MagicMock, patch import pytest from aiogram.types import InlineKeyboardButton from app.services.menu_layout.context import MenuContext from app.services.menu_layout.service import MenuLayoutService @pytest.mark.anyio async def test_build_button_connect_direct_mode_with_url(): """Тест: кнопка connect с open_mode=direct и валидным URL должна создавать WebAppInfo.""" button_config = { 'type': 'builtin', 'builtin_id': 'connect', 'text': {'ru': '🔗 Подключиться'}, 'action': 'subscription_connect', 'open_mode': 'direct', 'webapp_url': 'https://example.com/miniapp', } context = MenuContext( language='ru', has_active_subscription=True, subscription_is_active=True, ) texts = MagicMock() texts.t = lambda key, default: default button = MenuLayoutService._build_button(button_config, context, texts) assert button is not None assert isinstance(button, InlineKeyboardButton) assert button.web_app is not None assert button.web_app.url == 'https://example.com/miniapp' assert button.callback_data is None @pytest.mark.anyio async def test_build_button_connect_direct_mode_with_subscription_url(): """Тест: кнопка connect с open_mode=direct должна получать URL из подписки.""" button_config = { 'type': 'builtin', 'builtin_id': 'connect', 'text': {'ru': '🔗 Подключиться'}, 'action': 'subscription_connect', 'open_mode': 'direct', 'webapp_url': None, } # Мокаем подписку с URL mock_subscription = MagicMock() mock_subscription.subscription_url = 'https://subscription.example.com/link' mock_subscription.subscription_crypto_link = None context = MenuContext( language='ru', has_active_subscription=True, subscription_is_active=True, subscription=mock_subscription, ) texts = MagicMock() texts.t = lambda key, default: default with patch('app.utils.subscription_utils.get_display_subscription_link') as mock_get_link: mock_get_link.return_value = 'https://subscription.example.com/link' button = MenuLayoutService._build_button(button_config, context, texts) assert button is not None assert isinstance(button, InlineKeyboardButton) assert button.web_app is not None assert button.web_app.url == 'https://subscription.example.com/link' @pytest.mark.anyio async def test_build_button_connect_callback_mode(): """Тест: кнопка connect с open_mode=callback должна создавать callback кнопку.""" button_config = { 'type': 'builtin', 'builtin_id': 'connect', 'text': {'ru': '🔗 Подключиться'}, 'action': 'subscription_connect', 'open_mode': 'callback', 'webapp_url': None, } context = MenuContext( language='ru', has_active_subscription=True, subscription_is_active=True, ) texts = MagicMock() texts.t = lambda key, default: default button = MenuLayoutService._build_button(button_config, context, texts) assert button is not None assert isinstance(button, InlineKeyboardButton) assert button.callback_data == 'subscription_connect' assert button.web_app is None @pytest.mark.anyio async def test_build_button_connect_direct_mode_fallback_to_callback(): """Тест: кнопка connect с open_mode=direct без URL должна fallback на callback.""" button_config = { 'type': 'builtin', 'builtin_id': 'connect', 'text': {'ru': '🔗 Подключиться'}, 'action': 'subscription_connect', 'open_mode': 'direct', 'webapp_url': None, } context = MenuContext( language='ru', has_active_subscription=True, subscription_is_active=True, subscription=None, # Нет подписки ) texts = MagicMock() texts.t = lambda key, default: default with patch('app.services.menu_layout.service.settings') as mock_settings: mock_settings.MINIAPP_CUSTOM_URL = None button = MenuLayoutService._build_button(button_config, context, texts) assert button is not None assert isinstance(button, InlineKeyboardButton) # Должен fallback на callback_data, так как URL не найден assert button.callback_data == 'subscription_connect'