import { describe, it, expect, beforeAll, beforeEach, afterEach, vi } from 'vitest'; import type { Express } from 'express'; import { createApp } from '../../app.js'; import { initDb, getDb, getUnifiedApiKey } from '../../db/index.js'; import { mintDashboardToken, isGatedApiPath } from '../helpers/auth.js'; let dashToken = ''; async function request(app: Express, method: string, path: string, body?: any, headers: Record = {}) { const server = app.listen(0); const addr = server.address() as any; const url = `http://127.0.0.1:${addr.port}${path}`; const res = await fetch(url, { method, headers: { ...(body ? { 'Content-Type': 'application/json' } : {}), ...(isGatedApiPath(path) && !('Authorization' in headers) ? { Authorization: `Bearer ${dashToken}` } : {}), ...headers }, body: body ? JSON.stringify(body) : undefined, }); const data = await res.text(); server.close(); let json: any = null; try { json = JSON.parse(data); } catch {} return { status: res.status, body: json }; } function authHeaders() { return { Authorization: `Bearer ${getUnifiedApiKey()}` }; } // requested_model logging: a pinned request records the model id the client // named; an auto request records NULL. This is what lets analytics split // pinned vs auto traffic and surface failover overrides. describe('requested_model analytics logging', () => { let app: Express; let groqModelId: string; beforeAll(() => { process.env.ENCRYPTION_KEY = '0'.repeat(64); initDb(':memory:'); app = createApp(); dashToken = mintDashboardToken(); // Any enabled groq model from the seeded catalog will do as the pin target. groqModelId = (getDb().prepare(` SELECT m.model_id FROM models m JOIN fallback_config fc ON fc.model_db_id = m.id WHERE m.platform = 'groq' AND m.enabled = 1 ORDER BY fc.priority LIMIT 1 `).get() as { model_id: string }).model_id; }); beforeEach(async () => { const db = getDb(); db.prepare('DELETE FROM api_keys').run(); db.prepare('DELETE FROM requests').run(); const addKey = await request(app, 'POST', '/api/keys', { platform: 'groq', key: 'gsk_pinned_model_test', label: 'pinned-model', }); expect(addKey.status).toBe(201); const origFetch = global.fetch; vi.spyOn(global, 'fetch').mockImplementation(async (url, init) => { const urlStr = typeof url === 'string' ? url : url.toString(); if (urlStr.includes('api.groq.com')) { return { ok: true, json: () => Promise.resolve({ id: 'chatcmpl-pin', object: 'chat.completion', created: 1, model: groqModelId, choices: [{ index: 0, message: { role: 'assistant', content: 'hi' }, finish_reason: 'stop' }], usage: { prompt_tokens: 2, completion_tokens: 1, total_tokens: 3 }, }), } as any; } return origFetch(url, init); }); }); afterEach(() => { vi.restoreAllMocks(); }); it('logs the pinned model id when the client names a model', async () => { const { status } = await request(app, 'POST', '/v1/chat/completions', { model: groqModelId, messages: [{ role: 'user', content: 'hi' }], }, authHeaders()); expect(status).toBe(200); const row = getDb().prepare('SELECT model_id, requested_model FROM requests ORDER BY id DESC LIMIT 1').get() as any; expect(row.requested_model).toBe(groqModelId); expect(row.model_id).toBe(groqModelId); // pin honored }); it.each([['auto'], [undefined]])('logs NULL requested_model for auto routing (model: %s)', async (model) => { const { status } = await request(app, 'POST', '/v1/chat/completions', { ...(model ? { model } : {}), messages: [{ role: 'user', content: 'hi' }], }, authHeaders()); expect(status).toBe(200); const row = getDb().prepare('SELECT requested_model FROM requests ORDER BY id DESC LIMIT 1').get() as any; expect(row.requested_model).toBeNull(); }); });