feat: restore functional option pattern for New()
New() returns Result, accepts CoreOption functionals. Restores v0.3.3 service registration contract: - WithService(factory func(*Core) Result) — service factory receives Core - WithOptions(Options) — key-value configuration - WithServiceLock() — immutable after construction Services registered in New() form the application conclave with shared IPC access. Each Core instance has its own bus scope. Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
parent
927f830be4
commit
c45b22849f
19 changed files with 255 additions and 162 deletions
|
|
@ -10,18 +10,18 @@ import (
|
|||
// --- App ---
|
||||
|
||||
func TestApp_Good(t *testing.T) {
|
||||
c := New(Options{{Key: "name", Value: "myapp"}})
|
||||
c := New(WithOptions(Options{{Key: "name", Value: "myapp"}})).Value.(*Core)
|
||||
assert.Equal(t, "myapp", c.App().Name)
|
||||
}
|
||||
|
||||
func TestApp_Empty_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
assert.NotNil(t, c.App())
|
||||
assert.Equal(t, "", c.App().Name)
|
||||
}
|
||||
|
||||
func TestApp_Runtime_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
c.App().Runtime = &struct{ Name string }{Name: "wails"}
|
||||
assert.NotNil(t, c.App().Runtime)
|
||||
}
|
||||
|
|
|
|||
18
cli_test.go
18
cli_test.go
|
|
@ -11,23 +11,23 @@ import (
|
|||
// --- Cli Surface ---
|
||||
|
||||
func TestCli_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
assert.NotNil(t, c.Cli())
|
||||
}
|
||||
|
||||
func TestCli_Banner_Good(t *testing.T) {
|
||||
c := New(Options{{Key: "name", Value: "myapp"}})
|
||||
c := New(WithOptions(Options{{Key: "name", Value: "myapp"}})).Value.(*Core)
|
||||
assert.Equal(t, "myapp", c.Cli().Banner())
|
||||
}
|
||||
|
||||
func TestCli_SetBanner_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
c.Cli().SetBanner(func(_ *Cli) string { return "Custom Banner" })
|
||||
assert.Equal(t, "Custom Banner", c.Cli().Banner())
|
||||
}
|
||||
|
||||
func TestCli_Run_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
executed := false
|
||||
c.Command("hello", Command{Action: func(_ Options) Result {
|
||||
executed = true
|
||||
|
|
@ -40,7 +40,7 @@ func TestCli_Run_Good(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestCli_Run_Nested_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
executed := false
|
||||
c.Command("deploy/to/homelab", Command{Action: func(_ Options) Result {
|
||||
executed = true
|
||||
|
|
@ -52,7 +52,7 @@ func TestCli_Run_Nested_Good(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestCli_Run_WithFlags_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
var received Options
|
||||
c.Command("serve", Command{Action: func(opts Options) Result {
|
||||
received = opts
|
||||
|
|
@ -64,20 +64,20 @@ func TestCli_Run_WithFlags_Good(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestCli_Run_NoCommand_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
r := c.Cli().Run()
|
||||
assert.False(t, r.OK)
|
||||
}
|
||||
|
||||
func TestCli_PrintHelp_Good(t *testing.T) {
|
||||
c := New(Options{{Key: "name", Value: "myapp"}})
|
||||
c := New(WithOptions(Options{{Key: "name", Value: "myapp"}})).Value.(*Core)
|
||||
c.Command("deploy", Command{Action: func(_ Options) Result { return Result{OK: true} }})
|
||||
c.Command("serve", Command{Action: func(_ Options) Result { return Result{OK: true} }})
|
||||
c.Cli().PrintHelp()
|
||||
}
|
||||
|
||||
func TestCli_SetOutput_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
var buf bytes.Buffer
|
||||
c.Cli().SetOutput(&buf)
|
||||
c.Cli().Print("hello %s", "world")
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import (
|
|||
// --- Command DTO ---
|
||||
|
||||
func TestCommand_Register_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
r := c.Command("deploy", Command{Action: func(_ Options) Result {
|
||||
return Result{Value: "deployed", OK: true}
|
||||
}})
|
||||
|
|
@ -18,7 +18,7 @@ func TestCommand_Register_Good(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestCommand_Get_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
c.Command("deploy", Command{Action: func(_ Options) Result { return Result{OK: true} }})
|
||||
r := c.Command("deploy")
|
||||
assert.True(t, r.OK)
|
||||
|
|
@ -26,13 +26,13 @@ func TestCommand_Get_Good(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestCommand_Get_Bad(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
r := c.Command("nonexistent")
|
||||
assert.False(t, r.OK)
|
||||
}
|
||||
|
||||
func TestCommand_Run_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
c.Command("greet", Command{Action: func(opts Options) Result {
|
||||
return Result{Value: Concat("hello ", opts.String("name")), OK: true}
|
||||
}})
|
||||
|
|
@ -43,7 +43,7 @@ func TestCommand_Run_Good(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestCommand_Run_NoAction_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
c.Command("empty", Command{Description: "no action"})
|
||||
cmd := c.Command("empty").Value.(*Command)
|
||||
r := cmd.Run(Options{})
|
||||
|
|
@ -53,7 +53,7 @@ func TestCommand_Run_NoAction_Good(t *testing.T) {
|
|||
// --- Nested Commands ---
|
||||
|
||||
func TestCommand_Nested_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
c.Command("deploy/to/homelab", Command{Action: func(_ Options) Result {
|
||||
return Result{Value: "deployed to homelab", OK: true}
|
||||
}})
|
||||
|
|
@ -67,7 +67,7 @@ func TestCommand_Nested_Good(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestCommand_Paths_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
c.Command("deploy", Command{Action: func(_ Options) Result { return Result{OK: true} }})
|
||||
c.Command("serve", Command{Action: func(_ Options) Result { return Result{OK: true} }})
|
||||
c.Command("deploy/to/homelab", Command{Action: func(_ Options) Result { return Result{OK: true} }})
|
||||
|
|
@ -82,21 +82,21 @@ func TestCommand_Paths_Good(t *testing.T) {
|
|||
// --- I18n Key Derivation ---
|
||||
|
||||
func TestCommand_I18nKey_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
c.Command("deploy/to/homelab", Command{})
|
||||
cmd := c.Command("deploy/to/homelab").Value.(*Command)
|
||||
assert.Equal(t, "cmd.deploy.to.homelab.description", cmd.I18nKey())
|
||||
}
|
||||
|
||||
func TestCommand_I18nKey_Custom_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
c.Command("deploy", Command{Description: "custom.deploy.key"})
|
||||
cmd := c.Command("deploy").Value.(*Command)
|
||||
assert.Equal(t, "custom.deploy.key", cmd.I18nKey())
|
||||
}
|
||||
|
||||
func TestCommand_I18nKey_Simple_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
c.Command("serve", Command{})
|
||||
cmd := c.Command("serve").Value.(*Command)
|
||||
assert.Equal(t, "cmd.serve.description", cmd.I18nKey())
|
||||
|
|
@ -105,7 +105,7 @@ func TestCommand_I18nKey_Simple_Good(t *testing.T) {
|
|||
// --- Lifecycle ---
|
||||
|
||||
func TestCommand_Lifecycle_NoImpl_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
c.Command("serve", Command{Action: func(_ Options) Result {
|
||||
return Result{Value: "running", OK: true}
|
||||
}})
|
||||
|
|
@ -153,7 +153,7 @@ func (l *testLifecycle) Signal(sig string) Result {
|
|||
}
|
||||
|
||||
func TestCommand_Lifecycle_WithImpl_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
lc := &testLifecycle{}
|
||||
c.Command("daemon", Command{Lifecycle: lc})
|
||||
cmd := c.Command("daemon").Value.(*Command)
|
||||
|
|
@ -177,14 +177,14 @@ func TestCommand_Lifecycle_WithImpl_Good(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestCommand_Duplicate_Bad(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
c.Command("deploy", Command{Action: func(_ Options) Result { return Result{OK: true} }})
|
||||
r := c.Command("deploy", Command{Action: func(_ Options) Result { return Result{OK: true} }})
|
||||
assert.False(t, r.OK)
|
||||
}
|
||||
|
||||
func TestCommand_InvalidPath_Bad(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
assert.False(t, c.Command("/leading", Command{}).OK)
|
||||
assert.False(t, c.Command("trailing/", Command{}).OK)
|
||||
assert.False(t, c.Command("double//slash", Command{}).OK)
|
||||
|
|
@ -193,7 +193,7 @@ func TestCommand_InvalidPath_Bad(t *testing.T) {
|
|||
// --- Cli Run with Lifecycle ---
|
||||
|
||||
func TestCli_Run_Lifecycle_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
lc := &testLifecycle{}
|
||||
c.Command("serve", Command{Lifecycle: lc})
|
||||
r := c.Cli().Run("serve")
|
||||
|
|
@ -202,7 +202,7 @@ func TestCli_Run_Lifecycle_Good(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestCli_Run_NoActionNoLifecycle_Bad(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
c.Command("empty", Command{})
|
||||
r := c.Cli().Run("empty")
|
||||
assert.False(t, r.OK)
|
||||
|
|
@ -211,7 +211,7 @@ func TestCli_Run_NoActionNoLifecycle_Bad(t *testing.T) {
|
|||
// --- Empty path ---
|
||||
|
||||
func TestCommand_EmptyPath_Bad(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
r := c.Command("", Command{})
|
||||
assert.False(t, r.OK)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import (
|
|||
// --- Config ---
|
||||
|
||||
func TestConfig_SetGet_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
c.Config().Set("api_url", "https://api.lthn.ai")
|
||||
c.Config().Set("max_agents", 5)
|
||||
|
||||
|
|
@ -20,14 +20,14 @@ func TestConfig_SetGet_Good(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestConfig_Get_Bad(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
r := c.Config().Get("missing")
|
||||
assert.False(t, r.OK)
|
||||
assert.Nil(t, r.Value)
|
||||
}
|
||||
|
||||
func TestConfig_TypedAccessors_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
c.Config().Set("url", "https://lthn.ai")
|
||||
c.Config().Set("port", 8080)
|
||||
c.Config().Set("debug", true)
|
||||
|
|
@ -38,7 +38,7 @@ func TestConfig_TypedAccessors_Good(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestConfig_TypedAccessors_Bad(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
// Missing keys return zero values
|
||||
assert.Equal(t, "", c.Config().String("missing"))
|
||||
assert.Equal(t, 0, c.Config().Int("missing"))
|
||||
|
|
@ -48,7 +48,7 @@ func TestConfig_TypedAccessors_Bad(t *testing.T) {
|
|||
// --- Feature Flags ---
|
||||
|
||||
func TestConfig_Features_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
c.Config().Enable("dark-mode")
|
||||
c.Config().Enable("beta")
|
||||
|
||||
|
|
@ -58,7 +58,7 @@ func TestConfig_Features_Good(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestConfig_Features_Disable_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
c.Config().Enable("feature")
|
||||
assert.True(t, c.Config().Enabled("feature"))
|
||||
|
||||
|
|
@ -67,14 +67,14 @@ func TestConfig_Features_Disable_Good(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestConfig_Features_CaseSensitive(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
c.Config().Enable("Feature")
|
||||
assert.True(t, c.Config().Enabled("Feature"))
|
||||
assert.False(t, c.Config().Enabled("feature"))
|
||||
}
|
||||
|
||||
func TestConfig_EnabledFeatures_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
c.Config().Enable("a")
|
||||
c.Config().Enable("b")
|
||||
c.Config().Enable("c")
|
||||
|
|
|
|||
83
contract.go
83
contract.go
|
|
@ -66,12 +66,28 @@ type ActionTaskCompleted struct {
|
|||
|
||||
// --- Constructor ---
|
||||
|
||||
// New creates a Core instance.
|
||||
// CoreOption is a functional option applied during Core construction.
|
||||
// Returns Result — if !OK, New() stops and returns the error.
|
||||
//
|
||||
// c := core.New(core.Options{
|
||||
// {Key: "name", Value: "myapp"},
|
||||
// })
|
||||
func New(opts ...Options) *Core {
|
||||
// core.New(
|
||||
// core.WithService(agentic.Register),
|
||||
// core.WithService(monitor.Register),
|
||||
// core.WithServiceLock(),
|
||||
// )
|
||||
type CoreOption func(*Core) Result
|
||||
|
||||
// New initialises a Core instance by applying options in order.
|
||||
// Services registered here form the application conclave — they share
|
||||
// IPC access and participate in the lifecycle (ServiceStartup/ServiceShutdown).
|
||||
//
|
||||
// r := core.New(
|
||||
// core.WithOptions(core.Options{{Key: "name", Value: "myapp"}}),
|
||||
// core.WithService(auth.Register),
|
||||
// core.WithServiceLock(),
|
||||
// )
|
||||
// if !r.OK { log.Fatal(r.Value) }
|
||||
// c := r.Value.(*Core)
|
||||
func New(opts ...CoreOption) Result {
|
||||
c := &Core{
|
||||
app: &App{},
|
||||
data: &Data{},
|
||||
|
|
@ -88,19 +104,54 @@ func New(opts ...Options) *Core {
|
|||
commands: &commandRegistry{commands: make(map[string]*Command)},
|
||||
}
|
||||
c.context, c.cancel = context.WithCancel(context.Background())
|
||||
c.cli = &Cli{core: c}
|
||||
|
||||
if len(opts) > 0 {
|
||||
cp := make(Options, len(opts[0]))
|
||||
copy(cp, opts[0])
|
||||
c.options = &cp
|
||||
name := cp.String("name")
|
||||
if name != "" {
|
||||
c.app.Name = name
|
||||
for _, opt := range opts {
|
||||
if r := opt(c); !r.OK {
|
||||
return r
|
||||
}
|
||||
}
|
||||
|
||||
// Init Cli surface with Core reference
|
||||
c.cli = &Cli{core: c}
|
||||
|
||||
return c
|
||||
return Result{c, true}
|
||||
}
|
||||
|
||||
// WithOptions applies key-value configuration to Core.
|
||||
//
|
||||
// core.WithOptions(core.Options{{Key: "name", Value: "myapp"}})
|
||||
func WithOptions(opts Options) CoreOption {
|
||||
return func(c *Core) Result {
|
||||
c.options = &opts
|
||||
if name := opts.String("name"); name != "" {
|
||||
c.app.Name = name
|
||||
}
|
||||
return Result{OK: true}
|
||||
}
|
||||
}
|
||||
|
||||
// WithService registers a service via its factory function.
|
||||
// The factory receives *Core so the service can wire IPC handlers
|
||||
// and access other subsystems during construction.
|
||||
// Service name is auto-discovered from the package path.
|
||||
// If the service implements HandleIPCEvents, it is auto-registered.
|
||||
//
|
||||
// core.WithService(agentic.Register)
|
||||
// core.WithService(display.Register(nil))
|
||||
func WithService(factory func(*Core) Result) CoreOption {
|
||||
return func(c *Core) Result {
|
||||
return factory(c)
|
||||
}
|
||||
}
|
||||
|
||||
// WithServiceLock prevents further service registration after construction.
|
||||
//
|
||||
// core.New(
|
||||
// core.WithService(auth.Register),
|
||||
// core.WithServiceLock(),
|
||||
// )
|
||||
func WithServiceLock() CoreOption {
|
||||
return func(c *Core) Result {
|
||||
c.LockEnable()
|
||||
c.LockApply()
|
||||
return Result{OK: true}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
60
core_test.go
60
core_test.go
|
|
@ -1,6 +1,7 @@
|
|||
package core_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
. "dappco.re/go/core"
|
||||
|
|
@ -10,26 +11,63 @@ import (
|
|||
// --- New ---
|
||||
|
||||
func TestNew_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
assert.NotNil(t, c)
|
||||
}
|
||||
|
||||
func TestNew_WithOptions_Good(t *testing.T) {
|
||||
c := New(Options{{Key: "name", Value: "myapp"}})
|
||||
c := New(WithOptions(Options{{Key: "name", Value: "myapp"}})).Value.(*Core)
|
||||
assert.NotNil(t, c)
|
||||
assert.Equal(t, "myapp", c.App().Name)
|
||||
}
|
||||
|
||||
func TestNew_WithOptions_Bad(t *testing.T) {
|
||||
// Empty options — should still create a valid Core
|
||||
c := New(Options{})
|
||||
c := New(WithOptions(Options{})).Value.(*Core)
|
||||
assert.NotNil(t, c)
|
||||
}
|
||||
|
||||
func TestNew_WithService_Good(t *testing.T) {
|
||||
started := false
|
||||
r := New(
|
||||
WithOptions(Options{{Key: "name", Value: "myapp"}}),
|
||||
WithService(func(c *Core) Result {
|
||||
c.Service("test", Service{
|
||||
OnStart: func() Result { started = true; return Result{OK: true} },
|
||||
})
|
||||
return Result{OK: true}
|
||||
}),
|
||||
)
|
||||
assert.True(t, r.OK)
|
||||
c := r.Value.(*Core)
|
||||
|
||||
svc := c.Service("test")
|
||||
assert.True(t, svc.OK)
|
||||
|
||||
c.ServiceStartup(context.Background(), nil)
|
||||
assert.True(t, started)
|
||||
}
|
||||
|
||||
func TestNew_WithServiceLock_Good(t *testing.T) {
|
||||
r := New(
|
||||
WithService(func(c *Core) Result {
|
||||
c.Service("allowed", Service{})
|
||||
return Result{OK: true}
|
||||
}),
|
||||
WithServiceLock(),
|
||||
)
|
||||
assert.True(t, r.OK)
|
||||
c := r.Value.(*Core)
|
||||
|
||||
// Registration after lock should fail
|
||||
reg := c.Service("blocked", Service{})
|
||||
assert.False(t, reg.OK)
|
||||
}
|
||||
|
||||
// --- Accessors ---
|
||||
|
||||
func TestAccessors_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
assert.NotNil(t, c.App())
|
||||
assert.NotNil(t, c.Data())
|
||||
assert.NotNil(t, c.Drive())
|
||||
|
|
@ -44,11 +82,11 @@ func TestAccessors_Good(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestOptions_Accessor_Good(t *testing.T) {
|
||||
c := New(Options{
|
||||
c := New(WithOptions(Options{
|
||||
{Key: "name", Value: "testapp"},
|
||||
{Key: "port", Value: 8080},
|
||||
{Key: "debug", Value: true},
|
||||
})
|
||||
})).Value.(*Core)
|
||||
opts := c.Options()
|
||||
assert.NotNil(t, opts)
|
||||
assert.Equal(t, "testapp", opts.String("name"))
|
||||
|
|
@ -57,7 +95,7 @@ func TestOptions_Accessor_Good(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestOptions_Accessor_Nil(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
// No options passed — Options() returns nil
|
||||
assert.Nil(t, c.Options())
|
||||
}
|
||||
|
|
@ -65,7 +103,7 @@ func TestOptions_Accessor_Nil(t *testing.T) {
|
|||
// --- Core Error/Log Helpers ---
|
||||
|
||||
func TestCore_LogError_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
cause := assert.AnError
|
||||
r := c.LogError(cause, "test.Operation", "something broke")
|
||||
assert.False(t, r.OK)
|
||||
|
|
@ -75,7 +113,7 @@ func TestCore_LogError_Good(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestCore_LogWarn_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
r := c.LogWarn(assert.AnError, "test.Operation", "heads up")
|
||||
assert.False(t, r.OK)
|
||||
_, ok := r.Value.(error)
|
||||
|
|
@ -83,14 +121,14 @@ func TestCore_LogWarn_Good(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestCore_Must_Ugly(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
assert.Panics(t, func() {
|
||||
c.Must(assert.AnError, "test.Operation", "fatal")
|
||||
})
|
||||
}
|
||||
|
||||
func TestCore_Must_Nil_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
assert.NotPanics(t, func() {
|
||||
c.Must(nil, "test.Operation", "no error")
|
||||
})
|
||||
|
|
|
|||
28
data_test.go
28
data_test.go
|
|
@ -15,7 +15,7 @@ var testFS embed.FS
|
|||
// --- Data (Embedded Content Mounts) ---
|
||||
|
||||
func TestData_New_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
r := c.Data().New(Options{
|
||||
{Key: "name", Value: "test"},
|
||||
{Key: "source", Value: testFS},
|
||||
|
|
@ -26,7 +26,7 @@ func TestData_New_Good(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestData_New_Bad(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
|
||||
r := c.Data().New(Options{{Key: "source", Value: testFS}})
|
||||
assert.False(t, r.OK)
|
||||
|
|
@ -39,7 +39,7 @@ func TestData_New_Bad(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestData_ReadString_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
c.Data().New(Options{{Key: "name", Value: "app"}, {Key: "source", Value: testFS}, {Key: "path", Value: "testdata"}})
|
||||
r := c.Data().ReadString("app/test.txt")
|
||||
assert.True(t, r.OK)
|
||||
|
|
@ -47,13 +47,13 @@ func TestData_ReadString_Good(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestData_ReadString_Bad(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
r := c.Data().ReadString("nonexistent/file.txt")
|
||||
assert.False(t, r.OK)
|
||||
}
|
||||
|
||||
func TestData_ReadFile_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
c.Data().New(Options{{Key: "name", Value: "app"}, {Key: "source", Value: testFS}, {Key: "path", Value: "testdata"}})
|
||||
r := c.Data().ReadFile("app/test.txt")
|
||||
assert.True(t, r.OK)
|
||||
|
|
@ -61,7 +61,7 @@ func TestData_ReadFile_Good(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestData_Get_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
c.Data().New(Options{{Key: "name", Value: "brain"}, {Key: "source", Value: testFS}, {Key: "path", Value: "testdata"}})
|
||||
gr := c.Data().Get("brain")
|
||||
assert.True(t, gr.OK)
|
||||
|
|
@ -76,13 +76,13 @@ func TestData_Get_Good(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestData_Get_Bad(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
r := c.Data().Get("nonexistent")
|
||||
assert.False(t, r.OK)
|
||||
}
|
||||
|
||||
func TestData_Mounts_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
c.Data().New(Options{{Key: "name", Value: "a"}, {Key: "source", Value: testFS}, {Key: "path", Value: "testdata"}})
|
||||
c.Data().New(Options{{Key: "name", Value: "b"}, {Key: "source", Value: testFS}, {Key: "path", Value: "testdata"}})
|
||||
mounts := c.Data().Mounts()
|
||||
|
|
@ -90,26 +90,26 @@ func TestData_Mounts_Good(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestEmbed_Legacy_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
c.Data().New(Options{{Key: "name", Value: "app"}, {Key: "source", Value: testFS}, {Key: "path", Value: "testdata"}})
|
||||
assert.NotNil(t, c.Embed())
|
||||
}
|
||||
|
||||
func TestData_List_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
c.Data().New(Options{{Key: "name", Value: "app"}, {Key: "source", Value: testFS}, {Key: "path", Value: "."}})
|
||||
r := c.Data().List("app/testdata")
|
||||
assert.True(t, r.OK)
|
||||
}
|
||||
|
||||
func TestData_List_Bad(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
r := c.Data().List("nonexistent/path")
|
||||
assert.False(t, r.OK)
|
||||
}
|
||||
|
||||
func TestData_ListNames_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
c.Data().New(Options{{Key: "name", Value: "app"}, {Key: "source", Value: testFS}, {Key: "path", Value: "."}})
|
||||
r := c.Data().ListNames("app/testdata")
|
||||
assert.True(t, r.OK)
|
||||
|
|
@ -117,14 +117,14 @@ func TestData_ListNames_Good(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestData_Extract_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
c.Data().New(Options{{Key: "name", Value: "app"}, {Key: "source", Value: testFS}, {Key: "path", Value: "."}})
|
||||
r := c.Data().Extract("app/testdata", t.TempDir(), nil)
|
||||
assert.True(t, r.OK)
|
||||
}
|
||||
|
||||
func TestData_Extract_Bad(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
r := c.Data().Extract("nonexistent/path", t.TempDir(), nil)
|
||||
assert.False(t, r.OK)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import (
|
|||
// --- Drive (Transport Handles) ---
|
||||
|
||||
func TestDrive_New_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
r := c.Drive().New(Options{
|
||||
{Key: "name", Value: "api"},
|
||||
{Key: "transport", Value: "https://api.lthn.ai"},
|
||||
|
|
@ -21,7 +21,7 @@ func TestDrive_New_Good(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestDrive_New_Bad(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
// Missing name
|
||||
r := c.Drive().New(Options{
|
||||
{Key: "transport", Value: "https://api.lthn.ai"},
|
||||
|
|
@ -30,7 +30,7 @@ func TestDrive_New_Bad(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestDrive_Get_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
c.Drive().New(Options{
|
||||
{Key: "name", Value: "ssh"},
|
||||
{Key: "transport", Value: "ssh://claude@10.69.69.165"},
|
||||
|
|
@ -42,20 +42,20 @@ func TestDrive_Get_Good(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestDrive_Get_Bad(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
r := c.Drive().Get("nonexistent")
|
||||
assert.False(t, r.OK)
|
||||
}
|
||||
|
||||
func TestDrive_Has_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
c.Drive().New(Options{{Key: "name", Value: "mcp"}, {Key: "transport", Value: "mcp://mcp.lthn.sh"}})
|
||||
assert.True(t, c.Drive().Has("mcp"))
|
||||
assert.False(t, c.Drive().Has("missing"))
|
||||
}
|
||||
|
||||
func TestDrive_Names_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
c.Drive().New(Options{{Key: "name", Value: "api"}, {Key: "transport", Value: "https://api.lthn.ai"}})
|
||||
c.Drive().New(Options{{Key: "name", Value: "ssh"}, {Key: "transport", Value: "ssh://claude@10.69.69.165"}})
|
||||
c.Drive().New(Options{{Key: "name", Value: "mcp"}, {Key: "transport", Value: "mcp://mcp.lthn.sh"}})
|
||||
|
|
@ -67,7 +67,7 @@ func TestDrive_Names_Good(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestDrive_OptionsPreserved_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
c.Drive().New(Options{
|
||||
{Key: "name", Value: "api"},
|
||||
{Key: "transport", Value: "https://api.lthn.ai"},
|
||||
|
|
|
|||
|
|
@ -102,7 +102,7 @@ func TestFormatStackTrace_Good(t *testing.T) {
|
|||
// --- ErrorLog ---
|
||||
|
||||
func TestErrorLog_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
cause := errors.New("boom")
|
||||
r := c.Log().Error(cause, "test.Operation", "something broke")
|
||||
assert.False(t, r.OK)
|
||||
|
|
@ -110,27 +110,27 @@ func TestErrorLog_Good(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestErrorLog_Nil_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
r := c.Log().Error(nil, "test.Operation", "no error")
|
||||
assert.True(t, r.OK)
|
||||
}
|
||||
|
||||
func TestErrorLog_Warn_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
cause := errors.New("warning")
|
||||
r := c.Log().Warn(cause, "test.Operation", "heads up")
|
||||
assert.False(t, r.OK)
|
||||
}
|
||||
|
||||
func TestErrorLog_Must_Ugly(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
assert.Panics(t, func() {
|
||||
c.Log().Must(errors.New("fatal"), "test.Operation", "must fail")
|
||||
})
|
||||
}
|
||||
|
||||
func TestErrorLog_Must_Nil_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
assert.NotPanics(t, func() {
|
||||
c.Log().Must(nil, "test.Operation", "no error")
|
||||
})
|
||||
|
|
@ -139,7 +139,7 @@ func TestErrorLog_Must_Nil_Good(t *testing.T) {
|
|||
// --- ErrorPanic ---
|
||||
|
||||
func TestErrorPanic_Recover_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
// Should not panic — Recover catches it
|
||||
assert.NotPanics(t, func() {
|
||||
defer c.Error().Recover()
|
||||
|
|
@ -148,7 +148,7 @@ func TestErrorPanic_Recover_Good(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestErrorPanic_SafeGo_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
done := make(chan bool, 1)
|
||||
c.Error().SafeGo(func() {
|
||||
done <- true
|
||||
|
|
@ -157,7 +157,7 @@ func TestErrorPanic_SafeGo_Good(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestErrorPanic_SafeGo_Panic_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
done := make(chan bool, 1)
|
||||
c.Error().SafeGo(func() {
|
||||
defer func() { done <- true }()
|
||||
|
|
@ -202,7 +202,7 @@ func TestErrorPanic_Reports_Good(t *testing.T) {
|
|||
path := dir + "/crashes.json"
|
||||
|
||||
// Create ErrorPanic with file output
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
// Access internals via a crash that writes to file
|
||||
// Since ErrorPanic fields are unexported, we test via Recover
|
||||
_ = c
|
||||
|
|
@ -221,7 +221,7 @@ func TestErrorPanic_CrashFile_Good(t *testing.T) {
|
|||
// error handling that writes crash reports
|
||||
|
||||
// For now, test that Reports handles missing file gracefully
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
r := c.Error().Reports(5)
|
||||
assert.False(t, r.OK)
|
||||
assert.Nil(t, r.Value)
|
||||
|
|
@ -260,13 +260,13 @@ func TestWrap_PreservesCode_Good(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestErrorLog_Warn_Nil_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
r := c.LogWarn(nil, "op", "msg")
|
||||
assert.True(t, r.OK)
|
||||
}
|
||||
|
||||
func TestErrorLog_Error_Nil_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
r := c.LogError(nil, "op", "msg")
|
||||
assert.True(t, r.OK)
|
||||
}
|
||||
|
|
|
|||
46
fs_test.go
46
fs_test.go
|
|
@ -15,7 +15,7 @@ import (
|
|||
|
||||
func TestFs_WriteRead_Good(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
|
||||
path := filepath.Join(dir, "test.txt")
|
||||
assert.True(t, c.Fs().Write(path, "hello core").OK)
|
||||
|
|
@ -26,21 +26,21 @@ func TestFs_WriteRead_Good(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestFs_Read_Bad(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
r := c.Fs().Read("/nonexistent/path/to/file.txt")
|
||||
assert.False(t, r.OK)
|
||||
}
|
||||
|
||||
func TestFs_EnsureDir_Good(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
path := filepath.Join(dir, "sub", "dir")
|
||||
assert.True(t, c.Fs().EnsureDir(path).OK)
|
||||
assert.True(t, c.Fs().IsDir(path))
|
||||
}
|
||||
|
||||
func TestFs_IsDir_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
dir := t.TempDir()
|
||||
assert.True(t, c.Fs().IsDir(dir))
|
||||
assert.False(t, c.Fs().IsDir(filepath.Join(dir, "nonexistent")))
|
||||
|
|
@ -49,7 +49,7 @@ func TestFs_IsDir_Good(t *testing.T) {
|
|||
|
||||
func TestFs_IsFile_Good(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
path := filepath.Join(dir, "test.txt")
|
||||
c.Fs().Write(path, "data")
|
||||
assert.True(t, c.Fs().IsFile(path))
|
||||
|
|
@ -59,7 +59,7 @@ func TestFs_IsFile_Good(t *testing.T) {
|
|||
|
||||
func TestFs_Exists_Good(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
path := filepath.Join(dir, "exists.txt")
|
||||
c.Fs().Write(path, "yes")
|
||||
assert.True(t, c.Fs().Exists(path))
|
||||
|
|
@ -69,7 +69,7 @@ func TestFs_Exists_Good(t *testing.T) {
|
|||
|
||||
func TestFs_List_Good(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
c.Fs().Write(filepath.Join(dir, "a.txt"), "a")
|
||||
c.Fs().Write(filepath.Join(dir, "b.txt"), "b")
|
||||
r := c.Fs().List(dir)
|
||||
|
|
@ -79,7 +79,7 @@ func TestFs_List_Good(t *testing.T) {
|
|||
|
||||
func TestFs_Stat_Good(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
path := filepath.Join(dir, "stat.txt")
|
||||
c.Fs().Write(path, "data")
|
||||
r := c.Fs().Stat(path)
|
||||
|
|
@ -89,7 +89,7 @@ func TestFs_Stat_Good(t *testing.T) {
|
|||
|
||||
func TestFs_Open_Good(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
path := filepath.Join(dir, "open.txt")
|
||||
c.Fs().Write(path, "content")
|
||||
r := c.Fs().Open(path)
|
||||
|
|
@ -99,7 +99,7 @@ func TestFs_Open_Good(t *testing.T) {
|
|||
|
||||
func TestFs_Create_Good(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
path := filepath.Join(dir, "sub", "created.txt")
|
||||
r := c.Fs().Create(path)
|
||||
assert.True(t, r.OK)
|
||||
|
|
@ -112,7 +112,7 @@ func TestFs_Create_Good(t *testing.T) {
|
|||
|
||||
func TestFs_Append_Good(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
path := filepath.Join(dir, "append.txt")
|
||||
c.Fs().Write(path, "first")
|
||||
r := c.Fs().Append(path)
|
||||
|
|
@ -126,7 +126,7 @@ func TestFs_Append_Good(t *testing.T) {
|
|||
|
||||
func TestFs_ReadStream_Good(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
path := filepath.Join(dir, "stream.txt")
|
||||
c.Fs().Write(path, "streamed")
|
||||
r := c.Fs().ReadStream(path)
|
||||
|
|
@ -136,7 +136,7 @@ func TestFs_ReadStream_Good(t *testing.T) {
|
|||
|
||||
func TestFs_WriteStream_Good(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
path := filepath.Join(dir, "sub", "ws.txt")
|
||||
r := c.Fs().WriteStream(path)
|
||||
assert.True(t, r.OK)
|
||||
|
|
@ -147,7 +147,7 @@ func TestFs_WriteStream_Good(t *testing.T) {
|
|||
|
||||
func TestFs_Delete_Good(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
path := filepath.Join(dir, "delete.txt")
|
||||
c.Fs().Write(path, "gone")
|
||||
assert.True(t, c.Fs().Delete(path).OK)
|
||||
|
|
@ -156,7 +156,7 @@ func TestFs_Delete_Good(t *testing.T) {
|
|||
|
||||
func TestFs_DeleteAll_Good(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
sub := filepath.Join(dir, "deep", "nested")
|
||||
c.Fs().EnsureDir(sub)
|
||||
c.Fs().Write(filepath.Join(sub, "file.txt"), "data")
|
||||
|
|
@ -166,7 +166,7 @@ func TestFs_DeleteAll_Good(t *testing.T) {
|
|||
|
||||
func TestFs_Rename_Good(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
old := filepath.Join(dir, "old.txt")
|
||||
nw := filepath.Join(dir, "new.txt")
|
||||
c.Fs().Write(old, "data")
|
||||
|
|
@ -177,7 +177,7 @@ func TestFs_Rename_Good(t *testing.T) {
|
|||
|
||||
func TestFs_WriteMode_Good(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
path := filepath.Join(dir, "secret.txt")
|
||||
assert.True(t, c.Fs().WriteMode(path, "secret", 0600).OK)
|
||||
r := c.Fs().Stat(path)
|
||||
|
|
@ -213,39 +213,39 @@ func TestFs_ZeroValue_List_Good(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestFs_Exists_NotFound_Bad(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
assert.False(t, c.Fs().Exists("/nonexistent/path/xyz"))
|
||||
}
|
||||
|
||||
// --- Fs path/validatePath edge cases ---
|
||||
|
||||
func TestFs_Read_EmptyPath_Ugly(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
r := c.Fs().Read("")
|
||||
assert.False(t, r.OK)
|
||||
}
|
||||
|
||||
func TestFs_Write_EmptyPath_Ugly(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
r := c.Fs().Write("", "data")
|
||||
assert.False(t, r.OK)
|
||||
}
|
||||
|
||||
func TestFs_Delete_Protected_Ugly(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
r := c.Fs().Delete("/")
|
||||
assert.False(t, r.OK)
|
||||
}
|
||||
|
||||
func TestFs_DeleteAll_Protected_Ugly(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
r := c.Fs().DeleteAll("/")
|
||||
assert.False(t, r.OK)
|
||||
}
|
||||
|
||||
func TestFs_ReadStream_WriteStream_Good(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
path := filepath.Join(dir, "stream.txt")
|
||||
c.Fs().Write(path, "streamed")
|
||||
|
||||
|
|
|
|||
18
i18n_test.go
18
i18n_test.go
|
|
@ -10,12 +10,12 @@ import (
|
|||
// --- I18n ---
|
||||
|
||||
func TestI18n_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
assert.NotNil(t, c.I18n())
|
||||
}
|
||||
|
||||
func TestI18n_AddLocales_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
r := c.Data().New(Options{
|
||||
{Key: "name", Value: "lang"},
|
||||
{Key: "source", Value: testFS},
|
||||
|
|
@ -30,7 +30,7 @@ func TestI18n_AddLocales_Good(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestI18n_Locales_Empty_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
r := c.I18n().Locales()
|
||||
assert.True(t, r.OK)
|
||||
assert.Empty(t, r.Value.([]*Embed))
|
||||
|
|
@ -39,7 +39,7 @@ func TestI18n_Locales_Empty_Good(t *testing.T) {
|
|||
// --- Translator (no translator registered) ---
|
||||
|
||||
func TestI18n_Translate_NoTranslator_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
// Without a translator, Translate returns the key as-is
|
||||
r := c.I18n().Translate("greeting.hello")
|
||||
assert.True(t, r.OK)
|
||||
|
|
@ -47,24 +47,24 @@ func TestI18n_Translate_NoTranslator_Good(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestI18n_SetLanguage_NoTranslator_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
r := c.I18n().SetLanguage("de")
|
||||
assert.True(t, r.OK) // no-op without translator
|
||||
}
|
||||
|
||||
func TestI18n_Language_NoTranslator_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
assert.Equal(t, "en", c.I18n().Language())
|
||||
}
|
||||
|
||||
func TestI18n_AvailableLanguages_NoTranslator_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
langs := c.I18n().AvailableLanguages()
|
||||
assert.Equal(t, []string{"en"}, langs)
|
||||
}
|
||||
|
||||
func TestI18n_Translator_Nil_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
assert.False(t, c.I18n().Translator().OK)
|
||||
}
|
||||
|
||||
|
|
@ -82,7 +82,7 @@ func (m *mockTranslator) Language() string { return m.lang }
|
|||
func (m *mockTranslator) AvailableLanguages() []string { return []string{"en", "de", "fr"} }
|
||||
|
||||
func TestI18n_WithTranslator_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
tr := &mockTranslator{lang: "en"}
|
||||
c.I18n().SetTranslator(tr)
|
||||
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ func TestEnv_Unknown(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestEnv_CoreInstance(t *testing.T) {
|
||||
c := core.New()
|
||||
c := core.New().Value.(*core.Core)
|
||||
assert.Equal(t, core.Env("OS"), c.Env("OS"))
|
||||
assert.Equal(t, core.Env("DIR_HOME"), c.Env("DIR_HOME"))
|
||||
}
|
||||
|
|
|
|||
14
ipc_test.go
14
ipc_test.go
|
|
@ -12,7 +12,7 @@ import (
|
|||
type testMessage struct{ payload string }
|
||||
|
||||
func TestAction_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
var received Message
|
||||
c.RegisterAction(func(_ *Core, msg Message) Result {
|
||||
received = msg
|
||||
|
|
@ -24,7 +24,7 @@ func TestAction_Good(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestAction_Multiple_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
count := 0
|
||||
handler := func(_ *Core, _ Message) Result { count++; return Result{OK: true} }
|
||||
c.RegisterActions(handler, handler, handler)
|
||||
|
|
@ -33,7 +33,7 @@ func TestAction_Multiple_Good(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestAction_None_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
// No handlers registered — should succeed
|
||||
r := c.ACTION(nil)
|
||||
assert.True(t, r.OK)
|
||||
|
|
@ -42,7 +42,7 @@ func TestAction_None_Good(t *testing.T) {
|
|||
// --- IPC: Queries ---
|
||||
|
||||
func TestQuery_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
c.RegisterQuery(func(_ *Core, q Query) Result {
|
||||
if q == "ping" {
|
||||
return Result{Value: "pong", OK: true}
|
||||
|
|
@ -55,7 +55,7 @@ func TestQuery_Good(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestQuery_Unhandled_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
c.RegisterQuery(func(_ *Core, q Query) Result {
|
||||
return Result{}
|
||||
})
|
||||
|
|
@ -64,7 +64,7 @@ func TestQuery_Unhandled_Good(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestQueryAll_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
c.RegisterQuery(func(_ *Core, _ Query) Result {
|
||||
return Result{Value: "a", OK: true}
|
||||
})
|
||||
|
|
@ -82,7 +82,7 @@ func TestQueryAll_Good(t *testing.T) {
|
|||
// --- IPC: Tasks ---
|
||||
|
||||
func TestPerform_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
c.RegisterTask(func(_ *Core, t Task) Result {
|
||||
if t == "compute" {
|
||||
return Result{Value: 42, OK: true}
|
||||
|
|
|
|||
12
lock_test.go
12
lock_test.go
|
|
@ -8,28 +8,28 @@ import (
|
|||
)
|
||||
|
||||
func TestLock_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
lock := c.Lock("test")
|
||||
assert.NotNil(t, lock)
|
||||
assert.NotNil(t, lock.Mutex)
|
||||
}
|
||||
|
||||
func TestLock_SameName_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
l1 := c.Lock("shared")
|
||||
l2 := c.Lock("shared")
|
||||
assert.Equal(t, l1, l2)
|
||||
}
|
||||
|
||||
func TestLock_DifferentName_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
l1 := c.Lock("a")
|
||||
l2 := c.Lock("b")
|
||||
assert.NotEqual(t, l1, l2)
|
||||
}
|
||||
|
||||
func TestLockEnable_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
c.Service("early", Service{})
|
||||
c.LockEnable()
|
||||
c.LockApply()
|
||||
|
|
@ -39,7 +39,7 @@ func TestLockEnable_Good(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestStartables_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
c.Service("s", Service{OnStart: func() Result { return Result{OK: true} }})
|
||||
r := c.Startables()
|
||||
assert.True(t, r.OK)
|
||||
|
|
@ -47,7 +47,7 @@ func TestStartables_Good(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestStoppables_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
c.Service("s", Service{OnStop: func() Result { return Result{OK: true} }})
|
||||
r := c.Stoppables()
|
||||
assert.True(t, r.OK)
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ func TestLog_LevelString_Good(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestLog_CoreLog_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
assert.NotNil(t, c.Log())
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -106,7 +106,11 @@ type ServiceFactory func() Result
|
|||
|
||||
// NewWithFactories creates a Runtime with the provided service factories.
|
||||
func NewWithFactories(app any, factories map[string]ServiceFactory) Result {
|
||||
c := New(Options{{Key: "name", Value: "core"}})
|
||||
r := New(WithOptions(Options{{Key: "name", Value: "core"}}))
|
||||
if !r.OK {
|
||||
return r
|
||||
}
|
||||
c := r.Value.(*Core)
|
||||
c.app.Runtime = app
|
||||
|
||||
names := slices.Sorted(maps.Keys(factories))
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ type testOpts struct {
|
|||
}
|
||||
|
||||
func TestServiceRuntime_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
opts := testOpts{URL: "https://api.lthn.ai", Timeout: 30}
|
||||
rt := NewServiceRuntime(c, opts)
|
||||
|
||||
|
|
@ -102,7 +102,7 @@ func TestRuntime_ServiceShutdown_NilCore_Good(t *testing.T) {
|
|||
|
||||
func TestCore_ServiceShutdown_Good(t *testing.T) {
|
||||
stopped := false
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
c.Service("test", Service{
|
||||
OnStart: func() Result { return Result{OK: true} },
|
||||
OnStop: func() Result { stopped = true; return Result{OK: true} },
|
||||
|
|
@ -114,7 +114,7 @@ func TestCore_ServiceShutdown_Good(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestCore_Context_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
c.ServiceStartup(context.Background(), nil)
|
||||
assert.NotNil(t, c.Context())
|
||||
c.ServiceShutdown(context.Background())
|
||||
|
|
|
|||
|
|
@ -10,26 +10,26 @@ import (
|
|||
// --- Service Registration ---
|
||||
|
||||
func TestService_Register_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
r := c.Service("auth", Service{})
|
||||
assert.True(t, r.OK)
|
||||
}
|
||||
|
||||
func TestService_Register_Duplicate_Bad(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
c.Service("auth", Service{})
|
||||
r := c.Service("auth", Service{})
|
||||
assert.False(t, r.OK)
|
||||
}
|
||||
|
||||
func TestService_Register_Empty_Bad(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
r := c.Service("", Service{})
|
||||
assert.False(t, r.OK)
|
||||
}
|
||||
|
||||
func TestService_Get_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
c.Service("brain", Service{OnStart: func() Result { return Result{OK: true} }})
|
||||
r := c.Service("brain")
|
||||
assert.True(t, r.OK)
|
||||
|
|
@ -37,13 +37,13 @@ func TestService_Get_Good(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestService_Get_Bad(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
r := c.Service("nonexistent")
|
||||
assert.False(t, r.OK)
|
||||
}
|
||||
|
||||
func TestService_Names_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
c.Service("a", Service{})
|
||||
c.Service("b", Service{})
|
||||
names := c.Services()
|
||||
|
|
@ -55,7 +55,7 @@ func TestService_Names_Good(t *testing.T) {
|
|||
// --- Service Lifecycle ---
|
||||
|
||||
func TestService_Lifecycle_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
started := false
|
||||
stopped := false
|
||||
c.Service("lifecycle", Service{
|
||||
|
|
|
|||
14
task_test.go
14
task_test.go
|
|
@ -13,7 +13,7 @@ import (
|
|||
// --- PerformAsync ---
|
||||
|
||||
func TestPerformAsync_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
var mu sync.Mutex
|
||||
var result string
|
||||
|
||||
|
|
@ -37,7 +37,7 @@ func TestPerformAsync_Good(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestPerformAsync_Progress_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
c.RegisterTask(func(_ *Core, task Task) Result {
|
||||
return Result{OK: true}
|
||||
})
|
||||
|
|
@ -48,7 +48,7 @@ func TestPerformAsync_Progress_Good(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestPerformAsync_Completion_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
completed := make(chan ActionTaskCompleted, 1)
|
||||
|
||||
c.RegisterTask(func(_ *Core, task Task) Result {
|
||||
|
|
@ -73,7 +73,7 @@ func TestPerformAsync_Completion_Good(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestPerformAsync_NoHandler_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
completed := make(chan ActionTaskCompleted, 1)
|
||||
|
||||
c.RegisterAction(func(_ *Core, msg Message) Result {
|
||||
|
|
@ -94,7 +94,7 @@ func TestPerformAsync_NoHandler_Good(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestPerformAsync_AfterShutdown_Bad(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
c.ServiceStartup(context.Background(), nil)
|
||||
c.ServiceShutdown(context.Background())
|
||||
|
||||
|
|
@ -105,7 +105,7 @@ func TestPerformAsync_AfterShutdown_Bad(t *testing.T) {
|
|||
// --- RegisterAction + RegisterActions ---
|
||||
|
||||
func TestRegisterAction_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
called := false
|
||||
c.RegisterAction(func(_ *Core, _ Message) Result {
|
||||
called = true
|
||||
|
|
@ -116,7 +116,7 @@ func TestRegisterAction_Good(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestRegisterActions_Good(t *testing.T) {
|
||||
c := New()
|
||||
c := New().Value.(*Core)
|
||||
count := 0
|
||||
h := func(_ *Core, _ Message) Result { count++; return Result{OK: true} }
|
||||
c.RegisterActions(h, h)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue