Files
wifi-densepose/npm/packages/agentic-synth/tests/unit/cache/context-cache.test.js
ruv d803bfe2b1 Squashed 'vendor/ruvector/' content from commit b64c2172
git-subtree-dir: vendor/ruvector
git-subtree-split: b64c21726f2bb37286d9ee36a7869fef60cc6900
2026-02-28 14:39:40 -05:00

257 lines
6.5 KiB
JavaScript

/**
* Unit tests for ContextCache
*/
import { describe, it, expect, beforeEach, vi } from 'vitest';
import { ContextCache } from '../../../src/cache/context-cache.js';
describe('ContextCache', () => {
let cache;
beforeEach(() => {
cache = new ContextCache({
maxSize: 5,
ttl: 1000 // 1 second for testing
});
});
describe('constructor', () => {
it('should create cache with default options', () => {
const defaultCache = new ContextCache();
expect(defaultCache.maxSize).toBe(100);
expect(defaultCache.ttl).toBe(3600000);
});
it('should accept custom options', () => {
expect(cache.maxSize).toBe(5);
expect(cache.ttl).toBe(1000);
});
it('should initialize empty cache', () => {
expect(cache.cache.size).toBe(0);
});
it('should initialize stats', () => {
const stats = cache.getStats();
expect(stats.hits).toBe(0);
expect(stats.misses).toBe(0);
expect(stats.evictions).toBe(0);
});
});
describe('set and get', () => {
it('should store and retrieve value', () => {
cache.set('key1', 'value1');
const result = cache.get('key1');
expect(result).toBe('value1');
});
it('should return null for non-existent key', () => {
const result = cache.get('nonexistent');
expect(result).toBeNull();
});
it('should update existing key', () => {
cache.set('key1', 'value1');
cache.set('key1', 'value2');
expect(cache.get('key1')).toBe('value2');
expect(cache.cache.size).toBe(1);
});
it('should store complex objects', () => {
const obj = { nested: { data: [1, 2, 3] } };
cache.set('complex', obj);
expect(cache.get('complex')).toEqual(obj);
});
});
describe('TTL (Time To Live)', () => {
it('should return null for expired entries', async () => {
cache.set('key1', 'value1');
// Wait for TTL to expire
await new Promise(resolve => setTimeout(resolve, 1100));
const result = cache.get('key1');
expect(result).toBeNull();
});
it('should not return expired entries in has()', async () => {
cache.set('key1', 'value1');
expect(cache.has('key1')).toBe(true);
await new Promise(resolve => setTimeout(resolve, 1100));
expect(cache.has('key1')).toBe(false);
});
it('should delete expired entries', async () => {
cache.set('key1', 'value1');
expect(cache.cache.size).toBe(1);
await new Promise(resolve => setTimeout(resolve, 1100));
cache.get('key1'); // Triggers cleanup
expect(cache.cache.size).toBe(0);
});
});
describe('eviction', () => {
it('should evict LRU entry when at capacity', () => {
// Fill cache to capacity
for (let i = 0; i < 5; i++) {
cache.set(`key${i}`, `value${i}`);
}
expect(cache.cache.size).toBe(5);
// Access key1 to make key0 the LRU
cache.get('key1');
// Add new entry, should evict key0
cache.set('key5', 'value5');
expect(cache.cache.size).toBe(5);
expect(cache.get('key0')).toBeNull();
expect(cache.get('key5')).toBe('value5');
});
it('should track eviction stats', () => {
for (let i = 0; i < 6; i++) {
cache.set(`key${i}`, `value${i}`);
}
const stats = cache.getStats();
expect(stats.evictions).toBeGreaterThan(0);
});
});
describe('has', () => {
it('should return true for existing key', () => {
cache.set('key1', 'value1');
expect(cache.has('key1')).toBe(true);
});
it('should return false for non-existent key', () => {
expect(cache.has('nonexistent')).toBe(false);
});
});
describe('clear', () => {
it('should remove all entries', () => {
cache.set('key1', 'value1');
cache.set('key2', 'value2');
cache.clear();
expect(cache.cache.size).toBe(0);
expect(cache.get('key1')).toBeNull();
});
it('should reset statistics', () => {
cache.set('key1', 'value1');
cache.get('key1');
cache.get('nonexistent');
cache.clear();
const stats = cache.getStats();
expect(stats.hits).toBe(0);
expect(stats.misses).toBe(0);
});
});
describe('getStats', () => {
it('should track cache hits', () => {
cache.set('key1', 'value1');
cache.get('key1');
cache.get('key1');
const stats = cache.getStats();
expect(stats.hits).toBe(2);
});
it('should track cache misses', () => {
cache.get('nonexistent1');
cache.get('nonexistent2');
const stats = cache.getStats();
expect(stats.misses).toBe(2);
});
it('should calculate hit rate', () => {
cache.set('key1', 'value1');
cache.get('key1'); // hit
cache.get('key1'); // hit
cache.get('nonexistent'); // miss
const stats = cache.getStats();
expect(stats.hitRate).toBeCloseTo(0.666, 2);
});
it('should include cache size', () => {
cache.set('key1', 'value1');
cache.set('key2', 'value2');
const stats = cache.getStats();
expect(stats.size).toBe(2);
});
it('should handle zero hit rate', () => {
cache.get('nonexistent');
const stats = cache.getStats();
expect(stats.hitRate).toBe(0);
});
});
describe('access tracking', () => {
it('should update access count', () => {
cache.set('key1', 'value1');
cache.get('key1');
cache.get('key1');
const entry = cache.cache.get('key1');
expect(entry.accessCount).toBe(2);
});
it('should update last access time', () => {
cache.set('key1', 'value1');
const initialAccess = cache.cache.get('key1').lastAccess;
// Small delay
setTimeout(() => {
cache.get('key1');
const laterAccess = cache.cache.get('key1').lastAccess;
expect(laterAccess).toBeGreaterThan(initialAccess);
}, 10);
});
});
describe('performance', () => {
it('should handle 1000 operations quickly', () => {
const start = Date.now();
for (let i = 0; i < 1000; i++) {
cache.set(`key${i}`, `value${i}`);
cache.get(`key${i}`);
}
const duration = Date.now() - start;
expect(duration).toBeLessThan(100); // Less than 100ms
});
it('should maintain performance with large values', () => {
const largeValue = { data: new Array(1000).fill('x'.repeat(100)) };
const start = Date.now();
for (let i = 0; i < 100; i++) {
cache.set(`key${i}`, largeValue);
}
const duration = Date.now() - start;
expect(duration).toBeLessThan(100);
});
});
});