diff --git a/src/cache.ts b/src/cache.ts new file mode 100644 index 0000000..5bd7d60 --- /dev/null +++ b/src/cache.ts @@ -0,0 +1,4 @@ +export interface Cache { + get(key: string): Promise; + put(key: string, value: string): Promise; +} diff --git a/tests/semantic-cache.test.ts.bak b/tests/semantic-cache.test.ts.bak new file mode 100644 index 0000000..9558910 --- /dev/null +++ b/tests/semantic-cache.test.ts.bak @@ -0,0 +1,167 @@ +// 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(); + }); + }); +});