github.com/MetalBlockchain/metalgo@v1.11.9/utils/dynamicip/updater_test.go (about)

     1  // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved.
     2  // See the file LICENSE for licensing terms.
     3  
     4  package dynamicip
     5  
     6  import (
     7  	"context"
     8  	"net/netip"
     9  	"testing"
    10  	"time"
    11  
    12  	"github.com/stretchr/testify/require"
    13  
    14  	"github.com/MetalBlockchain/metalgo/utils"
    15  	"github.com/MetalBlockchain/metalgo/utils/logging"
    16  )
    17  
    18  var _ Resolver = (*mockResolver)(nil)
    19  
    20  type mockResolver struct {
    21  	onResolve func(context.Context) (netip.Addr, error)
    22  }
    23  
    24  func (r *mockResolver) Resolve(ctx context.Context) (netip.Addr, error) {
    25  	return r.onResolve(ctx)
    26  }
    27  
    28  func TestNewUpdater(t *testing.T) {
    29  	require := require.New(t)
    30  
    31  	const (
    32  		port            = 9651
    33  		updateFrequency = time.Millisecond
    34  		stopTimeout     = 5 * time.Second
    35  	)
    36  
    37  	var (
    38  		originalAddr        = netip.IPv4Unspecified()
    39  		originalAddrPort    = netip.AddrPortFrom(originalAddr, port)
    40  		newAddr             = netip.AddrFrom4([4]byte{1, 2, 3, 4})
    41  		expectedNewAddrPort = netip.AddrPortFrom(newAddr, port)
    42  		dynamicIP           = utils.NewAtomic(originalAddrPort)
    43  	)
    44  	resolver := &mockResolver{
    45  		onResolve: func(context.Context) (netip.Addr, error) {
    46  			return newAddr, nil
    47  		},
    48  	}
    49  	updaterIntf := NewUpdater(
    50  		dynamicIP,
    51  		resolver,
    52  		updateFrequency,
    53  	)
    54  
    55  	// Assert NewUpdater returns expected type
    56  	require.IsType(&updater{}, updaterIntf)
    57  	updater := updaterIntf.(*updater)
    58  
    59  	// Assert fields set
    60  	require.Equal(dynamicIP, updater.dynamicIP)
    61  	require.Equal(resolver, updater.resolver)
    62  	require.NotNil(updater.rootCtx)
    63  	require.NotNil(updater.rootCtxCancel)
    64  	require.NotNil(updater.doneChan)
    65  	require.Equal(updateFrequency, updater.updateFreq)
    66  
    67  	// Start updating the IP address
    68  	go updater.Dispatch(logging.NoLog{})
    69  
    70  	// Assert that the IP is updated within 5s.
    71  	require.Eventually(
    72  		func() bool {
    73  			return dynamicIP.Get() == expectedNewAddrPort
    74  		},
    75  		5*time.Second,
    76  		updateFrequency,
    77  	)
    78  
    79  	// Make sure stopChan and doneChan are closed when stop is called
    80  	updater.Stop()
    81  
    82  	ctx, cancel := context.WithTimeout(context.Background(), stopTimeout)
    83  	defer cancel()
    84  	select {
    85  	case <-updater.rootCtx.Done():
    86  	case <-ctx.Done():
    87  		require.FailNow("timeout waiting for root context cancellation")
    88  	}
    89  	select {
    90  	case _, open := <-updater.doneChan:
    91  		require.False(open)
    92  	case <-ctx.Done():
    93  		require.FailNow("timeout waiting for doneChan to close")
    94  	}
    95  }