Update voice bot functionality and clean up project structure

- Added voice message handling capabilities, including saving and deleting audio messages via callback queries.
- Refactored audio record management in the database to remove unnecessary fields and streamline operations.
- Introduced new keyboard options for voice interactions in the bot.
- Updated `.gitignore` to include voice user files for better project organization.
- Removed obsolete voice bot handler files to simplify the codebase.
This commit is contained in:
2025-09-01 19:17:05 +03:00
parent d128e54694
commit 2d40f4496e
29 changed files with 757 additions and 724 deletions

View File

@@ -136,11 +136,7 @@ async def test_audio_operations(temp_db):
# Добавляем аудио запись
with pytest.raises(sqlite3.IntegrityError):
await temp_db.add_audio_record(file_name, user_id, file_id)
# # Получаем file_id
# retrieved_file_id = await temp_db.get_audio_file_id(user_id)
# assert retrieved_file_id == file_id
await temp_db.add_audio_record(file_name, user_id)
# # Получаем имя файла
# retrieved_file_name = await temp_db.get_audio_file_name(user_id)

View File

@@ -0,0 +1,195 @@
import pytest
from unittest.mock import Mock, AsyncMock, patch
from datetime import datetime
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
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 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()
def test_get_last_message_text(self, mock_bot_db):
"""Тест получения последнего сообщения"""
mock_bot_db.last_date_audio.return_value = "2025-01-01 12:00:00"
result = get_last_message_text(mock_bot_db)
assert result is not None
assert "минут" in result or "часа" in result or "дня" in result
mock_bot_db.last_date_audio.assert_called_once()
def test_validate_voice_message_valid(self):
"""Тест валидации голосового сообщения"""
mock_message = Mock()
mock_message.content_type = 'voice'
result = validate_voice_message(mock_message)
assert result is True
def test_validate_voice_message_invalid(self):
"""Тест валидации невалидного сообщения"""
mock_message = Mock()
mock_message.voice = None
result = validate_voice_message(mock_message)
assert result is False
def test_get_user_emoji_safe(self, mock_bot_db):
"""Тест безопасного получения эмодзи пользователя"""
mock_bot_db.check_emoji_for_user.return_value = "😊"
result = get_user_emoji_safe(mock_bot_db, 123)
assert result == "😊"
mock_bot_db.check_emoji_for_user.assert_called_once_with(123)
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__])