// tests/semantic-cache.test.ts import { ChromaClient } from 'chromadb'; import { CacheConfig } from '../src/types'; // Mock ChromaDB module jest.mock('chromadb', () => ({ ChromaClient: jest.fn().mockImplementation(() => { return { getOrCreateCollection: jest.fn().mockResolvedValue({ query: jest.fn(), add: jest.fn(), reset: jest.fn(), }), deleteCollection: jest.fn(), }; }), IncludeEnum: { Documents: 'documents', Embeddings: 'embeddings', Metadatas: 'metadatas', Distances: 'distances', }, })); import { SemanticCacheService } from '../src/semantic-cache'; describe('SemanticCacheService', () => { const mockOllamaUrl = 'http://localhost:11434'; const mockCacheConfig: CacheConfig = { enabled: true, similarityThreshold: 0.85, collectionName: 'test_cache', embeddingModel: 'nomic-embed-text', chromaURL: 'http://localhost:8000', }; let cacheService: SemanticCacheService; let mockChromaClient: any; let mockCollection: any; beforeEach(() => { // Reset all mocks jest.clearAllMocks(); // Create a fresh instance for each test cacheService = new SemanticCacheService(mockOllamaUrl, mockCacheConfig); // Access the internal mocks mockChromaClient = (ChromaClient as jest.Mock).mock.instances[0]; mockCollection = mockChromaClient.getOrCreateCollection.mock.results[0].value; }); describe('constructor', () => { it('should initialize with correct configuration', () => { expect(mockChromaClient).toBeDefined(); expect(mockChromaClient.getOrCreateCollection).toHaveBeenCalledWith({ name: mockCacheConfig.collectionName, metadata: { 'hnsw:space': 'cosine' }, }); }); }); describe('initialize', () => { it('should initialize the cache collection', async () => { await cacheService.initialize(); expect(mockChromaClient.getOrCreateCollection).toHaveBeenCalledWith({ name: mockCacheConfig.collectionName, metadata: { 'hnsw:space': 'cosine' }, }); }); it('should not initialize when cache is disabled', async () => { const disabledConfig: CacheConfig = { ...mockCacheConfig, enabled: false }; const disabledCacheService = new SemanticCacheService(mockOllamaUrl, disabledConfig); await disabledCacheService.initialize(); expect(mockChromaClient.getOrCreateCollection).not.toHaveBeenCalled(); }); }); describe('getCache', () => { it('should return null when cache is disabled', async () => { const disabledConfig: CacheConfig = { ...mockCacheConfig, enabled: false }; const disabledCacheService = new SemanticCacheService(mockOllamaUrl, disabledConfig); const result = await disabledCacheService.getCache('test query'); expect(result).toBeNull(); expect(mockCollection.query).not.toHaveBeenCalled(); }); it('should return null when no cache hit', async () => { mockCollection.query.mockResolvedValue({ ids: [[]], documents: [[]], distances: [[]], }); const result = await cacheService.getCache('test query'); expect(result).toBeNull(); expect(mockCollection.query).toHaveBeenCalled(); }); it('should return cached content when hit', async () => { const cachedContent = 'cached response'; mockCollection.query.mockResolvedValue({ ids: [['test-id']], documents: [[cachedContent]], distances: [[0.9]], // Above threshold }); const result = await cacheService.getCache('test query'); expect(result).toBe(cachedContent); expect(mockCollection.query).toHaveBeenCalled(); }); }); describe('setCache', () => { it('should not set cache when disabled', async () => { const disabledConfig: CacheConfig = { ...mockCacheConfig, enabled: false }; const disabledCacheService = new SemanticCacheService(mockOllamaUrl, disabledConfig); await disabledCacheService.setCache('test query', 'test response'); expect(mockCollection.add).not.toHaveBeenCalled(); }); it('should add content to cache', async () => { const mockEmbedding = [0.1, 0.2, 0.3]; // Mock the fetch function for embedding generation global.fetch = jest.fn().mockResolvedValue({ ok: true, json: () => Promise.resolve({ embedding: mockEmbedding }), }); await cacheService.setCache('test query', 'test response'); expect(mockCollection.add).toHaveBeenCalled(); // Clean up global.fetch = undefined as any; }); }); describe('clearCache', () => { it('should not clear when cache is disabled', async () => { const disabledConfig: CacheConfig = { ...mockCacheConfig, enabled: false }; const disabledCacheService = new SemanticCacheService(mockOllamaUrl, disabledConfig); await disabledCacheService.clearCache(); expect(mockCollection.reset).not.toHaveBeenCalled(); }); it('should clear the cache collection', async () => { await cacheService.clearCache(); expect(mockCollection.reset).toHaveBeenCalled(); }); }); });