From 319b72c7a5f438229810ee8347402ac174fc4ff4 Mon Sep 17 00:00:00 2001 From: yetone Date: Fri, 9 Oct 2026 23:14:34 +0800 Subject: [PATCH] gateway: the loop guard's long-reply test streams 1000 lines, not all of gateway.go, and waits a minute (69c21cdd's macOS -race CI red) TestLongReplyThatDoesNotLoopGoesThrough/gemini failed on macOS -race in run 37946485821: "the stream never ended". It streamed all of gateway.go (199KB, ~20k events of 11 bytes) through the gateway on each protocol, and askStream waited 20s. Locally under -race each protocol took ~5s with the guard and ~5s without it (5.0s off, 6.0s on for gemini), so the time is the gateway translating 20k events, and CI's runner is ~4x slower: a bound with no room, not a slow guard. - The test streams gateway.go's first 40KB, cut at a line end (1001 lines, several times the guard's 256-unit window). - askStream waits a minute. - TestLoopGuardLeavesOrdinaryRepliesAlone feeds the first 256KB of each file (app.js and i18n.js run to megabytes): 72s -> 37s under -race. Ran: gofmt -l, go vet (darwin, GOOS=linux, GOOS=windows), go test -race -run 'TestLong|TestLoop' ./internal/gateway (long reply 3.9-4.7s per run for all 4 protocols). --- internal/gateway/loop_guard_test.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/internal/gateway/loop_guard_test.go b/internal/gateway/loop_guard_test.go index 38f72d25..85942b26 100644 --- a/internal/gateway/loop_guard_test.go +++ b/internal/gateway/loop_guard_test.go @@ -2,6 +2,7 @@ package gateway import ( "bufio" + "bytes" "encoding/base64" "encoding/json" "fmt" @@ -214,6 +215,11 @@ func TestLoopGuardLeavesOrdinaryRepliesAlone(t *testing.T) { if err != nil { t.Fatal(err) } + // app.js and i18n.js run to megabytes; 256K of each is still thousands + // of lines, many times the window, and keeps -race on CI quick + if len(b) > 256<<10 { + b = b[:bytes.LastIndexByte(b[:256<<10], '\n')+1] + } texts[f] = string(b) } var log, list, csv, hex, rules strings.Builder @@ -345,7 +351,7 @@ func askStream(t *testing.T, url, body string) string { }() select { case <-done: - case <-time.After(20 * time.Second): + case <-time.After(time.Minute): t.Fatal("the stream never ended") } return out.String() @@ -458,6 +464,10 @@ func TestLongReplyThatDoesNotLoopGoesThrough(t *testing.T) { if err != nil { t.Fatal(err) } + // ~1000 lines of real Go, several times the guard's window, and small + // enough that -race on a loaded CI runner streams it well inside + // askStream's bound (all of gateway.go took over 20s on macOS CI) + src = src[:bytes.LastIndexByte(src[:40<<10], '\n')+1] for _, x := range loopAsks { t.Run(x.name, func(t *testing.T) { v := &loopingVendor{reasoning: string(src), letGo: make(chan bool, 1)}