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 }