import assert from 'node:assert/strict'; import { clearFmpCacheForTests, fetchFmpJson, FMP_CACHE_TTL_MS, FmpFetchError, setFmpCacheNowForTests, } from './src/services/fmpCache.js'; type MockResponse = { ok: boolean; status: number; json: () => Promise; }; function createFetch(payloads: unknown[]) { let calls = 0; const fetchJson = async (): Promise => { const payload = payloads[calls] ?? payloads[payloads.length - 1]; calls += 1; return { ok: true, status: 200, json: async () => payload, }; }; return { fetchJson, get calls() { return calls; }, }; } async function main() { clearFmpCacheForTests(); let now = 1_000; setFmpCacheNowForTests(() => now); const url = 'https://financialmodelingprep.com/api/v3/profile/AAPL?apikey=test'; const otherUrl = 'https://financialmodelingprep.com/api/v3/profile/MSFT?apikey=test'; const fetcher = createFetch([{ symbol: 'AAPL' }, { symbol: 'AAPL-refresh' }]); assert.deepEqual(await fetchFmpJson(url, fetcher.fetchJson), { symbol: 'AAPL' }); assert.deepEqual(await fetchFmpJson(url, fetcher.fetchJson), { symbol: 'AAPL' }); assert.equal(fetcher.calls, 1, 'same full URL should be served from cache before TTL expiry'); assert.deepEqual(await fetchFmpJson(otherUrl, fetcher.fetchJson), { symbol: 'AAPL-refresh' }); assert.equal(fetcher.calls, 2, 'different full URL should get its own cache entry'); now += FMP_CACHE_TTL_MS + 1; assert.deepEqual(await fetchFmpJson(url, fetcher.fetchJson), { symbol: 'AAPL-refresh' }); assert.equal(fetcher.calls, 3, 'expired cache entry should be refetched'); clearFmpCacheForTests(); let failingCalls = 0; const failingFetch = async (): Promise => { failingCalls += 1; return { ok: false, status: 429, json: async () => ({ error: 'rate limit' }), }; }; await assert.rejects( () => fetchFmpJson(url, failingFetch), (error) => error instanceof FmpFetchError && error.status === 429, ); await assert.rejects(() => fetchFmpJson(url, failingFetch)); assert.equal(failingCalls, 2, 'failed upstream responses should not be cached'); clearFmpCacheForTests(); console.log('FMP cache tests passed'); } main().catch((error) => { console.error(error); process.exit(1); });