Skip to main content
Glama
StaticMapImageTool.test.ts15.2 kB
process.env.MAPBOX_ACCESS_TOKEN = 'eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ0ZXN0In0.signature'; import { setupFetch, assertHeadersSent } from '../../utils/requestUtils.test-helpers.js'; import { StaticMapImageTool } from '../static-map-image-tool/StaticMapImageTool.js'; describe('StaticMapImageTool', () => { afterEach(() => { jest.restoreAllMocks(); }); it('sends custom header', async () => { const mockFetch = setupFetch(); await new StaticMapImageTool().run({ center: { longitude: -74.006, latitude: 40.7128 }, zoom: 12, size: { width: 600, height: 400 }, style: 'mapbox/streets-v12' }); assertHeadersSent(mockFetch); }); it('returns image content with base64 data', async () => { // Mock image buffer const mockImageBuffer = Buffer.from('fake-image-data'); const mockArrayBuffer = mockImageBuffer.buffer.slice( mockImageBuffer.byteOffset, mockImageBuffer.byteOffset + mockImageBuffer.byteLength ); const mockFetch = setupFetch({ arrayBuffer: jest.fn().mockResolvedValue(mockArrayBuffer) }); const result = await new StaticMapImageTool().run({ center: { longitude: -74.006, latitude: 40.7128 }, zoom: 10, size: { width: 800, height: 600 }, style: 'mapbox/satellite-v9' }); expect(result.isError).toBe(false); expect(result.content).toHaveLength(1); expect(result.content[0]).toMatchObject({ type: 'image', data: mockImageBuffer.toString('base64'), mimeType: 'image/jpeg' // satellite is a raster style }); }); it('constructs correct Mapbox Static API URL', async () => { const mockFetch = setupFetch(); await new StaticMapImageTool().run({ center: { longitude: -122.4194, latitude: 37.7749 }, zoom: 15, size: { width: 1024, height: 768 }, style: 'mapbox/dark-v10' }); const calledUrl = mockFetch.mock.calls[0][0]; expect(calledUrl).toContain('styles/v1/mapbox/dark-v10/static/'); expect(calledUrl).toContain('-122.4194,37.7749,15'); expect(calledUrl).toContain('1024x768'); expect(calledUrl).toContain('access_token='); }); it('uses default style when not specified', async () => { const mockFetch = setupFetch(); await new StaticMapImageTool().run({ center: { longitude: 0, latitude: 0 }, zoom: 1, size: { width: 300, height: 200 } }); const calledUrl = mockFetch.mock.calls[0][0]; expect(calledUrl).toContain('styles/v1/mapbox/streets-v12/static/'); }); it('handles fetch errors gracefully', async () => { const mockFetch = setupFetch({ ok: false, status: 404, statusText: 'Not Found' }); const result = await new StaticMapImageTool().run({ center: { longitude: -74.006, latitude: 40.7128 }, zoom: 12, size: { width: 600, height: 400 } }); expect(result.isError).toBe(true); expect(result.content[0]).toMatchObject({ type: 'text', text: 'Failed to fetch map image: 404 Not Found' }); }); it('validates coordinate constraints', async () => { const tool = new StaticMapImageTool(); // Test invalid longitude await expect( tool.run({ center: { longitude: -181, latitude: 40 }, zoom: 10, size: { width: 600, height: 400 } }) ).resolves.toMatchObject({ isError: true }); // Test invalid latitude await expect( tool.run({ center: { longitude: -74, latitude: 90 }, zoom: 10, size: { width: 600, height: 400 } }) ).resolves.toMatchObject({ isError: true }); }); it('validates size constraints', async () => { const tool = new StaticMapImageTool(); // Test size too large await expect( tool.run({ center: { longitude: -74.006, latitude: 40.7128 }, zoom: 10, size: { width: 1281, height: 600 } }) ).resolves.toMatchObject({ isError: true }); // Test size too small await expect( tool.run({ center: { longitude: -74.006, latitude: 40.7128 }, zoom: 10, size: { width: 0, height: 600 } }) ).resolves.toMatchObject({ isError: true }); }); it('supports high density parameter', async () => { const mockFetch = setupFetch(); await new StaticMapImageTool().run({ center: { longitude: -74.006, latitude: 40.7128 }, zoom: 12, size: { width: 600, height: 400 }, highDensity: true }); const calledUrl = mockFetch.mock.calls[0][0]; expect(calledUrl).toContain('600x400@2x'); }); describe('overlay support', () => { it('adds marker overlay to URL', async () => { const mockFetch = setupFetch(); await new StaticMapImageTool().run({ center: { longitude: -74.006, latitude: 40.7128 }, zoom: 12, size: { width: 600, height: 400 }, overlays: [ { type: 'marker', longitude: -74.006, latitude: 40.7128, size: 'large', label: 'a', color: 'ff0000' } ] }); const calledUrl = mockFetch.mock.calls[0][0]; expect(calledUrl).toContain('pin-l-a+ff0000(-74.006,40.7128)/'); }); it('adds custom marker overlay to URL', async () => { const mockFetch = setupFetch(); await new StaticMapImageTool().run({ center: { longitude: -74.006, latitude: 40.7128 }, zoom: 12, size: { width: 600, height: 400 }, overlays: [ { type: 'custom-marker', longitude: -74.006, latitude: 40.7128, url: 'https://example.com/marker.png' } ] }); const calledUrl = mockFetch.mock.calls[0][0]; expect(calledUrl).toContain( `url-${encodeURIComponent('https://example.com/marker.png')}(-74.006,40.7128)/` ); }); it('adds path overlay to URL', async () => { const mockFetch = setupFetch(); await new StaticMapImageTool().run({ center: { longitude: -74.006, latitude: 40.7128 }, zoom: 12, size: { width: 600, height: 400 }, overlays: [ { type: 'path', encodedPolyline: 'u{~vFvyys@fS]', strokeWidth: 3, strokeColor: '0000ff', strokeOpacity: 0.8, fillColor: 'ff0000', fillOpacity: 0.5 } ] }); const calledUrl = mockFetch.mock.calls[0][0]; expect(calledUrl).toContain( `path-3+0000ff-0.8+ff0000-0.5(${encodeURIComponent('u{~vFvyys@fS]')})/` ); }); it('adds GeoJSON overlay to URL', async () => { const mockFetch = setupFetch(); const geoJsonData = { type: 'Point', coordinates: [-74.006, 40.7128] }; await new StaticMapImageTool().run({ center: { longitude: -74.006, latitude: 40.7128 }, zoom: 12, size: { width: 600, height: 400 }, overlays: [ { type: 'geojson', data: geoJsonData } ] }); const calledUrl = mockFetch.mock.calls[0][0]; expect(calledUrl).toContain( `geojson(${encodeURIComponent(JSON.stringify(geoJsonData))})/` ); }); it('supports multiple overlays in order', async () => { const mockFetch = setupFetch(); await new StaticMapImageTool().run({ center: { longitude: -74.006, latitude: 40.7128 }, zoom: 12, size: { width: 600, height: 400 }, overlays: [ { type: 'marker', longitude: -74.01, latitude: 40.71, size: 'small', color: '00ff00' }, { type: 'marker', longitude: -74.002, latitude: 40.715, size: 'large', label: 'b', color: 'ff0000' } ] }); const calledUrl = mockFetch.mock.calls[0][0]; expect(calledUrl).toContain( 'pin-s+00ff00(-74.01,40.71),pin-l-b+ff0000(-74.002,40.715)/' ); }); it('works without overlays', async () => { const mockFetch = setupFetch(); await new StaticMapImageTool().run({ center: { longitude: -74.006, latitude: 40.7128 }, zoom: 12, size: { width: 600, height: 400 } }); const calledUrl = mockFetch.mock.calls[0][0]; // Should not have overlay string before coordinates expect(calledUrl).toMatch(/static\/-74\.006,40\.7128,12/); }); it('transforms uppercase labels to lowercase', async () => { const mockFetch = setupFetch(); await new StaticMapImageTool().run({ center: { longitude: -74.006, latitude: 40.7128 }, zoom: 12, size: { width: 600, height: 400 }, overlays: [ { type: 'marker', longitude: -74.006, latitude: 40.7128, size: 'small', label: 'Z', color: '0000ff' } ] }); const calledUrl = mockFetch.mock.calls[0][0]; // Should contain lowercase 'z' even though 'Z' was provided expect(calledUrl).toContain('pin-s-z+0000ff(-74.006,40.7128)/'); }); it('supports Maki icon names as labels', async () => { const mockFetch = setupFetch(); await new StaticMapImageTool().run({ center: { longitude: -74.006, latitude: 40.7128 }, zoom: 12, size: { width: 600, height: 400 }, overlays: [ { type: 'marker', longitude: -74.006, latitude: 40.7128, size: 'large', label: 'embassy', color: 'ff0000' } ] }); const calledUrl = mockFetch.mock.calls[0][0]; expect(calledUrl).toContain('pin-l-embassy+ff0000(-74.006,40.7128)/'); }); it('transforms uppercase Maki icon names to lowercase', async () => { const mockFetch = setupFetch(); await new StaticMapImageTool().run({ center: { longitude: -74.006, latitude: 40.7128 }, zoom: 12, size: { width: 600, height: 400 }, overlays: [ { type: 'marker', longitude: -74.01, latitude: 40.71, size: 'small', label: 'AIRPORT', color: '00ff00' } ] }); const calledUrl = mockFetch.mock.calls[0][0]; // Should contain lowercase 'airport' even though 'AIRPORT' was provided expect(calledUrl).toContain('pin-s-airport+00ff00(-74.01,40.71)/'); }); it('supports numeric labels', async () => { const mockFetch = setupFetch(); await new StaticMapImageTool().run({ center: { longitude: -74.006, latitude: 40.7128 }, zoom: 12, size: { width: 600, height: 400 }, overlays: [ { type: 'marker', longitude: -74.006, latitude: 40.7128, size: 'large', label: '42', color: '0000ff' } ] }); const calledUrl = mockFetch.mock.calls[0][0]; expect(calledUrl).toContain('pin-l-42+0000ff(-74.006,40.7128)/'); }); it('handles complex overlay combination with paths and markers', async () => { const mockFetch = setupFetch(); await new StaticMapImageTool().run({ center: { longitude: -80.278, latitude: 25.796 }, zoom: 15, size: { width: 800, height: 600 }, style: 'mapbox/streets-v12', overlays: [ { type: 'marker', longitude: -80.2793529, latitude: 25.7950805, size: 'large', label: 'A', color: 'ff0000' }, { type: 'marker', longitude: -80.27899169921875, latitude: 25.796667098999023, size: 'large', label: 'S', color: '00cc00' }, { type: 'path', encodedPolyline: 'mbm|C~k~hNzBCdAg@ZYnAoDWsR[yHgAmEc@mDeAiWGqIHyJYwKQgA_@@h@|JKlRVj^W|Co@hCyBzDi@f@c@NeAjAg@|AXhRzAbD', strokeWidth: 4, strokeColor: '0066ff' } ] }); const calledUrl = mockFetch.mock.calls[0][0]; // Check that markers have lowercase labels expect(calledUrl).toContain('pin-l-a+ff0000(-80.2793529,25.7950805)'); expect(calledUrl).toContain( 'pin-l-s+00cc00(-80.27899169921875,25.796667098999023)' ); // Check path const expectedPolyline = 'mbm|C~k~hNzBCdAg@ZYnAoDWsR[yHgAmEc@mDeAiWGqIHyJYwKQgA_@@h@|JKlRVj^W|Co@hCyBzDi@f@c@NeAjAg@|AXhRzAbD'; expect(calledUrl).toContain( `path-4+0066ff(${encodeURIComponent(expectedPolyline)})` ); }); it('truncates non-Maki multi-character labels to first character', async () => { const mockFetch = setupFetch(); await new StaticMapImageTool().run({ center: { longitude: -74.006, latitude: 40.7128 }, zoom: 12, size: { width: 600, height: 400 }, overlays: [ { type: 'marker', longitude: -74.006, latitude: 40.7128, size: 'small', label: 'Hello', color: '0000ff' }, { type: 'marker', longitude: -74.01, latitude: 40.71, size: 'large', label: 'XYZ', color: 'ff0000' } ] }); const calledUrl = mockFetch.mock.calls[0][0]; // Should contain only the first character in lowercase expect(calledUrl).toContain('pin-s-h+0000ff(-74.006,40.7128)'); expect(calledUrl).toContain('pin-l-x+ff0000(-74.01,40.71)'); }); it('preserves full Maki icon names that are in the supported list', async () => { const mockFetch = setupFetch(); await new StaticMapImageTool().run({ center: { longitude: -74.006, latitude: 40.7128 }, zoom: 12, size: { width: 600, height: 400 }, overlays: [ { type: 'marker', longitude: -74.006, latitude: 40.7128, size: 'small', label: 'restaurant', color: '0000ff' }, { type: 'marker', longitude: -74.01, latitude: 40.71, size: 'large', label: 'hospital', color: 'ff0000' }, { type: 'marker', longitude: -74.005, latitude: 40.715, size: 'small', label: 'BICYCLE', // uppercase should be converted to lowercase color: '00ff00' } ] }); const calledUrl = mockFetch.mock.calls[0][0]; // Should preserve the full Maki icon names expect(calledUrl).toContain('pin-s-restaurant+0000ff(-74.006,40.7128)'); expect(calledUrl).toContain('pin-l-hospital+ff0000(-74.01,40.71)'); expect(calledUrl).toContain('pin-s-bicycle+00ff00(-74.005,40.715)'); }); }); });

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/Waldzell-Agentics/mcp-server'

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