Skip to main content
Glama
graphQLService.test.ts8.23 kB
import { describe, it, expect, vi, beforeAll, afterAll, beforeEach, } from "vitest"; import { GraphQLService } from "../../src/services/graphQLService"; import { getIntrospectionQuery } from "graphql"; import { TestSchemaFixtures, createComprehensiveSchema, createComprehensiveMockResponse, } from "../fixtures/testSchema"; describe("graphQLService unit tests", () => { let graphQLService: GraphQLService; beforeAll(async () => { graphQLService = new GraphQLService( "http://localhost:3000/graphql", "test-token", ); }); beforeEach(() => { vi.clearAllMocks(); }); afterAll(() => { vi.restoreAllMocks(); }); describe("getApiUrl", () => { it("Should return the api url", () => { const url = graphQLService.getApiUrl(); expect(url).toBe("http://localhost:3000/graphql"); }); }); describe("getApiToken", () => { it("Should return the api token", () => { const token = graphQLService.getApiToken(); expect(token).toBe("test-token"); }); it("Should return undefined when no token is provided", () => { const serviceWithoutToken = new GraphQLService( "http://localhost:3000/graphql", ); const token = serviceWithoutToken.getApiToken(); expect(token).toBeUndefined(); }); }); describe("fetchSchema", () => { it("Should fetch api schema successfully", async () => { // Mock successful fetch response using comprehensive schema const mockResponse = { ok: true, json: vi .fn() .mockResolvedValue(TestSchemaFixtures.comprehensive.success), }; global.fetch = vi.fn().mockResolvedValue(mockResponse); const schema = await graphQLService.fetchSchema(); expect(global.fetch).toHaveBeenCalledWith( "http://localhost:3000/graphql", { method: "POST", headers: { "Content-Type": "application/json", Authorization: "Bearer test-token", }, body: JSON.stringify({ query: getIntrospectionQuery() }), }, ); expect(schema).toBeDefined(); expect(schema.getQueryType()).toBeDefined(); expect(schema.getMutationType()).toBeDefined(); expect(schema.getSubscriptionType()).toBeDefined(); // Verify the schema has our expected types const userType = schema.getType("User"); const postType = schema.getType("Post"); const searchResultType = schema.getType("SearchResult"); const postStatusType = schema.getType("PostStatus"); expect(userType).toBeDefined(); expect(postType).toBeDefined(); expect(searchResultType).toBeDefined(); expect(postStatusType).toBeDefined(); }); it("Should make request without Authorization header when no token is provided", async () => { const serviceWithoutToken = new GraphQLService( "http://localhost:3000/graphql", ); const mockResponse = { ok: true, json: vi .fn() .mockResolvedValue(TestSchemaFixtures.comprehensive.success), }; global.fetch = vi.fn().mockResolvedValue(mockResponse); await serviceWithoutToken.fetchSchema(); expect(global.fetch).toHaveBeenCalledWith( "http://localhost:3000/graphql", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ query: getIntrospectionQuery() }), }, ); }); it("Should throw error when fetch fails with non-ok response", async () => { const mockResponse = { ok: false, statusText: "Internal Server Error", }; global.fetch = vi.fn().mockResolvedValue(mockResponse); await expect(graphQLService.fetchSchema()).rejects.toThrow( "Failed to fetch schema: Internal Server Error", ); }); it("Should throw error when GraphQL response contains errors", async () => { const mockResponse = { ok: true, json: vi .fn() .mockResolvedValue(TestSchemaFixtures.errors.introspectionDisabled), }; global.fetch = vi.fn().mockResolvedValue(mockResponse); await expect(graphQLService.fetchSchema()).rejects.toThrow( 'GraphQL introspection query failed: [{"message":"Schema introspection is disabled"}]', ); }); it("Should throw error when network request fails", async () => { global.fetch = vi.fn().mockRejectedValue(new Error("Network error")); await expect(graphQLService.fetchSchema()).rejects.toThrow( "Network error", ); }); it("Should handle unauthorized error responses", async () => { const mockResponse = { ok: true, json: vi.fn().mockResolvedValue(TestSchemaFixtures.errors.unauthorized), }; global.fetch = vi.fn().mockResolvedValue(mockResponse); await expect(graphQLService.fetchSchema()).rejects.toThrow( "You must be authenticated to perform this action.", ); }); it("Should handle rate limit error responses", async () => { const mockResponse = { ok: true, json: vi.fn().mockResolvedValue(TestSchemaFixtures.errors.rateLimited), }; global.fetch = vi.fn().mockResolvedValue(mockResponse); await expect(graphQLService.fetchSchema()).rejects.toThrow( "Rate limit exceeded", ); }); it("Should validate that returned schema matches expected structure", async () => { const mockResponse = { ok: true, json: vi .fn() .mockResolvedValue(TestSchemaFixtures.comprehensive.success), }; global.fetch = vi.fn().mockResolvedValue(mockResponse); const schema = await graphQLService.fetchSchema(); // Validate schema structure const queryType = schema.getQueryType(); expect(queryType).toBeDefined(); expect(queryType?.name).toBe("Query"); const mutationType = schema.getMutationType(); expect(mutationType).toBeDefined(); expect(mutationType?.name).toBe("Mutation"); const subscriptionType = schema.getSubscriptionType(); expect(subscriptionType).toBeDefined(); expect(subscriptionType?.name).toBe("Subscription"); // Validate specific fields exist const userField = queryType?.getFields()["user"]; const usersField = queryType?.getFields()["users"]; const searchField = queryType?.getFields()["search"]; expect(userField).toBeDefined(); expect(usersField).toBeDefined(); expect(searchField).toBeDefined(); const createUserField = mutationType?.getFields()["createUser"]; const createPostField = mutationType?.getFields()["createPost"]; expect(createUserField).toBeDefined(); expect(createPostField).toBeDefined(); const userCreatedField = subscriptionType?.getFields()["userCreated"]; const postCreatedField = subscriptionType?.getFields()["postCreated"]; expect(userCreatedField).toBeDefined(); expect(postCreatedField).toBeDefined(); }); it("Should validate enum types are properly parsed", async () => { const mockResponse = { ok: true, json: vi .fn() .mockResolvedValue(TestSchemaFixtures.comprehensive.success), }; global.fetch = vi.fn().mockResolvedValue(mockResponse); const schema = await graphQLService.fetchSchema(); const postStatusEnum = schema.getType("PostStatus"); expect(postStatusEnum).toBeDefined(); expect(postStatusEnum?.toString()).toContain("PostStatus"); }); it("Should validate union types are properly parsed", async () => { const mockResponse = { ok: true, json: vi .fn() .mockResolvedValue(TestSchemaFixtures.comprehensive.success), }; global.fetch = vi.fn().mockResolvedValue(mockResponse); const schema = await graphQLService.fetchSchema(); const searchResultUnion = schema.getType("SearchResult"); expect(searchResultUnion).toBeDefined(); expect(searchResultUnion?.toString()).toContain("SearchResult"); }); }); });

Latest Blog Posts

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/nfishel48/conduit'

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