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  }