kubo/peering/peering_test.go
Steven Allen e10289a93d fix: really cap the max backoff at 10 minutes
While preserving some randomness.

And add a test.
2020-05-25 21:20:23 -07:00

159 lines
4.6 KiB
Go

package peering
import (
"context"
"testing"
"time"
"github.com/libp2p/go-libp2p"
connmgr "github.com/libp2p/go-libp2p-connmgr"
"github.com/libp2p/go-libp2p-core/host"
"github.com/libp2p/go-libp2p-core/network"
"github.com/libp2p/go-libp2p-core/peer"
"github.com/stretchr/testify/require"
)
func newNode(ctx context.Context, t *testing.T) host.Host {
h, err := libp2p.New(
ctx,
libp2p.ListenAddrStrings("/ip4/127.0.0.1/tcp/0"),
// We'd like to set the connection manager low water to 0, but
// that would disable the connection manager.
libp2p.ConnectionManager(connmgr.NewConnManager(1, 100, 0)),
)
require.NoError(t, err)
return h
}
func TestPeeringService(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
h1 := newNode(ctx, t)
ps1 := NewPeeringService(h1)
h2 := newNode(ctx, t)
h3 := newNode(ctx, t)
h4 := newNode(ctx, t)
// peer 1 -> 2
ps1.AddPeer(peer.AddrInfo{ID: h2.ID(), Addrs: h2.Addrs()})
// We haven't started so we shouldn't have any peers.
require.Never(t, func() bool {
return len(h1.Network().Peers()) > 0
}, 100*time.Millisecond, 1*time.Second, "expected host 1 to have no peers")
// Use p4 to take up the one slot we have in the connection manager.
for _, h := range []host.Host{h1, h2} {
require.NoError(t, h.Connect(ctx, peer.AddrInfo{ID: h4.ID(), Addrs: h4.Addrs()}))
h.ConnManager().TagPeer(h4.ID(), "sticky-peer", 1000)
}
// Now start.
require.NoError(t, ps1.Start())
// starting twice is fine.
require.NoError(t, ps1.Start())
// We should eventually connect.
require.Eventually(t, func() bool {
return h1.Network().Connectedness(h2.ID()) == network.Connected
}, 30*time.Second, 10*time.Millisecond)
// Now explicitly connect to p3.
require.NoError(t, h1.Connect(ctx, peer.AddrInfo{ID: h3.ID(), Addrs: h3.Addrs()}))
require.Eventually(t, func() bool {
return h1.Network().Connectedness(h2.ID()) == network.Connected
}, 30*time.Second, 100*time.Millisecond)
require.Len(t, h1.Network().Peers(), 3)
// force a disconnect
h1.ConnManager().TrimOpenConns(ctx)
// Should disconnect from p3.
require.Eventually(t, func() bool {
return h1.Network().Connectedness(h3.ID()) != network.Connected
}, 5*time.Second, 10*time.Millisecond)
// Should remain connected to p2
require.Never(t, func() bool {
return h1.Network().Connectedness(h2.ID()) != network.Connected
}, 5*time.Second, 1*time.Second)
// Now force h2 to disconnect (we have an asymmetric peering).
conns := h2.Network().ConnsToPeer(h1.ID())
require.NotEmpty(t, conns)
h2.ConnManager().TrimOpenConns(ctx)
// All conns to peer should eventually close.
for _, c := range conns {
require.Eventually(t, func() bool {
s, err := c.NewStream()
if s != nil {
_ = s.Reset()
}
return err != nil
}, 5*time.Second, 10*time.Millisecond)
}
// Should eventually re-connect.
require.Eventually(t, func() bool {
return h1.Network().Connectedness(h2.ID()) == network.Connected
}, 30*time.Second, 1*time.Second)
// Unprotect 2 from 1.
ps1.RemovePeer(h2.ID())
// Trim connections.
h1.ConnManager().TrimOpenConns(ctx)
// Should disconnect
require.Eventually(t, func() bool {
return h1.Network().Connectedness(h2.ID()) != network.Connected
}, 5*time.Second, 10*time.Millisecond)
// Should never reconnect.
require.Never(t, func() bool {
return h1.Network().Connectedness(h2.ID()) == network.Connected
}, 20*time.Second, 1*time.Second)
// Until added back
ps1.AddPeer(peer.AddrInfo{ID: h2.ID(), Addrs: h2.Addrs()})
ps1.AddPeer(peer.AddrInfo{ID: h3.ID(), Addrs: h3.Addrs()})
require.Eventually(t, func() bool {
return h1.Network().Connectedness(h2.ID()) == network.Connected
}, 30*time.Second, 1*time.Second)
require.Eventually(t, func() bool {
return h1.Network().Connectedness(h3.ID()) == network.Connected
}, 30*time.Second, 1*time.Second)
// Should be able to repeatedly stop.
require.NoError(t, ps1.Stop())
require.NoError(t, ps1.Stop())
// Adding and removing should work after stopping.
ps1.AddPeer(peer.AddrInfo{ID: h4.ID(), Addrs: h4.Addrs()})
ps1.RemovePeer(h2.ID())
}
func TestNextBackoff(t *testing.T) {
minMaxBackoff := (100 - maxBackoffJitter) / 100 * maxBackoff
for x := 0; x < 1000; x++ {
ph := peerHandler{nextDelay: time.Second}
for min, max := time.Second*3/2, time.Second*5/2; min < minMaxBackoff; min, max = min*3/2, max*5/2 {
b := ph.nextBackoff()
if b > max || b < min {
t.Errorf("expected backoff %s to be between %s and %s", b, min, max)
}
}
for i := 0; i < 100; i++ {
b := ph.nextBackoff()
if b < minMaxBackoff || b > maxBackoff {
t.Fatal("failed to stay within max bounds")
}
}
}
}