mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-06-09 07:26:48 +00:00
BTSE: Fix duplicate pair errors on Million pairs (M_*) (#1401)
* BTSE: Fix duplicate error on Million pairs (M_*) BTSE has listed Pitbull token with two symbols: PIT-USD and M_PIT-USD for millons of PIT / USD. The native token is not tradable, so we ignore them and get a base of M_PIT because that's what later APIs will accept * BTSE: Fix test errors on locked market * Common: Improve AppendError and ExcludeError This change switches from a stateful multiError to caring more about the Unwrap() []error interface, the same as [go standard lib](https://github.com/golang/go/blob/go1.21.4/src/errors/wrap.go#L54-L68) Notably, if we implement Unwrap() []error and do NOT implement Is() then we get free compatibility with the core functions. The only distateful thing here is needing to deeply unwrap fmt.Errorf errors, since they don't flatten. I can't see any way around that * Pairs: Fix exchange config Pairs loading When a pair string contained two punctuation runes, the first one is used, and the configFormat is ignored. This fix checks the list and corrects any with the wrong delimiter, or errors if the format is inconsistent. * BTSE: Fix all tickers retrieved by GetTicker PR #764 introduced GetTickers, but it wasn't rolled out to BTSE. This fix ensures that when one ticker is a locked market, the rest continue to function. Particularly important if the locked market wasn't even enabled anyway. * Kucoin: Fix test config future pairs * BTSE: Remove PIT tests; Token removed BTSE have removed the PIT token pairs All these changes stand, and this just removes the test * ITBit: Fix fatal error on second run This fix removes incorrect config pair delimiter, because it would be re-inserted into config the first run, and then error the second time. This delimiter doesn't match the config we have. There's no implementation of fetching pairs, so what's in config files now is all that matters * Engine: Fix TestConfigAllJsonResponse * Clarity of non-matching json improved * Handling for fixing pair delimiters
This commit is contained in:
@@ -35,9 +35,10 @@ var (
|
||||
// ErrSymbolStringEmpty is an error when a symbol string is empty
|
||||
ErrSymbolStringEmpty = errors.New("symbol string is empty")
|
||||
|
||||
errPairStoreIsNil = errors.New("pair store is nil")
|
||||
errPairFormatIsNil = errors.New("pair format is nil")
|
||||
errPairMatcherIsNil = errors.New("pair matcher is nil")
|
||||
errPairStoreIsNil = errors.New("pair store is nil")
|
||||
errPairFormatIsNil = errors.New("pair format is nil")
|
||||
errPairMatcherIsNil = errors.New("pair matcher is nil")
|
||||
errPairConfigFormatNil = errors.New("pair config format is nil")
|
||||
)
|
||||
|
||||
// GetAssetTypes returns a list of stored asset types
|
||||
@@ -458,6 +459,33 @@ func (p *PairsManager) getPairStoreRequiresLock(a asset.Item) (*PairStore, error
|
||||
return pairStore, nil
|
||||
}
|
||||
|
||||
// SetDelimitersFromConfig ensures that the pairs adhere to the configured delimiters
|
||||
// Pairs.Unmarshal doesn't know what the delimiter is, so uses the first punctuation rune
|
||||
func (p *PairsManager) SetDelimitersFromConfig() error {
|
||||
for a, s := range p.Pairs {
|
||||
cf := s.ConfigFormat
|
||||
if cf == nil {
|
||||
cf = p.ConfigFormat
|
||||
}
|
||||
if cf == nil {
|
||||
return errPairConfigFormatNil
|
||||
}
|
||||
for i, p := range []Pairs{s.Enabled, s.Available} {
|
||||
for j := range p {
|
||||
if p[j].Delimiter == cf.Delimiter {
|
||||
continue
|
||||
}
|
||||
nP, err := NewPairDelimiter(p[j].String(), cf.Delimiter)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s.%s.%s: %w", a, []string{"enabled", "available"}[i], p[j], err)
|
||||
}
|
||||
p[j] = nP
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the unmarshal json interface so that the key can be
|
||||
// correctly unmarshalled from a string into a uint.
|
||||
func (fs *FullStore) UnmarshalJSON(d []byte) error {
|
||||
|
||||
@@ -536,7 +536,8 @@ func TestIsAssetEnabled_SetAssetEnabled(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnmarshalMarshal(t *testing.T) {
|
||||
// TestFullStoreUnmarshalMarshal tests json Mashal and Unmarshal
|
||||
func TestFullStoreUnmarshalMarshal(t *testing.T) {
|
||||
t.Parallel()
|
||||
var um = make(FullStore)
|
||||
um[asset.Spot] = &PairStore{AssetEnabled: convert.BoolPtr(true)}
|
||||
@@ -772,3 +773,51 @@ func TestLoad(t *testing.T) {
|
||||
assert.Equal(t, fmt2.Delimiter, base.RequestFormat.Delimiter, "RequestFormat Delimiter should be correct")
|
||||
}
|
||||
}
|
||||
|
||||
func checkPairDelimiter(tb testing.TB, p *PairsManager, err error, d, msg string) {
|
||||
tb.Helper()
|
||||
if assert.NoError(tb, err, "UnmarshalJSON should not error") {
|
||||
err := p.SetDelimitersFromConfig()
|
||||
assert.NoError(tb, err, "SetDelimitersFromConfig should not error")
|
||||
s := p.Pairs[asset.Spot]
|
||||
if assert.NotNil(tb, s, "Spot asset should not be nil") {
|
||||
for _, ps := range []Pairs{s.Enabled, s.Available} {
|
||||
if assert.NotEmpty(tb, ps, "PairStore should not be empty") {
|
||||
for _, p := range ps {
|
||||
assert.Equalf(tb, d, p.Delimiter, msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestPairManagerUnmarshal tests behaviour expectations:
|
||||
// * Should error with no ConfigFormat ( `CheckPairConsistency` catches that )
|
||||
// * Asset pair config should take precedent
|
||||
// * Global store pair config should apply when no Asset pair config
|
||||
func TestPairManagerSetDelimitersFromConfig(t *testing.T) {
|
||||
t.Parallel()
|
||||
p := new(PairsManager)
|
||||
|
||||
err := json.Unmarshal([]byte(`{"pairs":{"spot":{"enabled":"BTC-USD,M_PIT-USDT","available":"BTC-USD,M_PIT-USD"}}}`), p)
|
||||
if assert.NoError(t, err, "UnmarshalJSON should not error") {
|
||||
err = p.SetDelimitersFromConfig()
|
||||
assert.ErrorIs(t, err, errPairConfigFormatNil, "SetDelimitersFromConfig should error correctly")
|
||||
}
|
||||
|
||||
err = json.Unmarshal([]byte(`{"pairs":{"spot":{"configFormat":{"delimiter":"-"},"enabled":"BTC-USD,M_PIT-USDT","available":"BTC-USD,M_PIT-USD"}}}`), p)
|
||||
checkPairDelimiter(t, p, err, "-", "Delimiter should be correct with only bottom level configFormat")
|
||||
|
||||
err = json.Unmarshal([]byte(`{"configFormat":{"delimiter":"/"},"pairs":{"spot":{"enabled":"BTC/USD,M_PIT/USDT","available":"BTC/USD,M_PIT/USD"}}}`), p)
|
||||
checkPairDelimiter(t, p, err, "/", "Delimiter should be correct with top level configFormat")
|
||||
|
||||
err = json.Unmarshal([]byte(`{"configFormat":{"delimiter":"_"},"pairs":{"spot":{"configFormat":{"delimiter":"/"},"enabled":"BTC/USD,M_PIT/USDT","available":"BTC/USD,M_PIT/USD"}}}`), p)
|
||||
checkPairDelimiter(t, p, err, "/", "Delimiter should be correct with bottom level configFormat")
|
||||
|
||||
err = json.Unmarshal([]byte(`{"pairs":{"spot":{"configFormat":{"delimiter":"_"},"enabled":"BTC-USDT","available":"BTC-USDT"}}}`), p)
|
||||
if assert.NoError(t, err, "UnmarshalJSON should not error") {
|
||||
err := p.SetDelimitersFromConfig()
|
||||
assert.ErrorContains(t, err, "spot.enabled.BTC-USDT: delimiter: [_] not found in currencypair string", "SetDelimitersFromConfig should error correctly")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ func (p Pair) Upper() Pair {
|
||||
return p
|
||||
}
|
||||
|
||||
// UnmarshalJSON comforms type to the umarshaler interface
|
||||
// UnmarshalJSON implements json.Unmarshaler
|
||||
func (p *Pair) UnmarshalJSON(d []byte) error {
|
||||
var pair string
|
||||
err := json.Unmarshal(d, &pair)
|
||||
|
||||
@@ -5,6 +5,8 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -46,47 +48,18 @@ func TestUpper(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestPairUnmarshalJSON(t *testing.T) {
|
||||
var unmarshalHere Pair
|
||||
configPair, err := NewPairDelimiter("btc_usd", "_")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var p Pair
|
||||
assert.NoError(t, p.UnmarshalJSON([]byte(`"btc_usd"`)), "UnmarshalJSON should not error")
|
||||
assert.Equal(t, "btc", p.Base.String(), "Base should be correct")
|
||||
assert.Equal(t, "usd", p.Quote.String(), "Quote should be correct")
|
||||
assert.Equal(t, "_", p.Delimiter, "Delimiter should be correct")
|
||||
|
||||
encoded, err := json.Marshal(configPair)
|
||||
if err != nil {
|
||||
t.Fatal("Pair UnmarshalJSON() error", err)
|
||||
}
|
||||
assert.ErrorIs(t, p.UnmarshalJSON([]byte(`"btcusd"`)), errCannotCreatePair, "UnmarshalJSON with no delimiter should error")
|
||||
|
||||
err = json.Unmarshal(encoded, &unmarshalHere)
|
||||
if err != nil {
|
||||
t.Fatal("Pair UnmarshalJSON() error", err)
|
||||
}
|
||||
|
||||
err = json.Unmarshal(encoded, &unmarshalHere)
|
||||
if err != nil {
|
||||
t.Fatal("Pair UnmarshalJSON() error", err)
|
||||
}
|
||||
|
||||
if !unmarshalHere.Equal(configPair) {
|
||||
t.Errorf("Pair UnmarshalJSON() error expected %s but received %s",
|
||||
configPair, unmarshalHere)
|
||||
}
|
||||
|
||||
encoded, err = json.Marshal(EMPTYPAIR)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = json.Unmarshal(encoded, &unmarshalHere)
|
||||
if err != nil {
|
||||
t.Fatal("Pair UnmarshalJSON() error", err)
|
||||
}
|
||||
err = json.Unmarshal([]byte("null"), &unmarshalHere)
|
||||
if err != nil {
|
||||
t.Fatal("Pair UnmarshalJSON() error", err)
|
||||
}
|
||||
if unmarshalHere != EMPTYPAIR {
|
||||
t.Fatalf("Expected EMPTYPAIR got: %s", unmarshalHere)
|
||||
}
|
||||
assert.NoError(t, p.UnmarshalJSON([]byte(`""`)), "UnmarshalJSON should not error on empty value")
|
||||
assert.Equal(t, EMPTYPAIR, p, "UnmarshalJSON empty value should give EMPTYPAIR")
|
||||
assert.NoError(t, p.UnmarshalJSON([]byte(`null`)), "UnmarshalJSON should not error on empty value")
|
||||
assert.Equal(t, EMPTYPAIR, p, "UnmarshalJSON null value should give EMPTYPAIR")
|
||||
}
|
||||
|
||||
func TestPairMarshalJSON(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user