Files
telegram-helper-bot/tests/test_voice_handler.py
Andrey a5faa4bdc6 Переписал почти все тесты
feat: улучшено логирование и обработка скорингов в PostService и RagApiClient

- Добавлены отладочные сообщения для передачи скорингов в функции обработки постов.
- Обновлено логирование успешного получения скорингов из RAG API с дополнительной информацией.
- Оптимизирована обработка скорингов в функции get_text_message для улучшения отладки.
- Обновлены тесты для проверки новых функциональных возможностей и обработки ошибок.
2026-01-30 00:55:47 +03:00

233 lines
14 KiB
Python

from unittest.mock import AsyncMock, MagicMock, Mock, patch
import pytest
from aiogram import types
from aiogram.fsm.context import FSMContext
from helper_bot.handlers.voice.constants import (STATE_STANDUP_WRITE,
STATE_START)
from helper_bot.handlers.voice.voice_handler import VoiceHandlers
class TestVoiceHandler:
"""Тесты для VoiceHandler"""
@pytest.fixture
def mock_db(self):
"""Мок для базы данных"""
mock_db = Mock()
mock_db.settings = {
'Telegram': {
'group_for_logs': 'test_logs_chat',
'group_for_posts': 'test_posts_chat',
'preview_link': True
}
}
return mock_db
@pytest.fixture
def mock_settings(self):
"""Мок для настроек"""
return {
'Telegram': {
'group_for_logs': 'test_logs_chat',
'group_for_posts': 'test_posts_chat',
'preview_link': True
}
}
@pytest.fixture
def mock_message(self):
"""Мок для сообщения"""
message = Mock(spec=types.Message)
message.from_user = Mock()
message.from_user.id = 123
message.from_user.first_name = "Test"
message.from_user.full_name = "Test User"
message.chat = Mock()
message.chat.id = 456
message.answer = AsyncMock()
message.forward = AsyncMock()
return message
@pytest.fixture
def mock_state(self):
"""Мок для состояния FSM"""
state = Mock(spec=FSMContext)
state.set_state = AsyncMock()
return state
@pytest.fixture
def voice_handler(self, mock_db, mock_settings):
"""Экземпляр VoiceHandler для тестов"""
return VoiceHandlers(mock_db, mock_settings)
@pytest.mark.asyncio
async def test_voice_bot_button_handler_welcome_received(self, voice_handler, mock_message, mock_state, mock_db, mock_settings):
"""Тест обработчика кнопки когда приветствие уже получено"""
from unittest.mock import AsyncMock
mock_db.check_voice_bot_welcome_received = AsyncMock(return_value=True)
with patch.object(voice_handler, 'restart_function') as mock_restart:
with patch('helper_bot.handlers.voice.voice_handler.update_user_info') as mock_update_user:
mock_update_user.return_value = None
await voice_handler.voice_bot_button_handler(mock_message, mock_state, mock_db, mock_settings)
mock_db.check_voice_bot_welcome_received.assert_called_once_with(123)
mock_restart.assert_called_once_with(mock_message, mock_state, mock_db, mock_settings)
@pytest.mark.asyncio
async def test_voice_bot_button_handler_welcome_not_received(self, voice_handler, mock_message, mock_state, mock_db, mock_settings):
"""Тест обработчика кнопки когда приветствие не получено"""
from unittest.mock import AsyncMock
mock_db.check_voice_bot_welcome_received = AsyncMock(return_value=False)
with patch.object(voice_handler, 'start') as mock_start:
await voice_handler.voice_bot_button_handler(mock_message, mock_state, mock_db, mock_settings)
mock_db.check_voice_bot_welcome_received.assert_called_once_with(123)
mock_start.assert_called_once_with(mock_message, mock_state, mock_db, mock_settings)
@pytest.mark.asyncio
async def test_voice_bot_button_handler_exception(self, voice_handler, mock_message, mock_state, mock_db, mock_settings):
"""Тест обработчика кнопки при исключении"""
mock_db.check_voice_bot_welcome_received.side_effect = Exception("Test error")
with patch.object(voice_handler, 'start') as mock_start:
await voice_handler.voice_bot_button_handler(mock_message, mock_state, mock_db, mock_settings)
mock_start.assert_called_once_with(mock_message, mock_state, mock_db, mock_settings)
# Упрощенные тесты для основных функций
@pytest.mark.asyncio
async def test_standup_write(self, voice_handler, mock_message, mock_state, mock_db, mock_settings):
"""Тест функции standup_write"""
with patch('helper_bot.handlers.voice.voice_handler.messages.get_message') as mock_get_message:
mock_get_message.return_value = "Record voice message"
await voice_handler.standup_write(mock_message, mock_state, mock_db, mock_settings)
mock_state.set_state.assert_called_once_with(STATE_STANDUP_WRITE)
mock_message.answer.assert_called_once_with(
text="Record voice message",
reply_markup=types.ReplyKeyboardRemove()
)
def test_setup_handlers(self, voice_handler):
"""Тест настройки обработчиков"""
# Проверяем, что роутер содержит обработчики
assert len(voice_handler.router.message.handlers) > 0
@pytest.mark.asyncio
async def test_restart_function_sets_state_and_answers(self, voice_handler, mock_message, mock_state, mock_db, mock_settings):
"""restart_function пересылает в логи, обновляет инфо и отправляет клавиатуру."""
with patch('helper_bot.handlers.voice.voice_handler.update_user_info', new_callable=AsyncMock):
with patch('helper_bot.handlers.voice.voice_handler.check_user_emoji', new_callable=AsyncMock):
with patch('helper_bot.handlers.voice.voice_handler.get_main_keyboard') as mock_keyboard:
mock_keyboard.return_value = MagicMock()
await voice_handler.restart_function(mock_message, mock_state, mock_db, mock_settings)
mock_message.forward.assert_awaited_once_with(chat_id=mock_settings['Telegram']['group_for_logs'])
mock_state.set_state.assert_called_once_with(STATE_START)
mock_message.answer.assert_called_once()
assert 'Записывайся' in mock_message.answer.call_args[1]['text'] or 'слушай' in mock_message.answer.call_args[1]['text']
@pytest.mark.asyncio
async def test_start_sets_state_and_sends_welcome(self, voice_handler, mock_message, mock_state, mock_db, mock_settings):
"""start устанавливает состояние и отправляет приветствие через VoiceBotService."""
mock_db.mark_voice_bot_welcome_received = AsyncMock()
with patch('helper_bot.handlers.voice.voice_handler.update_user_info', new_callable=AsyncMock):
with patch('helper_bot.handlers.voice.voice_handler.get_user_emoji_safe', new_callable=AsyncMock, return_value='😊'):
with patch('helper_bot.handlers.voice.voice_handler.VoiceBotService') as mock_svc_cls:
mock_svc = MagicMock()
mock_svc.send_welcome_messages = AsyncMock()
mock_svc_cls.return_value = mock_svc
await voice_handler.start(mock_message, mock_state, mock_db, mock_settings)
mock_state.set_state.assert_called_once_with(STATE_START)
mock_svc.send_welcome_messages.assert_awaited_once()
mock_db.mark_voice_bot_welcome_received.assert_awaited_once_with(123)
@pytest.mark.asyncio
async def test_help_function_answers_help_message(self, voice_handler, mock_message, mock_state, mock_settings):
"""help_function пересылает в логи и отправляет HELP_MESSAGE."""
with patch('helper_bot.handlers.voice.voice_handler.update_user_info', new_callable=AsyncMock):
with patch('helper_bot.handlers.voice.voice_handler.messages.get_message') as mock_get:
mock_get.return_value = "Help text"
await voice_handler.help_function(mock_message, mock_state, mock_settings)
mock_message.forward.assert_awaited_once_with(chat_id=mock_settings['Telegram']['group_for_logs'])
mock_message.answer.assert_called_once_with(text="Help text", disable_web_page_preview=not mock_settings['Telegram']['preview_link'])
mock_state.set_state.assert_called_once_with(STATE_START)
@pytest.mark.asyncio
async def test_cancel_handler_returns_to_menu(self, voice_handler, mock_message, mock_state, mock_db, mock_settings):
"""cancel_handler пересылает в логи и возвращает в главное меню."""
with patch('helper_bot.handlers.voice.voice_handler.update_user_info', new_callable=AsyncMock):
with patch('helper_bot.handlers.voice.voice_handler.get_reply_keyboard', new_callable=AsyncMock) as mock_kb:
mock_kb.return_value = MagicMock()
await voice_handler.cancel_handler(mock_message, mock_state, mock_db, mock_settings)
mock_message.forward.assert_awaited_once()
mock_message.answer.assert_called_once()
assert 'Добро пожаловать' in mock_message.answer.call_args[1]['text'] or 'меню' in mock_message.answer.call_args[1]['text']
mock_state.set_state.assert_called_once()
@pytest.mark.asyncio
async def test_refresh_listen_function_clears_listenings(self, voice_handler, mock_message, mock_state, mock_db, mock_settings):
"""refresh_listen_function очищает прослушивания и отправляет сообщение."""
with patch('helper_bot.handlers.voice.voice_handler.update_user_info', new_callable=AsyncMock):
with patch('helper_bot.handlers.voice.voice_handler.get_main_keyboard') as mock_keyboard:
with patch('helper_bot.handlers.voice.voice_handler.VoiceBotService') as mock_svc_cls:
mock_svc = MagicMock()
mock_svc.clear_user_listenings = AsyncMock()
mock_svc_cls.return_value = mock_svc
with patch('helper_bot.handlers.voice.voice_handler.messages.get_message') as mock_get:
mock_get.return_value = "Прослушивания сброшены"
await voice_handler.refresh_listen_function(mock_message, mock_state, mock_db, mock_settings)
mock_svc.clear_user_listenings.assert_awaited_once_with(123)
mock_message.answer.assert_called_once()
mock_state.set_state.assert_called_once_with(STATE_START)
@pytest.mark.asyncio
async def test_suggest_voice_valid_sends_to_group_and_saves(self, voice_handler, mock_message, mock_state, mock_db, mock_settings):
"""suggest_voice при валидном голосовом отправляет в группу и сохраняет message_id."""
mock_message.voice = MagicMock()
mock_message.voice.file_id = "voice_123"
with patch('helper_bot.handlers.voice.voice_handler.validate_voice_message', new_callable=AsyncMock, return_value=True):
with patch('helper_bot.handlers.voice.voice_handler.send_voice_message', new_callable=AsyncMock) as mock_send:
sent = MagicMock()
sent.message_id = 999
mock_send.return_value = sent
mock_db.set_user_id_and_message_id_for_voice_bot = AsyncMock()
with patch('helper_bot.handlers.voice.voice_handler.get_reply_keyboard_for_voice') as mock_kb:
mock_kb.return_value = MagicMock()
with patch('helper_bot.handlers.voice.voice_handler.messages.get_message') as mock_get:
mock_get.return_value = "Голос сохранён"
await voice_handler.suggest_voice(mock_message, mock_state, mock_db, mock_settings)
mock_send.assert_awaited_once()
mock_db.set_user_id_and_message_id_for_voice_bot.assert_awaited_once_with(999, 123)
mock_message.answer.assert_called_once()
mock_state.set_state.assert_called_once_with(STATE_START)
@pytest.mark.asyncio
async def test_suggest_voice_invalid_keeps_state_standup_write(self, voice_handler, mock_message, mock_state, mock_db, mock_settings):
"""suggest_voice при невалидном голосовом оставляет состояние STANDUP_WRITE."""
with patch('helper_bot.handlers.voice.voice_handler.validate_voice_message', new_callable=AsyncMock, return_value=False):
with patch('helper_bot.handlers.voice.voice_handler.get_main_keyboard') as mock_keyboard:
mock_keyboard.return_value = MagicMock()
with patch('helper_bot.handlers.voice.voice_handler.messages.get_message') as mock_get:
mock_get.return_value = "Неверный контент"
await voice_handler.suggest_voice(mock_message, mock_state, mock_db, mock_settings)
mock_message.answer.assert_called()
mock_state.set_state.assert_called_once_with(STATE_STANDUP_WRITE)
@pytest.mark.asyncio
async def test_handle_emoji_message_answers_emoji(self, voice_handler, mock_message, mock_state, mock_settings):
"""handle_emoji_message пересылает в логи и отвечает эмодзи или ничего."""
with patch('helper_bot.handlers.voice.voice_handler.check_user_emoji', new_callable=AsyncMock, return_value='😊'):
await voice_handler.handle_emoji_message(mock_message, mock_state, mock_settings)
mock_message.forward.assert_awaited_once()
mock_state.set_state.assert_called_once_with(STATE_START)
mock_message.answer.assert_called_once_with(f'Твоя эмодзя - 😊', parse_mode='HTML')
if __name__ == '__main__':
pytest.main([__file__])