diff --git a/glide.lock b/glide.lock index c58c2a4d8..759553ea5 100644 --- a/glide.lock +++ b/glide.lock @@ -137,4 +137,14 @@ imports: - stats - tap - transport -testImports: [] +testImports: +- name: github.com/tendermint/merkleeyes + version: 87b0a111a716f1495f30ce58bd469e36ac220e09 + subpackages: + - app +- name: github.com/stretchr/testify + version: 69483b4bd14f5845b5a1e55bca19e954e827f1d0 + subpackages: + - assert + - require + diff --git a/glide.yaml b/glide.yaml index d51903d54..55c859628 100644 --- a/glide.yaml +++ b/glide.yaml @@ -37,3 +37,13 @@ import: - package: golang.org/x/crypto subpackages: - ripemd160 +testImport: +- package: github.com/stretchr/testify + version: ^1.1.4 + subpackages: + - assert + - require +- package: github.com/tendermint/merkleeyes + version: develop + subpackages: + - app diff --git a/rpc/client/app_test.go b/rpc/client/app_test.go new file mode 100644 index 000000000..b360dba3b --- /dev/null +++ b/rpc/client/app_test.go @@ -0,0 +1,45 @@ +package client_test + +import ( + "math/rand" + + meapp "github.com/tendermint/merkleeyes/app" + + wire "github.com/tendermint/go-wire" +) + +// MakeTxKV returns a text transaction, allong with expected key, value pair +func MakeTxKV() ([]byte, []byte, []byte) { + k := RandAsciiBytes(8) + v := RandAsciiBytes(8) + return k, v, makeSet(k, v) +} + +// blatently copied from merkleeyes/app/app_test.go +// constructs a "set" transaction +func makeSet(key, value []byte) []byte { + tx := make([]byte, 1+wire.ByteSliceSize(key)+wire.ByteSliceSize(value)) + buf := tx + buf[0] = meapp.WriteSet // Set TypeByte + buf = buf[1:] + n, err := wire.PutByteSlice(buf, key) + if err != nil { + panic(err) + } + buf = buf[n:] + n, err = wire.PutByteSlice(buf, value) + if err != nil { + panic(err) + } + return tx +} + +const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" + +func RandAsciiBytes(n int) []byte { + b := make([]byte, n) + for i := range b { + b[i] = letterBytes[rand.Intn(len(letterBytes))] + } + return b +} diff --git a/rpc/client/client.go b/rpc/client/client.go index b2d4ee219..a319b9bc7 100644 --- a/rpc/client/client.go +++ b/rpc/client/client.go @@ -1,4 +1,4 @@ -package rpcclient +package client import ( "encoding/json" diff --git a/rpc/client/main_test.go b/rpc/client/main_test.go new file mode 100644 index 000000000..5119ac41d --- /dev/null +++ b/rpc/client/main_test.go @@ -0,0 +1,21 @@ +package client_test + +import ( + "os" + "testing" + + meapp "github.com/tendermint/merkleeyes/app" + rpctest "github.com/tendermint/tendermint/rpc/test" +) + +func TestMain(m *testing.M) { + // start a tendermint node (and merkleeyes) in the background to test against + app := meapp.NewMerkleEyesApp("", 100) + node := rpctest.StartTendermint(app) + code := m.Run() + + // and shut down proper at the end + node.Stop() + node.Wait() + os.Exit(code) +} diff --git a/rpc/test/rpc_test.go b/rpc/client/rpc_test.go similarity index 85% rename from rpc/test/rpc_test.go rename to rpc/client/rpc_test.go index 50a74f37d..6711c2202 100644 --- a/rpc/test/rpc_test.go +++ b/rpc/client/rpc_test.go @@ -1,32 +1,33 @@ -package rpctest +package client_test import ( "testing" + "time" "github.com/stretchr/testify/assert" - // "github.com/stretchr/testify/require" - // merkle "github.com/tendermint/go-merkle" - // "github.com/tendermint/tendermint/types" + "github.com/stretchr/testify/require" + merkle "github.com/tendermint/go-merkle" + rpctest "github.com/tendermint/tendermint/rpc/test" + "github.com/tendermint/tendermint/types" ) // Make sure status is correct (we connect properly) func TestStatus(t *testing.T) { - c := GetClient() - chainID := GetConfig().GetString("chain_id") + c := rpctest.GetClient() + chainID := rpctest.GetConfig().GetString("chain_id") status, err := c.Status() if assert.Nil(t, err) { assert.Equal(t, chainID, status.NodeInfo.Network) } } -/* // Make some app checks func TestAppCalls(t *testing.T) { assert, require := assert.New(t), require.New(t) - c := GetClient() + c := rpctest.GetClient() _, err := c.Block(1) assert.NotNil(err) // no block yet - k, v, tx := TestTxKV() + k, v, tx := MakeTxKV() _, err = c.BroadcastTxCommit(tx) require.Nil(err) // wait before querying @@ -72,7 +73,7 @@ func TestAppCalls(t *testing.T) { // run most calls just to make sure no syntax errors func TestNoErrors(t *testing.T) { assert := assert.New(t) - c := GetClient() + c := rpctest.GetClient() _, err := c.NetInfo() assert.Nil(err) _, err = c.BlockchainInfo(0, 4) @@ -85,19 +86,20 @@ func TestNoErrors(t *testing.T) { // assert.Nil(err) gen, err := c.Genesis() if assert.Nil(err) { - assert.Equal(GetConfig().GetString("chain_id"), gen.Genesis.ChainID) + chainID := rpctest.GetConfig().GetString("chain_id") + assert.Equal(chainID, gen.Genesis.ChainID) } } func TestSubscriptions(t *testing.T) { assert, require := assert.New(t), require.New(t) - c := GetClient() + c := rpctest.GetClient() err := c.StartWebsocket() require.Nil(err) defer c.StopWebsocket() // subscribe to a transaction event - _, _, tx := TestTxKV() + _, _, tx := MakeTxKV() // this causes a panic in tendermint core!!! eventType := types.EventStringTx(types.Tx(tx)) c.Subscribe(eventType) @@ -128,4 +130,3 @@ func TestSubscriptions(t *testing.T) { // now make sure the event arrived assert.Equal(1, read) } -*/ diff --git a/rpc/test/helpers.go b/rpc/test/helpers.go index 287228e04..0c80f62a9 100644 --- a/rpc/test/helpers.go +++ b/rpc/test/helpers.go @@ -2,8 +2,12 @@ package rpctest import ( "fmt" + "testing" + "time" + "github.com/stretchr/testify/require" logger "github.com/tendermint/go-logger" + wire "github.com/tendermint/go-wire" abci "github.com/tendermint/abci/types" cfg "github.com/tendermint/go-config" @@ -12,6 +16,7 @@ import ( nm "github.com/tendermint/tendermint/node" "github.com/tendermint/tendermint/proxy" rpcclient "github.com/tendermint/tendermint/rpc/client" + ctypes "github.com/tendermint/tendermint/rpc/core/types" core_grpc "github.com/tendermint/tendermint/rpc/grpc" "github.com/tendermint/tendermint/types" ) @@ -89,3 +94,69 @@ func NewTendermint(app abci.Application) *nm.Node { node.Start() return node } + +//-------------------------------------------------------------------------------- +// Utilities for testing the websocket service + +// wait for an event; do things that might trigger events, and check them when they are received +// the check function takes an event id and the byte slice read off the ws +func waitForEvent(t *testing.T, wsc *client.WSClient, eventid string, dieOnTimeout bool, f func(), check func(string, interface{}) error) { + // go routine to wait for webscoket msg + goodCh := make(chan interface{}) + errCh := make(chan error) + + // Read message + go func() { + var err error + LOOP: + for { + select { + case r := <-wsc.ResultsCh: + result := new(ctypes.TMResult) + wire.ReadJSONPtr(result, r, &err) + if err != nil { + errCh <- err + break LOOP + } + event, ok := (*result).(*ctypes.ResultEvent) + if ok && event.Name == eventid { + goodCh <- event.Data + break LOOP + } + case err := <-wsc.ErrorsCh: + errCh <- err + break LOOP + case <-wsc.Quit: + break LOOP + } + } + }() + + // do stuff (transactions) + f() + + // wait for an event or timeout + timeout := time.NewTimer(10 * time.Second) + select { + case <-timeout.C: + if dieOnTimeout { + wsc.Stop() + require.True(t, false, "%s event was not received in time", eventid) + } + // else that's great, we didn't hear the event + // and we shouldn't have + case eventData := <-goodCh: + if dieOnTimeout { + // message was received and expected + // run the check + require.Nil(t, check(eventid, eventData)) + } else { + wsc.Stop() + require.True(t, false, "%s event was not expected", eventid) + } + case err := <-errCh: + panic(err) // Show the stack trace. + } +} + +//-------------------------------------------------------------------------------- diff --git a/rpc/test/helpers_old.go b/rpc/test/helpers_old.go deleted file mode 100644 index a5d2f41d9..000000000 --- a/rpc/test/helpers_old.go +++ /dev/null @@ -1,152 +0,0 @@ -package rpctest - -import ( - "testing" - "time" - - "github.com/stretchr/testify/require" - . "github.com/tendermint/go-common" - "github.com/tendermint/go-wire" - - client "github.com/tendermint/go-rpc/client" - ctypes "github.com/tendermint/tendermint/rpc/core/types" -) - -/* -// global variables for use across all tests -var ( - config cfg.Config - node *nm.Node - chainID string - rpcAddr string - requestAddr string - websocketAddr string - websocketEndpoint string - grpcAddr string - clientURI *client.ClientURI - clientJSON *client.ClientJSONRPC - clientGRPC core_grpc.BroadcastAPIClient -) - -// initialize config and create new node -func init() { - config = tendermint_test.ResetConfig("rpc_test_client_test") - chainID = config.GetString("chain_id") - rpcAddr = config.GetString("rpc_laddr") - grpcAddr = config.GetString("grpc_laddr") - requestAddr = rpcAddr - websocketAddr = rpcAddr - websocketEndpoint = "/websocket" - - clientURI = client.NewClientURI(requestAddr) - clientJSON = client.NewClientJSONRPC(requestAddr) - clientGRPC = core_grpc.StartGRPCClient(grpcAddr) - - // TODO: change consensus/state.go timeouts to be shorter - - // start a node - ready := make(chan struct{}) - go newNode(ready) - <-ready -} - -// create a new node and sleep forever -func newNode(ready chan struct{}) { - // Create & start node - node = nm.NewNodeDefault(config) - node.Start() - - time.Sleep(time.Second) - - ready <- struct{}{} - - // Sleep forever - ch := make(chan struct{}) - <-ch -} - -//-------------------------------------------------------------------------------- -// Utilities for testing the websocket service - -// create a new connection -func newWSClient(t *testing.T) *client.WSClient { - wsc := client.NewWSClient(websocketAddr, websocketEndpoint) - if _, err := wsc.Start(); err != nil { - panic(err) - } - return wsc -} -*/ - -// subscribe to an event -func subscribe(t *testing.T, wsc *client.WSClient, eventid string) { - require.Nil(t, wsc.Subscribe(eventid)) -} - -// unsubscribe from an event -func unsubscribe(t *testing.T, wsc *client.WSClient, eventid string) { - require.Nil(t, wsc.Unsubscribe(eventid)) -} - -// wait for an event; do things that might trigger events, and check them when they are received -// the check function takes an event id and the byte slice read off the ws -func waitForEvent(t *testing.T, wsc *client.WSClient, eventid string, dieOnTimeout bool, f func(), check func(string, interface{}) error) { - // go routine to wait for webscoket msg - goodCh := make(chan interface{}) - errCh := make(chan error) - - // Read message - go func() { - var err error - LOOP: - for { - select { - case r := <-wsc.ResultsCh: - result := new(ctypes.TMResult) - wire.ReadJSONPtr(result, r, &err) - if err != nil { - errCh <- err - break LOOP - } - event, ok := (*result).(*ctypes.ResultEvent) - if ok && event.Name == eventid { - goodCh <- event.Data - break LOOP - } - case err := <-wsc.ErrorsCh: - errCh <- err - break LOOP - case <-wsc.Quit: - break LOOP - } - } - }() - - // do stuff (transactions) - f() - - // wait for an event or timeout - timeout := time.NewTimer(10 * time.Second) - select { - case <-timeout.C: - if dieOnTimeout { - wsc.Stop() - panic(Fmt("%s event was not received in time", eventid)) - } - // else that's great, we didn't hear the event - // and we shouldn't have - case eventData := <-goodCh: - if dieOnTimeout { - // message was received and expected - // run the check - require.Nil(t, check(eventid, eventData)) - } else { - wsc.Stop() - panic(Fmt("%s event was not expected", eventid)) - } - case err := <-errCh: - panic(err) // Show the stack trace. - } -} - -//--------------------------------------------------------------------------------