From a0eeba09188eaf31c5e82f0ca675536f269fa1ca Mon Sep 17 00:00:00 2001 From: Artur Mukhamadiev Date: Sun, 15 Mar 2026 01:34:33 +0300 Subject: [PATCH] Enhance /hottest command with optional limit --- src/bot/handlers.py | 19 ++++++-- tests/bot/test_handlers.py | 8 ++-- tests/bot/test_hottest_command.py | 72 +++++++++++++++++++++++++++++-- 3 files changed, 89 insertions(+), 10 deletions(-) diff --git a/src/bot/handlers.py b/src/bot/handlers.py index d8cf135..f7bc9f9 100644 --- a/src/bot/handlers.py +++ b/src/bot/handlers.py @@ -51,7 +51,7 @@ def get_router(storage: IVectorStore, processor: ILLMProvider, allowed_chat_id: "/start - Start the bot\n" "/help - Show this help message\n" "/latest [category] - Show the latest enriched news trends\n" - "/hottest - Show top 10 ranked hot trends\n" + "/hottest [limit] - Show top ranked hot trends (default 10, max 50)\n" "/search query - Search for news\n" "/stats - Show database statistics\n" "/params - Show LLM processor parameters\n" @@ -95,11 +95,22 @@ def get_router(storage: IVectorStore, processor: ILLMProvider, allowed_chat_id: await message.answer("Latest news:", reply_markup=builder.as_markup()) @router.message(Command("hottest")) - async def command_hottest_handler(message: Message) -> None: + async def command_hottest_handler(message: Message, command: CommandObject) -> None: """ This handler receives messages with `/hottest` command """ - items = await storage.get_top_ranked(limit=10) + limit = 10 + if command.args: + try: + limit = int(command.args) + if limit <= 0: + limit = 10 + elif limit > 50: + limit = 50 + except ValueError: + pass + + items = await storage.get_top_ranked(limit=limit) if not items: await message.answer("No hot trends found yet.") @@ -113,7 +124,7 @@ def get_router(storage: IVectorStore, processor: ILLMProvider, allowed_chat_id: callback_data=f"detail:{item_id}" )) - await message.answer("Top 10 Hottest Trends:", reply_markup=builder.as_markup()) + await message.answer(f"Top {len(items)} Hottest Trends:", reply_markup=builder.as_markup()) @router.message(Command("search")) async def command_search_handler(message: Message, command: CommandObject) -> None: diff --git a/tests/bot/test_handlers.py b/tests/bot/test_handlers.py index 9f88ecd..8a7357c 100644 --- a/tests/bot/test_handlers.py +++ b/tests/bot/test_handlers.py @@ -176,15 +176,16 @@ async def test_command_hottest_handler(router, mock_storage, allowed_chat_id, mo message = AsyncMock() message.chat.id = int(allowed_chat_id) message.answer = AsyncMock() + command = CommandObject(prefix="/", command="hottest", args=None) mock_storage.get_top_ranked.return_value = [mock_item] - await handler(message=message) + await handler(message=message, command=command) 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 "Top 1 Hottest Trends:" in args[0] assert "reply_markup" in kwargs assert "🔥" in str(kwargs["reply_markup"]) @@ -194,10 +195,11 @@ async def test_command_hottest_handler_empty(router, mock_storage, allowed_chat_ message = AsyncMock() message.chat.id = int(allowed_chat_id) message.answer = AsyncMock() + command = CommandObject(prefix="/", command="hottest", args=None) mock_storage.get_top_ranked.return_value = [] - await handler(message=message) + await handler(message=message, command=command) message.answer.assert_called_once_with("No hot trends found yet.") diff --git a/tests/bot/test_hottest_command.py b/tests/bot/test_hottest_command.py index ca8cbff..bf80b9e 100644 --- a/tests/bot/test_hottest_command.py +++ b/tests/bot/test_hottest_command.py @@ -2,6 +2,7 @@ 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 @@ -59,14 +60,15 @@ async def test_command_hottest_handler_success(router, mock_storage, allowed_cha mock_storage.get_top_ranked.return_value = mock_items # 2. Act - await handler(message=message) + 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) message.answer.assert_called_once() args, kwargs = message.answer.call_args - assert "Top 10 Hottest Trends:" in args[0] + assert "Top 3 Hottest Trends:" in args[0] assert "reply_markup" in kwargs assert isinstance(kwargs["reply_markup"], InlineKeyboardMarkup) @@ -95,8 +97,72 @@ async def test_command_hottest_handler_empty(router, mock_storage, allowed_chat_ mock_storage.get_top_ranked.return_value = [] # 2. Act - await handler(message=message) + 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) 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) + +@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) + +@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)