"""`S3Storage.get_bytes` на заглушке Minio (без сети): чтение, закрытие response, `NoSuchKey` → `ObjectNotFoundError`.""" from typing import Any from unittest.mock import MagicMock import pytest from miniopy_async.error import S3Error from src.core.clients import S3Storage from src.core.storage import ObjectNotFoundError class StubResponse: """Ответ `aiohttp` с флагами закрытия.""" def __init__(self, data: bytes, *, fail_read: bool = False) -> None: self._data = data self._fail_read = fail_read self.closed = False self.released = False async def read(self) -> bytes: if self._fail_read: raise ConnectionError("обрыв чтения") return self._data def close(self) -> None: self.closed = True async def release(self) -> None: self.released = True class StubMinio: def __init__(self, response: StubResponse | None = None, error: Exception | None = None) -> None: self._response = response self._error = error self.calls: list[tuple[str, str]] = [] async def get_object(self, bucket: str, obj_name: str) -> StubResponse: self.calls.append((bucket, obj_name)) if self._error is not None: raise self._error assert self._response is not None # noqa: S101 return self._response def _s3_error(code: str) -> S3Error: return S3Error(code, "сообщение", None, None, None, MagicMock()) def _storage(minio: StubMinio) -> S3Storage: client: Any = minio return S3Storage(client, "arm-ka") async def test_get_bytes_returns_content_and_closes_response() -> None: response = StubResponse(b"payload") minio = StubMinio(response) assert await _storage(minio).get_bytes("uploads/a/b.xls") == b"payload" assert minio.calls == [("arm-ka", "uploads/a/b.xls")] assert response.closed is True assert response.released is True async def test_get_bytes_closes_response_when_read_fails() -> None: response = StubResponse(b"", fail_read=True) with pytest.raises(ConnectionError): await _storage(StubMinio(response)).get_bytes("k") assert response.closed is True assert response.released is True async def test_get_bytes_no_such_key_is_object_not_found() -> None: with pytest.raises(ObjectNotFoundError): await _storage(StubMinio(error=_s3_error("NoSuchKey"))).get_bytes("missing") async def test_get_bytes_other_s3_error_is_not_swallowed() -> None: with pytest.raises(S3Error) as caught: await _storage(StubMinio(error=_s3_error("AccessDenied"))).get_bytes("k") assert caught.value.code == "AccessDenied"