Files
goclaw/internal/tools/delegate_tool_hooks_test.go
viettranx 83e4f8b907 fix(tests): add ResetStuckSummoning stub to agent store mocks
Add missing ResetStuckSummoning method stub to mock AgentStore
implementations in test files. Follows from recent agent recovery
feature (d7342fbd) which added this method to the AgentStore interface
but missed updating these 4 test mock implementations.
2026-04-17 19:50:11 +07:00

176 lines
5.6 KiB
Go

package tools
import (
"context"
"testing"
"github.com/google/uuid"
"github.com/nextlevelbuilder/goclaw/internal/hooks"
"github.com/nextlevelbuilder/goclaw/internal/store"
)
// fakeDispatcher records Fire calls and returns a preset decision.
type fakeDispatcher struct {
decision hooks.Decision
calls int
}
func (f *fakeDispatcher) Fire(_ context.Context, _ hooks.Event) (hooks.FireResult, error) {
f.calls++
return hooks.FireResult{Decision: f.decision}, nil
}
// --- minimal noop stores to satisfy AgentLinkStore / AgentCRUDStore ---
type noopAgentLink struct{}
func (noopAgentLink) CreateLink(_ context.Context, _ *store.AgentLinkData) error { return nil }
func (noopAgentLink) DeleteLink(_ context.Context, _ uuid.UUID) error { return nil }
func (noopAgentLink) UpdateLink(_ context.Context, _ uuid.UUID, _ map[string]any) error {
return nil
}
func (noopAgentLink) GetLink(_ context.Context, _ uuid.UUID) (*store.AgentLinkData, error) {
return nil, nil
}
func (noopAgentLink) ListLinksFrom(_ context.Context, _ uuid.UUID) ([]store.AgentLinkData, error) {
return nil, nil
}
func (noopAgentLink) ListLinksTo(_ context.Context, _ uuid.UUID) ([]store.AgentLinkData, error) {
return nil, nil
}
func (noopAgentLink) CanDelegate(_ context.Context, _, _ uuid.UUID) (bool, error) {
return true, nil
}
func (noopAgentLink) GetLinkBetween(_ context.Context, _, _ uuid.UUID) (*store.AgentLinkData, error) {
return nil, nil
}
func (noopAgentLink) DelegateTargets(_ context.Context, _ uuid.UUID) ([]store.AgentLinkData, error) {
return nil, nil
}
func (noopAgentLink) SearchDelegateTargets(_ context.Context, _ uuid.UUID, _ string, _ int) ([]store.AgentLinkData, error) {
return nil, nil
}
func (noopAgentLink) SearchDelegateTargetsByEmbedding(_ context.Context, _ uuid.UUID, _ []float32, _ int) ([]store.AgentLinkData, error) {
return nil, nil
}
func (noopAgentLink) DeleteTeamLinksForAgent(_ context.Context, _, _ uuid.UUID) error { return nil }
type noopAgentCRUD struct {
keyToID map[string]uuid.UUID
}
func (n noopAgentCRUD) Create(_ context.Context, _ *store.AgentData) error { return nil }
func (n noopAgentCRUD) GetByKey(_ context.Context, key string) (*store.AgentData, error) {
id, ok := n.keyToID[key]
if !ok {
id = uuid.New()
}
return &store.AgentData{BaseModel: store.BaseModel{ID: id}, AgentKey: key}, nil
}
func (n noopAgentCRUD) GetByID(_ context.Context, _ uuid.UUID) (*store.AgentData, error) {
return nil, nil
}
func (n noopAgentCRUD) GetByIDUnscoped(_ context.Context, _ uuid.UUID) (*store.AgentData, error) {
return nil, nil
}
func (n noopAgentCRUD) GetByKeys(_ context.Context, _ []string) ([]store.AgentData, error) {
return nil, nil
}
func (n noopAgentCRUD) GetByIDs(_ context.Context, _ []uuid.UUID) ([]store.AgentData, error) {
return nil, nil
}
func (n noopAgentCRUD) Update(_ context.Context, _ uuid.UUID, _ map[string]any) error { return nil }
func (n noopAgentCRUD) Delete(_ context.Context, _ uuid.UUID) error { return nil }
func (n noopAgentCRUD) List(_ context.Context, _ string) ([]store.AgentData, error) { return nil, nil }
func (n noopAgentCRUD) GetDefault(_ context.Context) (*store.AgentData, error) { return nil, nil }
func (n noopAgentCRUD) ResetStuckSummoning(_ context.Context) (int64, error) { return 0, nil }
// --- helpers ---
func makeDelegateCtx() context.Context {
ctx := store.WithAgentID(context.Background(), uuid.New())
ctx = store.WithTenantID(ctx, uuid.New())
ctx = store.WithAgentKey(ctx, "parent-agent")
return ctx
}
// --- tests ---
func TestDelegateTool_SubagentStartBlock_AbortsDispatch(t *testing.T) {
runCalled := 0
runFn := func(_ context.Context, _ DelegateRequest) (DelegateResult, error) {
runCalled++
return DelegateResult{Content: "ok"}, nil
}
tool := NewDelegateTool(noopAgentLink{}, noopAgentCRUD{}, nil, runFn)
disp := &fakeDispatcher{decision: hooks.DecisionBlock}
tool.SetHookDispatcher(disp)
result := tool.Execute(makeDelegateCtx(), map[string]any{
"agent_key": "child-agent",
"task": "do something",
"mode": "sync",
})
if result == nil || !result.IsError {
t.Fatal("expected error result when hook blocks")
}
if runCalled != 0 {
t.Errorf("runFn must not be called; got %d calls", runCalled)
}
if disp.calls != 1 {
t.Errorf("expected 1 dispatcher Fire call; got %d", disp.calls)
}
}
func TestDelegateTool_SubagentStartAllow_ProceedsToRun(t *testing.T) {
runCalled := 0
runFn := func(_ context.Context, _ DelegateRequest) (DelegateResult, error) {
runCalled++
return DelegateResult{Content: "done"}, nil
}
tool := NewDelegateTool(noopAgentLink{}, noopAgentCRUD{}, nil, runFn)
disp := &fakeDispatcher{decision: hooks.DecisionAllow}
tool.SetHookDispatcher(disp)
result := tool.Execute(makeDelegateCtx(), map[string]any{
"agent_key": "child-agent",
"task": "do something",
"mode": "sync",
})
if result != nil && result.IsError {
t.Fatalf("unexpected error: %s", result.ForLLM)
}
if runCalled != 1 {
t.Errorf("expected runFn called once; got %d", runCalled)
}
}
func TestDelegateTool_NilDispatcher_SkipsHook(t *testing.T) {
runCalled := 0
runFn := func(_ context.Context, _ DelegateRequest) (DelegateResult, error) {
runCalled++
return DelegateResult{Content: "done"}, nil
}
// No SetHookDispatcher — hookDispatcher stays nil.
tool := NewDelegateTool(noopAgentLink{}, noopAgentCRUD{}, nil, runFn)
result := tool.Execute(makeDelegateCtx(), map[string]any{
"agent_key": "child-agent",
"task": "do something",
"mode": "sync",
})
if result != nil && result.IsError {
t.Fatalf("unexpected error: %s", result.ForLLM)
}
if runCalled != 1 {
t.Errorf("expected runFn called once; got %d", runCalled)
}
}