Files
notarius/internal/framework/readonlyfs/readonly_fs_test.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)
}
}
}