Integrate semantic cache with ChromaDB URL

This commit is contained in:
2026-05-07 21:34:19 +02:00
parent b37aaeb4d4
commit 96d7323377
9 changed files with 223 additions and 63 deletions
+46 -17
View File
@@ -7,12 +7,20 @@ import { CacheConfig } from '../src/types';
jest.mock('chromadb', () => ({
ChromaClient: jest.fn().mockImplementation(() => {
return {
getOrCreateCollection: jest.fn().mockResolvedValue({
getOrCreateCollection: jest.fn().mockReturnValue({
query: jest.fn(),
add: jest.fn(),
upsert: jest.fn(),
}),
deleteCollection: jest.fn(),
};
}),
IncludeEnum: {
Documents: 'documents',
Embeddings: 'embeddings',
Metadatas: 'metadatas',
Distances: 'distances',
},
}));
// Now import SemanticCacheService after mocking
@@ -21,10 +29,12 @@ import { SemanticCacheService } from '../src/semantic-cache';
jest.spyOn(global, 'fetch').mockImplementation(jest.fn());
const mockChromaClient = {
getOrCreateCollection: jest.fn().mockResolvedValue({
getOrCreateCollection: jest.fn().mockReturnValue({
query: jest.fn(),
add: jest.fn(),
upsert: jest.fn(),
}),
deleteCollection: jest.fn(),
};
// Set up mock instance
@@ -44,6 +54,7 @@ describe('SemanticCacheService', () => {
similarityThreshold: 0.85,
collectionName: 'test_cache',
embeddingModel: 'nomic-embed-text',
chromaUrl: 'http://localhost:8000',
};
service = new SemanticCacheService('http://localhost:11434', config);
@@ -211,32 +222,30 @@ describe('SemanticCacheService', () => {
json: () => Promise.resolve({ embedding: [0.1, 0.2, 0.3] }),
});
// crypto.randomUUID mock
const mockUuid = 'mock-uuid-123' as any;
jest.spyOn(crypto, 'randomUUID').mockReturnValue(mockUuid);
await service.setCache('test prompt', 'test response');
const mockCollection = mockChromaClient.getOrCreateCollection();
expect(mockCollection.add).toHaveBeenCalledWith({
ids: [mockUuid],
embeddings: [[0.1, 0.2, 0.3]],
metadatas: [{ fullResponse: 'test response' }],
});
expect(mockCollection.upsert).toHaveBeenCalledWith(
expect.objectContaining({
ids: [expect.any(String)],
embeddings: [[0.1, 0.2, 0.3]],
metadatas: [{ fullResponse: 'test response' }],
})
);
});
it('should not add entry when prompt is empty', async () => {
await service.setCache(' ', 'test response');
expect(mockFetch).not.toHaveBeenCalled();
expect(mockChromaClient.getOrCreateCollection().add).not.toHaveBeenCalled();
expect(mockChromaClient.getOrCreateCollection().upsert).not.toHaveBeenCalled();
});
it('should not add entry when response is empty', async () => {
await service.setCache('test prompt', ' ');
expect(mockFetch).not.toHaveBeenCalled();
expect(mockChromaClient.getOrCreateCollection().add).not.toHaveBeenCalled();
expect(mockChromaClient.getOrCreateCollection().upsert).not.toHaveBeenCalled();
});
it('should not add entry when cache is disabled', async () => {
@@ -255,13 +264,33 @@ describe('SemanticCacheService', () => {
json: () => Promise.resolve({ embedding: [0.1, 0.2, 0.3] }),
});
const mockUuid = 'mock-uuid-456' as any;
jest.spyOn(crypto, 'randomUUID').mockReturnValue(mockUuid);
mockChromaClient.getOrCreateCollection().add.mockRejectedValueOnce(new Error('Add failed'));
mockChromaClient
.getOrCreateCollection()
.upsert.mockRejectedValueOnce(new Error('Add failed'));
// Should not throw
await expect(service.setCache('test prompt', 'test response')).resolves.toBeUndefined();
});
});
describe('clearCache', () => {
beforeEach(async () => {
await service.initialize();
mockChromaClient.deleteCollection.mockResolvedValue(undefined);
});
it('should delete the collection and re-initialize', async () => {
await service.clearCache();
expect(mockChromaClient.deleteCollection).toHaveBeenCalledWith({ name: 'test_cache' });
// getOrCreateCollection called once in beforeEach initialize, once in clearCache re-init
expect(mockChromaClient.getOrCreateCollection).toHaveBeenCalledTimes(2);
});
it('should propagate errors from deleteCollection', async () => {
mockChromaClient.deleteCollection.mockRejectedValueOnce(new Error('Delete failed'));
await expect(service.clearCache()).rejects.toThrow('Delete failed');
});
});
});