152 lines
5.8 KiB
TypeScript
152 lines
5.8 KiB
TypeScript
import { expect, test } from "bun:test"
|
|
import { OpenCode } from "@opencode-ai/client/promise"
|
|
import { createOpenCodeBridge } from "../src/opencode"
|
|
import type { CompletionStore } from "../src/completion-store"
|
|
|
|
test("renames and archives only sessions discovered by the voice controller", async () => {
|
|
const requests: Request[] = []
|
|
const server: { archived?: number } = {}
|
|
const session = {
|
|
id: "ses_known",
|
|
projectID: "project-1",
|
|
cost: "0",
|
|
tokens: { input: 0, output: 0, reasoning: 0, cache: { read: 0, write: 0 } },
|
|
time: { created: 1, updated: 2 },
|
|
title: "Old title",
|
|
location: { directory: "/workspace" },
|
|
}
|
|
const fetch = Object.assign(
|
|
async (input: string | URL | Request, init?: RequestInit) => {
|
|
const request = input instanceof Request ? input : new Request(input.toString(), init)
|
|
requests.push(request)
|
|
const url = new URL(request.url)
|
|
if (url.pathname === "/api/event")
|
|
return new Response("", { headers: { "content-type": "text/event-stream" } })
|
|
if (url.pathname === "/api/project")
|
|
return Response.json([
|
|
{
|
|
id: "project-1",
|
|
worktree: "/workspace",
|
|
time: { created: 1, updated: 2 },
|
|
sandboxes: [],
|
|
},
|
|
])
|
|
if (url.pathname === "/api/session" && request.method === "GET")
|
|
return Response.json({ data: [session], cursor: {} })
|
|
if (url.pathname === "/api/session/ses_known" && request.method === "GET")
|
|
return Response.json({ data: { ...session, time: { ...session.time, archived: server.archived } } })
|
|
if (url.pathname === "/api/session/ses_known/rename" && request.method === "POST")
|
|
return new Response(null, { status: 204 })
|
|
if (url.pathname === "/api/session/ses_known/archive" && request.method === "POST") {
|
|
server.archived = Date.now()
|
|
return new Response(null, { status: 204 })
|
|
}
|
|
return new Response("Not found", { status: 404 })
|
|
},
|
|
{ preconnect: () => {} },
|
|
)
|
|
const client = OpenCode.make({
|
|
baseUrl: "https://opencode.test",
|
|
fetch,
|
|
})
|
|
const bridge = await createOpenCodeBridge({
|
|
client,
|
|
directory: "/workspace",
|
|
model: { providerID: "openai", id: "gpt-test" },
|
|
notify: () => {},
|
|
onSession: () => {},
|
|
completionStore: memoryCompletionStore(),
|
|
})
|
|
|
|
try {
|
|
expect(bridge.definitions.find((tool) => tool.name === "rename_session")).toMatchObject({
|
|
description: expect.stringContaining("does not prompt, wake, or interrupt"),
|
|
parameters: {
|
|
type: "object",
|
|
additionalProperties: false,
|
|
required: ["session_id", "title"],
|
|
},
|
|
})
|
|
expect(bridge.definitions.find((tool) => tool.name === "archive_session")).toMatchObject({
|
|
description: expect.stringContaining("explicitly confirms"),
|
|
parameters: {
|
|
type: "object",
|
|
additionalProperties: false,
|
|
required: ["session_id"],
|
|
},
|
|
})
|
|
|
|
expect(await bridge.execute("rename_session", { session_id: "ses_unknown", title: "Nope" })).toEqual({
|
|
status: "error",
|
|
message: "Use a session ID returned by find_sessions or start_session.",
|
|
retryable: false,
|
|
})
|
|
expect(await bridge.execute("archive_session", {})).toEqual({
|
|
status: "error",
|
|
message: "Use a session ID returned by find_sessions or start_session.",
|
|
retryable: false,
|
|
})
|
|
expect(await bridge.execute("archive_session", { session_id: "ses_unknown" })).toEqual({
|
|
status: "error",
|
|
message: "Use a session ID returned by find_sessions or start_session.",
|
|
retryable: false,
|
|
})
|
|
expect(requests.some((request) => new URL(request.url).pathname.includes("ses_unknown"))).toBe(false)
|
|
|
|
await bridge.execute("find_sessions", {
|
|
query: null,
|
|
scope: "current_project",
|
|
recency: "any",
|
|
limit: 10,
|
|
})
|
|
expect(await bridge.execute("rename_session", { session_id: "ses_known", title: " " })).toEqual({
|
|
status: "error",
|
|
message: "A non-empty session title is required.",
|
|
retryable: false,
|
|
})
|
|
expect(await bridge.execute("rename_session", { session_id: "ses_known", title: "Old title" })).toEqual({
|
|
status: "unchanged",
|
|
session_id: "ses_known",
|
|
previous_title: "Old title",
|
|
title: "Old title",
|
|
})
|
|
expect(requests.some((request) => new URL(request.url).pathname.endsWith("/rename"))).toBe(false)
|
|
|
|
expect(await bridge.execute("rename_session", { session_id: "ses_known", title: " New title " })).toEqual({
|
|
status: "renamed",
|
|
session_id: "ses_known",
|
|
previous_title: "Old title",
|
|
title: "New title",
|
|
})
|
|
const rename = requests.find((request) => new URL(request.url).pathname.endsWith("/rename"))
|
|
expect(rename?.method).toBe("POST")
|
|
expect(await rename?.json()).toEqual({ title: "New title" })
|
|
|
|
expect(await bridge.execute("archive_session", { session_id: "ses_known" })).toEqual({
|
|
status: "archived",
|
|
title: "Old title",
|
|
})
|
|
const archive = requests.find((request) => new URL(request.url).pathname.endsWith("/archive"))
|
|
expect(archive?.method).toBe("POST")
|
|
expect(await archive?.text()).toBe("")
|
|
expect(await bridge.execute("archive_session", { session_id: "ses_known" })).toEqual({
|
|
status: "already_archived",
|
|
title: "Old title",
|
|
})
|
|
expect(requests.filter((request) => new URL(request.url).pathname.endsWith("/archive"))).toHaveLength(1)
|
|
expect(requests.some((request) => /\/(prompt|interrupt)$/.test(new URL(request.url).pathname))).toBe(false)
|
|
} finally {
|
|
await bridge.close()
|
|
}
|
|
})
|
|
|
|
function memoryCompletionStore(): CompletionStore {
|
|
return {
|
|
entries: () => [],
|
|
admitting: async () => {},
|
|
pending: async () => {},
|
|
completed: async () => {},
|
|
delivered: async () => {},
|
|
close: async () => {},
|
|
}
|
|
}
|