fix(backend): fix data race in tests using captureDebugLogs

Christian Rocha created

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.

Change summary

internal/backend/backend_test.go | 27 +++++++++++++++++++++++----
1 file changed, 23 insertions(+), 4 deletions(-)

Detailed changes

internal/backend/backend_test.go 🔗

@@ -642,17 +642,36 @@ func protoWS(path, dataDir, clientID string) proto.Workspace {
 	return proto.Workspace{Path: path, DataDir: dataDir, ClientID: clientID}
 }
 
+// syncBuffer is a thread-safe buffer that can be safely read and written
+// from multiple goroutines.
+type syncBuffer struct {
+	mu  sync.Mutex
+	buf bytes.Buffer
+}
+
+func (sb *syncBuffer) Write(p []byte) (n int, err error) {
+	sb.mu.Lock()
+	defer sb.mu.Unlock()
+	return sb.buf.Write(p)
+}
+
+func (sb *syncBuffer) String() string {
+	sb.mu.Lock()
+	defer sb.mu.Unlock()
+	return sb.buf.String()
+}
+
 // captureDebugLogs installs a buffer-backed slog handler at Debug
 // level for the duration of the test, returning the buffer. The
 // previous default handler is restored via t.Cleanup.
-func captureDebugLogs(t *testing.T) *bytes.Buffer {
+func captureDebugLogs(t *testing.T) *syncBuffer {
 	t.Helper()
-	var buf bytes.Buffer
+	var sb syncBuffer
 	prev := slog.Default()
-	handler := slog.NewTextHandler(&buf, &slog.HandlerOptions{Level: slog.LevelDebug})
+	handler := slog.NewTextHandler(&sb, &slog.HandlerOptions{Level: slog.LevelDebug})
 	slog.SetDefault(slog.New(handler))
 	t.Cleanup(func() { slog.SetDefault(prev) })
-	return &buf
+	return &sb
 }
 
 // xdgIsolated points HOME and XDG_* variables at fresh tempdirs so