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,375 @@
1
+ """
2
+ Integration tests for Kafka consumer using real Kafka instance.
3
+ """
4
+
5
+ import time
6
+ from collections.abc import Generator
7
+
8
+ import pytest
9
+
10
+ from cledar.kafka.clients.consumer import KafkaConsumer
11
+ from cledar.kafka.clients.producer import KafkaProducer
12
+ from cledar.kafka.config.schemas import KafkaConsumerConfig, KafkaProducerConfig
13
+ from cledar.kafka.exceptions import KafkaConsumerNotConnectedError
14
+
15
+
16
+ @pytest.fixture
17
+ def producer_config(kafka_bootstrap_servers: str) -> KafkaProducerConfig:
18
+ """Create producer configuration for integration tests."""
19
+ return KafkaProducerConfig(
20
+ kafka_servers=kafka_bootstrap_servers,
21
+ kafka_group_id="integration-test-producer",
22
+ kafka_topic_prefix="integration-test.",
23
+ kafka_block_buffer_time_sec=1,
24
+ kafka_connection_check_timeout_sec=5,
25
+ kafka_connection_check_interval_sec=10,
26
+ )
27
+
28
+
29
+ @pytest.fixture
30
+ def consumer_config(kafka_bootstrap_servers: str) -> KafkaConsumerConfig:
31
+ """Create consumer configuration for integration tests."""
32
+ return KafkaConsumerConfig(
33
+ kafka_servers=kafka_bootstrap_servers,
34
+ kafka_group_id="integration-test-consumer",
35
+ kafka_offset="earliest",
36
+ kafka_topic_prefix="integration-test.",
37
+ kafka_block_consumer_time_sec=1,
38
+ kafka_connection_check_timeout_sec=5,
39
+ kafka_auto_commit_interval_ms=1000,
40
+ kafka_connection_check_interval_sec=10,
41
+ )
42
+
43
+
44
+ @pytest.fixture
45
+ def producer(
46
+ producer_config: KafkaProducerConfig,
47
+ ) -> Generator[KafkaProducer, None, None]:
48
+ """Create and connect a Kafka producer."""
49
+ producer = KafkaProducer(producer_config)
50
+ producer.connect()
51
+ yield producer
52
+ producer.shutdown()
53
+
54
+
55
+ @pytest.fixture
56
+ def consumer(
57
+ consumer_config: KafkaConsumerConfig,
58
+ ) -> Generator[KafkaConsumer, None, None]:
59
+ """Create and connect a Kafka consumer."""
60
+ consumer = KafkaConsumer(consumer_config)
61
+ consumer.connect()
62
+ yield consumer
63
+ consumer.shutdown()
64
+
65
+
66
+ def test_consumer_connect_and_subscribe(consumer: KafkaConsumer) -> None:
67
+ """Test consumer connection and subscription."""
68
+ topic = "test-consumer-basic"
69
+
70
+ # Subscribe to topic
71
+ consumer.subscribe([topic])
72
+
73
+ # Verify consumer is connected
74
+ assert consumer.is_alive()
75
+
76
+
77
+ def test_consumer_consume_messages(
78
+ producer: KafkaProducer, consumer: KafkaConsumer
79
+ ) -> None:
80
+ """Test consumer message consumption."""
81
+ topic = "test-consumer-consume"
82
+ test_value = '{"id": "test-1", "message": "Hello Consumer!"}'
83
+ test_key = "test-key"
84
+
85
+ # Send message first and subscribe via helper
86
+ producer.send(topic=topic, value=test_value, key=test_key)
87
+ time.sleep(2)
88
+ consumer.subscribe([topic])
89
+ time.sleep(1)
90
+
91
+ # Consume the message
92
+ message = consumer.consume_next()
93
+
94
+ assert message is not None
95
+ assert message.topic == f"integration-test.{topic}"
96
+ assert message.key == test_key
97
+ assert message.value == test_value
98
+
99
+
100
+ def test_consumer_consume_multiple_messages(
101
+ producer: KafkaProducer, consumer: KafkaConsumer
102
+ ) -> None:
103
+ """Test consumer consuming multiple messages."""
104
+ topic = "test-consumer-multiple"
105
+
106
+ # Send multiple messages first to create the topic
107
+ messages = []
108
+ for i in range(5):
109
+ test_value = f'{{"id": "test-{i}", "message": "Message {i}"}}'
110
+ test_key = f"key-{i}"
111
+ messages.append((test_value, test_key))
112
+ producer.send(topic=topic, value=test_value, key=test_key)
113
+
114
+ # Wait for topic and subscribe
115
+ time.sleep(3)
116
+ consumer.subscribe([topic])
117
+ time.sleep(1)
118
+
119
+ # Consume all messages
120
+ received_messages = []
121
+ for _ in range(5):
122
+ message = consumer.consume_next()
123
+ if message:
124
+ received_messages.append(message)
125
+
126
+ assert len(received_messages) == 5
127
+
128
+ # Verify message content
129
+ for i, message in enumerate(received_messages):
130
+ assert message.topic == f"integration-test.{topic}"
131
+ assert message.key == f"key-{i}"
132
+ assert message.value == messages[i][0]
133
+
134
+
135
+ def test_consumer_commit_messages(
136
+ producer: KafkaProducer, consumer: KafkaConsumer
137
+ ) -> None:
138
+ """Test consumer message committing."""
139
+ topic = "test-consumer-commit"
140
+ test_value = '{"id": "test-commit", "message": "Commit test"}'
141
+ test_key = "commit-key"
142
+
143
+ # Send message first, then subscribe
144
+ producer.send(topic=topic, value=test_value, key=test_key)
145
+ time.sleep(2)
146
+ consumer.subscribe([topic])
147
+ time.sleep(1)
148
+
149
+ # Consume the message
150
+ message = consumer.consume_next()
151
+ assert message is not None
152
+
153
+ # Commit the message
154
+ consumer.commit(message)
155
+
156
+ # Verify commit was successful (no exception raised)
157
+ assert True
158
+
159
+
160
+ def test_consumer_consume_with_special_characters(
161
+ producer: KafkaProducer, consumer: KafkaConsumer
162
+ ) -> None:
163
+ """Test consumer consuming messages with special characters."""
164
+ topic = "test-consumer-special-chars"
165
+ test_value = '{"id": "test-special", "message": "Special chars: @#$%^&*()"}'
166
+ test_key = "special-key-with-chars: @#$%^&*()"
167
+
168
+ # Send message first, then subscribe
169
+ producer.send(topic=topic, value=test_value, key=test_key)
170
+ time.sleep(2)
171
+ consumer.subscribe([topic])
172
+ time.sleep(1)
173
+
174
+ # Consume the message
175
+ message = consumer.consume_next()
176
+
177
+ assert message is not None
178
+ assert message.value == test_value
179
+ assert message.key == test_key
180
+
181
+
182
+ def test_consumer_consume_with_unicode(
183
+ producer: KafkaProducer, consumer: KafkaConsumer
184
+ ) -> None:
185
+ """Test consumer consuming messages with unicode characters."""
186
+ topic = "test-consumer-unicode"
187
+ test_value = '{"id": "test-unicode", "message": "Unicode: 测试名称"}'
188
+ test_key = "unicode-key-测试"
189
+
190
+ # Send message first to create the topic
191
+ producer.send(topic=topic, value=test_value, key=test_key)
192
+
193
+ # Wait for topic to be created and message to be sent
194
+ time.sleep(2)
195
+
196
+ # Subscribe consumer to topic
197
+ consumer.subscribe([topic])
198
+
199
+ # Wait for subscription to take effect
200
+ time.sleep(1)
201
+
202
+ # Consume the message
203
+ message = consumer.consume_next()
204
+
205
+ assert message is not None
206
+ assert message.value == test_value
207
+ assert message.key == test_key
208
+
209
+
210
+ def test_consumer_consume_large_messages(
211
+ producer: KafkaProducer, consumer: KafkaConsumer
212
+ ) -> None:
213
+ """Test consumer consuming large messages."""
214
+ topic = "test-consumer-large"
215
+
216
+ # Create a large message
217
+ large_data = "x" * 10000
218
+ test_value = f'{{"id": "test-large", "data": "{large_data}"}}'
219
+ test_key = "large-key"
220
+
221
+ # Send message first, then subscribe
222
+ producer.send(topic=topic, value=test_value, key=test_key)
223
+ time.sleep(3)
224
+ consumer.subscribe([topic])
225
+ time.sleep(1)
226
+
227
+ # Consume the message
228
+ message = consumer.consume_next()
229
+
230
+ assert message is not None
231
+ assert message.value == test_value
232
+ assert len(message.value) > 10000
233
+
234
+
235
+ def test_consumer_connection_check(consumer: KafkaConsumer) -> None:
236
+ """Test consumer connection checking."""
237
+ # Verify consumer is connected
238
+ assert consumer.is_alive()
239
+
240
+ # Check connection explicitly
241
+ consumer.check_connection()
242
+
243
+ # Should not raise any exception
244
+ assert True
245
+
246
+
247
+ def test_consumer_not_connected_error() -> None:
248
+ """Test consumer error when not connected."""
249
+ config = KafkaConsumerConfig(
250
+ kafka_servers="localhost:9092",
251
+ kafka_group_id="test-group",
252
+ )
253
+
254
+ consumer = KafkaConsumer(config)
255
+
256
+ # Should raise error when trying to subscribe without connecting
257
+ with pytest.raises(KafkaConsumerNotConnectedError):
258
+ consumer.subscribe(["test-topic"])
259
+
260
+
261
+ def test_consumer_shutdown(consumer_config: KafkaConsumerConfig) -> None:
262
+ """Test consumer shutdown."""
263
+ consumer = KafkaConsumer(consumer_config)
264
+ consumer.connect()
265
+
266
+ # Verify consumer is connected
267
+ assert consumer.is_alive()
268
+
269
+ # Shutdown consumer
270
+ consumer.shutdown()
271
+
272
+ # Consumer should be disconnected after shutdown
273
+ assert not consumer.is_alive()
274
+
275
+
276
+ def test_consumer_topic_prefix(
277
+ producer: KafkaProducer, consumer: KafkaConsumer
278
+ ) -> None:
279
+ """Test consumer topic prefix functionality."""
280
+ topic = "test-prefix"
281
+ test_value = '{"id": "test-prefix", "message": "Prefix test"}'
282
+ test_key = "prefix-key"
283
+
284
+ # Send message first to create the topic
285
+ producer.send(topic=topic, value=test_value, key=test_key)
286
+
287
+ # Wait for topic to be created and message to be sent
288
+ time.sleep(2)
289
+
290
+ # Subscribe consumer to topic (should use prefix from config)
291
+ consumer.subscribe([topic])
292
+
293
+ # Wait for subscription to take effect
294
+ time.sleep(1)
295
+
296
+ # Consume the message
297
+ message = consumer.consume_next()
298
+
299
+ assert message is not None
300
+ assert message.topic == f"integration-test.{topic}"
301
+ assert message.value == test_value
302
+
303
+
304
+ def test_consumer_offset_behavior(
305
+ producer: KafkaProducer, consumer: KafkaConsumer
306
+ ) -> None:
307
+ """Test consumer offset behavior."""
308
+ topic = "test-consumer-offset"
309
+
310
+ # Send a message first
311
+ test_value = '{"id": "test-offset", "message": "Offset test"}'
312
+ test_key = "offset-key"
313
+ producer.send(topic=topic, value=test_value, key=test_key)
314
+
315
+ # Wait and subscribe (earliest offset)
316
+ time.sleep(2)
317
+ consumer.subscribe([topic])
318
+ time.sleep(1)
319
+
320
+ # Consume the message
321
+ message = consumer.consume_next()
322
+
323
+ assert message is not None
324
+ assert message.value == test_value
325
+
326
+
327
+ def test_consumer_group_behavior(
328
+ producer: KafkaProducer, consumer_config: KafkaConsumerConfig
329
+ ) -> None:
330
+ """Test consumer group behavior."""
331
+ topic = "test-consumer-group"
332
+ test_value = '{"id": "test-group", "message": "Group test"}'
333
+ test_key = "group-key"
334
+
335
+ # Send a message first to create the topic
336
+ producer.send(topic=topic, value=test_value, key=test_key)
337
+
338
+ # Wait for topic to be created
339
+ time.sleep(2)
340
+
341
+ # Create two consumers with the same group
342
+ consumer1 = KafkaConsumer(consumer_config)
343
+ consumer2 = KafkaConsumer(consumer_config)
344
+
345
+ consumer1.connect()
346
+ consumer2.connect()
347
+
348
+ try:
349
+ # Subscribe both consumers to the same topic
350
+ consumer1.subscribe([topic])
351
+ consumer2.subscribe([topic])
352
+
353
+ # Wait for subscription to take effect
354
+ time.sleep(1)
355
+
356
+ # Send another message after consumers are subscribed
357
+ producer.send(topic=topic, value=test_value, key=test_key)
358
+
359
+ # Wait for message to be sent
360
+ time.sleep(1)
361
+
362
+ # Both consumers should be alive
363
+ assert consumer1.is_alive()
364
+ assert consumer2.is_alive()
365
+
366
+ # One of them should consume the message
367
+ message1 = consumer1.consume_next()
368
+ message2 = consumer2.consume_next()
369
+
370
+ # At least one should have received the message
371
+ assert message1 is not None or message2 is not None
372
+
373
+ finally:
374
+ consumer1.shutdown()
375
+ consumer2.shutdown()