2525from utils .test_service import FluentBitTestService
2626
2727
28+ EMPTY_MAP_RECORD_ID = "97789a11215b54828d2c3f50b864afed42543ff8"
29+
30+
2831class Service :
2932 def __init__ (self , config_file , * , use_schema_registry = False ):
3033 self .config_file = os .path .abspath (os .path .join (os .path .dirname (__file__ ), "../config" , config_file ))
@@ -182,6 +185,42 @@ def _decode_simple_msgpack(data, offset=0):
182185 raise ValueError (f"Unsupported MessagePack type 0x{ first :02x} " )
183186
184187
188+ def _decode_avro_long (data , offset = 0 ):
189+ encoded = 0
190+ shift = 0
191+
192+ while True :
193+ if offset >= len (data ):
194+ raise ValueError ("Truncated Avro long" )
195+
196+ byte = data [offset ]
197+ offset += 1
198+
199+ if shift == 63 and byte & 0x7E :
200+ raise ValueError ("Invalid Avro long" )
201+
202+ encoded |= (byte & 0x7F ) << shift
203+
204+ if byte & 0x80 == 0 :
205+ break
206+
207+ shift += 7
208+ if shift >= 64 :
209+ raise ValueError ("Invalid Avro long" )
210+
211+ return (encoded >> 1 ) ^ - (encoded & 1 ), offset
212+
213+
214+ def _decode_avro_string (data , offset = 0 ):
215+ size , offset = _decode_avro_long (data , offset )
216+ end = offset + size
217+
218+ if size < 0 or end > len (data ):
219+ raise ValueError ("Invalid Avro string" )
220+
221+ return data [offset :end ].decode ("utf-8" ), end
222+
223+
185224def _decode_otlp_proto (data , signal_type ):
186225 messages = {
187226 "logs" : ExportLogsServiceRequest (),
@@ -475,6 +514,8 @@ def _start_or_skip_without_avro_encoder(service):
475514 log_contents = _read_fluent_bit_log (service )
476515 error_message = str (error )
477516 unsupported_markers = [
517+ "unknown configuration property 'schema_str'" ,
518+ "unknown configuration property 'schema_id'" ,
478519 "unknown configuration property 'schema_registry_url'" ,
479520 "unknown configuration property 'schema_registry_subject'" ,
480521 "unknown configuration property 'schema_registry_version'" ,
@@ -495,6 +536,13 @@ def _start_or_skip_without_avro_encoder(service):
495536 raise
496537
497538
539+ def test_decode_avro_long_rejects_out_of_range_terminal_bits ():
540+ payload = b"\x80 " * 9 + b"\x02 "
541+
542+ with pytest .raises (ValueError , match = "Invalid Avro long" ):
543+ _decode_avro_long (payload )
544+
545+
498546def test_out_kafka_sends_json_payload ():
499547 service = Service ("out_kafka_basic.yaml" )
500548 service .start ()
@@ -597,6 +645,28 @@ def test_out_kafka_avro_resolves_schema_registry_subject():
597645 assert "application/vnd.schemaregistry.v1+json" in requests_seen [0 ]["headers" ]["Accept" ]
598646
599647
648+ def test_out_kafka_avro_encodes_empty_map ():
649+ service = Service ("out_kafka_avro_empty_map.yaml" )
650+ _start_or_skip_without_avro_encoder (service )
651+
652+ messages = service .wait_for_messages (1 )
653+ service .stop ()
654+
655+ message = messages [0 ]
656+ value = message ["value" ]
657+
658+ assert message ["topic" ] == "test"
659+ assert value [0 ] == 0
660+ assert int .from_bytes (value [1 :5 ], "big" ) == SCHEMA_ID
661+
662+ record_id , offset = _decode_avro_string (value , 5 )
663+ map_size , offset = _decode_avro_long (value , offset )
664+
665+ assert record_id == EMPTY_MAP_RECORD_ID
666+ assert map_size == 0
667+ assert offset == len (value )
668+
669+
600670def test_out_kafka_otlp_json_logs ():
601671 service = Service ("out_kafka_otlp_json.yaml" )
602672 service .start ()
0 commit comments