feat(ax): apply AX compliance sweep — usage examples and predictable names
Some checks failed
CI / test (push) Failing after 2s
CI / auto-merge (push) Failing after 0s
CI / auto-fix (push) Failing after 0s

- Add // Example: usage comments to all Medium interface methods in io.go
- Add // Example: comments to local, s3, sqlite, store, datanode, node medium methods
- Rename short variable `n` → `nodeTree` throughout node/node_test.go
- Rename short variable `s` → `keyValueStore` in store/store_test.go
- Rename counter variable `n` → `count` in store/store_test.go
- Rename `m` → `medium` in store/medium_test.go helper
- Remove redundant prose comments replaced by usage examples

Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
Snider 2026-03-31 12:19:56 +01:00
parent 378fc7c0de
commit 702286a583
10 changed files with 244 additions and 186 deletions

View file

@ -38,6 +38,8 @@ type Medium struct {
lock sync.RWMutex
}
// Example: medium := datanode.New()
// Example: _ = medium.Write("jobs/run.log", "started")
func New() *Medium {
return &Medium{
dataNode: borgdatanode.New(),

12
io.go
View file

@ -15,31 +15,43 @@ import (
// Example: backup, _ := io.NewSandboxed("/srv/backup")
// Example: _ = io.Copy(medium, "data/report.json", backup, "daily/report.json")
type Medium interface {
// Example: content, _ := medium.Read("config/app.yaml")
Read(path string) (string, error)
// Example: _ = medium.Write("config/app.yaml", "port: 8080")
Write(path, content string) error
// Example: _ = medium.WriteMode("keys/private.key", key, 0600)
WriteMode(path, content string, mode fs.FileMode) error
// Example: _ = medium.EnsureDir("config/app")
EnsureDir(path string) error
// Example: isFile := medium.IsFile("config/app.yaml")
IsFile(path string) bool
// Example: _ = medium.Delete("config/app.yaml")
Delete(path string) error
// Example: _ = medium.DeleteAll("logs/archive")
DeleteAll(path string) error
// Example: _ = medium.Rename("drafts/todo.txt", "archive/todo.txt")
Rename(oldPath, newPath string) error
// Example: entries, _ := medium.List("config")
List(path string) ([]fs.DirEntry, error)
// Example: info, _ := medium.Stat("config/app.yaml")
Stat(path string) (fs.FileInfo, error)
// Example: file, _ := medium.Open("config/app.yaml")
Open(path string) (fs.File, error)
// Example: writer, _ := medium.Create("logs/app.log")
Create(path string) (goio.WriteCloser, error)
// Example: writer, _ := medium.Append("logs/app.log")
Append(path string) (goio.WriteCloser, error)
// Example: reader, _ := medium.ReadStream("logs/app.log")

View file

@ -238,6 +238,7 @@ func (medium *Medium) WriteMode(path, content string, mode fs.FileMode) error {
return resultError("local.WriteMode", core.Concat("write failed: ", path), unrestrictedFileSystem.WriteMode(resolvedPath, content, mode))
}
// Example: _ = medium.EnsureDir("config/app")
func (medium *Medium) EnsureDir(path string) error {
resolvedPath, err := medium.validatePath(path)
if err != nil {
@ -246,6 +247,7 @@ func (medium *Medium) EnsureDir(path string) error {
return resultError("local.EnsureDir", core.Concat("ensure dir failed: ", path), unrestrictedFileSystem.EnsureDir(resolvedPath))
}
// Example: isDirectory := medium.IsDir("config")
func (medium *Medium) IsDir(path string) bool {
if path == "" {
return false
@ -257,6 +259,7 @@ func (medium *Medium) IsDir(path string) bool {
return unrestrictedFileSystem.IsDir(resolvedPath)
}
// Example: isFile := medium.IsFile("config/app.yaml")
func (medium *Medium) IsFile(path string) bool {
if path == "" {
return false
@ -268,6 +271,7 @@ func (medium *Medium) IsFile(path string) bool {
return unrestrictedFileSystem.IsFile(resolvedPath)
}
// Example: exists := medium.Exists("config/app.yaml")
func (medium *Medium) Exists(path string) bool {
resolvedPath, err := medium.validatePath(path)
if err != nil {
@ -276,6 +280,7 @@ func (medium *Medium) Exists(path string) bool {
return unrestrictedFileSystem.Exists(resolvedPath)
}
// Example: entries, _ := medium.List("config")
func (medium *Medium) List(path string) ([]fs.DirEntry, error) {
resolvedPath, err := medium.validatePath(path)
if err != nil {
@ -284,6 +289,7 @@ func (medium *Medium) List(path string) ([]fs.DirEntry, error) {
return resultDirEntries("local.List", core.Concat("list failed: ", path), unrestrictedFileSystem.List(resolvedPath))
}
// Example: info, _ := medium.Stat("config/app.yaml")
func (medium *Medium) Stat(path string) (fs.FileInfo, error) {
resolvedPath, err := medium.validatePath(path)
if err != nil {
@ -292,6 +298,7 @@ func (medium *Medium) Stat(path string) (fs.FileInfo, error) {
return resultFileInfo("local.Stat", core.Concat("stat failed: ", path), unrestrictedFileSystem.Stat(resolvedPath))
}
// Example: file, _ := medium.Open("config/app.yaml")
func (medium *Medium) Open(path string) (fs.File, error) {
resolvedPath, err := medium.validatePath(path)
if err != nil {
@ -300,6 +307,7 @@ func (medium *Medium) Open(path string) (fs.File, error) {
return resultFile("local.Open", core.Concat("open failed: ", path), unrestrictedFileSystem.Open(resolvedPath))
}
// Example: writer, _ := medium.Create("logs/app.log")
func (medium *Medium) Create(path string) (goio.WriteCloser, error) {
resolvedPath, err := medium.validatePath(path)
if err != nil {
@ -308,6 +316,7 @@ func (medium *Medium) Create(path string) (goio.WriteCloser, error) {
return resultWriteCloser("local.Create", core.Concat("create failed: ", path), unrestrictedFileSystem.Create(resolvedPath))
}
// Example: writer, _ := medium.Append("logs/app.log")
func (medium *Medium) Append(path string) (goio.WriteCloser, error) {
resolvedPath, err := medium.validatePath(path)
if err != nil {
@ -326,6 +335,7 @@ func (medium *Medium) WriteStream(path string) (goio.WriteCloser, error) {
return medium.Create(path)
}
// Example: _ = medium.Delete("config/app.yaml")
func (medium *Medium) Delete(path string) error {
resolvedPath, err := medium.validatePath(path)
if err != nil {
@ -337,6 +347,7 @@ func (medium *Medium) Delete(path string) error {
return resultError("local.Delete", core.Concat("delete failed: ", path), unrestrictedFileSystem.Delete(resolvedPath))
}
// Example: _ = medium.DeleteAll("logs/archive")
func (medium *Medium) DeleteAll(path string) error {
resolvedPath, err := medium.validatePath(path)
if err != nil {
@ -348,6 +359,7 @@ func (medium *Medium) DeleteAll(path string) error {
return resultError("local.DeleteAll", core.Concat("delete all failed: ", path), unrestrictedFileSystem.DeleteAll(resolvedPath))
}
// Example: _ = medium.Rename("drafts/todo.txt", "archive/todo.txt")
func (medium *Medium) Rename(oldPath, newPath string) error {
oldResolvedPath, err := medium.validatePath(oldPath)
if err != nil {

View file

@ -232,6 +232,7 @@ func (node *Node) CopyTo(target coreio.Medium, sourcePath, destPath string) erro
return nil
}
// Example: file, _ := nodeTree.Open("config/app.yaml")
func (node *Node) Open(name string) (fs.File, error) {
name = core.TrimPrefix(name, "/")
if dataFile, ok := node.files[name]; ok {
@ -249,6 +250,7 @@ func (node *Node) Open(name string) (fs.File, error) {
return nil, core.E("node.Open", core.Concat("path not found: ", name), fs.ErrNotExist)
}
// Example: info, _ := nodeTree.Stat("config/app.yaml")
func (node *Node) Stat(name string) (fs.FileInfo, error) {
name = core.TrimPrefix(name, "/")
if dataFile, ok := node.files[name]; ok {
@ -266,6 +268,7 @@ func (node *Node) Stat(name string) (fs.FileInfo, error) {
return nil, core.E("node.Stat", core.Concat("path not found: ", name), fs.ErrNotExist)
}
// Example: entries, _ := nodeTree.ReadDir("config")
func (node *Node) ReadDir(name string) ([]fs.DirEntry, error) {
name = core.TrimPrefix(name, "/")
if name == "." {
@ -314,6 +317,7 @@ func (node *Node) ReadDir(name string) ([]fs.DirEntry, error) {
return entries, nil
}
// Example: content, _ := nodeTree.Read("config/app.yaml")
func (node *Node) Read(filePath string) (string, error) {
filePath = core.TrimPrefix(filePath, "/")
file, ok := node.files[filePath]
@ -323,11 +327,13 @@ func (node *Node) Read(filePath string) (string, error) {
return string(file.content), nil
}
// Example: _ = nodeTree.Write("config/app.yaml", "port: 8080")
func (node *Node) Write(filePath, content string) error {
node.AddData(filePath, []byte(content))
return nil
}
// Example: _ = nodeTree.WriteMode("keys/private.key", key, 0600)
func (node *Node) WriteMode(filePath, content string, mode fs.FileMode) error {
return node.Write(filePath, content)
}
@ -337,17 +343,20 @@ func (node *Node) EnsureDir(directoryPath string) error {
return nil
}
// Example: exists := nodeTree.Exists("config/app.yaml")
func (node *Node) Exists(filePath string) bool {
_, err := node.Stat(filePath)
return err == nil
}
// Example: isFile := nodeTree.IsFile("config/app.yaml")
func (node *Node) IsFile(filePath string) bool {
filePath = core.TrimPrefix(filePath, "/")
_, ok := node.files[filePath]
return ok
}
// Example: isDirectory := nodeTree.IsDir("config")
func (node *Node) IsDir(filePath string) bool {
info, err := node.Stat(filePath)
if err != nil {
@ -356,6 +365,7 @@ func (node *Node) IsDir(filePath string) bool {
return info.IsDir()
}
// Example: _ = nodeTree.Delete("config/app.yaml")
func (node *Node) Delete(filePath string) error {
filePath = core.TrimPrefix(filePath, "/")
if _, ok := node.files[filePath]; ok {
@ -365,6 +375,7 @@ func (node *Node) Delete(filePath string) error {
return core.E("node.Delete", core.Concat("path not found: ", filePath), fs.ErrNotExist)
}
// Example: _ = nodeTree.DeleteAll("logs/archive")
func (node *Node) DeleteAll(filePath string) error {
filePath = core.TrimPrefix(filePath, "/")
@ -388,6 +399,7 @@ func (node *Node) DeleteAll(filePath string) error {
return nil
}
// Example: _ = nodeTree.Rename("drafts/todo.txt", "archive/todo.txt")
func (node *Node) Rename(oldPath, newPath string) error {
oldPath = core.TrimPrefix(oldPath, "/")
newPath = core.TrimPrefix(newPath, "/")
@ -403,6 +415,7 @@ func (node *Node) Rename(oldPath, newPath string) error {
return nil
}
// Example: entries, _ := nodeTree.List("config")
func (node *Node) List(filePath string) ([]fs.DirEntry, error) {
filePath = core.TrimPrefix(filePath, "/")
if filePath == "" || filePath == "." {
@ -411,11 +424,13 @@ func (node *Node) List(filePath string) ([]fs.DirEntry, error) {
return node.ReadDir(filePath)
}
// Example: writer, _ := nodeTree.Create("logs/app.log")
func (node *Node) Create(filePath string) (goio.WriteCloser, error) {
filePath = core.TrimPrefix(filePath, "/")
return &nodeWriter{node: node, path: filePath}, nil
}
// Example: writer, _ := nodeTree.Append("logs/app.log")
func (node *Node) Append(filePath string) (goio.WriteCloser, error) {
filePath = core.TrimPrefix(filePath, "/")
var existing []byte

View file

@ -15,16 +15,16 @@ import (
)
func TestNode_New_Good(t *testing.T) {
n := New()
require.NotNil(t, n, "New() must not return nil")
assert.NotNil(t, n.files, "New() must initialise the files map")
nodeTree := New()
require.NotNil(t, nodeTree, "New() must not return nil")
assert.NotNil(t, nodeTree.files, "New() must initialise the files map")
}
func TestNode_AddData_Good(t *testing.T) {
n := New()
n.AddData("foo.txt", []byte("foo"))
nodeTree := New()
nodeTree.AddData("foo.txt", []byte("foo"))
file, ok := n.files["foo.txt"]
file, ok := nodeTree.files["foo.txt"]
require.True(t, ok, "file foo.txt should be present")
assert.Equal(t, []byte("foo"), file.content)
@ -34,38 +34,38 @@ func TestNode_AddData_Good(t *testing.T) {
}
func TestNode_AddData_Bad(t *testing.T) {
n := New()
nodeTree := New()
n.AddData("", []byte("data"))
assert.Empty(t, n.files, "empty name must not be stored")
nodeTree.AddData("", []byte("data"))
assert.Empty(t, nodeTree.files, "empty name must not be stored")
n.AddData("dir/", nil)
assert.Empty(t, n.files, "directory entry must not be stored")
nodeTree.AddData("dir/", nil)
assert.Empty(t, nodeTree.files, "directory entry must not be stored")
}
func TestNode_AddData_EdgeCases_Good(t *testing.T) {
t.Run("Overwrite", func(t *testing.T) {
n := New()
n.AddData("foo.txt", []byte("foo"))
n.AddData("foo.txt", []byte("bar"))
nodeTree := New()
nodeTree.AddData("foo.txt", []byte("foo"))
nodeTree.AddData("foo.txt", []byte("bar"))
file := n.files["foo.txt"]
file := nodeTree.files["foo.txt"]
assert.Equal(t, []byte("bar"), file.content, "second AddData should overwrite")
})
t.Run("LeadingSlash", func(t *testing.T) {
n := New()
n.AddData("/hello.txt", []byte("hi"))
_, ok := n.files["hello.txt"]
nodeTree := New()
nodeTree.AddData("/hello.txt", []byte("hi"))
_, ok := nodeTree.files["hello.txt"]
assert.True(t, ok, "leading slash should be trimmed")
})
}
func TestNode_Open_Good(t *testing.T) {
n := New()
n.AddData("foo.txt", []byte("foo"))
nodeTree := New()
nodeTree.AddData("foo.txt", []byte("foo"))
file, err := n.Open("foo.txt")
file, err := nodeTree.Open("foo.txt")
require.NoError(t, err)
defer file.Close()
@ -76,17 +76,17 @@ func TestNode_Open_Good(t *testing.T) {
}
func TestNode_Open_Bad(t *testing.T) {
n := New()
_, err := n.Open("nonexistent.txt")
nodeTree := New()
_, err := nodeTree.Open("nonexistent.txt")
require.Error(t, err)
assert.ErrorIs(t, err, fs.ErrNotExist)
}
func TestNode_Open_Directory_Good(t *testing.T) {
n := New()
n.AddData("bar/baz.txt", []byte("baz"))
nodeTree := New()
nodeTree.AddData("bar/baz.txt", []byte("baz"))
file, err := n.Open("bar")
file, err := nodeTree.Open("bar")
require.NoError(t, err)
defer file.Close()
@ -99,88 +99,88 @@ func TestNode_Open_Directory_Good(t *testing.T) {
}
func TestNode_Stat_Good(t *testing.T) {
n := New()
n.AddData("foo.txt", []byte("foo"))
n.AddData("bar/baz.txt", []byte("baz"))
nodeTree := New()
nodeTree.AddData("foo.txt", []byte("foo"))
nodeTree.AddData("bar/baz.txt", []byte("baz"))
info, err := n.Stat("bar/baz.txt")
info, err := nodeTree.Stat("bar/baz.txt")
require.NoError(t, err)
assert.Equal(t, "baz.txt", info.Name())
assert.Equal(t, int64(3), info.Size())
assert.False(t, info.IsDir())
dirInfo, err := n.Stat("bar")
dirInfo, err := nodeTree.Stat("bar")
require.NoError(t, err)
assert.True(t, dirInfo.IsDir())
assert.Equal(t, "bar", dirInfo.Name())
}
func TestNode_Stat_Bad(t *testing.T) {
n := New()
_, err := n.Stat("nonexistent")
nodeTree := New()
_, err := nodeTree.Stat("nonexistent")
require.Error(t, err)
assert.ErrorIs(t, err, fs.ErrNotExist)
}
func TestNode_Stat_RootDirectory_Good(t *testing.T) {
n := New()
n.AddData("foo.txt", []byte("foo"))
nodeTree := New()
nodeTree.AddData("foo.txt", []byte("foo"))
info, err := n.Stat(".")
info, err := nodeTree.Stat(".")
require.NoError(t, err)
assert.True(t, info.IsDir())
assert.Equal(t, ".", info.Name())
}
func TestNode_ReadFile_Good(t *testing.T) {
n := New()
n.AddData("hello.txt", []byte("hello world"))
nodeTree := New()
nodeTree.AddData("hello.txt", []byte("hello world"))
data, err := n.ReadFile("hello.txt")
data, err := nodeTree.ReadFile("hello.txt")
require.NoError(t, err)
assert.Equal(t, []byte("hello world"), data)
}
func TestNode_ReadFile_Bad(t *testing.T) {
n := New()
_, err := n.ReadFile("missing.txt")
nodeTree := New()
_, err := nodeTree.ReadFile("missing.txt")
require.Error(t, err)
assert.ErrorIs(t, err, fs.ErrNotExist)
}
func TestNode_ReadFile_ReturnsCopy_Good(t *testing.T) {
n := New()
n.AddData("data.bin", []byte("original"))
nodeTree := New()
nodeTree.AddData("data.bin", []byte("original"))
data, err := n.ReadFile("data.bin")
data, err := nodeTree.ReadFile("data.bin")
require.NoError(t, err)
data[0] = 'X'
data2, err := n.ReadFile("data.bin")
data2, err := nodeTree.ReadFile("data.bin")
require.NoError(t, err)
assert.Equal(t, []byte("original"), data2, "ReadFile must return an independent copy")
}
func TestNode_ReadDir_Good(t *testing.T) {
n := New()
n.AddData("foo.txt", []byte("foo"))
n.AddData("bar/baz.txt", []byte("baz"))
n.AddData("bar/qux.txt", []byte("qux"))
nodeTree := New()
nodeTree.AddData("foo.txt", []byte("foo"))
nodeTree.AddData("bar/baz.txt", []byte("baz"))
nodeTree.AddData("bar/qux.txt", []byte("qux"))
entries, err := n.ReadDir(".")
entries, err := nodeTree.ReadDir(".")
require.NoError(t, err)
assert.Equal(t, []string{"bar", "foo.txt"}, sortedNames(entries))
barEntries, err := n.ReadDir("bar")
barEntries, err := nodeTree.ReadDir("bar")
require.NoError(t, err)
assert.Equal(t, []string{"baz.txt", "qux.txt"}, sortedNames(barEntries))
}
func TestNode_ReadDir_Bad(t *testing.T) {
n := New()
n.AddData("foo.txt", []byte("foo"))
nodeTree := New()
nodeTree.AddData("foo.txt", []byte("foo"))
_, err := n.ReadDir("foo.txt")
_, err := nodeTree.ReadDir("foo.txt")
require.Error(t, err)
var pathErr *fs.PathError
require.True(t, core.As(err, &pathErr))
@ -188,45 +188,45 @@ func TestNode_ReadDir_Bad(t *testing.T) {
}
func TestNode_ReadDir_IgnoresEmptyEntry_Good(t *testing.T) {
n := New()
n.AddData("bar/baz.txt", []byte("baz"))
n.AddData("empty_dir/", nil)
nodeTree := New()
nodeTree.AddData("bar/baz.txt", []byte("baz"))
nodeTree.AddData("empty_dir/", nil)
entries, err := n.ReadDir(".")
entries, err := nodeTree.ReadDir(".")
require.NoError(t, err)
assert.Equal(t, []string{"bar"}, sortedNames(entries))
}
func TestNode_Exists_Good(t *testing.T) {
n := New()
n.AddData("foo.txt", []byte("foo"))
n.AddData("bar/baz.txt", []byte("baz"))
nodeTree := New()
nodeTree.AddData("foo.txt", []byte("foo"))
nodeTree.AddData("bar/baz.txt", []byte("baz"))
assert.True(t, n.Exists("foo.txt"))
assert.True(t, n.Exists("bar"))
assert.True(t, nodeTree.Exists("foo.txt"))
assert.True(t, nodeTree.Exists("bar"))
}
func TestNode_Exists_Bad(t *testing.T) {
n := New()
assert.False(t, n.Exists("nonexistent"))
nodeTree := New()
assert.False(t, nodeTree.Exists("nonexistent"))
}
func TestNode_Exists_RootAndEmptyPath_Good(t *testing.T) {
n := New()
n.AddData("dummy.txt", []byte("dummy"))
nodeTree := New()
nodeTree.AddData("dummy.txt", []byte("dummy"))
assert.True(t, n.Exists("."), "root '.' must exist")
assert.True(t, n.Exists(""), "empty path (root) must exist")
assert.True(t, nodeTree.Exists("."), "root '.' must exist")
assert.True(t, nodeTree.Exists(""), "empty path (root) must exist")
}
func TestNode_Walk_Default_Good(t *testing.T) {
n := New()
n.AddData("foo.txt", []byte("foo"))
n.AddData("bar/baz.txt", []byte("baz"))
n.AddData("bar/qux.txt", []byte("qux"))
nodeTree := New()
nodeTree.AddData("foo.txt", []byte("foo"))
nodeTree.AddData("bar/baz.txt", []byte("baz"))
nodeTree.AddData("bar/qux.txt", []byte("qux"))
var paths []string
err := n.Walk(".", func(p string, d fs.DirEntry, err error) error {
err := nodeTree.Walk(".", func(p string, d fs.DirEntry, err error) error {
paths = append(paths, p)
return nil
}, WalkOptions{})
@ -237,10 +237,10 @@ func TestNode_Walk_Default_Good(t *testing.T) {
}
func TestNode_Walk_Default_Bad(t *testing.T) {
n := New()
nodeTree := New()
var called bool
err := n.Walk("nonexistent", func(p string, d fs.DirEntry, err error) error {
err := nodeTree.Walk("nonexistent", func(p string, d fs.DirEntry, err error) error {
called = true
assert.Error(t, err)
assert.ErrorIs(t, err, fs.ErrNotExist)
@ -251,13 +251,13 @@ func TestNode_Walk_Default_Bad(t *testing.T) {
}
func TestNode_Walk_CallbackError_Good(t *testing.T) {
n := New()
n.AddData("a/b.txt", []byte("b"))
n.AddData("a/c.txt", []byte("c"))
nodeTree := New()
nodeTree.AddData("a/b.txt", []byte("b"))
nodeTree.AddData("a/c.txt", []byte("c"))
walkErr := core.NewError("stop walking")
var paths []string
err := n.Walk(".", func(p string, d fs.DirEntry, err error) error {
err := nodeTree.Walk(".", func(p string, d fs.DirEntry, err error) error {
if p == "a/b.txt" {
return walkErr
}
@ -269,15 +269,15 @@ func TestNode_Walk_CallbackError_Good(t *testing.T) {
}
func TestNode_Walk_Good(t *testing.T) {
n := New()
n.AddData("root.txt", []byte("root"))
n.AddData("a/a1.txt", []byte("a1"))
n.AddData("a/b/b1.txt", []byte("b1"))
n.AddData("c/c1.txt", []byte("c1"))
nodeTree := New()
nodeTree.AddData("root.txt", []byte("root"))
nodeTree.AddData("a/a1.txt", []byte("a1"))
nodeTree.AddData("a/b/b1.txt", []byte("b1"))
nodeTree.AddData("c/c1.txt", []byte("c1"))
t.Run("MaxDepth", func(t *testing.T) {
var paths []string
err := n.Walk(".", func(p string, d fs.DirEntry, err error) error {
err := nodeTree.Walk(".", func(p string, d fs.DirEntry, err error) error {
paths = append(paths, p)
return nil
}, WalkOptions{MaxDepth: 1})
@ -289,7 +289,7 @@ func TestNode_Walk_Good(t *testing.T) {
t.Run("Filter", func(t *testing.T) {
var paths []string
err := n.Walk(".", func(p string, d fs.DirEntry, err error) error {
err := nodeTree.Walk(".", func(p string, d fs.DirEntry, err error) error {
paths = append(paths, p)
return nil
}, WalkOptions{Filter: func(p string, d fs.DirEntry) bool {
@ -303,7 +303,7 @@ func TestNode_Walk_Good(t *testing.T) {
t.Run("SkipErrors", func(t *testing.T) {
var called bool
err := n.Walk("nonexistent", func(p string, d fs.DirEntry, err error) error {
err := nodeTree.Walk("nonexistent", func(p string, d fs.DirEntry, err error) error {
called = true
return err
}, WalkOptions{SkipErrors: true})
@ -314,11 +314,11 @@ func TestNode_Walk_Good(t *testing.T) {
}
func TestNode_CopyFile_Good(t *testing.T) {
n := New()
n.AddData("foo.txt", []byte("foo"))
nodeTree := New()
nodeTree.AddData("foo.txt", []byte("foo"))
tmpfile := core.Path(t.TempDir(), "test.txt")
err := n.CopyFile("foo.txt", tmpfile, 0644)
err := nodeTree.CopyFile("foo.txt", tmpfile, 0644)
require.NoError(t, err)
content, err := coreio.Local.Read(tmpfile)
@ -327,40 +327,40 @@ func TestNode_CopyFile_Good(t *testing.T) {
}
func TestNode_CopyFile_Bad(t *testing.T) {
n := New()
nodeTree := New()
tmpfile := core.Path(t.TempDir(), "test.txt")
err := n.CopyFile("nonexistent.txt", tmpfile, 0644)
err := nodeTree.CopyFile("nonexistent.txt", tmpfile, 0644)
assert.Error(t, err)
n.AddData("foo.txt", []byte("foo"))
err = n.CopyFile("foo.txt", "/nonexistent_dir/test.txt", 0644)
nodeTree.AddData("foo.txt", []byte("foo"))
err = nodeTree.CopyFile("foo.txt", "/nonexistent_dir/test.txt", 0644)
assert.Error(t, err)
}
func TestNode_CopyFile_DirectorySource_Bad(t *testing.T) {
n := New()
n.AddData("bar/baz.txt", []byte("baz"))
nodeTree := New()
nodeTree.AddData("bar/baz.txt", []byte("baz"))
tmpfile := core.Path(t.TempDir(), "test.txt")
err := n.CopyFile("bar", tmpfile, 0644)
err := nodeTree.CopyFile("bar", tmpfile, 0644)
assert.Error(t, err)
}
func TestNode_CopyTo_Good(t *testing.T) {
n := New()
n.AddData("config/app.yaml", []byte("port: 8080"))
n.AddData("config/env/app.env", []byte("MODE=test"))
nodeTree := New()
nodeTree.AddData("config/app.yaml", []byte("port: 8080"))
nodeTree.AddData("config/env/app.env", []byte("MODE=test"))
fileTarget := coreio.NewMemoryMedium()
err := n.CopyTo(fileTarget, "config/app.yaml", "backup/app.yaml")
err := nodeTree.CopyTo(fileTarget, "config/app.yaml", "backup/app.yaml")
require.NoError(t, err)
content, err := fileTarget.Read("backup/app.yaml")
require.NoError(t, err)
assert.Equal(t, "port: 8080", content)
dirTarget := coreio.NewMemoryMedium()
err = n.CopyTo(dirTarget, "config", "backup/config")
err = nodeTree.CopyTo(dirTarget, "config", "backup/config")
require.NoError(t, err)
content, err = dirTarget.Read("backup/config/app.yaml")
require.NoError(t, err)
@ -371,35 +371,35 @@ func TestNode_CopyTo_Good(t *testing.T) {
}
func TestNode_CopyTo_Bad(t *testing.T) {
n := New()
err := n.CopyTo(coreio.NewMemoryMedium(), "missing", "backup/missing")
nodeTree := New()
err := nodeTree.CopyTo(coreio.NewMemoryMedium(), "missing", "backup/missing")
assert.Error(t, err)
}
func TestNode_MediumFacade_Good(t *testing.T) {
n := New()
nodeTree := New()
require.NoError(t, n.Write("docs/readme.txt", "hello"))
require.NoError(t, n.WriteMode("docs/mode.txt", "mode", 0600))
require.NoError(t, n.Write("docs/guide.txt", "guide"))
require.NoError(t, n.EnsureDir("ignored"))
require.NoError(t, nodeTree.Write("docs/readme.txt", "hello"))
require.NoError(t, nodeTree.WriteMode("docs/mode.txt", "mode", 0600))
require.NoError(t, nodeTree.Write("docs/guide.txt", "guide"))
require.NoError(t, nodeTree.EnsureDir("ignored"))
value, err := n.Read("docs/readme.txt")
value, err := nodeTree.Read("docs/readme.txt")
require.NoError(t, err)
assert.Equal(t, "hello", value)
value, err = n.Read("docs/guide.txt")
value, err = nodeTree.Read("docs/guide.txt")
require.NoError(t, err)
assert.Equal(t, "guide", value)
assert.True(t, n.IsFile("docs/readme.txt"))
assert.True(t, n.IsDir("docs"))
assert.True(t, nodeTree.IsFile("docs/readme.txt"))
assert.True(t, nodeTree.IsDir("docs"))
entries, err := n.List("docs")
entries, err := nodeTree.List("docs")
require.NoError(t, err)
assert.Equal(t, []string{"guide.txt", "mode.txt", "readme.txt"}, sortedNames(entries))
file, err := n.Open("docs/readme.txt")
file, err := nodeTree.Open("docs/readme.txt")
require.NoError(t, err)
info, err := file.Stat()
require.NoError(t, err)
@ -409,7 +409,7 @@ func TestNode_MediumFacade_Good(t *testing.T) {
assert.Nil(t, info.Sys())
require.NoError(t, file.Close())
dir, err := n.Open("docs")
dir, err := nodeTree.Open("docs")
require.NoError(t, err)
dirInfo, err := dir.Stat()
require.NoError(t, err)
@ -419,47 +419,47 @@ func TestNode_MediumFacade_Good(t *testing.T) {
assert.Nil(t, dirInfo.Sys())
require.NoError(t, dir.Close())
createWriter, err := n.Create("docs/generated.txt")
createWriter, err := nodeTree.Create("docs/generated.txt")
require.NoError(t, err)
_, err = createWriter.Write([]byte("generated"))
require.NoError(t, err)
require.NoError(t, createWriter.Close())
appendWriter, err := n.Append("docs/generated.txt")
appendWriter, err := nodeTree.Append("docs/generated.txt")
require.NoError(t, err)
_, err = appendWriter.Write([]byte(" content"))
require.NoError(t, err)
require.NoError(t, appendWriter.Close())
streamReader, err := n.ReadStream("docs/generated.txt")
streamReader, err := nodeTree.ReadStream("docs/generated.txt")
require.NoError(t, err)
streamData, err := io.ReadAll(streamReader)
require.NoError(t, err)
assert.Equal(t, "generated content", string(streamData))
require.NoError(t, streamReader.Close())
writeStream, err := n.WriteStream("docs/stream.txt")
writeStream, err := nodeTree.WriteStream("docs/stream.txt")
require.NoError(t, err)
_, err = writeStream.Write([]byte("stream"))
require.NoError(t, err)
require.NoError(t, writeStream.Close())
require.NoError(t, n.Rename("docs/stream.txt", "docs/stream-renamed.txt"))
assert.True(t, n.Exists("docs/stream-renamed.txt"))
require.NoError(t, nodeTree.Rename("docs/stream.txt", "docs/stream-renamed.txt"))
assert.True(t, nodeTree.Exists("docs/stream-renamed.txt"))
require.NoError(t, n.Delete("docs/stream-renamed.txt"))
assert.False(t, n.Exists("docs/stream-renamed.txt"))
require.NoError(t, nodeTree.Delete("docs/stream-renamed.txt"))
assert.False(t, nodeTree.Exists("docs/stream-renamed.txt"))
require.NoError(t, n.DeleteAll("docs"))
assert.False(t, n.Exists("docs"))
require.NoError(t, nodeTree.DeleteAll("docs"))
assert.False(t, nodeTree.Exists("docs"))
}
func TestNode_ToTar_Good(t *testing.T) {
n := New()
n.AddData("foo.txt", []byte("foo"))
n.AddData("bar/baz.txt", []byte("baz"))
nodeTree := New()
nodeTree.AddData("foo.txt", []byte("foo"))
nodeTree.AddData("bar/baz.txt", []byte("baz"))
tarball, err := n.ToTar()
tarball, err := nodeTree.ToTar()
require.NoError(t, err)
require.NotEmpty(t, tarball)
@ -500,11 +500,11 @@ func TestNode_FromTar_Good(t *testing.T) {
}
require.NoError(t, tw.Close())
n, err := FromTar(buf.Bytes())
nodeTree, err := FromTar(buf.Bytes())
require.NoError(t, err)
assert.True(t, n.Exists("foo.txt"), "foo.txt should exist")
assert.True(t, n.Exists("bar/baz.txt"), "bar/baz.txt should exist")
assert.True(t, nodeTree.Exists("foo.txt"), "foo.txt should exist")
assert.True(t, nodeTree.Exists("bar/baz.txt"), "bar/baz.txt should exist")
}
func TestNode_FromTar_Bad(t *testing.T) {
@ -514,41 +514,41 @@ func TestNode_FromTar_Bad(t *testing.T) {
}
func TestNode_TarRoundTrip_Good(t *testing.T) {
n1 := New()
n1.AddData("a.txt", []byte("alpha"))
n1.AddData("b/c.txt", []byte("charlie"))
nodeTree1 := New()
nodeTree1.AddData("a.txt", []byte("alpha"))
nodeTree1.AddData("b/c.txt", []byte("charlie"))
tarball, err := n1.ToTar()
tarball, err := nodeTree1.ToTar()
require.NoError(t, err)
n2, err := FromTar(tarball)
nodeTree2, err := FromTar(tarball)
require.NoError(t, err)
data, err := n2.ReadFile("a.txt")
data, err := nodeTree2.ReadFile("a.txt")
require.NoError(t, err)
assert.Equal(t, []byte("alpha"), data)
data, err = n2.ReadFile("b/c.txt")
data, err = nodeTree2.ReadFile("b/c.txt")
require.NoError(t, err)
assert.Equal(t, []byte("charlie"), data)
}
func TestNode_FSInterface_Good(t *testing.T) {
n := New()
n.AddData("hello.txt", []byte("world"))
nodeTree := New()
nodeTree.AddData("hello.txt", []byte("world"))
var fsys fs.FS = n
var fsys fs.FS = nodeTree
file, err := fsys.Open("hello.txt")
require.NoError(t, err)
defer file.Close()
var statFS fs.StatFS = n
var statFS fs.StatFS = nodeTree
info, err := statFS.Stat("hello.txt")
require.NoError(t, err)
assert.Equal(t, "hello.txt", info.Name())
assert.Equal(t, int64(5), info.Size())
var readFS fs.ReadFileFS = n
var readFS fs.ReadFileFS = nodeTree
data, err := readFS.ReadFile("hello.txt")
require.NoError(t, err)
assert.Equal(t, []byte("world"), data)

View file

@ -119,6 +119,7 @@ func (medium *Medium) objectKey(filePath string) string {
return medium.prefix + clean
}
// Example: content, _ := medium.Read("reports/daily.txt")
func (medium *Medium) Read(filePath string) (string, error) {
key := medium.objectKey(filePath)
if key == "" {
@ -141,6 +142,7 @@ func (medium *Medium) Read(filePath string) (string, error) {
return string(data), nil
}
// Example: _ = medium.Write("reports/daily.txt", "done")
func (medium *Medium) Write(filePath, content string) error {
key := medium.objectKey(filePath)
if key == "" {
@ -184,6 +186,7 @@ func (medium *Medium) IsFile(filePath string) bool {
return err == nil
}
// Example: _ = medium.Delete("reports/daily.txt")
func (medium *Medium) Delete(filePath string) error {
key := medium.objectKey(filePath)
if key == "" {
@ -470,6 +473,7 @@ func (medium *Medium) Append(filePath string) (goio.WriteCloser, error) {
}, nil
}
// Example: reader, _ := medium.ReadStream("reports/daily.txt")
func (medium *Medium) ReadStream(filePath string) (goio.ReadCloser, error) {
key := medium.objectKey(filePath)
if key == "" {
@ -486,6 +490,7 @@ func (medium *Medium) ReadStream(filePath string) (goio.ReadCloser, error) {
return out.Body, nil
}
// Example: writer, _ := medium.WriteStream("reports/daily.txt")
func (medium *Medium) WriteStream(filePath string) (goio.WriteCloser, error) {
return medium.Create(filePath)
}

View file

@ -88,6 +88,7 @@ func normaliseEntryPath(filePath string) string {
return core.TrimPrefix(clean, "/")
}
// Example: content, _ := medium.Read("config/app.yaml")
func (medium *Medium) Read(filePath string) (string, error) {
key := normaliseEntryPath(filePath)
if key == "" {
@ -111,6 +112,7 @@ func (medium *Medium) Read(filePath string) (string, error) {
return string(content), nil
}
// Example: _ = medium.Write("config/app.yaml", "port: 8080")
func (medium *Medium) Write(filePath, content string) error {
return medium.WriteMode(filePath, content, 0644)
}
@ -151,6 +153,7 @@ func (medium *Medium) EnsureDir(filePath string) error {
return nil
}
// Example: isFile := medium.IsFile("config/app.yaml")
func (medium *Medium) IsFile(filePath string) bool {
key := normaliseEntryPath(filePath)
if key == "" {
@ -401,6 +404,7 @@ func (medium *Medium) List(filePath string) ([]fs.DirEntry, error) {
return entries, nil
}
// Example: info, _ := medium.Stat("config/app.yaml")
func (medium *Medium) Stat(filePath string) (fs.FileInfo, error) {
key := normaliseEntryPath(filePath)
if key == "" {
@ -431,6 +435,7 @@ func (medium *Medium) Stat(filePath string) (fs.FileInfo, error) {
}, nil
}
// Example: file, _ := medium.Open("config/app.yaml")
func (medium *Medium) Open(filePath string) (fs.File, error) {
key := normaliseEntryPath(filePath)
if key == "" {
@ -462,6 +467,7 @@ func (medium *Medium) Open(filePath string) (fs.File, error) {
}, nil
}
// Example: writer, _ := medium.Create("logs/app.log")
func (medium *Medium) Create(filePath string) (goio.WriteCloser, error) {
key := normaliseEntryPath(filePath)
if key == "" {
@ -473,6 +479,7 @@ func (medium *Medium) Create(filePath string) (goio.WriteCloser, error) {
}, nil
}
// Example: writer, _ := medium.Append("logs/app.log")
func (medium *Medium) Append(filePath string) (goio.WriteCloser, error) {
key := normaliseEntryPath(filePath)
if key == "" {
@ -494,6 +501,7 @@ func (medium *Medium) Append(filePath string) (goio.WriteCloser, error) {
}, nil
}
// Example: reader, _ := medium.ReadStream("logs/app.log")
func (medium *Medium) ReadStream(filePath string) (goio.ReadCloser, error) {
key := normaliseEntryPath(filePath)
if key == "" {
@ -518,10 +526,12 @@ func (medium *Medium) ReadStream(filePath string) (goio.ReadCloser, error) {
return goio.NopCloser(bytes.NewReader(content)), nil
}
// Example: writer, _ := medium.WriteStream("logs/app.log")
func (medium *Medium) WriteStream(filePath string) (goio.WriteCloser, error) {
return medium.Create(filePath)
}
// Example: exists := medium.Exists("config/app.yaml")
func (medium *Medium) Exists(filePath string) bool {
key := normaliseEntryPath(filePath)
if key == "" {
@ -538,6 +548,7 @@ func (medium *Medium) Exists(filePath string) bool {
return count > 0
}
// Example: isDirectory := medium.IsDir("config")
func (medium *Medium) IsDir(filePath string) bool {
key := normaliseEntryPath(filePath)
if key == "" {

View file

@ -11,10 +11,10 @@ import (
func newTestKeyValueMedium(t *testing.T) *Medium {
t.Helper()
m, err := NewMedium(Options{Path: ":memory:"})
medium, err := NewMedium(Options{Path: ":memory:"})
require.NoError(t, err)
t.Cleanup(func() { m.Close() })
return m
t.Cleanup(func() { medium.Close() })
return medium
}
func TestKeyValueMedium_ReadWrite_Good(t *testing.T) {

View file

@ -18,6 +18,7 @@ type Store struct {
database *sql.DB
}
// Example: keyValueStore, _ := store.New(store.Options{Path: ":memory:"})
type Options struct {
Path string
}

View file

@ -10,17 +10,17 @@ import (
func newTestStore(t *testing.T) *Store {
t.Helper()
s, err := New(Options{Path: ":memory:"})
keyValueStore, err := New(Options{Path: ":memory:"})
require.NoError(t, err)
t.Cleanup(func() {
require.NoError(t, s.Close())
require.NoError(t, keyValueStore.Close())
})
return s
return keyValueStore
}
func TestStore_New_Options_Good(t *testing.T) {
s := newTestStore(t)
assert.NotNil(t, s)
keyValueStore := newTestStore(t)
assert.NotNil(t, keyValueStore)
}
func TestStore_New_Options_Bad(t *testing.T) {
@ -29,86 +29,86 @@ func TestStore_New_Options_Bad(t *testing.T) {
}
func TestStore_SetGet_Good(t *testing.T) {
s := newTestStore(t)
keyValueStore := newTestStore(t)
err := s.Set("config", "theme", "dark")
err := keyValueStore.Set("config", "theme", "dark")
require.NoError(t, err)
val, err := s.Get("config", "theme")
val, err := keyValueStore.Get("config", "theme")
require.NoError(t, err)
assert.Equal(t, "dark", val)
}
func TestStore_Get_NotFound_Bad(t *testing.T) {
s := newTestStore(t)
keyValueStore := newTestStore(t)
_, err := s.Get("config", "missing")
_, err := keyValueStore.Get("config", "missing")
assert.ErrorIs(t, err, NotFoundError)
}
func TestStore_Delete_Good(t *testing.T) {
s := newTestStore(t)
keyValueStore := newTestStore(t)
_ = s.Set("config", "key", "val")
err := s.Delete("config", "key")
_ = keyValueStore.Set("config", "key", "val")
err := keyValueStore.Delete("config", "key")
require.NoError(t, err)
_, err = s.Get("config", "key")
_, err = keyValueStore.Get("config", "key")
assert.ErrorIs(t, err, NotFoundError)
}
func TestStore_Count_Good(t *testing.T) {
s := newTestStore(t)
keyValueStore := newTestStore(t)
_ = s.Set("group", "a", "1")
_ = s.Set("group", "b", "2")
_ = s.Set("other", "c", "3")
_ = keyValueStore.Set("group", "a", "1")
_ = keyValueStore.Set("group", "b", "2")
_ = keyValueStore.Set("other", "c", "3")
n, err := s.Count("group")
count, err := keyValueStore.Count("group")
require.NoError(t, err)
assert.Equal(t, 2, n)
assert.Equal(t, 2, count)
}
func TestStore_DeleteGroup_Good(t *testing.T) {
s := newTestStore(t)
keyValueStore := newTestStore(t)
_ = s.Set("group", "a", "1")
_ = s.Set("group", "b", "2")
err := s.DeleteGroup("group")
_ = keyValueStore.Set("group", "a", "1")
_ = keyValueStore.Set("group", "b", "2")
err := keyValueStore.DeleteGroup("group")
require.NoError(t, err)
n, _ := s.Count("group")
assert.Equal(t, 0, n)
count, _ := keyValueStore.Count("group")
assert.Equal(t, 0, count)
}
func TestStore_GetAll_Good(t *testing.T) {
s := newTestStore(t)
keyValueStore := newTestStore(t)
_ = s.Set("group", "a", "1")
_ = s.Set("group", "b", "2")
_ = s.Set("other", "c", "3")
_ = keyValueStore.Set("group", "a", "1")
_ = keyValueStore.Set("group", "b", "2")
_ = keyValueStore.Set("other", "c", "3")
all, err := s.GetAll("group")
all, err := keyValueStore.GetAll("group")
require.NoError(t, err)
assert.Equal(t, map[string]string{"a": "1", "b": "2"}, all)
}
func TestStore_GetAll_Empty_Good(t *testing.T) {
s := newTestStore(t)
keyValueStore := newTestStore(t)
all, err := s.GetAll("empty")
all, err := keyValueStore.GetAll("empty")
require.NoError(t, err)
assert.Empty(t, all)
}
func TestStore_Render_Good(t *testing.T) {
s := newTestStore(t)
keyValueStore := newTestStore(t)
_ = s.Set("user", "pool", "pool.lthn.io:3333")
_ = s.Set("user", "wallet", "iz...")
_ = keyValueStore.Set("user", "pool", "pool.lthn.io:3333")
_ = keyValueStore.Set("user", "wallet", "iz...")
tmpl := `{"pool":"{{ .pool }}","wallet":"{{ .wallet }}"}`
out, err := s.Render(tmpl, "user")
out, err := keyValueStore.Render(tmpl, "user")
require.NoError(t, err)
assert.Contains(t, out, "pool.lthn.io:3333")
assert.Contains(t, out, "iz...")