mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-05-13 23:16:45 +00:00
Config: Refactor version packages (#1887)
* Config: Move config versions to separate pacakges * Config: Move version tests to blackbox texts * Config: Protect registerVersion from overflow * Config: Protect against version already registered
This commit is contained in:
21
config/versions/register.go
Normal file
21
config/versions/register.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package versions
|
||||
|
||||
import (
|
||||
v0 "github.com/thrasher-corp/gocryptotrader/config/versions/v0"
|
||||
v1 "github.com/thrasher-corp/gocryptotrader/config/versions/v1"
|
||||
v2 "github.com/thrasher-corp/gocryptotrader/config/versions/v2"
|
||||
v3 "github.com/thrasher-corp/gocryptotrader/config/versions/v3"
|
||||
v4 "github.com/thrasher-corp/gocryptotrader/config/versions/v4"
|
||||
v5 "github.com/thrasher-corp/gocryptotrader/config/versions/v5"
|
||||
v6 "github.com/thrasher-corp/gocryptotrader/config/versions/v6"
|
||||
)
|
||||
|
||||
func init() {
|
||||
Manager.registerVersion(0, &v0.Version{})
|
||||
Manager.registerVersion(1, &v1.Version{})
|
||||
Manager.registerVersion(2, &v2.Version{})
|
||||
Manager.registerVersion(3, &v3.Version{})
|
||||
Manager.registerVersion(4, &v4.Version{})
|
||||
Manager.registerVersion(5, &v5.Version{})
|
||||
Manager.registerVersion(6, &v6.Version{})
|
||||
}
|
||||
49
config/versions/register_test.go
Normal file
49
config/versions/register_test.go
Normal file
@@ -0,0 +1,49 @@
|
||||
package versions_test
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/thrasher-corp/gocryptotrader/config/versions"
|
||||
testutils "github.com/thrasher-corp/gocryptotrader/internal/testing/utils"
|
||||
)
|
||||
|
||||
func TestVersionsRegistered(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
r, err := testutils.RootPathFromCWD()
|
||||
require.NoError(t, err)
|
||||
|
||||
versionsDir := filepath.Join(r, "config", "versions")
|
||||
_, err = os.Stat(versionsDir)
|
||||
require.NoError(t, err, "config/versions must exist")
|
||||
|
||||
err = filepath.WalkDir(versionsDir, func(p string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !d.IsDir() || p == versionsDir {
|
||||
return nil
|
||||
}
|
||||
verStr := filepath.Base(p)
|
||||
verMatch := regexp.MustCompile(`v(\d+)`).FindStringSubmatch(verStr)
|
||||
if len(verMatch) != 2 {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
t.Run(verStr, func(t *testing.T) {
|
||||
version, err := strconv.ParseUint(verMatch[1], 10, 16)
|
||||
require.NoError(t, err, "verMatch must ParseUint without error")
|
||||
v := versions.Manager.Version(uint16(version))
|
||||
require.NotNil(t, v, "version.Manager init must register this version")
|
||||
require.Contains(t, reflect.TypeOf(v).String(), "*"+verStr+".Version", "version registered must be the correct type")
|
||||
})
|
||||
return filepath.SkipDir
|
||||
})
|
||||
require.NoError(t, err, "WalkDir must not error")
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
package versions
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
// Version0 is a baseline version with no changes, so we can downgrade back to nothing
|
||||
// It does not implement any upgrade interfaces
|
||||
type Version0 struct{}
|
||||
|
||||
func init() {
|
||||
Manager.registerVersion(0, &Version0{})
|
||||
}
|
||||
|
||||
// UpgradeConfig is an empty stub
|
||||
func (v *Version0) UpgradeConfig(_ context.Context, j []byte) ([]byte, error) {
|
||||
return j, nil
|
||||
}
|
||||
|
||||
// DowngradeConfig is an empty stub
|
||||
func (v *Version0) DowngradeConfig(_ context.Context, j []byte) ([]byte, error) {
|
||||
return j, nil
|
||||
}
|
||||
19
config/versions/v0/v0.go
Normal file
19
config/versions/v0/v0.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package v0
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
// Version is a baseline version with no changes, so we can downgrade back to nothing
|
||||
// It does not implement any upgrade interfaces
|
||||
type Version struct{}
|
||||
|
||||
// UpgradeConfig is an empty stub
|
||||
func (*Version) UpgradeConfig(_ context.Context, j []byte) ([]byte, error) {
|
||||
return j, nil
|
||||
}
|
||||
|
||||
// DowngradeConfig is an empty stub
|
||||
func (*Version) DowngradeConfig(_ context.Context, j []byte) ([]byte, error) {
|
||||
return j, nil
|
||||
}
|
||||
26
config/versions/v0/v0_test.go
Normal file
26
config/versions/v0/v0_test.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package v0_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
v0 "github.com/thrasher-corp/gocryptotrader/config/versions/v0"
|
||||
)
|
||||
|
||||
func TestUpgradeConfig(t *testing.T) {
|
||||
t.Parallel()
|
||||
in := []byte(`{"untouched":true}`)
|
||||
out, err := new(v0.Version).UpgradeConfig(context.Background(), in)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, in, out)
|
||||
}
|
||||
|
||||
func TestDowngradeConfig(t *testing.T) {
|
||||
t.Parallel()
|
||||
in := []byte(`{"untouched":true}`)
|
||||
out, err := new(v0.Version).DowngradeConfig(context.Background(), in)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, in, out)
|
||||
}
|
||||
@@ -1,26 +1,21 @@
|
||||
package versions
|
||||
package v1
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/buger/jsonparser"
|
||||
v0 "github.com/thrasher-corp/gocryptotrader/config/versions/v0"
|
||||
v1 "github.com/thrasher-corp/gocryptotrader/config/versions/v1"
|
||||
"github.com/thrasher-corp/gocryptotrader/encoding/json"
|
||||
)
|
||||
|
||||
// Version1 is an ExchangeVersion to upgrade currency pair format for exchanges
|
||||
type Version1 struct{}
|
||||
|
||||
func init() {
|
||||
Manager.registerVersion(1, &Version1{})
|
||||
}
|
||||
// Version is an ExchangeVersion to upgrade currency pair format for exchanges
|
||||
type Version struct{}
|
||||
|
||||
// Exchanges returns all exchanges: "*"
|
||||
func (v *Version1) Exchanges() []string { return []string{"*"} }
|
||||
func (*Version) Exchanges() []string { return []string{"*"} }
|
||||
|
||||
// UpgradeExchange will upgrade currency pair format
|
||||
func (v *Version1) UpgradeExchange(_ context.Context, e []byte) ([]byte, error) {
|
||||
func (*Version) UpgradeExchange(_ context.Context, e []byte) ([]byte, error) {
|
||||
if _, d, _, err := jsonparser.Get(e, "currencyPairs"); err == nil && d == jsonparser.Object {
|
||||
return e, nil
|
||||
}
|
||||
@@ -30,12 +25,12 @@ func (v *Version1) UpgradeExchange(_ context.Context, e []byte) ([]byte, error)
|
||||
return e, err
|
||||
}
|
||||
|
||||
p := &v1.PairsManager{
|
||||
p := &PairsManager{
|
||||
UseGlobalFormat: true,
|
||||
LastUpdated: d.PairsLastUpdated,
|
||||
ConfigFormat: d.ConfigCurrencyPairFormat,
|
||||
RequestFormat: d.RequestCurrencyPairFormat,
|
||||
Pairs: v1.FullStore{
|
||||
Pairs: FullStore{
|
||||
"spot": {
|
||||
Available: d.AvailablePairs,
|
||||
Enabled: d.EnabledPairs,
|
||||
@@ -53,6 +48,6 @@ func (v *Version1) UpgradeExchange(_ context.Context, e []byte) ([]byte, error)
|
||||
}
|
||||
|
||||
// DowngradeExchange doesn't do anything for v1; There's no downgrade path since the original state is lossy and v1 was before versioning
|
||||
func (v *Version1) DowngradeExchange(_ context.Context, e []byte) ([]byte, error) {
|
||||
func (*Version) DowngradeExchange(_ context.Context, e []byte) ([]byte, error) {
|
||||
return e, nil
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package versions
|
||||
package v1_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -7,12 +7,18 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
v1 "github.com/thrasher-corp/gocryptotrader/config/versions/v1"
|
||||
)
|
||||
|
||||
func TestVersion1Upgrade(t *testing.T) {
|
||||
func TestExchanges(t *testing.T) {
|
||||
t.Parallel()
|
||||
assert.Equal(t, []string{"*"}, new(v1.Version).Exchanges())
|
||||
}
|
||||
|
||||
func TestUpgradeExchange(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
v := &Version1{}
|
||||
v := &v1.Version{}
|
||||
in := []byte(`{"name":"Wibble","pairsLastUpdated":1566798411,"assetTypes":"spot","configCurrencyPairFormat":{"uppercase":true,"delimiter":"_"},"requestCurrencyPairFormat":{"uppercase":false,"delimiter":"_","separator":"-"},"enabledPairs":"LTC_BTC","availablePairs":"LTC_BTC,ETH_BTC,BTC_USD"}`)
|
||||
exp := []byte(`{"name":"Wibble","currencyPairs":{"bypassConfigFormatUpgrades":false,"requestFormat":{"uppercase":false,"delimiter":"_","separator":"-"},"configFormat":{"uppercase":true,"delimiter":"_"},"useGlobalFormat":true,"lastUpdated":1566798411,"pairs":{"spot":{"enabled":"LTC_BTC","available":"LTC_BTC,ETH_BTC,BTC_USD"}}}}`)
|
||||
|
||||
@@ -22,10 +28,10 @@ func TestVersion1Upgrade(t *testing.T) {
|
||||
assert.Equal(t, string(exp), string(out))
|
||||
}
|
||||
|
||||
func TestVersion1Downgrade(t *testing.T) {
|
||||
func TestDowngradeExchange(t *testing.T) {
|
||||
t.Parallel()
|
||||
in := []byte("just leave me alone, mkay?")
|
||||
out, err := new(Version1).DowngradeExchange(context.Background(), bytes.Clone(in))
|
||||
out, err := new(v1.Version).DowngradeExchange(context.Background(), bytes.Clone(in))
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, out, in)
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package versions
|
||||
package v2
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -6,18 +6,14 @@ import (
|
||||
"github.com/buger/jsonparser"
|
||||
)
|
||||
|
||||
// Version2 is an ExchangeVersion to change the name of GDAX to CoinbasePro
|
||||
type Version2 struct{}
|
||||
|
||||
func init() {
|
||||
Manager.registerVersion(2, &Version2{})
|
||||
}
|
||||
// Version is an ExchangeVersion to change the name of GDAX to CoinbasePro
|
||||
type Version struct{}
|
||||
|
||||
// Exchanges returns just GDAX and CoinbasePro
|
||||
func (v *Version2) Exchanges() []string { return []string{"GDAX", "CoinbasePro"} }
|
||||
func (*Version) Exchanges() []string { return []string{"GDAX", "CoinbasePro"} }
|
||||
|
||||
// UpgradeExchange will change the exchange name from GDAX to CoinbasePro
|
||||
func (v *Version2) UpgradeExchange(_ context.Context, e []byte) ([]byte, error) {
|
||||
func (*Version) UpgradeExchange(_ context.Context, e []byte) ([]byte, error) {
|
||||
if n, err := jsonparser.GetString(e, "name"); err == nil && n == "GDAX" {
|
||||
return jsonparser.Set(e, []byte(`"CoinbasePro"`), "name")
|
||||
}
|
||||
@@ -25,7 +21,7 @@ func (v *Version2) UpgradeExchange(_ context.Context, e []byte) ([]byte, error)
|
||||
}
|
||||
|
||||
// DowngradeExchange will change the exchange name from CoinbasePro to GDAX
|
||||
func (v *Version2) DowngradeExchange(_ context.Context, e []byte) ([]byte, error) {
|
||||
func (*Version) DowngradeExchange(_ context.Context, e []byte) ([]byte, error) {
|
||||
if n, err := jsonparser.GetString(e, "name"); err == nil && n == "CoinbasePro" {
|
||||
return jsonparser.Set(e, []byte(`"GDAX"`), "name")
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package versions
|
||||
package v2_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -6,30 +6,31 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
v2 "github.com/thrasher-corp/gocryptotrader/config/versions/v2"
|
||||
)
|
||||
|
||||
func TestVersion2Upgrade(t *testing.T) {
|
||||
func TestUpgradeExchange(t *testing.T) {
|
||||
t.Parallel()
|
||||
for _, tt := range [][]string{
|
||||
{"GDAX", "CoinbasePro"},
|
||||
{"Kraken", "Kraken"},
|
||||
{"CoinbasePro", "CoinbasePro"},
|
||||
} {
|
||||
out, err := new(Version2).UpgradeExchange(context.Background(), []byte(`{"name":"`+tt[0]+`"}`))
|
||||
out, err := new(v2.Version).UpgradeExchange(context.Background(), []byte(`{"name":"`+tt[0]+`"}`))
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, out)
|
||||
assert.Equalf(t, `{"name":"`+tt[1]+`"}`, string(out), "Test exchange name %s", tt[0])
|
||||
}
|
||||
}
|
||||
|
||||
func TestVersion2Downgrade(t *testing.T) {
|
||||
func TestDowngradeExchange(t *testing.T) {
|
||||
t.Parallel()
|
||||
for _, tt := range [][]string{
|
||||
{"GDAX", "GDAX"},
|
||||
{"Kraken", "Kraken"},
|
||||
{"CoinbasePro", "GDAX"},
|
||||
} {
|
||||
out, err := new(Version2).DowngradeExchange(context.Background(), []byte(`{"name":"`+tt[0]+`"}`))
|
||||
out, err := new(v2.Version).DowngradeExchange(context.Background(), []byte(`{"name":"`+tt[0]+`"}`))
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, out)
|
||||
assert.Equalf(t, `{"name":"`+tt[1]+`"}`, string(out), "Test exchange name %s", tt[0])
|
||||
@@ -1,4 +1,4 @@
|
||||
package versions
|
||||
package v3
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -8,18 +8,14 @@ import (
|
||||
"github.com/thrasher-corp/gocryptotrader/encoding/json"
|
||||
)
|
||||
|
||||
// Version3 is an ExchangeVersion to remove the publishPeriod from the exchange's orderbook config
|
||||
type Version3 struct{}
|
||||
|
||||
func init() {
|
||||
Manager.registerVersion(3, &Version3{})
|
||||
}
|
||||
// Version is an ExchangeVersion to remove the publishPeriod from the exchange's orderbook config
|
||||
type Version struct{}
|
||||
|
||||
// Exchanges returns all exchanges: "*"
|
||||
func (v *Version3) Exchanges() []string { return []string{"*"} }
|
||||
func (*Version) Exchanges() []string { return []string{"*"} }
|
||||
|
||||
// UpgradeExchange will remove the publishPeriod from the exchange's orderbook config
|
||||
func (v *Version3) UpgradeExchange(_ context.Context, e []byte) ([]byte, error) {
|
||||
func (*Version) UpgradeExchange(_ context.Context, e []byte) ([]byte, error) {
|
||||
e = jsonparser.Delete(e, "orderbook", "publishPeriod")
|
||||
return e, nil
|
||||
}
|
||||
@@ -27,7 +23,7 @@ func (v *Version3) UpgradeExchange(_ context.Context, e []byte) ([]byte, error)
|
||||
const defaultOrderbookPublishPeriod = time.Second * 10
|
||||
|
||||
// DowngradeExchange will downgrade the exchange's config by setting the default orderbook publish period
|
||||
func (v *Version3) DowngradeExchange(_ context.Context, e []byte) ([]byte, error) {
|
||||
func (*Version) DowngradeExchange(_ context.Context, e []byte) ([]byte, error) {
|
||||
if _, _, _, err := jsonparser.Get(e, "orderbook"); err != nil {
|
||||
return e, nil //nolint:nilerr // No error, just return the original config
|
||||
}
|
||||
@@ -1,42 +1,43 @@
|
||||
package versions
|
||||
package v3_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
v3 "github.com/thrasher-corp/gocryptotrader/config/versions/v3"
|
||||
)
|
||||
|
||||
func TestVersion3UpgradeExchange(t *testing.T) {
|
||||
func TestUpgradeExchange(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
got, err := (&Version3{}).UpgradeExchange(context.Background(), nil)
|
||||
got, err := (&v3.Version{}).UpgradeExchange(context.Background(), nil)
|
||||
require.NoError(t, err)
|
||||
require.Nil(t, got)
|
||||
|
||||
payload := []byte(`{"orderbook": {"verificationBypass": false,"websocketBufferLimit": 5,"websocketBufferEnabled": false,"publishPeriod": 10000000000}}`)
|
||||
expected := []byte(`{"orderbook": {"verificationBypass": false,"websocketBufferLimit": 5,"websocketBufferEnabled": false}}`)
|
||||
got, err = (&Version3{}).UpgradeExchange(context.Background(), payload)
|
||||
got, err = (&v3.Version{}).UpgradeExchange(context.Background(), payload)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, expected, got)
|
||||
}
|
||||
|
||||
func TestVersion3DowngradeExchange(t *testing.T) {
|
||||
func TestDowngradeExchange(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
got, err := (&Version3{}).DowngradeExchange(context.Background(), nil)
|
||||
got, err := (&v3.Version{}).DowngradeExchange(context.Background(), nil)
|
||||
require.NoError(t, err)
|
||||
require.Nil(t, got)
|
||||
|
||||
payload := []byte(`{"orderbook": {"verificationBypass": false,"websocketBufferLimit": 5,"websocketBufferEnabled": false}}`)
|
||||
expected := []byte(`{"orderbook": {"verificationBypass": false,"websocketBufferLimit": 5,"websocketBufferEnabled": false,"publishPeriod":10000000000}}`)
|
||||
got, err = (&Version3{}).DowngradeExchange(context.Background(), payload)
|
||||
got, err = (&v3.Version{}).DowngradeExchange(context.Background(), payload)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, expected, got)
|
||||
}
|
||||
|
||||
func TestVersion3Exchanges(t *testing.T) {
|
||||
func TestExchanges(t *testing.T) {
|
||||
t.Parallel()
|
||||
assert := require.New(t)
|
||||
assert.Equal([]string{"*"}, (&Version3{}).Exchanges())
|
||||
assert.Equal([]string{"*"}, (&v3.Version{}).Exchanges())
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package versions
|
||||
package v4
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -10,18 +10,14 @@ import (
|
||||
"github.com/buger/jsonparser"
|
||||
)
|
||||
|
||||
// Version4 is an Exchange upgrade to move currencyPairs.assetTypes to currencyPairs.pairs.*.assetEnabled
|
||||
type Version4 struct{}
|
||||
|
||||
func init() {
|
||||
Manager.registerVersion(4, &Version4{})
|
||||
}
|
||||
// Version is an Exchange upgrade to move currencyPairs.assetTypes to currencyPairs.pairs.*.assetEnabled
|
||||
type Version struct{}
|
||||
|
||||
// Exchanges returns all exchanges: "*"
|
||||
func (v *Version4) Exchanges() []string { return []string{"*"} }
|
||||
func (*Version) Exchanges() []string { return []string{"*"} }
|
||||
|
||||
// UpgradeExchange sets AssetEnabled: true for all assets listed in assetTypes, and false for any with no field
|
||||
func (v *Version4) UpgradeExchange(_ context.Context, e []byte) ([]byte, error) {
|
||||
func (*Version) UpgradeExchange(_ context.Context, e []byte) ([]byte, error) {
|
||||
toEnable := map[string]bool{}
|
||||
|
||||
assetTypesFn := func(asset []byte, valueType jsonparser.ValueType, _ int, _ error) {
|
||||
@@ -31,7 +27,7 @@ func (v *Version4) UpgradeExchange(_ context.Context, e []byte) ([]byte, error)
|
||||
}
|
||||
_, err := jsonparser.ArrayEach(e, assetTypesFn, "currencyPairs", "assetTypes")
|
||||
if err != nil && !errors.Is(err, jsonparser.KeyPathNotFoundError) {
|
||||
return e, fmt.Errorf("%w assetTypes: %w", errUpgrading, err)
|
||||
return e, fmt.Errorf("error upgrading assetTypes: %w", err)
|
||||
}
|
||||
|
||||
assetEnabledFn := func(assetBytes, v []byte, _ jsonparser.ValueType, _ int) (err error) {
|
||||
@@ -54,14 +50,14 @@ func (v *Version4) UpgradeExchange(_ context.Context, e []byte) ([]byte, error)
|
||||
return err
|
||||
}
|
||||
if err = jsonparser.ObjectEach(bytes.Clone(e), assetEnabledFn, "currencyPairs", "pairs"); err != nil {
|
||||
return e, fmt.Errorf("%w currencyPairs.pairs: %w", errUpgrading, err)
|
||||
return e, fmt.Errorf("error upgrading currencyPairs.pairs: %w", err)
|
||||
}
|
||||
e = jsonparser.Delete(e, "currencyPairs", "assetTypes")
|
||||
return e, err
|
||||
}
|
||||
|
||||
// DowngradeExchange moves AssetEnabled assets into AssetType field
|
||||
func (v *Version4) DowngradeExchange(_ context.Context, e []byte) ([]byte, error) {
|
||||
func (*Version) DowngradeExchange(_ context.Context, e []byte) ([]byte, error) {
|
||||
assetTypes := []string{}
|
||||
|
||||
assetEnabledFn := func(asset, v []byte, _ jsonparser.ValueType, _ int) error {
|
||||
@@ -1,4 +1,4 @@
|
||||
package versions
|
||||
package v4_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -8,31 +8,25 @@ import (
|
||||
"github.com/buger/jsonparser"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
v4 "github.com/thrasher-corp/gocryptotrader/config/versions/v4"
|
||||
)
|
||||
|
||||
func TestVersion4ExchangeType(t *testing.T) {
|
||||
func TestExchanges(t *testing.T) {
|
||||
t.Parallel()
|
||||
assert.Implements(t, (*ExchangeVersion)(nil), new(Version4))
|
||||
assert.Equal(t, []string{"*"}, new(v4.Version).Exchanges())
|
||||
}
|
||||
|
||||
func TestVersion4Exchanges(t *testing.T) {
|
||||
t.Parallel()
|
||||
assert.Equal(t, []string{"*"}, new(Version4).Exchanges())
|
||||
}
|
||||
|
||||
func TestVersion4Upgrade(t *testing.T) {
|
||||
func TestUpgradeExchange(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
_, err := new(Version4).UpgradeExchange(context.Background(), []byte{})
|
||||
require.ErrorIs(t, err, errUpgrading)
|
||||
require.ErrorContains(t, err, `assetTypes`)
|
||||
_, err := new(v4.Version).UpgradeExchange(context.Background(), []byte{})
|
||||
require.ErrorContains(t, err, `error upgrading assetTypes`)
|
||||
|
||||
_, err = new(Version4).UpgradeExchange(context.Background(), []byte(`{}`))
|
||||
require.ErrorIs(t, err, errUpgrading)
|
||||
require.ErrorContains(t, err, `currencyPairs.pairs`)
|
||||
_, err = new(v4.Version).UpgradeExchange(context.Background(), []byte(`{}`))
|
||||
require.ErrorContains(t, err, `error upgrading currencyPairs.pairs`)
|
||||
|
||||
in := []byte(`{"name":"Cracken","currencyPairs":{"assetTypes":["spot"],"pairs":{"spot":{"enabled":"BTC-AUD","available":"BTC-AUD"},"futures":{"assetEnabled":true},"options":{},"margin":{"assetEnabled":null}}}}`)
|
||||
out, err := new(Version4).UpgradeExchange(context.Background(), in)
|
||||
out, err := new(v4.Version).UpgradeExchange(context.Background(), in)
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, out)
|
||||
|
||||
@@ -55,26 +49,26 @@ func TestVersion4Upgrade(t *testing.T) {
|
||||
require.NoError(t, err, "Must find assetEnabled for margin")
|
||||
assert.False(t, e, "assetEnabled should be set to false")
|
||||
|
||||
out2, err := new(Version4).UpgradeExchange(context.Background(), out)
|
||||
out2, err := new(v4.Version).UpgradeExchange(context.Background(), out)
|
||||
require.NoError(t, err, "Must not error on re-upgrading")
|
||||
assert.Equal(t, out, out2, "Should not affect an already upgraded config")
|
||||
|
||||
in = []byte(`{"name":"Cracken","currencyPairs":{"assetTypes":["spot"],"pairs":{"spot":{"assetEnabled":{}}}}}`)
|
||||
_, err = new(Version4).UpgradeExchange(context.Background(), in)
|
||||
_, err = new(v4.Version).UpgradeExchange(context.Background(), in)
|
||||
require.NoError(t, err)
|
||||
|
||||
in = []byte(`{"name":"Cracken","currencyPairs":{"assetTypes":["spot"],"pairs":{"margin":{"assetEnabled":{}}}}}`)
|
||||
_, err = new(Version4).UpgradeExchange(context.Background(), in)
|
||||
_, err = new(v4.Version).UpgradeExchange(context.Background(), in)
|
||||
require.ErrorIs(t, err, jsonparser.UnknownValueTypeError)
|
||||
require.ErrorContains(t, err, "`margin`")
|
||||
require.ErrorContains(t, err, "`object`")
|
||||
}
|
||||
|
||||
func TestVersion4Downgrade(t *testing.T) {
|
||||
func TestDowngradeExchange(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
in := []byte(`{"name":"Cracken","currencyPairs":{"pairs":{"spot":{"enabled":"BTC-AUD","available":"BTC-AUD","assetEnabled":true},"futures":{"assetEnabled":false},"options":{},"options_combo":{"assetEnabled":true}}}}`)
|
||||
out, err := new(Version4).DowngradeExchange(context.Background(), in)
|
||||
out, err := new(v4.Version).DowngradeExchange(context.Background(), in)
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, out)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package versions
|
||||
package v5
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -6,25 +6,20 @@ import (
|
||||
"strconv"
|
||||
|
||||
"github.com/buger/jsonparser"
|
||||
v5 "github.com/thrasher-corp/gocryptotrader/config/versions/v5"
|
||||
)
|
||||
|
||||
// Version5 implements ConfigVersion
|
||||
type Version5 struct{}
|
||||
|
||||
func init() {
|
||||
Manager.registerVersion(5, &Version5{})
|
||||
}
|
||||
// Version implements ConfigVersion
|
||||
type Version struct{}
|
||||
|
||||
// UpgradeConfig handles upgrading config for OrderManager:
|
||||
// * Sets OrderManager config to defaults if it doesn't exist or enabled is null
|
||||
// * Sets respectOrderHistoryLimits to true if it doesn't exist or is null
|
||||
// * Sets futuresTrackingSeekDuration to positive if it's negative
|
||||
func (v *Version5) UpgradeConfig(_ context.Context, e []byte) ([]byte, error) {
|
||||
func (*Version) UpgradeConfig(_ context.Context, e []byte) ([]byte, error) {
|
||||
_, valueType, _, err := jsonparser.Get(e, "orderManager", "enabled")
|
||||
switch {
|
||||
case errors.Is(err, jsonparser.KeyPathNotFoundError), valueType == jsonparser.Null:
|
||||
return jsonparser.Set(e, v5.DefaultOrderbookConfig, "orderManager")
|
||||
return jsonparser.Set(e, DefaultOrderbookConfig, "orderManager")
|
||||
case err != nil:
|
||||
return e, err
|
||||
}
|
||||
@@ -37,7 +32,7 @@ func (v *Version5) UpgradeConfig(_ context.Context, e []byte) ([]byte, error) {
|
||||
}
|
||||
|
||||
if i, err := jsonparser.GetInt(e, "orderManager", "futuresTrackingSeekDuration"); err != nil {
|
||||
if e, err = jsonparser.Set(e, []byte(v5.DefaultFuturesTrackingSeekDuration), "orderManager", "futuresTrackingSeekDuration"); err != nil {
|
||||
if e, err = jsonparser.Set(e, []byte(DefaultFuturesTrackingSeekDuration), "orderManager", "futuresTrackingSeekDuration"); err != nil {
|
||||
return e, err
|
||||
}
|
||||
} else if i < 0 {
|
||||
@@ -49,7 +44,7 @@ func (v *Version5) UpgradeConfig(_ context.Context, e []byte) ([]byte, error) {
|
||||
}
|
||||
|
||||
// DowngradeConfig just reverses the futuresTrackingSeekDuration to negative, and leaves everything else alone
|
||||
func (v *Version5) DowngradeConfig(_ context.Context, e []byte) ([]byte, error) {
|
||||
func (*Version) DowngradeConfig(_ context.Context, e []byte) ([]byte, error) {
|
||||
if i, err := jsonparser.GetInt(e, "orderManager", "futuresTrackingSeekDuration"); err == nil && i > 0 {
|
||||
if e, err = jsonparser.Set(e, []byte(strconv.FormatInt(-i, 10)), "orderManager", "futuresTrackingSeekDuration"); err != nil {
|
||||
return e, err
|
||||
@@ -1,4 +1,4 @@
|
||||
package versions
|
||||
package v5_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -10,9 +10,10 @@ import (
|
||||
"github.com/buger/jsonparser"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
v5 "github.com/thrasher-corp/gocryptotrader/config/versions/v5"
|
||||
)
|
||||
|
||||
func TestVersion5Upgrade(t *testing.T) {
|
||||
func TestUpgradeConfig(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
expDef := `{"orderManager":{"enabled":true,"verbose":false,"activelyTrackFuturesPositions":true,"futuresTrackingSeekDuration":31536000000000000,"cancelOrdersOnShutdown":false,"respectOrderHistoryLimits":true}}`
|
||||
@@ -37,7 +38,7 @@ func TestVersion5Upgrade(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
_ = t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
out, err := new(Version5).UpgradeConfig(context.Background(), []byte(tt.in))
|
||||
out, err := new(v5.Version).UpgradeConfig(context.Background(), []byte(tt.in))
|
||||
if tt.err != nil {
|
||||
require.ErrorIs(t, err, tt.err)
|
||||
return
|
||||
@@ -50,16 +51,16 @@ func TestVersion5Upgrade(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestVersion5Downgrade(t *testing.T) {
|
||||
func TestDowngradeConfig(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
in := `{"orderManager":{"enabled":false,"verbose":true,"activelyTrackFuturesPositions":false,"futuresTrackingSeekDuration":-47000,"cancelOrdersOnShutdown":true,"respectOrderHistoryLimits":true}}`
|
||||
exp := `{"orderManager":{"enabled":false,"verbose":true,"activelyTrackFuturesPositions":false,"futuresTrackingSeekDuration":-47000,"cancelOrdersOnShutdown":true,"respectOrderHistoryLimits":true}}`
|
||||
out, err := new(Version5).DowngradeConfig(context.Background(), []byte(in))
|
||||
out, err := new(v5.Version).DowngradeConfig(context.Background(), []byte(in))
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, exp, string(out), "DowngradeConfig should just reverse the futuresTrackingSeekDuration")
|
||||
|
||||
out, err = new(Version5).DowngradeConfig(context.Background(), []byte(exp))
|
||||
out, err = new(v5.Version).DowngradeConfig(context.Background(), []byte(exp))
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, exp, string(out), "DowngradeConfig should leave an already negative futuresTrackingSeekDuration alone")
|
||||
}
|
||||
@@ -1,26 +1,21 @@
|
||||
package versions
|
||||
package v6
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"github.com/buger/jsonparser"
|
||||
v6 "github.com/thrasher-corp/gocryptotrader/config/versions/v6"
|
||||
)
|
||||
|
||||
// Version6 implements ConfigVersion
|
||||
type Version6 struct{}
|
||||
|
||||
func init() {
|
||||
Manager.registerVersion(6, &Version6{})
|
||||
}
|
||||
// Version implements ConfigVersion
|
||||
type Version struct{}
|
||||
|
||||
// UpgradeConfig checks and upgrades the portfolioAddresses.providers field
|
||||
func (v *Version6) UpgradeConfig(_ context.Context, e []byte) ([]byte, error) {
|
||||
func (*Version) UpgradeConfig(_ context.Context, e []byte) ([]byte, error) {
|
||||
_, valueType, _, err := jsonparser.Get(e, "portfolioAddresses", "providers")
|
||||
switch {
|
||||
case errors.Is(err, jsonparser.KeyPathNotFoundError), valueType == jsonparser.Null:
|
||||
return jsonparser.Set(e, v6.DefaultConfig, "portfolioAddresses", "providers")
|
||||
return jsonparser.Set(e, DefaultConfig, "portfolioAddresses", "providers")
|
||||
case err != nil:
|
||||
return e, err
|
||||
}
|
||||
@@ -28,7 +23,7 @@ func (v *Version6) UpgradeConfig(_ context.Context, e []byte) ([]byte, error) {
|
||||
}
|
||||
|
||||
// DowngradeConfig removes the portfolioAddresses.providers field
|
||||
func (v *Version6) DowngradeConfig(_ context.Context, e []byte) ([]byte, error) {
|
||||
func (*Version) DowngradeConfig(_ context.Context, e []byte) ([]byte, error) {
|
||||
e = jsonparser.Delete(e, "portfolioAddresses", "providers")
|
||||
return e, nil
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package versions
|
||||
package v6_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -11,30 +11,30 @@ import (
|
||||
v6 "github.com/thrasher-corp/gocryptotrader/config/versions/v6"
|
||||
)
|
||||
|
||||
func TestVersion6Upgrade(t *testing.T) {
|
||||
func TestUpgradeConfig(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
in := []byte(`
|
||||
{"portfolioAddresses":{"addresses":[{"Address":"1JCe8z4jJVNXSjohjM4i9Hh813dLCNx2Sy","CoinType":"BTC","Balance":0.00108832,"Description":"","WhiteListed":false,"ColdStorage":false,"SupportedExchanges":""}]}}
|
||||
`)
|
||||
|
||||
r, err := new(Version6).UpgradeConfig(context.Background(), in)
|
||||
r, err := new(v6.Version).UpgradeConfig(context.Background(), in)
|
||||
require.NoError(t, err, "UpgradeConfig must not error")
|
||||
require.True(t, bytes.Contains(r, v6.DefaultConfig))
|
||||
|
||||
r2, err := new(Version6).UpgradeConfig(context.Background(), r)
|
||||
r2, err := new(v6.Version).UpgradeConfig(context.Background(), r)
|
||||
require.NoError(t, err, "UpgradeConfig must not error")
|
||||
assert.Equal(t, r, r2, "UpgradeConfig should not affect an already upgraded config")
|
||||
}
|
||||
|
||||
func TestVersion6Downgrade(t *testing.T) {
|
||||
func TestDowngradeConfig(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
in := []byte(`
|
||||
{"portfolioAddresses":{"addresses":[{"Address":"1JCe8z4jJVNXSjohjM4i9Hh813dLCNx2Sy","CoinType":"BTC","Balance":0.00108832,"Description":"","WhiteListed":false,"ColdStorage":false,"SupportedExchanges":""}],"providers":[{"name":"Ethplorer","enabled":true},{"name":"XRPScan","enabled":true},{"name":"CryptoID","enabled":false,"apiKey":"Key"}]}}
|
||||
`)
|
||||
|
||||
r, err := new(Version6).DowngradeConfig(context.Background(), in)
|
||||
r, err := new(v6.Version).DowngradeConfig(context.Background(), in)
|
||||
require.NoError(t, err, "DowngradeConfig must not error")
|
||||
_, _, _, err = jsonparser.Get(r, "portfolioAddresses", "providers") //nolint:dogsled // Return values not needed
|
||||
assert.ErrorIs(t, err, jsonparser.KeyPathNotFoundError, "providers should be removed")
|
||||
@@ -32,6 +32,7 @@ const UseLatestVersion = math.MaxUint16
|
||||
|
||||
var (
|
||||
errVersionIncompatible = errors.New("version does not implement ConfigVersion or ExchangeVersion")
|
||||
errAlreadyRegistered = errors.New("version is already registered")
|
||||
errModifyingExchange = errors.New("error modifying exchange config")
|
||||
errNoVersions = errors.New("error retrieving latest config version: No config versions are registered")
|
||||
errApplyingVersion = errors.New("error applying version")
|
||||
@@ -40,7 +41,6 @@ var (
|
||||
errConfigVersionUnavail = errors.New("version is higher than the latest available version")
|
||||
errConfigVersionNegative = errors.New("version is negative")
|
||||
errConfigVersionMax = errors.New("version is above max versions")
|
||||
errUpgrading = errors.New("error upgrading")
|
||||
)
|
||||
|
||||
// ConfigVersion is a version that affects the general configuration
|
||||
@@ -211,15 +211,28 @@ func exchangeDeploy(ctx context.Context, patch ExchangeVersion, method func(Exch
|
||||
}
|
||||
|
||||
// registerVersion takes instances of config versions and adds them to the registry
|
||||
func (m *manager) registerVersion(ver int, v any) {
|
||||
func (m *manager) registerVersion(ver uint16, v any) {
|
||||
m.m.Lock()
|
||||
defer m.m.Unlock()
|
||||
if ver >= len(m.versions) {
|
||||
m.versions = slices.Grow(m.versions, ver+1)[:ver+1]
|
||||
if int(ver) >= len(m.versions) {
|
||||
m.versions = slices.Grow(m.versions, int(ver+1))[:ver+1]
|
||||
}
|
||||
if m.versions[ver] != nil {
|
||||
panic(fmt.Errorf("%w: %d", errAlreadyRegistered, ver))
|
||||
}
|
||||
m.versions[ver] = v
|
||||
}
|
||||
|
||||
// Version returns a version registered by init or nil if nothing has been registered with that version number
|
||||
func (m *manager) Version(version uint16) any {
|
||||
m.m.RLock()
|
||||
defer m.m.RUnlock()
|
||||
if int(version) < len(m.versions) {
|
||||
return m.versions[version]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// latest returns the highest version number
|
||||
func (m *manager) latest() (uint16, error) {
|
||||
m.m.RLock()
|
||||
|
||||
@@ -2,12 +2,17 @@ package versions
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math"
|
||||
"testing"
|
||||
|
||||
"github.com/buger/jsonparser"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/thrasher-corp/gocryptotrader/common"
|
||||
v0 "github.com/thrasher-corp/gocryptotrader/config/versions/v0"
|
||||
v1 "github.com/thrasher-corp/gocryptotrader/config/versions/v1"
|
||||
v2 "github.com/thrasher-corp/gocryptotrader/config/versions/v2"
|
||||
)
|
||||
|
||||
func TestDeploy(t *testing.T) {
|
||||
@@ -22,8 +27,8 @@ func TestDeploy(t *testing.T) {
|
||||
|
||||
m = manager{}
|
||||
|
||||
m.registerVersion(0, &Version0{})
|
||||
m.registerVersion(1, &Version1{})
|
||||
m.registerVersion(0, &v0.Version{})
|
||||
m.registerVersion(1, &v1.Version{})
|
||||
_, err = m.Deploy(context.Background(), []byte(`not an object`), UseLatestVersion)
|
||||
require.ErrorIs(t, err, jsonparser.KeyPathNotFoundError, "Must throw the correct error trying to add version to bad json")
|
||||
require.ErrorIs(t, err, common.ErrSettingField, "Must throw the correct error trying to add version to bad json")
|
||||
@@ -95,7 +100,7 @@ func TestRegisterVersion(t *testing.T) {
|
||||
t.Parallel()
|
||||
m := manager{}
|
||||
|
||||
m.registerVersion(0, &Version0{})
|
||||
m.registerVersion(0, &v0.Version{})
|
||||
assert.NotEmpty(t, m.versions)
|
||||
|
||||
m.registerVersion(2, &TestVersion2{})
|
||||
@@ -106,6 +111,10 @@ func TestRegisterVersion(t *testing.T) {
|
||||
m.registerVersion(1, &TestVersion1{})
|
||||
require.Equal(t, 3, len(m.versions), "Must leave len alone when registering out-of-sequence")
|
||||
require.NotNil(t, m.versions[1], "Must put Version 1 in the correct slot")
|
||||
|
||||
assert.PanicsWithError(t, fmt.Sprintf("%s: %d", errAlreadyRegistered, 2), func() {
|
||||
m.registerVersion(2, &TestVersion2{})
|
||||
}, "registeringVersion must panic registering an existing version")
|
||||
}
|
||||
|
||||
func TestLatest(t *testing.T) {
|
||||
@@ -114,14 +123,25 @@ func TestLatest(t *testing.T) {
|
||||
_, err := m.latest()
|
||||
require.ErrorIs(t, err, errNoVersions)
|
||||
|
||||
m.registerVersion(0, &Version0{})
|
||||
m.registerVersion(1, &Version1{})
|
||||
m.registerVersion(0, &v0.Version{})
|
||||
m.registerVersion(1, &v1.Version{})
|
||||
v, err := m.latest()
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, uint16(1), v)
|
||||
|
||||
m.registerVersion(2, &Version2{})
|
||||
m.registerVersion(2, &v2.Version{})
|
||||
v, err = m.latest()
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, uint16(2), v)
|
||||
}
|
||||
|
||||
func TestVersion(t *testing.T) {
|
||||
t.Parallel()
|
||||
m := manager{}
|
||||
m.registerVersion(0, &v0.Version{})
|
||||
l, err := m.latest()
|
||||
require.NoError(t, err, "latest must not error")
|
||||
assert.Nil(t, m.Version(l-1))
|
||||
assert.NotNil(t, m.Version(l))
|
||||
assert.Nil(t, m.Version(math.MaxUint16))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user