All checks were successful
Beta Release / beta (push) Successful in 2m24s
Major changes: - Refactor CLI entry point to Cobra commands (root, setup, scan, doctor, install, update, lsp, mcp, skills, config, version) - Add LSP registry with health checks, auto-install, and editor config generation - Add MCP registry with editor detection, status tracking, and per-editor configuration - Add workflow engine with planner and step execution for automated task chains - Add conversation search, export (Markdown/JSON), and detailed token counting - Add streaming shell chat handler with tool call/result events - Add skill validation, dry-run testing, and export endpoints - Enrich dashboard with Tools/Activity/Status tabs and tool cards grid - Add PRD documentation - Complete i18n for both EN and FR 💘 Generated with Crush Assisted-by: GLM-5.1 via Crush <crush@charm.land>
229 lines
5.3 KiB
Go
229 lines
5.3 KiB
Go
package mcp
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
)
|
|
|
|
func TestDefaultRegistry(t *testing.T) {
|
|
reg := DefaultRegistry()
|
|
if reg.SchemaVersion != "v1" {
|
|
t.Errorf("Expected v1, got %s", reg.SchemaVersion)
|
|
}
|
|
if len(reg.Servers) == 0 {
|
|
t.Error("Default registry should have servers")
|
|
}
|
|
|
|
names := map[string]bool{}
|
|
for _, s := range reg.Servers {
|
|
if names[s.Name] {
|
|
t.Errorf("Duplicate server name: %s", s.Name)
|
|
}
|
|
names[s.Name] = true
|
|
if s.Command == "" {
|
|
t.Errorf("Server %s missing command", s.Name)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestSaveAndLoadRegistry(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
registryPath := filepath.Join(tmpDir, "mcp-registry.yaml")
|
|
SetRegistryPath(registryPath)
|
|
|
|
reg := DefaultRegistry()
|
|
if err := SaveRegistry(reg); err != nil {
|
|
t.Fatalf("SaveRegistry failed: %v", err)
|
|
}
|
|
|
|
if _, err := os.Stat(registryPath); os.IsNotExist(err) {
|
|
t.Error("Registry file should exist")
|
|
}
|
|
|
|
loaded, err := LoadRegistry()
|
|
if err != nil {
|
|
t.Fatalf("LoadRegistry failed: %v", err)
|
|
}
|
|
if len(loaded.Servers) != len(reg.Servers) {
|
|
t.Errorf("Expected %d servers, got %d", len(reg.Servers), len(loaded.Servers))
|
|
}
|
|
}
|
|
|
|
func TestAddAndRemoveFromRegistry(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
SetRegistryPath(filepath.Join(tmpDir, "mcp-registry.yaml"))
|
|
SaveRegistry(DefaultRegistry())
|
|
|
|
newServer := RegistryServer{
|
|
Name: "test-server",
|
|
Description: "Test server",
|
|
Category: "test",
|
|
Command: "npx",
|
|
Args: []string{"-y", "test-pkg"},
|
|
InstallType: "npm",
|
|
}
|
|
|
|
if err := AddToRegistry(newServer); err != nil {
|
|
t.Fatalf("AddToRegistry failed: %v", err)
|
|
}
|
|
|
|
reg, _ := LoadRegistry()
|
|
found := false
|
|
for _, s := range reg.Servers {
|
|
if s.Name == "test-server" {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
if !found {
|
|
t.Error("test-server should be in registry")
|
|
}
|
|
|
|
if err := RemoveFromRegistry("test-server"); err != nil {
|
|
t.Fatalf("RemoveFromRegistry failed: %v", err)
|
|
}
|
|
|
|
reg, _ = LoadRegistry()
|
|
for _, s := range reg.Servers {
|
|
if s.Name == "test-server" {
|
|
t.Error("test-server should have been removed")
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestResolveEnv(t *testing.T) {
|
|
env := map[string]string{
|
|
"API_KEY": "",
|
|
"HOST": "localhost",
|
|
}
|
|
|
|
os.Setenv("API_KEY", "from-env")
|
|
defer os.Unsetenv("API_KEY")
|
|
|
|
resolved := ResolveEnv(env, nil)
|
|
if resolved["API_KEY"] != "from-env" {
|
|
t.Errorf("Expected from-env, got %s", resolved["API_KEY"])
|
|
}
|
|
if resolved["HOST"] != "localhost" {
|
|
t.Errorf("Expected localhost, got %s", resolved["HOST"])
|
|
}
|
|
}
|
|
|
|
func TestValidateConfig(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
configPath := filepath.Join(tmpDir, "test-config.json")
|
|
os.WriteFile(configPath, []byte(`{"mcps":{}}`), 0644)
|
|
|
|
if err := ValidateConfig(configPath); err != nil {
|
|
t.Errorf("Valid config should pass: %v", err)
|
|
}
|
|
|
|
badPath := filepath.Join(tmpDir, "nonexistent.json")
|
|
if err := ValidateConfig(badPath); err == nil {
|
|
t.Error("Nonexistent config should fail")
|
|
}
|
|
}
|
|
|
|
func TestEditorConfigs(t *testing.T) {
|
|
configs := EditorConfigs("/tmp")
|
|
if len(configs) < 3 {
|
|
t.Errorf("Expected at least 3 editor configs, got %d", len(configs))
|
|
}
|
|
|
|
names := map[string]bool{}
|
|
for _, c := range configs {
|
|
if names[c.Name] {
|
|
t.Errorf("Duplicate editor: %s", c.Name)
|
|
}
|
|
names[c.Name] = true
|
|
if c.ConfigPath == "" {
|
|
t.Errorf("Editor %s missing config path", c.Name)
|
|
}
|
|
if c.ConfigKey == "" {
|
|
t.Errorf("Editor %s missing config key", c.Name)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestDiscoverNpmServers(t *testing.T) {
|
|
servers, err := DiscoverNpmServers()
|
|
if err != nil {
|
|
t.Fatalf("DiscoverNpmServers failed: %v", err)
|
|
}
|
|
if len(servers) == 0 {
|
|
t.Error("Should discover some npm servers")
|
|
}
|
|
}
|
|
|
|
func TestReceiptRoundTrip(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
os.Setenv("HOME", tmpDir)
|
|
defer os.Unsetenv("HOME")
|
|
|
|
SetRegistryPath(filepath.Join(tmpDir, "reg.yaml"))
|
|
|
|
if err := SaveReceipt("test-server", "1.2.3"); err != nil {
|
|
t.Fatalf("SaveReceipt failed: %v", err)
|
|
}
|
|
|
|
version := GetInstalledVersion("test-server")
|
|
if version != "1.2.3" {
|
|
t.Errorf("Expected 1.2.3, got %s", version)
|
|
}
|
|
}
|
|
|
|
func TestInitRegistry(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
SetRegistryPath(filepath.Join(tmpDir, "init-reg.yaml"))
|
|
|
|
if err := InitRegistry(); err != nil {
|
|
t.Fatalf("InitRegistry failed: %v", err)
|
|
}
|
|
|
|
if _, err := os.Stat(filepath.Join(tmpDir, "init-reg.yaml")); os.IsNotExist(err) {
|
|
t.Error("Registry file should be created")
|
|
}
|
|
|
|
if err := InitRegistry(); err != nil {
|
|
t.Fatalf("Second InitRegistry should not fail: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestDetectInstalledEditors(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
os.MkdirAll(filepath.Join(tmpDir, ".config", "crush"), 0755)
|
|
os.WriteFile(filepath.Join(tmpDir, ".config", "crush", "crush.json"), []byte(`{}`), 0644)
|
|
os.MkdirAll(filepath.Join(tmpDir, ".cursor"), 0755)
|
|
|
|
editors := DetectInstalledEditors(tmpDir)
|
|
if len(editors) < 2 {
|
|
t.Errorf("Expected at least 2 editors, got %d", len(editors))
|
|
}
|
|
|
|
found := map[string]bool{}
|
|
for _, e := range editors {
|
|
found[e] = true
|
|
}
|
|
if !found["crush"] {
|
|
t.Error("Should detect crush")
|
|
}
|
|
if !found["cursor"] {
|
|
t.Error("Should detect cursor")
|
|
}
|
|
}
|
|
|
|
func TestCheckServerStatus(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
os.Setenv("HOME", tmpDir)
|
|
defer os.Unsetenv("HOME")
|
|
|
|
SetRegistryPath(filepath.Join(tmpDir, "reg.yaml"))
|
|
SaveRegistry(DefaultRegistry())
|
|
|
|
status := CheckServerStatus("nonexistent")
|
|
if status.Error == "" {
|
|
t.Error("Should have error for nonexistent server")
|
|
}
|
|
}
|