github.com/franono/tendermint@v0.32.2-0.20200527150959-749313264ce9/rpc/client/rpc_test.go (about)

     1  package client_test
     2  
     3  import (
     4  	"fmt"
     5  	"math"
     6  	"net/http"
     7  	"strings"
     8  	"sync"
     9  	"testing"
    10  	"time"
    11  
    12  	"github.com/stretchr/testify/assert"
    13  	"github.com/stretchr/testify/require"
    14  
    15  	abci "github.com/franono/tendermint/abci/types"
    16  
    17  	"github.com/franono/tendermint/libs/log"
    18  	tmmath "github.com/franono/tendermint/libs/math"
    19  	mempl "github.com/franono/tendermint/mempool"
    20  	"github.com/franono/tendermint/rpc/client"
    21  	rpchttp "github.com/franono/tendermint/rpc/client/http"
    22  	rpclocal "github.com/franono/tendermint/rpc/client/local"
    23  	ctypes "github.com/franono/tendermint/rpc/core/types"
    24  	rpcclient "github.com/franono/tendermint/rpc/jsonrpc/client"
    25  	rpctest "github.com/franono/tendermint/rpc/test"
    26  	"github.com/franono/tendermint/types"
    27  )
    28  
    29  func getHTTPClient() *rpchttp.HTTP {
    30  	rpcAddr := rpctest.GetConfig().RPC.ListenAddress
    31  	c, err := rpchttp.New(rpcAddr, "/websocket")
    32  	if err != nil {
    33  		panic(err)
    34  	}
    35  	c.SetLogger(log.TestingLogger())
    36  	return c
    37  }
    38  
    39  func getHTTPClientWithTimeout(timeout uint) *rpchttp.HTTP {
    40  	rpcAddr := rpctest.GetConfig().RPC.ListenAddress
    41  	c, err := rpchttp.NewWithTimeout(rpcAddr, "/websocket", timeout)
    42  	if err != nil {
    43  		panic(err)
    44  	}
    45  	c.SetLogger(log.TestingLogger())
    46  	return c
    47  }
    48  
    49  func getLocalClient() *rpclocal.Local {
    50  	return rpclocal.New(node)
    51  }
    52  
    53  // GetClients returns a slice of clients for table-driven tests
    54  func GetClients() []client.Client {
    55  	return []client.Client{
    56  		getHTTPClient(),
    57  		getLocalClient(),
    58  	}
    59  }
    60  
    61  func TestNilCustomHTTPClient(t *testing.T) {
    62  	require.Panics(t, func() {
    63  		_, _ = rpchttp.NewWithClient("http://example.com", "/websocket", nil)
    64  	})
    65  	require.Panics(t, func() {
    66  		_, _ = rpcclient.NewWithHTTPClient("http://example.com", nil)
    67  	})
    68  }
    69  
    70  func TestCustomHTTPClient(t *testing.T) {
    71  	remote := rpctest.GetConfig().RPC.ListenAddress
    72  	c, err := rpchttp.NewWithClient(remote, "/websocket", http.DefaultClient)
    73  	require.Nil(t, err)
    74  	status, err := c.Status()
    75  	require.NoError(t, err)
    76  	require.NotNil(t, status)
    77  }
    78  
    79  func TestCorsEnabled(t *testing.T) {
    80  	origin := rpctest.GetConfig().RPC.CORSAllowedOrigins[0]
    81  	remote := strings.Replace(rpctest.GetConfig().RPC.ListenAddress, "tcp", "http", -1)
    82  
    83  	req, err := http.NewRequest("GET", remote, nil)
    84  	require.Nil(t, err, "%+v", err)
    85  	req.Header.Set("Origin", origin)
    86  	c := &http.Client{}
    87  	resp, err := c.Do(req)
    88  	require.Nil(t, err, "%+v", err)
    89  	defer resp.Body.Close()
    90  
    91  	assert.Equal(t, resp.Header.Get("Access-Control-Allow-Origin"), origin)
    92  }
    93  
    94  // Make sure status is correct (we connect properly)
    95  func TestStatus(t *testing.T) {
    96  	for i, c := range GetClients() {
    97  		moniker := rpctest.GetConfig().Moniker
    98  		status, err := c.Status()
    99  		require.Nil(t, err, "%d: %+v", i, err)
   100  		assert.Equal(t, moniker, status.NodeInfo.Moniker)
   101  	}
   102  }
   103  
   104  // Make sure info is correct (we connect properly)
   105  func TestInfo(t *testing.T) {
   106  	for i, c := range GetClients() {
   107  		// status, err := c.Status()
   108  		// require.Nil(t, err, "%+v", err)
   109  		info, err := c.ABCIInfo()
   110  		require.Nil(t, err, "%d: %+v", i, err)
   111  		// TODO: this is not correct - fix merkleeyes!
   112  		// assert.EqualValues(t, status.SyncInfo.LatestBlockHeight, info.Response.LastBlockHeight)
   113  		assert.True(t, strings.Contains(info.Response.Data, "size"))
   114  	}
   115  }
   116  
   117  func TestNetInfo(t *testing.T) {
   118  	for i, c := range GetClients() {
   119  		nc, ok := c.(client.NetworkClient)
   120  		require.True(t, ok, "%d", i)
   121  		netinfo, err := nc.NetInfo()
   122  		require.Nil(t, err, "%d: %+v", i, err)
   123  		assert.True(t, netinfo.Listening)
   124  		assert.Equal(t, 0, len(netinfo.Peers))
   125  	}
   126  }
   127  
   128  func TestDumpConsensusState(t *testing.T) {
   129  	for i, c := range GetClients() {
   130  		// FIXME: fix server so it doesn't panic on invalid input
   131  		nc, ok := c.(client.NetworkClient)
   132  		require.True(t, ok, "%d", i)
   133  		cons, err := nc.DumpConsensusState()
   134  		require.Nil(t, err, "%d: %+v", i, err)
   135  		assert.NotEmpty(t, cons.RoundState)
   136  		assert.Empty(t, cons.Peers)
   137  	}
   138  }
   139  
   140  func TestConsensusState(t *testing.T) {
   141  	for i, c := range GetClients() {
   142  		// FIXME: fix server so it doesn't panic on invalid input
   143  		nc, ok := c.(client.NetworkClient)
   144  		require.True(t, ok, "%d", i)
   145  		cons, err := nc.ConsensusState()
   146  		require.Nil(t, err, "%d: %+v", i, err)
   147  		assert.NotEmpty(t, cons.RoundState)
   148  	}
   149  }
   150  
   151  func TestHealth(t *testing.T) {
   152  	for i, c := range GetClients() {
   153  		nc, ok := c.(client.NetworkClient)
   154  		require.True(t, ok, "%d", i)
   155  		_, err := nc.Health()
   156  		require.Nil(t, err, "%d: %+v", i, err)
   157  	}
   158  }
   159  
   160  func TestGenesisAndValidators(t *testing.T) {
   161  	for i, c := range GetClients() {
   162  
   163  		// make sure this is the right genesis file
   164  		gen, err := c.Genesis()
   165  		require.Nil(t, err, "%d: %+v", i, err)
   166  		// get the genesis validator
   167  		require.Equal(t, 1, len(gen.Genesis.Validators))
   168  		gval := gen.Genesis.Validators[0]
   169  
   170  		// get the current validators
   171  		h := int64(1)
   172  		vals, err := c.Validators(&h, 0, 0)
   173  		require.Nil(t, err, "%d: %+v", i, err)
   174  		require.Equal(t, 1, len(vals.Validators))
   175  		require.Equal(t, 1, vals.Count)
   176  		require.Equal(t, 1, vals.Total)
   177  		val := vals.Validators[0]
   178  
   179  		// make sure the current set is also the genesis set
   180  		assert.Equal(t, gval.Power, val.VotingPower)
   181  		assert.Equal(t, gval.PubKey, val.PubKey)
   182  	}
   183  }
   184  
   185  func TestABCIQuery(t *testing.T) {
   186  	for i, c := range GetClients() {
   187  		// write something
   188  		k, v, tx := MakeTxKV()
   189  		bres, err := c.BroadcastTxCommit(tx)
   190  		require.Nil(t, err, "%d: %+v", i, err)
   191  		apph := bres.Height + 1 // this is where the tx will be applied to the state
   192  
   193  		// wait before querying
   194  		client.WaitForHeight(c, apph, nil)
   195  		res, err := c.ABCIQuery("/key", k)
   196  		qres := res.Response
   197  		if assert.Nil(t, err) && assert.True(t, qres.IsOK()) {
   198  			assert.EqualValues(t, v, qres.Value)
   199  		}
   200  	}
   201  }
   202  
   203  // Make some app checks
   204  func TestAppCalls(t *testing.T) {
   205  	assert, require := assert.New(t), require.New(t)
   206  	for i, c := range GetClients() {
   207  
   208  		// get an offset of height to avoid racing and guessing
   209  		s, err := c.Status()
   210  		require.NoError(err)
   211  		// sh is start height or status height
   212  		sh := s.SyncInfo.LatestBlockHeight
   213  
   214  		// look for the future
   215  		h := sh + 20
   216  		_, err = c.Block(&h)
   217  		require.Error(err) // no block yet
   218  
   219  		// write something
   220  		k, v, tx := MakeTxKV()
   221  		bres, err := c.BroadcastTxCommit(tx)
   222  		require.NoError(err)
   223  		require.True(bres.DeliverTx.IsOK())
   224  		txh := bres.Height
   225  		apph := txh + 1 // this is where the tx will be applied to the state
   226  
   227  		// wait before querying
   228  		err = client.WaitForHeight(c, apph, nil)
   229  		require.NoError(err)
   230  
   231  		_qres, err := c.ABCIQueryWithOptions("/key", k, client.ABCIQueryOptions{Prove: false})
   232  		require.NoError(err)
   233  		qres := _qres.Response
   234  		if assert.True(qres.IsOK()) {
   235  			assert.Equal(k, qres.Key)
   236  			assert.EqualValues(v, qres.Value)
   237  		}
   238  
   239  		// make sure we can lookup the tx with proof
   240  		ptx, err := c.Tx(bres.Hash, true)
   241  		require.NoError(err)
   242  		assert.EqualValues(txh, ptx.Height)
   243  		assert.EqualValues(tx, ptx.Tx)
   244  
   245  		// and we can even check the block is added
   246  		block, err := c.Block(&apph)
   247  		require.NoError(err)
   248  		appHash := block.Block.Header.AppHash
   249  		assert.True(len(appHash) > 0)
   250  		assert.EqualValues(apph, block.Block.Header.Height)
   251  
   252  		// now check the results
   253  		blockResults, err := c.BlockResults(&txh)
   254  		require.Nil(err, "%d: %+v", i, err)
   255  		assert.Equal(txh, blockResults.Height)
   256  		if assert.Equal(1, len(blockResults.TxsResults)) {
   257  			// check success code
   258  			assert.EqualValues(0, blockResults.TxsResults[0].Code)
   259  		}
   260  
   261  		// check blockchain info, now that we know there is info
   262  		info, err := c.BlockchainInfo(apph, apph)
   263  		require.NoError(err)
   264  		assert.True(info.LastHeight >= apph)
   265  		if assert.Equal(1, len(info.BlockMetas)) {
   266  			lastMeta := info.BlockMetas[0]
   267  			assert.EqualValues(apph, lastMeta.Header.Height)
   268  			blockData := block.Block
   269  			assert.Equal(blockData.Header.AppHash, lastMeta.Header.AppHash)
   270  			assert.Equal(block.BlockID, lastMeta.BlockID)
   271  		}
   272  
   273  		// and get the corresponding commit with the same apphash
   274  		commit, err := c.Commit(&apph)
   275  		require.NoError(err)
   276  		cappHash := commit.Header.AppHash
   277  		assert.Equal(appHash, cappHash)
   278  		assert.NotNil(commit.Commit)
   279  
   280  		// compare the commits (note Commit(2) has commit from Block(3))
   281  		h = apph - 1
   282  		commit2, err := c.Commit(&h)
   283  		require.NoError(err)
   284  		assert.Equal(block.Block.LastCommit, commit2.Commit)
   285  
   286  		// and we got a proof that works!
   287  		_pres, err := c.ABCIQueryWithOptions("/key", k, client.ABCIQueryOptions{Prove: true})
   288  		require.NoError(err)
   289  		pres := _pres.Response
   290  		assert.True(pres.IsOK())
   291  
   292  		// XXX Test proof
   293  	}
   294  }
   295  
   296  func TestBroadcastTxSync(t *testing.T) {
   297  	require := require.New(t)
   298  
   299  	// TODO (melekes): use mempool which is set on RPC rather than getting it from node
   300  	mempool := node.Mempool()
   301  	initMempoolSize := mempool.Size()
   302  
   303  	for i, c := range GetClients() {
   304  		_, _, tx := MakeTxKV()
   305  		bres, err := c.BroadcastTxSync(tx)
   306  		require.Nil(err, "%d: %+v", i, err)
   307  		require.Equal(bres.Code, abci.CodeTypeOK) // FIXME
   308  
   309  		require.Equal(initMempoolSize+1, mempool.Size())
   310  
   311  		txs := mempool.ReapMaxTxs(len(tx))
   312  		require.EqualValues(tx, txs[0])
   313  		mempool.Flush()
   314  	}
   315  }
   316  
   317  func TestBroadcastTxCommit(t *testing.T) {
   318  	require := require.New(t)
   319  
   320  	mempool := node.Mempool()
   321  	for i, c := range GetClients() {
   322  		_, _, tx := MakeTxKV()
   323  		bres, err := c.BroadcastTxCommit(tx)
   324  		require.Nil(err, "%d: %+v", i, err)
   325  		require.True(bres.CheckTx.IsOK())
   326  		require.True(bres.DeliverTx.IsOK())
   327  
   328  		require.Equal(0, mempool.Size())
   329  	}
   330  }
   331  
   332  func TestUnconfirmedTxs(t *testing.T) {
   333  	_, _, tx := MakeTxKV()
   334  
   335  	ch := make(chan *abci.Response, 1)
   336  	mempool := node.Mempool()
   337  	err := mempool.CheckTx(tx, func(resp *abci.Response) { ch <- resp }, mempl.TxInfo{})
   338  	require.NoError(t, err)
   339  
   340  	// wait for tx to arrive in mempoool.
   341  	select {
   342  	case <-ch:
   343  	case <-time.After(5 * time.Second):
   344  		t.Error("Timed out waiting for CheckTx callback")
   345  	}
   346  
   347  	for _, c := range GetClients() {
   348  		mc := c.(client.MempoolClient)
   349  		res, err := mc.UnconfirmedTxs(1)
   350  		require.NoError(t, err)
   351  
   352  		assert.Equal(t, 1, res.Count)
   353  		assert.Equal(t, 1, res.Total)
   354  		assert.Equal(t, mempool.TxsBytes(), res.TotalBytes)
   355  		assert.Exactly(t, types.Txs{tx}, types.Txs(res.Txs))
   356  	}
   357  
   358  	mempool.Flush()
   359  }
   360  
   361  func TestNumUnconfirmedTxs(t *testing.T) {
   362  	_, _, tx := MakeTxKV()
   363  
   364  	ch := make(chan *abci.Response, 1)
   365  	mempool := node.Mempool()
   366  	err := mempool.CheckTx(tx, func(resp *abci.Response) { ch <- resp }, mempl.TxInfo{})
   367  	require.NoError(t, err)
   368  
   369  	// wait for tx to arrive in mempoool.
   370  	select {
   371  	case <-ch:
   372  	case <-time.After(5 * time.Second):
   373  		t.Error("Timed out waiting for CheckTx callback")
   374  	}
   375  
   376  	mempoolSize := mempool.Size()
   377  	for i, c := range GetClients() {
   378  		mc, ok := c.(client.MempoolClient)
   379  		require.True(t, ok, "%d", i)
   380  		res, err := mc.NumUnconfirmedTxs()
   381  		require.Nil(t, err, "%d: %+v", i, err)
   382  
   383  		assert.Equal(t, mempoolSize, res.Count)
   384  		assert.Equal(t, mempoolSize, res.Total)
   385  		assert.Equal(t, mempool.TxsBytes(), res.TotalBytes)
   386  	}
   387  
   388  	mempool.Flush()
   389  }
   390  
   391  func TestTx(t *testing.T) {
   392  	// first we broadcast a tx
   393  	c := getHTTPClient()
   394  	_, _, tx := MakeTxKV()
   395  	bres, err := c.BroadcastTxCommit(tx)
   396  	require.Nil(t, err, "%+v", err)
   397  
   398  	txHeight := bres.Height
   399  	txHash := bres.Hash
   400  
   401  	anotherTxHash := types.Tx("a different tx").Hash()
   402  
   403  	cases := []struct {
   404  		valid bool
   405  		prove bool
   406  		hash  []byte
   407  	}{
   408  		// only valid if correct hash provided
   409  		{true, false, txHash},
   410  		{true, true, txHash},
   411  		{false, false, anotherTxHash},
   412  		{false, true, anotherTxHash},
   413  		{false, false, nil},
   414  		{false, true, nil},
   415  	}
   416  
   417  	for i, c := range GetClients() {
   418  		for j, tc := range cases {
   419  			t.Logf("client %d, case %d", i, j)
   420  
   421  			// now we query for the tx.
   422  			// since there's only one tx, we know index=0.
   423  			ptx, err := c.Tx(tc.hash, tc.prove)
   424  
   425  			if !tc.valid {
   426  				require.NotNil(t, err)
   427  			} else {
   428  				require.Nil(t, err, "%+v", err)
   429  				assert.EqualValues(t, txHeight, ptx.Height)
   430  				assert.EqualValues(t, tx, ptx.Tx)
   431  				assert.Zero(t, ptx.Index)
   432  				assert.True(t, ptx.TxResult.IsOK())
   433  				assert.EqualValues(t, txHash, ptx.Hash)
   434  
   435  				// time to verify the proof
   436  				proof := ptx.Proof
   437  				if tc.prove && assert.EqualValues(t, tx, proof.Data) {
   438  					assert.NoError(t, proof.Proof.Verify(proof.RootHash, txHash))
   439  				}
   440  			}
   441  		}
   442  	}
   443  }
   444  
   445  func TestTxSearchWithTimeout(t *testing.T) {
   446  	// Get a client with a time-out of 10 secs.
   447  	timeoutClient := getHTTPClientWithTimeout(10)
   448  
   449  	// query using a compositeKey (see kvstore application)
   450  	result, err := timeoutClient.TxSearch("app.creator='Cosmoshi Netowoko'", false, 1, 30, "asc")
   451  	require.Nil(t, err)
   452  	if len(result.Txs) == 0 {
   453  		t.Fatal("expected a lot of transactions")
   454  	}
   455  }
   456  
   457  func TestTxSearch(t *testing.T) {
   458  	c := getHTTPClient()
   459  
   460  	// first we broadcast a few txs
   461  	for i := 0; i < 10; i++ {
   462  		_, _, tx := MakeTxKV()
   463  		_, err := c.BroadcastTxCommit(tx)
   464  		require.NoError(t, err)
   465  	}
   466  
   467  	// since we're not using an isolated test server, we'll have lingering transactions
   468  	// from other tests as well
   469  	result, err := c.TxSearch("tx.height >= 0", true, 1, 100, "asc")
   470  	require.NoError(t, err)
   471  	txCount := len(result.Txs)
   472  
   473  	// pick out the last tx to have something to search for in tests
   474  	find := result.Txs[len(result.Txs)-1]
   475  	anotherTxHash := types.Tx("a different tx").Hash()
   476  
   477  	for i, c := range GetClients() {
   478  		t.Logf("client %d", i)
   479  
   480  		// now we query for the tx.
   481  		result, err := c.TxSearch(fmt.Sprintf("tx.hash='%v'", find.Hash), true, 1, 30, "asc")
   482  		require.Nil(t, err)
   483  		require.Len(t, result.Txs, 1)
   484  		require.Equal(t, find.Hash, result.Txs[0].Hash)
   485  
   486  		ptx := result.Txs[0]
   487  		assert.EqualValues(t, find.Height, ptx.Height)
   488  		assert.EqualValues(t, find.Tx, ptx.Tx)
   489  		assert.Zero(t, ptx.Index)
   490  		assert.True(t, ptx.TxResult.IsOK())
   491  		assert.EqualValues(t, find.Hash, ptx.Hash)
   492  
   493  		// time to verify the proof
   494  		if assert.EqualValues(t, find.Tx, ptx.Proof.Data) {
   495  			assert.NoError(t, ptx.Proof.Proof.Verify(ptx.Proof.RootHash, find.Hash))
   496  		}
   497  
   498  		// query by height
   499  		result, err = c.TxSearch(fmt.Sprintf("tx.height=%d", find.Height), true, 1, 30, "asc")
   500  		require.Nil(t, err)
   501  		require.Len(t, result.Txs, 1)
   502  
   503  		// query for non existing tx
   504  		result, err = c.TxSearch(fmt.Sprintf("tx.hash='%X'", anotherTxHash), false, 1, 30, "asc")
   505  		require.Nil(t, err)
   506  		require.Len(t, result.Txs, 0)
   507  
   508  		// query using a compositeKey (see kvstore application)
   509  		result, err = c.TxSearch("app.creator='Cosmoshi Netowoko'", false, 1, 30, "asc")
   510  		require.Nil(t, err)
   511  		if len(result.Txs) == 0 {
   512  			t.Fatal("expected a lot of transactions")
   513  		}
   514  
   515  		// query using an index key
   516  		result, err = c.TxSearch("app.index_key='index is working'", false, 1, 30, "asc")
   517  		require.Nil(t, err)
   518  		if len(result.Txs) == 0 {
   519  			t.Fatal("expected a lot of transactions")
   520  		}
   521  
   522  		// query using an noindex key
   523  		result, err = c.TxSearch("app.noindex_key='index is working'", false, 1, 30, "asc")
   524  		require.Nil(t, err)
   525  		if len(result.Txs) != 0 {
   526  			t.Fatal("expected no transaction")
   527  		}
   528  
   529  		// query using a compositeKey (see kvstore application) and height
   530  		result, err = c.TxSearch("app.creator='Cosmoshi Netowoko' AND tx.height<10000", true, 1, 30, "asc")
   531  		require.Nil(t, err)
   532  		if len(result.Txs) == 0 {
   533  			t.Fatal("expected a lot of transactions")
   534  		}
   535  
   536  		// query a non existing tx with page 1 and txsPerPage 1
   537  		result, err = c.TxSearch("app.creator='Cosmoshi Neetowoko'", true, 1, 1, "asc")
   538  		require.Nil(t, err)
   539  		require.Len(t, result.Txs, 0)
   540  
   541  		// check sorting
   542  		result, err = c.TxSearch("tx.height >= 1", false, 1, 30, "asc")
   543  		require.Nil(t, err)
   544  		for k := 0; k < len(result.Txs)-1; k++ {
   545  			require.LessOrEqual(t, result.Txs[k].Height, result.Txs[k+1].Height)
   546  			require.LessOrEqual(t, result.Txs[k].Index, result.Txs[k+1].Index)
   547  		}
   548  
   549  		result, err = c.TxSearch("tx.height >= 1", false, 1, 30, "desc")
   550  		require.Nil(t, err)
   551  		for k := 0; k < len(result.Txs)-1; k++ {
   552  			require.GreaterOrEqual(t, result.Txs[k].Height, result.Txs[k+1].Height)
   553  			require.GreaterOrEqual(t, result.Txs[k].Index, result.Txs[k+1].Index)
   554  		}
   555  
   556  		// check pagination
   557  		var (
   558  			seen      = map[int64]bool{}
   559  			maxHeight int64
   560  			perPage   = 3
   561  			pages     = int(math.Ceil(float64(txCount) / float64(perPage)))
   562  		)
   563  		for page := 1; page <= pages; page++ {
   564  			result, err = c.TxSearch("tx.height >= 1", false, page, perPage, "asc")
   565  			require.NoError(t, err)
   566  			if page < pages {
   567  				require.Len(t, result.Txs, perPage)
   568  			} else {
   569  				require.LessOrEqual(t, len(result.Txs), perPage)
   570  			}
   571  			require.Equal(t, txCount, result.TotalCount)
   572  			for _, tx := range result.Txs {
   573  				require.False(t, seen[tx.Height],
   574  					"Found duplicate height %v in page %v", tx.Height, page)
   575  				require.Greater(t, tx.Height, maxHeight,
   576  					"Found decreasing height %v (max seen %v) in page %v", tx.Height, maxHeight, page)
   577  				seen[tx.Height] = true
   578  				maxHeight = tx.Height
   579  			}
   580  		}
   581  		require.Len(t, seen, txCount)
   582  	}
   583  }
   584  
   585  func TestBatchedJSONRPCCalls(t *testing.T) {
   586  	c := getHTTPClient()
   587  	testBatchedJSONRPCCalls(t, c)
   588  }
   589  
   590  func testBatchedJSONRPCCalls(t *testing.T, c *rpchttp.HTTP) {
   591  	k1, v1, tx1 := MakeTxKV()
   592  	k2, v2, tx2 := MakeTxKV()
   593  
   594  	batch := c.NewBatch()
   595  	r1, err := batch.BroadcastTxCommit(tx1)
   596  	require.NoError(t, err)
   597  	r2, err := batch.BroadcastTxCommit(tx2)
   598  	require.NoError(t, err)
   599  	require.Equal(t, 2, batch.Count())
   600  	bresults, err := batch.Send()
   601  	require.NoError(t, err)
   602  	require.Len(t, bresults, 2)
   603  	require.Equal(t, 0, batch.Count())
   604  
   605  	bresult1, ok := bresults[0].(*ctypes.ResultBroadcastTxCommit)
   606  	require.True(t, ok)
   607  	require.Equal(t, *bresult1, *r1)
   608  	bresult2, ok := bresults[1].(*ctypes.ResultBroadcastTxCommit)
   609  	require.True(t, ok)
   610  	require.Equal(t, *bresult2, *r2)
   611  	apph := tmmath.MaxInt64(bresult1.Height, bresult2.Height) + 1
   612  
   613  	client.WaitForHeight(c, apph, nil)
   614  
   615  	q1, err := batch.ABCIQuery("/key", k1)
   616  	require.NoError(t, err)
   617  	q2, err := batch.ABCIQuery("/key", k2)
   618  	require.NoError(t, err)
   619  	require.Equal(t, 2, batch.Count())
   620  	qresults, err := batch.Send()
   621  	require.NoError(t, err)
   622  	require.Len(t, qresults, 2)
   623  	require.Equal(t, 0, batch.Count())
   624  
   625  	qresult1, ok := qresults[0].(*ctypes.ResultABCIQuery)
   626  	require.True(t, ok)
   627  	require.Equal(t, *qresult1, *q1)
   628  	qresult2, ok := qresults[1].(*ctypes.ResultABCIQuery)
   629  	require.True(t, ok)
   630  	require.Equal(t, *qresult2, *q2)
   631  
   632  	require.Equal(t, qresult1.Response.Key, k1)
   633  	require.Equal(t, qresult2.Response.Key, k2)
   634  	require.Equal(t, qresult1.Response.Value, v1)
   635  	require.Equal(t, qresult2.Response.Value, v2)
   636  }
   637  
   638  func TestBatchedJSONRPCCallsCancellation(t *testing.T) {
   639  	c := getHTTPClient()
   640  	_, _, tx1 := MakeTxKV()
   641  	_, _, tx2 := MakeTxKV()
   642  
   643  	batch := c.NewBatch()
   644  	_, err := batch.BroadcastTxCommit(tx1)
   645  	require.NoError(t, err)
   646  	_, err = batch.BroadcastTxCommit(tx2)
   647  	require.NoError(t, err)
   648  	// we should have 2 requests waiting
   649  	require.Equal(t, 2, batch.Count())
   650  	// we want to make sure we cleared 2 pending requests
   651  	require.Equal(t, 2, batch.Clear())
   652  	// now there should be no batched requests
   653  	require.Equal(t, 0, batch.Count())
   654  }
   655  
   656  func TestSendingEmptyRequestBatch(t *testing.T) {
   657  	c := getHTTPClient()
   658  	batch := c.NewBatch()
   659  	_, err := batch.Send()
   660  	require.Error(t, err, "sending an empty batch of JSON RPC requests should result in an error")
   661  }
   662  
   663  func TestClearingEmptyRequestBatch(t *testing.T) {
   664  	c := getHTTPClient()
   665  	batch := c.NewBatch()
   666  	require.Zero(t, batch.Clear(), "clearing an empty batch of JSON RPC requests should result in a 0 result")
   667  }
   668  
   669  func TestConcurrentJSONRPCBatching(t *testing.T) {
   670  	var wg sync.WaitGroup
   671  	c := getHTTPClient()
   672  	for i := 0; i < 50; i++ {
   673  		wg.Add(1)
   674  		go func() {
   675  			defer wg.Done()
   676  			testBatchedJSONRPCCalls(t, c)
   677  		}()
   678  	}
   679  	wg.Wait()
   680  }