//go:build unittest package api import ( "reflect" "testing" "github.com/trezor/blockbook/common" "github.com/trezor/blockbook/fiat" ) func TestSetFiatRateToBalanceHistories_BatchesTickerLookup(t *testing.T) { histories := BalanceHistories{ {Time: 100}, {Time: 200}, {Time: 300}, } w := &Worker{ fiatRates: &fiat.FiatRates{Enabled: true}, } originalGetter := getTickersForTimestamps defer func() { getTickersForTimestamps = originalGetter }() calls := 0 var gotTimestamps []int64 getTickersForTimestamps = func(_ *fiat.FiatRates, timestamps []int64, _, _ string) (*[]*common.CurrencyRatesTicker, error) { calls++ gotTimestamps = append([]int64(nil), timestamps...) tickers := []*common.CurrencyRatesTicker{ {Rates: map[string]float32{"usd": 11, "eur": 22}}, nil, {Rates: map[string]float32{"usd": 33}}, } return &tickers, nil } err := w.setFiatRateToBalanceHistories(histories, []string{"USD", "eur", "cad"}, "address") if err != nil { t.Fatalf("setFiatRateToBalanceHistories returned error: %v", err) } if calls != 1 { t.Fatalf("expected 1 ticker lookup call, got %d", calls) } if !reflect.DeepEqual(gotTimestamps, []int64{100, 200, 300}) { t.Fatalf("unexpected timestamps: got %v", gotTimestamps) } if !reflect.DeepEqual(histories[0].FiatRates, map[string]float32{"usd": 11, "eur": 22, "cad": -1}) { t.Fatalf("unexpected rates for histories[0]: %v", histories[0].FiatRates) } if histories[1].FiatRates != nil { t.Fatalf("expected nil rates for histories[1], got %v", histories[1].FiatRates) } if !reflect.DeepEqual(histories[2].FiatRates, map[string]float32{"usd": 33, "eur": -1, "cad": -1}) { t.Fatalf("unexpected rates for histories[2]: %v", histories[2].FiatRates) } } func TestSetFiatRateToBalanceHistories_AllRatesWhenCurrenciesNotSpecified(t *testing.T) { histories := BalanceHistories{ {Time: 100}, } w := &Worker{ fiatRates: &fiat.FiatRates{Enabled: true}, } originalGetter := getTickersForTimestamps defer func() { getTickersForTimestamps = originalGetter }() getTickersForTimestamps = func(_ *fiat.FiatRates, _ []int64, _, _ string) (*[]*common.CurrencyRatesTicker, error) { tickers := []*common.CurrencyRatesTicker{ {Rates: map[string]float32{"usd": 11, "eur": 22}}, } return &tickers, nil } err := w.setFiatRateToBalanceHistories(histories, nil, "address") if err != nil { t.Fatalf("setFiatRateToBalanceHistories returned error: %v", err) } if !reflect.DeepEqual(histories[0].FiatRates, map[string]float32{"usd": 11, "eur": 22}) { t.Fatalf("unexpected rates for histories[0]: %v", histories[0].FiatRates) } } func TestSetFiatRateToBalanceHistories_BatchFailureFallsBackToPerPoint(t *testing.T) { histories := BalanceHistories{ {Time: 100}, {Time: 200}, {Time: 300}, } w := &Worker{ fiatRates: &fiat.FiatRates{Enabled: true}, } originalGetter := getTickersForTimestamps defer func() { getTickersForTimestamps = originalGetter }() calls := 0 var gotCalls [][]int64 getTickersForTimestamps = func(_ *fiat.FiatRates, timestamps []int64, _, _ string) (*[]*common.CurrencyRatesTicker, error) { calls++ gotCalls = append(gotCalls, append([]int64(nil), timestamps...)) if len(timestamps) > 1 { return nil, assertError("batch error") } switch timestamps[0] { case 100: tickers := []*common.CurrencyRatesTicker{ {Rates: map[string]float32{"usd": 11}}, } return &tickers, nil case 200: return nil, assertError("point error") case 300: tickers := []*common.CurrencyRatesTicker{ {Rates: map[string]float32{"usd": 33}}, } return &tickers, nil default: tickers := []*common.CurrencyRatesTicker{} return &tickers, nil } } err := w.setFiatRateToBalanceHistories(histories, []string{"usd"}, "address") if err != nil { t.Fatalf("setFiatRateToBalanceHistories returned error: %v", err) } if calls != 4 { t.Fatalf("expected 4 ticker lookup calls (1 batch + 3 point), got %d", calls) } wantCalls := [][]int64{ {100, 200, 300}, {100}, {200}, {300}, } if !reflect.DeepEqual(gotCalls, wantCalls) { t.Fatalf("unexpected lookup calls: got %v, want %v", gotCalls, wantCalls) } if !reflect.DeepEqual(histories[0].FiatRates, map[string]float32{"usd": 11}) { t.Fatalf("unexpected rates for histories[0]: %v", histories[0].FiatRates) } if histories[1].FiatRates != nil { t.Fatalf("expected nil rates for histories[1], got %v", histories[1].FiatRates) } if !reflect.DeepEqual(histories[2].FiatRates, map[string]float32{"usd": 33}) { t.Fatalf("unexpected rates for histories[2]: %v", histories[2].FiatRates) } } func TestSetFiatRateToBalanceHistories_SkipsLookupForEmptyHistory(t *testing.T) { w := &Worker{ fiatRates: &fiat.FiatRates{Enabled: true}, } originalGetter := getTickersForTimestamps defer func() { getTickersForTimestamps = originalGetter }() calls := 0 getTickersForTimestamps = func(_ *fiat.FiatRates, _ []int64, _, _ string) (*[]*common.CurrencyRatesTicker, error) { calls++ tickers := []*common.CurrencyRatesTicker{} return &tickers, nil } err := w.setFiatRateToBalanceHistories(BalanceHistories{}, []string{"usd"}, "address") if err != nil { t.Fatalf("setFiatRateToBalanceHistories returned error: %v", err) } if calls != 0 { t.Fatalf("expected 0 ticker lookup calls, got %d", calls) } } func TestSetFiatRateToBalanceHistories_SkipsLookupWhenFiatRatesDisabled(t *testing.T) { histories := BalanceHistories{{Time: 100}} w := &Worker{ fiatRates: &fiat.FiatRates{Enabled: false}, } originalGetter := getTickersForTimestamps defer func() { getTickersForTimestamps = originalGetter }() calls := 0 getTickersForTimestamps = func(_ *fiat.FiatRates, _ []int64, _, _ string) (*[]*common.CurrencyRatesTicker, error) { calls++ tickers := []*common.CurrencyRatesTicker{} return &tickers, nil } err := w.setFiatRateToBalanceHistories(histories, []string{"usd"}, "address") if err != nil { t.Fatalf("setFiatRateToBalanceHistories returned error: %v", err) } if calls != 0 { t.Fatalf("expected 0 ticker lookup calls when fiat rates are disabled, got %d", calls) } } func TestGetSecondaryTicker_SkipsLookupWithoutSecondaryCurrency(t *testing.T) { w := &Worker{ fiatRates: &fiat.FiatRates{Enabled: true}, } originalGetter := getCurrentTicker defer func() { getCurrentTicker = originalGetter }() calls := 0 getCurrentTicker = func(_ *fiat.FiatRates, _, _ string) *common.CurrencyRatesTicker { calls++ return &common.CurrencyRatesTicker{} } ticker := w.getSecondaryTicker("") if ticker != nil { t.Fatalf("expected nil ticker when secondary currency is not requested, got %+v", ticker) } if calls != 0 { t.Fatalf("expected no ticker lookup call, got %d", calls) } } func TestGetSecondaryTicker_PerformsLookupWithSecondaryCurrency(t *testing.T) { w := &Worker{ fiatRates: &fiat.FiatRates{Enabled: true}, } originalGetter := getCurrentTicker defer func() { getCurrentTicker = originalGetter }() calls := 0 expected := &common.CurrencyRatesTicker{Rates: map[string]float32{"usd": 1}} getCurrentTicker = func(_ *fiat.FiatRates, _, _ string) *common.CurrencyRatesTicker { calls++ return expected } ticker := w.getSecondaryTicker("usd") if ticker != expected { t.Fatalf("unexpected ticker returned: got %+v, want %+v", ticker, expected) } if calls != 1 { t.Fatalf("expected one ticker lookup call, got %d", calls) } } type assertError string func (e assertError) Error() string { return string(e) }