|
1 | 1 | import { beforeEach, describe, expect, it, vi } from "vitest"; |
2 | 2 | import type { OpenClawConfig } from "../../config/config.js"; |
3 | 3 | import { createAcpDispatchDeliveryCoordinator } from "./dispatch-acp-delivery.js"; |
4 | | -import type { ReplyDispatcher } from "./reply-dispatcher.js"; |
| 4 | +import { createReplyDispatcher, type ReplyDispatcher } from "./reply-dispatcher.js"; |
5 | 5 | import { buildTestCtx } from "./test-ctx.js"; |
6 | 6 | import { createAcpTestConfig } from "./test-fixtures/acp-runtime.js"; |
7 | 7 |
|
@@ -240,6 +240,93 @@ describe("createAcpDispatchDeliveryCoordinator", () => { |
240 | 240 | expect(coordinator.getRoutedCounts().block).toBe(0); |
241 | 241 | }); |
242 | 242 |
|
| 243 | + it("waits for direct block dispatcher delivery before resolving block delivery", async () => { |
| 244 | + const delivered: unknown[] = []; |
| 245 | + let releaseDelivery: (() => void) | undefined; |
| 246 | + let markDeliveryStarted: (() => void) | undefined; |
| 247 | + const deliveryStarted = new Promise<void>((resolve) => { |
| 248 | + markDeliveryStarted = resolve; |
| 249 | + }); |
| 250 | + const deliveryGate = new Promise<void>((resolve) => { |
| 251 | + releaseDelivery = resolve; |
| 252 | + }); |
| 253 | + const dispatcher = createReplyDispatcher({ |
| 254 | + deliver: async (payload) => { |
| 255 | + delivered.push(payload); |
| 256 | + markDeliveryStarted?.(); |
| 257 | + await deliveryGate; |
| 258 | + }, |
| 259 | + }); |
| 260 | + const coordinator = createAcpDispatchDeliveryCoordinator({ |
| 261 | + cfg: createAcpTestConfig(), |
| 262 | + ctx: buildTestCtx({ |
| 263 | + Provider: "visiblechat", |
| 264 | + Surface: "visiblechat", |
| 265 | + SessionKey: "agent:codex-acp:session-1", |
| 266 | + }), |
| 267 | + dispatcher, |
| 268 | + inboundAudio: false, |
| 269 | + shouldRouteToOriginating: false, |
| 270 | + }); |
| 271 | + |
| 272 | + let deliverySettled = false; |
| 273 | + const deliveryPromise = coordinator.deliver("block", { text: "hello" }, { skipTts: true }); |
| 274 | + void deliveryPromise.then(() => { |
| 275 | + deliverySettled = true; |
| 276 | + }); |
| 277 | + |
| 278 | + await deliveryStarted; |
| 279 | + |
| 280 | + expect(delivered).toEqual([{ text: "hello" }]); |
| 281 | + expect(deliverySettled).toBe(false); |
| 282 | + |
| 283 | + releaseDelivery?.(); |
| 284 | + await expect(deliveryPromise).resolves.toBe(true); |
| 285 | + expect(deliverySettled).toBe(true); |
| 286 | + }); |
| 287 | + |
| 288 | + it("stops waiting for direct block delivery when the ACP dispatch aborts", async () => { |
| 289 | + const delivered: unknown[] = []; |
| 290 | + const controller = new AbortController(); |
| 291 | + let releaseDelivery: (() => void) | undefined; |
| 292 | + let markDeliveryStarted: (() => void) | undefined; |
| 293 | + const deliveryStarted = new Promise<void>((resolve) => { |
| 294 | + markDeliveryStarted = resolve; |
| 295 | + }); |
| 296 | + const deliveryGate = new Promise<void>((resolve) => { |
| 297 | + releaseDelivery = resolve; |
| 298 | + }); |
| 299 | + const dispatcher = createReplyDispatcher({ |
| 300 | + deliver: async (payload) => { |
| 301 | + delivered.push(payload); |
| 302 | + markDeliveryStarted?.(); |
| 303 | + await deliveryGate; |
| 304 | + }, |
| 305 | + }); |
| 306 | + const coordinator = createAcpDispatchDeliveryCoordinator({ |
| 307 | + cfg: createAcpTestConfig(), |
| 308 | + ctx: buildTestCtx({ |
| 309 | + Provider: "visiblechat", |
| 310 | + Surface: "visiblechat", |
| 311 | + SessionKey: "agent:codex-acp:session-1", |
| 312 | + }), |
| 313 | + dispatcher, |
| 314 | + inboundAudio: false, |
| 315 | + shouldRouteToOriginating: false, |
| 316 | + abortSignal: controller.signal, |
| 317 | + }); |
| 318 | + |
| 319 | + const deliveryPromise = coordinator.deliver("block", { text: "hello" }, { skipTts: true }); |
| 320 | + await deliveryStarted; |
| 321 | + controller.abort(); |
| 322 | + |
| 323 | + await expect(deliveryPromise).resolves.toBe(true); |
| 324 | + expect(delivered).toEqual([{ text: "hello" }]); |
| 325 | + |
| 326 | + releaseDelivery?.(); |
| 327 | + await dispatcher.waitForIdle(); |
| 328 | + }); |
| 329 | + |
243 | 330 | it("strips split TTS directives from visible ACP block delivery", async () => { |
244 | 331 | const dispatcher = createDispatcher(); |
245 | 332 | const coordinator = createAcpDispatchDeliveryCoordinator({ |
|
0 commit comments