"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const vitest_1 = require("vitest");
const client_js_1 = require("./client.js");
// Mock undici fetch
const mockFetch = vitest_1.vi.fn();
vitest_1.vi.mock('undici', () => ({ fetch: (...args) => mockFetch(...args) }));
// Mock logger
vitest_1.vi.mock('../../utils/logger.js', () => ({
    createModuleLogger: () => ({
        debug: vitest_1.vi.fn(),
        info: vitest_1.vi.fn(),
        warn: vitest_1.vi.fn(),
        error: vitest_1.vi.fn(),
    }),
}));
// Mock config
vitest_1.vi.mock('../../utils/config.js', () => ({
    config: { openrouterApiKey: 'test-api-key' },
}));
function makeOkResponse(data) {
    return {
        ok: true,
        status: 200,
        statusText: 'OK',
        json: () => Promise.resolve(data),
        text: () => Promise.resolve(JSON.stringify(data)),
    };
}
function makeErrorResponse(status, body = 'error') {
    return {
        ok: false,
        status,
        statusText: 'Error',
        json: () => Promise.resolve({ error: body }),
        text: () => Promise.resolve(body),
    };
}
const SAMPLE_RESPONSE = {
    id: 'gen-123',
    choices: [{ message: { role: 'assistant', content: 'Hello!' }, finish_reason: 'stop' }],
    usage: { prompt_tokens: 10, completion_tokens: 5, total_tokens: 15 },
};
const SAMPLE_MESSAGES = [
    { role: 'system', content: 'You are helpful.' },
    { role: 'user', content: 'Hi' },
];
(0, vitest_1.describe)('OpenRouterClient', () => {
    let client;
    (0, vitest_1.beforeEach)(() => {
        client = new client_js_1.OpenRouterClient('test-key-123', { initialBackoffMs: 1 });
        mockFetch.mockReset();
    });
    (0, vitest_1.afterEach)(() => {
    });
    (0, vitest_1.describe)('constructor', () => {
        (0, vitest_1.it)('allows empty API key for deferred configuration', () => {
            (0, vitest_1.expect)(new client_js_1.OpenRouterClient('')).toBeInstanceOf(client_js_1.OpenRouterClient);
        });
        (0, vitest_1.it)('creates instance with valid key', () => {
            (0, vitest_1.expect)(new client_js_1.OpenRouterClient('valid-key')).toBeInstanceOf(client_js_1.OpenRouterClient);
        });
    });
    (0, vitest_1.describe)('chatCompletion', () => {
        (0, vitest_1.it)('sends correct request and returns response', async () => {
            mockFetch.mockResolvedValueOnce(makeOkResponse(SAMPLE_RESPONSE));
            const result = await client.chatCompletion(SAMPLE_MESSAGES);
            (0, vitest_1.expect)(result).toEqual(SAMPLE_RESPONSE);
            (0, vitest_1.expect)(mockFetch).toHaveBeenCalledOnce();
            const [url, options] = mockFetch.mock.calls[0];
            (0, vitest_1.expect)(url).toBe('https://openrouter.ai/api/v1/chat/completions');
            (0, vitest_1.expect)(options.method).toBe('POST');
            const headers = options.headers;
            (0, vitest_1.expect)(headers['Authorization']).toBe('Bearer test-key-123');
            (0, vitest_1.expect)(headers['Content-Type']).toBe('application/json');
            const body = JSON.parse(options.body);
            (0, vitest_1.expect)(body.model).toBe('google/gemini-3.1-flash-lite-preview');
            (0, vitest_1.expect)(body.messages).toEqual(SAMPLE_MESSAGES);
            (0, vitest_1.expect)(body.max_tokens).toBe(500);
            (0, vitest_1.expect)(body.temperature).toBeGreaterThanOrEqual(0.7);
            (0, vitest_1.expect)(body.temperature).toBeLessThanOrEqual(0.9);
        });
        (0, vitest_1.it)('uses custom options when provided', async () => {
            mockFetch.mockResolvedValueOnce(makeOkResponse(SAMPLE_RESPONSE));
            await client.chatCompletion(SAMPLE_MESSAGES, {
                temperature: 0.75,
                max_tokens: 1000,
                top_p: 0.95,
            });
            const body = JSON.parse(mockFetch.mock.calls[0][1].body);
            (0, vitest_1.expect)(body.temperature).toBe(0.75);
            (0, vitest_1.expect)(body.max_tokens).toBe(1000);
            (0, vitest_1.expect)(body.top_p).toBe(0.95);
        });
        (0, vitest_1.it)('clamps temperature to 0.7-0.9 range', async () => {
            mockFetch.mockResolvedValue(makeOkResponse(SAMPLE_RESPONSE));
            await client.chatCompletion(SAMPLE_MESSAGES, { temperature: 0.3 });
            let body = JSON.parse(mockFetch.mock.calls[0][1].body);
            (0, vitest_1.expect)(body.temperature).toBe(0.7);
            await client.chatCompletion(SAMPLE_MESSAGES, { temperature: 1.5 });
            body = JSON.parse(mockFetch.mock.calls[1][1].body);
            (0, vitest_1.expect)(body.temperature).toBe(0.9);
        });
        (0, vitest_1.it)('does not include top_p when not provided', async () => {
            mockFetch.mockResolvedValueOnce(makeOkResponse(SAMPLE_RESPONSE));
            await client.chatCompletion(SAMPLE_MESSAGES);
            const body = JSON.parse(mockFetch.mock.calls[0][1].body);
            (0, vitest_1.expect)(body).not.toHaveProperty('top_p');
        });
    });
    (0, vitest_1.describe)('chatCompletionWithVision', () => {
        (0, vitest_1.it)('sends multimodal messages correctly', async () => {
            mockFetch.mockResolvedValueOnce(makeOkResponse(SAMPLE_RESPONSE));
            const visionMessages = [
                {
                    role: 'user',
                    content: [
                        { type: 'text', text: 'What is in this image?' },
                        { type: 'image_url', image_url: { url: 'https://example.com/img.jpg' } },
                    ],
                },
            ];
            const result = await client.chatCompletionWithVision(visionMessages);
            (0, vitest_1.expect)(result).toEqual(SAMPLE_RESPONSE);
            const body = JSON.parse(mockFetch.mock.calls[0][1].body);
            (0, vitest_1.expect)(body.messages[0].content).toEqual(visionMessages[0].content);
        });
    });
    (0, vitest_1.describe)('retry logic', () => {
        (0, vitest_1.it)('retries on 500 errors with exponential backoff', async () => {
            mockFetch
                .mockResolvedValueOnce(makeErrorResponse(500))
                .mockResolvedValueOnce(makeErrorResponse(502))
                .mockResolvedValueOnce(makeOkResponse(SAMPLE_RESPONSE));
            const result = await client.chatCompletion(SAMPLE_MESSAGES);
            (0, vitest_1.expect)(result).toEqual(SAMPLE_RESPONSE);
            (0, vitest_1.expect)(mockFetch).toHaveBeenCalledTimes(3);
        });
        (0, vitest_1.it)('retries on 429 rate limit errors', async () => {
            mockFetch
                .mockResolvedValueOnce(makeErrorResponse(429, 'rate limited'))
                .mockResolvedValueOnce(makeOkResponse(SAMPLE_RESPONSE));
            const result = await client.chatCompletion(SAMPLE_MESSAGES);
            (0, vitest_1.expect)(result).toEqual(SAMPLE_RESPONSE);
            (0, vitest_1.expect)(mockFetch).toHaveBeenCalledTimes(2);
        });
        (0, vitest_1.it)('throws immediately on 4xx client errors (not 429)', async () => {
            mockFetch.mockResolvedValueOnce(makeErrorResponse(400, 'bad request'));
            await (0, vitest_1.expect)(client.chatCompletion(SAMPLE_MESSAGES)).rejects.toThrow('OpenRouter API error: 400');
            (0, vitest_1.expect)(mockFetch).toHaveBeenCalledOnce();
        });
        (0, vitest_1.it)('throws after exhausting all retries', async () => {
            mockFetch
                .mockResolvedValueOnce(makeErrorResponse(500))
                .mockResolvedValueOnce(makeErrorResponse(500))
                .mockResolvedValueOnce(makeErrorResponse(500));
            await (0, vitest_1.expect)(client.chatCompletion(SAMPLE_MESSAGES)).rejects.toThrow('OpenRouter API error: 500');
            (0, vitest_1.expect)(mockFetch).toHaveBeenCalledTimes(3);
        });
        (0, vitest_1.it)('retries on network errors', async () => {
            mockFetch
                .mockRejectedValueOnce(new Error('fetch failed'))
                .mockResolvedValueOnce(makeOkResponse(SAMPLE_RESPONSE));
            const result = await client.chatCompletion(SAMPLE_MESSAGES);
            (0, vitest_1.expect)(result).toEqual(SAMPLE_RESPONSE);
            (0, vitest_1.expect)(mockFetch).toHaveBeenCalledTimes(2);
        });
    });
});
(0, vitest_1.describe)('createOpenRouterClient', () => {
    (0, vitest_1.it)('creates client from config', async () => {
        const { createOpenRouterClient } = await import('./client.js');
        const client = createOpenRouterClient();
        (0, vitest_1.expect)(client).toBeInstanceOf(client_js_1.OpenRouterClient);
    });
});
//# sourceMappingURL=client.test.js.map