1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
|
package lb_test
import (
"context"
"errors"
"testing"
"time"
"github.com/go-kit/kit/endpoint"
"github.com/go-kit/kit/sd"
"github.com/go-kit/kit/sd/lb"
)
func TestRetryMaxTotalFail(t *testing.T) {
var (
endpoints = sd.FixedEndpointer{} // no endpoints
rr = lb.NewRoundRobin(endpoints)
retry = lb.Retry(999, time.Second, rr) // lots of retries
ctx = context.Background()
)
if _, err := retry(ctx, struct{}{}); err == nil {
t.Errorf("expected error, got none") // should fail
}
}
func TestRetryMaxPartialFail(t *testing.T) {
var (
endpoints = []endpoint.Endpoint{
func(context.Context, interface{}) (interface{}, error) { return nil, errors.New("error one") },
func(context.Context, interface{}) (interface{}, error) { return nil, errors.New("error two") },
func(context.Context, interface{}) (interface{}, error) { return struct{}{}, nil /* OK */ },
}
endpointer = sd.FixedEndpointer{
0: endpoints[0],
1: endpoints[1],
2: endpoints[2],
}
retries = len(endpoints) - 1 // not quite enough retries
rr = lb.NewRoundRobin(endpointer)
ctx = context.Background()
)
if _, err := lb.Retry(retries, time.Second, rr)(ctx, struct{}{}); err == nil {
t.Errorf("expected error two, got none")
}
}
func TestRetryMaxSuccess(t *testing.T) {
var (
endpoints = []endpoint.Endpoint{
func(context.Context, interface{}) (interface{}, error) { return nil, errors.New("error one") },
func(context.Context, interface{}) (interface{}, error) { return nil, errors.New("error two") },
func(context.Context, interface{}) (interface{}, error) { return struct{}{}, nil /* OK */ },
}
endpointer = sd.FixedEndpointer{
0: endpoints[0],
1: endpoints[1],
2: endpoints[2],
}
retries = len(endpoints) // exactly enough retries
rr = lb.NewRoundRobin(endpointer)
ctx = context.Background()
)
if _, err := lb.Retry(retries, time.Second, rr)(ctx, struct{}{}); err != nil {
t.Error(err)
}
}
func TestRetryTimeout(t *testing.T) {
t.Skip("Skipping flaky test")
var (
step = make(chan struct{})
e = func(context.Context, interface{}) (interface{}, error) { <-step; return struct{}{}, nil }
timeout = time.Millisecond
retry = lb.Retry(999, timeout, lb.NewRoundRobin(sd.FixedEndpointer{0: e}))
errs = make(chan error, 1)
invoke = func() { _, err := retry(context.Background(), struct{}{}); errs <- err }
)
go func() { step <- struct{}{} }() // queue up a flush of the endpoint
invoke() // invoke the endpoint and trigger the flush
if err := <-errs; err != nil { // that should succeed
t.Error(err)
}
go func() { time.Sleep(10 * timeout); step <- struct{}{} }() // a delayed flush
invoke() // invoke the endpoint
if err := <-errs; err != context.DeadlineExceeded { // that should not succeed
t.Errorf("wanted %v, got none", context.DeadlineExceeded)
}
}
func TestAbortEarlyCustomMessage(t *testing.T) {
var (
myErr = errors.New("aborting early")
cb = func(int, error) (bool, error) { return false, myErr }
endpoints = sd.FixedEndpointer{} // no endpoints
rr = lb.NewRoundRobin(endpoints)
retry = lb.RetryWithCallback(time.Second, rr, cb) // lots of retries
ctx = context.Background()
)
_, err := retry(ctx, struct{}{})
if want, have := myErr, err.(lb.RetryError).Final; want != have {
t.Errorf("want %v, have %v", want, have)
}
}
func TestErrorPassedUnchangedToCallback(t *testing.T) {
var (
myErr = errors.New("my custom error")
cb = func(_ int, err error) (bool, error) {
if want, have := myErr, err; want != have {
t.Errorf("want %v, have %v", want, have)
}
return false, nil
}
endpoint = func(ctx context.Context, request interface{}) (interface{}, error) {
return nil, myErr
}
endpoints = sd.FixedEndpointer{endpoint} // no endpoints
rr = lb.NewRoundRobin(endpoints)
retry = lb.RetryWithCallback(time.Second, rr, cb) // lots of retries
ctx = context.Background()
)
_, err := retry(ctx, struct{}{})
if want, have := myErr, err.(lb.RetryError).Final; want != have {
t.Errorf("want %v, have %v", want, have)
}
}
func TestHandleNilCallback(t *testing.T) {
var (
endpointer = sd.FixedEndpointer{
func(context.Context, interface{}) (interface{}, error) { return struct{}{}, nil /* OK */ },
}
rr = lb.NewRoundRobin(endpointer)
ctx = context.Background()
)
retry := lb.RetryWithCallback(time.Second, rr, nil)
if _, err := retry(ctx, struct{}{}); err != nil {
t.Error(err)
}
}
|