@@ -554,67 +554,29 @@ def test_unknown_span_type_preserved(self):
554554 "SpanType" : "OpenTelemetry" ,
555555 }
556556
557- print ("\n === Testing UNKNOWN span type preservation ===" )
558- print (f"Initial SpanType: { span_data ['SpanType' ]} " )
559- attributes_before = span_data ["Attributes" ]
560- assert isinstance (attributes_before , dict )
561- print (
562- f"openinference.span.kind: { attributes_before ['openinference.span.kind' ]} "
563- )
564- print (f"Attributes type before: { type (attributes_before )} " )
565-
566- # Process the span
567557 self .exporter ._process_span_attributes (span_data )
568558
569- print (f"SpanType after processing: { span_data ['SpanType' ]} " )
570- print (f"Attributes type after: { type (span_data ['Attributes' ])} " )
571-
572- # Verify span is processed correctly
573- self .assertEqual (
574- span_data ["SpanType" ],
575- "UNKNOWN" ,
576- "SpanType should be mapped to UNKNOWN from openinference.span.kind" ,
577- )
578- self .assertIn ("Attributes" , span_data , "Attributes should still be present" )
559+ self .assertEqual (span_data ["SpanType" ], "UNKNOWN" )
560+ self .assertIn ("Attributes" , span_data )
579561
580- # When input is dict, output stays as dict (optimized path)
581562 attributes = span_data ["Attributes" ]
582563 assert isinstance (attributes , dict )
583- self .assertIsInstance (
584- attributes , dict , "Attributes should remain as dict in optimized path"
585- )
586564
587- # Basic attribute mapping should still work
588- self .assertIn (
589- "input" ,
590- attributes ,
591- "input.value should be mapped to input by ATTRIBUTE_MAPPING" ,
592- )
593- self .assertIn (
594- "output" ,
595- attributes ,
596- "output.value should be mapped to output by ATTRIBUTE_MAPPING" ,
597- )
565+ self .assertIn ("input" , attributes )
566+ self .assertIn ("output" , attributes )
598567
599- # Verify mime types are preserved
600568 self .assertEqual (attributes ["input.mime_type" ], "application/json" )
601569 self .assertEqual (attributes ["output.mime_type" ], "application/json" )
602570
603- # Verify parsed values
604571 input_val = attributes ["input" ]
605572 assert isinstance (input_val , dict )
606- self .assertIsInstance (input_val , dict , "input should be parsed from JSON" )
607573 self .assertIn ("content" , input_val )
608574
609575 output_val = attributes ["output" ]
610576 assert isinstance (output_val , dict )
611- self .assertIsInstance (output_val , dict , "output should be parsed from JSON" )
612577 self .assertEqual (output_val ["label" ], "security" )
613578 self .assertEqual (output_val ["confidence" ], 0.95 )
614579
615- print ("✓ UNKNOWN span preserved and processed correctly" )
616- print (f"✓ Final attributes keys: { list (attributes .keys ())} " )
617-
618580 def test_json_strings_parsed_to_objects (self ):
619581 """Test that JSON-encoded strings starting with { or [ are parsed to objects.
620582
@@ -758,5 +720,51 @@ def test_upsert_span_failure_retries(self, exporter_with_mocks, mock_span):
758720 assert exporter_with_mocks .http_client .post .call_count == 4 # max_retries=4
759721
760722
723+ class TestNilUuidProcessKey :
724+ """ProcessKey nil UUID normalization during export."""
725+
726+ def _export_with_process_key (self , mock_env_vars , mock_span , process_key ):
727+ """Export a span with the given ProcessKey and return the exported payload."""
728+ mock_uipath_span = MagicMock ()
729+ mock_uipath_span .to_dict .return_value = {
730+ "TraceId" : "test-trace-id" ,
731+ "Id" : "span-id" ,
732+ "ProcessKey" : process_key ,
733+ "Attributes" : {},
734+ }
735+
736+ with (
737+ patch ("uipath.tracing._otel_exporters.httpx.Client" ),
738+ patch (
739+ "uipath.tracing._otel_exporters._SpanUtils.otel_span_to_uipath_span" ,
740+ return_value = mock_uipath_span ,
741+ ),
742+ ):
743+ exporter = LlmOpsHttpExporter ()
744+ mock_response = MagicMock ()
745+ mock_response .status_code = 200
746+ exporter .http_client .post .return_value = mock_response # type: ignore
747+ exporter ._build_url = MagicMock (return_value = "http://test/api" ) # type: ignore
748+
749+ exporter .export ([mock_span ])
750+
751+ return exporter .http_client .post .call_args .kwargs ["json" ][0 ] # type: ignore
752+
753+ def test_nil_uuid_normalized_to_none (self , mock_env_vars , mock_span ):
754+ payload = self ._export_with_process_key (
755+ mock_env_vars , mock_span , "00000000-0000-0000-0000-000000000000"
756+ )
757+ assert payload ["ProcessKey" ] is None
758+
759+ def test_valid_uuid_preserved (self , mock_env_vars , mock_span ):
760+ real_key = "65965c09-87e3-4fa3-a7be-3fdb3955bd47"
761+ payload = self ._export_with_process_key (mock_env_vars , mock_span , real_key )
762+ assert payload ["ProcessKey" ] == real_key
763+
764+ def test_none_stays_none (self , mock_env_vars , mock_span ):
765+ payload = self ._export_with_process_key (mock_env_vars , mock_span , None )
766+ assert payload ["ProcessKey" ] is None
767+
768+
761769if __name__ == "__main__" :
762770 unittest .main ()
0 commit comments