mirror of
https://github.com/anomalyco/opencode.git
synced 2026-10-23 14:40:23 +00:00
105 lines
4.7 KiB
TypeScript
105 lines
4.7 KiB
TypeScript
import { expect } from "bun:test"
|
|
import { OpenCode, type SessionMessageInfo } from "@opencode/client"
|
|
import { Session } from "@opencode/schema/session"
|
|
import { Effect } from "effect"
|
|
import { it } from "../../core/test/lib/effect"
|
|
import { ServerFetch } from "../src/fetch"
|
|
|
|
const messages: SessionMessageInfo[] = [
|
|
{ id: "msg_z", type: "user", text: "First request", time: { created: 300 } },
|
|
{
|
|
id: "msg_assistant",
|
|
type: "assistant",
|
|
agent: "build",
|
|
model: { providerID: "test", id: "test" },
|
|
content: [{ type: "text", text: "First answer" }],
|
|
finish: "stop",
|
|
time: { created: 400, completed: 500 },
|
|
},
|
|
{ id: "msg_b", type: "user", text: "Second request", time: { created: 100 } },
|
|
{ id: "msg_synthetic", type: "synthetic", text: "Background completion", time: { created: 200 } },
|
|
{
|
|
id: "msg_compaction",
|
|
type: "compaction",
|
|
status: "completed",
|
|
reason: "manual",
|
|
summary: "Conversation summary",
|
|
time: { created: 700 },
|
|
},
|
|
{ id: "msg_x", type: "user", text: "Third request", time: { created: 600 } },
|
|
{ id: "msg_system", type: "system", text: "Updated instructions", time: { created: 800 } },
|
|
{ id: "msg_a", type: "user", text: "Fourth request", time: { created: 500 } },
|
|
{ id: "msg_idle", type: "idle", outcome: "succeeded", time: { created: 900 } },
|
|
]
|
|
|
|
const setup = Effect.gen(function* () {
|
|
const handler = yield* ServerFetch.make({
|
|
app: { version: "test" },
|
|
database: { path: ":memory:" },
|
|
config: { project: false },
|
|
models: { fetch: false },
|
|
fs: { filewatcher: false },
|
|
})
|
|
const api = OpenCode.make({
|
|
baseUrl: "http://opencode.local",
|
|
fetch: Object.assign((input: string | URL | Request, init?: RequestInit) => handler(new Request(input, init)), {
|
|
preconnect: fetch.preconnect,
|
|
}),
|
|
})
|
|
const session = yield* Effect.promise(async () => {
|
|
const template = await api.session.create({ title: "Message filtering" })
|
|
return api.session.import({ info: { ...template, id: Session.ID.create() }, messages })
|
|
})
|
|
return { api, handler, sessionID: session.id }
|
|
})
|
|
|
|
it.live("filters message types before paginating in either direction through the generated client", () =>
|
|
Effect.gen(function* () {
|
|
const fixture = yield* setup
|
|
yield* Effect.promise(async () => {
|
|
const input = { sessionID: fixture.sessionID, type: "user", limit: 2 } as const
|
|
// Omission retains the full transcript, in durable sequence rather than timestamp or ID order.
|
|
expect(
|
|
(await fixture.api.message.list({ sessionID: fixture.sessionID })).data.map((message) => message.id),
|
|
).toEqual(messages.toReversed().map((message) => message.id))
|
|
for (const order of ["asc", "desc"] as const) {
|
|
const ids = order === "asc" ? ["msg_z", "msg_b", "msg_x", "msg_a"] : ["msg_a", "msg_x", "msg_b", "msg_z"]
|
|
const first = await fixture.api.message.list({ ...input, order })
|
|
expect(first.data.map((message) => message.id)).toEqual(ids.slice(0, 2))
|
|
if (!first.cursor.next) throw new Error("Expected a next cursor")
|
|
const second = await fixture.api.message.list({ ...input, cursor: first.cursor.next })
|
|
expect(second.data.map((message) => message.id)).toEqual(ids.slice(2))
|
|
if (!second.cursor.previous || !second.cursor.next) throw new Error("Expected previous and next cursors")
|
|
const previous = await fixture.api.message.list({ ...input, cursor: second.cursor.previous })
|
|
expect(previous.data).toEqual(first.data)
|
|
const end = await fixture.api.message.list({ ...input, cursor: second.cursor.next })
|
|
expect(end).toEqual({ data: [], cursor: { previous: null, next: null } })
|
|
}
|
|
expect((await fixture.api.message.list({ sessionID: fixture.sessionID, type: "compaction" })).data).toEqual([
|
|
messages[4],
|
|
])
|
|
expect(
|
|
(await fixture.api.message.list({ sessionID: fixture.sessionID, type: "assistant", limit: 1 })).data,
|
|
).toEqual([messages[1]])
|
|
expect((await fixture.api.message.list({ sessionID: fixture.sessionID, type: "idle" })).data).toEqual([
|
|
messages[8],
|
|
])
|
|
expect((await fixture.api.message.list({ sessionID: fixture.sessionID, type: "shell" })).data).toEqual([])
|
|
})
|
|
}),
|
|
)
|
|
|
|
it.live("rejects unknown message type filters at the HTTP boundary", () =>
|
|
Effect.gen(function* () {
|
|
const fixture = yield* setup
|
|
yield* Effect.promise(async () => {
|
|
for (const type of ["unknown", "tool", "User", ""]) {
|
|
const response = await fixture.handler(
|
|
new Request(`http://opencode.local/api/session/${fixture.sessionID}/message?type=${type}`),
|
|
)
|
|
expect(response.status).toBe(400)
|
|
expect(await response.json()).toMatchObject({ _tag: "InvalidRequestError" })
|
|
}
|
|
})
|
|
}),
|
|
)
|