github.com/status-im/status-go@v1.1.0/services/rpcfilters/latest_logs_test.go (about)

     1  package rpcfilters
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"math/big"
     7  	"sync"
     8  	"testing"
     9  	"time"
    10  
    11  	"github.com/stretchr/testify/assert"
    12  	"github.com/stretchr/testify/require"
    13  
    14  	ethereum "github.com/ethereum/go-ethereum"
    15  	"github.com/ethereum/go-ethereum/common"
    16  	"github.com/ethereum/go-ethereum/common/hexutil"
    17  	"github.com/ethereum/go-ethereum/core/types"
    18  )
    19  
    20  type callTracker struct {
    21  	mu       sync.Mutex
    22  	calls    int
    23  	reply    [][]types.Log
    24  	criteria []map[string]interface{}
    25  }
    26  
    27  func (c *callTracker) CallContext(ctx context.Context, result interface{}, chainID uint64, method string, args ...interface{}) error {
    28  	c.mu.Lock()
    29  	defer c.mu.Unlock()
    30  	c.calls++
    31  	if len(args) != 1 {
    32  		return errors.New("unexpected length of args")
    33  	}
    34  	crit := args[0].(map[string]interface{})
    35  	c.criteria = append(c.criteria, crit)
    36  	select {
    37  	case <-ctx.Done():
    38  		return errors.New("context canceled")
    39  	default:
    40  	}
    41  	if c.calls <= len(c.reply) {
    42  		rst := result.(*[]types.Log)
    43  		*rst = c.reply[c.calls-1]
    44  	}
    45  	return nil
    46  }
    47  
    48  func runLogsFetcherTest(t *testing.T, f *logsFilter, replies [][]types.Log, queries int) *callTracker {
    49  	c := callTracker{reply: replies}
    50  	var wg sync.WaitGroup
    51  	wg.Add(1)
    52  	go func() {
    53  		pollLogs(&c, 1, f, time.Second, 100*time.Millisecond)
    54  		wg.Done()
    55  	}()
    56  	tick := time.Tick(10 * time.Millisecond)
    57  	after := time.After(time.Second)
    58  	func() {
    59  		for {
    60  			select {
    61  			case <-after:
    62  				f.stop()
    63  				assert.FailNow(t, "failed waiting for requests")
    64  				return
    65  			case <-tick:
    66  				c.mu.Lock()
    67  				num := c.calls
    68  				c.mu.Unlock()
    69  				if num >= queries {
    70  					f.stop()
    71  					return
    72  				}
    73  			}
    74  		}
    75  	}()
    76  	wg.Wait()
    77  	require.Len(t, c.criteria, queries)
    78  	return &c
    79  }
    80  
    81  func TestLogsFetcherAdjusted(t *testing.T) {
    82  	f := &logsFilter{
    83  		ctx: context.TODO(),
    84  		crit: ethereum.FilterQuery{
    85  			FromBlock: big.NewInt(10),
    86  		},
    87  		done:      make(chan struct{}),
    88  		logsCache: newCache(defaultCacheSize),
    89  	}
    90  	logs := []types.Log{
    91  		{BlockNumber: 11}, {BlockNumber: 12},
    92  	}
    93  	c := runLogsFetcherTest(t, f, [][]types.Log{logs}, 2)
    94  	require.Equal(t, hexutil.EncodeBig(big.NewInt(10)), c.criteria[0]["fromBlock"])
    95  	require.Equal(t, c.criteria[1]["fromBlock"], "latest")
    96  }
    97  
    98  func TestAdjustedDueToReorg(t *testing.T) {
    99  	f := &logsFilter{
   100  		ctx: context.TODO(),
   101  		crit: ethereum.FilterQuery{
   102  			FromBlock: big.NewInt(10),
   103  		},
   104  		done:      make(chan struct{}),
   105  		logsCache: newCache(defaultCacheSize),
   106  	}
   107  	logs := []types.Log{
   108  		{BlockNumber: 11, BlockHash: common.Hash{1}}, {BlockNumber: 12, BlockHash: common.Hash{2}},
   109  	}
   110  	reorg := []types.Log{
   111  		{BlockNumber: 12, BlockHash: common.Hash{2, 2}},
   112  	}
   113  	c := runLogsFetcherTest(t, f, [][]types.Log{logs, reorg}, 3)
   114  	require.Equal(t, hexutil.EncodeBig(big.NewInt(10)), c.criteria[0]["fromBlock"])
   115  	require.Equal(t, "latest", c.criteria[1]["fromBlock"])
   116  	require.Equal(t, hexutil.EncodeBig(big.NewInt(11)), c.criteria[2]["fromBlock"])
   117  }
   118  
   119  func TestLogsFetcherCanceledContext(t *testing.T) {
   120  	ctx, cancel := context.WithCancel(context.Background())
   121  	f := &logsFilter{
   122  		ctx: ctx,
   123  		crit: ethereum.FilterQuery{
   124  			FromBlock: big.NewInt(10),
   125  		},
   126  		done:      make(chan struct{}),
   127  		logsCache: newCache(defaultCacheSize),
   128  	}
   129  	cancel()
   130  	c := runLogsFetcherTest(t, f, [][]types.Log{make([]types.Log, 2)}, 2)
   131  	require.Equal(t, hexutil.EncodeBig(big.NewInt(10)), c.criteria[0]["fromBlock"])
   132  	require.Equal(t, hexutil.EncodeBig(big.NewInt(10)), c.criteria[1]["fromBlock"])
   133  }