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 }