mirror of
https://github.com/router-for-me/Cli-Proxy-API-Management-Center.git
synced 2026-10-16 14:57:05 +08:00
229 lines
7.8 KiB
TypeScript
229 lines
7.8 KiB
TypeScript
import { afterEach, describe, expect, test } from 'bun:test';
|
|
import type { TFunction } from 'i18next';
|
|
import {
|
|
CODEX_CONFIG,
|
|
buildCodexQuotaWindows,
|
|
normalizeCodexAccountCredits,
|
|
} from '@/features/quota/providers/codex/data';
|
|
import type { CodexQuotaState, CodexUsagePayload } from '@/types';
|
|
import { apiCallApi, type ApiCallRequest, type ApiCallResult } from '@/services/api';
|
|
import {
|
|
CODEX_RATE_LIMIT_RESET_CREDITS_URL,
|
|
CODEX_SUBSCRIPTION_URL,
|
|
CODEX_USAGE_URL,
|
|
normalizeCodexResetCreditsPayload,
|
|
parseCodexUsagePayload,
|
|
} from '@/utils/quota';
|
|
|
|
const t = ((key: string) => key) as TFunction;
|
|
const originalApiCallRequest = apiCallApi.request;
|
|
|
|
const result = (statusCode: number, body: unknown = null): ApiCallResult => ({
|
|
statusCode,
|
|
header: {},
|
|
bodyText: body === null ? '' : JSON.stringify(body),
|
|
body,
|
|
});
|
|
|
|
const CURRENT_CODEX_USAGE_PAYLOAD: CodexUsagePayload = {
|
|
plan_type: 'pro',
|
|
rate_limit: {
|
|
allowed: true,
|
|
limit_reached: false,
|
|
primary_window: {
|
|
used_percent: 1,
|
|
limit_window_seconds: 604800,
|
|
reset_after_seconds: 601888,
|
|
reset_at: 1785902974,
|
|
},
|
|
secondary_window: null,
|
|
},
|
|
code_review_rate_limit: null,
|
|
additional_rate_limits: [
|
|
{
|
|
limit_name: 'GPT-5.3-Codex-Spark',
|
|
metered_feature: 'codex_bengalfox',
|
|
rate_limit: {
|
|
allowed: true,
|
|
limit_reached: false,
|
|
primary_window: {
|
|
used_percent: 0,
|
|
limit_window_seconds: 604800,
|
|
reset_after_seconds: 602111,
|
|
reset_at: 1785903197,
|
|
},
|
|
secondary_window: null,
|
|
},
|
|
},
|
|
],
|
|
rate_limit_reset_credits: {
|
|
available_count: 1,
|
|
applicable_available_count: 0,
|
|
},
|
|
};
|
|
|
|
afterEach(() => {
|
|
apiCallApi.request = originalApiCallRequest;
|
|
});
|
|
|
|
describe('Codex current usage payload', () => {
|
|
test('parses the proxied JSON body and classifies both primary weekly windows', () => {
|
|
const payload = parseCodexUsagePayload(JSON.stringify(CURRENT_CODEX_USAGE_PAYLOAD));
|
|
expect(payload).not.toBeNull();
|
|
|
|
const windows = buildCodexQuotaWindows(payload!, t);
|
|
|
|
expect(windows.map(({ id }) => id)).toEqual(['weekly', 'gpt-5-3-codex-spark-weekly-0']);
|
|
expect(windows.map(({ labelKey }) => labelKey)).toEqual([
|
|
'codex_quota.secondary_window',
|
|
'codex_quota.additional_secondary_window',
|
|
]);
|
|
expect(windows.map(({ usedPercent }) => usedPercent)).toEqual([1, 0]);
|
|
expect(windows[1]?.labelParams).toEqual({ name: 'GPT-5.3-Codex-Spark' });
|
|
});
|
|
|
|
test('shows reset support when total credits remain but none currently apply', () => {
|
|
const summary = normalizeCodexResetCreditsPayload(
|
|
CURRENT_CODEX_USAGE_PAYLOAD.rate_limit_reset_credits
|
|
);
|
|
|
|
expect(summary.invalidPayload).toBeFalse();
|
|
expect(summary.availableCount).toBe(1);
|
|
expect(summary.applicableAvailableCount).toBe(0);
|
|
|
|
const quota: CodexQuotaState = {
|
|
status: 'success',
|
|
windows: [],
|
|
rateLimitResetCreditsAvailableCount: summary.availableCount,
|
|
rateLimitResetCreditsApplicableAvailableCount: summary.applicableAvailableCount,
|
|
};
|
|
expect(CODEX_CONFIG.canResetQuota?.(quota)).toBeTrue();
|
|
});
|
|
|
|
test('keeps reset support for legacy payloads without applicable count', () => {
|
|
const quota: CodexQuotaState = {
|
|
status: 'success',
|
|
windows: [],
|
|
rateLimitResetCreditsAvailableCount: 1,
|
|
};
|
|
|
|
expect(CODEX_CONFIG.canResetQuota?.(quota)).toBeTrue();
|
|
});
|
|
});
|
|
|
|
describe('Codex account credits', () => {
|
|
test('normalizes remaining balance without confusing it with manual resets', () => {
|
|
expect(
|
|
normalizeCodexAccountCredits({ has_credits: false, unlimited: false, balance: '0' })
|
|
).toEqual({ balance: '0', unlimited: false });
|
|
expect(
|
|
normalizeCodexAccountCredits({ has_credits: true, unlimited: false, balance: ' 12.50 ' })
|
|
).toEqual({ balance: '12.50', unlimited: false });
|
|
expect(normalizeCodexAccountCredits({ unlimited: true, balance: null })).toEqual({
|
|
balance: null,
|
|
unlimited: true,
|
|
});
|
|
expect(normalizeCodexAccountCredits(null)).toEqual({ balance: null, unlimited: false });
|
|
expect(normalizeCodexAccountCredits({ balance: 'not available' })).toEqual({
|
|
balance: null,
|
|
unlimited: false,
|
|
});
|
|
expect(normalizeCodexAccountCredits({ balance: -1 })).toEqual({
|
|
balance: null,
|
|
unlimited: false,
|
|
});
|
|
});
|
|
|
|
test('reads credits from the existing usage request and forwards them into quota state', async () => {
|
|
const requests: ApiCallRequest[] = [];
|
|
apiCallApi.request = async (payload) => {
|
|
requests.push(payload);
|
|
if (payload.url === CODEX_USAGE_URL) {
|
|
return result(200, {
|
|
...CURRENT_CODEX_USAGE_PAYLOAD,
|
|
credits: { has_credits: true, unlimited: false, balance: '8.75' },
|
|
});
|
|
}
|
|
if (payload.url === CODEX_RATE_LIMIT_RESET_CREDITS_URL) {
|
|
return result(200, { available_count: 1, credits: [] });
|
|
}
|
|
throw new Error(`Unexpected URL: ${payload.url}`);
|
|
};
|
|
|
|
const data = await CODEX_CONFIG.fetchQuota(
|
|
{ name: 'codex.json', type: 'codex', auth_index: 'codex:1' },
|
|
t
|
|
);
|
|
const state = CODEX_CONFIG.buildSuccessState(data);
|
|
expect(state.creditBalance).toBe('8.75');
|
|
expect(state.creditsUnlimited).toBeFalse();
|
|
expect(state.rateLimitResetCreditsAvailableCount).toBe(1);
|
|
expect(requests.filter((request) => request.url === CODEX_USAGE_URL)).toHaveLength(1);
|
|
});
|
|
});
|
|
|
|
describe('Codex live subscription renewal', () => {
|
|
test('prefers the live active_until and sends the encoded account ID', async () => {
|
|
const requests: ApiCallRequest[] = [];
|
|
apiCallApi.request = async (payload) => {
|
|
requests.push(payload);
|
|
if (payload.url === CODEX_USAGE_URL) return result(200, CURRENT_CODEX_USAGE_PAYLOAD);
|
|
if (payload.url === CODEX_RATE_LIMIT_RESET_CREDITS_URL) {
|
|
return result(200, { available_count: 0, credits: [] });
|
|
}
|
|
if (payload.url.startsWith(CODEX_SUBSCRIPTION_URL)) {
|
|
return result(200, { active_until: '2026-10-03T13:27:01Z' });
|
|
}
|
|
throw new Error(`Unexpected URL: ${payload.url}`);
|
|
};
|
|
|
|
const quota = await CODEX_CONFIG.fetchQuota(
|
|
{
|
|
name: 'codex.json',
|
|
type: 'codex',
|
|
auth_index: 'codex:1',
|
|
metadata: { chatgpt_account_id: 'account/id + space' },
|
|
chatgpt_subscription_active_until: '2026-09-03T13:27:01Z',
|
|
},
|
|
t
|
|
);
|
|
|
|
expect(quota.subscriptionActiveUntil).toBe('2026-10-03T13:27:01Z');
|
|
const subscriptionRequest = requests.find((request) =>
|
|
request.url.startsWith(CODEX_SUBSCRIPTION_URL)
|
|
);
|
|
expect(subscriptionRequest?.url).toBe(
|
|
`${CODEX_SUBSCRIPTION_URL}?account_id=account%2Fid%20%2B%20space`
|
|
);
|
|
expect(subscriptionRequest?.authIndex).toBe('codex:1');
|
|
expect(subscriptionRequest?.header?.Authorization).toBe('Bearer $TOKEN$');
|
|
expect(subscriptionRequest?.header?.['Chatgpt-Account-Id']).toBe('account/id + space');
|
|
});
|
|
|
|
test('falls back to the credential date when the subscription probe fails', async () => {
|
|
apiCallApi.request = async (payload) => {
|
|
if (payload.url === CODEX_USAGE_URL) return result(200, CURRENT_CODEX_USAGE_PAYLOAD);
|
|
if (payload.url === CODEX_RATE_LIMIT_RESET_CREDITS_URL) {
|
|
return result(200, { available_count: 0, credits: [] });
|
|
}
|
|
if (payload.url.startsWith(CODEX_SUBSCRIPTION_URL)) {
|
|
return result(503, { error: 'temporarily unavailable' });
|
|
}
|
|
throw new Error(`Unexpected URL: ${payload.url}`);
|
|
};
|
|
|
|
const quota = await CODEX_CONFIG.fetchQuota(
|
|
{
|
|
name: 'codex.json',
|
|
type: 'codex',
|
|
auth_index: 'codex:2',
|
|
chatgpt_account_id: 'account-2',
|
|
chatgpt_subscription_active_until: '2026-09-03T13:27:01Z',
|
|
},
|
|
t
|
|
);
|
|
|
|
expect(quota.subscriptionActiveUntil).toBe('2026-09-03T13:27:01Z');
|
|
});
|
|
});
|