/** * Integration tests for the real streamCommandCode core using a local mock * Command Code server. No real API key or pi runtime required. */ import assert from "node:assert/strict"; import { after, before, beforeEach, describe, it } from "node:test"; import type { AssistantMessageEvent } from "../src/core.ts"; import { collectEvents, createTestDeps, makeContext, makeModel, objectAt, startMockCommandCodeServer, type MockCommandCodeServer, } from "./helpers.ts"; let server: MockCommandCodeServer; before(async () => { server = await startMockCommandCodeServer(); }); after(async () => { await server.close(); }); beforeEach(() => { server.reset(); }); function eventTypes(events: readonly AssistantMessageEvent[]): string[] { return events.map((event) => event.type); } describe("streamCommandCode — auth", () => { it("emits a missing-key error without touching the network", async () => { const { streamCommandCode } = createTestDeps({ apiBase: server.baseUrl(), env: {}, authPaths: [], }); const stream = streamCommandCode(makeModel(), makeContext(), { apiKey: "", }); const events = await collectEvents(stream); assert.deepEqual(eventTypes(events), ["error"]); assert.equal(events[0].type, "error"); assert.equal(events[0].reason, "error"); assert.match(events[0].error.errorMessage ?? "", /No Command Code API key/); assert.equal(server.requestCount(), 0); }); it("uses options.apiKey in the Authorization header", async () => { server.mockResponse({ type: "success", events: [JSON.stringify({ type: "finish", finishReason: "stop" })], }); const { streamCommandCode } = createTestDeps({ apiBase: server.baseUrl(), env: { COMMANDCODE_API_KEY: "env-key" }, }); await collectEvents( streamCommandCode(makeModel(), makeContext(), { apiKey: "option-key" }), ); assert.equal( server.lastRequestHeaders().authorization, "Bearer option-key", ); }); }); describe("streamCommandCode — successful streams", () => { it("emits start → text events → done and accumulates usage", async () => { server.mockResponse({ type: "success", events: [ JSON.stringify({ type: "text-delta", text: "Hel" }), JSON.stringify({ type: "text-delta", text: "lo" }), JSON.stringify({ type: "finish", finishReason: "stop", totalUsage: { inputTokens: 5, outputTokens: 2, inputTokenDetails: { cacheReadTokens: 3, cacheWriteTokens: 1 }, }, }), ], }); const { streamCommandCode, calculatedUsages } = createTestDeps({ apiBase: server.baseUrl(), }); const events = await collectEvents( streamCommandCode(makeModel(), makeContext(), { apiKey: "mock-key" }), ); assert.deepEqual(eventTypes(events), [ "start", "text_start", "text_delta", "text_delta", "text_end", "done", ]); const done = events.at(-1); assert.equal(done?.type, "done"); if (done?.type !== "done") throw new Error("expected done"); assert.equal(done.reason, "stop"); assert.equal(done.message.content[0]?.type, "text"); assert.equal( done.message.content[0]?.type === "text" ? done.message.content[0].text : "", "Hello", ); assert.equal(done.message.usage.totalTokens, 11); assert.equal(calculatedUsages.length, 1); }); it("ends on finish without waiting for an open upstream connection", async () => { server.mockResponse({ type: "success", events: [ JSON.stringify({ type: "text-delta", text: "done" }), JSON.stringify({ type: "finish", finishReason: "stop" }), ], hangAfterLast: true, }); const { streamCommandCode } = createTestDeps({ apiBase: server.baseUrl() }); const events = await collectEvents( streamCommandCode(makeModel(), makeContext(), { apiKey: "mock-key" }), 500, ); assert.equal(events.at(-1)?.type, "done"); await new Promise((resolve) => setTimeout(resolve, 50)); assert.ok( server.responseClosedBeforeEnd(), "client should cancel the still-open response body", ); }); it("emits reasoning and tool-call blocks in order", async () => { server.mockResponse({ type: "success", events: [ JSON.stringify({ type: "reasoning-delta", text: "think" }), JSON.stringify({ type: "reasoning-end" }), JSON.stringify({ type: "text-delta", text: "Using tool" }), JSON.stringify({ type: "tool-call", toolCallId: "call_1", toolName: "read_file", input: JSON.stringify({ path: "/tmp/x" }), }), JSON.stringify({ type: "finish", finishReason: "tool-calls" }), ], }); const { streamCommandCode } = createTestDeps({ apiBase: server.baseUrl() }); const events = await collectEvents( streamCommandCode(makeModel(), makeContext(), { apiKey: "mock-key" }), ); assert.deepEqual(eventTypes(events), [ "start", "thinking_start", "thinking_delta", "thinking_end", "text_start", "text_delta", "text_end", "toolcall_start", "toolcall_end", "done", ]); const done = events.at(-1); if (done?.type !== "done") throw new Error("expected done"); assert.equal(done.reason, "toolUse"); assert.deepEqual( done.message.content.map((content) => content.type), ["thinking", "text", "toolCall"], ); const toolCall = done.message.content[2]; assert.equal( toolCall?.type === "toolCall" ? toolCall.name : "", "read_file", ); }); it("flushes reasoning if finish arrives without reasoning-end", async () => { server.mockResponse({ type: "success", events: [ JSON.stringify({ type: "reasoning-delta", text: "unfinished thought" }), JSON.stringify({ type: "finish", finishReason: "stop" }), ], }); const { streamCommandCode } = createTestDeps({ apiBase: server.baseUrl() }); const events = await collectEvents( streamCommandCode(makeModel(), makeContext(), { apiKey: "mock-key" }), ); const done = events.at(-1); if (done?.type !== "done") throw new Error("expected done"); assert.equal(done.message.content[0]?.type, "thinking"); }); }); describe("streamCommandCode — request serialization", () => { it("sends the expected request body and default headers", async () => { server.mockResponse({ type: "success", events: [JSON.stringify({ type: "finish", finishReason: "stop" })], }); const { streamCommandCode } = createTestDeps({ apiBase: server.baseUrl() }); const context = makeContext({ messages: [ { role: "user", content: "first" }, { role: "assistant", content: [{ type: "text", text: "first response" }], }, { role: "user", content: "second" }, ], tools: [ { name: "get_weather", description: "Get weather", parameters: { kind: "object", properties: { city: { kind: "string" } }, }, }, ], }); await collectEvents( streamCommandCode(makeModel(), context, { apiKey: "mock-key", maxTokens: 500, }), ); const body = server.lastRequestBody(); assert.equal(objectAt(body, ["config", "workingDir"]), "/repo"); assert.equal(objectAt(body, ["config", "date"]), "2026-05-05"); assert.equal( objectAt(body, ["params", "model"]), "deepseek/deepseek-v4-flash", ); assert.equal(objectAt(body, ["params", "stream"]), true); assert.equal(objectAt(body, ["params", "max_tokens"]), 500); assert.equal( objectAt(body, ["params", "system"]), "You are a test assistant.", ); assert.equal( objectAt(body, ["params", "messages", "1", "content", "0", "text"]), "first response", ); assert.equal( objectAt(body, ["params", "tools", "0", "name"]), "get_weather", ); const headers = server.lastRequestHeaders(); assert.equal(headers.authorization, "Bearer mock-key"); assert.equal(headers["x-command-code-version"], "0.24.1"); assert.equal( headers["x-session-id"], "00000000-0000-4000-8000-000000000000", ); }); it("caps maxTokens and passes custom headers", async () => { server.mockResponse({ type: "success", events: [JSON.stringify({ type: "finish", finishReason: "stop" })], }); const { streamCommandCode } = createTestDeps({ apiBase: server.baseUrl() }); await collectEvents( streamCommandCode(makeModel({ maxTokens: 500_000 }), makeContext(), { apiKey: "mock-key", maxTokens: 500_000, headers: { "x-custom": "value" }, }), ); assert.equal( objectAt(server.lastRequestBody(), ["params", "max_tokens"]), 200_000, ); assert.equal(server.lastRequestHeaders()["x-custom"], "value"); }); it("runs onPayload and onResponse hooks", async () => { server.mockResponse({ type: "success", events: [JSON.stringify({ type: "finish", finishReason: "stop" })], }); const { streamCommandCode } = createTestDeps({ apiBase: server.baseUrl() }); let responseStatus = 0; await collectEvents( streamCommandCode(makeModel(), makeContext(), { apiKey: "mock-key", onPayload: () => ({ replaced: true }), onResponse: (response) => { responseStatus = response.status; }, }), ); assert.equal(objectAt(server.lastRequestBody(), ["replaced"]), true); assert.equal(responseStatus, 200); }); }); describe("streamCommandCode — upstream errors and malformed streams", () => { it("emits error for HTTP failures", async () => { server.mockResponse({ type: "error", status: 429, body: "rate limited" }); const { streamCommandCode } = createTestDeps({ apiBase: server.baseUrl() }); const events = await collectEvents( streamCommandCode(makeModel(), makeContext(), { apiKey: "mock-key" }), ); assert.deepEqual(eventTypes(events), ["start", "error"]); const error = events.at(-1); assert.equal(error?.type, "error"); if (error?.type !== "error") throw new Error("expected error"); assert.match(error.error.errorMessage ?? "", /429/); }); it("emits error for provider error events", async () => { server.mockResponse({ type: "success", events: [ JSON.stringify({ type: "error", error: { message: "provider failed" }, }), ], }); const { streamCommandCode } = createTestDeps({ apiBase: server.baseUrl() }); const events = await collectEvents( streamCommandCode(makeModel(), makeContext(), { apiKey: "mock-key" }), ); const error = events.at(-1); assert.equal(error?.type, "error"); if (error?.type !== "error") throw new Error("expected error"); assert.equal(error.error.errorMessage, "provider failed"); }); it("handles SSE lines, malformed lines, split chunks, and final line without newline", async () => { const textEvent = `data: ${JSON.stringify({ type: "text-delta", text: "split" })}\n`; const finishEvent = JSON.stringify({ type: "finish", finishReason: "max_tokens", }); server.mockResponse({ type: "success", chunks: [ "not json\n", textEvent.slice(0, 12), textEvent.slice(12), "event: ignored\n", "data: [DONE]\n", finishEvent, ], }); const { streamCommandCode } = createTestDeps({ apiBase: server.baseUrl() }); const events = await collectEvents( streamCommandCode(makeModel(), makeContext(), { apiKey: "mock-key" }), ); const done = events.at(-1); if (done?.type !== "done") throw new Error("expected done"); assert.equal(done.reason, "length"); assert.equal( done.message.content[0]?.type === "text" ? done.message.content[0].text : "", "split", ); }); });