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,609 @@
1
+ """
2
+ Comprehensive tests for configuration schemas covering validation,
3
+ edge cases, and data type handling.
4
+ """
5
+
6
+ from dataclasses import FrozenInstanceError
7
+
8
+ import pytest
9
+ from pydantic import ValidationError
10
+
11
+ from cledar.kafka.config.schemas import (
12
+ KafkaConsumerConfig,
13
+ KafkaProducerConfig,
14
+ KafkaSaslMechanism,
15
+ KafkaSecurityProtocol,
16
+ )
17
+ from cledar.kafka.models.message import KafkaMessage
18
+
19
+
20
+ def test_kafka_message_creation() -> None:
21
+ """Test creating a KafkaMessage with all fields."""
22
+ message = KafkaMessage(
23
+ topic="test-topic",
24
+ value='{"id": "123"}',
25
+ key="test-key",
26
+ offset=100,
27
+ partition=0,
28
+ )
29
+
30
+ assert message.topic == "test-topic"
31
+ assert message.value == '{"id": "123"}'
32
+ assert message.key == "test-key"
33
+ assert message.offset == 100
34
+ assert message.partition == 0
35
+
36
+
37
+ def test_kafka_message_with_none_values() -> None:
38
+ """Test creating a KafkaMessage with None values."""
39
+ message = KafkaMessage(
40
+ topic="test-topic",
41
+ value=None,
42
+ key=None,
43
+ offset=None,
44
+ partition=None,
45
+ )
46
+
47
+ assert message.topic == "test-topic"
48
+ assert message.value is None
49
+ assert message.key is None
50
+ assert message.offset is None
51
+ assert message.partition is None
52
+
53
+
54
+ def test_kafka_message_with_empty_strings() -> None:
55
+ """Test creating a KafkaMessage with empty strings."""
56
+ message = KafkaMessage(
57
+ topic="",
58
+ value="",
59
+ key="",
60
+ offset=0,
61
+ partition=0,
62
+ )
63
+
64
+ assert message.topic == ""
65
+ assert message.value == ""
66
+ assert message.key == ""
67
+ assert message.offset == 0
68
+ assert message.partition == 0
69
+
70
+
71
+ def test_kafka_message_with_special_characters() -> None:
72
+ """Test creating a KafkaMessage with special characters."""
73
+ special_topic = "topic-with-special-chars: @#$%^&*()"
74
+ special_value = '{"id": "test with special chars: \n\t"\'"}'
75
+ special_key = "key-with-special-chars: @#$%^&*()"
76
+
77
+ message = KafkaMessage(
78
+ topic=special_topic,
79
+ value=special_value,
80
+ key=special_key,
81
+ offset=100,
82
+ partition=0,
83
+ )
84
+
85
+ assert message.topic == special_topic
86
+ assert message.value == special_value
87
+ assert message.key == special_key
88
+
89
+
90
+ def test_kafka_message_with_unicode() -> None:
91
+ """Test creating a KafkaMessage with unicode characters."""
92
+ unicode_topic = "topic-with-unicode-测试"
93
+ unicode_value = '{"id": "测试ID", "name": "测试名称"}'
94
+ unicode_key = "key-with-unicode-测试"
95
+
96
+ message = KafkaMessage(
97
+ topic=unicode_topic,
98
+ value=unicode_value,
99
+ key=unicode_key,
100
+ offset=100,
101
+ partition=0,
102
+ )
103
+
104
+ assert message.topic == unicode_topic
105
+ assert message.value == unicode_value
106
+ assert message.key == unicode_key
107
+
108
+
109
+ def test_kafka_message_with_large_values() -> None:
110
+ """Test creating a KafkaMessage with large values."""
111
+ large_value = '{"data": "' + "x" * 10000 + '"}'
112
+
113
+ message = KafkaMessage(
114
+ topic="test-topic",
115
+ value=large_value,
116
+ key="test-key",
117
+ offset=100,
118
+ partition=0,
119
+ )
120
+
121
+ assert message.value == large_value
122
+ assert len(message.value) > 10000
123
+
124
+
125
+ def test_kafka_message_equality() -> None:
126
+ """Test KafkaMessage equality comparison."""
127
+ message1 = KafkaMessage(
128
+ topic="test-topic",
129
+ value='{"id": "123"}',
130
+ key="test-key",
131
+ offset=100,
132
+ partition=0,
133
+ )
134
+
135
+ message2 = KafkaMessage(
136
+ topic="test-topic",
137
+ value='{"id": "123"}',
138
+ key="test-key",
139
+ offset=100,
140
+ partition=0,
141
+ )
142
+
143
+ assert message1 == message2
144
+
145
+
146
+ def test_kafka_message_inequality() -> None:
147
+ """Test KafkaMessage inequality comparison."""
148
+ message1 = KafkaMessage(
149
+ topic="test-topic",
150
+ value='{"id": "123"}',
151
+ key="test-key",
152
+ offset=100,
153
+ partition=0,
154
+ )
155
+
156
+ message2 = KafkaMessage(
157
+ topic="different-topic",
158
+ value='{"id": "123"}',
159
+ key="test-key",
160
+ offset=100,
161
+ partition=0,
162
+ )
163
+
164
+ assert message1 != message2
165
+
166
+
167
+ def test_producer_config_minimal() -> None:
168
+ """Test creating a minimal KafkaProducerConfig."""
169
+ config = KafkaProducerConfig(
170
+ kafka_servers="localhost:9092",
171
+ kafka_group_id="test-group",
172
+ )
173
+
174
+ assert config.kafka_servers == "localhost:9092"
175
+ assert config.kafka_group_id == "test-group"
176
+ assert config.kafka_topic_prefix is None
177
+ assert config.kafka_block_buffer_time_sec == 10
178
+ assert config.kafka_connection_check_timeout_sec == 5
179
+ assert config.kafka_connection_check_interval_sec == 60
180
+
181
+
182
+ def test_producer_config_with_defaults() -> None:
183
+ """Test creating a KafkaProducerConfig with all defaults."""
184
+ config = KafkaProducerConfig(
185
+ kafka_servers="localhost:9092",
186
+ kafka_group_id="test-group",
187
+ kafka_topic_prefix="test.",
188
+ kafka_block_buffer_time_sec=15,
189
+ kafka_connection_check_timeout_sec=10,
190
+ kafka_connection_check_interval_sec=30,
191
+ )
192
+
193
+ assert config.kafka_servers == "localhost:9092"
194
+ assert config.kafka_group_id == "test-group"
195
+ assert config.kafka_topic_prefix == "test."
196
+ assert config.kafka_block_buffer_time_sec == 15
197
+ assert config.kafka_connection_check_timeout_sec == 10
198
+ assert config.kafka_connection_check_interval_sec == 30
199
+
200
+
201
+ def test_producer_config_with_custom_defaults() -> None:
202
+ """Test creating a KafkaProducerConfig with custom values."""
203
+ config = KafkaProducerConfig(
204
+ kafka_servers="kafka1:9092,kafka2:9092",
205
+ kafka_group_id="custom-group",
206
+ kafka_topic_prefix="custom.",
207
+ kafka_block_buffer_time_sec=20,
208
+ kafka_connection_check_timeout_sec=15,
209
+ kafka_connection_check_interval_sec=45,
210
+ )
211
+
212
+ assert config.kafka_servers == "kafka1:9092,kafka2:9092"
213
+ assert config.kafka_group_id == "custom-group"
214
+ assert config.kafka_topic_prefix == "custom."
215
+ assert config.kafka_block_buffer_time_sec == 20
216
+ assert config.kafka_connection_check_timeout_sec == 15
217
+ assert config.kafka_connection_check_interval_sec == 45
218
+
219
+
220
+ def test_producer_config_with_none_values() -> None:
221
+ """Test creating a KafkaProducerConfig with None values."""
222
+ config = KafkaProducerConfig(
223
+ kafka_servers="localhost:9092",
224
+ kafka_group_id="test-group",
225
+ kafka_topic_prefix=None,
226
+ )
227
+
228
+ assert config.kafka_topic_prefix is None
229
+
230
+
231
+ def test_producer_config_with_list_servers() -> None:
232
+ """Test creating a KafkaProducerConfig with list of servers."""
233
+ config = KafkaProducerConfig(
234
+ kafka_servers=["localhost:9092", "localhost:9093"],
235
+ kafka_group_id="test-group",
236
+ )
237
+
238
+ assert config.kafka_servers == ["localhost:9092", "localhost:9093"]
239
+
240
+
241
+ def test_producer_config_with_empty_list_servers() -> None:
242
+ """Test creating a KafkaProducerConfig with empty server list."""
243
+ with pytest.raises(ValidationError):
244
+ KafkaProducerConfig(
245
+ kafka_servers=[],
246
+ kafka_group_id="test-group",
247
+ )
248
+
249
+
250
+ def test_producer_config_with_empty_string_servers() -> None:
251
+ """Test creating a KafkaProducerConfig with empty string servers."""
252
+ with pytest.raises(ValidationError):
253
+ KafkaProducerConfig(
254
+ kafka_servers="",
255
+ kafka_group_id="test-group",
256
+ )
257
+
258
+
259
+ def test_producer_config_with_special_characters() -> None:
260
+ """Test creating a KafkaProducerConfig with special characters."""
261
+ config = KafkaProducerConfig(
262
+ kafka_servers="kafka-server:9092",
263
+ kafka_group_id="test-group-with-special-chars: @#$%^&*()",
264
+ kafka_topic_prefix="test-prefix-with-special-chars: @#$%^&*().",
265
+ )
266
+
267
+ assert config.kafka_group_id == "test-group-with-special-chars: @#$%^&*()"
268
+ assert config.kafka_topic_prefix == "test-prefix-with-special-chars: @#$%^&*()."
269
+
270
+
271
+ def test_producer_config_with_unicode() -> None:
272
+ """Test creating a KafkaProducerConfig with unicode characters."""
273
+ config = KafkaProducerConfig(
274
+ kafka_servers="kafka-server:9092",
275
+ kafka_group_id="test-group-with-unicode-测试",
276
+ kafka_topic_prefix="test-prefix-with-unicode-测试.",
277
+ )
278
+
279
+ assert config.kafka_group_id == "test-group-with-unicode-测试"
280
+ assert config.kafka_topic_prefix == "test-prefix-with-unicode-测试."
281
+
282
+
283
+ def test_producer_config_with_zero_timeouts() -> None:
284
+ """Test creating a KafkaProducerConfig with zero timeouts."""
285
+ config = KafkaProducerConfig(
286
+ kafka_servers="localhost:9092",
287
+ kafka_group_id="test-group",
288
+ kafka_block_buffer_time_sec=0,
289
+ kafka_connection_check_timeout_sec=0,
290
+ kafka_connection_check_interval_sec=0,
291
+ )
292
+
293
+ assert config.kafka_block_buffer_time_sec == 0
294
+ assert config.kafka_connection_check_timeout_sec == 0
295
+ assert config.kafka_connection_check_interval_sec == 0
296
+
297
+
298
+ def test_producer_config_with_negative_timeouts() -> None:
299
+ """Test creating a KafkaProducerConfig with negative timeouts."""
300
+ with pytest.raises(ValidationError):
301
+ KafkaProducerConfig(
302
+ kafka_servers="localhost:9092",
303
+ kafka_group_id="test-group",
304
+ kafka_block_buffer_time_sec=-1,
305
+ )
306
+
307
+
308
+ def test_producer_config_with_large_timeouts() -> None:
309
+ """Test creating a KafkaProducerConfig with large timeouts."""
310
+ config = KafkaProducerConfig(
311
+ kafka_servers="localhost:9092",
312
+ kafka_group_id="test-group",
313
+ kafka_block_buffer_time_sec=3600,
314
+ kafka_connection_check_timeout_sec=300,
315
+ kafka_connection_check_interval_sec=1800,
316
+ )
317
+
318
+ assert config.kafka_block_buffer_time_sec == 3600
319
+ assert config.kafka_connection_check_timeout_sec == 300
320
+ assert config.kafka_connection_check_interval_sec == 1800
321
+
322
+
323
+ def test_consumer_config_minimal() -> None:
324
+ """Test creating a minimal KafkaConsumerConfig."""
325
+ config = KafkaConsumerConfig(
326
+ kafka_servers="localhost:9092",
327
+ kafka_group_id="test-group",
328
+ )
329
+
330
+ assert config.kafka_servers == "localhost:9092"
331
+ assert config.kafka_group_id == "test-group"
332
+ assert config.kafka_offset == "latest"
333
+ assert config.kafka_topic_prefix is None
334
+ assert config.kafka_block_consumer_time_sec == 2
335
+ assert config.kafka_connection_check_timeout_sec == 5
336
+ assert config.kafka_auto_commit_interval_ms == 1000
337
+ assert config.kafka_connection_check_interval_sec == 60
338
+
339
+
340
+ def test_consumer_config_with_none_values() -> None:
341
+ """Test creating a KafkaConsumerConfig with None values."""
342
+ config = KafkaConsumerConfig(
343
+ kafka_servers="localhost:9092",
344
+ kafka_group_id="test-group",
345
+ kafka_topic_prefix=None,
346
+ )
347
+
348
+ assert config.kafka_topic_prefix is None
349
+
350
+
351
+ def test_consumer_config_with_list_servers() -> None:
352
+ """Test creating a KafkaConsumerConfig with list of servers."""
353
+ config = KafkaConsumerConfig(
354
+ kafka_servers=["localhost:9092", "localhost:9093"],
355
+ kafka_group_id="test-group",
356
+ )
357
+
358
+ assert config.kafka_servers == ["localhost:9092", "localhost:9093"]
359
+
360
+
361
+ def test_consumer_config_with_different_offsets() -> None:
362
+ """Test creating a KafkaConsumerConfig with different offset values."""
363
+ configs = [
364
+ KafkaConsumerConfig(
365
+ kafka_servers="localhost:9092",
366
+ kafka_group_id="test-group",
367
+ kafka_offset="earliest",
368
+ ),
369
+ KafkaConsumerConfig(
370
+ kafka_servers="localhost:9092",
371
+ kafka_group_id="test-group",
372
+ kafka_offset="latest",
373
+ ),
374
+ ]
375
+
376
+ assert configs[0].kafka_offset == "earliest"
377
+ assert configs[1].kafka_offset == "latest"
378
+
379
+
380
+ def test_consumer_config_with_special_characters() -> None:
381
+ """Test creating a KafkaConsumerConfig with special characters."""
382
+ config = KafkaConsumerConfig(
383
+ kafka_servers="kafka-server:9092",
384
+ kafka_group_id="test-group-with-special-chars: @#$%^&*()",
385
+ kafka_topic_prefix="test-prefix-with-special-chars: @#$%^&*().",
386
+ )
387
+
388
+ assert config.kafka_group_id == "test-group-with-special-chars: @#$%^&*()"
389
+ assert config.kafka_topic_prefix == "test-prefix-with-special-chars: @#$%^&*()."
390
+
391
+
392
+ def test_consumer_config_with_unicode() -> None:
393
+ """Test creating a KafkaConsumerConfig with unicode characters."""
394
+ config = KafkaConsumerConfig(
395
+ kafka_servers="kafka-server:9092",
396
+ kafka_group_id="test-group-with-unicode-测试",
397
+ kafka_topic_prefix="test-prefix-with-unicode-测试.",
398
+ )
399
+
400
+ assert config.kafka_group_id == "test-group-with-unicode-测试"
401
+ assert config.kafka_topic_prefix == "test-prefix-with-unicode-测试."
402
+
403
+
404
+ def test_consumer_config_with_zero_timeouts() -> None:
405
+ """Test creating a KafkaConsumerConfig with zero timeouts."""
406
+ config = KafkaConsumerConfig(
407
+ kafka_servers="localhost:9092",
408
+ kafka_group_id="test-group",
409
+ kafka_block_consumer_time_sec=0,
410
+ kafka_connection_check_timeout_sec=0,
411
+ kafka_connection_check_interval_sec=0,
412
+ kafka_auto_commit_interval_ms=0,
413
+ )
414
+
415
+ assert config.kafka_block_consumer_time_sec == 0
416
+ assert config.kafka_connection_check_timeout_sec == 0
417
+ assert config.kafka_connection_check_interval_sec == 0
418
+ assert config.kafka_auto_commit_interval_ms == 0
419
+
420
+
421
+ def test_consumer_config_with_negative_timeouts() -> None:
422
+ """Test creating a KafkaConsumerConfig with negative timeouts."""
423
+ with pytest.raises(ValidationError):
424
+ KafkaConsumerConfig(
425
+ kafka_servers="localhost:9092",
426
+ kafka_group_id="test-group",
427
+ kafka_block_consumer_time_sec=-1,
428
+ )
429
+
430
+
431
+ def test_consumer_config_with_large_timeouts() -> None:
432
+ """Test creating a KafkaConsumerConfig with large timeouts."""
433
+ config = KafkaConsumerConfig(
434
+ kafka_servers="localhost:9092",
435
+ kafka_group_id="test-group",
436
+ kafka_block_consumer_time_sec=300,
437
+ kafka_connection_check_timeout_sec=60,
438
+ kafka_connection_check_interval_sec=1800,
439
+ kafka_auto_commit_interval_ms=10000,
440
+ )
441
+
442
+ assert config.kafka_block_consumer_time_sec == 300
443
+ assert config.kafka_connection_check_timeout_sec == 60
444
+ assert config.kafka_connection_check_interval_sec == 1800
445
+ assert config.kafka_auto_commit_interval_ms == 10000
446
+
447
+
448
+ def test_consumer_config_with_empty_string_offset() -> None:
449
+ """Test creating a KafkaConsumerConfig with empty string offset."""
450
+ with pytest.raises(ValidationError):
451
+ KafkaConsumerConfig(
452
+ kafka_servers="localhost:9092",
453
+ kafka_group_id="test-group",
454
+ kafka_offset="",
455
+ )
456
+
457
+
458
+ def test_producer_config_missing_required_fields() -> None:
459
+ """Test that missing required fields raise ValidationError."""
460
+ with pytest.raises(ValidationError):
461
+ KafkaProducerConfig() # type: ignore[call-arg]
462
+
463
+ with pytest.raises(ValidationError):
464
+ KafkaProducerConfig(kafka_servers="localhost:9092") # type: ignore[call-arg]
465
+
466
+ with pytest.raises(ValidationError):
467
+ KafkaProducerConfig(kafka_group_id="test-group") # type: ignore[call-arg]
468
+
469
+
470
+ def test_consumer_config_missing_required_fields() -> None:
471
+ """Test that missing required fields raise ValidationError."""
472
+ with pytest.raises(ValidationError):
473
+ KafkaConsumerConfig() # type: ignore[call-arg]
474
+
475
+ with pytest.raises(ValidationError):
476
+ KafkaConsumerConfig(kafka_servers="localhost:9092") # type: ignore[call-arg]
477
+
478
+ with pytest.raises(ValidationError):
479
+ KafkaConsumerConfig(kafka_group_id="test-group") # type: ignore[call-arg]
480
+
481
+
482
+ def test_kafka_message_missing_required_fields() -> None:
483
+ """Test that missing required fields raise ValidationError."""
484
+ with pytest.raises(ValidationError):
485
+ KafkaMessage() # type: ignore[call-arg]
486
+
487
+ with pytest.raises(ValidationError):
488
+ KafkaMessage(topic="test-topic") # type: ignore[call-arg]
489
+
490
+
491
+ def test_config_immutability() -> None:
492
+ """Test that config objects are immutable."""
493
+ config = KafkaProducerConfig(
494
+ kafka_servers="localhost:9092",
495
+ kafka_group_id="test-group",
496
+ )
497
+
498
+ # Configs should be frozen (immutable)
499
+ with pytest.raises(FrozenInstanceError):
500
+ config.kafka_group_id = "new-group" # type: ignore[misc]
501
+
502
+
503
+ def test_config_equality() -> None:
504
+ """Test config equality comparison."""
505
+ config1 = KafkaProducerConfig(
506
+ kafka_servers="localhost:9092",
507
+ kafka_group_id="test-group",
508
+ )
509
+
510
+ config2 = KafkaProducerConfig(
511
+ kafka_servers="localhost:9092",
512
+ kafka_group_id="test-group",
513
+ )
514
+
515
+ assert config1 == config2
516
+
517
+
518
+ def test_config_inequality() -> None:
519
+ """Test config inequality comparison."""
520
+ config1 = KafkaProducerConfig(
521
+ kafka_servers="localhost:9092",
522
+ kafka_group_id="test-group",
523
+ )
524
+
525
+ config2 = KafkaProducerConfig(
526
+ kafka_servers="localhost:9092",
527
+ kafka_group_id="different-group",
528
+ )
529
+
530
+ assert config1 != config2
531
+
532
+
533
+ # SASL Configuration Tests
534
+
535
+
536
+ def test_config_with_sasl_authentication() -> None:
537
+ """Test creating configs with SASL authentication."""
538
+ producer_config = KafkaProducerConfig(
539
+ kafka_servers="localhost:9092",
540
+ kafka_group_id="test-group",
541
+ kafka_security_protocol=KafkaSecurityProtocol.SASL_PLAINTEXT,
542
+ kafka_sasl_mechanism=KafkaSaslMechanism.PLAIN,
543
+ kafka_sasl_username="test-user",
544
+ kafka_sasl_password="test-password",
545
+ )
546
+
547
+ consumer_config = KafkaConsumerConfig(
548
+ kafka_servers="localhost:9092",
549
+ kafka_group_id="test-group",
550
+ kafka_security_protocol=KafkaSecurityProtocol.SASL_SSL,
551
+ kafka_sasl_mechanism=KafkaSaslMechanism.SCRAM_SHA_256,
552
+ kafka_sasl_username="test-user",
553
+ kafka_sasl_password="test-password",
554
+ )
555
+
556
+ assert (
557
+ producer_config.kafka_security_protocol == KafkaSecurityProtocol.SASL_PLAINTEXT
558
+ )
559
+ assert producer_config.kafka_sasl_mechanism == KafkaSaslMechanism.PLAIN
560
+ assert consumer_config.kafka_security_protocol == KafkaSecurityProtocol.SASL_SSL
561
+ assert consumer_config.kafka_sasl_mechanism == KafkaSaslMechanism.SCRAM_SHA_256
562
+
563
+
564
+ def test_config_invalid_sasl_values() -> None:
565
+ """Test that invalid SASL values raise ValidationError."""
566
+ with pytest.raises(ValidationError):
567
+ KafkaProducerConfig(
568
+ kafka_servers="localhost:9092",
569
+ kafka_group_id="test-group",
570
+ kafka_security_protocol="INVALID", # type: ignore[arg-type]
571
+ )
572
+
573
+ with pytest.raises(ValidationError):
574
+ KafkaConsumerConfig(
575
+ kafka_servers="localhost:9092",
576
+ kafka_group_id="test-group",
577
+ kafka_sasl_mechanism="INVALID", # type: ignore[arg-type]
578
+ )
579
+
580
+
581
+ def test_to_kafka_config_with_and_without_sasl() -> None:
582
+ """Test that to_kafka_config() correctly includes/excludes SASL parameters."""
583
+ # Without SASL
584
+ config_no_sasl = KafkaProducerConfig(
585
+ kafka_servers="localhost:9092",
586
+ kafka_group_id="test-group",
587
+ )
588
+ kafka_config_no_sasl = config_no_sasl.to_kafka_config()
589
+
590
+ assert "security.protocol" not in kafka_config_no_sasl
591
+ assert "sasl.mechanism" not in kafka_config_no_sasl
592
+ assert "sasl.username" not in kafka_config_no_sasl
593
+ assert "sasl.password" not in kafka_config_no_sasl
594
+
595
+ # With SASL
596
+ config_with_sasl = KafkaConsumerConfig(
597
+ kafka_servers="localhost:9092",
598
+ kafka_group_id="test-group",
599
+ kafka_security_protocol=KafkaSecurityProtocol.SASL_SSL,
600
+ kafka_sasl_mechanism=KafkaSaslMechanism.SCRAM_SHA_256,
601
+ kafka_sasl_username="user",
602
+ kafka_sasl_password="pass",
603
+ )
604
+ kafka_config_with_sasl = config_with_sasl.to_kafka_config()
605
+
606
+ assert kafka_config_with_sasl["security.protocol"] == "SASL_SSL"
607
+ assert kafka_config_with_sasl["sasl.mechanism"] == "SCRAM-SHA-256"
608
+ assert kafka_config_with_sasl["sasl.username"] == "user"
609
+ assert kafka_config_with_sasl["sasl.password"] == "pass"