import uuid import pytest from unittest.mock import AsyncMock, MagicMock from aiogram.types import Message, InlineKeyboardMarkup from aiogram.filters import CommandObject from datetime import datetime from src.bot.handlers import get_router from src.processor.dto import EnrichedNewsItemDTO @pytest.fixture def mock_storage(): return AsyncMock() @pytest.fixture def mock_processor(): processor = MagicMock() processor.get_info.return_value = {"model": "test-model"} return processor @pytest.fixture def allowed_chat_id(): return "123456789" @pytest.fixture def router(mock_storage, mock_processor, allowed_chat_id): return get_router(mock_storage, mock_processor, allowed_chat_id) def get_handler(router, callback_name): for handler in router.message.handlers: if handler.callback.__name__ == callback_name: return handler.callback raise ValueError(f"Handler {callback_name} not found") @pytest.mark.asyncio async def test_command_hottest_handler_success(router, mock_storage, allowed_chat_id): """ Test that /hottest command calls get_top_ranked and returns a list of items. """ # 1. Arrange handler = get_handler(router, "command_hottest_handler") message = AsyncMock() message.chat = MagicMock() message.chat.id = int(allowed_chat_id) message.answer = AsyncMock() mock_items = [ EnrichedNewsItemDTO( title=f"Hot News {i}", url=f"https://example.com/{i}", content_text=f"Content {i}", source="Source", timestamp=datetime.now(), relevance_score=10-i, summary_ru=f"Сводка {i}", anomalies_detected=[], category="Tech" ) for i in range(3) ] mock_storage.get_top_ranked.return_value = mock_items # 2. Act command = CommandObject(prefix='/', command='hottest', args=None) await handler(message=message, command=command) # 3. Assert mock_storage.get_top_ranked.assert_called_once_with(limit=10, category=None) message.answer.assert_called_once() args, kwargs = message.answer.call_args assert "Top 3 Hottest Trends:" in args[0] assert "reply_markup" in kwargs assert isinstance(kwargs["reply_markup"], InlineKeyboardMarkup) # Check if all 3 items are in the markup markup = kwargs["reply_markup"] assert len(markup.inline_keyboard) == 3 # Check if icons and scores are present button_text = markup.inline_keyboard[0][0].text assert "🔥" in button_text assert "[10/10]" in button_text assert "Hot News 0" in button_text @pytest.mark.asyncio async def test_command_hottest_handler_empty(router, mock_storage, allowed_chat_id): """ Test that /hottest command handles empty results correctly. """ # 1. Arrange handler = get_handler(router, "command_hottest_handler") message = AsyncMock() message.chat = MagicMock() message.chat.id = int(allowed_chat_id) message.answer = AsyncMock() mock_storage.get_top_ranked.return_value = [] # 2. Act command = CommandObject(prefix='/', command='hottest', args=None) await handler(message=message, command=command) # 3. Assert mock_storage.get_top_ranked.assert_called_once_with(limit=10, category=None) message.answer.assert_called_once_with("No hot trends found yet.") @pytest.mark.asyncio async def test_command_hottest_handler_custom_limit(router, mock_storage, allowed_chat_id): """ Test that /hottest command with custom limit correctly passes it to storage. """ # 1. Arrange handler = get_handler(router, "command_hottest_handler") message = AsyncMock() message.chat = MagicMock() message.chat.id = int(allowed_chat_id) message.answer = AsyncMock() mock_storage.get_top_ranked.return_value = [] # 2. Act command = CommandObject(prefix='/', command='hottest', args='25') await handler(message=message, command=command) # 3. Assert mock_storage.get_top_ranked.assert_called_once_with(limit=25, category=None) @pytest.mark.asyncio async def test_command_hottest_handler_max_limit(router, mock_storage, allowed_chat_id): """ Test that /hottest command enforces maximum limit. """ # 1. Arrange handler = get_handler(router, "command_hottest_handler") message = AsyncMock() message.chat = MagicMock() message.chat.id = int(allowed_chat_id) message.answer = AsyncMock() mock_storage.get_top_ranked.return_value = [] # 2. Act command = CommandObject(prefix='/', command='hottest', args='1000') await handler(message=message, command=command) # 3. Assert mock_storage.get_top_ranked.assert_called_once_with(limit=50, category=None) @pytest.mark.asyncio async def test_command_hottest_handler_invalid_limit(router, mock_storage, allowed_chat_id): """ Test that /hottest command handles invalid limit by falling back to default. """ # 1. Arrange handler = get_handler(router, "command_hottest_handler") message = AsyncMock() message.chat = MagicMock() message.chat.id = int(allowed_chat_id) message.answer = AsyncMock() mock_storage.get_top_ranked.return_value = [] # 2. Act command = CommandObject(prefix='/', command='hottest', args='invalid') await handler(message=message, command=command) # 3. Assert mock_storage.get_top_ranked.assert_called_once_with(limit=10, category='invalid')