test(bus): migrate integration tests to Effect runner (#27132)

This commit is contained in:
Kit Langton
2026-05-12 15:58:54 -04:00
committed by GitHub
parent f7dbb4dac4
commit e0d0fe1ff7

View File

@@ -1,88 +1,88 @@
import { afterEach, describe, expect, test } from "bun:test" import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner"
import { Schema } from "effect" import { afterEach, describe, expect } from "bun:test"
import { Deferred, Effect, Layer, Schema } from "effect"
import { Bus } from "../../src/bus" import { Bus } from "../../src/bus"
import { BusEvent } from "../../src/bus/bus-event" import { BusEvent } from "../../src/bus/bus-event"
import { Instance } from "../../src/project/instance" import { disposeAllInstances, provideInstance, tmpdirScoped } from "../fixture/fixture"
import { WithInstance } from "../../src/project/with-instance" import { testEffect } from "../lib/effect"
import { disposeAllInstances, tmpdir } from "../fixture/fixture"
const TestEvent = BusEvent.define("test.integration", Schema.Struct({ value: Schema.Number })) const TestEvent = BusEvent.define("test.integration", Schema.Struct({ value: Schema.Number }))
const it = testEffect(Layer.mergeAll(Bus.layer, CrossSpawnSpawner.defaultLayer))
function withInstance(directory: string, fn: () => Promise<void>) {
return WithInstance.provide({ directory, fn })
}
describe("Bus integration: acquireRelease subscriber pattern", () => { describe("Bus integration: acquireRelease subscriber pattern", () => {
afterEach(() => disposeAllInstances()) afterEach(() => disposeAllInstances())
test("subscriber via callback facade receives events and cleans up on unsub", async () => { it.instance("subscriber via callback facade receives events and cleans up on unsub", () =>
await using tmp = await tmpdir() Effect.gen(function* () {
const received: number[] = [] const bus = yield* Bus.Service
const received: number[] = []
const receivedTwo = yield* Deferred.make<void>()
await withInstance(tmp.path, async () => { const unsub = yield* bus.subscribeCallback(TestEvent, (evt) => {
const unsub = Bus.subscribe(TestEvent, (evt) => {
received.push(evt.properties.value) received.push(evt.properties.value)
if (received.length === 2) Deferred.doneUnsafe(receivedTwo, Effect.void)
}) })
await Bun.sleep(10) yield* bus.publish(TestEvent, { value: 1 })
await Bus.publish(TestEvent, { value: 1 }) yield* bus.publish(TestEvent, { value: 2 })
await Bus.publish(TestEvent, { value: 2 }) yield* Deferred.await(receivedTwo).pipe(Effect.timeout("2 seconds"))
await Bun.sleep(10)
expect(received).toEqual([1, 2]) expect(received).toEqual([1, 2])
unsub() yield* Effect.sync(unsub)
await Bun.sleep(10) yield* bus.publish(TestEvent, { value: 3 })
await Bus.publish(TestEvent, { value: 3 }) yield* Effect.sleep("10 millis")
await Bun.sleep(10)
expect(received).toEqual([1, 2]) expect(received).toEqual([1, 2])
}) }),
}) )
test("subscribeAll receives events from multiple types", async () => { it.instance("subscribeAll receives events from multiple types", () =>
await using tmp = await tmpdir() Effect.gen(function* () {
const received: Array<{ type: string; value?: number }> = [] const bus = yield* Bus.Service
const received: Array<{ type: string; value?: number }> = []
const OtherEvent = BusEvent.define("test.other", Schema.Struct({ value: Schema.Number }))
const receivedTwo = yield* Deferred.make<void>()
const OtherEvent = BusEvent.define("test.other", Schema.Struct({ value: Schema.Number })) yield* bus.subscribeAllCallback((evt) => {
await withInstance(tmp.path, async () => {
Bus.subscribeAll((evt) => {
received.push({ type: evt.type, value: evt.properties.value }) received.push({ type: evt.type, value: evt.properties.value })
if (received.length === 2) Deferred.doneUnsafe(receivedTwo, Effect.void)
}) })
await Bun.sleep(10) yield* bus.publish(TestEvent, { value: 10 })
await Bus.publish(TestEvent, { value: 10 }) yield* bus.publish(OtherEvent, { value: 20 })
await Bus.publish(OtherEvent, { value: 20 }) yield* Deferred.await(receivedTwo).pipe(Effect.timeout("2 seconds"))
await Bun.sleep(10)
})
expect(received).toEqual([ expect(received).toEqual([
{ type: "test.integration", value: 10 }, { type: "test.integration", value: 10 },
{ type: "test.other", value: 20 }, { type: "test.other", value: 20 },
]) ])
}) }),
)
test("subscriber cleanup on instance disposal interrupts the stream", async () => { it.live("subscriber cleanup on instance disposal interrupts the stream", () =>
await using tmp = await tmpdir() Effect.gen(function* () {
const received: number[] = [] const dir = yield* tmpdirScoped()
let disposed = false const received: number[] = []
const seen = yield* Deferred.make<void>()
const disposed = yield* Deferred.make<void>()
await withInstance(tmp.path, async () => { yield* Effect.gen(function* () {
Bus.subscribeAll((evt) => { const bus = yield* Bus.Service
if (evt.type === Bus.InstanceDisposed.type) { yield* bus.subscribeAllCallback((evt) => {
disposed = true if (evt.type === Bus.InstanceDisposed.type) {
return Deferred.doneUnsafe(disposed, Effect.void)
} return
received.push(evt.properties.value) }
}) received.push(evt.properties.value)
await Bun.sleep(10) Deferred.doneUnsafe(seen, Effect.void)
await Bus.publish(TestEvent, { value: 1 }) })
await Bun.sleep(10) yield* bus.publish(TestEvent, { value: 1 })
}) yield* Deferred.await(seen).pipe(Effect.timeout("2 seconds"))
}).pipe(provideInstance(dir))
await disposeAllInstances() yield* Effect.promise(() => disposeAllInstances())
await Bun.sleep(50) yield* Deferred.await(disposed).pipe(Effect.timeout("2 seconds"))
expect(received).toEqual([1]) expect(received).toEqual([1])
expect(disposed).toBe(true) }),
}) )
}) })