| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161 |
- import { expect, test } from "bun:test"
- import type { SessionMessageInfo } from "@opencode-ai/client/promise"
- import type { Page, Route } from "@playwright/test"
- import { currentMessage, mockOpenCodeServer } from "../../utils/mock-server"
- test("preserves current messages", () => {
- const message = {
- id: "msg_current",
- type: "user",
- time: { created: 1 },
- text: "current",
- files: [{ data: "e30=", mime: "application/json", source: { type: "inline" } }],
- } satisfies SessionMessageInfo
- expect(currentMessage(message)).toBe(message)
- })
- test("converts rich legacy messages to current message types", () => {
- expect(
- currentMessage({
- info: { id: "msg_user", role: "user", time: { created: 1 } },
- parts: [
- { type: "text", text: "Use @src/a.ts with @explore" },
- {
- type: "file",
- mime: "application/json",
- filename: "data.json",
- url: "data:application/json;base64,e30=",
- },
- {
- type: "file",
- mime: "text/plain",
- filename: "a.ts",
- url: "src/a.ts",
- source: { type: "file", text: { value: "@src/a.ts", start: 4, end: 13 } },
- },
- { type: "agent", name: "explore", source: { value: "@explore", start: 19, end: 27 } },
- ],
- }),
- ).toEqual({
- id: "msg_user",
- type: "user",
- time: { created: 1 },
- text: "Use @src/a.ts with @explore",
- files: [
- { data: "e30=", mime: "application/json", name: "data.json", source: { type: "inline" } },
- {
- data: "",
- mime: "text/plain",
- name: "a.ts",
- source: { type: "uri", uri: "src/a.ts" },
- mention: { text: "@src/a.ts", start: 4, end: 13 },
- },
- ],
- agents: [{ name: "explore", mention: { text: "@explore", start: 19, end: 27 } }],
- })
- expect(
- currentMessage({
- info: {
- id: "msg_assistant",
- role: "assistant",
- time: { created: 2, completed: 5 },
- agent: "explore",
- modelID: "model",
- providerID: "provider",
- variant: "high",
- cost: 0.5,
- tokens: { input: 1, output: 2, reasoning: 3, cache: { read: 4, write: 5 } },
- finish: "tool-calls",
- error: { name: "MessageAbortedError", data: { message: "Stopped" } },
- },
- parts: [
- { type: "text", text: "Answer" },
- { type: "reasoning", text: "Thinking", time: { start: 2, end: 3 } },
- {
- id: "prt_tool",
- callID: "call_tool",
- type: "tool",
- tool: "read",
- state: {
- status: "completed",
- input: { filePath: "src/a.ts" },
- output: "contents",
- metadata: { title: "a.ts" },
- time: { start: 3, end: 4 },
- },
- },
- ],
- }),
- ).toEqual({
- id: "msg_assistant",
- type: "assistant",
- time: { created: 2, completed: 5 },
- agent: "explore",
- model: { id: "model", providerID: "provider", variant: "high" },
- cost: 0.5,
- tokens: { input: 1, output: 2, reasoning: 3, cache: { read: 4, write: 5 } },
- finish: "tool-calls",
- error: { type: "MessageAbortedError", message: "Stopped" },
- content: [
- { type: "text", text: "Answer" },
- { type: "reasoning", text: "Thinking", time: { created: 2, completed: 3 } },
- {
- type: "tool",
- id: "call_tool",
- name: "read",
- time: { created: 3, ran: 3, completed: 4 },
- state: {
- status: "completed",
- input: { filePath: "src/a.ts" },
- content: [{ type: "text", text: "contents" }],
- metadata: { title: "a.ts" },
- },
- },
- ],
- })
- })
- test("applies message latency after a list response gate is released", async () => {
- const events: string[] = []
- const gate = Promise.withResolvers<void>()
- let handler: ((route: Route) => Promise<void>) | undefined
- const page = {
- route: (_url: string, callback: (route: Route) => Promise<void>) => {
- handler = callback
- return Promise.resolve()
- },
- } as unknown as Page
- await mockOpenCodeServer(page, {
- provider: {},
- directory: "C:/OpenCode",
- project: {},
- sessions: [{ id: "session" }],
- messageDelay: 25,
- beforeMessagesResponse: () => {
- events.push("before")
- return gate.promise
- },
- onMessages: (request) => events.push(request.phase),
- pageMessages: () => {
- events.push("page")
- return { items: [] }
- },
- })
- const response = handler!({
- request: () => ({ url: () => "http://127.0.0.1:4096/api/session/session/message" }),
- fulfill: () => {
- events.push("fulfill")
- return Promise.resolve()
- },
- } as unknown as Route)
- expect(events).toEqual(["start", "before"])
- const released = performance.now()
- gate.resolve()
- await response
- expect(performance.now() - released).toBeGreaterThanOrEqual(20)
- expect(events).toEqual(["start", "before", "page", "end", "fulfill"])
- })
|