@@ -8,8 +8,8 @@ import type {
88 QaBusInboundMessageInput ,
99 QaBusMessage ,
1010 QaBusOutboundMessageInput ,
11- QaBusSearchMessagesInput ,
1211 QaBusReadMessageInput ,
12+ QaBusSearchMessagesInput ,
1313 QaBusStateSnapshot ,
1414 QaBusWaitForInput ,
1515} from "./runtime-api.js" ;
@@ -56,6 +56,20 @@ type QaTransportFailureAssertionOptions = {
5656 cursorSpace ?: QaTransportFailureCursorSpace ;
5757} ;
5858
59+ export type QaTransportOutboundMatch = {
60+ conversation ?: QaBusInboundMessageInput [ "conversation" ] ;
61+ senderId ?: string ;
62+ sinceIndex ?: number ;
63+ textIncludes ?: string ;
64+ threadId ?: string ;
65+ timeoutMs ?: number ;
66+ } ;
67+
68+ export type QaTransportWaitForNoOutboundInput = {
69+ quietMs ?: number ;
70+ sinceIndex ?: number ;
71+ } ;
72+
5973export type QaTransportCapabilities = {
6074 sendInboundMessage : QaTransportState [ "addInboundMessage" ] ;
6175 injectOutboundMessage : QaTransportState [ "addOutboundMessage" ] ;
@@ -165,6 +179,10 @@ export type QaTransportAdapter = {
165179 supportedActions : readonly QaTransportActionName [ ] ;
166180 state : QaTransportState ;
167181 capabilities : QaTransportCapabilities ;
182+ reset : ( ) => Promise < void > ;
183+ sendInbound : ( input : QaBusInboundMessageInput ) => Promise < QaBusMessage > ;
184+ waitForNoOutbound : ( input ?: QaTransportWaitForNoOutboundInput ) => Promise < void > ;
185+ waitForOutbound : ( input : QaTransportOutboundMatch ) => Promise < QaBusMessage > ;
168186 createGatewayConfig : ( params : { baseUrl : string } ) => QaTransportGatewayConfig ;
169187 waitReady : ( params : {
170188 gateway : QaTransportGatewayClient ;
@@ -248,4 +266,57 @@ export abstract class QaStateBackedTransportAdapter implements QaTransportAdapte
248266 accountId ?: string | null ;
249267 } ) => Promise < unknown > ;
250268 abstract createReportNotes : ( params : QaTransportReportParams ) => string [ ] ;
269+
270+ async reset ( ) {
271+ await this . state . reset ( ) ;
272+ }
273+
274+ async sendInbound ( input : QaBusInboundMessageInput ) {
275+ return await this . state . addInboundMessage ( input ) ;
276+ }
277+
278+ async waitForNoOutbound ( input : QaTransportWaitForNoOutboundInput = { } ) {
279+ const quietMs = resolveTimerTimeoutMs ( input . quietMs , 1_200 , 0 ) ;
280+ await sleep ( quietMs ) ;
281+ assertNoFailureReplies ( this . state , {
282+ sinceIndex : input . sinceIndex ,
283+ cursorSpace : "outbound" ,
284+ } ) ;
285+ const observed = this . outboundSince ( input . sinceIndex ) ;
286+ if ( observed . length > 0 ) {
287+ const summary = observed . map ( ( message ) => `${ message . id } :${ message . text } ` ) . join ( "\n" ) ;
288+ throw new Error ( `expected no outbound messages for ${ quietMs } ms, saw:\n${ summary } ` ) ;
289+ }
290+ }
291+
292+ async waitForOutbound ( input : QaTransportOutboundMatch ) {
293+ return await waitForQaTransportCondition ( ( ) => {
294+ assertNoFailureReplies ( this . state , {
295+ sinceIndex : input . sinceIndex ,
296+ cursorSpace : "outbound" ,
297+ } ) ;
298+ return this . outboundSince ( input . sinceIndex ) . find ( ( message ) => {
299+ if ( input . conversation && message . conversation . id !== input . conversation . id ) {
300+ return false ;
301+ }
302+ if ( input . conversation && message . conversation . kind !== input . conversation . kind ) {
303+ return false ;
304+ }
305+ if ( input . senderId && message . senderId !== input . senderId ) {
306+ return false ;
307+ }
308+ if ( input . threadId && message . threadId !== input . threadId ) {
309+ return false ;
310+ }
311+ return ! input . textIncludes || message . text . includes ( input . textIncludes ) ;
312+ } ) ;
313+ } , input . timeoutMs ) ;
314+ }
315+
316+ private outboundSince ( sinceIndex = 0 ) {
317+ return this . state
318+ . getSnapshot ( )
319+ . messages . filter ( ( message ) => message . direction === "outbound" )
320+ . slice ( sinceIndex ) ;
321+ }
251322}
0 commit comments