Explorar o código

test(pty): migrate session tests to Effect runner (#27222)

Kit Langton hai 4 meses
pai
achega
c96a77c60b
Modificáronse 1 ficheiros con 80 adicións e 87 borrados
  1. 80 87
      packages/opencode/test/pty/pty-session.test.ts

+ 80 - 87
packages/opencode/test/pty/pty-session.test.ts

@@ -1,103 +1,96 @@
-import { describe, expect, test } from "bun:test"
-import { AppRuntime } from "../../src/effect/app-runtime"
+import { describe, expect } from "bun:test"
 import { Bus } from "../../src/bus"
-import { Effect } from "effect"
-import { Instance } from "../../src/project/instance"
-import { WithInstance } from "../../src/project/with-instance"
+import { Config } from "../../src/config/config"
+import { Plugin } from "../../src/plugin"
 import { Pty } from "../../src/pty"
 import type { PtyID } from "../../src/pty/schema"
-import { tmpdir } from "../fixture/fixture"
-import { setTimeout as sleep } from "node:timers/promises"
+import { Effect, Layer, Queue } from "effect"
+import { testEffect } from "../lib/effect"
 
-const wait = async (fn: () => boolean, ms = 5000) => {
-  const end = Date.now() + ms
-  while (Date.now() < end) {
-    if (fn()) return
-    await sleep(25)
-  }
-  throw new Error("timeout waiting for pty events")
-}
-
-const pick = (log: Array<{ type: "created" | "exited" | "deleted"; id: PtyID }>, id: PtyID) => {
-  return log.filter((evt) => evt.id === id).map((evt) => evt.type)
-}
-
-describe("pty", () => {
-  test("publishes created, exited, deleted in order for a short-lived process", async () => {
-    if (process.platform === "win32") return
+type PtyEvent = { type: "created" | "exited" | "deleted"; id: PtyID }
 
-    await using dir = await tmpdir({ git: true })
+const it = testEffect(
+  Pty.layer.pipe(
+    Layer.provideMerge(Bus.layer),
+    Layer.provideMerge(Config.defaultLayer),
+    Layer.provideMerge(Plugin.defaultLayer),
+  ),
+)
+const ptyTest = process.platform === "win32" ? it.instance.skip : it.instance
 
-    await WithInstance.provide({
-      directory: dir.path,
-      fn: () =>
-        AppRuntime.runPromise(
-          Effect.gen(function* () {
-            const pty = yield* Pty.Service
-            const log: Array<{ type: "created" | "exited" | "deleted"; id: PtyID }> = []
-            const off = [
-              Bus.subscribe(Pty.Event.Created, (evt) => log.push({ type: "created", id: evt.properties.info.id })),
-              Bus.subscribe(Pty.Event.Exited, (evt) => log.push({ type: "exited", id: evt.properties.id })),
-              Bus.subscribe(Pty.Event.Deleted, (evt) => log.push({ type: "deleted", id: evt.properties.id })),
-            ]
+const subscribePtyEvents = Effect.fn("PtySessionTest.subscribePtyEvents")(function* () {
+  const bus = yield* Bus.Service
+  const events = yield* Queue.unbounded<PtyEvent>()
 
-            let id: PtyID | undefined
-            try {
-              const info = yield* pty.create({
-                command: "/usr/bin/env",
-                args: ["sh", "-c", "sleep 0.1"],
-                title: "sleep",
-              })
-              id = info.id
+  const subscribe = <A>(effect: Effect.Effect<() => void, never, A>) =>
+    Effect.acquireRelease(effect, (off) => Effect.sync(off))
 
-              yield* Effect.promise(() => wait(() => pick(log, id!).includes("exited")))
+  yield* subscribe(
+    bus.subscribeCallback(Pty.Event.Created, (evt) => {
+      Queue.offerUnsafe(events, { type: "created", id: evt.properties.info.id })
+    }),
+  )
+  yield* subscribe(
+    bus.subscribeCallback(Pty.Event.Exited, (evt) => {
+      Queue.offerUnsafe(events, { type: "exited", id: evt.properties.id })
+    }),
+  )
+  yield* subscribe(
+    bus.subscribeCallback(Pty.Event.Deleted, (evt) => {
+      Queue.offerUnsafe(events, { type: "deleted", id: evt.properties.id })
+    }),
+  )
 
-              yield* pty.remove(id)
-              yield* Effect.promise(() => wait(() => pick(log, id!).length >= 3))
-              expect(pick(log, id!)).toEqual(["created", "exited", "deleted"])
-            } finally {
-              off.forEach((x) => x())
-              if (id) yield* pty.remove(id)
-            }
-          }),
-        ),
-    })
-  })
+  return events
+})
 
-  test("publishes created, exited, deleted in order for /bin/sh + remove", async () => {
-    if (process.platform === "win32") return
+const createPty = Effect.fn("PtySessionTest.createPty")(function* (input: Pty.CreateInput) {
+  const pty = yield* Pty.Service
+  return yield* Effect.acquireRelease(pty.create(input), (info) => pty.remove(info.id).pipe(Effect.ignore))
+})
 
-    await using dir = await tmpdir({ git: true })
+const waitForEvents = (events: Queue.Queue<PtyEvent>, id: PtyID, count: number) => {
+  return Effect.gen(function* () {
+    const picked: Array<PtyEvent["type"]> = []
+    while (picked.length < count) {
+      const evt = yield* Queue.take(events)
+      if (evt.id === id) picked.push(evt.type)
+    }
+    return picked
+  }).pipe(
+    Effect.timeoutOrElse({
+      duration: "5 seconds",
+      orElse: () => Effect.fail(new Error("timeout waiting for pty events")),
+    }),
+  )
+}
 
-    await WithInstance.provide({
-      directory: dir.path,
-      fn: () =>
-        AppRuntime.runPromise(
-          Effect.gen(function* () {
-            const pty = yield* Pty.Service
-            const log: Array<{ type: "created" | "exited" | "deleted"; id: PtyID }> = []
-            const off = [
-              Bus.subscribe(Pty.Event.Created, (evt) => log.push({ type: "created", id: evt.properties.info.id })),
-              Bus.subscribe(Pty.Event.Exited, (evt) => log.push({ type: "exited", id: evt.properties.id })),
-              Bus.subscribe(Pty.Event.Deleted, (evt) => log.push({ type: "deleted", id: evt.properties.id })),
-            ]
+describe("pty", () => {
+  ptyTest("publishes created, exited, deleted in order for a short-lived process", () =>
+    Effect.gen(function* () {
+      const events = yield* subscribePtyEvents()
+      const info = yield* createPty({
+        command: "/usr/bin/env",
+        args: ["sh", "-c", "sleep 0.1"],
+        title: "sleep",
+      })
 
-            let id: PtyID | undefined
-            try {
-              const info = yield* pty.create({ command: "/bin/sh", title: "sh" })
-              id = info.id
+      expect(yield* waitForEvents(events, info.id, 3)).toEqual(["created", "exited", "deleted"])
+    }),
+    { git: true },
+  )
 
-              yield* Effect.promise(() => sleep(100))
+  ptyTest("publishes created, exited, deleted in order for /bin/sh + remove", () =>
+    Effect.gen(function* () {
+      const pty = yield* Pty.Service
+      const events = yield* subscribePtyEvents()
+      const info = yield* createPty({ command: "/bin/sh", title: "sh" })
 
-              yield* pty.remove(id)
-              yield* Effect.promise(() => wait(() => pick(log, id!).length >= 3))
-              expect(pick(log, id!)).toEqual(["created", "exited", "deleted"])
-            } finally {
-              off.forEach((x) => x())
-              if (id) yield* pty.remove(id)
-            }
-          }),
-        ),
-    })
-  })
+      expect(yield* waitForEvents(events, info.id, 1)).toEqual(["created"])
+      yield* pty.write(info.id, "exit\n")
+      expect(yield* waitForEvents(events, info.id, 2)).toEqual(["exited", "deleted"])
+      yield* pty.remove(info.id)
+    }),
+    { git: true },
+  )
 })