cledar-sdk 2.0.2__py3-none-any.whl → 2.0.3__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.
- cledar/__init__.py +0 -0
- cledar/kafka/README.md +239 -0
- cledar/kafka/__init__.py +40 -0
- cledar/kafka/clients/base.py +98 -0
- cledar/kafka/clients/consumer.py +110 -0
- cledar/kafka/clients/producer.py +80 -0
- cledar/kafka/config/schemas.py +178 -0
- cledar/kafka/exceptions.py +22 -0
- cledar/kafka/handlers/dead_letter.py +82 -0
- cledar/kafka/handlers/parser.py +49 -0
- cledar/kafka/logger.py +3 -0
- cledar/kafka/models/input.py +13 -0
- cledar/kafka/models/message.py +10 -0
- cledar/kafka/models/output.py +8 -0
- cledar/kafka/tests/.env.test.kafka +3 -0
- cledar/kafka/tests/README.md +216 -0
- cledar/kafka/tests/conftest.py +104 -0
- cledar/kafka/tests/integration/__init__.py +1 -0
- cledar/kafka/tests/integration/conftest.py +78 -0
- cledar/kafka/tests/integration/helpers.py +47 -0
- cledar/kafka/tests/integration/test_consumer_integration.py +375 -0
- cledar/kafka/tests/integration/test_integration.py +394 -0
- cledar/kafka/tests/integration/test_producer_consumer_interaction.py +388 -0
- cledar/kafka/tests/integration/test_producer_integration.py +217 -0
- cledar/kafka/tests/unit/__init__.py +1 -0
- cledar/kafka/tests/unit/test_base_kafka_client.py +391 -0
- cledar/kafka/tests/unit/test_config_validation.py +609 -0
- cledar/kafka/tests/unit/test_dead_letter_handler.py +443 -0
- cledar/kafka/tests/unit/test_error_handling.py +674 -0
- cledar/kafka/tests/unit/test_input_parser.py +310 -0
- cledar/kafka/tests/unit/test_input_parser_comprehensive.py +489 -0
- cledar/kafka/tests/unit/test_utils.py +25 -0
- cledar/kafka/tests/unit/test_utils_comprehensive.py +408 -0
- cledar/kafka/utils/callbacks.py +19 -0
- cledar/kafka/utils/messages.py +28 -0
- cledar/kafka/utils/topics.py +2 -0
- cledar/kserve/README.md +352 -0
- cledar/kserve/__init__.py +3 -0
- cledar/kserve/tests/__init__.py +0 -0
- cledar/kserve/tests/test_utils.py +64 -0
- cledar/kserve/utils.py +27 -0
- cledar/logging/README.md +53 -0
- cledar/logging/__init__.py +3 -0
- cledar/logging/tests/test_universal_plaintext_formatter.py +249 -0
- cledar/logging/universal_plaintext_formatter.py +94 -0
- cledar/monitoring/README.md +71 -0
- cledar/monitoring/__init__.py +3 -0
- cledar/monitoring/monitoring_server.py +112 -0
- cledar/monitoring/tests/integration/test_monitoring_server_int.py +162 -0
- cledar/monitoring/tests/test_monitoring_server.py +59 -0
- cledar/nonce/README.md +99 -0
- cledar/nonce/__init__.py +3 -0
- cledar/nonce/nonce_service.py +36 -0
- cledar/nonce/tests/__init__.py +0 -0
- cledar/nonce/tests/test_nonce_service.py +136 -0
- cledar/redis/README.md +536 -0
- cledar/redis/__init__.py +15 -0
- cledar/redis/async_example.py +111 -0
- cledar/redis/example.py +37 -0
- cledar/redis/exceptions.py +22 -0
- cledar/redis/logger.py +3 -0
- cledar/redis/model.py +10 -0
- cledar/redis/redis.py +525 -0
- cledar/redis/redis_config_store.py +252 -0
- cledar/redis/tests/test_async_integration_redis.py +158 -0
- cledar/redis/tests/test_async_redis_service.py +380 -0
- cledar/redis/tests/test_integration_redis.py +119 -0
- cledar/redis/tests/test_redis_service.py +319 -0
- cledar/storage/README.md +529 -0
- cledar/storage/__init__.py +4 -0
- cledar/storage/constants.py +3 -0
- cledar/storage/exceptions.py +50 -0
- cledar/storage/models.py +19 -0
- cledar/storage/object_storage.py +955 -0
- cledar/storage/tests/conftest.py +18 -0
- cledar/storage/tests/test_abfs.py +164 -0
- cledar/storage/tests/test_integration_filesystem.py +359 -0
- cledar/storage/tests/test_integration_s3.py +453 -0
- cledar/storage/tests/test_local.py +384 -0
- cledar/storage/tests/test_s3.py +521 -0
- {cledar_sdk-2.0.2.dist-info → cledar_sdk-2.0.3.dist-info}/METADATA +1 -1
- cledar_sdk-2.0.3.dist-info/RECORD +84 -0
- cledar_sdk-2.0.2.dist-info/RECORD +0 -4
- {cledar_sdk-2.0.2.dist-info → cledar_sdk-2.0.3.dist-info}/WHEEL +0 -0
- {cledar_sdk-2.0.2.dist-info → cledar_sdk-2.0.3.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
# mypy: disable-error-code=no-untyped-def
|
|
2
|
+
import io
|
|
3
|
+
from contextlib import contextmanager
|
|
4
|
+
from unittest.mock import MagicMock, patch
|
|
5
|
+
|
|
6
|
+
import pytest
|
|
7
|
+
from faker import Faker
|
|
8
|
+
|
|
9
|
+
from cledar.storage.exceptions import ReadFileError, UploadFileError
|
|
10
|
+
from cledar.storage.models import ObjectStorageServiceConfig
|
|
11
|
+
from cledar.storage.object_storage import ObjectStorageService
|
|
12
|
+
|
|
13
|
+
fake = Faker()
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@pytest.fixture(name="object_storage_service")
|
|
17
|
+
@patch("fsspec.filesystem")
|
|
18
|
+
def fixture_object_storage_service(
|
|
19
|
+
fsspec_client: MagicMock, object_storage_config: ObjectStorageServiceConfig
|
|
20
|
+
) -> ObjectStorageService:
|
|
21
|
+
fsspec_client.return_value = MagicMock()
|
|
22
|
+
return ObjectStorageService(object_storage_config)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@patch("fsspec.filesystem")
|
|
26
|
+
def test_init(
|
|
27
|
+
fsspec_client: MagicMock, object_storage_config: ObjectStorageServiceConfig
|
|
28
|
+
) -> None:
|
|
29
|
+
ObjectStorageService(object_storage_config)
|
|
30
|
+
|
|
31
|
+
fsspec_client.assert_any_call(
|
|
32
|
+
"s3",
|
|
33
|
+
key=object_storage_config.s3_access_key,
|
|
34
|
+
secret=object_storage_config.s3_secret_key,
|
|
35
|
+
client_kwargs={"endpoint_url": object_storage_config.s3_endpoint_url},
|
|
36
|
+
max_concurrency=object_storage_config.s3_max_concurrency,
|
|
37
|
+
)
|
|
38
|
+
fsspec_client.assert_any_call(
|
|
39
|
+
"file",
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def test_upload_buffer_local_success(
|
|
44
|
+
object_storage_service: ObjectStorageService,
|
|
45
|
+
) -> None:
|
|
46
|
+
"""
|
|
47
|
+
Test successful buffer upload to filesystem.
|
|
48
|
+
"""
|
|
49
|
+
buffer_str = io.StringIO(fake.text())
|
|
50
|
+
buffer_bytes = io.BytesIO(buffer_str.getvalue().encode())
|
|
51
|
+
destination_path = fake.file_path()
|
|
52
|
+
|
|
53
|
+
mock_file = MagicMock()
|
|
54
|
+
mock_file.write = MagicMock()
|
|
55
|
+
|
|
56
|
+
@contextmanager
|
|
57
|
+
def open_cm(*_args: object, **_kwargs: object):
|
|
58
|
+
yield mock_file
|
|
59
|
+
|
|
60
|
+
object_storage_service.local_client.open = MagicMock(
|
|
61
|
+
side_effect=lambda *a, **k: open_cm()
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
object_storage_service.upload_buffer(
|
|
65
|
+
buffer=buffer_bytes, destination_path=destination_path
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
object_storage_service.local_client.open.assert_called_once_with(
|
|
69
|
+
path=destination_path, mode="wb"
|
|
70
|
+
)
|
|
71
|
+
mock_file.write.assert_called_once_with(buffer_bytes.getbuffer())
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def test_upload_buffer_local_exception(
|
|
75
|
+
object_storage_service: ObjectStorageService,
|
|
76
|
+
) -> None:
|
|
77
|
+
"""
|
|
78
|
+
Test buffer upload to filesystem with write exception.
|
|
79
|
+
"""
|
|
80
|
+
buffer_str = io.StringIO(fake.text())
|
|
81
|
+
buffer_bytes = io.BytesIO(buffer_str.getvalue().encode())
|
|
82
|
+
destination_path = fake.file_path()
|
|
83
|
+
|
|
84
|
+
class Writer:
|
|
85
|
+
def write(self, _b: bytes) -> None:
|
|
86
|
+
raise UploadFileError
|
|
87
|
+
|
|
88
|
+
@contextmanager
|
|
89
|
+
def open_cm(*_args: object, **_kwargs: object):
|
|
90
|
+
yield Writer()
|
|
91
|
+
|
|
92
|
+
object_storage_service.local_client.open = MagicMock(
|
|
93
|
+
side_effect=lambda *a, **k: open_cm()
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
with pytest.raises(UploadFileError):
|
|
97
|
+
object_storage_service.upload_buffer(
|
|
98
|
+
buffer=buffer_bytes, destination_path=destination_path
|
|
99
|
+
)
|
|
100
|
+
object_storage_service.local_client.open.assert_called_once()
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def test_upload_buffer_local_missing_params(
|
|
104
|
+
object_storage_service: ObjectStorageService,
|
|
105
|
+
) -> None:
|
|
106
|
+
"""
|
|
107
|
+
Test buffer upload with missing parameters.
|
|
108
|
+
"""
|
|
109
|
+
buffer_str = io.StringIO(fake.text())
|
|
110
|
+
buffer_bytes = io.BytesIO(buffer_str.getvalue().encode())
|
|
111
|
+
|
|
112
|
+
with pytest.raises(
|
|
113
|
+
ValueError, match="Either destination_path or bucket and key must be provided"
|
|
114
|
+
):
|
|
115
|
+
object_storage_service.upload_buffer(buffer=buffer_bytes)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def test_read_file_local_success(
|
|
119
|
+
object_storage_service: ObjectStorageService,
|
|
120
|
+
) -> None:
|
|
121
|
+
"""
|
|
122
|
+
Test successful file read from filesystem.
|
|
123
|
+
"""
|
|
124
|
+
path = fake.file_path()
|
|
125
|
+
expected_content = fake.text().encode()
|
|
126
|
+
|
|
127
|
+
mock_file = MagicMock()
|
|
128
|
+
mock_file.read.return_value = expected_content
|
|
129
|
+
|
|
130
|
+
@contextmanager
|
|
131
|
+
def open_cm(*_args: object, **_kwargs: object):
|
|
132
|
+
yield mock_file
|
|
133
|
+
|
|
134
|
+
object_storage_service.local_client.open = MagicMock(
|
|
135
|
+
side_effect=lambda *a, **k: open_cm()
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
result = object_storage_service.read_file(path=path)
|
|
139
|
+
assert result == expected_content
|
|
140
|
+
object_storage_service.local_client.open.assert_called_once_with(
|
|
141
|
+
path=path, mode="rb"
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def test_read_file_local_retry_mechanism(
|
|
146
|
+
object_storage_service: ObjectStorageService,
|
|
147
|
+
) -> None:
|
|
148
|
+
"""
|
|
149
|
+
Test file read retry mechanism on filesystem errors.
|
|
150
|
+
"""
|
|
151
|
+
path = fake.file_path()
|
|
152
|
+
expected_content = fake.text().encode()
|
|
153
|
+
|
|
154
|
+
mock_file = MagicMock()
|
|
155
|
+
mock_file.read.return_value = expected_content
|
|
156
|
+
|
|
157
|
+
@contextmanager
|
|
158
|
+
def open_cm(*_args: object, **_kwargs: object):
|
|
159
|
+
yield mock_file
|
|
160
|
+
|
|
161
|
+
call_count = 0
|
|
162
|
+
|
|
163
|
+
def side_effect(*_args, **_kwargs):
|
|
164
|
+
nonlocal call_count
|
|
165
|
+
call_count += 1
|
|
166
|
+
if call_count <= 2:
|
|
167
|
+
raise OSError("Temporary error")
|
|
168
|
+
return open_cm()
|
|
169
|
+
|
|
170
|
+
object_storage_service.local_client.open = MagicMock(side_effect=side_effect)
|
|
171
|
+
|
|
172
|
+
result = object_storage_service.read_file(path=path, max_tries=3)
|
|
173
|
+
|
|
174
|
+
assert result == expected_content
|
|
175
|
+
assert call_count == 3
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def test_read_file_local_max_retries_exceeded(
|
|
179
|
+
object_storage_service: ObjectStorageService,
|
|
180
|
+
) -> None:
|
|
181
|
+
"""
|
|
182
|
+
Test file read when max retries are exceeded.
|
|
183
|
+
"""
|
|
184
|
+
path = fake.file_path()
|
|
185
|
+
|
|
186
|
+
@contextmanager
|
|
187
|
+
def open_cm(*_args: object, **_kwargs: object):
|
|
188
|
+
raise ReadFileError
|
|
189
|
+
|
|
190
|
+
object_storage_service.local_client.open = MagicMock(
|
|
191
|
+
side_effect=lambda *a, **k: open_cm()
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
with pytest.raises(ReadFileError):
|
|
195
|
+
object_storage_service.read_file(path=path, max_tries=2)
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def test_read_file_local_missing_params(
|
|
199
|
+
object_storage_service: ObjectStorageService,
|
|
200
|
+
) -> None:
|
|
201
|
+
"""
|
|
202
|
+
Test file read with missing parameters.
|
|
203
|
+
"""
|
|
204
|
+
with pytest.raises(
|
|
205
|
+
ValueError, match="Either path or bucket and key must be provided"
|
|
206
|
+
):
|
|
207
|
+
object_storage_service.read_file()
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def test_upload_file_local_success(
|
|
211
|
+
object_storage_service: ObjectStorageService,
|
|
212
|
+
) -> None:
|
|
213
|
+
"""
|
|
214
|
+
Test successful file upload to filesystem.
|
|
215
|
+
"""
|
|
216
|
+
file_path = fake.file_path()
|
|
217
|
+
destination_path = fake.file_path()
|
|
218
|
+
|
|
219
|
+
object_storage_service.local_client.put = MagicMock()
|
|
220
|
+
|
|
221
|
+
object_storage_service.upload_file(
|
|
222
|
+
file_path=file_path, destination_path=destination_path
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
object_storage_service.local_client.put.assert_called_once_with(
|
|
226
|
+
lpath=file_path, rpath=destination_path
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def test_upload_file_local_exception(
|
|
231
|
+
object_storage_service: ObjectStorageService,
|
|
232
|
+
) -> None:
|
|
233
|
+
"""
|
|
234
|
+
Test file upload to filesystem with exception.
|
|
235
|
+
"""
|
|
236
|
+
file_path = fake.file_path()
|
|
237
|
+
destination_path = fake.file_path()
|
|
238
|
+
|
|
239
|
+
object_storage_service.local_client.put.side_effect = UploadFileError
|
|
240
|
+
|
|
241
|
+
with pytest.raises(UploadFileError):
|
|
242
|
+
object_storage_service.upload_file(
|
|
243
|
+
file_path=file_path, destination_path=destination_path
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
object_storage_service.local_client.put.assert_called_once_with(
|
|
247
|
+
lpath=file_path, rpath=destination_path
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
def test_upload_file_local_missing_params(
|
|
252
|
+
object_storage_service: ObjectStorageService,
|
|
253
|
+
) -> None:
|
|
254
|
+
"""
|
|
255
|
+
Test file upload with missing parameters.
|
|
256
|
+
"""
|
|
257
|
+
file_path = fake.file_path()
|
|
258
|
+
|
|
259
|
+
with pytest.raises(
|
|
260
|
+
ValueError, match="Either destination_path or bucket and key must be provided"
|
|
261
|
+
):
|
|
262
|
+
object_storage_service.upload_file(file_path=file_path)
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
def test_list_objects_recursive_local(
|
|
266
|
+
object_storage_service: ObjectStorageService,
|
|
267
|
+
) -> None:
|
|
268
|
+
path = "/tmp/test"
|
|
269
|
+
mock_objects = [
|
|
270
|
+
"/tmp/test/file1.txt",
|
|
271
|
+
"/tmp/test/file2.txt",
|
|
272
|
+
"/tmp/test/subfolder/file3.txt",
|
|
273
|
+
]
|
|
274
|
+
object_storage_service.local_client.find.return_value = mock_objects
|
|
275
|
+
|
|
276
|
+
result = object_storage_service.list_objects(path=path, recursive=True)
|
|
277
|
+
|
|
278
|
+
assert len(result) == 3
|
|
279
|
+
assert "/tmp/test/file1.txt" in result
|
|
280
|
+
object_storage_service.local_client.find.assert_called_once()
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
def test_list_objects_non_recursive_local(
|
|
284
|
+
object_storage_service: ObjectStorageService,
|
|
285
|
+
) -> None:
|
|
286
|
+
path = "/tmp/test"
|
|
287
|
+
mock_objects = ["/tmp/test/file1.txt", "/tmp/test/file2.txt"]
|
|
288
|
+
object_storage_service.local_client.ls.return_value = mock_objects
|
|
289
|
+
|
|
290
|
+
result = object_storage_service.list_objects(path=path, recursive=False)
|
|
291
|
+
|
|
292
|
+
assert len(result) == 2
|
|
293
|
+
object_storage_service.local_client.ls.assert_called_once()
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
def test_delete_file_local(object_storage_service: ObjectStorageService) -> None:
|
|
297
|
+
path = "/tmp/test/file.txt"
|
|
298
|
+
|
|
299
|
+
object_storage_service.delete_file(path=path)
|
|
300
|
+
|
|
301
|
+
object_storage_service.local_client.rm.assert_called_once_with(path)
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
def test_file_exists_true_local(object_storage_service: ObjectStorageService) -> None:
|
|
305
|
+
path = "/tmp/test/file.txt"
|
|
306
|
+
object_storage_service.local_client.exists.return_value = True
|
|
307
|
+
|
|
308
|
+
result = object_storage_service.file_exists(path=path)
|
|
309
|
+
|
|
310
|
+
assert result is True
|
|
311
|
+
object_storage_service.local_client.exists.assert_called_once_with(path)
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
def test_file_exists_false_local(object_storage_service: ObjectStorageService) -> None:
|
|
315
|
+
path = "/tmp/test/file.txt"
|
|
316
|
+
object_storage_service.local_client.exists.return_value = False
|
|
317
|
+
|
|
318
|
+
result = object_storage_service.file_exists(path=path)
|
|
319
|
+
|
|
320
|
+
assert result is False
|
|
321
|
+
object_storage_service.local_client.exists.assert_called_once_with(path)
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
def test_download_file_local(object_storage_service: ObjectStorageService) -> None:
|
|
325
|
+
source_path = "/tmp/source/file.txt"
|
|
326
|
+
dest_path = "/tmp/dest/file.txt"
|
|
327
|
+
|
|
328
|
+
object_storage_service.download_file(dest_path, source_path=source_path)
|
|
329
|
+
|
|
330
|
+
object_storage_service.local_client.get.assert_called_once_with(
|
|
331
|
+
source_path, dest_path
|
|
332
|
+
)
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
def test_get_file_size_local(object_storage_service: ObjectStorageService) -> None:
|
|
336
|
+
path = "/tmp/test/file.txt"
|
|
337
|
+
expected_size = 2048
|
|
338
|
+
object_storage_service.local_client.info.return_value = {"size": expected_size}
|
|
339
|
+
|
|
340
|
+
result = object_storage_service.get_file_size(path=path)
|
|
341
|
+
|
|
342
|
+
assert result == expected_size
|
|
343
|
+
object_storage_service.local_client.info.assert_called_once_with(path)
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
def test_get_file_info_local(object_storage_service: ObjectStorageService) -> None:
|
|
347
|
+
path = "/tmp/test/file.txt"
|
|
348
|
+
expected_info = {
|
|
349
|
+
"size": 2048,
|
|
350
|
+
"mtime": 1234567890,
|
|
351
|
+
"type": "file",
|
|
352
|
+
}
|
|
353
|
+
object_storage_service.local_client.info.return_value = expected_info
|
|
354
|
+
|
|
355
|
+
result = object_storage_service.get_file_info(path=path)
|
|
356
|
+
|
|
357
|
+
assert result == expected_info
|
|
358
|
+
object_storage_service.local_client.info.assert_called_once_with(path)
|
|
359
|
+
|
|
360
|
+
|
|
361
|
+
def test_copy_file_local_to_local(
|
|
362
|
+
object_storage_service: ObjectStorageService,
|
|
363
|
+
) -> None:
|
|
364
|
+
source_path = "/tmp/source/file.txt"
|
|
365
|
+
dest_path = "/tmp/dest/file.txt"
|
|
366
|
+
|
|
367
|
+
object_storage_service.copy_file(source_path=source_path, dest_path=dest_path)
|
|
368
|
+
|
|
369
|
+
object_storage_service.local_client.copy.assert_called_once_with(
|
|
370
|
+
source_path, dest_path
|
|
371
|
+
)
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
def test_move_file_local_to_local(
|
|
375
|
+
object_storage_service: ObjectStorageService,
|
|
376
|
+
) -> None:
|
|
377
|
+
source_path = "/tmp/source/file.txt"
|
|
378
|
+
dest_path = "/tmp/dest/file.txt"
|
|
379
|
+
|
|
380
|
+
object_storage_service.move_file(source_path=source_path, dest_path=dest_path)
|
|
381
|
+
|
|
382
|
+
object_storage_service.local_client.move.assert_called_once_with(
|
|
383
|
+
source_path, dest_path
|
|
384
|
+
)
|