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 }