import test from "node:test"; import assert from "node:assert/strict"; const { proposeCandidates, extractJson, AnalystError } = await import("../lib/analyst.ts"); const cfg = { baseUrl: "https://api.test/v1", apiKey: "k", model: "m", extra: {} }; const digest = { sql: "SELECT 1", bytes: 8, subtreeCost: 1, parallel: false, nonParallelReason: null, earlyAbort: null, tables: ["t"], topOperators: [], rowGuessErrors: [], missingIndexes: [], warnings: [], waits: [] } as any; const ok = (content: string) => ({ ok: true, status: 200, json: async () => ({ choices: [{ message: { content } }] }) }); const CANDS = { candidates: [ { key: "idx_customer", title: "Covering index", diagnosis: "Scan of 1.2M rows on Sales.Orders, no key on CustomerId.", actions: [{ title: "Create index", detail: "CREATE INDEX ...", effort: "medium", requires_change_control: true }], rewrite_sql: null, expected: "seek instead of scan" }, { key: "rewrite_split", title: "Split the statement", diagnosis: "One statement scans everything then filters.", actions: [{ title: "Deploy rewrite", detail: "Two statements", effort: "high", requires_change_control: true }], rewrite_sql: "SELECT ...", expected: "far fewer logical reads" }] }; test("returns schema-valid candidates", async () => { const got = await proposeCandidates(cfg, digest, "", async () => ok(JSON.stringify(CANDS)) as any); assert.equal(got.length, 2); assert.equal(got[0].key, "idx_customer"); }); test("survives code fences and sends extra params", async () => { let sent: any; const fetchImpl = async (_u: any, init: any) => { sent = JSON.parse(init.body); return ok("```json\n" + JSON.stringify(CANDS) + "\n```") as any; }; const got = await proposeCandidates({ ...cfg, extra: { temperature: 0.1 } }, digest, "index exists already", fetchImpl as any); assert.equal(got.length, 2); assert.equal(sent.temperature, 0.1); assert.match(sent.messages[0].content, /candidates/); assert.match(sent.messages[1].content, /index exists already/); }); test("names the stage on auth, prose, and shape failures", async () => { await assert.rejects(() => proposeCandidates(cfg, digest, "", async () => ({ ok: false, status: 401, text: async () => "bad key" }) as any), (e: any) => e instanceof AnalystError && /401/.test(e.message)); await assert.rejects(() => proposeCandidates(cfg, digest, "", async () => ok("no json here") as any), AnalystError); await assert.rejects(() => proposeCandidates(cfg, digest, "", async () => ok(JSON.stringify({ candidates: [CANDS.candidates[0]] })) as any), (e: any) => /at least 2/.test(e.message)); }); test("extractJson copes with fences and chatter", () => { assert.deepEqual(extractJson('Sure!\n```json\n{"a":1}\n```'), { a: 1 }); assert.deepEqual(extractJson('noise {"a":1} noise'), { a: 1 }); });