github.com/msales/pkg/v3@v3.24.0/breaker/breaker_test.go (about) 1 package breaker_test 2 3 import ( 4 "errors" 5 "sync" 6 "testing" 7 "time" 8 9 "github.com/msales/pkg/v3/breaker" 10 "github.com/stretchr/testify/assert" 11 ) 12 13 func TestThresholdFuse(t *testing.T) { 14 f := breaker.ThresholdFuse(5) 15 16 assert.False(t, f.Trip(breaker.Counter{Failures: 5})) 17 assert.True(t, f.Trip(breaker.Counter{Failures: 6})) 18 } 19 20 func TestConsecutiveFuse(t *testing.T) { 21 f := breaker.ConsecutiveFuse(5) 22 23 assert.False(t, f.Trip(breaker.Counter{ConsecutiveFailures: 5})) 24 assert.True(t, f.Trip(breaker.Counter{ConsecutiveFailures: 6})) 25 } 26 27 func TestRateFuse(t *testing.T) { 28 f := breaker.RateFuse(10) 29 30 assert.False(t, f.Trip(breaker.Counter{Requests: 10, Failures: 9})) 31 assert.True(t, f.Trip(breaker.Counter{Requests: 10, Failures: 10})) 32 } 33 34 func TestRateFusePanicsIfRateOver100(t *testing.T) { 35 assert.Panics(t, func() { breaker.RateFuse(101) }) 36 } 37 38 func TestNewBreaker(t *testing.T) { 39 b := breaker.NewBreaker(breaker.ThresholdFuse(5)) 40 41 assert.IsType(t, &breaker.Breaker{}, b) 42 assert.Equal(t, breaker.StateClosed, b.State()) 43 } 44 45 func TestBreaker_Run(t *testing.T) { 46 b := breaker.NewBreaker(breaker.ThresholdFuse(1), breaker.WithSleep(100*time.Millisecond)) 47 48 err := b.Run(successFunc) 49 50 assert.NoError(t, err) 51 assert.Equal(t, breaker.StateClosed, b.State()) 52 53 err = b.Run(failureFunc) 54 55 assert.Error(t, err) 56 assert.Equal(t, breaker.StateClosed, b.State()) 57 58 err = b.Run(failureFunc) 59 60 assert.Error(t, err) 61 assert.Equal(t, breaker.StateOpen, b.State()) 62 63 err = b.Run(failureFunc) 64 65 assert.Equal(t, breaker.ErrOpenState, err) 66 assert.Equal(t, breaker.StateOpen, b.State()) 67 68 time.Sleep(100 * time.Millisecond) 69 70 assert.Equal(t, breaker.StateHalfOpen, b.State()) 71 72 err = b.Run(failureFunc) 73 74 assert.Equal(t, errTest, err) 75 assert.Equal(t, breaker.StateOpen, b.State()) 76 77 time.Sleep(100 * time.Millisecond) 78 79 assert.Equal(t, breaker.StateHalfOpen, b.State()) 80 81 err = b.Run(successFunc) 82 83 assert.NoError(t, err) 84 assert.Equal(t, breaker.StateClosed, b.State()) 85 } 86 87 func TestBreaker_RunOnlyAllowsXTestRequests(t *testing.T) { 88 b := breaker.NewBreaker(breaker.ThresholdFuse(1), breaker.WithSleep(100*time.Millisecond), breaker.WithTestRequests(2)) 89 _ = b.Run(failureFunc) 90 _ = b.Run(failureFunc) 91 92 assert.Equal(t, breaker.StateOpen, b.State()) 93 94 time.Sleep(101 * time.Millisecond) 95 96 errs := []error{} 97 mu := sync.Mutex{} 98 wg := sync.WaitGroup{} 99 wg.Add(4) 100 for i := 0; i < 4; i++ { 101 go func() { 102 err := b.Run(delayedSuccessFunc) 103 104 mu.Lock() 105 errs = append(errs, err) 106 mu.Unlock() 107 108 wg.Done() 109 }() 110 } 111 112 wg.Wait() 113 114 tooMany := 0 115 for _, err := range errs { 116 if err == breaker.ErrTooManyRequests { 117 tooMany++ 118 } 119 } 120 121 assert.Equal(t, 2, tooMany) 122 } 123 124 func TestBreaker_RunHandlesPanic(t *testing.T) { 125 b := breaker.NewBreaker(breaker.ThresholdFuse(1), breaker.WithSleep(100*time.Millisecond)) 126 _ = b.Run(failureFunc) 127 128 assert.Panics(t, func() { _ = b.Run(panicFunc) }) 129 assert.Equal(t, breaker.StateOpen, b.State()) 130 } 131 132 func BenchmarkBreaker_RunSuccess(b *testing.B) { 133 br := breaker.NewBreaker(breaker.ThresholdFuse(1), breaker.WithSleep(100*time.Millisecond)) 134 135 b.ReportAllocs() 136 b.ResetTimer() 137 for i := 0; i < b.N; i++ { 138 _ = br.Run(successFunc) 139 } 140 } 141 142 func BenchmarkBreaker_RunFailure(b *testing.B) { 143 br := breaker.NewBreaker(breaker.ThresholdFuse(uint64(b.N)), breaker.WithSleep(100*time.Millisecond)) 144 145 b.ReportAllocs() 146 b.ResetTimer() 147 for i := 0; i < b.N; i++ { 148 _ = br.Run(failureFunc) 149 } 150 } 151 152 var errTest = errors.New("test") 153 154 func successFunc() error { 155 return nil 156 } 157 158 func delayedSuccessFunc() error { 159 time.Sleep(10 * time.Millisecond) 160 return nil 161 } 162 163 func failureFunc() error { 164 return errTest 165 } 166 167 func panicFunc() error { 168 panic(errTest) 169 }