@@ -62,6 +62,162 @@ describe("protocol tests", () => {
62
62
await transport . close ( ) ;
63
63
expect ( oncloseMock ) . toHaveBeenCalled ( ) ;
64
64
} ) ;
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
+ } ) ;
65
221
} ) ;
66
222
67
223
describe ( "mergeCapabilities" , ( ) => {
0 commit comments