mirror of
https://github.com/jimeh/go-golden.git
synced 2026-02-19 11:16:47 +00:00
wip: some more drastic refactoring
This commit is contained in:
275
test/testfs/testfs.go
Normal file
275
test/testfs/testfs.go
Normal file
@@ -0,0 +1,275 @@
|
||||
package testfs
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Node struct {
|
||||
data []byte
|
||||
perm os.FileMode
|
||||
isDir bool
|
||||
}
|
||||
|
||||
type FS struct {
|
||||
Pwd string
|
||||
Nodes map[string]*Node
|
||||
}
|
||||
|
||||
func New() *FS {
|
||||
return &FS{
|
||||
Pwd: "/root",
|
||||
Nodes: map[string]*Node{
|
||||
"/": {perm: 0o755, isDir: true},
|
||||
"/root": {perm: 0o700, isDir: true},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (fs *FS) MkdirAll(name string, perm os.FileMode) error {
|
||||
if !path.IsAbs(name) && name != "" {
|
||||
name = path.Join(fs.Pwd, name)
|
||||
}
|
||||
|
||||
dirs := []string{name}
|
||||
for d := path.Dir(name); d != "/"; d = path.Dir(d) {
|
||||
dirs = append(dirs, d)
|
||||
}
|
||||
dirs = append(dirs, "/")
|
||||
|
||||
for i := len(dirs) - 1; i >= 0; i-- {
|
||||
dir := dirs[i]
|
||||
parent := path.Dir(dir)
|
||||
|
||||
if info, ok := fs.Nodes[dir]; ok {
|
||||
if !info.isDir {
|
||||
return &os.PathError{
|
||||
Op: "mkdir",
|
||||
Path: dir,
|
||||
Err: errors.New("not a directory"),
|
||||
}
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
parentInfo, ok := fs.Nodes[parent]
|
||||
if !ok {
|
||||
return &os.PathError{
|
||||
Op: "mkdir",
|
||||
Path: parent,
|
||||
Err: errors.New("no such file or directory"),
|
||||
}
|
||||
}
|
||||
if !parentInfo.isDir {
|
||||
return &os.PathError{
|
||||
Op: "mkdir",
|
||||
Path: parent,
|
||||
Err: errors.New("not a directory"),
|
||||
}
|
||||
}
|
||||
// Ensure all parent directories have execute permissions, and direct
|
||||
// parent also has write permission.
|
||||
if parentInfo.perm&0o100 == 0 || i == 1 && parentInfo.perm&0o200 == 0 {
|
||||
return &os.PathError{
|
||||
Op: "mkdir",
|
||||
Path: dir,
|
||||
Err: errors.New("permission denied"),
|
||||
}
|
||||
}
|
||||
|
||||
fs.Nodes[dir] = &Node{perm: perm, isDir: true}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (fs *FS) ReadFile(name string) ([]byte, error) {
|
||||
if !path.IsAbs(name) && name != "" {
|
||||
name = path.Join(fs.Pwd, name)
|
||||
}
|
||||
|
||||
_, err := fs.checkParents(name, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info, ok := fs.Nodes[name]
|
||||
if !ok {
|
||||
return nil, &os.PathError{
|
||||
Op: "open",
|
||||
Path: name,
|
||||
Err: errors.New("no such file or directory"),
|
||||
}
|
||||
}
|
||||
if info.isDir {
|
||||
return nil, &os.PathError{
|
||||
Op: "open",
|
||||
Path: name,
|
||||
Err: errors.New("is a directory"),
|
||||
}
|
||||
}
|
||||
if info.perm&0o400 == 0 {
|
||||
return nil, &os.PathError{
|
||||
Op: "open",
|
||||
Path: name,
|
||||
Err: errors.New("permission denied"),
|
||||
}
|
||||
}
|
||||
|
||||
return info.data, nil
|
||||
}
|
||||
|
||||
func (fs *FS) WriteFile(name string, data []byte, perm os.FileMode) error {
|
||||
if !path.IsAbs(name) && name != "" {
|
||||
name = path.Join(fs.Pwd, name)
|
||||
}
|
||||
|
||||
parent, err := fs.checkParents(name, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
info, ok := fs.Nodes[name]
|
||||
if ok {
|
||||
if info.isDir {
|
||||
return &os.PathError{
|
||||
Op: "open",
|
||||
Path: name,
|
||||
Err: errors.New("is a directory"),
|
||||
}
|
||||
}
|
||||
}
|
||||
// Return error if file exists and has no write permission, or if the file
|
||||
// does not exist and the direct parent has no write permission.
|
||||
if ok && info.perm&0o200 == 0 || !ok && parent.perm&0o200 == 0 {
|
||||
return &os.PathError{
|
||||
Op: "open",
|
||||
Path: name,
|
||||
Err: errors.New("permission denied"),
|
||||
}
|
||||
}
|
||||
|
||||
fs.Nodes[name] = &Node{data: data, perm: perm}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (fs *FS) Remove(name string) error {
|
||||
if !path.IsAbs(name) && name != "" {
|
||||
name = path.Join(fs.Pwd, name)
|
||||
}
|
||||
|
||||
parent, err := fs.checkParents(name, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if parent != nil && parent.perm&0o200 == 0 {
|
||||
return &os.PathError{
|
||||
Op: "remove",
|
||||
Path: name,
|
||||
Err: errors.New("permission denied"),
|
||||
}
|
||||
}
|
||||
|
||||
info, ok := fs.Nodes[name]
|
||||
if !ok {
|
||||
return &os.PathError{
|
||||
Op: "remove",
|
||||
Path: name,
|
||||
Err: errors.New("no such file or directory"),
|
||||
}
|
||||
}
|
||||
if info.perm&0o200 == 0 {
|
||||
return &os.PathError{
|
||||
Op: "remove",
|
||||
Path: name,
|
||||
Err: errors.New("permission denied"),
|
||||
}
|
||||
}
|
||||
if info.isDir {
|
||||
for p := range fs.Nodes {
|
||||
if strings.HasPrefix(p, name) && p != name {
|
||||
return &os.PathError{
|
||||
Op: "remove",
|
||||
Path: name,
|
||||
Err: errors.New("directory not empty"),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delete(fs.Nodes, name)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (fs *FS) Exists(name string) bool {
|
||||
if !path.IsAbs(name) && name != "" {
|
||||
name = path.Join(fs.Pwd, name)
|
||||
}
|
||||
|
||||
_, ok := fs.Nodes[name]
|
||||
|
||||
return ok
|
||||
}
|
||||
|
||||
func (fs *FS) FileMode(name string) (os.FileMode, error) {
|
||||
if !path.IsAbs(name) && name != "" {
|
||||
name = path.Join(fs.Pwd, name)
|
||||
}
|
||||
|
||||
if info, ok := fs.Nodes[name]; ok {
|
||||
return info.perm, nil
|
||||
}
|
||||
|
||||
return 0, &os.PathError{
|
||||
Op: "open",
|
||||
Path: name,
|
||||
Err: os.ErrNotExist,
|
||||
}
|
||||
}
|
||||
|
||||
func (fs *FS) checkParents(absPath string, noExistError bool) (*Node, error) {
|
||||
var parents []string
|
||||
for d := path.Dir(absPath); d != "/"; d = path.Dir(d) {
|
||||
parents = append(parents, d)
|
||||
}
|
||||
parents = append(parents, "/")
|
||||
var directParent *Node
|
||||
|
||||
for i := 0; i < len(parents); i++ {
|
||||
dir := parents[i]
|
||||
info, ok := fs.Nodes[dir]
|
||||
if !ok && noExistError {
|
||||
return nil, &os.PathError{
|
||||
Op: "open",
|
||||
Path: dir,
|
||||
Err: errors.New("no such file or directory"),
|
||||
}
|
||||
}
|
||||
if info != nil && !info.isDir {
|
||||
return nil, &os.PathError{
|
||||
Op: "open",
|
||||
Path: dir,
|
||||
Err: errors.New("not a directory"),
|
||||
}
|
||||
}
|
||||
// Ensure all parent directories have execute permissions.
|
||||
if info != nil && info.perm&0o100 == 0 {
|
||||
return nil, &os.PathError{
|
||||
Op: "open",
|
||||
Path: dir,
|
||||
Err: errors.New("permission denied"),
|
||||
}
|
||||
}
|
||||
if i == 0 {
|
||||
directParent = info
|
||||
}
|
||||
}
|
||||
|
||||
return directParent, nil
|
||||
}
|
||||
670
test/testfs/testfs_test.go
Normal file
670
test/testfs/testfs_test.go
Normal file
@@ -0,0 +1,670 @@
|
||||
package testfs
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestFSMkdirAll(t *testing.T) {
|
||||
type args struct {
|
||||
path string
|
||||
perm os.FileMode
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
nodes map[string]*Node
|
||||
want map[string]*Node
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "create relative new dir",
|
||||
args: args{path: "newdir", perm: 0o755},
|
||||
want: map[string]*Node{
|
||||
"/root/newdir": {perm: 0o755, isDir: true},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "create absolute new dir",
|
||||
args: args{path: "/opt/newdir", perm: 0o755},
|
||||
want: map[string]*Node{
|
||||
"/opt": {perm: 0o755, isDir: true},
|
||||
"/opt/newdir": {perm: 0o755, isDir: true},
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
name: "create relative nested dirs",
|
||||
args: args{path: "nested/dir/structure", perm: 0o755},
|
||||
want: map[string]*Node{
|
||||
"/root/nested": {perm: 0o755, isDir: true},
|
||||
"/root/nested/dir": {perm: 0o755, isDir: true},
|
||||
"/root/nested/dir/structure": {perm: 0o755, isDir: true},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "create absolute nested dirs",
|
||||
args: args{path: "/opt/nested/dir/structure", perm: 0o755},
|
||||
want: map[string]*Node{
|
||||
"/opt": {perm: 0o755, isDir: true},
|
||||
"/opt/nested": {perm: 0o755, isDir: true},
|
||||
"/opt/nested/dir": {perm: 0o755, isDir: true},
|
||||
"/opt/nested/dir/structure": {perm: 0o755, isDir: true},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "create relative nested dirs with other perms",
|
||||
args: args{path: "nested/dir/structure", perm: 0o750},
|
||||
want: map[string]*Node{
|
||||
"/root/nested": {perm: 0o750, isDir: true},
|
||||
"/root/nested/dir": {perm: 0o750, isDir: true},
|
||||
"/root/nested/dir/structure": {perm: 0o750, isDir: true},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "create absolute nested dirs with other perms",
|
||||
args: args{path: "/opt/nested/dir/structure", perm: 0o750},
|
||||
want: map[string]*Node{
|
||||
"/opt": {perm: 0o750, isDir: true},
|
||||
"/opt/nested": {perm: 0o750, isDir: true},
|
||||
"/opt/nested/dir": {perm: 0o750, isDir: true},
|
||||
"/opt/nested/dir/structure": {perm: 0o750, isDir: true},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "create relative nested dirs with existing dirs",
|
||||
args: args{path: "nested/dir/structure", perm: 0o755},
|
||||
want: map[string]*Node{
|
||||
"/root/nested": {perm: 0o755, isDir: true},
|
||||
"/root/nested/dir": {perm: 0o755, isDir: true},
|
||||
"/root/nested/dir/structure": {perm: 0o755, isDir: true},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "create absolute nested dirs with existing dirs",
|
||||
args: args{path: "/root/nested/dir/structure", perm: 0o755},
|
||||
want: map[string]*Node{
|
||||
"/root/nested": {perm: 0o755, isDir: true},
|
||||
"/root/nested/dir": {perm: 0o755, isDir: true},
|
||||
"/root/nested/dir/structure": {perm: 0o755, isDir: true},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "create relative under file",
|
||||
args: args{path: "file/newdir", perm: 0o755},
|
||||
nodes: map[string]*Node{
|
||||
"/root/file": {perm: 0o644},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "create absolute under file",
|
||||
args: args{path: "/root/file/newdir", perm: 0o755},
|
||||
nodes: map[string]*Node{
|
||||
"/root/file": {perm: 0o644},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "create relative directory without execute permission",
|
||||
args: args{path: "dir/newdir", perm: 0o755},
|
||||
nodes: map[string]*Node{
|
||||
"/root": {perm: 0o644},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "create absolute directory without execute permission",
|
||||
args: args{path: "/root/dir/newdir", perm: 0o755},
|
||||
nodes: map[string]*Node{
|
||||
"/root": {perm: 0o644},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "create relative directory without write permission",
|
||||
args: args{path: "dir/newdir", perm: 0o755},
|
||||
nodes: map[string]*Node{
|
||||
"/root": {perm: 0o444},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "create absolute directory without write permission",
|
||||
args: args{path: "/root/dir/newdir", perm: 0o755},
|
||||
nodes: map[string]*Node{
|
||||
"/root": {perm: 0o444},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
fs := &FS{
|
||||
Pwd: "/root",
|
||||
Nodes: map[string]*Node{
|
||||
"/": {perm: 0o755, isDir: true},
|
||||
"/root": {perm: 0o700, isDir: true},
|
||||
},
|
||||
}
|
||||
|
||||
for fp, info := range tt.nodes {
|
||||
fs.Nodes[fp] = info
|
||||
}
|
||||
|
||||
err := fs.MkdirAll(tt.args.path, tt.args.perm)
|
||||
|
||||
if tt.wantErr {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
|
||||
for fp, info := range tt.want {
|
||||
got := fs.Nodes[fp]
|
||||
assert.Equal(t, info, got, "path: %s", fp)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFSReadFile(t *testing.T) {
|
||||
type args struct {
|
||||
name string
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
nodes map[string]*Node
|
||||
want []byte
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "relative read existing file",
|
||||
args: args{name: "file.txt"},
|
||||
nodes: map[string]*Node{
|
||||
"/": {perm: 0o755, isDir: true},
|
||||
"/root": {perm: 0o755, isDir: true},
|
||||
"/root/file.txt": {data: []byte("file content"), perm: 0o644},
|
||||
},
|
||||
want: []byte("file content"),
|
||||
},
|
||||
{
|
||||
name: "absolute read existing file",
|
||||
args: args{name: "/opt/file.txt"},
|
||||
nodes: map[string]*Node{
|
||||
"/": {perm: 0o755, isDir: true},
|
||||
"/opt": {perm: 0o755, isDir: true},
|
||||
"/opt/file.txt": {data: []byte("file content"), perm: 0o644},
|
||||
},
|
||||
want: []byte("file content"),
|
||||
},
|
||||
{
|
||||
name: "relative file does not exist",
|
||||
args: args{name: "nonexistent.txt"},
|
||||
nodes: map[string]*Node{
|
||||
"/": {perm: 0o755, isDir: true},
|
||||
"/root": {perm: 0o755, isDir: true},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "absolute file does not exist",
|
||||
args: args{name: "/opt/nonexistent.txt"},
|
||||
nodes: map[string]*Node{
|
||||
"/": {perm: 0o755, isDir: true},
|
||||
"/opt": {perm: 0o755, isDir: true},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "relative file is a directory",
|
||||
args: args{name: "dir"},
|
||||
nodes: map[string]*Node{
|
||||
"/": {perm: 0o755, isDir: true},
|
||||
"/root": {perm: 0o755, isDir: true},
|
||||
"/root/dir": {perm: 0o755, isDir: true},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "absolute file is a directory",
|
||||
args: args{name: "/opt/dir"},
|
||||
nodes: map[string]*Node{
|
||||
"/": {perm: 0o755, isDir: true},
|
||||
"/opt": {perm: 0o755, isDir: true},
|
||||
"/opt/dir": {perm: 0o755, isDir: true},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "relative file permission denied",
|
||||
args: args{name: "file.txt"},
|
||||
nodes: map[string]*Node{
|
||||
"/": {perm: 0o755, isDir: true},
|
||||
"/root": {perm: 0o755, isDir: true},
|
||||
"/root/file.txt": {data: []byte("file content"), perm: 0o200},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "relative no directory read permission",
|
||||
args: args{name: "file.txt"},
|
||||
nodes: map[string]*Node{
|
||||
"/": {perm: 0o755, isDir: true},
|
||||
"/root": {perm: 0o355, isDir: true},
|
||||
"/root/file.txt": {data: []byte("file content"), perm: 0o644},
|
||||
},
|
||||
want: []byte("file content"),
|
||||
},
|
||||
{
|
||||
name: "relative no directory execute permission",
|
||||
args: args{name: "file.txt"},
|
||||
nodes: map[string]*Node{
|
||||
"/": {perm: 0o755, isDir: true},
|
||||
"/root": {perm: 0o655, isDir: true},
|
||||
"/root/file.txt": {data: []byte("file content"), perm: 0o200},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "relative no grandparent directory execute permission",
|
||||
args: args{name: "foo/file.txt"},
|
||||
nodes: map[string]*Node{
|
||||
"/": {perm: 0o755, isDir: true},
|
||||
"/root": {perm: 0o655, isDir: true},
|
||||
"/root/foo": {perm: 0o755, isDir: true},
|
||||
"/root/foo/file.txt": {data: []byte("hello"), perm: 0o200},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
fs := &FS{
|
||||
Pwd: "/root",
|
||||
Nodes: tt.nodes,
|
||||
}
|
||||
|
||||
got, err := fs.ReadFile(tt.args.name)
|
||||
|
||||
if tt.wantErr {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, tt.want, got)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFSWriteFile(t *testing.T) {
|
||||
type args struct {
|
||||
name string
|
||||
data []byte
|
||||
perm os.FileMode
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
nodes map[string]*Node
|
||||
wantPath string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "relative write to new file",
|
||||
args: args{
|
||||
name: "newfile.txt",
|
||||
data: []byte("new content"),
|
||||
perm: 0o644,
|
||||
},
|
||||
wantPath: "/tmp/newfile.txt",
|
||||
nodes: map[string]*Node{
|
||||
"/": {perm: 0o755, isDir: true},
|
||||
"/tmp": {perm: 0o755, isDir: true},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "absolute write to new file",
|
||||
args: args{
|
||||
name: "/opt/newfile.txt",
|
||||
data: []byte("new content"),
|
||||
perm: 0o644,
|
||||
},
|
||||
wantPath: "/opt/newfile.txt",
|
||||
nodes: map[string]*Node{
|
||||
"/": {perm: 0o755, isDir: true},
|
||||
"/opt": {perm: 0o755, isDir: true},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "relative overwrite existing file",
|
||||
args: args{
|
||||
name: "existing.txt",
|
||||
data: []byte("overwritten"),
|
||||
perm: 0o644,
|
||||
},
|
||||
wantPath: "/tmp/existing.txt",
|
||||
nodes: map[string]*Node{
|
||||
"/": {perm: 0o755, isDir: true},
|
||||
"/tmp": {perm: 0o755, isDir: true},
|
||||
"/tmp/existing": {data: []byte("existing"), perm: 0o644},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "absolute overwrite existing file",
|
||||
args: args{
|
||||
name: "/opt/existing.txt",
|
||||
data: []byte("overwritten"),
|
||||
perm: 0o644,
|
||||
},
|
||||
wantPath: "/opt/existing.txt",
|
||||
nodes: map[string]*Node{
|
||||
"/": {perm: 0o755, isDir: true},
|
||||
"/opt": {perm: 0o755, isDir: true},
|
||||
"/opt/existing": {data: []byte("existing"), perm: 0o644},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "relative overwrite file permissions denied",
|
||||
args: args{
|
||||
name: "existing.txt",
|
||||
data: []byte("overwritten"),
|
||||
perm: 0o644,
|
||||
},
|
||||
wantPath: "/tmp/existing.txt",
|
||||
nodes: map[string]*Node{
|
||||
"/": {perm: 0o755, isDir: true},
|
||||
"/tmp": {perm: 0o755, isDir: true},
|
||||
"/tmp/existing.txt": {data: []byte("existing"), perm: 0o400},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "absolute overwrite file permissions denied",
|
||||
args: args{
|
||||
name: "/opt/existing.txt",
|
||||
data: []byte("overwritten"),
|
||||
perm: 0o644,
|
||||
},
|
||||
wantPath: "/opt/existing.txt",
|
||||
nodes: map[string]*Node{
|
||||
"/": {perm: 0o755, isDir: true},
|
||||
"/opt": {perm: 0o755, isDir: true},
|
||||
"/opt/existing.txt": {data: []byte("existing"), perm: 0o400},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "relative overwrite directory",
|
||||
args: args{
|
||||
name: "dir",
|
||||
data: []byte("overwritten"),
|
||||
perm: 0o644,
|
||||
},
|
||||
wantPath: "/tmp/dir",
|
||||
nodes: map[string]*Node{
|
||||
"/": {perm: 0o755, isDir: true},
|
||||
"/tmp": {perm: 0o755, isDir: true},
|
||||
"/tmp/dir": {perm: 0o644, isDir: true},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "absolute overwrite directory",
|
||||
args: args{
|
||||
name: "/opt/dir",
|
||||
data: []byte("overwritten"),
|
||||
perm: 0o644,
|
||||
},
|
||||
wantPath: "/opt/dir",
|
||||
nodes: map[string]*Node{
|
||||
"/": {perm: 0o755, isDir: true},
|
||||
"/opt": {perm: 0o755, isDir: true},
|
||||
"/opt/dir": {perm: 0o644, isDir: true},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "relative write to non-existent directory",
|
||||
args: args{
|
||||
name: "nonexistentdir/newfile.txt",
|
||||
data: []byte("this will fail"),
|
||||
perm: 0o644,
|
||||
},
|
||||
wantPath: "/tmp/nonexistentdir/newfile.txt",
|
||||
nodes: map[string]*Node{
|
||||
"/": {perm: 0o755, isDir: true},
|
||||
"/tmp": {perm: 0o755, isDir: true},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "absolute write to non-existent directory",
|
||||
args: args{
|
||||
name: "/opt/nonexistentdir/newfile.txt",
|
||||
data: []byte("this will fail"),
|
||||
perm: 0o644,
|
||||
},
|
||||
wantPath: "/opt/nonexistentdir/newfile.txt",
|
||||
nodes: map[string]*Node{
|
||||
"/": {perm: 0o755, isDir: true},
|
||||
"/opt": {perm: 0o755, isDir: true},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "relative write parent directory is a file",
|
||||
args: args{
|
||||
name: "file/newfile.txt",
|
||||
data: []byte("this will fail"),
|
||||
perm: 0o644,
|
||||
},
|
||||
wantPath: "/tmp/file/newfile.txt",
|
||||
nodes: map[string]*Node{
|
||||
"/": {perm: 0o755, isDir: true},
|
||||
"/tmp": {perm: 0o755, isDir: true},
|
||||
"/tmp/file": {data: []byte("file content"), perm: 0o644},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "relative no parent directory write permission denied",
|
||||
args: args{
|
||||
name: "dir/newfile.txt",
|
||||
data: []byte("this will fail"),
|
||||
perm: 0o644,
|
||||
},
|
||||
wantPath: "/tmp/dir/newfile.txt",
|
||||
nodes: map[string]*Node{
|
||||
"/": {perm: 0o755, isDir: true},
|
||||
"/tmp": {perm: 0o755, isDir: true},
|
||||
"/tmp/dir": {perm: 0o500, isDir: true},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "relative no parent directory execute permission denied",
|
||||
args: args{
|
||||
name: "dir/newfile.txt",
|
||||
data: []byte("this will fail"),
|
||||
perm: 0o644,
|
||||
},
|
||||
wantPath: "/tmp/dir/newfile.txt",
|
||||
nodes: map[string]*Node{
|
||||
"/": {perm: 0o755, isDir: true},
|
||||
"/tmp": {perm: 0o755, isDir: true},
|
||||
"/tmp/dir": {perm: 0o600, isDir: true},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
fs := &FS{
|
||||
Pwd: "/tmp",
|
||||
Nodes: tt.nodes,
|
||||
}
|
||||
|
||||
err := fs.WriteFile(tt.args.name, tt.args.data, tt.args.perm)
|
||||
|
||||
if tt.wantErr {
|
||||
assert.Error(t, err)
|
||||
if _, ok := tt.nodes[tt.wantPath]; ok {
|
||||
assert.Equal(t,
|
||||
tt.nodes[tt.wantPath],
|
||||
fs.Nodes[tt.wantPath],
|
||||
)
|
||||
} else {
|
||||
assert.NotContains(t, fs.Nodes, tt.wantPath)
|
||||
}
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
|
||||
got := fs.Nodes[tt.wantPath]
|
||||
assert.Equal(t, tt.args.data, got.data)
|
||||
assert.Equal(t, tt.args.perm, got.perm)
|
||||
assert.Equal(t, false, got.isDir)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFSRemove(t *testing.T) {
|
||||
type args struct {
|
||||
name string
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
nodes map[string]*Node
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "relative remove existing file",
|
||||
args: args{name: "file.txt"},
|
||||
nodes: map[string]*Node{
|
||||
"/": {perm: 0o755, isDir: true},
|
||||
"/root": {perm: 0o755, isDir: true},
|
||||
"/root/file.txt": {data: []byte("file content"), perm: 0o644},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "absolute remove existing file",
|
||||
args: args{name: "/opt/file.txt"},
|
||||
nodes: map[string]*Node{
|
||||
"/": {perm: 0o755, isDir: true},
|
||||
"/opt": {perm: 0o755, isDir: true},
|
||||
"/opt/file.txt": {data: []byte("file content"), perm: 0o644},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "relative file does not exist",
|
||||
args: args{name: "nonexistent.txt"},
|
||||
nodes: map[string]*Node{
|
||||
"/": {perm: 0o755, isDir: true},
|
||||
"/root": {perm: 0o755, isDir: true},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "absolute file does not exist",
|
||||
args: args{name: "/opt/nonexistent.txt"},
|
||||
nodes: map[string]*Node{
|
||||
"/": {perm: 0o755, isDir: true},
|
||||
"/opt": {perm: 0o755, isDir: true},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "relative file is a directory",
|
||||
args: args{name: "dir"},
|
||||
nodes: map[string]*Node{
|
||||
"/": {perm: 0o755, isDir: true},
|
||||
"/root": {perm: 0o755, isDir: true},
|
||||
"/root/dir": {perm: 0o755, isDir: true},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "absolute file is a directory",
|
||||
args: args{name: "/opt/dir"},
|
||||
nodes: map[string]*Node{
|
||||
"/": {perm: 0o755, isDir: true},
|
||||
"/opt": {perm: 0o755, isDir: true},
|
||||
"/opt/dir": {perm: 0o755, isDir: true},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "relative file permission denied",
|
||||
args: args{name: "file.txt"},
|
||||
nodes: map[string]*Node{
|
||||
"/root": {perm: 0o755, isDir: true},
|
||||
"/root/file.txt": {data: []byte("file content"), perm: 0o400},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "absolute file permission denied",
|
||||
args: args{name: "/opt/file.txt"},
|
||||
nodes: map[string]*Node{
|
||||
"/": {perm: 0o755, isDir: true},
|
||||
"/opt": {perm: 0o755, isDir: true},
|
||||
"/opt/file.txt": {data: []byte("file content"), perm: 0o400},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "relative no directory write permission",
|
||||
args: args{name: "file.txt"},
|
||||
nodes: map[string]*Node{
|
||||
"/root": {perm: 0o555, isDir: true},
|
||||
"/root/file.txt": {data: []byte("file content"), perm: 0o644},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "relative no directory execute permission",
|
||||
args: args{name: "file.txt"},
|
||||
nodes: map[string]*Node{
|
||||
"/root": {perm: 0o655, isDir: true},
|
||||
"/root/file.txt": {data: []byte("file content"), perm: 0o644},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "relative no grandparent directory execute permission",
|
||||
args: args{name: "file.txt"},
|
||||
nodes: map[string]*Node{
|
||||
"/root": {perm: 0o655, isDir: true},
|
||||
"/root/dir": {perm: 0o755, isDir: true},
|
||||
"/root/dir/file.txt": {
|
||||
data: []byte("file content"), perm: 0o644,
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
fs := &FS{
|
||||
Pwd: "/root",
|
||||
Nodes: tt.nodes,
|
||||
}
|
||||
|
||||
err := fs.Remove(tt.args.name)
|
||||
|
||||
if tt.wantErr {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user