diff --git a/packages/opencode/test/server/httpapi-sdk.test.ts b/packages/opencode/test/server/httpapi-sdk.test.ts new file mode 100644 index 0000000000..d022859469 --- /dev/null +++ b/packages/opencode/test/server/httpapi-sdk.test.ts @@ -0,0 +1,84 @@ +import { afterEach, describe, expect, test } from "bun:test" +import { Flag } from "@opencode-ai/core/flag/flag" +import { createOpencodeClient } from "@opencode-ai/sdk/v2" +import { ExperimentalHttpApiServer } from "../../src/server/routes/instance/httpapi/server" +import path from "path" +import { resetDatabase } from "../fixture/db" +import { tmpdir } from "../fixture/fixture" + +const original = Flag.OPENCODE_EXPERIMENTAL_HTTPAPI + +function client(directory?: string) { + Flag.OPENCODE_EXPERIMENTAL_HTTPAPI = true + const handler = ExperimentalHttpApiServer.webHandler().handler + const fetch = Object.assign( + (request: RequestInfo | URL, init?: RequestInit) => + handler(new Request(request, init), ExperimentalHttpApiServer.context), + { preconnect: globalThis.fetch.preconnect }, + ) satisfies typeof globalThis.fetch + return createOpencodeClient({ + baseUrl: "http://localhost", + directory, + fetch, + }) +} + +async function expectStatus(result: Promise<{ response: Response }>, status: number) { + expect((await result).response.status).toBe(status) +} + +afterEach(async () => { + Flag.OPENCODE_EXPERIMENTAL_HTTPAPI = original + await resetDatabase() +}) + +describe("HttpApi SDK", () => { + test("uses the generated SDK for global and control routes", async () => { + const sdk = client() + const health = await sdk.global.health() + + expect(health.response.status).toBe(200) + expect(health.data).toMatchObject({ healthy: true }) + + const events = await sdk.global.event({ signal: AbortSignal.timeout(1_000) }) + try { + const first = await events.stream.next() + expect(first.value).toMatchObject({ payload: { type: "server.connected" } }) + } finally { + await events.stream.return(undefined) + } + + const log = await sdk.app.log({ service: "httpapi-sdk-test", level: "info", message: "hello" }) + expect(log.response.status).toBe(200) + expect(log.data).toBe(true) + + await expectStatus(sdk.auth.set({ providerID: "test" }), 400) + }) + + test("uses the generated SDK for safe instance routes", async () => { + await using tmp = await tmpdir({ + config: { formatter: false, lsp: false }, + init: (dir) => Bun.write(path.join(dir, "hello.txt"), "hello"), + }) + const sdk = client(tmp.path) + + const file = await sdk.file.read({ path: "hello.txt" }) + expect(file.response.status).toBe(200) + expect(file.data).toMatchObject({ content: "hello" }) + + const session = await sdk.session.create({ title: "sdk" }) + expect(session.response.status).toBe(200) + expect(session.data).toMatchObject({ title: "sdk" }) + + const listed = await sdk.session.list({ roots: true, limit: 10 }) + expect(listed.response.status).toBe(200) + expect(listed.data?.map((item) => item.id)).toContain(session.data?.id) + + await Promise.all([ + expectStatus(sdk.project.current(), 200), + expectStatus(sdk.config.get(), 200), + expectStatus(sdk.config.providers(), 200), + expectStatus(sdk.find.files({ query: "hello", limit: 10 }), 200), + ]) + }) +})