Files
gocryptotrader/engine/currency_state_manager_test.go
Ryan O'Hara-Reid 5dfbbf84de engine/exchanges: Add exchange currency state subsystem (#774)
* state: Add management system (init)

* linter: fix

* engine: gofmt

* gct: after merge fixup

* documentation: add

* rpc: implement services for testing

* gctcli: gofmt state_management.go

* documentation: reinstate lost information

* state: Add pair check to determine trading operation

* exchanges: add interface for specific state scoped subsystem functionality

* engine/order_man: reduce code footprint using new method

* RPC: implement pair trading request and change exported name to something specific to state

* engine: add tests

* engine: Add to withdraw manager

* documentation: reinstate soxipy in contrib. list

* engine: const fake name

* Glorious: NITERINOS

* merge: fix issues

* engine: csm incorporate service name into log output

* engine: fix linter issues

* gct: fix tests

* currencystate: remove management type

* rpc: fix tests

* backtester: fix tests

* Update engine/currency_state_manager.go

Co-authored-by: Scott <gloriousCode@users.noreply.github.com>

* Update engine/currency_state_manager.go

Co-authored-by: Scott <gloriousCode@users.noreply.github.com>

* Update exchanges/currencystate/currency_state.go

Co-authored-by: Scott <gloriousCode@users.noreply.github.com>

* Update exchanges/alert/alert.go

Co-authored-by: Scott <gloriousCode@users.noreply.github.com>

* Update exchanges/alert/alert.go

Co-authored-by: Scott <gloriousCode@users.noreply.github.com>

* glorious: nits

* config: integrate with config and remove flag delay adjustment

* gctcli: fix issues after name changes

* engine: gofmt manager file

* Update engine/rpcserver.go

Co-authored-by: Scott <gloriousCode@users.noreply.github.com>

* engine: Add enable/disable manager functions, add default popoulation for potential assets

* linter: fix

* engine/test: bump subsystem count

* Update engine/currency_state_manager.go

Co-authored-by: Scott <gloriousCode@users.noreply.github.com>

* Update exchanges/bithumb/bithumb.go

Co-authored-by: Scott <gloriousCode@users.noreply.github.com>

* glorious: nits addressed

* alert: fix commenting for its generalized purpose

* glorious: nits

* engine: use standard string in log output

* bitfinex: apply patch, thanks @thrasher-

* bitfinex: fix spelling

* engine/currencystate: Add logs/fix logs

Co-authored-by: Scott <gloriousCode@users.noreply.github.com>
2021-09-27 13:33:49 +10:00

377 lines
10 KiB
Go

package engine
import (
"context"
"errors"
"sync"
"testing"
"time"
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/currency"
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
"github.com/thrasher-corp/gocryptotrader/exchanges/currencystate"
)
func TestSetupCurrencyStateManager(t *testing.T) {
t.Parallel()
_, err := SetupCurrencyStateManager(0, nil)
if !errors.Is(err, errNilExchangeManager) {
t.Fatalf("received: '%v' but expected: '%v'", err, errNilExchangeManager)
}
cm, err := SetupCurrencyStateManager(0, &ExchangeManager{})
if !errors.Is(err, nil) {
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
}
if cm.sleep != DefaultStateManagerDelay {
t.Fatal("unexpected value")
}
}
var (
errManager = errors.New("manager level error")
errExchange = errors.New("exchange level error")
)
type fakeExchangeManagerino struct {
ErrorMeOne bool
ErrorMeTwo bool
}
func (f *fakeExchangeManagerino) GetExchanges() ([]exchange.IBotExchange, error) {
if f.ErrorMeOne {
return nil, errManager
}
return []exchange.IBotExchange{&fakerino{errorMe: f.ErrorMeTwo}}, nil
}
func (f *fakeExchangeManagerino) GetExchangeByName(_ string) (exchange.IBotExchange, error) {
if f.ErrorMeOne {
return nil, errManager
}
return &fakerino{errorMe: f.ErrorMeTwo}, nil
}
type fakerino struct {
exchange.IBotExchange
errorMe bool
GetAvailablePairsError bool
GetBaseError bool
}
func (f *fakerino) UpdateCurrencyStates(_ context.Context, _ asset.Item) error {
if f.errorMe {
return common.ErrNotYetImplemented
}
return nil
}
func (f *fakerino) GetAssetTypes(_ bool) asset.Items {
return asset.Items{asset.Spot}
}
func (f *fakerino) GetName() string {
return "testssssssssssssss"
}
func (f *fakerino) GetCurrencyStateSnapshot() ([]currencystate.Snapshot, error) {
if f.errorMe {
return nil, errExchange
}
return []currencystate.Snapshot{
{Code: currency.SHORTY, Asset: asset.Spot},
}, nil
}
func (f *fakerino) CanWithdraw(c currency.Code, a asset.Item) error {
if f.errorMe {
return errExchange
}
return nil
}
func (f *fakerino) CanDeposit(c currency.Code, a asset.Item) error {
if f.errorMe {
return errExchange
}
return nil
}
func (f *fakerino) CanTrade(c currency.Code, a asset.Item) error {
if f.errorMe {
return errExchange
}
return nil
}
func (f *fakerino) CanTradePair(p currency.Pair, a asset.Item) error {
if f.errorMe {
return errExchange
}
return nil
}
func (f *fakerino) GetAvailablePairs(a asset.Item) (currency.Pairs, error) {
if f.GetAvailablePairsError {
return nil, errExchange
}
return currency.Pairs{currency.NewPair(currency.BTC, currency.USD)}, nil
}
func (f *fakerino) GetBase() *exchange.Base {
if f.GetBaseError {
return nil
}
return &exchange.Base{States: currencystate.NewCurrencyStates()}
}
func TestCurrencyStateManagerIsRunning(t *testing.T) {
t.Parallel()
err := (*CurrencyStateManager)(nil).Stop()
if !errors.Is(err, ErrNilSubsystem) {
t.Fatalf("received: '%v' but expected: '%v'", err, ErrNilSubsystem)
}
err = (&CurrencyStateManager{}).Stop()
if !errors.Is(err, ErrSubSystemNotStarted) {
t.Fatalf("received: '%v' but expected: '%v'", err, ErrSubSystemNotStarted)
}
err = (&CurrencyStateManager{started: 1, shutdown: make(chan struct{})}).Stop()
if !errors.Is(err, nil) {
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
}
err = (*CurrencyStateManager)(nil).Start()
if !errors.Is(err, ErrNilSubsystem) {
t.Fatalf("received: '%v' but expected: '%v'", err, ErrNilSubsystem)
}
err = (&CurrencyStateManager{started: 1}).Start()
if !errors.Is(err, ErrSubSystemAlreadyStarted) {
t.Fatalf("received: '%v' but expected: '%v'", err, ErrSubSystemAlreadyStarted)
}
man := &CurrencyStateManager{
shutdown: make(chan struct{}),
iExchangeManager: &fakeExchangeManagerino{ErrorMeOne: true},
sleep: time.Minute}
err = man.Start()
if !errors.Is(err, nil) {
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
}
time.Sleep(time.Millisecond)
err = man.Stop()
if !errors.Is(err, nil) {
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
}
man.iExchangeManager = &fakeExchangeManagerino{ErrorMeOne: true}
err = man.Start()
if !errors.Is(err, nil) {
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
}
time.Sleep(time.Millisecond)
err = man.Stop()
if !errors.Is(err, nil) {
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
}
man.iExchangeManager = &fakeExchangeManagerino{ErrorMeOne: true}
err = man.Start()
if !errors.Is(err, nil) {
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
}
time.Sleep(time.Millisecond)
if !man.IsRunning() {
t.Fatal("this should be running")
}
err = man.Stop()
if !errors.Is(err, nil) {
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
}
if man.IsRunning() {
t.Fatal("this should be stopped")
}
}
func TestGetAllRPC(t *testing.T) {
t.Parallel()
_, err := (*CurrencyStateManager)(nil).GetAllRPC("")
if !errors.Is(err, ErrSubSystemNotStarted) {
t.Fatalf("received: '%v' but expected: '%v'", err, ErrSubSystemNotStarted)
}
_, err = (&CurrencyStateManager{
started: 1,
iExchangeManager: &fakeExchangeManagerino{ErrorMeOne: true},
}).GetAllRPC("")
if !errors.Is(err, errManager) {
t.Fatalf("received: '%v' but expected: '%v'", err, errManager)
}
_, err = (&CurrencyStateManager{
started: 1,
iExchangeManager: &fakeExchangeManagerino{ErrorMeTwo: true},
}).GetAllRPC("")
if !errors.Is(err, errExchange) {
t.Fatalf("received: '%v' but expected: '%v'", err, errExchange)
}
_, err = (&CurrencyStateManager{
started: 1,
iExchangeManager: &fakeExchangeManagerino{},
}).GetAllRPC("")
if !errors.Is(err, nil) {
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
}
}
func TestCanWithdrawRPC(t *testing.T) {
t.Parallel()
_, err := (*CurrencyStateManager)(nil).CanWithdrawRPC("", currency.Code{}, "")
if !errors.Is(err, ErrSubSystemNotStarted) {
t.Fatalf("received: '%v' but expected: '%v'", err, ErrSubSystemNotStarted)
}
_, err = (&CurrencyStateManager{
started: 1,
iExchangeManager: &fakeExchangeManagerino{ErrorMeOne: true},
}).CanWithdrawRPC("", currency.Code{}, "")
if !errors.Is(err, errManager) {
t.Fatalf("received: '%v' but expected: '%v'", err, errManager)
}
_, err = (&CurrencyStateManager{
started: 1,
iExchangeManager: &fakeExchangeManagerino{ErrorMeTwo: true},
}).CanWithdrawRPC("", currency.Code{}, "")
if !errors.Is(err, errExchange) {
t.Fatalf("received: '%v' but expected: '%v'", err, errExchange)
}
_, err = (&CurrencyStateManager{
started: 1,
iExchangeManager: &fakeExchangeManagerino{},
}).CanWithdrawRPC("", currency.Code{}, "")
if !errors.Is(err, nil) {
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
}
}
func TestCanDepositRPC(t *testing.T) {
t.Parallel()
_, err := (*CurrencyStateManager)(nil).CanDepositRPC("", currency.Code{}, "")
if !errors.Is(err, ErrSubSystemNotStarted) {
t.Fatalf("received: '%v' but expected: '%v'", err, ErrSubSystemNotStarted)
}
_, err = (&CurrencyStateManager{
started: 1,
iExchangeManager: &fakeExchangeManagerino{ErrorMeOne: true},
}).CanDepositRPC("", currency.Code{}, "")
if !errors.Is(err, errManager) {
t.Fatalf("received: '%v' but expected: '%v'", err, errManager)
}
_, err = (&CurrencyStateManager{
started: 1,
iExchangeManager: &fakeExchangeManagerino{ErrorMeTwo: true},
}).CanDepositRPC("", currency.Code{}, "")
if !errors.Is(err, errExchange) {
t.Fatalf("received: '%v' but expected: '%v'", err, errExchange)
}
_, err = (&CurrencyStateManager{
started: 1,
iExchangeManager: &fakeExchangeManagerino{},
}).CanDepositRPC("", currency.Code{}, "")
if !errors.Is(err, nil) {
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
}
}
func TestCanTradeRPC(t *testing.T) {
t.Parallel()
_, err := (*CurrencyStateManager)(nil).CanTradeRPC("", currency.Code{}, "")
if !errors.Is(err, ErrSubSystemNotStarted) {
t.Fatalf("received: '%v' but expected: '%v'", err, ErrSubSystemNotStarted)
}
_, err = (&CurrencyStateManager{
started: 1,
iExchangeManager: &fakeExchangeManagerino{ErrorMeOne: true},
}).CanTradeRPC("", currency.Code{}, "")
if !errors.Is(err, errManager) {
t.Fatalf("received: '%v' but expected: '%v'", err, errManager)
}
_, err = (&CurrencyStateManager{
started: 1,
iExchangeManager: &fakeExchangeManagerino{ErrorMeTwo: true},
}).CanTradeRPC("", currency.Code{}, "")
if !errors.Is(err, errExchange) {
t.Fatalf("received: '%v' but expected: '%v'", err, errExchange)
}
_, err = (&CurrencyStateManager{
started: 1,
iExchangeManager: &fakeExchangeManagerino{},
}).CanTradeRPC("", currency.Code{}, "")
if !errors.Is(err, nil) {
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
}
}
func TestCanTradePairRPC(t *testing.T) {
t.Parallel()
_, err := (*CurrencyStateManager)(nil).CanTradePairRPC("", currency.Pair{}, "")
if !errors.Is(err, ErrSubSystemNotStarted) {
t.Fatalf("received: '%v' but expected: '%v'", err, ErrSubSystemNotStarted)
}
_, err = (&CurrencyStateManager{
started: 1,
iExchangeManager: &fakeExchangeManagerino{ErrorMeOne: true},
}).CanTradePairRPC("", currency.Pair{}, "")
if !errors.Is(err, errManager) {
t.Fatalf("received: '%v' but expected: '%v'", err, errManager)
}
_, err = (&CurrencyStateManager{
started: 1,
iExchangeManager: &fakeExchangeManagerino{ErrorMeTwo: true},
}).CanTradePairRPC("", currency.Pair{}, "")
if !errors.Is(err, errExchange) {
t.Fatalf("received: '%v' but expected: '%v'", err, errExchange)
}
_, err = (&CurrencyStateManager{
started: 1,
iExchangeManager: &fakeExchangeManagerino{},
}).CanTradePairRPC("", currency.Pair{}, "")
if !errors.Is(err, nil) {
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
}
}
func TestUpdate(t *testing.T) {
man := &CurrencyStateManager{}
var wg sync.WaitGroup
wg.Add(3)
man.update(&fakerino{errorMe: true, GetAvailablePairsError: true}, &wg, asset.Items{asset.Spot})
man.update(&fakerino{errorMe: true, GetBaseError: true}, &wg, asset.Items{asset.Spot})
man.update(&fakerino{errorMe: true}, &wg, asset.Items{asset.Spot})
}