Skip to main content
Glama
MetadataDiscoveryService.test.ts4.45 kB
import { describe, it, expect, beforeEach, vi } from 'vitest'; import { DefaultMetadataDiscoveryService } from '../../../src/services/metadata/MetadataDiscoveryService.js'; import { DefaultMetadataErrorService } from '../../../src/services/metadata/MetadataErrorService.js'; import { InMemoryMetadataMetricsService } from '../../../src/services/metadata/MetadataMetricsService.js'; import { DefaultMetadataTransformService } from '../../../src/services/metadata/MetadataTransformService.js'; import type { MetadataCacheKey, MetadataCacheService, MetadataResult, } from '../../../src/services/metadata/types.js'; import { UniversalResourceType } from '../../../src/handlers/tool-configs/universal/types.js'; class FakeMetadataCacheService implements MetadataCacheService { private readonly store = new Map<string, MetadataResult>(); async getOrLoad( key: MetadataCacheKey, loader: () => Promise<MetadataResult> ) { const cacheKey = `${key.resourceType}:${key.objectSlug ?? ''}`; if (this.store.has(cacheKey)) { return { data: this.store.get(cacheKey)!, fromCache: true }; } const data = await loader(); this.store.set(cacheKey, data); return { data, fromCache: false }; } } const mockGet = vi.fn(); const mockClient = { get: mockGet }; describe('DefaultMetadataDiscoveryService', () => { let cacheService: FakeMetadataCacheService; let metricsService: InMemoryMetadataMetricsService; let service: DefaultMetadataDiscoveryService; beforeEach(() => { cacheService = new FakeMetadataCacheService(); metricsService = new InMemoryMetadataMetricsService(); service = new DefaultMetadataDiscoveryService( cacheService, metricsService, new DefaultMetadataTransformService(), new DefaultMetadataErrorService() ); vi.clearAllMocks(); (globalThis as any).setTestApiClient?.(mockClient); }); it('discovers attributes with caching and metrics', async () => { const attributes = [{ title: 'Name', api_slug: 'name', type: 'text' }]; mockGet.mockResolvedValue({ data: { data: attributes } }); const first = await service.discoverForType({ resourceType: UniversalResourceType.COMPANIES, }); const second = await service.discoverForType({ resourceType: UniversalResourceType.COMPANIES, }); expect(first.mappings).toEqual({ Name: 'name' }); expect(second.mappings).toEqual({ Name: 'name' }); expect(mockGet).toHaveBeenCalledTimes(1); const summary = metricsService.getMetrics({ includeErrors: true }); expect(summary.totalRequests).toBe(2); expect(summary.cacheHits).toBe(1); }); it('filters task attributes by category without API calls', async () => { mockGet.mockReset(); const result = await service.discoverTasks({ categories: ['basic'] }); expect(result.resource_type).toBe(UniversalResourceType.TASKS); const attributes = result.attributes as Array<{ category?: string }>; expect(attributes.every((attr) => attr.category === 'basic')).toBe(true); expect(mockGet).not.toHaveBeenCalled(); }); it('returns normalized record metadata with mappings and counts', async () => { mockGet.mockResolvedValue({ data: { data: [ { title: 'Name', api_slug: 'name', category: 'basic', type: 'text', }, { title: 'Notes', api_slug: 'notes', category: 'extended', type: 'text', }, ], }, }); const result = await service.discoverObject({ objectSlug: 'companies', categories: ['basic'], }); expect(mockGet).toHaveBeenCalledWith( '/objects/companies/attributes?categories=basic' ); expect(result.count).toBe(1); expect(result.mappings).toEqual({ Name: 'name' }); expect(result.resource_type).toBe(UniversalResourceType.RECORDS); expect(result.objectSlug).toBe('companies'); expect(Array.isArray(result.attributes)).toBe(true); expect((result.attributes as unknown[]).length).toBe(1); }); it('propagates structured errors from API failures', async () => { mockGet.mockRejectedValue(new Error('boom')); await expect( service.discoverForType({ resourceType: UniversalResourceType.PEOPLE, useCache: false, }) ).rejects.toThrowError('Failed to discover people attributes: boom'); }); });

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/kesslerio/attio-mcp-server'

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