import { describe, expect, test } from "bun:test"; import { mkdtempSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { openDb } from "../src/db"; import { settings } from "../src/db/schema"; import { encrypt, loadOrCreateKey } from "../src/lib/crypto"; import { chatJSON } from "../src/lib/llm"; function deps(fetchImpl: typeof fetch) { const dir = mkdtempSync(join(tmpdir(), "helios-")); const db = openDb(dir); const key = loadOrCreateKey(dir); return { db, key, fetchImpl }; } describe("chatJSON", () => { test("posts OpenAI-compatible request with bearer key and parses JSON content", async () => { let captured: { url: string; init: RequestInit } | null = null; const mock = (async (url: any, init: any) => { captured = { url: String(url), init }; return new Response(JSON.stringify({ choices: [{ message: { content: '{"ok":1}' } }] }), { status: 200 }); }) as typeof fetch; const d = deps(mock); await d.db.insert(settings).values({ key: "llm_key", value: encrypt(d.key, "sk-test-1234") }); const out = await chatJSON(d, { system: "sys", user: "usr" }); expect(out).toEqual({ ok: 1 }); expect(captured!.url).toBe("https://openrouter.ai/api/v1/chat/completions"); const body = JSON.parse(String(captured!.init.body)); expect(body.response_format).toEqual({ type: "json_object" }); expect(body.messages).toEqual([{ role: "system", content: "sys" }, { role: "user", content: "usr" }]); expect((captured!.init.headers as Record).authorization).toBe("Bearer sk-test-1234"); }); test("no key → no auth header (Ollama)", async () => { let headers: Record = {}; const mock = (async (_: any, init: any) => { headers = init.headers; return new Response(JSON.stringify({ choices: [{ message: { content: "```json\n{\"a\":2}\n```" } }] }), { status: 200 }); }) as typeof fetch; const out = await chatJSON(deps(mock), { system: "s", user: "u" }); expect(out).toEqual({ a: 2 }); // fenced JSON stripped expect(headers.authorization).toBeUndefined(); }); test("non-2xx throws llm_error", async () => { const mock = (async () => new Response("nope", { status: 401 })) as unknown as typeof fetch; expect(chatJSON(deps(mock), { system: "s", user: "u" })).rejects.toThrow(/llm_error/); }); });