feat: switch OpenCode Go usage to API
OpenCode Go now reads quota usage with a bearer API key from OpenCode auth.json Removes the old workspace ID and browser cookie credential flow Deletes legacy OpenCode Go credential files during upgrade
This commit is contained in:
@@ -1,17 +1,62 @@
|
||||
import { describe, expect, it } from 'bun:test';
|
||||
import { fetchOpenCodeGoUsage, parseOpenCodeGoUsage } from './opencode-go.js';
|
||||
import fs from 'node:fs';
|
||||
import os from 'node:os';
|
||||
import path from 'node:path';
|
||||
import { afterAll, afterEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
const previousDataDirectory = process.env.OPENCHAMBER_DATA_DIR;
|
||||
const temporaryDataDirectory = fs.mkdtempSync(path.join(os.tmpdir(), 'openchamber-opencode-go-'));
|
||||
process.env.OPENCHAMBER_DATA_DIR = temporaryDataDirectory;
|
||||
|
||||
vi.mock('../../opencode/auth.js', () => ({
|
||||
readAuthFile: () => ({ 'opencode-go': { key: 'test-key' } }),
|
||||
}));
|
||||
|
||||
import { fetchOpenCodeGoUsage, fetchQuota, parseOpenCodeGoUsage } from './opencode-go.js';
|
||||
|
||||
afterEach(() => {
|
||||
vi.unstubAllGlobals();
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
if (previousDataDirectory === undefined) delete process.env.OPENCHAMBER_DATA_DIR;
|
||||
else process.env.OPENCHAMBER_DATA_DIR = previousDataDirectory;
|
||||
fs.rmSync(temporaryDataDirectory, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
describe('OpenCode Go quota provider', () => {
|
||||
it('parses partial SSR usage windows in either field order', () => {
|
||||
const windows = parseOpenCodeGoUsage('rollingUsage:$R[1]={usagePercent:25,resetInSec:60} weeklyUsage:$R[2]={resetInSec:120,usagePercent:40}', 1_000);
|
||||
it('parses partial API usage windows', () => {
|
||||
const windows = parseOpenCodeGoUsage({ usage: { rolling: { percent: 25, resetsAt: '2026-08-12T12:00:00.000Z' }, weekly: { percent: 40, resetsAt: '2026-08-19T12:00:00.000Z' } } });
|
||||
expect(windows['5h'].usedPercent).toBe(25);
|
||||
expect(windows['5h'].resetAt).toBe(61_000);
|
||||
expect(windows['5h'].resetAt).toBe('2026-08-12T12:00:00.000Z');
|
||||
expect(windows.weekly.usedPercent).toBe(40);
|
||||
expect(windows.monthly).toBeUndefined();
|
||||
});
|
||||
|
||||
it('does not expose credentials in authentication errors', async () => {
|
||||
const credential = { workspaceId: 'wrk_test', authCookie: 'secret' };
|
||||
await expect(fetchOpenCodeGoUsage(credential, async () => new Response('', { status: 403 }))).rejects.toThrow('authentication failed');
|
||||
await expect(fetchOpenCodeGoUsage('secret', async () => new Response('', { status: 403 }))).rejects.toThrow('authentication failed');
|
||||
});
|
||||
|
||||
it('uses the Go usage API with bearer authentication', async () => {
|
||||
let request;
|
||||
const usage = await fetchOpenCodeGoUsage('secret', async (url, options) => {
|
||||
request = { url, options };
|
||||
return new Response(JSON.stringify({ usage: { rolling: { percent: 25, resetsAt: '2026-08-12T12:00:00.000Z' } } }));
|
||||
});
|
||||
expect(request.url).toBe('https://opencode.ai/zen/go/v1/usage');
|
||||
expect(request.options.headers).toMatchObject({ Accept: 'application/json', Authorization: 'Bearer secret' });
|
||||
expect(request.options.headers.Cookie).toBeUndefined();
|
||||
expect(usage['5h'].usedPercent).toBe(25);
|
||||
});
|
||||
|
||||
it('reads the API key from the OpenCode auth file', async () => {
|
||||
const legacyPath = path.join(temporaryDataDirectory, 'quota', 'opencode-go.json');
|
||||
fs.mkdirSync(path.dirname(legacyPath), { recursive: true });
|
||||
fs.writeFileSync(legacyPath, '{not valid json', { mode: 0o600 });
|
||||
const fetchMock = vi.fn().mockResolvedValue(new Response(JSON.stringify({ usage: { rolling: { percent: 25, resetsAt: '2026-08-12T12:00:00.000Z' } } })));
|
||||
vi.stubGlobal('fetch', fetchMock);
|
||||
const result = await fetchQuota();
|
||||
expect(result).toMatchObject({ providerId: 'opencode-go', ok: true, configured: true });
|
||||
expect(fetchMock.mock.calls[0][1].headers.Authorization).toBe('Bearer test-key');
|
||||
expect(fs.existsSync(legacyPath)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user