style(usage): restyle request rows and include HTTP error details
Render each request as a rounded card without changing columns or fields. Prefix upstream error messages with their HTTP status when it is not already present.
This commit is contained in:
1 parent
b1fe595f0b
commit
ce3d54acd0
11 files changed
+184
-53
No files matched your search
@@ -39,7 +39,7 @@ test('edge accounts expose exhausted, unknown, disabled and upstream-error state
|
||||
expect((await quota('antigravity-empty-quota.json')).status).toBe('error');
|
||||
expect((await quota('xai-paid-api.json')).rows[0].remainingPercent).toBeNull();
|
||||
expect((await quota('codex-disabled.json')).status).toBe('error');
|
||||
expect(await quota('claude-expired.json')).toMatchObject({ status: 'error', error: 'Mock OAuth token expired' });
|
||||
expect(await quota('claude-expired.json')).toMatchObject({ status: 'error', error: 'HTTP 401: Mock OAuth token expired' });
|
||||
});
|
||||
|
||||
test('Codex and Claude resets consume credits and refresh only that account', async () => {
|
||||
|
||||
+1
-1
@@ -84,7 +84,7 @@ describe('Devin live quota', () => {
|
||||
it('reports upstream errors and empty successful responses', async () => {
|
||||
const post = spyOn(managementApi, 'post').mockResolvedValue({ status_code: 401, body: { error: 'expired' } } as never);
|
||||
mocks.push(post);
|
||||
expect(await loadQuota(file)).toMatchObject({ status: 'error', rows: [], error: 'expired' });
|
||||
expect(await loadQuota(file)).toMatchObject({ status: 'error', rows: [], error: 'HTTP 401: expired' });
|
||||
post.mockResolvedValue({ status_code: 200, body: liveStatus({ planInfo: { planName: 'Pro' } }) } as never);
|
||||
expect(await loadQuota(file)).toMatchObject({ status: 'error', rows: [] });
|
||||
});
|
||||
|
||||
@@ -118,19 +118,28 @@ describe('v8 Management API requests', () => {
|
||||
});
|
||||
|
||||
describe('apiCallErrorMessage', () => {
|
||||
it('从对象和 JSON 字符串错误体中提取可读消息', () => {
|
||||
it('保留 HTTP 状态,并从对象和 JSON 字符串错误体中提取可读消息', () => {
|
||||
expect(apiCallErrorMessage({
|
||||
status_code: 401,
|
||||
body: { error: { message: 'token expired' } },
|
||||
})).toBe('token expired');
|
||||
body: { error: { message: 'token expired', code: 'invalid_api_key' } },
|
||||
})).toBe('HTTP 401: token expired (invalid_api_key)');
|
||||
|
||||
expect(apiCallErrorMessage({
|
||||
status_code: 401,
|
||||
body: { error: { message: 'Encountered invalidated oauth token for user, failing request', code: 401 } },
|
||||
})).toBe('HTTP 401: Encountered invalidated oauth token for user, failing request');
|
||||
|
||||
expect(apiCallErrorMessage({
|
||||
statusCode: 403,
|
||||
bodyText: JSON.stringify({ detail: 'permission denied' }),
|
||||
})).toBe('permission denied');
|
||||
})).toBe('HTTP 403: permission denied');
|
||||
});
|
||||
|
||||
it('没有错误体时回退到 HTTP 状态', () => {
|
||||
it('消息已经包含状态码时不重复,没有错误体时回退到 HTTP 状态', () => {
|
||||
expect(apiCallErrorMessage({
|
||||
status_code: 401,
|
||||
body: { error: { message: 'HTTP 401: token expired' } },
|
||||
})).toBe('HTTP 401: token expired');
|
||||
expect(apiCallErrorMessage({ status_code: 429 })).toBe('Upstream returned HTTP 429');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -140,7 +140,7 @@ describe('quota action confirmation with fully mocked IPC', () => {
|
||||
consumeError = true;
|
||||
expect(await resetCodexQuotaWithConfirmation(file, async () => true)).toBe('error');
|
||||
expect(getQuotaCacheSnapshot()[key]).toMatchObject({
|
||||
rows: previous.rows, actionResult: { action: 'reset', status: 'error', error: 'reset denied' },
|
||||
rows: previous.rows, actionResult: { action: 'reset', status: 'error', error: 'HTTP 409: reset denied' },
|
||||
});
|
||||
expect(canResetCodexQuota(file, getQuotaCacheSnapshot()[key])).toBe(true);
|
||||
expect(await resetCodexQuotaWithConfirmation(file, async () => false)).toBe('cancelled');
|
||||
@@ -154,7 +154,7 @@ describe('quota action confirmation with fully mocked IPC', () => {
|
||||
refreshError = true;
|
||||
expect(await resetCodexQuotaWithConfirmation(file, async () => true)).toBe('refresh-error');
|
||||
expect(getQuotaCacheSnapshot()[key]).toMatchObject({
|
||||
rows: previous.rows, actionResult: { action: 'reset', status: 'refresh-error', error: 'usage unavailable' },
|
||||
rows: previous.rows, actionResult: { action: 'reset', status: 'refresh-error', error: 'HTTP 503: usage unavailable' },
|
||||
});
|
||||
expect(canResetCodexQuota(file, getQuotaCacheSnapshot()[key])).toBe(true);
|
||||
expect(await resetCodexQuotaWithConfirmation(file, async () => false)).toBe('cancelled');
|
||||
|
||||
@@ -40,7 +40,7 @@ describe('quota API compatibility', () => {
|
||||
...codexFile,
|
||||
metadata: { id_token: { chatgpt_account_id: 'account-test', plan_type: 'pro', chatgpt_subscription_active_until: '2030-01-01T00:00:00Z' } },
|
||||
});
|
||||
expect(result).toMatchObject({ status: 'success', plan: 'pro', creditBalance: '12.5', creditsUnlimited: false, resetCredits: 2, resetCreditsApplicable: 0, resetCreditsError: 'credits forbidden' });
|
||||
expect(result).toMatchObject({ status: 'success', plan: 'pro', creditBalance: '12.5', creditsUnlimited: false, resetCredits: 2, resetCreditsApplicable: 0, resetCreditsError: 'HTTP 403: credits forbidden' });
|
||||
expect(result.subscriptionActiveUntil).toBe('2030-01-01T00:00:00Z');
|
||||
expect(calls).toHaveLength(2);
|
||||
for (const call of calls) {
|
||||
@@ -168,7 +168,7 @@ describe('xAI quota queries aligned with Management Center', () => {
|
||||
it('账单和回退都失败时保留原始账单错误', async () => {
|
||||
handler = (request) => request.url.includes('cli-chat-proxy')
|
||||
? { status_code: 403, body: 'billing denied' } : { status_code: 429, body: 'paid denied' };
|
||||
expect(await loadQuota(file)).toMatchObject({ status: 'error', error: 'billing denied' });
|
||||
expect(await loadQuota(file)).toMatchObject({ status: 'error', error: 'HTTP 403: billing denied' });
|
||||
expect(calls).toHaveLength(4);
|
||||
});
|
||||
|
||||
@@ -176,7 +176,7 @@ describe('xAI quota queries aligned with Management Center', () => {
|
||||
handler = (request) => request.url.endsWith('/me')
|
||||
? success({}) : { status_code: 429, body: 'chat denied' };
|
||||
expect(await loadQuota({ ...file, using_api: true, prefix: 'paid' }))
|
||||
.toMatchObject({ status: 'error', error: 'chat denied' });
|
||||
.toMatchObject({ status: 'error', error: 'HTTP 429: chat denied' });
|
||||
expect(calls).toHaveLength(2);
|
||||
});
|
||||
|
||||
@@ -269,7 +269,7 @@ describe('quota request synchronization', () => {
|
||||
const reset = consumeCodexResetCredit(codexFile);
|
||||
const refresh = loadQuota(codexFile);
|
||||
await expect(reset).rejects.toThrow('reset denied');
|
||||
expect(await refresh).toMatchObject({ status: 'error', error: 'reset denied' });
|
||||
expect(await refresh).toMatchObject({ status: 'error', error: 'HTTP 409: reset denied' });
|
||||
expect((await loadQuota(codexFile)).status).toBe('success');
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user