Files
telegram-helper-bot/tests/test_callback_handlers.py
Andrey fc0517c011 Enhance bot functionality with new features and improvements
- Added a new `/status` endpoint in `server_prometheus.py` to provide process status information, including uptime and resource usage metrics.
- Implemented a PID manager in `run_helper.py` to track the bot's process, improving monitoring capabilities.
- Introduced a method to delete audio moderation records in `audio_repository.py`, enhancing database management.
- Updated voice message handling in callback handlers to ensure proper deletion of audio moderation records.
- Improved error handling and logging in various services, ensuring better tracking of media processing and file downloads.
- Refactored media handling functions to streamline operations and improve code readability.
- Enhanced metrics tracking for file downloads and media processing, providing better insights into bot performance.
2025-09-04 00:46:45 +03:00

298 lines
15 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import pytest
from unittest.mock import Mock, AsyncMock, patch, MagicMock
from datetime import datetime
import time
from helper_bot.handlers.callback.callback_handlers import (
save_voice_message,
delete_voice_message
)
from helper_bot.handlers.voice.constants import CALLBACK_SAVE, CALLBACK_DELETE
@pytest.fixture
def mock_call():
"""Мок для CallbackQuery"""
call = Mock()
call.message = Mock()
call.message.message_id = 12345
call.message.voice = Mock()
call.message.voice.file_id = "test_file_id_123"
call.bot = Mock()
call.bot.delete_message = AsyncMock()
call.answer = AsyncMock()
return call
@pytest.fixture
def mock_bot_db():
"""Мок для базы данных"""
mock_db = Mock()
mock_db.get_user_id_by_message_id_for_voice_bot = AsyncMock(return_value=67890)
mock_db.delete_audio_moderate_record = AsyncMock()
return mock_db
@pytest.fixture
def mock_settings():
"""Мок для настроек"""
return {
'Telegram': {
'group_for_posts': 'test_group_id'
}
}
@pytest.fixture
def mock_audio_service():
"""Мок для AudioFileService"""
mock_service = Mock()
mock_service.generate_file_name = AsyncMock(return_value="message_from_67890_number_1")
mock_service.save_audio_file = AsyncMock()
mock_service.download_and_save_audio = AsyncMock()
return mock_service
class TestSaveVoiceMessage:
"""Тесты для функции save_voice_message"""
@pytest.mark.asyncio
async def test_save_voice_message_success(self, mock_call, mock_bot_db, mock_settings, mock_audio_service):
"""Тест успешного сохранения голосового сообщения"""
with patch('helper_bot.handlers.callback.callback_handlers.AudioFileService') as mock_service_class:
mock_service_class.return_value = mock_audio_service
await save_voice_message(mock_call, bot_db=mock_bot_db, settings=mock_settings)
# Проверяем, что все методы вызваны
mock_bot_db.get_user_id_by_message_id_for_voice_bot.assert_called_once_with(12345)
mock_audio_service.generate_file_name.assert_called_once_with(67890)
mock_audio_service.save_audio_file.assert_called_once()
mock_audio_service.download_and_save_audio.assert_called_once_with(
mock_call.bot, mock_call.message, "message_from_67890_number_1"
)
# Проверяем удаление сообщения из чата
mock_call.bot.delete_message.assert_called_once_with(
chat_id='test_group_id',
message_id=12345
)
# Проверяем удаление записи из audio_moderate
mock_bot_db.delete_audio_moderate_record.assert_called_once_with(12345)
# Проверяем ответ пользователю
mock_call.answer.assert_called_once_with(text='Сохранено!', cache_time=3)
@pytest.mark.asyncio
async def test_save_voice_message_with_correct_parameters(self, mock_call, mock_bot_db, mock_settings, mock_audio_service):
"""Тест сохранения с правильными параметрами"""
with patch('helper_bot.handlers.callback.callback_handlers.AudioFileService') as mock_service_class:
mock_service_class.return_value = mock_audio_service
await save_voice_message(mock_call, bot_db=mock_bot_db, settings=mock_settings)
# Проверяем параметры save_audio_file
save_call_args = mock_audio_service.save_audio_file.call_args
assert save_call_args[0][0] == "message_from_67890_number_1" # file_name
assert save_call_args[0][1] == 67890 # user_id
assert isinstance(save_call_args[0][2], datetime) # date_added
assert save_call_args[0][3] == "test_file_id_123" # file_id
@pytest.mark.asyncio
async def test_save_voice_message_exception_handling(self, mock_call, mock_bot_db, mock_settings):
"""Тест обработки исключений при сохранении"""
mock_bot_db.get_user_id_by_message_id_for_voice_bot.side_effect = Exception("Database error")
await save_voice_message(mock_call, bot_db=mock_bot_db, settings=mock_settings)
# Проверяем, что при ошибке отправляется соответствующий ответ
mock_call.answer.assert_called_once_with(text='Ошибка при сохранении!', cache_time=3)
@pytest.mark.asyncio
async def test_save_voice_message_audio_service_exception(self, mock_call, mock_bot_db, mock_settings, mock_audio_service):
"""Тест обработки исключений в AudioFileService"""
mock_audio_service.save_audio_file.side_effect = Exception("Save error")
with patch('helper_bot.handlers.callback.callback_handlers.AudioFileService') as mock_service_class:
mock_service_class.return_value = mock_audio_service
await save_voice_message(mock_call, bot_db=mock_bot_db, settings=mock_settings)
# Проверяем, что при ошибке отправляется соответствующий ответ
mock_call.answer.assert_called_once_with(text='Ошибка при сохранении!', cache_time=3)
@pytest.mark.asyncio
async def test_save_voice_message_download_exception(self, mock_call, mock_bot_db, mock_settings, mock_audio_service):
"""Тест обработки исключений при скачивании файла"""
mock_audio_service.download_and_save_audio.side_effect = Exception("Download error")
with patch('helper_bot.handlers.callback.callback_handlers.AudioFileService') as mock_service_class:
mock_service_class.return_value = mock_audio_service
await save_voice_message(mock_call, bot_db=mock_bot_db, settings=mock_settings)
# Проверяем, что при ошибке отправляется соответствующий ответ
mock_call.answer.assert_called_once_with(text='Ошибка при сохранении!', cache_time=3)
class TestDeleteVoiceMessage:
"""Тесты для функции delete_voice_message"""
@pytest.mark.asyncio
async def test_delete_voice_message_success(self, mock_call, mock_bot_db, mock_settings):
"""Тест успешного удаления голосового сообщения"""
await delete_voice_message(mock_call, bot_db=mock_bot_db, settings=mock_settings)
# Проверяем удаление сообщения из чата
mock_call.bot.delete_message.assert_called_once_with(
chat_id='test_group_id',
message_id=12345
)
# Проверяем удаление записи из audio_moderate
mock_bot_db.delete_audio_moderate_record.assert_called_once_with(12345)
# Проверяем ответ пользователю
mock_call.answer.assert_called_once_with(text='Удалено!', cache_time=3)
@pytest.mark.asyncio
async def test_delete_voice_message_exception_handling(self, mock_call, mock_bot_db, mock_settings):
"""Тест обработки исключений при удалении"""
mock_call.bot.delete_message.side_effect = Exception("Delete error")
await delete_voice_message(mock_call, bot_db=mock_bot_db, settings=mock_settings)
# Проверяем, что при ошибке отправляется соответствующий ответ
mock_call.answer.assert_called_once_with(text='Ошибка при удалении!', cache_time=3)
@pytest.mark.asyncio
async def test_delete_voice_message_database_exception(self, mock_call, mock_bot_db, mock_settings):
"""Тест обработки исключений в базе данных при удалении"""
mock_bot_db.delete_audio_moderate_record.side_effect = Exception("Database error")
await delete_voice_message(mock_call, bot_db=mock_bot_db, settings=mock_settings)
# Проверяем, что при ошибке отправляется соответствующий ответ
mock_call.answer.assert_called_once_with(text='Ошибка при удалении!', cache_time=3)
class TestCallbackHandlersIntegration:
"""Интеграционные тесты для callback handlers"""
@pytest.mark.asyncio
async def test_save_voice_message_full_workflow(self, mock_call, mock_bot_db, mock_settings):
"""Тест полного рабочего процесса сохранения"""
with patch('helper_bot.handlers.callback.callback_handlers.AudioFileService') as mock_service_class:
mock_service = Mock()
mock_service.generate_file_name = AsyncMock(return_value="message_from_67890_number_1")
mock_service.save_audio_file = AsyncMock()
mock_service.download_and_save_audio = AsyncMock()
mock_service_class.return_value = mock_service
await save_voice_message(mock_call, bot_db=mock_bot_db, settings=mock_settings)
# Проверяем последовательность вызовов
assert mock_bot_db.get_user_id_by_message_id_for_voice_bot.called
assert mock_service.generate_file_name.called
assert mock_service.save_audio_file.called
assert mock_service.download_and_save_audio.called
assert mock_call.bot.delete_message.called
assert mock_bot_db.delete_audio_moderate_record.called
assert mock_call.answer.called
@pytest.mark.asyncio
async def test_delete_voice_message_full_workflow(self, mock_call, mock_bot_db, mock_settings):
"""Тест полного рабочего процесса удаления"""
await delete_voice_message(mock_call, bot_db=mock_bot_db, settings=mock_settings)
# Проверяем последовательность вызовов
assert mock_call.bot.delete_message.called
assert mock_bot_db.delete_audio_moderate_record.called
assert mock_call.answer.called
@pytest.mark.asyncio
async def test_audio_moderate_cleanup_consistency(self, mock_call, mock_bot_db, mock_settings):
"""Тест консистентности очистки audio_moderate"""
# Тестируем, что в обоих случаях (сохранение и удаление)
# вызывается delete_audio_moderate_record
# Создаем отдельные моки для каждого теста
mock_bot_db_save = Mock()
mock_bot_db_save.get_user_id_by_message_id_for_voice_bot = AsyncMock(return_value=67890)
mock_bot_db_save.delete_audio_moderate_record = AsyncMock()
mock_bot_db_delete = Mock()
mock_bot_db_delete.delete_audio_moderate_record = AsyncMock()
# Тест для сохранения
with patch('helper_bot.handlers.callback.callback_handlers.AudioFileService') as mock_service_class:
mock_service = Mock()
mock_service.generate_file_name = AsyncMock(return_value="message_from_67890_number_1")
mock_service.save_audio_file = AsyncMock()
mock_service.download_and_save_audio = AsyncMock()
mock_service_class.return_value = mock_service
await save_voice_message(mock_call, bot_db=mock_bot_db_save, settings=mock_settings)
save_calls = mock_bot_db_save.delete_audio_moderate_record.call_count
# Тест для удаления
await delete_voice_message(mock_call, bot_db=mock_bot_db_delete, settings=mock_settings)
delete_calls = mock_bot_db_delete.delete_audio_moderate_record.call_count
# Проверяем, что в обоих случаях вызывается очистка
assert save_calls == 1
assert delete_calls == 1
class TestCallbackHandlersEdgeCases:
"""Тесты граничных случаев для callback handlers"""
@pytest.mark.asyncio
async def test_save_voice_message_no_voice_attribute(self, mock_bot_db, mock_settings):
"""Тест сохранения когда у сообщения нет voice атрибута"""
call = Mock()
call.message = Mock()
call.message.message_id = 12345
call.message.voice = None # Нет голосового сообщения
call.bot = Mock()
call.bot.delete_message = AsyncMock()
call.answer = AsyncMock()
with patch('helper_bot.handlers.callback.callback_handlers.AudioFileService'):
await save_voice_message(call, bot_db=mock_bot_db, settings=mock_settings)
# Должна быть ошибка
call.answer.assert_called_once_with(text='Ошибка при сохранении!', cache_time=3)
@pytest.mark.asyncio
async def test_save_voice_message_user_not_found(self, mock_call, mock_bot_db, mock_settings):
"""Тест сохранения когда пользователь не найден"""
mock_bot_db.get_user_id_by_message_id_for_voice_bot.return_value = None
with patch('helper_bot.handlers.callback.callback_handlers.AudioFileService'):
await save_voice_message(mock_call, bot_db=mock_bot_db, settings=mock_settings)
# Должна быть ошибка
mock_call.answer.assert_called_once_with(text='Ошибка при сохранении!', cache_time=3)
@pytest.mark.asyncio
async def test_delete_voice_message_with_different_message_id(self, mock_bot_db, mock_settings):
"""Тест удаления с другим message_id"""
call = Mock()
call.message = Mock()
call.message.message_id = 99999 # Другой ID
call.bot = Mock()
call.bot.delete_message = AsyncMock()
call.answer = AsyncMock()
await delete_voice_message(call, bot_db=mock_bot_db, settings=mock_settings)
# Проверяем, что используется правильный message_id
call.bot.delete_message.assert_called_once_with(
chat_id='test_group_id',
message_id=99999
)
mock_bot_db.delete_audio_moderate_record.assert_called_once_with(99999)
if __name__ == '__main__':
pytest.main([__file__])