github.com/okex/exchain@v1.8.0/libs/tendermint/rpc/jsonrpc/jsonrpc_test.go (about)

     1  package jsonrpc
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	crand "crypto/rand"
     7  	"encoding/json"
     8  	"fmt"
     9  	"net/http"
    10  	"os"
    11  	"os/exec"
    12  	"testing"
    13  	"time"
    14  
    15  	"github.com/go-kit/kit/log/term"
    16  	"github.com/stretchr/testify/assert"
    17  	"github.com/stretchr/testify/require"
    18  
    19  	amino "github.com/tendermint/go-amino"
    20  
    21  	tmbytes "github.com/okex/exchain/libs/tendermint/libs/bytes"
    22  	"github.com/okex/exchain/libs/tendermint/libs/log"
    23  	tmrand "github.com/okex/exchain/libs/tendermint/libs/rand"
    24  
    25  	client "github.com/okex/exchain/libs/tendermint/rpc/jsonrpc/client"
    26  	server "github.com/okex/exchain/libs/tendermint/rpc/jsonrpc/server"
    27  	types "github.com/okex/exchain/libs/tendermint/rpc/jsonrpc/types"
    28  )
    29  
    30  // Client and Server should work over tcp or unix sockets
    31  const (
    32  	tcpAddr = "tcp://127.0.0.1:47768"
    33  
    34  	unixSocket = "/tmp/rpc_test.sock"
    35  	unixAddr   = "unix://" + unixSocket
    36  
    37  	websocketEndpoint = "/websocket/endpoint"
    38  
    39  	testVal = "acbd"
    40  )
    41  
    42  type ResultEcho struct {
    43  	Value string `json:"value"`
    44  }
    45  
    46  type ResultEchoInt struct {
    47  	Value int `json:"value"`
    48  }
    49  
    50  type ResultEchoBytes struct {
    51  	Value []byte `json:"value"`
    52  }
    53  
    54  type ResultEchoDataBytes struct {
    55  	Value tmbytes.HexBytes `json:"value"`
    56  }
    57  
    58  // Define some routes
    59  var Routes = map[string]*server.RPCFunc{
    60  	"echo":            server.NewRPCFunc(EchoResult, "arg"),
    61  	"echo_ws":         server.NewWSRPCFunc(EchoWSResult, "arg"),
    62  	"echo_bytes":      server.NewRPCFunc(EchoBytesResult, "arg"),
    63  	"echo_data_bytes": server.NewRPCFunc(EchoDataBytesResult, "arg"),
    64  	"echo_int":        server.NewRPCFunc(EchoIntResult, "arg"),
    65  }
    66  
    67  // Amino codec required to encode/decode everything above.
    68  var RoutesCdc = amino.NewCodec()
    69  
    70  func EchoResult(ctx *types.Context, v string) (*ResultEcho, error) {
    71  	return &ResultEcho{v}, nil
    72  }
    73  
    74  func EchoWSResult(ctx *types.Context, v string) (*ResultEcho, error) {
    75  	return &ResultEcho{v}, nil
    76  }
    77  
    78  func EchoIntResult(ctx *types.Context, v int) (*ResultEchoInt, error) {
    79  	return &ResultEchoInt{v}, nil
    80  }
    81  
    82  func EchoBytesResult(ctx *types.Context, v []byte) (*ResultEchoBytes, error) {
    83  	return &ResultEchoBytes{v}, nil
    84  }
    85  
    86  func EchoDataBytesResult(ctx *types.Context, v tmbytes.HexBytes) (*ResultEchoDataBytes, error) {
    87  	return &ResultEchoDataBytes{v}, nil
    88  }
    89  
    90  func TestMain(m *testing.M) {
    91  	setup()
    92  	code := m.Run()
    93  	os.Exit(code)
    94  }
    95  
    96  var colorFn = func(keyvals ...interface{}) term.FgBgColor {
    97  	for i := 0; i < len(keyvals)-1; i += 2 {
    98  		if keyvals[i] == "socket" {
    99  			if keyvals[i+1] == "tcp" {
   100  				return term.FgBgColor{Fg: term.DarkBlue}
   101  			} else if keyvals[i+1] == "unix" {
   102  				return term.FgBgColor{Fg: term.DarkCyan}
   103  			}
   104  		}
   105  	}
   106  	return term.FgBgColor{}
   107  }
   108  
   109  // launch unix and tcp servers
   110  func setup() {
   111  	logger := log.NewTMLoggerWithColorFn(log.NewSyncWriter(os.Stdout), colorFn)
   112  
   113  	cmd := exec.Command("rm", "-f", unixSocket)
   114  	err := cmd.Start()
   115  	if err != nil {
   116  		panic(err)
   117  	}
   118  	if err = cmd.Wait(); err != nil {
   119  		panic(err)
   120  	}
   121  
   122  	tcpLogger := logger.With("socket", "tcp")
   123  	mux := http.NewServeMux()
   124  	server.RegisterRPCFuncs(mux, Routes, RoutesCdc, tcpLogger)
   125  	wm := server.NewWebsocketManager(Routes, RoutesCdc, server.ReadWait(5*time.Second), server.PingPeriod(1*time.Second))
   126  	wm.SetLogger(tcpLogger)
   127  	mux.HandleFunc(websocketEndpoint, wm.WebsocketHandler)
   128  	config := server.DefaultConfig()
   129  	listener1, err := server.Listen(tcpAddr, config)
   130  	if err != nil {
   131  		panic(err)
   132  	}
   133  	go server.Serve(listener1, mux, tcpLogger, config)
   134  
   135  	unixLogger := logger.With("socket", "unix")
   136  	mux2 := http.NewServeMux()
   137  	server.RegisterRPCFuncs(mux2, Routes, RoutesCdc, unixLogger)
   138  	wm = server.NewWebsocketManager(Routes, RoutesCdc)
   139  	wm.SetLogger(unixLogger)
   140  	mux2.HandleFunc(websocketEndpoint, wm.WebsocketHandler)
   141  	listener2, err := server.Listen(unixAddr, config)
   142  	if err != nil {
   143  		panic(err)
   144  	}
   145  	go server.Serve(listener2, mux2, unixLogger, config)
   146  
   147  	// wait for servers to start
   148  	time.Sleep(time.Second * 2)
   149  }
   150  
   151  func echoViaHTTP(cl client.Caller, val string) (string, error) {
   152  	params := map[string]interface{}{
   153  		"arg": val,
   154  	}
   155  	result := new(ResultEcho)
   156  	if _, err := cl.Call("echo", params, result); err != nil {
   157  		return "", err
   158  	}
   159  	return result.Value, nil
   160  }
   161  
   162  func echoIntViaHTTP(cl client.Caller, val int) (int, error) {
   163  	params := map[string]interface{}{
   164  		"arg": val,
   165  	}
   166  	result := new(ResultEchoInt)
   167  	if _, err := cl.Call("echo_int", params, result); err != nil {
   168  		return 0, err
   169  	}
   170  	return result.Value, nil
   171  }
   172  
   173  func echoBytesViaHTTP(cl client.Caller, bytes []byte) ([]byte, error) {
   174  	params := map[string]interface{}{
   175  		"arg": bytes,
   176  	}
   177  	result := new(ResultEchoBytes)
   178  	if _, err := cl.Call("echo_bytes", params, result); err != nil {
   179  		return []byte{}, err
   180  	}
   181  	return result.Value, nil
   182  }
   183  
   184  func echoDataBytesViaHTTP(cl client.Caller, bytes tmbytes.HexBytes) (tmbytes.HexBytes, error) {
   185  	params := map[string]interface{}{
   186  		"arg": bytes,
   187  	}
   188  	result := new(ResultEchoDataBytes)
   189  	if _, err := cl.Call("echo_data_bytes", params, result); err != nil {
   190  		return []byte{}, err
   191  	}
   192  	return result.Value, nil
   193  }
   194  
   195  func testWithHTTPClient(t *testing.T, cl client.HTTPClient) {
   196  	val := testVal
   197  	got, err := echoViaHTTP(cl, val)
   198  	require.Nil(t, err)
   199  	assert.Equal(t, got, val)
   200  
   201  	val2 := randBytes(t)
   202  	got2, err := echoBytesViaHTTP(cl, val2)
   203  	require.Nil(t, err)
   204  	assert.Equal(t, got2, val2)
   205  
   206  	val3 := tmbytes.HexBytes(randBytes(t))
   207  	got3, err := echoDataBytesViaHTTP(cl, val3)
   208  	require.Nil(t, err)
   209  	assert.Equal(t, got3, val3)
   210  
   211  	val4 := tmrand.Intn(10000)
   212  	got4, err := echoIntViaHTTP(cl, val4)
   213  	require.Nil(t, err)
   214  	assert.Equal(t, got4, val4)
   215  }
   216  
   217  func echoViaWS(cl *client.WSClient, val string) (string, error) {
   218  	params := map[string]interface{}{
   219  		"arg": val,
   220  	}
   221  	err := cl.Call(context.Background(), "echo", params)
   222  	if err != nil {
   223  		return "", err
   224  	}
   225  
   226  	msg := <-cl.ResponsesCh
   227  	if msg.Error != nil {
   228  		return "", err
   229  
   230  	}
   231  	result := new(ResultEcho)
   232  	err = json.Unmarshal(msg.Result, result)
   233  	if err != nil {
   234  		return "", nil
   235  	}
   236  	return result.Value, nil
   237  }
   238  
   239  func echoBytesViaWS(cl *client.WSClient, bytes []byte) ([]byte, error) {
   240  	params := map[string]interface{}{
   241  		"arg": bytes,
   242  	}
   243  	err := cl.Call(context.Background(), "echo_bytes", params)
   244  	if err != nil {
   245  		return []byte{}, err
   246  	}
   247  
   248  	msg := <-cl.ResponsesCh
   249  	if msg.Error != nil {
   250  		return []byte{}, msg.Error
   251  
   252  	}
   253  	result := new(ResultEchoBytes)
   254  	err = json.Unmarshal(msg.Result, result)
   255  	if err != nil {
   256  		return []byte{}, nil
   257  	}
   258  	return result.Value, nil
   259  }
   260  
   261  func testWithWSClient(t *testing.T, cl *client.WSClient) {
   262  	val := testVal
   263  	got, err := echoViaWS(cl, val)
   264  	require.Nil(t, err)
   265  	assert.Equal(t, got, val)
   266  
   267  	val2 := randBytes(t)
   268  	got2, err := echoBytesViaWS(cl, val2)
   269  	require.Nil(t, err)
   270  	assert.Equal(t, got2, val2)
   271  }
   272  
   273  //-------------
   274  
   275  func TestServersAndClientsBasic(t *testing.T) {
   276  	serverAddrs := [...]string{tcpAddr} //unixAddr
   277  	for _, addr := range serverAddrs {
   278  		cl1, err := client.NewURI(addr)
   279  		require.Nil(t, err)
   280  		fmt.Printf("=== testing server on %s using URI client", addr)
   281  		testWithHTTPClient(t, cl1)
   282  
   283  		cl2, err := client.New(addr)
   284  		require.Nil(t, err)
   285  		fmt.Printf("=== testing server on %s using JSONRPC client", addr)
   286  		testWithHTTPClient(t, cl2)
   287  
   288  		cl3, err := client.NewWS(addr, websocketEndpoint)
   289  		require.Nil(t, err)
   290  		cl3.SetLogger(log.TestingLogger())
   291  		err = cl3.Start()
   292  		require.Nil(t, err)
   293  		fmt.Printf("=== testing server on %s using WS client", addr)
   294  		testWithWSClient(t, cl3)
   295  		cl3.Stop()
   296  	}
   297  }
   298  
   299  func TestHexStringArg(t *testing.T) {
   300  	cl, err := client.NewURI(tcpAddr)
   301  	require.Nil(t, err)
   302  	// should NOT be handled as hex
   303  	val := "0xabc"
   304  	got, err := echoViaHTTP(cl, val)
   305  	require.Nil(t, err)
   306  	assert.Equal(t, got, val)
   307  }
   308  
   309  func TestQuotedStringArg(t *testing.T) {
   310  	cl, err := client.NewURI(tcpAddr)
   311  	require.Nil(t, err)
   312  	// should NOT be unquoted
   313  	val := "\"abc\""
   314  	got, err := echoViaHTTP(cl, val)
   315  	require.Nil(t, err)
   316  	assert.Equal(t, got, val)
   317  }
   318  
   319  func TestWSNewWSRPCFunc(t *testing.T) {
   320  	cl, err := client.NewWS(tcpAddr, websocketEndpoint)
   321  	require.Nil(t, err)
   322  	cl.SetLogger(log.TestingLogger())
   323  	err = cl.Start()
   324  	require.Nil(t, err)
   325  	defer cl.Stop()
   326  
   327  	val := testVal
   328  	params := map[string]interface{}{
   329  		"arg": val,
   330  	}
   331  	err = cl.Call(context.Background(), "echo_ws", params)
   332  	require.Nil(t, err)
   333  
   334  	msg := <-cl.ResponsesCh
   335  	if msg.Error != nil {
   336  		t.Fatal(err)
   337  	}
   338  	result := new(ResultEcho)
   339  	err = json.Unmarshal(msg.Result, result)
   340  	require.Nil(t, err)
   341  	got := result.Value
   342  	assert.Equal(t, got, val)
   343  }
   344  
   345  func TestWSHandlesArrayParams(t *testing.T) {
   346  	cl, err := client.NewWS(tcpAddr, websocketEndpoint)
   347  	require.Nil(t, err)
   348  	cl.SetLogger(log.TestingLogger())
   349  	err = cl.Start()
   350  	require.Nil(t, err)
   351  	defer cl.Stop()
   352  
   353  	val := testVal
   354  	params := []interface{}{val}
   355  	err = cl.CallWithArrayParams(context.Background(), "echo_ws", params)
   356  	require.Nil(t, err)
   357  
   358  	msg := <-cl.ResponsesCh
   359  	if msg.Error != nil {
   360  		t.Fatalf("%+v", err)
   361  	}
   362  	result := new(ResultEcho)
   363  	err = json.Unmarshal(msg.Result, result)
   364  	require.Nil(t, err)
   365  	got := result.Value
   366  	assert.Equal(t, got, val)
   367  }
   368  
   369  // TestWSClientPingPong checks that a client & server exchange pings
   370  // & pongs so connection stays alive.
   371  func TestWSClientPingPong(t *testing.T) {
   372  	cl, err := client.NewWS(tcpAddr, websocketEndpoint)
   373  	require.Nil(t, err)
   374  	cl.SetLogger(log.TestingLogger())
   375  	err = cl.Start()
   376  	require.Nil(t, err)
   377  	defer cl.Stop()
   378  
   379  	time.Sleep(6 * time.Second)
   380  }
   381  
   382  func randBytes(t *testing.T) []byte {
   383  	n := tmrand.Intn(10) + 2
   384  	buf := make([]byte, n)
   385  	_, err := crand.Read(buf)
   386  	require.Nil(t, err)
   387  	return bytes.Replace(buf, []byte("="), []byte{100}, -1)
   388  }