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

     1  package provider
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"io"
     7  	"net/http"
     8  	"net/http/httptest"
     9  	"reflect"
    10  	"testing"
    11  
    12  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/base"
    13  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/types"
    14  	"golang.org/x/time/rate"
    15  )
    16  
    17  func Test_alchemyPrepareQuery(t *testing.T) {
    18  	q := &Query{
    19  		Resources: []string{"int", "ext"},
    20  	}
    21  	result, err := alchemyPrepareQuery(q)
    22  	if err != nil {
    23  		t.Fatal(err)
    24  	}
    25  	expected := []string{
    26  		"internal:to",
    27  		"internal:from",
    28  		"external:to",
    29  		"external:from",
    30  	}
    31  	if !reflect.DeepEqual(result.Resources, expected) {
    32  		t.Fatal("wrong query", result)
    33  	}
    34  }
    35  
    36  func mockAlchemyServer(t *testing.T) (ts *httptest.Server) {
    37  	t.Helper()
    38  
    39  	pages := []alchemyResponseBody{
    40  		{
    41  			Transfers: []AlchemyTx{
    42  				{
    43  					BlockNumber: "1",
    44  					Hash:        "1",
    45  				},
    46  				{
    47  					BlockNumber: "1",
    48  					Hash:        "2",
    49  				},
    50  				{
    51  					BlockNumber: "1",
    52  					Hash:        "3",
    53  				},
    54  			},
    55  			PageKey: "second",
    56  		},
    57  		{
    58  			Transfers: []AlchemyTx{
    59  				{
    60  					BlockNumber: "2",
    61  					Hash:        "1",
    62  				},
    63  			},
    64  		},
    65  	}
    66  	ts = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    67  		var request struct {
    68  			Params []alchemyRequestParam `json:"params"`
    69  		}
    70  		var result struct {
    71  			Result alchemyResponseBody `json:"result"`
    72  		}
    73  
    74  		body, err := io.ReadAll(r.Body)
    75  		if err != nil {
    76  			t.Fatal(err)
    77  		}
    78  		defer r.Body.Close()
    79  		if err := json.Unmarshal(body, &request); err != nil {
    80  			t.Fatal(err)
    81  		}
    82  
    83  		switch request.Params[0].PageKey {
    84  		case "":
    85  			fallthrough
    86  		case "first":
    87  			result.Result = pages[0]
    88  			result.Result.PageKey = "second"
    89  		case "second":
    90  			result.Result = pages[1]
    91  			result.Result.PageKey = ""
    92  		default:
    93  			result.Result = alchemyResponseBody{}
    94  			result.Result.PageKey = ""
    95  		}
    96  
    97  		w.Header().Set("Content-Type", "application/json")
    98  
    99  		b, err := json.Marshal(result)
   100  		if err != nil {
   101  			t.Fatal(err)
   102  		}
   103  		w.Write(b)
   104  	}))
   105  
   106  	return ts
   107  }
   108  
   109  func TestAlchemyProvider_TransactionsByAddress(t *testing.T) {
   110  	perPage := 3
   111  	ts := mockAlchemyServer(t)
   112  	defer ts.Close()
   113  
   114  	provider := AlchemyProvider{
   115  		perPage: perPage,
   116  		baseUrl: ts.URL,
   117  		getTransactionAppearance: func(hash string) (types.Appearance, error) {
   118  			return types.Appearance{}, nil
   119  		},
   120  	}
   121  	provider.limiter = rate.NewLimiter(5, 5)
   122  
   123  	query := &Query{
   124  		Addresses: []base.Address{
   125  			base.HexToAddress("0xf503017d7baf7fbc0fff7492b751025c6a78179b"),
   126  		},
   127  		Resources: []string{"int"},
   128  	}
   129  	ctx, cancel := context.WithCancel(context.Background())
   130  	defer cancel()
   131  	errors := make(chan error)
   132  	results := provider.TransactionsByAddress(ctx, query, errors)
   133  
   134  	count := 0
   135  LOOP:
   136  	for {
   137  		select {
   138  		case err, ok := <-errors:
   139  			if ok {
   140  				cancel()
   141  				t.Fatal(err)
   142  			}
   143  		case data, ok := <-results:
   144  			if !ok {
   145  				break LOOP
   146  			}
   147  			t.Log("got data", data)
   148  			count++
   149  		}
   150  	}
   151  
   152  	if count != 8 {
   153  		t.Fatal("wrong count:", count)
   154  	}
   155  }
   156  
   157  func TestAlchemyProvider_Appearances(t *testing.T) {
   158  	perPage := 3
   159  	ts := mockAlchemyServer(t)
   160  	defer ts.Close()
   161  
   162  	provider := AlchemyProvider{
   163  		perPage: perPage,
   164  		baseUrl: ts.URL,
   165  		getTransactionAppearance: func(hash string) (types.Appearance, error) {
   166  			return types.Appearance{}, nil
   167  		},
   168  	}
   169  	provider.limiter = rate.NewLimiter(5, 5)
   170  
   171  	query := &Query{
   172  		Addresses: []base.Address{
   173  			base.HexToAddress("0xf503017d7baf7fbc0fff7492b751025c6a78179b"),
   174  		},
   175  		Resources: []string{"int"},
   176  	}
   177  	ctx, cancel := context.WithCancel(context.Background())
   178  	defer cancel()
   179  	errors := make(chan error)
   180  	results := provider.Appearances(ctx, query, errors)
   181  
   182  	count := 0
   183  LOOP:
   184  	for {
   185  		select {
   186  		case err, ok := <-errors:
   187  			if ok {
   188  				cancel()
   189  				t.Fatal(err)
   190  			}
   191  		case data, ok := <-results:
   192  			if !ok {
   193  				break LOOP
   194  			}
   195  			t.Log("got data", data)
   196  			count++
   197  		}
   198  	}
   199  
   200  	if count != 8 {
   201  		t.Fatal("wrong count:", count)
   202  	}
   203  }
   204  
   205  func TestAlchemyProvider_Count(t *testing.T) {
   206  	perPage := 3
   207  	ts := mockAlchemyServer(t)
   208  	defer ts.Close()
   209  
   210  	provider := AlchemyProvider{
   211  		perPage: perPage,
   212  		baseUrl: ts.URL,
   213  		getTransactionAppearance: func(hash string) (types.Appearance, error) {
   214  			return types.Appearance{}, nil
   215  		},
   216  	}
   217  
   218  	query := &Query{
   219  		Addresses: []base.Address{
   220  			base.HexToAddress("0xf503017d7baf7fbc0fff7492b751025c6a78179b"),
   221  		},
   222  		Resources: []string{"int"},
   223  	}
   224  	ctx, cancel := context.WithCancel(context.Background())
   225  	defer cancel()
   226  	errors := make(chan error)
   227  
   228  	results := provider.Count(ctx, query, errors)
   229  
   230  	count := make([]types.Monitor, 0, 1)
   231  LOOP:
   232  	for {
   233  		select {
   234  		case err, ok := <-errors:
   235  			if ok {
   236  				cancel()
   237  				t.Fatal(err)
   238  			}
   239  		case data, ok := <-results:
   240  			if !ok {
   241  				break LOOP
   242  			}
   243  			count = append(count, data)
   244  
   245  		}
   246  	}
   247  
   248  	if l := len(count); l != 1 {
   249  		t.Fatal("wrong len:", l)
   250  	}
   251  	if n := count[0].NRecords; n != 8 {
   252  		t.Fatal("wrong NRecords", n)
   253  	}
   254  }