Skip to content

Commit

Permalink
Merge pull request #8 from xmidt-org/improve
Browse files Browse the repository at this point in the history
Improve FS interface.
  • Loading branch information
schmidtw authored Oct 3, 2023
2 parents 73d9463 + 5f919aa commit 12679eb
Show file tree
Hide file tree
Showing 7 changed files with 490 additions and 12 deletions.
35 changes: 35 additions & 0 deletions internal/fs/fs.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,38 @@ type FS interface {
// WriteFile writes the file with the specified permissions. Should match os.WriteFile().
WriteFile(name string, data []byte, perm fs.FileMode) error
}

// Option is an interface for options that can be applied in order via the Operate function.
type Option interface {
Apply(FS) error
}

// OptionFunc is a function that implements the Option interface.
type OptionFunc func(FS) error

func (f OptionFunc) Apply(fs FS) error {
return f(fs)
}

// Operate applies the specified options to the filesystem. This allows for declaring
// a set of conditions that must be present (like creating a directory path) before
// the final operation is performed.
//
// Example:
//
// Operate(fs,
// MakeDir("tmp", 0755),
// MakeDir("tmp/foo", 0755),
// MakeDir("tmp/foo/bar", 0755),
// WriteFile("tmp/foo/bar/baz.txt", []byte("hello world"), 0644))
func Operate(f FS, opts ...Option) error {
for _, opt := range opts {
if opt == nil {
continue
}
if err := opt.Apply(f); err != nil {
return err
}
}
return nil
}
278 changes: 278 additions & 0 deletions internal/fs/fs_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,278 @@
// SPDX-FileCopyrightText: 2023 Comcast Cable Communications Management, LLC
// SPDX-License-Identifier: Apache-2.0

package fs_test

import (
"errors"
"io/fs"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
xafs "github.com/xmidt-org/xmidt-agent/internal/fs"
"github.com/xmidt-org/xmidt-agent/internal/fs/mem"
)

var (
errUnknown = errors.New("unknown error")
)

func TestMakeDir(t *testing.T) {
tests := []struct {
description string
opt xafs.Option
opts []xafs.Option
start *mem.FS
expect *mem.FS
expectErr error
}{
{
description: "simple path",
opt: xafs.MakeDir("foo", 0755),
expect: mem.New(mem.WithDir("foo", 0755)),
}, {
description: "simple existing path",
opt: xafs.MakeDir("foo", 0755),
start: mem.New(mem.WithDir("foo", 0755)),
expect: mem.New(mem.WithDir("foo", 0755)),
}, {
description: "not a directory",
opt: xafs.MakeDir("foo", 0755),
start: mem.New(mem.WithFile("foo", "data", 0755)),
expectErr: xafs.ErrNotDirectory,
}, {
description: "not able to read",
opt: xafs.MakeDir("foo", 0755),
start: mem.New(mem.WithDir("foo", 0111)),
expectErr: fs.ErrPermission,
}, {
description: "error opening the file",
opt: xafs.MakeDir("foo", 0755),
start: mem.New(mem.WithError("foo", errUnknown)),
expectErr: errUnknown,
}, {
description: "two directory path",
opts: []xafs.Option{
xafs.MakeDir("foo", 0700),
xafs.MakeDir("foo/bar", 0750),
xafs.MakeDir("foo/bar/car", 0755),
},
expect: mem.New(
mem.WithDir("foo", 0700),
mem.WithDir("foo/bar", 0750),
mem.WithDir("foo/bar/car", 0755),
),
},
}

for _, tc := range tests {
t.Run(tc.description, func(t *testing.T) {
require := require.New(t)
assert := assert.New(t)

opts := append(tc.opts, tc.opt)
fs := tc.start
if fs == nil {
fs = mem.New()
}

last := opts[len(opts)-1]
opts = opts[:len(opts)-1]

err := xafs.Operate(fs, opts...)
require.NoError(err)

err = xafs.Operate(fs, last)

assert.ErrorIs(err, tc.expectErr)

if tc.expectErr == nil {
assert.Equal(tc.expect, fs)
}
})
}
}

func TestReadFileWithSHA256(t *testing.T) {
tests := []struct {
description string
filename string
start *mem.FS
expect string
expectErr error
}{
{
description: "simple path",
filename: "./foo",
start: mem.New(
mem.WithDir(".", 0755),
mem.WithFile("./foo", "text\n", 0644),
mem.WithFile("./foo.sha256", "b9e68e1bea3e5b19ca6b2f98b73a54b73daafaa250484902e09982e07a12e733 foo\n", 0644),
),
expect: "text\n",
}, {
description: "no ./ prefix",
filename: "foo",
start: mem.New(
mem.WithDir(".", 0755),
mem.WithFile("foo", "text\n", 0644),
mem.WithFile("foo.sha256", "b9e68e1bea3e5b19ca6b2f98b73a54b73daafaa250484902e09982e07a12e733 foo\n", 0644),
),
expect: "text\n",
}, {
description: "deeper path",
filename: "cat/foo",
start: mem.New(
mem.WithDir("cat", 0755),
mem.WithFile("cat/foo", "text\n", 0644),
mem.WithFile("cat/foo.sha256", "b9e68e1bea3e5b19ca6b2f98b73a54b73daafaa250484902e09982e07a12e733 foo\n", 0644),
),
expect: "text\n",
}, {
description: "no file",
filename: "./missing",
start: mem.New(),
expectErr: fs.ErrNotExist,
}, {
description: "no sha256 file",
filename: "./foo",
start: mem.New(
mem.WithDir(".", 0755),
mem.WithFile("./foo", "text\n", 0644),
),
expectErr: fs.ErrNotExist,
}, {
description: "invalid sha",
filename: "./foo",
start: mem.New(
mem.WithDir(".", 0755),
mem.WithFile("./foo", "text\n", 0644),
mem.WithFile("./foo.sha256", "0000000000000000000000000000000000000000000000000000000000000000 foo\n", 0644),
),
expectErr: xafs.ErrInvalidSHA,
},
}

for _, tc := range tests {
t.Run(tc.description, func(t *testing.T) {
assert := assert.New(t)

// fmt.Print(tc.start)

var buf []byte

err := xafs.Operate(tc.start, xafs.ReadFileWithSHA256(tc.filename, &buf))

assert.ErrorIs(err, tc.expectErr)
if tc.expectErr == nil {
assert.Equal([]byte(tc.expect), buf)
} else {
assert.Empty(buf)
}
})
}
}

func TestWriteileWithSHA256(t *testing.T) {
tests := []struct {
description string
filename string
data string
perm fs.FileMode
opts []xafs.Option
start *mem.FS
expect *mem.FS
expectErr error
}{
{
description: "simple path",
filename: "./foo",
data: "text\n",
perm: 0644,
start: mem.New(mem.WithDir(".", 0755)),
expect: mem.New(
mem.WithDir(".", 0755),
mem.WithFile("./foo", "text\n", 0644),
mem.WithFile("./foo.sha256", "b9e68e1bea3e5b19ca6b2f98b73a54b73daafaa250484902e09982e07a12e733 foo\n", 0644),
),
}, {
description: "no ./ prefix",
filename: "foo",
data: "text\n",
perm: 0644,
start: mem.New(mem.WithDir(".", 0755)),
expect: mem.New(
mem.WithDir(".", 0755),
mem.WithFile("foo", "text\n", 0644),
mem.WithFile("foo.sha256", "b9e68e1bea3e5b19ca6b2f98b73a54b73daafaa250484902e09982e07a12e733 foo\n", 0644)),
}, {
description: "deeper path",
filename: "cat/foo",
data: "text\n",
perm: 0644,
start: mem.New(mem.WithDir("cat", 0755)),
expect: mem.New(
mem.WithDir("cat", 0755),
mem.WithFile("cat/foo", "text\n", 0644),
mem.WithFile("cat/foo.sha256", "b9e68e1bea3e5b19ca6b2f98b73a54b73daafaa250484902e09982e07a12e733 foo\n", 0644)),
}, {
description: "over files",
filename: "foo",
data: "text\n",
perm: 0644,
start: mem.New(
mem.WithDir(".", 0755),
mem.WithFile("foo", "some other text\n", 0644),
mem.WithFile("foo.sha256", "0000000000000000000000000000000000000000000000000000000000000000 foo\n", 0644)),
expect: mem.New(
mem.WithDir(".", 0755),
mem.WithFile("foo", "text\n", 0644),
mem.WithFile("foo.sha256", "b9e68e1bea3e5b19ca6b2f98b73a54b73daafaa250484902e09982e07a12e733 foo\n", 0644)),
}, {
description: "unable to write to file",
filename: "foo",
data: "text\n",
perm: 0644,
start: mem.New(
mem.WithDir(".", 0755),
mem.WithError("foo", errUnknown)),
expectErr: errUnknown,
expect: mem.New(
mem.WithDir(".", 0755),
mem.WithError("foo", errUnknown)),
}, {
description: "unable to write to sha file",
filename: "foo",
data: "text\n",
perm: 0644,
start: mem.New(
mem.WithDir(".", 0755),
mem.WithError("foo.sha256", errUnknown),
),
expectErr: errUnknown,
expect: mem.New(
mem.WithDir(".", 0755),
mem.WithFile("foo", "text\n", 0644),
mem.WithError("foo.sha256", errUnknown)),
},
}

for _, tc := range tests {
t.Run(tc.description, func(t *testing.T) {
require := require.New(t)
assert := assert.New(t)

// fmt.Print(tc.start)
fs := tc.start

err := xafs.Operate(fs, tc.opts...)
require.NoError(err)

err = xafs.Operate(fs, xafs.WriteFileWithSHA256(tc.filename, []byte(tc.data), tc.perm))

assert.ErrorIs(err, tc.expectErr)
assert.Equal(tc.expect, fs)
})
}
}
2 changes: 1 addition & 1 deletion internal/fs/mem/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func (f *File) Stat() (iofs.FileInfo, error) {
size: int64(len(f.Bytes)),
mode: f.Perm,
modTime: time.Time{},
isDir: false,
isDir: f.isDir,
}, nil
}

Expand Down
12 changes: 12 additions & 0 deletions internal/fs/mem/file_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,18 @@ func TestFile_Stat(t *testing.T) {
mode: iofs.FileMode(0644),
isDir: false,
},
}, {
description: "a simple dir",
file: File{
Perm: iofs.FileMode(0755),
name: "foo",
isDir: true,
},
want: &fileInfo{
name: "foo",
mode: iofs.FileMode(0755),
isDir: true,
},
},
}
for _, tc := range tests {
Expand Down
Loading

0 comments on commit 12679eb

Please sign in to comment.