import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; import { RegistryCache } from '../../src/registry/cache.js'; import type { RegistryServer, SearchOptions } from '../../src/registry/types.js'; function makeServer(name: string): RegistryServer { return { name, description: `${name} server`, packages: {}, envTemplate: [], transport: 'stdio', popularityScore: 0, verified: false, sourceRegistry: 'official', }; } const defaultOptions: SearchOptions = { query: 'test' }; describe('RegistryCache', () => { beforeEach(() => { vi.useFakeTimers(); }); afterEach(() => { vi.useRealTimers(); }); it('returns null for cache miss', () => { const cache = new RegistryCache(); expect(cache.get('unknown', defaultOptions)).toBeNull(); }); it('returns data for cache hit within TTL', () => { const cache = new RegistryCache(); const data = [makeServer('test')]; cache.set('test', defaultOptions, data); expect(cache.get('test', defaultOptions)).toEqual(data); }); it('returns null after TTL expires', () => { const cache = new RegistryCache(1000); // 1 second TTL cache.set('test', defaultOptions, [makeServer('test')]); vi.advanceTimersByTime(1001); expect(cache.get('test', defaultOptions)).toBeNull(); }); it('generates deterministic cache keys', () => { const cache = new RegistryCache(); const data = [makeServer('test')]; cache.set('query', { query: 'query', limit: 10 }, data); expect(cache.get('query', { query: 'query', limit: 10 })).toEqual(data); }); it('generates different keys for different queries', () => { const cache = new RegistryCache(); cache.set('a', { query: 'a' }, [makeServer('a')]); expect(cache.get('b', { query: 'b' })).toBeNull(); }); it('tracks hits and misses correctly', () => { const cache = new RegistryCache(); cache.set('test', defaultOptions, [makeServer('test')]); cache.get('test', defaultOptions); // hit cache.get('test', defaultOptions); // hit cache.get('miss', { query: 'miss' }); // miss const ratio = cache.getHitRatio(); expect(ratio.hits).toBe(2); expect(ratio.misses).toBe(1); expect(ratio.ratio).toBeCloseTo(2 / 3); }); it('returns 0 ratio when no accesses', () => { const cache = new RegistryCache(); expect(cache.getHitRatio().ratio).toBe(0); }); it('clears all entries and resets metrics', () => { const cache = new RegistryCache(); cache.set('a', { query: 'a' }, [makeServer('a')]); cache.get('a', { query: 'a' }); // hit cache.clear(); expect(cache.get('a', { query: 'a' })).toBeNull(); expect(cache.size).toBe(0); expect(cache.getHitRatio().hits).toBe(0); }); });