Files
ccs/tests/unit/cliproxy/quota-fetcher-codex.test.ts
T
kaitranntt ad8327d17e test(cliproxy): add unit tests for quota fetchers and auth utilities
Add comprehensive unit tests for:
- buildCodexQuotaWindows(): window parsing, clamping, reset time calculation
- buildGeminiCliBuckets(): bucket grouping, model series, token types
- resolveGeminiCliProjectId(): project ID extraction from account field
- sanitizeEmail(): email to filename conversion
- isTokenExpired(): token expiry validation

Also removes unused 'preferred' field from GEMINI_CLI_GROUPS (YAGNI)

39 new tests covering edge cases:
- Percentage clamping (0-100, 0-1)
- Missing/null fields
- camelCase/snake_case API responses
- Empty inputs
- Invalid date strings

Addresses code review feedback on PR #395
2026-01-29 15:01:26 -05:00

191 lines
5.1 KiB
TypeScript

/**
* Codex Quota Fetcher Unit Tests
*
* Tests for Codex quota window parsing and transformation logic
*/
import { describe, it, expect } from 'bun:test';
import { buildCodexQuotaWindows } from '../../../src/cliproxy/quota-fetcher-codex';
describe('Codex Quota Fetcher', () => {
describe('buildCodexQuotaWindows', () => {
it('should parse snake_case API response', () => {
const response = {
rate_limit: {
primary_window: {
used_percent: 25,
reset_after_seconds: 3600,
},
secondary_window: {
used_percent: 50,
reset_after_seconds: 86400,
},
},
};
const windows = buildCodexQuotaWindows(response);
expect(windows).toHaveLength(2);
expect(windows[0].label).toBe('Primary');
expect(windows[0].usedPercent).toBe(25);
expect(windows[0].remainingPercent).toBe(75);
expect(windows[0].resetAfterSeconds).toBe(3600);
expect(windows[1].label).toBe('Secondary');
expect(windows[1].usedPercent).toBe(50);
});
it('should parse camelCase API response', () => {
const response = {
rateLimit: {
primaryWindow: {
usedPercent: 30,
resetAfterSeconds: 7200,
},
},
};
const windows = buildCodexQuotaWindows(response);
expect(windows).toHaveLength(1);
expect(windows[0].usedPercent).toBe(30);
expect(windows[0].resetAfterSeconds).toBe(7200);
});
it('should handle code review rate limits', () => {
const response = {
code_review_rate_limit: {
primary_window: {
used_percent: 80,
reset_after_seconds: 1800,
},
},
};
const windows = buildCodexQuotaWindows(response);
expect(windows).toHaveLength(1);
expect(windows[0].label).toBe('Code Review (Primary)');
expect(windows[0].usedPercent).toBe(80);
});
it('should clamp usedPercent to 0-100 range', () => {
const response = {
rate_limit: {
primary_window: {
used_percent: 150, // Over 100
reset_after_seconds: null,
},
secondary_window: {
used_percent: -20, // Negative
reset_after_seconds: null,
},
},
};
const windows = buildCodexQuotaWindows(response);
expect(windows[0].usedPercent).toBe(100);
expect(windows[0].remainingPercent).toBe(0);
expect(windows[1].usedPercent).toBe(0);
expect(windows[1].remainingPercent).toBe(100);
});
it('should handle null reset_after_seconds', () => {
const response = {
rate_limit: {
primary_window: {
used_percent: 10,
reset_after_seconds: null,
},
},
};
const windows = buildCodexQuotaWindows(response);
expect(windows[0].resetAfterSeconds).toBeNull();
expect(windows[0].resetAt).toBeNull();
});
it('should calculate resetAt from positive seconds', () => {
const response = {
rate_limit: {
primary_window: {
used_percent: 10,
reset_after_seconds: 3600, // 1 hour
},
},
};
const before = Date.now();
const windows = buildCodexQuotaWindows(response);
const after = Date.now();
expect(windows[0].resetAt).not.toBeNull();
const resetTime = new Date(windows[0].resetAt!).getTime();
expect(resetTime).toBeGreaterThanOrEqual(before + 3600000);
expect(resetTime).toBeLessThanOrEqual(after + 3600000);
});
it('should not calculate resetAt for zero or negative seconds', () => {
const response = {
rate_limit: {
primary_window: {
used_percent: 10,
reset_after_seconds: 0,
},
secondary_window: {
used_percent: 20,
reset_after_seconds: -100,
},
},
};
const windows = buildCodexQuotaWindows(response);
expect(windows[0].resetAt).toBeNull();
expect(windows[1].resetAt).toBeNull();
});
it('should return empty array for empty response', () => {
const windows = buildCodexQuotaWindows({});
expect(windows).toHaveLength(0);
});
it('should return empty array for missing rate limit', () => {
const response = {
plan_type: 'plus',
};
const windows = buildCodexQuotaWindows(response);
expect(windows).toHaveLength(0);
});
it('should handle missing window data gracefully', () => {
const response = {
rate_limit: {
primary_window: undefined,
secondary_window: null,
},
};
const windows = buildCodexQuotaWindows(response as never);
expect(windows).toHaveLength(0);
});
it('should default usedPercent to 0 when missing', () => {
const response = {
rate_limit: {
primary_window: {
reset_after_seconds: 3600,
},
},
};
const windows = buildCodexQuotaWindows(response);
expect(windows[0].usedPercent).toBe(0);
expect(windows[0].remainingPercent).toBe(100);
});
});
});