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 }