frontend-server.test.ts 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  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([
  40. "ui.state",
  41. "ui.snapshot",
  42. "ui.click.semantic",
  43. "ui.capture",
  44. ]),
  45. },
  46. })
  47. socket.send(JSON.stringify({ jsonrpc: "2.0", id: 1, method: "ui.state" }))
  48. expect(yield* Queue.take(messages)).toMatchObject({
  49. id: 1,
  50. result: { focused: { editor: false }, elements: [] },
  51. })
  52. socket.send(JSON.stringify({ jsonrpc: "2.0", id: 2, method: "ui.capture" }))
  53. expect(yield* Queue.take(messages)).toMatchObject({
  54. id: 2,
  55. result: {
  56. cols: 100,
  57. rows: 40,
  58. cursor: [0, 0],
  59. lines: expect.any(Array),
  60. },
  61. })
  62. socket.send(JSON.stringify({ jsonrpc: "2.0", id: 3, method: "ui.snapshot" }))
  63. expect(yield* Queue.take(messages)).toEqual({
  64. jsonrpc: "2.0",
  65. id: 3,
  66. result: { format: "opencode-ui-snapshot-v1", nodes: [] },
  67. })
  68. socket.send("{")
  69. expect(yield* Queue.take(messages)).toMatchObject({
  70. id: null,
  71. error: { code: -32000 },
  72. })
  73. }),
  74. ).pipe(Effect.provide(FileSystem.layerNoop({}))),
  75. )
  76. const url = new URL(endpoint)
  77. const rebound = Bun.serve({ hostname: url.hostname, port: Number(url.port), fetch: () => new Response() })
  78. await rebound.stop(true)
  79. })