github.com/gravitational/teleport/api@v0.0.0-20240507183017-3110591cbafc/utils/retryutils/retry_test.go (about) 1 /* 2 Copyright 2021-2022 Gravitational, Inc. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package retryutils 18 19 import ( 20 "testing" 21 "time" 22 23 "github.com/stretchr/testify/require" 24 ) 25 26 // TestLinear tests retry logic 27 func TestLinear(t *testing.T) { 28 t.Parallel() 29 30 r, err := NewLinear(LinearConfig{ 31 Step: time.Second, 32 Max: 3 * time.Second, 33 }) 34 require.NoError(t, err) 35 testLinear(t, r) 36 } 37 38 func TestLinearV2(t *testing.T) { 39 t.Parallel() 40 41 r2, err := NewRetryV2(RetryV2Config{ 42 Driver: NewLinearDriver(time.Second), 43 Max: 3 * time.Second, 44 }) 45 require.NoError(t, err) 46 testLinear(t, r2) 47 } 48 49 func testLinear(t *testing.T, r Retry) { 50 require.Equal(t, time.Duration(0), r.Duration()) 51 r.Inc() 52 require.Equal(t, time.Second, r.Duration()) 53 r.Inc() 54 require.Equal(t, 2*time.Second, r.Duration()) 55 r.Inc() 56 require.Equal(t, 3*time.Second, r.Duration()) 57 r.Inc() 58 require.Equal(t, 3*time.Second, r.Duration()) 59 r.Reset() 60 require.Equal(t, time.Duration(0), r.Duration()) 61 } 62 63 func TestExponential(t *testing.T) { 64 t.Parallel() 65 66 r, err := NewRetryV2(RetryV2Config{ 67 Driver: NewExponentialDriver(time.Second), 68 Max: 12 * time.Second, 69 }) 70 require.NoError(t, err) 71 72 require.Equal(t, time.Duration(0), r.Duration()) 73 r.Inc() 74 require.Equal(t, time.Second, r.Duration()) 75 r.Inc() 76 require.Equal(t, 2*time.Second, r.Duration()) 77 r.Inc() 78 require.Equal(t, 4*time.Second, r.Duration()) 79 r.Inc() 80 require.Equal(t, 8*time.Second, r.Duration()) 81 r.Inc() 82 // should hit configured maximum 83 require.Equal(t, 12*time.Second, r.Duration()) 84 r.Reset() 85 require.Equal(t, time.Duration(0), r.Duration()) 86 87 // verify that exponentiation is capped s.t. we don't wrap 88 for i := 0; i < 128; i++ { 89 r.Inc() 90 require.True(t, r.Duration() > 0 && r.Duration() <= time.Second*12) 91 } 92 } 93 94 func TestLinearRetryMax(t *testing.T) { 95 t.Parallel() 96 97 cases := []struct { 98 desc string 99 config LinearConfig 100 previousCompareFn require.ComparisonAssertionFunc 101 }{ 102 { 103 desc: "FullJitter", 104 config: LinearConfig{ 105 First: time.Second * 45, 106 Step: time.Second * 30, 107 Max: time.Minute, 108 Jitter: NewFullJitter(), 109 }, 110 previousCompareFn: require.NotEqual, 111 }, 112 { 113 desc: "HalfJitter", 114 config: LinearConfig{ 115 First: time.Second * 45, 116 Step: time.Second * 30, 117 Max: time.Minute, 118 Jitter: NewHalfJitter(), 119 }, 120 previousCompareFn: require.NotEqual, 121 }, 122 { 123 desc: "SeventhJitter", 124 config: LinearConfig{ 125 First: time.Second * 45, 126 Step: time.Second * 30, 127 Max: time.Minute, 128 Jitter: NewSeventhJitter(), 129 }, 130 previousCompareFn: require.NotEqual, 131 }, 132 133 { 134 desc: "NoJitter", 135 config: LinearConfig{ 136 First: time.Second * 45, 137 Step: time.Second * 30, 138 Max: time.Minute, 139 }, 140 previousCompareFn: require.Equal, 141 }, 142 } 143 144 for _, tc := range cases { 145 tc := tc 146 t.Run(tc.desc, func(t *testing.T) { 147 t.Parallel() 148 linear, err := NewLinear(tc.config) 149 require.NoError(t, err) 150 151 // artificially spike the attempts to get to max 152 linear.attempt = 100 153 154 // get the initial previous value to compare with 155 previous := linear.Duration() 156 linear.Inc() 157 158 for i := 0; i < 50; i++ { 159 duration := linear.Duration() 160 linear.Inc() 161 162 // ensure duration does not exceed maximum 163 require.LessOrEqual(t, duration, tc.config.Max) 164 165 // ensure duration comparison to previous is satisfied 166 tc.previousCompareFn(t, duration, previous) 167 } 168 }) 169 } 170 }