Add Codex usage to /status
This commit is contained in:
204
src/services/api/codexUsage.test.ts
Normal file
204
src/services/api/codexUsage.test.ts
Normal file
@@ -0,0 +1,204 @@
|
||||
import { describe, expect, test } from 'bun:test'
|
||||
|
||||
import {
|
||||
buildCodexUsageRows,
|
||||
formatCodexPlanType,
|
||||
getCodexUsageUrl,
|
||||
normalizeCodexUsagePayload,
|
||||
} from './codexUsage.js'
|
||||
|
||||
describe('normalizeCodexUsagePayload', () => {
|
||||
test('normalizes live Codex usage payloads from /backend-api/wham/usage', () => {
|
||||
const usage = normalizeCodexUsagePayload({
|
||||
plan_type: 'plus',
|
||||
rate_limit: {
|
||||
primary_window: {
|
||||
used_percent: 38,
|
||||
limit_window_seconds: 18_000,
|
||||
reset_at: 1_775_154_358,
|
||||
},
|
||||
secondary_window: {
|
||||
used_percent: 32,
|
||||
limit_window_seconds: 604_800,
|
||||
reset_at: 1_775_685_041,
|
||||
},
|
||||
},
|
||||
code_review_rate_limit: {
|
||||
primary_window: {
|
||||
used_percent: 0,
|
||||
limit_window_seconds: 604_800,
|
||||
reset_at: 1_775_744_471,
|
||||
},
|
||||
secondary_window: null,
|
||||
},
|
||||
credits: {
|
||||
has_credits: false,
|
||||
unlimited: false,
|
||||
balance: '0',
|
||||
},
|
||||
})
|
||||
|
||||
expect(usage.planType).toBe('plus')
|
||||
expect(usage.snapshots).toHaveLength(2)
|
||||
expect(usage.snapshots[0]).toMatchObject({
|
||||
limitName: 'codex',
|
||||
primary: {
|
||||
usedPercent: 38,
|
||||
windowMinutes: 300,
|
||||
},
|
||||
secondary: {
|
||||
usedPercent: 32,
|
||||
windowMinutes: 10_080,
|
||||
},
|
||||
})
|
||||
expect(usage.snapshots[1]).toMatchObject({
|
||||
limitName: 'code review',
|
||||
primary: {
|
||||
usedPercent: 0,
|
||||
windowMinutes: 10_080,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
test('supports direct protocol-style snapshot collections', () => {
|
||||
const usage = normalizeCodexUsagePayload({
|
||||
rateLimitsByLimitId: {
|
||||
codex: {
|
||||
limit_name: 'codex',
|
||||
primary: {
|
||||
used_percent: 12,
|
||||
window_minutes: 300,
|
||||
resets_at: 1_700_000_000,
|
||||
},
|
||||
credits: {
|
||||
has_credits: true,
|
||||
unlimited: false,
|
||||
balance: '25',
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
expect(usage.snapshots).toEqual([
|
||||
{
|
||||
limitName: 'codex',
|
||||
primary: {
|
||||
usedPercent: 12,
|
||||
windowMinutes: 300,
|
||||
resetsAt: new Date(1_700_000_000 * 1000).toISOString(),
|
||||
},
|
||||
secondary: undefined,
|
||||
credits: {
|
||||
hasCredits: true,
|
||||
unlimited: false,
|
||||
balance: '25',
|
||||
},
|
||||
},
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe('buildCodexUsageRows', () => {
|
||||
test('builds Codex-like labels for primary and secondary windows', () => {
|
||||
const rows = buildCodexUsageRows([
|
||||
{
|
||||
limitName: 'codex',
|
||||
primary: {
|
||||
usedPercent: 38,
|
||||
windowMinutes: 300,
|
||||
resetsAt: '2026-04-02T10:00:00.000Z',
|
||||
},
|
||||
secondary: {
|
||||
usedPercent: 32,
|
||||
windowMinutes: 10_080,
|
||||
resetsAt: '2026-04-09T10:00:00.000Z',
|
||||
},
|
||||
},
|
||||
{
|
||||
limitName: 'code review',
|
||||
primary: {
|
||||
usedPercent: 0,
|
||||
windowMinutes: 10_080,
|
||||
resetsAt: '2026-04-09T10:00:00.000Z',
|
||||
},
|
||||
},
|
||||
])
|
||||
|
||||
expect(rows).toEqual([
|
||||
{
|
||||
kind: 'window',
|
||||
label: '5h limit',
|
||||
usedPercent: 38,
|
||||
resetsAt: '2026-04-02T10:00:00.000Z',
|
||||
},
|
||||
{
|
||||
kind: 'window',
|
||||
label: 'Weekly limit',
|
||||
usedPercent: 32,
|
||||
resetsAt: '2026-04-09T10:00:00.000Z',
|
||||
},
|
||||
{
|
||||
kind: 'window',
|
||||
label: 'Code review Weekly limit',
|
||||
usedPercent: 0,
|
||||
resetsAt: '2026-04-09T10:00:00.000Z',
|
||||
},
|
||||
])
|
||||
})
|
||||
|
||||
test('renders credits rows only when credits are available', () => {
|
||||
const rows = buildCodexUsageRows([
|
||||
{
|
||||
limitName: 'codex',
|
||||
credits: {
|
||||
hasCredits: true,
|
||||
unlimited: false,
|
||||
balance: '25.2',
|
||||
},
|
||||
},
|
||||
{
|
||||
limitName: 'code review',
|
||||
credits: {
|
||||
hasCredits: true,
|
||||
unlimited: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
limitName: 'other',
|
||||
credits: {
|
||||
hasCredits: true,
|
||||
unlimited: false,
|
||||
balance: '0',
|
||||
},
|
||||
},
|
||||
])
|
||||
|
||||
expect(rows).toEqual([
|
||||
{
|
||||
kind: 'text',
|
||||
label: 'Credits',
|
||||
value: '25 credits',
|
||||
},
|
||||
{
|
||||
kind: 'text',
|
||||
label: 'Code review limit',
|
||||
value: '',
|
||||
},
|
||||
{
|
||||
kind: 'text',
|
||||
label: 'Credits',
|
||||
value: 'Unlimited',
|
||||
},
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe('Codex usage helpers', () => {
|
||||
test('formats plan labels and usage endpoint url', () => {
|
||||
expect(formatCodexPlanType('team_max')).toBe('Team Max')
|
||||
expect(getCodexUsageUrl()).toBe('https://chatgpt.com/backend-api/wham/usage')
|
||||
expect(getCodexUsageUrl('https://chatgpt.com/backend-api/codex')).toBe(
|
||||
'https://chatgpt.com/backend-api/wham/usage',
|
||||
)
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user