63 lines
2.9 KiB
TypeScript
63 lines
2.9 KiB
TypeScript
import { describe, expect, test } from "bun:test"
|
|
import { Browser } from "@opencode-ai/schema/browser"
|
|
import { BrowserTunnel } from "@opencode-ai/schema/browser-tunnel"
|
|
import { Session } from "@opencode-ai/schema/session"
|
|
import { Effect } from "effect"
|
|
import { BrowserTunnelProtocol } from "../src/browser-tunnel.js"
|
|
|
|
describe("BrowserTunnelProtocol", () => {
|
|
test("round trips binary data without copying protocol semantics into it", async () => {
|
|
const payload = new Uint8Array([0, 1, 2, 255])
|
|
const decoded = await Effect.runPromise(BrowserTunnelProtocol.decodeFromServer(BrowserTunnelProtocol.data(payload)))
|
|
expect(decoded).toEqual({ type: "data", data: payload })
|
|
expect(Array.from(BrowserTunnelProtocol.data(new Uint8Array([1, 2])))).toEqual([0, 1, 2])
|
|
})
|
|
|
|
test("round trips typed control messages", async () => {
|
|
const message: BrowserTunnel.FromDesktop = {
|
|
type: "browser.tunnel.open",
|
|
sessionID: Session.ID.make("ses_browser_tunnel"),
|
|
leaseID: Browser.LeaseID.make("brl_browsertunnel"),
|
|
target: { host: BrowserTunnel.Host.make("localhost"), port: BrowserTunnel.Port.make(5173) },
|
|
receiveWindow: BrowserTunnel.WindowSize.make(BrowserTunnelProtocol.InitialWindowBytes),
|
|
receiveFrames: BrowserTunnel.FrameWindow.make(BrowserTunnelProtocol.InitialFrameWindow),
|
|
}
|
|
const decoded = await Effect.runPromise(
|
|
BrowserTunnelProtocol.decodeFromDesktop(BrowserTunnelProtocol.encodeFromDesktop(message)),
|
|
)
|
|
expect(decoded).toEqual({ type: "control", message })
|
|
})
|
|
|
|
test("rejects unframed, unknown, and malformed control payloads", async () => {
|
|
for (const input of [
|
|
"unframed",
|
|
new Uint8Array([9, 0]),
|
|
new Uint8Array([BrowserTunnelProtocol.FrameType.Control, 255]),
|
|
]) {
|
|
expect(await Effect.runPromise(BrowserTunnelProtocol.decodeFromDesktop(input).pipe(Effect.result))).toMatchObject(
|
|
{
|
|
_tag: "Failure",
|
|
failure: { _tag: "BrowserTunnelProtocol.FrameError" },
|
|
},
|
|
)
|
|
}
|
|
})
|
|
|
|
test("pins control framing and public size limits", async () => {
|
|
expect(Array.from(BrowserTunnelProtocol.encodeFromDesktop({ type: "browser.tunnel.end" }))).toEqual([
|
|
BrowserTunnelProtocol.FrameType.Control,
|
|
...new TextEncoder().encode('{"type":"browser.tunnel.end"}'),
|
|
])
|
|
expect(() => BrowserTunnelProtocol.data(new Uint8Array())).toThrow()
|
|
expect(() => BrowserTunnelProtocol.data(new Uint8Array(BrowserTunnelProtocol.MaxDataBytes + 1))).toThrow()
|
|
|
|
const extra = new TextEncoder().encode('{"type":"browser.tunnel.end","extra":true}')
|
|
const frame = new Uint8Array(extra.byteLength + 1)
|
|
frame[0] = BrowserTunnelProtocol.FrameType.Control
|
|
frame.set(extra, 1)
|
|
expect(await Effect.runPromise(BrowserTunnelProtocol.decodeFromDesktop(frame).pipe(Effect.result))).toMatchObject({
|
|
_tag: "Failure",
|
|
failure: { _tag: "BrowserTunnelProtocol.FrameError" },
|
|
})
|
|
})
|
|
})
|