Skip to content

Commit a0ad392

Browse files
committed
chore: refactor tests
Signed-off-by: Danny Kopping <danny@coder.com>
1 parent 3b5ee80 commit a0ad392

File tree

2 files changed

+141
-209
lines changed

2 files changed

+141
-209
lines changed

bridge_integration_test.go

Lines changed: 81 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -170,87 +170,91 @@ func TestAnthropicMessages(t *testing.T) {
170170
func TestAnthropicMessagesModelThoughts(t *testing.T) {
171171
t.Parallel()
172172

173-
t.Run("thinking captured with builtin tool", func(t *testing.T) {
174-
t.Parallel()
175-
176-
cases := []struct {
177-
name string
178-
streaming bool
179-
fixture []byte
180-
expectedToolCallID string
181-
expectedThoughts []string
182-
}{
183-
{
184-
name: "single thinking block/streaming",
185-
streaming: true,
186-
fixture: fixtures.AntSingleBuiltinTool,
187-
expectedToolCallID: "toolu_01RX68weRSquLx6HUTj65iBo",
188-
expectedThoughts: []string{"The user wants me to read"},
189-
},
190-
{
191-
name: "single thinking block/blocking",
192-
streaming: false,
193-
fixture: fixtures.AntSingleBuiltinTool,
194-
expectedToolCallID: "toolu_01AusGgY5aKFhzWrFBv9JfHq",
195-
expectedThoughts: []string{"The user wants me to read"},
196-
},
197-
{
198-
name: "multiple thinking blocks/streaming",
199-
streaming: true,
200-
fixture: fixtures.AntMultiThinkingBuiltinTool,
201-
expectedToolCallID: "toolu_01RX68weRSquLx6HUTj65iBo",
202-
expectedThoughts: []string{"The user wants me to read", "I should use the Read tool"},
203-
},
204-
{
205-
name: "multiple thinking blocks/blocking",
206-
streaming: false,
207-
fixture: fixtures.AntMultiThinkingBuiltinTool,
208-
expectedToolCallID: "toolu_01AusGgY5aKFhzWrFBv9JfHq",
209-
expectedThoughts: []string{"The user wants me to read", "I should use the Read tool"},
210-
},
211-
}
173+
cases := []struct {
174+
name string
175+
streaming bool
176+
fixture []byte
177+
expectedToolCallID string
178+
expectedThoughts []string // nil means no tool usages expected at all
179+
}{
180+
{
181+
name: "single thinking block/streaming",
182+
streaming: true,
183+
fixture: fixtures.AntSingleBuiltinTool,
184+
expectedToolCallID: "toolu_01RX68weRSquLx6HUTj65iBo",
185+
expectedThoughts: []string{"The user wants me to read"},
186+
},
187+
{
188+
name: "single thinking block/blocking",
189+
streaming: false,
190+
fixture: fixtures.AntSingleBuiltinTool,
191+
expectedToolCallID: "toolu_01AusGgY5aKFhzWrFBv9JfHq",
192+
expectedThoughts: []string{"The user wants me to read"},
193+
},
194+
{
195+
name: "multiple thinking blocks/streaming",
196+
streaming: true,
197+
fixture: fixtures.AntMultiThinkingBuiltinTool,
198+
expectedToolCallID: "toolu_01RX68weRSquLx6HUTj65iBo",
199+
expectedThoughts: []string{"The user wants me to read", "I should use the Read tool"},
200+
},
201+
{
202+
name: "multiple thinking blocks/blocking",
203+
streaming: false,
204+
fixture: fixtures.AntMultiThinkingBuiltinTool,
205+
expectedToolCallID: "toolu_01AusGgY5aKFhzWrFBv9JfHq",
206+
expectedThoughts: []string{"The user wants me to read", "I should use the Read tool"},
207+
},
208+
{
209+
name: "no thoughts without tool calls",
210+
streaming: true,
211+
fixture: fixtures.AntSimple, // This fixture contains thoughts, but they're not associated with tool calls.
212+
},
213+
}
212214

213-
for _, tc := range cases {
214-
t.Run(tc.name, func(t *testing.T) {
215-
t.Parallel()
215+
for _, tc := range cases {
216+
t.Run(tc.name, func(t *testing.T) {
217+
t.Parallel()
216218

217-
ctx, cancel := context.WithTimeout(t.Context(), time.Second*30)
218-
t.Cleanup(cancel)
219+
ctx, cancel := context.WithTimeout(t.Context(), time.Second*30)
220+
t.Cleanup(cancel)
219221

220-
fix := fixtures.Parse(t, tc.fixture)
221-
upstream := testutil.NewMockUpstream(t, ctx, testutil.NewFixtureResponse(fix))
222+
fix := fixtures.Parse(t, tc.fixture)
223+
upstream := testutil.NewMockUpstream(t, ctx, testutil.NewFixtureResponse(fix))
222224

223-
recorderClient := &testutil.MockRecorder{}
224-
logger := slogtest.Make(t, &slogtest.Options{}).Leveled(slog.LevelDebug)
225-
providers := []aibridge.Provider{provider.NewAnthropic(anthropicCfg(upstream.URL, apiKey), nil)}
226-
b, err := aibridge.NewRequestBridge(ctx, providers, recorderClient, mcp.NewServerProxyManager(nil, testTracer), logger, nil, testTracer)
227-
require.NoError(t, err)
225+
recorderClient := &testutil.MockRecorder{}
226+
logger := slogtest.Make(t, &slogtest.Options{}).Leveled(slog.LevelDebug)
227+
providers := []aibridge.Provider{provider.NewAnthropic(anthropicCfg(upstream.URL, apiKey), nil)}
228+
b, err := aibridge.NewRequestBridge(ctx, providers, recorderClient, mcp.NewServerProxyManager(nil, testTracer), logger, nil, testTracer)
229+
require.NoError(t, err)
228230

229-
mockSrv := httptest.NewUnstartedServer(b)
230-
t.Cleanup(mockSrv.Close)
231-
mockSrv.Config.BaseContext = func(_ net.Listener) context.Context {
232-
return aibcontext.AsActor(ctx, userID, nil)
233-
}
234-
mockSrv.Start()
231+
mockSrv := httptest.NewUnstartedServer(b)
232+
t.Cleanup(mockSrv.Close)
233+
mockSrv.Config.BaseContext = func(_ net.Listener) context.Context {
234+
return aibcontext.AsActor(ctx, userID, nil)
235+
}
236+
mockSrv.Start()
235237

236-
reqBody, err := sjson.SetBytes(fix.Request(), "stream", tc.streaming)
237-
require.NoError(t, err)
238-
req := createAnthropicMessagesReq(t, mockSrv.URL, reqBody)
239-
client := &http.Client{}
240-
resp, err := client.Do(req)
241-
require.NoError(t, err)
242-
require.Equal(t, http.StatusOK, resp.StatusCode)
243-
defer resp.Body.Close()
238+
reqBody, err := sjson.SetBytes(fix.Request(), "stream", tc.streaming)
239+
require.NoError(t, err)
240+
req := createAnthropicMessagesReq(t, mockSrv.URL, reqBody)
241+
client := &http.Client{}
242+
resp, err := client.Do(req)
243+
require.NoError(t, err)
244+
require.Equal(t, http.StatusOK, resp.StatusCode)
245+
defer resp.Body.Close()
244246

245-
if tc.streaming {
246-
sp := aibridge.NewSSEParser()
247-
require.NoError(t, sp.Parse(resp.Body))
248-
assert.Contains(t, sp.AllEvents(), "message_start")
249-
assert.Contains(t, sp.AllEvents(), "message_stop")
250-
}
247+
if tc.streaming {
248+
sp := aibridge.NewSSEParser()
249+
require.NoError(t, sp.Parse(resp.Body))
250+
assert.Contains(t, sp.AllEvents(), "message_start")
251+
assert.Contains(t, sp.AllEvents(), "message_stop")
252+
}
251253

252-
// Verify tool usage was recorded with associated model thoughts.
253-
toolUsages := recorderClient.RecordedToolUsages()
254+
toolUsages := recorderClient.RecordedToolUsages()
255+
if tc.expectedThoughts == nil {
256+
assert.Empty(t, toolUsages)
257+
} else {
254258
require.Len(t, toolUsages, 1)
255259
assert.Equal(t, "Read", toolUsages[0].Tool)
256260
assert.Equal(t, tc.expectedToolCallID, toolUsages[0].ToolCallID)
@@ -259,55 +263,11 @@ func TestAnthropicMessagesModelThoughts(t *testing.T) {
259263
for i, expected := range tc.expectedThoughts {
260264
assert.Contains(t, toolUsages[0].ModelThoughts[i].Content, expected)
261265
}
266+
}
262267

263-
recorderClient.VerifyAllInterceptionsEnded(t)
264-
})
265-
}
266-
})
267-
268-
t.Run("no thoughts without tool calls", func(t *testing.T) {
269-
t.Parallel()
270-
271-
ctx, cancel := context.WithTimeout(t.Context(), time.Second*30)
272-
t.Cleanup(cancel)
273-
274-
// Use the simple fixture which has no tool calls — any thinking blocks
275-
// should not be persisted since they can't be associated with a tool call.
276-
fix := fixtures.Parse(t, fixtures.AntSimple)
277-
upstream := testutil.NewMockUpstream(t, ctx, testutil.NewFixtureResponse(fix))
278-
279-
recorderClient := &testutil.MockRecorder{}
280-
logger := slogtest.Make(t, &slogtest.Options{}).Leveled(slog.LevelDebug)
281-
providers := []aibridge.Provider{provider.NewAnthropic(anthropicCfg(upstream.URL, apiKey), nil)}
282-
b, err := aibridge.NewRequestBridge(ctx, providers, recorderClient, mcp.NewServerProxyManager(nil, testTracer), logger, nil, testTracer)
283-
require.NoError(t, err)
284-
285-
mockSrv := httptest.NewUnstartedServer(b)
286-
t.Cleanup(mockSrv.Close)
287-
mockSrv.Config.BaseContext = func(_ net.Listener) context.Context {
288-
return aibcontext.AsActor(ctx, userID, nil)
289-
}
290-
mockSrv.Start()
291-
292-
reqBody, err := sjson.SetBytes(fix.Request(), "stream", true)
293-
require.NoError(t, err)
294-
req := createAnthropicMessagesReq(t, mockSrv.URL, reqBody)
295-
client := &http.Client{}
296-
resp, err := client.Do(req)
297-
require.NoError(t, err)
298-
require.Equal(t, http.StatusOK, resp.StatusCode)
299-
defer resp.Body.Close()
300-
301-
sp := aibridge.NewSSEParser()
302-
require.NoError(t, sp.Parse(resp.Body))
303-
304-
// No tool usages (and therefore no thoughts) should be recorded
305-
// when there are no tool calls.
306-
toolUsages := recorderClient.RecordedToolUsages()
307-
assert.Empty(t, toolUsages)
308-
309-
recorderClient.VerifyAllInterceptionsEnded(t)
310-
})
268+
recorderClient.VerifyAllInterceptionsEnded(t)
269+
})
270+
}
311271
}
312272

313273
func TestAWSBedrockIntegration(t *testing.T) {

responses_integration_test.go

Lines changed: 60 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -945,67 +945,70 @@ func TestResponsesInjectedTool(t *testing.T) {
945945
func TestResponsesModelThoughts(t *testing.T) {
946946
t.Parallel()
947947

948-
t.Run("reasoning captured with builtin tool", func(t *testing.T) {
949-
t.Parallel()
950-
951-
cases := []struct {
952-
name string
953-
fixture []byte
954-
expectedToolCallID string
955-
expectedThoughts []string
956-
}{
957-
{
958-
name: "single reasoning/blocking",
959-
fixture: fixtures.OaiResponsesBlockingSingleBuiltinTool,
960-
expectedToolCallID: "call_CJSaa2u51JG996575oVljuNq",
961-
expectedThoughts: []string{"The user wants to add 3 and 5"},
962-
},
963-
{
964-
name: "single reasoning/streaming",
965-
fixture: fixtures.OaiResponsesStreamingBuiltinTool,
966-
expectedToolCallID: "call_7VaiUXZYuuuwWwviCrckxq6t",
967-
expectedThoughts: []string{"The user wants to add 3 and 5"},
968-
},
969-
{
970-
name: "multiple reasoning items/blocking",
971-
fixture: fixtures.OaiResponsesBlockingMultiReasoningBuiltinTool,
972-
expectedToolCallID: "call_CJSaa2u51JG996575oVljuNq",
973-
expectedThoughts: []string{"The user wants to add 3 and 5", "After adding, I will check if the result is prime"},
974-
},
975-
{
976-
name: "multiple reasoning items/streaming",
977-
fixture: fixtures.OaiResponsesStreamingMultiReasoningBuiltinTool,
978-
expectedToolCallID: "call_7VaiUXZYuuuwWwviCrckxq6t",
979-
expectedThoughts: []string{"The user wants to add 3 and 5", "After adding, I will check if the result is prime"},
980-
},
981-
}
948+
cases := []struct {
949+
name string
950+
fixture []byte
951+
expectedToolCallID string
952+
expectedThoughts []string // nil means no tool usages expected at all
953+
}{
954+
{
955+
name: "single reasoning/blocking",
956+
fixture: fixtures.OaiResponsesBlockingSingleBuiltinTool,
957+
expectedToolCallID: "call_CJSaa2u51JG996575oVljuNq",
958+
expectedThoughts: []string{"The user wants to add 3 and 5"},
959+
},
960+
{
961+
name: "single reasoning/streaming",
962+
fixture: fixtures.OaiResponsesStreamingBuiltinTool,
963+
expectedToolCallID: "call_7VaiUXZYuuuwWwviCrckxq6t",
964+
expectedThoughts: []string{"The user wants to add 3 and 5"},
965+
},
966+
{
967+
name: "multiple reasoning items/blocking",
968+
fixture: fixtures.OaiResponsesBlockingMultiReasoningBuiltinTool,
969+
expectedToolCallID: "call_CJSaa2u51JG996575oVljuNq",
970+
expectedThoughts: []string{"The user wants to add 3 and 5", "After adding, I will check if the result is prime"},
971+
},
972+
{
973+
name: "multiple reasoning items/streaming",
974+
fixture: fixtures.OaiResponsesStreamingMultiReasoningBuiltinTool,
975+
expectedToolCallID: "call_7VaiUXZYuuuwWwviCrckxq6t",
976+
expectedThoughts: []string{"The user wants to add 3 and 5", "After adding, I will check if the result is prime"},
977+
},
978+
{
979+
name: "no thoughts without tool calls",
980+
fixture: fixtures.OaiResponsesStreamingCodex, // This fixture contains reasoning, but it's not associated with tool calls.
981+
},
982+
}
982983

983-
for _, tc := range cases {
984-
t.Run(tc.name, func(t *testing.T) {
985-
t.Parallel()
984+
for _, tc := range cases {
985+
t.Run(tc.name, func(t *testing.T) {
986+
t.Parallel()
986987

987-
ctx, cancel := context.WithTimeout(t.Context(), time.Second*30)
988-
t.Cleanup(cancel)
988+
ctx, cancel := context.WithTimeout(t.Context(), time.Second*30)
989+
t.Cleanup(cancel)
989990

990-
fix := fixtures.Parse(t, tc.fixture)
991-
upstream := testutil.NewMockUpstream(t, ctx, testutil.NewFixtureResponse(fix))
991+
fix := fixtures.Parse(t, tc.fixture)
992+
upstream := testutil.NewMockUpstream(t, ctx, testutil.NewFixtureResponse(fix))
992993

993-
prov := provider.NewOpenAI(openaiCfg(upstream.URL, apiKey))
994-
srv, mockRecorder := newTestSrv(t, ctx, prov, nil, testTracer)
995-
defer srv.Close()
994+
prov := provider.NewOpenAI(openaiCfg(upstream.URL, apiKey))
995+
srv, mockRecorder := newTestSrv(t, ctx, prov, nil, testTracer)
996+
defer srv.Close()
996997

997-
req := createOpenAIResponsesReq(t, srv.URL, fix.Request())
998-
client := &http.Client{}
999-
resp, err := client.Do(req)
1000-
require.NoError(t, err)
1001-
require.Equal(t, http.StatusOK, resp.StatusCode)
1002-
defer resp.Body.Close()
998+
req := createOpenAIResponsesReq(t, srv.URL, fix.Request())
999+
client := &http.Client{}
1000+
resp, err := client.Do(req)
1001+
require.NoError(t, err)
1002+
require.Equal(t, http.StatusOK, resp.StatusCode)
1003+
defer resp.Body.Close()
10031004

1004-
_, err = io.ReadAll(resp.Body)
1005-
require.NoError(t, err)
1005+
_, err = io.ReadAll(resp.Body)
1006+
require.NoError(t, err)
10061007

1007-
// Verify tool usage was recorded with associated model thoughts.
1008-
toolUsages := mockRecorder.RecordedToolUsages()
1008+
toolUsages := mockRecorder.RecordedToolUsages()
1009+
if tc.expectedThoughts == nil {
1010+
require.Empty(t, toolUsages)
1011+
} else {
10091012
require.Len(t, toolUsages, 1)
10101013
require.Equal(t, "add", toolUsages[0].Tool)
10111014
require.Equal(t, tc.expectedToolCallID, toolUsages[0].ToolCallID)
@@ -1014,40 +1017,9 @@ func TestResponsesModelThoughts(t *testing.T) {
10141017
for i, expected := range tc.expectedThoughts {
10151018
require.Contains(t, toolUsages[0].ModelThoughts[i].Content, expected)
10161019
}
1017-
})
1018-
}
1019-
})
1020-
1021-
t.Run("no thoughts without tool calls", func(t *testing.T) {
1022-
t.Parallel()
1023-
1024-
ctx, cancel := context.WithTimeout(t.Context(), time.Second*30)
1025-
t.Cleanup(cancel)
1026-
1027-
// Use the simple fixture which has no tool calls — any reasoning
1028-
// should not be persisted since it can't be associated with a tool call.
1029-
fix := fixtures.Parse(t, fixtures.OaiResponsesStreamingCodex)
1030-
upstream := testutil.NewMockUpstream(t, ctx, testutil.NewFixtureResponse(fix))
1031-
1032-
prov := provider.NewOpenAI(openaiCfg(upstream.URL, apiKey))
1033-
srv, mockRecorder := newTestSrv(t, ctx, prov, nil, testTracer)
1034-
defer srv.Close()
1035-
1036-
req := createOpenAIResponsesReq(t, srv.URL, fix.Request())
1037-
client := &http.Client{}
1038-
resp, err := client.Do(req)
1039-
require.NoError(t, err)
1040-
require.Equal(t, http.StatusOK, resp.StatusCode)
1041-
defer resp.Body.Close()
1042-
1043-
_, err = io.ReadAll(resp.Body)
1044-
require.NoError(t, err)
1045-
1046-
// No tool usages (and therefore no thoughts) should be recorded
1047-
// when there are no tool calls.
1048-
toolUsages := mockRecorder.RecordedToolUsages()
1049-
require.Empty(t, toolUsages)
1050-
})
1020+
}
1021+
})
1022+
}
10511023
}
10521024

10531025
func createOpenAIResponsesReq(t *testing.T, baseURL string, input []byte) *http.Request {

0 commit comments

Comments
 (0)