Add cache interface and semantic cache tests
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
export interface Cache {
|
||||
get(key: string): Promise<string | null>;
|
||||
put(key: string, value: string): Promise<void>;
|
||||
}
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user