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") } }