From eaec0c8ea51ae7304fcce75e1c7431d0d0881806 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Fri, 26 May 2017 13:24:32 +0200 Subject: [PATCH] deduplicate tests in rpc/test and rpc/client (Refs #496) --- rpc/client/event_test.go | 67 +++++++- rpc/client/rpc_test.go | 91 ++++++++++ rpc/test/client_test.go | 352 --------------------------------------- rpc/test/grpc_test.go | 6 +- rpc/test/helpers.go | 92 ---------- 5 files changed, 159 insertions(+), 449 deletions(-) delete mode 100644 rpc/test/client_test.go diff --git a/rpc/client/event_test.go b/rpc/client/event_test.go index 1b99854cb..ad5522e49 100644 --- a/rpc/client/event_test.go +++ b/rpc/client/event_test.go @@ -31,7 +31,39 @@ func TestHeaderEvents(t *testing.T) { } } -func TestTxEvents(t *testing.T) { +func TestBlockEvents(t *testing.T) { + require := require.New(t) + for i, c := range GetClients() { + // start for this test it if it wasn't already running + if !c.IsRunning() { + // if so, then we start it, listen, and stop it. + st, err := c.Start() + require.Nil(err, "%d: %+v", i, err) + require.True(st, "%d", i) + defer c.Stop() + } + + // listen for a new block; ensure height increases by 1 + var firstBlockHeight int + for i := 0; i < 3; i++ { + evtTyp := types.EventStringNewBlock() + evt, err := client.WaitForOneEvent(c, evtTyp, 1*time.Second) + require.Nil(err, "%d: %+v", i, err) + blockEvent, ok := evt.Unwrap().(types.EventDataNewBlock) + require.True(ok, "%d: %#v", i, evt) + + block := blockEvent.Block + if i == 0 { + firstBlockHeight = block.Header.Height + continue + } + + require.Equal(block.Header.Height, firstBlockHeight+i) + } + } +} + +func TestTxEventsSentWithBroadcastTxAsync(t *testing.T) { require := require.New(t) for i, c := range GetClients() { // start for this test it if it wasn't already running @@ -63,3 +95,36 @@ func TestTxEvents(t *testing.T) { require.True(txe.Code.IsOK()) } } + +func TestTxEventsSentWithBroadcastTxSync(t *testing.T) { + require := require.New(t) + for i, c := range GetClients() { + // start for this test it if it wasn't already running + if !c.IsRunning() { + // if so, then we start it, listen, and stop it. + st, err := c.Start() + require.Nil(err, "%d: %+v", i, err) + require.True(st, "%d", i) + defer c.Stop() + } + + // make the tx + _, _, tx := merktest.MakeTxKV() + evtTyp := types.EventStringTx(types.Tx(tx)) + + // send async + txres, err := c.BroadcastTxSync(tx) + require.Nil(err, "%+v", err) + require.True(txres.Code.IsOK()) + + // and wait for confirmation + evt, err := client.WaitForOneEvent(c, evtTyp, 1*time.Second) + require.Nil(err, "%d: %+v", i, err) + // and make sure it has the proper info + txe, ok := evt.Unwrap().(types.EventDataTx) + require.True(ok, "%d: %#v", i, evt) + // make sure this is the proper tx + require.EqualValues(tx, txe.Tx) + require.True(txe.Code.IsOK()) + } +} diff --git a/rpc/client/rpc_test.go b/rpc/client/rpc_test.go index 13b440479..e82f8df42 100644 --- a/rpc/client/rpc_test.go +++ b/rpc/client/rpc_test.go @@ -10,6 +10,7 @@ import ( merktest "github.com/tendermint/merkleeyes/testutil" "github.com/tendermint/tendermint/rpc/client" rpctest "github.com/tendermint/tendermint/rpc/test" + "github.com/tendermint/tendermint/types" ) func getHTTPClient() *client.HTTP { @@ -182,3 +183,93 @@ func TestAppCalls(t *testing.T) { } } } + +func TestBroadcastTxSync(t *testing.T) { + require := require.New(t) + + mempool := node.MempoolReactor().Mempool + initMempoolSize := mempool.Size() + + for i, c := range GetClients() { + _, _, tx := merktest.MakeTxKV() + bres, err := c.BroadcastTxSync(tx) + require.Nil(err, "%d: %+v", i, err) + require.True(bres.Code.IsOK()) + + require.Equal(initMempoolSize+1, mempool.Size()) + + txs := mempool.Reap(1) + require.EqualValues(tx, txs[0]) + mempool.Flush() + } +} + +func TestBroadcastTxCommit(t *testing.T) { + require := require.New(t) + + mempool := node.MempoolReactor().Mempool + for i, c := range GetClients() { + _, _, tx := merktest.MakeTxKV() + bres, err := c.BroadcastTxCommit(tx) + require.Nil(err, "%d: %+v", i, err) + require.True(bres.CheckTx.Code.IsOK()) + require.True(bres.DeliverTx.Code.IsOK()) + + require.Equal(0, mempool.Size()) + } +} + +func TestTx(t *testing.T) { + assert, require := assert.New(t), require.New(t) + + // first we broadcast a tx + c := getHTTPClient() + _, _, tx := merktest.MakeTxKV() + bres, err := c.BroadcastTxCommit(tx) + require.Nil(err, "%+v", err) + + txHeight := bres.Height + txHash := bres.Hash + + anotherTxHash := types.Tx("a different tx").Hash() + + cases := []struct { + valid bool + hash []byte + prove bool + }{ + // only valid if correct hash provided + {true, txHash, false}, + {true, txHash, true}, + {false, anotherTxHash, false}, + {false, anotherTxHash, true}, + {false, nil, false}, + {false, nil, true}, + } + + for i, c := range GetClients() { + for j, tc := range cases { + t.Logf("client %d, case %d", i, j) + + // now we query for the tx. + // since there's only one tx, we know index=0. + ptx, err := c.Tx(tc.hash, tc.prove) + + if !tc.valid { + require.NotNil(err) + } else { + require.Nil(err, "%+v", err) + assert.Equal(txHeight, ptx.Height) + assert.EqualValues(tx, ptx.Tx) + assert.Equal(0, ptx.Index) + assert.True(ptx.TxResult.Code.IsOK()) + + // time to verify the proof + proof := ptx.Proof + if tc.prove && assert.EqualValues(tx, proof.Data) { + assert.True(proof.Proof.Verify(proof.Index, proof.Total, txHash, proof.RootHash)) + } + } + } + } +} diff --git a/rpc/test/client_test.go b/rpc/test/client_test.go deleted file mode 100644 index b7df67841..000000000 --- a/rpc/test/client_test.go +++ /dev/null @@ -1,352 +0,0 @@ -package rpctest - -import ( - "bytes" - crand "crypto/rand" - "fmt" - "math/rand" - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - abci "github.com/tendermint/abci/types" - "github.com/tendermint/go-wire/data" - . "github.com/tendermint/tmlibs/common" - - "github.com/tendermint/tendermint/rpc/core" - ctypes "github.com/tendermint/tendermint/rpc/core/types" - rpc "github.com/tendermint/tendermint/rpc/lib/client" - "github.com/tendermint/tendermint/state/txindex/null" - "github.com/tendermint/tendermint/types" -) - -//-------------------------------------------------------------------------------- -// Test the HTTP client -// These tests assume the dummy app -//-------------------------------------------------------------------------------- - -//-------------------------------------------------------------------------------- -// status - -func TestURIStatus(t *testing.T) { - testStatus(t, GetURIClient()) -} - -func TestJSONStatus(t *testing.T) { - testStatus(t, GetJSONClient()) -} - -func testStatus(t *testing.T, client rpc.HTTPClient) { - moniker := GetConfig().Moniker - result := new(ctypes.ResultStatus) - _, err := client.Call("status", map[string]interface{}{}, result) - require.Nil(t, err) - assert.Equal(t, moniker, result.NodeInfo.Moniker) -} - -//-------------------------------------------------------------------------------- -// broadcast tx sync - -// random bytes (excluding byte('=')) -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) -} - -func TestURIBroadcastTxSync(t *testing.T) { - testBroadcastTxSync(t, GetURIClient()) -} - -func TestJSONBroadcastTxSync(t *testing.T) { - testBroadcastTxSync(t, GetJSONClient()) -} - -func testBroadcastTxSync(t *testing.T, client rpc.HTTPClient) { - mem := node.MempoolReactor().Mempool - initMemSize := mem.Size() - result := new(ctypes.ResultBroadcastTx) - tx := randBytes(t) - _, err := client.Call("broadcast_tx_sync", map[string]interface{}{"tx": tx}, result) - require.Nil(t, err) - - require.Equal(t, abci.CodeType_OK, result.Code) - require.Equal(t, initMemSize+1, mem.Size()) - txs := mem.Reap(1) - require.EqualValues(t, tx, txs[0]) - mem.Flush() -} - -//-------------------------------------------------------------------------------- -// query - -func testTxKV(t *testing.T) ([]byte, []byte, types.Tx) { - k := randBytes(t) - v := randBytes(t) - return k, v, types.Tx(Fmt("%s=%s", k, v)) -} - -func sendTx(t *testing.T, client rpc.HTTPClient) ([]byte, []byte) { - result := new(ctypes.ResultBroadcastTxCommit) - k, v, tx := testTxKV(t) - _, err := client.Call("broadcast_tx_commit", map[string]interface{}{"tx": tx}, result) - require.Nil(t, err) - require.NotNil(t, 0, result.DeliverTx, "%#v", result) - require.EqualValues(t, 0, result.CheckTx.Code, "%#v", result) - require.EqualValues(t, 0, result.DeliverTx.Code, "%#v", result) - return k, v -} - -func TestURIABCIQuery(t *testing.T) { - testABCIQuery(t, GetURIClient()) -} - -func TestJSONABCIQuery(t *testing.T) { - testABCIQuery(t, GetJSONClient()) -} - -func testABCIQuery(t *testing.T, client rpc.HTTPClient) { - k, _ := sendTx(t, client) - time.Sleep(time.Millisecond * 500) - result := new(ctypes.ResultABCIQuery) - _, err := client.Call("abci_query", - map[string]interface{}{"path": "", "data": data.Bytes(k), "prove": false}, result) - require.Nil(t, err) - - require.EqualValues(t, 0, result.Code) - - // XXX: specific to value returned by the dummy - require.NotEqual(t, 0, len(result.Value)) -} - -//-------------------------------------------------------------------------------- -// broadcast tx commit - -func TestURIBroadcastTxCommit(t *testing.T) { - testBroadcastTxCommit(t, GetURIClient()) -} - -func TestJSONBroadcastTxCommit(t *testing.T) { - testBroadcastTxCommit(t, GetJSONClient()) -} - -func testBroadcastTxCommit(t *testing.T, client rpc.HTTPClient) { - require := require.New(t) - - result := new(ctypes.ResultBroadcastTxCommit) - tx := randBytes(t) - _, err := client.Call("broadcast_tx_commit", map[string]interface{}{"tx": tx}, result) - require.Nil(err) - - checkTx := result.CheckTx - require.Equal(abci.CodeType_OK, checkTx.Code) - deliverTx := result.DeliverTx - require.Equal(abci.CodeType_OK, deliverTx.Code) - mem := node.MempoolReactor().Mempool - require.Equal(0, mem.Size()) - // TODO: find tx in block -} - -//-------------------------------------------------------------------------------- -// query tx - -func TestURITx(t *testing.T) { - testTx(t, GetURIClient(), true) - - core.SetTxIndexer(&null.TxIndex{}) - defer core.SetTxIndexer(node.ConsensusState().GetState().TxIndexer) - - testTx(t, GetURIClient(), false) -} - -func TestJSONTx(t *testing.T) { - testTx(t, GetJSONClient(), true) - - core.SetTxIndexer(&null.TxIndex{}) - testTx(t, GetJSONClient(), false) - core.SetTxIndexer(node.ConsensusState().GetState().TxIndexer) -} - -func testTx(t *testing.T, client rpc.HTTPClient, withIndexer bool) { - assert, require := assert.New(t), require.New(t) - - // first we broadcast a tx - result := new(ctypes.ResultBroadcastTxCommit) - txBytes := randBytes(t) - tx := types.Tx(txBytes) - _, err := client.Call("broadcast_tx_commit", map[string]interface{}{"tx": txBytes}, result) - require.Nil(err) - - checkTx := result.CheckTx - require.Equal(abci.CodeType_OK, checkTx.Code) - deliverTx := result.DeliverTx - require.Equal(abci.CodeType_OK, deliverTx.Code) - mem := node.MempoolReactor().Mempool - require.Equal(0, mem.Size()) - - txHash := tx.Hash() - txHash2 := types.Tx("a different tx").Hash() - - cases := []struct { - valid bool - hash []byte - prove bool - }{ - // only valid if correct hash provided - {true, txHash, false}, - {true, txHash, true}, - {false, txHash2, false}, - {false, txHash2, true}, - {false, nil, false}, - {false, nil, true}, - } - - for i, tc := range cases { - idx := fmt.Sprintf("%d", i) - - // now we query for the tx. - // since there's only one tx, we know index=0. - result2 := new(ctypes.ResultTx) - query := map[string]interface{}{ - "hash": tc.hash, - "prove": tc.prove, - } - _, err = client.Call("tx", query, result2) - valid := (withIndexer && tc.valid) - if !valid { - require.NotNil(err, idx) - } else { - require.Nil(err, idx) - assert.Equal(tx, result2.Tx, idx) - assert.Equal(result.Height, result2.Height, idx) - assert.Equal(0, result2.Index, idx) - assert.Equal(abci.CodeType_OK, result2.TxResult.Code, idx) - // time to verify the proof - proof := result2.Proof - if tc.prove && assert.Equal(tx, proof.Data, idx) { - assert.True(proof.Proof.Verify(proof.Index, proof.Total, tx.Hash(), proof.RootHash), idx) - } - } - } - -} - -//-------------------------------------------------------------------------------- -// Test the websocket service - -var wsTyp = "JSONRPC" - -// make a simple connection to the server -func TestWSConnect(t *testing.T) { - wsc := GetWSClient() - wsc.Stop() -} - -// receive a new block message -func TestWSNewBlock(t *testing.T) { - wsc := GetWSClient() - eid := types.EventStringNewBlock() - require.Nil(t, wsc.Subscribe(eid)) - - defer func() { - require.Nil(t, wsc.Unsubscribe(eid)) - wsc.Stop() - }() - waitForEvent(t, wsc, eid, true, func() {}, func(eid string, b interface{}) error { - // fmt.Println("Check:", b) - return nil - }) -} - -// receive a few new block messages in a row, with increasing height -func TestWSBlockchainGrowth(t *testing.T) { - if testing.Short() { - t.Skip("skipping test in short mode.") - } - wsc := GetWSClient() - eid := types.EventStringNewBlock() - require.Nil(t, wsc.Subscribe(eid)) - - defer func() { - require.Nil(t, wsc.Unsubscribe(eid)) - wsc.Stop() - }() - - // listen for NewBlock, ensure height increases by 1 - - var initBlockN int - for i := 0; i < 3; i++ { - waitForEvent(t, wsc, eid, true, func() {}, func(eid string, eventData interface{}) error { - block := eventData.(types.TMEventData).Unwrap().(types.EventDataNewBlock).Block - if i == 0 { - initBlockN = block.Header.Height - } else { - if block.Header.Height != initBlockN+i { - return fmt.Errorf("Expected block %d, got block %d", initBlockN+i, block.Header.Height) - } - } - - return nil - }) - } -} - -func TestWSTxEvent(t *testing.T) { - require := require.New(t) - wsc := GetWSClient() - tx := randBytes(t) - - // listen for the tx I am about to submit - eid := types.EventStringTx(types.Tx(tx)) - require.Nil(wsc.Subscribe(eid)) - - defer func() { - require.Nil(wsc.Unsubscribe(eid)) - wsc.Stop() - }() - - // send an tx - result := new(ctypes.ResultBroadcastTx) - _, err := GetJSONClient().Call("broadcast_tx_sync", map[string]interface{}{"tx": tx}, result) - require.Nil(err) - - waitForEvent(t, wsc, eid, true, func() {}, func(eid string, b interface{}) error { - evt, ok := b.(types.TMEventData).Unwrap().(types.EventDataTx) - require.True(ok, "Got wrong event type: %#v", b) - require.Equal(tx, []byte(evt.Tx), "Returned different tx") - require.Equal(abci.CodeType_OK, evt.Code) - return nil - }) -} - -/* TODO: this with dummy app.. -func TestWSDoubleFire(t *testing.T) { - if testing.Short() { - t.Skip("skipping test in short mode.") - } - con := newWSCon(t) - eid := types.EventStringAccInput(user[0].Address) - subscribe(t, con, eid) - defer func() { - unsubscribe(t, con, eid) - con.Close() - }() - amt := int64(100) - toAddr := user[1].Address - // broadcast the transaction, wait to hear about it - waitForEvent(t, con, eid, true, func() { - tx := makeDefaultSendTxSigned(t, wsTyp, toAddr, amt) - broadcastTx(t, wsTyp, tx) - }, func(eid string, b []byte) error { - return nil - }) - // but make sure we don't hear about it twice - waitForEvent(t, con, eid, false, func() { - }, func(eid string, b []byte) error { - return nil - }) -}*/ diff --git a/rpc/test/grpc_test.go b/rpc/test/grpc_test.go index 4935a09d9..5ca40a3b0 100644 --- a/rpc/test/grpc_test.go +++ b/rpc/test/grpc_test.go @@ -6,15 +6,13 @@ import ( "golang.org/x/net/context" "github.com/stretchr/testify/require" - "github.com/tendermint/tendermint/rpc/grpc" + core_grpc "github.com/tendermint/tendermint/rpc/grpc" ) -//------------------------------------------- - func TestBroadcastTx(t *testing.T) { require := require.New(t) res, err := GetGRPCClient().BroadcastTx(context.Background(), &core_grpc.RequestBroadcastTx{[]byte("this is a tx")}) - require.Nil(err) + require.Nil(err, "%+v", err) require.EqualValues(0, res.CheckTx.Code) require.EqualValues(0, res.DeliverTx.Code) } diff --git a/rpc/test/helpers.go b/rpc/test/helpers.go index 51bb0b8fe..14da15c94 100644 --- a/rpc/test/helpers.go +++ b/rpc/test/helpers.go @@ -1,25 +1,19 @@ package rpctest import ( - "encoding/json" "fmt" "math/rand" "os" "path/filepath" "strings" - "testing" - "time" - "github.com/stretchr/testify/require" "github.com/tendermint/tmlibs/log" abci "github.com/tendermint/abci/types" cfg "github.com/tendermint/tendermint/config" nm "github.com/tendermint/tendermint/node" "github.com/tendermint/tendermint/proxy" - ctypes "github.com/tendermint/tendermint/rpc/core/types" core_grpc "github.com/tendermint/tendermint/rpc/grpc" - client "github.com/tendermint/tendermint/rpc/lib/client" "github.com/tendermint/tendermint/types" ) @@ -65,32 +59,11 @@ func GetConfig() *cfg.Config { return config } -// GetURIClient gets a uri client pointing to the test tendermint rpc -func GetURIClient() *client.URIClient { - rpcAddr := GetConfig().RPC.ListenAddress - return client.NewURIClient(rpcAddr) -} - -// GetJSONClient gets a http/json client pointing to the test tendermint rpc -func GetJSONClient() *client.JSONRPCClient { - rpcAddr := GetConfig().RPC.ListenAddress - return client.NewJSONRPCClient(rpcAddr) -} - func GetGRPCClient() core_grpc.BroadcastAPIClient { grpcAddr := config.RPC.GRPCListenAddress return core_grpc.StartGRPCClient(grpcAddr) } -func GetWSClient() *client.WSClient { - rpcAddr := GetConfig().RPC.ListenAddress - wsc := client.NewWSClient(rpcAddr, "/websocket") - if _, err := wsc.Start(); err != nil { - panic(err) - } - return wsc -} - // StartTendermint starts a test tendermint server in a go routine and returns when it is initialized func StartTendermint(app abci.Application) *nm.Node { node := NewTendermint(app) @@ -111,68 +84,3 @@ func NewTendermint(app abci.Application) *nm.Node { node := nm.NewNode(config, privValidator, papp, logger) 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.ResultEvent) - err = json.Unmarshal(r, result) - if err != nil { - // cant distinguish between error and wrong type ... - continue - } - if result.Name == eventid { - goodCh <- result.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. - } -} - -//--------------------------------------------------------------------------------