import path from 'path';
process.env.RUNBOOK_ROOT = path.resolve('tests/fixtures');
import { createSearchService } from '../../../src/services/searchService.mjs';
import { createIndexer } from '../../../src/services/indexer.mjs';
import { createFsAdapter } from '../../../src/adapters/fsio.mjs';
import { performance } from 'perf_hooks';
import { describe, test, expect, runTests } from '../core/_harness.mjs';
describe('performance:cache-optimization', () => {
let index;
let config;
// Setup shared index for all tests
async function setupIndex() {
if (!index) {
const fsAdapter = createFsAdapter(path.resolve('tests/fixtures'));
config = { freshnessDays: 90, topKDefault: 5 };
const indexer = createIndexer({ fsAdapter, config, logger: { log: () => {} } });
index = await indexer.buildIndex();
}
}
test('search service with cache is faster for repeated queries', async () => {
await setupIndex();
const cachedService = createSearchService({ index, config, logger: { log: () => {} }, useCache: true });
const uncachedService = createSearchService({ index, config, logger: { log: () => {} }, useCache: false });
const query = 'database connection timeout performance test';
const iterations = 50;
// Warm up both services with single call
cachedService.search(query);
uncachedService.search(query);
// Measure uncached performance
const startUncached = performance.now();
for (let i = 0; i < iterations; i++) {
uncachedService.search(query);
}
const uncachedTime = performance.now() - startUncached;
// Measure cached performance
const startCached = performance.now();
for (let i = 0; i < iterations; i++) {
cachedService.search(query);
}
const cachedTime = performance.now() - startCached;
// Cache should be faster (at least 20% improvement)
const improvement = (uncachedTime - cachedTime) / uncachedTime;
console.log(`Uncached: ${uncachedTime.toFixed(2)}ms, Cached: ${cachedTime.toFixed(2)}ms, Improvement: ${(improvement * 100).toFixed(1)}%`);
expect(improvement > 0.1).toBe(true); // At least 10% improvement
expect(cachedTime < uncachedTime).toBe(true);
});
test('cache provides consistent results', async () => {
await setupIndex();
const cachedService = createSearchService({ index, config, logger: { log: () => {} }, useCache: true });
const uncachedService = createSearchService({ index, config, logger: { log: () => {} }, useCache: false });
const queries = [
'database connection',
'web service error',
'system performance',
'network timeout'
];
for (const query of queries) {
const cachedResult = cachedService.search(query);
const uncachedResult = uncachedService.search(query);
// Results should be identical
expect(cachedResult.results.length).toBe(uncachedResult.results.length);
expect(cachedResult.query).toBe(uncachedResult.query);
expect(cachedResult.total).toBe(uncachedResult.total);
// Check first result matches (if any)
if (cachedResult.results.length > 0) {
expect(cachedResult.results[0].doc_id).toBe(uncachedResult.results[0].doc_id);
expect(cachedResult.results[0].score).toBe(uncachedResult.results[0].score);
}
}
});
test('cache handles different queries appropriately', async () => {
await setupIndex();
const service = createSearchService({ index, config, logger: { log: () => {} }, useCache: true });
// Different queries should not interfere
const result1 = service.search('database timeout');
const result2 = service.search('web service error');
const result3 = service.search('database timeout'); // Should hit cache
expect(result1.query).toBe('database timeout');
expect(result2.query).toBe('web service error');
expect(result3.query).toBe('database timeout');
// First and third should be identical (cached)
expect(result1.results.length).toBe(result3.results.length);
expect(result1.total).toBe(result3.total);
});
});
await runTests();