status-go/transactions/testhelpers.go

140 lines
3.8 KiB
Go

package transactions
import (
"context"
"fmt"
"math/big"
"testing"
eth "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/rpc"
"github.com/status-im/status-go/rpc/chain"
"github.com/status-im/status-go/services/wallet/bigint"
"github.com/status-im/status-go/services/wallet/common"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
)
type MockETHClient struct {
mock.Mock
}
func (m *MockETHClient) BatchCallContext(ctx context.Context, b []rpc.BatchElem) error {
args := m.Called(ctx, b)
return args.Error(0)
}
type MockChainClient struct {
mock.Mock
Clients map[common.ChainID]*MockETHClient
}
func NewMockChainClient() *MockChainClient {
return &MockChainClient{
Clients: make(map[common.ChainID]*MockETHClient),
}
}
func (m *MockChainClient) SetAvailableClients(chainIDs []common.ChainID) *MockChainClient {
for _, chainID := range chainIDs {
if _, ok := m.Clients[chainID]; !ok {
m.Clients[chainID] = new(MockETHClient)
}
}
return m
}
func (m *MockChainClient) AbstractEthClient(chainID common.ChainID) (chain.BatchCallClient, error) {
if _, ok := m.Clients[chainID]; !ok {
panic(fmt.Sprintf("no mock client for chainID %d", chainID))
}
return m.Clients[chainID], nil
}
func GenerateTestPendingTransactions(start int, count int) []PendingTransaction {
if count > 127 {
panic("can't generate more than 127 distinct transactions")
}
txs := make([]PendingTransaction, count)
for i := start; i < count; i++ {
txs[i] = PendingTransaction{
Hash: eth.HexToHash(fmt.Sprintf("0x1%d", i)),
From: eth.HexToAddress(fmt.Sprintf("0x2%d", i)),
To: eth.HexToAddress(fmt.Sprintf("0x3%d", i)),
Type: RegisterENS,
AdditionalData: "someuser.stateofus.eth",
Value: bigint.BigInt{Int: big.NewInt(int64(i))},
GasLimit: bigint.BigInt{Int: big.NewInt(21000)},
GasPrice: bigint.BigInt{Int: big.NewInt(int64(i))},
ChainID: 777,
Status: new(TxStatus),
AutoDelete: new(bool),
Symbol: "ETH",
Timestamp: uint64(i),
}
*txs[i].Status = Pending // set to pending by default
*txs[i].AutoDelete = true // set to true by default
}
return txs
}
type TestTxSummary struct {
failStatus bool
DontConfirm bool
// Timestamp will be used to mock the Timestamp if greater than 0
Timestamp int
}
func MockTestTransactions(t *testing.T, chainClient *MockChainClient, testTxs []TestTxSummary) []PendingTransaction {
txs := GenerateTestPendingTransactions(0, len(testTxs))
for txIdx := range txs {
tx := &txs[txIdx]
if testTxs[txIdx].Timestamp > 0 {
tx.Timestamp = uint64(testTxs[txIdx].Timestamp)
}
// Mock the first call to getTransactionByHash
chainClient.SetAvailableClients([]common.ChainID{tx.ChainID})
cl := chainClient.Clients[tx.ChainID]
call := cl.On("BatchCallContext", mock.Anything, mock.MatchedBy(func(b []rpc.BatchElem) bool {
ok := len(b) == len(testTxs)
for i := range b {
ok = ok && b[i].Method == GetTransactionReceiptRPCName && b[i].Args[0] == tx.Hash
}
return ok
})).Return(nil)
if testTxs[txIdx].DontConfirm {
call = call.Times(0)
} else {
call = call.Once()
}
call.Run(func(args mock.Arguments) {
elems := args.Get(1).([]rpc.BatchElem)
for i := range elems {
receiptWrapper, ok := elems[i].Result.(*nullableReceipt)
require.True(t, ok)
require.NotNil(t, receiptWrapper)
// Simulate parsing of eth_getTransactionReceipt response
if !testTxs[i].DontConfirm {
status := types.ReceiptStatusSuccessful
if testTxs[i].failStatus {
status = types.ReceiptStatusFailed
}
receiptWrapper.Receipt = &types.Receipt{
BlockNumber: new(big.Int).SetUint64(1),
Status: status,
}
}
}
})
}
return txs
}