import pytest from unittest.mock import Mock, AsyncMock, patch from datetime import datetime from voice_bot.handlers.services import VoiceBotService, AudioFileService from voice_bot.handlers.exceptions import VoiceMessageError, AudioProcessingError from voice_bot.handlers.utils import format_time_ago, plural_time 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 def test_get_random_audio_success(self, voice_service, mock_bot_db): """Тест успешного получения случайного аудио""" mock_bot_db.check_listen_audio.return_value = ['audio1', 'audio2'] mock_bot_db.get_user_id_by_file_name.return_value = 123 mock_bot_db.get_date_by_file_name.return_value = '2025-01-01 12:00:00' mock_bot_db.check_emoji_for_user.return_value = '😊' result = 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] == '😊' def test_get_random_audio_no_audio(self, voice_service, mock_bot_db): """Тест получения аудио когда их нет""" mock_bot_db.check_listen_audio.return_value = [] result = voice_service.get_random_audio(456) assert result is None def test_mark_audio_as_listened_success(self, voice_service, mock_bot_db): """Тест успешной пометки аудио как прослушанного""" voice_service.mark_audio_as_listened('test_audio', 123) mock_bot_db.mark_listened_audio.assert_called_once_with('test_audio', user_id=123) def test_clear_user_listenings_success(self, voice_service, mock_bot_db): """Тест успешной очистки прослушиваний""" voice_service.clear_user_listenings(123) mock_bot_db.delete_listen_count_for_user.assert_called_once_with(123) class TestAudioFileService: """Тесты для AudioFileService""" @pytest.fixture def mock_bot_db(self): """Мок для базы данных""" return Mock() @pytest.fixture def audio_service(self, mock_bot_db): """Экземпляр AudioFileService для тестов""" return AudioFileService(mock_bot_db) def test_generate_file_name_first_audio(self, audio_service, mock_bot_db): """Тест генерации имени для первого аудио пользователя""" mock_bot_db.get_last_user_audio_record.return_value = False file_name = audio_service.generate_file_name(123) assert file_name == 'message_from_123_number_1' def test_generate_file_name_subsequent_audio(self, audio_service, mock_bot_db): """Тест генерации имени для последующих аудио пользователя""" mock_bot_db.get_last_user_audio_record.return_value = True mock_bot_db.get_path_for_audio_record.return_value = 'existing_file' mock_bot_db.get_id_for_audio_record.return_value = 5 with patch('pathlib.Path.exists') as mock_exists: mock_exists.return_value = True file_name = audio_service.generate_file_name(123) assert file_name == 'message_from_123_number_6' def test_save_audio_file_success(self, audio_service, mock_bot_db): """Тест успешного сохранения аудио файла в БД""" file_name = 'test_file' user_id = 123 file_id = 1 date_added = datetime.now() audio_service.save_audio_file(file_name, user_id, file_id, date_added) mock_bot_db.add_audio_record.assert_called_once_with( file_name, user_id, date_added, 0, file_id ) class TestUtils: """Тесты для утилит""" def test_plural_time_minutes(self): """Тест множественного числа для минут""" assert plural_time(1, 1) == '1 минуту' assert plural_time(1, 2) == '2 минуты' assert plural_time(1, 5) == '5 минут' assert plural_time(1, 11) == '11 минут' assert plural_time(1, 21) == '21 минуту' def test_plural_time_hours(self): """Тест множественного числа для часов""" assert plural_time(2, 1) == '1 час' assert plural_time(2, 2) == '2 часа' assert plural_time(2, 5) == '5 часов' assert plural_time(2, 11) == '11 часов' assert plural_time(2, 21) == '21 час' def test_plural_time_days(self): """Тест множественного числа для дней""" assert plural_time(3, 1) == '1 день' assert plural_time(3, 2) == '2 дня' assert plural_time(3, 5) == '5 дней' assert plural_time(3, 11) == '11 дней' assert plural_time(3, 21) == '21 день' def test_format_time_ago_minutes(self): """Тест форматирования времени для минут""" from datetime import datetime, timedelta # Создаем время 30 минут назад past_time = datetime.now() - timedelta(minutes=30) time_str = past_time.strftime("%Y-%m-%d %H:%M:%S") result = format_time_ago(time_str) assert '30 минут' in result assert 'назад' in result def test_format_time_ago_hours(self): """Тест форматирования времени для часов""" from datetime import datetime, timedelta # Создаем время 2 часа назад past_time = datetime.now() - timedelta(hours=2) time_str = past_time.strftime("%Y-%m-%d %H:%M:%S") result = format_time_ago(time_str) assert '2 часа' in result assert 'назад' in result def test_format_time_ago_days(self): """Тест форматирования времени для дней""" from datetime import datetime, timedelta # Создаем время 3 дня назад past_time = datetime.now() - timedelta(days=3) time_str = past_time.strftime("%Y-%m-%d %H:%M:%S") result = format_time_ago(time_str) assert '3 дня' in result assert 'назад' in result class TestExceptions: """Тесты для исключений""" def test_voice_bot_error_inheritance(self): """Тест наследования исключений""" assert issubclass(VoiceMessageError, VoiceBotError) assert issubclass(AudioProcessingError, VoiceBotError) assert issubclass(DatabaseError, VoiceBotError) assert issubclass(FileOperationError, VoiceBotError) def test_exception_messages(self): """Тест сообщений исключений""" try: raise VoiceMessageError("Тестовая ошибка") except VoiceMessageError as e: assert str(e) == "Тестовая ошибка" try: raise AudioProcessingError("Ошибка обработки") except AudioProcessingError as e: assert str(e) == "Ошибка обработки" if __name__ == '__main__': pytest.main([__file__])