Skip to main content
Glama

hypertool-mcp

mcpConfigFileLoader.test.tsโ€ข8.68 kB
import { describe, it, expect, beforeEach, vi, afterEach } from "vitest"; import * as path from "path"; import * as fs from "fs/promises"; import { discoverMcpConfigFile, loadMcpConfigFile, } from "./mcpConfigFileLoader.js"; import { loadUserPreferences, saveUserPreferences } from "./preferenceStore.js"; import { MCPConfigParser } from "./mcpConfigParser.js"; // Mock modules vi.mock("fs/promises"); vi.mock("./preferenceStore.js"); vi.mock("./mcpConfigParser.js"); const mockFs = fs as vi.Mocked<typeof fs>; const mockLoadUserPreferences = vi.mocked(loadUserPreferences); const mockSaveUserPreferences = vi.mocked(saveUserPreferences); const mockMCPConfigParser = vi.mocked(MCPConfigParser); describe("mcpConfigFileLoader", () => { let mockUserPreferences: any; beforeEach(() => { mockUserPreferences = { mcpConfigPath: undefined }; vi.clearAllMocks(); // Default mock implementations mockLoadUserPreferences.mockResolvedValue(mockUserPreferences); mockSaveUserPreferences.mockResolvedValue(undefined); // Mock MCPConfigParser const mockParserInstance = { parseFile: vi.fn(), }; mockMCPConfigParser.mockImplementation(() => mockParserInstance as any); }); afterEach(() => { vi.clearAllMocks(); }); describe("discoverMcpConfigFile - CLI Path Resolution", () => { it("should resolve relative CLI paths to absolute paths", async () => { const relativePath = "./test-config.json"; const expectedAbsolutePath = path.resolve(relativePath); // Mock file exists at resolved path mockFs.access.mockImplementation((path) => { if (path === expectedAbsolutePath) { return Promise.resolve(); } return Promise.reject(new Error("ENOENT")); }); const result = await discoverMcpConfigFile(relativePath, true); expect(result.configPath).toBe(expectedAbsolutePath); expect(result.source).toBe("cli"); expect(result.errorMessage).toBeUndefined(); expect(mockFs.access).toHaveBeenCalledWith(expectedAbsolutePath); }); it("should handle relative paths with parent directories", async () => { const relativePath = "../configs/mcp.json"; const expectedAbsolutePath = path.resolve(relativePath); mockFs.access.mockImplementation((path) => { if (path === expectedAbsolutePath) { return Promise.resolve(); } return Promise.reject(new Error("ENOENT")); }); const result = await discoverMcpConfigFile(relativePath, true); expect(result.configPath).toBe(expectedAbsolutePath); expect(result.source).toBe("cli"); expect(result.errorMessage).toBeUndefined(); }); it("should preserve absolute paths unchanged", async () => { const absolutePath = "/absolute/path/to/config.json"; mockFs.access.mockImplementation((path) => { if (path === absolutePath) { return Promise.resolve(); } return Promise.reject(new Error("ENOENT")); }); const result = await discoverMcpConfigFile(absolutePath, true); expect(result.configPath).toBe(absolutePath); expect(result.source).toBe("cli"); expect(result.errorMessage).toBeUndefined(); }); it("should provide meaningful error messages for non-existent relative paths", async () => { const relativePath = "./non-existent.json"; const expectedAbsolutePath = path.resolve(relativePath); mockFs.access.mockRejectedValue(new Error("ENOENT")); const result = await discoverMcpConfigFile(relativePath); expect(result.configPath).toBeNull(); expect(result.source).toBe("none"); expect(result.errorMessage).toContain(relativePath); expect(result.errorMessage).toContain(expectedAbsolutePath); }); it("should resolve paths based on current working directory at call time", async () => { const relativePath = "./config.json"; const expectedAbsolutePath = path.resolve(relativePath); mockFs.access.mockImplementation((path) => { if (path === expectedAbsolutePath) { return Promise.resolve(); } return Promise.reject(new Error("ENOENT")); }); const result = await discoverMcpConfigFile(relativePath, true); expect(result.configPath).toBe(expectedAbsolutePath); expect(result.source).toBe("cli"); expect(result.errorMessage).toBeUndefined(); expect(mockFs.access).toHaveBeenCalledWith(expectedAbsolutePath); }); it("should store resolved absolute path in user preferences", async () => { const relativePath = "./config.json"; const expectedAbsolutePath = path.resolve(relativePath); mockFs.access.mockImplementation((path) => { if (path === expectedAbsolutePath) { return Promise.resolve(); } return Promise.reject(new Error("ENOENT")); }); await discoverMcpConfigFile(relativePath, true); expect(mockLoadUserPreferences).toHaveBeenCalled(); expect(mockSaveUserPreferences).toHaveBeenCalledWith( expect.objectContaining({ mcpConfigPath: expectedAbsolutePath, }) ); }); it("should not update preferences when updatePreference is false", async () => { const relativePath = "./config.json"; const expectedAbsolutePath = path.resolve(relativePath); mockFs.access.mockImplementation((path) => { if (path === expectedAbsolutePath) { return Promise.resolve(); } return Promise.reject(new Error("ENOENT")); }); await discoverMcpConfigFile(relativePath, false); expect(mockLoadUserPreferences).not.toHaveBeenCalled(); expect(mockSaveUserPreferences).not.toHaveBeenCalled(); }); }); describe("loadMcpConfigFile", () => { it("should load config file successfully", async () => { const configPath = "/path/to/config.json"; const mockConfig = { mcpServers: { test: { type: "stdio", command: "test" } }, }; const mockParserInstance = new MCPConfigParser(); vi.mocked(mockParserInstance.parseFile).mockResolvedValue({ success: true, config: mockConfig, }); const result = await loadMcpConfigFile(configPath); expect(result).toEqual({ ...mockConfig, _metadata: expect.objectContaining({ path: configPath, loadedAt: expect.any(String), }), }); }); it("should handle parser errors", async () => { const configPath = "/path/to/config.json"; const mockParserInstance = new MCPConfigParser(); vi.mocked(mockParserInstance.parseFile).mockResolvedValue({ success: false, error: "Parse error", }); await expect(loadMcpConfigFile(configPath)).rejects.toThrow( "Failed to parse MCP config: Parse error" ); }); }); describe("Edge cases", () => { it("should handle test environment config override", async () => { const testConfigPath = "/test/config.json"; // Set environment variables const originalNodeEnv = process.env.NODE_ENV; const originalTestConfig = process.env.HYPERTOOL_TEST_CONFIG; try { process.env.NODE_ENV = "test"; process.env.HYPERTOOL_TEST_CONFIG = testConfigPath; mockFs.access.mockImplementation((path) => { if (path === testConfigPath) { return Promise.resolve(); } return Promise.reject(new Error("ENOENT")); }); const result = await discoverMcpConfigFile("./some-path.json"); expect(result.configPath).toBe(testConfigPath); expect(result.source).toBe("cli"); expect(result.errorMessage).toBeUndefined(); } finally { process.env.NODE_ENV = originalNodeEnv; process.env.HYPERTOOL_TEST_CONFIG = originalTestConfig; } }); it("should handle preference save failure gracefully", async () => { const relativePath = "./config.json"; const expectedAbsolutePath = path.resolve(relativePath); mockFs.access.mockImplementation((path) => { if (path === expectedAbsolutePath) { return Promise.resolve(); } return Promise.reject(new Error("ENOENT")); }); mockSaveUserPreferences.mockRejectedValue(new Error("Save failed")); // Should still succeed even if preference save fails const result = await discoverMcpConfigFile(relativePath, true); expect(result.configPath).toBe(expectedAbsolutePath); expect(result.source).toBe("cli"); expect(result.errorMessage).toBeUndefined(); }); }); });

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/toolprint/hypertool-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server