@@ -194,6 +194,108 @@ describe("RetryableTask", () => {
194194 // Assert - should still allow retry (timeout is infinite)
195195 expect ( delay ) . toBeGreaterThan ( 0 ) ;
196196 } ) ;
197+
198+ it ( "should return undefined when handleFailure predicate returns false" , ( ) => {
199+ // Arrange
200+ const retryPolicy = new RetryPolicy ( {
201+ maxNumberOfAttempts : 10 ,
202+ firstRetryIntervalInMilliseconds : 1000 ,
203+ handleFailure : ( failure ) => failure . errorType !== "FatalError" ,
204+ } ) ;
205+ const action = new pb . OrchestratorAction ( ) ;
206+ const task = new RetryableTask < string > ( retryPolicy , action , new Date ( ) , "activity" ) ;
207+
208+ // Record a FatalError (should NOT be retried)
209+ const failureDetails = new pb . TaskFailureDetails ( ) ;
210+ failureDetails . setErrortype ( "FatalError" ) ;
211+ failureDetails . setErrormessage ( "This is a fatal error" ) ;
212+ task . recordFailure ( "This is a fatal error" , failureDetails ) ;
213+
214+ const currentTime = new Date ( ) ;
215+
216+ // Act
217+ const delay = task . computeNextDelayInMilliseconds ( currentTime ) ;
218+
219+ // Assert - should return undefined because handleFailure returns false for FatalError
220+ expect ( delay ) . toBeUndefined ( ) ;
221+ } ) ;
222+
223+ it ( "should allow retry when handleFailure predicate returns true" , ( ) => {
224+ // Arrange
225+ const retryPolicy = new RetryPolicy ( {
226+ maxNumberOfAttempts : 10 ,
227+ firstRetryIntervalInMilliseconds : 1000 ,
228+ handleFailure : ( failure ) => failure . errorType !== "FatalError" ,
229+ } ) ;
230+ const action = new pb . OrchestratorAction ( ) ;
231+ const task = new RetryableTask < string > ( retryPolicy , action , new Date ( ) , "activity" ) ;
232+
233+ // Record a TransientError (should be retried)
234+ const failureDetails = new pb . TaskFailureDetails ( ) ;
235+ failureDetails . setErrortype ( "TransientError" ) ;
236+ failureDetails . setErrormessage ( "This is a transient error" ) ;
237+ task . recordFailure ( "This is a transient error" , failureDetails ) ;
238+
239+ const currentTime = new Date ( ) ;
240+
241+ // Act
242+ const delay = task . computeNextDelayInMilliseconds ( currentTime ) ;
243+
244+ // Assert - should return a delay because handleFailure returns true for TransientError
245+ expect ( delay ) . toBeDefined ( ) ;
246+ expect ( delay ) . toBeGreaterThan ( 0 ) ;
247+ } ) ;
248+
249+ it ( "should filter based on error message content in handleFailure" , ( ) => {
250+ // Arrange
251+ const retryPolicy = new RetryPolicy ( {
252+ maxNumberOfAttempts : 10 ,
253+ firstRetryIntervalInMilliseconds : 1000 ,
254+ handleFailure : ( failure ) => failure . message ?. includes ( "timeout" ) ?? false ,
255+ } ) ;
256+ const action = new pb . OrchestratorAction ( ) ;
257+ const task = new RetryableTask < string > ( retryPolicy , action , new Date ( ) , "activity" ) ;
258+
259+ // Record a validation error (should NOT be retried - no "timeout" in message)
260+ const failureDetails = new pb . TaskFailureDetails ( ) ;
261+ failureDetails . setErrortype ( "ValidationError" ) ;
262+ failureDetails . setErrormessage ( "Invalid input: field is required" ) ;
263+ task . recordFailure ( "Invalid input: field is required" , failureDetails ) ;
264+
265+ const currentTime = new Date ( ) ;
266+
267+ // Act
268+ const delay = task . computeNextDelayInMilliseconds ( currentTime ) ;
269+
270+ // Assert - should return undefined because message doesn't contain "timeout"
271+ expect ( delay ) . toBeUndefined ( ) ;
272+ } ) ;
273+
274+ it ( "should retry when error message matches handleFailure criteria" , ( ) => {
275+ // Arrange
276+ const retryPolicy = new RetryPolicy ( {
277+ maxNumberOfAttempts : 10 ,
278+ firstRetryIntervalInMilliseconds : 1000 ,
279+ handleFailure : ( failure ) => failure . message ?. includes ( "timeout" ) ?? false ,
280+ } ) ;
281+ const action = new pb . OrchestratorAction ( ) ;
282+ const task = new RetryableTask < string > ( retryPolicy , action , new Date ( ) , "activity" ) ;
283+
284+ // Record a timeout error (should be retried - has "timeout" in message)
285+ const failureDetails = new pb . TaskFailureDetails ( ) ;
286+ failureDetails . setErrortype ( "NetworkError" ) ;
287+ failureDetails . setErrormessage ( "Connection timeout - please retry" ) ;
288+ task . recordFailure ( "Connection timeout - please retry" , failureDetails ) ;
289+
290+ const currentTime = new Date ( ) ;
291+
292+ // Act
293+ const delay = task . computeNextDelayInMilliseconds ( currentTime ) ;
294+
295+ // Assert - should return a delay because message contains "timeout"
296+ expect ( delay ) . toBeDefined ( ) ;
297+ expect ( delay ) . toBeGreaterThan ( 0 ) ;
298+ } ) ;
197299 } ) ;
198300
199301 describe ( "incrementAttemptCount" , ( ) => {
0 commit comments