Files
telegram-helper-bot/tests/test_s3_storage.py
Andrey a5faa4bdc6 Переписал почти все тесты
feat: улучшено логирование и обработка скорингов в PostService и RagApiClient

- Добавлены отладочные сообщения для передачи скорингов в функции обработки постов.
- Обновлено логирование успешного получения скорингов из RAG API с дополнительной информацией.
- Оптимизирована обработка скорингов в функции get_text_message для улучшения отладки.
- Обновлены тесты для проверки новых функциональных возможностей и обработки ошибок.
2026-01-30 00:55:47 +03:00

217 lines
8.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
Тесты для 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