|
|
@@ -18,27 +18,34 @@ package node
|
|
|
|
|
|
import (
|
|
|
"errors"
|
|
|
+ "fmt"
|
|
|
"io/ioutil"
|
|
|
+ "math/rand"
|
|
|
"os"
|
|
|
"reflect"
|
|
|
"testing"
|
|
|
+ "time"
|
|
|
|
|
|
"github.com/ethereum/go-ethereum/crypto"
|
|
|
"github.com/ethereum/go-ethereum/p2p"
|
|
|
+ "github.com/ethereum/go-ethereum/rpc"
|
|
|
)
|
|
|
|
|
|
var (
|
|
|
testNodeKey, _ = crypto.GenerateKey()
|
|
|
+)
|
|
|
|
|
|
- testNodeConfig = &Config{
|
|
|
+func testNodeConfig() *Config {
|
|
|
+ return &Config{
|
|
|
+ IpcPath: fmt.Sprintf("test-%d.ipc", rand.Int63()),
|
|
|
PrivateKey: testNodeKey,
|
|
|
Name: "test node",
|
|
|
}
|
|
|
-)
|
|
|
+}
|
|
|
|
|
|
// Tests that an empty protocol stack can be started, restarted and stopped.
|
|
|
func TestNodeLifeCycle(t *testing.T) {
|
|
|
- stack, err := New(testNodeConfig)
|
|
|
+ stack, err := New(testNodeConfig())
|
|
|
if err != nil {
|
|
|
t.Fatalf("failed to create protocol stack: %v", err)
|
|
|
}
|
|
|
@@ -101,7 +108,7 @@ func TestNodeUsedDataDir(t *testing.T) {
|
|
|
|
|
|
// Tests whether services can be registered and duplicates caught.
|
|
|
func TestServiceRegistry(t *testing.T) {
|
|
|
- stack, err := New(testNodeConfig)
|
|
|
+ stack, err := New(testNodeConfig())
|
|
|
if err != nil {
|
|
|
t.Fatalf("failed to create protocol stack: %v", err)
|
|
|
}
|
|
|
@@ -133,7 +140,7 @@ func TestServiceRegistry(t *testing.T) {
|
|
|
|
|
|
// Tests that registered services get started and stopped correctly.
|
|
|
func TestServiceLifeCycle(t *testing.T) {
|
|
|
- stack, err := New(testNodeConfig)
|
|
|
+ stack, err := New(testNodeConfig())
|
|
|
if err != nil {
|
|
|
t.Fatalf("failed to create protocol stack: %v", err)
|
|
|
}
|
|
|
@@ -183,7 +190,7 @@ func TestServiceLifeCycle(t *testing.T) {
|
|
|
|
|
|
// Tests that services are restarted cleanly as new instances.
|
|
|
func TestServiceRestarts(t *testing.T) {
|
|
|
- stack, err := New(testNodeConfig)
|
|
|
+ stack, err := New(testNodeConfig())
|
|
|
if err != nil {
|
|
|
t.Fatalf("failed to create protocol stack: %v", err)
|
|
|
}
|
|
|
@@ -231,7 +238,7 @@ func TestServiceRestarts(t *testing.T) {
|
|
|
// Tests that if a service fails to initialize itself, none of the other services
|
|
|
// will be allowed to even start.
|
|
|
func TestServiceConstructionAbortion(t *testing.T) {
|
|
|
- stack, err := New(testNodeConfig)
|
|
|
+ stack, err := New(testNodeConfig())
|
|
|
if err != nil {
|
|
|
t.Fatalf("failed to create protocol stack: %v", err)
|
|
|
}
|
|
|
@@ -278,7 +285,7 @@ func TestServiceConstructionAbortion(t *testing.T) {
|
|
|
// Tests that if a service fails to start, all others started before it will be
|
|
|
// shut down.
|
|
|
func TestServiceStartupAbortion(t *testing.T) {
|
|
|
- stack, err := New(testNodeConfig)
|
|
|
+ stack, err := New(testNodeConfig())
|
|
|
if err != nil {
|
|
|
t.Fatalf("failed to create protocol stack: %v", err)
|
|
|
}
|
|
|
@@ -331,7 +338,7 @@ func TestServiceStartupAbortion(t *testing.T) {
|
|
|
// Tests that even if a registered service fails to shut down cleanly, it does
|
|
|
// not influece the rest of the shutdown invocations.
|
|
|
func TestServiceTerminationGuarantee(t *testing.T) {
|
|
|
- stack, err := New(testNodeConfig)
|
|
|
+ stack, err := New(testNodeConfig())
|
|
|
if err != nil {
|
|
|
t.Fatalf("failed to create protocol stack: %v", err)
|
|
|
}
|
|
|
@@ -406,7 +413,7 @@ func TestServiceTerminationGuarantee(t *testing.T) {
|
|
|
// TestServiceRetrieval tests that individual services can be retrieved.
|
|
|
func TestServiceRetrieval(t *testing.T) {
|
|
|
// Create a simple stack and register two service types
|
|
|
- stack, err := New(testNodeConfig)
|
|
|
+ stack, err := New(testNodeConfig())
|
|
|
if err != nil {
|
|
|
t.Fatalf("failed to create protocol stack: %v", err)
|
|
|
}
|
|
|
@@ -441,7 +448,7 @@ func TestServiceRetrieval(t *testing.T) {
|
|
|
|
|
|
// Tests that all protocols defined by individual services get launched.
|
|
|
func TestProtocolGather(t *testing.T) {
|
|
|
- stack, err := New(testNodeConfig)
|
|
|
+ stack, err := New(testNodeConfig())
|
|
|
if err != nil {
|
|
|
t.Fatalf("failed to create protocol stack: %v", err)
|
|
|
}
|
|
|
@@ -494,3 +501,75 @@ func TestProtocolGather(t *testing.T) {
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+// Tests that all APIs defined by individual services get exposed.
|
|
|
+func TestAPIGather(t *testing.T) {
|
|
|
+ stack, err := New(testNodeConfig())
|
|
|
+ if err != nil {
|
|
|
+ t.Fatalf("failed to create protocol stack: %v", err)
|
|
|
+ }
|
|
|
+ // Register a batch of services with some configured APIs
|
|
|
+ calls := make(chan string, 1)
|
|
|
+
|
|
|
+ services := map[string]struct {
|
|
|
+ APIs []rpc.API
|
|
|
+ Maker InstrumentingWrapper
|
|
|
+ }{
|
|
|
+ "Zero APIs": {[]rpc.API{}, InstrumentedServiceMakerA},
|
|
|
+ "Single API": {[]rpc.API{
|
|
|
+ {"single", "1", &OneMethodApi{fun: func() { calls <- "single.v1" }}, true},
|
|
|
+ }, InstrumentedServiceMakerB},
|
|
|
+ "Many APIs": {[]rpc.API{
|
|
|
+ {"multi", "1", &OneMethodApi{fun: func() { calls <- "multi.v1" }}, true},
|
|
|
+ {"multi.v2", "2", &OneMethodApi{fun: func() { calls <- "multi.v2" }}, true},
|
|
|
+ {"multi.v2.nested", "2", &OneMethodApi{fun: func() { calls <- "multi.v2.nested" }}, true},
|
|
|
+ }, InstrumentedServiceMakerC},
|
|
|
+ }
|
|
|
+ for id, config := range services {
|
|
|
+ config := config
|
|
|
+ constructor := func(*ServiceContext) (Service, error) {
|
|
|
+ return &InstrumentedService{apis: config.APIs}, nil
|
|
|
+ }
|
|
|
+ if err := stack.Register(config.Maker(constructor)); err != nil {
|
|
|
+ t.Fatalf("service %s: registration failed: %v", id, err)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // Start the services and ensure all API start successfully
|
|
|
+ if err := stack.Start(); err != nil {
|
|
|
+ t.Fatalf("failed to start protocol stack: %v", err)
|
|
|
+ }
|
|
|
+ defer stack.Stop()
|
|
|
+
|
|
|
+ // Connect to the RPC server and verify the various registered endpoints
|
|
|
+ ipcClient, err := rpc.NewIPCClient(stack.IpcEndpoint())
|
|
|
+ if err != nil {
|
|
|
+ t.Fatalf("failed to connect to the IPC API server: %v", err)
|
|
|
+ }
|
|
|
+
|
|
|
+ tests := []struct {
|
|
|
+ Method string
|
|
|
+ Result string
|
|
|
+ }{
|
|
|
+ {"single_theOneMethod", "single.v1"},
|
|
|
+ {"multi_theOneMethod", "multi.v1"},
|
|
|
+ {"multi.v2_theOneMethod", "multi.v2"},
|
|
|
+ {"multi.v2.nested_theOneMethod", "multi.v2.nested"},
|
|
|
+ }
|
|
|
+ for i, test := range tests {
|
|
|
+ if err := ipcClient.Send(rpc.JSONRequest{Id: new(int64), Version: "2.0", Method: test.Method}); err != nil {
|
|
|
+ t.Fatalf("test %d: failed to send API request: %v", i, err)
|
|
|
+ }
|
|
|
+ reply := new(rpc.JSONSuccessResponse)
|
|
|
+ if err := ipcClient.Recv(reply); err != nil {
|
|
|
+ t.Fatalf("test %d: failed to read API reply: %v", i, err)
|
|
|
+ }
|
|
|
+ select {
|
|
|
+ case result := <-calls:
|
|
|
+ if result != test.Result {
|
|
|
+ t.Errorf("test %d: result mismatch: have %s, want %s", i, result, test.Result)
|
|
|
+ }
|
|
|
+ case <-time.After(time.Second):
|
|
|
+ t.Fatalf("test %d: rpc execution timeout", i)
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|