github.com/decred/dcrlnd@v0.7.6/netann/host_ann_test.go (about)

     1  package netann
     2  
     3  import (
     4  	"net"
     5  	"testing"
     6  	"time"
     7  
     8  	"github.com/decred/dcrlnd/ticker"
     9  	"github.com/stretchr/testify/assert"
    10  	"github.com/stretchr/testify/require"
    11  )
    12  
    13  // TestHostAnnouncerUpdates tests that the HostAnnouncer will properly announce
    14  // a new set of addresses each time a target host changes and will noop if not
    15  // change happens during an interval.
    16  func TestHostAnnouncerUpdates(t *testing.T) {
    17  	t.Parallel()
    18  
    19  	hosts := []string{"test.com", "example.com"}
    20  	startingAddrs := []net.Addr{
    21  		&net.TCPAddr{
    22  			IP: net.ParseIP("1.1.1.1"),
    23  		},
    24  		&net.TCPAddr{
    25  			IP: net.ParseIP("8.8.8.8"),
    26  		},
    27  	}
    28  
    29  	ticker := ticker.NewForce(time.Hour * 24)
    30  
    31  	testTimeout := time.Millisecond * 200
    32  
    33  	type annReq struct {
    34  		newAddrs     []net.Addr
    35  		removedAddrs map[string]struct{}
    36  	}
    37  
    38  	testCases := []struct {
    39  		preAdvertisedIPs map[string]struct{}
    40  		startingAddrs    []net.Addr
    41  
    42  		preTickHosts  map[string]net.Addr
    43  		postTickHosts map[string]net.Addr
    44  
    45  		updateTriggered bool
    46  
    47  		newAddrs     []net.Addr
    48  		removedAddrs map[string]struct{}
    49  	}{
    50  		// The set of addresses are the same before and after a tick we
    51  		// expect no change.
    52  		{
    53  			preTickHosts: map[string]net.Addr{
    54  				"test.com": &net.TCPAddr{
    55  					IP: net.ParseIP("1.1.1.1"),
    56  				},
    57  				"example.com": &net.TCPAddr{
    58  					IP: net.ParseIP("8.8.8.8"),
    59  				},
    60  			},
    61  			startingAddrs: startingAddrs,
    62  
    63  			postTickHosts: map[string]net.Addr{
    64  				"test.com": &net.TCPAddr{
    65  					IP: net.ParseIP("1.1.1.1"),
    66  				},
    67  				"example.com": &net.TCPAddr{
    68  					IP: net.ParseIP("8.8.8.8"),
    69  				},
    70  			},
    71  
    72  			updateTriggered: false,
    73  		},
    74  
    75  		// Half of the addresses are changed out, the new one should be
    76  		// added with the old one forgotten.
    77  		{
    78  			preTickHosts: map[string]net.Addr{
    79  				"test.com": &net.TCPAddr{
    80  					IP: net.ParseIP("1.1.1.1"),
    81  				},
    82  				"example.com": &net.TCPAddr{
    83  					IP: net.ParseIP("8.8.8.8"),
    84  				},
    85  			},
    86  			startingAddrs: startingAddrs,
    87  
    88  			postTickHosts: map[string]net.Addr{
    89  				"test.com": &net.TCPAddr{
    90  					IP: net.ParseIP("1.1.1.1"),
    91  				},
    92  				"example.com": &net.TCPAddr{
    93  					IP: net.ParseIP("9.9.9.9"),
    94  				},
    95  			},
    96  
    97  			updateTriggered: true,
    98  			newAddrs: []net.Addr{
    99  				&net.TCPAddr{
   100  					IP: net.ParseIP("9.9.9.9"),
   101  				},
   102  			},
   103  			removedAddrs: map[string]struct{}{
   104  				"8.8.8.8:0": {},
   105  			},
   106  		},
   107  
   108  		// All addresses change, they should all be refreshed.
   109  		{
   110  			preTickHosts: map[string]net.Addr{
   111  				"test.com": &net.TCPAddr{
   112  					IP: net.ParseIP("1.1.1.1"),
   113  				},
   114  				"example.com": &net.TCPAddr{
   115  					IP: net.ParseIP("8.8.8.8"),
   116  				},
   117  			},
   118  			startingAddrs: startingAddrs,
   119  
   120  			postTickHosts: map[string]net.Addr{
   121  				"test.com": &net.TCPAddr{
   122  					IP: net.ParseIP("2.2.2.2"),
   123  				},
   124  				"example.com": &net.TCPAddr{
   125  					IP: net.ParseIP("9.9.9.9"),
   126  				},
   127  			},
   128  
   129  			updateTriggered: true,
   130  			newAddrs: []net.Addr{
   131  				&net.TCPAddr{
   132  					IP: net.ParseIP("2.2.2.2"),
   133  				},
   134  				&net.TCPAddr{
   135  					IP: net.ParseIP("9.9.9.9"),
   136  				},
   137  			},
   138  			removedAddrs: map[string]struct{}{
   139  				"8.8.8.8:0": {},
   140  				"1.1.1.1:0": {},
   141  			},
   142  		},
   143  
   144  		// Two addresses, one has already been advertised on start up,
   145  		// so we only expect one of them to be announced again. After
   146  		// the tick we don't expect an update trigger since nothing.
   147  		// changed.
   148  		{
   149  			preAdvertisedIPs: map[string]struct{}{
   150  				"1.1.1.1:0": {},
   151  			},
   152  			startingAddrs: []net.Addr{
   153  				&net.TCPAddr{
   154  					IP: net.ParseIP("8.8.8.8"),
   155  				},
   156  			},
   157  			preTickHosts: map[string]net.Addr{
   158  				"test.com": &net.TCPAddr{
   159  					IP: net.ParseIP("1.1.1.1"),
   160  				},
   161  				"example.com": &net.TCPAddr{
   162  					IP: net.ParseIP("8.8.8.8"),
   163  				},
   164  			},
   165  			postTickHosts: map[string]net.Addr{
   166  				"test.com": &net.TCPAddr{
   167  					IP: net.ParseIP("1.1.1.1"),
   168  				},
   169  				"example.com": &net.TCPAddr{
   170  					IP: net.ParseIP("8.8.8.8"),
   171  				},
   172  			},
   173  
   174  			updateTriggered: false,
   175  		},
   176  	}
   177  	for idx, testCase := range testCases {
   178  		hostResps := make(chan net.Addr)
   179  		annReqs := make(chan annReq)
   180  		hostAnncer := NewHostAnnouncer(HostAnnouncerConfig{
   181  			Hosts:         hosts,
   182  			AdvertisedIPs: testCase.preAdvertisedIPs,
   183  			RefreshTicker: ticker,
   184  			LookupHost: func(str string) (net.Addr, error) {
   185  				return <-hostResps, nil
   186  			},
   187  			AnnounceNewIPs: func(newAddrs []net.Addr,
   188  				removeAddrs map[string]struct{}) error {
   189  
   190  				annReqs <- annReq{
   191  					newAddrs:     newAddrs,
   192  					removedAddrs: removeAddrs,
   193  				}
   194  
   195  				return nil
   196  			},
   197  		})
   198  		if err := hostAnncer.Start(); err != nil {
   199  			t.Fatalf("unable to start announcer: %v", err)
   200  		}
   201  
   202  		// As soon as the announcer starts, it'll try to query for the
   203  		// state of the hosts. We'll return the preTick state for all
   204  		// hosts.
   205  		for i := 0; i < len(hosts); i++ {
   206  			hostResps <- testCase.preTickHosts[hosts[i]]
   207  		}
   208  
   209  		// Since this is the first time the announcer is starting up,
   210  		// we expect it to advertise the hosts as they exist before any
   211  		// updates.
   212  		select {
   213  		case addrUpdate := <-annReqs:
   214  			assert.Equal(
   215  				t, testCase.startingAddrs, addrUpdate.newAddrs,
   216  				"addresses should match",
   217  			)
   218  			assert.Empty(
   219  				t, addrUpdate.removedAddrs,
   220  				"removed addrs should match",
   221  			)
   222  
   223  		case <-time.After(testTimeout):
   224  			t.Fatalf("#%v: no addr update sent", idx)
   225  		}
   226  
   227  		// We'll now force a tick which'll force another query. This
   228  		// time we'll respond with the set of the hosts as they should
   229  		// be post-tick.
   230  		ticker.Force <- time.Time{}
   231  
   232  		for i := 0; i < len(hosts); i++ {
   233  			hostResps <- testCase.postTickHosts[hosts[i]]
   234  		}
   235  
   236  		// If we expect an update, then we'll assert that we received
   237  		// the proper set of modified addresses.
   238  		if testCase.updateTriggered {
   239  
   240  			select {
   241  			// The receive update should match exactly what the
   242  			// test case dictates.
   243  			case addrUpdate := <-annReqs:
   244  				require.Equal(
   245  					t, testCase.newAddrs, addrUpdate.newAddrs,
   246  					"addresses should match",
   247  				)
   248  
   249  				require.Equal(
   250  					t, testCase.removedAddrs, addrUpdate.removedAddrs,
   251  					"removed addrs should match",
   252  				)
   253  
   254  			case <-time.After(testTimeout):
   255  				t.Fatalf("#%v: no addr update set", idx)
   256  			}
   257  
   258  			if err := hostAnncer.Stop(); err != nil {
   259  				t.Fatalf("unable to stop announcer: %v", err)
   260  			}
   261  			continue
   262  		}
   263  
   264  		// Otherwise, no updates should be sent since nothing changed.
   265  		select {
   266  		case <-annReqs:
   267  			t.Fatalf("#%v: expected no call to AnnounceNewIPs", idx)
   268  
   269  		case <-time.After(testTimeout):
   270  		}
   271  
   272  		if err := hostAnncer.Stop(); err != nil {
   273  			t.Fatalf("unable to stop announcer: %v", err)
   274  		}
   275  	}
   276  }