373 lines
16 KiB
Python
373 lines
16 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__])
|