Skip to main content
Glama
utils.test.ts11.4 kB
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; import { extractDomain, checkDomainAccess, parseFetchTarget, fetchContent, getVersion, type TargetInfo, logger, } from "#lib/index"; import { URL } from "url"; import * as fs from "node:fs/promises"; import * as path from "node:path"; import * as url from "url"; import * as nodeFs from "fs"; import { mockPathResolution, mockFileSystem } from "../../test-utils"; Object.defineProperty(import.meta, "url", { value: "file:///fake/path/src/lib/utils.ts", }); vi.mock("node:fs/promises"); vi.mock("node:fs"); vi.mock("fs"); // Setup mocks mockPathResolution(path); mockFileSystem(fs); mockFileSystem(nodeFs); vi.mock("url", async () => { const actual = await vi.importActual<typeof import("url")>("url"); return { ...actual, fileURLToPath: vi.fn().mockImplementation((url: string | URL) => { if ( url === "file:///fake/path/src/lib/utils.ts" || url === import.meta.url ) { return "/fake/path/src/lib/utils.ts"; } const urlString = typeof url === "string" ? url : url.href; if (urlString.startsWith("file://")) { return urlString.slice(7); } return actual.fileURLToPath(url); }), }; }); vi.mock("path", async () => { const actual = await vi.importActual("path"); return { ...actual, dirname: vi.fn().mockImplementation((p: string) => { if (p === "/fake/path/src/lib/utils.ts") return "/fake/path/src/lib"; if (p === "/fake/path/src/lib") return "/fake/path/src"; if (p === "/fake/path/src") return "/fake/path"; return p; }), join: vi.fn().mockImplementation((...parts: string[]) => parts.join("/")), }; }); describe("extractDomain", () => { it("should extract domain from valid HTTP URL", () => { expect(extractDomain("http://example.com/path")).toBe("example.com"); }); it("should extract domain from valid HTTPS URL", () => { expect(extractDomain("https://sub.example.com/path")).toBe( "sub.example.com" ); }); it("should return null for invalid URL", () => { expect(extractDomain("not-a-url")).toBeNull(); }); it("should return null for non-HTTP/HTTPS protocols", () => { expect(extractDomain("file:///path/to/file")).toBeNull(); }); }); describe("checkDomainAccess", () => { const loggerInfoSpy = vi.spyOn(logger, "info").mockImplementation(() => {}); const originalEnv = process.env; beforeEach(() => { vi.resetAllMocks(); process.env = { ...originalEnv }; }); afterEach(() => { process.env = originalEnv; }); it("should allow access for matching domain", () => { const targetInfo = { type: "remote" as const, url: new URL("https://example.com"), hostname: "example.com", }; const allowedDomains = new Set(["example.com"]); expect(() => checkDomainAccess(targetInfo, allowedDomains)).not.toThrow(); }); it("should allow access when wildcard is present", () => { const targetInfo = { type: "remote" as const, url: new URL("https://any-domain.com"), hostname: "any-domain.com", }; const allowedDomains = new Set(["*"]); expect(() => checkDomainAccess(targetInfo, allowedDomains)).not.toThrow(); }); it("should deny access for non-matching domain", () => { const targetInfo = { type: "remote" as const, url: new URL("https://example.com"), hostname: "example.com", }; const allowedDomains = new Set(["other-domain.com"]); expect(() => checkDomainAccess(targetInfo, allowedDomains)).toThrow( "Access denied: Fetching from domain 'example.com' is not allowed" ); }); it("should allow local file access", () => { const targetInfo = { type: "localPath" as const, resolvedPath: "/path/to/file", originalInput: "/path/to/file", }; const allowedDomains = new Set([]); expect(() => checkDomainAccess(targetInfo, allowedDomains)).not.toThrow(); }); it("should throw for unsupported target type", () => { const targetInfo = { type: "unsupported" as const, reason: "test", originalInput: "test", }; const allowedDomains = new Set([]); expect(() => checkDomainAccess(targetInfo, allowedDomains)).toThrow( "Internal error: Unsupported target type" ); }); it("should log allowed domains", () => { const targetInfo = { type: "remote" as const, url: new URL("https://example.com"), hostname: "example.com", }; const allowedDomains = new Set(["example.com"]); checkDomainAccess(targetInfo, allowedDomains); // Verify the log message contains the expected content expect(loggerInfoSpy).toHaveBeenCalledWith( expect.stringContaining("Domain 'example.com' is allowed.") ); }); }); describe("parseFetchTarget", () => { beforeEach(() => { vi.resetAllMocks(); }); it("should parse HTTP URL", async () => { const result = await parseFetchTarget("http://example.com/path"); expect(result).toEqual({ type: "remote", url: new URL("http://example.com/path"), hostname: "example.com", }); }); it("should parse HTTPS URL", async () => { const result = await parseFetchTarget("https://example.com/path"); expect(result).toEqual({ type: "remote", url: new URL("https://example.com/path"), hostname: "example.com", }); }); it("should handle file URL", async () => { const filePath = "/absolute/path/to/file"; const fileUrl = `file://${filePath}`; const result = await parseFetchTarget(fileUrl); expect(result.type).toBe("unsupported"); const unsupportedResult = result as { type: "unsupported"; reason: string; originalInput: string; }; expect(unsupportedResult.originalInput).toBe(fileUrl); expect(unsupportedResult.reason).toContain( "Failed to convert file: URL to path" ); }); it("should parse local path", async () => { const localPath = "/path/to/file"; vi.mocked(fs.stat).mockResolvedValueOnce({} as any); const result = await parseFetchTarget(localPath); expect(result).toEqual({ type: "localPath", resolvedPath: path.resolve(localPath), originalInput: localPath, }); }); it("should handle non-existent local path", async () => { const localPath = "/non/existent/path"; vi.mocked(fs.stat).mockRejectedValueOnce(new Error("ENOENT")); const result = await parseFetchTarget(localPath); expect(result).toEqual({ type: "unsupported", originalInput: localPath, reason: expect.stringContaining("Path does not exist or is inaccessible"), }); }); it("should handle unsupported URL protocol", async () => { const result = await parseFetchTarget("ftp://example.com"); expect(result).toEqual({ type: "unsupported", originalInput: "ftp://example.com", reason: "Unsupported URL protocol: ftp:", }); }); }); describe("fetchContent", () => { beforeEach(() => { vi.resetAllMocks(); }); it("should fetch remote content", async () => { const mockResponse = { ok: true, text: vi.fn().mockResolvedValue("content"), }; global.fetch = vi.fn().mockResolvedValue(mockResponse as any); const targetInfo = { type: "remote" as const, url: new URL("https://example.com/"), hostname: "example.com", }; const result = await fetchContent(targetInfo); expect(result).toBe("content"); expect(fetch).toHaveBeenCalledWith("https://example.com/"); }); it("should log when fetching remote content", async () => { const mockResponse = { ok: true, text: vi.fn().mockResolvedValue("content"), }; global.fetch = vi.fn().mockResolvedValue(mockResponse as any); const loggerInfoSpy = vi.spyOn(logger, "info"); const targetInfo = { type: "remote" as const, url: new URL("https://example.com/"), hostname: "example.com", }; await fetchContent(targetInfo); // Verify the log message contains the expected fetch URL expect(loggerInfoSpy).toHaveBeenCalledWith( expect.stringContaining("Fetching remote URL: https://example.com/") ); }); it("should handle remote fetch failure", async () => { global.fetch = vi.fn().mockResolvedValueOnce({ ok: false, status: 404, statusText: "Not Found", }); const targetInfo = { type: "remote" as const, url: new URL("https://example.com"), hostname: "example.com", }; await expect(fetchContent(targetInfo)).rejects.toThrow("HTTP error 404"); }); it("should fetch local file content", async () => { vi.mocked(fs.readFile).mockResolvedValueOnce("local content" as any); const targetInfo = { type: "localPath" as const, resolvedPath: "/path/to/file", originalInput: "file.txt", }; const content = await fetchContent(targetInfo); expect(content).toBe("local content"); expect(fs.readFile).toHaveBeenCalledWith("/path/to/file", "utf-8"); }); it("should fetch local file content from file URL", async () => { vi.mocked(fs.readFile).mockResolvedValueOnce("file url content" as any); const loggerInfoSpy = vi.spyOn(logger, "info"); const targetInfo = { type: "localFileUrl" as const, filePath: "/local/file/path", url: new URL("file:///local/file/path"), }; const content = await fetchContent(targetInfo); expect(content).toBe("file url content"); expect(fs.readFile).toHaveBeenCalledWith("/local/file/path", "utf-8"); // Verify the log message contains the expected file path expect(loggerInfoSpy).toHaveBeenCalledWith( expect.stringContaining( "Reading local file path from file: URL: /local/file/path" ) ); }); it("should handle local file read failure", async () => { vi.mocked(fs.readFile).mockRejectedValueOnce(new Error("ENOENT")); const targetInfo = { type: "localPath" as const, resolvedPath: "/path/to/file", originalInput: "/path/to/file", }; await expect(fetchContent(targetInfo)).rejects.toThrow(); }); it("should throw for unsupported target type", async () => { const targetInfo = { type: "unsupported" as const, reason: "Test reason", originalInput: "test", }; await expect(fetchContent(targetInfo)).rejects.toThrow( "Cannot fetch content for unsupported target type: Test reason" ); }); it("should throw on unknown target type (exhaustive check)", async () => { const targetInfo = { type: "unknown", reason: "Unknown type test", originalInput: "test", } as unknown as TargetInfo; await expect(fetchContent(targetInfo)).rejects.toThrow( "Internal error: Unhandled target info type" ); }); }); describe("getVersion", () => { it("verifies the getVersion implementation", () => { const mockReadFileSync = nodeFs.readFileSync; expect(mockReadFileSync).toBeDefined(); const packageJson = JSON.parse(JSON.stringify({ version: "1.2.3" })); expect(packageJson.version).toBe("1.2.3"); const mockUrl = url.fileURLToPath; expect(mockUrl).toBeDefined(); expect(typeof getVersion).toBe("function"); }); }); // Copyright (C) 2025 Christopher White // SPDX-License-Identifier: AGPL-3.0-or-later

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/maverickg59/sushimcp'

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