Skip to content

Commit 855bda8

Browse files
authored
test(question): wait on question events (anomalyco#27124)
1 parent 756488d commit 855bda8

2 files changed

Lines changed: 32 additions & 14 deletions

File tree

packages/opencode/test/question/question.test.ts

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { afterEach, expect } from "bun:test"
2-
import { Cause, Effect, Exit, Fiber, Layer } from "effect"
2+
import { Cause, Effect, Exit, Fiber, Layer, Queue } from "effect"
33
import { Question } from "../../src/question"
44
import { Instance } from "../../src/project/instance"
55
import { InstanceRuntime } from "../../src/project/instance-runtime"
@@ -8,8 +8,11 @@ import { disposeAllInstances, provideInstance, reloadTestInstance, tmpdirScoped
88
import { SessionID } from "../../src/session/schema"
99
import { testEffect } from "../lib/effect"
1010
import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner"
11+
import { Bus } from "../../src/bus"
1112

12-
const it = testEffect(Layer.mergeAll(Question.defaultLayer, CrossSpawnSpawner.defaultLayer))
13+
const it = testEffect(
14+
Layer.mergeAll(Question.layer.pipe(Layer.provideMerge(Bus.layer)), CrossSpawnSpawner.defaultLayer),
15+
)
1316

1417
const askEffect = Effect.fn("QuestionTest.ask")(function* (input: {
1518
sessionID: SessionID
@@ -44,15 +47,19 @@ const rejectAll = Effect.gen(function* () {
4447
yield* Effect.forEach(yield* listEffect, (req) => rejectEffect(req.id), { discard: true })
4548
})
4649

47-
const waitForPending = (count: number) =>
48-
Effect.gen(function* () {
49-
for (let i = 0; i < 100; i++) {
50-
const pending = yield* listEffect
51-
if (pending.length === count) return pending
52-
yield* Effect.sleep("10 millis")
53-
}
54-
return yield* Effect.fail(new Error(`timed out waiting for ${count} pending question request(s)`))
55-
})
50+
const waitForPending = Effect.fn("QuestionTest.waitForPending")(function* (count: number) {
51+
const question = yield* Question.Service
52+
const bus = yield* Bus.Service
53+
const asked = yield* Queue.unbounded<void>()
54+
const off = yield* bus.subscribeCallback(Question.Event.Asked, () => Queue.offerUnsafe(asked, undefined))
55+
yield* Effect.addFinalizer(() => Effect.sync(off))
56+
57+
for (;;) {
58+
const pending = yield* question.list()
59+
if (pending.length === count) return pending
60+
yield* Queue.take(asked).pipe(Effect.timeout("2 seconds"))
61+
}
62+
})
5663

5764
it.instance(
5865
"ask - remains pending until answered",

packages/opencode/test/tool/question.test.ts

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import { describe, expect } from "bun:test"
2-
import { Effect, Fiber, Layer } from "effect"
2+
import { Effect, Fiber, Layer, Queue } from "effect"
33
import { QuestionTool } from "../../src/tool/question"
44
import { Question } from "../../src/question"
55
import { SessionID, MessageID } from "../../src/session/schema"
66
import { Agent } from "../../src/agent/agent"
77
import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner"
88
import { Truncate } from "@/tool/truncate"
99
import { testEffect } from "../lib/effect"
10+
import { Bus } from "../../src/bus"
1011

1112
const ctx = {
1213
sessionID: SessionID.make("ses_test-session"),
@@ -20,15 +21,25 @@ const ctx = {
2021
}
2122

2223
const it = testEffect(
23-
Layer.mergeAll(Question.defaultLayer, CrossSpawnSpawner.defaultLayer, Truncate.defaultLayer, Agent.defaultLayer),
24+
Layer.mergeAll(
25+
Question.layer.pipe(Layer.provideMerge(Bus.layer)),
26+
CrossSpawnSpawner.defaultLayer,
27+
Truncate.defaultLayer,
28+
Agent.defaultLayer,
29+
),
2430
)
2531

2632
const pending = Effect.fn("QuestionToolTest.pending")(function* (question: Question.Interface) {
33+
const bus = yield* Bus.Service
34+
const asked = yield* Queue.unbounded<void>()
35+
const off = yield* bus.subscribeCallback(Question.Event.Asked, () => Queue.offerUnsafe(asked, undefined))
36+
yield* Effect.addFinalizer(() => Effect.sync(off))
37+
2738
for (;;) {
2839
const items = yield* question.list()
2940
const item = items[0]
3041
if (item) return item
31-
yield* Effect.sleep("10 millis")
42+
yield* Queue.take(asked).pipe(Effect.timeout("2 seconds"))
3243
}
3344
})
3445

0 commit comments

Comments
 (0)