diff --git a/pkg/server/service_health_test.go b/pkg/server/service_health_test.go index 5f33e0aaf2..7716ca13bd 100644 --- a/pkg/server/service_health_test.go +++ b/pkg/server/service_health_test.go @@ -6,6 +6,7 @@ import ( "net" "net/http" "strings" + "sync" "testing" "time" @@ -118,12 +119,31 @@ func TestFrontendAssetPathsStayLocalAndBounded(t *testing.T) { } } +// syncBuffer is a goroutine-safe bytes.Buffer for capturing watchdog log output +// written from another goroutine while the test polls it. +type syncBuffer struct { + mu sync.Mutex + buf bytes.Buffer +} + +func (b *syncBuffer) Write(p []byte) (int, error) { + b.mu.Lock() + defer b.mu.Unlock() + return b.buf.Write(p) +} + +func (b *syncBuffer) String() string { + b.mu.Lock() + defer b.mu.Unlock() + return b.buf.String() +} + // A bound-but-not-serving listener is otherwise undiagnosable: nothing accepts // connections and no startup log line is emitted while a synchronous step // stalls. The watchdog must name the last completed phase and dump the local // goroutine stacks so a recurrence can be read from the field log. func TestStartupWatchdogLogsPhaseAndStack(t *testing.T) { - var buf bytes.Buffer + var buf syncBuffer phase := &startupPhase{} phase.mark("config watcher started")