Enhance /hottest command with optional limit

This commit is contained in:
Artur Mukhamadiev 2026-03-15 01:34:33 +03:00
parent 9fdb4b35cd
commit a0eeba0918
3 changed files with 89 additions and 10 deletions

View File

@ -51,7 +51,7 @@ def get_router(storage: IVectorStore, processor: ILLMProvider, allowed_chat_id:
"/start - Start the bot\n" "/start - Start the bot\n"
"/help - Show this help message\n" "/help - Show this help message\n"
"/latest [category] - Show the latest enriched news trends\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" "/search query - Search for news\n"
"/stats - Show database statistics\n" "/stats - Show database statistics\n"
"/params - Show LLM processor parameters\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()) await message.answer("Latest news:", reply_markup=builder.as_markup())
@router.message(Command("hottest")) @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 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: if not items:
await message.answer("No hot trends found yet.") 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}" 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")) @router.message(Command("search"))
async def command_search_handler(message: Message, command: CommandObject) -> None: async def command_search_handler(message: Message, command: CommandObject) -> None:

View File

@ -176,15 +176,16 @@ async def test_command_hottest_handler(router, mock_storage, allowed_chat_id, mo
message = AsyncMock() message = AsyncMock()
message.chat.id = int(allowed_chat_id) message.chat.id = int(allowed_chat_id)
message.answer = AsyncMock() message.answer = AsyncMock()
command = CommandObject(prefix="/", command="hottest", args=None)
mock_storage.get_top_ranked.return_value = [mock_item] 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) mock_storage.get_top_ranked.assert_called_once_with(limit=10)
message.answer.assert_called_once() message.answer.assert_called_once()
args, kwargs = message.answer.call_args 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 "reply_markup" in kwargs
assert "🔥" in str(kwargs["reply_markup"]) 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 = AsyncMock()
message.chat.id = int(allowed_chat_id) message.chat.id = int(allowed_chat_id)
message.answer = AsyncMock() message.answer = AsyncMock()
command = CommandObject(prefix="/", command="hottest", args=None)
mock_storage.get_top_ranked.return_value = [] 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.") message.answer.assert_called_once_with("No hot trends found yet.")

View File

@ -2,6 +2,7 @@ import uuid
import pytest import pytest
from unittest.mock import AsyncMock, MagicMock from unittest.mock import AsyncMock, MagicMock
from aiogram.types import Message, InlineKeyboardMarkup from aiogram.types import Message, InlineKeyboardMarkup
from aiogram.filters import CommandObject
from datetime import datetime from datetime import datetime
from src.bot.handlers import get_router 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 mock_storage.get_top_ranked.return_value = mock_items
# 2. Act # 2. Act
await handler(message=message) command = CommandObject(prefix='/', command='hottest', args=None)
await handler(message=message, command=command)
# 3. Assert # 3. Assert
mock_storage.get_top_ranked.assert_called_once_with(limit=10) mock_storage.get_top_ranked.assert_called_once_with(limit=10)
message.answer.assert_called_once() message.answer.assert_called_once()
args, kwargs = message.answer.call_args 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 "reply_markup" in kwargs
assert isinstance(kwargs["reply_markup"], InlineKeyboardMarkup) 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 = [] mock_storage.get_top_ranked.return_value = []
# 2. Act # 2. Act
await handler(message=message) command = CommandObject(prefix='/', command='hottest', args=None)
await handler(message=message, command=command)
# 3. Assert # 3. Assert
mock_storage.get_top_ranked.assert_called_once_with(limit=10) mock_storage.get_top_ranked.assert_called_once_with(limit=10)
message.answer.assert_called_once_with("No hot trends found yet.") 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)