Files
kandev/apps/backend/internal/agent/registry/registry_test.go
T
2026-02-16 20:38:01 +00:00

333 lines
8.5 KiB
Go

package registry
import (
"context"
"testing"
"time"
"github.com/kandev/kandev/internal/agent/agents"
"github.com/kandev/kandev/internal/agentctl/server/adapter"
"github.com/kandev/kandev/internal/common/logger"
)
// testAgent is a minimal implementation of agents.Agent for testing.
type testAgent struct {
id string
name string
description string
enabled bool
displayOrder int
runtime *agents.RuntimeConfig
}
func (a *testAgent) ID() string { return a.id }
func (a *testAgent) Name() string { return a.name }
func (a *testAgent) DisplayName() string { return a.name }
func (a *testAgent) Description() string { return a.description }
func (a *testAgent) Enabled() bool { return a.enabled }
func (a *testAgent) DisplayOrder() int { return a.displayOrder }
func (a *testAgent) Logo(agents.LogoVariant) []byte { return nil }
func (a *testAgent) IsInstalled(context.Context) (*agents.DiscoveryResult, error) {
return nil, agents.ErrNotSupported
}
func (a *testAgent) DefaultModel() string { return "" }
func (a *testAgent) ListModels(context.Context) (*agents.ModelList, error) {
return nil, agents.ErrNotSupported
}
func (a *testAgent) CreateAdapter(*adapter.Config, *logger.Logger) (adapter.AgentAdapter, error) {
return nil, agents.ErrNotSupported
}
func (a *testAgent) BuildCommand(agents.CommandOptions) agents.Command { return agents.Command{} }
func (a *testAgent) PermissionSettings() map[string]agents.PermissionSetting {
return nil
}
func (a *testAgent) Runtime() *agents.RuntimeConfig { return a.runtime }
func newTestLogger() *logger.Logger {
log, _ := logger.NewLogger(logger.LoggingConfig{
Level: "error",
Format: "json",
})
return log
}
func validAgentConfig(id, name string) *testAgent {
return &testAgent{
id: id,
name: name,
description: "Test agent",
enabled: true,
runtime: &agents.RuntimeConfig{
Image: "test/image",
Tag: "latest",
WorkingDir: "/workspace",
ResourceLimits: agents.ResourceLimits{
MemoryMB: 1024,
CPUCores: 1.0,
Timeout: time.Hour,
},
Capabilities: []string{"test"},
},
}
}
func TestNewRegistry(t *testing.T) {
log := newTestLogger()
reg := NewRegistry(log)
if reg == nil {
t.Fatal("expected non-nil registry")
} else if len(reg.agents) != 0 {
t.Errorf("expected empty agents map, got %d", len(reg.agents))
}
}
func TestRegistry_Register(t *testing.T) {
log := newTestLogger()
reg := NewRegistry(log)
ag := validAgentConfig("test-agent", "Test Agent")
// Test successful registration
err := reg.Register(ag)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
// Test duplicate registration
err = reg.Register(ag)
if err == nil {
t.Error("expected error for duplicate registration")
}
}
func TestRegistry_RegisterValidation(t *testing.T) {
log := newTestLogger()
reg := NewRegistry(log)
tests := []struct {
name string
agent agents.Agent
errMsg string
}{
{
name: "empty ID",
agent: &testAgent{id: "", name: "test"},
errMsg: "agent type ID is required",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := reg.Register(tt.agent)
if err == nil {
t.Error("expected error")
} else if err.Error() != tt.errMsg {
t.Errorf("expected error %q, got %q", tt.errMsg, err.Error())
}
})
}
}
func TestRegistry_Get(t *testing.T) {
log := newTestLogger()
reg := NewRegistry(log)
ag := validAgentConfig("test-agent", "Test Agent")
_ = reg.Register(ag)
// Test successful get
got, ok := reg.Get("test-agent")
if !ok {
t.Fatal("expected agent to be found")
}
if got.ID() != ag.ID() {
t.Errorf("expected ID %q, got %q", ag.ID(), got.ID())
}
// Test not found
_, ok = reg.Get("non-existent")
if ok {
t.Error("expected agent to not be found")
}
}
func TestRegistry_Unregister(t *testing.T) {
log := newTestLogger()
reg := NewRegistry(log)
ag := validAgentConfig("test-agent", "Test Agent")
_ = reg.Register(ag)
// Test successful unregister
err := reg.Unregister("test-agent")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
// Verify it's gone
if reg.Exists("test-agent") {
t.Error("agent type should not exist after unregister")
}
// Test unregister non-existent
err = reg.Unregister("non-existent")
if err == nil {
t.Error("expected error for non-existent agent type")
}
}
func TestRegistry_List(t *testing.T) {
log := newTestLogger()
reg := NewRegistry(log)
// Empty list
list := reg.List()
if len(list) != 0 {
t.Errorf("expected empty list, got %d", len(list))
}
// Add agents
_ = reg.Register(validAgentConfig("agent-1", "Agent 1"))
_ = reg.Register(validAgentConfig("agent-2", "Agent 2"))
list = reg.List()
if len(list) != 2 {
t.Errorf("expected 2 agents, got %d", len(list))
}
}
func TestRegistry_ListEnabled(t *testing.T) {
log := newTestLogger()
reg := NewRegistry(log)
enabled := validAgentConfig("enabled-agent", "Enabled Agent")
enabled.enabled = true
_ = reg.Register(enabled)
disabled := validAgentConfig("disabled-agent", "Disabled Agent")
disabled.enabled = false
_ = reg.Register(disabled)
enabledList := reg.ListEnabled()
if len(enabledList) != 1 {
t.Errorf("expected 1 enabled agent, got %d", len(enabledList))
}
if enabledList[0].ID() != "enabled-agent" {
t.Errorf("expected enabled-agent, got %s", enabledList[0].ID())
}
}
func TestRegistry_Exists(t *testing.T) {
log := newTestLogger()
reg := NewRegistry(log)
ag := validAgentConfig("test-agent", "Test Agent")
_ = reg.Register(ag)
if !reg.Exists("test-agent") {
t.Error("expected agent type to exist")
}
if reg.Exists("non-existent") {
t.Error("expected agent type to not exist")
}
}
func TestRegistry_LoadDefaults(t *testing.T) {
log := newTestLogger()
reg := NewRegistry(log)
reg.LoadDefaults()
// Verify at least some default agents are loaded
list := reg.List()
if len(list) == 0 {
t.Skip("no default agents configured")
}
// Check a known default agent exists
if !reg.Exists("auggie") {
t.Error("expected default agent 'auggie' to be loaded")
}
}
func TestRegistry_List_OrderedByDisplayOrder(t *testing.T) {
log := newTestLogger()
reg := NewRegistry(log)
// Register agents in reverse display order
a3 := validAgentConfig("agent-c", "Agent C")
a3.displayOrder = 30
a2 := validAgentConfig("agent-b", "Agent B")
a2.displayOrder = 20
a1 := validAgentConfig("agent-a", "Agent A")
a1.displayOrder = 10
_ = reg.Register(a3)
_ = reg.Register(a1)
_ = reg.Register(a2)
list := reg.List()
if len(list) != 3 {
t.Fatalf("expected 3 agents, got %d", len(list))
}
if list[0].ID() != "agent-a" || list[1].ID() != "agent-b" || list[2].ID() != "agent-c" {
t.Errorf("expected agents ordered by DisplayOrder [agent-a, agent-b, agent-c], got [%s, %s, %s]",
list[0].ID(), list[1].ID(), list[2].ID())
}
}
func TestRegistry_ListEnabled_OrderedByDisplayOrder(t *testing.T) {
log := newTestLogger()
reg := NewRegistry(log)
a3 := validAgentConfig("agent-c", "Agent C")
a3.displayOrder = 30
a2 := validAgentConfig("agent-b", "Agent B")
a2.displayOrder = 20
a2.enabled = false // disabled — should be excluded
a1 := validAgentConfig("agent-a", "Agent A")
a1.displayOrder = 10
_ = reg.Register(a3)
_ = reg.Register(a1)
_ = reg.Register(a2)
list := reg.ListEnabled()
if len(list) != 2 {
t.Fatalf("expected 2 enabled agents, got %d", len(list))
}
if list[0].ID() != "agent-a" || list[1].ID() != "agent-c" {
t.Errorf("expected enabled agents ordered [agent-a, agent-c], got [%s, %s]",
list[0].ID(), list[1].ID())
}
}
func TestAgentTypeConfig_ToAPIType(t *testing.T) {
ag := validAgentConfig("test-agent", "Test Agent")
ag.description = "Test description"
ag.runtime.Capabilities = []string{"cap1", "cap2"}
ag.runtime.Env = map[string]string{"KEY": "value"}
apiType := ToAPIType(ag)
if apiType.ID != ag.ID() {
t.Errorf("expected ID %q, got %q", ag.ID(), apiType.ID)
}
if apiType.Name != ag.Name() {
t.Errorf("expected Name %q, got %q", ag.Name(), apiType.Name)
}
if apiType.Description != ag.Description() {
t.Errorf("expected Description %q, got %q", ag.Description(), apiType.Description)
}
if apiType.DockerImage != ag.runtime.Image {
t.Errorf("expected DockerImage %q, got %q", ag.runtime.Image, apiType.DockerImage)
}
if len(apiType.Capabilities) != len(ag.runtime.Capabilities) {
t.Errorf("expected %d capabilities, got %d", len(ag.runtime.Capabilities), len(apiType.Capabilities))
}
if apiType.Enabled != ag.Enabled() {
t.Errorf("expected Enabled %v, got %v", ag.Enabled(), apiType.Enabled)
}
}