Skip to main content
Glama
cache-manager.test.ts•7.76 kB
import { describe, it, expect, vi, beforeEach } from 'vitest'; import { CacheManager } from '../cache-manager.js'; import { FileSearchOptions, FileSearchResult } from '../search-strategies.js'; // Mock logger vi.mock('../../../logger.js', () => ({ default: { debug: vi.fn(), info: vi.fn(), warn: vi.fn(), error: vi.fn() } })); describe('CacheManager', () => { let cacheManager: CacheManager; const mockOptions: FileSearchOptions = { pattern: 'test', searchStrategy: 'fuzzy', cacheResults: true }; const mockResults: FileSearchResult[] = [ { filePath: '/test/file1.ts', score: 0.9, matchType: 'fuzzy', relevanceFactors: ['test factor'], metadata: { size: 1024, lastModified: new Date(), extension: '.ts' } } ]; beforeEach(() => { cacheManager = new CacheManager({ maxEntries: 10, defaultTtl: 1000, // 1 second for testing maxMemoryUsage: 1024 * 1024, // 1MB enableStats: true }); }); describe('basic caching', () => { it('should return null for cache miss', () => { const result = cacheManager.get('nonexistent', mockOptions); expect(result).toBeNull(); }); it('should store and retrieve cached results', () => { cacheManager.set('test', mockOptions, mockResults); const cached = cacheManager.get('test', mockOptions); expect(cached).toEqual(mockResults); }); it('should not cache when cacheResults is false', () => { const noCacheOptions = { ...mockOptions, cacheResults: false }; cacheManager.set('test', noCacheOptions, mockResults); const cached = cacheManager.get('test', noCacheOptions); expect(cached).toBeNull(); }); it('should generate different keys for different options', () => { const options1 = { ...mockOptions, fileTypes: ['.ts'] }; const options2 = { ...mockOptions, fileTypes: ['.js'] }; cacheManager.set('test', options1, mockResults); cacheManager.set('test', options2, mockResults); const cached1 = cacheManager.get('test', options1); const cached2 = cacheManager.get('test', options2); expect(cached1).toEqual(mockResults); expect(cached2).toEqual(mockResults); }); }); describe('TTL expiration', () => { it('should expire entries after TTL', async () => { const shortTtlCache = new CacheManager({ defaultTtl: 10 // 10ms }); shortTtlCache.set('test', mockOptions, mockResults); // Should be cached immediately expect(shortTtlCache.get('test', mockOptions)).toEqual(mockResults); // Wait for expiration await new Promise(resolve => setTimeout(resolve, 20)); // Should be expired expect(shortTtlCache.get('test', mockOptions)).toBeNull(); }); }); describe('LRU eviction', () => { it('should evict least recently used entries when max entries exceeded', () => { const smallCache = new CacheManager({ maxEntries: 2 }); // Add first entry smallCache.set('test1', mockOptions, mockResults); expect(smallCache.get('test1', mockOptions)).toEqual(mockResults); // Add second entry smallCache.set('test2', mockOptions, mockResults); expect(smallCache.get('test2', mockOptions)).toEqual(mockResults); // Add third entry (should evict first) smallCache.set('test3', mockOptions, mockResults); // First entry should be evicted expect(smallCache.get('test1', mockOptions)).toBeNull(); expect(smallCache.get('test2', mockOptions)).toEqual(mockResults); expect(smallCache.get('test3', mockOptions)).toEqual(mockResults); }); it('should update access order on cache hits', () => { const smallCache = new CacheManager({ maxEntries: 2 }); // Add two entries smallCache.set('test1', mockOptions, mockResults); smallCache.set('test2', mockOptions, mockResults); // Access first entry to make it recently used smallCache.get('test1', mockOptions); // Add third entry (should evict second, not first) smallCache.set('test3', mockOptions, mockResults); expect(smallCache.get('test1', mockOptions)).toEqual(mockResults); expect(smallCache.get('test2', mockOptions)).toBeNull(); expect(smallCache.get('test3', mockOptions)).toEqual(mockResults); }); }); describe('cache clearing', () => { it('should clear all entries', () => { cacheManager.set('test1', mockOptions, mockResults); cacheManager.set('test2', mockOptions, mockResults); cacheManager.clear(); expect(cacheManager.get('test1', mockOptions)).toBeNull(); expect(cacheManager.get('test2', mockOptions)).toBeNull(); }); it('should clear entries for specific project path', () => { const projectResults1: FileSearchResult[] = [ { filePath: '/project1/file.ts', score: 0.9, matchType: 'fuzzy', relevanceFactors: ['test'] } ]; const projectResults2: FileSearchResult[] = [ { filePath: '/project2/file.ts', score: 0.9, matchType: 'fuzzy', relevanceFactors: ['test'] } ]; cacheManager.set('test1', mockOptions, projectResults1); cacheManager.set('test2', mockOptions, projectResults2); // Clear only project1 cacheManager.clear('/project1'); expect(cacheManager.get('test1', mockOptions)).toBeNull(); expect(cacheManager.get('test2', mockOptions)).toEqual(projectResults2); }); }); describe('statistics', () => { it('should provide cache statistics', () => { cacheManager.set('test', mockOptions, mockResults); cacheManager.get('test', mockOptions); // Cache hit const stats = cacheManager.getStats(); expect(stats.totalEntries).toBe(1); expect(stats.hitRate).toBeGreaterThan(0); expect(stats.memoryUsage).toBeGreaterThan(0); expect(stats.evictions).toBe(0); }); it('should track hit count', () => { cacheManager.set('test', mockOptions, mockResults); // Multiple hits cacheManager.get('test', mockOptions); cacheManager.get('test', mockOptions); cacheManager.get('test', mockOptions); const stats = cacheManager.getStats(); expect(stats.hitRate).toBeGreaterThan(0); }); it('should track evictions', () => { const smallCache = new CacheManager({ maxEntries: 1, enableStats: true }); smallCache.set('test1', mockOptions, mockResults); smallCache.set('test2', mockOptions, mockResults); // Should evict test1 const stats = smallCache.getStats(); expect(stats.evictions).toBe(1); }); }); describe('memory management', () => { it('should calculate memory usage', () => { cacheManager.set('test', mockOptions, mockResults); const stats = cacheManager.getStats(); expect(stats.memoryUsage).toBeGreaterThan(0); }); it('should evict when memory limit exceeded', () => { const lowMemoryCache = new CacheManager({ maxMemoryUsage: 100 // Very low limit }); // Add entry that might exceed memory limit const largeResults = Array(100).fill(mockResults[0]); lowMemoryCache.set('large', mockOptions, largeResults); // Should still work (eviction logic should handle it) expect(lowMemoryCache.getStats().totalEntries).toBeGreaterThanOrEqual(0); }); }); });

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/freshtechbro/vibe-coder-mcp'

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