1212# See the License for the specific language governing permissions and
1313# limitations under the License.
1414
15+ """High level end-to-end test of the generate content mocking.
16+
17+ The primary purpose of this test is to verify that the instrumentation
18+ package does not break the underlying GenAI SDK that it instruments.
19+
20+ This test suite also has some minimal validation of the instrumentation
21+ outputs; however, validating the instrumentation output (other than
22+ verifying that instrumentation does not break the GenAI SDK) is a
23+ secondary goal of this test. Detailed testing of the instrumentation
24+ output is the purview of the other tests in this directory."""
1525
1626import google .genai
1727from google .genai import types as genai_types
2333import pytest
2434
2535from ..common .auth import FakeCredentials
36+ from ..common .otel_mocker import OTelMocker
2637
2738from opentelemetry .instrumentation .google_genai import (
2839 GoogleGenAiSdkInstrumentor ,
2940)
3041
3142
43+ def _should_redact_header (header_key ):
44+ if header_key .startswith ('x-goog' ):
45+ return True
46+ if header_key .startswith ('sec-goog' ):
47+ return True
48+ return False
49+
50+
51+ def _redact_headers (headers ):
52+ for header_key in headers :
53+ if _should_redact_header (header_key .lower ()):
54+ del headers [header_key ]
55+
56+
57+ def _before_record_request (request ):
58+ _redact_headers (request .headers )
59+ return request
60+
61+
62+ def _before_record_response (response ):
63+ _redact_headers (response .headers )
64+ return response
65+
66+
67+ @pytest .fixture (scope = 'module' )
68+ def vcr_config ():
69+ return {
70+ 'filter_query_parameters' : [
71+ 'key' ,
72+ 'apiKey' ,
73+ 'quotaUser' ,
74+ 'userProject' ,
75+ 'token' ,
76+ 'access_token' ,
77+ 'accessToken' ,
78+ 'refesh_token' ,
79+ 'refreshToken' ,
80+ 'authuser' ,
81+ 'bearer' ,
82+ 'bearer_token' ,
83+ 'bearerToken' ,
84+ 'userIp' ,
85+ ],
86+ 'filter_post_data_parameters' : [
87+ 'apikey' ,
88+ 'api_key' ,
89+ 'key'
90+ ],
91+ 'filter_headers' : [
92+ 'authorization' ,
93+ ],
94+ 'before_record_request' : _before_record_request ,
95+ 'before_record_response' : _before_record_response ,
96+ }
97+
98+
3299@pytest .fixture
33100def instrumentor ():
34101 return GoogleGenAiSdkInstrumentor ()
@@ -41,6 +108,14 @@ def setup_instrumentation(instrumentor):
41108 instrumentor .uninstrument ()
42109
43110
111+ @pytest .fixture (autouse = True )
112+ def otel_mocker ():
113+ result = OTelMocker ()
114+ result .install ()
115+ yield result
116+ result .uninstall ()
117+
118+
44119@pytest .fixture (autouse = True , params = [True , False ])
45120def setup_content_recording (request ):
46121 os .environ ["OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT" ] = str (request .param )
@@ -162,17 +237,18 @@ async def _gather_all():
162237
163238
164239@pytest .mark .vcr
165- def test_single_response (generate_content , model ):
240+ def test_single_response (generate_content , model , otel_mocker ):
166241 response = generate_content (
167242 model = model ,
168243 contents = "Create a poem about Open Telemetry." )
169244 assert response is not None
170245 assert response .text is not None
171246 assert len (response .text ) > 0
247+ otel_mocker .assert_has_span_named (f"generate_content { model } " )
172248
173249
174250@pytest .mark .vcr
175- def test_multiple_responses (generate_content_stream , model ):
251+ def test_multiple_responses (generate_content_stream , model , otel_mocker ):
176252 count = 0
177253 for response in generate_content_stream (
178254 model = model ,
@@ -183,4 +259,4 @@ def test_multiple_responses(generate_content_stream, model):
183259 assert len (response .text ) > 0
184260 count += 1
185261 assert count == 2
186-
262+ otel_mocker . assert_has_span_named ( f"generate_content { model } " )
0 commit comments