瀏覽代碼

fix: migrate sync http api test to effect runner (#27175)

Kit Langton 3 月之前
父節點
當前提交
9c54255aeb
共有 1 個文件被更改,包括 130 次插入107 次删除
  1. 130 107
      packages/opencode/test/server/httpapi-sync.test.ts

+ 130 - 107
packages/opencode/test/server/httpapi-sync.test.ts

@@ -1,29 +1,25 @@
-import { afterEach, describe, expect, mock, spyOn, test } from "bun:test"
+import { afterEach, describe, expect, mock, spyOn } from "bun:test"
 import { Context, Effect } from "effect"
 import { Context, Effect } from "effect"
 import { Flag } from "@opencode-ai/core/flag/flag"
 import { Flag } from "@opencode-ai/core/flag/flag"
-import { Instance } from "../../src/project/instance"
-import { WithInstance } from "../../src/project/with-instance"
 import { Server } from "../../src/server/server"
 import { Server } from "../../src/server/server"
 import { SyncPaths } from "../../src/server/routes/instance/httpapi/groups/sync"
 import { SyncPaths } from "../../src/server/routes/instance/httpapi/groups/sync"
 import { ExperimentalHttpApiServer } from "../../src/server/routes/instance/httpapi/server"
 import { ExperimentalHttpApiServer } from "../../src/server/routes/instance/httpapi/server"
 import { Session } from "@/session/session"
 import { Session } from "@/session/session"
 import * as Log from "@opencode-ai/core/util/log"
 import * as Log from "@opencode-ai/core/util/log"
 import { resetDatabase } from "../fixture/db"
 import { resetDatabase } from "../fixture/db"
-import { disposeAllInstances, tmpdir } from "../fixture/fixture"
+import { disposeAllInstances, TestInstance } from "../fixture/fixture"
+import { testEffect } from "../lib/effect"
 
 
 void Log.init({ print: false })
 void Log.init({ print: false })
 
 
 const originalWorkspaces = Flag.OPENCODE_EXPERIMENTAL_WORKSPACES
 const originalWorkspaces = Flag.OPENCODE_EXPERIMENTAL_WORKSPACES
 const context = Context.empty() as Context.Context<unknown>
 const context = Context.empty() as Context.Context<unknown>
+const it = testEffect(Session.defaultLayer)
 
 
 function app() {
 function app() {
   return Server.Default().app
   return Server.Default().app
 }
 }
 
 
-function runSession<A, E>(fx: Effect.Effect<A, E, Session.Service>) {
-  return Effect.runPromise(fx.pipe(Effect.provide(Session.defaultLayer)))
-}
-
 afterEach(async () => {
 afterEach(async () => {
   mock.restore()
   mock.restore()
   Flag.OPENCODE_EXPERIMENTAL_WORKSPACES = originalWorkspaces
   Flag.OPENCODE_EXPERIMENTAL_WORKSPACES = originalWorkspaces
@@ -32,111 +28,138 @@ afterEach(async () => {
 })
 })
 
 
 describe("sync HttpApi", () => {
 describe("sync HttpApi", () => {
-  test("serves sync routes", async () => {
-    Flag.OPENCODE_EXPERIMENTAL_WORKSPACES = true
-    await using tmp = await tmpdir({ git: true, config: { formatter: false, lsp: false } })
-    const headers = { "x-opencode-directory": tmp.path, "content-type": "application/json" }
-    const info = spyOn(Log.create({ service: "server.sync" }), "info")
-
-    const session = await WithInstance.provide({
-      directory: tmp.path,
-      fn: async () => runSession(Session.Service.use((svc) => svc.create({ title: "sync" }))),
-    })
+  it.instance(
+    "serves sync routes",
+    () =>
+      Effect.gen(function* () {
+        Flag.OPENCODE_EXPERIMENTAL_WORKSPACES = true
+        const tmp = yield* TestInstance
+        const headers = { "x-opencode-directory": tmp.directory, "content-type": "application/json" }
+        const info = spyOn(Log.create({ service: "server.sync" }), "info")
+        const session = yield* Session.Service.use((svc) => svc.create({ title: "sync" }))
 
 
-    const started = await app().request(SyncPaths.start, { method: "POST", headers })
-    expect(started.status).toBe(200)
-    expect(await started.json()).toBe(true)
+        const started = yield* Effect.promise(() =>
+          Promise.resolve(app().request(SyncPaths.start, { method: "POST", headers })),
+        )
+        expect(started.status).toBe(200)
+        expect(yield* Effect.promise(() => started.json())).toBe(true)
 
 
-    const history = await app().request(SyncPaths.history, {
-      method: "POST",
-      headers,
-      body: JSON.stringify({}),
-    })
-    expect(history.status).toBe(200)
-    const rows = (await history.json()) as Array<{
-      id: string
-      aggregate_id: string
-      seq: number
-      type: string
-      data: Record<string, unknown>
-    }>
-    expect(rows.map((row) => row.aggregate_id)).toContain(session.id)
+        const history = yield* Effect.promise(() =>
+          Promise.resolve(
+            app().request(SyncPaths.history, {
+              method: "POST",
+              headers,
+              body: JSON.stringify({}),
+            }),
+          ),
+        )
+        expect(history.status).toBe(200)
+        const rows = (yield* Effect.promise(() => history.json())) as Array<{
+          id: string
+          aggregate_id: string
+          seq: number
+          type: string
+          data: Record<string, unknown>
+        }>
+        expect(rows.map((row) => row.aggregate_id)).toContain(session.id)
 
 
-    const replayed = await app().request(SyncPaths.replay, {
-      method: "POST",
-      headers,
-      body: JSON.stringify({
-        directory: tmp.path,
-        events: rows
-          .filter((row) => row.aggregate_id === session.id)
-          .map((row) => ({
-            id: row.id,
-            aggregateID: row.aggregate_id,
-            seq: row.seq,
-            type: row.type,
-            data: row.data,
-          })),
+        const replayed = yield* Effect.promise(() =>
+          Promise.resolve(
+            app().request(SyncPaths.replay, {
+              method: "POST",
+              headers,
+              body: JSON.stringify({
+                directory: tmp.directory,
+                events: rows
+                  .filter((row) => row.aggregate_id === session.id)
+                  .map((row) => ({
+                    id: row.id,
+                    aggregateID: row.aggregate_id,
+                    seq: row.seq,
+                    type: row.type,
+                    data: row.data,
+                  })),
+              }),
+            }),
+          ),
+        )
+        expect(replayed.status).toBe(200)
+        expect(yield* Effect.promise(() => replayed.json())).toEqual({ sessionID: session.id })
+        expect(info.mock.calls.some(([message]) => message === "sync replay requested")).toBe(true)
+        expect(info.mock.calls.some(([message]) => message === "sync replay complete")).toBe(true)
       }),
       }),
-    })
-    expect(replayed.status).toBe(200)
-    expect(await replayed.json()).toEqual({ sessionID: session.id })
-    expect(info.mock.calls.some(([message]) => message === "sync replay requested")).toBe(true)
-    expect(info.mock.calls.some(([message]) => message === "sync replay complete")).toBe(true)
-  })
+    { git: true, config: { formatter: false, lsp: false } },
+  )
 
 
-  test("validates seq values", async () => {
-    await using tmp = await tmpdir({ git: true, config: { formatter: false, lsp: false } })
-    const headers = { "x-opencode-directory": tmp.path, "content-type": "application/json" }
-    const cases = [
-      {
-        path: SyncPaths.history,
-        body: { aggregate: -1 },
-      },
-      {
-        path: SyncPaths.history,
-        body: { aggregate: 1.5 },
-      },
-      {
-        path: SyncPaths.replay,
-        body: {
-          directory: tmp.path,
-          events: [{ id: "event", aggregateID: "session", seq: -1, type: "session.created", data: {} }],
-        },
-      },
-      {
-        path: SyncPaths.replay,
-        body: {
-          directory: tmp.path,
-          events: [{ id: "event", aggregateID: "session", seq: 1.5, type: "session.created", data: {} }],
-        },
-      },
-    ]
+  it.instance(
+    "validates seq values",
+    () =>
+      Effect.gen(function* () {
+        const tmp = yield* TestInstance
+        const headers = { "x-opencode-directory": tmp.directory, "content-type": "application/json" }
+        const cases = [
+          {
+            path: SyncPaths.history,
+            body: { aggregate: -1 },
+          },
+          {
+            path: SyncPaths.history,
+            body: { aggregate: 1.5 },
+          },
+          {
+            path: SyncPaths.replay,
+            body: {
+              directory: tmp.directory,
+              events: [{ id: "event", aggregateID: "session", seq: -1, type: "session.created", data: {} }],
+            },
+          },
+          {
+            path: SyncPaths.replay,
+            body: {
+              directory: tmp.directory,
+              events: [{ id: "event", aggregateID: "session", seq: 1.5, type: "session.created", data: {} }],
+            },
+          },
+        ]
 
 
-    for (const item of cases) {
-      const response = await app().request(item.path, {
-        method: "POST",
-        headers,
-        body: JSON.stringify(item.body),
-      })
-      expect(response.status).toBe(400)
-    }
-  })
-
-  test.todo("returns structured validation errors", async () => {
-    await using tmp = await tmpdir({ git: true, config: { formatter: false, lsp: false } })
-    const response = await ExperimentalHttpApiServer.webHandler().handler(
-      new Request(`http://localhost${SyncPaths.history}`, {
-        method: "POST",
-        headers: { "x-opencode-directory": tmp.path, "content-type": "application/json" },
-        body: JSON.stringify({ aggregate: -1 }),
+        for (const item of cases) {
+          const response = yield* Effect.promise(() =>
+            Promise.resolve(
+              app().request(item.path, {
+                method: "POST",
+                headers,
+                body: JSON.stringify(item.body),
+              }),
+            ),
+          )
+          expect(response.status).toBe(400)
+        }
       }),
       }),
-      context,
-    )
+    { git: true, config: { formatter: false, lsp: false } },
+  )
+
+  it.instance.skip(
+    "returns structured validation errors",
+    () =>
+      Effect.gen(function* () {
+        const tmp = yield* TestInstance
+        const response = yield* Effect.promise(() =>
+          ExperimentalHttpApiServer.webHandler().handler(
+            new Request(`http://localhost${SyncPaths.history}`, {
+              method: "POST",
+              headers: { "x-opencode-directory": tmp.directory, "content-type": "application/json" },
+              body: JSON.stringify({ aggregate: -1 }),
+            }),
+            context,
+          ),
+        )
 
 
-    expect(response.status).toBe(400)
-    expect(response.headers.get("content-type") ?? "").toContain("application/json")
-    const body = (await response.json()) as Record<string, unknown>
-    expect(body.success).toBe(false)
-    expect(Array.isArray(body.error) || Array.isArray(body.errors)).toBe(true)
-  })
+        expect(response.status).toBe(400)
+        expect(response.headers.get("content-type") ?? "").toContain("application/json")
+        const body = (yield* Effect.promise(() => response.json())) as Record<string, unknown>
+        expect(body.success).toBe(false)
+        expect(Array.isArray(body.error) || Array.isArray(body.errors)).toBe(true)
+      }),
+    { git: true, config: { formatter: false, lsp: false } },
+  )
 })
 })