Skip to main content
Glama
KnowledgeGraphManagerSearch.test.ts6.9 kB
import { describe, it, expect, vi, beforeEach } from 'vitest'; import { fileURLToPath } from 'url'; import path from 'path'; import { KnowledgeGraphManager, SemanticSearchOptions } from '../KnowledgeGraphManager.js'; import { StorageProvider } from '../storage/StorageProvider.js'; import type { LRUCache } from 'lru-cache'; // Setup test paths const __dirname = path.dirname(fileURLToPath(import.meta.url)); const testFilePath = path.join(__dirname, '../../test-output/test-memory.json'); describe('KnowledgeGraphManager Search', () => { let manager: KnowledgeGraphManager; let mockStorageProvider: Partial<StorageProvider>; let mockEmbeddingJobManager: { embeddingService: { generateEmbedding: (text: string) => Promise<number[]>; }; scheduleEntityEmbedding: (entityName: string, priority?: number) => Promise<string>; storageProvider: any; rateLimiter: { tokens: number; lastRefill: number; tokensPerInterval: number; interval: number; }; cache: any; cacheOptions: { size: number; ttl: number }; }; beforeEach(async () => { // Mock storage provider mockStorageProvider = { loadGraph: vi.fn().mockResolvedValue({ entities: [], relations: [] }), saveGraph: vi.fn().mockResolvedValue(undefined), searchNodes: vi.fn().mockResolvedValue({ entities: [{ name: 'KeywordResult', entityType: 'Test', observations: ['keyword result'] }], relations: [], }), semanticSearch: vi.fn().mockResolvedValue({ entities: [ { name: 'SemanticResult', entityType: 'Test', observations: ['semantic result'] }, ], relations: [], total: 1, facets: { entityType: { counts: { Test: 1 } } }, timeTaken: 10, }), openNodes: vi.fn().mockResolvedValue({ entities: [], relations: [] }), }; // Mock embedding service const mockEmbeddingService = { generateEmbedding: vi.fn().mockResolvedValue(Array(1536).fill(0.1)), }; // Mock embedding job manager mockEmbeddingJobManager = { embeddingService: mockEmbeddingService, scheduleEntityEmbedding: vi.fn().mockResolvedValue('mock-job-id'), storageProvider: mockStorageProvider, rateLimiter: { tokens: 60, lastRefill: Date.now(), tokensPerInterval: 60, interval: 60 * 1000, }, cache: {}, cacheOptions: { size: 1000, ttl: 3600000 }, }; // Create manager with mocks manager = new KnowledgeGraphManager({ storageProvider: mockStorageProvider as StorageProvider, memoryFilePath: testFilePath, embeddingJobManager: mockEmbeddingJobManager as any, }); }); it('should use basic searchNodes when no options are provided', async () => { // Call the search method without options const result = await manager.search('test query'); // Should call searchNodes expect(mockStorageProvider.searchNodes).toHaveBeenCalledWith('test query'); expect(mockStorageProvider.semanticSearch).not.toHaveBeenCalled(); // Result should be what searchNodes returns expect(result.entities.length).toBe(1); expect(result.entities[0].name).toBe('KeywordResult'); }); it('should use semanticSearch when semanticSearch option is true', async () => { // Call the search method with semanticSearch option const result = await manager.search('test query', { semanticSearch: true }); // Should call semanticSearch expect(mockStorageProvider.semanticSearch).toHaveBeenCalledWith( 'test query', expect.objectContaining({ semanticSearch: true, queryVector: expect.any(Array), }) ); expect(mockStorageProvider.searchNodes).not.toHaveBeenCalled(); // Result should be what semanticSearch returns expect(result.entities.length).toBe(1); expect(result.entities[0].name).toBe('SemanticResult'); }); it('should use semanticSearch when hybridSearch option is true', async () => { // Call the search method with hybridSearch option const result = await manager.search('test query', { hybridSearch: true }); // Should call semanticSearch with both options expect(mockStorageProvider.semanticSearch).toHaveBeenCalledWith( 'test query', expect.objectContaining({ hybridSearch: true, semanticSearch: true, queryVector: expect.any(Array), }) ); // Result should be what semanticSearch returns expect(result.entities.length).toBe(1); expect(result.entities[0].name).toBe('SemanticResult'); }); it('should fall back to searchNodes if semanticSearch is not available', async () => { // Remove semanticSearch method from the mock delete mockStorageProvider.semanticSearch; // Call the search method with semanticSearch option const result = await manager.search('test query', { semanticSearch: true }); // Should fall back to searchNodes expect(mockStorageProvider.searchNodes).toHaveBeenCalledWith('test query'); // Result should be what searchNodes returns expect(result.entities.length).toBe(1); expect(result.entities[0].name).toBe('KeywordResult'); }); it('should fall back to basic search for file-based implementation', async () => { // Create a manager without a storage provider const fileBasedManager = new KnowledgeGraphManager({ memoryFilePath: testFilePath, }); // Mock searchNodes implementation fileBasedManager.searchNodes = vi.fn().mockResolvedValue({ entities: [{ name: 'FileResult', entityType: 'Test', observations: ['file result'] }], relations: [], }); // Call the search method const result = await fileBasedManager.search('test query', { semanticSearch: true }); // Should call searchNodes expect(fileBasedManager.searchNodes).toHaveBeenCalledWith('test query'); // Result should be what searchNodes returns expect(result.entities.length).toBe(1); expect(result.entities[0].name).toBe('FileResult'); }); it('should pass additional search options to semanticSearch', async () => { // Call search with multiple options const searchOptions: SemanticSearchOptions = { semanticSearch: true, minSimilarity: 0.8, limit: 20, includeExplanations: true, filters: [{ field: 'entityType', operator: 'eq', value: 'Person' }], }; await manager.search('test query', searchOptions); // Should pass all options to semanticSearch with a queryVector expect(mockStorageProvider.semanticSearch).toHaveBeenCalledWith( 'test query', expect.objectContaining({ semanticSearch: true, minSimilarity: 0.8, limit: 20, includeExplanations: true, filters: [{ field: 'entityType', operator: 'eq', value: 'Person' }], queryVector: expect.any(Array), }) ); }); });

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/gannonh/memento-mcp'

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