import { describe, expect, test } from "bun:test" import type { OpenCodeEvent, SessionMessageInfo } from "@opencode-ai/client/promise" import { createV2SessionReducer } from "./server-session-v2-reducer" const event = (input: object) => input as OpenCodeEvent const base = { created: 1, location: { directory: "/repo" }, durable: { aggregateID: "ses_1", seq: 1, version: 1 } } describe("v2 session reducer", () => { test("projects promoted input and streaming assistant content", () => { const reducer = createV2SessionReducer() let messages: SessionMessageInfo[] = [] const apply = (input: object) => { const result = reducer.reduce(messages, event(input)) if (result) messages = result.messages return result } apply({ ...base, id: "evt_admitted", type: "session.input.admitted", data: { sessionID: "ses_1", inputID: "msg_user", input: { type: "user", delivery: "steer", data: { text: "hello" } }, }, }) apply({ ...base, id: "evt_promoted", type: "session.input.promoted", data: { sessionID: "ses_1", inputID: "msg_user" }, }) apply({ ...base, id: "evt_step", type: "session.step.started", data: { sessionID: "ses_1", assistantMessageID: "msg_assistant", agent: "build", model: { id: "model", providerID: "provider" }, }, }) apply({ ...base, id: "evt_text_start", type: "session.text.started", data: { sessionID: "ses_1", assistantMessageID: "msg_assistant", ordinal: 0 }, }) apply({ ...base, id: "evt_text_delta", type: "session.text.delta", data: { sessionID: "ses_1", assistantMessageID: "msg_assistant", ordinal: 0, delta: "hel" }, }) apply({ ...base, id: "evt_text_end", type: "session.text.ended", data: { sessionID: "ses_1", assistantMessageID: "msg_assistant", ordinal: 0, text: "hello" }, }) expect(messages[0]).toMatchObject({ id: "msg_user", type: "user", text: "hello" }) expect(messages[1]).toMatchObject({ id: "msg_assistant", type: "assistant", content: [{ type: "text", text: "hello" }], }) }) test("folds tool, retry, and completion events", () => { const reducer = createV2SessionReducer() let messages: SessionMessageInfo[] = [] const apply = (input: object) => { const result = reducer.reduce(messages, event(input)) if (result) messages = result.messages } apply({ ...base, id: "evt_step", type: "session.step.started", data: { sessionID: "ses_1", assistantMessageID: "msg_assistant", agent: "build", model: { id: "model", providerID: "provider" }, }, }) apply({ ...base, id: "evt_tool_start", type: "session.tool.input.started", data: { sessionID: "ses_1", assistantMessageID: "msg_assistant", callID: "call_1", name: "bash" }, }) apply({ ...base, id: "evt_tool_delta", type: "session.tool.input.delta", data: { sessionID: "ses_1", assistantMessageID: "msg_assistant", callID: "call_1", delta: "{}" }, }) apply({ ...base, id: "evt_tool_called", type: "session.tool.called", data: { sessionID: "ses_1", assistantMessageID: "msg_assistant", callID: "call_1", input: {}, executed: true }, }) apply({ ...base, id: "evt_tool_success", type: "session.tool.success", data: { sessionID: "ses_1", assistantMessageID: "msg_assistant", callID: "call_1", metadata: {}, content: [{ type: "text", text: "done" }], executed: true, }, }) apply({ ...base, id: "evt_retry", type: "session.retry.scheduled", data: { sessionID: "ses_1", assistantMessageID: "msg_assistant", attempt: 2, at: 10, error: { type: "ProviderError", message: "retry" }, }, }) apply({ ...base, id: "evt_done", type: "session.execution.succeeded", data: { sessionID: "ses_1" } }) expect(messages[0]).toMatchObject({ type: "assistant", retry: undefined, content: [{ type: "tool", id: "call_1", state: { status: "completed", content: [{ text: "done" }] } }], }) }) test("requests hydration when promotion admission was missed", () => { const result = createV2SessionReducer().reduce( [], event({ ...base, id: "evt_promoted", type: "session.input.promoted", data: { sessionID: "ses_1", inputID: "msg_user" }, }), ) expect(result).toMatchObject({ sessionID: "ses_1", missing: "msg_user", touched: [] }) }) })