754 lines
19 KiB
Go
754 lines
19 KiB
Go
package vfstest
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"io/fs"
|
|
"math/rand/v2"
|
|
"runtime"
|
|
"slices"
|
|
"sync"
|
|
"testing"
|
|
"testing/fstest"
|
|
"unicode/utf16"
|
|
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/testutil"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/vfs"
|
|
"gotest.tools/v3/assert"
|
|
)
|
|
|
|
func TestInsensitive(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
contents := []byte("bar")
|
|
|
|
vfs := convertMapFS(fstest.MapFS{
|
|
"foo/bar/baz": &fstest.MapFile{
|
|
Data: contents,
|
|
Sys: 1234,
|
|
},
|
|
"foo/bar2/baz2": &fstest.MapFile{
|
|
Data: contents,
|
|
Sys: 1234,
|
|
},
|
|
"foo/bar3/baz3": &fstest.MapFile{
|
|
Data: contents,
|
|
Sys: 1234,
|
|
},
|
|
}, false /*useCaseSensitiveFileNames*/, nil)
|
|
|
|
sensitive, err := fs.ReadFile(vfs, "foo/bar/baz")
|
|
assert.NilError(t, err)
|
|
assert.DeepEqual(t, sensitive, contents)
|
|
sensitiveInfo, err := fs.Stat(vfs, "foo/bar/baz")
|
|
assert.NilError(t, err)
|
|
assert.Equal(t, sensitiveInfo.Sys(), 1234)
|
|
sensitiveRealPath, err := vfs.Realpath("foo/bar/baz")
|
|
assert.NilError(t, err)
|
|
assert.Equal(t, sensitiveRealPath, "foo/bar/baz")
|
|
entries, err := fs.ReadDir(vfs, "foo")
|
|
assert.NilError(t, err)
|
|
assert.DeepEqual(t, dirEntriesToNames(entries), []string{"bar", "bar2", "bar3"})
|
|
|
|
_, err = vfs.Realpath("does/not/exist")
|
|
assert.ErrorContains(t, err, "file does not exist")
|
|
_, err = fs.Stat(vfs, "does/not/exist")
|
|
assert.ErrorContains(t, err, "file does not exist")
|
|
|
|
assert.NilError(t, fstest.TestFS(vfs, "foo/bar/baz"))
|
|
|
|
insensitive, err := fs.ReadFile(vfs, "Foo/Bar/Baz")
|
|
assert.NilError(t, err)
|
|
assert.DeepEqual(t, insensitive, contents)
|
|
insensitiveInfo, err := fs.Stat(vfs, "Foo/Bar/Baz")
|
|
assert.NilError(t, err)
|
|
assert.Equal(t, insensitiveInfo.Sys(), 1234)
|
|
insensitiveRealPath, err := vfs.Realpath("Foo/Bar/Baz")
|
|
assert.NilError(t, err)
|
|
assert.Equal(t, insensitiveRealPath, "foo/bar/baz")
|
|
entries, err = fs.ReadDir(vfs, "Foo")
|
|
assert.NilError(t, err)
|
|
assert.DeepEqual(t, dirEntriesToNames(entries), []string{"bar", "bar2", "bar3"})
|
|
|
|
_, err = vfs.Realpath("Does/Not/Exist")
|
|
assert.ErrorContains(t, err, "file does not exist")
|
|
_, err = fs.Stat(vfs, "Does/Not/Exist")
|
|
assert.ErrorContains(t, err, "file does not exist")
|
|
|
|
// TODO: TestFS doesn't understand case-insensitive file systems.
|
|
// This same thing would happen with an os.Dir on Windows.
|
|
// assert.NilError(t, fstest.TestFS(vfs, "Foo/Bar/Baz"))
|
|
}
|
|
|
|
func TestInsensitiveUpper(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
contents := []byte("bar")
|
|
|
|
vfs := convertMapFS(fstest.MapFS{
|
|
"Foo/Bar/Baz": &fstest.MapFile{
|
|
Data: contents,
|
|
Sys: 1234,
|
|
},
|
|
"Foo/Bar2/Baz2": &fstest.MapFile{
|
|
Data: contents,
|
|
Sys: 1234,
|
|
},
|
|
"Foo/Bar3/Baz3": &fstest.MapFile{
|
|
Data: contents,
|
|
Sys: 1234,
|
|
},
|
|
}, false /*useCaseSensitiveFileNames*/, nil)
|
|
|
|
sensitive, err := fs.ReadFile(vfs, "foo/bar/baz")
|
|
assert.NilError(t, err)
|
|
assert.DeepEqual(t, sensitive, contents)
|
|
sensitiveInfo, err := fs.Stat(vfs, "foo/bar/baz")
|
|
assert.NilError(t, err)
|
|
assert.Equal(t, sensitiveInfo.Sys(), 1234)
|
|
entries, err := fs.ReadDir(vfs, "foo")
|
|
assert.NilError(t, err)
|
|
assert.DeepEqual(t, dirEntriesToNames(entries), []string{"Bar", "Bar2", "Bar3"})
|
|
|
|
// assert.NilError(t, fstest.TestFS(vfs, "foo/bar/baz"))
|
|
|
|
insensitive, err := fs.ReadFile(vfs, "Foo/Bar/Baz")
|
|
assert.NilError(t, err)
|
|
assert.DeepEqual(t, insensitive, contents)
|
|
insensitiveInfo, err := fs.Stat(vfs, "Foo/Bar/Baz")
|
|
assert.NilError(t, err)
|
|
assert.Equal(t, insensitiveInfo.Sys(), 1234)
|
|
entries, err = fs.ReadDir(vfs, "Foo")
|
|
assert.NilError(t, err)
|
|
assert.DeepEqual(t, dirEntriesToNames(entries), []string{"Bar", "Bar2", "Bar3"})
|
|
|
|
assert.NilError(t, fstest.TestFS(vfs, "Foo/Bar/Baz"))
|
|
}
|
|
|
|
func TestSensitive(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
contents := []byte("bar")
|
|
|
|
vfs := convertMapFS(fstest.MapFS{
|
|
"foo/bar/baz": &fstest.MapFile{
|
|
Data: contents,
|
|
Sys: 1234,
|
|
},
|
|
"foo/bar2/baz2": &fstest.MapFile{
|
|
Data: contents,
|
|
Sys: 1234,
|
|
},
|
|
"foo/bar3/baz3": &fstest.MapFile{
|
|
Data: contents,
|
|
Sys: 1234,
|
|
},
|
|
}, true /*useCaseSensitiveFileNames*/, nil)
|
|
|
|
sensitive, err := fs.ReadFile(vfs, "foo/bar/baz")
|
|
assert.NilError(t, err)
|
|
assert.DeepEqual(t, sensitive, contents)
|
|
sensitiveInfo, err := fs.Stat(vfs, "foo/bar/baz")
|
|
assert.NilError(t, err)
|
|
assert.Equal(t, sensitiveInfo.Sys(), 1234)
|
|
|
|
assert.NilError(t, fstest.TestFS(vfs, "foo/bar/baz"))
|
|
|
|
_, err = fs.ReadFile(vfs, "Foo/Bar/Baz")
|
|
assert.ErrorContains(t, err, "file does not exist")
|
|
}
|
|
|
|
func TestSensitiveDuplicatePath(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
testfs := fstest.MapFS{
|
|
"foo": &fstest.MapFile{
|
|
Data: []byte("bar"),
|
|
},
|
|
"Foo": &fstest.MapFile{
|
|
Data: []byte("baz"),
|
|
},
|
|
}
|
|
|
|
testutil.AssertPanics(t, func() {
|
|
convertMapFS(testfs, false /*useCaseSensitiveFileNames*/, nil)
|
|
}, `duplicate path: "Foo" and "foo" have the same canonical path`)
|
|
}
|
|
|
|
func TestInsensitiveDuplicatePath(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
testfs := fstest.MapFS{
|
|
"foo": &fstest.MapFile{
|
|
Data: []byte("bar"),
|
|
},
|
|
"Foo": &fstest.MapFile{
|
|
Data: []byte("baz"),
|
|
},
|
|
}
|
|
|
|
convertMapFS(testfs, true /*useCaseSensitiveFileNames*/, nil)
|
|
}
|
|
|
|
func dirEntriesToNames(entries []fs.DirEntry) []string {
|
|
names := make([]string, len(entries))
|
|
for i, entry := range entries {
|
|
names[i] = entry.Name()
|
|
}
|
|
return names
|
|
}
|
|
|
|
func TestWritableFS(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
fs := FromMap[any](nil, false)
|
|
|
|
err := fs.WriteFile("/foo/bar/baz", "hello, world", false)
|
|
assert.NilError(t, err)
|
|
|
|
content, ok := fs.ReadFile("/foo/bar/baz")
|
|
assert.Assert(t, ok)
|
|
assert.Equal(t, content, "hello, world")
|
|
|
|
err = fs.WriteFile("/foo/bar/baz", "goodbye, world", false)
|
|
assert.NilError(t, err)
|
|
|
|
content, ok = fs.ReadFile("/foo/bar/baz")
|
|
assert.Assert(t, ok)
|
|
assert.Equal(t, content, "goodbye, world")
|
|
|
|
err = fs.WriteFile("/foo/bar/baz/oops", "goodbye, world", false)
|
|
assert.ErrorContains(t, err, `mkdir "foo/bar/baz": path exists but is not a directory`)
|
|
}
|
|
|
|
func TestWritableFSDelete(t *testing.T) {
|
|
t.Parallel()
|
|
fs := FromMap[any](nil, false)
|
|
|
|
_ = fs.WriteFile("/foo/bar/file.ts", "remove", false)
|
|
assert.Assert(t, fs.FileExists("/foo/bar/file.ts"))
|
|
err := fs.Remove("/foo/bar/file.ts")
|
|
assert.NilError(t, err)
|
|
assert.Assert(t, !fs.FileExists("/foo/bar/file.ts"))
|
|
|
|
_ = fs.WriteFile("/foo/bar/test/remove2.ts", "remove2", false)
|
|
assert.Assert(t, fs.DirectoryExists("/foo/bar/test"))
|
|
err = fs.Remove("/foo/bar/test")
|
|
assert.NilError(t, err)
|
|
assert.Assert(t, !fs.FileExists("/foo/bar/test/remove2.ts"))
|
|
assert.Assert(t, !fs.DirectoryExists("/foo/bar/test"))
|
|
|
|
// no errors when removing file/dir that does not exist
|
|
err = fs.Remove("/foo/bar/test")
|
|
assert.NilError(t, err)
|
|
err = fs.Remove("/foo/bar/file.ts")
|
|
assert.NilError(t, err)
|
|
|
|
_ = fs.WriteFile("/foo/barbar", "remove2", false)
|
|
_ = fs.Remove("/foo/bar")
|
|
assert.Assert(t, fs.FileExists("/foo/barbar"))
|
|
}
|
|
|
|
func TestStress(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
fs := FromMap[any](nil, false)
|
|
|
|
ops := []func(){
|
|
func() { _ = fs.WriteFile("/foo/bar/baz.txt", "hello, world", false) },
|
|
func() { fs.ReadFile("/foo/bar/baz.txt") },
|
|
func() { fs.DirectoryExists("/foo/bar") },
|
|
func() { fs.FileExists("/foo/bar") },
|
|
func() { fs.FileExists("/foo/bar/baz.txt") },
|
|
func() { fs.GetAccessibleEntries("/foo/bar") },
|
|
func() { fs.Realpath("/foo/bar/baz.txt") },
|
|
func() {
|
|
_ = fs.WalkDir("/", func(path string, d vfs.DirEntry, err error) error {
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_, err = d.Info()
|
|
return err
|
|
})
|
|
},
|
|
}
|
|
|
|
var wg sync.WaitGroup
|
|
for range runtime.GOMAXPROCS(0) {
|
|
wg.Add(1)
|
|
go func() {
|
|
defer wg.Done()
|
|
|
|
randomOps := slices.Clone(ops)
|
|
rand.Shuffle(len(randomOps), func(i, j int) {
|
|
randomOps[i], randomOps[j] = randomOps[j], randomOps[i]
|
|
})
|
|
|
|
for i := range 10000 {
|
|
randomOps[i%len(randomOps)]()
|
|
}
|
|
}()
|
|
}
|
|
}
|
|
|
|
func TestParentDirFile(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
testfs := fstest.MapFS{
|
|
"foo": &fstest.MapFile{
|
|
Data: []byte("bar"),
|
|
},
|
|
"foo/oops": &fstest.MapFile{
|
|
Data: []byte("baz"),
|
|
},
|
|
}
|
|
|
|
testutil.AssertPanics(t, func() {
|
|
convertMapFS(testfs, false /*useCaseSensitiveFileNames*/, nil)
|
|
}, `failed to create intermediate directories for "foo/oops": mkdir "foo": path exists but is not a directory`)
|
|
}
|
|
|
|
func TestFromMap(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
t.Run("POSIX", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
fs := FromMap(map[string]any{
|
|
"/string": "hello, world",
|
|
"/bytes": []byte("hello, world"),
|
|
"/mapfile": &fstest.MapFile{
|
|
Data: []byte("hello, world"),
|
|
},
|
|
}, false)
|
|
|
|
content, ok := fs.ReadFile("/string")
|
|
assert.Assert(t, ok)
|
|
assert.Equal(t, content, "hello, world")
|
|
|
|
content, ok = fs.ReadFile("/bytes")
|
|
assert.Assert(t, ok)
|
|
assert.Equal(t, content, "hello, world")
|
|
|
|
content, ok = fs.ReadFile("/mapfile")
|
|
assert.Assert(t, ok)
|
|
assert.Equal(t, content, "hello, world")
|
|
})
|
|
|
|
t.Run("Windows", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
fs := FromMap(map[string]any{
|
|
"c:/string": "hello, world",
|
|
"d:/bytes": []byte("hello, world"),
|
|
"e:/mapfile": &fstest.MapFile{
|
|
Data: []byte("hello, world"),
|
|
},
|
|
}, false)
|
|
|
|
content, ok := fs.ReadFile("c:/string")
|
|
assert.Assert(t, ok)
|
|
assert.Equal(t, content, "hello, world")
|
|
|
|
content, ok = fs.ReadFile("d:/bytes")
|
|
assert.Assert(t, ok)
|
|
assert.Equal(t, content, "hello, world")
|
|
|
|
content, ok = fs.ReadFile("e:/mapfile")
|
|
assert.Assert(t, ok)
|
|
assert.Equal(t, content, "hello, world")
|
|
})
|
|
|
|
t.Run("Mixed", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
testutil.AssertPanics(t, func() {
|
|
FromMap(map[string]any{
|
|
"/string": "hello, world",
|
|
"c:/bytes": []byte("hello, world"),
|
|
}, false)
|
|
}, `mixed posix and windows paths`)
|
|
})
|
|
|
|
t.Run("NonRooted", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
testutil.AssertPanics(t, func() {
|
|
FromMap(map[string]any{
|
|
"string": "hello, world",
|
|
}, false)
|
|
}, `non-rooted path "string"`)
|
|
})
|
|
|
|
t.Run("NonNormalized", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
testutil.AssertPanics(t, func() {
|
|
FromMap(map[string]any{
|
|
"/string/": "hello, world",
|
|
}, false)
|
|
}, `non-normalized path "/string/"`)
|
|
})
|
|
|
|
t.Run("NonNormalized2", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
testutil.AssertPanics(t, func() {
|
|
FromMap(map[string]any{
|
|
"/string/../foo": "hello, world",
|
|
}, false)
|
|
}, `non-normalized path "/string/../foo"`)
|
|
})
|
|
|
|
t.Run("InvalidFile", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
testutil.AssertPanics(t, func() {
|
|
FromMap(map[string]any{
|
|
"/string": 1234,
|
|
}, false)
|
|
}, `invalid file type int`)
|
|
})
|
|
}
|
|
|
|
func TestVFSTestMapFS(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
fs := FromMap(map[string]string{
|
|
"/foo.ts": "hello, world",
|
|
"/dir1/file1.ts": "export const foo = 42;",
|
|
"/dir1/file2.ts": "export const foo = 42;",
|
|
"/dir2/file1.ts": "export const foo = 42;",
|
|
}, false /*useCaseSensitiveFileNames*/)
|
|
|
|
t.Run("ReadFile", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
content, ok := fs.ReadFile("/foo.ts")
|
|
assert.Assert(t, ok)
|
|
assert.Equal(t, content, "hello, world")
|
|
|
|
content, ok = fs.ReadFile("/does/not/exist.ts")
|
|
assert.Assert(t, !ok)
|
|
assert.Equal(t, content, "")
|
|
})
|
|
|
|
t.Run("Realpath", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
realpath := fs.Realpath("/foo.ts")
|
|
assert.Equal(t, realpath, "/foo.ts")
|
|
|
|
realpath = fs.Realpath("/Foo.ts")
|
|
assert.Equal(t, realpath, "/foo.ts")
|
|
|
|
realpath = fs.Realpath("/does/not/exist.ts")
|
|
assert.Equal(t, realpath, "/does/not/exist.ts")
|
|
})
|
|
|
|
t.Run("UseCaseSensitiveFileNames", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
assert.Assert(t, !fs.UseCaseSensitiveFileNames())
|
|
})
|
|
}
|
|
|
|
func TestVFSTestMapFSWindows(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
fs := FromMap(map[string]string{
|
|
"c:/foo.ts": "hello, world",
|
|
"c:/dir1/file1.ts": "export const foo = 42;",
|
|
"c:/dir1/file2.ts": "export const foo = 42;",
|
|
"c:/dir2/file1.ts": "export const foo = 42;",
|
|
}, false)
|
|
|
|
t.Run("ReadFile", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
content, ok := fs.ReadFile("c:/foo.ts")
|
|
assert.Assert(t, ok)
|
|
assert.Equal(t, content, "hello, world")
|
|
|
|
content, ok = fs.ReadFile("c:/does/not/exist.ts")
|
|
assert.Assert(t, !ok)
|
|
assert.Equal(t, content, "")
|
|
})
|
|
|
|
t.Run("Realpath", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
realpath := fs.Realpath("c:/foo.ts")
|
|
assert.Equal(t, realpath, "c:/foo.ts")
|
|
|
|
realpath = fs.Realpath("c:/Foo.ts")
|
|
assert.Equal(t, realpath, "c:/foo.ts")
|
|
|
|
realpath = fs.Realpath("c:/does/not/exist.ts")
|
|
assert.Equal(t, realpath, "c:/does/not/exist.ts")
|
|
})
|
|
}
|
|
|
|
func TestBOM(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
const expected = "hello, world"
|
|
|
|
tests := []struct {
|
|
name string
|
|
order binary.ByteOrder
|
|
bom [2]byte
|
|
}{
|
|
{"BigEndian", binary.BigEndian, [2]byte{0xFE, 0xFF}},
|
|
{"LittleEndian", binary.LittleEndian, [2]byte{0xFF, 0xFE}},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
var codePoints []uint16
|
|
|
|
for _, r := range expected {
|
|
codePoints = utf16.AppendRune(codePoints, r)
|
|
}
|
|
|
|
buf := tt.bom[:]
|
|
|
|
for _, r := range codePoints {
|
|
var err error
|
|
buf, err = binary.Append(buf, tt.order, r)
|
|
assert.NilError(t, err)
|
|
}
|
|
|
|
fs := FromMap(map[string][]byte{
|
|
"/foo.ts": buf,
|
|
}, true)
|
|
|
|
content, ok := fs.ReadFile("/foo.ts")
|
|
assert.Assert(t, ok)
|
|
assert.Equal(t, content, expected)
|
|
})
|
|
}
|
|
|
|
t.Run("UTF8", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
fs := FromMap(map[string][]byte{
|
|
"/foo.ts": []byte("\xEF\xBB\xBF" + expected),
|
|
}, true)
|
|
|
|
content, ok := fs.ReadFile("/foo.ts")
|
|
assert.Assert(t, ok)
|
|
assert.Equal(t, content, expected)
|
|
})
|
|
}
|
|
|
|
func TestSymlink(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
fs := FromMap(map[string]any{
|
|
"/foo.ts": "hello, world",
|
|
"/symlink.ts": Symlink("/foo.ts"),
|
|
"/some/dir/file.ts": "hello, world",
|
|
"/some/dirlink": Symlink("/some/dir"),
|
|
"/a": Symlink("/b"),
|
|
"/b": Symlink("/c"),
|
|
"/c": Symlink("/d"),
|
|
"/d/existing.ts": "this is existing.ts",
|
|
}, false)
|
|
|
|
t.Run("ReadFile", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
content, ok := fs.ReadFile("/symlink.ts")
|
|
assert.Assert(t, ok)
|
|
assert.Equal(t, content, "hello, world")
|
|
|
|
content, ok = fs.ReadFile("/some/dirlink/file.ts")
|
|
assert.Assert(t, ok)
|
|
assert.Equal(t, content, "hello, world")
|
|
|
|
content, ok = fs.ReadFile("/a/existing.ts")
|
|
assert.Assert(t, ok)
|
|
assert.Equal(t, content, "this is existing.ts")
|
|
})
|
|
|
|
t.Run("Realpath", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
realpath := fs.Realpath("/symlink.ts")
|
|
assert.Equal(t, realpath, "/foo.ts")
|
|
|
|
realpath = fs.Realpath("/some/dirlink")
|
|
assert.Equal(t, realpath, "/some/dir")
|
|
|
|
realpath = fs.Realpath("/some/dirlink/file.ts")
|
|
assert.Equal(t, realpath, "/some/dir/file.ts")
|
|
})
|
|
|
|
t.Run("FileExists", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
assert.Assert(t, fs.FileExists("/symlink.ts"))
|
|
assert.Assert(t, fs.FileExists("/some/dirlink/file.ts"))
|
|
assert.Assert(t, fs.FileExists("/a/existing.ts"))
|
|
})
|
|
|
|
t.Run("DirectoryExists", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
assert.Assert(t, fs.DirectoryExists("/some/dirlink"))
|
|
assert.Assert(t, fs.DirectoryExists("/d"))
|
|
assert.Assert(t, fs.DirectoryExists("/c"))
|
|
assert.Assert(t, fs.DirectoryExists("/b"))
|
|
assert.Assert(t, fs.DirectoryExists("/a"))
|
|
})
|
|
}
|
|
|
|
func TestWritableFSSymlink(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
fs := FromMap(map[string]any{
|
|
"/some/dir/other.ts": "NOTHING",
|
|
"/other.ts": Symlink("/some/dir/other.ts"),
|
|
"/some/dirlink": Symlink("/some/dir"),
|
|
"/brokenlink": Symlink("/does/not/exist"),
|
|
"/a": Symlink("/b"),
|
|
"/b": Symlink("/c"),
|
|
"/c": Symlink("/d"),
|
|
"/d/existing.ts": "hello, world",
|
|
}, false)
|
|
|
|
err := fs.WriteFile("/some/dirlink/file.ts", "hello, world", false)
|
|
assert.NilError(t, err)
|
|
|
|
content, ok := fs.ReadFile("/some/dirlink/file.ts")
|
|
assert.Assert(t, ok)
|
|
assert.Equal(t, content, "hello, world")
|
|
|
|
content, ok = fs.ReadFile("/some/dir/file.ts")
|
|
assert.Assert(t, ok)
|
|
assert.Equal(t, content, "hello, world")
|
|
|
|
err = fs.WriteFile("/some/dirlink/file.ts", "goodbye, world", false)
|
|
assert.NilError(t, err)
|
|
|
|
content, ok = fs.ReadFile("/some/dirlink/file.ts")
|
|
assert.Assert(t, ok)
|
|
assert.Equal(t, content, "goodbye, world")
|
|
|
|
err = fs.WriteFile("/other.ts", "hello, world", false)
|
|
assert.NilError(t, err)
|
|
|
|
content, ok = fs.ReadFile("/other.ts")
|
|
assert.Assert(t, ok)
|
|
assert.Equal(t, content, "hello, world")
|
|
|
|
content, ok = fs.ReadFile("/some/dir/other.ts")
|
|
assert.Assert(t, ok)
|
|
assert.Equal(t, content, "hello, world")
|
|
|
|
err = fs.WriteFile("/some/dirlink", "hello, world", false)
|
|
assert.Error(t, err, `write "some/dirlink": path exists but is not a regular file`)
|
|
|
|
// Can't write inside a broken dir symlink
|
|
err = fs.WriteFile("/brokenlink/file.ts", "hello, world", false)
|
|
assert.Error(t, err, `broken symlink "brokenlink" -> "does/not/exist"`)
|
|
|
|
err = fs.WriteFile("/brokenlink/also/wrong/file.ts", "hello, world", false)
|
|
assert.Error(t, err, `broken symlink "brokenlink" -> "does/not/exist"`)
|
|
|
|
// But we can write to a broken file symlink
|
|
err = fs.WriteFile("/brokenlink", "hello, world", false)
|
|
assert.NilError(t, err)
|
|
content, ok = fs.ReadFile("/brokenlink")
|
|
assert.Assert(t, ok)
|
|
assert.Equal(t, content, "hello, world")
|
|
content, ok = fs.ReadFile("/does/not/exist")
|
|
assert.Assert(t, ok)
|
|
assert.Equal(t, content, "hello, world")
|
|
}
|
|
|
|
func TestWritableFSSymlinkChain(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
fs := FromMap(map[string]any{
|
|
"/a": Symlink("/b"),
|
|
"/b": Symlink("/c"),
|
|
"/c": Symlink("/d"),
|
|
"/d/existing.ts": "hello, world",
|
|
}, false)
|
|
|
|
err := fs.WriteFile("/a/foo/bar/new.ts", "this is new.ts", false)
|
|
assert.NilError(t, err)
|
|
content, ok := fs.ReadFile("/a/foo/bar/new.ts")
|
|
assert.Assert(t, ok)
|
|
assert.Equal(t, content, "this is new.ts")
|
|
content, ok = fs.ReadFile("/b/foo/bar/new.ts")
|
|
assert.Assert(t, ok)
|
|
assert.Equal(t, content, "this is new.ts")
|
|
content, ok = fs.ReadFile("/d/foo/bar/new.ts")
|
|
assert.Assert(t, ok)
|
|
assert.Equal(t, content, "this is new.ts")
|
|
}
|
|
|
|
func TestWritableFSSymlinkChainNotDir(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
fs := FromMap(map[string]any{
|
|
"/a": Symlink("/b"),
|
|
"/b": Symlink("/c"),
|
|
"/c": Symlink("/d"),
|
|
"/d": "hello, world",
|
|
}, false)
|
|
|
|
err := fs.WriteFile("/a/foo/bar/new.ts", "this is new.ts", false)
|
|
assert.Error(t, err, `mkdir "d": path exists but is not a directory`)
|
|
}
|
|
|
|
func TestWritableFSSymlinkDelete(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
fs := FromMap(map[string]any{
|
|
"/some/dir/other.ts": "NOTHING",
|
|
"/other.ts": Symlink("/some/dir/other.ts"),
|
|
"/some/dirlink": Symlink("/some/dir"),
|
|
"/brokenlink": Symlink("/does/not/exist"),
|
|
"/a": Symlink("/b"),
|
|
"/b": Symlink("/c"),
|
|
"/c": Symlink("/d"),
|
|
"/d/existing.ts": "hello, world",
|
|
}, false)
|
|
|
|
err := fs.Remove("/a")
|
|
assert.NilError(t, err)
|
|
assert.Assert(t, !fs.DirectoryExists("/a"))
|
|
assert.Assert(t, fs.DirectoryExists("/b"))
|
|
assert.Assert(t, fs.DirectoryExists("/c"))
|
|
assert.Assert(t, fs.FileExists("/d/existing.ts"))
|
|
|
|
// symlinks should still exist even if underlying file/dir is deleted
|
|
err = fs.Remove("/d")
|
|
assert.NilError(t, err)
|
|
assert.Assert(t, !fs.DirectoryExists("/b"))
|
|
assert.Assert(t, !fs.DirectoryExists("/c"))
|
|
assert.Assert(t, !fs.DirectoryExists("/d"))
|
|
assert.Assert(t, !fs.FileExists("/d/again.ts"))
|
|
err = fs.WriteFile("/d/again.ts", "d exists again", false)
|
|
assert.NilError(t, err)
|
|
assert.Assert(t, fs.DirectoryExists("/b"))
|
|
assert.Assert(t, fs.DirectoryExists("/c"))
|
|
content, _ := fs.ReadFile("/b/again.ts")
|
|
assert.Equal(t, content, "d exists again")
|
|
|
|
assert.Assert(t, !fs.FileExists("/brokenlink"))
|
|
assert.Assert(t, !fs.DirectoryExists("/brokenlink"))
|
|
err = fs.Remove("/does/not/exist") // should do nothing
|
|
assert.NilError(t, err)
|
|
assert.Assert(t, !fs.FileExists("/brokenlink"))
|
|
assert.Assert(t, !fs.DirectoryExists("/brokenlink"))
|
|
err = fs.WriteFile("/does/not/exist", "hello, world", false)
|
|
assert.NilError(t, err)
|
|
assert.Assert(t, fs.FileExists("/brokenlink"))
|
|
}
|