mirror of
https://github.com/aquasecurity/trivy.git
synced 2025-12-12 07:40:48 -08:00
504 lines
10 KiB
Go
504 lines
10 KiB
Go
package mapfs_test
|
|
|
|
import (
|
|
"io"
|
|
"io/fs"
|
|
"runtime"
|
|
"testing"
|
|
|
|
"github.com/samber/lo"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/aquasecurity/trivy/pkg/mapfs"
|
|
)
|
|
|
|
type fileInfo struct {
|
|
name string
|
|
fileMode fs.FileMode
|
|
isDir bool
|
|
size int64
|
|
}
|
|
|
|
var (
|
|
filePerm = lo.Ternary(runtime.GOOS == "windows", fs.FileMode(0o666), fs.FileMode(0o644))
|
|
helloFileInfo = fileInfo{
|
|
name: "hello.txt",
|
|
fileMode: filePerm,
|
|
isDir: false,
|
|
size: 11,
|
|
}
|
|
btxtFileInfo = fileInfo{
|
|
name: "b.txt",
|
|
fileMode: filePerm,
|
|
isDir: false,
|
|
size: 3,
|
|
}
|
|
virtualFileInfo = fileInfo{
|
|
name: "virtual.txt",
|
|
fileMode: 0o600,
|
|
isDir: false,
|
|
size: 7,
|
|
}
|
|
cdirFileInfo = fileInfo{
|
|
name: "c",
|
|
fileMode: fs.FileMode(0o700) | fs.ModeDir,
|
|
isDir: true,
|
|
size: 256,
|
|
}
|
|
)
|
|
|
|
func initFS(t *testing.T) *mapfs.FS {
|
|
fsys := mapfs.New()
|
|
require.NoError(t, fsys.MkdirAll("a/b/c", 0o700))
|
|
require.NoError(t, fsys.MkdirAll("a/b/empty", 0o700))
|
|
require.NoError(t, fsys.WriteFile("hello.txt", "testdata/hello.txt"))
|
|
require.NoError(t, fsys.WriteFile("a/b/b.txt", "testdata/b.txt"))
|
|
require.NoError(t, fsys.WriteFile("a/b/c/c.txt", "testdata/c.txt"))
|
|
require.NoError(t, fsys.WriteFile("a/b/c/.dotfile", "testdata/dotfile"))
|
|
require.NoError(t, fsys.WriteVirtualFile("a/b/c/virtual.txt", []byte("virtual"), 0o600))
|
|
return fsys
|
|
}
|
|
|
|
func assertFileInfo(t *testing.T, want fileInfo, got fs.FileInfo) {
|
|
if got == nil {
|
|
return
|
|
}
|
|
assert.Equal(t, want.name, got.Name())
|
|
assert.Equal(t, want.fileMode, got.Mode())
|
|
assert.Equal(t, want.isDir, got.Mode().IsDir())
|
|
assert.Equal(t, want.isDir, got.IsDir())
|
|
assert.Equal(t, want.size, got.Size())
|
|
}
|
|
|
|
func TestFS_Filter(t *testing.T) {
|
|
fsys := initFS(t)
|
|
t.Run("empty files", func(t *testing.T) {
|
|
newFS, err := fsys.Filter(nil)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, fsys, newFS)
|
|
})
|
|
t.Run("happy", func(t *testing.T) {
|
|
newFS, err := fsys.Filter([]string{
|
|
"hello.txt",
|
|
"a/b/c/.dotfile",
|
|
})
|
|
require.NoError(t, err)
|
|
_, err = newFS.Stat("hello.txt")
|
|
require.ErrorIs(t, err, fs.ErrNotExist)
|
|
_, err = newFS.Stat("a/b/c/.dotfile")
|
|
require.ErrorIs(t, err, fs.ErrNotExist)
|
|
fi, err := newFS.Stat("a/b/c/c.txt")
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "c.txt", fi.Name())
|
|
})
|
|
}
|
|
|
|
func TestFS_Stat(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
filePath string
|
|
want fileInfo
|
|
wantErr assert.ErrorAssertionFunc
|
|
}{
|
|
{
|
|
name: "ordinary file",
|
|
filePath: "hello.txt",
|
|
want: helloFileInfo,
|
|
wantErr: assert.NoError,
|
|
},
|
|
{
|
|
name: "nested file",
|
|
filePath: "a/b/b.txt",
|
|
want: btxtFileInfo,
|
|
wantErr: assert.NoError,
|
|
},
|
|
{
|
|
name: "virtual file",
|
|
filePath: "a/b/c/virtual.txt",
|
|
want: virtualFileInfo,
|
|
wantErr: assert.NoError,
|
|
},
|
|
{
|
|
name: "dir",
|
|
filePath: "a/b/c",
|
|
want: cdirFileInfo,
|
|
wantErr: assert.NoError,
|
|
},
|
|
{
|
|
name: "no such file",
|
|
filePath: "nosuch.txt",
|
|
wantErr: assert.Error,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
fsys := initFS(t)
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got, err := fsys.Stat(tt.filePath)
|
|
tt.wantErr(t, err)
|
|
assertFileInfo(t, tt.want, got)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestFS_ReadDir(t *testing.T) {
|
|
type dirEntry struct {
|
|
name string
|
|
fileMode fs.FileMode
|
|
isDir bool
|
|
size int64
|
|
fileInfo fileInfo
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
filePath string
|
|
want []dirEntry
|
|
wantErr assert.ErrorAssertionFunc
|
|
}{
|
|
{
|
|
name: "at root",
|
|
filePath: ".",
|
|
want: []dirEntry{
|
|
{
|
|
name: "a",
|
|
fileMode: fs.FileMode(0o700) | fs.ModeDir,
|
|
isDir: true,
|
|
size: 0x100,
|
|
fileInfo: fileInfo{
|
|
name: "a",
|
|
fileMode: fs.FileMode(0o700) | fs.ModeDir,
|
|
isDir: true,
|
|
size: 0x100,
|
|
},
|
|
},
|
|
{
|
|
name: "hello.txt",
|
|
fileMode: filePerm,
|
|
isDir: false,
|
|
size: 11,
|
|
fileInfo: helloFileInfo,
|
|
},
|
|
},
|
|
wantErr: assert.NoError,
|
|
},
|
|
{
|
|
name: "multiple files",
|
|
filePath: "a/b/c",
|
|
want: []dirEntry{
|
|
{
|
|
name: ".dotfile",
|
|
fileMode: filePerm,
|
|
isDir: false,
|
|
size: 7,
|
|
fileInfo: fileInfo{
|
|
name: ".dotfile",
|
|
fileMode: filePerm,
|
|
isDir: false,
|
|
size: 7,
|
|
},
|
|
},
|
|
{
|
|
name: "c.txt",
|
|
fileMode: filePerm,
|
|
isDir: false,
|
|
size: 0,
|
|
fileInfo: fileInfo{
|
|
name: "c.txt",
|
|
fileMode: filePerm,
|
|
isDir: false,
|
|
size: 0,
|
|
},
|
|
},
|
|
{
|
|
name: "virtual.txt",
|
|
fileMode: 0o600,
|
|
isDir: false,
|
|
size: 0,
|
|
fileInfo: virtualFileInfo,
|
|
},
|
|
},
|
|
wantErr: assert.NoError,
|
|
},
|
|
{
|
|
name: "no such dir",
|
|
filePath: "nosuch/",
|
|
wantErr: assert.Error,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
fsys := initFS(t)
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
entries, err := fsys.ReadDir(tt.filePath)
|
|
tt.wantErr(t, err)
|
|
|
|
for _, z := range lo.Zip2(entries, tt.want) {
|
|
got, want := z.A, z.B
|
|
assert.Equal(t, want.name, got.Name())
|
|
assert.Equal(t, want.fileMode, got.Type(), want.name)
|
|
assert.Equal(t, want.isDir, got.IsDir(), want.name)
|
|
|
|
fi, err := got.Info()
|
|
require.NoError(t, err)
|
|
assertFileInfo(t, want.fileInfo, fi)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestFS_Open(t *testing.T) {
|
|
type file struct {
|
|
fileInfo fileInfo
|
|
body string
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
filePath string
|
|
want file
|
|
wantErr assert.ErrorAssertionFunc
|
|
}{
|
|
{
|
|
name: "ordinary file",
|
|
filePath: "hello.txt",
|
|
want: file{
|
|
fileInfo: helloFileInfo,
|
|
body: "hello world",
|
|
},
|
|
wantErr: assert.NoError,
|
|
},
|
|
{
|
|
name: "virtual file",
|
|
filePath: "a/b/c/virtual.txt",
|
|
want: file{
|
|
fileInfo: virtualFileInfo,
|
|
body: "virtual",
|
|
},
|
|
wantErr: assert.NoError,
|
|
},
|
|
{
|
|
name: "dir",
|
|
filePath: "a/b/c",
|
|
want: file{
|
|
fileInfo: cdirFileInfo,
|
|
},
|
|
wantErr: assert.NoError,
|
|
},
|
|
{
|
|
name: "no such file",
|
|
filePath: "nosuch.txt",
|
|
wantErr: assert.Error,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
fsys := initFS(t)
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
f, err := fsys.Open(tt.filePath)
|
|
tt.wantErr(t, err)
|
|
if f == nil {
|
|
return
|
|
}
|
|
defer func() {
|
|
require.NoError(t, f.Close())
|
|
}()
|
|
|
|
fi, err := f.Stat()
|
|
require.NoError(t, err)
|
|
assertFileInfo(t, tt.want.fileInfo, fi)
|
|
|
|
if tt.want.body != "" {
|
|
b, err := io.ReadAll(f)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, tt.want.body, string(b))
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestFS_ReadFile(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
filePath string
|
|
want string
|
|
wantErr assert.ErrorAssertionFunc
|
|
}{
|
|
{
|
|
name: "ordinary file",
|
|
filePath: "hello.txt",
|
|
want: "hello world",
|
|
wantErr: assert.NoError,
|
|
},
|
|
{
|
|
name: "virtual file",
|
|
filePath: "a/b/c/virtual.txt",
|
|
want: "virtual",
|
|
wantErr: assert.NoError,
|
|
},
|
|
{
|
|
name: "no such file",
|
|
filePath: "nosuch.txt",
|
|
wantErr: assert.Error,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
fsys := initFS(t)
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
b, err := fsys.ReadFile(tt.filePath)
|
|
tt.wantErr(t, err)
|
|
assert.Equal(t, tt.want, string(b))
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestFS_Sub(t *testing.T) {
|
|
fsys := initFS(t)
|
|
sub, err := fsys.Sub("a/b")
|
|
require.NoError(t, err)
|
|
|
|
data, err := sub.(fs.ReadFileFS).ReadFile("c/.dotfile")
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "dotfile", string(data))
|
|
}
|
|
|
|
func TestFS_Glob(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
pattern string
|
|
want []string
|
|
wantErr assert.ErrorAssertionFunc
|
|
}{
|
|
{
|
|
name: "root",
|
|
pattern: "*",
|
|
want: []string{
|
|
"a",
|
|
"hello.txt",
|
|
},
|
|
wantErr: assert.NoError,
|
|
},
|
|
{
|
|
name: "pattern",
|
|
pattern: "*/b/c/*.txt",
|
|
want: []string{
|
|
"a/b/c/c.txt",
|
|
"a/b/c/virtual.txt",
|
|
},
|
|
wantErr: assert.NoError,
|
|
},
|
|
{
|
|
name: "no such",
|
|
pattern: "nosuch",
|
|
wantErr: assert.NoError,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
fsys := initFS(t)
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
results, err := fsys.Glob(tt.pattern)
|
|
tt.wantErr(t, err)
|
|
assert.Equal(t, tt.want, results)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestFS_Remove(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
path string
|
|
wantErr assert.ErrorAssertionFunc
|
|
}{
|
|
{
|
|
name: "ordinary file",
|
|
path: "hello.txt",
|
|
wantErr: assert.NoError,
|
|
},
|
|
{
|
|
name: "nested file",
|
|
path: "a/b/b.txt",
|
|
wantErr: assert.NoError,
|
|
},
|
|
{
|
|
name: "virtual file",
|
|
path: "a/b/c/virtual.txt",
|
|
wantErr: assert.NoError,
|
|
},
|
|
{
|
|
name: "empty dir",
|
|
path: "a/b/empty",
|
|
wantErr: assert.NoError,
|
|
},
|
|
{
|
|
name: "empty path",
|
|
path: "",
|
|
wantErr: assert.NoError,
|
|
},
|
|
{
|
|
name: "non-empty dir",
|
|
path: "a/b/c",
|
|
wantErr: assert.Error,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
fsys := initFS(t)
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
err := fsys.Remove(tt.path)
|
|
tt.wantErr(t, err)
|
|
if err != nil || tt.path == "" {
|
|
return
|
|
}
|
|
|
|
_, err = fsys.Stat(tt.path)
|
|
require.ErrorIs(t, err, fs.ErrNotExist)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestFS_RemoveAll(t *testing.T) {
|
|
fsys := initFS(t)
|
|
t.Run("ordinary file", func(t *testing.T) {
|
|
err := fsys.RemoveAll("hello.txt")
|
|
require.NoError(t, err)
|
|
_, err = fsys.Stat("hello.txt")
|
|
require.ErrorIs(t, err, fs.ErrNotExist)
|
|
})
|
|
t.Run("non-empty dir", func(t *testing.T) {
|
|
err := fsys.RemoveAll("a/b")
|
|
require.NoError(t, err)
|
|
_, err = fsys.Stat("a/b/c/c.txt")
|
|
require.ErrorIs(t, err, fs.ErrNotExist)
|
|
_, err = fsys.Stat("a/b/c/.dotfile")
|
|
require.ErrorIs(t, err, fs.ErrNotExist)
|
|
_, err = fsys.Stat("a/b/c/virtual.txt")
|
|
require.ErrorIs(t, err, fs.ErrNotExist)
|
|
})
|
|
}
|
|
|
|
func TestFS_WithUnderlyingRoot(t *testing.T) {
|
|
root := "testdata/subdir"
|
|
fsys := mapfs.New(mapfs.WithUnderlyingRoot(root))
|
|
require.NoError(t, fsys.WriteFile("foo.txt", root+"/foo.txt"))
|
|
require.NoError(t, fsys.WriteFile("..foo.txt", root+"/..foo.txt"))
|
|
|
|
fi, err := fsys.Stat("..")
|
|
require.NoError(t, err)
|
|
assert.True(t, fi.IsDir())
|
|
|
|
fi, err = fsys.Stat("../hello.txt")
|
|
require.NoError(t, err)
|
|
assert.False(t, fi.IsDir())
|
|
|
|
fi, err = fsys.Stat("foo.txt")
|
|
require.NoError(t, err)
|
|
assert.False(t, fi.IsDir())
|
|
|
|
fi, err = fsys.Stat("..foo.txt")
|
|
require.NoError(t, err)
|
|
assert.False(t, fi.IsDir())
|
|
}
|