import { describe, expect, test } from "bun:test" import { createApiForServer, createSdkForServer } from "./server" import { createCompatibleApi } from "./server-compat" function setup(protocol: "v1" | "v2") { const requests: Request[] = [] const fetcher = Object.assign( async (input: string | URL | Request, init?: RequestInit) => { const request = new Request(input, init) requests.push(request) if (request.method === "PATCH") { return Response.json({ id: "ses_1", slug: "ses_1", projectID: "project", directory: "/repo", title: "Session", version: "1", time: { created: 1, updated: 1 }, }) } if (request.method === "POST" && request.url.endsWith("/prompt_async")) return new Response(undefined, { status: 204 }) if (request.method === "POST" && request.url.endsWith("/prompt")) { return Response.json({ admittedSeq: 1, id: "msg_1", sessionID: "ses_1", timeCreated: 1, type: "user", data: { text: "hello" }, delivery: "steer", }) } if (request.method === "GET") return Response.json([]) return new Response(undefined, { status: 204 }) }, { preconnect: globalThis.fetch.preconnect }, ) const server = { url: "http://localhost:4096" } const api = createCompatibleApi({ protocol: Promise.resolve(protocol), current: createApiForServer({ server, fetch: fetcher }), legacy: (directory) => createSdkForServer({ server, fetch: fetcher, directory, throwOnError: true }), directory: "/repo", }) return { api, requests } } describe("createCompatibleApi", () => { test("routes V1 archive through the legacy session update", async () => { const { api, requests } = setup("v1") await api.session.archive({ sessionID: "ses_1", directory: "/repo" }) const url = new URL(requests[0]!.url) expect(url.pathname).toBe("/session/ses_1") expect(requests[0]!.headers.get("x-opencode-directory")).toBe("%2Frepo") expect(requests[0]!.method).toBe("PATCH") expect(await requests[0]!.json()).toMatchObject({ time: { archived: expect.any(Number) } }) }) test("converts current prompts to the V1 prompt contract", async () => { const { api, requests } = setup("v1") await api.session.prompt({ sessionID: "ses_1", id: "msg_1", text: "hello", agent: "build", model: { providerID: "provider", modelID: "model" }, }) expect(new URL(requests[0]!.url).pathname).toBe("/session/ses_1/prompt_async") expect(await requests[0]!.json()).toMatchObject({ messageID: "msg_1", agent: "build", model: { providerID: "provider", modelID: "model" }, parts: [{ type: "text", text: "hello" }], }) }) test("keeps V2 session actions on the current API", async () => { const { api, requests } = setup("v2") await api.session.archive({ sessionID: "ses_1" }) expect(new URL(requests[0]!.url).pathname).toBe("/api/session/ses_1/archive") expect(requests[0]!.method).toBe("POST") }) test("uses the global V1 session search endpoint", async () => { const { api, requests } = setup("v1") await api.session.list({ parentID: null, search: "session", limit: 50 }) expect(new URL(requests[0]!.url).pathname).toBe("/experimental/session") }) })