gitlab.com/SiaPrime/SiaPrime@v1.4.1/modules/gateway/gateway_test.go (about)

     1  package gateway
     2  
     3  import (
     4  	"io/ioutil"
     5  	"net"
     6  	"os"
     7  	"path/filepath"
     8  	"strconv"
     9  	"sync"
    10  	"testing"
    11  	"time"
    12  
    13  	"gitlab.com/SiaPrime/SiaPrime/build"
    14  	"gitlab.com/SiaPrime/SiaPrime/modules"
    15  	siasync "gitlab.com/SiaPrime/SiaPrime/sync"
    16  )
    17  
    18  // newTestingGateway returns a gateway ready to use in a testing environment.
    19  func newTestingGateway(t *testing.T) *Gateway {
    20  	if testing.Short() {
    21  		panic("newTestingGateway called during short test")
    22  	}
    23  
    24  	g, err := New("localhost:0", false, build.TempDir("gateway", t.Name()))
    25  	if err != nil {
    26  		panic(err)
    27  	}
    28  	return g
    29  }
    30  
    31  // newNamedTestingGateway returns a gateway ready to use in a testing
    32  // environment. The gateway's persist folder will have the specified suffix.
    33  func newNamedTestingGateway(t *testing.T, suffix string) *Gateway {
    34  	if testing.Short() {
    35  		panic("newTestingGateway called during short test")
    36  	}
    37  
    38  	g, err := New("localhost:0", false, build.TempDir("gateway", t.Name()+suffix))
    39  	if err != nil {
    40  		panic(err)
    41  	}
    42  	return g
    43  }
    44  
    45  // NDF safe connection helpers
    46  func connectToNode(g1 *Gateway, g2 *Gateway, manual bool) error {
    47  	return build.Retry(100, 10*time.Millisecond, func() error {
    48  		if manual {
    49  			return g1.ConnectManual(g2.Address())
    50  		}
    51  		return g1.Connect(g2.Address())
    52  	})
    53  }
    54  func disconnectFromNode(g1 *Gateway, g2 *Gateway, manual bool) error {
    55  	return build.Retry(100, 10*time.Millisecond, func() error {
    56  		if manual {
    57  			return g1.DisconnectManual(g2.Address())
    58  		}
    59  		return g1.Disconnect(g2.Address())
    60  	})
    61  }
    62  
    63  // TestExportedMethodsErrAfterClose tests that exported methods like Close and
    64  // Connect error with siasync.ErrStopped after the gateway has been closed.
    65  func TestExportedMethodsErrAfterClose(t *testing.T) {
    66  	if testing.Short() {
    67  		t.SkipNow()
    68  	}
    69  	t.Parallel()
    70  	g := newTestingGateway(t)
    71  
    72  	if err := g.Close(); err != nil {
    73  		t.Fatal(err)
    74  	}
    75  	if err := g.Close(); err != siasync.ErrStopped {
    76  		t.Fatalf("expected %q, got %q", siasync.ErrStopped, err)
    77  	}
    78  	if err := g.Connect("localhost:1234"); err != siasync.ErrStopped {
    79  		t.Fatalf("expected %q, got %q", siasync.ErrStopped, err)
    80  	}
    81  }
    82  
    83  // TestAddress tests that Gateway.Address returns the address of its listener.
    84  // Also tests that the address is not unspecified and is a loopback address.
    85  // The address must be a loopback address for testing.
    86  func TestAddress(t *testing.T) {
    87  	if testing.Short() {
    88  		t.SkipNow()
    89  	}
    90  	t.Parallel()
    91  	g := newTestingGateway(t)
    92  	defer g.Close()
    93  
    94  	if g.Address() != g.myAddr {
    95  		t.Fatal("Address does not return g.myAddr")
    96  	}
    97  	if g.Address() != modules.NetAddress(g.listener.Addr().String()) {
    98  		t.Fatalf("wrong address: expected %v, got %v", g.listener.Addr(), g.Address())
    99  	}
   100  	host := modules.NetAddress(g.listener.Addr().String()).Host()
   101  	ip := net.ParseIP(host)
   102  	if ip == nil {
   103  		t.Fatal("address is not an IP address")
   104  	}
   105  	if ip.IsUnspecified() {
   106  		t.Fatal("expected a non-unspecified address")
   107  	}
   108  	if !ip.IsLoopback() {
   109  		t.Fatal("expected a loopback address")
   110  	}
   111  }
   112  
   113  // TestPeers checks that two gateways are able to connect to each other.
   114  func TestPeers(t *testing.T) {
   115  	if testing.Short() {
   116  		t.SkipNow()
   117  	}
   118  	t.Parallel()
   119  	g1 := newNamedTestingGateway(t, "1")
   120  	defer g1.Close()
   121  	g2 := newNamedTestingGateway(t, "2")
   122  	defer g2.Close()
   123  
   124  	err := g1.Connect(g2.Address())
   125  	if err != nil {
   126  		t.Fatal("failed to connect:", err)
   127  	}
   128  	peers := g1.Peers()
   129  	if len(peers) != 1 || peers[0].NetAddress != g2.Address() {
   130  		t.Fatal("g1 has bad peer list:", peers)
   131  	}
   132  	err = g1.Disconnect(g2.Address())
   133  	if err != nil {
   134  		t.Fatal("failed to disconnect:", err)
   135  	}
   136  	peers = g1.Peers()
   137  	if len(peers) != 0 {
   138  		t.Fatal("g1 has peers after disconnect:", peers)
   139  	}
   140  }
   141  
   142  // TestNew checks that a call to New is effective.
   143  func TestNew(t *testing.T) {
   144  	if testing.Short() {
   145  		t.SkipNow()
   146  	}
   147  	t.Parallel()
   148  
   149  	if _, err := New("", false, ""); err == nil {
   150  		t.Fatal("expecting persistDir error, got nil")
   151  	}
   152  	if _, err := New("localhost:0", false, ""); err == nil {
   153  		t.Fatal("expecting persistDir error, got nil")
   154  	}
   155  	if g, err := New("foo", false, build.TempDir("gateway", t.Name()+"1")); err == nil {
   156  		t.Fatal("expecting listener error, got nil", g.myAddr)
   157  	}
   158  	// create corrupted nodes.json
   159  	dir := build.TempDir("gateway", t.Name()+"2")
   160  	os.MkdirAll(dir, 0700)
   161  	err := ioutil.WriteFile(filepath.Join(dir, "nodes.json"), []byte{1, 2, 3}, 0660)
   162  	if err != nil {
   163  		t.Fatal("couldn't create corrupted file:", err)
   164  	}
   165  	if _, err := New("localhost:0", false, dir); err == nil {
   166  		t.Fatal("expected load error, got nil")
   167  	}
   168  }
   169  
   170  // TestClose creates and closes a gateway.
   171  func TestClose(t *testing.T) {
   172  	if testing.Short() {
   173  		t.SkipNow()
   174  	}
   175  	t.Parallel()
   176  
   177  	g := newTestingGateway(t)
   178  	err := g.Close()
   179  	if err != nil {
   180  		t.Fatal(err)
   181  	}
   182  }
   183  
   184  // TestParallelClose spins up 3 gateways, connects them all, and then closes
   185  // them in parallel. The goal of this test is to make it more vulnerable to any
   186  // potential nondeterministic failures.
   187  func TestParallelClose(t *testing.T) {
   188  	if testing.Short() {
   189  		t.SkipNow()
   190  	}
   191  	t.Parallel()
   192  
   193  	// Spin up three gateways in parallel.
   194  	var gs [3]*Gateway
   195  	var wg sync.WaitGroup
   196  	wg.Add(3)
   197  	for i := range gs {
   198  		go func(i int) {
   199  			gs[i] = newNamedTestingGateway(t, strconv.Itoa(i))
   200  			wg.Done()
   201  		}(i)
   202  	}
   203  	wg.Wait()
   204  
   205  	// Connect g1 to g2, g2 to g3. They may connect to eachother further.
   206  	wg.Add(2)
   207  	for i := range gs[:2] {
   208  		go func(i int) {
   209  			err := gs[i].Connect(gs[i+1].myAddr)
   210  			if err != nil {
   211  				panic(err)
   212  			}
   213  			wg.Done()
   214  		}(i)
   215  	}
   216  	wg.Wait()
   217  
   218  	// Close all three gateways in parallel.
   219  	wg.Add(3)
   220  	for i := range gs {
   221  		go func(i int) {
   222  			err := gs[i].Close()
   223  			if err != nil {
   224  				panic(err)
   225  			}
   226  			wg.Done()
   227  		}(i)
   228  	}
   229  	wg.Wait()
   230  }
   231  
   232  // TestManualConnectDisconnect checks if a user initiated connect and
   233  // disconnect works as expected.
   234  func TestManualConnectDisconnect(t *testing.T) {
   235  	if testing.Short() {
   236  		t.SkipNow()
   237  	}
   238  	t.Parallel()
   239  	g1 := newNamedTestingGateway(t, "1")
   240  	defer g1.Close()
   241  	g2 := newNamedTestingGateway(t, "2")
   242  	defer g2.Close()
   243  
   244  	// g1 should be able to connect to g2
   245  	if err := connectToNode(g1, g2, false); err != nil {
   246  		t.Fatal("failed to connect:", err)
   247  	}
   248  	// g2 manually disconnects from g1 and therefore blacklists it
   249  	if err := disconnectFromNode(g2, g1, true); err != nil {
   250  		t.Fatal("failed to disconnect:", err)
   251  	}
   252  	// Neither g1 nor g2 can connect after g1 being blacklisted
   253  	if err := connectToNode(g1, g2, false); err == nil {
   254  		t.Fatal("shouldn't be able to connect")
   255  	}
   256  	if err := connectToNode(g1, g2, true); err == nil {
   257  		t.Fatal("shouldn't be able to connect")
   258  	}
   259  	if err := connectToNode(g2, g1, false); err == nil {
   260  		t.Fatal("shouldn't be able to connect")
   261  	}
   262  
   263  	// g2 manually connects and therefore removes g1 from the blacklist again
   264  	if err := connectToNode(g2, g1, true); err != nil {
   265  		t.Fatal("failed to connect:", err)
   266  	}
   267  
   268  	// g2 disconnects and lets g1 connect which should also be possible now
   269  	if err := disconnectFromNode(g2, g1, false); err != nil {
   270  		t.Fatal("failed to disconnect:", err)
   271  	}
   272  	if err := connectToNode(g1, g2, false); err != nil {
   273  		t.Fatal("failed to connect:", err)
   274  	}
   275  
   276  	// same thing again but the other way round
   277  	if err := disconnectFromNode(g2, g1, false); err != nil {
   278  		t.Fatal("failed to disconnect:", err)
   279  	}
   280  	if err := connectToNode(g2, g1, false); err != nil {
   281  		t.Fatal("failed to connect:", err)
   282  	}
   283  }
   284  
   285  // TestManualConnectDisconnectPersist checks if the blacklist is persistet on
   286  // disk
   287  func TestManualConnectDisconnectPersist(t *testing.T) {
   288  	if testing.Short() {
   289  		t.SkipNow()
   290  	}
   291  	t.Parallel()
   292  	g1 := newNamedTestingGateway(t, "1")
   293  	defer g1.Close()
   294  	g2 := newNamedTestingGateway(t, "2")
   295  
   296  	// g1 should be able to connect to g2
   297  	if err := connectToNode(g1, g2, false); err != nil {
   298  		t.Fatal("failed to connect:", err)
   299  	}
   300  
   301  	// g2 manually disconnects from g1 and therefore blacklists it
   302  	if err := disconnectFromNode(g2, g1, true); err != nil {
   303  		t.Fatal("failed to disconnect:", err)
   304  	}
   305  
   306  	// Neither g1 nor g2 can connect after g1 being blacklisted
   307  	if err := connectToNode(g1, g2, false); err == nil {
   308  		t.Fatal("shouldn't be able to connect")
   309  	}
   310  	if err := connectToNode(g1, g2, true); err == nil {
   311  		t.Fatal("shouldn't be able to connect")
   312  	}
   313  	if err := connectToNode(g2, g1, false); err == nil {
   314  		t.Fatal("shouldn't be able to connect")
   315  	}
   316  
   317  	// Restart g2 without deleting the tmp dir
   318  	g2.Close()
   319  	g2, err := New("localhost:0", false, g2.persistDir)
   320  	if err != nil {
   321  		t.Fatal(err)
   322  	}
   323  	defer g2.Close()
   324  
   325  	// Neither g1 nor g2 can connect after g1 being blacklisted
   326  	if err := connectToNode(g1, g2, false); err == nil {
   327  		t.Fatal("shouldn't be able to connect")
   328  	}
   329  	if err := connectToNode(g1, g2, true); err == nil {
   330  		t.Fatal("shouldn't be able to connect")
   331  	}
   332  	if err := connectToNode(g2, g1, false); err == nil {
   333  		t.Fatal("shouldn't be able to connect")
   334  	}
   335  }