소스 검색

test(server): reproduce partial startup catalog reads

Kit Langton 1 개월 전
부모
커밋
2ca34a7e63
2개의 변경된 파일74개의 추가작업 그리고 0개의 파일을 삭제
  1. 56 0
      packages/server/test/catalog-readiness.test.ts
  2. 18 0
      packages/server/test/fixtures/delayed-catalog-plugin.ts

+ 56 - 0
packages/server/test/catalog-readiness.test.ts

@@ -0,0 +1,56 @@
+import fs from "fs/promises"
+import os from "os"
+import path from "path"
+import { afterEach, describe, expect, test } from "bun:test"
+import { Layer } from "effect"
+import { HttpRouter, HttpServer } from "effect/unstable/http"
+import { createEmbeddedRoutes } from "../src/routes"
+
+const directories: string[] = []
+
+afterEach(async () => {
+  await Promise.all(directories.splice(0).map((directory) => fs.rm(directory, { recursive: true, force: true })))
+})
+
+describe("catalog readiness", () => {
+  test("catalog reads wait for initial plugin activation", async () => {
+    const directory = await fs.mkdtemp(path.join(os.tmpdir(), "opencode-server-catalog-"))
+    directories.push(directory)
+    await fs.writeFile(
+      path.join(directory, "opencode.json"),
+      JSON.stringify({
+        plugins: ["-*", path.join(import.meta.dir, "fixtures/delayed-catalog-plugin.ts")],
+      }),
+    )
+
+    const server = HttpRouter.toWebHandler(createEmbeddedRoutes().pipe(Layer.provide(HttpServer.layerServices)))
+    const request = (pathname: string) =>
+      server.handler(
+        new Request(`http://localhost${pathname}`, {
+          headers: { "x-opencode-directory": directory },
+        }),
+      )
+
+    try {
+      const [providers, models] = await Promise.all([request("/api/provider"), request("/api/model")])
+      expect(providers.status).toBe(200)
+      expect(models.status).toBe(200)
+      const initialProviders = (await providers.json()).data
+      const initialModels = (await models.json()).data
+
+      expect((await request("/api/model/default")).status).toBe(200)
+      const [readyProviders, readyModels] = await Promise.all([request("/api/provider"), request("/api/model")])
+      expect((await readyProviders.json()).data).toContainEqual(expect.objectContaining({ id: "delayed-provider" }))
+      expect((await readyModels.json()).data).toContainEqual(
+        expect.objectContaining({ providerID: "delayed-provider", id: "delayed-model" }),
+      )
+
+      expect(initialProviders).toContainEqual(expect.objectContaining({ id: "delayed-provider" }))
+      expect(initialModels).toContainEqual(
+        expect.objectContaining({ providerID: "delayed-provider", id: "delayed-model" }),
+      )
+    } finally {
+      await server.dispose()
+    }
+  })
+})

+ 18 - 0
packages/server/test/fixtures/delayed-catalog-plugin.ts

@@ -0,0 +1,18 @@
+import { Plugin } from "@opencode-ai/plugin/v2/effect"
+import { Effect } from "effect"
+
+export default Plugin.define({
+  id: "delayed-catalog",
+  effect: Effect.fn(function* (ctx) {
+    yield* Effect.sleep("300 millis")
+    yield* ctx.catalog.transform((catalog) => {
+      catalog.provider.update("delayed-provider", (provider) => {
+        provider.name = "Delayed Provider"
+      })
+      catalog.model.update("delayed-provider", "delayed-model", (model) => {
+        model.name = "Delayed Model"
+        model.enabled = true
+      })
+    })
+  }),
+})