package client import ( "maps" "os" "path/filepath" "slices" "testing" "github.com/fsnotify/fsnotify" ) func TestDaemonWatchesOnlyRulePaths(t *testing.T) { m, _ := setup(t) watcher, err := fsnotify.NewWatcher() if err != nil { t.Fatal(err) } t.Cleanup(func() { watcher.Close() }) d := &daemon{config: m.config, watcher: watcher, owners: map[string]string{}, trees: map[string]tree{}} gitDir := filepath.Join(m.root, ".git") expectWatched := func(want ...string) { t.Helper() slices.Sort(want) if got := slices.Sorted(maps.Keys(d.owners)); !slices.Equal(got, want) { t.Fatalf("watching %v, want %v", got, want) } } write(t, m.root, "target/debug/deps/big.o", "build output") write(t, m.root, "docs/agents/sub/note.md", "note") d.watch(m.root) expectWatched(m.root, gitDir, filepath.Join(m.root, "docs"), filepath.Join(m.root, "docs/agents"), filepath.Join(m.root, "docs/agents/sub")) write(t, m.root, RulesFile, "shared notes/plans\n") if err := os.RemoveAll(filepath.Join(m.root, "docs/agents/sub")); err != nil { t.Fatal(err) } d.watch(m.root) expectWatched(m.root, gitDir) for name, want := range map[string]bool{ "notes": true, "notes/plans/a.md": true, RulesFile: true, ".git/HEAD": true, ".git/index": false, "notes/plans/a.lodge-tmp": false, "docs/agents/x.md": false, "target/debug/deps/big.o": false, } { if got := d.trees[m.root].relevant(m.root, filepath.Join(m.root, name)); got != want { t.Errorf("relevant(%s) = %v, want %v", name, got, want) } } } func TestDaemonSkipsIgnoredPaths(t *testing.T) { m, _ := setup(t) watcher, err := fsnotify.NewWatcher() if err != nil { t.Fatal(err) } t.Cleanup(func() { watcher.Close() }) d := &daemon{config: m.config, watcher: watcher, owners: map[string]string{}, trees: map[string]tree{}} write(t, m.root, RulesFile, "shared vault\nignore vault/.obsidian/workspace*.json\nignore .DS_Store\nignore vault/.trash\n") write(t, m.root, "vault/.obsidian/app.json", "settings") write(t, m.root, "vault/.trash/old.md", "old") d.watch(m.root) if _, watched := d.owners[filepath.Join(m.root, "vault/.trash")]; watched { t.Fatal("ignored directory is watched") } for name, want := range map[string]bool{ "vault/note.md": true, "vault/.obsidian/app.json": true, "vault/.obsidian/workspace.json": false, "vault/.obsidian/workspace-mobile.json": false, "vault/.DS_Store": false, "vault/.trash/old.md": false, } { if got := d.trees[m.root].relevant(m.root, filepath.Join(m.root, name)); got != want { t.Errorf("relevant(%s) = %v, want %v", name, got, want) } } } func TestDaemonForgetsVanishedWorktrees(t *testing.T) { cfg := &Config{DataDir: t.TempDir()} if err := writeJSONFile(cfg.registryPath(), []string{filepath.Join(t.TempDir(), "gone")}); err != nil { t.Fatal(err) } d := &daemon{config: cfg, owners: map[string]string{}, trees: map[string]tree{}} d.syncAll() if roots := cfg.Worktrees(); len(roots) != 0 { t.Fatalf("registry still lists %v", roots) } }