UNPKG

@geocoding-ai/mcp

Version:

Model Context Protocol server for geocoding

95 lines (94 loc) 4.09 kB
// src/tools/reverseGeocode.test.ts import { describe, it, expect, mock, beforeEach, afterEach } from 'bun:test'; import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { registerReverseGeocodeTool } from '../../tools/reverseGeocode.js'; import * as nominatimClient from '../../clients/nominatimClient.js'; import { handleGeocodeResult } from '../../tools/prepareResponse.js'; // Using actual implementation // Mock McpServer to capture the handler mock.module('@modelcontextprotocol/sdk/server/mcp.js', () => ({ McpServer: class { tool = mock((_name, _description, _schema, handler) => { ; this.handler = handler; }); }, })); // Mock only nominatimClient mock.module('@/clients/nominatimClient.js', () => ({ // Also mock geocodeAddress to avoid interference if it's imported by other modules under test geocodeAddress: mock(async (params) => [ { place_id: 999, display_name: `Geocode mock for ${params.query}` }, ]), reverseGeocode: mock(async (params) => { // Default successful mock implementation return { place_id: 456, display_name: `Address for ${params.lat},${params.lon}`, }; }), })); describe('registerReverseGeocodeTool', () => { let serverInstance; let toolHandler; beforeEach(() => { serverInstance = new McpServer({ name: 'test-server', version: '1.0' }); registerReverseGeocodeTool(serverInstance); toolHandler = serverInstance.handler; }); afterEach(() => { mock.restore(); // Restore all mocks }); it("should register a tool named 'reverse_geocode'", () => { expect(serverInstance.tool).toHaveBeenCalled(); const mockCalls = serverInstance.tool.mock .calls; expect(mockCalls[0]?.[0]).toBe('reverse_geocode'); expect(typeof mockCalls[0]?.[1]).toBe('string'); // Description expect(mockCalls[0]?.[2]).toBeDefined(); // Schema expect(typeof mockCalls[0]?.[3]).toBe('function'); // Handler }); it('should call reverseGeocode and use actual handleGeocodeResult on successful execution', async () => { if (!toolHandler) throw new Error('Handler not registered'); const params = { lat: '40.7128', lon: '-74.0060' }; const mockReverseGeocodeApiResult = { place_id: 1, display_name: 'New York, NY', }; const expectedCallToolResult = handleGeocodeResult(mockReverseGeocodeApiResult); const reverseGeocodeSpy = nominatimClient.reverseGeocode; reverseGeocodeSpy.mockResolvedValue(mockReverseGeocodeApiResult); const result = await toolHandler(params); expect(reverseGeocodeSpy).toHaveBeenCalledWith(params); expect(result).toEqual(expectedCallToolResult); }); it('should pass params correctly to reverseGeocode', async () => { if (!toolHandler) throw new Error('Handler not registered'); const params = { lat: '48.8566', lon: '2.3522', zoom: 10, format: 'jsonv2', }; const reverseGeocodeSpy = nominatimClient.reverseGeocode; reverseGeocodeSpy.mockResolvedValue({ place_id: 789, display_name: 'Paris Result', }); await toolHandler(params); expect(reverseGeocodeSpy).toHaveBeenCalledWith(params); }); it('should handle errors from reverseGeocode by propagating the error', async () => { if (!toolHandler) throw new Error('Handler not registered'); const params = { lat: '0', lon: '0' }; // Params to trigger error const errorMessage = 'Nominatim API error for reverse geocode'; const reverseGeocodeSpy = nominatimClient.reverseGeocode; reverseGeocodeSpy.mockImplementation(async () => { throw new Error(errorMessage); }); expect(toolHandler(params)).rejects.toThrow(errorMessage); expect(reverseGeocodeSpy).toHaveBeenCalledWith(params); }); });