11"""Tests for auto-instrumentation functionality."""
22
33import pytest
4- from unittest .mock import patch , MagicMock
4+ from unittest .mock import patch , MagicMock , create_autospec
55from contextlib import ExitStack
66
7+ from opentelemetry .sdk .trace import TracerProvider as SDKTracerProvider
8+
79from sap_cloud_sdk .core .telemetry .auto_instrument import auto_instrument
810
911
@@ -17,6 +19,8 @@ def mock_traceloop_components():
1719 'http_exporter' : stack .enter_context (patch ('sap_cloud_sdk.core.telemetry.auto_instrument.HTTPSpanExporter' )),
1820 'console_exporter' : stack .enter_context (patch ('sap_cloud_sdk.core.telemetry.auto_instrument.ConsoleSpanExporter' )),
1921 'transformer' : stack .enter_context (patch ('sap_cloud_sdk.core.telemetry.auto_instrument.GenAIAttributeTransformer' )),
22+ 'baggage_processor' : stack .enter_context (patch ('sap_cloud_sdk.core.telemetry.auto_instrument.BaggageSpanProcessor' )),
23+ 'get_tracer_provider' : stack .enter_context (patch ('sap_cloud_sdk.core.telemetry.auto_instrument.trace.get_tracer_provider' , return_value = create_autospec (SDKTracerProvider ))),
2024 'create_resource' : stack .enter_context (patch ('sap_cloud_sdk.core.telemetry.auto_instrument.create_resource_attributes_from_env' )),
2125 'get_app_name' : stack .enter_context (patch ('sap_cloud_sdk.core.telemetry.auto_instrument._get_app_name' )),
2226 }
@@ -41,31 +45,16 @@ def test_auto_instrument_with_endpoint_success(self, mock_traceloop_components):
4145 assert call_kwargs ['should_enrich_metrics' ] is True
4246 assert call_kwargs ['disable_batch' ] is True
4347
44- def test_auto_instrument_appends_v1_traces_to_endpoint (self , mock_traceloop_components ):
45- """Test that auto_instrument appends /v1/traces to endpoint if not present ."""
48+ def test_auto_instrument_uses_grpc_exporter_by_default (self , mock_traceloop_components ):
49+ """Test that auto_instrument uses gRPC exporter by default, letting it read endpoint from env ."""
4650 mock_traceloop_components ['get_app_name' ].return_value = 'test-app'
4751 mock_traceloop_components ['create_resource' ].return_value = {}
4852
4953 with patch .dict ('os.environ' , {'OTEL_EXPORTER_OTLP_ENDPOINT' : 'http://localhost:4317' }, clear = True ):
5054 auto_instrument ()
5155
52- # Verify exporter was called with /v1/traces appended (grpc by default)
53- mock_traceloop_components ['grpc_exporter' ].assert_called_once_with (
54- endpoint = 'http://localhost:4317/v1/traces'
55- )
56-
57- def test_auto_instrument_preserves_existing_v1_traces (self , mock_traceloop_components ):
58- """Test that auto_instrument doesn't duplicate /v1/traces if already present."""
59- mock_traceloop_components ['get_app_name' ].return_value = 'test-app'
60- mock_traceloop_components ['create_resource' ].return_value = {}
61-
62- with patch .dict ('os.environ' , {'OTEL_EXPORTER_OTLP_ENDPOINT' : 'http://localhost:4317/v1/traces' }, clear = True ):
63- auto_instrument ()
64-
65- # Verify exporter was called with original endpoint (grpc by default)
66- mock_traceloop_components ['grpc_exporter' ].assert_called_once_with (
67- endpoint = 'http://localhost:4317/v1/traces'
68- )
56+ mock_traceloop_components ['grpc_exporter' ].assert_called_once_with ()
57+ mock_traceloop_components ['http_exporter' ].assert_not_called ()
6958
7059 def test_auto_instrument_creates_resource_with_attributes (self , mock_traceloop_components ):
7160 """Test that auto_instrument creates resource with correct attributes."""
@@ -104,17 +93,14 @@ def test_auto_instrument_logs_initialization(self, mock_traceloop_components):
10493 assert any ('initialized successfully' in msg .lower () for msg in info_calls )
10594
10695 def test_auto_instrument_with_trailing_slash (self , mock_traceloop_components ):
107- """Test that auto_instrument handles endpoint with trailing slash."""
96+ """Test that auto_instrument works with a trailing slash endpoint (exporter reads from env) ."""
10897 mock_traceloop_components ['get_app_name' ].return_value = 'test-app'
10998 mock_traceloop_components ['create_resource' ].return_value = {}
11099
111100 with patch .dict ('os.environ' , {'OTEL_EXPORTER_OTLP_ENDPOINT' : 'http://localhost:4317/' }, clear = True ):
112101 auto_instrument ()
113102
114- # Verify trailing slash is removed before appending /v1/traces (grpc by default)
115- mock_traceloop_components ['grpc_exporter' ].assert_called_once_with (
116- endpoint = 'http://localhost:4317/v1/traces'
117- )
103+ mock_traceloop_components ['grpc_exporter' ].assert_called_once_with ()
118104
119105 def test_auto_instrument_with_http_protobuf_protocol (self , mock_traceloop_components ):
120106 """Test that auto_instrument uses HTTP exporter when OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf."""
@@ -127,11 +113,7 @@ def test_auto_instrument_with_http_protobuf_protocol(self, mock_traceloop_compon
127113 }, clear = True ):
128114 auto_instrument ()
129115
130- # Verify HTTP exporter was called with /v1/traces appended
131- mock_traceloop_components ['http_exporter' ].assert_called_once_with (
132- endpoint = 'http://localhost:4318/v1/traces'
133- )
134- # Verify gRPC exporter was not called
116+ mock_traceloop_components ['http_exporter' ].assert_called_once_with ()
135117 mock_traceloop_components ['grpc_exporter' ].assert_not_called ()
136118
137119 def test_auto_instrument_passes_transformer_to_traceloop (self , mock_traceloop_components ):
@@ -226,3 +208,16 @@ def test_auto_instrument_without_endpoint_or_console(self):
226208 mock_logger .warning .assert_called_once ()
227209 warning_message = mock_logger .warning .call_args [0 ][0 ]
228210 assert "OTEL_EXPORTER_OTLP_ENDPOINT not set" in warning_message
211+
212+ def test_auto_instrument_passes_baggage_span_processor (self , mock_traceloop_components ):
213+ """Test that auto_instrument registers a BaggageSpanProcessor on the tracer provider."""
214+ mock_traceloop_components ['get_app_name' ].return_value = 'test-app'
215+ mock_traceloop_components ['create_resource' ].return_value = {}
216+ mock_processor_instance = MagicMock ()
217+ mock_traceloop_components ['baggage_processor' ].return_value = mock_processor_instance
218+
219+ with patch .dict ('os.environ' , {'OTEL_EXPORTER_OTLP_ENDPOINT' : 'http://localhost:4317' }, clear = True ):
220+ auto_instrument ()
221+
222+ mock_traceloop_components ['baggage_processor' ].assert_called_once ()
223+ mock_traceloop_components ['get_tracer_provider' ].return_value .add_span_processor .assert_called_once_with (mock_processor_instance )
0 commit comments