blockbook/fiat/fiat_rates_test.go
2023-02-01 17:58:37 +01:00

298 lines
8.6 KiB
Go

//go:build unittest
package fiat
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/http/httptest"
"os"
"reflect"
"testing"
"time"
"github.com/golang/glog"
"github.com/martinboehm/btcutil/chaincfg"
"github.com/trezor/blockbook/bchain"
"github.com/trezor/blockbook/bchain/coins/btc"
"github.com/trezor/blockbook/common"
"github.com/trezor/blockbook/db"
)
func TestMain(m *testing.M) {
// set the current directory to blockbook root so that ./static/ works
if err := os.Chdir(".."); err != nil {
glog.Fatal("Chdir error:", err)
}
c := m.Run()
chaincfg.ResetParams()
os.Exit(c)
}
func setupRocksDB(t *testing.T, parser bchain.BlockChainParser) (*db.RocksDB, *common.InternalState, string) {
tmp, err := ioutil.TempDir("", "testdb")
if err != nil {
t.Fatal(err)
}
d, err := db.NewRocksDB(tmp, 100000, -1, parser, nil)
if err != nil {
t.Fatal(err)
}
is, err := d.LoadInternalState("fakecoin")
if err != nil {
t.Fatal(err)
}
d.SetInternalState(is)
return d, is, tmp
}
func closeAndDestroyRocksDB(t *testing.T, db *db.RocksDB, dbpath string) {
// destroy db
if err := db.Close(); err != nil {
t.Fatal(err)
}
os.RemoveAll(dbpath)
}
type testBitcoinParser struct {
*btc.BitcoinParser
}
func bitcoinTestnetParser() *btc.BitcoinParser {
return btc.NewBitcoinParser(
btc.GetChainParams("test"),
&btc.Configuration{BlockAddressesToKeep: 1})
}
// getFiatRatesMockData reads a stub JSON response from a file and returns its content as string
func getFiatRatesMockData(name string) (string, error) {
var filename string
filename = "fiat/mock_data/" + name + ".json"
mockFile, err := os.Open(filename)
if err != nil {
glog.Errorf("Cannot open file %v", filename)
return "", err
}
b, err := ioutil.ReadAll(mockFile)
if err != nil {
glog.Errorf("Cannot read file %v", filename)
return "", err
}
return string(b), nil
}
func TestFiatRates(t *testing.T) {
d, _, tmp := setupRocksDB(t, &testBitcoinParser{
BitcoinParser: bitcoinTestnetParser(),
})
defer closeAndDestroyRocksDB(t, d, tmp)
mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var err error
var mockData string
if r.URL.Path == "/ping" {
w.WriteHeader(200)
} else if r.URL.Path == "/coins/list" {
mockData, err = getFiatRatesMockData("coinlist")
} else if r.URL.Path == "/simple/supported_vs_currencies" {
mockData, err = getFiatRatesMockData("vs_currencies")
} else if r.URL.Path == "/simple/price" {
if r.URL.Query().Get("ids") == "ethereum" {
mockData, err = getFiatRatesMockData("simpleprice_base")
} else {
mockData, err = getFiatRatesMockData("simpleprice_tokens")
}
} else if r.URL.Path == "/coins/ethereum/market_chart" {
vsCurrency := r.URL.Query().Get("vs_currency")
if vsCurrency == "usd" {
days := r.URL.Query().Get("days")
if days == "max" {
mockData, err = getFiatRatesMockData("market_chart_eth_usd_max")
} else {
mockData, err = getFiatRatesMockData("market_chart_eth_usd_1")
}
} else {
mockData, err = getFiatRatesMockData("market_chart_eth_other")
}
} else if r.URL.Path == "/coins/vendit/market_chart" || r.URL.Path == "/coins/ethereum-cash-token/market_chart" {
mockData, err = getFiatRatesMockData("market_chart_token_other")
} else {
t.Fatalf("Unknown URL path: %v", r.URL.Path)
}
if err != nil {
t.Fatalf("Error loading stub data: %v", err)
}
fmt.Fprintln(w, mockData)
}))
defer mockServer.Close()
// mocked CoinGecko API
configJSON := `{"fiat_rates": "coingecko", "fiat_rates_params": "{\"url\": \"` + mockServer.URL + `\", \"coin\": \"ethereum\",\"platformIdentifier\":\"ethereum\",\"platformVsCurrency\": \"eth\",\"periodSeconds\": 60}"}`
type fiatRatesConfig struct {
FiatRates string `json:"fiat_rates"`
FiatRatesParams string `json:"fiat_rates_params"`
}
var config fiatRatesConfig
err := json.Unmarshal([]byte(configJSON), &config)
if err != nil {
t.Fatalf("Error parsing config: %v", err)
}
if config.FiatRates == "" || config.FiatRatesParams == "" {
t.Fatalf("Error parsing FiatRates config - empty parameter")
return
}
fiatRates, err := NewFiatRatesDownloader(d, config.FiatRates, config.FiatRatesParams, nil)
if err != nil {
t.Fatalf("FiatRates init error: %v", err)
}
if config.FiatRates == "coingecko" {
// get current tickers
currentTickers, err := fiatRates.downloader.CurrentTickers()
if err != nil {
t.Fatalf("Error in CurrentTickers: %v", err)
return
}
if currentTickers == nil {
t.Fatalf("CurrentTickers returned nil value")
return
}
wantCurrentTickers := db.CurrencyRatesTicker{
Rates: map[string]float32{
"aed": 8447.1,
"ars": 268901,
"aud": 3314.36,
"btc": 0.07531005,
"eth": 1,
"eur": 2182.99,
"ltc": 29.097696,
"usd": 2299.72,
},
TokenRates: map[string]float32{
"0x5e9997684d061269564f94e5d11ba6ce6fa9528c": 5.58195e-07,
"0x906710835d1ae85275eb770f06873340ca54274b": 1.39852e-10,
},
Timestamp: currentTickers.Timestamp,
}
if !reflect.DeepEqual(currentTickers, &wantCurrentTickers) {
t.Fatalf("CurrentTickers() = %v, want %v", *currentTickers, wantCurrentTickers)
}
ticker, err := fiatRates.db.FiatRatesFindLastTicker("usd", "")
if err != nil {
t.Fatalf("FiatRatesFindLastTicker failed with error: %v", err)
}
if ticker != nil {
t.Fatalf("FiatRatesFindLastTicker found unexpected data")
}
// update historical tickers for the first time
err = fiatRates.downloader.UpdateHistoricalTickers()
if err != nil {
t.Fatalf("UpdateHistoricalTickers 1st pass failed with error: %v", err)
}
err = fiatRates.downloader.UpdateHistoricalTokenTickers()
if err != nil {
t.Fatalf("UpdateHistoricalTokenTickers 1st pass failed with error: %v", err)
}
ticker, err = fiatRates.db.FiatRatesFindLastTicker("usd", "")
if err != nil || ticker == nil {
t.Fatalf("FiatRatesFindLastTicker failed with error: %v", err)
}
wantTicker := db.CurrencyRatesTicker{
Rates: map[string]float32{
"aed": 241272.48,
"ars": 241272.48,
"aud": 241272.48,
"btc": 241272.48,
"eth": 241272.48,
"eur": 241272.48,
"ltc": 241272.48,
"usd": 1794.5397,
},
TokenRates: map[string]float32{
"0x5e9997684d061269564f94e5d11ba6ce6fa9528c": 4.161734e+07,
"0x906710835d1ae85275eb770f06873340ca54274b": 4.161734e+07,
},
Timestamp: time.Unix(1654732800, 0).UTC(),
}
if !reflect.DeepEqual(ticker, &wantTicker) {
t.Fatalf("UpdateHistoricalTickers(usd) 1st pass = %v, want %v", *ticker, wantTicker)
}
ticker, err = fiatRates.db.FiatRatesFindLastTicker("eur", "")
if err != nil || ticker == nil {
t.Fatalf("FiatRatesFindLastTicker failed with error: %v", err)
}
wantTicker = db.CurrencyRatesTicker{
Rates: map[string]float32{
"aed": 240402.97,
"ars": 240402.97,
"aud": 240402.97,
"btc": 240402.97,
"eth": 240402.97,
"eur": 240402.97,
"ltc": 240402.97,
},
TokenRates: map[string]float32{
"0x5e9997684d061269564f94e5d11ba6ce6fa9528c": 4.1464476e+07,
"0x906710835d1ae85275eb770f06873340ca54274b": 4.1464476e+07,
},
Timestamp: time.Unix(1654819200, 0).UTC(),
}
if !reflect.DeepEqual(ticker, &wantTicker) {
t.Fatalf("UpdateHistoricalTickers(eur) 1st pass = %v, want %v", *ticker, wantTicker)
}
// update historical tickers for the second time
err = fiatRates.downloader.UpdateHistoricalTickers()
if err != nil {
t.Fatalf("UpdateHistoricalTickers 2nd pass failed with error: %v", err)
}
err = fiatRates.downloader.UpdateHistoricalTokenTickers()
if err != nil {
t.Fatalf("UpdateHistoricalTokenTickers 2nd pass failed with error: %v", err)
}
ticker, err = fiatRates.db.FiatRatesFindLastTicker("usd", "")
if err != nil || ticker == nil {
t.Fatalf("FiatRatesFindLastTicker failed with error: %v", err)
}
wantTicker = db.CurrencyRatesTicker{
Rates: map[string]float32{
"aed": 240402.97,
"ars": 240402.97,
"aud": 240402.97,
"btc": 240402.97,
"eth": 240402.97,
"eur": 240402.97,
"ltc": 240402.97,
"usd": 1788.4183,
},
TokenRates: map[string]float32{
"0x5e9997684d061269564f94e5d11ba6ce6fa9528c": 4.1464476e+07,
"0x906710835d1ae85275eb770f06873340ca54274b": 4.1464476e+07,
},
Timestamp: time.Unix(1654819200, 0).UTC(),
}
if !reflect.DeepEqual(ticker, &wantTicker) {
t.Fatalf("UpdateHistoricalTickers(usd) 2nd pass = %v, want %v", *ticker, wantTicker)
}
ticker, err = fiatRates.db.FiatRatesFindLastTicker("eur", "")
if err != nil || ticker == nil {
t.Fatalf("FiatRatesFindLastTicker failed with error: %v", err)
}
if !reflect.DeepEqual(ticker, &wantTicker) {
t.Fatalf("UpdateHistoricalTickers(eur) 2nd pass = %v, want %v", *ticker, wantTicker)
}
}
}