154 lines
4.4 KiB
Go
154 lines
4.4 KiB
Go
package readonlyfs
|
|
|
|
import (
|
|
"errors"
|
|
"io"
|
|
"io/fs"
|
|
"testing"
|
|
"testing/fstest"
|
|
)
|
|
|
|
func TestNewPassesFSTest(t *testing.T) {
|
|
for _, test := range []struct {
|
|
name string
|
|
files map[string][]byte
|
|
paths []string
|
|
}{
|
|
{name: "empty", files: map[string][]byte{}, paths: nil},
|
|
{name: "single file", files: map[string][]byte{"message.txt": []byte("hello")}, paths: []string{"message.txt"}},
|
|
{name: "nested tree", files: map[string][]byte{"a/one.txt": []byte("one"), "a/b/two.txt": []byte("two")}, paths: []string{"a/one.txt", "a/b/two.txt"}},
|
|
} {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
fsys, err := New(test.files)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := fstest.TestFS(fsys, test.paths...); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestNewOwnsConstructionAndReadBytes(t *testing.T) {
|
|
input := []byte("value")
|
|
fsys, err := New(map[string][]byte{"value.txt": input})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
input[0] = 'x'
|
|
first, err := fsys.ReadFile("value.txt")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
first[0] = 'y'
|
|
second, err := fsys.ReadFile("value.txt")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if string(second) != "value" {
|
|
t.Fatalf("ReadFile() = %q, want owned bytes", second)
|
|
}
|
|
}
|
|
|
|
func TestFilesystemStandardBehavior(t *testing.T) {
|
|
fsys, err := New(map[string][]byte{
|
|
"z.txt": []byte("z"),
|
|
"nested/a.md": []byte("abc"),
|
|
"nested/b.md": []byte("b"),
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
entries, err := fsys.ReadDir("nested")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(entries) != 2 || entries[0].Name() != "a.md" || entries[1].Name() != "b.md" {
|
|
t.Fatalf("ReadDir(nested) = %#v, want sorted entries", entries)
|
|
}
|
|
info, err := entries[0].Info()
|
|
if err != nil || info.Mode().Perm() != 0o444 || info.Size() != 3 {
|
|
t.Fatalf("file info = %#v, %v", info, err)
|
|
}
|
|
root, err := fsys.Open(".")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
rootInfo, err := root.Stat()
|
|
if err != nil || !rootInfo.IsDir() || rootInfo.Mode() != fs.ModeDir|0o555 {
|
|
t.Fatalf("root info = %#v, %v", rootInfo, err)
|
|
}
|
|
directory := root.(fs.ReadDirFile)
|
|
first, err := directory.ReadDir(1)
|
|
if err != nil || len(first) != 1 || first[0].Name() != "nested" {
|
|
t.Fatalf("first directory page = %#v, %v", first, err)
|
|
}
|
|
second, err := directory.ReadDir(1)
|
|
if err != nil || len(second) != 1 || second[0].Name() != "z.txt" {
|
|
t.Fatalf("second directory page = %#v, %v", second, err)
|
|
}
|
|
if _, err := directory.ReadDir(1); !errors.Is(err, io.EOF) {
|
|
t.Fatalf("final directory page error = %v, want EOF", err)
|
|
}
|
|
secondRoot, err := fsys.Open(".")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
secondDirectory := secondRoot.(fs.ReadDirFile)
|
|
firstAgain, err := secondDirectory.ReadDir(1)
|
|
if err != nil || len(firstAgain) != 1 || firstAgain[0].Name() != "nested" {
|
|
t.Fatalf("independent directory handle page = %#v, %v", firstAgain, err)
|
|
}
|
|
|
|
left, err := fsys.Open("nested/a.md")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
right, err := fsys.Open("nested/a.md")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
leftByte := make([]byte, 1)
|
|
rightByte := make([]byte, 1)
|
|
if _, err := left.Read(leftByte); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := right.Read(rightByte); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if string(leftByte) != "a" || string(rightByte) != "a" {
|
|
t.Fatalf("independent file handles read %q and %q", leftByte, rightByte)
|
|
}
|
|
for _, test := range []struct {
|
|
name string
|
|
op func() error
|
|
want error
|
|
}{
|
|
{name: "invalid open path", op: func() error { _, err := fsys.Open("../value"); return err }, want: fs.ErrInvalid},
|
|
{name: "missing file", op: func() error { _, err := fsys.Open("missing"); return err }, want: fs.ErrNotExist},
|
|
{name: "read directory", op: func() error { _, err := fsys.ReadFile("nested"); return err }, want: errIsDirectory},
|
|
{name: "read directory from file", op: func() error { _, err := fsys.ReadDir("z.txt"); return err }, want: errNotDirectory},
|
|
} {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
if err := test.op(); !errors.Is(err, test.want) {
|
|
t.Fatalf("operation error = %v, want %v", err, test.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestNewRejectsInvalidAndConflictingPaths(t *testing.T) {
|
|
for _, files := range []map[string][]byte{
|
|
{".": []byte("root")},
|
|
{"": []byte("empty")},
|
|
{"../outside": []byte("outside")},
|
|
{"nested//file": []byte("invalid")},
|
|
{"file": []byte("file"), "file/nested": []byte("nested")},
|
|
} {
|
|
if _, err := New(files); err == nil {
|
|
t.Fatalf("New(%#v) error = nil, want rejection", files)
|
|
}
|
|
}
|
|
}
|