github.com/status-im/status-go@v1.1.0/services/wallet/transfer/concurrent_test.go (about)

     1  package transfer
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"math/big"
     7  	"sort"
     8  	"testing"
     9  	"time"
    10  
    11  	"github.com/status-im/status-go/services/wallet/balance"
    12  
    13  	"github.com/stretchr/testify/require"
    14  
    15  	"github.com/ethereum/go-ethereum/core/types"
    16  
    17  	"github.com/ethereum/go-ethereum/common"
    18  )
    19  
    20  func TestConcurrentErrorInterrupts(t *testing.T) {
    21  	concurrent := NewConcurrentDownloader(context.Background(), NoThreadLimit)
    22  	var interrupted bool
    23  	concurrent.Add(func(ctx context.Context) error {
    24  		select {
    25  		case <-ctx.Done():
    26  			interrupted = true
    27  		case <-time.After(10 * time.Second):
    28  		}
    29  		return nil
    30  	})
    31  	err := errors.New("interrupt")
    32  	concurrent.Add(func(ctx context.Context) error {
    33  		return err
    34  	})
    35  	concurrent.Wait()
    36  	require.True(t, interrupted)
    37  	require.Equal(t, err, concurrent.Error())
    38  }
    39  
    40  func TestConcurrentCollectsTransfers(t *testing.T) {
    41  	concurrent := NewConcurrentDownloader(context.Background(), NoThreadLimit)
    42  	concurrent.Add(func(context.Context) error {
    43  		concurrent.Push(Transfer{})
    44  		return nil
    45  	})
    46  	concurrent.Add(func(context.Context) error {
    47  		concurrent.Push(Transfer{})
    48  		return nil
    49  	})
    50  	concurrent.Wait()
    51  	require.Len(t, concurrent.Get(), 2)
    52  }
    53  
    54  type balancesFixture []*big.Int
    55  
    56  func (f balancesFixture) BalanceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (*big.Int, error) {
    57  	index := int(blockNumber.Int64())
    58  	if index > len(f)-1 {
    59  		return nil, errors.New("balance unknown")
    60  	}
    61  	return f[index], nil
    62  }
    63  
    64  func (f balancesFixture) NonceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error) {
    65  	return uint64(0), nil
    66  }
    67  
    68  func (f balancesFixture) HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) {
    69  	return &types.Header{
    70  		Number: number,
    71  	}, nil
    72  }
    73  
    74  func (f balancesFixture) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) {
    75  	return &types.Header{
    76  		Number: big.NewInt(0),
    77  	}, nil
    78  }
    79  
    80  func (f balancesFixture) NetworkID() uint64 {
    81  	return 0
    82  }
    83  
    84  func (f balancesFixture) CallBlockHashByTransaction(ctx context.Context, blockNumber *big.Int, index uint) (common.Hash, error) {
    85  	return common.HexToHash("0x0"), nil
    86  }
    87  
    88  type batchesFixture [][]Transfer
    89  
    90  func (f batchesFixture) GetTransfersByNumber(ctx context.Context, number *big.Int) (rst []Transfer, err error) {
    91  	index := int(number.Int64())
    92  	if index > len(f)-1 {
    93  		return nil, errors.New("unknown block")
    94  	}
    95  	return f[index], nil
    96  }
    97  
    98  func TestConcurrentEthDownloader(t *testing.T) {
    99  	type options struct {
   100  		balances balancesFixture
   101  		batches  batchesFixture
   102  		result   []DBHeader
   103  		last     *big.Int
   104  	}
   105  	type testCase struct {
   106  		desc    string
   107  		options options
   108  	}
   109  	for _, tc := range []testCase{
   110  		{
   111  			desc: "NoBalances",
   112  			options: options{
   113  				last:     big.NewInt(3),
   114  				balances: balancesFixture{big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0)},
   115  			},
   116  		},
   117  		{
   118  			desc: "LastBlock",
   119  			options: options{
   120  				last:     big.NewInt(3),
   121  				balances: balancesFixture{big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(10)},
   122  				batches:  batchesFixture{{}, {}, {}, {{BlockNumber: big.NewInt(3)}, {BlockNumber: big.NewInt(3)}}},
   123  				result:   []DBHeader{{Number: big.NewInt(3)}},
   124  			},
   125  		},
   126  		{
   127  			desc: "ChangesInEveryBlock",
   128  			options: options{
   129  				last:     big.NewInt(3),
   130  				balances: balancesFixture{big.NewInt(0), big.NewInt(3), big.NewInt(7), big.NewInt(10)},
   131  				batches:  batchesFixture{{}, {{BlockNumber: big.NewInt(1)}}, {{BlockNumber: big.NewInt(2)}}, {{BlockNumber: big.NewInt(3)}}},
   132  				result:   []DBHeader{{Number: big.NewInt(1)}, {Number: big.NewInt(2)}, {Number: big.NewInt(3)}},
   133  			},
   134  		},
   135  	} {
   136  		t.Run(tc.desc, func(t *testing.T) {
   137  			ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
   138  			defer cancel()
   139  			concurrent := NewConcurrentDownloader(ctx, 0)
   140  			_, headers, _, _ := findBlocksWithEthTransfers(
   141  				ctx, tc.options.balances, balance.NewCacherWithTTL(5*time.Minute),
   142  				common.Address{}, zero, tc.options.last, false, NoThreadLimit)
   143  			concurrent.Wait()
   144  			require.NoError(t, concurrent.Error())
   145  			rst := concurrent.Get()
   146  			require.Len(t, headers, len(tc.options.result))
   147  			sort.Slice(rst, func(i, j int) bool {
   148  				return rst[i].BlockNumber.Cmp(rst[j].BlockNumber) < 0
   149  			})
   150  			/*for i := range rst {
   151  				require.Equal(t, tc.options.result[i].BlockNumber, rst[i].BlockNumber)
   152  			}*/
   153  		})
   154  	}
   155  }