github.com/trezor/blockbook@v0.4.1-0.20240328132726-e9a08582ee2c/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/trezor/blockbook/bchain" 18 "github.com/trezor/blockbook/bchain/coins/btc" 19 "github.com/trezor/blockbook/common" 20 "github.com/trezor/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, config *common.Config) (*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(config) 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 mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 87 var err error 88 var mockData string 89 90 if r.URL.Path == "/ping" { 91 w.WriteHeader(200) 92 } else if r.URL.Path == "/coins/list" { 93 mockData, err = getFiatRatesMockData("coinlist") 94 } else if r.URL.Path == "/simple/supported_vs_currencies" { 95 mockData, err = getFiatRatesMockData("vs_currencies") 96 } else if r.URL.Path == "/simple/price" { 97 if r.URL.Query().Get("ids") == "ethereum" { 98 mockData, err = getFiatRatesMockData("simpleprice_base") 99 } else { 100 mockData, err = getFiatRatesMockData("simpleprice_tokens") 101 } 102 } else if r.URL.Path == "/coins/ethereum/market_chart" { 103 vsCurrency := r.URL.Query().Get("vs_currency") 104 if vsCurrency == "usd" { 105 days := r.URL.Query().Get("days") 106 if days == "max" { 107 mockData, err = getFiatRatesMockData("market_chart_eth_usd_max") 108 } else { 109 mockData, err = getFiatRatesMockData("market_chart_eth_usd_1") 110 } 111 } else { 112 mockData, err = getFiatRatesMockData("market_chart_eth_other") 113 } 114 } else if r.URL.Path == "/coins/vendit/market_chart" || r.URL.Path == "/coins/ethereum-cash-token/market_chart" { 115 mockData, err = getFiatRatesMockData("market_chart_token_other") 116 } else { 117 t.Fatalf("Unknown URL path: %v", r.URL.Path) 118 } 119 120 if err != nil { 121 t.Fatalf("Error loading stub data: %v", err) 122 } 123 fmt.Fprintln(w, mockData) 124 })) 125 defer mockServer.Close() 126 127 // config with mocked CoinGecko API 128 config := common.Config{ 129 CoinName: "fakecoin", 130 FiatRates: "coingecko", 131 FiatRatesParams: `{"url": "` + mockServer.URL + `", "coin": "ethereum","platformIdentifier": "ethereum","platformVsCurrency": "eth","periodSeconds": 60}`, 132 } 133 134 d, _, tmp := setupRocksDB(t, &testBitcoinParser{ 135 BitcoinParser: bitcoinTestnetParser(), 136 }, &config) 137 defer closeAndDestroyRocksDB(t, d, tmp) 138 139 fiatRates, err := NewFiatRates(d, &config, nil, nil) 140 if err != nil { 141 t.Fatalf("FiatRates init error: %v", err) 142 } 143 144 // get current tickers 145 currentTickers, err := fiatRates.downloader.CurrentTickers() 146 if err != nil { 147 t.Fatalf("Error in CurrentTickers: %v", err) 148 return 149 } 150 if currentTickers == nil { 151 t.Fatalf("CurrentTickers returned nil value") 152 return 153 } 154 155 wantCurrentTickers := common.CurrencyRatesTicker{ 156 Rates: map[string]float32{ 157 "aed": 8447.1, 158 "ars": 268901, 159 "aud": 3314.36, 160 "btc": 0.07531005, 161 "eth": 1, 162 "eur": 2182.99, 163 "ltc": 29.097696, 164 "usd": 2299.72, 165 }, 166 TokenRates: map[string]float32{ 167 "0x5e9997684d061269564f94e5d11ba6ce6fa9528c": 5.58195e-07, 168 "0x906710835d1ae85275eb770f06873340ca54274b": 1.39852e-10, 169 }, 170 Timestamp: currentTickers.Timestamp, 171 } 172 if !reflect.DeepEqual(currentTickers, &wantCurrentTickers) { 173 t.Fatalf("CurrentTickers() = %v, want %v", *currentTickers, wantCurrentTickers) 174 } 175 176 ticker, err := fiatRates.db.FiatRatesFindLastTicker("usd", "") 177 if err != nil { 178 t.Fatalf("FiatRatesFindLastTicker failed with error: %v", err) 179 } 180 if ticker != nil { 181 t.Fatalf("FiatRatesFindLastTicker found unexpected data") 182 } 183 184 // update historical tickers for the first time 185 err = fiatRates.downloader.UpdateHistoricalTickers() 186 if err != nil { 187 t.Fatalf("UpdateHistoricalTickers 1st pass failed with error: %v", err) 188 } 189 err = fiatRates.downloader.UpdateHistoricalTokenTickers() 190 if err != nil { 191 t.Fatalf("UpdateHistoricalTokenTickers 1st pass failed with error: %v", err) 192 } 193 194 ticker, err = fiatRates.db.FiatRatesFindLastTicker("usd", "") 195 if err != nil || ticker == nil { 196 t.Fatalf("FiatRatesFindLastTicker failed with error: %v", err) 197 } 198 wantTicker := common.CurrencyRatesTicker{ 199 Rates: map[string]float32{ 200 "aed": 241272.48, 201 "ars": 241272.48, 202 "aud": 241272.48, 203 "btc": 241272.48, 204 "eth": 241272.48, 205 "eur": 241272.48, 206 "ltc": 241272.48, 207 "usd": 1794.5397, 208 }, 209 TokenRates: map[string]float32{ 210 "0x5e9997684d061269564f94e5d11ba6ce6fa9528c": 4.161734e+07, 211 "0x906710835d1ae85275eb770f06873340ca54274b": 4.161734e+07, 212 }, 213 Timestamp: time.Unix(1654732800, 0).UTC(), 214 } 215 if !reflect.DeepEqual(ticker, &wantTicker) { 216 t.Fatalf("UpdateHistoricalTickers(usd) 1st pass = %v, want %v", *ticker, wantTicker) 217 } 218 219 ticker, err = fiatRates.db.FiatRatesFindLastTicker("eur", "") 220 if err != nil || ticker == nil { 221 t.Fatalf("FiatRatesFindLastTicker failed with error: %v", err) 222 } 223 wantTicker = common.CurrencyRatesTicker{ 224 Rates: map[string]float32{ 225 "aed": 240402.97, 226 "ars": 240402.97, 227 "aud": 240402.97, 228 "btc": 240402.97, 229 "eth": 240402.97, 230 "eur": 240402.97, 231 "ltc": 240402.97, 232 }, 233 TokenRates: map[string]float32{ 234 "0x5e9997684d061269564f94e5d11ba6ce6fa9528c": 4.1464476e+07, 235 "0x906710835d1ae85275eb770f06873340ca54274b": 4.1464476e+07, 236 }, 237 Timestamp: time.Unix(1654819200, 0).UTC(), 238 } 239 if !reflect.DeepEqual(ticker, &wantTicker) { 240 t.Fatalf("UpdateHistoricalTickers(eur) 1st pass = %v, want %v", *ticker, wantTicker) 241 } 242 243 // update historical tickers for the second time 244 err = fiatRates.downloader.UpdateHistoricalTickers() 245 if err != nil { 246 t.Fatalf("UpdateHistoricalTickers 2nd pass failed with error: %v", err) 247 } 248 err = fiatRates.downloader.UpdateHistoricalTokenTickers() 249 if err != nil { 250 t.Fatalf("UpdateHistoricalTokenTickers 2nd pass failed with error: %v", err) 251 } 252 ticker, err = fiatRates.db.FiatRatesFindLastTicker("usd", "") 253 if err != nil || ticker == nil { 254 t.Fatalf("FiatRatesFindLastTicker failed with error: %v", err) 255 } 256 wantTicker = common.CurrencyRatesTicker{ 257 Rates: map[string]float32{ 258 "aed": 240402.97, 259 "ars": 240402.97, 260 "aud": 240402.97, 261 "btc": 240402.97, 262 "eth": 240402.97, 263 "eur": 240402.97, 264 "ltc": 240402.97, 265 "usd": 1788.4183, 266 }, 267 TokenRates: map[string]float32{ 268 "0x5e9997684d061269564f94e5d11ba6ce6fa9528c": 4.1464476e+07, 269 "0x906710835d1ae85275eb770f06873340ca54274b": 4.1464476e+07, 270 }, 271 Timestamp: time.Unix(1654819200, 0).UTC(), 272 } 273 if !reflect.DeepEqual(ticker, &wantTicker) { 274 t.Fatalf("UpdateHistoricalTickers(usd) 2nd pass = %v, want %v", *ticker, wantTicker) 275 } 276 ticker, err = fiatRates.db.FiatRatesFindLastTicker("eur", "") 277 if err != nil || ticker == nil { 278 t.Fatalf("FiatRatesFindLastTicker failed with error: %v", err) 279 } 280 if !reflect.DeepEqual(ticker, &wantTicker) { 281 t.Fatalf("UpdateHistoricalTickers(eur) 2nd pass = %v, want %v", *ticker, wantTicker) 282 } 283 }