@@ -933,4 +933,58 @@ describe("In-Memory Backend", () => {
933933 expect ( backend . toClientStatus ( suspendedInstance ! . status ) ) . toEqual ( OrchestrationStatus . SUSPENDED ) ;
934934 } ) ;
935935 } ) ;
936+
937+ it ( "should clean up stale waiters after waitForState timeout" , async ( ) => {
938+ const instanceId = "timeout-cleanup-test" ;
939+ backend . createInstance ( instanceId , "testOrch" ) ;
940+
941+ // Call waitForState with a predicate that will never match and a short timeout
942+ await expect (
943+ backend . waitForState (
944+ instanceId ,
945+ ( ) => false , // Will never match
946+ 50 , // 50ms timeout
947+ ) ,
948+ ) . rejects . toThrow ( "Timeout waiting for orchestration" ) ;
949+
950+ // After the timeout, the stale waiter should be cleaned up.
951+ // Access internal stateWaiters to verify the waiter was removed.
952+ const stateWaitersMap = ( backend as any ) . stateWaiters as Map < string , any [ ] > ;
953+
954+ // The timed-out waiter should have been removed, and since it was the only
955+ // waiter, the instance entry should be removed from the map entirely.
956+ expect ( stateWaitersMap . has ( instanceId ) ) . toBe ( false ) ;
957+ } ) ;
958+
959+ it ( "should remove only the timed-out waiter when multiple waiters exist" , async ( ) => {
960+ const instanceId = "multi-waiter-timeout-test" ;
961+ backend . createInstance ( instanceId , "testOrch" ) ;
962+
963+ // Start a waiter with a long timeout (won't time out during the test)
964+ const longWaitPromise = backend . waitForState (
965+ instanceId ,
966+ ( ) => false , // Never matches
967+ 60000 , // 60 second timeout — won't fire
968+ ) ;
969+
970+ // Start a waiter with a very short timeout
971+ await expect (
972+ backend . waitForState (
973+ instanceId ,
974+ ( ) => false , // Never matches
975+ 50 , // 50ms timeout
976+ ) ,
977+ ) . rejects . toThrow ( "Timeout waiting for orchestration" ) ;
978+
979+ // After the short timeout, only the long-lived waiter should remain
980+ const stateWaitersMap = ( backend as any ) . stateWaiters as Map < string , any [ ] > ;
981+ const waiters = stateWaitersMap . get ( instanceId ) ;
982+
983+ expect ( waiters ) . toBeDefined ( ) ;
984+ expect ( waiters ! . length ) . toBe ( 1 ) ;
985+
986+ // Clean up: reset to clear the remaining waiter and its timer
987+ backend . reset ( ) ;
988+ await longWaitPromise . catch ( ( ) => { } ) ; // Ignore the reset rejection
989+ } ) ;
936990} ) ;
0 commit comments