11"""Unit tests configuration module."""
22
33import json
4+ import os
5+ import re
6+ from typing import Any , Mapping , MutableMapping
47
58import pytest
9+ import vertexai
610import yaml
11+ from google .auth .credentials import AnonymousCredentials
12+ from vcr import VCR
13+ from vcr .record_mode import RecordMode
14+ from vcr .request import Request
715
16+ from opentelemetry .instrumentation .vertexai .utils import (
17+ OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT ,
18+ )
19+ from opentelemetry .instrumentation .vertexai import VertexAIInstrumentor
820from opentelemetry .sdk ._events import EventLoggerProvider
921from opentelemetry .sdk ._logs import LoggerProvider
1022from opentelemetry .sdk ._logs .export import (
1123 InMemoryLogExporter ,
1224 SimpleLogRecordProcessor ,
1325)
26+ from opentelemetry .sdk .metrics import (
27+ MeterProvider ,
28+ )
29+ from opentelemetry .sdk .metrics .export import (
30+ InMemoryMetricReader ,
31+ )
1432from opentelemetry .sdk .trace import TracerProvider
1533from opentelemetry .sdk .trace .export import SimpleSpanProcessor
1634from opentelemetry .sdk .trace .export .in_memory_span_exporter import (
1735 InMemorySpanExporter ,
1836)
1937
38+ # from opentelemetry.instrumentation.vertexai_v2 import VertexAIInstrumentor
39+
2040
2141@pytest .fixture (scope = "function" , name = "span_exporter" )
2242def fixture_span_exporter ():
@@ -30,6 +50,12 @@ def fixture_log_exporter():
3050 yield exporter
3151
3252
53+ @pytest .fixture (scope = "function" , name = "metric_reader" )
54+ def fixture_metric_reader ():
55+ exporter = InMemoryMetricReader ()
56+ yield exporter
57+
58+
3359@pytest .fixture (scope = "function" , name = "tracer_provider" )
3460def fixture_tracer_provider (span_exporter ):
3561 provider = TracerProvider ()
@@ -46,17 +72,110 @@ def fixture_event_logger_provider(log_exporter):
4672 return event_logger_provider
4773
4874
75+ @pytest .fixture (scope = "function" , name = "meter_provider" )
76+ def fixture_meter_provider (metric_reader ):
77+ return MeterProvider (
78+ metric_readers = [metric_reader ],
79+ )
80+
81+
82+ @pytest .fixture (scope = "function" , name = "metric_reader" )
83+ def fixture_metric_reader ():
84+ exporter = InMemoryMetricReader ()
85+ yield exporter
86+
87+
88+ @pytest .fixture (autouse = True )
89+ def vertexai_init (vcr : VCR ) -> None :
90+ # Unfortunately I couldn't find a nice way to globally reset the global_config for each
91+ # test because different vertex submodules reference the global instance directly
92+ # https://github.com/googleapis/python-aiplatform/blob/v1.74.0/google/cloud/aiplatform/initializer.py#L687
93+ # so this config will leak if we don't call init() for each test.
94+
95+ # When not recording (in CI), don't do any auth. That prevents trying to read application
96+ # default credentials from the filesystem or metadata server and oauth token exchange. This
97+ # is not the interesting part of our instrumentation to test.
98+ if vcr .record_mode is RecordMode .NONE :
99+ vertexai .init (credentials = AnonymousCredentials ())
100+ else :
101+ vertexai .init ()
102+
103+
104+ @pytest .fixture
105+ def instrument_no_content (
106+ tracer_provider , event_logger_provider , meter_provider
107+ ):
108+ os .environ .update (
109+ {OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT : "False" }
110+ )
111+
112+ instrumentor = VertexAIInstrumentor ()
113+ instrumentor .instrument (
114+ tracer_provider = tracer_provider ,
115+ event_logger_provider = event_logger_provider ,
116+ meter_provider = meter_provider ,
117+ )
118+
119+ yield instrumentor
120+ os .environ .pop (OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT , None )
121+ instrumentor .uninstrument ()
122+
123+
124+ @pytest .fixture
125+ def instrument_with_content (
126+ tracer_provider , event_logger_provider , meter_provider
127+ ):
128+ os .environ .update (
129+ {OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT : "True" }
130+ )
131+ instrumentor = VertexAIInstrumentor ()
132+ instrumentor .instrument (
133+ tracer_provider = tracer_provider ,
134+ event_logger_provider = event_logger_provider ,
135+ meter_provider = meter_provider ,
136+ )
137+
138+ yield instrumentor
139+ os .environ .pop (OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT , None )
140+ instrumentor .uninstrument ()
141+
142+
49143@pytest .fixture (scope = "module" )
50144def vcr_config ():
145+ filter_header_regexes = [
146+ r"X-.*" ,
147+ "Server" ,
148+ "Date" ,
149+ "Expires" ,
150+ "Authorization" ,
151+ ]
152+
153+ def filter_headers (headers : Mapping [str , str ]) -> Mapping [str , str ]:
154+ return {
155+ key : val
156+ for key , val in headers .items ()
157+ if not any (
158+ re .match (filter_re , key , re .IGNORECASE )
159+ for filter_re in filter_header_regexes
160+ )
161+ }
162+
163+ def before_record_cb (request : Request ):
164+ request .headers = filter_headers (request .headers )
165+ request .uri = re .sub (
166+ r"/projects/[^/]+/" , "/projects/fake-project/" , request .uri
167+ )
168+ return request
169+
170+ def before_response_cb (response : MutableMapping [str , Any ]):
171+ response ["headers" ] = filter_headers (response ["headers" ])
172+ return response
173+
51174 return {
52- "filter_headers" : [
53- ("cookie" , "test_cookie" ),
54- ("authorization" , "Bearer test_vertexai_api_key" ),
55- ("vertexai-organization" , "test_vertexai_org_id" ),
56- ("vertexai-project" , "test_vertexai_project_id" ),
57- ],
58175 "decode_compressed_response" : True ,
59- "before_record_response" : scrub_response_headers ,
176+ "before_record_request" : before_record_cb ,
177+ "before_record_response" : before_response_cb ,
178+ "ignore_hosts" : ["oauth2.googleapis.com" ],
60179 }
61180
62181
@@ -125,12 +244,3 @@ def deserialize(cassette_string):
125244def fixture_vcr (vcr ):
126245 vcr .register_serializer ("yaml" , PrettyPrintJSONBody )
127246 return vcr
128-
129-
130- def scrub_response_headers (response ):
131- """
132- This scrubs sensitive response headers. Note they are case-sensitive!
133- """
134- response ["headers" ]["vertexai-organization" ] = "test_vertexai_org_id"
135- response ["headers" ]["Set-Cookie" ] = "test_set_cookie"
136- return response
0 commit comments