import { test } from 'node:test'; import assert from 'node:assert/strict'; import { createUsageFetcher, USAGE_CACHE_MS, USAGE_ENDPOINT } from '../src/daemon/usage.js'; const SECRET = 'tok-secret-do-not-leak'; /** 构造可控时钟 + 计数 fetch 的 fetcher(不真打 API) */ function setup(opts: { body?: unknown; status?: number; fetchError?: Error; token?: string | null; } = {}): { getUsage: () => Promise; calls: { fetch: number; token: number }; logs: string[]; clock: { t: number }; } { const calls = { fetch: 0, token: 0 }; const logs: string[] = []; const clock = { t: 1_000_000 }; const body = opts.body ?? { five_hour: { utilization: 47.0, resets_at: '2026-06-12T22:30:00+00:00' }, seven_day: { utilization: 57.0, resets_at: '2026-06-13T12:00:00+00:00' }, }; const fetchFn = (async (url: string | URL | Request, init?: RequestInit) => { calls.fetch += 1; assert.equal(String(url), USAGE_ENDPOINT); assert.match(String((init?.headers as Record).Authorization), /^Bearer /); if (opts.fetchError) throw opts.fetchError; return { ok: (opts.status ?? 200) < 400, status: opts.status ?? 200, json: async () => body, } as Response; }) as typeof fetch; const getUsage = createUsageFetcher({ fetchFn, readToken: async () => { calls.token += 1; return opts.token === undefined ? SECRET : opts.token; }, now: () => clock.t, log: { info: (m) => logs.push(m), error: (m) => logs.push(m) }, }); return { getUsage, calls, logs, clock }; } test('usage:成功解析 five_hour/seven_day → UsageInfo(percent 取整,resetsAt 透传)', async () => { const { getUsage } = setup(); const u = await getUsage(); assert.deepEqual(u, { session: { percent: 47, resetsAt: '2026-06-12T22:30:00+00:00' }, weekly: { percent: 57, resetsAt: '2026-06-13T12:00:00+00:00' }, }); }); test('usage:60s 内存缓存——窗口内不重复请求,过期后重新拉取', async () => { const { getUsage, calls, clock } = setup(); await getUsage(); await getUsage(); clock.t += USAGE_CACHE_MS - 1; await getUsage(); assert.equal(calls.fetch, 1); clock.t += 2; // 越过缓存窗口 await getUsage(); assert.equal(calls.fetch, 2); }); test('usage:并发调用共享同一 in-flight 请求', async () => { const { getUsage, calls } = setup(); const [a, b] = await Promise.all([getUsage(), getUsage()]); assert.equal(calls.fetch, 1); assert.deepEqual(a, b); }); test('usage:fetch 抛错 → null 且负缓存 60s(不重试风暴),日志不含 token', async () => { const { getUsage, calls, logs } = setup({ fetchError: new Error('network down') }); assert.equal(await getUsage(), null); assert.equal(await getUsage(), null); assert.equal(calls.fetch, 1); // 失败结果同样被缓存 assert.ok(logs.some((m) => m.includes('network down'))); assert.ok(logs.every((m) => !m.includes(SECRET))); }); test('usage:HTTP 非 200 → null', async () => { const { getUsage, logs } = setup({ status: 401 }); assert.equal(await getUsage(), null); assert.ok(logs.some((m) => m.includes('401'))); }); test('usage:无凭证 → null 且不发起请求', async () => { const { getUsage, calls } = setup({ token: null }); assert.equal(await getUsage(), null); assert.equal(calls.fetch, 0); }); test('usage:响应缺少两个窗口字段 → null;只有一个窗口 → 另一侧为 null', async () => { const none = setup({ body: { unrelated: true } }); assert.equal(await none.getUsage(), null); const onlyWeek = setup({ body: { seven_day: { utilization: 88.6, resets_at: null } } }); assert.deepEqual(await onlyWeek.getUsage(), { session: null, weekly: { percent: 89, resetsAt: null }, }); });