Download packages/client/test/promise.test.ts from SaylorTwift/opencode: direct link, hf CLI and curl.
- Browser
- Download file 9.2 kB
-
https://huggingface.co/SaylorTwift/opencode/resolve/main/packages/client/test/promise.test.ts
- Command line
-
hf download hf://SaylorTwift/opencode/packages/client/test/promise.test.ts
-
curl -L -o promise.test.ts https://huggingface.co/SaylorTwift/opencode/resolve/main/packages/client/test/promise.test.ts
9.2 kB
| import { expect, test } from "bun:test" | |
| import { isSessionNotFoundError, isUnauthorizedError, OpenCode } from "../src" | |
| test("exposes every standard HTTP API group", () => { | |
| const client = OpenCode.make({ baseUrl: "http://localhost:3000" }) | |
| expect(Object.keys(client)).toEqual([ | |
| "health", | |
| "location", | |
| "agents", | |
| "sessions", | |
| "messages", | |
| "models", | |
| "providers", | |
| "integrations", | |
| "credentials", | |
| "permissions", | |
| "files", | |
| "commands", | |
| "skills", | |
| "events", | |
| "ptys", | |
| "questions", | |
| "references", | |
| "projectCopies", | |
| ]) | |
| expect(Object.keys(client.messages)).toEqual(["list"]) | |
| expect(Object.keys(client.integrations)).toEqual([ | |
| "list", | |
| "get", | |
| "connectKey", | |
| "connectOauth", | |
| "attemptStatus", | |
| "attemptComplete", | |
| "attemptCancel", | |
| ]) | |
| expect(Object.keys(client.files)).toEqual(["list", "find"]) | |
| expect(Object.keys(client.ptys)).toEqual(["list", "create", "get", "update", "remove"]) | |
| }) | |
| test("sessions.get returns the wire projection", async () => { | |
| const client = OpenCode.make({ | |
| baseUrl: "http://localhost:3000", | |
| fetch: async (input) => { | |
| expect(typeof input === "string" ? input : input instanceof URL ? input.href : input.url).toBe( | |
| "http://localhost:3000/api/session/ses_test", | |
| ) | |
| return Response.json(session) | |
| }, | |
| }) | |
| const result = await client.sessions.get({ sessionID: "ses_test" }) | |
| expect(result.time.created).toBe(1_717_171_717_000) | |
| }) | |
| test("events.subscribe exposes the Promise event stream wire projection", async () => { | |
| const client = OpenCode.make({ | |
| baseUrl: "http://localhost:3000", | |
| fetch: async () => | |
| new Response( | |
| `: heartbeat\n\ndata: ${JSON.stringify({ id: "evt_connected", type: "server.connected", data: {} })}\n\n` + | |
| `data: ${JSON.stringify(modelSwitchedEvent)}\n\n`, | |
| { headers: { "content-type": "text/event-stream" } }, | |
| ), | |
| }) | |
| const events = [] | |
| for await (const event of client.events.subscribe()) events.push(event) | |
| expect(events).toEqual([{ id: "evt_connected", type: "server.connected", data: {} }, modelSwitchedEvent]) | |
| expect(events[1]?.type === "session.next.model.switched" && events[1].data.timestamp).toBe(1_717_171_717_000) | |
| }) | |
| test("events.subscribe terminates on malformed Promise SSE data", async () => { | |
| const client = OpenCode.make({ | |
| baseUrl: "http://localhost:3000", | |
| fetch: async () => new Response("data: {not-json}\n\n", { headers: { "content-type": "text/event-stream" } }), | |
| }) | |
| await expect(client.events.subscribe()[Symbol.asyncIterator]().next()).rejects.toMatchObject({ | |
| name: "ClientError", | |
| reason: "MalformedResponse", | |
| }) | |
| }) | |
| test("session methods use the public HTTP contract", async () => { | |
| const requests: Array<{ url: string; init?: RequestInit }> = [] | |
| let historyPage = 0 | |
| const client = OpenCode.make({ | |
| baseUrl: "http://localhost:3000", | |
| fetch: async (input, init) => { | |
| const url = typeof input === "string" ? input : input instanceof URL ? input.href : input.url | |
| requests.push({ url, init }) | |
| if (url.includes("/event")) { | |
| return new Response(`data: ${JSON.stringify(modelSwitchedEvent)}\n\n`, { | |
| headers: { "content-type": "text/event-stream" }, | |
| }) | |
| } | |
| if (url.includes("/history")) { | |
| historyPage++ | |
| return Response.json( | |
| historyPage === 1 ? { data: [modelSwitchedEvent], hasMore: true } : { data: [], hasMore: false }, | |
| ) | |
| } | |
| if (url.includes("/prompt")) return Response.json(admission) | |
| if (url.includes("/context")) return Response.json({ data: [] }) | |
| if (url.includes("/message/")) return Response.json({ data: modelSwitchedMessage }) | |
| if (url.endsWith("/api/session/active")) return Response.json({ data: { ses_test: { type: "running" } } }) | |
| if (init?.method === "POST" && url.endsWith("/api/session")) return Response.json(session) | |
| if (init?.method === "POST") return new Response(null, { status: 204 }) | |
| return Response.json({ data: [session.data], cursor: { next: "next" } }) | |
| }, | |
| }) | |
| const page = await client.sessions.list({ limit: 10, order: "desc" }) | |
| const active = await client.sessions.active() | |
| const created = await client.sessions.create({ location: { directory: "/tmp/project" } }) | |
| await client.sessions.switchAgent({ sessionID: "ses_test", agent: "build" }) | |
| await client.sessions.switchModel({ | |
| sessionID: "ses_test", | |
| model: { id: "claude", providerID: "anthropic" }, | |
| }) | |
| const admitted = await client.sessions.prompt({ | |
| sessionID: "ses_test", | |
| prompt: { text: "Hello" }, | |
| resume: false, | |
| }) | |
| await client.sessions.compact({ sessionID: "ses_test" }) | |
| await client.sessions.wait({ sessionID: "ses_test" }) | |
| const context = await client.sessions.context({ sessionID: "ses_test" }) | |
| const history = await client.sessions.history({ sessionID: "ses_test", after: 0, limit: 1 }) | |
| const historyAfter = history.data.at(-1)?.durable?.seq | |
| const historyNext = history.hasMore | |
| ? await client.sessions.history({ sessionID: "ses_test", after: historyAfter, limit: 2 }) | |
| : undefined | |
| const events = [] | |
| for await (const event of client.sessions.events({ sessionID: "ses_test", after: 0 })) events.push(event) | |
| await client.sessions.interrupt({ sessionID: "ses_test" }) | |
| const message = await client.sessions.message({ sessionID: "ses_test", messageID: "msg_model" }) | |
| expect(page.cursor.next).toBe("next") | |
| expect(active).toEqual({ ses_test: { type: "running" } }) | |
| expect(created.id).toBe("ses_test") | |
| expect(admitted.id).toBe("msg_test") | |
| expect(context).toEqual([]) | |
| expect(history).toEqual({ data: [modelSwitchedEvent], hasMore: true }) | |
| expect(historyNext).toEqual({ data: [], hasMore: false }) | |
| expect(events).toEqual([modelSwitchedEvent]) | |
| expect(message).toEqual(modelSwitchedMessage) | |
| expect(requests.map((request) => [request.init?.method, request.url])).toEqual([ | |
| ["GET", "http://localhost:3000/api/session?limit=10&order=desc"], | |
| ["GET", "http://localhost:3000/api/session/active"], | |
| ["POST", "http://localhost:3000/api/session"], | |
| ["POST", "http://localhost:3000/api/session/ses_test/agent"], | |
| ["POST", "http://localhost:3000/api/session/ses_test/model"], | |
| ["POST", "http://localhost:3000/api/session/ses_test/prompt"], | |
| ["POST", "http://localhost:3000/api/session/ses_test/compact"], | |
| ["POST", "http://localhost:3000/api/session/ses_test/wait"], | |
| ["GET", "http://localhost:3000/api/session/ses_test/context"], | |
| ["GET", "http://localhost:3000/api/session/ses_test/history?limit=1&after=0"], | |
| ["GET", "http://localhost:3000/api/session/ses_test/history?limit=2&after=1"], | |
| ["GET", "http://localhost:3000/api/session/ses_test/event?after=0"], | |
| ["POST", "http://localhost:3000/api/session/ses_test/interrupt"], | |
| ["GET", "http://localhost:3000/api/session/ses_test/message/msg_model"], | |
| ]) | |
| const body = requests.find((request) => request.url.endsWith("/api/session/ses_test/prompt"))?.init?.body | |
| if (typeof body !== "string") throw new Error("Expected JSON request body") | |
| expect(JSON.parse(body)).toEqual({ | |
| prompt: { text: "Hello" }, | |
| resume: false, | |
| }) | |
| }) | |
| test("middleware errors remain declared client errors", async () => { | |
| const client = OpenCode.make({ | |
| baseUrl: "http://localhost:3000", | |
| fetch: async () => | |
| Response.json({ _tag: "UnauthorizedError", message: "Authentication required" }, { status: 401 }), | |
| }) | |
| try { | |
| await client.sessions.create({}) | |
| throw new Error("Expected request to fail") | |
| } catch (error) { | |
| expect(isUnauthorizedError(error)).toBe(true) | |
| } | |
| }) | |
| test("sessions.history decodes SessionNotFoundError", async () => { | |
| const client = OpenCode.make({ | |
| baseUrl: "http://localhost:3000", | |
| fetch: async () => | |
| Response.json( | |
| { _tag: "SessionNotFoundError", sessionID: "ses_missing", message: "Session not found" }, | |
| { status: 404 }, | |
| ), | |
| }) | |
| try { | |
| await client.sessions.history({ sessionID: "ses_missing" }) | |
| throw new Error("Expected request to fail") | |
| } catch (error) { | |
| expect(isSessionNotFoundError(error)).toBe(true) | |
| } | |
| }) | |
| const session = { | |
| data: { | |
| id: "ses_test", | |
| projectID: "project", | |
| cost: 0, | |
| tokens: { | |
| input: 1, | |
| output: 2, | |
| reasoning: 3, | |
| cache: { read: 4, write: 5 }, | |
| }, | |
| time: { | |
| created: 1_717_171_717_000, | |
| updated: 1_717_171_717_000, | |
| }, | |
| title: "Test", | |
| location: { directory: "/tmp/project" }, | |
| }, | |
| } | |
| const admission = { | |
| data: { | |
| admittedSeq: 0, | |
| id: "msg_test", | |
| sessionID: "ses_test", | |
| prompt: { text: "Hello" }, | |
| delivery: "steer", | |
| timeCreated: 1_717_171_717_000, | |
| }, | |
| } | |
| const modelSwitchedMessage = { | |
| id: "msg_model", | |
| type: "model-switched", | |
| time: { created: 1_717_171_717_000 }, | |
| model: { id: "claude", providerID: "anthropic" }, | |
| } | |
| const modelSwitchedEvent = { | |
| id: "evt_model", | |
| type: "session.next.model.switched", | |
| durable: { aggregateID: "ses_test", seq: 1, version: 1 }, | |
| data: { | |
| timestamp: 1_717_171_717_000, | |
| sessionID: "ses_test", | |
| messageID: "msg_model", | |
| model: { id: "claude", providerID: "anthropic" }, | |
| }, | |
| } | |