package files import ( "os" "path/filepath" "syscall" "testing" ) func TestList(t *testing.T) { dir := t.TempDir() subdir := filepath.Join(dir, "subdir") if err := os.MkdirAll(subdir, 0o755); err != nil { t.Fatal(err) } f1 := filepath.Join(dir, "a.txt") if err := os.WriteFile(f1, []byte("hello"), 0o644); err != nil { t.Fatal(err) } f2 := filepath.Join(dir, "b.log") if err := os.WriteFile(f2, []byte("world"), 0o644); err != nil { t.Fatal(err) } res, err := List(dir, nil, 1, 50) if err != nil { t.Fatalf("List: %v", err) } if res.Total != 3 { // subdir, a.txt, b.log t.Errorf("Total=%d, want 3", res.Total) } if res.HasMore { t.Errorf("HasMore=true, want false") } } func TestListPaged(t *testing.T) { dir := t.TempDir() for i := 0; i < 10; i++ { if err := os.WriteFile(filepath.Join(dir, string(rune('a'+i))+".txt"), []byte("x"), 0o644); err != nil { t.Fatal(err) } } page1, err := List(dir, nil, 1, 3) if err != nil { t.Fatalf("page1: %v", err) } if len(page1.Entries) != 3 { t.Errorf("page1 len=%d, want 3", len(page1.Entries)) } if !page1.HasMore { t.Errorf("page1.HasMore=false, want true") } page2, err := List(dir, nil, 2, 3) if err != nil { t.Fatalf("page2: %v", err) } if len(page2.Entries) != 3 { t.Errorf("page2 len=%d, want 3", len(page2.Entries)) } page4, err := List(dir, nil, 4, 3) if err != nil { t.Fatalf("page4: %v", err) } if len(page4.Entries) != 1 { t.Errorf("page4 len=%d, want 1", len(page4.Entries)) } if page4.HasMore { t.Errorf("page4.HasMore=true, want false") } } func TestListRejectsNotDir(t *testing.T) { dir := t.TempDir() f := filepath.Join(dir, "file.txt") if err := os.WriteFile(f, []byte("hi"), 0o644); err != nil { t.Fatal(err) } _, err := List(f, nil, 1, 50) if err == nil { t.Error("expected error for non-directory") } } func TestInfo(t *testing.T) { dir := t.TempDir() f := filepath.Join(dir, "test.txt") if err := os.WriteFile(f, []byte("hello world"), 0o644); err != nil { t.Fatal(err) } info, err := Info(f, nil) if err != nil { t.Fatalf("Info: %v", err) } if info.Name != "test.txt" { t.Errorf("Name=%q, want test.txt", info.Name) } if info.IsDir { t.Errorf("IsDir=true, want false") } if info.Size != 11 { t.Errorf("Size=%d, want 11", info.Size) } } func TestMkdir(t *testing.T) { dir := t.TempDir() newDir := filepath.Join(dir, "newdir", "nested") if err := Mkdir(newDir, nil); err != nil { t.Fatalf("Mkdir: %v", err) } info, err := os.Stat(newDir) if err != nil { t.Fatalf("stat: %v", err) } if !info.IsDir() { t.Error("expected dir") } } func TestMkdirDisallowed(t *testing.T) { dir := t.TempDir() err := Mkdir(filepath.Join(dir, "ok"), []string{"/tmp"}) if err == nil { t.Error("expected error for disallowed path") } } func TestRename(t *testing.T) { dir := t.TempDir() f := filepath.Join(dir, "old.txt") if err := os.WriteFile(f, []byte("hi"), 0o644); err != nil { t.Fatal(err) } if err := Rename(f, "new.txt", nil); err != nil { t.Fatalf("Rename: %v", err) } if _, err := os.Stat(filepath.Join(dir, "new.txt")); err != nil { t.Errorf("new file not found: %v", err) } if _, err := os.Stat(f); err == nil { t.Error("old file still exists") } } func TestRenameBadName(t *testing.T) { dir := t.TempDir() f := filepath.Join(dir, "old.txt") if err := os.WriteFile(f, []byte("hi"), 0o644); err != nil { t.Fatal(err) } tests := []string{"/etc/passwd", "..", ".", "-f", ""} for _, name := range tests { err := Rename(f, name, nil) if err == nil { t.Errorf("Rename(%q): expected error", name) } } } func TestDelete(t *testing.T) { dir := t.TempDir() f := filepath.Join(dir, "todelete.txt") if err := os.WriteFile(f, []byte("hi"), 0o644); err != nil { t.Fatal(err) } if err := Delete(f, nil); err != nil { t.Fatalf("Delete: %v", err) } if _, err := os.Stat(f); err == nil { t.Error("file still exists after delete") } } func TestDeleteRecursively(t *testing.T) { dir := t.TempDir() subdir := filepath.Join(dir, "sub") if err := os.MkdirAll(subdir, 0o755); err != nil { t.Fatal(err) } if err := os.WriteFile(filepath.Join(subdir, "f.txt"), []byte("x"), 0o644); err != nil { t.Fatal(err) } if err := Delete(subdir, nil); err != nil { t.Fatalf("Delete: %v", err) } if _, err := os.Stat(subdir); err == nil { t.Error("subdir still exists") } } func TestDeleteRejectsRoot(t *testing.T) { err := Delete("/", nil) if err == nil { t.Error("expected error deleting root") } } func TestChmod(t *testing.T) { dir := t.TempDir() f := filepath.Join(dir, "file.txt") if err := os.WriteFile(f, []byte("hi"), 0o644); err != nil { t.Fatal(err) } if err := Chmod(f, 0o600, nil); err != nil { t.Fatalf("Chmod: %v", err) } info, _ := os.Stat(f) if info.Mode().Perm() != 0o600 { t.Errorf("mode=%o, want 600", info.Mode().Perm()) } } func TestChown(t *testing.T) { dir := t.TempDir() f := filepath.Join(dir, "file.txt") if err := os.WriteFile(f, []byte("hi"), 0o644); err != nil { t.Fatal(err) } stat, _ := os.Stat(f) st := stat.Sys().(*syscall.Stat_t) if err := Chown(f, int(st.Uid), int(st.Gid), nil); err != nil { t.Fatalf("Chown: %v", err) } } func TestSearch(t *testing.T) { dir := t.TempDir() sub := filepath.Join(dir, "photos") if err := os.MkdirAll(sub, 0o755); err != nil { t.Fatal(err) } files := map[string]string{ "myphoto.jpg": "xxx", "photo_album.txt": "yyy", "report.pdf": "zzz", "data.csv": "aaa", } for name, content := range files { if err := os.WriteFile(filepath.Join(dir, name), []byte(content), 0o644); err != nil { t.Fatal(err) } } if err := os.WriteFile(filepath.Join(sub, "vacation.png"), []byte("img"), 0o644); err != nil { t.Fatal(err) } hits, err := Search(dir, "photo", 50, nil) if err != nil { t.Fatalf("Search: %v", err) } if len(hits) < 2 { t.Errorf("got %d hits, want at least 2", len(hits)) } } func TestSearchLimit(t *testing.T) { dir := t.TempDir() for i := 0; i < 20; i++ { if err := os.WriteFile(filepath.Join(dir, "file_match_"+string(rune('a'+i))+".txt"), []byte("x"), 0o644); err != nil { t.Fatal(err) } } hits, err := Search(dir, "file_match", 5, nil) if err != nil { t.Fatalf("Search: %v", err) } if len(hits) != 5 { t.Errorf("got %d hits, want 5", len(hits)) } } func TestValidatePathAllowed(t *testing.T) { dir := t.TempDir() f := filepath.Join(dir, "allowed.txt") if err := os.WriteFile(f, []byte("hi"), 0o644); err != nil { t.Fatal(err) } errs := []string{ filepath.Join(dir, "..", "etc", "passwd"), "/etc/passwd", "/tmp/../../../secret", } for _, path := range errs { _, err := List(path, []string{dir}, 1, 50) if err == nil { t.Errorf("expected error for %q", path) } } }