Jelajahi Sumber

test(server): migrate project init git test to Effect runner (#27218)

Kit Langton 3 bulan lalu
induk
melakukan
0879f5e22d
1 mengubah file dengan 86 tambahan dan 79 penghapusan
  1. 86 79
      packages/opencode/test/server/project-init-git.test.ts

+ 86 - 79
packages/opencode/test/server/project-init-git.test.ts

@@ -1,113 +1,120 @@
-import { afterEach, describe, expect, test } from "bun:test"
-import { Effect } from "effect"
+import { afterEach, describe, expect } from "bun:test"
+import { AppFileSystem } from "@opencode-ai/core/filesystem"
+import { Effect, Layer } from "effect"
 import path from "path"
-import { GlobalBus } from "../../src/bus/global"
+import { InstanceRef } from "../../src/effect/instance-ref"
+import { InstanceBootstrap } from "../../src/project/bootstrap-service"
+import { InstanceStore } from "../../src/project/instance-store"
+import { GlobalBus, type GlobalEvent } from "../../src/bus/global"
 import { Snapshot } from "../../src/snapshot"
 import { Server } from "../../src/server/server"
-import { Filesystem } from "@/util/filesystem"
 import * as Log from "@opencode-ai/core/util/log"
 import { resetDatabase } from "../fixture/db"
-import { disposeAllInstances, provideInstance, tmpdir } from "../fixture/fixture"
+import { disposeAllInstances, TestInstance } from "../fixture/fixture"
+import { testEffect } from "../lib/effect"
 
 void Log.init({ print: false })
 
 afterEach(async () => {
+  await disposeAllInstances()
   await resetDatabase()
 })
 
-const disposedEvents = (seen: { directory?: string; payload: { type: string } }[], dir: string) =>
+const noopBootstrap = Layer.succeed(InstanceBootstrap.Service, InstanceBootstrap.Service.of({ run: Effect.void }))
+const testInstanceStore = InstanceStore.defaultLayer.pipe(Layer.provide(noopBootstrap))
+
+const it = testEffect(Layer.mergeAll(AppFileSystem.defaultLayer, Snapshot.defaultLayer, testInstanceStore))
+
+function request(directory: string, url: string, init: RequestInit = {}) {
+  return Effect.promise(() => {
+    const headers = new Headers(init.headers)
+    headers.set("x-opencode-directory", directory)
+    return Promise.resolve(Server.Default().app.request(url, { ...init, headers }))
+  })
+}
+
+function json<T>(response: Response) {
+  return Effect.promise(() => response.json() as Promise<T>)
+}
+
+function collectGlobalEvents() {
+  return Effect.acquireRelease(
+    Effect.sync(() => {
+      const seen: GlobalEvent[] = []
+      const on = (event: GlobalEvent) => {
+        seen.push(event)
+      }
+      GlobalBus.on("event", on)
+      return { seen, on }
+    }),
+    ({ on }) => Effect.sync(() => GlobalBus.off("event", on)),
+  )
+}
+
+const disposedEvents = (seen: GlobalEvent[], dir: string) =>
   seen.filter((evt) => evt.directory === dir && evt.payload.type === "server.instance.disposed").length
 
 describe("project.initGit endpoint", () => {
-  test("initializes git and reloads immediately", async () => {
-    await using tmp = await tmpdir()
-    const app = Server.Default().app
-    const seen: { directory?: string; payload: { type: string } }[] = []
-    const fn = (evt: { directory?: string; payload: { type: string } }) => {
-      seen.push(evt)
-    }
-    GlobalBus.on("event", fn)
+  it.instance("initializes git and reloads immediately", () =>
+    Effect.gen(function* () {
+      const tmp = yield* TestInstance
+      const fs = yield* AppFileSystem.Service
+      const events = yield* collectGlobalEvents()
 
-    try {
-      const init = await app.request("/project/git/init", {
+      const init = yield* request(tmp.directory, "/project/git/init", {
         method: "POST",
-        headers: {
-          "x-opencode-directory": tmp.path,
-        },
       })
-      const body = await init.json()
+      const body = yield* json(init)
       expect(init.status).toBe(200)
       expect(body).toMatchObject({
         id: "global",
         vcs: "git",
-        worktree: tmp.path,
+        worktree: tmp.directory,
       })
       // Reload behavior: bus emits exactly one server.instance.disposed for the directory.
-      expect(disposedEvents(seen, tmp.path)).toBe(1)
-      expect(await Filesystem.exists(path.join(tmp.path, ".git", "opencode"))).toBe(false)
+      expect(disposedEvents(events.seen, tmp.directory)).toBe(1)
+      expect(yield* fs.exists(path.join(tmp.directory, ".git", "opencode"))).toBe(false)
 
-      const current = await app.request("/project/current", {
-        headers: {
-          "x-opencode-directory": tmp.path,
-        },
-      })
+      const current = yield* request(tmp.directory, "/project/current")
       expect(current.status).toBe(200)
-      expect(await current.json()).toMatchObject({
+      expect(yield* json(current)).toMatchObject({
         id: "global",
         vcs: "git",
-        worktree: tmp.path,
+        worktree: tmp.directory,
       })
 
-      expect(
-        await Effect.runPromise(
-          Snapshot.Service.use((svc) => svc.track()).pipe(
-            provideInstance(tmp.path),
-            Effect.provide(Snapshot.defaultLayer),
-          ),
-        ),
-      ).toBeTruthy()
-    } finally {
-      await disposeAllInstances()
-      GlobalBus.off("event", fn)
-    }
-  })
+      const ctx = yield* InstanceStore.Service.use((store) => store.reload({ directory: tmp.directory }))
+      const tracked = yield* Snapshot.Service.use((snapshot) => snapshot.track()).pipe(
+        Effect.provideService(InstanceRef, ctx),
+      )
+      expect(tracked).toBeTruthy()
+    }),
+  )
 
-  test("does not reload when the project is already git", async () => {
-    await using tmp = await tmpdir({ git: true })
-    const app = Server.Default().app
-    const seen: { directory?: string; payload: { type: string } }[] = []
-    const fn = (evt: { directory?: string; payload: { type: string } }) => {
-      seen.push(evt)
-    }
-    GlobalBus.on("event", fn)
+  it.instance(
+    "does not reload when the project is already git",
+    () =>
+      Effect.gen(function* () {
+        const tmp = yield* TestInstance
+        const events = yield* collectGlobalEvents()
 
-    try {
-      const init = await app.request("/project/git/init", {
-        method: "POST",
-        headers: {
-          "x-opencode-directory": tmp.path,
-        },
-      })
-      expect(init.status).toBe(200)
-      expect(await init.json()).toMatchObject({
-        vcs: "git",
-        worktree: tmp.path,
-      })
-      expect(disposedEvents(seen, tmp.path)).toBe(0)
+        const init = yield* request(tmp.directory, "/project/git/init", {
+          method: "POST",
+        })
+        expect(init.status).toBe(200)
+        expect(yield* json(init)).toMatchObject({
+          vcs: "git",
+          worktree: tmp.directory,
+        })
+        expect(disposedEvents(events.seen, tmp.directory)).toBe(0)
 
-      const current = await app.request("/project/current", {
-        headers: {
-          "x-opencode-directory": tmp.path,
-        },
-      })
-      expect(current.status).toBe(200)
-      expect(await current.json()).toMatchObject({
-        vcs: "git",
-        worktree: tmp.path,
-      })
-    } finally {
-      await disposeAllInstances()
-      GlobalBus.off("event", fn)
-    }
-  })
+        const current = yield* request(tmp.directory, "/project/current")
+        expect(current.status).toBe(200)
+        expect(yield* json(current)).toMatchObject({
+          vcs: "git",
+          worktree: tmp.directory,
+        })
+      }),
+    { git: true },
+  )
 })