frontend-server.test.ts 2.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182
  1. import { expect, test } from "bun:test"
  2. import { Effect, FileSystem, Queue } from "effect"
  3. import { SimulationActions } from "../src/frontend/actions"
  4. import { SimulationRenderer } from "../src/frontend/renderer"
  5. import { SimulationServer } from "../src/frontend/server"
  6. import { availableEndpoint, connect } from "./fixture/websocket"
  7. test("scopes the frontend control server and reports malformed JSON", async () => {
  8. const endpoint = availableEndpoint()
  9. await Effect.runPromise(
  10. Effect.scoped(
  11. Effect.gen(function* () {
  12. const renderer = yield* SimulationRenderer.create({})
  13. yield* SimulationServer.start(SimulationActions.createHarness(renderer), endpoint)
  14. const socket = yield* connect(endpoint)
  15. const messages = yield* Queue.unbounded<unknown>()
  16. socket.addEventListener("message", (event) => {
  17. Queue.offerUnsafe(messages, JSON.parse(String(event.data)))
  18. })
  19. socket.send(
  20. JSON.stringify({
  21. jsonrpc: "2.0",
  22. id: 0,
  23. method: "simulation.handshake",
  24. params: {
  25. client: { name: "test", version: "test" },
  26. expectedRole: "ui",
  27. offeredVersions: [1],
  28. requiredCapabilities: ["ui.state"],
  29. optionalCapabilities: [],
  30. },
  31. }),
  32. )
  33. expect(yield* Queue.take(messages)).toMatchObject({
  34. id: 0,
  35. result: {
  36. protocolVersion: 1,
  37. role: "ui",
  38. server: { name: "opencode", version: expect.any(String) },
  39. capabilities: expect.arrayContaining(["ui.state", "ui.snapshot", "ui.click.semantic", "ui.capture"]),
  40. },
  41. })
  42. socket.send(JSON.stringify({ jsonrpc: "2.0", id: 1, method: "ui.state" }))
  43. expect(yield* Queue.take(messages)).toMatchObject({
  44. id: 1,
  45. result: { focused: { editor: false }, elements: [] },
  46. })
  47. socket.send(JSON.stringify({ jsonrpc: "2.0", id: 2, method: "ui.capture" }))
  48. expect(yield* Queue.take(messages)).toMatchObject({
  49. id: 2,
  50. result: {
  51. cols: 100,
  52. rows: 40,
  53. cursor: [0, 0],
  54. lines: expect.any(Array),
  55. },
  56. })
  57. socket.send(JSON.stringify({ jsonrpc: "2.0", id: 3, method: "ui.snapshot" }))
  58. expect(yield* Queue.take(messages)).toEqual({
  59. jsonrpc: "2.0",
  60. id: 3,
  61. result: { format: "opencode-ui-snapshot-v1", nodes: [] },
  62. })
  63. socket.send("{")
  64. expect(yield* Queue.take(messages)).toMatchObject({
  65. id: null,
  66. error: { code: -32000 },
  67. })
  68. }),
  69. ).pipe(Effect.provide(FileSystem.layerNoop({}))),
  70. )
  71. const url = new URL(endpoint)
  72. const rebound = Bun.serve({ hostname: url.hostname, port: Number(url.port), fetch: () => new Response() })
  73. await rebound.stop(true)
  74. })