import uuid import pytest from unittest.mock import AsyncMock, MagicMock from aiogram.types import Message, InlineKeyboardMarkup, CallbackQuery from aiogram.filters import CommandObject from datetime import datetime from src.bot.handlers import get_router, AccessMiddleware from src.processor.dto import EnrichedNewsItemDTO @pytest.fixture def mock_item(): return EnrichedNewsItemDTO( title="Breakthrough in Quantum Computing", url="https://example.com/quantum-breakthrough", content_text="Researchers have achieved a new milestone in quantum supremacy...", source="Example Domain News", timestamp=datetime.now(), relevance_score=9, summary_ru="Исследователи достигли нового рубежа в квантовом превосходстве.", anomalies_detected=["Quantum supremacy", "Room temperature superconductors"], category="Science" ) @pytest.fixture def mock_storage(mock_item): storage = AsyncMock() storage.search.return_value = [mock_item] storage.get_by_id.return_value = mock_item storage.get_stats.return_value = {"total": 1, "AI": 1} return storage @pytest.fixture def allowed_chat_id(): return "123456789" @pytest.fixture def mock_processor(): processor = MagicMock() processor.get_info.return_value = { "model": "test-model", "base_url": "http://test-url", "prompt_summary": "Test summary" } return processor @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") def get_callback_handler(router, callback_name): for handler in router.callback_query.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_start_handler(router, allowed_chat_id): handler = get_handler(router, "command_start_handler") message = AsyncMock() message.chat.id = int(allowed_chat_id) message.from_user = MagicMock() message.from_user.full_name = "Test User" message.answer = AsyncMock() await handler(message) message.answer.assert_called_once() args, kwargs = message.answer.call_args assert "Welcome" in args[0] or "Trend-Scout" in args[0] @pytest.mark.asyncio async def test_command_help_handler(router, allowed_chat_id): handler = get_handler(router, "command_help_handler") message = AsyncMock() message.chat.id = int(allowed_chat_id) message.answer = AsyncMock() await handler(message) message.answer.assert_called_once() args, kwargs = message.answer.call_args assert "/start" in args[0] assert "/latest" in args[0] @pytest.mark.asyncio async def test_command_params_handler(router, mock_processor, allowed_chat_id): handler = get_handler(router, "command_params_handler") message = AsyncMock() message.chat.id = int(allowed_chat_id) message.answer = AsyncMock() await handler(message) mock_processor.get_info.assert_called_once() message.answer.assert_called_once() args, kwargs = message.answer.call_args assert "LLM Processor Parameters" in args[0] assert "test-model" in args[0] assert "HTML" in kwargs.get("parse_mode", "") @pytest.mark.asyncio async def test_command_latest_handler(router, mock_storage, allowed_chat_id): handler = get_handler(router, "command_latest_handler") message = AsyncMock() message.chat.id = int(allowed_chat_id) message.answer = AsyncMock() command = CommandObject(prefix="/", command="latest", args=None) await handler(message=message, command=command) message.answer.assert_called_once() args, kwargs = message.answer.call_args assert "Latest news:" in args[0] assert "reply_markup" in kwargs assert isinstance(kwargs["reply_markup"], InlineKeyboardMarkup) assert len(kwargs["reply_markup"].inline_keyboard) == 1 @pytest.mark.asyncio async def test_command_search_handler(router, mock_storage, allowed_chat_id): handler = get_handler(router, "command_search_handler") message = AsyncMock() message.chat.id = int(allowed_chat_id) message.answer = AsyncMock() command = CommandObject(prefix="/", command="search", args="quantum") await handler(message=message, command=command) message.answer.assert_called_once() args, kwargs = message.answer.call_args assert "Search results:" in args[0] assert "reply_markup" in kwargs mock_storage.search.assert_called_once_with(query="quantum", limit=10) @pytest.mark.asyncio async def test_detail_callback_handler(router, mock_storage, mock_item): handler = get_callback_handler(router, "detail_callback_handler") callback = AsyncMock(spec=CallbackQuery) item_id = str(uuid.uuid5(uuid.NAMESPACE_URL, mock_item.url)) callback.data = f"detail:{item_id}" callback.message = AsyncMock() callback.message.answer = AsyncMock() callback.answer = AsyncMock() await handler(callback) mock_storage.get_by_id.assert_called_once_with(item_id) callback.message.answer.assert_called_once() args, kwargs = callback.message.answer.call_args assert mock_item.title in args[0] assert mock_item.summary_ru in args[0] callback.answer.assert_called_once() @pytest.mark.asyncio async def test_command_stats_handler(router, mock_storage, allowed_chat_id): handler = get_handler(router, "command_stats_handler") message = AsyncMock() message.chat.id = int(allowed_chat_id) message.answer = AsyncMock() await handler(message=message) message.answer.assert_called_once() args, kwargs = message.answer.call_args assert "Database Statistics" in args[0] @pytest.mark.asyncio async def test_command_hottest_handler(router, mock_storage, allowed_chat_id, mock_item): handler = get_handler(router, "command_hottest_handler") message = AsyncMock() message.chat.id = int(allowed_chat_id) message.answer = AsyncMock() mock_storage.get_top_ranked.return_value = [mock_item] await handler(message=message) mock_storage.get_top_ranked.assert_called_once_with(limit=10) message.answer.assert_called_once() args, kwargs = message.answer.call_args assert "Top 10 Hottest Trends:" in args[0] assert "reply_markup" in kwargs assert "🔥" in str(kwargs["reply_markup"]) @pytest.mark.asyncio async def test_command_hottest_handler_empty(router, mock_storage, allowed_chat_id): handler = get_handler(router, "command_hottest_handler") message = AsyncMock() message.chat.id = int(allowed_chat_id) message.answer = AsyncMock() mock_storage.get_top_ranked.return_value = [] await handler(message=message) message.answer.assert_called_once_with("No hot trends found yet.") @pytest.mark.asyncio async def test_access_middleware_allowed(allowed_chat_id): middleware = AccessMiddleware(allowed_chat_id) handler = AsyncMock() event = MagicMock(spec=Message) event.chat = MagicMock() event.chat.id = int(allowed_chat_id) event.answer = AsyncMock() data = {} await middleware(handler, event, data) handler.assert_called_once_with(event, data) event.answer.assert_not_called() @pytest.mark.asyncio async def test_access_middleware_denied(allowed_chat_id): middleware = AccessMiddleware(allowed_chat_id) handler = AsyncMock() event = MagicMock(spec=Message) event.chat = MagicMock() event.chat.id = 999999999 # Different ID event.answer = AsyncMock() data = {} await middleware(handler, event, data) handler.assert_not_called() event.answer.assert_called_once_with("Access Denied")