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.
Files changed (85) hide show
  1. cledar/__init__.py +0 -0
  2. cledar/kafka/README.md +239 -0
  3. cledar/kafka/__init__.py +40 -0
  4. cledar/kafka/clients/base.py +98 -0
  5. cledar/kafka/clients/consumer.py +110 -0
  6. cledar/kafka/clients/producer.py +80 -0
  7. cledar/kafka/config/schemas.py +178 -0
  8. cledar/kafka/exceptions.py +22 -0
  9. cledar/kafka/handlers/dead_letter.py +82 -0
  10. cledar/kafka/handlers/parser.py +49 -0
  11. cledar/kafka/logger.py +3 -0
  12. cledar/kafka/models/input.py +13 -0
  13. cledar/kafka/models/message.py +10 -0
  14. cledar/kafka/models/output.py +8 -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 +19 -0
  35. cledar/kafka/utils/messages.py +28 -0
  36. cledar/kafka/utils/topics.py +2 -0
  37. cledar/kserve/README.md +352 -0
  38. cledar/kserve/__init__.py +3 -0
  39. cledar/kserve/tests/__init__.py +0 -0
  40. cledar/kserve/tests/test_utils.py +64 -0
  41. cledar/kserve/utils.py +27 -0
  42. cledar/logging/README.md +53 -0
  43. cledar/logging/__init__.py +3 -0
  44. cledar/logging/tests/test_universal_plaintext_formatter.py +249 -0
  45. cledar/logging/universal_plaintext_formatter.py +94 -0
  46. cledar/monitoring/README.md +71 -0
  47. cledar/monitoring/__init__.py +3 -0
  48. cledar/monitoring/monitoring_server.py +112 -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 +3 -0
  53. cledar/nonce/nonce_service.py +36 -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 +15 -0
  58. cledar/redis/async_example.py +111 -0
  59. cledar/redis/example.py +37 -0
  60. cledar/redis/exceptions.py +22 -0
  61. cledar/redis/logger.py +3 -0
  62. cledar/redis/model.py +10 -0
  63. cledar/redis/redis.py +525 -0
  64. cledar/redis/redis_config_store.py +252 -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 +4 -0
  71. cledar/storage/constants.py +3 -0
  72. cledar/storage/exceptions.py +50 -0
  73. cledar/storage/models.py +19 -0
  74. cledar/storage/object_storage.py +955 -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.0.3.dist-info}/METADATA +1 -1
  82. cledar_sdk-2.0.3.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.0.3.dist-info}/WHEEL +0 -0
  85. {cledar_sdk-2.0.2.dist-info → cledar_sdk-2.0.3.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,391 @@
1
+ """
2
+ Comprehensive tests for BaseKafkaClient covering edge cases, connection monitoring,
3
+ and error handling scenarios.
4
+ """
5
+
6
+ import threading
7
+ import time
8
+ from unittest.mock import MagicMock, patch
9
+
10
+ import pytest
11
+ from confluent_kafka import Consumer, KafkaException, Producer
12
+
13
+ from cledar.kafka import (
14
+ BaseKafkaClient,
15
+ KafkaConnectionError,
16
+ KafkaConsumerConfig,
17
+ KafkaConsumerNotConnectedError,
18
+ KafkaProducerConfig,
19
+ KafkaProducerNotConnectedError,
20
+ )
21
+
22
+
23
+ @pytest.fixture
24
+ def producer_config() -> KafkaProducerConfig:
25
+ """Create a producer configuration for testing."""
26
+ return KafkaProducerConfig(
27
+ kafka_servers="localhost:9092",
28
+ kafka_group_id="test-group",
29
+ kafka_topic_prefix="test.",
30
+ kafka_block_buffer_time_sec=10,
31
+ kafka_connection_check_timeout_sec=1,
32
+ kafka_connection_check_interval_sec=1, # Short interval for testing
33
+ )
34
+
35
+
36
+ @pytest.fixture
37
+ def consumer_config() -> KafkaConsumerConfig:
38
+ """Create a consumer configuration for testing."""
39
+ return KafkaConsumerConfig(
40
+ kafka_servers="localhost:9092",
41
+ kafka_group_id="test-group",
42
+ kafka_offset="latest",
43
+ kafka_topic_prefix="test.",
44
+ kafka_block_consumer_time_sec=1,
45
+ kafka_connection_check_timeout_sec=1,
46
+ kafka_auto_commit_interval_ms=1000,
47
+ kafka_connection_check_interval_sec=1, # Short interval for testing
48
+ )
49
+
50
+
51
+ def test_init_with_producer_config(producer_config: KafkaProducerConfig) -> None:
52
+ """Test BaseKafkaClient initialization with producer config."""
53
+ client = BaseKafkaClient(config=producer_config)
54
+ assert client.config == producer_config
55
+ assert client.client is None
56
+ assert client.connection_check_thread is None
57
+ assert not client._stop_event.is_set()
58
+
59
+
60
+ def test_init_with_consumer_config(consumer_config: KafkaConsumerConfig) -> None:
61
+ """Test BaseKafkaClient initialization with consumer config."""
62
+ client = BaseKafkaClient(config=consumer_config)
63
+ assert client.config == consumer_config
64
+ assert client.client is None
65
+ assert client.connection_check_thread is None
66
+ assert not client._stop_event.is_set()
67
+
68
+
69
+ def test_is_alive_without_client(producer_config: KafkaProducerConfig) -> None:
70
+ """Test is_alive returns False when client is not connected."""
71
+ client = BaseKafkaClient(config=producer_config)
72
+ assert not client.is_alive()
73
+
74
+
75
+ @patch("cledar.kafka.clients.base.logger")
76
+ def test_check_connection_without_client_producer(
77
+ mock_logger: MagicMock, producer_config: KafkaProducerConfig
78
+ ) -> None:
79
+ """Test check_connection raises appropriate error for producer without client."""
80
+ client = BaseKafkaClient(config=producer_config)
81
+
82
+ with pytest.raises(KafkaProducerNotConnectedError):
83
+ client.check_connection()
84
+
85
+ mock_logger.error.assert_called_once()
86
+
87
+
88
+ @patch("cledar.kafka.clients.base.logger")
89
+ def test_check_connection_without_client_consumer(
90
+ mock_logger: MagicMock, consumer_config: KafkaConsumerConfig
91
+ ) -> None:
92
+ """Test check_connection raises appropriate error for consumer without client."""
93
+ client = BaseKafkaClient(config=consumer_config)
94
+
95
+ with pytest.raises(KafkaConsumerNotConnectedError):
96
+ client.check_connection()
97
+
98
+ mock_logger.error.assert_called_once()
99
+
100
+
101
+ @patch("cledar.kafka.clients.base.logger")
102
+ def test_check_connection_success(
103
+ mock_logger: MagicMock, producer_config: KafkaProducerConfig
104
+ ) -> None:
105
+ """Test successful connection check."""
106
+ client = BaseKafkaClient(config=producer_config)
107
+ mock_client = MagicMock(spec=Producer)
108
+ mock_client.list_topics.return_value = None
109
+ client.client = mock_client
110
+
111
+ # Should not raise any exception
112
+ client.check_connection()
113
+
114
+ mock_client.list_topics.assert_called_once_with(
115
+ timeout=client.config.kafka_connection_check_timeout_sec
116
+ )
117
+
118
+
119
+ @patch("cledar.kafka.clients.base.logger")
120
+ def test_check_connection_kafka_exception(
121
+ mock_logger: MagicMock, producer_config: KafkaProducerConfig
122
+ ) -> None:
123
+ """Test connection check with KafkaException."""
124
+ client = BaseKafkaClient(config=producer_config)
125
+ mock_client = MagicMock(spec=Producer)
126
+ mock_client.list_topics.side_effect = KafkaException("Connection failed")
127
+ client.client = mock_client
128
+
129
+ with pytest.raises(KafkaConnectionError) as exc_info:
130
+ client.check_connection()
131
+
132
+ assert isinstance(exc_info.value.__cause__, KafkaException)
133
+ mock_logger.exception.assert_called_once()
134
+
135
+
136
+ def test_is_alive_success(producer_config: KafkaProducerConfig) -> None:
137
+ """Test is_alive returns True when connection is successful."""
138
+ client = BaseKafkaClient(config=producer_config)
139
+ mock_client = MagicMock(spec=Producer)
140
+ mock_client.list_topics.return_value = None
141
+ client.client = mock_client
142
+
143
+ assert client.is_alive()
144
+
145
+
146
+ def test_is_alive_connection_error(producer_config: KafkaProducerConfig) -> None:
147
+ """Test is_alive returns False when connection fails."""
148
+ client = BaseKafkaClient(config=producer_config)
149
+ mock_client = MagicMock(spec=Producer)
150
+ mock_client.list_topics.side_effect = KafkaException("Connection failed")
151
+ client.client = mock_client
152
+
153
+ assert not client.is_alive()
154
+
155
+
156
+ def test_is_alive_not_connected_error(producer_config: KafkaProducerConfig) -> None:
157
+ """Test is_alive returns False when client is not connected."""
158
+ client = BaseKafkaClient(config=producer_config)
159
+ client.client = None
160
+
161
+ assert not client.is_alive()
162
+
163
+
164
+ @patch("threading.Thread")
165
+ @patch("cledar.kafka.clients.base.logger")
166
+ def test_start_connection_check_thread(
167
+ mock_logger: MagicMock, mock_thread: MagicMock, producer_config: KafkaProducerConfig
168
+ ) -> None:
169
+ """Test starting connection check thread."""
170
+ client = BaseKafkaClient(config=producer_config)
171
+ mock_thread_instance = MagicMock()
172
+ mock_thread.return_value = mock_thread_instance
173
+
174
+ client.start_connection_check_thread()
175
+
176
+ mock_thread.assert_called_once_with(target=client._monitor_connection)
177
+ mock_thread_instance.start.assert_called_once()
178
+ assert client.connection_check_thread == mock_thread_instance
179
+ # Should have logged info for initialization and thread start
180
+ assert mock_logger.info.call_count >= 1
181
+
182
+
183
+ @patch("threading.Thread")
184
+ def test_start_connection_check_thread_already_started(
185
+ mock_thread: MagicMock, producer_config: KafkaProducerConfig
186
+ ) -> None:
187
+ """Test that starting connection check thread twice doesn't create new thread."""
188
+ client = BaseKafkaClient(config=producer_config)
189
+ mock_thread_instance = MagicMock()
190
+ mock_thread.return_value = mock_thread_instance
191
+
192
+ client.start_connection_check_thread()
193
+ client.start_connection_check_thread()
194
+
195
+ # Should only be called once
196
+ mock_thread.assert_called_once()
197
+
198
+
199
+ @patch("cledar.kafka.clients.base.logger")
200
+ def test_monitor_connection_success(
201
+ mock_logger: MagicMock, producer_config: KafkaProducerConfig
202
+ ) -> None:
203
+ """Test connection monitoring with successful checks."""
204
+ client = BaseKafkaClient(config=producer_config)
205
+ mock_client = MagicMock(spec=Producer)
206
+ mock_client.list_topics.return_value = None
207
+ client.client = mock_client
208
+
209
+ # Set a very short interval for testing
210
+ client.config = KafkaProducerConfig(
211
+ kafka_servers="localhost:9092",
212
+ kafka_group_id="test-group",
213
+ kafka_connection_check_interval_sec=0, # Use 0 for immediate execution
214
+ )
215
+
216
+ # Start monitoring in a separate thread
217
+ monitor_thread = threading.Thread(target=client._monitor_connection)
218
+ monitor_thread.start()
219
+
220
+ # Let it run for a short time
221
+ time.sleep(0.05)
222
+
223
+ # Stop the monitoring
224
+ client._stop_event.set()
225
+ monitor_thread.join(timeout=1)
226
+
227
+ # Should have logged successful connections
228
+ assert any(
229
+ "connection status: Connected" in str(call)
230
+ for call in mock_logger.info.call_args_list
231
+ )
232
+
233
+
234
+ @patch("cledar.kafka.clients.base.logger")
235
+ def test_monitor_connection_failure(
236
+ mock_logger: MagicMock, producer_config: KafkaProducerConfig
237
+ ) -> None:
238
+ """Test connection monitoring with connection failures."""
239
+ client = BaseKafkaClient(config=producer_config)
240
+ mock_client = MagicMock(spec=Producer)
241
+ mock_client.list_topics.side_effect = KafkaException("Connection failed")
242
+ client.client = mock_client
243
+
244
+ # Set a very short interval for testing
245
+ client.config = KafkaProducerConfig(
246
+ kafka_servers="localhost:9092",
247
+ kafka_group_id="test-group",
248
+ kafka_connection_check_interval_sec=0, # Use 0 for immediate execution
249
+ )
250
+
251
+ # Start monitoring in a separate thread
252
+ monitor_thread = threading.Thread(target=client._monitor_connection)
253
+ monitor_thread.start()
254
+
255
+ # Let it run for a short time
256
+ time.sleep(0.05)
257
+
258
+ # Stop the monitoring
259
+ client._stop_event.set()
260
+ monitor_thread.join(timeout=1)
261
+
262
+ # Should have logged connection failures
263
+ assert any(
264
+ "connection check failed" in str(call)
265
+ for call in mock_logger.exception.call_args_list
266
+ )
267
+
268
+
269
+ @patch("cledar.kafka.clients.base.logger")
270
+ def test_shutdown_without_thread(
271
+ mock_logger: MagicMock, producer_config: KafkaProducerConfig
272
+ ) -> None:
273
+ """Test shutdown when no connection check thread is running."""
274
+ client = BaseKafkaClient(config=producer_config)
275
+ client.shutdown()
276
+
277
+ # Should not try to join a non-existent thread
278
+ mock_logger.info.assert_any_call("Closing %s...", "BaseKafkaClient")
279
+
280
+
281
+ @patch("cledar.kafka.clients.base.logger")
282
+ def test_shutdown_with_producer(
283
+ mock_logger: MagicMock, producer_config: KafkaProducerConfig
284
+ ) -> None:
285
+ """Test shutdown with producer client."""
286
+ client = BaseKafkaClient(config=producer_config)
287
+ mock_client = MagicMock(spec=Producer)
288
+ mock_thread = MagicMock()
289
+
290
+ client.client = mock_client
291
+ client.connection_check_thread = mock_thread
292
+
293
+ client.shutdown()
294
+
295
+ assert client._stop_event.is_set()
296
+ mock_thread.join.assert_called_once()
297
+ mock_client.flush.assert_called_once_with(-1)
298
+ mock_logger.info.assert_called()
299
+
300
+
301
+ @patch("cledar.kafka.clients.base.logger")
302
+ def test_shutdown_with_consumer(
303
+ mock_logger: MagicMock, consumer_config: KafkaConsumerConfig
304
+ ) -> None:
305
+ """Test shutdown with consumer client."""
306
+ client = BaseKafkaClient(config=consumer_config)
307
+ mock_client = MagicMock(spec=Consumer)
308
+ mock_thread = MagicMock()
309
+
310
+ client.client = mock_client
311
+ client.connection_check_thread = mock_thread
312
+
313
+ client.shutdown()
314
+
315
+ assert client._stop_event.is_set()
316
+ mock_thread.join.assert_called_once()
317
+ mock_client.close.assert_called_once()
318
+ mock_logger.info.assert_called()
319
+
320
+
321
+ def test_stop_event_initialization(producer_config: KafkaProducerConfig) -> None:
322
+ """Test that stop event is properly initialized."""
323
+ client = BaseKafkaClient(config=producer_config)
324
+ assert not client._stop_event.is_set()
325
+
326
+ client._stop_event.set()
327
+ assert client._stop_event.is_set()
328
+
329
+
330
+ def test_config_type_detection_producer(producer_config: KafkaProducerConfig) -> None:
331
+ """Test that producer config is correctly identified."""
332
+ client = BaseKafkaClient(config=producer_config)
333
+
334
+ # This is tested indirectly through check_connection behavior
335
+ with pytest.raises(KafkaProducerNotConnectedError):
336
+ client.check_connection()
337
+
338
+
339
+ def test_config_type_detection_consumer(consumer_config: KafkaConsumerConfig) -> None:
340
+ """Test that consumer config is correctly identified."""
341
+ client = BaseKafkaClient(config=consumer_config)
342
+
343
+ # This is tested indirectly through check_connection behavior
344
+ with pytest.raises(KafkaConsumerNotConnectedError):
345
+ client.check_connection()
346
+
347
+
348
+ @patch("cledar.kafka.clients.base.logger")
349
+ def test_post_init_logging(
350
+ mock_logger: MagicMock, producer_config: KafkaProducerConfig
351
+ ) -> None:
352
+ """Test that __post_init__ logs initialization."""
353
+ BaseKafkaClient(config=producer_config)
354
+
355
+ mock_logger.info.assert_called_once()
356
+ assert "Initializing BaseKafkaClient" in str(mock_logger.info.call_args)
357
+
358
+
359
+ def test_connection_check_timeout_configuration(
360
+ producer_config: KafkaProducerConfig,
361
+ ) -> None:
362
+ """Test that connection check timeout is properly configured."""
363
+ client = BaseKafkaClient(config=producer_config)
364
+ mock_client = MagicMock(spec=Producer)
365
+ client.client = mock_client
366
+
367
+ client.check_connection()
368
+
369
+ mock_client.list_topics.assert_called_once_with(
370
+ timeout=client.config.kafka_connection_check_timeout_sec
371
+ )
372
+
373
+
374
+ def test_connection_check_interval_configuration(
375
+ producer_config: KafkaProducerConfig,
376
+ ) -> None:
377
+ """Test that connection check interval is properly configured."""
378
+ client = BaseKafkaClient(config=producer_config)
379
+ expected_interval = 5
380
+ client.config = KafkaProducerConfig(
381
+ kafka_servers="localhost:9092",
382
+ kafka_group_id="test-group",
383
+ kafka_connection_check_interval_sec=expected_interval,
384
+ )
385
+
386
+ # Test that the interval is used in _monitor_connection
387
+ with patch.object(client._stop_event, "wait") as mock_wait:
388
+ mock_wait.return_value = True # Stop immediately
389
+ client._monitor_connection()
390
+
391
+ mock_wait.assert_called_once_with(expected_interval)