Files
HomeAgent/internal/plugin/sdk/bus_test.go
root 3e3c6a24d2 v4 architecture: pipeline stages, SDK, event bus, LLM-driven memory consolidation
- SDK PluginAPI (internal/plugin/sdk/): RegisterTool/RegisterStage/Subscribe/Publish
- EventBus (internal/events/): system-level pub/sub with wildcard support
- StageHost (internal/agent/core/stages.go): 7-stage message pipeline
- Agent core: on_input/pre_action/post_action/before_toolcall/after_toolcall/before_output/after_output
- Plugin Registry: SDK plugin registration and tool routing
- GraphDB.MergeEntities: entity consolidation with relation redirection
- memory_merge tool: allows LLM to merge similar entities
- Consolidation task: heartbeat detects conflicts, enqueues via IO for LLM decision
- _consolidation_ internal channel for system-level memory maintenance
- Comprehensive documentation: ARCHITECTURE.md, PLAN.md, DESIGN.md, README.md
- 54 tests across all packages, all passing
2026-07-03 08:04:39 +08:00

134 lines
2.9 KiB
Go

package sdk
import (
"testing"
)
func TestInProcessBus(t *testing.T) {
bus := NewInProcessBus()
var called bool
bus.Subscribe(EventRawInput, func(evt *Event) {
called = true
if evt.Source != "test" {
t.Errorf("expected source test, got %s", evt.Source)
}
})
bus.Publish(&Event{
Type: EventRawInput,
Source: "test",
})
if !called {
t.Error("handler was not called")
}
}
func TestInProcessBusWildcard(t *testing.T) {
bus := NewInProcessBus()
count := 0
bus.Subscribe(EventAll, func(evt *Event) {
count++
})
bus.Publish(&Event{Type: EventRawInput, Source: "s1"})
bus.Publish(&Event{Type: EventToolCall, Source: "s2"})
if count != 2 {
t.Errorf("expected 2, got %d", count)
}
}
func TestPluginAPI(t *testing.T) {
bus := NewInProcessBus()
api := NewPluginAPI("test", "1.0.0", bus, nil, nil)
if api.Name != "test" {
t.Errorf("expected test, got %s", api.Name)
}
if api.Version != "1.0.0" {
t.Errorf("expected 1.0.0, got %s", api.Version)
}
var stageCalled bool
api.RegisterStage(StageOnInput, func(ctx *StageContext) error {
stageCalled = true
if ctx.RawMessage != "hello" {
t.Errorf("expected hello, got %s", ctx.RawMessage)
}
return nil
})
ctx := &StageContext{RawMessage: "hello"}
for _, handler := range api.StageHandlers(StageOnInput) {
handler(ctx)
}
if !stageCalled {
t.Error("stage handler was not called")
}
}
func TestPluginAPITool(t *testing.T) {
api := NewPluginAPI("test", "1.0.0", nil, nil, nil)
err := api.RegisterTool("test_tool", func(args map[string]interface{}) (interface{}, error) {
return "ok", nil
})
if err != nil {
t.Fatalf("register tool: %v", err)
}
if _, ok := api.Tools()["test_tool"]; !ok {
t.Error("tool not found")
}
// duplicate registration should fail
err = api.RegisterTool("test_tool", func(args map[string]interface{}) (interface{}, error) {
return "ok", nil
})
if err == nil {
t.Error("expected error on duplicate tool registration")
}
}
func TestPluginAPIStageShortCircuit(t *testing.T) {
api := NewPluginAPI("test", "1.0.0", nil, nil, nil)
api.RegisterStage(StageOnInput, func(ctx *StageContext) error {
resp := "intercepted"
ctx.Response = &resp
return nil
})
ctx := &StageContext{RawMessage: "hello"}
handlers := api.StageHandlers(StageOnInput)
if len(handlers) != 1 {
t.Fatalf("expected 1 handler, got %d", len(handlers))
}
handlers[0](ctx)
if ctx.Response == nil || *ctx.Response != "intercepted" {
t.Errorf("expected intercepted, got %v", ctx.Response)
}
}
func TestAllStages(t *testing.T) {
stages := AllStages()
expected := []Stage{
StageOnInput, StagePreAction, StagePostAction,
StageBeforeToolcall, StageAfterToolcall,
StageBeforeOutput, StageAfterOutput,
}
for _, s := range expected {
if !stages[s] {
t.Errorf("missing stage: %s", s)
}
}
if len(stages) != len(expected) {
t.Errorf("expected %d stages, got %d", len(expected), len(stages))
}
}