add test suite
This commit is contained in:
@@ -0,0 +1,358 @@
|
||||
package walker
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
"git.flo.fo/FLO/dirmd/internal/config"
|
||||
)
|
||||
|
||||
func writeFile(t *testing.T, dir, name, content string) {
|
||||
t.Helper()
|
||||
path := filepath.Join(dir, name)
|
||||
if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
|
||||
t.Fatalf("mkdir for %s: %v", name, err)
|
||||
}
|
||||
if err := os.WriteFile(path, []byte(content), 0644); err != nil {
|
||||
t.Fatalf("write %s: %v", name, err)
|
||||
}
|
||||
}
|
||||
|
||||
func baseCfg(absRoot string) *config.Config {
|
||||
return &config.Config{
|
||||
AbsRoot: absRoot,
|
||||
InputPath: absRoot,
|
||||
MaxSize: 1024 * 1024,
|
||||
MaxDepth: 20,
|
||||
Ignores: config.DefaultIgnores,
|
||||
Extensions: config.DefaultExts,
|
||||
}
|
||||
}
|
||||
|
||||
func relPaths(entries []Entry) []string {
|
||||
paths := make([]string, 0, len(entries))
|
||||
for _, e := range entries {
|
||||
paths = append(paths, e.RelPath)
|
||||
}
|
||||
return paths
|
||||
}
|
||||
|
||||
func containsPath(paths []string, target string) bool {
|
||||
for _, p := range paths {
|
||||
if p == target {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func TestWalkDirectory_happy(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
writeFile(t, root, "main.go", "package main\n")
|
||||
writeFile(t, root, "cmd/tool/main.go", "package main\n")
|
||||
writeFile(t, root, "cmd/tool/util.go", "package tool\n")
|
||||
writeFile(t, root, "internal/x/y/deep.go", "package y\n")
|
||||
|
||||
result, err := Run(baseCfg(root))
|
||||
if err != nil {
|
||||
t.Fatalf("Run: %v", err)
|
||||
}
|
||||
|
||||
got := relPaths(result.Entries)
|
||||
want := []string{"main.go", "cmd/tool/main.go", "cmd/tool/util.go", "internal/x/y/deep.go"}
|
||||
if len(got) != len(want) {
|
||||
t.Fatalf("entries = %v; want %v", got, want)
|
||||
}
|
||||
for _, w := range want {
|
||||
if !containsPath(got, w) {
|
||||
t.Errorf("missing entry %q in %v", w, got)
|
||||
}
|
||||
}
|
||||
if result.AbsRoot != root {
|
||||
t.Errorf("AbsRoot = %q; want %q", result.AbsRoot, root)
|
||||
}
|
||||
if result.Cfg == nil {
|
||||
t.Error("Cfg not attached to result")
|
||||
}
|
||||
}
|
||||
|
||||
func TestWalkDirectory_respects_filters(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
setup func(t *testing.T) string
|
||||
must []string
|
||||
musts []string
|
||||
}{
|
||||
{
|
||||
name: "default_ignores_skip_dirs_and_files",
|
||||
setup: func(t *testing.T) string {
|
||||
root := t.TempDir()
|
||||
writeFile(t, root, "main.go", "x\n")
|
||||
writeFile(t, root, ".git/config", "x\n")
|
||||
writeFile(t, root, "node_modules/pkg/index.js", "x\n")
|
||||
writeFile(t, root, "go.mod", "module x\n")
|
||||
writeFile(t, root, "docs/readme.txt", "x\n")
|
||||
return root
|
||||
},
|
||||
must: []string{"main.go", "docs/readme.txt"},
|
||||
musts: []string{".git/config", "node_modules/pkg/index.js", "go.mod"},
|
||||
},
|
||||
{
|
||||
name: "default_exts_skip_binary_types",
|
||||
setup: func(t *testing.T) string {
|
||||
root := t.TempDir()
|
||||
writeFile(t, root, "keep.go", "x\n")
|
||||
writeFile(t, root, "img.png", "x\n")
|
||||
writeFile(t, root, "arch.zip", "x\n")
|
||||
return root
|
||||
},
|
||||
must: []string{"keep.go"},
|
||||
musts: []string{"img.png", "arch.zip"},
|
||||
},
|
||||
{
|
||||
name: "hidden_files_and_dirs_skipped",
|
||||
setup: func(t *testing.T) string {
|
||||
root := t.TempDir()
|
||||
writeFile(t, root, "visible.go", "x\n")
|
||||
writeFile(t, root, ".hidden.go", "x\n")
|
||||
writeFile(t, root, ".github/workflows/ci.yml", "x\n")
|
||||
return root
|
||||
},
|
||||
must: []string{"visible.go"},
|
||||
musts: []string{".hidden.go", ".github/workflows/ci.yml"},
|
||||
},
|
||||
{
|
||||
name: "max_size_skips_large_files",
|
||||
setup: func(t *testing.T) string {
|
||||
root := t.TempDir()
|
||||
writeFile(t, root, "small.go", "x\n")
|
||||
writeFile(t, root, "large.go", "this content exceeds ten bytes\n")
|
||||
return root
|
||||
},
|
||||
must: []string{"small.go"},
|
||||
musts: []string{"large.go"},
|
||||
},
|
||||
{
|
||||
name: "omit_test_skips_go_tests",
|
||||
setup: func(t *testing.T) string {
|
||||
root := t.TempDir()
|
||||
writeFile(t, root, "main.go", "x\n")
|
||||
writeFile(t, root, "main_test.go", "x\n")
|
||||
writeFile(t, root, "tests/helper.py", "x\n")
|
||||
return root
|
||||
},
|
||||
must: []string{"main.go"},
|
||||
musts: []string{"main_test.go", "tests/helper.py"},
|
||||
},
|
||||
{
|
||||
name: "omit_md_keeps_readme_only",
|
||||
setup: func(t *testing.T) string {
|
||||
root := t.TempDir()
|
||||
writeFile(t, root, "README.md", "x\n")
|
||||
writeFile(t, root, "NOTES.md", "x\n")
|
||||
writeFile(t, root, "src/notes.md", "x\n")
|
||||
return root
|
||||
},
|
||||
must: []string{"README.md"},
|
||||
musts: []string{"NOTES.md", "src/notes.md"},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
root := tt.setup(t)
|
||||
cfg := baseCfg(root)
|
||||
switch tt.name {
|
||||
case "max_size_skips_large_files":
|
||||
cfg.MaxSize = 10
|
||||
case "omit_test_skips_go_tests":
|
||||
cfg.OmitTest = true
|
||||
case "omit_md_keeps_readme_only":
|
||||
cfg.OmitMd = true
|
||||
}
|
||||
|
||||
result, err := Run(cfg)
|
||||
if err != nil {
|
||||
t.Fatalf("Run: %v", err)
|
||||
}
|
||||
|
||||
got := relPaths(result.Entries)
|
||||
for _, w := range tt.must {
|
||||
if !containsPath(got, w) {
|
||||
t.Errorf("missing %q in %v", w, got)
|
||||
}
|
||||
}
|
||||
for _, w := range tt.musts {
|
||||
if containsPath(got, w) {
|
||||
t.Errorf("unexpected %q in %v", w, got)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestWalkDirectory_custom_ignore(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
writeFile(t, root, "main.go", "x\n")
|
||||
writeFile(t, root, "scratch/notes.txt", "x\n")
|
||||
|
||||
cfg := baseCfg(root)
|
||||
cfg.Ignores = append([]string{}, config.DefaultIgnores...)
|
||||
cfg.Ignores = append(cfg.Ignores, "scratch")
|
||||
|
||||
result, err := Run(cfg)
|
||||
if err != nil {
|
||||
t.Fatalf("Run: %v", err)
|
||||
}
|
||||
got := relPaths(result.Entries)
|
||||
if containsPath(got, "scratch/notes.txt") {
|
||||
t.Errorf("custom-ignored dir walked: %v", got)
|
||||
}
|
||||
if !containsPath(got, "main.go") {
|
||||
t.Errorf("missing main.go in %v", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWalkDirectory_max_depth(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
writeFile(t, root, "a/b/c/d/e/deep.go", "x\n")
|
||||
writeFile(t, root, "a/shallow.go", "x\n")
|
||||
|
||||
cfg := baseCfg(root)
|
||||
cfg.MaxDepth = 3
|
||||
|
||||
result, err := Run(cfg)
|
||||
if err != nil {
|
||||
t.Fatalf("Run: %v", err)
|
||||
}
|
||||
got := relPaths(result.Entries)
|
||||
if containsPath(got, "a/b/c/d/e/deep.go") {
|
||||
t.Errorf("file beyond max depth included: %v", got)
|
||||
}
|
||||
if !containsPath(got, "a/shallow.go") {
|
||||
t.Errorf("missing shallow file in %v", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWalkDirectory_symlink_skipped(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("symlinks unreliable on windows")
|
||||
}
|
||||
|
||||
root := t.TempDir()
|
||||
target := filepath.Join(root, "real", "file.go")
|
||||
if err := os.MkdirAll(filepath.Dir(target), 0755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.WriteFile(target, []byte("x\n"), 0644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.Symlink(filepath.Join(root, "real"), filepath.Join(root, "linkdir")); err != nil {
|
||||
t.Skipf("cannot create symlink: %v", err)
|
||||
}
|
||||
|
||||
result, err := Run(baseCfg(root))
|
||||
if err != nil {
|
||||
t.Fatalf("Run: %v", err)
|
||||
}
|
||||
got := relPaths(result.Entries)
|
||||
if containsPath(got, "linkdir/file.go") {
|
||||
t.Errorf("symlinked dir walked: %v", got)
|
||||
}
|
||||
if !containsPath(got, "real/file.go") {
|
||||
t.Errorf("missing real entry in %v", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWalkDirectory_instructions_read(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
writeFile(t, root, "main.go", "x\n")
|
||||
writeFile(t, root, ".dirmd", "be helpful\n")
|
||||
|
||||
cfg := baseCfg(root)
|
||||
cfg.IncludeInstructions = true
|
||||
|
||||
result, err := Run(cfg)
|
||||
if err != nil {
|
||||
t.Fatalf("Run: %v", err)
|
||||
}
|
||||
if result.Instructions != "be helpful\n" {
|
||||
t.Errorf("Instructions = %q; want %q", result.Instructions, "be helpful\n")
|
||||
}
|
||||
}
|
||||
|
||||
func TestWalkDirectory_instructions_not_read_by_default(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
writeFile(t, root, "main.go", "x\n")
|
||||
writeFile(t, root, ".dirmd", "be helpful\n")
|
||||
|
||||
result, err := Run(baseCfg(root))
|
||||
if err != nil {
|
||||
t.Fatalf("Run: %v", err)
|
||||
}
|
||||
if result.Instructions != "" {
|
||||
t.Errorf("Instructions = %q; want empty", result.Instructions)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWalkDirectory_git_commit_outside_repo(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
writeFile(t, root, "main.go", "x\n")
|
||||
|
||||
result, err := Run(baseCfg(root))
|
||||
if err != nil {
|
||||
t.Fatalf("Run: %v", err)
|
||||
}
|
||||
if result.Commit != nil {
|
||||
t.Errorf("Commit = %v; want nil outside a git repo", result.Commit)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProcessSingleFile(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
writeFile(t, root, "main.go", "package main\n")
|
||||
|
||||
cfg := baseCfg(filepath.Join(root, "main.go"))
|
||||
cfg.SingleFile = true
|
||||
|
||||
result, err := Run(cfg)
|
||||
if err != nil {
|
||||
t.Fatalf("Run: %v", err)
|
||||
}
|
||||
if len(result.Entries) != 1 {
|
||||
t.Fatalf("entries = %v; want single entry", result.Entries)
|
||||
}
|
||||
if result.Entries[0].RelPath != "main.go" {
|
||||
t.Errorf("RelPath = %q; want %q", result.Entries[0].RelPath, "main.go")
|
||||
}
|
||||
if result.AbsRoot != root {
|
||||
t.Errorf("AbsRoot = %q; want %q", result.AbsRoot, root)
|
||||
}
|
||||
if !result.SingleFile {
|
||||
t.Error("SingleFile not set")
|
||||
}
|
||||
}
|
||||
|
||||
func TestProcessSingleFile_filtered_out(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
writeFile(t, root, "image.png", "x\n")
|
||||
|
||||
cfg := baseCfg(filepath.Join(root, "image.png"))
|
||||
cfg.SingleFile = true
|
||||
|
||||
_, err := Run(cfg)
|
||||
if err == nil {
|
||||
t.Fatal("expected error for filtered single file")
|
||||
}
|
||||
}
|
||||
|
||||
func TestProcessSingleFile_missing(t *testing.T) {
|
||||
cfg := baseCfg("/nonexistent/path/file.go")
|
||||
cfg.SingleFile = true
|
||||
|
||||
_, err := Run(cfg)
|
||||
if err == nil {
|
||||
t.Fatal("expected error for missing file")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user