# Импортируем моки в самом начале 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'])