Enhance bot functionality with new features and improvements

- Added a new `/status` endpoint in `server_prometheus.py` to provide process status information, including uptime and resource usage metrics.
- Implemented a PID manager in `run_helper.py` to track the bot's process, improving monitoring capabilities.
- Introduced a method to delete audio moderation records in `audio_repository.py`, enhancing database management.
- Updated voice message handling in callback handlers to ensure proper deletion of audio moderation records.
- Improved error handling and logging in various services, ensuring better tracking of media processing and file downloads.
- Refactored media handling functions to streamline operations and improve code readability.
- Enhanced metrics tracking for file downloads and media processing, providing better insights into bot performance.
This commit is contained in:
2025-09-04 00:46:45 +03:00
parent ae7bd476bb
commit fc0517c011
17 changed files with 1421 additions and 84 deletions

View File

@@ -0,0 +1,301 @@
"""
Тесты для улучшенных методов обработки медиа
"""
import pytest
import os
import tempfile
from unittest.mock import Mock, AsyncMock, patch, MagicMock
from aiogram import types
from helper_bot.utils.helper_func import (
download_file,
add_in_db_media,
add_in_db_media_mediagroup,
send_media_group_message_to_private_chat
)
class TestDownloadFile:
"""Тесты для функции download_file"""
@pytest.mark.asyncio
async def test_download_file_success_photo(self):
"""Тест успешного скачивания фото"""
# Создаем временную директорию
with tempfile.TemporaryDirectory() as temp_dir:
with patch('helper_bot.utils.helper_func.os.makedirs'), \
patch('helper_bot.utils.helper_func.os.path.exists', return_value=True), \
patch('helper_bot.utils.helper_func.os.path.getsize', return_value=1024), \
patch('helper_bot.utils.helper_func.os.path.basename', return_value='photo.jpg'), \
patch('helper_bot.utils.helper_func.os.path.splitext', return_value=('photo', '.jpg')):
# Мокаем сообщение и бота
mock_message = Mock()
mock_message.bot = Mock()
mock_file = Mock()
mock_file.file_path = 'photos/photo.jpg'
mock_message.bot.get_file = AsyncMock(return_value=mock_file)
mock_message.bot.download_file = AsyncMock()
# Вызываем функцию
result = await download_file(mock_message, 'test_file_id', 'photo')
# Проверяем результат
assert result is not None
assert 'files/photos/test_file_id.jpg' in result
mock_message.bot.get_file.assert_called_once_with('test_file_id')
mock_message.bot.download_file.assert_called_once()
@pytest.mark.asyncio
async def test_download_file_invalid_parameters(self):
"""Тест с неверными параметрами"""
result = await download_file(None, 'test_file_id', 'photo')
assert result is None
mock_message = Mock()
mock_message.bot = None
result = await download_file(mock_message, 'test_file_id', 'photo')
assert result is None
@pytest.mark.asyncio
async def test_download_file_error(self):
"""Тест обработки ошибки при скачивании"""
mock_message = Mock()
mock_message.bot = Mock()
mock_message.bot.get_file = AsyncMock(side_effect=Exception("Network error"))
result = await download_file(mock_message, 'test_file_id', 'photo')
assert result is None
class TestAddInDbMedia:
"""Тесты для функции add_in_db_media"""
@pytest.mark.asyncio
async def test_add_in_db_media_success_photo(self):
"""Тест успешного добавления фото в БД"""
# Мокаем сообщение
mock_message = Mock()
mock_message.message_id = 123
mock_message.photo = [Mock()]
mock_message.photo[-1].file_id = 'photo_123'
mock_message.video = None
mock_message.voice = None
mock_message.audio = None
mock_message.video_note = None
# Мокаем БД
mock_db = AsyncMock()
mock_db.add_post_content = AsyncMock(return_value=True)
with patch('helper_bot.utils.helper_func.download_file', return_value='files/photos/photo_123.jpg'):
result = await add_in_db_media(mock_message, mock_db)
assert result is True
mock_db.add_post_content.assert_called_once_with(123, 123, 'files/photos/photo_123.jpg', 'photo')
@pytest.mark.asyncio
async def test_add_in_db_media_download_fails(self):
"""Тест когда скачивание файла не удается"""
mock_message = Mock()
mock_message.message_id = 123
mock_message.photo = [Mock()]
mock_message.photo[-1].file_id = 'photo_123'
mock_message.video = None
mock_message.voice = None
mock_message.audio = None
mock_message.video_note = None
mock_db = AsyncMock()
with patch('helper_bot.utils.helper_func.download_file', return_value=None):
result = await add_in_db_media(mock_message, mock_db)
assert result is False
mock_db.add_post_content.assert_not_called()
@pytest.mark.asyncio
async def test_add_in_db_media_db_fails(self):
"""Тест когда добавление в БД не удается"""
mock_message = Mock()
mock_message.message_id = 123
mock_message.photo = [Mock()]
mock_message.photo[-1].file_id = 'photo_123'
mock_message.video = None
mock_message.voice = None
mock_message.audio = None
mock_message.video_note = None
mock_db = AsyncMock()
mock_db.add_post_content = AsyncMock(return_value=False)
with patch('helper_bot.utils.helper_func.download_file', return_value='files/photos/photo_123.jpg'), \
patch('helper_bot.utils.helper_func.os.remove'):
result = await add_in_db_media(mock_message, mock_db)
assert result is False
mock_db.add_post_content.assert_called_once()
@pytest.mark.asyncio
async def test_add_in_db_media_unsupported_content(self):
"""Тест с неподдерживаемым типом контента"""
mock_message = Mock()
mock_message.message_id = 123
mock_message.photo = None
mock_message.video = None
mock_message.voice = None
mock_message.audio = None
mock_message.video_note = None
mock_db = AsyncMock()
result = await add_in_db_media(mock_message, mock_db)
assert result is False
mock_db.add_post_content.assert_not_called()
class TestAddInDbMediaMediagroup:
"""Тесты для функции add_in_db_media_mediagroup"""
@pytest.mark.asyncio
async def test_add_in_db_media_mediagroup_success(self):
"""Тест успешного добавления медиагруппы в БД"""
# Создаем моки сообщений
mock_message1 = Mock()
mock_message1.message_id = 1
mock_message1.photo = [Mock()]
mock_message1.photo[-1].file_id = 'photo_1'
mock_message1.video = None
mock_message1.voice = None
mock_message1.audio = None
mock_message1.video_note = None
mock_message2 = Mock()
mock_message2.message_id = 2
mock_message2.photo = None
mock_message2.video = Mock()
mock_message2.video.file_id = 'video_1'
mock_message2.voice = None
mock_message2.audio = None
mock_message2.video_note = None
sent_messages = [mock_message1, mock_message2]
# Мокаем БД
mock_db = AsyncMock()
mock_db.add_post_content = AsyncMock(return_value=True)
with patch('helper_bot.utils.helper_func.download_file', return_value='files/test.jpg'):
result = await add_in_db_media_mediagroup(sent_messages, mock_db, main_post_id=100)
assert result is True
assert mock_db.add_post_content.call_count == 2
@pytest.mark.asyncio
async def test_add_in_db_media_mediagroup_empty_list(self):
"""Тест с пустым списком сообщений"""
mock_db = AsyncMock()
result = await add_in_db_media_mediagroup([], mock_db)
assert result is False
mock_db.add_post_content.assert_not_called()
@pytest.mark.asyncio
async def test_add_in_db_media_mediagroup_partial_failure(self):
"""Тест когда часть сообщений обрабатывается успешно"""
# Создаем моки сообщений
mock_message1 = Mock()
mock_message1.message_id = 1
mock_message1.photo = [Mock()]
mock_message1.photo[-1].file_id = 'photo_1'
mock_message1.video = None
mock_message1.voice = None
mock_message1.audio = None
mock_message1.video_note = None
mock_message2 = Mock()
mock_message2.message_id = 2
mock_message2.photo = None
mock_message2.video = None
mock_message2.voice = None
mock_message2.audio = None
mock_message2.video_note = None # Неподдерживаемый тип
sent_messages = [mock_message1, mock_message2]
# Мокаем БД
mock_db = AsyncMock()
mock_db.add_post_content = AsyncMock(return_value=True)
with patch('helper_bot.utils.helper_func.download_file', return_value='files/test.jpg'):
result = await add_in_db_media_mediagroup(sent_messages, mock_db)
# Должен вернуть False, так как есть ошибки (второе сообщение не поддерживается)
assert result is False
assert mock_db.add_post_content.call_count == 1
class TestSendMediaGroupMessageToPrivateChat:
"""Тесты для функции send_media_group_message_to_private_chat"""
@pytest.mark.asyncio
async def test_send_media_group_message_success(self):
"""Тест успешной отправки медиагруппы"""
# Мокаем сообщение
mock_message = Mock()
mock_message.from_user.id = 123
mock_message.bot = Mock()
# Мокаем отправленное сообщение
mock_sent_message = Mock()
mock_sent_message.message_id = 456
mock_sent_message.caption = "Test caption"
mock_message.bot.send_media_group = AsyncMock(return_value=[mock_sent_message])
# Мокаем БД
mock_db = AsyncMock()
mock_db.add_post = AsyncMock()
with patch('helper_bot.utils.helper_func.add_in_db_media_mediagroup', return_value=True):
result = await send_media_group_message_to_private_chat(
100, mock_message, [], mock_db, main_post_id=789
)
assert result == 456
mock_message.bot.send_media_group.assert_called_once()
mock_db.add_post.assert_called_once()
@pytest.mark.asyncio
async def test_send_media_group_message_media_processing_fails(self):
"""Тест когда обработка медиа не удается"""
# Мокаем сообщение
mock_message = Mock()
mock_message.from_user.id = 123
mock_message.bot = Mock()
# Мокаем отправленное сообщение
mock_sent_message = Mock()
mock_sent_message.message_id = 456
mock_sent_message.caption = "Test caption"
mock_message.bot.send_media_group = AsyncMock(return_value=[mock_sent_message])
# Мокаем БД
mock_db = AsyncMock()
mock_db.add_post = AsyncMock()
with patch('helper_bot.utils.helper_func.add_in_db_media_mediagroup', return_value=False):
result = await send_media_group_message_to_private_chat(
100, mock_message, [], mock_db, main_post_id=789
)
assert result == 456 # Функция все равно возвращает message_id
mock_message.bot.send_media_group.assert_called_once()
mock_db.add_post.assert_called_once()
if __name__ == "__main__":
pytest.main([__file__])