|
4 | 4 | "context" |
5 | 5 | "errors" |
6 | 6 | "fmt" |
| 7 | + "math" |
7 | 8 | "os" |
8 | 9 | "testing" |
9 | 10 | "time" |
@@ -664,3 +665,71 @@ func TestIsRecoverable(t *testing.T) { |
664 | 665 | err = fmt.Errorf("wrapping: %w", err) |
665 | 666 | assert.False(t, IsRecoverable(err)) |
666 | 667 | } |
| 668 | + |
| 669 | +func TestFullJitterBackoffDelay(t *testing.T) { |
| 670 | + // Seed for predictable randomness in tests |
| 671 | + // In real usage, math/rand is auto-seeded in Go 1.20+ or should be seeded once at program start. |
| 672 | + // For library test predictability, local seeding is fine. |
| 673 | + // However, retry-go's RandomDelay uses global math/rand without explicit seeding in tests. |
| 674 | + // Let's follow the existing pattern of not explicitly seeding in each test for now, |
| 675 | + // assuming test runs are isolated enough or that exact delay values aren't asserted, |
| 676 | + // but rather ranges or properties. |
| 677 | + |
| 678 | + baseDelay := 50 * time.Millisecond |
| 679 | + maxDelay := 500 * time.Millisecond |
| 680 | + |
| 681 | + config := &Config{ |
| 682 | + delay: baseDelay, |
| 683 | + maxDelay: maxDelay, |
| 684 | + // other fields can be zero/default for this test |
| 685 | + } |
| 686 | + |
| 687 | + attempts := []uint{0, 1, 2, 3, 4, 5, 6, 10} |
| 688 | + |
| 689 | + for _, n := range attempts { |
| 690 | + delay := FullJitterBackoffDelay(n, errors.New("test error"), config) |
| 691 | + |
| 692 | + expectedMaxCeiling := float64(baseDelay) * math.Pow(2, float64(n)) |
| 693 | + if expectedMaxCeiling > float64(maxDelay) { |
| 694 | + expectedMaxCeiling = float64(maxDelay) |
| 695 | + } |
| 696 | + |
| 697 | + assert.True(t, delay >= 0, "Delay should be non-negative. Got: %v for attempt %d", delay, n) |
| 698 | + assert.True(t, delay <= time.Duration(expectedMaxCeiling), |
| 699 | + "Delay %v should be less than or equal to current backoff ceiling %v for attempt %d", delay, time.Duration(expectedMaxCeiling), n) |
| 700 | + |
| 701 | + t.Logf("Attempt %d: BaseDelay=%v, MaxDelay=%v, Calculated Ceiling=~%v, Actual Delay=%v", |
| 702 | + n, baseDelay, maxDelay, time.Duration(expectedMaxCeiling), delay) |
| 703 | + |
| 704 | + // Test with MaxDelay disabled (0) |
| 705 | + configNoMax := &Config{delay: baseDelay, maxDelay: 0} |
| 706 | + delayNoMax := FullJitterBackoffDelay(n, errors.New("test error"), configNoMax) |
| 707 | + expectedCeilingNoMax := float64(baseDelay) * math.Pow(2, float64(n)) |
| 708 | + if expectedCeilingNoMax > float64(10*time.Minute) { // Avoid overflow for very large N |
| 709 | + expectedCeilingNoMax = float64(10 * time.Minute) |
| 710 | + } |
| 711 | + assert.True(t, delayNoMax >= 0, "Delay (no max) should be non-negative. Got: %v for attempt %d", delayNoMax, n) |
| 712 | + assert.True(t, delayNoMax <= time.Duration(expectedCeilingNoMax), |
| 713 | + "Delay (no max) %v should be less than or equal to current backoff ceiling %v for attempt %d", delayNoMax, time.Duration(expectedCeilingNoMax), n) |
| 714 | + } |
| 715 | + |
| 716 | + // Test case where baseDelay might be zero |
| 717 | + configZeroBase := &Config{delay: 0, maxDelay: maxDelay} |
| 718 | + delayZeroBase := FullJitterBackoffDelay(0, errors.New("test error"), configZeroBase) |
| 719 | + assert.Equal(t, time.Duration(0), delayZeroBase, "Delay with zero base delay should be 0") |
| 720 | + |
| 721 | + delayZeroBaseAttempt1 := FullJitterBackoffDelay(1, errors.New("test error"), configZeroBase) |
| 722 | + assert.Equal(t, time.Duration(0), delayZeroBaseAttempt1, "Delay with zero base delay (attempt > 0) should be 0") |
| 723 | + |
| 724 | + // Test with very small base delay |
| 725 | + smallBaseDelay := 1 * time.Nanosecond |
| 726 | + configSmallBase := &Config{delay: smallBaseDelay, maxDelay: 100 * time.Nanosecond} |
| 727 | + for i := uint(0); i < 5; i++ { |
| 728 | + d := FullJitterBackoffDelay(i, errors.New("test"), configSmallBase) |
| 729 | + ceil := float64(smallBaseDelay) * math.Pow(2, float64(i)) |
| 730 | + if ceil > 100 { |
| 731 | + ceil = 100 |
| 732 | + } |
| 733 | + assert.True(t, d <= time.Duration(ceil)) |
| 734 | + } |
| 735 | +} |
0 commit comments