Files
bottohelp/tests/test_core/test_message_router.py

297 lines
11 KiB
Python

"""
Тесты для MessageTypeRouter.
"""
import pytest
from unittest.mock import Mock, AsyncMock
from telegram import Update, Message, User, CallbackQuery
from core.message_router import MessageTypeRouter, MessageHandlerConfig
from core.permissions import UserRole
class TestMessageTypeRouter:
"""Тесты маршрутизатора типов сообщений"""
@pytest.fixture
def message_router(self):
"""Фикстура для MessageTypeRouter"""
return MessageTypeRouter()
@pytest.fixture
def mock_update_text(self):
"""Мок-объект для текстового сообщения"""
update = Mock(spec=Update)
message = Mock(spec=Message)
user = Mock(spec=User)
user.id = 123456789
message.text = "Hello world"
message.voice = None
message.photo = None
update.message = message
update.effective_user = user
# Mock chat for permission checking
chat = Mock()
member = Mock()
member.status = 'member'
chat.get_member = AsyncMock(return_value=member)
update.effective_chat = chat
return update
@pytest.fixture
def mock_update_voice(self):
"""Мок-объект для голосового сообщения"""
update = Mock(spec=Update)
message = Mock(spec=Message)
user = Mock(spec=User)
user.id = 123456789
voice = Mock()
message.text = None
message.voice = voice
message.photo = None
update.message = message
update.effective_user = user
# Mock chat for permission checking
chat = Mock()
member = Mock()
member.status = 'member'
chat.get_member = AsyncMock(return_value=member)
update.effective_chat = chat
return update
@pytest.fixture
def mock_update_callback(self):
"""Мок-объект для callback запроса"""
update = Mock(spec=Update)
callback_query = Mock(spec=CallbackQuery)
user = Mock(spec=User)
user.id = 123456789
callback_query.data = "test_callback"
update.callback_query = callback_query
update.effective_user = user
# Mock chat for permission checking
chat = Mock()
member = Mock()
member.status = 'member'
chat.get_member = AsyncMock(return_value=member)
update.effective_chat = chat
return update
@pytest.fixture
def mock_context(self):
"""Мок-объект для контекста"""
return Mock()
def test_initialization(self, message_router):
"""Тест инициализации маршрутизатора"""
assert message_router.text_handlers == {}
assert message_router.callback_handlers == {}
assert message_router.voice_handlers == []
assert len(message_router.photo_handlers) == 0
def test_register_text_handler(self, message_router):
"""Тест регистрации обработчика текста"""
async def handler(update, context):
pass
message_router.register_text_handler(
r'hello', handler, UserRole.USER, 1, 'Test handler'
)
assert len(message_router.text_handlers) == 1
pattern, config = list(message_router.text_handlers.items())[0]
assert config.required_role == UserRole.USER
assert config.priority == 1
assert config.description == 'Test handler'
def test_register_text_handler_invalid_regex(self, message_router):
"""Тест регистрации обработчика с невалидным regex"""
async def handler(update, context):
pass
# Неверный regex не должен вызывать исключение, но и не регистрироваться
initial_count = len(message_router.text_handlers)
message_router.register_text_handler(
r'[invalid', handler, UserRole.USER
)
assert len(message_router.text_handlers) == initial_count
def test_register_callback_handler(self, message_router):
"""Тест регистрации обработчика callback"""
async def handler(update, context):
pass
message_router.register_callback_handler(
'test_callback', handler, UserRole.USER, 'Test callback handler'
)
assert 'test_callback' in message_router.callback_handlers
config = message_router.callback_handlers['test_callback']
assert config.required_role == UserRole.USER
assert config.description == 'Test callback handler'
def test_register_media_handler(self, message_router):
"""Тест регистрации обработчика медиа"""
async def handler(update, context):
pass
message_router.register_media_handler(
'photo', handler, UserRole.USER, 'Photo handler'
)
assert len(message_router.photo_handlers) == 1
config = message_router.photo_handlers[0]
assert config.required_role == UserRole.USER
assert config.description == 'Photo handler'
def test_register_voice_handler(self, message_router):
"""Тест регистрации обработчика голосовых сообщений"""
async def handler(update, context):
pass
message_router.register_media_handler(
'voice', handler, UserRole.USER, 'Voice handler'
)
assert len(message_router.voice_handlers) == 1
config = message_router.voice_handlers[0]
assert config.required_role == UserRole.USER
@pytest.mark.asyncio
async def test_route_text_message_no_match(self, message_router, mock_update_text, mock_context):
"""Тест маршрутизации текста без совпадений"""
result = await message_router.route_text_message(mock_update_text, mock_context)
assert result == False
@pytest.mark.asyncio
async def test_route_text_message_with_match(self, message_router, mock_update_text, mock_context):
"""Тест маршрутизации текста с совпадением"""
handler_called = False
async def test_handler(update, context):
nonlocal handler_called
handler_called = True
message_router.register_text_handler(r'hello', test_handler, UserRole.USER)
result = await message_router.route_text_message(mock_update_text, mock_context)
# Проверяем, что обработчик был вызван
assert handler_called == True
assert result == True
@pytest.mark.asyncio
async def test_route_text_message_insufficient_permissions(self, message_router, mock_update_text, mock_context):
"""Тест маршрутизации текста при недостаточных правах"""
handler_called = False
async def test_handler(update, context):
nonlocal handler_called
handler_called = True
# Регистрируем обработчик требующий админские права
message_router.register_text_handler(r'hello', test_handler, UserRole.ADMIN)
result = await message_router.route_text_message(mock_update_text, mock_context)
assert result == False
assert handler_called == False
@pytest.mark.asyncio
async def test_route_callback_exact_match(self, message_router, mock_update_callback, mock_context):
"""Тест маршрутизации callback с точным совпадением"""
handler_called = False
async def test_handler(update, context):
nonlocal handler_called
handler_called = True
message_router.register_callback_handler('test_callback', test_handler, UserRole.USER)
result = await message_router.route_callback(mock_update_callback, mock_context)
# Проверяем, что обработчик был вызван
assert handler_called == True
assert result == True
@pytest.mark.asyncio
async def test_route_callback_prefix_match(self, message_router, mock_update_callback, mock_context):
"""Тест маршрутизации callback по префиксу"""
handler_called = False
async def test_handler(update, context):
nonlocal handler_called
handler_called = True
message_router.register_callback_handler('test_', test_handler, UserRole.USER)
result = await message_router.route_callback(mock_update_callback, mock_context)
# Проверяем, что обработчик был вызван
assert handler_called == True
assert result == True
@pytest.mark.asyncio
async def test_route_callback_no_match(self, message_router, mock_update_callback, mock_context):
"""Тест маршрутизации callback без совпадений"""
result = await message_router.route_callback(mock_update_callback, mock_context)
assert result == False
@pytest.mark.asyncio
async def test_route_voice_message(self, message_router, mock_update_voice, mock_context):
"""Тест маршрутизации голосовых сообщений"""
handler_called = False
async def test_handler(update, context):
nonlocal handler_called
handler_called = True
message_router.register_media_handler('voice', test_handler, UserRole.USER)
result = await message_router.route_voice_message(mock_update_voice, mock_context)
# Проверяем, что обработчик был вызван
assert handler_called == True
assert result == True
def test_get_registered_handlers_count(self, message_router):
"""Тест получения статистики обработчиков"""
# Регистрируем несколько обработчиков
async def handler(update, context):
pass
message_router.register_text_handler(r'test', handler)
message_router.register_callback_handler('callback', handler)
message_router.register_media_handler('photo', handler)
stats = message_router.get_registered_handlers_count()
assert stats['text'] == 1
assert stats['callback'] == 1
assert stats['photo'] == 1
assert stats['voice'] == 0
def test_message_handler_config(self):
"""Тест конфигурации обработчика"""
async def handler():
pass
config = MessageHandlerConfig(handler, UserRole.MODERATOR, 5, 'Test config')
assert config.handler == handler
assert config.required_role == UserRole.MODERATOR
assert config.priority == 5
assert config.description == 'Test config'