|
@@ -1,10 +1,12 @@
|
|
|
-import { afterEach, describe, expect, test } from "bun:test"
|
|
|
|
|
|
|
+import { afterEach, describe, expect } from "bun:test"
|
|
|
import path from "path"
|
|
import path from "path"
|
|
|
import { Server } from "../../src/server/server"
|
|
import { Server } from "../../src/server/server"
|
|
|
import * as Log from "@opencode-ai/core/util/log"
|
|
import * as Log from "@opencode-ai/core/util/log"
|
|
|
|
|
+import { Effect, Fiber } from "effect"
|
|
|
import { resetDatabase } from "../fixture/db"
|
|
import { resetDatabase } from "../fixture/db"
|
|
|
import { disposeAllInstances, tmpdir } from "../fixture/fixture"
|
|
import { disposeAllInstances, tmpdir } from "../fixture/fixture"
|
|
|
-import { waitGlobalBusEventPromise } from "./global-bus"
|
|
|
|
|
|
|
+import { it } from "../lib/effect"
|
|
|
|
|
+import { waitGlobalBusEvent } from "./global-bus"
|
|
|
|
|
|
|
|
void Log.init({ print: false })
|
|
void Log.init({ print: false })
|
|
|
|
|
|
|
@@ -12,76 +14,100 @@ function app() {
|
|
|
return Server.Default().app
|
|
return Server.Default().app
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-async function waitDisposed(directory: string) {
|
|
|
|
|
- await waitGlobalBusEventPromise({
|
|
|
|
|
|
|
+function waitDisposed(directory: string) {
|
|
|
|
|
+ return waitGlobalBusEvent({
|
|
|
message: "timed out waiting for instance disposal",
|
|
message: "timed out waiting for instance disposal",
|
|
|
predicate: (event) => event.payload.type === "server.instance.disposed" && event.directory === directory,
|
|
predicate: (event) => event.payload.type === "server.instance.disposed" && event.directory === directory,
|
|
|
})
|
|
})
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+const tmpdirEffect = (options: Parameters<typeof tmpdir>[0]) =>
|
|
|
|
|
+ Effect.acquireRelease(
|
|
|
|
|
+ Effect.promise(() => tmpdir(options)),
|
|
|
|
|
+ (tmp) => Effect.promise(() => tmp[Symbol.asyncDispose]()),
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
afterEach(async () => {
|
|
afterEach(async () => {
|
|
|
await disposeAllInstances()
|
|
await disposeAllInstances()
|
|
|
await resetDatabase()
|
|
await resetDatabase()
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
describe("config HttpApi", () => {
|
|
describe("config HttpApi", () => {
|
|
|
- test("serves config update through the default server app", async () => {
|
|
|
|
|
- await using tmp = await tmpdir({ config: { formatter: false, lsp: false } })
|
|
|
|
|
- const disposed = waitDisposed(tmp.path)
|
|
|
|
|
-
|
|
|
|
|
- const response = await app().request("/config", {
|
|
|
|
|
- method: "PATCH",
|
|
|
|
|
- headers: {
|
|
|
|
|
- "content-type": "application/json",
|
|
|
|
|
- "x-opencode-directory": tmp.path,
|
|
|
|
|
- },
|
|
|
|
|
- body: JSON.stringify({ username: "patched-user", formatter: false, lsp: false }),
|
|
|
|
|
- })
|
|
|
|
|
|
|
+ it.live(
|
|
|
|
|
+ "serves config update through the default server app",
|
|
|
|
|
+ Effect.gen(function* () {
|
|
|
|
|
+ const tmp = yield* tmpdirEffect({ config: { formatter: false, lsp: false } })
|
|
|
|
|
+ const disposed = yield* waitDisposed(tmp.path).pipe(Effect.forkScoped)
|
|
|
|
|
|
|
|
- expect(response.status).toBe(200)
|
|
|
|
|
- expect(await response.json()).toMatchObject({ username: "patched-user", formatter: false, lsp: false })
|
|
|
|
|
- await disposed
|
|
|
|
|
- expect(await Bun.file(path.join(tmp.path, "config.json")).json()).toMatchObject({
|
|
|
|
|
- username: "patched-user",
|
|
|
|
|
- formatter: false,
|
|
|
|
|
- lsp: false,
|
|
|
|
|
- })
|
|
|
|
|
- })
|
|
|
|
|
|
|
+ const response = yield* Effect.promise(() =>
|
|
|
|
|
+ Promise.resolve(
|
|
|
|
|
+ app().request("/config", {
|
|
|
|
|
+ method: "PATCH",
|
|
|
|
|
+ headers: {
|
|
|
|
|
+ "content-type": "application/json",
|
|
|
|
|
+ "x-opencode-directory": tmp.path,
|
|
|
|
|
+ },
|
|
|
|
|
+ body: JSON.stringify({ username: "patched-user", formatter: false, lsp: false }),
|
|
|
|
|
+ }),
|
|
|
|
|
+ ),
|
|
|
|
|
+ )
|
|
|
|
|
|
|
|
- test("serves config with active provider model status", async () => {
|
|
|
|
|
- await using tmp = await tmpdir({
|
|
|
|
|
- config: {
|
|
|
|
|
|
|
+ expect(response.status).toBe(200)
|
|
|
|
|
+ expect(yield* Effect.promise(() => response.json())).toMatchObject({
|
|
|
|
|
+ username: "patched-user",
|
|
|
formatter: false,
|
|
formatter: false,
|
|
|
lsp: false,
|
|
lsp: false,
|
|
|
- provider: {
|
|
|
|
|
- omniroute: {
|
|
|
|
|
- models: {
|
|
|
|
|
- "gpt-4o": {
|
|
|
|
|
- status: "active",
|
|
|
|
|
|
|
+ })
|
|
|
|
|
+ yield* Fiber.join(disposed)
|
|
|
|
|
+ expect(yield* Effect.promise(() => Bun.file(path.join(tmp.path, "config.json")).json())).toMatchObject({
|
|
|
|
|
+ username: "patched-user",
|
|
|
|
|
+ formatter: false,
|
|
|
|
|
+ lsp: false,
|
|
|
|
|
+ })
|
|
|
|
|
+ }),
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ it.live(
|
|
|
|
|
+ "serves config with active provider model status",
|
|
|
|
|
+ Effect.gen(function* () {
|
|
|
|
|
+ const tmp = yield* tmpdirEffect({
|
|
|
|
|
+ config: {
|
|
|
|
|
+ formatter: false,
|
|
|
|
|
+ lsp: false,
|
|
|
|
|
+ provider: {
|
|
|
|
|
+ omniroute: {
|
|
|
|
|
+ models: {
|
|
|
|
|
+ "gpt-4o": {
|
|
|
|
|
+ status: "active",
|
|
|
|
|
+ },
|
|
|
},
|
|
},
|
|
|
},
|
|
},
|
|
|
},
|
|
},
|
|
|
},
|
|
},
|
|
|
- },
|
|
|
|
|
- })
|
|
|
|
|
|
|
+ })
|
|
|
|
|
|
|
|
- const response = await app().request("/config", {
|
|
|
|
|
- headers: {
|
|
|
|
|
- "x-opencode-directory": tmp.path,
|
|
|
|
|
- },
|
|
|
|
|
- })
|
|
|
|
|
|
|
+ const response = yield* Effect.promise(() =>
|
|
|
|
|
+ Promise.resolve(
|
|
|
|
|
+ app().request("/config", {
|
|
|
|
|
+ headers: {
|
|
|
|
|
+ "x-opencode-directory": tmp.path,
|
|
|
|
|
+ },
|
|
|
|
|
+ }),
|
|
|
|
|
+ ),
|
|
|
|
|
+ )
|
|
|
|
|
|
|
|
- expect(response.status).toBe(200)
|
|
|
|
|
- expect(await response.json()).toMatchObject({
|
|
|
|
|
- provider: {
|
|
|
|
|
- omniroute: {
|
|
|
|
|
- models: {
|
|
|
|
|
- "gpt-4o": {
|
|
|
|
|
- status: "active",
|
|
|
|
|
|
|
+ expect(response.status).toBe(200)
|
|
|
|
|
+ expect(yield* Effect.promise(() => response.json())).toMatchObject({
|
|
|
|
|
+ provider: {
|
|
|
|
|
+ omniroute: {
|
|
|
|
|
+ models: {
|
|
|
|
|
+ "gpt-4o": {
|
|
|
|
|
+ status: "active",
|
|
|
|
|
+ },
|
|
|
},
|
|
},
|
|
|
},
|
|
},
|
|
|
},
|
|
},
|
|
|
- },
|
|
|
|
|
- })
|
|
|
|
|
- })
|
|
|
|
|
|
|
+ })
|
|
|
|
|
+ }),
|
|
|
|
|
+ )
|
|
|
})
|
|
})
|