httpapi-config.test.ts 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. import { afterEach, describe, expect } from "bun:test"
  2. import path from "path"
  3. import { Server } from "../../src/server/server"
  4. import { Effect, Fiber } from "effect"
  5. import { resetDatabase } from "../fixture/db"
  6. import { disposeAllInstances, tmpdir } from "../fixture/fixture"
  7. import { it } from "../lib/effect"
  8. import { waitGlobalBusEvent } from "./global-bus"
  9. function app() {
  10. return Server.Default().app
  11. }
  12. function waitDisposed(directory: string) {
  13. return waitGlobalBusEvent({
  14. message: "timed out waiting for instance disposal",
  15. predicate: (event) => event.payload.type === "server.instance.disposed" && event.directory === directory,
  16. })
  17. }
  18. const tmpdirEffect = (options: Parameters<typeof tmpdir>[0]) =>
  19. Effect.acquireRelease(
  20. Effect.promise(() => tmpdir(options)),
  21. (tmp) => Effect.promise(() => tmp[Symbol.asyncDispose]()),
  22. )
  23. afterEach(async () => {
  24. await disposeAllInstances()
  25. await resetDatabase()
  26. })
  27. describe("config HttpApi", () => {
  28. it.live(
  29. "serves config update through the default server app",
  30. Effect.gen(function* () {
  31. const tmp = yield* tmpdirEffect({ config: { formatter: false, lsp: false } })
  32. const disposed = yield* waitDisposed(tmp.path).pipe(Effect.forkScoped({ startImmediately: true }))
  33. const response = yield* Effect.promise(() =>
  34. Promise.resolve(
  35. app().request("/config", {
  36. method: "PATCH",
  37. headers: {
  38. "content-type": "application/json",
  39. "x-opencode-directory": tmp.path,
  40. },
  41. body: JSON.stringify({ username: "patched-user", formatter: false, lsp: false }),
  42. }),
  43. ),
  44. )
  45. expect(response.status).toBe(200)
  46. expect(yield* Effect.promise(() => response.json())).toMatchObject({
  47. username: "patched-user",
  48. formatter: false,
  49. lsp: false,
  50. })
  51. yield* Fiber.join(disposed)
  52. expect(yield* Effect.promise(() => Bun.file(path.join(tmp.path, "config.json")).json())).toMatchObject({
  53. username: "patched-user",
  54. formatter: false,
  55. lsp: false,
  56. })
  57. }),
  58. )
  59. it.live(
  60. "serves config with active provider model status",
  61. Effect.gen(function* () {
  62. const tmp = yield* tmpdirEffect({
  63. config: {
  64. formatter: false,
  65. lsp: false,
  66. provider: {
  67. omniroute: {
  68. models: {
  69. "gpt-4o": {
  70. status: "active",
  71. },
  72. },
  73. },
  74. },
  75. },
  76. })
  77. const response = yield* Effect.promise(() =>
  78. Promise.resolve(
  79. app().request("/config", {
  80. headers: {
  81. "x-opencode-directory": tmp.path,
  82. },
  83. }),
  84. ),
  85. )
  86. expect(response.status).toBe(200)
  87. expect(yield* Effect.promise(() => response.json())).toMatchObject({
  88. provider: {
  89. omniroute: {
  90. models: {
  91. "gpt-4o": {
  92. status: "active",
  93. },
  94. },
  95. },
  96. },
  97. })
  98. }),
  99. )
  100. })