sdk/iox/globfs_test.go

234 lines
6.4 KiB
Go
Raw Normal View History

package iox
import (
"io/fs"
"reflect"
"sort"
"testing"
"testing/fstest"
)
func setupFS() fs.ReadDirFS {
// Create an in-memory FS with a mix of files and directories
return fstest.MapFS{
"main.go": &fstest.MapFile{Data: []byte("package main")},
"main_test.go": &fstest.MapFile{Data: []byte("package main_test")},
"README.md": &fstest.MapFile{Data: []byte("# readme")},
"LICENSE": &fstest.MapFile{Data: []byte("MIT")},
"docs/guide.md": &fstest.MapFile{Data: []byte("Docs")},
"docs/other.txt": &fstest.MapFile{Data: []byte("Other")},
"docs/hidden/.keep": &fstest.MapFile{Data: []byte("")},
"assets/img.png": &fstest.MapFile{Data: []byte("PNG")},
"assets/style.css": &fstest.MapFile{Data: []byte("CSS")},
".gitignore": &fstest.MapFile{Data: []byte("*.log")},
".hiddenfile": &fstest.MapFile{Data: []byte("")},
"emptydir/": &fstest.MapFile{Mode: fs.ModeDir},
}
}
// helper to get base names for easier comparison
func basenames(entries []fs.DirEntry) []string {
names := []string{}
for _, e := range entries {
names = append(names, e.Name())
}
sort.Strings(names)
return names
}
func TestGlobFSMultiplePatterns(t *testing.T) {
memfs := setupFS()
gfs := NewGlobFS(memfs, "*.go", "*.md", "assets/*", "docs/guide.md", ".gitignore")
tests := []struct {
path string
want []string
wantErr bool
}{
{path: ".", want: []string{"README.md", "assets", "docs", "main.go", "main_test.go", ".gitignore"}},
{path: "assets", want: []string{"img.png", "style.css"}},
{path: "docs", want: []string{"guide.md"}},
{path: "docs/hidden", want: []string{}, wantErr: true},
{path: "emptydir", want: []string{}, wantErr: true},
}
for _, tc := range tests {
tc := tc // capture range variable
t.Run(tc.path, func(t *testing.T) {
entries, err := fs.ReadDir(gfs, tc.path)
if tc.wantErr && err == nil {
t.Errorf("expected error, got nil")
return
}
if !tc.wantErr && err != nil {
t.Errorf("unexpected error: %v", err)
return
}
got := basenames(entries)
sort.Strings(tc.want)
if !reflect.DeepEqual(got, tc.want) {
t.Errorf("got %v; want %v", got, tc.want)
}
})
}
}
func TestGlobFSOpen(t *testing.T) {
memfs := setupFS()
gfs := NewGlobFS(memfs, "*.go", "*.md", "assets/*", "docs/guide.md", ".gitignore")
type test struct {
path string
wantErr bool
}
tests := []test{
{path: "main.go"},
{path: "README.md"},
{path: "LICENSE", wantErr: true},
{path: "assets/img.png"},
{path: "assets/style.css"},
{path: "assets/nonexistent.png", wantErr: true},
{path: "docs/guide.md"},
{path: "docs/other.txt", wantErr: true},
{path: ".gitignore"},
{path: ".hiddenfile", wantErr: true},
{path: "docs/hidden/.keep", wantErr: true},
{path: "emptydir", wantErr: true},
{path: "docs"}, // allowed because it contains matching file(s)
{path: "assets"}, // allowed because it contains matching file(s)
}
for _, tc := range tests {
tc := tc
t.Run(tc.path, func(t *testing.T) {
f, err := gfs.Open(tc.path)
if tc.wantErr && err == nil {
t.Errorf("expected error, got file")
if f != nil {
f.Close()
}
} else if !tc.wantErr && err != nil {
t.Errorf("unexpected error: %v", err)
} else if !tc.wantErr && err == nil {
info, _ := f.Stat()
if info.IsDir() {
_, derr := fs.ReadDir(gfs, tc.path)
if derr != nil && !tc.wantErr {
t.Errorf("unexpected error: %v", derr)
}
}
f.Close()
}
})
}
}
func TestGlobFSReadFile(t *testing.T) {
memfs := setupFS()
gfs := NewGlobFS(memfs, "*.go", "*.md", "assets/*", ".gitignore")
tests := []struct {
name string
want []byte
wantErr bool
}{
{name: "main.go", want: []byte("package main")},
{name: "main_test.go", want: []byte("package main_test")},
{name: "README.md", want: []byte("# readme")},
{name: "assets/img.png", want: []byte("PNG")},
{name: "assets/style.css", want: []byte("CSS")},
{name: ".gitignore", want: []byte("*.log")},
{name: "LICENSE", wantErr: true}, // not allowed by filter
{name: "docs/guide.md", wantErr: true}, // not allowed by filter
{name: "docs/hidden/.keep", wantErr: true}, // not allowed by filter
{name: "doesnotexist.txt", wantErr: true}, // does not exist
}
for _, tc := range tests {
tc := tc
t.Run(tc.name, func(t *testing.T) {
got, err := fs.ReadFile(gfs, tc.name)
if tc.wantErr {
if err == nil {
t.Errorf("expected error, got nil (got=%q)", got)
}
} else {
if err != nil {
t.Errorf("unexpected error: %v", err)
}
if string(got) != string(tc.want) {
t.Errorf("got %q; want %q", got, tc.want)
}
}
})
}
}
func TestGlobFSRelativePaths(t *testing.T) {
memfs := setupFS()
gfs := NewGlobFS(memfs, "docs/*.md")
entries, err := fs.ReadDir(gfs, "docs")
if err != nil {
t.Fatal(err)
}
got := basenames(entries)
want := []string{"guide.md"}
if !reflect.DeepEqual(got, want) {
t.Errorf("docs/*.md: got %v, want %v", got, want)
}
}
func TestGlobFSNoMatchesOpen(t *testing.T) {
gfs := NewGlobFS(setupFS(), "*.xyz")
_, err := gfs.Open("main.go")
if err == nil {
t.Fatal("expected error when opening file with no matches")
}
}
func TestGlobFSNoMatchesStat(t *testing.T) {
gfs := NewGlobFS(setupFS(), "*.xyz")
_, err := fs.Stat(gfs, "main.go")
if err == nil {
t.Fatal("expected error with no matches: stat")
}
}
func TestGlobFSNoMatchesReadDir(t *testing.T) {
gfs := NewGlobFS(setupFS(), "*.xyz")
_, err := fs.ReadDir(gfs, "main.go")
if err == nil {
t.Fatal("expected error with no matches: readdir")
}
}
func TestGlobFSNoMatchesReadFile(t *testing.T) {
gfs := NewGlobFS(setupFS(), "*.xyz")
_, err := fs.ReadFile(gfs, "main.go")
if err == nil {
t.Fatal("expected error with no matches: readfile")
}
}
func TestGlobFS_IntegrationWithStdlib(t *testing.T) {
memfs := setupFS()
gfs := NewGlobFS(memfs, "*.go", "docs/guide.md")
// Use fs.WalkDir with our filtered FS
var walked []string
err := fs.WalkDir(gfs, ".", func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
walked = append(walked, path)
return nil
})
if err != nil {
t.Fatal(err)
}
// Only files and dirs matching or containing matches should appear
for _, p := range walked {
if p == "." || p == "main.go" || p == "main_test.go" || p == "docs" || p == "docs/guide.md" {
continue
}
t.Errorf("WalkDir: unexpected path %q", p)
}
}