refactor(ax): trim test prose comments
Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
parent
1cc185cb35
commit
313b704f54
10 changed files with 12 additions and 294 deletions
|
|
@ -11,7 +11,6 @@ import (
|
|||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// Compile-time check: Medium implements io.Medium.
|
||||
var _ coreio.Medium = (*Medium)(nil)
|
||||
|
||||
func TestClient_ReadWrite_Good(t *testing.T) {
|
||||
|
|
@ -69,7 +68,7 @@ func TestClient_IsFile_Good(t *testing.T) {
|
|||
|
||||
assert.True(t, m.IsFile("file.go"))
|
||||
assert.False(t, m.IsFile("missing.go"))
|
||||
assert.False(t, m.IsFile("")) // empty path
|
||||
assert.False(t, m.IsFile(""))
|
||||
}
|
||||
|
||||
func TestClient_EnsureDir_Good(t *testing.T) {
|
||||
|
|
@ -96,10 +95,8 @@ func TestClient_Delete_Good(t *testing.T) {
|
|||
func TestClient_Delete_Bad(t *testing.T) {
|
||||
medium := New()
|
||||
|
||||
// Example: medium.Delete("ghost.txt")
|
||||
assert.Error(t, medium.Delete("ghost.txt"))
|
||||
|
||||
// Delete non-empty dir
|
||||
require.NoError(t, medium.Write("dir/file.txt", "content"))
|
||||
assert.Error(t, medium.Delete("dir"))
|
||||
}
|
||||
|
|
@ -257,7 +254,6 @@ func TestClient_Stat_Good(t *testing.T) {
|
|||
assert.Equal(t, int64(5), info.Size())
|
||||
assert.False(t, info.IsDir())
|
||||
|
||||
// Root stat
|
||||
info, err = m.Stat("")
|
||||
require.NoError(t, err)
|
||||
assert.True(t, info.IsDir())
|
||||
|
|
@ -280,7 +276,6 @@ func TestClient_Open_Good(t *testing.T) {
|
|||
func TestClient_CreateAppend_Good(t *testing.T) {
|
||||
m := New()
|
||||
|
||||
// Create
|
||||
w, err := m.Create("new.txt")
|
||||
require.NoError(t, err)
|
||||
w.Write([]byte("hello"))
|
||||
|
|
@ -290,7 +285,6 @@ func TestClient_CreateAppend_Good(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
assert.Equal(t, "hello", got)
|
||||
|
||||
// Append
|
||||
w, err = m.Append("new.txt")
|
||||
require.NoError(t, err)
|
||||
w.Write([]byte(" world"))
|
||||
|
|
@ -321,13 +315,11 @@ func TestClient_Append_ReadFailure_Bad(t *testing.T) {
|
|||
func TestClient_Streams_Good(t *testing.T) {
|
||||
m := New()
|
||||
|
||||
// WriteStream
|
||||
ws, err := m.WriteStream("stream.txt")
|
||||
require.NoError(t, err)
|
||||
ws.Write([]byte("streamed"))
|
||||
ws.Close()
|
||||
|
||||
// ReadStream
|
||||
rs, err := m.ReadStream("stream.txt")
|
||||
require.NoError(t, err)
|
||||
data, err := io.ReadAll(rs)
|
||||
|
|
@ -356,7 +348,6 @@ func TestClient_SnapshotRestore_Good(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
assert.NotEmpty(t, snap)
|
||||
|
||||
// Restore into a new Medium
|
||||
m2, err := FromTar(snap)
|
||||
require.NoError(t, err)
|
||||
|
||||
|
|
@ -377,11 +368,9 @@ func TestClient_Restore_Good(t *testing.T) {
|
|||
snap, err := m.Snapshot()
|
||||
require.NoError(t, err)
|
||||
|
||||
// Modify
|
||||
require.NoError(t, m.Write("original.txt", "after"))
|
||||
require.NoError(t, m.Write("extra.txt", "extra"))
|
||||
|
||||
// Restore to snapshot
|
||||
require.NoError(t, m.Restore(snap))
|
||||
|
||||
got, err := m.Read("original.txt")
|
||||
|
|
@ -399,7 +388,6 @@ func TestClient_DataNode_Good(t *testing.T) {
|
|||
dn := m.DataNode()
|
||||
assert.NotNil(t, dn)
|
||||
|
||||
// Verify we can use the DataNode directly
|
||||
f, err := dn.Open("test.txt")
|
||||
require.NoError(t, err)
|
||||
defer f.Close()
|
||||
|
|
@ -423,7 +411,7 @@ func TestClient_Overwrite_Good(t *testing.T) {
|
|||
func TestClient_Exists_Good(t *testing.T) {
|
||||
m := New()
|
||||
|
||||
assert.True(t, m.Exists("")) // root
|
||||
assert.True(t, m.Exists(""))
|
||||
assert.False(t, m.Exists("x"))
|
||||
|
||||
require.NoError(t, m.Write("x", "y"))
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ func TestClient_New_ResolvesRoot_Good(t *testing.T) {
|
|||
root := t.TempDir()
|
||||
m, err := New(root)
|
||||
assert.NoError(t, err)
|
||||
// Example: local.New("/srv/app") resolves macOS "/var" to "/private/var" before sandbox checks.
|
||||
resolved, err := resolveSymlinksPath(root)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, resolved, m.filesystemRoot)
|
||||
|
|
@ -24,29 +23,23 @@ func TestClient_New_ResolvesRoot_Good(t *testing.T) {
|
|||
func TestClient_Path_Sandboxed_Good(t *testing.T) {
|
||||
m := &Medium{filesystemRoot: "/home/user"}
|
||||
|
||||
// Normal paths
|
||||
assert.Equal(t, "/home/user/file.txt", m.sandboxedPath("file.txt"))
|
||||
assert.Equal(t, "/home/user/dir/file.txt", m.sandboxedPath("dir/file.txt"))
|
||||
|
||||
// Empty returns root
|
||||
assert.Equal(t, "/home/user", m.sandboxedPath(""))
|
||||
|
||||
// Traversal attempts get sanitised
|
||||
assert.Equal(t, "/home/user/file.txt", m.sandboxedPath("../file.txt"))
|
||||
assert.Equal(t, "/home/user/file.txt", m.sandboxedPath("dir/../file.txt"))
|
||||
|
||||
// Absolute paths are constrained to sandbox (no escape)
|
||||
assert.Equal(t, "/home/user/etc/passwd", m.sandboxedPath("/etc/passwd"))
|
||||
}
|
||||
|
||||
func TestClient_Path_RootFilesystem_Good(t *testing.T) {
|
||||
m := &Medium{filesystemRoot: "/"}
|
||||
|
||||
// When root is "/", absolute paths pass through
|
||||
assert.Equal(t, "/etc/passwd", m.sandboxedPath("/etc/passwd"))
|
||||
assert.Equal(t, "/home/user/file.txt", m.sandboxedPath("/home/user/file.txt"))
|
||||
|
||||
// Relative paths are relative to CWD when root is "/"
|
||||
cwd := currentWorkingDir()
|
||||
assert.Equal(t, core.Path(cwd, "file.txt"), m.sandboxedPath("file.txt"))
|
||||
}
|
||||
|
|
@ -55,7 +48,6 @@ func TestClient_ReadWrite_Basic_Good(t *testing.T) {
|
|||
root := t.TempDir()
|
||||
m, _ := New(root)
|
||||
|
||||
// Write and read back
|
||||
err := m.Write("test.txt", "hello")
|
||||
assert.NoError(t, err)
|
||||
|
||||
|
|
@ -63,7 +55,6 @@ func TestClient_ReadWrite_Basic_Good(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
assert.Equal(t, "hello", content)
|
||||
|
||||
// Write creates parent dirs
|
||||
err = m.Write("a/b/c.txt", "nested")
|
||||
assert.NoError(t, err)
|
||||
|
||||
|
|
@ -71,7 +62,6 @@ func TestClient_ReadWrite_Basic_Good(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
assert.Equal(t, "nested", content)
|
||||
|
||||
// Read nonexistent
|
||||
_, err = m.Read("nope.txt")
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
|
@ -228,7 +218,6 @@ func TestClient_Delete_Good(t *testing.T) {
|
|||
medium, err := New(testRoot)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Create and delete a file
|
||||
err = medium.Write("file.txt", "content")
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, medium.IsFile("file.txt"))
|
||||
|
|
@ -237,7 +226,6 @@ func TestClient_Delete_Good(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
assert.False(t, medium.IsFile("file.txt"))
|
||||
|
||||
// Create and delete an empty directory
|
||||
err = medium.EnsureDir("emptydir")
|
||||
assert.NoError(t, err)
|
||||
err = medium.Delete("emptydir")
|
||||
|
|
@ -251,11 +239,9 @@ func TestClient_Delete_NotEmpty_Bad(t *testing.T) {
|
|||
medium, err := New(testRoot)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Create a directory with a file
|
||||
err = medium.Write("mydir/file.txt", "content")
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Try to delete non-empty directory
|
||||
err = medium.Delete("mydir")
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
|
@ -266,13 +252,11 @@ func TestClient_DeleteAll_Good(t *testing.T) {
|
|||
medium, err := New(testRoot)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Create nested structure
|
||||
err = medium.Write("mydir/file1.txt", "content1")
|
||||
assert.NoError(t, err)
|
||||
err = medium.Write("mydir/subdir/file2.txt", "content2")
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Delete all
|
||||
err = medium.DeleteAll("mydir")
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, medium.Exists("mydir"))
|
||||
|
|
@ -286,7 +270,6 @@ func TestClient_Rename_Good(t *testing.T) {
|
|||
medium, err := New(testRoot)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Rename a file
|
||||
err = medium.Write("old.txt", "content")
|
||||
assert.NoError(t, err)
|
||||
err = medium.Rename("old.txt", "new.txt")
|
||||
|
|
@ -308,8 +291,6 @@ func TestClient_Rename_TraversalSanitised_Good(t *testing.T) {
|
|||
err = medium.Write("file.txt", "content")
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Traversal attempts are sanitised (.. becomes .), so this renames to "./escaped.txt"
|
||||
// which is just "escaped.txt" in the root
|
||||
err = medium.Rename("file.txt", "../escaped.txt")
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, medium.Exists("file.txt"))
|
||||
|
|
@ -322,7 +303,6 @@ func TestClient_List_Good(t *testing.T) {
|
|||
medium, err := New(testRoot)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Create some files and directories
|
||||
err = medium.Write("file1.txt", "content1")
|
||||
assert.NoError(t, err)
|
||||
err = medium.Write("file2.txt", "content2")
|
||||
|
|
@ -330,7 +310,6 @@ func TestClient_List_Good(t *testing.T) {
|
|||
err = medium.EnsureDir("subdir")
|
||||
assert.NoError(t, err)
|
||||
|
||||
// List root
|
||||
entries, err := medium.List(".")
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, entries, 3)
|
||||
|
|
@ -350,7 +329,6 @@ func TestClient_Stat_Good(t *testing.T) {
|
|||
medium, err := New(testRoot)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Stat a file
|
||||
err = medium.Write("file.txt", "hello world")
|
||||
assert.NoError(t, err)
|
||||
info, err := medium.Stat("file.txt")
|
||||
|
|
@ -359,7 +337,6 @@ func TestClient_Stat_Good(t *testing.T) {
|
|||
assert.Equal(t, int64(11), info.Size())
|
||||
assert.False(t, info.IsDir())
|
||||
|
||||
// Stat a directory
|
||||
err = medium.EnsureDir("mydir")
|
||||
assert.NoError(t, err)
|
||||
info, err = medium.Stat("mydir")
|
||||
|
|
@ -414,7 +391,6 @@ func TestClient_ReadStream_Basic_Good(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
defer reader.Close()
|
||||
|
||||
// Read only first 9 bytes
|
||||
limitReader := io.LimitReader(reader, 9)
|
||||
data, err := io.ReadAll(limitReader)
|
||||
assert.NoError(t, err)
|
||||
|
|
@ -441,15 +417,12 @@ func TestClient_WriteStream_Basic_Good(t *testing.T) {
|
|||
func TestClient_Path_TraversalSandbox_Good(t *testing.T) {
|
||||
m := &Medium{filesystemRoot: "/sandbox"}
|
||||
|
||||
// Multiple levels of traversal
|
||||
assert.Equal(t, "/sandbox/file.txt", m.sandboxedPath("../../../file.txt"))
|
||||
assert.Equal(t, "/sandbox/target", m.sandboxedPath("dir/../../target"))
|
||||
|
||||
// Traversal with hidden files
|
||||
assert.Equal(t, "/sandbox/.ssh/id_rsa", m.sandboxedPath(".ssh/id_rsa"))
|
||||
assert.Equal(t, "/sandbox/id_rsa", m.sandboxedPath(".ssh/../id_rsa"))
|
||||
|
||||
// Null bytes (Go's filepath.Clean handles them, but good to check)
|
||||
assert.Equal(t, "/sandbox/file\x00.txt", m.sandboxedPath("file\x00.txt"))
|
||||
}
|
||||
|
||||
|
|
@ -458,7 +431,6 @@ func TestClient_ValidatePath_SymlinkEscape_Bad(t *testing.T) {
|
|||
m, err := New(root)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Create a directory outside the sandbox
|
||||
outside := t.TempDir()
|
||||
outsideFile := core.Path(outside, "secret.txt")
|
||||
outsideMedium, err := New("/")
|
||||
|
|
@ -466,22 +438,17 @@ func TestClient_ValidatePath_SymlinkEscape_Bad(t *testing.T) {
|
|||
err = outsideMedium.Write(outsideFile, "secret")
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Test 1: Simple traversal
|
||||
_, err = m.validatePath("../outside.txt")
|
||||
assert.NoError(t, err) // sandboxedPath sanitises to root, so this shouldn't escape
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Test 2: Symlink escape
|
||||
// Create a symlink inside the sandbox pointing outside
|
||||
linkPath := core.Path(root, "evil_link")
|
||||
err = syscall.Symlink(outside, linkPath)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Try to access a file through the symlink
|
||||
_, err = m.validatePath("evil_link/secret.txt")
|
||||
assert.Error(t, err)
|
||||
assert.ErrorIs(t, err, fs.ErrPermission)
|
||||
|
||||
// Test 3: Nested symlink escape
|
||||
err = m.EnsureDir("inner")
|
||||
assert.NoError(t, err)
|
||||
innerDir := core.Path(root, "inner")
|
||||
|
|
@ -499,24 +466,19 @@ func TestClient_EmptyPaths_Good(t *testing.T) {
|
|||
m, err := New(root)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Read empty path (should fail as it's a directory)
|
||||
_, err = m.Read("")
|
||||
assert.Error(t, err)
|
||||
|
||||
// Write empty path (should fail as it's a directory)
|
||||
err = m.Write("", "content")
|
||||
assert.Error(t, err)
|
||||
|
||||
// EnsureDir empty path (should be ok, it's just the root)
|
||||
err = m.EnsureDir("")
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.False(t, m.IsDir(""))
|
||||
|
||||
// Exists empty path (root exists)
|
||||
assert.True(t, m.Exists(""))
|
||||
|
||||
// List empty path (lists root)
|
||||
entries, err := m.List("")
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, entries)
|
||||
|
|
|
|||
|
|
@ -10,8 +10,6 @@ import (
|
|||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// --- MemoryMedium Tests ---
|
||||
|
||||
func TestMemoryMedium_NewMemoryMedium_Good(t *testing.T) {
|
||||
medium := NewMemoryMedium()
|
||||
assert.NotNil(t, medium)
|
||||
|
|
@ -66,7 +64,6 @@ func TestClient_MockMedium_Write_Good(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
assert.Equal(t, "content", m.files["test.txt"])
|
||||
|
||||
// Overwrite existing file
|
||||
err = m.Write("test.txt", "new content")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "new content", m.files["test.txt"])
|
||||
|
|
@ -296,8 +293,6 @@ func TestClient_MockMedium_StreamAndFSHelpers_Good(t *testing.T) {
|
|||
assert.Equal(t, "stream output", m.files["streamed.txt"])
|
||||
}
|
||||
|
||||
// --- Wrapper Function Tests ---
|
||||
|
||||
func TestClient_Read_Good(t *testing.T) {
|
||||
m := NewMockMedium()
|
||||
m.files["test.txt"] = "hello"
|
||||
|
|
@ -367,7 +362,6 @@ func TestClient_Copy_Good(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
assert.Equal(t, "hello", dest.files["test.txt"])
|
||||
|
||||
// Copy to different path
|
||||
source.files["original.txt"] = "content"
|
||||
err = Copy(source, "original.txt", dest, "copied.txt")
|
||||
assert.NoError(t, err)
|
||||
|
|
@ -381,13 +375,9 @@ func TestClient_Copy_Bad(t *testing.T) {
|
|||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
// --- Local Global Tests ---
|
||||
|
||||
func TestClient_LocalGlobal_Good(t *testing.T) {
|
||||
// io.Local should be initialised by init()
|
||||
assert.NotNil(t, Local, "io.Local should be initialised")
|
||||
|
||||
// Should be able to use it as a Medium
|
||||
var m = Local
|
||||
assert.NotNil(t, m)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,20 +14,12 @@ import (
|
|||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// New
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// AddData
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
func TestNode_AddData_Good(t *testing.T) {
|
||||
n := New()
|
||||
n.AddData("foo.txt", []byte("foo"))
|
||||
|
|
@ -44,11 +36,9 @@ func TestNode_AddData_Good(t *testing.T) {
|
|||
func TestNode_AddData_Bad(t *testing.T) {
|
||||
n := New()
|
||||
|
||||
// Empty name is silently ignored.
|
||||
n.AddData("", []byte("data"))
|
||||
assert.Empty(t, n.files, "empty name must not be stored")
|
||||
|
||||
// Directory entry (trailing slash) is silently ignored.
|
||||
n.AddData("dir/", nil)
|
||||
assert.Empty(t, n.files, "directory entry must not be stored")
|
||||
}
|
||||
|
|
@ -71,10 +61,6 @@ func TestNode_AddData_EdgeCases_Good(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Open
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
func TestNode_Open_Good(t *testing.T) {
|
||||
n := New()
|
||||
n.AddData("foo.txt", []byte("foo"))
|
||||
|
|
@ -100,12 +86,10 @@ func TestNode_Open_Directory_Good(t *testing.T) {
|
|||
n := New()
|
||||
n.AddData("bar/baz.txt", []byte("baz"))
|
||||
|
||||
// Opening a directory should succeed.
|
||||
file, err := n.Open("bar")
|
||||
require.NoError(t, err)
|
||||
defer file.Close()
|
||||
|
||||
// Reading from a directory should fail.
|
||||
_, err = file.Read(make([]byte, 1))
|
||||
require.Error(t, err)
|
||||
|
||||
|
|
@ -114,23 +98,17 @@ func TestNode_Open_Directory_Good(t *testing.T) {
|
|||
assert.Equal(t, fs.ErrInvalid, pathErr.Err)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Stat
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
func TestNode_Stat_Good(t *testing.T) {
|
||||
n := New()
|
||||
n.AddData("foo.txt", []byte("foo"))
|
||||
n.AddData("bar/baz.txt", []byte("baz"))
|
||||
|
||||
// File stat.
|
||||
info, err := n.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())
|
||||
|
||||
// Directory stat.
|
||||
dirInfo, err := n.Stat("bar")
|
||||
require.NoError(t, err)
|
||||
assert.True(t, dirInfo.IsDir())
|
||||
|
|
@ -148,17 +126,12 @@ func TestNode_Stat_RootDirectory_Good(t *testing.T) {
|
|||
n := New()
|
||||
n.AddData("foo.txt", []byte("foo"))
|
||||
|
||||
// Root directory.
|
||||
info, err := n.Stat(".")
|
||||
require.NoError(t, err)
|
||||
assert.True(t, info.IsDir())
|
||||
assert.Equal(t, ".", info.Name())
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ReadFile
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
func TestNode_ReadFile_Good(t *testing.T) {
|
||||
n := New()
|
||||
n.AddData("hello.txt", []byte("hello world"))
|
||||
|
|
@ -179,7 +152,6 @@ func TestNode_ReadFile_ReturnsCopy_Good(t *testing.T) {
|
|||
n := New()
|
||||
n.AddData("data.bin", []byte("original"))
|
||||
|
||||
// Returned slice must be a copy — mutating it must not affect internal state.
|
||||
data, err := n.ReadFile("data.bin")
|
||||
require.NoError(t, err)
|
||||
data[0] = 'X'
|
||||
|
|
@ -189,22 +161,16 @@ func TestNode_ReadFile_ReturnsCopy_Good(t *testing.T) {
|
|||
assert.Equal(t, []byte("original"), data2, "ReadFile must return an independent copy")
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ReadDir
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
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"))
|
||||
|
||||
// Root.
|
||||
entries, err := n.ReadDir(".")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, []string{"bar", "foo.txt"}, sortedNames(entries))
|
||||
|
||||
// Subdirectory.
|
||||
barEntries, err := n.ReadDir("bar")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, []string{"baz.txt", "qux.txt"}, sortedNames(barEntries))
|
||||
|
|
@ -214,7 +180,6 @@ func TestNode_ReadDir_Bad(t *testing.T) {
|
|||
n := New()
|
||||
n.AddData("foo.txt", []byte("foo"))
|
||||
|
||||
// Reading a file as a directory should fail.
|
||||
_, err := n.ReadDir("foo.txt")
|
||||
require.Error(t, err)
|
||||
var pathErr *fs.PathError
|
||||
|
|
@ -225,17 +190,13 @@ 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) // Ignored by AddData.
|
||||
n.AddData("empty_dir/", nil)
|
||||
|
||||
entries, err := n.ReadDir(".")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, []string{"bar"}, sortedNames(entries))
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Exists
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
func TestNode_Exists_Good(t *testing.T) {
|
||||
n := New()
|
||||
n.AddData("foo.txt", []byte("foo"))
|
||||
|
|
@ -258,10 +219,6 @@ func TestNode_Exists_RootAndEmptyPath_Good(t *testing.T) {
|
|||
assert.True(t, n.Exists(""), "empty path (root) must exist")
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Walk
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
func TestNode_Walk_Default_Good(t *testing.T) {
|
||||
n := New()
|
||||
n.AddData("foo.txt", []byte("foo"))
|
||||
|
|
@ -298,7 +255,6 @@ func TestNode_Walk_CallbackError_Good(t *testing.T) {
|
|||
n.AddData("a/b.txt", []byte("b"))
|
||||
n.AddData("a/c.txt", []byte("c"))
|
||||
|
||||
// Stop walk early with a custom error.
|
||||
walkErr := core.NewError("stop walking")
|
||||
var paths []string
|
||||
err := n.Walk(".", func(p string, d fs.DirEntry, err error) error {
|
||||
|
|
@ -357,10 +313,6 @@ func TestNode_Walk_Good(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// CopyFile
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
func TestNode_CopyFile_Good(t *testing.T) {
|
||||
n := New()
|
||||
n.AddData("foo.txt", []byte("foo"))
|
||||
|
|
@ -378,11 +330,9 @@ func TestNode_CopyFile_Bad(t *testing.T) {
|
|||
n := New()
|
||||
tmpfile := core.Path(t.TempDir(), "test.txt")
|
||||
|
||||
// Source does not exist.
|
||||
err := n.CopyFile("nonexistent.txt", tmpfile, 0644)
|
||||
assert.Error(t, err)
|
||||
|
||||
// Destination not writable.
|
||||
n.AddData("foo.txt", []byte("foo"))
|
||||
err = n.CopyFile("foo.txt", "/nonexistent_dir/test.txt", 0644)
|
||||
assert.Error(t, err)
|
||||
|
|
@ -393,7 +343,6 @@ func TestNode_CopyFile_DirectorySource_Bad(t *testing.T) {
|
|||
n.AddData("bar/baz.txt", []byte("baz"))
|
||||
tmpfile := core.Path(t.TempDir(), "test.txt")
|
||||
|
||||
// Attempting to copy a directory should fail.
|
||||
err := n.CopyFile("bar", tmpfile, 0644)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
|
@ -505,10 +454,6 @@ func TestNode_MediumFacade_Good(t *testing.T) {
|
|||
assert.False(t, n.Exists("docs"))
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ToTar / FromTar
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
func TestNode_ToTar_Good(t *testing.T) {
|
||||
n := New()
|
||||
n.AddData("foo.txt", []byte("foo"))
|
||||
|
|
@ -518,7 +463,6 @@ func TestNode_ToTar_Good(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
require.NotEmpty(t, tarball)
|
||||
|
||||
// Verify tar content.
|
||||
tr := tar.NewReader(bytes.NewReader(tarball))
|
||||
files := make(map[string]string)
|
||||
for {
|
||||
|
|
@ -564,7 +508,6 @@ func TestNode_FromTar_Good(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestNode_FromTar_Bad(t *testing.T) {
|
||||
// Truncated data that cannot be a valid tar.
|
||||
truncated := make([]byte, 100)
|
||||
_, err := FromTar(truncated)
|
||||
assert.Error(t, err, "truncated data should produce an error")
|
||||
|
|
@ -581,7 +524,6 @@ func TestNode_TarRoundTrip_Good(t *testing.T) {
|
|||
n2, err := FromTar(tarball)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify n2 matches n1.
|
||||
data, err := n2.ReadFile("a.txt")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, []byte("alpha"), data)
|
||||
|
|
@ -591,38 +533,27 @@ func TestNode_TarRoundTrip_Good(t *testing.T) {
|
|||
assert.Equal(t, []byte("charlie"), data)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// fs.FS interface compliance
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
func TestNode_FSInterface_Good(t *testing.T) {
|
||||
n := New()
|
||||
n.AddData("hello.txt", []byte("world"))
|
||||
|
||||
// fs.FS
|
||||
var fsys fs.FS = n
|
||||
file, err := fsys.Open("hello.txt")
|
||||
require.NoError(t, err)
|
||||
defer file.Close()
|
||||
|
||||
// fs.StatFS
|
||||
var statFS fs.StatFS = n
|
||||
info, err := statFS.Stat("hello.txt")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "hello.txt", info.Name())
|
||||
assert.Equal(t, int64(5), info.Size())
|
||||
|
||||
// fs.ReadFileFS
|
||||
var readFS fs.ReadFileFS = n
|
||||
data, err := readFS.ReadFile("hello.txt")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, []byte("world"), data)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Helpers
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
func sortedNames(entries []fs.DirEntry) []string {
|
||||
var names []string
|
||||
for _, e := range entries {
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ import (
|
|||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// mockS3 is an in-memory mock implementing the Client interface.
|
||||
type mockS3 struct {
|
||||
mu sync.RWMutex
|
||||
objects map[string][]byte
|
||||
|
|
@ -124,7 +123,6 @@ func (m *mockS3) ListObjectsV2(_ context.Context, params *awss3.ListObjectsV2Inp
|
|||
maxKeys = *params.MaxKeys
|
||||
}
|
||||
|
||||
// Collect all matching keys sorted
|
||||
var allKeys []string
|
||||
for k := range m.objects {
|
||||
if core.HasPrefix(k, prefix) {
|
||||
|
|
@ -142,7 +140,6 @@ func (m *mockS3) ListObjectsV2(_ context.Context, params *awss3.ListObjectsV2Inp
|
|||
if delimiter != "" {
|
||||
parts := core.SplitN(rest, delimiter, 2)
|
||||
if len(parts) == 2 {
|
||||
// This key has a delimiter after the prefix -> common prefix
|
||||
cp := core.Concat(prefix, parts[0], delimiter)
|
||||
commonPrefixes[cp] = true
|
||||
continue
|
||||
|
|
@ -163,7 +160,6 @@ func (m *mockS3) ListObjectsV2(_ context.Context, params *awss3.ListObjectsV2Inp
|
|||
}
|
||||
|
||||
var cpSlice []types.CommonPrefix
|
||||
// Sort common prefixes for deterministic output
|
||||
var cpKeys []string
|
||||
for cp := range commonPrefixes {
|
||||
cpKeys = append(cpKeys, cp)
|
||||
|
|
@ -184,7 +180,6 @@ func (m *mockS3) CopyObject(_ context.Context, params *awss3.CopyObjectInput, _
|
|||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
// CopySource is "bucket/key"
|
||||
source := aws.ToString(params.CopySource)
|
||||
parts := core.SplitN(source, "/", 2)
|
||||
if len(parts) != 2 {
|
||||
|
|
@ -204,8 +199,6 @@ func (m *mockS3) CopyObject(_ context.Context, params *awss3.CopyObjectInput, _
|
|||
return &awss3.CopyObjectOutput{}, nil
|
||||
}
|
||||
|
||||
// --- Helper ---
|
||||
|
||||
func newTestMedium(t *testing.T) (*Medium, *mockS3) {
|
||||
t.Helper()
|
||||
mock := newMockS3()
|
||||
|
|
@ -214,8 +207,6 @@ func newTestMedium(t *testing.T) (*Medium, *mockS3) {
|
|||
return m, mock
|
||||
}
|
||||
|
||||
// --- Tests ---
|
||||
|
||||
func TestS3_New_Good(t *testing.T) {
|
||||
mock := newMockS3()
|
||||
m, err := New(Options{Bucket: "my-bucket", Client: mock})
|
||||
|
|
@ -242,7 +233,6 @@ func TestS3_New_Options_Good(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
assert.Equal(t, "data/", m.prefix)
|
||||
|
||||
// Prefix without trailing slash gets one added
|
||||
m2, err := New(Options{Bucket: "bucket", Client: mock, Prefix: "data"})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "data/", m2.prefix)
|
||||
|
|
@ -284,7 +274,6 @@ func TestS3_ReadWrite_Prefix_Good(t *testing.T) {
|
|||
err = m.Write("file.txt", "data")
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify the key has the prefix
|
||||
_, ok := mock.objects["pfx/file.txt"]
|
||||
assert.True(t, ok, "object should be stored with prefix")
|
||||
|
||||
|
|
@ -295,7 +284,6 @@ func TestS3_ReadWrite_Prefix_Good(t *testing.T) {
|
|||
|
||||
func TestS3_EnsureDir_Good(t *testing.T) {
|
||||
medium, _ := newTestMedium(t)
|
||||
// Example: err := medium.EnsureDir("any/path")
|
||||
err := medium.EnsureDir("any/path")
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
|
@ -343,7 +331,6 @@ func TestS3_Delete_EmptyPath_Bad(t *testing.T) {
|
|||
func TestS3_DeleteAll_Good(t *testing.T) {
|
||||
m, _ := newTestMedium(t)
|
||||
|
||||
// Create nested structure
|
||||
require.NoError(t, m.Write("dir/file1.txt", "a"))
|
||||
require.NoError(t, m.Write("dir/sub/file2.txt", "b"))
|
||||
require.NoError(t, m.Write("other.txt", "c"))
|
||||
|
|
@ -442,7 +429,6 @@ func TestS3_List_Good(t *testing.T) {
|
|||
assert.True(t, names["sub"], "should list sub directory")
|
||||
assert.Len(t, entries, 3)
|
||||
|
||||
// Check that sub is a directory
|
||||
for _, e := range entries {
|
||||
if e.Name() == "sub" {
|
||||
assert.True(t, e.IsDir())
|
||||
|
|
@ -622,7 +608,6 @@ func TestS3_Exists_DirectoryPrefix_Good(t *testing.T) {
|
|||
m, _ := newTestMedium(t)
|
||||
|
||||
require.NoError(t, m.Write("dir/file.txt", "content"))
|
||||
// "dir" should exist as a directory prefix
|
||||
assert.True(t, m.Exists("dir"))
|
||||
}
|
||||
|
||||
|
|
@ -640,7 +625,6 @@ func TestS3_IsDir_Good(t *testing.T) {
|
|||
func TestS3_ObjectKey_Good(t *testing.T) {
|
||||
mock := newMockS3()
|
||||
|
||||
// No prefix
|
||||
m, _ := New(Options{Bucket: "bucket", Client: mock})
|
||||
assert.Equal(t, "file.txt", m.objectKey("file.txt"))
|
||||
assert.Equal(t, "dir/file.txt", m.objectKey("dir/file.txt"))
|
||||
|
|
@ -648,21 +632,17 @@ func TestS3_ObjectKey_Good(t *testing.T) {
|
|||
assert.Equal(t, "file.txt", m.objectKey("/file.txt"))
|
||||
assert.Equal(t, "file.txt", m.objectKey("../file.txt"))
|
||||
|
||||
// With prefix
|
||||
m2, _ := New(Options{Bucket: "bucket", Client: mock, Prefix: "pfx"})
|
||||
assert.Equal(t, "pfx/file.txt", m2.objectKey("file.txt"))
|
||||
assert.Equal(t, "pfx/dir/file.txt", m2.objectKey("dir/file.txt"))
|
||||
assert.Equal(t, "pfx/", m2.objectKey(""))
|
||||
}
|
||||
|
||||
// Compile-time check: Medium satisfies the io.Medium interface.
|
||||
func TestS3_InterfaceCompliance(t *testing.T) {
|
||||
mock := newMockS3()
|
||||
m, err := New(Options{Bucket: "bucket", Client: mock})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify all methods exist by calling them in a way that
|
||||
// proves compile-time satisfaction of the interface.
|
||||
var _ interface {
|
||||
Read(string) (string, error)
|
||||
Write(string, string) error
|
||||
|
|
|
|||
|
|
@ -11,8 +11,6 @@ import (
|
|||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// ── XORObfuscator ──────────────────────────────────────────────────
|
||||
|
||||
func TestCryptoSigil_XORObfuscator_RoundTrip_Good(t *testing.T) {
|
||||
ob := &XORObfuscator{}
|
||||
data := []byte("the axioms are in the weights")
|
||||
|
|
@ -47,7 +45,6 @@ func TestCryptoSigil_XORObfuscator_Deterministic_Good(t *testing.T) {
|
|||
|
||||
func TestCryptoSigil_XORObfuscator_LargeData_Good(t *testing.T) {
|
||||
ob := &XORObfuscator{}
|
||||
// Larger than one SHA-256 block (32 bytes) to test multi-block key stream.
|
||||
data := make([]byte, 256)
|
||||
for i := range data {
|
||||
data[i] = byte(i)
|
||||
|
|
@ -73,13 +70,10 @@ func TestCryptoSigil_XORObfuscator_SymmetricProperty_Good(t *testing.T) {
|
|||
data := []byte("XOR is its own inverse")
|
||||
entropy := []byte("nonce")
|
||||
|
||||
// XOR is symmetric: Obfuscate(Obfuscate(x)) == x
|
||||
double := ob.Obfuscate(ob.Obfuscate(data, entropy), entropy)
|
||||
assert.Equal(t, data, double)
|
||||
}
|
||||
|
||||
// ── ShuffleMaskObfuscator ──────────────────────────────────────────
|
||||
|
||||
func TestCryptoSigil_ShuffleMaskObfuscator_RoundTrip_Good(t *testing.T) {
|
||||
ob := &ShuffleMaskObfuscator{}
|
||||
data := []byte("shuffle and mask protect patterns")
|
||||
|
|
@ -144,8 +138,6 @@ func TestCryptoSigil_ShuffleMaskObfuscator_SingleByte_Good(t *testing.T) {
|
|||
assert.Equal(t, data, restored)
|
||||
}
|
||||
|
||||
// ── NewChaChaPolySigil ─────────────────────────────────────────────
|
||||
|
||||
func TestCryptoSigil_NewChaChaPolySigil_Good(t *testing.T) {
|
||||
key := make([]byte, 32)
|
||||
_, _ = rand.Read(key)
|
||||
|
|
@ -166,7 +158,6 @@ func TestCryptoSigil_NewChaChaPolySigil_KeyIsCopied_Good(t *testing.T) {
|
|||
s, err := NewChaChaPolySigil(key, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Mutating the original key should not affect the sigil.
|
||||
key[0] ^= 0xFF
|
||||
assert.Equal(t, original, s.Key)
|
||||
}
|
||||
|
|
@ -186,8 +177,6 @@ func TestCryptoSigil_NewChaChaPolySigil_EmptyKey_Bad(t *testing.T) {
|
|||
assert.ErrorIs(t, err, InvalidKeyError)
|
||||
}
|
||||
|
||||
// ── NewChaChaPolySigil Custom Obfuscator ───────────────────────────
|
||||
|
||||
func TestCryptoSigil_NewChaChaPolySigil_CustomObfuscator_Good(t *testing.T) {
|
||||
key := make([]byte, 32)
|
||||
_, _ = rand.Read(key)
|
||||
|
|
@ -204,7 +193,6 @@ func TestCryptoSigil_NewChaChaPolySigil_CustomObfuscatorNil_Good(t *testing.T) {
|
|||
|
||||
s, err := NewChaChaPolySigil(key, nil)
|
||||
require.NoError(t, err)
|
||||
// Falls back to default XORObfuscator.
|
||||
assert.IsType(t, &XORObfuscator{}, s.Obfuscator)
|
||||
}
|
||||
|
||||
|
|
@ -213,8 +201,6 @@ func TestCryptoSigil_NewChaChaPolySigil_CustomObfuscator_InvalidKey_Bad(t *testi
|
|||
assert.ErrorIs(t, err, InvalidKeyError)
|
||||
}
|
||||
|
||||
// ── ChaChaPolySigil In/Out (encrypt/decrypt) ───────────────────────
|
||||
|
||||
func TestCryptoSigil_ChaChaPolySigil_RoundTrip_Good(t *testing.T) {
|
||||
key := make([]byte, 32)
|
||||
_, _ = rand.Read(key)
|
||||
|
|
@ -226,7 +212,7 @@ func TestCryptoSigil_ChaChaPolySigil_RoundTrip_Good(t *testing.T) {
|
|||
ciphertext, err := s.In(plaintext)
|
||||
require.NoError(t, err)
|
||||
assert.NotEqual(t, plaintext, ciphertext)
|
||||
assert.Greater(t, len(ciphertext), len(plaintext)) // nonce + tag overhead
|
||||
assert.Greater(t, len(ciphertext), len(plaintext))
|
||||
|
||||
decrypted, err := s.Out(ciphertext)
|
||||
require.NoError(t, err)
|
||||
|
|
@ -274,7 +260,7 @@ func TestCryptoSigil_ChaChaPolySigil_EmptyPlaintext_Good(t *testing.T) {
|
|||
|
||||
ciphertext, err := s.In([]byte{})
|
||||
require.NoError(t, err)
|
||||
assert.NotEmpty(t, ciphertext) // Has nonce + tag even for empty plaintext.
|
||||
assert.NotEmpty(t, ciphertext)
|
||||
|
||||
decrypted, err := s.Out(ciphertext)
|
||||
require.NoError(t, err)
|
||||
|
|
@ -292,7 +278,6 @@ func TestCryptoSigil_ChaChaPolySigil_DifferentCiphertextsPerCall_Good(t *testing
|
|||
ct1, _ := s.In(plaintext)
|
||||
ct2, _ := s.In(plaintext)
|
||||
|
||||
// Different nonces → different ciphertexts.
|
||||
assert.NotEqual(t, ct1, ct2)
|
||||
}
|
||||
|
||||
|
|
@ -338,14 +323,12 @@ func TestCryptoSigil_ChaChaPolySigil_TamperedCiphertext_Bad(t *testing.T) {
|
|||
s, _ := NewChaChaPolySigil(key, nil)
|
||||
ciphertext, _ := s.In([]byte("authentic data"))
|
||||
|
||||
// Flip a bit in the ciphertext body (after nonce).
|
||||
ciphertext[30] ^= 0xFF
|
||||
|
||||
_, err := s.Out(ciphertext)
|
||||
assert.ErrorIs(t, err, DecryptionFailedError)
|
||||
}
|
||||
|
||||
// failReader returns an error on read — for testing nonce generation failure.
|
||||
type failReader struct{}
|
||||
|
||||
func (f *failReader) Read([]byte) (int, error) {
|
||||
|
|
@ -363,14 +346,12 @@ func TestCryptoSigil_ChaChaPolySigil_RandomReaderFailure_Bad(t *testing.T) {
|
|||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
// ── ChaChaPolySigil without obfuscator ─────────────────────────────
|
||||
|
||||
func TestCryptoSigil_ChaChaPolySigil_NoObfuscator_Good(t *testing.T) {
|
||||
key := make([]byte, 32)
|
||||
_, _ = rand.Read(key)
|
||||
|
||||
s, _ := NewChaChaPolySigil(key, nil)
|
||||
s.Obfuscator = nil // Disable pre-obfuscation.
|
||||
s.Obfuscator = nil
|
||||
|
||||
plaintext := []byte("raw encryption without pre-obfuscation")
|
||||
ciphertext, err := s.In(plaintext)
|
||||
|
|
@ -381,8 +362,6 @@ func TestCryptoSigil_ChaChaPolySigil_NoObfuscator_Good(t *testing.T) {
|
|||
assert.Equal(t, plaintext, decrypted)
|
||||
}
|
||||
|
||||
// ── GetNonceFromCiphertext ─────────────────────────────────────────
|
||||
|
||||
func TestCryptoSigil_GetNonceFromCiphertext_Good(t *testing.T) {
|
||||
key := make([]byte, 32)
|
||||
_, _ = rand.Read(key)
|
||||
|
|
@ -392,9 +371,8 @@ func TestCryptoSigil_GetNonceFromCiphertext_Good(t *testing.T) {
|
|||
|
||||
nonce, err := GetNonceFromCiphertext(ciphertext)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, nonce, 24) // XChaCha20 nonce is 24 bytes.
|
||||
assert.Len(t, nonce, 24)
|
||||
|
||||
// Nonce should match the prefix of the ciphertext.
|
||||
assert.Equal(t, ciphertext[:24], nonce)
|
||||
}
|
||||
|
||||
|
|
@ -409,7 +387,6 @@ func TestCryptoSigil_GetNonceFromCiphertext_NonceCopied_Good(t *testing.T) {
|
|||
original := make([]byte, len(nonce))
|
||||
copy(original, nonce)
|
||||
|
||||
// Mutating the nonce should not affect the ciphertext.
|
||||
nonce[0] ^= 0xFF
|
||||
assert.Equal(t, original, ciphertext[:24])
|
||||
}
|
||||
|
|
@ -424,8 +401,6 @@ func TestCryptoSigil_GetNonceFromCiphertext_Empty_Bad(t *testing.T) {
|
|||
assert.ErrorIs(t, err, CiphertextTooShortError)
|
||||
}
|
||||
|
||||
// ── ChaChaPolySigil in Transmute pipeline ──────────────────────────
|
||||
|
||||
func TestCryptoSigil_ChaChaPolySigil_InTransmutePipeline_Good(t *testing.T) {
|
||||
key := make([]byte, 32)
|
||||
_, _ = rand.Read(key)
|
||||
|
|
@ -439,7 +414,6 @@ func TestCryptoSigil_ChaChaPolySigil_InTransmutePipeline_Good(t *testing.T) {
|
|||
encoded, err := Transmute(plaintext, chain)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Result should be hex-encoded ciphertext.
|
||||
assert.True(t, isHex(encoded))
|
||||
|
||||
decoded, err := Untransmute(encoded, chain)
|
||||
|
|
@ -456,8 +430,6 @@ func isHex(data []byte) bool {
|
|||
return len(data) > 0
|
||||
}
|
||||
|
||||
// ── Transmute error propagation ────────────────────────────────────
|
||||
|
||||
type failSigil struct{}
|
||||
|
||||
func (f *failSigil) In([]byte) ([]byte, error) { return nil, core.NewError("fail in") }
|
||||
|
|
@ -475,24 +447,18 @@ func TestCryptoSigil_Untransmute_ErrorPropagation_Bad(t *testing.T) {
|
|||
assert.Contains(t, err.Error(), "fail out")
|
||||
}
|
||||
|
||||
// ── GzipSigil with custom output writer (edge case) ───────────────
|
||||
|
||||
func TestCryptoSigil_GzipSigil_CustomOutputWriter_Good(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
s := &GzipSigil{outputWriter: &buf}
|
||||
|
||||
// With a custom output writer, compressed data goes to buf, returned bytes will be empty
|
||||
// because the internal buffer 'b' is unused when s.outputWriter is set.
|
||||
_, err := s.In([]byte("test data"))
|
||||
require.NoError(t, err)
|
||||
assert.Greater(t, buf.Len(), 0)
|
||||
}
|
||||
|
||||
// ── deriveKeyStream edge: exactly 32 bytes ─────────────────────────
|
||||
|
||||
func TestCryptoSigil_DeriveKeyStream_ExactBlockSize_Good(t *testing.T) {
|
||||
ob := &XORObfuscator{}
|
||||
data := make([]byte, 32) // Exactly one SHA-256 block.
|
||||
data := make([]byte, 32)
|
||||
for i := range data {
|
||||
data[i] = byte(i)
|
||||
}
|
||||
|
|
@ -503,14 +469,12 @@ func TestCryptoSigil_DeriveKeyStream_ExactBlockSize_Good(t *testing.T) {
|
|||
assert.Equal(t, data, restored)
|
||||
}
|
||||
|
||||
// ── random reader fallback in In ───────────────────────────────────
|
||||
|
||||
func TestCryptoSigil_ChaChaPolySigil_NilRandomReader_Good(t *testing.T) {
|
||||
key := make([]byte, 32)
|
||||
_, _ = rand.Read(key)
|
||||
|
||||
s, _ := NewChaChaPolySigil(key, nil)
|
||||
s.randomReader = nil // Should fall back to crypto/rand.Reader.
|
||||
s.randomReader = nil
|
||||
|
||||
ciphertext, err := s.In([]byte("fallback reader"))
|
||||
require.NoError(t, err)
|
||||
|
|
@ -520,7 +484,6 @@ func TestCryptoSigil_ChaChaPolySigil_NilRandomReader_Good(t *testing.T) {
|
|||
assert.Equal(t, []byte("fallback reader"), decrypted)
|
||||
}
|
||||
|
||||
// limitReader returns exactly N bytes then EOF — for deterministic tests.
|
||||
type limitReader struct {
|
||||
data []byte
|
||||
pos int
|
||||
|
|
|
|||
|
|
@ -13,10 +13,6 @@ import (
|
|||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ReverseSigil
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
func TestSigil_ReverseSigil_Good(t *testing.T) {
|
||||
s := &ReverseSigil{}
|
||||
|
||||
|
|
@ -24,7 +20,6 @@ func TestSigil_ReverseSigil_Good(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
assert.Equal(t, []byte("olleh"), out)
|
||||
|
||||
// Symmetric: Out does the same thing.
|
||||
restored, err := s.Out(out)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, []byte("hello"), restored)
|
||||
|
|
@ -33,7 +28,6 @@ func TestSigil_ReverseSigil_Good(t *testing.T) {
|
|||
func TestSigil_ReverseSigil_Bad(t *testing.T) {
|
||||
s := &ReverseSigil{}
|
||||
|
||||
// Empty input returns empty.
|
||||
out, err := s.In([]byte{})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, []byte{}, out)
|
||||
|
|
@ -42,7 +36,6 @@ func TestSigil_ReverseSigil_Bad(t *testing.T) {
|
|||
func TestSigil_ReverseSigil_NilInput_Good(t *testing.T) {
|
||||
s := &ReverseSigil{}
|
||||
|
||||
// Nil input returns nil.
|
||||
out, err := s.In(nil)
|
||||
require.NoError(t, err)
|
||||
assert.Nil(t, out)
|
||||
|
|
@ -52,10 +45,6 @@ func TestSigil_ReverseSigil_NilInput_Good(t *testing.T) {
|
|||
assert.Nil(t, out)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// HexSigil
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
func TestSigil_HexSigil_Good(t *testing.T) {
|
||||
s := &HexSigil{}
|
||||
data := []byte("hello world")
|
||||
|
|
@ -72,11 +61,9 @@ func TestSigil_HexSigil_Good(t *testing.T) {
|
|||
func TestSigil_HexSigil_Bad(t *testing.T) {
|
||||
s := &HexSigil{}
|
||||
|
||||
// Invalid hex input.
|
||||
_, err := s.Out([]byte("zzzz"))
|
||||
assert.Error(t, err)
|
||||
|
||||
// Empty input.
|
||||
out, err := s.In([]byte{})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, []byte{}, out)
|
||||
|
|
@ -94,10 +81,6 @@ func TestSigil_HexSigil_NilInput_Good(t *testing.T) {
|
|||
assert.Nil(t, out)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Base64Sigil
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
func TestSigil_Base64Sigil_Good(t *testing.T) {
|
||||
s := &Base64Sigil{}
|
||||
data := []byte("composable transforms")
|
||||
|
|
@ -114,11 +97,9 @@ func TestSigil_Base64Sigil_Good(t *testing.T) {
|
|||
func TestSigil_Base64Sigil_Bad(t *testing.T) {
|
||||
s := &Base64Sigil{}
|
||||
|
||||
// Invalid base64 (wrong padding).
|
||||
_, err := s.Out([]byte("!!!"))
|
||||
assert.Error(t, err)
|
||||
|
||||
// Empty input.
|
||||
out, err := s.In([]byte{})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, []byte{}, out)
|
||||
|
|
@ -136,10 +117,6 @@ func TestSigil_Base64Sigil_NilInput_Good(t *testing.T) {
|
|||
assert.Nil(t, out)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// GzipSigil
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
func TestSigil_GzipSigil_Good(t *testing.T) {
|
||||
s := &GzipSigil{}
|
||||
data := []byte("the quick brown fox jumps over the lazy dog")
|
||||
|
|
@ -156,14 +133,12 @@ func TestSigil_GzipSigil_Good(t *testing.T) {
|
|||
func TestSigil_GzipSigil_Bad(t *testing.T) {
|
||||
s := &GzipSigil{}
|
||||
|
||||
// Invalid gzip data.
|
||||
_, err := s.Out([]byte("not gzip"))
|
||||
assert.Error(t, err)
|
||||
|
||||
// Empty input compresses to a valid gzip stream.
|
||||
compressed, err := s.In([]byte{})
|
||||
require.NoError(t, err)
|
||||
assert.NotEmpty(t, compressed) // gzip header is always present
|
||||
assert.NotEmpty(t, compressed)
|
||||
|
||||
decompressed, err := s.Out(compressed)
|
||||
require.NoError(t, err)
|
||||
|
|
@ -182,10 +157,6 @@ func TestSigil_GzipSigil_NilInput_Good(t *testing.T) {
|
|||
assert.Nil(t, out)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// JSONSigil
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
func TestSigil_JSONSigil_Good(t *testing.T) {
|
||||
s := &JSONSigil{Indent: false}
|
||||
data := []byte(`{ "key" : "value" }`)
|
||||
|
|
@ -194,7 +165,6 @@ func TestSigil_JSONSigil_Good(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
assert.Equal(t, []byte(`{"key":"value"}`), compacted)
|
||||
|
||||
// Out is passthrough.
|
||||
passthrough, err := s.Out(compacted)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, compacted, passthrough)
|
||||
|
|
@ -213,7 +183,6 @@ func TestSigil_JSONSigil_Indent_Good(t *testing.T) {
|
|||
func TestSigil_JSONSigil_Bad(t *testing.T) {
|
||||
s := &JSONSigil{Indent: false}
|
||||
|
||||
// Invalid JSON.
|
||||
_, err := s.In([]byte("not json"))
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
|
@ -221,21 +190,15 @@ func TestSigil_JSONSigil_Bad(t *testing.T) {
|
|||
func TestSigil_JSONSigil_NilInput_Good(t *testing.T) {
|
||||
s := &JSONSigil{Indent: false}
|
||||
|
||||
// Nil input is passed through without error, matching the Sigil contract.
|
||||
out, err := s.In(nil)
|
||||
require.NoError(t, err)
|
||||
assert.Nil(t, out)
|
||||
|
||||
// Out with nil is passthrough.
|
||||
out, err = s.Out(nil)
|
||||
require.NoError(t, err)
|
||||
assert.Nil(t, out)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// HashSigil
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
func TestSigil_HashSigil_Good(t *testing.T) {
|
||||
data := []byte("hash me")
|
||||
|
||||
|
|
@ -273,7 +236,6 @@ func TestSigil_HashSigil_Good(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
assert.Len(t, hashed, tt.size)
|
||||
|
||||
// Out is passthrough.
|
||||
passthrough, err := s.Out(hashed)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, hashed, passthrough)
|
||||
|
|
@ -282,7 +244,6 @@ func TestSigil_HashSigil_Good(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestSigil_HashSigil_Bad(t *testing.T) {
|
||||
// Unsupported hash constant.
|
||||
s := &HashSigil{Hash: 0}
|
||||
_, err := s.In([]byte("data"))
|
||||
assert.Error(t, err)
|
||||
|
|
@ -290,7 +251,6 @@ func TestSigil_HashSigil_Bad(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestSigil_HashSigil_EmptyInput_Good(t *testing.T) {
|
||||
// Hashing empty data should still produce a valid digest.
|
||||
s, err := NewSigil("sha256")
|
||||
require.NoError(t, err)
|
||||
|
||||
|
|
@ -299,10 +259,6 @@ func TestSigil_HashSigil_EmptyInput_Good(t *testing.T) {
|
|||
assert.Len(t, hashed, sha256.Size)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// NewSigil factory
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
func TestSigil_NewSigil_Good(t *testing.T) {
|
||||
names := []string{
|
||||
"reverse", "hex", "base64", "gzip", "json", "json-indent",
|
||||
|
|
@ -333,10 +289,6 @@ func TestSigil_NewSigil_EmptyName_Bad(t *testing.T) {
|
|||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Transmute / Untransmute
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
func TestSigil_Transmute_Good(t *testing.T) {
|
||||
data := []byte("round trip")
|
||||
|
||||
|
|
@ -395,16 +347,13 @@ func TestSigil_Transmute_GzipRoundTrip_Good(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestSigil_Transmute_Bad(t *testing.T) {
|
||||
// Transmute with a sigil that will fail: hex decode on non-hex input.
|
||||
hexSigil := &HexSigil{}
|
||||
|
||||
// Calling Out (decode) with invalid input via manual chain.
|
||||
_, err := Untransmute([]byte("not-hex!!"), []Sigil{hexSigil})
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestSigil_Transmute_NilAndEmptyInput_Good(t *testing.T) {
|
||||
// Empty sigil chain is a no-op.
|
||||
data := []byte("unchanged")
|
||||
|
||||
result, err := Transmute(data, nil)
|
||||
|
|
@ -415,7 +364,6 @@ func TestSigil_Transmute_NilAndEmptyInput_Good(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
assert.Equal(t, data, result)
|
||||
|
||||
// Nil data through a chain.
|
||||
hexSigil, _ := NewSigil("hex")
|
||||
result, err = Transmute(nil, []Sigil{hexSigil})
|
||||
require.NoError(t, err)
|
||||
|
|
|
|||
|
|
@ -18,8 +18,6 @@ func newTestMedium(t *testing.T) *Medium {
|
|||
return m
|
||||
}
|
||||
|
||||
// --- Constructor Tests ---
|
||||
|
||||
func TestSqlite_New_Good(t *testing.T) {
|
||||
m, err := New(Options{Path: ":memory:"})
|
||||
require.NoError(t, err)
|
||||
|
|
@ -40,8 +38,6 @@ func TestSqlite_New_EmptyPath_Bad(t *testing.T) {
|
|||
assert.Contains(t, err.Error(), "database path is required")
|
||||
}
|
||||
|
||||
// --- Read/Write Tests ---
|
||||
|
||||
func TestSqlite_ReadWrite_Good(t *testing.T) {
|
||||
m := newTestMedium(t)
|
||||
|
||||
|
|
@ -104,8 +100,6 @@ func TestSqlite_Read_IsDirectory_Bad(t *testing.T) {
|
|||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
// --- EnsureDir Tests ---
|
||||
|
||||
func TestSqlite_EnsureDir_Good(t *testing.T) {
|
||||
m := newTestMedium(t)
|
||||
|
||||
|
|
@ -128,8 +122,6 @@ func TestSqlite_EnsureDir_Idempotent_Good(t *testing.T) {
|
|||
assert.True(t, m.IsDir("mydir"))
|
||||
}
|
||||
|
||||
// --- IsFile Tests ---
|
||||
|
||||
func TestSqlite_IsFile_Good(t *testing.T) {
|
||||
m := newTestMedium(t)
|
||||
|
||||
|
|
@ -142,8 +134,6 @@ func TestSqlite_IsFile_Good(t *testing.T) {
|
|||
assert.False(t, m.IsFile(""))
|
||||
}
|
||||
|
||||
// --- FileGet/FileSet Tests ---
|
||||
|
||||
func TestSqlite_FileGetFileSet_Good(t *testing.T) {
|
||||
m := newTestMedium(t)
|
||||
|
||||
|
|
@ -155,8 +145,6 @@ func TestSqlite_FileGetFileSet_Good(t *testing.T) {
|
|||
assert.Equal(t, "value", val)
|
||||
}
|
||||
|
||||
// --- Delete Tests ---
|
||||
|
||||
func TestSqlite_Delete_Good(t *testing.T) {
|
||||
m := newTestMedium(t)
|
||||
|
||||
|
|
@ -203,8 +191,6 @@ func TestSqlite_Delete_NotEmpty_Bad(t *testing.T) {
|
|||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
// --- DeleteAll Tests ---
|
||||
|
||||
func TestSqlite_DeleteAll_Good(t *testing.T) {
|
||||
m := newTestMedium(t)
|
||||
|
||||
|
|
@ -244,8 +230,6 @@ func TestSqlite_DeleteAll_EmptyPath_Bad(t *testing.T) {
|
|||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
// --- Rename Tests ---
|
||||
|
||||
func TestSqlite_Rename_Good(t *testing.T) {
|
||||
m := newTestMedium(t)
|
||||
|
||||
|
|
@ -298,8 +282,6 @@ func TestSqlite_Rename_EmptyPath_Bad(t *testing.T) {
|
|||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
// --- List Tests ---
|
||||
|
||||
func TestSqlite_List_Good(t *testing.T) {
|
||||
m := newTestMedium(t)
|
||||
|
||||
|
|
@ -356,8 +338,6 @@ func TestSqlite_List_DirectoryEntry_Good(t *testing.T) {
|
|||
assert.True(t, info.IsDir())
|
||||
}
|
||||
|
||||
// --- Stat Tests ---
|
||||
|
||||
func TestSqlite_Stat_Good(t *testing.T) {
|
||||
m := newTestMedium(t)
|
||||
|
||||
|
|
@ -395,8 +375,6 @@ func TestSqlite_Stat_EmptyPath_Bad(t *testing.T) {
|
|||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
// --- Open Tests ---
|
||||
|
||||
func TestSqlite_Open_Good(t *testing.T) {
|
||||
m := newTestMedium(t)
|
||||
|
||||
|
|
@ -430,8 +408,6 @@ func TestSqlite_Open_IsDirectory_Bad(t *testing.T) {
|
|||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
// --- Create Tests ---
|
||||
|
||||
func TestSqlite_Create_Good(t *testing.T) {
|
||||
m := newTestMedium(t)
|
||||
|
||||
|
|
@ -473,8 +449,6 @@ func TestSqlite_Create_EmptyPath_Bad(t *testing.T) {
|
|||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
// --- Append Tests ---
|
||||
|
||||
func TestSqlite_Append_Good(t *testing.T) {
|
||||
m := newTestMedium(t)
|
||||
|
||||
|
|
@ -514,8 +488,6 @@ func TestSqlite_Append_EmptyPath_Bad(t *testing.T) {
|
|||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
// --- ReadStream Tests ---
|
||||
|
||||
func TestSqlite_ReadStream_Good(t *testing.T) {
|
||||
m := newTestMedium(t)
|
||||
|
||||
|
|
@ -545,8 +517,6 @@ func TestSqlite_ReadStream_IsDirectory_Bad(t *testing.T) {
|
|||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
// --- WriteStream Tests ---
|
||||
|
||||
func TestSqlite_WriteStream_Good(t *testing.T) {
|
||||
m := newTestMedium(t)
|
||||
|
||||
|
|
@ -562,8 +532,6 @@ func TestSqlite_WriteStream_Good(t *testing.T) {
|
|||
assert.Equal(t, "piped data", content)
|
||||
}
|
||||
|
||||
// --- Exists Tests ---
|
||||
|
||||
func TestSqlite_Exists_Good(t *testing.T) {
|
||||
m := newTestMedium(t)
|
||||
|
||||
|
|
@ -581,8 +549,6 @@ func TestSqlite_Exists_EmptyPath_Good(t *testing.T) {
|
|||
assert.True(t, m.Exists(""))
|
||||
}
|
||||
|
||||
// --- IsDir Tests ---
|
||||
|
||||
func TestSqlite_IsDir_Good(t *testing.T) {
|
||||
m := newTestMedium(t)
|
||||
|
||||
|
|
@ -595,8 +561,6 @@ func TestSqlite_IsDir_Good(t *testing.T) {
|
|||
assert.False(t, m.IsDir(""))
|
||||
}
|
||||
|
||||
// --- normaliseEntryPath Tests ---
|
||||
|
||||
func TestSqlite_NormaliseEntryPath_Good(t *testing.T) {
|
||||
assert.Equal(t, "file.txt", normaliseEntryPath("file.txt"))
|
||||
assert.Equal(t, "dir/file.txt", normaliseEntryPath("dir/file.txt"))
|
||||
|
|
@ -608,12 +572,9 @@ func TestSqlite_NormaliseEntryPath_Good(t *testing.T) {
|
|||
assert.Equal(t, "", normaliseEntryPath("/"))
|
||||
}
|
||||
|
||||
// --- Interface Compliance ---
|
||||
|
||||
func TestSqlite_InterfaceCompliance(t *testing.T) {
|
||||
m := newTestMedium(t)
|
||||
|
||||
// Verify all methods exist by asserting the interface shape.
|
||||
var _ interface {
|
||||
Read(string) (string, error)
|
||||
Write(string, string) error
|
||||
|
|
@ -636,8 +597,6 @@ func TestSqlite_InterfaceCompliance(t *testing.T) {
|
|||
} = m
|
||||
}
|
||||
|
||||
// --- Custom Table ---
|
||||
|
||||
func TestSqlite_CustomTable_Good(t *testing.T) {
|
||||
m, err := New(Options{Path: ":memory:", Table: "my_files"})
|
||||
require.NoError(t, err)
|
||||
|
|
|
|||
|
|
@ -121,12 +121,10 @@ func TestMedium_Medium_Stat_Good(t *testing.T) {
|
|||
m := newTestMedium(t)
|
||||
_ = m.Write("grp/key", "hello")
|
||||
|
||||
// Stat group
|
||||
info, err := m.Stat("grp")
|
||||
require.NoError(t, err)
|
||||
assert.True(t, info.IsDir())
|
||||
|
||||
// Stat key
|
||||
info, err = m.Stat("grp/key")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, int64(5), info.Size())
|
||||
|
|
@ -190,7 +188,6 @@ func TestMedium_Medium_AsMedium_Good(t *testing.T) {
|
|||
m := s.AsMedium()
|
||||
require.NoError(t, m.Write("grp/key", "val"))
|
||||
|
||||
// Accessible through both APIs
|
||||
val, err := s.Get("grp", "key")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "val", val)
|
||||
|
|
|
|||
|
|
@ -193,7 +193,7 @@ func (service *Service) HandleWorkspaceCommand(command WorkspaceCommand) core.Re
|
|||
|
||||
// Example: result := service.HandleWorkspaceMessage(core.New(), WorkspaceCommand{Action: WorkspaceSwitchAction, WorkspaceID: "f3f0d7"})
|
||||
// Example: legacy := service.HandleWorkspaceMessage(core.New(), map[string]any{"action": WorkspaceCreateAction, "identifier": "alice", "password": "pass123"})
|
||||
func (service *Service) HandleWorkspaceMessage(coreRuntime *core.Core, message core.Message) core.Result {
|
||||
func (service *Service) HandleWorkspaceMessage(_ *core.Core, message core.Message) core.Result {
|
||||
command, ok := workspaceCommandFromMessage(message)
|
||||
if !ok {
|
||||
return core.Result{OK: true}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue