import { describe, test, expect, beforeEach, afterEach, vi } from 'vitest';
import { CacheManagerImpl } from '../../src/core/cache-manager.js';
describe('CacheManager', () => {
let cacheManager: CacheManagerImpl;
beforeEach(() => {
cacheManager = CacheManagerImpl.getInstance();
// Reset all cache statistics before each test
cacheManager.resetAllStats();
});
afterEach(() => {
// Clean up caches after each test
cacheManager.invalidate();
});
describe('Cache Operations', () => {
test('should store and retrieve values from OCR cache', () => {
const ocrCache = cacheManager.getOCRCache();
const testKey = 'test_ocr_key';
const testValue = 'extracted text from image';
// Store value
ocrCache.set(testKey, testValue);
// Retrieve value
const retrieved = ocrCache.get(testKey);
expect(retrieved).toBe(testValue);
});
test('should store and retrieve values from screenshot cache', () => {
const screenshotCache = cacheManager.getScreenshotCache();
const testKey = 'screenshot:fullscreen';
const testValue = { width: 1920, height: 1080, data: 'mock_screenshot_data' };
// Store value
screenshotCache.set(testKey, testValue);
// Retrieve value
const retrieved = screenshotCache.get(testKey);
expect(retrieved).toEqual(testValue);
});
test('should store and retrieve values from window cache', () => {
const windowCache = cacheManager.getWindowCache();
const testKey = 'list_windows';
const testValue = { windows: [{ title: 'Test Window', x: 0, y: 0 }] };
// Store value
windowCache.set(testKey, testValue);
// Retrieve value
const retrieved = windowCache.get(testKey);
expect(retrieved).toEqual(testValue);
});
});
describe('Cache Key Generation', () => {
test('should generate consistent region keys', () => {
const region1 = { x: 100, y: 100, width: 200, height: 200 };
const region2 = { x: 100, y: 100, width: 200, height: 200 };
const key1 = CacheManagerImpl.generateRegionKey(region1);
const key2 = CacheManagerImpl.generateRegionKey(region2);
expect(key1).toBe(key2);
expect(key1).toBe('100,100,200,200');
});
test('should generate fullscreen key for undefined region', () => {
const key = CacheManagerImpl.generateRegionKey(undefined);
expect(key).toBe('fullscreen');
});
test('should generate image hashes', () => {
const imageData = Buffer.from('test image data');
const hash = CacheManagerImpl.generateImageHash(imageData);
expect(hash).toBeDefined();
expect(hash.length).toBe(32); // MD5 hash length
expect(typeof hash).toBe('string');
});
});
describe('Cache Statistics', () => {
test('should track cache hits and misses', () => {
const ocrCache = cacheManager.getOCRCache();
const testKey = 'test_stats_key';
const testValue = 'test value';
// Initial stats should be zero
const initialStats = cacheManager.getAllCacheStats();
expect(initialStats.ocr.hitCount).toBe(0);
expect(initialStats.ocr.missCount).toBe(0);
// Cache miss
const missed = ocrCache.get(testKey);
expect(missed).toBeUndefined();
// Store value
ocrCache.set(testKey, testValue);
// Cache hit
const hit = ocrCache.get(testKey);
expect(hit).toBe(testValue);
// Check stats
const finalStats = cacheManager.getAllCacheStats();
expect(finalStats.ocr.hitCount).toBe(1);
expect(finalStats.ocr.missCount).toBe(1);
expect(finalStats.ocr.hitRate).toBe(0.5); // 1 hit out of 2 total requests
});
test('should calculate cache efficiency correctly', () => {
const ocrCache = cacheManager.getOCRCache();
// Generate some cache activity
for (let i = 0; i < 10; i++) {
ocrCache.set(`key${i}`, `value${i}`);
}
// Generate hits
for (let i = 0; i < 5; i++) {
ocrCache.get(`key${i}`);
}
// Generate misses
for (let i = 10; i < 15; i++) {
ocrCache.get(`key${i}`);
}
const efficiency = cacheManager.getCacheEfficiency();
expect(efficiency.overall).toBe(0.5); // 5 hits out of 10 total requests
expect(efficiency.byCache.ocr).toBe(0.5);
expect(efficiency.recommendations.length).toBeGreaterThanOrEqual(0); // Can be 0 if performance is in normal range
});
});
describe('TTL and Expiration', () => {
test('should expire cache entries after TTL', async () => {
const ocrCache = cacheManager.getOCRCache();
const testKey = 'ttl_test_key';
const testValue = 'test value';
// Store with very short TTL
ocrCache.set(testKey, testValue, 10); // 10ms
// Should be available immediately
expect(ocrCache.get(testKey)).toBe(testValue);
// Wait for expiration
await new Promise(resolve => setTimeout(resolve, 20));
// Should be expired
expect(ocrCache.get(testKey)).toBeUndefined();
});
test('should handle custom TTL values', () => {
const screenshotCache = cacheManager.getScreenshotCache();
const testKey = 'custom_ttl_key';
const testValue = 'test data';
// Store with custom TTL
screenshotCache.set(testKey, testValue, 5000); // 5 seconds
// Should be available
expect(screenshotCache.get(testKey)).toBe(testValue);
});
});
describe('Cache Invalidation', () => {
test('should invalidate specific cache types', () => {
const ocrCache = cacheManager.getOCRCache();
const screenshotCache = cacheManager.getScreenshotCache();
const windowCache = cacheManager.getWindowCache();
// Store test data
ocrCache.set('ocr_key', 'ocr_value');
screenshotCache.set('screenshot_key', 'screenshot_value');
windowCache.set('window_key', 'window_value');
// Smart invalidation for screen changes (should clear screenshot and OCR)
cacheManager.smartInvalidate('screen_change');
expect(ocrCache.get('ocr_key')).toBeUndefined();
expect(screenshotCache.get('screenshot_key')).toBeUndefined();
expect(windowCache.get('window_key')).toBe('window_value'); // Should still exist
});
test('should invalidate window cache on window actions', () => {
const windowCache = cacheManager.getWindowCache();
const ocrCache = cacheManager.getOCRCache();
// Store test data
windowCache.set('window_key', 'window_value');
ocrCache.set('ocr_key', 'ocr_value');
// Smart invalidation for window actions
cacheManager.smartInvalidate('window_action');
expect(windowCache.get('window_key')).toBeUndefined();
expect(ocrCache.get('ocr_key')).toBe('ocr_value'); // Should still exist
});
test('should clear all caches', () => {
const ocrCache = cacheManager.getOCRCache();
const screenshotCache = cacheManager.getScreenshotCache();
const windowCache = cacheManager.getWindowCache();
// Store test data
ocrCache.set('ocr_key', 'ocr_value');
screenshotCache.set('screenshot_key', 'screenshot_value');
windowCache.set('window_key', 'window_value');
// Clear all caches
cacheManager.invalidate();
expect(ocrCache.get('ocr_key')).toBeUndefined();
expect(screenshotCache.get('screenshot_key')).toBeUndefined();
expect(windowCache.get('window_key')).toBeUndefined();
});
});
describe('Error Handling', () => {
test('should handle cache errors gracefully', () => {
const ocrCache = cacheManager.getOCRCache();
// Test with invalid key (should not throw)
expect(() => ocrCache.get('')).not.toThrow();
expect(() => ocrCache.set('', 'value')).not.toThrow();
expect(() => ocrCache.has('')).not.toThrow();
});
test('should continue functioning after cache errors', () => {
const ocrCache = cacheManager.getOCRCache();
// Attempt operations that might fail
ocrCache.get('nonexistent_key');
ocrCache.set('test_key', 'test_value');
// Normal operations should still work
expect(ocrCache.get('test_key')).toBe('test_value');
});
});
describe('Cache Warming', () => {
test('should complete cache warming without errors', async () => {
// Cache warming should not throw
await expect(cacheManager.warmCaches()).resolves.not.toThrow();
});
});
describe('Performance Metrics', () => {
test('should track cache sizes', () => {
const ocrCache = cacheManager.getOCRCache();
// Add multiple entries
for (let i = 0; i < 5; i++) {
ocrCache.set(`key${i}`, `value${i}`);
}
const stats = cacheManager.getAllCacheStats();
expect(stats.ocr.size).toBe(5);
});
});
});