mirror of
https://github.com/anomalyco/opencode.git
synced 2026-08-27 22:10:11 -04:00
test: share session message expectations (#45454)
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
export * as Expected from "./session-message.js"
|
||||
|
||||
// Partial expected values, not input fixtures. Keep each assertion's matcher and fields explicit.
|
||||
export const user = <Text>(text: Text) => ({ type: "user" as const, text })
|
||||
|
||||
export const text = <Text>(text: Text) => ({ type: "text" as const, text })
|
||||
|
||||
export const reasoning = <Text>(text: Text) => ({ type: "reasoning" as const, text })
|
||||
|
||||
export const assistant = <const Fields extends object, Content>(fields: Fields, content: Content) => ({
|
||||
...fields,
|
||||
type: "assistant" as const,
|
||||
content,
|
||||
})
|
||||
|
||||
export const completedTool = <Identity extends object, Fields extends object>(identity: Identity, fields: Fields) => ({
|
||||
...identity,
|
||||
type: "tool" as const,
|
||||
state: { ...fields, status: "completed" as const },
|
||||
})
|
||||
|
||||
export const failedTool = <Identity extends object, Fields extends object>(identity: Identity, fields: Fields) => ({
|
||||
...identity,
|
||||
type: "tool" as const,
|
||||
state: { ...fields, status: "error" as const },
|
||||
})
|
||||
@@ -32,6 +32,7 @@ import { SessionTable } from "@opencode-ai/core/session/sql"
|
||||
import { SessionStore } from "@opencode-ai/core/session/store"
|
||||
import { SessionTransfer } from "@opencode-ai/core/session/transfer"
|
||||
import { Workspace } from "@opencode-ai/core/workspace"
|
||||
import { Expected } from "./lib/session-message"
|
||||
import { testEffect } from "./lib/effect"
|
||||
import { LocationServiceMap } from "@opencode-ai/core/location-service-map"
|
||||
import { promptLocationLayer } from "./fixture/prompt-location"
|
||||
@@ -186,7 +187,7 @@ describe("Session.create", () => {
|
||||
"session.inbox.enqueued",
|
||||
])
|
||||
expect(yield* session.messages({ sessionID: created.id })).toMatchObject([
|
||||
{ id: expect.any(String), type: "user", text: "Preserved history" },
|
||||
{ id: expect.any(String), ...Expected.user("Preserved history") },
|
||||
])
|
||||
expect(yield* SessionInbox.find(db, pending.id)).toMatchObject({ payload: { text: "Preserved inbox" } })
|
||||
expect(
|
||||
@@ -449,10 +450,7 @@ describe("Session.create", () => {
|
||||
|
||||
expect(forked).toMatchObject({ title: "Parent (fork #1)", fork: { sessionID: parent.id } })
|
||||
expect(forked.parentID).toBeUndefined()
|
||||
expect(forkContext).toMatchObject([
|
||||
{ type: "user", text: "First" },
|
||||
{ type: "synthetic", text: "parent note" },
|
||||
])
|
||||
expect(forkContext).toMatchObject([Expected.user("First"), { type: "synthetic", text: "parent note" }])
|
||||
expect(forkContext.map((message) => message.id)).not.toEqual(parentContext.map((message) => message.id))
|
||||
expect(history).toHaveLength(1)
|
||||
expect(history[0]).toMatchObject({
|
||||
@@ -637,13 +635,10 @@ describe("Session.create", () => {
|
||||
const forked = yield* session.fork({ sessionID: parent.id, boundary: { type: "through" } })
|
||||
|
||||
expect(yield* session.context(parent.id)).toMatchObject([
|
||||
{ type: "user", text: "Run both tools" },
|
||||
{
|
||||
type: "assistant",
|
||||
content: [{ type: "tool", id: "call_running", state: { status: "running" } }],
|
||||
},
|
||||
Expected.user("Run both tools"),
|
||||
Expected.assistant({}, [{ type: "tool", id: "call_running", state: { status: "running" } }]),
|
||||
])
|
||||
expect(yield* session.context(forked.id)).toMatchObject([{ type: "user", text: "Run both tools" }])
|
||||
expect(yield* session.context(forked.id)).toMatchObject([Expected.user("Run both tools")])
|
||||
}),
|
||||
)
|
||||
|
||||
@@ -670,10 +665,10 @@ describe("Session.create", () => {
|
||||
const running = yield* session.fork({ sessionID: parent.id, boundary: { type: "through" } })
|
||||
|
||||
expect(yield* session.context(parent.id)).toMatchObject([
|
||||
{ type: "user", text: "Run a shell" },
|
||||
Expected.user("Run a shell"),
|
||||
{ type: "shell", command: "sleep 10", status: "running" },
|
||||
])
|
||||
expect(yield* session.context(running.id)).toMatchObject([{ type: "user", text: "Run a shell" }])
|
||||
expect(yield* session.context(running.id)).toMatchObject([Expected.user("Run a shell")])
|
||||
|
||||
yield* bus.publish(SessionEvent.Shell.Ended, {
|
||||
sessionID: parent.id,
|
||||
@@ -682,9 +677,9 @@ describe("Session.create", () => {
|
||||
})
|
||||
const completed = yield* session.fork({ sessionID: parent.id, boundary: { type: "through" } })
|
||||
|
||||
expect(yield* session.context(running.id)).toMatchObject([{ type: "user", text: "Run a shell" }])
|
||||
expect(yield* session.context(running.id)).toMatchObject([Expected.user("Run a shell")])
|
||||
expect(yield* session.context(completed.id)).toMatchObject([
|
||||
{ type: "user", text: "Run a shell" },
|
||||
Expected.user("Run a shell"),
|
||||
{ type: "shell", command: "sleep 10", status: "exited", output: { output: "complete" } },
|
||||
])
|
||||
}),
|
||||
@@ -760,8 +755,8 @@ describe("Session.create", () => {
|
||||
expect(yield* session.context(beforeFirst.id)).toEqual([])
|
||||
expect(beforeFirst).toMatchObject({ cost: 0, tokens: { input: 0, output: 0, reasoning: 0 } })
|
||||
expect(yield* session.context(complete.id)).toMatchObject([
|
||||
{ type: "user", text: "First" },
|
||||
{ type: "user", text: "Second" },
|
||||
Expected.user("First"),
|
||||
Expected.user("Second"),
|
||||
{ type: "assistant", finish: "stop" },
|
||||
])
|
||||
expect(complete).toMatchObject({
|
||||
@@ -936,7 +931,7 @@ describe("Session.create", () => {
|
||||
yield* Effect.forEach(serialized.slice(2), (event) => bus.replay(event), { discard: true })
|
||||
expect(yield* SessionInbox.find(db, admitted.id)).toBeUndefined()
|
||||
expect(yield* store.context(created.id)).toMatchObject([
|
||||
{ id: admitted.id, type: "user", text: "Replay lifecycle" },
|
||||
{ id: admitted.id, ...Expected.user("Replay lifecycle") },
|
||||
])
|
||||
expect(
|
||||
(yield* db
|
||||
@@ -1160,9 +1155,7 @@ describe("SessionTransfer", () => {
|
||||
recent: "pending",
|
||||
})
|
||||
|
||||
expect((yield* transfer.export({ sessionID: source.id })).messages).toMatchObject([
|
||||
{ type: "user", text: "Settled" },
|
||||
])
|
||||
expect((yield* transfer.export({ sessionID: source.id })).messages).toMatchObject([Expected.user("Settled")])
|
||||
}),
|
||||
)
|
||||
|
||||
@@ -1307,7 +1300,7 @@ describe("SessionTransfer", () => {
|
||||
expect(imported).toMatchObject({ id: sessionID, title: "Exported", location })
|
||||
expect(imported.time).toMatchObject({ idle: DateTime.makeUnsafe(200), viewed: DateTime.makeUnsafe(150) })
|
||||
expect(messages).toMatchObject([
|
||||
{ id: sourceMessageID, type: "user", text: "Imported message" },
|
||||
{ id: sourceMessageID, ...Expected.user("Imported message") },
|
||||
{ id: errorMessageID, type: "compaction", error: { type: "test_error", message: "Original error" } },
|
||||
])
|
||||
expect(yield* Bus.latestSequence(db, sessionID)).toBe(2)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -42,6 +42,7 @@ import { tmpdir } from "./fixture/tmpdir"
|
||||
import { tempGlobalLayer } from "./fixture/global"
|
||||
import { testEffect } from "./lib/effect"
|
||||
import { permissionLayer } from "./lib/permission"
|
||||
import { Expected } from "./lib/session-message"
|
||||
import { toolIdentity, executeTool, registerToolPlugin, toolDefinitions } from "./lib/tool"
|
||||
|
||||
const sessionID = Session.ID.make("ses_shell_tool_test")
|
||||
@@ -654,10 +655,7 @@ describe("ShellTool ordinary shell syntax", () => {
|
||||
value: { status: "completed", metadata: { exit: 0 } },
|
||||
})
|
||||
if (Exit.isSuccess(result.exit))
|
||||
expect(result.exit.value.content?.[0]).toEqual({
|
||||
type: "text",
|
||||
text: isWindows ? "hello\r\n" : "hello\n",
|
||||
})
|
||||
expect(result.exit.value.content?.[0]).toEqual(Expected.text(isWindows ? "hello\r\n" : "hello\n"))
|
||||
}),
|
||||
pwsh ?? "pwsh",
|
||||
))
|
||||
@@ -728,10 +726,9 @@ describe("ShellTool", () => {
|
||||
expect(settled.status).toBe("completed")
|
||||
expect(settled.metadata).toMatchObject({ exit: 0, truncated: false })
|
||||
expect(settled.content?.[0]).toEqual({ type: "text", text: "hello" })
|
||||
expect(settled.content?.[1]).toMatchObject({
|
||||
type: "text",
|
||||
text: expect.stringContaining("Command exited with code 0."),
|
||||
})
|
||||
expect(settled.content?.[1]).toMatchObject(
|
||||
Expected.text(expect.stringContaining("Command exited with code 0.")),
|
||||
)
|
||||
expect(assertions).toMatchObject([
|
||||
{
|
||||
sessionID,
|
||||
@@ -789,10 +786,9 @@ describe("ShellTool", () => {
|
||||
),
|
||||
Effect.andThen((settled) =>
|
||||
Effect.sync(() =>
|
||||
expect(settled.content?.[0]).toMatchObject({
|
||||
type: "text",
|
||||
text: expect.stringContaining(realpathSync(path.join(tmp.path, "src"))),
|
||||
}),
|
||||
expect(settled.content?.[0]).toMatchObject(
|
||||
Expected.text(expect.stringContaining(realpathSync(path.join(tmp.path, "src")))),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
@@ -1121,10 +1117,9 @@ describe("ShellTool", () => {
|
||||
expect(settled.status).toBe("completed")
|
||||
expect(settled.metadata).toMatchObject({ exit: 7, truncated: false })
|
||||
expect(settled.content?.[0]).toEqual({ type: "text", text: "body" })
|
||||
expect(settled.content?.[1]).toMatchObject({
|
||||
type: "text",
|
||||
text: expect.stringContaining("Command exited with code 7"),
|
||||
})
|
||||
expect(settled.content?.[1]).toMatchObject(
|
||||
Expected.text(expect.stringContaining("Command exited with code 7")),
|
||||
)
|
||||
}),
|
||||
),
|
||||
)
|
||||
@@ -1151,10 +1146,9 @@ describe("ShellTool", () => {
|
||||
if (!content || content.type !== "text") throw new Error("Expected text content")
|
||||
expect(content.text.includes("output-start")).toBe(false)
|
||||
expect(content.text.includes("output-end")).toBe(true)
|
||||
expect(content).toMatchObject({
|
||||
type: "text",
|
||||
text: expect.stringContaining("output truncated; full output saved to:"),
|
||||
})
|
||||
expect(content).toMatchObject(
|
||||
Expected.text(expect.stringContaining("output truncated; full output saved to:")),
|
||||
)
|
||||
}),
|
||||
),
|
||||
)
|
||||
@@ -1260,14 +1254,8 @@ describe("ShellTool", () => {
|
||||
Effect.andThen((settled) =>
|
||||
Effect.sync(() => {
|
||||
expect(settled.metadata).toMatchObject({ timeout: true, truncated: false })
|
||||
expect(settled.content?.[0]).toMatchObject({
|
||||
type: "text",
|
||||
text: expect.stringContaining("before timeout"),
|
||||
})
|
||||
expect(settled.content?.[1]).toMatchObject({
|
||||
type: "text",
|
||||
text: expect.stringContaining("Command timed out"),
|
||||
})
|
||||
expect(settled.content?.[0]).toMatchObject(Expected.text(expect.stringContaining("before timeout")))
|
||||
expect(settled.content?.[1]).toMatchObject(Expected.text(expect.stringContaining("Command timed out")))
|
||||
}),
|
||||
),
|
||||
)
|
||||
|
||||
@@ -5,6 +5,7 @@ import type { OpenCodeEvent } from "@opencode-ai/client"
|
||||
import { SessionMessage } from "@opencode-ai/core/session/message"
|
||||
import { Bus } from "@opencode-ai/core/bus"
|
||||
import { Event } from "@opencode-ai/schema/event"
|
||||
import { Expected } from "../../../../core/test/lib/session-message"
|
||||
import { createEffect, onMount, type ParentProps } from "solid-js"
|
||||
import { ConfigProvider } from "../../../src/config"
|
||||
import { ClientProvider, useClient } from "../../../src/context/client"
|
||||
@@ -2825,7 +2826,7 @@ test("renders admitted prompts immediately and tracks them until promoted", asyn
|
||||
})
|
||||
await wait(() => sync.session.message.list(sessionID)?.length === 1)
|
||||
const admitted = sync.session.message.list(sessionID)?.[0]
|
||||
expect(admitted).toMatchObject({ id: messageID, type: "user", text: "hello" })
|
||||
expect(admitted).toMatchObject({ id: messageID, ...Expected.user("hello") })
|
||||
expect(admitted?.metadata).toBeUndefined()
|
||||
expect(sync.session.pending.list(sessionID)).toEqual([
|
||||
{
|
||||
@@ -3250,7 +3251,7 @@ test("hydrates durable pending prompts into the visible transcript", async () =>
|
||||
await mounted
|
||||
await sync.session.pending.sync(sessionID)
|
||||
expect(sync.session.message.list(sessionID)).toEqual([
|
||||
{ id: item.id, type: "user", text: "waiting", time: { created: 5 } },
|
||||
{ id: item.id, ...Expected.user("waiting"), time: { created: 5 } },
|
||||
])
|
||||
|
||||
await sync.session.message.sync(sessionID)
|
||||
|
||||
Reference in New Issue
Block a user