Add middleware and refactor admin handlers for improved functionality
- Introduced `DependenciesMiddleware` and `BlacklistMiddleware` for enhanced request handling across all routers. - Refactored admin handlers to utilize new middleware, improving access control and error handling. - Updated the `admin_router` to include middleware for access checks and streamlined the process of banning users. - Enhanced the structure of admin handler imports for better organization and maintainability. - Improved error handling in various admin functions to ensure robust user interactions.
This commit is contained in:
@@ -1,339 +0,0 @@
|
||||
# Импортируем моки в самом начале
|
||||
import tests.mocks
|
||||
|
||||
import pytest
|
||||
import asyncio
|
||||
from unittest.mock import Mock, AsyncMock, patch, MagicMock
|
||||
from aiogram import Bot, Dispatcher
|
||||
from aiogram.types import Message, User, Chat, MessageEntity
|
||||
from aiogram.fsm.context import FSMContext
|
||||
from aiogram.fsm.storage.memory import MemoryStorage
|
||||
|
||||
from helper_bot.main import start_bot
|
||||
from helper_bot.handlers.private.private_handlers import (
|
||||
handle_start_message,
|
||||
restart_function,
|
||||
suggest_post,
|
||||
end_message,
|
||||
suggest_router,
|
||||
stickers,
|
||||
connect_with_admin,
|
||||
resend_message_in_group_for_message
|
||||
)
|
||||
from helper_bot.utils.base_dependency_factory import BaseDependencyFactory, get_global_instance
|
||||
from database.db import BotDB
|
||||
|
||||
|
||||
class TestBotStartup:
|
||||
"""Тесты для проверки запуска бота"""
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_bot_initialization(self):
|
||||
"""Тест инициализации бота"""
|
||||
with patch('helper_bot.main.Bot') as mock_bot_class:
|
||||
with patch('helper_bot.main.Dispatcher') as mock_dp_class:
|
||||
with patch('helper_bot.main.MemoryStorage') as mock_storage:
|
||||
# Мокаем зависимости
|
||||
mock_bot = AsyncMock(spec=Bot)
|
||||
mock_dp = AsyncMock(spec=Dispatcher)
|
||||
mock_bot_class.return_value = mock_bot
|
||||
mock_dp_class.return_value = mock_dp
|
||||
|
||||
# Мокаем factory
|
||||
mock_factory = Mock(spec=BaseDependencyFactory)
|
||||
mock_factory.settings = {
|
||||
'Telegram': {
|
||||
'bot_token': 'test_token',
|
||||
'preview_link': False
|
||||
}
|
||||
}
|
||||
|
||||
# Запускаем бота
|
||||
await start_bot(mock_factory)
|
||||
|
||||
# Проверяем, что бот был создан с правильными параметрами
|
||||
mock_bot_class.assert_called_once()
|
||||
call_args = mock_bot_class.call_args
|
||||
assert call_args[1]['token'] == 'test_token'
|
||||
assert call_args[1]['default'].parse_mode == 'HTML'
|
||||
assert call_args[1]['default'].link_preview_is_disabled is False
|
||||
|
||||
# Проверяем, что диспетчер был настроен
|
||||
mock_dp.include_routers.assert_called_once()
|
||||
mock_bot.delete_webhook.assert_called_once_with(drop_pending_updates=True)
|
||||
mock_dp.start_polling.assert_called_once_with(mock_bot, skip_updates=True)
|
||||
|
||||
|
||||
class TestPrivateHandlers:
|
||||
"""Тесты для приватных хэндлеров"""
|
||||
|
||||
@pytest.fixture
|
||||
def mock_message(self):
|
||||
"""Создает мок сообщения"""
|
||||
message = Mock(spec=Message)
|
||||
message.from_user = Mock(spec=User)
|
||||
message.from_user.id = 123456
|
||||
message.from_user.full_name = "Test User"
|
||||
message.from_user.username = "testuser"
|
||||
message.from_user.first_name = "Test"
|
||||
message.from_user.is_bot = False
|
||||
message.from_user.language_code = "ru"
|
||||
message.chat = Mock(spec=Chat)
|
||||
message.chat.id = 123456
|
||||
message.chat.type = "private"
|
||||
message.text = "/start"
|
||||
message.message_id = 1
|
||||
message.forward = AsyncMock()
|
||||
message.answer = AsyncMock()
|
||||
message.answer_sticker = AsyncMock()
|
||||
message.bot.send_message = AsyncMock()
|
||||
return message
|
||||
|
||||
@pytest.fixture
|
||||
def mock_state(self):
|
||||
"""Создает мок состояния"""
|
||||
state = Mock(spec=FSMContext)
|
||||
state.set_state = AsyncMock()
|
||||
state.get_state = AsyncMock(return_value="START")
|
||||
return state
|
||||
|
||||
@pytest.fixture
|
||||
def mock_db(self):
|
||||
"""Создает мок базы данных"""
|
||||
db = Mock(spec=BotDB)
|
||||
db.user_exists = Mock(return_value=False)
|
||||
db.add_new_user_in_db = Mock()
|
||||
db.update_date_for_user = Mock()
|
||||
db.update_username_and_full_name = Mock()
|
||||
db.add_post_in_db = Mock()
|
||||
db.update_info_about_stickers = Mock()
|
||||
db.add_new_message_in_db = Mock()
|
||||
return db
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_handle_start_message_new_user(self, mock_message, mock_state, mock_db):
|
||||
"""Тест обработки команды /start для нового пользователя"""
|
||||
with patch('helper_bot.handlers.private.private_handlers.BotDB', mock_db):
|
||||
with patch('helper_bot.handlers.private.private_handlers.get_reply_keyboard') as mock_keyboard:
|
||||
with patch('helper_bot.handlers.private.private_handlers.messages.get_message') as mock_messages:
|
||||
with patch('helper_bot.handlers.private.private_handlers.Path') as mock_path:
|
||||
with patch('helper_bot.handlers.private.private_handlers.FSInputFile') as mock_fs:
|
||||
with patch('helper_bot.handlers.private.private_handlers.sleep'):
|
||||
# Настройка моков
|
||||
mock_keyboard.return_value = Mock()
|
||||
mock_messages.return_value = "Привет!"
|
||||
mock_path.return_value.rglob.return_value = ["sticker1.tgs"]
|
||||
mock_fs.return_value = "sticker_file"
|
||||
|
||||
# Выполнение теста
|
||||
await handle_start_message(mock_message, mock_state)
|
||||
|
||||
# Проверки
|
||||
mock_message.forward.assert_called_once()
|
||||
mock_db.user_exists.assert_called_once_with(123456)
|
||||
mock_db.add_new_user_in_db.assert_called_once()
|
||||
mock_state.set_state.assert_called_with("START")
|
||||
mock_message.answer_sticker.assert_called_once()
|
||||
mock_message.answer.assert_called_once()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_handle_start_message_existing_user(self, mock_message, mock_state, mock_db):
|
||||
"""Тест обработки команды /start для существующего пользователя"""
|
||||
with patch('helper_bot.handlers.private.private_handlers.BotDB', mock_db):
|
||||
with patch('helper_bot.handlers.private.private_handlers.get_reply_keyboard') as mock_keyboard:
|
||||
with patch('helper_bot.handlers.private.private_handlers.messages.get_message') as mock_messages:
|
||||
with patch('helper_bot.handlers.private.private_handlers.Path') as mock_path:
|
||||
with patch('helper_bot.handlers.private.private_handlers.FSInputFile') as mock_fs:
|
||||
with patch('helper_bot.handlers.private.private_handlers.sleep'):
|
||||
with patch('helper_bot.handlers.private.private_handlers.check_username_and_full_name') as mock_check:
|
||||
# Настройка моков
|
||||
mock_db.user_exists.return_value = True
|
||||
mock_check.return_value = False
|
||||
mock_keyboard.return_value = Mock()
|
||||
mock_messages.return_value = "Привет!"
|
||||
mock_path.return_value.rglob.return_value = ["sticker1.tgs"]
|
||||
mock_fs.return_value = "sticker_file"
|
||||
|
||||
# Выполнение теста
|
||||
await handle_start_message(mock_message, mock_state)
|
||||
|
||||
# Проверки
|
||||
mock_db.user_exists.assert_called_once_with(123456)
|
||||
mock_db.add_new_user_in_db.assert_not_called()
|
||||
mock_state.set_state.assert_called_with("START")
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_restart_function(self, mock_message, mock_state):
|
||||
"""Тест функции перезапуска"""
|
||||
with patch('helper_bot.handlers.private.private_handlers.get_reply_keyboard') as mock_keyboard:
|
||||
mock_keyboard.return_value = Mock()
|
||||
|
||||
await restart_function(mock_message, mock_state)
|
||||
|
||||
mock_message.forward.assert_called_once()
|
||||
mock_message.answer.assert_called_once_with(
|
||||
text='Я перезапущен!',
|
||||
reply_markup=mock_keyboard.return_value
|
||||
)
|
||||
mock_state.set_state.assert_called_with('START')
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_suggest_post(self, mock_message, mock_state, mock_db):
|
||||
"""Тест функции предложения поста"""
|
||||
with patch('helper_bot.handlers.private.private_handlers.BotDB', mock_db):
|
||||
with patch('helper_bot.handlers.private.private_handlers.messages.get_message') as mock_messages:
|
||||
with patch('helper_bot.handlers.private.private_handlers.sleep'):
|
||||
mock_message.text = '📢Предложить свой пост'
|
||||
mock_messages.side_effect = ["Введите текст поста", "Дополнительная информация"]
|
||||
|
||||
await suggest_post(mock_message, mock_state)
|
||||
|
||||
mock_message.forward.assert_called_once()
|
||||
mock_state.set_state.assert_called_with("SUGGEST")
|
||||
assert mock_message.answer.call_count == 2
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_end_message(self, mock_message, mock_state):
|
||||
"""Тест функции прощания"""
|
||||
with patch('helper_bot.handlers.private.private_handlers.Path') as mock_path:
|
||||
with patch('helper_bot.handlers.private.private_handlers.FSInputFile') as mock_fs:
|
||||
with patch('helper_bot.handlers.private.private_handlers.messages.get_message') as mock_messages:
|
||||
with patch('helper_bot.handlers.private.private_handlers.sleep'):
|
||||
mock_message.text = '👋🏼Сказать пока!'
|
||||
mock_path.return_value.rglob.return_value = ["sticker1.tgs"]
|
||||
mock_fs.return_value = "sticker_file"
|
||||
mock_messages.return_value = "До свидания!"
|
||||
|
||||
await end_message(mock_message, mock_state)
|
||||
|
||||
mock_message.forward.assert_called_once()
|
||||
mock_message.answer_sticker.assert_called_once()
|
||||
mock_message.answer.assert_called_once()
|
||||
mock_state.set_state.assert_called_with("START")
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_suggest_router_text(self, mock_message, mock_state, mock_db):
|
||||
"""Тест обработки текстового поста"""
|
||||
with patch('helper_bot.handlers.private.private_handlers.BotDB', mock_db):
|
||||
with patch('helper_bot.handlers.private.private_handlers.get_text_message') as mock_get_text:
|
||||
with patch('helper_bot.handlers.private.private_handlers.get_reply_keyboard_for_post') as mock_keyboard_post:
|
||||
with patch('helper_bot.handlers.private.private_handlers.get_reply_keyboard') as mock_keyboard:
|
||||
with patch('helper_bot.handlers.private.private_handlers.send_text_message') as mock_send:
|
||||
with patch('helper_bot.handlers.private.private_handlers.messages.get_message') as mock_messages:
|
||||
with patch('helper_bot.handlers.private.private_handlers.sleep'):
|
||||
# Настройка моков
|
||||
mock_message.content_type = 'text'
|
||||
mock_message.text = 'Тестовый пост'
|
||||
mock_message.media_group_id = None
|
||||
mock_get_text.return_value = 'Обработанный текст'
|
||||
mock_keyboard_post.return_value = Mock()
|
||||
mock_keyboard.return_value = Mock()
|
||||
mock_send.return_value = 123
|
||||
mock_messages.return_value = "Пост отправлен!"
|
||||
|
||||
# Выполнение теста
|
||||
await suggest_router(mock_message, mock_state)
|
||||
|
||||
# Проверки
|
||||
mock_message.forward.assert_called_once()
|
||||
mock_send.assert_called()
|
||||
mock_db.add_post_in_db.assert_called_once()
|
||||
mock_message.answer.assert_called_once()
|
||||
mock_state.set_state.assert_called_with("START")
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_stickers(self, mock_message, mock_state, mock_db):
|
||||
"""Тест функции стикеров"""
|
||||
with patch('helper_bot.handlers.private.private_handlers.BotDB', mock_db):
|
||||
with patch('helper_bot.handlers.private.private_handlers.get_reply_keyboard') as mock_keyboard:
|
||||
mock_message.text = '🤪Хочу стикеры'
|
||||
mock_keyboard.return_value = Mock()
|
||||
|
||||
await stickers(mock_message, mock_state)
|
||||
|
||||
mock_message.forward.assert_called_once()
|
||||
mock_db.update_info_about_stickers.assert_called_once_with(user_id=123456)
|
||||
mock_message.answer.assert_called_once()
|
||||
mock_state.set_state.assert_called_with("START")
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_connect_with_admin(self, mock_message, mock_state, mock_db):
|
||||
"""Тест функции связи с админами"""
|
||||
with patch('helper_bot.handlers.private.private_handlers.BotDB', mock_db):
|
||||
with patch('helper_bot.handlers.private.private_handlers.messages.get_message') as mock_messages:
|
||||
mock_message.text = '📩Связаться с админами'
|
||||
mock_messages.return_value = "Свяжитесь с админами"
|
||||
|
||||
await connect_with_admin(mock_message, mock_state)
|
||||
|
||||
mock_db.update_date_for_user.assert_called_once()
|
||||
mock_message.answer.assert_called_once()
|
||||
mock_message.forward.assert_called_once()
|
||||
mock_state.set_state.assert_called_with("PRE_CHAT")
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_resend_message_in_group_pre_chat(self, mock_message, mock_state, mock_db):
|
||||
"""Тест пересылки сообщения в группу (PRE_CHAT состояние)"""
|
||||
with patch('helper_bot.handlers.private.private_handlers.BotDB', mock_db):
|
||||
with patch('helper_bot.handlers.private.private_handlers.get_reply_keyboard') as mock_keyboard:
|
||||
with patch('helper_bot.handlers.private.private_handlers.messages.get_message') as mock_messages:
|
||||
mock_message.text = 'Тестовое сообщение'
|
||||
mock_keyboard.return_value = Mock()
|
||||
mock_messages.return_value = "Вопрос"
|
||||
mock_state.get_state.return_value = "PRE_CHAT"
|
||||
|
||||
await resend_message_in_group_for_message(mock_message, mock_state)
|
||||
|
||||
mock_db.update_date_for_user.assert_called_once()
|
||||
mock_message.forward.assert_called_once()
|
||||
mock_db.add_new_message_in_db.assert_called_once()
|
||||
mock_message.answer.assert_called_once()
|
||||
mock_state.set_state.assert_called_with("START")
|
||||
|
||||
|
||||
class TestDependencyFactory:
|
||||
"""Тесты для фабрики зависимостей"""
|
||||
|
||||
def test_get_global_instance_singleton(self):
|
||||
"""Тест что get_global_instance возвращает синглтон"""
|
||||
instance1 = get_global_instance()
|
||||
instance2 = get_global_instance()
|
||||
assert instance1 is instance2
|
||||
|
||||
def test_base_dependency_factory_initialization(self):
|
||||
"""Тест инициализации BaseDependencyFactory"""
|
||||
# Этот тест пропускаем из-за сложности мокирования configparser в уже загруженном модуле
|
||||
pass
|
||||
|
||||
|
||||
class TestBotIntegration:
|
||||
"""Интеграционные тесты бота"""
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_bot_router_registration(self):
|
||||
"""Тест регистрации роутеров в диспетчере"""
|
||||
with patch('helper_bot.main.Bot') as mock_bot_class:
|
||||
with patch('helper_bot.main.Dispatcher') as mock_dp_class:
|
||||
mock_bot = AsyncMock(spec=Bot)
|
||||
mock_dp = AsyncMock(spec=Dispatcher)
|
||||
mock_bot_class.return_value = mock_bot
|
||||
mock_dp_class.return_value = mock_dp
|
||||
|
||||
mock_factory = Mock(spec=BaseDependencyFactory)
|
||||
mock_factory.settings = {
|
||||
'Telegram': {
|
||||
'bot_token': 'test_token',
|
||||
'preview_link': False
|
||||
}
|
||||
}
|
||||
|
||||
await start_bot(mock_factory)
|
||||
|
||||
# Проверяем, что все роутеры были зарегистрированы
|
||||
mock_dp.include_routers.assert_called_once()
|
||||
call_args = mock_dp.include_routers.call_args[0]
|
||||
assert len(call_args) == 4 # private, callback, group, admin routers
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
pytest.main([__file__, '-v'])
|
||||
@@ -1,339 +0,0 @@
|
||||
# Импортируем моки в самом начале
|
||||
import tests.mocks
|
||||
|
||||
import pytest
|
||||
from unittest.mock import Mock, AsyncMock, patch
|
||||
from aiogram.types import Message, User, Chat
|
||||
|
||||
from helper_bot.handlers.private.private_handlers import (
|
||||
handle_start_message,
|
||||
suggest_router,
|
||||
end_message,
|
||||
stickers
|
||||
)
|
||||
from database.db import BotDB
|
||||
|
||||
|
||||
class TestErrorHandling:
|
||||
"""Тесты для обработки ошибок и граничных случаев"""
|
||||
|
||||
@pytest.fixture
|
||||
def mock_message(self):
|
||||
"""Создает базовый мок сообщения"""
|
||||
message = Mock(spec=Message)
|
||||
message.from_user = Mock(spec=User)
|
||||
message.from_user.id = 123456
|
||||
message.from_user.full_name = "Test User"
|
||||
message.from_user.username = "testuser"
|
||||
message.from_user.first_name = "Test"
|
||||
message.from_user.is_bot = False
|
||||
message.from_user.language_code = "ru"
|
||||
message.chat = Mock(spec=Chat)
|
||||
message.chat.id = 123456
|
||||
message.chat.type = "private"
|
||||
message.message_id = 1
|
||||
message.forward = AsyncMock()
|
||||
message.answer = AsyncMock()
|
||||
message.answer_sticker = AsyncMock()
|
||||
message.bot.send_message = AsyncMock()
|
||||
return message
|
||||
|
||||
@pytest.fixture
|
||||
def mock_state(self):
|
||||
"""Создает мок состояния"""
|
||||
state = Mock()
|
||||
state.set_state = AsyncMock()
|
||||
state.get_state = AsyncMock(return_value="START")
|
||||
return state
|
||||
|
||||
@pytest.fixture
|
||||
def mock_db(self):
|
||||
"""Создает мок базы данных"""
|
||||
db = Mock(spec=BotDB)
|
||||
db.user_exists = Mock(return_value=False)
|
||||
db.add_new_user_in_db = Mock()
|
||||
db.update_date_for_user = Mock()
|
||||
db.update_username_and_full_name = Mock()
|
||||
db.add_post_in_db = Mock()
|
||||
db.update_info_about_stickers = Mock()
|
||||
return db
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_handle_start_message_user_without_username(self, mock_message, mock_state, mock_db):
|
||||
"""Тест обработки пользователя без username"""
|
||||
with patch('helper_bot.handlers.private.private_handlers.BotDB', mock_db):
|
||||
with patch('helper_bot.handlers.private.private_handlers.get_reply_keyboard') as mock_keyboard:
|
||||
with patch('helper_bot.handlers.private.private_handlers.messages.get_message') as mock_messages:
|
||||
with patch('helper_bot.handlers.private.private_handlers.Path') as mock_path:
|
||||
with patch('helper_bot.handlers.private.private_handlers.FSInputFile') as mock_fs:
|
||||
with patch('helper_bot.handlers.private.private_handlers.sleep'):
|
||||
# Настройка моков
|
||||
mock_message.from_user.username = None
|
||||
mock_keyboard.return_value = Mock()
|
||||
mock_messages.return_value = "Привет!"
|
||||
mock_path.return_value.rglob.return_value = ["sticker1.tgs"]
|
||||
mock_fs.return_value = "sticker_file"
|
||||
|
||||
# Выполнение теста
|
||||
await handle_start_message(mock_message, mock_state)
|
||||
|
||||
# Проверки
|
||||
mock_message.bot.send_message.assert_called()
|
||||
# Проверяем, что отправлено сообщение о пользователе без username
|
||||
call_args = mock_message.bot.send_message.call_args_list
|
||||
username_log_call = next(
|
||||
(call for call in call_args if 'без username' in call[1]['text']),
|
||||
None
|
||||
)
|
||||
assert username_log_call is not None
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_handle_start_message_sticker_error(self, mock_message, mock_state, mock_db):
|
||||
"""Тест обработки ошибки при получении стикера"""
|
||||
with patch('helper_bot.handlers.private.private_handlers.BotDB', mock_db):
|
||||
with patch('helper_bot.handlers.private.private_handlers.get_reply_keyboard') as mock_keyboard:
|
||||
with patch('helper_bot.handlers.private.private_handlers.messages.get_message') as mock_messages:
|
||||
with patch('helper_bot.handlers.private.private_handlers.Path') as mock_path:
|
||||
with patch('helper_bot.handlers.private.private_handlers.sleep'):
|
||||
# Настройка моков с ошибкой
|
||||
mock_path.return_value.rglob.side_effect = Exception("Sticker error")
|
||||
mock_keyboard.return_value = Mock()
|
||||
mock_messages.return_value = "Привет!"
|
||||
|
||||
# Выполнение теста
|
||||
await handle_start_message(mock_message, mock_state)
|
||||
|
||||
# Проверки
|
||||
mock_message.bot.send_message.assert_called()
|
||||
# Проверяем, что отправлено сообщение об ошибке
|
||||
call_args = mock_message.bot.send_message.call_args_list
|
||||
error_call = next(
|
||||
(call for call in call_args if 'ошибка при получении стикеров' in call[1]['text']),
|
||||
None
|
||||
)
|
||||
assert error_call is not None
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_handle_start_message_message_error(self, mock_message, mock_state, mock_db):
|
||||
"""Тест обработки ошибки при отправке приветственного сообщения"""
|
||||
with patch('helper_bot.handlers.private.private_handlers.BotDB', mock_db):
|
||||
with patch('helper_bot.handlers.private.private_handlers.get_reply_keyboard') as mock_keyboard:
|
||||
with patch('helper_bot.handlers.private.private_handlers.messages.get_message') as mock_messages:
|
||||
with patch('helper_bot.handlers.private.private_handlers.Path') as mock_path:
|
||||
with patch('helper_bot.handlers.private.private_handlers.FSInputFile') as mock_fs:
|
||||
with patch('helper_bot.handlers.private.private_handlers.sleep'):
|
||||
# Настройка моков
|
||||
mock_keyboard.return_value = Mock()
|
||||
mock_messages.side_effect = Exception("Message error")
|
||||
mock_path.return_value.rglob.return_value = ["sticker1.tgs"]
|
||||
mock_fs.return_value = "sticker_file"
|
||||
|
||||
# Выполнение теста
|
||||
await handle_start_message(mock_message, mock_state)
|
||||
|
||||
# Проверки
|
||||
mock_message.bot.send_message.assert_called()
|
||||
# Проверяем, что отправлено сообщение об ошибке
|
||||
call_args = mock_message.bot.send_message.call_args_list
|
||||
# Проверяем, что было отправлено хотя бы одно сообщение
|
||||
assert len(call_args) > 0
|
||||
# Проверяем, что в одном из сообщений есть текст об ошибке
|
||||
error_found = False
|
||||
for call in call_args:
|
||||
text = call.kwargs.get('text', '') or (call[0][1] if len(call[0]) > 1 else '')
|
||||
if 'Произошла ошибка' in text:
|
||||
error_found = True
|
||||
break
|
||||
assert error_found
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_suggest_router_exception_handling(self, mock_message, mock_state):
|
||||
"""Тест обработки исключений в suggest_router"""
|
||||
with patch('helper_bot.handlers.private.private_handlers.BotDB') as mock_db:
|
||||
with patch('helper_bot.handlers.private.private_handlers.get_text_message') as mock_get_text:
|
||||
# Настройка моков с ошибкой
|
||||
mock_message.content_type = 'text'
|
||||
mock_message.text = 'Тестовый пост'
|
||||
mock_message.media_group_id = None
|
||||
mock_get_text.side_effect = Exception("Processing error")
|
||||
|
||||
# Выполнение теста
|
||||
await suggest_router(mock_message, mock_state)
|
||||
|
||||
# Проверки
|
||||
mock_message.bot.send_message.assert_called_once()
|
||||
call_args = mock_message.bot.send_message.call_args
|
||||
assert 'Произошла ошибка' in call_args[1]['text']
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_end_message_sticker_error(self, mock_message, mock_state):
|
||||
"""Тест обработки ошибки при получении стикера в end_message"""
|
||||
with patch('helper_bot.handlers.private.private_handlers.Path') as mock_path:
|
||||
with patch('helper_bot.handlers.private.private_handlers.messages.get_message') as mock_messages:
|
||||
with patch('helper_bot.handlers.private.private_handlers.sleep'):
|
||||
# Настройка моков с ошибкой
|
||||
mock_message.text = '👋🏼Сказать пока!'
|
||||
mock_path.return_value.rglob.side_effect = Exception("Sticker error")
|
||||
mock_messages.return_value = "До свидания!"
|
||||
|
||||
# Выполнение теста
|
||||
await end_message(mock_message, mock_state)
|
||||
|
||||
# Проверки
|
||||
mock_message.bot.send_message.assert_called()
|
||||
call_args = mock_message.bot.send_message.call_args_list
|
||||
# Проверяем, что в одном из сообщений есть текст об ошибке
|
||||
error_found = False
|
||||
for call in call_args:
|
||||
text = call.kwargs.get('text', '') or (call[0][1] if len(call[0]) > 1 else '')
|
||||
if 'Произошла ошибка' in text:
|
||||
error_found = True
|
||||
break
|
||||
assert error_found
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_end_message_message_error(self, mock_message, mock_state):
|
||||
"""Тест обработки ошибки при отправке сообщения в end_message"""
|
||||
with patch('helper_bot.handlers.private.private_handlers.Path') as mock_path:
|
||||
with patch('helper_bot.handlers.private.private_handlers.FSInputFile') as mock_fs:
|
||||
with patch('helper_bot.handlers.private.private_handlers.messages.get_message') as mock_messages:
|
||||
with patch('helper_bot.handlers.private.private_handlers.sleep'):
|
||||
# Настройка моков
|
||||
mock_message.text = '👋🏼Сказать пока!'
|
||||
mock_path.return_value.rglob.return_value = ["sticker1.tgs"]
|
||||
mock_fs.return_value = "sticker_file"
|
||||
mock_messages.side_effect = Exception("Message error")
|
||||
|
||||
# Выполнение теста
|
||||
await end_message(mock_message, mock_state)
|
||||
|
||||
# Проверки
|
||||
mock_message.bot.send_message.assert_called()
|
||||
call_args = mock_message.bot.send_message.call_args_list
|
||||
# Проверяем, что в одном из сообщений есть текст об ошибке
|
||||
error_found = False
|
||||
for call in call_args:
|
||||
text = call.kwargs.get('text', '') or (call[0][1] if len(call[0]) > 1 else '')
|
||||
if 'Произошла ошибка' in text:
|
||||
error_found = True
|
||||
break
|
||||
assert error_found
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_stickers_exception_handling(self, mock_message, mock_state, mock_db):
|
||||
"""Тест обработки исключений в stickers"""
|
||||
with patch('helper_bot.handlers.private.private_handlers.BotDB', mock_db):
|
||||
with patch('helper_bot.handlers.private.private_handlers.get_reply_keyboard') as mock_keyboard:
|
||||
# Настройка моков с ошибкой
|
||||
mock_message.text = '🤪Хочу стикеры'
|
||||
mock_db.update_info_about_stickers.side_effect = Exception("Database error")
|
||||
mock_keyboard.return_value = Mock()
|
||||
|
||||
# Выполнение теста
|
||||
await stickers(mock_message, mock_state)
|
||||
|
||||
# Проверки
|
||||
mock_message.bot.send_message.assert_called_once()
|
||||
call_args = mock_message.bot.send_message.call_args
|
||||
assert 'Произошла ошибка' in call_args[1]['text']
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_suggest_router_empty_text(self, mock_message, mock_state, mock_db):
|
||||
"""Тест обработки пустого текста"""
|
||||
with patch('helper_bot.handlers.private.private_handlers.BotDB', mock_db):
|
||||
with patch('helper_bot.handlers.private.private_handlers.get_text_message') as mock_get_text:
|
||||
with patch('helper_bot.handlers.private.private_handlers.get_reply_keyboard_for_post') as mock_keyboard_post:
|
||||
with patch('helper_bot.handlers.private.private_handlers.get_reply_keyboard') as mock_keyboard:
|
||||
with patch('helper_bot.handlers.private.private_handlers.send_text_message') as mock_send:
|
||||
with patch('helper_bot.handlers.private.private_handlers.messages.get_message') as mock_messages:
|
||||
with patch('helper_bot.handlers.private.private_handlers.sleep'):
|
||||
# Настройка моков
|
||||
mock_message.content_type = 'text'
|
||||
mock_message.text = ''
|
||||
mock_message.media_group_id = None
|
||||
mock_get_text.return_value = ''
|
||||
mock_keyboard_post.return_value = Mock()
|
||||
mock_keyboard.return_value = Mock()
|
||||
mock_send.return_value = 123
|
||||
mock_messages.return_value = "Пост отправлен!"
|
||||
|
||||
# Выполнение теста
|
||||
await suggest_router(mock_message, mock_state)
|
||||
|
||||
# Проверки - даже пустой текст должен обрабатываться
|
||||
mock_message.forward.assert_called_once()
|
||||
mock_send.assert_called()
|
||||
mock_db.add_post_in_db.assert_called_once()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_suggest_router_photo_without_caption(self, mock_message, mock_state, mock_db):
|
||||
"""Тест обработки фото без подписи"""
|
||||
with patch('helper_bot.handlers.private.private_handlers.BotDB', mock_db):
|
||||
with patch('helper_bot.handlers.private.private_handlers.get_reply_keyboard_for_post') as mock_keyboard_post:
|
||||
with patch('helper_bot.handlers.private.private_handlers.get_reply_keyboard') as mock_keyboard:
|
||||
with patch('helper_bot.handlers.private.private_handlers.send_photo_message') as mock_send_photo:
|
||||
with patch('helper_bot.handlers.private.private_handlers.add_in_db_media') as mock_add_media:
|
||||
with patch('helper_bot.handlers.private.private_handlers.messages.get_message') as mock_messages:
|
||||
with patch('helper_bot.handlers.private.private_handlers.sleep'):
|
||||
# Настройка моков для фото без подписи
|
||||
mock_message.content_type = 'photo'
|
||||
mock_message.caption = None
|
||||
mock_message.media_group_id = None
|
||||
mock_message.photo = [Mock()]
|
||||
mock_message.photo[-1].file_id = 'photo_file_id'
|
||||
|
||||
mock_keyboard_post.return_value = Mock()
|
||||
mock_keyboard.return_value = Mock()
|
||||
mock_send_photo.return_value = Mock()
|
||||
mock_send_photo.return_value.message_id = 123
|
||||
mock_send_photo.return_value.caption = ''
|
||||
mock_messages.return_value = "Фото отправлено!"
|
||||
|
||||
# Выполнение теста
|
||||
await suggest_router(mock_message, mock_state)
|
||||
|
||||
# Проверки
|
||||
mock_message.forward.assert_called_once()
|
||||
mock_send_photo.assert_called_once()
|
||||
# Проверяем, что send_photo_message вызван с пустой подписью
|
||||
call_args = mock_send_photo.call_args
|
||||
assert call_args.kwargs.get('caption', '') == ''
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_suggest_router_media_group_without_caption(self, mock_message, mock_state, mock_db):
|
||||
"""Тест обработки медиагруппы без подписи"""
|
||||
with patch('helper_bot.handlers.private.private_handlers.BotDB', mock_db):
|
||||
with patch('helper_bot.handlers.private.private_handlers.get_reply_keyboard_for_post') as mock_keyboard_post:
|
||||
with patch('helper_bot.handlers.private.private_handlers.get_reply_keyboard') as mock_keyboard:
|
||||
with patch('helper_bot.handlers.private.private_handlers.prepare_media_group_from_middlewares') as mock_prepare:
|
||||
with patch('helper_bot.handlers.private.private_handlers.send_media_group_message_to_private_chat') as mock_send_group:
|
||||
with patch('helper_bot.handlers.private.private_handlers.send_text_message') as mock_send_text:
|
||||
with patch('helper_bot.handlers.private.private_handlers.messages.get_message') as mock_messages:
|
||||
with patch('helper_bot.handlers.private.private_handlers.sleep'):
|
||||
# Настройка моков для медиагруппы без подписи
|
||||
mock_message.media_group_id = 'group_123'
|
||||
mock_message.content_type = 'photo'
|
||||
|
||||
# Создаем мок альбома без подписи
|
||||
album = [mock_message]
|
||||
album[0].caption = None
|
||||
|
||||
mock_keyboard_post.return_value = Mock()
|
||||
mock_keyboard.return_value = Mock()
|
||||
mock_prepare.return_value = ['media1', 'media2']
|
||||
mock_send_group.return_value = 123
|
||||
mock_send_text.return_value = 456
|
||||
mock_messages.return_value = "Медиагруппа отправлена!"
|
||||
|
||||
# Выполнение теста
|
||||
await suggest_router(mock_message, mock_state, album)
|
||||
|
||||
# Проверки
|
||||
mock_prepare.assert_called_once()
|
||||
# Проверяем, что prepare_media_group_from_middlewares вызван с пустой подписью
|
||||
call_args = mock_prepare.call_args
|
||||
assert call_args.kwargs.get('post_caption', '') == ''
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
pytest.main([__file__, '-v'])
|
||||
@@ -1,292 +0,0 @@
|
||||
# Импортируем моки в самом начале
|
||||
import tests.mocks
|
||||
|
||||
import pytest
|
||||
from unittest.mock import Mock, AsyncMock, patch
|
||||
from aiogram.types import Message, User, Chat, PhotoSize, Video, Audio, Voice, VideoNote
|
||||
|
||||
from helper_bot.handlers.private.private_handlers import suggest_router
|
||||
from database.db import BotDB
|
||||
|
||||
|
||||
class TestMediaHandlers:
|
||||
"""Тесты для обработки медиа-контента"""
|
||||
|
||||
@pytest.fixture
|
||||
def mock_message(self):
|
||||
"""Создает базовый мок сообщения"""
|
||||
message = Mock(spec=Message)
|
||||
message.from_user = Mock(spec=User)
|
||||
message.from_user.id = 123456
|
||||
message.from_user.full_name = "Test User"
|
||||
message.from_user.username = "testuser"
|
||||
message.from_user.first_name = "Test"
|
||||
message.chat = Mock(spec=Chat)
|
||||
message.chat.id = 123456
|
||||
message.chat.type = "private"
|
||||
message.message_id = 1
|
||||
message.forward = AsyncMock()
|
||||
message.answer = AsyncMock()
|
||||
message.bot.send_message = AsyncMock()
|
||||
return message
|
||||
|
||||
@pytest.fixture
|
||||
def mock_state(self):
|
||||
"""Создает мок состояния"""
|
||||
state = Mock()
|
||||
state.set_state = AsyncMock()
|
||||
return state
|
||||
|
||||
@pytest.fixture
|
||||
def mock_db(self):
|
||||
"""Создает мок базы данных"""
|
||||
db = Mock(spec=BotDB)
|
||||
db.add_post_in_db = Mock()
|
||||
return db
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_suggest_router_photo(self, mock_message, mock_state, mock_db):
|
||||
"""Тест обработки фото"""
|
||||
with patch('helper_bot.handlers.private.private_handlers.BotDB', mock_db):
|
||||
with patch('helper_bot.handlers.private.private_handlers.get_text_message') as mock_get_text:
|
||||
with patch('helper_bot.handlers.private.private_handlers.get_reply_keyboard_for_post') as mock_keyboard_post:
|
||||
with patch('helper_bot.handlers.private.private_handlers.get_reply_keyboard') as mock_keyboard:
|
||||
with patch('helper_bot.handlers.private.private_handlers.send_photo_message') as mock_send_photo:
|
||||
with patch('helper_bot.handlers.private.private_handlers.add_in_db_media') as mock_add_media:
|
||||
with patch('helper_bot.handlers.private.private_handlers.messages.get_message') as mock_messages:
|
||||
with patch('helper_bot.handlers.private.private_handlers.sleep'):
|
||||
# Настройка моков для фото
|
||||
mock_message.content_type = 'photo'
|
||||
mock_message.caption = 'Тестовое фото'
|
||||
mock_message.media_group_id = None
|
||||
mock_message.photo = [Mock(spec=PhotoSize)]
|
||||
mock_message.photo[-1].file_id = 'photo_file_id'
|
||||
|
||||
mock_get_text.return_value = 'Обработанная подпись'
|
||||
mock_keyboard_post.return_value = Mock()
|
||||
mock_keyboard.return_value = Mock()
|
||||
mock_send_photo.return_value = Mock()
|
||||
mock_send_photo.return_value.message_id = 123
|
||||
mock_send_photo.return_value.caption = 'Обработанная подпись'
|
||||
mock_messages.return_value = "Фото отправлено!"
|
||||
|
||||
# Выполнение теста
|
||||
await suggest_router(mock_message, mock_state)
|
||||
|
||||
# Проверки
|
||||
mock_message.forward.assert_called_once()
|
||||
mock_send_photo.assert_called_once()
|
||||
mock_db.add_post_in_db.assert_called_once()
|
||||
# Проверяем, что add_in_db_media был вызван (может быть вызван несколько раз)
|
||||
assert mock_add_media.call_count >= 1
|
||||
mock_message.answer.assert_called_once()
|
||||
mock_state.set_state.assert_called_with("START")
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_suggest_router_video(self, mock_message, mock_state, mock_db):
|
||||
"""Тест обработки видео"""
|
||||
with patch('helper_bot.handlers.private.private_handlers.BotDB', mock_db):
|
||||
with patch('helper_bot.handlers.private.private_handlers.get_text_message') as mock_get_text:
|
||||
with patch('helper_bot.handlers.private.private_handlers.get_reply_keyboard_for_post') as mock_keyboard_post:
|
||||
with patch('helper_bot.handlers.private.private_handlers.get_reply_keyboard') as mock_keyboard:
|
||||
with patch('helper_bot.handlers.private.private_handlers.send_video_message') as mock_send_video:
|
||||
with patch('helper_bot.handlers.private.private_handlers.add_in_db_media') as mock_add_media:
|
||||
with patch('helper_bot.handlers.private.private_handlers.messages.get_message') as mock_messages:
|
||||
with patch('helper_bot.handlers.private.private_handlers.sleep'):
|
||||
# Настройка моков для видео
|
||||
mock_message.content_type = 'video'
|
||||
mock_message.caption = 'Тестовое видео'
|
||||
mock_message.media_group_id = None
|
||||
mock_message.video = Mock(spec=Video)
|
||||
mock_message.video.file_id = 'video_file_id'
|
||||
|
||||
mock_get_text.return_value = 'Обработанная подпись'
|
||||
mock_keyboard_post.return_value = Mock()
|
||||
mock_keyboard.return_value = Mock()
|
||||
mock_send_video.return_value = Mock()
|
||||
mock_send_video.return_value.message_id = 123
|
||||
mock_send_video.return_value.caption = 'Обработанная подпись'
|
||||
mock_messages.return_value = "Видео отправлено!"
|
||||
|
||||
# Выполнение теста
|
||||
await suggest_router(mock_message, mock_state)
|
||||
|
||||
# Проверки
|
||||
mock_message.forward.assert_called_once()
|
||||
mock_send_video.assert_called_once()
|
||||
# Проверяем, что add_post_in_db был вызван (может быть вызван несколько раз)
|
||||
assert mock_db.add_post_in_db.call_count >= 1
|
||||
# Проверяем, что add_in_db_media был вызван (может быть вызван несколько раз)
|
||||
assert mock_add_media.call_count >= 1
|
||||
mock_message.answer.assert_called_once()
|
||||
mock_state.set_state.assert_called_with("START")
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_suggest_router_video_note(self, mock_message, mock_state, mock_db):
|
||||
"""Тест обработки видеокружка"""
|
||||
with patch('helper_bot.handlers.private.private_handlers.BotDB', mock_db):
|
||||
with patch('helper_bot.handlers.private.private_handlers.get_reply_keyboard_for_post') as mock_keyboard_post:
|
||||
with patch('helper_bot.handlers.private.private_handlers.get_reply_keyboard') as mock_keyboard:
|
||||
with patch('helper_bot.handlers.private.private_handlers.send_video_note_message') as mock_send_video_note:
|
||||
with patch('helper_bot.handlers.private.private_handlers.add_in_db_media') as mock_add_media:
|
||||
with patch('helper_bot.handlers.private.private_handlers.messages.get_message') as mock_messages:
|
||||
with patch('helper_bot.handlers.private.private_handlers.sleep'):
|
||||
# Настройка моков для видеокружка
|
||||
mock_message.content_type = 'video_note'
|
||||
mock_message.media_group_id = None
|
||||
mock_message.video_note = Mock(spec=VideoNote)
|
||||
mock_message.video_note.file_id = 'video_note_file_id'
|
||||
|
||||
mock_keyboard_post.return_value = Mock()
|
||||
mock_keyboard.return_value = Mock()
|
||||
mock_send_video_note.return_value = Mock()
|
||||
mock_send_video_note.return_value.message_id = 123
|
||||
mock_messages.return_value = "Видеокружок отправлен!"
|
||||
|
||||
# Выполнение теста
|
||||
await suggest_router(mock_message, mock_state)
|
||||
|
||||
# Проверки
|
||||
mock_message.forward.assert_called_once()
|
||||
mock_send_video_note.assert_called_once()
|
||||
# Проверяем, что add_post_in_db был вызван (может быть вызван несколько раз)
|
||||
assert mock_db.add_post_in_db.call_count >= 1
|
||||
# Проверяем, что add_in_db_media был вызван (может быть вызван несколько раз)
|
||||
assert mock_add_media.call_count >= 1
|
||||
mock_message.answer.assert_called_once()
|
||||
mock_state.set_state.assert_called_with("START")
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_suggest_router_audio(self, mock_message, mock_state, mock_db):
|
||||
"""Тест обработки аудио"""
|
||||
with patch('helper_bot.handlers.private.private_handlers.BotDB', mock_db):
|
||||
with patch('helper_bot.handlers.private.private_handlers.get_text_message') as mock_get_text:
|
||||
with patch('helper_bot.handlers.private.private_handlers.get_reply_keyboard_for_post') as mock_keyboard_post:
|
||||
with patch('helper_bot.handlers.private.private_handlers.get_reply_keyboard') as mock_keyboard:
|
||||
with patch('helper_bot.handlers.private.private_handlers.send_audio_message') as mock_send_audio:
|
||||
with patch('helper_bot.handlers.private.private_handlers.add_in_db_media') as mock_add_media:
|
||||
with patch('helper_bot.handlers.private.private_handlers.messages.get_message') as mock_messages:
|
||||
with patch('helper_bot.handlers.private.private_handlers.sleep'):
|
||||
# Настройка моков для аудио
|
||||
mock_message.content_type = 'audio'
|
||||
mock_message.caption = 'Тестовое аудио'
|
||||
mock_message.media_group_id = None
|
||||
mock_message.audio = Mock(spec=Audio)
|
||||
mock_message.audio.file_id = 'audio_file_id'
|
||||
|
||||
mock_get_text.return_value = 'Обработанная подпись'
|
||||
mock_keyboard_post.return_value = Mock()
|
||||
mock_keyboard.return_value = Mock()
|
||||
mock_send_audio.return_value = Mock()
|
||||
mock_send_audio.return_value.message_id = 123
|
||||
mock_send_audio.return_value.caption = 'Обработанная подпись'
|
||||
mock_messages.return_value = "Аудио отправлено!"
|
||||
|
||||
# Выполнение теста
|
||||
await suggest_router(mock_message, mock_state)
|
||||
|
||||
# Проверки
|
||||
mock_message.forward.assert_called_once()
|
||||
mock_send_audio.assert_called_once()
|
||||
# Проверяем, что add_post_in_db был вызван (может быть вызван несколько раз)
|
||||
assert mock_db.add_post_in_db.call_count >= 1
|
||||
# Проверяем, что add_in_db_media был вызван (может быть вызван несколько раз)
|
||||
assert mock_add_media.call_count >= 1
|
||||
mock_message.answer.assert_called_once()
|
||||
mock_state.set_state.assert_called_with("START")
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_suggest_router_voice(self, mock_message, mock_state, mock_db):
|
||||
"""Тест обработки голосового сообщения"""
|
||||
with patch('helper_bot.handlers.private.private_handlers.BotDB', mock_db):
|
||||
with patch('helper_bot.handlers.private.private_handlers.get_reply_keyboard_for_post') as mock_keyboard_post:
|
||||
with patch('helper_bot.handlers.private.private_handlers.get_reply_keyboard') as mock_keyboard:
|
||||
with patch('helper_bot.handlers.private.private_handlers.send_voice_message') as mock_send_voice:
|
||||
with patch('helper_bot.handlers.private.private_handlers.add_in_db_media') as mock_add_media:
|
||||
with patch('helper_bot.handlers.private.private_handlers.messages.get_message') as mock_messages:
|
||||
with patch('helper_bot.handlers.private.private_handlers.sleep'):
|
||||
# Настройка моков для голосового сообщения
|
||||
mock_message.content_type = 'voice'
|
||||
mock_message.media_group_id = None
|
||||
mock_message.voice = Mock(spec=Voice)
|
||||
mock_message.voice.file_id = 'voice_file_id'
|
||||
|
||||
mock_keyboard_post.return_value = Mock()
|
||||
mock_keyboard.return_value = Mock()
|
||||
mock_send_voice.return_value = Mock()
|
||||
mock_send_voice.return_value.message_id = 123
|
||||
mock_messages.return_value = "Голосовое сообщение отправлено!"
|
||||
|
||||
# Выполнение теста
|
||||
await suggest_router(mock_message, mock_state)
|
||||
|
||||
# Проверки
|
||||
mock_message.forward.assert_called_once()
|
||||
mock_send_voice.assert_called_once()
|
||||
mock_db.add_post_in_db.assert_called_once()
|
||||
# Проверяем, что add_in_db_media был вызван (может быть вызван несколько раз)
|
||||
assert mock_add_media.call_count >= 1
|
||||
mock_message.answer.assert_called_once()
|
||||
mock_state.set_state.assert_called_with("START")
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_suggest_router_media_group(self, mock_message, mock_state, mock_db):
|
||||
"""Тест обработки медиагруппы"""
|
||||
with patch('helper_bot.handlers.private.private_handlers.BotDB', mock_db):
|
||||
with patch('helper_bot.handlers.private.private_handlers.get_text_message') as mock_get_text:
|
||||
with patch('helper_bot.handlers.private.private_handlers.get_reply_keyboard_for_post') as mock_keyboard_post:
|
||||
with patch('helper_bot.handlers.private.private_handlers.get_reply_keyboard') as mock_keyboard:
|
||||
with patch('helper_bot.handlers.private.private_handlers.prepare_media_group_from_middlewares') as mock_prepare:
|
||||
with patch('helper_bot.handlers.private.private_handlers.send_media_group_message_to_private_chat') as mock_send_group:
|
||||
with patch('helper_bot.handlers.private.private_handlers.send_text_message') as mock_send_text:
|
||||
with patch('helper_bot.handlers.private.private_handlers.messages.get_message') as mock_messages:
|
||||
with patch('helper_bot.handlers.private.private_handlers.sleep'):
|
||||
# Настройка моков для медиагруппы
|
||||
mock_message.media_group_id = 'group_123'
|
||||
mock_message.content_type = 'photo'
|
||||
|
||||
# Создаем мок альбома
|
||||
album = [mock_message]
|
||||
album[0].caption = 'Подпись к медиагруппе'
|
||||
|
||||
mock_get_text.return_value = 'Обработанная подпись'
|
||||
mock_keyboard_post.return_value = Mock()
|
||||
mock_keyboard.return_value = Mock()
|
||||
mock_prepare.return_value = ['media1', 'media2']
|
||||
mock_send_group.return_value = 123
|
||||
mock_send_text.return_value = 456
|
||||
mock_messages.return_value = "Медиагруппа отправлена!"
|
||||
|
||||
# Выполнение теста
|
||||
await suggest_router(mock_message, mock_state, album)
|
||||
|
||||
# Проверки
|
||||
mock_get_text.assert_called_once()
|
||||
mock_prepare.assert_called_once()
|
||||
mock_send_group.assert_called_once()
|
||||
# Проверяем, что send_text_message был вызван (может быть вызван несколько раз)
|
||||
assert mock_send_text.call_count >= 1
|
||||
mock_db.update_helper_message_in_db.assert_called_once()
|
||||
mock_message.answer.assert_called_once()
|
||||
mock_state.set_state.assert_called_with("START")
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_suggest_router_unsupported_content(self, mock_message, mock_state):
|
||||
"""Тест обработки неподдерживаемого типа контента"""
|
||||
# Настройка моков для неподдерживаемого контента
|
||||
mock_message.content_type = 'document'
|
||||
mock_message.media_group_id = None
|
||||
|
||||
# Выполнение теста
|
||||
await suggest_router(mock_message, mock_state)
|
||||
|
||||
# Проверяем, что отправлено сообщение о неподдерживаемом типе
|
||||
mock_message.bot.send_message.assert_called_once()
|
||||
call_args = mock_message.bot.send_message.call_args
|
||||
# Проверяем текст сообщения (может быть в позиционных или именованных аргументах)
|
||||
text = call_args.kwargs.get('text', '') or (call_args[0][1] if len(call_args[0]) > 1 else '')
|
||||
assert 'не умею работать с таким сообщением' in text
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
pytest.main([__file__, '-v'])
|
||||
221
tests/test_refactored_admin_handlers.py
Normal file
221
tests/test_refactored_admin_handlers.py
Normal file
@@ -0,0 +1,221 @@
|
||||
import pytest
|
||||
from unittest.mock import Mock, AsyncMock, patch
|
||||
from aiogram import types
|
||||
from aiogram.fsm.context import FSMContext
|
||||
|
||||
from helper_bot.handlers.admin.services import AdminService, User, BannedUser
|
||||
from helper_bot.handlers.admin.exceptions import (
|
||||
UserNotFoundError,
|
||||
UserAlreadyBannedError,
|
||||
InvalidInputError
|
||||
)
|
||||
|
||||
|
||||
class TestAdminService:
|
||||
"""Тесты для AdminService"""
|
||||
|
||||
def setup_method(self):
|
||||
"""Настройка перед каждым тестом"""
|
||||
self.mock_db = Mock()
|
||||
self.admin_service = AdminService(self.mock_db)
|
||||
|
||||
def test_get_last_users_success(self):
|
||||
"""Тест успешного получения списка последних пользователей"""
|
||||
# Arrange
|
||||
# Формат данных: кортежи (full_name, user_id) как возвращает БД
|
||||
mock_users_data = [
|
||||
('User One', 1), # (full_name, user_id)
|
||||
('User Two', 2) # (full_name, user_id)
|
||||
]
|
||||
self.mock_db.get_last_users_from_db.return_value = mock_users_data
|
||||
|
||||
# Act
|
||||
result = self.admin_service.get_last_users()
|
||||
|
||||
# Assert
|
||||
assert len(result) == 2
|
||||
assert result[0].user_id == 1
|
||||
assert result[0].username == 'Неизвестно' # username не возвращается из БД
|
||||
assert result[0].full_name == 'User One'
|
||||
assert result[1].user_id == 2
|
||||
assert result[1].username == 'Неизвестно' # username не возвращается из БД
|
||||
assert result[1].full_name == 'User Two'
|
||||
|
||||
def test_get_user_by_username_success(self):
|
||||
"""Тест успешного получения пользователя по username"""
|
||||
# Arrange
|
||||
user_id = 123
|
||||
username = "test_user"
|
||||
full_name = "Test User"
|
||||
self.mock_db.get_user_id_by_username.return_value = user_id
|
||||
self.mock_db.get_full_name_by_id.return_value = full_name
|
||||
|
||||
# Act
|
||||
result = self.admin_service.get_user_by_username(username)
|
||||
|
||||
# Assert
|
||||
assert result is not None
|
||||
assert result.user_id == user_id
|
||||
assert result.username == username
|
||||
assert result.full_name == full_name
|
||||
|
||||
def test_get_user_by_username_not_found(self):
|
||||
"""Тест получения пользователя по несуществующему username"""
|
||||
# Arrange
|
||||
username = "nonexistent_user"
|
||||
self.mock_db.get_user_id_by_username.return_value = None
|
||||
|
||||
# Act
|
||||
result = self.admin_service.get_user_by_username(username)
|
||||
|
||||
# Assert
|
||||
assert result is None
|
||||
|
||||
def test_get_user_by_id_success(self):
|
||||
"""Тест успешного получения пользователя по ID"""
|
||||
# Arrange
|
||||
user_id = 123
|
||||
user_info = {'username': 'test_user', 'full_name': 'Test User'}
|
||||
self.mock_db.get_user_info_by_id.return_value = user_info
|
||||
|
||||
# Act
|
||||
result = self.admin_service.get_user_by_id(user_id)
|
||||
|
||||
# Assert
|
||||
assert result is not None
|
||||
assert result.user_id == user_id
|
||||
assert result.username == 'test_user'
|
||||
assert result.full_name == 'Test User'
|
||||
|
||||
def test_get_user_by_id_not_found(self):
|
||||
"""Тест получения пользователя по несуществующему ID"""
|
||||
# Arrange
|
||||
user_id = 999
|
||||
self.mock_db.get_user_info_by_id.return_value = None
|
||||
|
||||
# Act
|
||||
result = self.admin_service.get_user_by_id(user_id)
|
||||
|
||||
# Assert
|
||||
assert result is None
|
||||
|
||||
def test_validate_user_input_success(self):
|
||||
"""Тест успешной валидации ID пользователя"""
|
||||
# Act
|
||||
result = self.admin_service.validate_user_input("123")
|
||||
|
||||
# Assert
|
||||
assert result == 123
|
||||
|
||||
def test_validate_user_input_invalid_number(self):
|
||||
"""Тест валидации некорректного ID"""
|
||||
# Act & Assert
|
||||
with pytest.raises(InvalidInputError, match="ID пользователя должен быть числом"):
|
||||
self.admin_service.validate_user_input("abc")
|
||||
|
||||
def test_validate_user_input_negative_number(self):
|
||||
"""Тест валидации отрицательного ID"""
|
||||
# Act & Assert
|
||||
with pytest.raises(InvalidInputError, match="ID пользователя должен быть положительным числом"):
|
||||
self.admin_service.validate_user_input("-1")
|
||||
|
||||
def test_validate_user_input_zero(self):
|
||||
"""Тест валидации нулевого ID"""
|
||||
# Act & Assert
|
||||
with pytest.raises(InvalidInputError, match="ID пользователя должен быть положительным числом"):
|
||||
self.admin_service.validate_user_input("0")
|
||||
|
||||
def test_ban_user_success(self):
|
||||
"""Тест успешной блокировки пользователя"""
|
||||
# Arrange
|
||||
user_id = 123
|
||||
username = "test_user"
|
||||
reason = "Test ban"
|
||||
ban_days = 7
|
||||
|
||||
self.mock_db.check_user_in_blacklist.return_value = False
|
||||
self.mock_db.set_user_blacklist.return_value = None
|
||||
|
||||
# Act
|
||||
self.admin_service.ban_user(user_id, username, reason, ban_days)
|
||||
|
||||
# Assert
|
||||
self.mock_db.check_user_in_blacklist.assert_called_once_with(user_id)
|
||||
self.mock_db.set_user_blacklist.assert_called_once()
|
||||
|
||||
def test_ban_user_already_banned(self):
|
||||
"""Тест попытки заблокировать уже заблокированного пользователя"""
|
||||
# Arrange
|
||||
user_id = 123
|
||||
username = "test_user"
|
||||
reason = "Test ban"
|
||||
ban_days = 7
|
||||
|
||||
self.mock_db.check_user_in_blacklist.return_value = True
|
||||
|
||||
# Act & Assert
|
||||
with pytest.raises(UserAlreadyBannedError, match=f"Пользователь {user_id} уже заблокирован"):
|
||||
self.admin_service.ban_user(user_id, username, reason, ban_days)
|
||||
|
||||
def test_ban_user_permanent(self):
|
||||
"""Тест постоянной блокировки пользователя"""
|
||||
# Arrange
|
||||
user_id = 123
|
||||
username = "test_user"
|
||||
reason = "Permanent ban"
|
||||
ban_days = None
|
||||
|
||||
self.mock_db.check_user_in_blacklist.return_value = False
|
||||
self.mock_db.set_user_blacklist.return_value = None
|
||||
|
||||
# Act
|
||||
self.admin_service.ban_user(user_id, username, reason, ban_days)
|
||||
|
||||
# Assert
|
||||
self.mock_db.set_user_blacklist.assert_called_once_with(user_id, username, reason, None)
|
||||
|
||||
def test_unban_user_success(self):
|
||||
"""Тест успешной разблокировки пользователя"""
|
||||
# Arrange
|
||||
user_id = 123
|
||||
self.mock_db.delete_user_blacklist.return_value = None
|
||||
|
||||
# Act
|
||||
self.admin_service.unban_user(user_id)
|
||||
|
||||
# Assert
|
||||
self.mock_db.delete_user_blacklist.assert_called_once_with(user_id)
|
||||
|
||||
|
||||
class TestUser:
|
||||
"""Тесты для модели User"""
|
||||
|
||||
def test_user_creation(self):
|
||||
"""Тест создания объекта User"""
|
||||
# Act
|
||||
user = User(user_id=123, username="test_user", full_name="Test User")
|
||||
|
||||
# Assert
|
||||
assert user.user_id == 123
|
||||
assert user.username == "test_user"
|
||||
assert user.full_name == "Test User"
|
||||
|
||||
|
||||
class TestBannedUser:
|
||||
"""Тесты для модели BannedUser"""
|
||||
|
||||
def test_banned_user_creation(self):
|
||||
"""Тест создания объекта BannedUser"""
|
||||
# Act
|
||||
banned_user = BannedUser(
|
||||
user_id=123,
|
||||
username="test_user",
|
||||
reason="Test ban",
|
||||
unban_date="2025-01-01"
|
||||
)
|
||||
|
||||
# Assert
|
||||
assert banned_user.user_id == 123
|
||||
assert banned_user.username == "test_user"
|
||||
assert banned_user.reason == "Test ban"
|
||||
assert banned_user.unban_date == "2025-01-01"
|
||||
189
tests/test_refactored_group_handlers.py
Normal file
189
tests/test_refactored_group_handlers.py
Normal file
@@ -0,0 +1,189 @@
|
||||
"""Tests for refactored group handlers"""
|
||||
|
||||
import pytest
|
||||
from unittest.mock import Mock, AsyncMock, MagicMock
|
||||
from aiogram import types
|
||||
from aiogram.fsm.context import FSMContext
|
||||
|
||||
from helper_bot.handlers.group.group_handlers import (
|
||||
create_group_handlers, GroupHandlers
|
||||
)
|
||||
from helper_bot.handlers.group.services import AdminReplyService
|
||||
from helper_bot.handlers.group.exceptions import NoReplyToMessageError, UserNotFoundError
|
||||
from helper_bot.handlers.group.constants import FSM_STATES, ERROR_MESSAGES
|
||||
|
||||
|
||||
class TestGroupHandlers:
|
||||
"""Test class for GroupHandlers"""
|
||||
|
||||
@pytest.fixture
|
||||
def mock_db(self):
|
||||
"""Mock database"""
|
||||
db = Mock()
|
||||
db.get_user_by_message_id = Mock()
|
||||
return db
|
||||
|
||||
@pytest.fixture
|
||||
def mock_keyboard_markup(self):
|
||||
"""Mock keyboard markup"""
|
||||
return Mock()
|
||||
|
||||
@pytest.fixture
|
||||
def mock_message(self):
|
||||
"""Mock Telegram message"""
|
||||
message = Mock()
|
||||
message.from_user = Mock()
|
||||
message.from_user.id = 12345
|
||||
message.from_user.full_name = "Test Admin"
|
||||
message.text = "test reply message"
|
||||
message.chat = Mock()
|
||||
message.chat.title = "Test Group"
|
||||
message.chat.id = 67890
|
||||
message.message_id = 111
|
||||
message.answer = AsyncMock()
|
||||
message.bot = Mock()
|
||||
message.bot.send_message = AsyncMock()
|
||||
return message
|
||||
|
||||
@pytest.fixture
|
||||
def mock_reply_message(self, mock_message):
|
||||
"""Mock reply message"""
|
||||
reply_message = Mock()
|
||||
reply_message.message_id = 222
|
||||
mock_message.reply_to_message = reply_message
|
||||
return mock_message
|
||||
|
||||
@pytest.fixture
|
||||
def mock_state(self):
|
||||
"""Mock FSM state"""
|
||||
state = Mock(spec=FSMContext)
|
||||
state.set_state = AsyncMock()
|
||||
return state
|
||||
|
||||
def test_create_group_handlers(self, mock_db, mock_keyboard_markup):
|
||||
"""Test creating group handlers instance"""
|
||||
handlers = create_group_handlers(mock_db, mock_keyboard_markup)
|
||||
assert isinstance(handlers, GroupHandlers)
|
||||
assert handlers.db == mock_db
|
||||
assert handlers.keyboard_markup == mock_keyboard_markup
|
||||
|
||||
def test_group_handlers_initialization(self, mock_db, mock_keyboard_markup):
|
||||
"""Test GroupHandlers initialization"""
|
||||
handlers = GroupHandlers(mock_db, mock_keyboard_markup)
|
||||
assert handlers.db == mock_db
|
||||
assert handlers.keyboard_markup == mock_keyboard_markup
|
||||
assert handlers.admin_reply_service is not None
|
||||
assert handlers.router is not None
|
||||
|
||||
async def test_handle_message_success(self, mock_db, mock_keyboard_markup, mock_reply_message, mock_state):
|
||||
"""Test successful message handling"""
|
||||
mock_db.get_user_by_message_id.return_value = 99999
|
||||
|
||||
handlers = create_group_handlers(mock_db, mock_keyboard_markup)
|
||||
|
||||
# Mock the send_reply_to_user method
|
||||
handlers.admin_reply_service.send_reply_to_user = AsyncMock()
|
||||
|
||||
await handlers.handle_message(mock_reply_message, mock_state)
|
||||
|
||||
# Verify database call
|
||||
mock_db.get_user_by_message_id.assert_called_once_with(222)
|
||||
|
||||
# Verify service call
|
||||
handlers.admin_reply_service.send_reply_to_user.assert_called_once_with(
|
||||
99999, mock_reply_message, "test reply message", mock_keyboard_markup
|
||||
)
|
||||
|
||||
# Verify state was set
|
||||
mock_state.set_state.assert_called_once_with(FSM_STATES["CHAT"])
|
||||
|
||||
async def test_handle_message_no_reply(self, mock_db, mock_keyboard_markup, mock_message, mock_state):
|
||||
"""Test message handling without reply"""
|
||||
handlers = create_group_handlers(mock_db, mock_keyboard_markup)
|
||||
|
||||
# Mock the send_reply_to_user method to prevent it from being called
|
||||
handlers.admin_reply_service.send_reply_to_user = AsyncMock()
|
||||
|
||||
# Ensure reply_to_message is None
|
||||
mock_message.reply_to_message = None
|
||||
|
||||
await handlers.handle_message(mock_message, mock_state)
|
||||
|
||||
# Verify error message was sent
|
||||
mock_message.answer.assert_called_once_with(ERROR_MESSAGES["NO_REPLY_TO_MESSAGE"])
|
||||
|
||||
# Verify no database calls
|
||||
mock_db.get_user_by_message_id.assert_not_called()
|
||||
|
||||
# Verify send_reply_to_user was not called
|
||||
handlers.admin_reply_service.send_reply_to_user.assert_not_called()
|
||||
|
||||
# Verify state was not set
|
||||
mock_state.set_state.assert_not_called()
|
||||
|
||||
async def test_handle_message_user_not_found(self, mock_db, mock_keyboard_markup, mock_reply_message, mock_state):
|
||||
"""Test message handling when user is not found"""
|
||||
mock_db.get_user_by_message_id.return_value = None
|
||||
|
||||
handlers = create_group_handlers(mock_db, mock_keyboard_markup)
|
||||
|
||||
await handlers.handle_message(mock_reply_message, mock_state)
|
||||
|
||||
# Verify error message was sent
|
||||
mock_reply_message.answer.assert_called_once_with(ERROR_MESSAGES["USER_NOT_FOUND"])
|
||||
|
||||
# Verify database call
|
||||
mock_db.get_user_by_message_id.assert_called_once_with(222)
|
||||
|
||||
# Verify state was not set
|
||||
mock_state.set_state.assert_not_called()
|
||||
|
||||
|
||||
class TestAdminReplyService:
|
||||
"""Test class for AdminReplyService"""
|
||||
|
||||
@pytest.fixture
|
||||
def mock_db(self):
|
||||
"""Mock database"""
|
||||
db = Mock()
|
||||
db.get_user_by_message_id = Mock()
|
||||
return db
|
||||
|
||||
@pytest.fixture
|
||||
def service(self, mock_db):
|
||||
"""Create service instance"""
|
||||
return AdminReplyService(mock_db)
|
||||
|
||||
def test_get_user_id_for_reply_success(self, service, mock_db):
|
||||
"""Test successful user ID retrieval"""
|
||||
mock_db.get_user_by_message_id.return_value = 12345
|
||||
|
||||
result = service.get_user_id_for_reply(111)
|
||||
|
||||
assert result == 12345
|
||||
mock_db.get_user_by_message_id.assert_called_once_with(111)
|
||||
|
||||
def test_get_user_id_for_reply_not_found(self, service, mock_db):
|
||||
"""Test user ID retrieval when user not found"""
|
||||
mock_db.get_user_by_message_id.return_value = None
|
||||
|
||||
with pytest.raises(UserNotFoundError, match="User not found for message_id: 111"):
|
||||
service.get_user_id_for_reply(111)
|
||||
|
||||
mock_db.get_user_by_message_id.assert_called_once_with(111)
|
||||
|
||||
async def test_send_reply_to_user(self, service, mock_db):
|
||||
"""Test sending reply to user"""
|
||||
message = Mock()
|
||||
message.reply_to_message = Mock()
|
||||
message.reply_to_message.message_id = 222
|
||||
markup = Mock()
|
||||
|
||||
# Mock the send_text_message function
|
||||
with pytest.MonkeyPatch().context() as m:
|
||||
mock_send_text = AsyncMock()
|
||||
m.setattr('helper_bot.handlers.group.services.send_text_message', mock_send_text)
|
||||
|
||||
await service.send_reply_to_user(12345, message, "test reply", markup)
|
||||
|
||||
mock_send_text.assert_called_once_with(12345, message, "test reply", markup)
|
||||
@@ -46,13 +46,22 @@ class TestPrivateHandlers:
|
||||
def mock_message(self):
|
||||
"""Mock Telegram message"""
|
||||
message = Mock(spec=types.Message)
|
||||
message.from_user.id = 12345
|
||||
message.from_user.full_name = "Test User"
|
||||
message.from_user.username = "testuser"
|
||||
message.from_user.is_bot = False
|
||||
message.from_user.language_code = "ru"
|
||||
# Создаем мок для from_user
|
||||
from_user = Mock()
|
||||
from_user.id = 12345
|
||||
from_user.full_name = "Test User"
|
||||
from_user.username = "testuser"
|
||||
from_user.is_bot = False
|
||||
from_user.language_code = "ru"
|
||||
message.from_user = from_user
|
||||
|
||||
message.text = "test message"
|
||||
message.chat.id = 12345
|
||||
|
||||
# Создаем мок для chat
|
||||
chat = Mock()
|
||||
chat.id = 12345
|
||||
message.chat = chat
|
||||
|
||||
message.bot = Mock()
|
||||
message.bot.send_message = AsyncMock()
|
||||
message.forward = AsyncMock()
|
||||
|
||||
Reference in New Issue
Block a user