github.com/sberex/go-sberex@v1.8.2-0.20181113200658-ed96ac38f7d7/node/node_test.go (about)

     1  // This file is part of the go-sberex library. The go-sberex library is 
     2  // free software: you can redistribute it and/or modify it under the terms 
     3  // of the GNU Lesser General Public License as published by the Free 
     4  // Software Foundation, either version 3 of the License, or (at your option)
     5  // any later version.
     6  //
     7  // The go-sberex library is distributed in the hope that it will be useful, 
     8  // but WITHOUT ANY WARRANTY; without even the implied warranty of
     9  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 
    10  // General Public License <http://www.gnu.org/licenses/> for more details.
    11  
    12  package node
    13  
    14  import (
    15  	"errors"
    16  	"io/ioutil"
    17  	"os"
    18  	"reflect"
    19  	"testing"
    20  	"time"
    21  
    22  	"github.com/Sberex/go-sberex/crypto"
    23  	"github.com/Sberex/go-sberex/p2p"
    24  	"github.com/Sberex/go-sberex/rpc"
    25  )
    26  
    27  var (
    28  	testNodeKey, _ = crypto.GenerateKey()
    29  )
    30  
    31  func testNodeConfig() *Config {
    32  	return &Config{
    33  		Name: "test node",
    34  		P2P:  p2p.Config{PrivateKey: testNodeKey},
    35  	}
    36  }
    37  
    38  // Tests that an empty protocol stack can be started, restarted and stopped.
    39  func TestNodeLifeCycle(t *testing.T) {
    40  	stack, err := New(testNodeConfig())
    41  	if err != nil {
    42  		t.Fatalf("failed to create protocol stack: %v", err)
    43  	}
    44  	// Ensure that a stopped node can be stopped again
    45  	for i := 0; i < 3; i++ {
    46  		if err := stack.Stop(); err != ErrNodeStopped {
    47  			t.Fatalf("iter %d: stop failure mismatch: have %v, want %v", i, err, ErrNodeStopped)
    48  		}
    49  	}
    50  	// Ensure that a node can be successfully started, but only once
    51  	if err := stack.Start(); err != nil {
    52  		t.Fatalf("failed to start node: %v", err)
    53  	}
    54  	if err := stack.Start(); err != ErrNodeRunning {
    55  		t.Fatalf("start failure mismatch: have %v, want %v ", err, ErrNodeRunning)
    56  	}
    57  	// Ensure that a node can be restarted arbitrarily many times
    58  	for i := 0; i < 3; i++ {
    59  		if err := stack.Restart(); err != nil {
    60  			t.Fatalf("iter %d: failed to restart node: %v", i, err)
    61  		}
    62  	}
    63  	// Ensure that a node can be stopped, but only once
    64  	if err := stack.Stop(); err != nil {
    65  		t.Fatalf("failed to stop node: %v", err)
    66  	}
    67  	if err := stack.Stop(); err != ErrNodeStopped {
    68  		t.Fatalf("stop failure mismatch: have %v, want %v ", err, ErrNodeStopped)
    69  	}
    70  }
    71  
    72  // Tests that if the data dir is already in use, an appropriate error is returned.
    73  func TestNodeUsedDataDir(t *testing.T) {
    74  	// Create a temporary folder to use as the data directory
    75  	dir, err := ioutil.TempDir("", "")
    76  	if err != nil {
    77  		t.Fatalf("failed to create temporary data directory: %v", err)
    78  	}
    79  	defer os.RemoveAll(dir)
    80  
    81  	// Create a new node based on the data directory
    82  	original, err := New(&Config{DataDir: dir})
    83  	if err != nil {
    84  		t.Fatalf("failed to create original protocol stack: %v", err)
    85  	}
    86  	if err := original.Start(); err != nil {
    87  		t.Fatalf("failed to start original protocol stack: %v", err)
    88  	}
    89  	defer original.Stop()
    90  
    91  	// Create a second node based on the same data directory and ensure failure
    92  	duplicate, err := New(&Config{DataDir: dir})
    93  	if err != nil {
    94  		t.Fatalf("failed to create duplicate protocol stack: %v", err)
    95  	}
    96  	if err := duplicate.Start(); err != ErrDatadirUsed {
    97  		t.Fatalf("duplicate datadir failure mismatch: have %v, want %v", err, ErrDatadirUsed)
    98  	}
    99  }
   100  
   101  // Tests whether services can be registered and duplicates caught.
   102  func TestServiceRegistry(t *testing.T) {
   103  	stack, err := New(testNodeConfig())
   104  	if err != nil {
   105  		t.Fatalf("failed to create protocol stack: %v", err)
   106  	}
   107  	// Register a batch of unique services and ensure they start successfully
   108  	services := []ServiceConstructor{NewNoopServiceA, NewNoopServiceB, NewNoopServiceC}
   109  	for i, constructor := range services {
   110  		if err := stack.Register(constructor); err != nil {
   111  			t.Fatalf("service #%d: registration failed: %v", i, err)
   112  		}
   113  	}
   114  	if err := stack.Start(); err != nil {
   115  		t.Fatalf("failed to start original service stack: %v", err)
   116  	}
   117  	if err := stack.Stop(); err != nil {
   118  		t.Fatalf("failed to stop original service stack: %v", err)
   119  	}
   120  	// Duplicate one of the services and retry starting the node
   121  	if err := stack.Register(NewNoopServiceB); err != nil {
   122  		t.Fatalf("duplicate registration failed: %v", err)
   123  	}
   124  	if err := stack.Start(); err == nil {
   125  		t.Fatalf("duplicate service started")
   126  	} else {
   127  		if _, ok := err.(*DuplicateServiceError); !ok {
   128  			t.Fatalf("duplicate error mismatch: have %v, want %v", err, DuplicateServiceError{})
   129  		}
   130  	}
   131  }
   132  
   133  // Tests that registered services get started and stopped correctly.
   134  func TestServiceLifeCycle(t *testing.T) {
   135  	stack, err := New(testNodeConfig())
   136  	if err != nil {
   137  		t.Fatalf("failed to create protocol stack: %v", err)
   138  	}
   139  	// Register a batch of life-cycle instrumented services
   140  	services := map[string]InstrumentingWrapper{
   141  		"A": InstrumentedServiceMakerA,
   142  		"B": InstrumentedServiceMakerB,
   143  		"C": InstrumentedServiceMakerC,
   144  	}
   145  	started := make(map[string]bool)
   146  	stopped := make(map[string]bool)
   147  
   148  	for id, maker := range services {
   149  		id := id // Closure for the constructor
   150  		constructor := func(*ServiceContext) (Service, error) {
   151  			return &InstrumentedService{
   152  				startHook: func(*p2p.Server) { started[id] = true },
   153  				stopHook:  func() { stopped[id] = true },
   154  			}, nil
   155  		}
   156  		if err := stack.Register(maker(constructor)); err != nil {
   157  			t.Fatalf("service %s: registration failed: %v", id, err)
   158  		}
   159  	}
   160  	// Start the node and check that all services are running
   161  	if err := stack.Start(); err != nil {
   162  		t.Fatalf("failed to start protocol stack: %v", err)
   163  	}
   164  	for id := range services {
   165  		if !started[id] {
   166  			t.Fatalf("service %s: freshly started service not running", id)
   167  		}
   168  		if stopped[id] {
   169  			t.Fatalf("service %s: freshly started service already stopped", id)
   170  		}
   171  	}
   172  	// Stop the node and check that all services have been stopped
   173  	if err := stack.Stop(); err != nil {
   174  		t.Fatalf("failed to stop protocol stack: %v", err)
   175  	}
   176  	for id := range services {
   177  		if !stopped[id] {
   178  			t.Fatalf("service %s: freshly terminated service still running", id)
   179  		}
   180  	}
   181  }
   182  
   183  // Tests that services are restarted cleanly as new instances.
   184  func TestServiceRestarts(t *testing.T) {
   185  	stack, err := New(testNodeConfig())
   186  	if err != nil {
   187  		t.Fatalf("failed to create protocol stack: %v", err)
   188  	}
   189  	// Define a service that does not support restarts
   190  	var (
   191  		running bool
   192  		started int
   193  	)
   194  	constructor := func(*ServiceContext) (Service, error) {
   195  		running = false
   196  
   197  		return &InstrumentedService{
   198  			startHook: func(*p2p.Server) {
   199  				if running {
   200  					panic("already running")
   201  				}
   202  				running = true
   203  				started++
   204  			},
   205  		}, nil
   206  	}
   207  	// Register the service and start the protocol stack
   208  	if err := stack.Register(constructor); err != nil {
   209  		t.Fatalf("failed to register the service: %v", err)
   210  	}
   211  	if err := stack.Start(); err != nil {
   212  		t.Fatalf("failed to start protocol stack: %v", err)
   213  	}
   214  	defer stack.Stop()
   215  
   216  	if !running || started != 1 {
   217  		t.Fatalf("running/started mismatch: have %v/%d, want true/1", running, started)
   218  	}
   219  	// Restart the stack a few times and check successful service restarts
   220  	for i := 0; i < 3; i++ {
   221  		if err := stack.Restart(); err != nil {
   222  			t.Fatalf("iter %d: failed to restart stack: %v", i, err)
   223  		}
   224  	}
   225  	if !running || started != 4 {
   226  		t.Fatalf("running/started mismatch: have %v/%d, want true/4", running, started)
   227  	}
   228  }
   229  
   230  // Tests that if a service fails to initialize itself, none of the other services
   231  // will be allowed to even start.
   232  func TestServiceConstructionAbortion(t *testing.T) {
   233  	stack, err := New(testNodeConfig())
   234  	if err != nil {
   235  		t.Fatalf("failed to create protocol stack: %v", err)
   236  	}
   237  	// Define a batch of good services
   238  	services := map[string]InstrumentingWrapper{
   239  		"A": InstrumentedServiceMakerA,
   240  		"B": InstrumentedServiceMakerB,
   241  		"C": InstrumentedServiceMakerC,
   242  	}
   243  	started := make(map[string]bool)
   244  	for id, maker := range services {
   245  		id := id // Closure for the constructor
   246  		constructor := func(*ServiceContext) (Service, error) {
   247  			return &InstrumentedService{
   248  				startHook: func(*p2p.Server) { started[id] = true },
   249  			}, nil
   250  		}
   251  		if err := stack.Register(maker(constructor)); err != nil {
   252  			t.Fatalf("service %s: registration failed: %v", id, err)
   253  		}
   254  	}
   255  	// Register a service that fails to construct itself
   256  	failure := errors.New("fail")
   257  	failer := func(*ServiceContext) (Service, error) {
   258  		return nil, failure
   259  	}
   260  	if err := stack.Register(failer); err != nil {
   261  		t.Fatalf("failer registration failed: %v", err)
   262  	}
   263  	// Start the protocol stack and ensure none of the services get started
   264  	for i := 0; i < 100; i++ {
   265  		if err := stack.Start(); err != failure {
   266  			t.Fatalf("iter %d: stack startup failure mismatch: have %v, want %v", i, err, failure)
   267  		}
   268  		for id := range services {
   269  			if started[id] {
   270  				t.Fatalf("service %s: started should not have", id)
   271  			}
   272  			delete(started, id)
   273  		}
   274  	}
   275  }
   276  
   277  // Tests that if a service fails to start, all others started before it will be
   278  // shut down.
   279  func TestServiceStartupAbortion(t *testing.T) {
   280  	stack, err := New(testNodeConfig())
   281  	if err != nil {
   282  		t.Fatalf("failed to create protocol stack: %v", err)
   283  	}
   284  	// Register a batch of good services
   285  	services := map[string]InstrumentingWrapper{
   286  		"A": InstrumentedServiceMakerA,
   287  		"B": InstrumentedServiceMakerB,
   288  		"C": InstrumentedServiceMakerC,
   289  	}
   290  	started := make(map[string]bool)
   291  	stopped := make(map[string]bool)
   292  
   293  	for id, maker := range services {
   294  		id := id // Closure for the constructor
   295  		constructor := func(*ServiceContext) (Service, error) {
   296  			return &InstrumentedService{
   297  				startHook: func(*p2p.Server) { started[id] = true },
   298  				stopHook:  func() { stopped[id] = true },
   299  			}, nil
   300  		}
   301  		if err := stack.Register(maker(constructor)); err != nil {
   302  			t.Fatalf("service %s: registration failed: %v", id, err)
   303  		}
   304  	}
   305  	// Register a service that fails to start
   306  	failure := errors.New("fail")
   307  	failer := func(*ServiceContext) (Service, error) {
   308  		return &InstrumentedService{
   309  			start: failure,
   310  		}, nil
   311  	}
   312  	if err := stack.Register(failer); err != nil {
   313  		t.Fatalf("failer registration failed: %v", err)
   314  	}
   315  	// Start the protocol stack and ensure all started services stop
   316  	for i := 0; i < 100; i++ {
   317  		if err := stack.Start(); err != failure {
   318  			t.Fatalf("iter %d: stack startup failure mismatch: have %v, want %v", i, err, failure)
   319  		}
   320  		for id := range services {
   321  			if started[id] && !stopped[id] {
   322  				t.Fatalf("service %s: started but not stopped", id)
   323  			}
   324  			delete(started, id)
   325  			delete(stopped, id)
   326  		}
   327  	}
   328  }
   329  
   330  // Tests that even if a registered service fails to shut down cleanly, it does
   331  // not influece the rest of the shutdown invocations.
   332  func TestServiceTerminationGuarantee(t *testing.T) {
   333  	stack, err := New(testNodeConfig())
   334  	if err != nil {
   335  		t.Fatalf("failed to create protocol stack: %v", err)
   336  	}
   337  	// Register a batch of good services
   338  	services := map[string]InstrumentingWrapper{
   339  		"A": InstrumentedServiceMakerA,
   340  		"B": InstrumentedServiceMakerB,
   341  		"C": InstrumentedServiceMakerC,
   342  	}
   343  	started := make(map[string]bool)
   344  	stopped := make(map[string]bool)
   345  
   346  	for id, maker := range services {
   347  		id := id // Closure for the constructor
   348  		constructor := func(*ServiceContext) (Service, error) {
   349  			return &InstrumentedService{
   350  				startHook: func(*p2p.Server) { started[id] = true },
   351  				stopHook:  func() { stopped[id] = true },
   352  			}, nil
   353  		}
   354  		if err := stack.Register(maker(constructor)); err != nil {
   355  			t.Fatalf("service %s: registration failed: %v", id, err)
   356  		}
   357  	}
   358  	// Register a service that fails to shot down cleanly
   359  	failure := errors.New("fail")
   360  	failer := func(*ServiceContext) (Service, error) {
   361  		return &InstrumentedService{
   362  			stop: failure,
   363  		}, nil
   364  	}
   365  	if err := stack.Register(failer); err != nil {
   366  		t.Fatalf("failer registration failed: %v", err)
   367  	}
   368  	// Start the protocol stack, and ensure that a failing shut down terminates all
   369  	for i := 0; i < 100; i++ {
   370  		// Start the stack and make sure all is online
   371  		if err := stack.Start(); err != nil {
   372  			t.Fatalf("iter %d: failed to start protocol stack: %v", i, err)
   373  		}
   374  		for id := range services {
   375  			if !started[id] {
   376  				t.Fatalf("iter %d, service %s: service not running", i, id)
   377  			}
   378  			if stopped[id] {
   379  				t.Fatalf("iter %d, service %s: service already stopped", i, id)
   380  			}
   381  		}
   382  		// Stop the stack, verify failure and check all terminations
   383  		err := stack.Stop()
   384  		if err, ok := err.(*StopError); !ok {
   385  			t.Fatalf("iter %d: termination failure mismatch: have %v, want StopError", i, err)
   386  		} else {
   387  			failer := reflect.TypeOf(&InstrumentedService{})
   388  			if err.Services[failer] != failure {
   389  				t.Fatalf("iter %d: failer termination failure mismatch: have %v, want %v", i, err.Services[failer], failure)
   390  			}
   391  			if len(err.Services) != 1 {
   392  				t.Fatalf("iter %d: failure count mismatch: have %d, want %d", i, len(err.Services), 1)
   393  			}
   394  		}
   395  		for id := range services {
   396  			if !stopped[id] {
   397  				t.Fatalf("iter %d, service %s: service not terminated", i, id)
   398  			}
   399  			delete(started, id)
   400  			delete(stopped, id)
   401  		}
   402  	}
   403  }
   404  
   405  // TestServiceRetrieval tests that individual services can be retrieved.
   406  func TestServiceRetrieval(t *testing.T) {
   407  	// Create a simple stack and register two service types
   408  	stack, err := New(testNodeConfig())
   409  	if err != nil {
   410  		t.Fatalf("failed to create protocol stack: %v", err)
   411  	}
   412  	if err := stack.Register(NewNoopService); err != nil {
   413  		t.Fatalf("noop service registration failed: %v", err)
   414  	}
   415  	if err := stack.Register(NewInstrumentedService); err != nil {
   416  		t.Fatalf("instrumented service registration failed: %v", err)
   417  	}
   418  	// Make sure none of the services can be retrieved until started
   419  	var noopServ *NoopService
   420  	if err := stack.Service(&noopServ); err != ErrNodeStopped {
   421  		t.Fatalf("noop service retrieval mismatch: have %v, want %v", err, ErrNodeStopped)
   422  	}
   423  	var instServ *InstrumentedService
   424  	if err := stack.Service(&instServ); err != ErrNodeStopped {
   425  		t.Fatalf("instrumented service retrieval mismatch: have %v, want %v", err, ErrNodeStopped)
   426  	}
   427  	// Start the stack and ensure everything is retrievable now
   428  	if err := stack.Start(); err != nil {
   429  		t.Fatalf("failed to start stack: %v", err)
   430  	}
   431  	defer stack.Stop()
   432  
   433  	if err := stack.Service(&noopServ); err != nil {
   434  		t.Fatalf("noop service retrieval mismatch: have %v, want %v", err, nil)
   435  	}
   436  	if err := stack.Service(&instServ); err != nil {
   437  		t.Fatalf("instrumented service retrieval mismatch: have %v, want %v", err, nil)
   438  	}
   439  }
   440  
   441  // Tests that all protocols defined by individual services get launched.
   442  func TestProtocolGather(t *testing.T) {
   443  	stack, err := New(testNodeConfig())
   444  	if err != nil {
   445  		t.Fatalf("failed to create protocol stack: %v", err)
   446  	}
   447  	// Register a batch of services with some configured number of protocols
   448  	services := map[string]struct {
   449  		Count int
   450  		Maker InstrumentingWrapper
   451  	}{
   452  		"Zero Protocols":  {0, InstrumentedServiceMakerA},
   453  		"Single Protocol": {1, InstrumentedServiceMakerB},
   454  		"Many Protocols":  {25, InstrumentedServiceMakerC},
   455  	}
   456  	for id, config := range services {
   457  		protocols := make([]p2p.Protocol, config.Count)
   458  		for i := 0; i < len(protocols); i++ {
   459  			protocols[i].Name = id
   460  			protocols[i].Version = uint(i)
   461  		}
   462  		constructor := func(*ServiceContext) (Service, error) {
   463  			return &InstrumentedService{
   464  				protocols: protocols,
   465  			}, nil
   466  		}
   467  		if err := stack.Register(config.Maker(constructor)); err != nil {
   468  			t.Fatalf("service %s: registration failed: %v", id, err)
   469  		}
   470  	}
   471  	// Start the services and ensure all protocols start successfully
   472  	if err := stack.Start(); err != nil {
   473  		t.Fatalf("failed to start protocol stack: %v", err)
   474  	}
   475  	defer stack.Stop()
   476  
   477  	protocols := stack.Server().Protocols
   478  	if len(protocols) != 26 {
   479  		t.Fatalf("mismatching number of protocols launched: have %d, want %d", len(protocols), 26)
   480  	}
   481  	for id, config := range services {
   482  		for ver := 0; ver < config.Count; ver++ {
   483  			launched := false
   484  			for i := 0; i < len(protocols); i++ {
   485  				if protocols[i].Name == id && protocols[i].Version == uint(ver) {
   486  					launched = true
   487  					break
   488  				}
   489  			}
   490  			if !launched {
   491  				t.Errorf("configured protocol not launched: %s v%d", id, ver)
   492  			}
   493  		}
   494  	}
   495  }
   496  
   497  // Tests that all APIs defined by individual services get exposed.
   498  func TestAPIGather(t *testing.T) {
   499  	stack, err := New(testNodeConfig())
   500  	if err != nil {
   501  		t.Fatalf("failed to create protocol stack: %v", err)
   502  	}
   503  	// Register a batch of services with some configured APIs
   504  	calls := make(chan string, 1)
   505  	makeAPI := func(result string) *OneMethodApi {
   506  		return &OneMethodApi{fun: func() { calls <- result }}
   507  	}
   508  	services := map[string]struct {
   509  		APIs  []rpc.API
   510  		Maker InstrumentingWrapper
   511  	}{
   512  		"Zero APIs": {
   513  			[]rpc.API{}, InstrumentedServiceMakerA},
   514  		"Single API": {
   515  			[]rpc.API{
   516  				{Namespace: "single", Version: "1", Service: makeAPI("single.v1"), Public: true},
   517  			}, InstrumentedServiceMakerB},
   518  		"Many APIs": {
   519  			[]rpc.API{
   520  				{Namespace: "multi", Version: "1", Service: makeAPI("multi.v1"), Public: true},
   521  				{Namespace: "multi.v2", Version: "2", Service: makeAPI("multi.v2"), Public: true},
   522  				{Namespace: "multi.v2.nested", Version: "2", Service: makeAPI("multi.v2.nested"), Public: true},
   523  			}, InstrumentedServiceMakerC},
   524  	}
   525  
   526  	for id, config := range services {
   527  		config := config
   528  		constructor := func(*ServiceContext) (Service, error) {
   529  			return &InstrumentedService{apis: config.APIs}, nil
   530  		}
   531  		if err := stack.Register(config.Maker(constructor)); err != nil {
   532  			t.Fatalf("service %s: registration failed: %v", id, err)
   533  		}
   534  	}
   535  	// Start the services and ensure all API start successfully
   536  	if err := stack.Start(); err != nil {
   537  		t.Fatalf("failed to start protocol stack: %v", err)
   538  	}
   539  	defer stack.Stop()
   540  
   541  	// Connect to the RPC server and verify the various registered endpoints
   542  	client, err := stack.Attach()
   543  	if err != nil {
   544  		t.Fatalf("failed to connect to the inproc API server: %v", err)
   545  	}
   546  	defer client.Close()
   547  
   548  	tests := []struct {
   549  		Method string
   550  		Result string
   551  	}{
   552  		{"single_theOneMethod", "single.v1"},
   553  		{"multi_theOneMethod", "multi.v1"},
   554  		{"multi.v2_theOneMethod", "multi.v2"},
   555  		{"multi.v2.nested_theOneMethod", "multi.v2.nested"},
   556  	}
   557  	for i, test := range tests {
   558  		if err := client.Call(nil, test.Method); err != nil {
   559  			t.Errorf("test %d: API request failed: %v", i, err)
   560  		}
   561  		select {
   562  		case result := <-calls:
   563  			if result != test.Result {
   564  				t.Errorf("test %d: result mismatch: have %s, want %s", i, result, test.Result)
   565  			}
   566  		case <-time.After(time.Second):
   567  			t.Fatalf("test %d: rpc execution timeout", i)
   568  		}
   569  	}
   570  }