From a8f0f99c3d2a62b22957d226cec997f2ec04832b Mon Sep 17 00:00:00 2001 From: Bohdan Triapitsyn Date: Wed, 9 Sep 2026 23:23:22 +0300 Subject: [PATCH] fix(quota): normalize OpenCode Go reset timestamps Go returns ISO reset dates, but the shared usage contract requires numeric timestamps. Convert dates to milliseconds before building usage windows. Verified all three windows against the live Go API. Added numeric timestamp and partial-window regressions; 14 focused tests, web type-check, lint, and syntax checks pass. --- .../server/lib/quota/providers/opencode-go.js | 2 +- .../lib/quota/providers/opencode-go.test.js | 27 ++++++++++++++++++- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/packages/web/server/lib/quota/providers/opencode-go.js b/packages/web/server/lib/quota/providers/opencode-go.js index b6b97370..f87710a7 100644 --- a/packages/web/server/lib/quota/providers/opencode-go.js +++ b/packages/web/server/lib/quota/providers/opencode-go.js @@ -25,7 +25,7 @@ export const parseOpenCodeGoUsage = (payload) => { if (typeof resetAt !== 'string' || !Number.isFinite(new Date(resetAt).getTime())) continue; windows[key] = toUsageWindow({ usedPercent: Math.min(100, Math.max(0, usedPercent)), - resetAt, + resetAt: new Date(resetAt).getTime(), windowSeconds: null, }); } diff --git a/packages/web/server/lib/quota/providers/opencode-go.test.js b/packages/web/server/lib/quota/providers/opencode-go.test.js index 505cbee5..f0a89a9b 100644 --- a/packages/web/server/lib/quota/providers/opencode-go.test.js +++ b/packages/web/server/lib/quota/providers/opencode-go.test.js @@ -27,11 +27,36 @@ describe('OpenCode Go quota provider', () => { it('parses partial API usage windows', () => { const windows = parseOpenCodeGoUsage({ usage: { rolling: { percent: 25, resetsAt: '2026-08-12T12:00:00.000Z' }, weekly: { percent: 40, resetsAt: '2026-08-19T12:00:00.000Z' } } }); expect(windows['5h'].usedPercent).toBe(25); - expect(windows['5h'].resetAt).toBe('2026-08-12T12:00:00.000Z'); + expect(windows['5h'].resetAt).toBe(Date.parse('2026-08-12T12:00:00.000Z')); expect(windows.weekly.usedPercent).toBe(40); expect(windows.monthly).toBeUndefined(); }); + it('returns numeric reset timestamps for all usage windows', async () => { + const resetsAt = '2026-10-01T00:00:00.000Z'; + const windows = await fetchOpenCodeGoUsage('test-key', async () => new Response(JSON.stringify({ + usage: { + rolling: { percent: 25, resetsAt }, + weekly: { percent: 40, resetsAt }, + monthly: { percent: 60, resetsAt }, + }, + }))); + expect(Object.keys(windows)).toEqual(['5h', 'weekly', 'monthly']); + for (const window of Object.values(windows)) { + expect(window.resetAt).toBe(Date.parse(resetsAt)); + } + }); + + it('preserves valid windows when another reset date is invalid', () => { + const windows = parseOpenCodeGoUsage({ usage: { + rolling: { percent: 25, resetsAt: 'invalid' }, + weekly: { percent: 40, resetsAt: '2026-08-19T12:00:00.000Z' }, + monthly: { percent: 60, resetsAt: null }, + } }); + expect(Object.keys(windows)).toEqual(['weekly']); + expect(windows.weekly.resetAt).toBe(Date.parse('2026-08-19T12:00:00.000Z')); + }); + it('does not expose credentials in authentication errors', async () => { await expect(fetchOpenCodeGoUsage('secret', async () => new Response('', { status: 403 }))).rejects.toThrow('authentication failed'); });