Skip to content
This repository was archived by the owner on Apr 27, 2026. It is now read-only.

Commit 91810da

Browse files
authored
fix(test): synchronize testHangServer handler goroutines to prevent race (#105)
Use sync.WaitGroup to track active HTTP handler goroutines and wait for all of them to fully exit before closing reqsCh in t.Cleanup. Previously, cleanup closed reqsCh (to unblock waiting tests) before all handlers had exited, creating a data race between a handler sending to reqsCh and the cleanup goroutine closing it. The cleanup order is now: 1. close(cleanupCh) — signal handlers to stop 2. server.CloseClientConnections() — force clients to disconnect 3. server.Close() — stop accepting new requests 4. wg.Wait() — wait for all handlers to exit 5. close(reqsCh) — safe to close now
1 parent 6e70b94 commit 91810da

1 file changed

Lines changed: 16 additions & 6 deletions

File tree

internal/command/command_test.go

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"path/filepath"
2424
"runtime"
2525
"strings"
26+
"sync"
2627
"testing"
2728

2829
"github.com/google/go-cmp/cmp"
@@ -1135,11 +1136,11 @@ func checkGoldenReference(t *testing.T, output *terminal.TestOutput, fixturePath
11351136

11361137
// Verify that the log starts with a version message
11371138
type versionMessage struct {
1138-
Level string `json:"@level"`
1139-
Message string `json:"@message"`
1140-
Type string `json:"type"`
1141-
Ghoten string `json:"ghoten"`
1142-
UI string `json:"ui"`
1139+
Level string `json:"@level"`
1140+
Message string `json:"@message"`
1141+
Type string `json:"type"`
1142+
Ghoten string `json:"ghoten"`
1143+
UI string `json:"ui"`
11431144
}
11441145
var gotVersion versionMessage
11451146
if err := json.Unmarshal([]byte(gotLines[0]), &gotVersion); err != nil {
@@ -1231,7 +1232,15 @@ func testHangServer(t testing.TB) (server *httptest.Server, reqs <-chan *http.Re
12311232
// channel reads in the caller.
12321233
reqsCh := make(chan *http.Request, 8)
12331234

1235+
// wg tracks all active handler goroutines so that cleanup can wait
1236+
// for them to fully exit before closing reqsCh, preventing a data
1237+
// race between a handler sending to reqsCh and cleanup closing it.
1238+
var wg sync.WaitGroup
1239+
12341240
server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
1241+
wg.Add(1)
1242+
defer wg.Done()
1243+
12351244
// We intentionally don't take any action on this request until
12361245
// the test cleanup function runs, but we will notify our
12371246
// caller that the request was started.
@@ -1262,9 +1271,10 @@ func testHangServer(t testing.TB) (server *httptest.Server, reqs <-chan *http.Re
12621271
t.Helper()
12631272
t.Log("shutting down testHangServer")
12641273
close(cleanupCh) // terminate any active handlers
1265-
close(reqsCh) // unblock any test that's awaiting a request notification
12661274
server.CloseClientConnections() // force any active clients to disconnect
12671275
server.Close() // stop accepting new requests and wait for existing ones to stop
1276+
wg.Wait() // wait for all handlers to fully exit before closing reqsCh
1277+
close(reqsCh) // unblock any test that's awaiting a request notification
12681278
})
12691279
return server, reqsCh
12701280
}

0 commit comments

Comments
 (0)