11package dev .braintrust .instrumentation .genai ;
22
3- import static com .github .tomakehurst .wiremock .client .WireMock .*;
4- import static com .github .tomakehurst .wiremock .core .WireMockConfiguration .wireMockConfig ;
53import static org .junit .jupiter .api .Assertions .*;
64
75import com .fasterxml .jackson .databind .ObjectMapper ;
8- import com .github .tomakehurst .wiremock .junit5 .WireMockExtension ;
96import com .google .genai .Client ;
107import com .google .genai .types .GenerateContentConfig ;
118import com .google .genai .types .HttpOptions ;
1411import lombok .SneakyThrows ;
1512import org .junit .jupiter .api .BeforeEach ;
1613import org .junit .jupiter .api .Test ;
17- import org .junit .jupiter .api .extension .RegisterExtension ;
1814
1915public class BraintrustGenAITest {
2016 private static final ObjectMapper JSON_MAPPER = new ObjectMapper ();
2117
22- @ RegisterExtension
23- static WireMockExtension wireMock =
24- WireMockExtension .newInstance ().options (wireMockConfig ().dynamicPort ()).build ();
25-
2618 private TestHarness testHarness ;
2719
2820 @ BeforeEach
2921 void beforeEach () {
3022 testHarness = TestHarness .setup ();
31- wireMock .resetAll ();
3223 }
3324
3425 @ Test
3526 @ SneakyThrows
3627 void testWrapGemini () {
37- // Mock the Gemini API response
38- wireMock .stubFor (
39- post (urlPathMatching ("/v1beta/models/.*:generateContent" ))
40- .willReturn (
41- aResponse ()
42- .withStatus (200 )
43- .withHeader ("Content-Type" , "application/json" )
44- .withBody (
45- """
46- {
47- "candidates": [
48- {
49- "content": {
50- "parts": [
51- {
52- "text": "The capital of France is Paris."
53- }
54- ],
55- "role": "model"
56- },
57- "finishReason": "STOP"
58- }
59- ],
60- "usageMetadata": {
61- "promptTokenCount": 10,
62- "candidatesTokenCount": 8,
63- "totalTokenCount": 18
64- },
65- "modelVersion": "gemini-2.0-flash-lite"
66- }
67- """ )));
68-
69- // Create Gemini client pointing to WireMock server
28+ // Create Gemini client using VCR
7029 HttpOptions httpOptions =
71- HttpOptions .builder ().baseUrl ("http://localhost:" + wireMock . getPort ()).build ();
30+ HttpOptions .builder ().baseUrl (testHarness . geminiBaseUrl ()).build ();
7231
7332 // Wrap with Braintrust instrumentation
7433 var geminiClient =
7534 BraintrustGenAI .wrap (
7635 testHarness .openTelemetry (),
77- new Client .Builder ().apiKey ("test-api-key" ).httpOptions (httpOptions ));
36+ new Client .Builder ()
37+ .apiKey (testHarness .geminiApiKey ())
38+ .httpOptions (httpOptions ));
7839
7940 var config = GenerateContentConfig .builder ().temperature (0.0f ).maxOutputTokens (50 ).build ();
8041
@@ -84,8 +45,7 @@ void testWrapGemini() {
8445
8546 // Verify the response
8647 assertNotNull (response );
87- wireMock .verify (1 , postRequestedFor (urlPathMatching ("/v1beta/models/.*:generateContent" )));
88- assertEquals ("The capital of France is Paris." , response .text ());
48+ assertNotNull (response .text ());
8949
9050 // Verify spans were exported
9151 var spans = testHarness .awaitExportedSpans ();
@@ -109,9 +69,9 @@ void testWrapGemini() {
10969 String metricsJson = span .getAttributes ().get (AttributeKey .stringKey ("braintrust.metrics" ));
11070 assertNotNull (metricsJson , "braintrust.metrics should be set" );
11171 var metrics = JSON_MAPPER .readTree (metricsJson );
112- assertEquals ( 10 , metrics .get ("prompt_tokens" ).asInt ());
113- assertEquals ( 8 , metrics .get ("completion_tokens" ).asInt ());
114- assertEquals ( 18 , metrics .get ("tokens" ).asInt ());
72+ assertTrue ( metrics .get ("prompt_tokens" ).asInt () > 0 , "prompt_tokens should be > 0" );
73+ assertTrue ( metrics .get ("completion_tokens" ).asInt () > 0 , "completion_tokens should be > 0" );
74+ assertTrue ( metrics .get ("tokens" ).asInt () > 0 , "tokens should be > 0" );
11575
11676 // Verify braintrust.span_attributes marks this as an LLM span
11777 String spanAttributesJson =
@@ -135,62 +95,25 @@ void testWrapGemini() {
13595 assertNotNull (outputJson , "braintrust.output_json should be set" );
13696 var output = JSON_MAPPER .readTree (outputJson );
13797 assertTrue (output .has ("candidates" ), "output should have candidates" );
138- assertEquals ("STOP" , output .get ("candidates" ).get (0 ).get ("finishReason" ).asText ());
139- assertEquals (
140- "The capital of France is Paris." ,
141- output .get ("candidates" )
142- .get (0 )
143- .get ("content" )
144- .get ("parts" )
145- .get (0 )
146- .get ("text" )
147- .asText ());
98+ assertNotNull (output .get ("candidates" ).get (0 ).get ("finishReason" ));
99+ assertNotNull (
100+ output .get ("candidates" ).get (0 ).get ("content" ).get ("parts" ).get (0 ).get ("text" ));
148101 }
149102
150103 @ Test
151104 @ SneakyThrows
152105 void testWrapGeminiAsync () {
153- // Mock the Gemini API response
154- wireMock .stubFor (
155- post (urlPathMatching ("/v1beta/models/.*:generateContent" ))
156- .willReturn (
157- aResponse ()
158- .withStatus (200 )
159- .withHeader ("Content-Type" , "application/json" )
160- .withBody (
161- """
162- {
163- "candidates": [
164- {
165- "content": {
166- "parts": [
167- {
168- "text": "The capital of Germany is Berlin."
169- }
170- ],
171- "role": "model"
172- },
173- "finishReason": "STOP"
174- }
175- ],
176- "usageMetadata": {
177- "promptTokenCount": 10,
178- "candidatesTokenCount": 8,
179- "totalTokenCount": 18
180- },
181- "modelVersion": "gemini-2.0-flash-lite"
182- }
183- """ )));
184-
185- // Create Gemini client pointing to WireMock server
106+ // Create Gemini client using VCR
186107 HttpOptions httpOptions =
187- HttpOptions .builder ().baseUrl ("http://localhost:" + wireMock . getPort ()).build ();
108+ HttpOptions .builder ().baseUrl (testHarness . geminiBaseUrl ()).build ();
188109
189110 // Wrap with Braintrust instrumentation
190111 var geminiClient =
191112 BraintrustGenAI .wrap (
192113 testHarness .openTelemetry (),
193- new Client .Builder ().apiKey ("test-api-key" ).httpOptions (httpOptions ));
114+ new Client .Builder ()
115+ .apiKey (testHarness .geminiApiKey ())
116+ .httpOptions (httpOptions ));
194117
195118 var config = GenerateContentConfig .builder ().temperature (0.0f ).maxOutputTokens (50 ).build ();
196119
@@ -203,8 +126,7 @@ void testWrapGeminiAsync() {
203126
204127 // Verify the response
205128 assertNotNull (response );
206- wireMock .verify (1 , postRequestedFor (urlPathMatching ("/v1beta/models/.*:generateContent" )));
207- assertEquals ("The capital of Germany is Berlin." , response .text ());
129+ assertNotNull (response .text ());
208130
209131 // Verify spans were exported
210132 var spans = testHarness .awaitExportedSpans ();
@@ -228,9 +150,9 @@ void testWrapGeminiAsync() {
228150 String metricsJson = span .getAttributes ().get (AttributeKey .stringKey ("braintrust.metrics" ));
229151 assertNotNull (metricsJson , "braintrust.metrics should be set" );
230152 var metrics = JSON_MAPPER .readTree (metricsJson );
231- assertEquals ( 10 , metrics .get ("prompt_tokens" ).asInt ());
232- assertEquals ( 8 , metrics .get ("completion_tokens" ).asInt ());
233- assertEquals ( 18 , metrics .get ("tokens" ).asInt ());
153+ assertTrue ( metrics .get ("prompt_tokens" ).asInt () > 0 , "prompt_tokens should be > 0" );
154+ assertTrue ( metrics .get ("completion_tokens" ).asInt () > 0 , "completion_tokens should be > 0" );
155+ assertTrue ( metrics .get ("tokens" ).asInt () > 0 , "tokens should be > 0" );
234156
235157 // Verify braintrust.span_attributes marks this as an LLM span
236158 String spanAttributesJson =
@@ -254,15 +176,8 @@ void testWrapGeminiAsync() {
254176 assertNotNull (outputJson , "braintrust.output_json should be set" );
255177 var output = JSON_MAPPER .readTree (outputJson );
256178 assertTrue (output .has ("candidates" ), "output should have candidates" );
257- assertEquals ("STOP" , output .get ("candidates" ).get (0 ).get ("finishReason" ).asText ());
258- assertEquals (
259- "The capital of Germany is Berlin." ,
260- output .get ("candidates" )
261- .get (0 )
262- .get ("content" )
263- .get ("parts" )
264- .get (0 )
265- .get ("text" )
266- .asText ());
179+ assertNotNull (output .get ("candidates" ).get (0 ).get ("finishReason" ));
180+ assertNotNull (
181+ output .get ("candidates" ).get (0 ).get ("content" ).get ("parts" ).get (0 ).get ("text" ));
267182 }
268183}
0 commit comments