'use strict'; import test from 'node:test'; import assert from 'node:assert/strict'; import { OpenRouterProvider } from '../src/providers/OpenRouterProvider.js'; test('OpenRouterProvider shares process-lifetime session ID by default', () => { assert.ok(OpenRouterProvider.processSessionId, 'processSessionId should exist'); assert.match( OpenRouterProvider.processSessionId, /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i, 'processSessionId should be a valid UUID', ); const provider1 = new OpenRouterProvider({ apiKey: 'test-key-1' }); const provider2 = new OpenRouterProvider({ apiKey: 'test-key-2' }); assert.equal(provider1.sessionId, OpenRouterProvider.processSessionId); assert.equal(provider2.sessionId, OpenRouterProvider.processSessionId); assert.equal(provider1.sessionId, provider2.sessionId); }); test('OpenRouterProvider allows custom sessionId override in constructor', () => { const provider = new OpenRouterProvider({ apiKey: 'test-key', sessionId: 'custom-session-123' }); assert.equal(provider.sessionId, 'custom-session-123'); }); test('chatCompletions sends session_id in JSON body and x-session-id in HTTP header', async () => { let capturedUrl = null; let capturedOptions = null; const originalFetch = globalThis.fetch; globalThis.fetch = async (url, options) => { capturedUrl = url; capturedOptions = options; return { ok: true, status: 200, body: { getReader() { let done = false; return { async read() { if (done) return { done: true, value: undefined }; done = true; const encoder = new TextEncoder(); return { done: false, value: encoder.encode('data: {"choices":[{"delta":{"content":"ok"}}]}\n\ndata: [DONE]\n\n'), }; }, }; }, }, }; }; try { const provider = new OpenRouterProvider({ apiKey: 'sk-test' }); const res = await provider.chatCompletions({ model: 'test/model', messages: [{ role: 'user', content: 'hello' }], }); assert.equal(res.content, 'ok'); assert.ok(capturedOptions, 'fetch should have been called'); // Check header assert.equal(capturedOptions.headers['x-session-id'], OpenRouterProvider.processSessionId); // Check body const parsedBody = JSON.parse(capturedOptions.body); assert.equal(parsedBody.session_id, OpenRouterProvider.processSessionId); assert.equal(parsedBody.model, 'test/model'); // Test custom sessionId override per-call await provider.chatCompletions({ model: 'test/model', messages: [{ role: 'user', content: 'hello again' }], sessionId: 'call-specific-session', }); const parsedBody2 = JSON.parse(capturedOptions.body); assert.equal(capturedOptions.headers['x-session-id'], 'call-specific-session'); assert.equal(parsedBody2.session_id, 'call-specific-session'); } finally { globalThis.fetch = originalFetch; } }); test('OpenRouterProvider resolves apiUrl from constructor option, OPENROUTER_API_URL env, or default', () => { const originalEnv = process.env.OPENROUTER_API_URL; try { delete process.env.OPENROUTER_API_URL; const defaultProvider = new OpenRouterProvider({ apiKey: 'test-key' }); assert.equal(defaultProvider.apiUrl, 'https://openrouter.ai/api/v1/chat/completions'); process.env.OPENROUTER_API_URL = 'http://127.0.0.1:8787/api/v1/chat/completions'; const envProvider = new OpenRouterProvider({ apiKey: 'test-key' }); assert.equal(envProvider.apiUrl, 'http://127.0.0.1:8787/api/v1/chat/completions'); const explicitProvider = new OpenRouterProvider({ apiKey: 'test-key', apiUrl: 'http://127.0.0.1:9999/custom', }); assert.equal(explicitProvider.apiUrl, 'http://127.0.0.1:9999/custom'); } finally { if (originalEnv !== undefined) { process.env.OPENROUTER_API_URL = originalEnv; } else { delete process.env.OPENROUTER_API_URL; } } }); test('OpenRouterProvider emits connected, firstevent, reasoning, and content status in order', async () => { const originalFetch = globalThis.fetch; const statuses = []; const chunks = [ ': OPENROUTER PROCESSING\n\n', 'data: {"choices":[{"delta":{"content":"","role":"assistant","reasoning":"Thinking..."}}]}\n\n', 'data: {"choices":[{"delta":{"content":"","role":"assistant","reasoning":" more"}}]}\n\n', 'data: {"choices":[{"delta":{"content":"Hello","role":"assistant"}}]}\n\n', 'data: {"choices":[{"delta":{"content":" world","role":"assistant"}}]}\n\n', 'data: [DONE]\n\n', ]; globalThis.fetch = async () => { return { ok: true, status: 200, body: { getReader() { let idx = 0; return { async read() { if (idx >= chunks.length) return { done: true, value: undefined }; const encoder = new TextEncoder(); const val = encoder.encode(chunks[idx++]); return { done: false, value: val }; }, }; }, }, }; }; try { const provider = new OpenRouterProvider({ apiKey: 'sk-test' }); const res = await provider.chatCompletions({ model: 'test/model', messages: [{ role: 'user', content: 'hello' }], onStatus: (s) => statuses.push(s), }); assert.equal(res.content, 'Hello world'); assert.equal(res.reasoning, 'Thinking... more'); assert.deepEqual(statuses, ['connected', 'firstevent', 'reasoning', 'content']); } finally { globalThis.fetch = originalFetch; } }); test('OpenRouterProvider emits working status on first tool call delta', async () => { const originalFetch = globalThis.fetch; const statuses = []; const chunks = [ ': OPENROUTER PROCESSING\n\n', 'data: {"choices":[{"delta":{"role":"assistant","tool_calls":[{"index":0,"id":"call_1","type":"function","function":{"name":"foo","arguments":""}}]}}]}\n\n', 'data: {"choices":[{"delta":{"role":"assistant","tool_calls":[{"index":0,"function":{"arguments":"{}"}}]}}]}\n\n', 'data: [DONE]\n\n', ]; globalThis.fetch = async () => { return { ok: true, status: 200, body: { getReader() { let idx = 0; return { async read() { if (idx >= chunks.length) return { done: true, value: undefined }; const encoder = new TextEncoder(); const val = encoder.encode(chunks[idx++]); return { done: false, value: val }; }, }; }, }, }; }; try { const provider = new OpenRouterProvider({ apiKey: 'sk-test' }); const res = await provider.chatCompletions({ model: 'test/model', messages: [{ role: 'user', content: 'hello' }], onStatus: (s) => statuses.push(s), }); assert.equal(res.tool_calls?.length, 1); assert.equal(res.tool_calls[0].function.name, 'foo'); assert.deepEqual(statuses, ['connected', 'firstevent', 'working']); } finally { globalThis.fetch = originalFetch; } });