Files
trivy/pkg/mapfs/fs_test.go
Matthieu MOREL a19e0aa1ba fix: octalLiteral from go-critic (#8811)
Signed-off-by: Matthieu MOREL <matthieu.morel35@gmail.com>
2025-05-05 13:49:07 +00:00

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())
}