mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-05-13 23:16:45 +00:00
If one asset is enabled but has no enabled pairs, we should ignore that asset, even for non-pair related subscriptions. That matches the existing code, but wasn't happening in the context of asset.All subscriptions with just one asset in this state. If a user wanted to have non-pair subscriptions, they should use asset.Empty. Would expect other breakages with no pairs enabled, too. Note: No enabled pairs for an enabled asset is a transient issue which can occur due to enableOnePair racing against no available pairs. The second run, available pairs would be populated and enableOnePair would work. This situation could persist due to running with --dry or containerised, though. Fixes: ` Okx websocket: subscription failure, myOrders all : subscription template did not generate the expected number of pair records for spread: Got 1; Expected 0 ` Relates to #1420
132 lines
3.8 KiB
Go
132 lines
3.8 KiB
Go
package subscription
|
|
|
|
import (
|
|
"maps"
|
|
"strings"
|
|
"testing"
|
|
"text/template"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"github.com/thrasher-corp/gocryptotrader/common"
|
|
"github.com/thrasher-corp/gocryptotrader/currency"
|
|
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
|
|
)
|
|
|
|
type mockEx struct {
|
|
pairs assetPairs
|
|
assets asset.Items
|
|
tpl string
|
|
auth bool
|
|
errPairs error
|
|
errFormat error
|
|
}
|
|
|
|
func newMockEx() *mockEx {
|
|
return &mockEx{
|
|
assets: asset.Items{asset.Spot, asset.Futures, asset.Index},
|
|
pairs: assetPairs{
|
|
asset.Spot: currency.Pairs{ethusdcPair, btcusdtPair, currency.NewPair(currency.LTC, currency.USDT)},
|
|
asset.Futures: currency.Pairs{ethusdcPair, btcusdtPair},
|
|
asset.Index: currency.Pairs{btcusdtPair},
|
|
},
|
|
}
|
|
}
|
|
|
|
func (m *mockEx) IsAssetWebsocketSupported(a asset.Item) bool {
|
|
return a != asset.Index
|
|
}
|
|
|
|
func (m *mockEx) GetEnabledPairs(a asset.Item) (currency.Pairs, error) {
|
|
return m.pairs[a], m.errPairs
|
|
}
|
|
|
|
func (m *mockEx) GetPairFormat(_ asset.Item, _ bool) (currency.PairFormat, error) {
|
|
return currency.PairFormat{Uppercase: true, Delimiter: "-"}, m.errFormat
|
|
}
|
|
|
|
func (m *mockEx) GetSubscriptionTemplate(s *Subscription) (*template.Template, error) {
|
|
if s.Channel == "nil" {
|
|
return nil, nil
|
|
}
|
|
return template.New(m.tpl).
|
|
Funcs(template.FuncMap{
|
|
"assetName": func(a asset.Item) string {
|
|
if a == asset.Futures {
|
|
return "future"
|
|
}
|
|
return a.String()
|
|
},
|
|
"updateAssetPairs": func(ap assetPairs) string {
|
|
ap[asset.Futures] = nil
|
|
ap[asset.Spot] = ap[asset.Spot][0:1]
|
|
return ""
|
|
},
|
|
"batch": func(pairs currency.Pairs, size int) []string {
|
|
s := []string{}
|
|
for _, p := range common.Batch(pairs, size) {
|
|
p = p.Format(currency.PairFormat{Uppercase: true})
|
|
s = append(s, p.Join())
|
|
}
|
|
return s
|
|
},
|
|
}).
|
|
ParseFiles("testdata/" + m.tpl)
|
|
}
|
|
|
|
func (m *mockEx) GetAssetTypes(_ bool) asset.Items { return m.assets }
|
|
func (m *mockEx) CanUseAuthenticatedWebsocketEndpoints() bool { return m.auth }
|
|
|
|
// equalLists is a utility function to compare subscription lists and show a pretty failure message
|
|
// It overcomes the verbose depth of assert.ElementsMatch spewConfig
|
|
// Duplicate of internal/testing/subscriptions/EqualLists
|
|
func equalLists(tb testing.TB, a, b List) bool {
|
|
tb.Helper()
|
|
for _, sub := range append(a, b...) {
|
|
sub.Key = &StrictKey{&ExactKey{sub}}
|
|
}
|
|
s, err := NewStoreFromList(a)
|
|
require.NoError(tb, err, "NewStoreFromList must not error")
|
|
added, missing := s.Diff(b)
|
|
if len(added) > 0 || len(missing) > 0 {
|
|
fail := "Differences:"
|
|
if len(added) > 0 {
|
|
fail = fail + "\n + " + strings.Join(added.Strings(), "\n + ")
|
|
}
|
|
if len(missing) > 0 {
|
|
fail = fail + "\n - " + strings.Join(missing.Strings(), "\n - ")
|
|
}
|
|
assert.Fail(tb, fail, "Subscriptions should be equal")
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
// StrictKey is key type for subscriptions where all the pairs, QualifiedChannel and Params in a Subscription must match exactly
|
|
type StrictKey struct {
|
|
*ExactKey
|
|
}
|
|
|
|
var _ MatchableKey = StrictKey{} // Enforce StrictKey must implement MatchableKey
|
|
|
|
// Match implements MatchableKey
|
|
// Returns true if the key fields exactly matches the subscription, including all Pairs, QualifiedChannel and Params
|
|
func (k StrictKey) Match(eachKey MatchableKey) bool {
|
|
if !k.ExactKey.Match(eachKey) {
|
|
return false
|
|
}
|
|
eachSub := eachKey.GetSubscription()
|
|
return eachSub.QualifiedChannel == k.QualifiedChannel &&
|
|
maps.Equal(eachSub.Params, k.Params)
|
|
}
|
|
|
|
// String implements Stringer; returns the Asset, Channel and Pairs
|
|
// Does not provide concurrency protection on the subscription it points to
|
|
func (k StrictKey) String() string {
|
|
s := k.Subscription
|
|
if s == nil {
|
|
return "Uninitialised StrictKey"
|
|
}
|
|
return s.QualifiedChannel + " " + ExactKey{s}.String()
|
|
}
|