from __future__ import annotations import io import unittest from unittest.mock import MagicMock, PropertyMock, patch from govoplan_files.backend.storage.backends import S3StorageBackend, StorageBackendError def _backend() -> S3StorageBackend: return S3StorageBackend( bucket="files", endpoint_url="https://objects.example.test", region_name="test", access_key_id="access", secret_access_key="secret", ) class S3StorageBackendTests(unittest.TestCase): def test_get_bytes_rejects_declared_oversize_object_without_reading(self) -> None: body = MagicMock() client = MagicMock() client.get_object.return_value = {"ContentLength": 6, "Body": body} with patch.dict("os.environ", {"GOVOPLAN_CONNECTOR_MAX_FILE_TRANSFER_BYTES": "5"}), patch.object( S3StorageBackend, "client", new_callable=PropertyMock, return_value=client, ), self.assertRaisesRegex(StorageBackendError, "deployment limit"): _backend().get_bytes("large.bin") body.read.assert_not_called() body.close.assert_called_once_with() def test_iter_bytes_rejects_undeclared_oversize_object(self) -> None: client = MagicMock() client.get_object.return_value = {"Body": io.BytesIO(b"123456")} with patch.dict("os.environ", {"GOVOPLAN_CONNECTOR_MAX_FILE_TRANSFER_BYTES": "5"}), patch.object( S3StorageBackend, "client", new_callable=PropertyMock, return_value=client, ), self.assertRaisesRegex(StorageBackendError, "deployment limit"): list(_backend().iter_bytes("large.bin", chunk_size=3)) def test_iter_bytes_closes_streaming_body_when_consumer_stops_early(self) -> None: body = MagicMock() body.read.side_effect = (b"123", b"456", b"") client = MagicMock() client.get_object.return_value = {"ContentLength": 6, "Body": body} with patch.dict("os.environ", {"GOVOPLAN_CONNECTOR_MAX_FILE_TRANSFER_BYTES": "10"}), patch.object( S3StorageBackend, "client", new_callable=PropertyMock, return_value=client, ): chunks = _backend().iter_bytes("object.bin", chunk_size=3) self.assertEqual(b"123", next(chunks)) chunks.close() body.close.assert_called_once_with() if __name__ == "__main__": unittest.main()