import { assert, expect, test } from "vite-plus/test"; import { AgentAuthError } from "#profiles"; import { collectOpenCodeOutput, openCodeArgs, openCodeError, parseOpenCodeEvents, } from "./opencode.ts"; test("builds a non-interactive auto-approved OpenCode command", () => { expect(openCodeArgs("openrouter/qwen/qwen3.8-27b", "Review this")).toStrictEqual([ "run", "--format", "json", "--auto", "--model", "openrouter/qwen/qwen3.8-27b", "Review this", ]); }); test("returns the final visible OpenCode text event", () => { expect( parseOpenCodeEvents( [ JSON.stringify({ type: "step_start", part: { type: "step-start" } }), JSON.stringify({ type: "text", part: { type: "text", text: "draft" } }), JSON.stringify({ type: "text", part: { type: "text", text: "done" } }), JSON.stringify({ type: "step_finish", part: { type: "step-finish" } }), ].join("\n"), ), ).toBe("done"); }); test("rejects a successful OpenCode run without visible text", async () => { await expect(async () => { parseOpenCodeEvents('{"type":"step_start"}'); await Promise.resolve(); }).rejects.toThrow("no text output"); }); const encode = (value: string): Uint8Array => new TextEncoder().encode(value); test("streams OpenCode output while collecting it", async () => { const stdout = new ReadableStream({ start(controller) { controller.enqueue(encode("first\n")); controller.enqueue(encode("second\n")); controller.close(); }, }); const stderr = new ReadableStream({ start(controller) { controller.enqueue(encode("warning\n")); controller.close(); }, }); const shown: string[] = []; const result = await collectOpenCodeOutput( stdout, stderr, (chunk: Uint8Array) => { shown.push(`out:${new TextDecoder().decode(chunk)}`); }, (chunk: Uint8Array) => { shown.push(`err:${new TextDecoder().decode(chunk)}`); }, ); expect(result).toStrictEqual({ stdout: "first\nsecond\n", stderr: "warning\n" }); expect(shown).toStrictEqual(["out:first\n", "err:warning\n", "out:second\n"]); }); const openRouterError = (statusCode: number, message: string): string => JSON.stringify({ type: "error", sessionID: "ses_test", error: { name: "APIError", data: { message, statusCode, isRetryable: false, responseBody: JSON.stringify({ error: { message, code: statusCode } }), }, }, }); test("reports a rejected OpenRouter key as a sign-in failure", () => { const error = openCodeError(openRouterError(401, "User not found.")); assert.instanceOf(error, AgentAuthError); expect(error.message).toBe("opencode sign-in failed"); assert.instanceOf(error.cause, Error); expect(error.cause.message).toBe("opencode APIError (API error 401): User not found."); }); test("surfaces an OpenCode error event instead of empty output", () => { expect(() => parseOpenCodeEvents(openRouterError(429, "Rate limit exceeded"))).toThrow( "API error 429", ); expect(openCodeError('{"type":"text","part":{"text":"ok"}}')).toBeNull(); });