// Unit tests for the optional LLM classifier's pure logic (run with `node --test`). Zero-dep. import { test } from "node:test"; import assert from "node:assert/strict"; import { buildClassifyMessages, parseClassification, DEFAULT_MODEL } from "./llm.js"; test("DEFAULT_MODEL is a small ollama model", () => { assert.equal(DEFAULT_MODEL, "qwen2.5:3b"); }); test("buildClassifyMessages embeds the candidate list as JSON for the user turn", () => { const msgs = buildClassifyMessages(["Tool", "the best"]); assert.equal(msgs.length, 2); assert.equal(msgs[0].role, "system"); assert.match(msgs[0].content, /artist/i); assert.equal(msgs[1].role, "user"); assert.deepEqual(JSON.parse(msgs[1].content), { candidates: ["Tool", "the best"] }); }); test("parseClassification keeps only artist/album candidates that were in the list", () => { const candidates = ["Cattle Decapitation", "Devourment", "the best part"]; const reply = JSON.stringify({ results: [ { name: "Cattle Decapitation", kind: "artist" }, { name: "Devourment", kind: "ARTIST" }, // case-insensitive kind { name: "the best part", kind: "none" }, ], }); const got = parseClassification(reply, candidates); assert.ok(got.has("cattle decapitation")); assert.ok(got.has("devourment")); assert.ok(!got.has("the best part")); }); test("parseClassification accepts album kind and a bare array reply", () => { const got = parseClassification( JSON.stringify([{ name: "Reek of Putrefaction", kind: "album" }]), ["Reek of Putrefaction"], ); assert.ok(got.has("reek of putrefaction")); }); test("parseClassification ignores hallucinated names not in the candidate list", () => { const got = parseClassification( JSON.stringify({ results: [{ name: "Some Band I Invented", kind: "artist" }] }), ["Real Candidate"], ); assert.equal(got.size, 0); }); test("parseClassification returns an empty set on unparseable / junk replies", () => { assert.equal(parseClassification("not json at all", ["X"]).size, 0); assert.equal(parseClassification("", ["X"]).size, 0); assert.equal(parseClassification(JSON.stringify({ nope: true }), ["X"]).size, 0); assert.equal(parseClassification(JSON.stringify({ results: [null, { kind: "artist" }] }), ["X"]).size, 0); });