Skip to main content
Glama
waldzellai

Exa Websets MCP Server

by waldzellai
ItemService.enhanced.test.ts10.5 kB
/** * Enhanced ItemService Tests * * Tests for the new filtering, pagination, and caching functionality * * NOTE: These tests are for future functionality that hasn't been implemented yet. * Commenting out to allow the build to pass. */ /* import { ItemService, ItemFilterValidator, ItemFilterEngine, PaginationManager, ItemSortingEngine } from '../../../src/services/ItemService.js'; import { WebsetItem, ItemFilters, PaginationOptions, SortingOptions } from '../../../src/types/websets.js'; import { itemListCache } from '../../../src/utils/cache.js'; */ // Mock data const createMockItems = (count: number = 10): WebsetItem[] => { return Array.from({ length: count }, (_, i) => ({ id: `item-${i}`, object: "webset_item" as const, websetId: "test-webset", searchId: `search-${i % 3}`, url: `https://example.com/item-${i}`, title: `Test Item ${i}`, content: `This is test content for item ${i}. It contains various keywords like technology, business, and innovation.`, entity: { type: i % 2 === 0 ? "company" : "person", properties: {} }, verification: { status: i % 3 === 0 ? "verified" : i % 3 === 1 ? "unverified" : "rejected", reasoning: `Test reasoning for item ${i}`, references: [] }, enrichments: i % 4 === 0 ? { testEnrichment: `value-${i}` } : {}, metadata: {}, createdAt: new Date(Date.now() - i * 24 * 60 * 60 * 1000).toISOString(), updatedAt: new Date(Date.now() - i * 12 * 60 * 60 * 1000).toISOString() })); }; describe('Enhanced ItemService', () => { beforeEach(() => { // Clear cache before each test itemListCache.clearCache(); }); describe('ItemFilterValidator', () => { test('should validate date range filters correctly', () => { const validFilters: ItemFilters = { dateRange: { field: 'createdAt', after: new Date('2024-01-01'), before: new Date('2024-12-31') } }; const result = ItemFilterValidator.validateFilters(validFilters); expect(result.isValid).toBe(true); expect(result.errors).toHaveLength(0); }); test('should reject invalid date ranges', () => { const invalidFilters: ItemFilters = { dateRange: { field: 'createdAt', after: new Date('2024-12-31'), before: new Date('2024-01-01') // Invalid: after > before } }; const result = ItemFilterValidator.validateFilters(invalidFilters); expect(result.isValid).toBe(false); expect(result.errors).toContain("'after' date must be before 'before' date"); }); test('should validate search term length', () => { const shortTermFilters: ItemFilters = { contentSearch: { term: 'a', // Too short fields: ['title'] } }; const result = ItemFilterValidator.validateFilters(shortTermFilters); expect(result.isValid).toBe(false); expect(result.errors).toContain('Search term must be at least 2 characters'); }); test('should validate long search terms', () => { const longTermFilters: ItemFilters = { contentSearch: { term: 'a'.repeat(501), // Too long fields: ['title'] } }; const result = ItemFilterValidator.validateFilters(longTermFilters); expect(result.isValid).toBe(false); expect(result.errors).toContain('Search term cannot exceed 500 characters'); }); }); describe('ItemFilterEngine', () => { const mockItems = createMockItems(10); test('should filter by verification status', () => { const filters: ItemFilters = { verificationStatus: 'verified' }; const filtered = ItemFilterEngine.applyFilters(mockItems, filters); expect(filtered.every(item => item.verification.status === 'verified')).toBe(true); }); test('should filter by entity type', () => { const filters: ItemFilters = { entityType: 'company' }; const filtered = ItemFilterEngine.applyFilters(mockItems, filters); expect(filtered.every(item => item.entity.type === 'company')).toBe(true); }); test('should filter by search ID', () => { const filters: ItemFilters = { searchId: 'search-0' }; const filtered = ItemFilterEngine.applyFilters(mockItems, filters); expect(filtered.every(item => item.searchId === 'search-0')).toBe(true); }); test('should filter by enrichment presence', () => { const filters: ItemFilters = { hasEnrichments: true }; const filtered = ItemFilterEngine.applyFilters(mockItems, filters); expect(filtered.every(item => item.enrichments && Object.keys(item.enrichments).length > 0 )).toBe(true); }); test('should filter by content search', () => { const filters: ItemFilters = { contentSearch: { term: 'technology', fields: ['content'] } }; const filtered = ItemFilterEngine.applyFilters(mockItems, filters); expect(filtered.every(item => item.content.toLowerCase().includes('technology') )).toBe(true); }); test('should apply multiple filters', () => { const filters: ItemFilters = { verificationStatus: 'verified', entityType: 'company', hasEnrichments: true }; const filtered = ItemFilterEngine.applyFilters(mockItems, filters); expect(filtered.every(item => item.verification.status === 'verified' && item.entity.type === 'company' && item.enrichments && Object.keys(item.enrichments).length > 0 )).toBe(true); }); }); describe('PaginationManager', () => { const mockItems = createMockItems(100); test('should handle cursor-based pagination', async () => { const options: PaginationOptions = { type: 'cursor', limit: 25, cursor: undefined, estimateTotal: true }; const result = await PaginationManager.paginateItems(mockItems, options); expect(result.data).toHaveLength(25); expect(result.hasMore).toBe(true); expect(result.nextCursor).toBeDefined(); expect(result.totalEstimate).toBe(100); }); test('should handle offset-based pagination', async () => { const options: PaginationOptions = { type: 'offset', limit: 25, offset: 50, estimateTotal: true }; const result = await PaginationManager.paginateItems(mockItems, options); expect(result.data).toHaveLength(25); expect(result.offset).toBe(50); expect(result.nextOffset).toBe(75); expect(result.totalEstimate).toBe(100); }); test('should handle pagination at end of dataset', async () => { const options: PaginationOptions = { type: 'offset', limit: 25, offset: 90, estimateTotal: true }; const result = await PaginationManager.paginateItems(mockItems, options); expect(result.data).toHaveLength(10); // Only 10 items left expect(result.hasMore).toBe(false); expect(result.nextOffset).toBeUndefined(); }); }); describe('ItemSortingEngine', () => { const mockItems = createMockItems(5); test('should sort by creation date ascending', () => { const sorting: SortingOptions = { field: 'createdAt', order: 'asc' }; const sorted = ItemSortingEngine.sortItems(mockItems, sorting); for (let i = 1; i < sorted.length; i++) { expect(new Date(sorted[i-1].createdAt).getTime()) .toBeLessThanOrEqual(new Date(sorted[i].createdAt).getTime()); } }); test('should sort by creation date descending', () => { const sorting: SortingOptions = { field: 'createdAt', order: 'desc' }; const sorted = ItemSortingEngine.sortItems(mockItems, sorting); for (let i = 1; i < sorted.length; i++) { expect(new Date(sorted[i-1].createdAt).getTime()) .toBeGreaterThanOrEqual(new Date(sorted[i].createdAt).getTime()); } }); test('should sort by title alphabetically', () => { const sorting: SortingOptions = { field: 'title', order: 'asc' }; const sorted = ItemSortingEngine.sortItems(mockItems, sorting); for (let i = 1; i < sorted.length; i++) { expect(sorted[i-1].title.localeCompare(sorted[i].title)) .toBeLessThanOrEqual(0); } }); test('should sort by verification status', () => { const sorting: SortingOptions = { field: 'verificationStatus', order: 'asc' }; const sorted = ItemSortingEngine.sortItems(mockItems, sorting); for (let i = 1; i < sorted.length; i++) { expect(sorted[i-1].verification.status.localeCompare(sorted[i].verification.status)) .toBeLessThanOrEqual(0); } }); }); describe('Caching', () => { test('should cache and retrieve results', () => { const cacheKey = 'test-key'; const testData = { items: [{ id: 'test' }] }; // Set cache itemListCache.setCachedResult(cacheKey, testData); // Get cache const cached = itemListCache.getCachedResult(cacheKey); expect(cached).toBeTruthy(); expect(cached?.data).toEqual(testData); }); test('should return null for expired cache', (done) => { const cacheKey = 'test-key-expire'; const testData = { items: [{ id: 'test' }] }; // Mock short TTL by directly manipulating timestamp itemListCache.setCachedResult(cacheKey, testData); // Manually set old timestamp const cache = (itemListCache as any).cache; const entry = cache.get(cacheKey); entry.timestamp = Date.now() - 6 * 60 * 1000; // 6 minutes ago const cached = itemListCache.getCachedResult(cacheKey); expect(cached).toBeNull(); done(); }); test('should generate consistent cache keys', () => { const websetId = 'test-webset'; const options = { filters: { status: 'verified' }, pagination: { limit: 25 } }; const key1 = itemListCache.generateCacheKey(websetId, options); const key2 = itemListCache.generateCacheKey(websetId, options); expect(key1).toBe(key2); }); }); });

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/waldzellai/exa-mcp-server-websets'

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