import test from "node:test"; import assert from "node:assert/strict"; import fs from "node:fs"; import os from "node:os"; import path from "node:path"; const qoderCli = await import("../../open-sse/services/qoderCli.ts"); /** * Write a fake `qodercli` binary and point CLI_QODER_BIN at it (see the twin * helper in qoder-executor.test.ts). The stub authenticates unless the PAT * contains "bad", covering both `--list-models` (validation) and `--print`. */ function withStubQoderCli(fn: () => void | Promise) { const prevBin = process.env.CLI_QODER_BIN; const dir = fs.mkdtempSync(path.join(os.tmpdir(), "qodercli-stub-")); const stub = path.join(dir, "qodercli"); fs.writeFileSync( stub, [ "#!/bin/sh", 'is_bad() { case "$QODER_PERSONAL_ACCESS_TOKEN" in *bad*) return 0;; *) return 1;; esac; }', 'case "$*" in', " *--list-models*)", ' if is_bad; then echo "Not logged in · Please run /login"; exit 0; fi', ' printf "MODEL\\nAuto\\nQwen3-Coder\\n"; exit 0;;', " *--print*)", " cat >/dev/null;", ' if is_bad; then printf \'{"type":"result","subtype":"success","is_error":true,"result":"Not logged in · Please run /login"}\\n\'; exit 0; fi', ' printf \'{"type":"result","subtype":"success","is_error":false,"result":"OK from stub"}\\n\'; exit 0;;', "esac", "exit 0", ].join("\n"), { mode: 0o755 } ); process.env.CLI_QODER_BIN = stub; const restore = () => { if (prevBin === undefined) delete process.env.CLI_QODER_BIN; else process.env.CLI_QODER_BIN = prevBin; fs.rmSync(dir, { recursive: true, force: true }); }; return Promise.resolve().then(fn).finally(restore); } function withEnv( overrides: Record, fn: () => void | Promise ) { const previous = new Map(); for (const [key, value] of Object.entries(overrides)) { previous.set(key, process.env[key]); if (value === undefined || value === null) { delete process.env[key]; } else { process.env[key] = String(value); } } return Promise.resolve() .then(fn) .finally(() => { for (const [key, value] of previous.entries()) { if (value === undefined) { delete process.env[key]; } else { process.env[key] = value; } } }); } test("qoder cli env helpers honor explicit command and workspace overrides", async () => { await withEnv( { CLI_QODER_BIN: " custom-qoder ", QODER_CLI_WORKSPACE: "/tmp/qoder-workspace", OMNIROUTE_QODER_WORKSPACE: "/tmp/ignored", }, () => { assert.equal(qoderCli.getQoderCliCommand(), "custom-qoder"); assert.equal(qoderCli.getQoderCliWorkspace(), "/tmp/qoder-workspace"); } ); await withEnv( { CLI_QODER_BIN: undefined, QODER_CLI_WORKSPACE: undefined, OMNIROUTE_QODER_WORKSPACE: "/tmp/fallback-workspace", }, () => { assert.equal(qoderCli.getQoderCliCommand(), "qodercli"); assert.equal(qoderCli.getQoderCliWorkspace(), "/tmp/fallback-workspace"); } ); }); test("qoder cli provider metadata helpers normalize PAT transport and detect transport type", () => { assert.deepEqual(qoderCli.normalizeQoderPatProviderData({ region: "us" }), { region: "us", authMode: "pat", transport: "qodercli", }); assert.equal(qoderCli.isQoderCliTransport({ transport: "qodercli" }), true); assert.equal(qoderCli.isQoderCliTransport({ authMode: "pat" }), true); assert.equal(qoderCli.isQoderCliTransport({ transport: "http-legacy", authMode: "pat" }), false); assert.equal(qoderCli.isQoderCliTransport({ transport: "http" }), false); }); test("qoder cli static models are copied and model-to-level mapping covers major families", () => { const models = qoderCli.getStaticQoderModels(); const snapshot = qoderCli.getStaticQoderModels(); models[0].name = "mutated"; assert.notEqual(snapshot[0].name, "mutated"); assert.equal(qoderCli.mapQoderModelToLevel("deepseek-r1"), "ultimate"); assert.equal(qoderCli.mapQoderModelToLevel("qwen3-max-preview"), "performance"); assert.equal(qoderCli.mapQoderModelToLevel("kimi-k2-0905"), "kmodel"); assert.equal(qoderCli.mapQoderModelToLevel("qwen3-coder-plus"), "qmodel"); assert.equal(qoderCli.mapQoderModelToLevel("qoder-rome-30ba3b"), "qmodel"); assert.equal(qoderCli.mapQoderModelToLevel("glm-5.2"), "gm51model"); assert.equal(qoderCli.mapQoderModelToLevel("minimax-m3"), "mmodel"); assert.equal(qoderCli.mapQoderModelToLevel("gm51model"), "gm51model"); assert.equal(qoderCli.mapQoderModelToLevel("totally-unknown"), "auto"); assert.equal(qoderCli.mapQoderModelToLevel(""), null); }); test("buildQoderPrompt flattens mixed content, tool calls, tool results and JSON output instructions", () => { const prompt = qoderCli.buildQoderPrompt({ tools: [ { type: "function", function: { name: "lookup_weather" } }, { type: "function", function: { name: "" } }, { name: "anthropic_tool" }, ], response_format: { type: "json_schema", json_schema: { schema: { type: "object", properties: { city: { type: "string" } } } }, }, messages: [ { role: "system", content: "Top level system" }, { role: "user", content: [ { type: "text", text: "Describe this image" }, { type: "input_image", image_url: "ignored" }, ], }, { role: "assistant", content: "Thinking aloud", tool_calls: [ { function: { name: "lookup_weather", arguments: '{"city":"Sao Paulo"}', }, }, ], }, { role: "tool", name: "lookup_weather", content: [{ type: "text", text: "26C and sunny" }], }, ], }); assert.match( prompt, /Caller-side tools are available externally: lookup_weather, anthropic_tool/ ); assert.match(prompt, /Return only valid JSON matching this schema/); assert.match(prompt, /SYSTEM:\nTop level system/); assert.match(prompt, /USER:\nDescribe this image\n\[Image omitted\]/); assert.match(prompt, /TOOL_CALL lookup_weather: \{"city":"Sao Paulo"\}/); assert.match(prompt, /TOOL \(lookup_weather\):\n26C and sunny/); assert.match(prompt, /Reply now with the assistant response only\./); }); test("buildQoderPrompt supports input arrays and json_object responses", () => { const prompt = qoderCli.buildQoderPrompt({ response_format: { type: "json_object" }, input: [{ role: "user", content: [{ type: "input_text", text: "hello from input" }] }], }); assert.match(prompt, /Return only valid JSON\./); assert.match(prompt, /Conversation transcript:/); assert.match(prompt, /USER:\nhello from input/); }); test("qoder cli payload helpers normalize envelope text and completion payload shapes", () => { assert.equal( qoderCli.extractTextFromQoderEnvelope({ message: { content: "hello" }, }), "hello" ); assert.equal( qoderCli.extractTextFromQoderEnvelope({ content: [ { type: "text", text: "hi" }, { type: "ignored", text: "drop" }, { text: " there" }, ], }), "hi there" ); assert.equal(qoderCli.extractTextFromQoderEnvelope(null), ""); const completion = qoderCli.buildQoderCompletionPayload({ model: "qwen3-coder-plus", text: "Ship it", }); assert.equal(completion.object, "chat.completion"); assert.equal(completion.model, "qwen3-coder-plus"); assert.equal(completion.choices[0].message.content, "Ship it"); const chunk = qoderCli.buildQoderChunk({ id: "chunk-1", model: "qoder-rome-30ba3b", created: 123, delta: { content: "partial" }, finishReason: "stop", }); assert.deepEqual(chunk, { id: "chunk-1", object: "chat.completion.chunk", created: 123, model: "qoder-rome-30ba3b", choices: [ { index: 0, delta: { content: "partial" }, finish_reason: "stop", }, ], }); }); test("qoder cli failure parsing classifies auth, timeout and generic upstream errors", async () => { assert.deepEqual(qoderCli.parseQoderCliFailure("Invalid API key"), { status: 401, message: "Invalid API key", code: "upstream_auth_error", }); assert.deepEqual(qoderCli.parseQoderCliFailure("", "request timeout"), { status: 504, message: "request timeout", code: "timeout", }); assert.deepEqual(qoderCli.parseQoderCliFailure("bad gateway", "more context"), { status: 502, message: "bad gateway\nmore context", code: "upstream_error", }); const authResponse = qoderCli.createQoderErrorResponse({ status: 401, message: "denied", code: "upstream_auth_error", }); const providerResponse = qoderCli.createQoderErrorResponse({ status: 502, message: "boom", code: "upstream_error", }); assert.equal(authResponse.status, 401); assert.deepEqual(await authResponse.json(), { error: { message: "denied", type: "authentication_error", code: "upstream_auth_error", }, }); assert.equal(providerResponse.status, 502); assert.deepEqual(await providerResponse.json(), { error: { message: "boom", type: "provider_error", code: "upstream_error", }, }); }); test("validateQoderCliPat returns valid when qodercli lists models for the PAT", async () => { await withStubQoderCli(async () => { const result = await qoderCli.validateQoderCliPat({ apiKey: "pt-valid-token" }); assert.deepEqual(result, { valid: true, error: null, unsupported: false }); }); }); test("validateQoderCliPat rejects a PAT that qodercli reports as not logged in", async () => { await withStubQoderCli(async () => { const result = await qoderCli.validateQoderCliPat({ apiKey: "pt-bad-token" }); assert.equal(result.valid, false); assert.match(result.error!, /not authorized|integrations/i); }); }); test("validateQoderCliPat rejects an encrypted auth blob without spawning", async () => { const blobToken = "x".repeat(600); const result = await qoderCli.validateQoderCliPat({ apiKey: blobToken }); assert.equal(result.valid, false); assert.match(result.error!, /encrypted auth blob/i); }); test("validateQoderCliPat requires a token", async () => { const result = await qoderCli.validateQoderCliPat({ apiKey: "" }); assert.equal(result.valid, false); assert.match(result.error!, /No Qoder token/i); }); test("validateQoderCliPat surfaces a clear error when qodercli is missing", async () => { const prevBin = process.env.CLI_QODER_BIN; process.env.CLI_QODER_BIN = "/nonexistent/qodercli-please-fail"; try { const result = await qoderCli.validateQoderCliPat({ apiKey: "pt-valid-token" }); assert.equal(result.valid, false); assert.match(result.error!, /qodercli|CLI_QODER_BIN|not found/i); } finally { if (prevBin === undefined) delete process.env.CLI_QODER_BIN; else process.env.CLI_QODER_BIN = prevBin; } }); test("runQoderCli drives the stub binary and returns its JSON envelope", async () => { await withStubQoderCli(async () => { const run = await qoderCli.runQoderCli({ token: "pt-valid-token", prompt: "hello", stream: false, model: "qwen3-coder-plus", }); assert.equal(run.ok, true); const parsed = qoderCli.parseQoderCliResult(run.stdout); assert.equal(parsed.isError, false); assert.equal(parsed.text, "OK from stub"); }); }); test("parseQoderCliResult extracts text, flags errors and tolerates banner noise", () => { assert.deepEqual( qoderCli.parseQoderCliResult('{"type":"result","is_error":false,"result":"pong"}'), { text: "pong", isError: false, errorMessage: "" } ); const errored = qoderCli.parseQoderCliResult( '{"type":"result","is_error":true,"result":"Not logged in"}' ); assert.equal(errored.isError, true); assert.equal(errored.errorMessage, "Not logged in"); // Leading banner/log lines before the JSON envelope must still parse. const noisy = qoderCli.parseQoderCliResult( 'starting qodercli...\nwarming up\n{"type":"result","is_error":false,"result":"hi"}' ); assert.equal(noisy.text, "hi"); assert.equal(noisy.isError, false); const empty = qoderCli.parseQoderCliResult(" "); assert.equal(empty.isError, true); }); test("parseQoderCliFailure classifies qodercli auth output as 401", () => { for (const msg of [ "Not logged in · Please run /login", "Failed to fetch model list: auth.exchangeJobToken failed: invalid personal token format", ]) { const failure = qoderCli.parseQoderCliFailure(msg); assert.equal(failure.status, 401, msg); assert.equal(failure.code, "upstream_auth_error", msg); } }); test("runQoderCli survives qodercli exiting before it reads a large stdin (async EPIPE)", async () => { const prevBin = process.env.CLI_QODER_BIN; const dir = fs.mkdtempSync(path.join(os.tmpdir(), "qodercli-stub-")); const stub = path.join(dir, "qodercli"); // Exits immediately WITHOUT reading stdin. Writing a >pipe-buffer prompt then // races an async EPIPE/EINVAL on the closed stdin; without a stream 'error' // listener that crashes the whole process (gemini-code-assist review). fs.writeFileSync(stub, "#!/bin/sh\nexit 0\n", { mode: 0o755 }); process.env.CLI_QODER_BIN = stub; try { const run = await qoderCli.runQoderCli({ token: "pt-x", prompt: "x".repeat(1_000_000), stream: false, model: "auto", }); // The assertion is simply that we get here — a resolved result, no crash. assert.equal(typeof run.ok, "boolean"); } finally { if (prevBin === undefined) delete process.env.CLI_QODER_BIN; else process.env.CLI_QODER_BIN = prevBin; fs.rmSync(dir, { recursive: true, force: true }); } }); test("runQoderCli preserves multi-byte UTF-8 output (Chinese) via stream setEncoding", async () => { const prevBin = process.env.CLI_QODER_BIN; const dir = fs.mkdtempSync(path.join(os.tmpdir(), "qodercli-stub-")); const stub = path.join(dir, "qodercli"); // qodercli commonly returns Chinese; stream chunk boundaries must not corrupt it. fs.writeFileSync( stub, '#!/bin/sh\ncat >/dev/null\nprintf \'{"type":"result","is_error":false,"result":"你好世界,测试"}\\n\'\nexit 0\n', { mode: 0o755 } ); process.env.CLI_QODER_BIN = stub; try { const run = await qoderCli.runQoderCli({ token: "pt-x", prompt: "hi", stream: false, model: "auto", }); assert.equal(run.ok, true); assert.equal(qoderCli.parseQoderCliResult(run.stdout).text, "你好世界,测试"); } finally { if (prevBin === undefined) delete process.env.CLI_QODER_BIN; else process.env.CLI_QODER_BIN = prevBin; fs.rmSync(dir, { recursive: true, force: true }); } }); test("parseQoderCliModelNames extracts display names, dropping header/noise", () => { const names = qoderCli.parseQoderCliModelNames( "MODEL\nAuto\nGLM-5.2\nKimi-K2.7-Code\n\nDeepSeek-V4-Pro\n" ); assert.deepEqual(names, ["Auto", "GLM-5.2", "Kimi-K2.7-Code", "DeepSeek-V4-Pro"]); // Auth/error lines must not be mistaken for model names. assert.deepEqual(qoderCli.parseQoderCliModelNames("Not logged in · Please run /login"), []); }); test("resolveQoderModelName prefers a live display name, then static, then Auto", () => { const live = ["Auto", "GLM-5.2", "Kimi-K2.7-Code"]; // punctuation/case-insensitive match against the live list assert.equal(qoderCli.resolveQoderModelName("glm-5.2", live), "GLM-5.2"); assert.equal(qoderCli.resolveQoderModelName("GLM-5.2", live), "GLM-5.2"); assert.equal(qoderCli.resolveQoderModelName("kimi-k2.7-code", live), "Kimi-K2.7-Code"); // not in the live list → static family map (level key) assert.equal(qoderCli.resolveQoderModelName("qwen3-coder-plus", live), "qmodel"); // unknown → Auto; empty → Auto assert.equal(qoderCli.resolveQoderModelName("totally-unknown", live), "auto"); assert.equal(qoderCli.resolveQoderModelName("", live), "auto"); // no live list at all → falls back to the static map assert.equal(qoderCli.resolveQoderModelName("glm-5.2", []), "gm51model"); }); test("runQoderCli resolves the request against live --list-models and passes the display name to -m", async () => { qoderCli.__clearQoderModelNamesCache(); const prevBin = process.env.CLI_QODER_BIN; const dir = fs.mkdtempSync(path.join(os.tmpdir(), "qodercli-stub-")); const stub = path.join(dir, "qodercli"); // --list-models → a live catalog; --print → echo back the -m value it received. fs.writeFileSync( stub, [ "#!/bin/sh", 'case "$*" in', ' *--list-models*) printf "MODEL\\nAuto\\nGLM-5.2\\nKimi-K2.7-Code\\n"; exit 0;;', "esac", 'model=""', 'while [ $# -gt 0 ]; do if [ "$1" = "--model" ]; then model="$2"; fi; shift; done', "cat >/dev/null", 'printf \'{"type":"result","is_error":false,"result":"%s"}\\n\' "$model"', "exit 0", ].join("\n"), { mode: 0o755 } ); process.env.CLI_QODER_BIN = stub; try { const run = await qoderCli.runQoderCli({ token: "pt-model-resolve", prompt: "hi", stream: false, model: "glm-5.2", }); assert.equal(run.ok, true); // The stub echoed the -m value → proves runQoderCli sent the resolved display name. assert.equal(qoderCli.parseQoderCliResult(run.stdout).text, "GLM-5.2"); } finally { qoderCli.__clearQoderModelNamesCache(); if (prevBin === undefined) delete process.env.CLI_QODER_BIN; else process.env.CLI_QODER_BIN = prevBin; fs.rmSync(dir, { recursive: true, force: true }); } });