157 lines
4.7 KiB
TypeScript
157 lines
4.7 KiB
TypeScript
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",
|
|
structured: {},
|
|
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: [] })
|
|
})
|
|
})
|