Browse Source

Merge pull request #11 from tendermint/feature/refactor-tests

WSClient failing to echo bytes
pull/456/head
Ethan Buchman 8 years ago
committed by GitHub
parent
commit
052c2c1575
5 changed files with 141 additions and 150 deletions
  1. +3
    -0
      Makefile
  2. +1
    -0
      README.md
  3. +0
    -1
      client/http_client.go
  4. +24
    -2
      client/ws_client.go
  5. +113
    -147
      rpc_test.go

+ 3
- 0
Makefile View File

@ -11,5 +11,8 @@ test:
get_deps: get_deps:
@echo "--> Running go get" @echo "--> Running go get"
@go get -v -d $(PACKAGES) @go get -v -d $(PACKAGES)
@go list -f '{{join .TestImports "\n"}}' ./... | \
grep -v /vendor/ | sort | uniq | \
xargs go get -v -d
.PHONY: all test get_deps .PHONY: all test get_deps

+ 1
- 0
README.md View File

@ -125,3 +125,4 @@ IMPROVEMENTS:
- added `HTTPClient` interface, which can be used for both `ClientURI` - added `HTTPClient` interface, which can be used for both `ClientURI`
and `ClientJSONRPC` and `ClientJSONRPC`
- all params are now optional (Golang's default will be used if some param is missing) - all params are now optional (Golang's default will be used if some param is missing)
- added `Call` method to `WSClient` (see method's doc for details)

+ 0
- 1
client/http_client.go View File

@ -72,7 +72,6 @@ func (c *JSONRPCClient) Call(method string, params map[string]interface{}, resul
// (handlers.go:176) on the server side // (handlers.go:176) on the server side
encodedParams := make(map[string]interface{}) encodedParams := make(map[string]interface{})
for k, v := range params { for k, v := range params {
// log.Printf("%s: %v (%s)\n", k, v, string(wire.JSONBytes(v)))
bytes := json.RawMessage(wire.JSONBytes(v)) bytes := json.RawMessage(wire.JSONBytes(v))
encodedParams[k] = &bytes encodedParams[k] = &bytes
} }


+ 24
- 2
client/ws_client.go View File

@ -10,6 +10,7 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
cmn "github.com/tendermint/go-common" cmn "github.com/tendermint/go-common"
types "github.com/tendermint/go-rpc/types" types "github.com/tendermint/go-rpc/types"
wire "github.com/tendermint/go-wire"
) )
const ( const (
@ -120,7 +121,8 @@ func (wsc *WSClient) receiveEventsRoutine() {
close(wsc.ErrorsCh) close(wsc.ErrorsCh)
} }
// subscribe to an event
// Subscribe to an event. Note the server must have a "subscribe" route
// defined.
func (wsc *WSClient) Subscribe(eventid string) error { func (wsc *WSClient) Subscribe(eventid string) error {
err := wsc.WriteJSON(types.RPCRequest{ err := wsc.WriteJSON(types.RPCRequest{
JSONRPC: "2.0", JSONRPC: "2.0",
@ -131,7 +133,8 @@ func (wsc *WSClient) Subscribe(eventid string) error {
return err return err
} }
// unsubscribe from an event
// Unsubscribe from an event. Note the server must have a "unsubscribe" route
// defined.
func (wsc *WSClient) Unsubscribe(eventid string) error { func (wsc *WSClient) Unsubscribe(eventid string) error {
err := wsc.WriteJSON(types.RPCRequest{ err := wsc.WriteJSON(types.RPCRequest{
JSONRPC: "2.0", JSONRPC: "2.0",
@ -141,3 +144,22 @@ func (wsc *WSClient) Unsubscribe(eventid string) error {
}) })
return err return err
} }
// Call asynchronously calls a given method by sending an RPCRequest to the
// server. Results will be available on ResultsCh, errors, if any, on ErrorsCh.
func (wsc *WSClient) Call(method string, params map[string]interface{}) error {
// we need this step because we attempt to decode values using `go-wire`
// (handlers.go:470) on the server side
encodedParams := make(map[string]interface{})
for k, v := range params {
bytes := json.RawMessage(wire.JSONBytes(v))
encodedParams[k] = &bytes
}
err := wsc.WriteJSON(types.RPCRequest{
JSONRPC: "2.0",
Method: method,
Params: encodedParams,
ID: "",
})
return err
}

+ 113
- 147
rpc_test.go View File

@ -3,12 +3,15 @@ package rpc
import ( import (
"bytes" "bytes"
crand "crypto/rand" crand "crypto/rand"
"fmt"
"math/rand" "math/rand"
"net/http" "net/http"
"os/exec" "os/exec"
"testing" "testing"
"time" "time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
client "github.com/tendermint/go-rpc/client" client "github.com/tendermint/go-rpc/client"
server "github.com/tendermint/go-rpc/server" server "github.com/tendermint/go-rpc/server"
types "github.com/tendermint/go-rpc/types" types "github.com/tendermint/go-rpc/types"
@ -28,38 +31,37 @@ const (
// Define a type for results and register concrete versions // Define a type for results and register concrete versions
type Result interface{} type Result interface{}
type ResultStatus struct {
type ResultEcho struct {
Value string Value string
} }
type ResultBytes struct {
type ResultEchoBytes struct {
Value []byte Value []byte
} }
var _ = wire.RegisterInterface( var _ = wire.RegisterInterface(
struct{ Result }{}, struct{ Result }{},
wire.ConcreteType{&ResultStatus{}, 0x1},
wire.ConcreteType{&ResultBytes{}, 0x2},
wire.ConcreteType{&ResultEcho{}, 0x1},
wire.ConcreteType{&ResultEchoBytes{}, 0x2},
) )
// Define some routes // Define some routes
var Routes = map[string]*server.RPCFunc{ var Routes = map[string]*server.RPCFunc{
"status": server.NewRPCFunc(StatusResult, "arg"),
"status_ws": server.NewWSRPCFunc(StatusWSResult, "arg"),
"bytes": server.NewRPCFunc(BytesResult, "arg"),
"echo": server.NewRPCFunc(EchoResult, "arg"),
"echo_ws": server.NewWSRPCFunc(EchoWSResult, "arg"),
"echo_bytes": server.NewRPCFunc(EchoBytesResult, "arg"),
} }
// an rpc function
func StatusResult(v string) (Result, error) {
return &ResultStatus{v}, nil
func EchoResult(v string) (Result, error) {
return &ResultEcho{v}, nil
} }
func StatusWSResult(wsCtx types.WSRPCContext, v string) (Result, error) {
return &ResultStatus{v}, nil
func EchoWSResult(wsCtx types.WSRPCContext, v string) (Result, error) {
return &ResultEcho{v}, nil
} }
func BytesResult(v []byte) (Result, error) {
return &ResultBytes{v}, nil
func EchoBytesResult(v []byte) (Result, error) {
return &ResultEchoBytes{v}, nil
} }
// launch unix and tcp servers // launch unix and tcp servers
@ -69,7 +71,9 @@ func init() {
if err != nil { if err != nil {
panic(err) panic(err)
} }
err = cmd.Wait()
if err = cmd.Wait(); err != nil {
panic(err)
}
mux := http.NewServeMux() mux := http.NewServeMux()
server.RegisterRPCFuncs(mux, Routes) server.RegisterRPCFuncs(mux, Routes)
@ -95,54 +99,49 @@ func init() {
// wait for servers to start // wait for servers to start
time.Sleep(time.Second * 2) time.Sleep(time.Second * 2)
} }
func testURI(t *testing.T, cl *client.URIClient) {
val := "acbd"
func echoViaHTTP(cl client.HTTPClient, val string) (string, error) {
params := map[string]interface{}{ params := map[string]interface{}{
"arg": val, "arg": val,
} }
var result Result var result Result
_, err := cl.Call("status", params, &result)
if err != nil {
t.Fatal(err)
}
got := result.(*ResultStatus).Value
if got != val {
t.Fatalf("Got: %v .... Expected: %v \n", got, val)
if _, err := cl.Call("echo", params, &result); err != nil {
return "", err
} }
return result.(*ResultEcho).Value, nil
} }
func testJSONRPC(t *testing.T, cl *client.JSONRPCClient) {
val := "acbd"
func echoBytesViaHTTP(cl client.HTTPClient, bytes []byte) ([]byte, error) {
params := map[string]interface{}{ params := map[string]interface{}{
"arg": val,
"arg": bytes,
} }
var result Result var result Result
_, err := cl.Call("status", params, &result)
if err != nil {
t.Fatal(err)
}
got := result.(*ResultStatus).Value
if got != val {
t.Fatalf("Got: %v .... Expected: %v \n", got, val)
if _, err := cl.Call("echo_bytes", params, &result); err != nil {
return []byte{}, err
} }
return result.(*ResultEchoBytes).Value, nil
} }
func testWS(t *testing.T, cl *client.WSClient) {
func testWithHTTPClient(t *testing.T, cl client.HTTPClient) {
val := "acbd" val := "acbd"
got, err := echoViaHTTP(cl, val)
require.Nil(t, err)
assert.Equal(t, got, val)
val2 := randBytes(t)
got2, err := echoBytesViaHTTP(cl, val2)
require.Nil(t, err)
assert.Equal(t, got2, val2)
}
func echoViaWS(cl *client.WSClient, val string) (string, error) {
params := map[string]interface{}{ params := map[string]interface{}{
"arg": val, "arg": val,
} }
err := cl.WriteJSON(types.RPCRequest{
JSONRPC: "2.0",
ID: "",
Method: "status",
Params: params,
})
err := cl.Call("echo", params)
if err != nil { if err != nil {
t.Fatal(err)
return "", err
} }
select { select {
@ -150,127 +149,92 @@ func testWS(t *testing.T, cl *client.WSClient) {
result := new(Result) result := new(Result)
wire.ReadJSONPtr(result, msg, &err) wire.ReadJSONPtr(result, msg, &err)
if err != nil { if err != nil {
t.Fatal(err)
}
got := (*result).(*ResultStatus).Value
if got != val {
t.Fatalf("Got: %v .... Expected: %v \n", got, val)
return "", nil
} }
return (*result).(*ResultEcho).Value, nil
case err := <-cl.ErrorsCh: case err := <-cl.ErrorsCh:
t.Fatal(err)
return "", err
} }
} }
//-------------
func TestURI_TCP(t *testing.T) {
cl := client.NewURIClient(tcpAddr)
testURI(t, cl)
}
func TestURI_UNIX(t *testing.T) {
cl := client.NewURIClient(unixAddr)
testURI(t, cl)
}
func echoBytesViaWS(cl *client.WSClient, bytes []byte) ([]byte, error) {
params := map[string]interface{}{
"arg": bytes,
}
err := cl.Call("echo_bytes", params)
if err != nil {
return []byte{}, err
}
func TestJSONRPC_TCP(t *testing.T) {
cl := client.NewJSONRPCClient(tcpAddr)
testJSONRPC(t, cl)
select {
case msg := <-cl.ResultsCh:
result := new(Result)
wire.ReadJSONPtr(result, msg, &err)
if err != nil {
return []byte{}, nil
}
return (*result).(*ResultEchoBytes).Value, nil
case err := <-cl.ErrorsCh:
return []byte{}, err
}
} }
func TestJSONRPC_UNIX(t *testing.T) {
cl := client.NewJSONRPCClient(unixAddr)
testJSONRPC(t, cl)
func testWithWSClient(t *testing.T, cl *client.WSClient) {
val := "acbd"
got, err := echoViaWS(cl, val)
require.Nil(t, err)
assert.Equal(t, got, val)
val2 := randBytes(t)
got2, err := echoBytesViaWS(cl, val2)
require.Nil(t, err)
assert.Equal(t, got2, val2)
} }
func TestWS_TCP(t *testing.T) {
cl := client.NewWSClient(tcpAddr, websocketEndpoint)
_, err := cl.Start()
if err != nil {
t.Fatal(err)
}
testWS(t, cl)
}
//-------------
func TestWS_UNIX(t *testing.T) {
cl := client.NewWSClient(unixAddr, websocketEndpoint)
_, err := cl.Start()
if err != nil {
t.Fatal(err)
func TestServersAndClientsBasic(t *testing.T) {
serverAddrs := [...]string{tcpAddr, unixAddr}
for _, addr := range serverAddrs {
cl1 := client.NewURIClient(addr)
fmt.Printf("=== testing server on %s using %v client", addr, cl1)
testWithHTTPClient(t, cl1)
cl2 := client.NewJSONRPCClient(tcpAddr)
fmt.Printf("=== testing server on %s using %v client", addr, cl2)
testWithHTTPClient(t, cl2)
cl3 := client.NewWSClient(tcpAddr, websocketEndpoint)
_, err := cl3.Start()
require.Nil(t, err)
fmt.Printf("=== testing server on %s using %v client", addr, cl3)
testWithWSClient(t, cl3)
cl3.Stop()
} }
testWS(t, cl)
} }
func TestHexStringArg(t *testing.T) { func TestHexStringArg(t *testing.T) {
cl := client.NewURIClient(tcpAddr) cl := client.NewURIClient(tcpAddr)
// should NOT be handled as hex // should NOT be handled as hex
val := "0xabc" val := "0xabc"
params := map[string]interface{}{
"arg": val,
}
var result Result
_, err := cl.Call("status", params, &result)
if err != nil {
t.Fatal(err)
}
got := result.(*ResultStatus).Value
if got != val {
t.Fatalf("Got: %v .... Expected: %v \n", got, val)
}
got, err := echoViaHTTP(cl, val)
require.Nil(t, err)
assert.Equal(t, got, val)
} }
func TestQuotedStringArg(t *testing.T) { func TestQuotedStringArg(t *testing.T) {
cl := client.NewURIClient(tcpAddr) cl := client.NewURIClient(tcpAddr)
// should NOT be unquoted // should NOT be unquoted
val := "\"abc\"" val := "\"abc\""
params := map[string]interface{}{
"arg": val,
}
var result Result
_, err := cl.Call("status", params, &result)
if err != nil {
t.Fatal(err)
}
got := result.(*ResultStatus).Value
if got != val {
t.Fatalf("Got: %v .... Expected: %v \n", got, val)
}
}
func randBytes(t *testing.T) []byte {
n := rand.Intn(10) + 2
buf := make([]byte, n)
_, err := crand.Read(buf)
if err != nil {
t.Fatal(err)
}
return bytes.Replace(buf, []byte("="), []byte{100}, -1)
}
func TestByteSliceViaJSONRPC(t *testing.T) {
cl := client.NewJSONRPCClient(unixAddr)
val := randBytes(t)
params := map[string]interface{}{
"arg": val,
}
var result Result
_, err := cl.Call("bytes", params, &result)
if err != nil {
t.Fatal(err)
}
got := result.(*ResultBytes).Value
if bytes.Compare(got, val) != 0 {
t.Fatalf("Got: %v .... Expected: %v \n", got, val)
}
got, err := echoViaHTTP(cl, val)
require.Nil(t, err)
assert.Equal(t, got, val)
} }
func TestWSNewWSRPCFunc(t *testing.T) { func TestWSNewWSRPCFunc(t *testing.T) {
cl := client.NewWSClient(unixAddr, websocketEndpoint)
cl := client.NewWSClient(tcpAddr, websocketEndpoint)
_, err := cl.Start() _, err := cl.Start()
if err != nil {
t.Fatal(err)
}
require.Nil(t, err)
defer cl.Stop() defer cl.Stop()
val := "acbd" val := "acbd"
@ -280,25 +244,27 @@ func TestWSNewWSRPCFunc(t *testing.T) {
err = cl.WriteJSON(types.RPCRequest{ err = cl.WriteJSON(types.RPCRequest{
JSONRPC: "2.0", JSONRPC: "2.0",
ID: "", ID: "",
Method: "status_ws",
Method: "echo_ws",
Params: params, Params: params,
}) })
if err != nil {
t.Fatal(err)
}
require.Nil(t, err)
select { select {
case msg := <-cl.ResultsCh: case msg := <-cl.ResultsCh:
result := new(Result) result := new(Result)
wire.ReadJSONPtr(result, msg, &err) wire.ReadJSONPtr(result, msg, &err)
if err != nil {
t.Fatal(err)
}
got := (*result).(*ResultStatus).Value
if got != val {
t.Fatalf("Got: %v .... Expected: %v \n", got, val)
}
require.Nil(t, err)
got := (*result).(*ResultEcho).Value
assert.Equal(t, got, val)
case err := <-cl.ErrorsCh: case err := <-cl.ErrorsCh:
t.Fatal(err) t.Fatal(err)
} }
} }
func randBytes(t *testing.T) []byte {
n := rand.Intn(10) + 2
buf := make([]byte, n)
_, err := crand.Read(buf)
require.Nil(t, err)
return bytes.Replace(buf, []byte("="), []byte{100}, -1)
}

Loading…
Cancel
Save