github.com/chadouming/blockbook@v0.0.0-20230522201718-fae8f5aff8a3/fiat/fiat_rates_test.go (about) 1 //go:build unittest 2 3 package fiat 4 5 import ( 6 "fmt" 7 "io" 8 "net/http" 9 "net/http/httptest" 10 "os" 11 "reflect" 12 "testing" 13 "time" 14 15 "github.com/golang/glog" 16 "github.com/martinboehm/btcutil/chaincfg" 17 "github.com/chadouming/blockbook/bchain" 18 "github.com/chadouming/blockbook/bchain/coins/btc" 19 "github.com/chadouming/blockbook/common" 20 "github.com/chadouming/blockbook/db" 21 ) 22 23 func TestMain(m *testing.M) { 24 // set the current directory to blockbook root so that ./static/ works 25 if err := os.Chdir(".."); err != nil { 26 glog.Fatal("Chdir error:", err) 27 } 28 c := m.Run() 29 chaincfg.ResetParams() 30 os.Exit(c) 31 } 32 33 func setupRocksDB(t *testing.T, parser bchain.BlockChainParser) (*db.RocksDB, *common.InternalState, string) { 34 tmp, err := os.MkdirTemp("", "testdb") 35 if err != nil { 36 t.Fatal(err) 37 } 38 d, err := db.NewRocksDB(tmp, 100000, -1, parser, nil, false) 39 if err != nil { 40 t.Fatal(err) 41 } 42 is, err := d.LoadInternalState("fakecoin") 43 if err != nil { 44 t.Fatal(err) 45 } 46 d.SetInternalState(is) 47 return d, is, tmp 48 } 49 50 func closeAndDestroyRocksDB(t *testing.T, db *db.RocksDB, dbpath string) { 51 // destroy db 52 if err := db.Close(); err != nil { 53 t.Fatal(err) 54 } 55 os.RemoveAll(dbpath) 56 } 57 58 type testBitcoinParser struct { 59 *btc.BitcoinParser 60 } 61 62 func bitcoinTestnetParser() *btc.BitcoinParser { 63 return btc.NewBitcoinParser( 64 btc.GetChainParams("test"), 65 &btc.Configuration{BlockAddressesToKeep: 1}) 66 } 67 68 // getFiatRatesMockData reads a stub JSON response from a file and returns its content as string 69 func getFiatRatesMockData(name string) (string, error) { 70 var filename string 71 filename = "fiat/mock_data/" + name + ".json" 72 mockFile, err := os.Open(filename) 73 if err != nil { 74 glog.Errorf("Cannot open file %v", filename) 75 return "", err 76 } 77 b, err := io.ReadAll(mockFile) 78 if err != nil { 79 glog.Errorf("Cannot read file %v", filename) 80 return "", err 81 } 82 return string(b), nil 83 } 84 85 func TestFiatRates(t *testing.T) { 86 d, _, tmp := setupRocksDB(t, &testBitcoinParser{ 87 BitcoinParser: bitcoinTestnetParser(), 88 }) 89 defer closeAndDestroyRocksDB(t, d, tmp) 90 91 mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 92 var err error 93 var mockData string 94 95 if r.URL.Path == "/ping" { 96 w.WriteHeader(200) 97 } else if r.URL.Path == "/coins/list" { 98 mockData, err = getFiatRatesMockData("coinlist") 99 } else if r.URL.Path == "/simple/supported_vs_currencies" { 100 mockData, err = getFiatRatesMockData("vs_currencies") 101 } else if r.URL.Path == "/simple/price" { 102 if r.URL.Query().Get("ids") == "ethereum" { 103 mockData, err = getFiatRatesMockData("simpleprice_base") 104 } else { 105 mockData, err = getFiatRatesMockData("simpleprice_tokens") 106 } 107 } else if r.URL.Path == "/coins/ethereum/market_chart" { 108 vsCurrency := r.URL.Query().Get("vs_currency") 109 if vsCurrency == "usd" { 110 days := r.URL.Query().Get("days") 111 if days == "max" { 112 mockData, err = getFiatRatesMockData("market_chart_eth_usd_max") 113 } else { 114 mockData, err = getFiatRatesMockData("market_chart_eth_usd_1") 115 } 116 } else { 117 mockData, err = getFiatRatesMockData("market_chart_eth_other") 118 } 119 } else if r.URL.Path == "/coins/vendit/market_chart" || r.URL.Path == "/coins/ethereum-cash-token/market_chart" { 120 mockData, err = getFiatRatesMockData("market_chart_token_other") 121 } else { 122 t.Fatalf("Unknown URL path: %v", r.URL.Path) 123 } 124 125 if err != nil { 126 t.Fatalf("Error loading stub data: %v", err) 127 } 128 fmt.Fprintln(w, mockData) 129 })) 130 defer mockServer.Close() 131 132 // mocked CoinGecko API 133 configJSON := `{"fiat_rates": "coingecko", "fiat_rates_params": "{\"url\": \"` + mockServer.URL + `\", \"coin\": \"ethereum\",\"platformIdentifier\":\"ethereum\",\"platformVsCurrency\": \"eth\",\"periodSeconds\": 60}"}` 134 135 fiatRates, err := NewFiatRates(d, []byte(configJSON), nil, nil) 136 if err != nil { 137 t.Fatalf("FiatRates init error: %v", err) 138 } 139 140 // get current tickers 141 currentTickers, err := fiatRates.downloader.CurrentTickers() 142 if err != nil { 143 t.Fatalf("Error in CurrentTickers: %v", err) 144 return 145 } 146 if currentTickers == nil { 147 t.Fatalf("CurrentTickers returned nil value") 148 return 149 } 150 151 wantCurrentTickers := common.CurrencyRatesTicker{ 152 Rates: map[string]float32{ 153 "aed": 8447.1, 154 "ars": 268901, 155 "aud": 3314.36, 156 "btc": 0.07531005, 157 "eth": 1, 158 "eur": 2182.99, 159 "ltc": 29.097696, 160 "usd": 2299.72, 161 }, 162 TokenRates: map[string]float32{ 163 "0x5e9997684d061269564f94e5d11ba6ce6fa9528c": 5.58195e-07, 164 "0x906710835d1ae85275eb770f06873340ca54274b": 1.39852e-10, 165 }, 166 Timestamp: currentTickers.Timestamp, 167 } 168 if !reflect.DeepEqual(currentTickers, &wantCurrentTickers) { 169 t.Fatalf("CurrentTickers() = %v, want %v", *currentTickers, wantCurrentTickers) 170 } 171 172 ticker, err := fiatRates.db.FiatRatesFindLastTicker("usd", "") 173 if err != nil { 174 t.Fatalf("FiatRatesFindLastTicker failed with error: %v", err) 175 } 176 if ticker != nil { 177 t.Fatalf("FiatRatesFindLastTicker found unexpected data") 178 } 179 180 // update historical tickers for the first time 181 err = fiatRates.downloader.UpdateHistoricalTickers() 182 if err != nil { 183 t.Fatalf("UpdateHistoricalTickers 1st pass failed with error: %v", err) 184 } 185 err = fiatRates.downloader.UpdateHistoricalTokenTickers() 186 if err != nil { 187 t.Fatalf("UpdateHistoricalTokenTickers 1st pass failed with error: %v", err) 188 } 189 190 ticker, err = fiatRates.db.FiatRatesFindLastTicker("usd", "") 191 if err != nil || ticker == nil { 192 t.Fatalf("FiatRatesFindLastTicker failed with error: %v", err) 193 } 194 wantTicker := common.CurrencyRatesTicker{ 195 Rates: map[string]float32{ 196 "aed": 241272.48, 197 "ars": 241272.48, 198 "aud": 241272.48, 199 "btc": 241272.48, 200 "eth": 241272.48, 201 "eur": 241272.48, 202 "ltc": 241272.48, 203 "usd": 1794.5397, 204 }, 205 TokenRates: map[string]float32{ 206 "0x5e9997684d061269564f94e5d11ba6ce6fa9528c": 4.161734e+07, 207 "0x906710835d1ae85275eb770f06873340ca54274b": 4.161734e+07, 208 }, 209 Timestamp: time.Unix(1654732800, 0).UTC(), 210 } 211 if !reflect.DeepEqual(ticker, &wantTicker) { 212 t.Fatalf("UpdateHistoricalTickers(usd) 1st pass = %v, want %v", *ticker, wantTicker) 213 } 214 215 ticker, err = fiatRates.db.FiatRatesFindLastTicker("eur", "") 216 if err != nil || ticker == nil { 217 t.Fatalf("FiatRatesFindLastTicker failed with error: %v", err) 218 } 219 wantTicker = common.CurrencyRatesTicker{ 220 Rates: map[string]float32{ 221 "aed": 240402.97, 222 "ars": 240402.97, 223 "aud": 240402.97, 224 "btc": 240402.97, 225 "eth": 240402.97, 226 "eur": 240402.97, 227 "ltc": 240402.97, 228 }, 229 TokenRates: map[string]float32{ 230 "0x5e9997684d061269564f94e5d11ba6ce6fa9528c": 4.1464476e+07, 231 "0x906710835d1ae85275eb770f06873340ca54274b": 4.1464476e+07, 232 }, 233 Timestamp: time.Unix(1654819200, 0).UTC(), 234 } 235 if !reflect.DeepEqual(ticker, &wantTicker) { 236 t.Fatalf("UpdateHistoricalTickers(eur) 1st pass = %v, want %v", *ticker, wantTicker) 237 } 238 239 // update historical tickers for the second time 240 err = fiatRates.downloader.UpdateHistoricalTickers() 241 if err != nil { 242 t.Fatalf("UpdateHistoricalTickers 2nd pass failed with error: %v", err) 243 } 244 err = fiatRates.downloader.UpdateHistoricalTokenTickers() 245 if err != nil { 246 t.Fatalf("UpdateHistoricalTokenTickers 2nd pass failed with error: %v", err) 247 } 248 ticker, err = fiatRates.db.FiatRatesFindLastTicker("usd", "") 249 if err != nil || ticker == nil { 250 t.Fatalf("FiatRatesFindLastTicker failed with error: %v", err) 251 } 252 wantTicker = common.CurrencyRatesTicker{ 253 Rates: map[string]float32{ 254 "aed": 240402.97, 255 "ars": 240402.97, 256 "aud": 240402.97, 257 "btc": 240402.97, 258 "eth": 240402.97, 259 "eur": 240402.97, 260 "ltc": 240402.97, 261 "usd": 1788.4183, 262 }, 263 TokenRates: map[string]float32{ 264 "0x5e9997684d061269564f94e5d11ba6ce6fa9528c": 4.1464476e+07, 265 "0x906710835d1ae85275eb770f06873340ca54274b": 4.1464476e+07, 266 }, 267 Timestamp: time.Unix(1654819200, 0).UTC(), 268 } 269 if !reflect.DeepEqual(ticker, &wantTicker) { 270 t.Fatalf("UpdateHistoricalTickers(usd) 2nd pass = %v, want %v", *ticker, wantTicker) 271 } 272 ticker, err = fiatRates.db.FiatRatesFindLastTicker("eur", "") 273 if err != nil || ticker == nil { 274 t.Fatalf("FiatRatesFindLastTicker failed with error: %v", err) 275 } 276 if !reflect.DeepEqual(ticker, &wantTicker) { 277 t.Fatalf("UpdateHistoricalTickers(eur) 2nd pass = %v, want %v", *ticker, wantTicker) 278 } 279 }