- Deleted the Makefile, `README_TESTING.md`, and several deployment scripts to streamline the project. - Updated `.dockerignore` to exclude unnecessary development files. - Adjusted database schema comments for clarity. - Refactored metrics handling in middleware for improved command extraction and logging. - Enhanced command mappings for buttons and callbacks in constants for better maintainability. - Start refactor voice bot
233 lines
9.1 KiB
Python
233 lines
9.1 KiB
Python
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__])
|