on main help sign in
fix(backend): fix data race in tests using captureDebugLogs

The captureDebugLogs function was using a plain bytes.Buffer which is not safe for concurrent access. When app.New spawns goroutines that log asynchronously (e.g., mcp.Initialize), the test would read from the buffer while goroutines were still writing to it, causing a data race. This change introduces a syncBuffer type that wraps bytes.Buffer with a mutex, making it safe for concurrent reads and writes.

commita1e4a7c944bb75fe3c2adb47de137985b5eaa7c8 authorChristian Rocha <christian@rocha.is> dateTue 26 May 2026, 11:25 (5 months ago) parente530b1f
1 file changed+23−4
internal/backend/backend_test.go+23−4
internal/backend/backend_test.go+23−4
@@ -642,17 +642,36 @@ func protoWS(path, dataDir, clientID string) proto.Workspace {
642642 return proto.Workspace{Path: path, DataDir: dataDir, ClientID: clientID}
643643}
644644
645+// syncBuffer is a thread-safe buffer that can be safely read and written
646+// from multiple goroutines.
647+type syncBuffer struct {
648+ mu sync.Mutex
649+ buf bytes.Buffer
650+}
651+
652+func (sb *syncBuffer) Write(p []byte) (n int, err error) {
653+ sb.mu.Lock()
654+ defer sb.mu.Unlock()
655+ return sb.buf.Write(p)
656+}
657+
658+func (sb *syncBuffer) String() string {
659+ sb.mu.Lock()
660+ defer sb.mu.Unlock()
661+ return sb.buf.String()
662+}
663+
645664// captureDebugLogs installs a buffer-backed slog handler at Debug
646665// level for the duration of the test, returning the buffer. The
647666// previous default handler is restored via t.Cleanup.
648−func captureDebugLogs(t *testing.T) *bytes.Buffer {
667+func captureDebugLogs(t *testing.T) *syncBuffer {
649668 t.Helper()
650− var buf bytes.Buffer
669+ var sb syncBuffer
651670 prev := slog.Default()
652− handler := slog.NewTextHandler(&buf, &slog.HandlerOptions{Level: slog.LevelDebug})
671+ handler := slog.NewTextHandler(&sb, &slog.HandlerOptions{Level: slog.LevelDebug})
653672 slog.SetDefault(slog.New(handler))
654673 t.Cleanup(func() { slog.SetDefault(prev) })
655− return &buf
674+ return &sb
656675}
657676
658677// xdgIsolated points HOME and XDG_* variables at fresh tempdirs so