add MempoolStream unit test

This commit is contained in:
Larry Ruane 2021-07-26 08:45:12 -06:00
parent cd98e6a14b
commit 98585ae464
3 changed files with 161 additions and 3 deletions

View File

@ -114,7 +114,8 @@ type (
}
}
// zcashd rpc "getrawtransaction"
// zcashd rpc "getrawtransaction txid 1" (1 means verbose), there are
// many more fields but these are the only ones we current need.
ZcashdRpcReplyGetrawtransaction struct {
Hex string
Height int

View File

@ -5,6 +5,7 @@ package common
import (
"bufio"
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
@ -418,3 +419,158 @@ func TestGenerateCerts(t *testing.T) {
t.Fatal("GenerateCerts returned nil")
}
}
// ------------------------------------------ GetMempoolStream
// Note that in mocking zcashd's RPC replies here, we don't really need
// actual txids or transactions, or even strings with the correct format
// for those, except that a transaction must be a hex string.
func mempoolStub(method string, params []json.RawMessage) (json.RawMessage, error) {
step++
switch step {
case 1:
// This will be a getblockchaininfo request
if method != "getblockchaininfo" {
testT.Fatal("expecting blockchaininfo")
}
r, _ := json.Marshal(&ZcashdRpcReplyGetblockchaininfo{
BestBlockHash: "010203",
Blocks: 200,
})
return r, nil
case 2:
// No new block has arrived.
if method != "getblockchaininfo" {
testT.Fatal("expecting blockchaininfo")
}
r, _ := json.Marshal(&ZcashdRpcReplyGetblockchaininfo{
BestBlockHash: "010203",
Blocks: 200,
})
return r, nil
case 3:
// Expect a getrawmempool next.
if method != "getrawmempool" {
testT.Fatal("expecting getrawmempool")
}
// In reality, this would be a hex txid
r, _ := json.Marshal([]string{
"mempooltxid-1",
})
return r, nil
case 4:
// Next, it should ask for this transaction (non-verbose).
if method != "getrawtransaction" {
testT.Fatal("expecting getrawtransaction")
}
var txid string
json.Unmarshal(params[0], &txid)
if txid != "mempooltxid-1" {
testT.Fatal("unexpected txid")
}
r, _ := json.Marshal("aabb")
return r, nil
case 5:
// Simulate that still no new block has arrived ...
if method != "getblockchaininfo" {
testT.Fatal("expecting blockchaininfo")
}
r, _ := json.Marshal(&ZcashdRpcReplyGetblockchaininfo{
BestBlockHash: "010203",
Blocks: 200,
})
return r, nil
case 6:
// ... but there a second tx has arrived in the mempool
if method != "getrawmempool" {
testT.Fatal("expecting getrawmempool")
}
// In reality, this would be a hex txid
r, _ := json.Marshal([]string{
"mempooltxid-2",
"mempooltxid-1"})
return r, nil
case 7:
// The new mempool tx (and only that one) gets fetched
if method != "getrawtransaction" {
testT.Fatal("expecting getrawtransaction")
}
var txid string
json.Unmarshal(params[0], &txid)
if txid != "mempooltxid-2" {
testT.Fatal("unexpected txid")
}
r, _ := json.Marshal("ccdd")
return r, nil
case 8:
// A new block arrives, this will cause these two tx to be returned
if method != "getblockchaininfo" {
testT.Fatal("expecting blockchaininfo")
}
r, _ := json.Marshal(&ZcashdRpcReplyGetblockchaininfo{
BestBlockHash: "d1d2d3",
Blocks: 201,
})
return r, nil
}
testT.Fatal("ran out of cases")
return nil, nil
}
func TestMempoolStream(t *testing.T) {
testT = t
RawRequest = mempoolStub
Time.Sleep = sleepStub
Time.Now = nowStub
// In real life, wall time is not close to zero, simulate that.
sleepDuration = 1000 * time.Second
var replies []*walletrpc.RawTransaction
// The first request after startup immediately returns an empty list.
err := GetMempool(func(tx *walletrpc.RawTransaction) error {
t.Fatal("send to client function called on initial GetMempool call")
return nil
})
if err != nil {
t.Fatal("GetMempool failed")
}
// This should return two transactions.
err = GetMempool(func(tx *walletrpc.RawTransaction) error {
replies = append(replies, tx)
return nil
})
if err != nil {
t.Fatal("GetMempool failed")
}
if len(replies) != 2 {
t.Fatal("unexpected number of tx")
}
// The interface guarantees that the transactions will be returned
// in the order they entered the mempool.
if !bytes.Equal([]byte(replies[0].GetData()), []byte{0xaa, 0xbb}) {
t.Fatal("unexpected tx contents")
}
if replies[0].GetHeight() != 200 {
t.Fatal("unexpected tx height")
}
if !bytes.Equal([]byte(replies[1].GetData()), []byte{0xcc, 0xdd}) {
t.Fatal("unexpected tx contents")
}
if replies[1].GetHeight() != 200 {
t.Fatal("unexpected tx height")
}
// Time started at 1000 seconds (since 1970), and just over 4 seconds
// should have elapsed. The units here are nanoseconds.
if sleepDuration != 1004400000000 {
t.Fatal("unexpected end time")
}
if step != 8 {
t.Fatal("unexpected number of zcashd RPCs")
}
step = 0
sleepCount = 0
sleepDuration = 0
}

View File

@ -44,7 +44,8 @@ func GetMempool(sendToClient func(*walletrpc.RawTransaction) error) error {
// Wait for more transactions to be added to the list
for {
// Don't fetch the mempool more often than every 2 seconds.
if time.Since(g_lastTime) > 2*time.Second {
now := Time.Now()
if now.After(g_lastTime.Add(2 * time.Second)) {
blockChainInfo, err := getLatestBlockChainInfo()
if err != nil {
g_lock.Unlock()
@ -64,7 +65,7 @@ func GetMempool(sendToClient func(*walletrpc.RawTransaction) error) error {
g_lock.Unlock()
return err
}
g_lastTime = Time.Now()
g_lastTime = now
}
// Send transactions we haven't sent yet, best to not do so while
// holding the mutex, since this call may get flow-controlled.