@@ -15,6 +15,8 @@ const { createSessionStoreDir, openClient } = setupGatewaySessionsTestHarness();
1515type MockCalls = {
1616 mock : { calls : unknown [ ] [ ] } ;
1717} ;
18+ type SessionStoreEntryOptions = Parameters < typeof sessionStoreEntry > [ 1 ] ;
19+ type MutationMethod = "sessions.patch" | "sessions.compact" ;
1820
1921function isRecord ( value : unknown ) : value is Record < string , unknown > {
2022 return typeof value === "object" && value !== null ;
@@ -116,20 +118,31 @@ async function invokeSessionsList({
116118 return { request, respond } ;
117119}
118120
119- async function invokeSessionsPatch ( params : Record < string , unknown > ) {
121+ async function invokeSessionMutation ( {
122+ method,
123+ params,
124+ context = { } ,
125+ subscribedConnIds = new Set ( [ "conn-1" ] ) ,
126+ } : {
127+ method : MutationMethod ;
128+ params : Record < string , unknown > ;
129+ context ?: Record < string , unknown > ;
130+ subscribedConnIds ?: Set < string > ;
131+ } ) {
120132 const broadcastToConnIds = vi . fn ( ) ;
121133 const respond = vi . fn ( ) ;
122134 const sessionsHandlers = await getSessionsHandlers ( ) ;
123135 const { getRuntimeConfig } = await getGatewayConfigModule ( ) ;
124- await sessionsHandlers [ "sessions.patch" ] ( {
136+ await sessionsHandlers [ method ] ( {
125137 req : { } as never ,
126138 params,
127139 respond,
128140 context : {
129141 broadcastToConnIds,
130- getSessionEventSubscriberConnIds : ( ) => new Set ( [ "conn-1" ] ) ,
142+ getSessionEventSubscriberConnIds : ( ) => subscribedConnIds ,
131143 loadGatewayModelCatalog : async ( ) => ( { providers : [ ] } ) ,
132144 getRuntimeConfig,
145+ ...context ,
133146 } as never ,
134147 client : null ,
135148 isWebchatConnect : ( ) => false ,
@@ -140,6 +153,31 @@ async function invokeSessionsPatch(params: Record<string, unknown>) {
140153 } ;
141154}
142155
156+ async function invokeSessionsPatch ( params : Record < string , unknown > ) {
157+ return invokeSessionMutation ( { method : "sessions.patch" , params } ) ;
158+ }
159+
160+ async function writeMainSessionStore ( options ?: SessionStoreEntryOptions ) {
161+ await createSessionStoreDir ( ) ;
162+ await writeSessionStore ( {
163+ entries : {
164+ main : sessionStoreEntry ( "sess-main" , options ) ,
165+ } ,
166+ } ) ;
167+ }
168+
169+ function expectMainPatchBroadcast (
170+ result : Awaited < ReturnType < typeof invokeSessionsPatch > > ,
171+ expected : Record < string , unknown > ,
172+ ) {
173+ expectFields ( result . responsePayload , { ok : true , key : "agent:main:main" } ) ;
174+ expectChangedBroadcast ( result . broadcastToConnIds , {
175+ sessionKey : "agent:main:main" ,
176+ reason : "patch" ,
177+ ...expected ,
178+ } ) ;
179+ }
180+
143181async function setupGlobalAgentSessionStores ( {
144182 writePrimeStore = false ,
145183 withTranscripts = false ,
@@ -257,25 +295,33 @@ async function invokeSessionsCompact({
257295 params : Record < string , unknown > ;
258296 subscribedConnIds ?: Set < string > ;
259297} ) {
260- const broadcastToConnIds = vi . fn ( ) ;
261- const respond = vi . fn ( ) ;
262- const sessionsHandlers = await getSessionsHandlers ( ) ;
263- await sessionsHandlers [ "sessions.compact" ] ( {
264- req : { } as never ,
298+ return invokeSessionMutation ( {
299+ method : "sessions.compact" ,
265300 params,
266- respond,
267301 context : {
268- broadcastToConnIds,
269- getSessionEventSubscriberConnIds : ( ) => subscribedConnIds ,
270302 getRuntimeConfig,
271- } as never ,
272- client : null ,
273- isWebchatConnect : ( ) => false ,
303+ } ,
304+ subscribedConnIds,
274305 } ) ;
275- return {
276- broadcastToConnIds,
277- responsePayload : expectRespondPayload ( respond ) ,
278- } ;
306+ }
307+
308+ async function expectListedSessionActiveRun (
309+ requestId : string ,
310+ run : Record < string , unknown > ,
311+ expected : boolean ,
312+ ) {
313+ await writeMainSessionStore ( ) ;
314+
315+ const { respond } = await invokeSessionsList ( {
316+ requestId,
317+ context : {
318+ chatAbortControllers : new Map ( [ [ "run-1" , { sessionKey : "agent:main:main" , ...run } ] ] ) ,
319+ } ,
320+ } ) ;
321+
322+ const payload = expectRespondPayload ( respond ) ;
323+ const session = findSession ( payload , "agent:main:main" ) ;
324+ expect ( session . hasActiveRun ) . toBe ( expected ) ;
279325}
280326
281327test ( "sessions.list keeps bulk rows lightweight and uses persisted model fields" , async ( ) => {
@@ -369,17 +415,12 @@ test("sessions.list keeps bulk rows lightweight and uses persisted model fields"
369415} ) ;
370416
371417test ( "sessions.list uses the gateway model catalog for effective thinking defaults" , async ( ) => {
372- await createSessionStoreDir ( ) ;
373418 testState . agentConfig = {
374419 model : { primary : "test-provider/reasoner" } ,
375420 } ;
376- await writeSessionStore ( {
377- entries : {
378- main : sessionStoreEntry ( "sess-main" , {
379- modelProvider : "test-provider" ,
380- model : "reasoner" ,
381- } ) ,
382- } ,
421+ await writeMainSessionStore ( {
422+ modelProvider : "test-provider" ,
423+ model : "reasoner" ,
383424 } ) ;
384425
385426 const { respond } = await invokeSessionsList ( {
@@ -407,67 +448,23 @@ test("sessions.list uses the gateway model catalog for effective thinking defaul
407448} ) ;
408449
409450test ( "sessions.list marks sessions with active abortable runs" , async ( ) => {
410- await createSessionStoreDir ( ) ;
411- await writeSessionStore ( {
412- entries : {
413- main : sessionStoreEntry ( "sess-main" ) ,
414- } ,
415- } ) ;
416-
417- const { respond } = await invokeSessionsList ( {
418- requestId : "req-sessions-list-active-run" ,
419- context : {
420- chatAbortControllers : new Map ( [ [ "run-1" , { sessionKey : "agent:main:main" } ] ] ) ,
421- } ,
422- } ) ;
423-
424- const payload = expectRespondPayload ( respond ) ;
425- const session = findSession ( payload , "agent:main:main" ) ;
426- expect ( session . hasActiveRun ) . toBe ( true ) ;
451+ await expectListedSessionActiveRun ( "req-sessions-list-active-run" , { } , true ) ;
427452} ) ;
428453
429454test ( "sessions.list ignores terminal abortable runs kept for retry guards" , async ( ) => {
430- await createSessionStoreDir ( ) ;
431- await writeSessionStore ( {
432- entries : {
433- main : sessionStoreEntry ( "sess-main" ) ,
434- } ,
435- } ) ;
436-
437- const { respond } = await invokeSessionsList ( {
438- requestId : "req-sessions-list-terminal-run" ,
439- context : {
440- chatAbortControllers : new Map ( [
441- [ "run-1" , { sessionKey : "agent:main:main" , projectSessionActive : false } ] ,
442- ] ) ,
443- } ,
444- } ) ;
445-
446- const payload = expectRespondPayload ( respond ) ;
447- const session = findSession ( payload , "agent:main:main" ) ;
448- expect ( session . hasActiveRun ) . toBe ( false ) ;
455+ await expectListedSessionActiveRun (
456+ "req-sessions-list-terminal-run" ,
457+ { projectSessionActive : false } ,
458+ false ,
459+ ) ;
449460} ) ;
450461
451462test ( "sessions.list ignores hidden internal abortable runs" , async ( ) => {
452- await createSessionStoreDir ( ) ;
453- await writeSessionStore ( {
454- entries : {
455- main : sessionStoreEntry ( "sess-main" ) ,
456- } ,
457- } ) ;
458-
459- const { respond } = await invokeSessionsList ( {
460- requestId : "req-sessions-list-hidden-run" ,
461- context : {
462- chatAbortControllers : new Map ( [
463- [ "run-1" , { sessionKey : "agent:main:main" , controlUiVisible : false } ] ,
464- ] ) ,
465- } ,
466- } ) ;
467-
468- const payload = expectRespondPayload ( respond ) ;
469- const session = findSession ( payload , "agent:main:main" ) ;
470- expect ( session . hasActiveRun ) . toBe ( false ) ;
463+ await expectListedSessionActiveRun (
464+ "req-sessions-list-hidden-run" ,
465+ { controlUiVisible : false } ,
466+ false ,
467+ ) ;
471468} ) ;
472469
473470test ( "sessions.list yields before responding during bulk transcript hydration" , async ( ) => {
@@ -518,12 +515,7 @@ test("sessions.list yields before responding during bulk transcript hydration",
518515} ) ;
519516
520517test ( "sessions.list does not block on slow model catalog discovery" , async ( ) => {
521- await createSessionStoreDir ( ) ;
522- await writeSessionStore ( {
523- entries : {
524- main : sessionStoreEntry ( "sess-main" ) ,
525- } ,
526- } ) ;
518+ await writeMainSessionStore ( ) ;
527519
528520 vi . useFakeTimers ( ) ;
529521 try {
@@ -586,15 +578,12 @@ test("sessions.changed mutation events include live usage metadata", async () =>
586578 } ,
587579 } ) ;
588580
589- const { broadcastToConnIds , responsePayload } = await invokeSessionsPatch ( {
581+ const result = await invokeSessionsPatch ( {
590582 key : "main" ,
591583 label : "Renamed" ,
592584 } ) ;
593585
594- expectFields ( responsePayload , { ok : true , key : "agent:main:main" } ) ;
595- expectChangedBroadcast ( broadcastToConnIds , {
596- sessionKey : "agent:main:main" ,
597- reason : "patch" ,
586+ expectMainPatchBroadcast ( result , {
598587 totalTokens : 6_643 ,
599588 totalTokensFresh : true ,
600589 contextTokens : 123_456 ,
@@ -605,59 +594,36 @@ test("sessions.changed mutation events include live usage metadata", async () =>
605594} ) ;
606595
607596test ( "sessions.changed mutation events include live session setting metadata" , async ( ) => {
608- await createSessionStoreDir ( ) ;
609- await writeSessionStore ( {
610- entries : {
611- main : sessionStoreEntry ( "sess-main" , {
612- verboseLevel : "on" ,
613- responseUsage : "full" ,
614- fastMode : true ,
615- lastChannel : "telegram" ,
616- lastTo : "-100123" ,
617- lastAccountId : "acct-1" ,
618- lastThreadId : 42 ,
619- } ) ,
620- } ,
621- } ) ;
622-
623- const { broadcastToConnIds, responsePayload } = await invokeSessionsPatch ( {
624- key : "main" ,
625- verboseLevel : "on" ,
626- } ) ;
627-
628- expectFields ( responsePayload , { ok : true , key : "agent:main:main" } ) ;
629- expectChangedBroadcast ( broadcastToConnIds , {
630- sessionKey : "agent:main:main" ,
631- reason : "patch" ,
597+ const sessionSettings = {
632598 verboseLevel : "on" ,
633599 responseUsage : "full" ,
634600 fastMode : true ,
635601 lastChannel : "telegram" ,
636602 lastTo : "-100123" ,
637603 lastAccountId : "acct-1" ,
638604 lastThreadId : 42 ,
605+ } satisfies SessionStoreEntryOptions ;
606+ await writeMainSessionStore ( sessionSettings ) ;
607+
608+ const result = await invokeSessionsPatch ( {
609+ key : "main" ,
610+ verboseLevel : "on" ,
639611 } ) ;
612+
613+ expectMainPatchBroadcast ( result , sessionSettings ) ;
640614} ) ;
641615
642616test ( "sessions.changed mutation events include sendPolicy metadata" , async ( ) => {
643- await createSessionStoreDir ( ) ;
644- await writeSessionStore ( {
645- entries : {
646- main : sessionStoreEntry ( "sess-main" , {
647- sendPolicy : "deny" ,
648- } ) ,
649- } ,
617+ await writeMainSessionStore ( {
618+ sendPolicy : "deny" ,
650619 } ) ;
651620
652- const { broadcastToConnIds , responsePayload } = await invokeSessionsPatch ( {
621+ const result = await invokeSessionsPatch ( {
653622 key : "main" ,
654623 sendPolicy : "deny" ,
655624 } ) ;
656625
657- expectFields ( responsePayload , { ok : true , key : "agent:main:main" } ) ;
658- expectChangedBroadcast ( broadcastToConnIds , {
659- sessionKey : "agent:main:main" ,
660- reason : "patch" ,
626+ expectMainPatchBroadcast ( result , {
661627 sendPolicy : "deny" ,
662628 } ) ;
663629} ) ;
0 commit comments