func TestBehaviourSynchronously(t *testing.T) {
	create := func(t *testing.T) (ServerSelector, net.Addr, net.Addr) {
		ss := createWithServers(t, []string{"127.0.0.1:4200", "127.255.0.1:4200"})
		addrA := ss.addrs[0]
		addrB := ss.addrs[1]
		if addrA == addrB {
			t.Fatalf("for test to be valid addresses must differ")
		}
		return ss, addrA, addrB
	}

	t.Run("after a net error, an address should be not be pickable", func(t *testing.T) {
		ss, addrA, _ := create(t)

		ss.OnResult(addrA, &net.OpError{})
		fuzzer(func(k string) {
			addr, err := ss.PickServer(k)
			requireNoError(t, err)
			if addrA == addr {
				t.Fatalf("address still available")
			}
		})
	})

	t.Run("after a successful retry, an address should be available again", func(t *testing.T) {
		ss, addrA, _ := create(t)

		ss.OnResult(addrA, &net.OpError{})
		ss.OnResult(addrA, nil)

		gotAddr := 0
		fuzzer(func(k string) {
			addr, err := ss.PickServer(k)
			requireNoError(t, err)
			if addr == addrA {
				gotAddr++
			}
		})
		if gotAddr == 0 {
			t.Fatalf("address not pickable again")
		}
	})

	t.Run("on error a retry is scheduled, and after the wait the addr is available again", func(t *testing.T) {
		ss, addrA, addrB := create(t)
		// when all address have errors, we return an error that signals no servers are available (circuit breaker)
		ss.OnResult(addrA, &net.OpError{})
		ss.OnResult(addrB, &net.OpError{})

		_, err := ss.PickServer("hi")
		assertEqualError(t, err, ErrNoServers.Error())

		// ensure that the retry goroutine that schedule didn't make the server available again before we expect
		time.Sleep(startingWait / 2)
		_, err = ss.PickServer("hi")
		assertEqualError(t, err, ErrNoServers.Error())

		// and that after the retry wait expires we do schedule a retry
		time.Sleep(startingWait * 2)
		a, err := ss.PickServer("hi")
		if err != nil {
			t.Fatalf("should have become eligable for a retry")
		}
		if a == nil {
			t.Fatalf("no addr returned for retry")
		}
	})
}
// Note: this test is technically non-deterministic as the retry scheduling is async,
// but failures should be exceedingly rare due to the length of time until the first timeout.
//
// If a fix is required: probably simplest to change the startingWait to a var and modify in this test
this test is technically non-deterministic as the retry scheduling is async,
but failures should be exceedingly rare due to the length of time until the first timeout.

If a fix is required: probably simplest to change the startingWait to a var and modify in this test
// Note : this test is technically non - deterministic as the retry scheduling is async // but failures should be exceedingly rare due to the length of time until the first timeout . // // If a fix is required : probably simplest to change the startingWait to a var and modify in this test
func TestBehaviourSynchronously(t *testing.T) {
	create := func(t *testing.T) (ServerSelector, net.Addr, net.Addr) {
		ss := createWithServers(t, []string{"127.0.0.1:4200", "127.255.0.1:4200"})
		addrA := ss.addrs[0]
		addrB := ss.addrs[1]
		if addrA == addrB {
			t.Fatalf("for test to be valid addresses must differ")
		}
		return ss, addrA, addrB
	}
	t.Run("after a net error, an address should be not be pickable", func(t *testing.T) {
		ss, addrA, _ := create(t)
		ss.OnResult(addrA, &net.OpError{})
		fuzzer(func(k string) {
			addr, err := ss.PickServer(k)
			requireNoError(t, err)
			if addrA == addr {
				t.Fatalf("address still available")
			}
		})
	})
	t.Run("after a successful retry, an address should be available again", func(t *testing.T) {
		ss, addrA, _ := create(t)
		ss.OnResult(addrA, &net.OpError{})
		ss.OnResult(addrA, nil)
		gotAddr := 0
		fuzzer(func(k string) {
			addr, err := ss.PickServer(k)
			requireNoError(t, err)
			if addr == addrA {
				gotAddr++
			}
		})
		if gotAddr == 0 {
			t.Fatalf("address not pickable again")
		}
	})
	t.Run("on error a retry is scheduled, and after the wait the addr is available again", func(t *testing.T) {
		ss, addrA, addrB := create(t)
		ss.OnResult(addrA, &net.OpError{})
		ss.OnResult(addrB, &net.OpError{})
		_, err := ss.PickServer("hi")
		assertEqualError(t, err, ErrNoServers.Error())
		time.Sleep(startingWait / 2)
		_, err = ss.PickServer("hi")
		assertEqualError(t, err, ErrNoServers.Error())
		time.Sleep(startingWait * 2)
		a, err := ss.PickServer("hi")
		if err != nil {
			t.Fatalf("should have become eligable for a retry")
		}
		if a == nil {
			t.Fatalf("no addr returned for retry")
		}
	})
}
func TestBehaviourSynchronously ( t * testing . T ) { create := func ( t * testing . T ) ( ServerSelector , net . Addr , net . Addr ) { ss := createWithServers ( t , [ ] string { " " , " " } ) addrA := ss . addrs [ 0 ] addrB := ss . addrs [ 1 ] if addrA == addrB { t . Fatalf ( " " ) } return ss , addrA , addrB } t . Run ( " " , func ( t * testing . T ) { ss , addrA , _ := create ( t ) ss . OnResult ( addrA , & net . OpError { } ) fuzzer ( func ( k string ) { addr , err := ss . PickServer ( k ) requireNoError ( t , err ) if addrA == addr { t . Fatalf ( " " ) } } ) } ) t . Run ( " " , func ( t * testing . T ) { ss , addrA , _ := create ( t ) ss . OnResult ( addrA , & net . OpError { } ) ss . OnResult ( addrA , nil ) gotAddr := 0 fuzzer ( func ( k string ) { addr , err := ss . PickServer ( k ) requireNoError ( t , err ) if addr == addrA { gotAddr ++ } } ) if gotAddr == 0 { t . Fatalf ( " " ) } } ) t . Run ( " " , func ( t * testing . T ) { ss , addrA , addrB := create ( t ) ss . OnResult ( addrA , & net . OpError { } ) ss . OnResult ( addrB , & net . OpError { } ) _ , err := ss . PickServer ( " " ) assertEqualError ( t , err , ErrNoServers . Error ( ) ) time . Sleep ( startingWait / 2 ) _ , err = ss . PickServer ( " " ) assertEqualError ( t , err , ErrNoServers . Error ( ) ) time . Sleep ( startingWait * 2 ) a , err := ss . PickServer ( " " ) if err != nil { t . Fatalf ( " " ) } if a == nil { t . Fatalf ( " " ) } } ) }
this test is technically non-deterministic as the retry scheduling is async,
but failures should be exceedingly rare due to the length of time until the first timeout.
this test is technically non - deterministic as the retry scheduling is async but failures should be exceedingly rare due to the length of time until the first timeout .
// when all address have errors, we return an error that signals no servers are available (circuit breaker) // ensure that the retry goroutine that schedule didn't make the server available again before we expect // and that after the retry wait expires we do schedule a retry