Files
openchamber/packages/web/server/lib/quota/providers/crof.test.js
T

99 lines
2.8 KiB
JavaScript

import { afterEach, describe, expect, it, vi } from 'vitest';
vi.mock('../../opencode/auth.js', () => ({
readAuthFile: () => ({ crof: { key: 'test-token' } }),
}));
import { fetchQuota } from './crof.js';
afterEach(() => {
vi.unstubAllGlobals();
});
const mockResponse = (body, init = {}) => ({
ok: true,
status: 200,
json: async () => body,
...init,
});
describe('Crof quota provider', () => {
it('reports credits balance as valueLabel with null percent', async () => {
// Documented /usage_api/ response from https://crof.ai/docs.md
vi.stubGlobal('fetch', vi.fn().mockResolvedValue(
mockResponse({ usable_requests: 450, credits: 12.3456 }),
));
const result = await fetchQuota();
expect(result.ok).toBe(true);
expect(result.providerId).toBe('crof');
expect(result.usage.windows.credits.usedPercent).toBeNull();
expect(result.usage.windows.credits.valueLabel).toBe('$12.35');
expect(result.usage.windows.credits.windowSeconds).toBeNull();
expect(result.usage.windows.credits.resetAt).toBeNull();
});
it('tolerates missing credits field', async () => {
vi.stubGlobal('fetch', vi.fn().mockResolvedValue(
mockResponse({ usable_requests: 0 }),
));
const result = await fetchQuota();
expect(result.ok).toBe(true);
expect(result.usage.windows.credits.valueLabel).toBeUndefined();
expect(result.usage.windows.credits.usedPercent).toBeNull();
});
it('parses numeric-string credits', async () => {
vi.stubGlobal('fetch', vi.fn().mockResolvedValue(
mockResponse({ credits: '99.5' }),
));
const result = await fetchQuota();
expect(result.usage.windows.credits.valueLabel).toBe('$99.50');
});
it('maps 401 to session-expired error', async () => {
vi.stubGlobal('fetch', vi.fn().mockResolvedValue({
ok: false,
status: 401,
json: async () => ({}),
}));
const result = await fetchQuota();
expect(result.ok).toBe(false);
expect(result.configured).toBe(true);
expect(result.error).toBe('Session expired — please re-authenticate with CrofAI');
});
it('surfaces non-401 API errors with status', async () => {
vi.stubGlobal('fetch', vi.fn().mockResolvedValue({
ok: false,
status: 503,
json: async () => ({}),
}));
const result = await fetchQuota();
expect(result.ok).toBe(false);
expect(result.error).toBe('API error: 503');
});
it('reports invalid-response on JSON parse failure', async () => {
vi.stubGlobal('fetch', vi.fn().mockResolvedValue({
ok: true,
status: 200,
json: async () => { throw new SyntaxError('Unexpected token'); },
}));
const result = await fetchQuota();
expect(result.ok).toBe(false);
expect(result.error).toBe('Invalid response from provider');
});
});