Files

78 lines
2.5 KiB
JavaScript
Raw Permalink Normal View History

import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import fs from 'fs';
import os from 'os';
import path from 'path';
import { clearCache, scanWithCache, setCachedScan, getCachedScan } from './cache.js';
let tempDataDir;
beforeEach(() => {
tempDataDir = fs.mkdtempSync(path.join(os.tmpdir(), 'skills-cache-test-'));
process.env.OPENCHAMBER_DATA_DIR = tempDataDir;
});
afterEach(() => {
delete process.env.OPENCHAMBER_DATA_DIR;
clearCache();
vi.restoreAllMocks();
fs.rmSync(tempDataDir, { recursive: true, force: true });
});
const flushDiskWrites = async () => new Promise((resolve) => setTimeout(resolve, 1200));
describe('scanWithCache', () => {
it('deduplicates concurrent loaders for the same key', async () => {
const loader = vi.fn(async () => {
await new Promise((resolve) => setTimeout(resolve, 20));
return { ok: true, items: [] };
});
const [a, b] = await Promise.all([
scanWithCache('k', loader),
scanWithCache('k', loader),
]);
expect(loader).toHaveBeenCalledTimes(1);
expect(a).toEqual(b);
});
it('limits concurrent scans across different keys', async () => {
let running = 0;
let peak = 0;
const loader = async () => {
running += 1;
peak = Math.max(peak, running);
await new Promise((resolve) => setTimeout(resolve, 20));
running -= 1;
return { ok: true, items: [] };
};
await Promise.all(Array.from({ length: 6 }, (_, i) => scanWithCache(`key-${i}`, loader)));
expect(peak).toBeLessThanOrEqual(2);
});
it('does not cache failed scans', async () => {
await scanWithCache('bad', async () => ({ ok: false, error: { kind: 'networkError', message: 'x' } }));
expect(getCachedScan('bad')).toBeNull();
});
it('refresh bypasses the cache', async () => {
setCachedScan('fresh', { ok: true, items: ['cached'] });
const result = await scanWithCache('fresh', async () => ({ ok: true, items: ['reloaded'] }), { refresh: true });
expect(result.items).toEqual(['reloaded']);
expect(getCachedScan('fresh').items).toEqual(['reloaded']);
});
it('persists successful scans to disk for later processes', async () => {
await scanWithCache('persisted', async () => ({ ok: true, items: [{ skillName: 'x' }] }));
await flushDiskWrites();
const onDisk = JSON.parse(fs.readFileSync(path.join(tempDataDir, 'skills-catalog-cache.json'), 'utf8'));
expect(onDisk.persisted.value.items).toEqual([{ skillName: 'x' }]);
});
});