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