67 lines
2.1 KiB
TypeScript
67 lines
2.1 KiB
TypeScript
|
|
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
||
|
|
import { cache } from '../cache';
|
||
|
|
|
||
|
|
describe('MemoryCache stale window', () => {
|
||
|
|
beforeEach(() => {
|
||
|
|
cache.clear();
|
||
|
|
vi.useFakeTimers();
|
||
|
|
});
|
||
|
|
|
||
|
|
afterEach(() => {
|
||
|
|
vi.useRealTimers();
|
||
|
|
});
|
||
|
|
|
||
|
|
it('returns fresh entries from get()', () => {
|
||
|
|
cache.set('k', { v: 1 }, 1000);
|
||
|
|
expect(cache.get<{ v: number }>('k')).toEqual({ v: 1 });
|
||
|
|
});
|
||
|
|
|
||
|
|
it('returns null from get() once the ttl elapsed', () => {
|
||
|
|
cache.set('k', { v: 1 }, 1000);
|
||
|
|
vi.advanceTimersByTime(1001);
|
||
|
|
expect(cache.get('k')).toBeNull();
|
||
|
|
});
|
||
|
|
|
||
|
|
it('still returns the value from getStale() after the ttl elapsed', () => {
|
||
|
|
cache.set('k', { v: 1 }, 1000, 60_000);
|
||
|
|
vi.advanceTimersByTime(1001);
|
||
|
|
expect(cache.getStale<{ v: number }>('k')).toEqual({ v: 1 });
|
||
|
|
});
|
||
|
|
|
||
|
|
it('drops the entry from getStale() past the hard max age', () => {
|
||
|
|
cache.set('k', { v: 1 }, 1000, 5000);
|
||
|
|
vi.advanceTimersByTime(5001);
|
||
|
|
expect(cache.getStale('k')).toBeNull();
|
||
|
|
});
|
||
|
|
|
||
|
|
it('does not let get() destroy the stale fallback', () => {
|
||
|
|
// get() must not delete an expired entry — otherwise the fallback is gone
|
||
|
|
// the moment any caller asks for fresh data first.
|
||
|
|
cache.set('k', { v: 1 }, 1000, 60_000);
|
||
|
|
vi.advanceTimersByTime(1001);
|
||
|
|
expect(cache.get('k')).toBeNull();
|
||
|
|
expect(cache.getStale<{ v: number }>('k')).toEqual({ v: 1 });
|
||
|
|
});
|
||
|
|
|
||
|
|
it('never expires stale before the ttl itself', () => {
|
||
|
|
// A misconfigured STALE_MAX_AGE below CACHE_TTL must not shorten the fresh
|
||
|
|
// window.
|
||
|
|
cache.set('k', { v: 1 }, 10_000, 1000);
|
||
|
|
vi.advanceTimersByTime(9000);
|
||
|
|
expect(cache.get<{ v: number }>('k')).toEqual({ v: 1 });
|
||
|
|
});
|
||
|
|
|
||
|
|
it('evicts the oldest entry when capacity is exceeded', () => {
|
||
|
|
for (let i = 0; i < 201; i++) cache.set(`k${i}`, i, 60_000);
|
||
|
|
expect(cache.get('k0')).toBeNull();
|
||
|
|
expect(cache.get('k200')).toBe(200);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('delete() removes the entry from get() and getStale()', () => {
|
||
|
|
cache.set('k', { v: 1 }, 1000, 60_000);
|
||
|
|
cache.delete('k');
|
||
|
|
expect(cache.get('k')).toBeNull();
|
||
|
|
expect(cache.getStale('k')).toBeNull();
|
||
|
|
});
|
||
|
|
});
|