Переписал почти все тесты

feat: улучшено логирование и обработка скорингов в PostService и RagApiClient

- Добавлены отладочные сообщения для передачи скорингов в функции обработки постов.
- Обновлено логирование успешного получения скорингов из RAG API с дополнительной информацией.
- Оптимизирована обработка скорингов в функции get_text_message для улучшения отладки.
- Обновлены тесты для проверки новых функциональных возможностей и обработки ошибок.
This commit is contained in:
2026-01-30 00:55:47 +03:00
parent e87f4af82f
commit a5faa4bdc6
27 changed files with 4320 additions and 8 deletions

216
tests/test_s3_storage.py Normal file
View File

@@ -0,0 +1,216 @@
"""
Тесты для helper_bot.utils.s3_storage (S3StorageService).
"""
import tempfile
from pathlib import Path
from unittest.mock import AsyncMock, MagicMock, patch
import pytest
from helper_bot.utils.s3_storage import S3StorageService
@pytest.mark.unit
class TestS3StorageServiceInit:
"""Тесты инициализации S3StorageService."""
def test_init_stores_params(self):
"""Параметры сохраняются в атрибутах."""
with patch("helper_bot.utils.s3_storage.aioboto3.Session"):
service = S3StorageService(
endpoint_url="http://s3",
access_key="ak",
secret_key="sk",
bucket_name="bucket",
region="eu-west-1",
)
assert service.endpoint_url == "http://s3"
assert service.bucket_name == "bucket"
assert service.region == "eu-west-1"
@pytest.mark.unit
class TestS3StorageServiceGenerateS3Key:
"""Тесты generate_s3_key."""
@pytest.fixture
def service(self):
"""Сервис без реального session."""
with patch("helper_bot.utils.s3_storage.aioboto3.Session"):
return S3StorageService(
endpoint_url="http://s3",
access_key="ak",
secret_key="sk",
bucket_name="b",
)
def test_photo_key(self, service):
"""Ключ для photo — photos/{id}.jpg."""
key = service.generate_s3_key("photo", "file_123")
assert key == "photos/file_123.jpg"
def test_video_key(self, service):
"""Ключ для video — videos/{id}.mp4."""
key = service.generate_s3_key("video", "vid_1")
assert key == "videos/vid_1.mp4"
def test_audio_key(self, service):
"""Ключ для audio — music/{id}.mp3."""
key = service.generate_s3_key("audio", "a1")
assert key == "music/a1.mp3"
def test_voice_key(self, service):
"""Ключ для voice — voice/{id}.ogg."""
key = service.generate_s3_key("voice", "v1")
assert key == "voice/v1.ogg"
def test_other_key(self, service):
"""Неизвестный тип — other/{id}.bin."""
key = service.generate_s3_key("other", "x")
assert key == "other/x.bin"
@pytest.mark.unit
@pytest.mark.asyncio
class TestS3StorageServiceUploadDownload:
"""Тесты upload_file, download_file, file_exists, delete_file через мок session.client."""
@pytest.fixture
def service(self):
"""Сервис с замоканной session."""
mock_session = MagicMock()
mock_context = AsyncMock()
mock_s3 = MagicMock()
mock_s3.upload_file = AsyncMock()
mock_s3.upload_fileobj = AsyncMock()
mock_s3.download_file = AsyncMock()
mock_s3.head_object = AsyncMock()
mock_s3.delete_object = AsyncMock()
mock_context.__aenter__.return_value = mock_s3
mock_context.__aexit__.return_value = None
mock_session.client.return_value = mock_context
with patch("helper_bot.utils.s3_storage.aioboto3.Session", return_value=mock_session):
s = S3StorageService(
endpoint_url="http://s3",
access_key="ak",
secret_key="sk",
bucket_name="bucket",
)
s._mock_s3 = mock_s3
return s
async def test_upload_file_success(self, service):
"""upload_file при успехе возвращает True."""
result = await service.upload_file("/tmp/f", "key")
assert result is True
service._mock_s3.upload_file.assert_called_once()
async def test_upload_file_with_content_type(self, service):
"""upload_file с content_type передаёт ExtraArgs."""
await service.upload_file("/tmp/f", "key", content_type="image/jpeg")
call_kwargs = service._mock_s3.upload_file.call_args[1]
assert call_kwargs.get("ExtraArgs", {}).get("ContentType") == "image/jpeg"
async def test_upload_file_exception_returns_false(self, service):
"""При исключении upload_file возвращает False."""
service._mock_s3.upload_file = AsyncMock(side_effect=Exception("network error"))
result = await service.upload_file("/tmp/f", "key")
assert result is False
async def test_download_file_success(self, service):
"""download_file при успехе возвращает True."""
with patch("os.makedirs"):
result = await service.download_file("key", "/tmp/out")
assert result is True
service._mock_s3.download_file.assert_called_once()
async def test_download_file_exception_returns_false(self, service):
"""При исключении download_file возвращает False."""
service._mock_s3.download_file = AsyncMock(side_effect=Exception("error"))
with patch("os.makedirs"):
result = await service.download_file("key", "/tmp/out")
assert result is False
async def test_upload_fileobj_success(self, service):
"""upload_fileobj при успехе возвращает True."""
f = MagicMock()
result = await service.upload_fileobj(f, "key")
assert result is True
service._mock_s3.upload_fileobj.assert_called_once()
async def test_file_exists_true(self, service):
"""file_exists при успешном head_object возвращает True."""
result = await service.file_exists("key")
assert result is True
async def test_file_exists_false_on_exception(self, service):
"""file_exists при исключении возвращает False."""
service._mock_s3.head_object = AsyncMock(side_effect=Exception())
result = await service.file_exists("key")
assert result is False
async def test_delete_file_success(self, service):
"""delete_file при успехе возвращает True."""
result = await service.delete_file("key")
assert result is True
service._mock_s3.delete_object.assert_called_once_with(
Bucket="bucket", Key="key"
)
async def test_delete_file_exception_returns_false(self, service):
"""При исключении delete_file возвращает False."""
service._mock_s3.delete_object = AsyncMock(side_effect=Exception())
result = await service.delete_file("key")
assert result is False
@pytest.mark.unit
@pytest.mark.asyncio
class TestS3StorageServiceDownloadToTemp:
"""Тесты download_to_temp."""
async def test_download_to_temp_success_returns_path(self):
"""При успешном download возвращается путь к временному файлу."""
mock_session = MagicMock()
mock_context = AsyncMock()
mock_s3 = MagicMock()
mock_s3.download_file = AsyncMock()
mock_context.__aenter__.return_value = mock_s3
mock_context.__aexit__.return_value = None
mock_session.client.return_value = mock_context
with patch("helper_bot.utils.s3_storage.aioboto3.Session", return_value=mock_session):
service = S3StorageService(
endpoint_url="http://s3",
access_key="ak",
secret_key="sk",
bucket_name="b",
)
with patch("os.makedirs"):
path = await service.download_to_temp("photos/1.jpg")
if path:
assert Path(path).suffix in (".jpg", "")
try:
Path(path).unlink(missing_ok=True)
except Exception:
pass
async def test_download_to_temp_failure_returns_none(self):
"""При неуспешном download возвращается None."""
mock_session = MagicMock()
mock_context = AsyncMock()
mock_s3 = MagicMock()
mock_s3.download_file = AsyncMock()
mock_context.__aenter__.return_value = mock_s3
mock_context.__aexit__.return_value = None
mock_session.client.return_value = mock_context
with patch("helper_bot.utils.s3_storage.aioboto3.Session", return_value=mock_session):
service = S3StorageService(
endpoint_url="http://s3",
access_key="ak",
secret_key="sk",
bucket_name="b",
)
with patch.object(service, "download_file", AsyncMock(return_value=False)):
path = await service.download_to_temp("key")
assert path is None