mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-05-13 15:09:42 +00:00
* Bybit: Fix race in TestUpdateAccountInfo and TestWSHandleData * DriveBy rename TestWSHandleData * This doesn't address running with -race=2+ due to the singleton * Accounts: Add account.GetService() * exchange: Assertify TestSetupDefaults * Exchanges: Add account.Service override for testing * Exchanges: Remove duplicate IsWebsocketEnabled test from TestSetupDefaults * Dispatch: Replace nil checks with NilGuard * Engine: Remove deprecated printAccountHoldingsChangeSummary * Dispatcher: Add EnsureRunning method * Accounts: Move singleton accounts service to exchange Accounts * Move singleton accounts service to exchange Accounts This maintains the concept of a global store, whilst allowing exchanges to override it when needed, particularly for testing. APIServer: * Remove getAllActiveAccounts from apiserver Deprecated apiserver only thing using this, so remove it instead of updating it * Update comment for UpdateAccountBalances everywhere * Docs: Add punctuation to function comments * Bybit: Coverage for wsProcessWalletPushData Save
199 lines
6.1 KiB
Go
199 lines
6.1 KiB
Go
package engine
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"sync"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"github.com/thrasher-corp/gocryptotrader/currency"
|
|
"github.com/thrasher-corp/gocryptotrader/exchange/accounts"
|
|
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
|
|
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
|
|
"github.com/thrasher-corp/gocryptotrader/portfolio"
|
|
)
|
|
|
|
func TestSetupPortfolioManager(t *testing.T) {
|
|
_, err := setupPortfolioManager(nil, 0, nil)
|
|
assert.ErrorIs(t, err, errNilExchangeManager)
|
|
|
|
m, err := setupPortfolioManager(NewExchangeManager(), 0, nil)
|
|
assert.NoError(t, err)
|
|
|
|
if m == nil {
|
|
t.Error("expected manager")
|
|
}
|
|
}
|
|
|
|
func TestIsPortfolioManagerRunning(t *testing.T) {
|
|
var m *portfolioManager
|
|
if m.IsRunning() {
|
|
t.Error("expected false")
|
|
}
|
|
|
|
m, err := setupPortfolioManager(NewExchangeManager(), 0, nil)
|
|
assert.NoError(t, err)
|
|
|
|
if m.IsRunning() {
|
|
t.Error("expected false")
|
|
}
|
|
var wg sync.WaitGroup
|
|
err = m.Start(&wg)
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
if !m.IsRunning() {
|
|
t.Error("expected true")
|
|
}
|
|
}
|
|
|
|
func TestPortfolioManagerStart(t *testing.T) {
|
|
var m *portfolioManager
|
|
var wg sync.WaitGroup
|
|
err := m.Start(nil)
|
|
assert.ErrorIs(t, err, ErrNilSubsystem)
|
|
|
|
m, err = setupPortfolioManager(NewExchangeManager(), 0, nil)
|
|
assert.NoError(t, err)
|
|
|
|
err = m.Start(nil)
|
|
assert.ErrorIs(t, err, errNilWaitGroup)
|
|
|
|
err = m.Start(&wg)
|
|
assert.NoError(t, err)
|
|
|
|
err = m.Start(&wg)
|
|
assert.ErrorIs(t, err, ErrSubSystemAlreadyStarted)
|
|
}
|
|
|
|
func TestPortfolioManagerStop(t *testing.T) {
|
|
var m *portfolioManager
|
|
var wg sync.WaitGroup
|
|
err := m.Stop()
|
|
assert.ErrorIs(t, err, ErrNilSubsystem)
|
|
|
|
m, err = setupPortfolioManager(NewExchangeManager(), 0, nil)
|
|
assert.NoError(t, err)
|
|
|
|
err = m.Stop()
|
|
assert.ErrorIs(t, err, ErrSubSystemNotStarted)
|
|
|
|
err = m.Start(&wg)
|
|
assert.NoError(t, err)
|
|
|
|
err = m.Stop()
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
func TestProcessPortfolio(t *testing.T) {
|
|
em := NewExchangeManager()
|
|
exch, err := em.NewExchangeByName("Bitstamp")
|
|
require.NoError(t, err)
|
|
|
|
exch.SetDefaults()
|
|
err = em.Add(exch)
|
|
require.NoError(t, err)
|
|
|
|
m, err := setupPortfolioManager(em, 0, nil)
|
|
assert.NoError(t, err)
|
|
|
|
m.processPortfolio()
|
|
}
|
|
|
|
func TestUpdateExchangeBalances(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
assert.ErrorContains(t, (*portfolioManager)(nil).updateExchangeBalances(), "nil pointer: *engine.portfolioManager")
|
|
assert.ErrorIs(t, new(portfolioManager).updateExchangeBalances(), ErrNilSubsystem)
|
|
|
|
m, err := setupPortfolioManager(NewExchangeManager(), 0, &portfolio.Base{Verbose: true})
|
|
require.NoError(t, err, "setupPortfolioManager must not error")
|
|
assert.NoError(t, m.updateExchangeBalances(), "updateExchangeBalances should not error with an empty exchange list")
|
|
|
|
e := &mockExchange{err: errors.New("Mock UpdateBalanceError")}
|
|
m.exchangeManager.exchanges = map[string]exchange.IBotExchange{"mock": e}
|
|
assert.NoError(t, m.updateExchangeBalances(), "updateExchangeBalances should not error on disabled exchanges")
|
|
|
|
e.enabled = true
|
|
assert.NoError(t, m.updateExchangeBalances(), "updateExchangeBalances should skip exchange without auth support")
|
|
|
|
e.authSupported = true
|
|
assert.ErrorIs(t, m.updateExchangeBalances(), e.err, "error should contain the UpdateAccountBalances error message")
|
|
}
|
|
|
|
func TestUpdateExchangeAddressBalances(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
assert.ErrorContains(t, (*portfolioManager)(nil).updateExchangeAddressBalances(nil), "nil pointer: *engine.portfolioManager")
|
|
assert.ErrorContains(t, new(portfolioManager).updateExchangeAddressBalances(nil), "nil pointer: <nil>")
|
|
|
|
e := &mockExchange{enabled: false, err: errors.New("Mock UpdateBalanceError")}
|
|
m, err := setupPortfolioManager(NewExchangeManager(), 0, nil)
|
|
require.NoError(t, err, "setupPortfolioManager must not error")
|
|
assert.ErrorContains(t, m.updateExchangeAddressBalances(e), "nil pointer: *accounts.Accounts", "updateExchangeAddressBalances should propagate CurrencyBalances errors")
|
|
|
|
a := accounts.MustNewAccounts(e)
|
|
e.accounts = a
|
|
subAcct := accounts.NewSubAccount(asset.Spot, "")
|
|
subAcct.Balances.Set(currency.BTC, accounts.Balance{Total: 1.5})
|
|
subAcct.Balances.Set(currency.ETH, accounts.Balance{Total: 0})
|
|
require.NoError(t, a.Save(t.Context(), accounts.SubAccounts{subAcct}, false), "accounts.Save must not error")
|
|
require.NoError(t, m.updateExchangeAddressBalances(e))
|
|
require.Len(t, m.base.Addresses, 1, "must have one address for the positive balance")
|
|
assert.Equal(t, 1.5, m.base.Addresses[0].Balance, "balance should match on a new address")
|
|
|
|
subAcct.Balances.Set(currency.BTC, accounts.Balance{Total: 2})
|
|
require.NoError(t, a.Save(t.Context(), accounts.SubAccounts{subAcct}, true), "accounts.Save must not error")
|
|
require.NoError(t, m.updateExchangeAddressBalances(e))
|
|
require.Len(t, m.base.Addresses, 1, "must have one address for the positive balance")
|
|
assert.Equal(t, 2.0, m.base.Addresses[0].Balance, "balance should match after update existing address")
|
|
|
|
subAcct.Balances.Set(currency.BTC, accounts.Balance{Total: 0})
|
|
require.NoError(t, a.Save(t.Context(), accounts.SubAccounts{subAcct}, true), "accounts.Save must not error")
|
|
require.NoError(t, m.updateExchangeAddressBalances(e))
|
|
assert.Empty(t, m.base.Addresses, "should have removed address with no balance")
|
|
}
|
|
|
|
// mockExchange is a minimal mock for testing
|
|
type mockExchange struct {
|
|
exchange.IBotExchange
|
|
enabled bool
|
|
authSupported bool
|
|
err error
|
|
accounts *accounts.Accounts
|
|
}
|
|
|
|
func (m *mockExchange) GetName() string {
|
|
return "mocky"
|
|
}
|
|
|
|
func (m *mockExchange) IsEnabled() bool {
|
|
return m.enabled
|
|
}
|
|
|
|
func (m *mockExchange) IsRESTAuthenticationSupported() bool {
|
|
return m.authSupported
|
|
}
|
|
|
|
func (m *mockExchange) HasAssetTypeAccountSegregation() bool {
|
|
return true
|
|
}
|
|
|
|
func (m *mockExchange) GetAssetTypes(bool) asset.Items {
|
|
return asset.Items{asset.Spot, asset.Futures}
|
|
}
|
|
|
|
func (m *mockExchange) UpdateAccountBalances(context.Context, asset.Item) (accounts.SubAccounts, error) {
|
|
return nil, m.err
|
|
}
|
|
|
|
func (m *mockExchange) GetBase() *exchange.Base {
|
|
return &exchange.Base{Name: "mocky", Accounts: m.accounts}
|
|
}
|
|
|
|
func (m *mockExchange) GetCredentials(context.Context) (*accounts.Credentials, error) {
|
|
return &accounts.Credentials{Key: m.GetName()}, nil
|
|
}
|