Files
gocryptotrader/exchanges/subscription/template_test.go
cranktakular fd9aaf00a2 Coinbase: Update exchange implementation (#1480)
* Slight enhance of Coinbase tests

Continual enhance of Coinbase tests

The revamp continues

Oh jeez the Orderbook part's unfinished don't look

Coinbase revamp, Orderbook still unfinished

* Coinbase revamp; CreateReport is still WIP

* More coinbase improvements; onto sandbox testing

* Coinbase revamp continues

* Coinbase revamp continues

* Coinbasepro revamp is ceaseless

* Coinbase revamp, starting on advanced trade API

* Coinbase Advanced Trade Starts in Ernest

V3 done, onto V2

Coinbase revamp nears completion

Coinbase revamp nears completion

Test commit should fail

Coinbase revamp nears completion

* Coinbase revamp stage wrapper

* Coinbase wrapper coherence continues

* Coinbase wrapper continues writhing

* Coinbase wrapper & codebase cleanup

* Coinbase updates & wrap progress

* More Coinbase wrapper progress

* Wrapper is wrapped, kinda

* Test & type checking

* Coinbase REST revamp finished

* Post-merge fix

* WS revamp begins

* WS Main Revamp Done?

* CB websocket tidying up

* Coinbase WS wrapperupperer

* Coinbase revamp done??

* Linter progress

* Continued lint cleanup

* Further lint cleanup

* Increased lint coverage

* Does this fix all sloppy reassigns & shadowing?

* Undoing retry policy change

* Documentation regeneration

* Coinbase code improvements

* Providing warning about known issue

* Updating an error to new format

* Making gocritic happy

* Review adherence

* Endpoints moved to V3 & nil pointer fixes

* Removing seemingly superfluous constant

* Glorious improvements

* Removing unused error

* Partial public endpoint addition

* Slight improvements

* Wrapper improvements; still a few errors left in other packages

* A lil Coinbase progress

* Json cleaning

* Lint appeasement

* Config repair

* Config fix (real)

* Little fix

* New public endpoint incorporation

* Additional fixes

* Improvements & Appeasements

* LineSaver

* Additional fixes

* Another fix

* Fixing picked nits

* Quick fixies

* Lil fixes

* Subscriptions: Add List.Enabled

* CoinbasePro: Add subscription templating

* fixup! CoinbasePro: Add subscription templating

* fixup! CoinbasePro: Add subscription templating

* Comment fix

* Subsequent fixes

* Issues hopefully fixed

* Lint fix

* Glorious fixes

* Json formatting

* ShazNits

* (L/N)i(n/)t

* Adding a test

* Tiny test improvement

* Template patch testing

* Fixes

* Further shaznits

* Lint nit

* JWT move and other fixes

* Small nits

* Shaznit, singular

* Post-merge fix

* Post-merge fixes

* Typo fix

* Some glorious nits

* Required changes

* Stop going

* Alias attempt

* Alias fix & test cleanup

* Test fix

* GetDepositAddress logic improvement

* Status update: Fixed

* Lint fix

* Happy birthday to PR 1480

* Cleanups

* Necessary nit corrections

* Fixing sillybug

* As per request

* Programming progress

* Order fixes

* Further fixies

* Test fix

* Pre-merge fixes

* More shaznits

* Context

* Sonic error handling

* Import fix

* Better Sonic error handling

* Perfect Sonic error handling?

* F purge

* Coinbase improvements

* API Update Conformity

* Coinbase continuation

* Coinbase order improvements

* Coinbase order improvements

* CreateOrderConfig improvements

* Managing API updates

* Coinbase API update progression

* jwt rename

* Comment link fix

* Coinbase v2 cleanup

* Post-merge fixes

* Review fixes

* GK's suggestions

* Linter fix

* Minor gbjk fixes

* Nit fixes

* Merge fix

* Lint fixes

* Coinbase rename stage 1

* Coinbase rename stage 2

* Coinbase rename stage 3

* Coinbase rename stage 4

* Coinbase rename final fix

* Coinbase: PoC on converting to request structs

* Applying requested changes

* Many review fixes, handled

* Thrashed by nits

* More minor modifications

* The last nit!?

---------

Co-authored-by: Gareth Kirwan <gbjkirwan@gmail.com>
2025-09-16 13:37:00 +10:00

160 lines
7.4 KiB
Go

package subscription
import (
"errors"
"testing"
"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"
"github.com/thrasher-corp/gocryptotrader/exchanges/kline"
)
// TestExpandTemplates exercises ExpandTemplates
func TestExpandTemplates(t *testing.T) {
t.Parallel()
e := newMockEx()
e.tpl = "subscriptions.tmpl"
// Functionality tests
l := List{
{Channel: "single-channel"},
{Channel: "expand-assets", Asset: asset.All, Interval: kline.FifteenMin},
{Channel: "expand-pairs", Asset: asset.All, Levels: 1},
{Channel: "expand-pairs", Asset: asset.Spot, Levels: 2},
{Channel: "single-channel", QualifiedChannel: "just one sub already processed"},
{Channel: "update-asset-pairs", Asset: asset.All},
{Channel: "expand-pairs", Asset: asset.Spot, Pairs: e.pairs[asset.Spot][0:2], Levels: 3},
{Channel: "batching", Asset: asset.Spot},
{Channel: "single-channel", Authenticated: true},
}
_, err := l.ExpandTemplates(&mockExWithSubValidator{mockEx: e, GenerateBadSubscription: true})
require.ErrorIs(t, err, errValidateSubscriptionsTestError)
_, err = l.ExpandTemplates(&mockExWithSubValidator{mockEx: e})
require.NoError(t, err)
_, err = l.ExpandTemplates(&mockExWithSubValidator{mockEx: e, FailGetSubscriptions: true})
require.ErrorIs(t, err, ErrNotFound)
got, err := l.ExpandTemplates(e)
require.NoError(t, err, "ExpandTemplates must not error")
exp := List{
{Channel: "single-channel", QualifiedChannel: "single-channel"},
{Channel: "expand-assets", QualifiedChannel: "spot-expand-assets@15m", Asset: asset.Spot, Pairs: e.pairs[asset.Spot], Interval: kline.FifteenMin},
{Channel: "expand-assets", QualifiedChannel: "future-expand-assets@15m", Asset: asset.Futures, Pairs: e.pairs[asset.Futures], Interval: kline.FifteenMin},
{Channel: "single-channel", QualifiedChannel: "just one sub already processed"},
{Channel: "update-asset-pairs", QualifiedChannel: "spot-btcusdt-update-asset-pairs", Asset: asset.Spot, Pairs: currency.Pairs{btcusdtPair}},
{Channel: "expand-pairs", QualifiedChannel: "spot-USDTBTC-expand-pairs@3", Asset: asset.Spot, Pairs: e.pairs[asset.Spot][1:2], Levels: 3},
{Channel: "expand-pairs", QualifiedChannel: "spot-USDCETH-expand-pairs@3", Asset: asset.Spot, Pairs: e.pairs[asset.Spot][:1], Levels: 3},
}
for a, pairs := range e.pairs {
if a == asset.Index { // Not IsAssetWebsocketEnabled
continue
}
for _, p := range common.SortStrings(pairs) {
pStr := p.Swap().String()
if a == asset.Spot {
exp = append(exp, List{
{Channel: "expand-pairs", QualifiedChannel: "spot-" + pStr + "-expand-pairs@1", Asset: a, Pairs: currency.Pairs{p}, Levels: 1},
&Subscription{Channel: "expand-pairs", QualifiedChannel: "spot-" + pStr + "-expand-pairs@2", Asset: a, Pairs: currency.Pairs{p}, Levels: 2},
}...)
} else {
exp = append(exp,
&Subscription{Channel: "expand-pairs", QualifiedChannel: "future-" + pStr + "-expand-pairs@1", Asset: a, Pairs: currency.Pairs{p}, Levels: 1},
)
}
}
}
for _, b := range common.Batch(common.SortStrings(e.pairs[asset.Spot]), 3) {
exp = append(exp, &Subscription{Channel: "batching", QualifiedChannel: "spot-" + b.Join() + "-batching", Asset: asset.Spot, Pairs: b})
}
if !equalLists(t, exp, got) {
t.FailNow() // If the first list isn't equal testing it again will duplicate test failures
}
e.auth = true
got, err = l.ExpandTemplates(e)
require.NoError(t, err, "ExpandTemplates must not error")
exp = append(exp, &Subscription{Channel: "single-channel", QualifiedChannel: "single-channel-authed"})
equalLists(t, exp, got)
// Test with just one asset to ensure asset.All works, and disabled assets don't error
e.assets = e.assets[:1]
l = List{
{Channel: "expand-assets", Asset: asset.All, Interval: kline.OneHour},
{Channel: "expand-pairs", Asset: asset.All, Levels: 4},
{Channel: "single-channel", Asset: asset.Futures},
}
got, err = l.ExpandTemplates(e)
require.NoError(t, err, "ExpandTemplates must not error")
exp = List{
{Channel: "expand-assets", QualifiedChannel: "spot-expand-assets@1h", Asset: asset.Spot, Pairs: e.pairs[asset.Spot], Interval: kline.OneHour},
}
for _, p := range e.pairs[asset.Spot] {
exp = append(exp, List{
{Channel: "expand-pairs", QualifiedChannel: "spot-" + p.Swap().String() + "-expand-pairs@4", Asset: asset.Spot, Pairs: currency.Pairs{p}, Levels: 4},
}...)
}
equalLists(t, exp, got)
// Users can specify pairs which aren't available, even across diverse assets
// Use-case: Coinbase user sub for futures BTC-USD would return all BTC pairs and all USD pairs, even though BTC-USD might not be enabled or available
p := currency.Pairs{currency.NewPairWithDelimiter("BEAR", "PEAR", "🐻")}
got, err = List{{Channel: "expand-pairs", Asset: asset.All, Pairs: p}}.ExpandTemplates(e)
require.NoError(t, err, "Must not error with fictional pairs")
exp = List{{Channel: "expand-pairs", QualifiedChannel: "spot-PEARBEAR-expand-pairs@0", Asset: asset.Spot, Pairs: p}}
equalLists(t, exp, got)
// Error cases
_, err = List{{Channel: "nil"}}.ExpandTemplates(e)
assert.ErrorIs(t, err, errInvalidTemplate, "Should get correct error on nil template")
e.tpl = "errors.tmpl"
_, err = List{{Channel: "error1"}}.ExpandTemplates(e)
assert.ErrorContains(t, err, "wrong number of args for String", "Should error correctly with execution error")
_, err = List{{Channel: "empty-content", Asset: asset.Spot}}.ExpandTemplates(e)
assert.ErrorIs(t, err, errNoTemplateContent, "Should error correctly when no content generated")
assert.ErrorContains(t, err, "empty-content", "Should error correctly when no content generated")
_, err = List{{Channel: "error2", Asset: asset.All}}.ExpandTemplates(e)
assert.ErrorIs(t, err, errAssetRecords, "Should error correctly when invalid number of asset entries")
_, err = List{{Channel: "error3", Asset: asset.Spot}}.ExpandTemplates(e)
assert.ErrorIs(t, err, errPairRecords, "Should error correctly when invalid number of pair entries")
_, err = List{{Channel: "error4", Asset: asset.Spot}}.ExpandTemplates(e)
assert.ErrorIs(t, err, errTooManyBatchSizePerAsset, "Should error correctly when too many BatchSize directives")
_, err = List{{Channel: "error5", Asset: asset.Spot}}.ExpandTemplates(e)
assert.ErrorIs(t, err, common.ErrTypeAssertFailure, "Should error correctly when batch size isn't an int")
e.tpl = "parse-error.tmpl"
e.tpl = "parse-error.tmpl"
_, err = l.ExpandTemplates(e)
assert.ErrorContains(t, err, "function \"explode\" not defined", "Should error correctly on unparsable template")
e.errFormat = errors.New("the planet Krypton is gone")
_, err = l.ExpandTemplates(e)
assert.ErrorIs(t, err, e.errFormat, "Should error correctly on GetPairFormat")
e.errPairs = errors.New("bad parenting from Jor-El")
_, err = l.ExpandTemplates(e)
assert.ErrorIs(t, err, e.errPairs, "Should error correctly on GetEnabledPairs")
l = List{{Channel: "single-channel", QualifiedChannel: "already happy"}}
got, err = l.ExpandTemplates(e)
require.NoError(t, err)
require.Len(t, got, 1, "Must get back the one sub")
assert.Equal(t, "already happy", l[0].QualifiedChannel, "Should get back the one sub")
assert.NotSame(t, &got, &l, "Should get back a different actual list")
_, err = List{{Channel: "nil"}}.ExpandTemplates(e)
assert.ErrorIs(t, err, errInvalidTemplate, "Should get correct error on nil template")
}