go/ipc_test.go
Snider 2dff772a40 feat: implement RFC plans 1-5 — Registry[T], Action/Task, Process, primitives
Plans 1-5 complete for core/go scope. 456 tests, 84.4% coverage, 100% AX-7 naming.

Critical bugs (Plan 1):
- P4-3+P7-3: ACTION broadcast calls all handlers with panic recovery
- P7-2+P7-4: RunE() with defer ServiceShutdown, Run() delegates
- P3-1: Startable/Stoppable return Result (breaking, clean)
- P9-1: Zero os/exec — App.Find() rewritten with os.Stat+PATH
- I3: Embed() removed, I15: New() comment fixed
- I9: CommandLifecycle removed → Command.Managed field

Registry[T] (Plan 2):
- Universal thread-safe named collection with 3 lock modes
- All 5 registries migrated: services, commands, drive, data, lock
- Insertion order preserved (fixes P4-1)
- c.RegistryOf("name") cross-cutting accessor

Action/Task system (Plan 3):
- Action type with Run()/Exists(), ActionHandler signature
- c.Action("name") dual-purpose accessor (register/invoke)
- TaskDef with Steps — sequential chain, async dispatch, previous-input piping
- Panic recovery on all Action execution
- broadcast() internal, ACTION() sugar

Process primitive (Plan 4):
- c.Process() returns Action sugar — Run/RunIn/RunWithEnv/Start/Kill/Exists
- No deps added — delegates to c.Action("process.*")
- Permission-by-registration: no handler = no capability

Missing primitives (Plan 5):
- core.ID() — atomic counter + crypto/rand suffix
- ValidateName() / SanitisePath() — reusable validation
- Fs.WriteAtomic() — write-to-temp-then-rename
- Fs.NewUnrestricted() / Fs.Root() — legitimate sandbox bypass
- AX-7: 456/456 tests renamed to TestFile_Function_{Good,Bad,Ugly}

Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-25 15:18:25 +00:00

144 lines
3.5 KiB
Go

package core_test
import (
"testing"
. "dappco.re/go/core"
"github.com/stretchr/testify/assert"
)
// --- IPC: Actions ---
type testMessage struct{ payload string }
func TestAction_Good(t *testing.T) {
c := New()
var received Message
c.RegisterAction(func(_ *Core, msg Message) Result {
received = msg
return Result{OK: true}
})
r := c.ACTION(testMessage{payload: "hello"})
assert.True(t, r.OK)
assert.Equal(t, testMessage{payload: "hello"}, received)
}
func TestAction_Multiple_Good(t *testing.T) {
c := New()
count := 0
handler := func(_ *Core, _ Message) Result { count++; return Result{OK: true} }
c.RegisterActions(handler, handler, handler)
c.ACTION(nil)
assert.Equal(t, 3, count)
}
func TestAction_None_Good(t *testing.T) {
c := New()
// No handlers registered — should succeed
r := c.ACTION(nil)
assert.True(t, r.OK)
}
func TestAction_Bad_HandlerFails(t *testing.T) {
c := New()
c.RegisterAction(func(_ *Core, _ Message) Result {
return Result{Value: NewError("intentional"), OK: false}
})
// ACTION is broadcast — even with a failing handler, dispatch succeeds
r := c.ACTION(testMessage{payload: "test"})
assert.True(t, r.OK)
}
func TestAction_Ugly_HandlerFailsChainContinues(t *testing.T) {
c := New()
var order []int
c.RegisterAction(func(_ *Core, _ Message) Result {
order = append(order, 1)
return Result{OK: true}
})
c.RegisterAction(func(_ *Core, _ Message) Result {
order = append(order, 2)
return Result{Value: NewError("handler 2 fails"), OK: false}
})
c.RegisterAction(func(_ *Core, _ Message) Result {
order = append(order, 3)
return Result{OK: true}
})
r := c.ACTION(testMessage{payload: "test"})
assert.True(t, r.OK)
assert.Equal(t, []int{1, 2, 3}, order, "all 3 handlers must fire even when handler 2 returns !OK")
}
func TestAction_Ugly_HandlerPanicsChainContinues(t *testing.T) {
c := New()
var order []int
c.RegisterAction(func(_ *Core, _ Message) Result {
order = append(order, 1)
return Result{OK: true}
})
c.RegisterAction(func(_ *Core, _ Message) Result {
panic("handler 2 explodes")
})
c.RegisterAction(func(_ *Core, _ Message) Result {
order = append(order, 3)
return Result{OK: true}
})
r := c.ACTION(testMessage{payload: "test"})
assert.True(t, r.OK)
assert.Equal(t, []int{1, 3}, order, "handlers 1 and 3 must fire even when handler 2 panics")
}
// --- IPC: Queries ---
func TestIpc_Query_Good(t *testing.T) {
c := New()
c.RegisterQuery(func(_ *Core, q Query) Result {
if q == "ping" {
return Result{Value: "pong", OK: true}
}
return Result{}
})
r := c.QUERY("ping")
assert.True(t, r.OK)
assert.Equal(t, "pong", r.Value)
}
func TestIpc_Query_Unhandled_Good(t *testing.T) {
c := New()
c.RegisterQuery(func(_ *Core, q Query) Result {
return Result{}
})
r := c.QUERY("unknown")
assert.False(t, r.OK)
}
func TestIpc_QueryAll_Good(t *testing.T) {
c := New()
c.RegisterQuery(func(_ *Core, _ Query) Result {
return Result{Value: "a", OK: true}
})
c.RegisterQuery(func(_ *Core, _ Query) Result {
return Result{Value: "b", OK: true}
})
r := c.QUERYALL("anything")
assert.True(t, r.OK)
results := r.Value.([]any)
assert.Len(t, results, 2)
assert.Contains(t, results, "a")
assert.Contains(t, results, "b")
}
// --- IPC: Tasks ---
func TestIpc_Perform_Good(t *testing.T) {
c := New()
c.RegisterTask(func(_ *Core, t Task) Result {
if t == "compute" {
return Result{Value: 42, OK: true}
}
return Result{}
})
r := c.PERFORM("compute")
assert.True(t, r.OK)
assert.Equal(t, 42, r.Value)
}