import { afterEach, describe, expect, it, mock } from 'bun:test'; import { ensureManagedModelPrefixes } from '../../../src/cliproxy/managed-model-prefixes'; const originalFetch = global.fetch; interface MockAuthFileRecord { account_type?: string; name: string; provider?: string; type?: string; } interface DownloadResponse { body: string; status?: number; } function jsonResponse(body: unknown, status = 200): Response { return new Response(JSON.stringify(body), { status, headers: { 'Content-Type': 'application/json' }, }); } function textResponse(body: string, status = 200): Response { return new Response(body, { status, headers: { 'Content-Type': 'application/json' }, }); } function installFetchMock(options: { files: MockAuthFileRecord[]; downloads?: Record; patchStatuses?: Record; }) { const requests: Array<{ url: string; method: string; body: string | undefined }> = []; global.fetch = mock((input: string | URL | Request, init?: RequestInit) => { const url = typeof input === 'string' ? input : input instanceof URL ? input.toString() : input.url; const method = init?.method ?? 'GET'; const body = typeof init?.body === 'string' ? init.body : undefined; requests.push({ url, method, body }); if (url.endsWith('/v0/management/auth-files') && method === 'GET') { return Promise.resolve(jsonResponse({ files: options.files })); } if (url.includes('/v0/management/auth-files/download') && method === 'GET') { const name = new URL(url).searchParams.get('name'); if (!name) { return Promise.reject(new Error(`Missing auth file name for ${url}`)); } const response = options.downloads?.[name]; if (response instanceof Error) { return Promise.reject(response); } if (!response) { return Promise.resolve(textResponse('{}', 404)); } return Promise.resolve(textResponse(response.body, response.status ?? 200)); } if (url.endsWith('/v0/management/auth-files/fields') && method === 'PATCH') { const payload = JSON.parse(body ?? '{}') as { name?: string }; const status = (payload.name && options.patchStatuses?.[payload.name]) ?? 200; return Promise.resolve(jsonResponse({ ok: status < 400 }, status)); } return Promise.reject(new Error(`Unexpected fetch ${method} ${url}`)); }) as typeof fetch; return requests; } afterEach(() => { global.fetch = originalFetch; }); describe('ensureManagedModelPrefixes', () => { it('patches missing managed prefixes for matching oauth providers', async () => { const requests = installFetchMock({ files: [ { account_type: 'oauth', name: 'gemini-main', provider: 'gemini' }, { account_type: 'oauth', name: 'agy-main', provider: 'antigravity' }, { account_type: 'apikey', name: 'gemini-key', provider: 'gemini' }, ], downloads: { 'gemini-main': { body: JSON.stringify({ prefix: null, provider: 'gemini' }) }, 'agy-main': { body: JSON.stringify({ prefix: null, provider: 'antigravity' }) }, }, }); const result = await ensureManagedModelPrefixes(['gemini']); expect(result).toEqual({ checked: 1, updated: 1 }); const patchRequest = requests.find( (request) => request.url.endsWith('/v0/management/auth-files/fields') && request.method === 'PATCH' ); expect(patchRequest?.body).toBe(JSON.stringify({ name: 'gemini-main', prefix: 'gcli' })); const downloadedNames = requests .filter((request) => request.url.includes('/v0/management/auth-files/download')) .map((request) => new URL(request.url).searchParams.get('name')); expect(downloadedNames).toEqual(['gemini-main']); }); it('returns immediately when called for providers without managed prefixes', async () => { const fetchMock = mock(() => Promise.reject(new Error('should not fetch'))); global.fetch = fetchMock as typeof fetch; const result = await ensureManagedModelPrefixes(['codex']); expect(result).toEqual({ checked: 0, updated: 0 }); expect(fetchMock).not.toHaveBeenCalled(); }); it('skips files that already have the managed prefix or a different custom prefix', async () => { const requests = installFetchMock({ files: [ { account_type: 'oauth', name: 'gemini-managed', provider: 'gemini' }, { account_type: 'oauth', name: 'gemini-custom', provider: 'gemini' }, ], downloads: { 'gemini-managed': { body: JSON.stringify({ prefix: 'gcli', provider: 'gemini' }) }, 'gemini-custom': { body: JSON.stringify({ prefix: 'team-a', provider: 'gemini' }) }, }, }); const result = await ensureManagedModelPrefixes(['gemini']); expect(result).toEqual({ checked: 2, updated: 0 }); expect( requests.some( (request) => request.url.endsWith('/v0/management/auth-files/fields') && request.method === 'PATCH' ) ).toBe(false); }); it('skips patching when the downloaded auth file belongs to a different provider', async () => { const requests = installFetchMock({ files: [{ account_type: 'oauth', name: 'gemini-shadowed', provider: 'gemini' }], downloads: { 'gemini-shadowed': { body: JSON.stringify({ prefix: null, provider: 'antigravity' }), }, }, }); const result = await ensureManagedModelPrefixes(['gemini']); expect(result).toEqual({ checked: 1, updated: 0 }); expect( requests.some( (request) => request.url.endsWith('/v0/management/auth-files/fields') && request.method === 'PATCH' ) ).toBe(false); }); it('swallows read and patch failures so later files can still be repaired', async () => { const requests = installFetchMock({ files: [ { account_type: 'oauth', name: 'gemini-unreadable', provider: 'gemini' }, { account_type: 'oauth', name: 'gemini-patch-fails', provider: 'gemini' }, { account_type: 'oauth', name: 'gemini-success', provider: 'gemini' }, ], downloads: { 'gemini-unreadable': new Error('network down'), 'gemini-patch-fails': { body: JSON.stringify({ prefix: null, provider: 'gemini' }) }, 'gemini-success': { body: JSON.stringify({ prefix: null, provider: 'gemini' }) }, }, patchStatuses: { 'gemini-patch-fails': 500, }, }); const result = await ensureManagedModelPrefixes(['gemini']); expect(result).toEqual({ checked: 3, updated: 1 }); const patchPayloads = requests .filter( (request) => request.url.endsWith('/v0/management/auth-files/fields') && request.method === 'PATCH' ) .map((request) => request.body); expect(patchPayloads).toEqual([ JSON.stringify({ name: 'gemini-patch-fails', prefix: 'gcli' }), JSON.stringify({ name: 'gemini-success', prefix: 'gcli' }), ]); }); });