|
| 1 | +import { afterEach, describe, expect, test } from "bun:test" |
| 2 | +import type { UpgradeWebSocket } from "hono/ws" |
| 3 | +import { Flag } from "@opencode-ai/core/flag/flag" |
| 4 | +import { Instance } from "../../src/project/instance" |
| 5 | +import { InstanceRoutes } from "../../src/server/routes/instance" |
| 6 | +import { EventPaths } from "../../src/server/routes/instance/httpapi/event" |
| 7 | +import { Log } from "../../src/util" |
| 8 | +import { resetDatabase } from "../fixture/db" |
| 9 | +import { tmpdir } from "../fixture/fixture" |
| 10 | + |
| 11 | +void Log.init({ print: false }) |
| 12 | + |
| 13 | +const original = Flag.OPENCODE_EXPERIMENTAL_HTTPAPI |
| 14 | +const websocket = (() => () => new Response(null, { status: 501 })) as unknown as UpgradeWebSocket |
| 15 | + |
| 16 | +function app() { |
| 17 | + Flag.OPENCODE_EXPERIMENTAL_HTTPAPI = true |
| 18 | + return InstanceRoutes(websocket) |
| 19 | +} |
| 20 | + |
| 21 | +async function readFirstChunk(response: Response) { |
| 22 | + if (!response.body) throw new Error("missing response body") |
| 23 | + const reader = response.body.getReader() |
| 24 | + const result = await Promise.race([ |
| 25 | + reader.read(), |
| 26 | + new Promise<never>((_, reject) => setTimeout(() => reject(new Error("timed out waiting for event")), 5_000)), |
| 27 | + ]) |
| 28 | + await reader.cancel() |
| 29 | + return new TextDecoder().decode(result.value) |
| 30 | +} |
| 31 | + |
| 32 | +afterEach(async () => { |
| 33 | + Flag.OPENCODE_EXPERIMENTAL_HTTPAPI = original |
| 34 | + await Instance.disposeAll() |
| 35 | + await resetDatabase() |
| 36 | +}) |
| 37 | + |
| 38 | +describe("event HttpApi bridge", () => { |
| 39 | + test("serves event stream through experimental Effect route", async () => { |
| 40 | + await using tmp = await tmpdir({ git: true, config: { formatter: false, lsp: false } }) |
| 41 | + const response = await app().request(EventPaths.event, { headers: { "x-opencode-directory": tmp.path } }) |
| 42 | + |
| 43 | + expect(response.status).toBe(200) |
| 44 | + expect(response.headers.get("content-type")).toContain("text/event-stream") |
| 45 | + expect(response.headers.get("cache-control")).toBe("no-cache, no-transform") |
| 46 | + expect(response.headers.get("x-accel-buffering")).toBe("no") |
| 47 | + expect(response.headers.get("x-content-type-options")).toBe("nosniff") |
| 48 | + expect(await readFirstChunk(response)).toContain( |
| 49 | + 'data: {"type":"server.connected","properties":{}}\n\n', |
| 50 | + ) |
| 51 | + }) |
| 52 | +}) |
0 commit comments