cledar-sdk 2.0.2__py3-none-any.whl → 2.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. cledar/__init__.py +1 -0
  2. cledar/kafka/README.md +239 -0
  3. cledar/kafka/__init__.py +42 -0
  4. cledar/kafka/clients/base.py +117 -0
  5. cledar/kafka/clients/consumer.py +138 -0
  6. cledar/kafka/clients/producer.py +97 -0
  7. cledar/kafka/config/schemas.py +262 -0
  8. cledar/kafka/exceptions.py +17 -0
  9. cledar/kafka/handlers/dead_letter.py +88 -0
  10. cledar/kafka/handlers/parser.py +83 -0
  11. cledar/kafka/logger.py +5 -0
  12. cledar/kafka/models/input.py +17 -0
  13. cledar/kafka/models/message.py +14 -0
  14. cledar/kafka/models/output.py +12 -0
  15. cledar/kafka/tests/.env.test.kafka +3 -0
  16. cledar/kafka/tests/README.md +216 -0
  17. cledar/kafka/tests/conftest.py +104 -0
  18. cledar/kafka/tests/integration/__init__.py +1 -0
  19. cledar/kafka/tests/integration/conftest.py +78 -0
  20. cledar/kafka/tests/integration/helpers.py +47 -0
  21. cledar/kafka/tests/integration/test_consumer_integration.py +375 -0
  22. cledar/kafka/tests/integration/test_integration.py +394 -0
  23. cledar/kafka/tests/integration/test_producer_consumer_interaction.py +388 -0
  24. cledar/kafka/tests/integration/test_producer_integration.py +217 -0
  25. cledar/kafka/tests/unit/__init__.py +1 -0
  26. cledar/kafka/tests/unit/test_base_kafka_client.py +391 -0
  27. cledar/kafka/tests/unit/test_config_validation.py +609 -0
  28. cledar/kafka/tests/unit/test_dead_letter_handler.py +443 -0
  29. cledar/kafka/tests/unit/test_error_handling.py +674 -0
  30. cledar/kafka/tests/unit/test_input_parser.py +310 -0
  31. cledar/kafka/tests/unit/test_input_parser_comprehensive.py +489 -0
  32. cledar/kafka/tests/unit/test_utils.py +25 -0
  33. cledar/kafka/tests/unit/test_utils_comprehensive.py +408 -0
  34. cledar/kafka/utils/callbacks.py +28 -0
  35. cledar/kafka/utils/messages.py +39 -0
  36. cledar/kafka/utils/topics.py +15 -0
  37. cledar/kserve/README.md +352 -0
  38. cledar/kserve/__init__.py +5 -0
  39. cledar/kserve/tests/__init__.py +0 -0
  40. cledar/kserve/tests/test_utils.py +64 -0
  41. cledar/kserve/utils.py +30 -0
  42. cledar/logging/README.md +53 -0
  43. cledar/logging/__init__.py +5 -0
  44. cledar/logging/tests/test_universal_plaintext_formatter.py +249 -0
  45. cledar/logging/universal_plaintext_formatter.py +99 -0
  46. cledar/monitoring/README.md +71 -0
  47. cledar/monitoring/__init__.py +5 -0
  48. cledar/monitoring/monitoring_server.py +156 -0
  49. cledar/monitoring/tests/integration/test_monitoring_server_int.py +162 -0
  50. cledar/monitoring/tests/test_monitoring_server.py +59 -0
  51. cledar/nonce/README.md +99 -0
  52. cledar/nonce/__init__.py +5 -0
  53. cledar/nonce/nonce_service.py +62 -0
  54. cledar/nonce/tests/__init__.py +0 -0
  55. cledar/nonce/tests/test_nonce_service.py +136 -0
  56. cledar/redis/README.md +536 -0
  57. cledar/redis/__init__.py +17 -0
  58. cledar/redis/async_example.py +112 -0
  59. cledar/redis/example.py +67 -0
  60. cledar/redis/exceptions.py +25 -0
  61. cledar/redis/logger.py +5 -0
  62. cledar/redis/model.py +14 -0
  63. cledar/redis/redis.py +764 -0
  64. cledar/redis/redis_config_store.py +333 -0
  65. cledar/redis/tests/test_async_integration_redis.py +158 -0
  66. cledar/redis/tests/test_async_redis_service.py +380 -0
  67. cledar/redis/tests/test_integration_redis.py +119 -0
  68. cledar/redis/tests/test_redis_service.py +319 -0
  69. cledar/storage/README.md +529 -0
  70. cledar/storage/__init__.py +6 -0
  71. cledar/storage/constants.py +5 -0
  72. cledar/storage/exceptions.py +79 -0
  73. cledar/storage/models.py +41 -0
  74. cledar/storage/object_storage.py +1274 -0
  75. cledar/storage/tests/conftest.py +18 -0
  76. cledar/storage/tests/test_abfs.py +164 -0
  77. cledar/storage/tests/test_integration_filesystem.py +359 -0
  78. cledar/storage/tests/test_integration_s3.py +453 -0
  79. cledar/storage/tests/test_local.py +384 -0
  80. cledar/storage/tests/test_s3.py +521 -0
  81. {cledar_sdk-2.0.2.dist-info → cledar_sdk-2.1.0.dist-info}/METADATA +1 -1
  82. cledar_sdk-2.1.0.dist-info/RECORD +84 -0
  83. cledar_sdk-2.0.2.dist-info/RECORD +0 -4
  84. {cledar_sdk-2.0.2.dist-info → cledar_sdk-2.1.0.dist-info}/WHEEL +0 -0
  85. {cledar_sdk-2.0.2.dist-info → cledar_sdk-2.1.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,18 @@
1
+ import pytest
2
+ from faker import Faker
3
+
4
+ from cledar.storage import ObjectStorageServiceConfig
5
+
6
+ fake = Faker()
7
+
8
+
9
+ @pytest.fixture
10
+ def object_storage_config() -> ObjectStorageServiceConfig:
11
+ return ObjectStorageServiceConfig(
12
+ s3_access_key=fake.password(),
13
+ s3_endpoint_url=fake.url(),
14
+ s3_secret_key=fake.password(),
15
+ s3_max_concurrency=10,
16
+ azure_account_name=fake.word(),
17
+ azure_account_key=fake.password(),
18
+ )
@@ -0,0 +1,164 @@
1
+ # mypy: disable-error-code=no-untyped-def
2
+ # pylint: disable=import-error
3
+ import io
4
+ from contextlib import contextmanager
5
+ from unittest.mock import MagicMock, patch
6
+
7
+ import pytest
8
+ from faker import Faker
9
+
10
+ from cledar.storage import ObjectStorageService, ObjectStorageServiceConfig
11
+
12
+ fake = Faker()
13
+
14
+
15
+ @pytest.fixture(name="object_storage_service")
16
+ @patch("fsspec.filesystem")
17
+ def fixture_object_storage_service(
18
+ fsspec_client: MagicMock, object_storage_config: ObjectStorageServiceConfig
19
+ ) -> ObjectStorageService:
20
+ # first call returns s3, second file, third abfs
21
+ fsspec_client.side_effect = [MagicMock(), MagicMock(), MagicMock()]
22
+ return ObjectStorageService(object_storage_config)
23
+
24
+
25
+ def test_upload_file_to_abfs_path(object_storage_service: ObjectStorageService) -> None:
26
+ file_path = fake.file_path()
27
+ dest_path = "abfs://container/path/to/file.txt"
28
+ object_storage_service.azure_client.put = MagicMock()
29
+
30
+ object_storage_service.upload_file(file_path=file_path, destination_path=dest_path)
31
+
32
+ object_storage_service.azure_client.put.assert_called_once_with(
33
+ lpath=file_path, rpath=dest_path
34
+ )
35
+
36
+
37
+ def test_upload_buffer_to_abfs_path(
38
+ object_storage_service: ObjectStorageService,
39
+ ) -> None:
40
+ buffer_bytes = io.BytesIO(fake.text().encode())
41
+ dest_path = "abfss://container/path/to/file.txt"
42
+
43
+ mock_file = MagicMock()
44
+ mock_file.write = MagicMock()
45
+
46
+ @contextmanager
47
+ def open_cm(*_args: object, **_kwargs: object):
48
+ yield mock_file
49
+
50
+ object_storage_service.azure_client.open = MagicMock(
51
+ side_effect=lambda *a, **k: open_cm()
52
+ )
53
+
54
+ object_storage_service.upload_buffer(
55
+ buffer=buffer_bytes, destination_path=dest_path
56
+ )
57
+
58
+ object_storage_service.azure_client.open.assert_called_once_with(
59
+ path=dest_path, mode="wb"
60
+ )
61
+ mock_file.write.assert_called_once()
62
+
63
+
64
+ def test_read_file_from_abfs_path(object_storage_service: ObjectStorageService) -> None:
65
+ path = "abfs://container/path/to/file.txt"
66
+ expected_content = fake.text().encode()
67
+
68
+ mock_file = MagicMock()
69
+ mock_file.read.return_value = expected_content
70
+
71
+ @contextmanager
72
+ def open_cm(*_args: object, **_kwargs: object):
73
+ yield mock_file
74
+
75
+ object_storage_service.azure_client.open = MagicMock(
76
+ side_effect=lambda *a, **k: open_cm()
77
+ )
78
+
79
+ result = object_storage_service.read_file(path=path)
80
+ assert result == expected_content
81
+ object_storage_service.azure_client.open.assert_called_once_with(
82
+ path=path, mode="rb"
83
+ )
84
+
85
+
86
+ def test_list_objects_abfs_recursive(
87
+ object_storage_service: ObjectStorageService,
88
+ ) -> None:
89
+ path = "abfs://container/prefix"
90
+ mock_objects = [
91
+ "abfs://container/prefix/file1",
92
+ "abfs://container/prefix/file2",
93
+ ]
94
+ object_storage_service.azure_client.find.return_value = mock_objects
95
+
96
+ result = object_storage_service.list_objects(path=path, recursive=True)
97
+ assert result == mock_objects
98
+ object_storage_service.azure_client.find.assert_called_once_with(path)
99
+
100
+
101
+ def test_list_objects_abfs_non_recursive(
102
+ object_storage_service: ObjectStorageService,
103
+ ) -> None:
104
+ path = "abfs://container/prefix"
105
+ mock_objects = ["abfs://container/prefix/file1"]
106
+ object_storage_service.azure_client.ls.return_value = mock_objects
107
+
108
+ result = object_storage_service.list_objects(path=path, recursive=False)
109
+ assert result == mock_objects
110
+ object_storage_service.azure_client.ls.assert_called_once_with(path, detail=False)
111
+
112
+
113
+ def test_delete_file_abfs(object_storage_service: ObjectStorageService) -> None:
114
+ path = "abfs://container/file.txt"
115
+ object_storage_service.delete_file(path=path)
116
+ object_storage_service.azure_client.rm.assert_called_once_with(path)
117
+
118
+
119
+ def test_file_exists_abfs(object_storage_service: ObjectStorageService) -> None:
120
+ path = "abfs://container/file.txt"
121
+ object_storage_service.azure_client.exists.return_value = True
122
+ assert object_storage_service.file_exists(path=path) is True
123
+ object_storage_service.azure_client.exists.assert_called_once_with(path)
124
+
125
+
126
+ def test_download_file_from_abfs(object_storage_service: ObjectStorageService) -> None:
127
+ source_path = "abfs://container/file.txt"
128
+ dest_path = "/tmp/dest.txt"
129
+ object_storage_service.azure_client.get = MagicMock()
130
+ object_storage_service.download_file(dest_path, source_path=source_path)
131
+ object_storage_service.azure_client.get.assert_called_once_with(
132
+ source_path, dest_path
133
+ )
134
+
135
+
136
+ def test_get_file_size_abfs(object_storage_service: ObjectStorageService) -> None:
137
+ path = "abfs://container/file.txt"
138
+ object_storage_service.azure_client.info.return_value = {"size": 123}
139
+ result = object_storage_service.get_file_size(path=path)
140
+ assert result == 123
141
+
142
+
143
+ def test_get_file_info_abfs(object_storage_service: ObjectStorageService) -> None:
144
+ path = "abfs://container/file.txt"
145
+ info = {"size": 1}
146
+ object_storage_service.azure_client.info.return_value = info
147
+ result = object_storage_service.get_file_info(path=path)
148
+ assert result == info
149
+
150
+
151
+ def test_copy_file_abfs_to_abfs(object_storage_service: ObjectStorageService) -> None:
152
+ src = "abfs://container/src.txt"
153
+ dst = "abfs://container/dst.txt"
154
+ object_storage_service.azure_client.copy = MagicMock()
155
+ object_storage_service.copy_file(source_path=src, dest_path=dst)
156
+ object_storage_service.azure_client.copy.assert_called_once_with(src, dst)
157
+
158
+
159
+ def test_move_file_abfs_to_abfs(object_storage_service: ObjectStorageService) -> None:
160
+ src = "abfs://container/src.txt"
161
+ dst = "abfs://container/dst.txt"
162
+ object_storage_service.azure_client.move = MagicMock()
163
+ object_storage_service.move_file(source_path=src, dest_path=dst)
164
+ object_storage_service.azure_client.move.assert_called_once_with(src, dst)
@@ -0,0 +1,359 @@
1
+ # mypy: disable-error-code=no-untyped-def
2
+ import io
3
+ import tempfile
4
+ from collections.abc import Generator
5
+ from pathlib import Path
6
+
7
+ import pytest
8
+ from faker import Faker
9
+
10
+ from cledar.storage.exceptions import ReadFileError
11
+ from cledar.storage.models import ObjectStorageServiceConfig
12
+ from cledar.storage.object_storage import ObjectStorageService
13
+
14
+ fake = Faker()
15
+
16
+
17
+ @pytest.fixture(scope="module")
18
+ def object_storage_service() -> ObjectStorageService:
19
+ """
20
+ Create an ObjectStorageService with minimal S3 config (only for local operations).
21
+ """
22
+ # we still need to provide S3 config, but it won't be used for local operations
23
+ config = ObjectStorageServiceConfig(
24
+ s3_endpoint_url="http://localhost:9000",
25
+ s3_access_key="dummy",
26
+ s3_secret_key="dummy",
27
+ s3_max_concurrency=10,
28
+ )
29
+ return ObjectStorageService(config)
30
+
31
+
32
+ @pytest.fixture
33
+ def test_dir() -> Generator[str, None, None]:
34
+ """
35
+ Create a temporary directory and clean it up after test.
36
+ """
37
+ with tempfile.TemporaryDirectory() as temp_dir:
38
+ yield temp_dir
39
+
40
+
41
+ def test_upload_and_read_buffer(
42
+ object_storage_service: ObjectStorageService, test_dir: str
43
+ ) -> None:
44
+ """
45
+ Test uploading a buffer and reading it back.
46
+ """
47
+ test_content = fake.text().encode()
48
+ buffer = io.BytesIO(test_content)
49
+ file_path = f"{test_dir}/test_buffer_{fake.file_name()}"
50
+
51
+ object_storage_service.upload_buffer(buffer=buffer, destination_path=file_path)
52
+
53
+ result = object_storage_service.read_file(path=file_path)
54
+ assert result == test_content
55
+
56
+
57
+ def test_upload_buffer_invalid_params(
58
+ object_storage_service: ObjectStorageService,
59
+ ) -> None:
60
+ """
61
+ Test that upload_buffer raises error with invalid params.
62
+ """
63
+ buffer = io.BytesIO(b"test")
64
+ with pytest.raises(
65
+ ValueError,
66
+ match="Either destination_path or bucket and key must be provided",
67
+ ):
68
+ object_storage_service.upload_buffer(buffer=buffer)
69
+
70
+
71
+ def test_upload_and_download_file(
72
+ object_storage_service: ObjectStorageService, test_dir: str
73
+ ) -> None:
74
+ """
75
+ Test uploading a file and downloading it.
76
+ """
77
+ test_content = fake.text().encode()
78
+ source_path = f"{test_dir}/source_{fake.file_name()}"
79
+ dest_path = f"{test_dir}/dest_{fake.file_name()}"
80
+
81
+ with open(source_path, "wb") as f:
82
+ f.write(test_content)
83
+ upload_path = f"{test_dir}/uploaded_{fake.file_name()}"
84
+ object_storage_service.upload_file(
85
+ file_path=source_path, destination_path=upload_path
86
+ )
87
+
88
+ object_storage_service.download_file(dest_path=dest_path, source_path=upload_path)
89
+
90
+ with open(dest_path, "rb") as f:
91
+ downloaded_content = f.read()
92
+ assert downloaded_content == test_content
93
+
94
+
95
+ def test_read_file_retry_mechanism(
96
+ object_storage_service: ObjectStorageService, test_dir: str
97
+ ) -> None:
98
+ """
99
+ Test that read_file succeeds with valid file.
100
+ """
101
+ test_content = fake.text().encode()
102
+ buffer = io.BytesIO(test_content)
103
+ file_path = f"{test_dir}/retry_{fake.file_name()}"
104
+
105
+ object_storage_service.upload_buffer(buffer=buffer, destination_path=file_path)
106
+
107
+ result = object_storage_service.read_file(path=file_path, max_tries=3)
108
+ assert result == test_content
109
+
110
+
111
+ def test_list_objects_recursive(
112
+ object_storage_service: ObjectStorageService, test_dir: str
113
+ ) -> None:
114
+ """
115
+ Test listing objects recursively.
116
+ """
117
+ files = [
118
+ "folder1/file1.txt",
119
+ "folder1/file2.txt",
120
+ "folder1/subfolder/file3.txt",
121
+ "folder2/file4.txt",
122
+ ]
123
+
124
+ for file_path in files:
125
+ full_path = f"{test_dir}/{file_path}"
126
+ Path(full_path).parent.mkdir(parents=True, exist_ok=True)
127
+ buffer = io.BytesIO(fake.text().encode())
128
+ object_storage_service.upload_buffer(buffer=buffer, destination_path=full_path)
129
+
130
+ result = object_storage_service.list_objects(path=test_dir, recursive=True)
131
+
132
+ result_basenames = [Path(r).relative_to(test_dir).as_posix() for r in result]
133
+
134
+ assert len(result_basenames) >= 4
135
+ for file_path in files:
136
+ assert file_path in result_basenames
137
+
138
+
139
+ def test_list_objects_with_prefix(
140
+ object_storage_service: ObjectStorageService, test_dir: str
141
+ ) -> None:
142
+ """
143
+ Test listing objects with prefix filter.
144
+ """
145
+ files = [
146
+ "prefix1/file1.txt",
147
+ "prefix1/file2.txt",
148
+ "prefix2/file3.txt",
149
+ ]
150
+
151
+ for file_path in files:
152
+ full_path = f"{test_dir}/{file_path}"
153
+ Path(full_path).parent.mkdir(parents=True, exist_ok=True)
154
+ buffer = io.BytesIO(fake.text().encode())
155
+ object_storage_service.upload_buffer(buffer=buffer, destination_path=full_path)
156
+
157
+ prefix1_dir = f"{test_dir}/prefix1"
158
+ result = object_storage_service.list_objects(path=prefix1_dir, recursive=True)
159
+
160
+ result_basenames = [Path(r).name for r in result]
161
+
162
+ assert len(result_basenames) >= 2
163
+ assert "file1.txt" in result_basenames
164
+ assert "file2.txt" in result_basenames
165
+ assert "file3.txt" not in result_basenames
166
+
167
+
168
+ def test_list_objects_non_recursive(
169
+ object_storage_service: ObjectStorageService, test_dir: str
170
+ ) -> None:
171
+ """
172
+ Test listing objects non-recursively.
173
+ """
174
+ files = [
175
+ "root1.txt",
176
+ "root2.txt",
177
+ "folder/nested.txt",
178
+ ]
179
+
180
+ for file_path in files:
181
+ full_path = f"{test_dir}/{file_path}"
182
+ Path(full_path).parent.mkdir(parents=True, exist_ok=True)
183
+ buffer = io.BytesIO(fake.text().encode())
184
+ object_storage_service.upload_buffer(buffer=buffer, destination_path=full_path)
185
+
186
+ result = object_storage_service.list_objects(path=test_dir, recursive=False)
187
+
188
+ result_basenames = [Path(r).name for r in result]
189
+
190
+ assert len(result) >= 2
191
+ has_root_file = any("root" in r for r in result_basenames)
192
+ has_folder = any("folder" in r for r in result_basenames)
193
+ assert has_root_file or has_folder
194
+
195
+
196
+ def test_file_exists(
197
+ object_storage_service: ObjectStorageService, test_dir: str
198
+ ) -> None:
199
+ """
200
+ Test checking if file exists.
201
+ """
202
+ file_path = f"{test_dir}/exists_{fake.file_name()}"
203
+
204
+ assert object_storage_service.file_exists(path=file_path) is False
205
+
206
+ buffer = io.BytesIO(fake.text().encode())
207
+ object_storage_service.upload_buffer(buffer=buffer, destination_path=file_path)
208
+
209
+ assert object_storage_service.file_exists(path=file_path) is True
210
+
211
+
212
+ def test_delete_file(
213
+ object_storage_service: ObjectStorageService, test_dir: str
214
+ ) -> None:
215
+ """
216
+ Test deleting a file.
217
+ """
218
+ file_path = f"{test_dir}/delete_{fake.file_name()}"
219
+
220
+ buffer = io.BytesIO(fake.text().encode())
221
+ object_storage_service.upload_buffer(buffer=buffer, destination_path=file_path)
222
+
223
+ assert object_storage_service.file_exists(path=file_path) is True
224
+
225
+ object_storage_service.delete_file(path=file_path)
226
+
227
+ assert object_storage_service.file_exists(path=file_path) is False
228
+
229
+
230
+ def test_get_file_size(
231
+ object_storage_service: ObjectStorageService, test_dir: str
232
+ ) -> None:
233
+ """
234
+ Test getting file size.
235
+ """
236
+ test_content = fake.text().encode()
237
+ file_path = f"{test_dir}/size_{fake.file_name()}"
238
+
239
+ buffer = io.BytesIO(test_content)
240
+ object_storage_service.upload_buffer(buffer=buffer, destination_path=file_path)
241
+
242
+ size = object_storage_service.get_file_size(path=file_path)
243
+ assert size == len(test_content)
244
+
245
+
246
+ def test_get_file_info(
247
+ object_storage_service: ObjectStorageService, test_dir: str
248
+ ) -> None:
249
+ """
250
+ Test getting file metadata.
251
+ """
252
+ test_content = fake.text().encode()
253
+ file_path = f"{test_dir}/info_{fake.file_name()}"
254
+
255
+ buffer = io.BytesIO(test_content)
256
+ object_storage_service.upload_buffer(buffer=buffer, destination_path=file_path)
257
+
258
+ info = object_storage_service.get_file_info(path=file_path)
259
+
260
+ assert "size" in info
261
+ assert info["size"] == len(test_content)
262
+ assert info is not None
263
+
264
+
265
+ def test_copy_file(object_storage_service: ObjectStorageService, test_dir: str) -> None:
266
+ """
267
+ Test copying a file.
268
+ """
269
+ test_content = fake.text().encode()
270
+ source_path = f"{test_dir}/copy_source_{fake.file_name()}"
271
+ dest_path = f"{test_dir}/copy_dest_{fake.file_name()}"
272
+
273
+ buffer = io.BytesIO(test_content)
274
+ object_storage_service.upload_buffer(buffer=buffer, destination_path=source_path)
275
+
276
+ object_storage_service.copy_file(source_path=source_path, dest_path=dest_path)
277
+
278
+ assert object_storage_service.file_exists(path=source_path) is True
279
+ assert object_storage_service.file_exists(path=dest_path) is True
280
+
281
+ dest_content = object_storage_service.read_file(path=dest_path)
282
+ assert dest_content == test_content
283
+
284
+
285
+ def test_move_file(object_storage_service: ObjectStorageService, test_dir: str) -> None:
286
+ """
287
+ Test moving a file.
288
+ """
289
+ test_content = fake.text().encode()
290
+ source_path = f"{test_dir}/move_source_{fake.file_name()}"
291
+ dest_path = f"{test_dir}/move_dest_{fake.file_name()}"
292
+
293
+ buffer = io.BytesIO(test_content)
294
+ object_storage_service.upload_buffer(buffer=buffer, destination_path=source_path)
295
+
296
+ object_storage_service.move_file(source_path=source_path, dest_path=dest_path)
297
+
298
+ assert object_storage_service.file_exists(path=source_path) is False
299
+ assert object_storage_service.file_exists(path=dest_path) is True
300
+
301
+ dest_content = object_storage_service.read_file(path=dest_path)
302
+ assert dest_content == test_content
303
+
304
+
305
+ def test_read_nonexistent_file(
306
+ object_storage_service: ObjectStorageService, test_dir: str
307
+ ) -> None:
308
+ """
309
+ Test reading a file that doesn't exist.
310
+ """
311
+ non_existent_path = f"{test_dir}/nonexistent_{str(fake.uuid4())}.txt"
312
+
313
+ with pytest.raises(ReadFileError):
314
+ object_storage_service.read_file(path=non_existent_path, max_tries=1)
315
+
316
+
317
+ def test_invalid_parameters(
318
+ object_storage_service: ObjectStorageService,
319
+ ) -> None:
320
+ """
321
+ Test operations with invalid parameters.
322
+ """
323
+ with pytest.raises(
324
+ ValueError, match="Either path or bucket and key must be provided"
325
+ ):
326
+ object_storage_service.read_file()
327
+
328
+ with pytest.raises(
329
+ ValueError,
330
+ match="Either destination_path or bucket and key must be provided",
331
+ ):
332
+ object_storage_service.upload_buffer(buffer=io.BytesIO(b"test"))
333
+
334
+ with pytest.raises(
335
+ ValueError,
336
+ match="Either source_path or source_bucket and source_key must be provided",
337
+ ):
338
+ object_storage_service.copy_file(dest_path="/tmp/test")
339
+
340
+
341
+ def test_large_file_upload_download(
342
+ object_storage_service: ObjectStorageService, test_dir: str
343
+ ) -> None:
344
+ """
345
+ Test uploading and downloading a larger file (10MB).
346
+ """
347
+ size_mb = 10
348
+ test_content = fake.binary(length=size_mb * 1024 * 1024)
349
+ file_path = f"{test_dir}/large_{fake.file_name()}"
350
+
351
+ buffer = io.BytesIO(test_content)
352
+ object_storage_service.upload_buffer(buffer=buffer, destination_path=file_path)
353
+
354
+ size = object_storage_service.get_file_size(path=file_path)
355
+ assert size == len(test_content)
356
+
357
+ result = object_storage_service.read_file(path=file_path)
358
+ assert result == test_content
359
+ assert len(result) == size_mb * 1024 * 1024