@@ -3,6 +3,18 @@ const assert = require('node:assert/strict');
33const fs = require ( 'node:fs' ) ;
44const path = require ( 'node:path' ) ;
55const ts = require ( 'typescript' ) ;
6+ const activeControllers = new Set ( ) ;
7+ test . afterEach ( ( ) => {
8+ for ( const controller of activeControllers ) controller . stop ( ) ;
9+ activeControllers . clear ( ) ;
10+ } ) ;
11+ function createController ( Controller , manager , hooks ) {
12+ // These transcript fixtures model hosts without a message queue. Leaving
13+ // the capability method absent starts error/retry timers in the real store.
14+ const controller = new Controller ( Object . assign ( manager , { supportsMessageQueue : ( ) => false } ) , hooks ) ;
15+ activeControllers . add ( controller ) ;
16+ return controller ;
17+ }
618function load ( name , dependencies = { } ) {
719 const source = fs . readFileSync ( path . join ( __dirname , '../../entry/src/main/ets/services' , `${ name } .ets` ) , 'utf8' ) ;
820 const compiled = ts . transpileModule ( source , { compilerOptions : { target : ts . ScriptTarget . ES2022 , module : ts . ModuleKind . CommonJS } } ) . outputText ;
@@ -18,6 +30,7 @@ const { DurableSessionReducer } = reducerModule;
1830const subagentModule = load ( 'SubagentStreamCoordinator' , { './DurableSessionReducer' : reducerModule } ) ;
1931function loadController ( ) {
2032 return load ( 'ChatSessionController' , {
33+ './MessageQueueStore' : load ( 'MessageQueueStore' , { '../model/MessageQueue' : load ( '../model/MessageQueue' ) } ) ,
2134 './DurableSessionReducer' : reducerModule , './InteractionMailboxStore' : mailboxModule ,
2235 './SubagentStreamCoordinator' : subagentModule
2336 } ) ;
@@ -33,7 +46,7 @@ function suggestionFixture() {
3346 const snapshots = [ ] ;
3447 const manager = { getModelCatalog : async ( ) => ( { version : 1 , models : [ ] , default_models : { } } ) ,
3548 subscribeSession : ( _id , next ) => { callbacks = next ; return { wake ( ) { } , close ( ) { } } ; } } ;
36- const controller = new ChatSessionController ( manager , { onSnapshot : value => snapshots . push ( value ) , canPoll : ( ) => true , onError : error => { throw error ; } } ) ;
49+ const controller = createController ( ChatSessionController , Object . assign ( manager , { supportsMessageQueue : ( ) => false } ) , { onSnapshot : value => snapshots . push ( value ) , canPoll : ( ) => true , onError : error => { throw error ; } } ) ;
3750 controller . start ( 'session' , { pollVersion : 0 , knownMessageCount : 0 , knownModelCatalogVersion : 0 } ) ;
3851 return { controller, snapshots, stream : ( ) => callbacks , latest : ( ) => snapshots . at ( - 1 ) } ;
3952}
@@ -126,7 +139,7 @@ test('controller hydrates from the same record log without transcript RPC', asyn
126139 const { ChatSessionController } = loadController ( ) ;
127140 let callbacks , snapshots = [ ] ;
128141 const manager = { getModelCatalog : async ( ) => ( { version : 1 , models : [ ] , default_models : { } } ) , subscribeSession : ( _id , next ) => { callbacks = next ; return { wake ( ) { } , close ( ) { } } ; } , getSessionMessages : ( ) => { throw new Error ( 'Snapshot RPC is forbidden' ) ; } } ;
129- const controller = new ChatSessionController ( manager , { onSnapshot : value => snapshots . push ( value ) , canPoll : ( ) => true , onError : error => { throw error ; } } ) ;
142+ const controller = createController ( ChatSessionController , Object . assign ( manager , { supportsMessageQueue : ( ) => false } ) , { onSnapshot : value => snapshots . push ( value ) , canPoll : ( ) => true , onError : error => { throw error ; } } ) ;
130143 controller . start ( 'session' , { pollVersion : 0 , knownMessageCount : 0 , knownModelCatalogVersion : 0 } ) ;
131144 await callbacks . onEvent ( record ( 1 , 'partial' ) ) ;
132145 assert . equal ( snapshots . length , 0 ) ;
@@ -180,7 +193,7 @@ test('initial and resumed mailbox restores questions independently of transcript
180193 return { sessionId :'session' , permissions :{ revision :0 , requests :[ ] } , userQuestions :{ revision :1 , questions :[ { toolId :'question-tool' , sessionId :'session' , questions :question , registeredAtMs :1 } ] } } ;
181194 }
182195 } ;
183- const controller = new ChatSessionController ( manager , { onSnapshot :value => snapshot = value , onMailbox :value => mailbox = value , onError :error => { throw error ; } , canPoll :( ) => true } ) ;
196+ const controller = createController ( ChatSessionController , Object . assign ( manager , { supportsMessageQueue : ( ) => false } ) , { onSnapshot :value => snapshot = value , onMailbox :value => mailbox = value , onError :error => { throw error ; } , canPoll :( ) => true } ) ;
184197 controller . start ( 'session' , { pollVersion :0 , knownMessageCount :0 , knownModelCatalogVersion :0 } ) ;
185198 await callbacks . onResumed ( ) ; await callbacks . onCaughtUp ( ) ;
186199 assert . equal ( snapshot . activeTurn , undefined ) ;
@@ -321,7 +334,7 @@ test('history publishes user and assistant together after reduction, including a
321334 getModelCatalog : ( ) => new Promise ( resolve => { resolveCatalog = resolve ; } ) ,
322335 subscribeSession : ( _id , next ) => { callbacks = next ; return { close ( ) { } } ; }
323336 } ;
324- const controller = new ChatSessionController ( manager , { canPoll : ( ) => true ,
337+ const controller = createController ( ChatSessionController , Object . assign ( manager , { supportsMessageQueue : ( ) => false } ) , { canPoll : ( ) => true ,
325338 onSnapshot : value => snapshots . push ( value ) , onError : error => { throw error ; } } ) ;
326339 controller . start ( 'session' , { pollVersion : 0 , knownMessageCount : 0 , knownModelCatalogVersion : 0 } ) ;
327340 await callbacks . onCaughtUp ( ) ;
@@ -355,7 +368,7 @@ async function durableTimelineHarness() {
355368 timeline . reset ( 'session' ) ;
356369 let stream ;
357370 const { ChatSessionController } = loadController ( ) ;
358- const controller = new ChatSessionController ( {
371+ const controller = createController ( ChatSessionController , {
359372 getModelCatalog : async ( ) => ( { version : 1 , models : [ ] , default_models : { } } ) ,
360373 subscribeSession : ( _id , callbacks ) => { stream = callbacks ; return { wake ( ) { } , close ( ) { } } ; }
361374 } , { onSnapshot : snapshot => timeline . applySnapshot ( snapshot ) , canPoll : ( ) => true ,
@@ -455,7 +468,7 @@ test('host restart terminal record settles active turn without reporting success
455468 const { ChatSessionController } = loadController ( ) ;
456469 let stream , snapshot ;
457470 const timeline = new ChatTimelineStore ( ) ; timeline . reset ( 'session' ) ;
458- const controller = new ChatSessionController ( { getModelCatalog : async ( ) => ( { version :1 , models :[ ] , default_models :{ } } ) ,
471+ const controller = createController ( ChatSessionController , { getModelCatalog : async ( ) => ( { version :1 , models :[ ] , default_models :{ } } ) ,
459472 subscribeSession : ( _id , callbacks ) => { stream = callbacks ; return { wake ( ) { } , close ( ) { } } ; }
460473 } , { onSnapshot : value => { snapshot = value ; timeline . applySnapshot ( value ) ; } , canPoll :( ) => true , onError :error => { throw error ; } } ) ;
461474 controller . start ( 'session' , { pollVersion :0 , knownMessageCount :0 , knownModelCatalogVersion :0 } ) ;
0 commit comments