func TestRandom(t *testing.T) { p := loadbalancer.NewStaticPublisher([]endpoint.Endpoint{}) defer p.Stop() lb := loadbalancer.Random(p) if _, err := lb.Get(); err == nil { t.Error("want error, got none") } counts := []int{0, 0, 0} p.Replace([]endpoint.Endpoint{ func(context.Context, interface{}) (interface{}, error) { counts[0]++; return struct{}{}, nil }, func(context.Context, interface{}) (interface{}, error) { counts[1]++; return struct{}{}, nil }, func(context.Context, interface{}) (interface{}, error) { counts[2]++; return struct{}{}, nil }, }) assertLoadBalancerNotEmpty(t, lb) n := 10000 for i := 0; i < n; i++ { e, _ := lb.Get() e(context.Background(), struct{}{}) } want := float64(n) / float64(len(counts)) tolerance := (want / 100.0) * 5 // 5% for _, have := range counts { if math.Abs(want-float64(have)) > tolerance { t.Errorf("want %.0f, have %d", want, have) } } }
func TestRoundRobin(t *testing.T) { p := loadbalancer.NewStaticPublisher([]endpoint.Endpoint{}) defer p.Stop() lb := loadbalancer.RoundRobin(p) if _, err := lb.Get(); err == nil { t.Error("want error, got none") } counts := []int{0, 0, 0} p.Replace([]endpoint.Endpoint{ func(context.Context, interface{}) (interface{}, error) { counts[0]++; return struct{}{}, nil }, func(context.Context, interface{}) (interface{}, error) { counts[1]++; return struct{}{}, nil }, func(context.Context, interface{}) (interface{}, error) { counts[2]++; return struct{}{}, nil }, }) runtime.Gosched() for i, want := range [][]int{ {1, 0, 0}, {1, 1, 0}, {1, 1, 1}, {2, 1, 1}, {2, 2, 1}, {2, 2, 2}, {3, 2, 2}, } { e, _ := lb.Get() e(context.Background(), struct{}{}) if have := counts; !reflect.DeepEqual(want, have) { t.Errorf("%d: want %v, have %v", i+1, want, have) } } }
func TestRetryMax(t *testing.T) { var ( endpoints = []endpoint.Endpoint{} p = loadbalancer.NewStaticPublisher(endpoints) lb = loadbalancer.RoundRobin(p) ) if _, err := loadbalancer.Retry(999, time.Second, lb)(context.Background(), struct{}{}); err == nil { t.Errorf("expected error, got none") } 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 */ }, } p.Replace(endpoints) runtime.Gosched() if _, err := loadbalancer.Retry(len(endpoints)-1, time.Second, lb)(context.Background(), struct{}{}); err == nil { t.Errorf("expected error, got none") } if _, err := loadbalancer.Retry(len(endpoints), time.Second, lb)(context.Background(), struct{}{}); err != nil { t.Error(err) } }
func TestStaticPublisher(t *testing.T) { endpoints := []endpoint.Endpoint{ func(context.Context, interface{}) (interface{}, error) { return struct{}{}, nil }, } p := loadbalancer.NewStaticPublisher(endpoints) defer p.Stop() c := make(chan []endpoint.Endpoint, 1) p.Subscribe(c) if want, have := len(endpoints), len(<-c); want != have { t.Errorf("want %d, have %d", want, have) } endpoints = []endpoint.Endpoint{} p.Replace(endpoints) if want, have := len(endpoints), len(<-c); want != have { t.Errorf("want %d, have %d", want, have) } }
func TestRetryTimeout(t *testing.T) { var ( step = make(chan struct{}) e = func(context.Context, interface{}) (interface{}, error) { <-step; return struct{}{}, nil } timeout = time.Millisecond retry = loadbalancer.Retry(999, timeout, loadbalancer.RoundRobin(loadbalancer.NewStaticPublisher([]endpoint.Endpoint{e}))) errs = make(chan error) invoke = func() { _, err := retry(context.Background(), struct{}{}); errs <- err } ) go invoke() // invoke the endpoint step <- struct{}{} // tell the endpoint to return if err := <-errs; err != nil { // that should succeed t.Error(err) } go invoke() // invoke the endpoint time.Sleep(2 * timeout) // wait step <- struct{}{} // tell the endpoint to return if err := <-errs; err != context.DeadlineExceeded { // that should not succeed t.Errorf("wanted error, got none") } }