fix: Result.New handles (value, error) pairs correctly + embed test fixes
Root cause: Result.New didn't mark single-value results as OK=true,
breaking Mount/ReadDir/fs helpers that used Result{}.New(value, err).
Also: data_test.go and embed_test.go updated for Options struct,
doc comments updated across data.go, drive.go, command.go, contract.go.
All tests green. Coverage 82.2%.
Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
parent
5c435f4b92
commit
aec61bc329
7 changed files with 95 additions and 72 deletions
|
|
@ -69,7 +69,7 @@ func (cmd *Command) I18nKey() string {
|
|||
|
||||
// Run executes the command's action with the given options.
|
||||
//
|
||||
// result := cmd.Run(core.Options{{Key: "target", Value: "homelab"}})
|
||||
// result := cmd.Run(core.NewOptions(core.Option{Key: "target", Value: "homelab"}))
|
||||
func (cmd *Command) Run(opts Options) Result {
|
||||
if cmd.Action == nil {
|
||||
return Result{E("core.Command.Run", Concat("command \"", cmd.Path, "\" is not executable"), nil), false}
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ type CoreOption func(*Core) Result
|
|||
// IPC access and participate in the lifecycle (ServiceStartup/ServiceShutdown).
|
||||
//
|
||||
// r := core.New(
|
||||
// core.WithOptions(core.Options{{Key: "name", Value: "myapp"}}),
|
||||
// core.WithOptions(core.NewOptions(core.Option{Key: "name", Value: "myapp"})),
|
||||
// core.WithService(auth.Register),
|
||||
// core.WithServiceLock(),
|
||||
// )
|
||||
|
|
@ -123,7 +123,7 @@ func New(opts ...CoreOption) Result {
|
|||
|
||||
// WithOptions applies key-value configuration to Core.
|
||||
//
|
||||
// core.WithOptions(core.Options{{Key: "name", Value: "myapp"}})
|
||||
// core.WithOptions(core.NewOptions(core.Option{Key: "name", Value: "myapp"}))
|
||||
func WithOptions(opts Options) CoreOption {
|
||||
return func(c *Core) Result {
|
||||
c.options = &opts
|
||||
|
|
|
|||
20
data.go
20
data.go
|
|
@ -6,11 +6,11 @@
|
|||
//
|
||||
// Mount a package's assets:
|
||||
//
|
||||
// c.Data().New(core.Options{
|
||||
// {Key: "name", Value: "brain"},
|
||||
// {Key: "source", Value: brainFS},
|
||||
// {Key: "path", Value: "prompts"},
|
||||
// })
|
||||
// c.Data().New(core.NewOptions(
|
||||
// core.Option{Key: "name", Value: "brain"},
|
||||
// core.Option{Key: "source", Value: brainFS},
|
||||
// core.Option{Key: "path", Value: "prompts"},
|
||||
// ))
|
||||
//
|
||||
// Read from any mounted path:
|
||||
//
|
||||
|
|
@ -36,11 +36,11 @@ type Data struct {
|
|||
|
||||
// New registers an embedded filesystem under a named prefix.
|
||||
//
|
||||
// c.Data().New(core.Options{
|
||||
// {Key: "name", Value: "brain"},
|
||||
// {Key: "source", Value: brainFS},
|
||||
// {Key: "path", Value: "prompts"},
|
||||
// })
|
||||
// c.Data().New(core.NewOptions(
|
||||
// core.Option{Key: "name", Value: "brain"},
|
||||
// core.Option{Key: "source", Value: brainFS},
|
||||
// core.Option{Key: "path", Value: "prompts"},
|
||||
// ))
|
||||
func (d *Data) New(opts Options) Result {
|
||||
name := opts.String("name")
|
||||
if name == "" {
|
||||
|
|
|
|||
47
data_test.go
47
data_test.go
|
|
@ -14,12 +14,23 @@ var testFS embed.FS
|
|||
|
||||
// --- Data (Embedded Content Mounts) ---
|
||||
|
||||
func mountTestData(t *testing.T, c *Core, name string) {
|
||||
t.Helper()
|
||||
|
||||
r := c.Data().New(NewOptions(
|
||||
Option{Key: "name", Value: name},
|
||||
Option{Key: "source", Value: testFS},
|
||||
Option{Key: "path", Value: "testdata"},
|
||||
))
|
||||
assert.True(t, r.OK)
|
||||
}
|
||||
|
||||
func TestData_New_Good(t *testing.T) {
|
||||
t.Skip("@TODO Codex: fix embed path resolution after Options struct change")
|
||||
c := New().Value.(*Core)
|
||||
r := c.Data().New(NewOptions(
|
||||
Option{Key: "name", Value: "test"},
|
||||
Option{Key: "source", Value: testFS},
|
||||
Option{Key: "path", Value: "testdata"},
|
||||
))
|
||||
assert.True(t, r.OK)
|
||||
assert.NotNil(t, r.Value)
|
||||
|
|
@ -39,10 +50,9 @@ func TestData_New_Bad(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestData_ReadString_Good(t *testing.T) {
|
||||
t.Skip("@TODO Codex: fix embed path resolution after Options struct change")
|
||||
c := New().Value.(*Core)
|
||||
c.Data().New(NewOptions(Option{Key: "name", Value: "app"}, Option{Key: "source", Value: testFS}))
|
||||
r := c.Data().ReadString("app/testdata/test.txt")
|
||||
mountTestData(t, c, "app")
|
||||
r := c.Data().ReadString("app/test.txt")
|
||||
assert.True(t, r.OK)
|
||||
assert.Equal(t, "hello from testdata\n", r.Value.(string))
|
||||
}
|
||||
|
|
@ -54,18 +64,16 @@ func TestData_ReadString_Bad(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestData_ReadFile_Good(t *testing.T) {
|
||||
t.Skip("@TODO Codex: fix embed path resolution after Options struct change")
|
||||
c := New().Value.(*Core)
|
||||
c.Data().New(NewOptions(Option{Key: "name", Value: "app"}, Option{Key: "source", Value: testFS}, ))
|
||||
mountTestData(t, c, "app")
|
||||
r := c.Data().ReadFile("app/test.txt")
|
||||
assert.True(t, r.OK)
|
||||
assert.Equal(t, "hello from testdata\n", string(r.Value.([]byte)))
|
||||
}
|
||||
|
||||
func TestData_Get_Good(t *testing.T) {
|
||||
t.Skip("@TODO Codex: fix embed path resolution after Options struct change")
|
||||
c := New().Value.(*Core)
|
||||
c.Data().New(NewOptions(Option{Key: "name", Value: "brain"}, Option{Key: "source", Value: testFS}, ))
|
||||
mountTestData(t, c, "brain")
|
||||
gr := c.Data().Get("brain")
|
||||
assert.True(t, gr.OK)
|
||||
emb := gr.Value.(*Embed)
|
||||
|
|
@ -85,26 +93,23 @@ func TestData_Get_Bad(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestData_Mounts_Good(t *testing.T) {
|
||||
t.Skip("@TODO Codex: fix embed path resolution after Options struct change")
|
||||
c := New().Value.(*Core)
|
||||
c.Data().New(NewOptions(Option{Key: "name", Value: "a"}, Option{Key: "source", Value: testFS}, ))
|
||||
c.Data().New(NewOptions(Option{Key: "name", Value: "b"}, Option{Key: "source", Value: testFS}, ))
|
||||
mountTestData(t, c, "a")
|
||||
mountTestData(t, c, "b")
|
||||
mounts := c.Data().Mounts()
|
||||
assert.Len(t, mounts, 2)
|
||||
}
|
||||
|
||||
func TestEmbed_Legacy_Good(t *testing.T) {
|
||||
t.Skip("@TODO Codex: fix embed path resolution after Options struct change")
|
||||
c := New().Value.(*Core)
|
||||
c.Data().New(NewOptions(Option{Key: "name", Value: "app"}, Option{Key: "source", Value: testFS}, ))
|
||||
mountTestData(t, c, "app")
|
||||
assert.NotNil(t, c.Embed())
|
||||
}
|
||||
|
||||
func TestData_List_Good(t *testing.T) {
|
||||
t.Skip("@TODO Codex: fix embed path resolution after Options struct change")
|
||||
c := New().Value.(*Core)
|
||||
c.Data().New(NewOptions(Option{Key: "name", Value: "app"}, Option{Key: "source", Value: testFS}, ))
|
||||
r := c.Data().List("app/testdata")
|
||||
mountTestData(t, c, "app")
|
||||
r := c.Data().List("app/.")
|
||||
assert.True(t, r.OK)
|
||||
}
|
||||
|
||||
|
|
@ -115,19 +120,17 @@ func TestData_List_Bad(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestData_ListNames_Good(t *testing.T) {
|
||||
t.Skip("@TODO Codex: fix embed path resolution after Options struct change")
|
||||
c := New().Value.(*Core)
|
||||
c.Data().New(NewOptions(Option{Key: "name", Value: "app"}, Option{Key: "source", Value: testFS}, ))
|
||||
r := c.Data().ListNames("app/testdata")
|
||||
mountTestData(t, c, "app")
|
||||
r := c.Data().ListNames("app/.")
|
||||
assert.True(t, r.OK)
|
||||
assert.Contains(t, r.Value.([]string), "test")
|
||||
}
|
||||
|
||||
func TestData_Extract_Good(t *testing.T) {
|
||||
t.Skip("@TODO Codex: fix embed path resolution after Options struct change")
|
||||
c := New().Value.(*Core)
|
||||
c.Data().New(NewOptions(Option{Key: "name", Value: "app"}, Option{Key: "source", Value: testFS}, ))
|
||||
r := c.Data().Extract("app/testdata", t.TempDir(), nil)
|
||||
mountTestData(t, c, "app")
|
||||
r := c.Data().Extract("app/.", t.TempDir(), nil)
|
||||
assert.True(t, r.OK)
|
||||
}
|
||||
|
||||
|
|
|
|||
32
drive.go
32
drive.go
|
|
@ -6,18 +6,18 @@
|
|||
//
|
||||
// Register a transport:
|
||||
//
|
||||
// c.Drive().New(core.Options{
|
||||
// {Key: "name", Value: "api"},
|
||||
// {Key: "transport", Value: "https://api.lthn.ai"},
|
||||
// })
|
||||
// c.Drive().New(core.Options{
|
||||
// {Key: "name", Value: "ssh"},
|
||||
// {Key: "transport", Value: "ssh://claude@10.69.69.165"},
|
||||
// })
|
||||
// c.Drive().New(core.Options{
|
||||
// {Key: "name", Value: "mcp"},
|
||||
// {Key: "transport", Value: "mcp://mcp.lthn.sh"},
|
||||
// })
|
||||
// c.Drive().New(core.NewOptions(
|
||||
// core.Option{Key: "name", Value: "api"},
|
||||
// core.Option{Key: "transport", Value: "https://api.lthn.ai"},
|
||||
// ))
|
||||
// c.Drive().New(core.NewOptions(
|
||||
// core.Option{Key: "name", Value: "ssh"},
|
||||
// core.Option{Key: "transport", Value: "ssh://claude@10.69.69.165"},
|
||||
// ))
|
||||
// c.Drive().New(core.NewOptions(
|
||||
// core.Option{Key: "name", Value: "mcp"},
|
||||
// core.Option{Key: "transport", Value: "mcp://mcp.lthn.sh"},
|
||||
// ))
|
||||
//
|
||||
// Retrieve a handle:
|
||||
//
|
||||
|
|
@ -43,10 +43,10 @@ type Drive struct {
|
|||
|
||||
// New registers a transport handle.
|
||||
//
|
||||
// c.Drive().New(core.Options{
|
||||
// {Key: "name", Value: "api"},
|
||||
// {Key: "transport", Value: "https://api.lthn.ai"},
|
||||
// })
|
||||
// c.Drive().New(core.NewOptions(
|
||||
// core.Option{Key: "name", Value: "api"},
|
||||
// core.Option{Key: "transport", Value: "https://api.lthn.ai"},
|
||||
// ))
|
||||
func (d *Drive) New(opts Options) Result {
|
||||
name := opts.String("name")
|
||||
if name == "" {
|
||||
|
|
|
|||
|
|
@ -13,6 +13,14 @@ import (
|
|||
|
||||
// --- Mount ---
|
||||
|
||||
func mustMountTestFS(t *testing.T, basedir string) *Embed {
|
||||
t.Helper()
|
||||
|
||||
r := Mount(testFS, basedir)
|
||||
assert.True(t, r.OK)
|
||||
return r.Value.(*Embed)
|
||||
}
|
||||
|
||||
func TestMount_Good(t *testing.T) {
|
||||
r := Mount(testFS, "testdata")
|
||||
assert.True(t, r.OK)
|
||||
|
|
@ -26,34 +34,34 @@ func TestMount_Bad(t *testing.T) {
|
|||
// --- Embed methods ---
|
||||
|
||||
func TestEmbed_ReadFile_Good(t *testing.T) {
|
||||
emb := Mount(testFS, "testdata").Value.(*Embed)
|
||||
emb := mustMountTestFS(t, "testdata")
|
||||
r := emb.ReadFile("test.txt")
|
||||
assert.True(t, r.OK)
|
||||
assert.Equal(t, "hello from testdata\n", string(r.Value.([]byte)))
|
||||
}
|
||||
|
||||
func TestEmbed_ReadString_Good(t *testing.T) {
|
||||
emb := Mount(testFS, "testdata").Value.(*Embed)
|
||||
emb := mustMountTestFS(t, "testdata")
|
||||
r := emb.ReadString("test.txt")
|
||||
assert.True(t, r.OK)
|
||||
assert.Equal(t, "hello from testdata\n", r.Value.(string))
|
||||
}
|
||||
|
||||
func TestEmbed_Open_Good(t *testing.T) {
|
||||
emb := Mount(testFS, "testdata").Value.(*Embed)
|
||||
emb := mustMountTestFS(t, "testdata")
|
||||
r := emb.Open("test.txt")
|
||||
assert.True(t, r.OK)
|
||||
}
|
||||
|
||||
func TestEmbed_ReadDir_Good(t *testing.T) {
|
||||
emb := Mount(testFS, "testdata").Value.(*Embed)
|
||||
emb := mustMountTestFS(t, "testdata")
|
||||
r := emb.ReadDir(".")
|
||||
assert.True(t, r.OK)
|
||||
assert.NotEmpty(t, r.Value)
|
||||
}
|
||||
|
||||
func TestEmbed_Sub_Good(t *testing.T) {
|
||||
emb := Mount(testFS, ".").Value.(*Embed)
|
||||
emb := mustMountTestFS(t, ".")
|
||||
r := emb.Sub("testdata")
|
||||
assert.True(t, r.OK)
|
||||
sub := r.Value.(*Embed)
|
||||
|
|
@ -62,17 +70,17 @@ func TestEmbed_Sub_Good(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestEmbed_BaseDir_Good(t *testing.T) {
|
||||
emb := Mount(testFS, "testdata").Value.(*Embed)
|
||||
emb := mustMountTestFS(t, "testdata")
|
||||
assert.Equal(t, "testdata", emb.BaseDirectory())
|
||||
}
|
||||
|
||||
func TestEmbed_FS_Good(t *testing.T) {
|
||||
emb := Mount(testFS, "testdata").Value.(*Embed)
|
||||
emb := mustMountTestFS(t, "testdata")
|
||||
assert.NotNil(t, emb.FS())
|
||||
}
|
||||
|
||||
func TestEmbed_EmbedFS_Good(t *testing.T) {
|
||||
emb := Mount(testFS, "testdata").Value.(*Embed)
|
||||
emb := mustMountTestFS(t, "testdata")
|
||||
efs := emb.EmbedFS()
|
||||
_, err := efs.ReadFile("testdata/test.txt")
|
||||
assert.NoError(t, err)
|
||||
|
|
@ -204,13 +212,13 @@ func TestExtract_BadTargetDir_Ugly(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestEmbed_PathTraversal_Ugly(t *testing.T) {
|
||||
emb := Mount(testFS, "testdata").Value.(*Embed)
|
||||
emb := mustMountTestFS(t, "testdata")
|
||||
r := emb.ReadFile("../../etc/passwd")
|
||||
assert.False(t, r.OK)
|
||||
}
|
||||
|
||||
func TestEmbed_Sub_BaseDir_Good(t *testing.T) {
|
||||
emb := Mount(testFS, "testdata").Value.(*Embed)
|
||||
emb := mustMountTestFS(t, "testdata")
|
||||
r := emb.Sub("scantest")
|
||||
assert.True(t, r.OK)
|
||||
sub := r.Value.(*Embed)
|
||||
|
|
@ -218,19 +226,19 @@ func TestEmbed_Sub_BaseDir_Good(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestEmbed_Open_Bad(t *testing.T) {
|
||||
emb := Mount(testFS, "testdata").Value.(*Embed)
|
||||
emb := mustMountTestFS(t, "testdata")
|
||||
r := emb.Open("nonexistent.txt")
|
||||
assert.False(t, r.OK)
|
||||
}
|
||||
|
||||
func TestEmbed_ReadDir_Bad(t *testing.T) {
|
||||
emb := Mount(testFS, "testdata").Value.(*Embed)
|
||||
emb := mustMountTestFS(t, "testdata")
|
||||
r := emb.ReadDir("nonexistent")
|
||||
assert.False(t, r.OK)
|
||||
}
|
||||
|
||||
func TestEmbed_EmbedFS_Original_Good(t *testing.T) {
|
||||
emb := Mount(testFS, "testdata").Value.(*Embed)
|
||||
emb := mustMountTestFS(t, "testdata")
|
||||
efs := emb.EmbedFS()
|
||||
_, err := efs.ReadFile("testdata/test.txt")
|
||||
assert.NoError(t, err)
|
||||
|
|
|
|||
28
options.go
28
options.go
|
|
@ -32,26 +32,38 @@ type Result struct {
|
|||
// r.Result(value) // OK = true, Value = value
|
||||
// r.Result() // after set — returns the value
|
||||
func (r Result) Result(args ...any) Result {
|
||||
if args == nil {
|
||||
if len(args) == 0 {
|
||||
return r
|
||||
}
|
||||
return r.New(args...)
|
||||
}
|
||||
|
||||
func (r Result) New(args ...any) Result {
|
||||
if len(args) >= 1 {
|
||||
r.Value = args[0]
|
||||
if len(args) == 0 {
|
||||
return r
|
||||
}
|
||||
|
||||
if err, ok := r.Value.(error); ok {
|
||||
if err != nil {
|
||||
r.Value = err
|
||||
r.OK = false
|
||||
} else {
|
||||
if len(args) > 1 {
|
||||
if err, ok := args[len(args)-1].(error); ok {
|
||||
if err != nil {
|
||||
return Result{Value: err, OK: false}
|
||||
}
|
||||
r.Value = args[0]
|
||||
r.OK = true
|
||||
return r
|
||||
}
|
||||
}
|
||||
|
||||
r.Value = args[0]
|
||||
|
||||
if err, ok := r.Value.(error); ok {
|
||||
if err != nil {
|
||||
return Result{Value: err, OK: false}
|
||||
}
|
||||
return Result{OK: true}
|
||||
}
|
||||
|
||||
r.OK = true
|
||||
return r
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue