package windsurf import ( "bytes" "log/slog" "strings" "sync" "testing" "time" ) // TestScanLSOutputEmitsLines checks that scanLSOutput forwards each non-empty // line (with \r stripped) to slog and exits on EOF without leaking goroutines. func TestScanLSOutputEmitsLines(t *testing.T) { prev := slog.Default() defer slog.SetDefault(prev) var buf bytes.Buffer var mu sync.Mutex slog.SetDefault(slog.New(slog.NewTextHandler(&lockedWriter{b: &buf, mu: &mu}, &slog.HandlerOptions{Level: slog.LevelDebug}))) input := "line one\r\nline two\n\nline three\n" done := make(chan struct{}) go func() { scanLSOutput(strings.NewReader(input), "test-key", 4242, "stdout") close(done) }() select { case <-done: case <-time.After(2 * time.Second): t.Fatal("scanLSOutput did not return within 2s after EOF") } mu.Lock() out := buf.String() mu.Unlock() for _, want := range []string{"line one", "line two", "line three", "test-key", "stdout", "pid=4242"} { if !strings.Contains(out, want) { t.Errorf("expected log output to contain %q, got:\n%s", want, out) } } if strings.Count(out, "windsurf_ls_output") != 3 { t.Errorf("expected 3 log entries (empty line skipped), got:\n%s", out) } } // lockedWriter serializes slog handler writes so the test can read the buffer // safely from the test goroutine. type lockedWriter struct { b *bytes.Buffer mu *sync.Mutex } func (w *lockedWriter) Write(p []byte) (int, error) { w.mu.Lock() defer w.mu.Unlock() return w.b.Write(p) }