cmd/filewatch-mcp/integration_test.go
Ref: Size: 2.4 KiB
package main
import (
"bufio"
"encoding/json"
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
"testing"
"time"
)
// TestEndToEnd boots the binary as a subprocess, simulates a save, and
// verifies a notifications/claude/channel frame appears on stdout AND the
// file gets rewritten with [fw-XXXXXXXX].
func TestEndToEnd(t *testing.T) {
if testing.Short() {
t.Skip("integration")
}
if _, err := exec.LookPath("go"); err != nil {
t.Skip("go toolchain not on PATH")
}
dir := t.TempDir()
target := filepath.Join(dir, "foo.go")
if err := os.WriteFile(target, []byte("package foo\n"), 0o644); err != nil {
t.Fatal(err)
}
cmd := exec.Command("go", "run", ".", "--root", dir, "--debounce", "100ms")
stdin, err := cmd.StdinPipe()
if err != nil {
t.Fatal(err)
}
stdout, err := cmd.StdoutPipe()
if err != nil {
t.Fatal(err)
}
cmd.Stderr = os.Stderr
if err := cmd.Start(); err != nil {
t.Fatal(err)
}
defer func() {
stdin.Close()
_ = cmd.Process.Kill()
_ = cmd.Wait()
}()
// Send initialize so srv.Run completes the handshake.
fmt.Fprintln(stdin, `{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{}}}`)
sc := bufio.NewScanner(stdout)
sc.Buffer(make([]byte, 1<<16), 1<<16)
if !sc.Scan() {
t.Fatal("no init response")
}
// Trigger an atomic save (tmpfile + rename) to be realistic.
tmp := target + ".tmp"
if err := os.WriteFile(tmp, []byte("// @claude please review\npackage foo\n"), 0o644); err != nil {
t.Fatal(err)
}
if err := os.Rename(tmp, target); err != nil {
t.Fatal(err)
}
// Look for the channel notification in subsequent frames.
deadline := time.Now().Add(5 * time.Second)
var sawChannel bool
for time.Now().Before(deadline) {
if !sc.Scan() {
break
}
var f map[string]any
if err := json.Unmarshal(sc.Bytes(), &f); err != nil {
continue
}
if f["method"] == "notifications/claude/channel" {
sawChannel = true
break
}
}
if !sawChannel {
t.Fatal("no notifications/claude/channel frame on stdout")
}
// Verify the file got stamped. Allow a brief moment for the rewriter
// to run after the notification was emitted.
deadline = time.Now().Add(2 * time.Second)
var got []byte
for time.Now().Before(deadline) {
got, _ = os.ReadFile(target)
if strings.Contains(string(got), "@claude[fw-") {
return
}
time.Sleep(50 * time.Millisecond)
}
t.Errorf("file not stamped:\n%s", got)
}