github.com/TrueBlocks/trueblocks-core/src/apps/chifra@v0.0.0-20241022031540-b362680128f7/pkg/rpc/provider/covalent_test.go (about)

     1  package provider
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"net/http"
     7  	"net/http/httptest"
     8  	"strings"
     9  	"testing"
    10  	"time"
    11  
    12  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/base"
    13  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/identifiers"
    14  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/types"
    15  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/utils"
    16  	"golang.org/x/time/rate"
    17  )
    18  
    19  func TestCovalentProvider_url(t *testing.T) {
    20  	paginator := NewPageNumberPaginator(0, 0, 0)
    21  	provider := CovalentProvider{
    22  		chain:   "mainnet",
    23  		apiKey:  "fake",
    24  		baseUrl: covalentBaseUrl,
    25  	}
    26  	var err error
    27  	var result string
    28  	var expected string
    29  	var pageNumber int
    30  	var ok bool
    31  
    32  	pageNumber, ok = paginator.Page().(int)
    33  	if !ok {
    34  		t.Fatal("cannot cast page to int")
    35  	}
    36  
    37  	result, err = provider.url(
    38  		base.HexToAddress("0xf503017d7baf7fbc0fff7492b751025c6a78179b"),
    39  		pageNumber,
    40  	)
    41  	if err != nil {
    42  		t.Fatal(err)
    43  	}
    44  
    45  	expected = "https://api.covalenthq.com/v1/eth-mainnet/address/0xf503017d7baf7fbc0fff7492b751025c6a78179b/transactions_v3/page/0/"
    46  	if result != expected {
    47  		t.Fatal("wrong value", result)
    48  	}
    49  
    50  	// Change page
    51  
    52  	if err = paginator.NextPage(); err != nil {
    53  		t.Fatal(err)
    54  	}
    55  	pageNumber, ok = paginator.Page().(int)
    56  	if !ok {
    57  		t.Fatal("cannot cast page to int")
    58  	}
    59  
    60  	result, err = provider.url(
    61  		base.HexToAddress("0xf503017d7baf7fbc0fff7492b751025c6a78179b"),
    62  		pageNumber,
    63  	)
    64  	if err != nil {
    65  		t.Fatal(err)
    66  	}
    67  
    68  	expected = "https://api.covalenthq.com/v1/eth-mainnet/address/0xf503017d7baf7fbc0fff7492b751025c6a78179b/transactions_v3/page/1/"
    69  	if result != expected {
    70  		t.Fatal("wrong value", result)
    71  	}
    72  }
    73  
    74  func mockCovalentServer(t *testing.T) (ts *httptest.Server) {
    75  	t.Helper()
    76  
    77  	pages := []covalentResponseBody{
    78  		{
    79  			Data: covalentResponseData{
    80  				Items: []covalentTransaction{
    81  					{
    82  						BlockHeight: utils.PointerOf(1),
    83  						TxOffset:    utils.PointerOf(1),
    84  
    85  						BlockHash:     utils.PointerOf(""),
    86  						From:          utils.PointerOf(""),
    87  						GasSpent:      utils.PointerOf(int64(0)),
    88  						Successful:    utils.PointerOf(false),
    89  						BlockSignedAt: &time.Time{},
    90  						To:            utils.PointerOf(""),
    91  						Value:         &base.Wei{},
    92  					},
    93  					{
    94  						BlockHeight: utils.PointerOf(1),
    95  						TxOffset:    utils.PointerOf(2),
    96  
    97  						BlockHash:     utils.PointerOf(""),
    98  						From:          utils.PointerOf(""),
    99  						GasSpent:      utils.PointerOf(int64(0)),
   100  						Successful:    utils.PointerOf(false),
   101  						BlockSignedAt: &time.Time{},
   102  						To:            utils.PointerOf(""),
   103  						Value:         &base.Wei{},
   104  					},
   105  					{
   106  						BlockHeight: utils.PointerOf(1),
   107  						TxOffset:    utils.PointerOf(3),
   108  
   109  						BlockHash:     utils.PointerOf(""),
   110  						From:          utils.PointerOf(""),
   111  						GasSpent:      utils.PointerOf(int64(0)),
   112  						Successful:    utils.PointerOf(false),
   113  						BlockSignedAt: &time.Time{},
   114  						To:            utils.PointerOf(""),
   115  						Value:         &base.Wei{},
   116  					},
   117  				},
   118  				Links: &covalentLinks{
   119  					Next: "/1",
   120  				},
   121  			},
   122  		},
   123  		{
   124  			Data: covalentResponseData{
   125  				Items: []covalentTransaction{
   126  					{
   127  						BlockHeight: utils.PointerOf(2),
   128  						TxOffset:    utils.PointerOf(1),
   129  
   130  						BlockHash:     utils.PointerOf(""),
   131  						From:          utils.PointerOf(""),
   132  						GasSpent:      utils.PointerOf(int64(0)),
   133  						Successful:    utils.PointerOf(false),
   134  						BlockSignedAt: &time.Time{},
   135  						To:            utils.PointerOf(""),
   136  						Value:         &base.Wei{},
   137  					},
   138  				},
   139  				Links: &covalentLinks{},
   140  			},
   141  		},
   142  	}
   143  	ts = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   144  		var result covalentResponseBody
   145  		switch r.URL.Path {
   146  		case "/0":
   147  			result = pages[0]
   148  		case "/1":
   149  			result = pages[1]
   150  		default:
   151  			result = covalentResponseBody{}
   152  		}
   153  
   154  		b, err := json.Marshal(result)
   155  		if err != nil {
   156  			t.Fatal(err)
   157  		}
   158  		w.Write(b)
   159  	}))
   160  
   161  	return ts
   162  }
   163  
   164  func TestCovalentProvider_fetchData(t *testing.T) {
   165  	ts := mockCovalentServer(t)
   166  	defer ts.Close()
   167  
   168  	provider := CovalentProvider{
   169  		chain:   "mainnet",
   170  		baseUrl: ts.URL + "/[{PAGE}]",
   171  	}
   172  	provider.limiter = rate.NewLimiter(5, 5)
   173  	paginator := provider.NewPaginator(&Query{})
   174  
   175  	var data []SlurpedPageItem
   176  	// var count int
   177  	var err error
   178  	data, _, err = provider.fetchData(context.TODO(), base.HexToAddress("0xf503017d7baf7fbc0fff7492b751025c6a78179b"), paginator, "int")
   179  	if err != nil {
   180  		t.Fatal(err)
   181  	}
   182  
   183  	if l := len(data); l == 0 {
   184  		t.Fatal("empty page")
   185  	}
   186  	if paginator.Done() {
   187  		t.Fatal("paginator done but it should not be")
   188  	}
   189  
   190  	if err = paginator.NextPage(); err != nil {
   191  		t.Fatal(err)
   192  	}
   193  
   194  	data, _, err = provider.fetchData(context.TODO(), base.HexToAddress("0xf503017d7baf7fbc0fff7492b751025c6a78179b"), paginator, "int")
   195  	if err != nil {
   196  		t.Fatal(err)
   197  	}
   198  	if l := len(data); l == 0 {
   199  		t.Fatal("empty page")
   200  	}
   201  	if !paginator.Done() {
   202  		t.Fatal("paginator should be done")
   203  	}
   204  }
   205  
   206  func TestCovalentProvider_TransactionsByAddress(t *testing.T) {
   207  	ts := mockCovalentServer(t)
   208  	defer ts.Close()
   209  
   210  	provider := CovalentProvider{
   211  		chain:   "mainnet",
   212  		baseUrl: ts.URL + "/[{PAGE}]",
   213  	}
   214  	provider.limiter = rate.NewLimiter(5, 5)
   215  
   216  	query := &Query{
   217  		Addresses: []base.Address{
   218  			base.HexToAddress("0xf503017d7baf7fbc0fff7492b751025c6a78179b"),
   219  		},
   220  		Resources: []string{"int"},
   221  	}
   222  	ctx, cancel := context.WithCancel(context.Background())
   223  	defer cancel()
   224  	errors := make(chan error)
   225  	results := provider.TransactionsByAddress(ctx, query, errors)
   226  
   227  	count := 0
   228  LOOP:
   229  	for {
   230  		select {
   231  		case err, ok := <-errors:
   232  			if ok {
   233  				cancel()
   234  				t.Fatal(err)
   235  			}
   236  		case data, ok := <-results:
   237  			if !ok {
   238  				break LOOP
   239  			}
   240  			t.Log("got data", data)
   241  			count++
   242  		}
   243  	}
   244  
   245  	if count != 4 {
   246  		t.Fatal("wrong count:", count)
   247  	}
   248  
   249  	// Filter: all txs filtered out
   250  
   251  	query.BlockRange = []identifiers.Identifier{
   252  		{
   253  			Orig: "14000000",
   254  		},
   255  	}
   256  	provider.TransactionsByAddress(ctx, query, errors)
   257  	err := <-errors
   258  	if err == nil {
   259  		t.Fatal("expected error")
   260  	}
   261  	if !strings.Contains(err.Error(), "zero transactions reported") {
   262  		t.Fatal("expected zero transactions reported error")
   263  	}
   264  }
   265  
   266  func TestCovalentProvider_Appearances(t *testing.T) {
   267  	perPage := 3
   268  	ts := mockCovalentServer(t)
   269  	defer ts.Close()
   270  
   271  	provider := CovalentProvider{
   272  		chain:   "mainnet",
   273  		baseUrl: ts.URL + "/[{PAGE}]",
   274  	}
   275  	provider.limiter = rate.NewLimiter(5, 5)
   276  
   277  	query := &Query{
   278  		Addresses: []base.Address{
   279  			base.HexToAddress("0xf503017d7baf7fbc0fff7492b751025c6a78179b"),
   280  		},
   281  		Resources: []string{"int"},
   282  		PerPage:   uint(perPage),
   283  	}
   284  	ctx, cancel := context.WithCancel(context.Background())
   285  	defer cancel()
   286  	errors := make(chan error)
   287  	results := provider.Appearances(ctx, query, errors)
   288  
   289  	count := 0
   290  LOOP:
   291  	for {
   292  		select {
   293  		case err, ok := <-errors:
   294  			if ok {
   295  				cancel()
   296  				t.Fatal(err)
   297  			}
   298  		case data, ok := <-results:
   299  			if !ok {
   300  				break LOOP
   301  			}
   302  			t.Log("got data", data)
   303  			count++
   304  		}
   305  	}
   306  
   307  	if count != 4 {
   308  		t.Fatal("wrong count:", count)
   309  	}
   310  }
   311  
   312  func TestCovalentProvider_Count(t *testing.T) {
   313  	perPage := 3
   314  	ts := mockCovalentServer(t)
   315  	defer ts.Close()
   316  
   317  	provider := CovalentProvider{
   318  		chain:   "mainnet",
   319  		baseUrl: ts.URL + "/[{PAGE}]",
   320  	}
   321  	provider.limiter = rate.NewLimiter(5, 5)
   322  
   323  	query := &Query{
   324  		Addresses: []base.Address{
   325  			base.HexToAddress("0xf503017d7baf7fbc0fff7492b751025c6a78179b"),
   326  		},
   327  		Resources: []string{"int"},
   328  		PerPage:   uint(perPage),
   329  	}
   330  	ctx, cancel := context.WithCancel(context.Background())
   331  	defer cancel()
   332  	errors := make(chan error)
   333  
   334  	results := provider.Count(ctx, query, errors)
   335  
   336  	count := make([]types.Monitor, 0, 1)
   337  LOOP:
   338  	for {
   339  		select {
   340  		case err, ok := <-errors:
   341  			if ok {
   342  				cancel()
   343  				t.Fatal(err)
   344  			}
   345  		case data, ok := <-results:
   346  			if !ok {
   347  				break LOOP
   348  			}
   349  			count = append(count, data)
   350  
   351  		}
   352  	}
   353  
   354  	if l := len(count); l != 1 {
   355  		t.Fatal("wrong len:", l)
   356  	}
   357  	if n := count[0].NRecords; n != 4 {
   358  		t.Fatal("wrong NRecords", n)
   359  	}
   360  }