import pytest from unittest.mock import Mock, AsyncMock, patch from datetime import datetime from pathlib import Path from helper_bot.handlers.voice.services import VoiceBotService from helper_bot.handlers.voice.exceptions import VoiceMessageError, AudioProcessingError from helper_bot.handlers.voice.utils import get_last_message_text, validate_voice_message, get_user_emoji_safe class TestVoiceBotService: """Тесты для VoiceBotService""" @pytest.fixture def mock_bot_db(self): """Мок для базы данных""" mock_db = Mock() mock_db.settings = { 'Settings': {'logs': True}, 'Telegram': {'important_logs': 'test_chat_id'} } return mock_db @pytest.fixture def mock_settings(self): """Мок для настроек""" return { 'Settings': {'logs': True}, 'Telegram': {'preview_link': True} } @pytest.fixture def voice_service(self, mock_bot_db, mock_settings): """Экземпляр VoiceBotService для тестов""" return VoiceBotService(mock_bot_db, mock_settings) @pytest.mark.asyncio async def test_get_welcome_sticker_success(self, voice_service, mock_settings): """Тест успешного получения стикера""" with patch('pathlib.Path.rglob') as mock_rglob: mock_rglob.return_value = ['/path/to/sticker1.tgs', '/path/to/sticker2.tgs'] sticker = await voice_service.get_welcome_sticker() assert sticker is not None mock_rglob.assert_called_once() @pytest.mark.asyncio async def test_get_welcome_sticker_no_stickers(self, voice_service, mock_settings): """Тест получения стикера когда их нет""" with patch('pathlib.Path.rglob') as mock_rglob: mock_rglob.return_value = [] sticker = await voice_service.get_welcome_sticker() assert sticker is None @pytest.mark.asyncio async def test_get_random_audio_success(self, voice_service, mock_bot_db): """Тест успешного получения случайного аудио""" mock_bot_db.check_listen_audio = AsyncMock(return_value=['audio1', 'audio2']) mock_bot_db.get_user_id_by_file_name = AsyncMock(return_value=123) mock_bot_db.get_date_by_file_name = AsyncMock(return_value='2025-01-01 12:00:00') mock_bot_db.get_user_emoji = AsyncMock(return_value='😊') result = await voice_service.get_random_audio(456) assert result is not None assert len(result) == 3 assert result[0] in ['audio1', 'audio2'] assert result[1] == '2025-01-01 12:00:00' assert result[2] == '😊' @pytest.mark.asyncio async def test_get_random_audio_no_audio(self, voice_service, mock_bot_db): """Тест получения аудио когда их нет""" mock_bot_db.check_listen_audio = AsyncMock(return_value=[]) result = await voice_service.get_random_audio(456) assert result is None @pytest.mark.asyncio async def test_mark_audio_as_listened_success(self, voice_service, mock_bot_db): """Тест успешной пометки аудио как прослушанного""" mock_bot_db.mark_listened_audio = AsyncMock() await voice_service.mark_audio_as_listened('test_audio', 123) mock_bot_db.mark_listened_audio.assert_called_once_with('test_audio', user_id=123) @pytest.mark.asyncio async def test_clear_user_listenings_success(self, voice_service, mock_bot_db): """Тест успешной очистки прослушиваний""" mock_bot_db.delete_listen_count_for_user = AsyncMock() await voice_service.clear_user_listenings(123) mock_bot_db.delete_listen_count_for_user.assert_called_once_with(123) @pytest.mark.asyncio async def test_get_remaining_audio_count_success(self, voice_service, mock_bot_db): """Тест получения количества оставшихся аудио""" mock_bot_db.check_listen_audio = AsyncMock(return_value=['audio1', 'audio2', 'audio3']) result = await voice_service.get_remaining_audio_count(123) assert result == 3 mock_bot_db.check_listen_audio.assert_called_once_with(user_id=123) @pytest.mark.asyncio async def test_get_remaining_audio_count_zero(self, voice_service, mock_bot_db): """Тест получения количества оставшихся аудио когда их нет""" mock_bot_db.check_listen_audio = AsyncMock(return_value=[]) result = await voice_service.get_remaining_audio_count(123) assert result == 0 mock_bot_db.check_listen_audio.assert_called_once_with(user_id=123) @pytest.mark.asyncio async def test_send_welcome_messages_success(self, voice_service, mock_bot_db, mock_settings): """Тест успешной отправки приветственных сообщений.""" mock_message = Mock() mock_message.from_user.id = 123 mock_message.answer = AsyncMock() mock_message.answer.return_value = Mock() mock_message.answer_sticker = AsyncMock() with patch.object(voice_service, 'get_welcome_sticker', new_callable=AsyncMock, return_value='test_sticker.tgs'): with patch('helper_bot.handlers.voice.services.asyncio.sleep', new_callable=AsyncMock): await voice_service.send_welcome_messages(mock_message, '😊') assert mock_message.answer.call_count >= 1 @pytest.mark.asyncio async def test_send_welcome_messages_no_sticker(self, voice_service, mock_bot_db, mock_settings): """Тест отправки приветственных сообщений без стикера.""" mock_message = Mock() mock_message.from_user.id = 123 mock_message.answer = AsyncMock() mock_message.answer.return_value = Mock() with patch.object(voice_service, 'get_welcome_sticker', new_callable=AsyncMock, return_value=None): with patch('helper_bot.handlers.voice.services.asyncio.sleep', new_callable=AsyncMock): await voice_service.send_welcome_messages(mock_message, '😊') assert mock_message.answer.call_count >= 1 class TestVoiceHandlers: """Тесты для VoiceHandlers""" @pytest.fixture def mock_db(self): """Мок для базы данных""" return Mock() @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 voice_handlers(self, mock_db, mock_settings): """Экземпляр VoiceHandlers для тестов""" from helper_bot.handlers.voice.voice_handler import VoiceHandlers return VoiceHandlers(mock_db, mock_settings) def test_voice_handlers_initialization(self, voice_handlers): """Тест инициализации VoiceHandlers""" assert voice_handlers.db is not None assert voice_handlers.settings is not None assert voice_handlers.router is not None def test_setup_handlers(self, voice_handlers): """Тест настройки обработчиков""" # Проверяем, что роутер содержит обработчики assert len(voice_handlers.router.message.handlers) > 0 class TestUtils: """Тесты для утилит""" @pytest.fixture def mock_bot_db(self): """Мок для базы данных""" return Mock() @pytest.mark.asyncio async def test_get_last_message_text(self, mock_bot_db): """Тест получения последнего сообщения""" # Возвращаем UNIX timestamp mock_bot_db.last_date_audio = AsyncMock(return_value=1641034800) # 2022-01-01 12:00:00 result = await get_last_message_text(mock_bot_db) assert result is not None assert "минут" in result or "часа" in result or "дня" in result or "день" in result or "дней" in result mock_bot_db.last_date_audio.assert_called_once() @pytest.mark.asyncio async def test_validate_voice_message_valid(self): """Тест валидации голосового сообщения""" mock_message = Mock() mock_message.content_type = 'voice' mock_message.voice = Mock() result = await validate_voice_message(mock_message) assert result is True @pytest.mark.asyncio async def test_validate_voice_message_invalid(self): """Тест валидации невалидного сообщения""" mock_message = Mock() mock_message.voice = None result = await validate_voice_message(mock_message) assert result is False @pytest.mark.asyncio async def test_get_user_emoji_safe(self, mock_bot_db): """Тест безопасного получения эмодзи пользователя""" mock_bot_db.get_user_emoji = AsyncMock(return_value="😊") result = await get_user_emoji_safe(mock_bot_db, 123) assert result == "😊" mock_bot_db.get_user_emoji.assert_called_once_with(123) @pytest.mark.asyncio async def test_get_user_emoji_safe_none(self, mock_bot_db): """Тест безопасного получения эмодзи когда его нет""" mock_bot_db.get_user_emoji = AsyncMock(return_value=None) result = await get_user_emoji_safe(mock_bot_db, 123) assert result == "😊" @pytest.mark.asyncio async def test_get_user_emoji_safe_error(self, mock_bot_db): """Тест безопасного получения эмодзи при ошибке""" mock_bot_db.get_user_emoji = AsyncMock(return_value="Ошибка") result = await get_user_emoji_safe(mock_bot_db, 123) assert result == "Ошибка" class TestExceptions: """Тесты для исключений""" def test_voice_message_error(self): """Тест VoiceMessageError""" try: raise VoiceMessageError("Тестовая ошибка") except VoiceMessageError as e: assert str(e) == "Тестовая ошибка" def test_audio_processing_error(self): """Тест AudioProcessingError""" try: raise AudioProcessingError("Ошибка обработки") except AudioProcessingError as e: assert str(e) == "Ошибка обработки" if __name__ == '__main__': pytest.main([__file__])