// plugin_test.go -- 插件系统 Host 的单元测试. // // 覆盖场景: // - Load 加载插件定义 // - Get 获取插件 // - Enable/Disable 启用/禁用 // - List/ListEnabled 列出 // - Count 计数 // - Close 关闭 // - GetAllSkills/GetAllHooks/GetAllMCPServers 汇总 package plugin import ( "testing" "git.flytoex.net/yuanwei/flyto-agent/pkg/execenv" ) // TestHost_Load 测试加载插件定义 func TestHost_Load(t *testing.T) { h := NewHost(execenv.DefaultExecutor{}) err := h.Load(Definition{Name: "test", Description: "a test plugin", Source: "local"}) if err != nil { t.Fatalf("加载失败: %v", err) } if h.Count() != 1 { t.Errorf("数量应为 1, 实际: %d", h.Count()) } } // TestHost_Get 测试获取插件 func TestHost_Get(t *testing.T) { h := NewHost(execenv.DefaultExecutor{}) h.Load(Definition{Name: "myPlugin", Description: "desc"}) p, ok := h.Get("myPlugin") if !ok { t.Fatal("应能找到插件") } if p.Name != "myPlugin" { t.Errorf("名称不匹配: %q", p.Name) } _, ok = h.Get("nonExistent") if ok { t.Error("不应找到不存在的插件") } } // TestHost_EnableDisable 测试启用/禁用 func TestHost_EnableDisable(t *testing.T) { h := NewHost(execenv.DefaultExecutor{}) h.Load(Definition{Name: "p1"}) // 默认启用 p, _ := h.Get("p1") if !p.Enabled { t.Error("默认应启用") } // 禁用 if err := h.Disable("p1"); err != nil { t.Fatalf("禁用失败: %v", err) } p, _ = h.Get("p1") if p.Enabled { t.Error("禁用后应为 false") } // 启用 if err := h.Enable("p1"); err != nil { t.Fatalf("启用失败: %v", err) } p, _ = h.Get("p1") if !p.Enabled { t.Error("启用后应为 true") } // 不存在的插件 if err := h.Enable("nonexistent"); err == nil { t.Error("启用不存在的插件应报错") } if err := h.Disable("nonexistent"); err == nil { t.Error("禁用不存在的插件应报错") } } // TestHost_List 测试列出所有插件 func TestHost_List(t *testing.T) { h := NewHost(execenv.DefaultExecutor{}) h.Load(Definition{Name: "a"}) h.Load(Definition{Name: "b"}) h.Load(Definition{Name: "c"}) list := h.List() if len(list) != 3 { t.Errorf("期望 3 个插件, 实际 %d", len(list)) } } // TestHost_ListEnabled 测试列出启用的插件 func TestHost_ListEnabled(t *testing.T) { h := NewHost(execenv.DefaultExecutor{}) h.Load(Definition{Name: "a"}) h.Load(Definition{Name: "b"}) h.Load(Definition{Name: "c"}) h.Disable("b") enabled := h.ListEnabled() if len(enabled) != 2 { t.Errorf("期望 2 个启用插件, 实际 %d", len(enabled)) } } // TestHost_Close 测试关闭 func TestHost_Close(t *testing.T) { h := NewHost(execenv.DefaultExecutor{}) h.Load(Definition{Name: "a"}) h.Close() if h.Count() != 0 { t.Errorf("关闭后应为 0, 实际: %d", h.Count()) } } // TestHost_GetAllHooks 测试汇总所有 hook func TestHost_GetAllHooks(t *testing.T) { h := NewHost(execenv.DefaultExecutor{}) // 直接操作内部状态进行测试 h.Load(Definition{Name: "p1"}) h.Load(Definition{Name: "p2"}) // 添加 hooks p1, _ := h.Get("p1") p1.Hooks["pre_tool_use"] = []HookDef{{Command: "echo p1"}} p2, _ := h.Get("p2") p2.Hooks["pre_tool_use"] = []HookDef{{Command: "echo p2"}} allHooks := h.GetAllHooks() if len(allHooks["pre_tool_use"]) != 2 { t.Errorf("应合并 2 个 hook, 实际 %d", len(allHooks["pre_tool_use"])) } } // TestHost_DisabledPluginExcluded 测试禁用插件不参与汇总 func TestHost_DisabledPluginExcluded(t *testing.T) { h := NewHost(execenv.DefaultExecutor{}) h.Load(Definition{Name: "p1"}) h.Load(Definition{Name: "p2"}) p1, _ := h.Get("p1") p1.Hooks["pre_tool_use"] = []HookDef{{Command: "echo p1"}} p2, _ := h.Get("p2") p2.Hooks["pre_tool_use"] = []HookDef{{Command: "echo p2"}} h.Disable("p2") allHooks := h.GetAllHooks() if len(allHooks["pre_tool_use"]) != 1 { t.Errorf("禁用后应只有 1 个 hook, 实际 %d", len(allHooks["pre_tool_use"])) } }