83 lines
2.8 KiB
TypeScript
83 lines
2.8 KiB
TypeScript
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<string, unknown>, {
|
|
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",
|
|
]);
|
|
});
|
|
});
|