import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; import { OpenAIClient } from './openai.js'; vi.mock('../auth/openai.js', () => ({ ensureValidOpenAIAuth: vi.fn(async () => ({ access_token: 'at', refresh_token: 'rt', expires_at: Date.now() + 60_000, created_at: new Date().toISOString(), account_id: 'acct', })), })); function makeSse(events: Array<{ event: string; data: unknown }>): string { return events .map((e) => `event: ${e.event}\ndata: ${JSON.stringify(e.data)}\n\n`) .join(''); } describe('OpenAIClient OAuth (Codex)', () => { const originalFetch = globalThis.fetch; beforeEach(() => { vi.useFakeTimers(); }); afterEach(() => { globalThis.fetch = originalFetch; vi.useRealTimers(); vi.restoreAllMocks(); }); it('streams SSE and accumulates output_text.delta', async () => { const sse = makeSse([ { event: 'response.created', data: { type: 'response.created', response: { id: 'r1' } } }, { event: 'response.output_text.delta', data: { type: 'response.output_text.delta', delta: 'hel' } }, { event: 'response.output_text.delta', data: { type: 'response.output_text.delta', delta: 'lo' } }, { event: 'response.completed', data: { type: 'response.completed', response: { usage: { input_tokens: 2, output_tokens: 2 } } } }, ]); globalThis.fetch = vi.fn(async (_url: string | URL | Request, init?: RequestInit) => { const body = typeof init?.body === 'string' ? init.body : ''; if (!body) { throw new Error('Expected JSON body'); } const parsed = JSON.parse(body) as Record; expect(parsed.store).toBe(false); expect(parsed.stream).toBe(true); expect(typeof parsed.instructions).toBe('string'); expect(Array.isArray(parsed.input)).toBe(true); const stream = new ReadableStream({ start(controller) { controller.enqueue(new TextEncoder().encode(sse)); controller.close(); }, }); return new Response(stream, { status: 200 }); }) as typeof fetch; const client = new OpenAIClient({ model: 'gpt-5.3-codex', useOAuth: true }); const resp = await client.chat({ system: 'You are helpful.', messages: [{ role: 'user', content: 'hi' }], }); expect(resp.content).toBe('hello'); expect(resp.usage).toEqual({ inputTokens: 2, outputTokens: 2 }); }); });