github.com/ethersphere/bee/v2@v2.2.0/pkg/p2p/libp2p/internal/reacher/reacher_test.go (about)

     1  // Copyright 2021 The Swarm Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package reacher_test
     6  
     7  import (
     8  	"context"
     9  	"errors"
    10  	"testing"
    11  	"time"
    12  
    13  	"github.com/ethersphere/bee/v2/pkg/p2p"
    14  	"github.com/ethersphere/bee/v2/pkg/p2p/libp2p/internal/reacher"
    15  	"github.com/ethersphere/bee/v2/pkg/swarm"
    16  	"github.com/ethersphere/bee/v2/pkg/util/testutil"
    17  	ma "github.com/multiformats/go-multiaddr"
    18  	"go.uber.org/atomic"
    19  )
    20  
    21  var defaultOptions = reacher.Options{
    22  	PingTimeout:        time.Second * 5,
    23  	Workers:            8,
    24  	RetryAfterDuration: time.Second,
    25  }
    26  
    27  func TestPingSuccess(t *testing.T) {
    28  	t.Parallel()
    29  
    30  	for _, tc := range []struct {
    31  		name          string
    32  		pingFunc      func(context.Context, ma.Multiaddr) (time.Duration, error)
    33  		reachableFunc func(chan struct{}) func(addr swarm.Address, got p2p.ReachabilityStatus)
    34  	}{
    35  		{
    36  			name: "ping success",
    37  			pingFunc: func(context.Context, ma.Multiaddr) (time.Duration, error) {
    38  				return 0, nil
    39  			},
    40  			reachableFunc: func(done chan struct{}) func(addr swarm.Address, got p2p.ReachabilityStatus) {
    41  				return func(addr swarm.Address, got p2p.ReachabilityStatus) {
    42  					if got != p2p.ReachabilityStatusPublic {
    43  						t.Fatalf("got %v, want %v", got, p2p.ReachabilityStatusPublic)
    44  					}
    45  					done <- struct{}{}
    46  				}
    47  			},
    48  		},
    49  		{
    50  			name: "ping failure",
    51  			pingFunc: func(context.Context, ma.Multiaddr) (time.Duration, error) {
    52  				return 0, errors.New("test error")
    53  			},
    54  			reachableFunc: func(done chan struct{}) func(addr swarm.Address, got p2p.ReachabilityStatus) {
    55  				return func(addr swarm.Address, got p2p.ReachabilityStatus) {
    56  					if got != p2p.ReachabilityStatusPrivate {
    57  						t.Fatalf("got %v, want %v", got, p2p.ReachabilityStatusPrivate)
    58  					}
    59  					done <- struct{}{}
    60  				}
    61  			},
    62  		},
    63  	} {
    64  		tc := tc
    65  		t.Run(tc.name, func(t *testing.T) {
    66  			t.Parallel()
    67  
    68  			done := make(chan struct{})
    69  			mock := newMock(tc.pingFunc, tc.reachableFunc(done))
    70  
    71  			r := reacher.New(mock, mock, &defaultOptions)
    72  			testutil.CleanupCloser(t, r)
    73  
    74  			overlay := swarm.RandAddress(t)
    75  
    76  			r.Connected(overlay, nil)
    77  
    78  			select {
    79  			case <-time.After(time.Second * 5):
    80  				t.Fatalf("test timed out")
    81  			case <-done:
    82  			}
    83  		})
    84  	}
    85  }
    86  
    87  func TestDisconnected(t *testing.T) {
    88  	t.Parallel()
    89  
    90  	var (
    91  		disconnectedOverlay = swarm.RandAddress(t)
    92  		disconnectedMa, _   = ma.NewMultiaddr("/ip4/127.0.0.1/tcp/7071/p2p/16Uiu2HAmTBuJT9LvNmBiQiNoTsxE5mtNy6YG3paw79m94CRa9sRb")
    93  	)
    94  
    95  	/*
    96  		Because the Disconnected is called after Connected, it may be that one of the workers
    97  		have picked up the peer already. So to test that the Disconnected really works,
    98  		if the ping function pings the peer we are trying to disconnect, we return an error
    99  		which triggers another attempt in the future, which by the, the peer should already be removed.
   100  	*/
   101  	var errs atomic.Int64
   102  	pingFunc := func(_ context.Context, a ma.Multiaddr) (time.Duration, error) {
   103  		if a != nil && a.Equal(disconnectedMa) {
   104  			errs.Inc()
   105  			if errs.Load() > 1 {
   106  				t.Fatalf("overlay should be disconnected already")
   107  			}
   108  			return 0, errors.New("test error")
   109  		}
   110  		return 0, nil
   111  	}
   112  
   113  	reachableFunc := func(addr swarm.Address, b p2p.ReachabilityStatus) {}
   114  
   115  	mock := newMock(pingFunc, reachableFunc)
   116  
   117  	r := reacher.New(mock, mock, &defaultOptions)
   118  	testutil.CleanupCloser(t, r)
   119  
   120  	r.Connected(swarm.RandAddress(t), nil)
   121  	r.Connected(disconnectedOverlay, disconnectedMa)
   122  	r.Disconnected(disconnectedOverlay)
   123  }
   124  
   125  type mock struct {
   126  	pingFunc      func(context.Context, ma.Multiaddr) (time.Duration, error)
   127  	reachableFunc func(swarm.Address, p2p.ReachabilityStatus)
   128  }
   129  
   130  func newMock(ping func(context.Context, ma.Multiaddr) (time.Duration, error), reach func(swarm.Address, p2p.ReachabilityStatus)) *mock {
   131  	return &mock{
   132  		pingFunc:      ping,
   133  		reachableFunc: reach,
   134  	}
   135  }
   136  
   137  func (m *mock) Ping(ctx context.Context, addr ma.Multiaddr) (time.Duration, error) {
   138  	return m.pingFunc(ctx, addr)
   139  }
   140  
   141  func (m *mock) Reachable(addr swarm.Address, status p2p.ReachabilityStatus) {
   142  	m.reachableFunc(addr, status)
   143  }