// manifest_test.go -- 插件清单解析和验证的单元测试. // // 覆盖场景: // - LoadManifest 正常加载 // - 缺少必填字段报错 // - 无效名称字符报错 // - MCP 服务器配置验证 // - FormatManifestInfo 格式化 // - isValidNameChar 字符验证 package plugin import ( "encoding/json" "os" "path/filepath" "strings" "testing" ) // TestLoadManifest_Normal 测试正常加载清单 func TestLoadManifest_Normal(t *testing.T) { dir := t.TempDir() manifest := map[string]any{ "name": "test-plugin", "description": "A test plugin", "version": "1.0.0", "skills_path": "skills", } data, _ := json.MarshalIndent(manifest, "", " ") os.WriteFile(filepath.Join(dir, "plugin.json"), data, 0644) m, err := LoadManifest(dir) if err != nil { t.Fatalf("加载失败: %v", err) } if m.Name != "test-plugin" { t.Errorf("Name: %q", m.Name) } if m.Version != "1.0.0" { t.Errorf("Version: %q", m.Version) } } // TestLoadManifest_MissingName 测试缺少名称 func TestLoadManifest_MissingName(t *testing.T) { dir := t.TempDir() manifest := map[string]any{ "description": "no name", } data, _ := json.MarshalIndent(manifest, "", " ") os.WriteFile(filepath.Join(dir, "plugin.json"), data, 0644) _, err := LoadManifest(dir) if err == nil { t.Error("缺少名称应报错") } } // TestLoadManifest_InvalidNameChar 测试无效名称字符 func TestLoadManifest_InvalidNameChar(t *testing.T) { dir := t.TempDir() manifest := map[string]any{ "name": "invalid name!", } data, _ := json.MarshalIndent(manifest, "", " ") os.WriteFile(filepath.Join(dir, "plugin.json"), data, 0644) _, err := LoadManifest(dir) if err == nil { t.Error("无效名称字符应报错") } } // TestLoadManifest_MCPValidation 测试 MCP 服务器配置验证 func TestLoadManifest_MCPValidation(t *testing.T) { dir := t.TempDir() // 缺少 transport manifest := map[string]any{ "name": "test", "mcp_servers": map[string]any{ "server1": map[string]any{}, }, } data, _ := json.MarshalIndent(manifest, "", " ") os.WriteFile(filepath.Join(dir, "plugin.json"), data, 0644) _, err := LoadManifest(dir) if err == nil { t.Error("缺少 transport 应报错") } } // TestLoadManifest_StdioMCPNeedsCommand 测试 stdio MCP 需要 command func TestLoadManifest_StdioMCPNeedsCommand(t *testing.T) { dir := t.TempDir() manifest := map[string]any{ "name": "test", "mcp_servers": map[string]any{ "server1": map[string]any{ "transport": "stdio", // 缺少 command }, }, } data, _ := json.MarshalIndent(manifest, "", " ") os.WriteFile(filepath.Join(dir, "plugin.json"), data, 0644) _, err := LoadManifest(dir) if err == nil { t.Error("stdio 缺少 command 应报错") } } // TestLoadManifest_DefaultVersion 测试默认版本号 func TestLoadManifest_DefaultVersion(t *testing.T) { dir := t.TempDir() manifest := map[string]any{ "name": "test", } data, _ := json.MarshalIndent(manifest, "", " ") os.WriteFile(filepath.Join(dir, "plugin.json"), data, 0644) m, err := LoadManifest(dir) if err != nil { t.Fatalf("加载失败: %v", err) } if m.Version != "0.0.0" { t.Errorf("默认版本应为 0.0.0, 实际: %q", m.Version) } } // TestIsValidNameChar 测试名称字符验证 func TestIsValidNameChar(t *testing.T) { valid := []rune{'a', 'z', 'A', 'Z', '0', '9', '-', '_'} for _, c := range valid { if !isValidNameChar(c) { t.Errorf("'%c' 应为合法字符", c) } } invalid := []rune{' ', '!', '@', '#', '/', '\\', '.', '中'} for _, c := range invalid { if isValidNameChar(c) { t.Errorf("'%c' 不应为合法字符", c) } } } // TestFormatManifestInfo 测试清单信息格式化 func TestFormatManifestInfo(t *testing.T) { m := &Manifest{ Name: "my-plugin", Description: "A great plugin", Version: "2.0.0", MCPServers: map[string]MCPDef{"server1": {Transport: "stdio"}}, Dependencies: []string{"dep1", "dep2"}, } info := FormatManifestInfo(m) if !strings.Contains(info, "my-plugin") { t.Error("应包含插件名") } if !strings.Contains(info, "2.0.0") { t.Error("应包含版本") } if !strings.Contains(info, "MCP Servers: 1") { t.Error("应包含 MCP 服务器数量") } } // TestLoadManifest_FileNotExist 测试文件不存在 func TestLoadManifest_FileNotExist(t *testing.T) { _, err := LoadManifest("/nonexistent/dir") if err == nil { t.Error("文件不存在应报错") } }