@@ -62,6 +62,162 @@ describe("protocol tests", () => {
6262 await transport . close ( ) ;
6363 expect ( oncloseMock ) . toHaveBeenCalled ( ) ;
6464 } ) ;
65+
66+ describe ( "progress notification timeout behavior" , ( ) => {
67+ beforeEach ( ( ) => {
68+ jest . useFakeTimers ( ) ;
69+ } ) ;
70+ afterEach ( ( ) => {
71+ jest . useRealTimers ( ) ;
72+ } ) ;
73+
74+ test ( "should reset timeout when progress notification is received" , async ( ) => {
75+ await protocol . connect ( transport ) ;
76+ const request = { method : "example" , params : { } } ;
77+ const mockSchema : ZodType < { result : string } > = z . object ( {
78+ result : z . string ( ) ,
79+ } ) ;
80+ const onProgressMock = jest . fn ( ) ;
81+ const requestPromise = protocol . request ( request , mockSchema , {
82+ timeout : 1000 ,
83+ resetTimeoutOnProgress : true ,
84+ onprogress : onProgressMock ,
85+ } ) ;
86+ jest . advanceTimersByTime ( 800 ) ;
87+ if ( transport . onmessage ) {
88+ transport . onmessage ( {
89+ jsonrpc : "2.0" ,
90+ method : "notifications/progress" ,
91+ params : {
92+ progressToken : 0 ,
93+ progress : 50 ,
94+ total : 100 ,
95+ } ,
96+ } ) ;
97+ }
98+ await Promise . resolve ( ) ;
99+ expect ( onProgressMock ) . toHaveBeenCalledWith ( {
100+ progress : 50 ,
101+ total : 100 ,
102+ } ) ;
103+ jest . advanceTimersByTime ( 800 ) ;
104+ if ( transport . onmessage ) {
105+ transport . onmessage ( {
106+ jsonrpc : "2.0" ,
107+ id : 0 ,
108+ result : { result : "success" } ,
109+ } ) ;
110+ }
111+ await Promise . resolve ( ) ;
112+ await expect ( requestPromise ) . resolves . toEqual ( { result : "success" } ) ;
113+ } ) ;
114+
115+ test ( "should respect maxTotalTimeout" , async ( ) => {
116+ await protocol . connect ( transport ) ;
117+ const request = { method : "example" , params : { } } ;
118+ const mockSchema : ZodType < { result : string } > = z . object ( {
119+ result : z . string ( ) ,
120+ } ) ;
121+ const onProgressMock = jest . fn ( ) ;
122+ const requestPromise = protocol . request ( request , mockSchema , {
123+ timeout : 1000 ,
124+ maxTotalTimeout : 150 ,
125+ resetTimeoutOnProgress : true ,
126+ onprogress : onProgressMock ,
127+ } ) ;
128+
129+ // First progress notification should work
130+ jest . advanceTimersByTime ( 80 ) ;
131+ if ( transport . onmessage ) {
132+ transport . onmessage ( {
133+ jsonrpc : "2.0" ,
134+ method : "notifications/progress" ,
135+ params : {
136+ progressToken : 0 ,
137+ progress : 50 ,
138+ total : 100 ,
139+ } ,
140+ } ) ;
141+ }
142+ await Promise . resolve ( ) ;
143+ expect ( onProgressMock ) . toHaveBeenCalledWith ( {
144+ progress : 50 ,
145+ total : 100 ,
146+ } ) ;
147+ jest . advanceTimersByTime ( 80 ) ;
148+ if ( transport . onmessage ) {
149+ transport . onmessage ( {
150+ jsonrpc : "2.0" ,
151+ method : "notifications/progress" ,
152+ params : {
153+ progressToken : 0 ,
154+ progress : 75 ,
155+ total : 100 ,
156+ } ,
157+ } ) ;
158+ }
159+ await expect ( requestPromise ) . rejects . toThrow ( "Maximum total timeout exceeded" ) ;
160+ expect ( onProgressMock ) . toHaveBeenCalledTimes ( 1 ) ;
161+ } ) ;
162+
163+ test ( "should timeout if no progress received within timeout period" , async ( ) => {
164+ await protocol . connect ( transport ) ;
165+ const request = { method : "example" , params : { } } ;
166+ const mockSchema : ZodType < { result : string } > = z . object ( {
167+ result : z . string ( ) ,
168+ } ) ;
169+ const requestPromise = protocol . request ( request , mockSchema , {
170+ timeout : 100 ,
171+ resetTimeoutOnProgress : true ,
172+ } ) ;
173+ jest . advanceTimersByTime ( 101 ) ;
174+ await expect ( requestPromise ) . rejects . toThrow ( "Request timed out" ) ;
175+ } ) ;
176+
177+ test ( "should handle multiple progress notifications correctly" , async ( ) => {
178+ await protocol . connect ( transport ) ;
179+ const request = { method : "example" , params : { } } ;
180+ const mockSchema : ZodType < { result : string } > = z . object ( {
181+ result : z . string ( ) ,
182+ } ) ;
183+ const onProgressMock = jest . fn ( ) ;
184+ const requestPromise = protocol . request ( request , mockSchema , {
185+ timeout : 1000 ,
186+ resetTimeoutOnProgress : true ,
187+ onprogress : onProgressMock ,
188+ } ) ;
189+
190+ // Simulate multiple progress updates
191+ for ( let i = 1 ; i <= 3 ; i ++ ) {
192+ jest . advanceTimersByTime ( 800 ) ;
193+ if ( transport . onmessage ) {
194+ transport . onmessage ( {
195+ jsonrpc : "2.0" ,
196+ method : "notifications/progress" ,
197+ params : {
198+ progressToken : 0 ,
199+ progress : i * 25 ,
200+ total : 100 ,
201+ } ,
202+ } ) ;
203+ }
204+ await Promise . resolve ( ) ;
205+ expect ( onProgressMock ) . toHaveBeenNthCalledWith ( i , {
206+ progress : i * 25 ,
207+ total : 100 ,
208+ } ) ;
209+ }
210+ if ( transport . onmessage ) {
211+ transport . onmessage ( {
212+ jsonrpc : "2.0" ,
213+ id : 0 ,
214+ result : { result : "success" } ,
215+ } ) ;
216+ }
217+ await Promise . resolve ( ) ;
218+ await expect ( requestPromise ) . resolves . toEqual ( { result : "success" } ) ;
219+ } ) ;
220+ } ) ;
65221} ) ;
66222
67223describe ( "mergeCapabilities" , ( ) => {
0 commit comments