import { describe, expect, test } from "vitest"; import { finishSuccessfulCliCommand } from "../src/cli/qmd.ts"; import { LlamaCpp } from "../src/llm.ts"; describe("CLI successful-exit lifecycle", () => { test("exits 0 after successful JSON output when post-output LLM cleanup fails", async () => { const exitCodes: number[] = []; const stderr: string[] = []; const flushed: string[] = []; await finishSuccessfulCliCommand({ command: "query", format: "json", platform: "linux", cleanup: async () => { throw new Error("ggml_metal_device_free abort simulation"); }, exit: (code) => { exitCodes.push(code); }, stdout: { write: (chunk: string | Uint8Array, cb?: (error?: Error | null) => void) => { flushed.push(String(chunk)); cb?.(); return true; } }, stderr: { write: (chunk: string | Uint8Array, cb?: (error?: Error | null) => void) => { stderr.push(String(chunk)); cb?.(); return true; } }, }); expect(exitCodes).toEqual([0]); expect(stderr.join("")).toContain("QMD Warning: cleanup after successful output failed"); expect(flushed).toEqual([""]); }); test("uses immediate exit for successful macOS JSON query after stdout flush", async () => { const calls: string[] = []; await finishSuccessfulCliCommand({ command: "query", format: "json", platform: "darwin", cleanup: async () => { calls.push("cleanup"); }, exit: (code) => { calls.push(`exit:${code}`); }, immediateExit: (code) => { calls.push(`immediate-exit:${code}`); }, stdout: { write: (_chunk: string | Uint8Array, cb?: (error?: Error | null) => void) => { calls.push("stdout-flush"); cb?.(); return true; } }, stderr: { write: (_chunk: string | Uint8Array, cb?: (error?: Error | null) => void) => { calls.push("stderr-flush"); cb?.(); return true; } }, }); expect(calls).toEqual(["stdout-flush", "stderr-flush", "immediate-exit:0"]); }); test("disposes Llama resources in dependency order before CLI exit", async () => { const calls: string[] = []; const llm = new LlamaCpp({ inactivityTimeoutMs: 0 }); const disposable = (name: string) => ({ dispose: async () => { calls.push(name); }, }); Object.assign(llm as unknown as Record, { embedContexts: [disposable("embed-context")], rerankContexts: [disposable("rerank-context")], embedModel: disposable("embed-model"), generateModel: disposable("generate-model"), rerankModel: disposable("rerank-model"), llama: disposable("llama"), }); await llm.dispose(); expect(calls).toEqual([ "embed-context", "rerank-context", "embed-model", "generate-model", "rerank-model", "llama", ]); }); });