1 package waiter
2
3 import (
4 mathrand "math/rand"
5 "strings"
6 "testing"
7 "time"
8
9 "github.com/aws/smithy-go/rand"
10 )
11
12 func TestComputeDelay(t *testing.T) {
13 cases := map[string]struct {
14 totalAttempts int64
15 minDelay time.Duration
16 maxDelay time.Duration
17 maxWaitTime time.Duration
18 expectedMaxDelays []time.Duration
19 expectedError string
20 expectedMinAttempts int
21 }{
22 "standard": {
23 totalAttempts: 8,
24 minDelay: 2 * time.Second,
25 maxDelay: 120 * time.Second,
26 maxWaitTime: 300 * time.Second,
27 expectedMaxDelays: []time.Duration{2, 4, 8, 16, 32, 64, 120, 120},
28 expectedMinAttempts: 8,
29 },
30 "zero minDelay": {
31 totalAttempts: 3,
32 minDelay: 0,
33 maxDelay: 120 * time.Second,
34 maxWaitTime: 300 * time.Second,
35 expectedError: "minDelay must be greater than zero",
36 },
37 "zero maxDelay": {
38 totalAttempts: 3,
39 minDelay: 10 * time.Second,
40 maxDelay: 0,
41 maxWaitTime: 300 * time.Second,
42 expectedError: "maxDelay must be greater than zero",
43 },
44 "zero remaining time": {
45 totalAttempts: 3,
46 minDelay: 10 * time.Second,
47 maxDelay: 20 * time.Second,
48 maxWaitTime: 0,
49 expectedMaxDelays: []time.Duration{0},
50 expectedMinAttempts: 1,
51 },
52 "max wait time is less than min delay": {
53 totalAttempts: 3,
54 minDelay: 10 * time.Second,
55 maxDelay: 20 * time.Second,
56 maxWaitTime: 5 * time.Second,
57 expectedMaxDelays: []time.Duration{0},
58 expectedMinAttempts: 1,
59 },
60 "large minDelay": {
61 totalAttempts: 80,
62 minDelay: 150 * time.Minute,
63 maxDelay: 200 * time.Minute,
64 maxWaitTime: 250 * time.Minute,
65 expectedMinAttempts: 1,
66 },
67 "large maxDelay": {
68 totalAttempts: 80,
69 minDelay: 15 * time.Minute,
70 maxDelay: 2000 * time.Minute,
71 maxWaitTime: 250 * time.Minute,
72 expectedMinAttempts: 5,
73 },
74 }
75
76 for name, c := range cases {
77 t.Run(name, func(t *testing.T) {
78
79 r := rand.Reader
80 defer func() {
81 rand.Reader = r
82 }()
83 rand.Reader = mathrand.New(mathrand.NewSource(1))
84
85
86 delays, err := mockwait(c.totalAttempts, c.minDelay, c.maxDelay, c.maxWaitTime)
87
88 if len(c.expectedError) != 0 {
89 if err == nil {
90 t.Fatalf("expected error, got none")
91 }
92 if e, a := c.expectedError, err.Error(); !strings.Contains(a, e) {
93 t.Fatalf("expected error %v, got %v instead", e, a)
94 }
95 } else if err != nil {
96 t.Fatalf("expected no error, got %v", err)
97 }
98
99 if e, a := c.expectedMinAttempts, len(delays); e > a {
100 t.Logf("%v", delays)
101 t.Fatalf("expected minimum attempts to be %v, got %v", e, a)
102 }
103
104 for i, expectedDelay := range c.expectedMaxDelays {
105 if e, a := expectedDelay*time.Second, delays[i]; e < a {
106 t.Fatalf("attempt %d : expected delay to be less than %v, got %v", i+1, e, a)
107 }
108
109 if e, a := c.minDelay, delays[i]; e > a && c.maxWaitTime > c.minDelay {
110 t.Fatalf("attempt %d : expected delay to be more than %v, got %v", i+1, e, a)
111 }
112 }
113 t.Logf("delays : %v", delays)
114 })
115 }
116 }
117
118 func mockwait(maxAttempts int64, minDelay, maxDelay, maxWaitTime time.Duration) ([]time.Duration, error) {
119 delays := make([]time.Duration, 0)
120 remainingTime := maxWaitTime
121 var attempt int64
122
123 for {
124 attempt++
125
126 if maxAttempts < attempt {
127 break
128 }
129
130 delay, err := ComputeDelay(attempt, minDelay, maxDelay, remainingTime)
131 if err != nil {
132 return delays, err
133 }
134
135 delays = append(delays, delay)
136
137 remainingTime -= delay
138 if remainingTime < minDelay {
139 break
140 }
141 }
142
143 return delays, nil
144 }
145
View as plain text