import { describe, expect, it } from "bun:test"; import { type OpencodeClient } from "@opencode-ai/sdk/v2"; import { OpencodeRuntimeAdapter } from "../../src/runtime/opencode.js"; const createRuntimeAdapter = ( messages: unknown[], calls: { reverted: string[]; removed: string[]; } = { reverted: [], removed: [] }, ) => Object.assign(Object.create(OpencodeRuntimeAdapter.prototype), { messages: async () => messages, revertMessage: async (_sessionId: string, messageId: string) => { calls.reverted.push(messageId); }, removeMessage: async (_sessionId: string, messageId: string) => { calls.removed.push(messageId); }, }) as OpencodeRuntimeAdapter; describe("OpencodeRuntimeAdapter.revertToUserMessage", () => { it("skips reverting the first user message when the runtime session is empty", async () => { const calls = { reverted: [] as string[], removed: [] as string[] }; const runtime = createRuntimeAdapter([], calls); await runtime.revertToUserMessage("session-1", { userOrdinal: 1 }); expect(calls).toEqual({ reverted: [], removed: [] }); }); it("keeps ordinal mismatches visible when runtime messages exist", async () => { const runtime = createRuntimeAdapter([ { info: { id: "user-1", role: "user" } }, { info: { id: "assistant-1", role: "assistant" } }, ]); await expect( runtime.revertToUserMessage("session-1", { userOrdinal: 2 }), ).rejects.toThrow("target user message not found to revert"); }); it("reverts and removes messages from the target user message onward", async () => { const calls = { reverted: [] as string[], removed: [] as string[] }; const runtime = createRuntimeAdapter( [ { info: { id: "user-1", role: "user" } }, { info: { id: "assistant-1", role: "assistant" } }, { info: { id: "user-2", role: "user" } }, { info: { id: "assistant-2", role: "assistant" } }, ], calls, ); await runtime.revertToUserMessage("session-1", { userOrdinal: 2 }); expect(calls).toEqual({ reverted: ["user-2"], removed: ["assistant-2", "user-2"], }); }); }); describe("OpencodeRuntimeAdapter.ensureClient", () => { it("retries bootstrap after a failed startup attempt", async () => { let attempts = 0; const client = { global: { health: async () => ({ data: { healthy: true } }) }, } as unknown as OpencodeClient; const runtime = Object.assign(Object.create(OpencodeRuntimeAdapter.prototype), { clientPromise: null, closeServer: null, bootstrapClient: async () => { attempts += 1; if (attempts === 1) { throw new Error("startup failed"); } return client; }, }) as OpencodeRuntimeAdapter; await expect(runtime.ensureClient()).rejects.toThrow("startup failed"); await expect(runtime.ensureClient()).resolves.toBe(client); expect(attempts).toBe(2); }); });