"""Тесты для MenuLayoutService.""" import pytest from unittest.mock import AsyncMock, MagicMock, patch from aiogram.types import InlineKeyboardButton from app.services.menu_layout.service import MenuLayoutService from app.services.menu_layout.context import MenuContext @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"