server-connection.test.ts 3.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071
  1. import { NodeFileSystem } from "@effect/platform-node"
  2. import { Global } from "@opencode-ai/util/global"
  3. import { OPENCODE_VERSION } from "../src/version"
  4. import { expect, test } from "bun:test"
  5. import { Effect, FileSystem, Scope } from "effect"
  6. import fs from "node:fs/promises"
  7. import os from "node:os"
  8. import path from "node:path"
  9. import { ServerConnection } from "../src/services/server-connection"
  10. import { ServiceConfig } from "../src/services/service-config"
  11. test("resolution groups Effect-native lifecycle operations only for the managed service", async () => {
  12. const root = await fs.mkdtemp(path.join(os.tmpdir(), "opencode-server-resolution-"))
  13. const id = "server-resolution-test"
  14. const server = Bun.serve({
  15. port: 0,
  16. fetch() {
  17. return Response.json({
  18. healthy: true,
  19. version: OPENCODE_VERSION,
  20. pid: process.pid,
  21. })
  22. },
  23. })
  24. const registration = path.join(root, "state", ServiceConfig.filename())
  25. const layer = Global.layerWith({ config: path.join(root, "config"), state: path.join(root, "state") })
  26. const runPromise = <A, E>(effect: Effect.Effect<A, E, Global.Service | FileSystem.FileSystem | Scope.Scope>) =>
  27. Effect.runPromise(effect.pipe(Effect.provide(layer), Effect.provide(NodeFileSystem.layer), Effect.scoped))
  28. try {
  29. await fs.mkdir(path.dirname(registration), { recursive: true })
  30. await fs.writeFile(
  31. registration,
  32. JSON.stringify({
  33. id,
  34. version: OPENCODE_VERSION,
  35. url: server.url.toString(),
  36. pid: process.pid,
  37. }),
  38. )
  39. const resolved = await runPromise(ServerConnection.resolve({}))
  40. expect(resolved.endpoint.url).toBe(server.url.toString())
  41. expect(resolved.service).toBeDefined()
  42. if (!resolved.service) throw new Error("Expected managed service capabilities")
  43. expect(Effect.isEffect(resolved.service.reconnect())).toBe(true)
  44. expect(Effect.isEffect(resolved.service.restart())).toBe(true)
  45. expect(await runPromise(resolved.service.reconnect())).toEqual(resolved.endpoint)
  46. const explicit = await runPromise(ServerConnection.resolve({ server: server.url.toString() }))
  47. expect(explicit.endpoint.url).toBe(server.url.toString())
  48. expect(explicit.service).toBeUndefined()
  49. } finally {
  50. await server.stop(true)
  51. await fs.rm(root, { recursive: true, force: true })
  52. }
  53. })
  54. test("service options only require a matching version when requested", async () => {
  55. const root = await fs.mkdtemp(path.join(os.tmpdir(), "opencode-service-options-"))
  56. const layer = Global.layerWith({ config: path.join(root, "config"), state: path.join(root, "state") })
  57. const runPromise = <A, E>(effect: Effect.Effect<A, E, Global.Service | FileSystem.FileSystem | Scope.Scope>) =>
  58. Effect.runPromise(effect.pipe(Effect.provide(layer), Effect.provide(NodeFileSystem.layer), Effect.scoped))
  59. try {
  60. expect((await runPromise(ServiceConfig.options())).version).toBeUndefined()
  61. expect((await runPromise(ServiceConfig.options({ checkVersion: true }))).version).toBe(OPENCODE_VERSION)
  62. } finally {
  63. await fs.rm(root, { recursive: true, force: true })
  64. }
  65. })