@@ -238,3 +238,99 @@ func TestRetryBackoff_RetryAt_Random(t *testing.T) {
238238 now = retryAt
239239 }
240240}
241+
242+ func TestRetryable_IncrementNonRetryableAttempt (t * testing.T ) {
243+ t .Parallel ()
244+
245+ fixedInterval := 2 * time .Hour
246+ v := Retryable {}
247+
248+ // First attempt
249+ v .IncrementNonRetryableAttempt (utctime .MustParse ("2000-01-01T00:00:00.000Z" ).Time (), "credential expired" , fixedInterval )
250+ assert .Equal (t , Retryable {
251+ RetryAttempt : 1 ,
252+ RetryReason : "credential expired" ,
253+ FirstFailedAt : ptr .Ptr (utctime .MustParse ("2000-01-01T00:00:00.000Z" )),
254+ LastFailedAt : ptr .Ptr (utctime .MustParse ("2000-01-01T00:00:00.000Z" )),
255+ RetryAfter : ptr .Ptr (utctime .MustParse ("2000-01-01T02:00:00.000Z" )), // +2 hours
256+ }, v )
257+
258+ // Second attempt - FirstFailedAt should remain unchanged
259+ v .IncrementNonRetryableAttempt (utctime .MustParse ("2000-01-01T02:00:00.000Z" ).Time (), "credential expired" , fixedInterval )
260+ assert .Equal (t , Retryable {
261+ RetryAttempt : 2 ,
262+ RetryReason : "credential expired" ,
263+ FirstFailedAt : ptr .Ptr (utctime .MustParse ("2000-01-01T00:00:00.000Z" )), // unchanged
264+ LastFailedAt : ptr .Ptr (utctime .MustParse ("2000-01-01T02:00:00.000Z" )),
265+ RetryAfter : ptr .Ptr (utctime .MustParse ("2000-01-01T04:00:00.000Z" )), // +2 hours (fixed)
266+ }, v )
267+
268+ // Third attempt - still fixed interval
269+ v .IncrementNonRetryableAttempt (utctime .MustParse ("2000-01-01T04:00:00.000Z" ).Time (), "credential expired" , fixedInterval )
270+ assert .Equal (t , Retryable {
271+ RetryAttempt : 3 ,
272+ RetryReason : "credential expired" ,
273+ FirstFailedAt : ptr .Ptr (utctime .MustParse ("2000-01-01T00:00:00.000Z" )), // unchanged
274+ LastFailedAt : ptr .Ptr (utctime .MustParse ("2000-01-01T04:00:00.000Z" )),
275+ RetryAfter : ptr .Ptr (utctime .MustParse ("2000-01-01T06:00:00.000Z" )), // +2 hours (fixed)
276+ }, v )
277+ }
278+
279+ func TestRetryable_IncrementNonRetryableAttempt_VsExponential (t * testing.T ) {
280+ t .Parallel ()
281+
282+ // This test demonstrates the difference between exponential backoff (IncrementRetryAttempt)
283+ // and fixed interval (IncrementNonRetryableAttempt)
284+
285+ backoff := NoRandomizationBackoff ()
286+ fixedInterval := 2 * time .Hour
287+
288+ exponential := Retryable {}
289+ fixed := Retryable {}
290+
291+ baseTime := utctime .MustParse ("2000-01-01T00:00:00.000Z" ).Time ()
292+
293+ // First attempt - both use their base interval
294+ exponential .IncrementRetryAttempt (backoff , baseTime , "retryable error" )
295+ fixed .IncrementNonRetryableAttempt (baseTime , "non-retryable error" , fixedInterval )
296+
297+ assert .Equal (t , "2000-01-01T00:02:00.000Z" , exponential .RetryAfter .String ()) // +2 min (exponential base)
298+ assert .Equal (t , "2000-01-01T02:00:00.000Z" , fixed .RetryAfter .String ()) // +2 hours (fixed)
299+
300+ // Second attempt - exponential increases, fixed stays same
301+ exponential .IncrementRetryAttempt (backoff , baseTime .Add (2 * time .Minute ), "retryable error" )
302+ fixed .IncrementNonRetryableAttempt (baseTime .Add (2 * time .Hour ), "non-retryable error" , fixedInterval )
303+
304+ assert .Equal (t , "2000-01-01T00:10:00.000Z" , exponential .RetryAfter .String ()) // +8 min (exponential x4)
305+ assert .Equal (t , "2000-01-01T04:00:00.000Z" , fixed .RetryAfter .String ()) // +2 hours (fixed)
306+
307+ // Third attempt - exponential continues to grow, fixed stays same
308+ exponential .IncrementRetryAttempt (backoff , baseTime .Add (10 * time .Minute ), "retryable error" )
309+ fixed .IncrementNonRetryableAttempt (baseTime .Add (4 * time .Hour ), "non-retryable error" , fixedInterval )
310+
311+ assert .Equal (t , "2000-01-01T00:42:00.000Z" , exponential .RetryAfter .String ()) // +32 min (exponential x4)
312+ assert .Equal (t , "2000-01-01T06:00:00.000Z" , fixed .RetryAfter .String ()) // +2 hours (fixed)
313+
314+ // Verify FirstFailedAt remains unchanged for both
315+ assert .Equal (t , baseTime , exponential .FirstFailedAt .Time ())
316+ assert .Equal (t , baseTime , fixed .FirstFailedAt .Time ())
317+ }
318+
319+ func TestRetryable_IncrementNonRetryableAttempt_TerminalInterval (t * testing.T ) {
320+ t .Parallel ()
321+
322+ // Test with a very long "terminal" interval (simulating max attempts reached)
323+ terminalInterval := 10 * 365 * 24 * time .Hour // ~10 years
324+ v := Retryable {}
325+
326+ baseTime := utctime .MustParse ("2000-01-01T00:00:00.000Z" ).Time ()
327+ v .IncrementNonRetryableAttempt (baseTime , "max attempts reached" , terminalInterval )
328+
329+ // Verify far-future retry time (should be at least 9 years in the future)
330+ expectedRetryAfter := utctime .From (baseTime .Add (terminalInterval ))
331+ assert .Equal (t , expectedRetryAfter .String (), v .RetryAfter .String ())
332+
333+ // Verify it's far in the future (more than 9 years)
334+ nineYearsLater := baseTime .Add (9 * 365 * 24 * time .Hour )
335+ assert .True (t , v .RetryAfter .Time ().After (nineYearsLater ), "RetryAfter should be more than 9 years in the future" )
336+ }
0 commit comments