import pytest from unittest.mock import AsyncMock, MagicMock, patch from aiogram.types import Message, BufferedInputFile 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.fixture def mock_items(): return [ 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) ] @pytest.mark.asyncio async def test_command_get_hottest_handler_no_args(router, mock_storage, allowed_chat_id, mock_items): """ Test /get_hottest with no arguments (default limit 10, format csv). """ # 1. Arrange handler = get_handler(router, "command_get_hottest_handler") message = AsyncMock() message.chat = MagicMock() message.chat.id = int(allowed_chat_id) mock_storage.get_top_ranked.return_value = mock_items # 2. Act command = CommandObject(prefix='/', command='get_hottest', args=None) with patch("src.bot.handlers.CsvTrendExporter") as MockCsvExporter: mock_exporter = AsyncMock() mock_exporter.export.return_value = b"csv data" MockCsvExporter.return_value = mock_exporter await handler(message=message, command=command) # 3. Assert mock_storage.get_top_ranked.assert_called_once_with(limit=10) message.answer_document.assert_called_once() args, kwargs = message.answer_document.call_args assert "document" in kwargs assert isinstance(kwargs["document"], BufferedInputFile) assert kwargs["document"].filename == "hottest_trends.csv" assert kwargs["caption"] == "🔥 Top 3 hottest trends!" @pytest.mark.asyncio async def test_command_get_hottest_handler_invalid_limit(router, mock_storage, allowed_chat_id): """ Test /get_hottest with invalid limit (not a number). """ # 1. Arrange handler = get_handler(router, "command_get_hottest_handler") message = AsyncMock() message.chat = MagicMock() message.chat.id = int(allowed_chat_id) # 2. Act command = CommandObject(prefix='/', command='get_hottest', args='abc') await handler(message=message, command=command) # 3. Assert message.answer.assert_called_once_with("Please provide a valid number, e.g., /get_hottest 10") mock_storage.get_top_ranked.assert_not_called() @pytest.mark.asyncio async def test_command_get_hottest_handler_capped_limit(router, mock_storage, allowed_chat_id, mock_items): """ Test /get_hottest with limit > 50 (should be capped). """ # 1. Arrange handler = get_handler(router, "command_get_hottest_handler") message = AsyncMock() message.chat = MagicMock() message.chat.id = int(allowed_chat_id) mock_storage.get_top_ranked.return_value = mock_items # 2. Act command = CommandObject(prefix='/', command='get_hottest', args='100') await handler(message=message, command=command) # 3. Assert mock_storage.get_top_ranked.assert_called_once_with(limit=50) @pytest.mark.asyncio async def test_command_get_hottest_handler_custom_limit_md(router, mock_storage, allowed_chat_id, mock_items): """ Test /get_hottest with limit and md format. """ # 1. Arrange handler = get_handler(router, "command_get_hottest_handler") message = AsyncMock() message.chat = MagicMock() message.chat.id = int(allowed_chat_id) mock_storage.get_top_ranked.return_value = mock_items # 2. Act command = CommandObject(prefix='/', command='get_hottest', args='5 md') with patch("src.bot.handlers.MarkdownTrendExporter") as MockMdExporter: mock_exporter = AsyncMock() mock_exporter.export.return_value = b"md data" MockMdExporter.return_value = mock_exporter await handler(message=message, command=command) # 3. Assert mock_storage.get_top_ranked.assert_called_once_with(limit=5) message.answer_document.assert_called_once() args, kwargs = message.answer_document.call_args assert kwargs["document"].filename == "hottest_trends.md" assert kwargs["caption"] == "🔥 Top 3 hottest trends!" @pytest.mark.asyncio async def test_command_get_hottest_handler_no_records(router, mock_storage, allowed_chat_id): """ Test /get_hottest when no records found. """ # 1. Arrange handler = get_handler(router, "command_get_hottest_handler") message = AsyncMock() message.chat = MagicMock() message.chat.id = int(allowed_chat_id) mock_storage.get_top_ranked.return_value = [] # 2. Act command = CommandObject(prefix='/', command='get_hottest', args=None) await handler(message=message, command=command) # 3. Assert message.answer.assert_called_once_with("No hot trends found yet.") message.answer_document.assert_not_called()