"""Tests for MCP client configuration snippet generation (core/config_snippets.py).""" import json import pytest from core.config_snippets import generate_config, get_supported_clients # ── Test Data ───────────────────────────────────────────────── BASE_URL = "https://mcp.example.com" USER_ID = "abc123-uuid" ALIAS = "myblog" API_KEY = "mhu_testapikey1234567890abcdefghijklmnopqr" EXPECTED_ENDPOINT = f"{BASE_URL}/u/{USER_ID}/{ALIAS}/mcp" # ── Supported Clients ──────────────────────────────────────── class TestSupportedClients: """Test get_supported_clients function.""" @pytest.mark.unit def test_get_supported_clients(self): """Should return all supported client types.""" clients = get_supported_clients() assert len(clients) == 6 client_ids = [c["id"] for c in clients] assert "claude_desktop" in client_ids assert "claude_code" in client_ids assert "cursor" in client_ids assert "vscode" in client_ids assert "chatgpt" in client_ids @pytest.mark.unit def test_supported_clients_have_labels(self): """Each client should have id, label, and description.""" for client in get_supported_clients(): assert "id" in client assert "label" in client assert "description" in client assert len(client["label"]) > 0 assert len(client["description"]) > 0 # ── Claude Desktop / Claude Code Format ────────────────────── class TestClaudeFormat: """Test Claude Desktop and Claude Code config generation.""" @pytest.mark.unit def test_claude_desktop_format(self): """Claude Desktop config should be valid JSON with mcpServers.""" snippet = generate_config(BASE_URL, USER_ID, ALIAS, API_KEY, "claude_desktop") config = json.loads(snippet) assert "mcpServers" in config server_name = f"mcphub-{ALIAS}" assert server_name in config["mcpServers"] server = config["mcpServers"][server_name] assert server["url"] == EXPECTED_ENDPOINT assert server["type"] == "streamableHttp" assert f"Bearer {API_KEY}" in server["headers"]["Authorization"] @pytest.mark.unit def test_claude_code_format(self): """Claude Code config should use the same mcpServers format with http type.""" snippet = generate_config(BASE_URL, USER_ID, ALIAS, API_KEY, "claude_code") config = json.loads(snippet) assert "mcpServers" in config server_name = f"mcphub-{ALIAS}" assert server_name in config["mcpServers"] assert config["mcpServers"][server_name]["type"] == "http" # ── Cursor / VS Code Format ───────────────────────────────── class TestCursorVSCodeFormat: """Test Cursor and VS Code config generation.""" @pytest.mark.unit def test_cursor_format(self): """Cursor config should be valid JSON with mcp.servers and http type.""" snippet = generate_config(BASE_URL, USER_ID, ALIAS, API_KEY, "cursor") config = json.loads(snippet) assert "mcp" in config assert "servers" in config["mcp"] server_name = f"mcphub-{ALIAS}" assert server_name in config["mcp"]["servers"] server = config["mcp"]["servers"][server_name] assert server["url"] == EXPECTED_ENDPOINT assert server["type"] == "http" assert f"Bearer {API_KEY}" in server["headers"]["Authorization"] @pytest.mark.unit def test_vscode_format(self): """VS Code config should use mcp.servers format with http type.""" snippet = generate_config(BASE_URL, USER_ID, ALIAS, API_KEY, "vscode") config = json.loads(snippet) assert "mcp" in config assert "servers" in config["mcp"] server_name = f"mcphub-{ALIAS}" assert config["mcp"]["servers"][server_name]["type"] == "http" # ── ChatGPT Format ─────────────────────────────────────────── class TestChatGPTFormat: """Test ChatGPT config generation.""" @pytest.mark.unit def test_chatgpt_format(self): """ChatGPT config should return the raw endpoint URL only.""" snippet = generate_config(BASE_URL, USER_ID, ALIAS, API_KEY, "chatgpt") assert snippet == EXPECTED_ENDPOINT # Should NOT be JSON with pytest.raises(json.JSONDecodeError): json.loads(snippet) # ── Error Handling ─────────────────────────────────────────── class TestErrorHandling: """Test error conditions.""" @pytest.mark.unit def test_invalid_client_type(self): """Unsupported client type should raise ValueError.""" with pytest.raises(ValueError, match="Unsupported client type"): generate_config(BASE_URL, USER_ID, ALIAS, API_KEY, "unknown_client") # ── URL Correctness ────────────────────────────────────────── class TestURLCorrectness: """Test that generated configs contain the correct endpoint URL.""" @pytest.mark.unit def test_config_contains_correct_url(self): """All config formats should contain /u/{user_id}/{alias}/mcp.""" for client_type in ("claude_desktop", "claude_code", "cursor", "vscode", "chatgpt"): snippet = generate_config(BASE_URL, USER_ID, ALIAS, API_KEY, client_type) assert ( f"/u/{USER_ID}/{ALIAS}/mcp" in snippet ), f"{client_type} config missing expected URL path" @pytest.mark.unit def test_trailing_slash_stripped(self): """Base URL with trailing slash should not produce double slashes.""" snippet = generate_config( "https://mcp.example.com/", USER_ID, ALIAS, API_KEY, "claude_desktop" ) assert "//u/" not in snippet assert f"/u/{USER_ID}/{ALIAS}/mcp" in snippet