import assert from 'node:assert/strict' import test from 'node:test' import { createLru } from '../server/lib/lru.ts' function clock(start = 1000) { let t = start return { now: () => t, advance: (ms: number) => (t += ms) } } test('lru: get answers a value within its TTL and nothing after it', () => { const c = clock() const lru = createLru({ max: 10, ttlMs: 60_000, now: c.now }) lru.set('/a', 'A') assert.equal(lru.get('/a'), 'A') c.advance(59_999) assert.equal(lru.get('/a'), 'A') c.advance(1) assert.equal(lru.get('/a'), undefined) assert.equal(lru.size, 0) }) test('lru: the least recently used entry goes first when full', () => { const lru = createLru({ max: 2, ttlMs: 60_000 }) lru.set('a', 1) lru.set('b', 2) assert.equal(lru.get('a'), 1) // a is now the most recent lru.set('c', 3) assert.equal(lru.get('b'), undefined) assert.equal(lru.get('a'), 1) assert.equal(lru.get('c'), 3) assert.equal(lru.size, 2) }) test('lru: load calls the loader once for concurrent requests of one key', async () => { const lru = createLru({ max: 10, ttlMs: 60_000 }) let calls = 0 let release: (v: string) => void = () => {} const loader = () => { calls++ return new Promise((r) => (release = r)) } const one = lru.load('/x', loader) const two = lru.load('/x', loader) release('X') assert.deepEqual(await Promise.all([one, two]), ['X', 'X']) assert.equal(calls, 1) assert.equal(await lru.load('/x', loader), 'X') assert.equal(calls, 1, 'a cached value needs no call') }) test('lru: a value the cacheable test refuses is not stored, and a failed load is not cached', async () => { const lru = createLru({ max: 10, ttlMs: 60_000 }) let calls = 0 await lru.load('/n', async () => (calls++, null), (v) => v !== null) await lru.load('/n', async () => (calls++, null), (v) => v !== null) assert.equal(calls, 2) await assert.rejects(lru.load('/e', async () => Promise.reject(new Error('down')))) assert.equal(await lru.load('/e', async () => 'ok'), 'ok') }) test('lru: clear empties the cache and an answer started before it is never stored', async () => { const lru = createLru({ max: 10, ttlMs: 60_000 }) let release: (v: string) => void = () => {} const stale = lru.load('/p', () => new Promise((r) => (release = r))) lru.clear() release('stale') assert.equal(await stale, 'stale', 'the request in flight still gets its answer') assert.equal(lru.get('/p'), undefined, 'but it is not cached after the purge') let calls = 0 assert.equal(await lru.load('/p', async () => (calls++, 'fresh')), 'fresh') assert.equal(calls, 1) }) test('lru: a load after clear does not join a load started before it', async () => { const lru = createLru({ max: 10, ttlMs: 60_000 }) let releaseOld: (v: string) => void = () => {} const old = lru.load('/q', () => new Promise((r) => (releaseOld = r))) lru.clear() const fresh = lru.load('/q', async () => 'new') releaseOld('old') assert.equal(await old, 'old') assert.equal(await fresh, 'new') assert.equal(lru.get('/q'), 'new') }) test('lru: ttl 0 caches nothing but still shares a load in flight', async () => { const lru = createLru({ max: 10, ttlMs: 0 }) let calls = 0 const loader = async () => (calls++, 'v') await Promise.all([lru.load('/z', loader), lru.load('/z', loader)]) assert.equal(calls, 1) await lru.load('/z', loader) assert.equal(calls, 2) lru.set('/z', 'w') assert.equal(lru.get('/z'), undefined) }) test('lru: a loader that throws at once rejects and leaves nothing in flight', async () => { const lru = createLru({ max: 10, ttlMs: 60_000 }) const boom = (() => { throw new Error('sync') }) as unknown as () => Promise await assert.rejects(lru.load('/s', boom), /sync/) assert.equal(await lru.load('/s', async () => 'after'), 'after') })