package walk import ( "os" "path/filepath" "testing" "git.mleku.dev/mleku/dendrite/pkg/ratio" ) func setupTestRepo(t *testing.T) string { t.Helper() dir := t.TempDir() // Create a mini repo structure. dirs := []string{ "pkg/a", "pkg/b", "cmd/main", "_output", "node_modules", } for _, d := range dirs { os.MkdirAll(filepath.Join(dir, d), 0o755) } // Create source files. files := map[string]string{ "main.go": "package main\n", "pkg/a/a.go": "package a\n", "pkg/a/a_test.go": "package a\n", "pkg/b/b.go": "package b\n", "cmd/main/main.go": "package main\n", "README.md": "# readme\n", "_output/gen.go": "package gen\n", "node_modules/x.js": "export default {};\n", } for name, content := range files { os.WriteFile(filepath.Join(dir, name), []byte(content), 0o644) } return dir } func TestBuildManifest(t *testing.T) { dir := setupTestRepo(t) m, err := Build(dir, 42, DefaultExclude) if err != nil { t.Fatalf("Build: %v", err) } // Should include everything: main.go, pkg/a/a.go, pkg/a/a_test.go, pkg/b/b.go, // cmd/main/main.go, README.md, _output/gen.go // Should exclude only: node_modules/x.js (excluded directory) if len(m.Files) != 7 { t.Fatalf("expected 7 files, got %d: %v", len(m.Files), m.Files) } for _, f := range m.Files { if f == filepath.Join("node_modules", "x.js") { t.Fatalf("unexpected file in manifest: %s", f) } } } func TestDeterministicPermutation(t *testing.T) { dir := setupTestRepo(t) m1, _ := Build(dir, 42, DefaultExclude) m2, _ := Build(dir, 42, DefaultExclude) if len(m1.Files) != len(m2.Files) { t.Fatalf("different file counts: %d vs %d", len(m1.Files), len(m2.Files)) } for i := range m1.Files { if m1.Files[i] != m2.Files[i] { t.Fatalf("order differs at %d: %s vs %s", i, m1.Files[i], m2.Files[i]) } } } func TestDifferentSeedDifferentOrder(t *testing.T) { dir := setupTestRepo(t) m1, _ := Build(dir, 42, DefaultExclude) m2, _ := Build(dir, 99, DefaultExclude) if len(m1.Files) != len(m2.Files) { t.Fatalf("different file counts") } same := true for i := range m1.Files { if m1.Files[i] != m2.Files[i] { same = false break } } if same { t.Fatal("different seeds produced identical order") } } func TestErgodicCoverage(t *testing.T) { dir := setupTestRepo(t) m, _ := Build(dir, 42, DefaultExclude) w := NewWalker(m) visited := make(map[string]bool) for { path, ok := w.Next() if !ok { break } if visited[path] { t.Fatalf("file visited twice: %s", path) } visited[path] = true } if len(visited) != len(m.Files) { t.Fatalf("visited %d files, expected %d", len(visited), len(m.Files)) } if !w.Done() { t.Fatal("walker should be done") } } func TestResume(t *testing.T) { dir := setupTestRepo(t) m, _ := Build(dir, 42, DefaultExclude) w1 := NewWalker(m) w1.Next() w1.Next() w2 := Resume(m, w1.Position) if w2.Remaining() != w1.Remaining() { t.Fatalf("remaining mismatch: %d vs %d", w2.Remaining(), w1.Remaining()) } path1, ok1 := w1.Next() path2, ok2 := w2.Next() if ok1 != ok2 || path1 != path2 { t.Fatalf("resumed walker diverged: %s/%v vs %s/%v", path1, ok1, path2, ok2) } } func TestProgress(t *testing.T) { dir := setupTestRepo(t) m, _ := Build(dir, 42, DefaultExclude) w := NewWalker(m) if !w.Progress().IsZero() { t.Fatalf("progress should be 0 at start, got %s", w.Progress()) } for !w.Done() { w.Next() } if !w.Progress().Equal(ratio.One) { t.Fatalf("progress should be 1 at end, got %s", w.Progress()) } }