From b8906c84a1996f0054df4b64471b3a5a1dba98dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Banaszewski?= Date: Fri, 24 Apr 2026 18:28:22 +0200 Subject: [PATCH] fix: fixes aibridge integration tests failing on windows (#24665) Fixes aibridge responses and apidump integration tests in windows CI job. example failing job before the fix: https://github.com/coder/coder/actions/runs/24873191866/job/72823942378 --- .../internal/integrationtest/apidump_test.go | 10 ++++++---- .../internal/integrationtest/mockupstream.go | 20 ++++++++++--------- .../integrationtest/responses_test.go | 8 ++++---- 3 files changed, 21 insertions(+), 17 deletions(-) diff --git a/aibridge/internal/integrationtest/apidump_test.go b/aibridge/internal/integrationtest/apidump_test.go index 8ec62297b0..c64eded197 100644 --- a/aibridge/internal/integrationtest/apidump_test.go +++ b/aibridge/internal/integrationtest/apidump_test.go @@ -22,6 +22,8 @@ import ( "github.com/coder/coder/v2/aibridge/provider" ) +const osSep = string(filepath.Separator) + func TestAPIDump(t *testing.T) { t.Parallel() @@ -164,9 +166,9 @@ func TestAPIDump(t *testing.T) { require.NotEmpty(t, respDumpFile, "response dump file should exist") // Verify dump files are in the correct provider subdirectory. - require.Contains(t, reqDumpFile, filepath.Join(dumpDir, tc.expectProviderDir)+"/", + require.Contains(t, reqDumpFile, filepath.Join(dumpDir, tc.expectProviderDir)+osSep, "request dump should be in the %s provider directory", tc.expectProviderDir) - require.Contains(t, respDumpFile, filepath.Join(dumpDir, tc.expectProviderDir)+"/", + require.Contains(t, respDumpFile, filepath.Join(dumpDir, tc.expectProviderDir)+osSep, "response dump should be in the %s provider directory", tc.expectProviderDir) // Verify request dump contains expected HTTP request format. @@ -284,12 +286,12 @@ func TestAPIDumpPassthrough(t *testing.T) { require.NotEmpty(t, reqDumpFile, "request dump file should exist") require.FileExists(t, reqDumpFile) - require.Contains(t, reqDumpFile, "/passthrough/") + require.Contains(t, reqDumpFile, osSep+"passthrough"+osSep) require.Contains(t, reqDumpFile, tc.expectDumpName) require.NotEmpty(t, respDumpFile, "response dump file should exist") require.FileExists(t, respDumpFile) - require.Contains(t, respDumpFile, "/passthrough/") + require.Contains(t, respDumpFile, osSep+"passthrough"+osSep) require.Contains(t, respDumpFile, tc.expectDumpName) // Verify request dump. diff --git a/aibridge/internal/integrationtest/mockupstream.go b/aibridge/internal/integrationtest/mockupstream.go index 2731ed5506..ea493a7639 100644 --- a/aibridge/internal/integrationtest/mockupstream.go +++ b/aibridge/internal/integrationtest/mockupstream.go @@ -218,18 +218,20 @@ func (ms *mockUpstream) writeSSE(w http.ResponseWriter, data []byte) { return } - // Write line-by-line to simulate SSE events arriving incrementally - scanner := bufio.NewScanner(bytes.NewReader(data)) - scanner.Buffer(make([]byte, 0, 64*1024), 1024*1024) - for scanner.Scan() { - _, err := fmt.Fprintf(w, "%s\n", scanner.Text()) - if eventstream.IsConnError(err) { - return // client disconnected, stop writing + // Write line-by-line to simulate SSE events arriving incrementally. + // SplitAfter keeps the line endings so fixture bytes (LF or CRLF) replay verbatim. + for _, line := range bytes.SplitAfter(data, []byte("\n")) { + if len(line) == 0 { + continue + } + if _, err := w.Write(line); err != nil { + if eventstream.IsConnError(err) { + return // client disconnected, stop writing + } + require.NoError(ms.t, err) } - require.NoError(ms.t, err) flusher.Flush() } - require.NoError(ms.t, scanner.Err()) } // isRawHTTPResponse returns true if data starts with "HTTP/", indicating diff --git a/aibridge/internal/integrationtest/responses_test.go b/aibridge/internal/integrationtest/responses_test.go index dd333fd3aa..5d675c6db2 100644 --- a/aibridge/internal/integrationtest/responses_test.go +++ b/aibridge/internal/integrationtest/responses_test.go @@ -609,13 +609,13 @@ func TestClientAndConnectionError(t *testing.T) { name: "blocking_connection_refused", addr: startRejectingListener(t), streaming: false, - errContains: "connection reset by peer", + errContains: `connection reset by peer|forcibly closed`, // RST error message differs between Linux/macOS|Windows. }, { name: "streaming_connection_refused", addr: startRejectingListener(t), streaming: true, - errContains: "connection reset by peer", + errContains: `connection reset by peer|forcibly closed`, // RST error message differs between Linux/macOS|Windows. }, { name: "blocking_bad_url", @@ -1062,13 +1062,13 @@ func TestResponsesModelThoughts(t *testing.T) { } } -func requireResponsesError(t *testing.T, code int, message string, body []byte) { +func requireResponsesError(t *testing.T, code int, messagePattern string, body []byte) { var respErr responses.Error err := json.Unmarshal(body, &respErr) require.NoError(t, err) require.Equal(t, strconv.Itoa(code), respErr.Code) - require.Contains(t, respErr.Message, message) + require.Regexp(t, messagePattern, respErr.Message) } func responsesRequestBytes(t *testing.T, streaming bool, additionalFields ...keyVal) []byte {