Skip to main content
Glama
advanced-operations-content.test.tsβ€’11.6 kB
import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest'; import { UniversalResourceType, ContentSearchType, TimeframeType, ContentSearchParams, TimeframeSearchParams, } from '@handlers/tool-configs/universal/types.js'; import { setupUnitTestMocks, cleanupMocks, getMockInstances, } from '@test/handlers/tool-configs/universal/helpers/index.js'; describe('Universal Advanced Operations - Content & Timeframe Tests', () => { let searchByContentConfig: any; let searchByTimeframeConfig: any; beforeEach(async () => { await setupUnitTestMocks(); // Import after mocks are set up const advancedOps = await import( '@handlers/tool-configs/universal/advanced-operations.js' ); searchByContentConfig = advancedOps.searchByContentConfig; searchByTimeframeConfig = advancedOps.searchByTimeframeConfig; }); afterEach(() => { cleanupMocks(); }); describe('records_search_by_content tool', () => { it('should search companies by notes content', async () => { const mockResults = [ { id: { record_id: 'comp-1' }, values: { name: [{ value: 'Company with Notes' }], }, }, ]; const { mockSearchService } = getMockInstances(); mockSearchService.searchRecords.mockResolvedValue(mockResults); const params: ContentSearchParams = { resource_type: UniversalResourceType.NOTES, content_type: ContentSearchType.NOTES, search_query: 'important meeting', limit: 10, }; const result = await searchByContentConfig.handler(params); expect(result).toEqual(mockResults); expect(mockSearchService.searchRecords).toHaveBeenCalled(); }); it('should search people by notes content', async () => { const mockResults = [ { id: { record_id: 'person-1' }, values: { name: [{ value: 'Person with Notes' }], }, }, ]; const { mockSearchService } = getMockInstances(); mockSearchService.searchRecords.mockResolvedValue(mockResults); const params: ContentSearchParams = { resource_type: UniversalResourceType.NOTES, content_type: ContentSearchType.NOTES, search_query: 'follow up', limit: 5, }; const result = await searchByContentConfig.handler(params); expect(result).toEqual(mockResults); expect(mockSearchService.searchRecords).toHaveBeenCalled(); }); it('should search people by activity content', async () => { const mockResults = [ { id: { record_id: 'person-1' }, values: { name: [{ value: 'Active Person' }], }, }, ]; const { mockSpecialized } = getMockInstances(); mockSpecialized.searchPeopleByActivity.mockResolvedValue(mockResults); const params: ContentSearchParams = { resource_type: UniversalResourceType.PEOPLE, content_type: ContentSearchType.ACTIVITY, search_query: 'activity search', }; const result = await searchByContentConfig.handler(params); expect(result).toEqual(mockResults); expect(mockSpecialized.searchPeopleByActivity).toHaveBeenCalledWith({ dateRange: { preset: 'last_month', }, interactionType: 'any', }); }); it('should handle unsupported interaction content search', async () => { const params: ContentSearchParams = { resource_type: UniversalResourceType.COMPANIES, content_type: ContentSearchType.INTERACTIONS, search_query: 'interaction search', }; await expect(searchByContentConfig.handler(params)).rejects.toThrow( /Interaction content search is not currently available/ ); }); it('should format content search results correctly', async () => { const mockResults = [ { id: { record_id: 'comp-1' }, values: { name: [{ value: 'Company with Content' }], }, }, ]; const { mockHandlers } = getMockInstances(); mockHandlers.formatResourceType.mockReturnValue('company'); const formatted = (searchByContentConfig.formatResult as any)( mockResults, ContentSearchType.NOTES, UniversalResourceType.COMPANIES ); expect(formatted).toContain('Found 1 company with matching notes'); expect(formatted).toContain('1. Company with Content (ID: comp-1)'); }); it('should handle invalid resource types in content search', async () => { const invalidParams = { resource_type: 'invalid-type' as any, content_type: ContentSearchType.NOTES, search_query: 'test', }; await expect( searchByContentConfig.handler(invalidParams) ).rejects.toThrow( /Content search not supported for resource type invalid-type/ ); }); }); describe('records_search_by_timeframe tool', () => { it('should search people by creation date', async () => { const mockResults = [ { id: { record_id: 'person-1' }, values: { name: [{ value: 'Recently Created Person' }], }, created_at: '2023-12-01T00:00:00Z', }, ]; const { mockSpecialized } = getMockInstances(); mockSpecialized.searchPeopleByCreationDate.mockResolvedValue(mockResults); const params: TimeframeSearchParams = { resource_type: UniversalResourceType.PEOPLE, timeframe_type: TimeframeType.CREATED, start_date: '2023-12-01T00:00:00Z', end_date: '2023-12-31T23:59:59Z', limit: 10, }; const result = await searchByTimeframeConfig.handler(params); expect(result).toEqual(mockResults); expect(mockSpecialized.searchPeopleByCreationDate).toHaveBeenCalledWith({ start: '2023-12-01T00:00:00Z', end: '2023-12-31T23:59:59Z', }); }); it('should search people by modification date', async () => { const mockResults = [ { id: { record_id: 'person-1' }, values: { name: [{ value: 'Recently Modified Person' }], }, updated_at: '2023-12-15T10:30:00Z', }, ]; const { mockSearchService } = getMockInstances(); mockSearchService.searchRecords.mockResolvedValue(mockResults); const params: TimeframeSearchParams = { resource_type: UniversalResourceType.PEOPLE, timeframe_type: TimeframeType.MODIFIED, start_date: '2023-12-01T00:00:00Z', end_date: '2023-12-31T23:59:59Z', }; const result = await searchByTimeframeConfig.handler(params); expect(result).toEqual(mockResults); expect(mockSearchService.searchRecords).toHaveBeenCalled(); }); it('should search people by last interaction with date validation', async () => { const mockResults = [ { id: { record_id: 'person-1' }, values: { name: [{ value: 'Recently Interacted Person' }], }, }, ]; const { mockSearchService } = getMockInstances(); mockSearchService.searchRecords.mockResolvedValue(mockResults); const params: TimeframeSearchParams = { resource_type: UniversalResourceType.PEOPLE, timeframe_type: TimeframeType.LAST_INTERACTION, start_date: '2023-12-01T00:00:00Z', end_date: '2023-12-31T23:59:59Z', }; const result = await searchByTimeframeConfig.handler(params); expect(result).toEqual(mockResults); expect(mockSearchService.searchRecords).toHaveBeenCalled(); }); it('should handle missing date range for last interaction', async () => { const params: TimeframeSearchParams = { resource_type: UniversalResourceType.PEOPLE, timeframe_type: TimeframeType.LAST_INTERACTION, }; await expect(searchByTimeframeConfig.handler(params)).rejects.toThrow( 'At least one date (start_date or end_date) is required for timeframe search' ); }); it('should support timeframe search for companies', async () => { // Companies timeframe search is now enabled // This test validates that the old restriction is removed const mockResults = [ { id: { record_id: 'comp-1' }, values: { name: [{ value: 'Test Company' }] }, }, ]; const { mockSearchService } = getMockInstances(); mockSearchService.searchRecords.mockResolvedValue(mockResults); const params: TimeframeSearchParams = { resource_type: UniversalResourceType.COMPANIES, timeframe_type: TimeframeType.CREATED, start_date: '2023-12-01T00:00:00Z', }; // Should successfully execute without throwing errors const result = await searchByTimeframeConfig.handler(params); expect(result).toEqual(mockResults); expect(mockSearchService.searchRecords).toHaveBeenCalled(); }); it('should format timeframe results with date info', async () => { const mockResults = [ { id: { record_id: 'person-1' }, values: { name: [{ value: 'Test Person' }], }, created_at: '2023-12-01T10:30:00Z', }, ]; const { mockHandlers } = getMockInstances(); mockHandlers.formatResourceType.mockReturnValue('person'); const formatted = (searchByTimeframeConfig.formatResult as any)( mockResults, TimeframeType.CREATED, UniversalResourceType.PEOPLE ); expect(formatted).toContain('Found 1 person by created'); expect(formatted).toContain( '1. Test Person (created: 12/1/2023) (ID: person-1)' ); }); }); describe('Content and Timeframe validation and edge cases', () => { it('should handle validation errors in content and timeframe tools', async () => { const { mockSchemas } = getMockInstances(); // Store the original mock implementation to restore it later const originalMock = mockSchemas.validateUniversalToolParams; mockSchemas.validateUniversalToolParams.mockImplementation(() => { throw new Error('Validation failed'); }); const tools = [ { tool: searchByContentConfig, params: { resource_type: UniversalResourceType.COMPANIES, content_type: ContentSearchType.NOTES, search_query: 'test', }, }, { tool: searchByTimeframeConfig, params: { resource_type: UniversalResourceType.PEOPLE, timeframe_type: TimeframeType.CREATED, }, }, ]; for (const { tool, params } of tools) { await expect(tool.handler(params)).rejects.toThrow('Validation failed'); } // Restore the original mock behavior to not affect other tests mockSchemas.validateUniversalToolParams.mockImplementation( (operation: string, params: any) => { return params || {}; } ); }); it('should handle empty results gracefully', async () => { const emptyResults: any[] = []; // For empty arrays, formatters should show "found 0" not "No results found" based on current implementation expect( (searchByContentConfig.formatResult as any)(emptyResults) ).toContain('Found 0 records with matching'); expect( (searchByTimeframeConfig.formatResult as any)(emptyResults) ).toContain('Found 0 records by'); }); }); });

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