github.com/ethersphere/bee/v2@v2.2.0/pkg/p2p/libp2p/internal/breaker/breaker_test.go (about) 1 // Copyright 2020 The Swarm Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package breaker_test 6 7 import ( 8 "errors" 9 "testing" 10 "time" 11 12 "github.com/ethersphere/bee/v2/pkg/p2p/libp2p/internal/breaker" 13 ) 14 15 func TestExecute(t *testing.T) { 16 t.Parallel() 17 18 testErr := errors.New("test error") 19 shouldNotBeCalledErr := errors.New("should not be called") 20 failInterval := 10 * time.Minute 21 startBackoff := 1 * time.Minute 22 initTime := time.Now() 23 24 testCases := map[string]struct { 25 limit int 26 ferrors []error 27 iterations int 28 times []time.Time 29 expectedErrs []error 30 }{ 31 "f() returns nil": { 32 limit: 5, 33 iterations: 1, 34 ferrors: []error{nil}, 35 times: nil, 36 expectedErrs: []error{nil}, 37 }, 38 "f() returns error": { 39 limit: 5, 40 ferrors: []error{testErr}, 41 iterations: 1, 42 times: nil, 43 expectedErrs: []error{testErr}, 44 }, 45 "Break error": { 46 limit: 1, 47 ferrors: []error{testErr, shouldNotBeCalledErr}, 48 iterations: 3, 49 times: nil, 50 expectedErrs: []error{testErr, breaker.ErrClosed, breaker.ErrClosed}, 51 }, 52 "Break error - mix iterations": { 53 limit: 3, 54 ferrors: []error{testErr, nil, testErr, testErr, testErr, shouldNotBeCalledErr}, 55 iterations: 6, 56 times: nil, 57 expectedErrs: []error{testErr, nil, testErr, testErr, testErr, breaker.ErrClosed}, 58 }, 59 "Expiration - return f() error": { 60 limit: 3, 61 ferrors: []error{testErr, testErr, testErr, testErr, testErr}, 62 iterations: 5, 63 times: []time.Time{initTime, initTime, initTime.Add(2 * failInterval), initTime, initTime, initTime, initTime}, 64 expectedErrs: []error{testErr, testErr, testErr, testErr, testErr}, 65 }, 66 "Backoff - close, reopen, close, don't open": { 67 limit: 1, 68 ferrors: []error{testErr, shouldNotBeCalledErr, testErr, shouldNotBeCalledErr, testErr, shouldNotBeCalledErr, shouldNotBeCalledErr}, 69 iterations: 7, 70 times: []time.Time{initTime, initTime, initTime, initTime.Add(startBackoff + time.Second), initTime, initTime, initTime, initTime.Add(2*startBackoff + time.Second), initTime, initTime, initTime, initTime.Add(startBackoff + time.Second)}, 71 expectedErrs: []error{testErr, breaker.ErrClosed, testErr, breaker.ErrClosed, testErr, breaker.ErrClosed, breaker.ErrClosed}, 72 }, 73 } 74 75 for name, tc := range testCases { 76 tc := tc 77 t.Run(name, func(t *testing.T) { 78 t.Parallel() 79 80 ctMock := ¤tTimeMock{ 81 times: tc.times, 82 } 83 84 b := breaker.NewBreakerWithCurrentTimeFn(breaker.Options{ 85 Limit: tc.limit, 86 StartBackoff: startBackoff, 87 FailInterval: failInterval, 88 }, ctMock.Time) 89 90 for i := 0; i < tc.iterations; i++ { 91 if err := b.Execute(func() error { 92 if errors.Is(tc.ferrors[i], shouldNotBeCalledErr) { 93 t.Fatal(tc.ferrors[i]) 94 } 95 96 return tc.ferrors[i] 97 }); !errors.Is(err, tc.expectedErrs[i]) { 98 t.Fatalf("expected err: %s, got: %s, iteration %v", tc.expectedErrs[i], err, i) 99 } 100 } 101 }) 102 } 103 } 104 105 func TestClosedUntil(t *testing.T) { 106 t.Parallel() 107 108 timestamp := time.Now() 109 startBackoff := 1 * time.Minute 110 testError := errors.New("test error") 111 ctMock := ¤tTimeMock{ 112 times: []time.Time{timestamp, timestamp, timestamp}, 113 } 114 115 b := breaker.NewBreakerWithCurrentTimeFn(breaker.Options{ 116 Limit: 1, 117 StartBackoff: startBackoff, 118 }, ctMock.Time) 119 120 notClosed := b.ClosedUntil() 121 if notClosed != timestamp { 122 t.Fatalf("expected: %s, got: %s", timestamp, notClosed) 123 } 124 125 if err := b.Execute(func() error { 126 return testError 127 }); !errors.Is(err, testError) { 128 t.Fatalf("expected nil got %s", err) 129 } 130 131 closed := b.ClosedUntil() 132 if closed != timestamp.Add(startBackoff) { 133 t.Fatalf("expected: %s, got: %s", timestamp.Add(startBackoff), notClosed) 134 } 135 } 136 137 type currentTimeMock struct { 138 times []time.Time 139 curr int 140 } 141 142 func (c *currentTimeMock) Time() time.Time { 143 if c.times == nil { 144 return time.Now() 145 } 146 147 t := c.times[c.curr] 148 c.curr++ 149 return t 150 }