Skip to main content
Glama
stories.integration.test.ts5.26 kB
// tests/integration/stories.integration.test.ts import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { registerStoryTools } from '@src/tools/stories'; // Import config and API_ENDPOINTS for verifying URL construction AFTER mocking them. // Mock fetch globally for this test file global.fetch = jest.fn(); // Mock the config module jest.mock('@src/config/index', () => ({ config: { spaceId: 'mock-space-id', managementToken: 'mock-management-token', publicToken: 'mock-public-token', // Include publicToken for completeness if any part of stories.ts might use it }, API_ENDPOINTS: { MANAGEMENT: 'https://mock-mapi.storyblok.com/v1', // Use a distinct mock URL CONTENT: 'https://mock-api.storyblok.com/v2', }, getConfig: () => ({ // Mock getConfig as well spaceId: 'mock-space-id', managementToken: 'mock-management-token', publicToken: 'mock-public-token', }), })); // Import mocked versions for assertions import { config as mockedConfig, API_ENDPOINTS as mockedApiEndpoints } from '@src/config/index'; describe('Story Tools Integration Tests', () => { let server: McpServer; beforeEach(() => { server = new McpServer({ name: 'story-integration-test-server', version: '1.0.0' }); (global.fetch as jest.Mock).mockClear(); }); afterEach(() => { jest.clearAllMocks(); }); describe('get-story tool', () => { let getStoryHandler: (args: { id: string }) => Promise<any>; beforeEach(() => { const toolSpy = jest.spyOn(server, 'tool'); registerStoryTools(server); // Register all story tools const getStoryCall = toolSpy.mock.calls.find(call => call[0] === 'get-story'); if (!getStoryCall || typeof getStoryCall[3] !== 'function') { throw new Error('get-story handler was not registered correctly or not found by spy.'); } getStoryHandler = getStoryCall[3]; toolSpy.mockRestore(); }); it('should fetch and return a story successfully', async () => { const mockStoryId = '12345'; const mockStoryData = { story: { id: mockStoryId, name: 'Test Story', slug: 'test-story', content: { component: 'page', body: 'Hello world' } } }; (global.fetch as jest.Mock).mockResolvedValueOnce({ ok: true, status: 200, json: async () => mockStoryData, text: async () => JSON.stringify(mockStoryData) }); const result = await getStoryHandler({ id: mockStoryId }); expect(global.fetch).toHaveBeenCalledTimes(1); const expectedUrl = `${mockedApiEndpoints.MANAGEMENT}/spaces/${mockedConfig.spaceId}/stories/${mockStoryId}`; expect((global.fetch as jest.Mock).mock.calls[0][0]).toBe(expectedUrl); expect(result.isError).toBeUndefined(); expect(result.content).toBeDefined(); expect(result.content[0].type).toBe('text'); expect(JSON.parse(result.content[0].text)).toEqual(mockStoryData); }); it('should return an error if fetch fails (e.g., 404 Not Found)', async () => { const mockStoryId = '67890'; const apiErrorPayload = { // This is the 'details' part of our enhancedError // These fields would typically come from Storyblok's actual error response slug: mockStoryId, message: "The requested story was not found.", code: "story_not_found" }; const fullExpectedUrl = `${mockedApiEndpoints.MANAGEMENT}/spaces/${mockedConfig.spaceId}/stories/${mockStoryId}`; const enhancedErrorString = JSON.stringify({ error: "404 Not Found", // This is constructed by handleApiResponse details: apiErrorPayload, context: { endpoint: fullExpectedUrl, // Use the full URL here spaceId: mockedConfig.spaceId, tokenPermissions: "unknown", requiredPermissions: "unknown", suggestedFix: "The requested resource was not found. Please check the endpoint and resource ID." } }); (global.fetch as jest.Mock).mockResolvedValueOnce({ ok: false, status: 404, statusText: 'Not Found', // handleApiResponse calls .text() first when !ok, then tries to parse it. text: async () => JSON.stringify(apiErrorPayload) }); const result = await getStoryHandler({ id: mockStoryId }); expect(global.fetch).toHaveBeenCalledTimes(1); expect(result.isError).toBe(true); expect(result.content).toBeDefined(); // The error message from handleApiResponse is a stringified JSON of the enhancedError object expect(result.content[0].text).toBe(`Error: ${enhancedErrorString}`); }); it('should return an error if fetch throws an exception (network error)', async () => { const mockStoryId = 'exception-id'; const networkError = new Error('Network connection failed'); (global.fetch as jest.Mock).mockRejectedValueOnce(networkError); const result = await getStoryHandler({ id: mockStoryId }); expect(global.fetch).toHaveBeenCalledTimes(1); expect(result.isError).toBe(true); expect(result.content[0].text).toBe(`Error: ${networkError.message}`); // Direct error message }); }); });

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/arb-ec/mcp-storyblok-server'

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