Files
gocryptotrader/engine/engine_test.go
Gareth Kirwan 0ecd082254 wsManager: Fix IsRunning true before flushed (#1201)
* Engine: Expose WebsocketRoutineManager

Without exposing the manager users don't know when it's ready to use.

* wsManager: Remove duplicate shutdown chan make

The shutdown channel was already in setup.
Consumers could reasonably expect it to not be replaced in between the
two, and it's not really part of Start to assign it.

* wsManager: Fix IsRunning true before flushed

Consumers must be able to tell when it's safe to start new
subscriptions. Before this fix any new subscriptions would get unsubbed
as part of the flush during `websocketRoutine`.

* WSM: Fix Stop/Start/Stop failing

We previously removed the shutdown channel from Start to avoid duplicate
allocation.
However that will result in a closed channel after the first Stop
So it's better to remove it from the setup.
It's private anyway.

* WSM: Export WebsocketRoutineManager type
2023-05-23 09:19:28 +10:00

341 lines
8.4 KiB
Go

package engine
import (
"errors"
"os"
"testing"
"time"
"github.com/thrasher-corp/gocryptotrader/config"
)
func TestLoadConfigWithSettings(t *testing.T) {
empty := ""
somePath := "somePath"
// Clean up after the tests
defer os.RemoveAll(somePath)
tests := []struct {
name string
flags []string
settings *Settings
want *string
wantErr bool
}{
{
name: "invalid file",
settings: &Settings{
ConfigFile: "nonExistent.json",
},
wantErr: true,
},
{
name: "test file",
settings: &Settings{
ConfigFile: config.TestFile,
CoreSettings: CoreSettings{EnableDryRun: true},
},
want: &empty,
wantErr: false,
},
{
name: "data dir in settings overrides config data dir",
flags: []string{"datadir"},
settings: &Settings{
ConfigFile: config.TestFile,
DataDir: somePath,
CoreSettings: CoreSettings{EnableDryRun: true},
},
want: &somePath,
wantErr: false,
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
// prepare the 'flags'
flagSet := make(map[string]bool)
for _, v := range tt.flags {
flagSet[v] = true
}
// Run the test
got, err := loadConfigWithSettings(tt.settings, flagSet)
if (err != nil) != tt.wantErr {
t.Errorf("loadConfigWithSettings() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != nil || tt.want != nil {
if (got == nil && tt.want != nil) || (got != nil && tt.want == nil) {
t.Errorf("loadConfigWithSettings() = is nil %v, want nil %v", got == nil, tt.want == nil)
} else if got.DataDirectory != *tt.want {
t.Errorf("loadConfigWithSettings() = %v, want %v", got.DataDirectory, *tt.want)
}
}
})
}
}
func TestStartStopDoesNotCausePanic(t *testing.T) {
t.Parallel()
tempDir := t.TempDir()
botOne, err := NewFromSettings(&Settings{
ConfigFile: config.TestFile,
CoreSettings: CoreSettings{EnableDryRun: true},
DataDir: tempDir,
}, nil)
if err != nil {
t.Fatal(err)
}
botOne.Settings.EnableGRPCProxy = false
for i := range botOne.Config.Exchanges {
if botOne.Config.Exchanges[i].Name != testExchange {
// there is no need to load all exchanges for this test
botOne.Config.Exchanges[i].Enabled = false
}
}
if err = botOne.Start(); err != nil {
t.Error(err)
}
botOne.Stop()
}
var enableExperimentalTest = false
func TestStartStopTwoDoesNotCausePanic(t *testing.T) {
t.Parallel()
if !enableExperimentalTest {
t.Skip("test is functional, however does not need to be included in go test runs")
}
tempDir := t.TempDir()
tempDir2 := t.TempDir()
botOne, err := NewFromSettings(&Settings{
ConfigFile: config.TestFile,
CoreSettings: CoreSettings{EnableDryRun: true},
DataDir: tempDir,
}, nil)
if err != nil {
t.Error(err)
}
botOne.Settings.EnableGRPCProxy = false
botTwo, err := NewFromSettings(&Settings{
ConfigFile: config.TestFile,
CoreSettings: CoreSettings{EnableDryRun: true},
DataDir: tempDir2,
}, nil)
if err != nil {
t.Error(err)
}
botTwo.Settings.EnableGRPCProxy = false
if err = botOne.Start(); err != nil {
t.Error(err)
}
if err = botTwo.Start(); err != nil {
t.Error(err)
}
botOne.Stop()
botTwo.Stop()
}
func TestGetExchangeByName(t *testing.T) {
t.Parallel()
_, err := (*ExchangeManager)(nil).GetExchangeByName("tehehe")
if !errors.Is(err, ErrNilSubsystem) {
t.Errorf("received: %v expected: %v", err, ErrNilSubsystem)
}
em := NewExchangeManager()
exch, err := em.NewExchangeByName(testExchange)
if !errors.Is(err, nil) {
t.Fatalf("received '%v' expected '%v'", err, nil)
}
exch.SetDefaults()
exch.SetEnabled(true)
err = em.Add(exch)
if !errors.Is(err, nil) {
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
}
e := &Engine{ExchangeManager: em}
if !exch.IsEnabled() {
t.Errorf("TestGetExchangeByName: Unexpected result")
}
exch.SetEnabled(false)
bfx, err := e.GetExchangeByName(testExchange)
if err != nil {
t.Fatal(err)
}
if bfx.IsEnabled() {
t.Errorf("TestGetExchangeByName: Unexpected result")
}
if exch.GetName() != testExchange {
t.Errorf("TestGetExchangeByName: Unexpected result")
}
_, err = e.GetExchangeByName("Asdasd")
if !errors.Is(err, ErrExchangeNotFound) {
t.Errorf("received: %v expected: %v", err, ErrExchangeNotFound)
}
}
func TestUnloadExchange(t *testing.T) {
t.Parallel()
em := NewExchangeManager()
exch, err := em.NewExchangeByName(testExchange)
if !errors.Is(err, nil) {
t.Fatalf("received '%v' expected '%v'", err, nil)
}
exch.SetDefaults()
exch.SetEnabled(true)
err = em.Add(exch)
if !errors.Is(err, nil) {
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
}
e := &Engine{ExchangeManager: em,
Config: &config.Config{Exchanges: []config.Exchange{{Name: testExchange}}},
}
err = e.UnloadExchange("asdf")
if !errors.Is(err, config.ErrExchangeNotFound) {
t.Errorf("error '%v', expected '%v'", err, config.ErrExchangeNotFound)
}
err = e.UnloadExchange(testExchange)
if err != nil {
t.Errorf("TestUnloadExchange: Failed to get exchange. %s",
err)
}
err = e.UnloadExchange(testExchange)
if !errors.Is(err, ErrExchangeNotFound) {
t.Errorf("error '%v', expected '%v'", err, ErrExchangeNotFound)
}
}
func TestDryRunParamInteraction(t *testing.T) {
t.Parallel()
bot := &Engine{
ExchangeManager: NewExchangeManager(),
Settings: Settings{},
Config: &config.Config{
Exchanges: []config.Exchange{
{
Name: testExchange,
WebsocketTrafficTimeout: time.Second,
},
},
},
}
if err := bot.LoadExchange(testExchange, nil); err != nil {
t.Error(err)
}
exchCfg, err := bot.Config.GetExchangeConfig(testExchange)
if err != nil {
t.Error(err)
}
if exchCfg.Verbose {
t.Error("verbose should have been disabled")
}
if err = bot.UnloadExchange(testExchange); err != nil {
t.Error(err)
}
// Now set dryrun mode to true,
// enable exchange verbose mode and verify that verbose mode
// will be set on Bitfinex
bot.Settings.EnableDryRun = true
bot.Settings.CheckParamInteraction = true
bot.Settings.EnableExchangeVerbose = true
if err = bot.LoadExchange(testExchange, nil); err != nil {
t.Error(err)
}
exchCfg, err = bot.Config.GetExchangeConfig(testExchange)
if err != nil {
t.Error(err)
}
if !bot.Settings.EnableDryRun ||
!exchCfg.Verbose {
t.Error("dryrun should be true and verbose should be true")
}
}
func TestFlagSetWith(t *testing.T) {
var isRunning bool
flags := make(FlagSet)
// Flag not set default to config
flags.WithBool("NOT SET", &isRunning, true)
if !isRunning {
t.Fatalf("received: '%v' but expected: '%v'", isRunning, true)
}
flags.WithBool("NOT SET", &isRunning, false)
if isRunning {
t.Fatalf("received: '%v' but expected: '%v'", isRunning, false)
}
flags["IS SET"] = true
isRunning = true
// Flag set true which will override config
flags.WithBool("IS SET", &isRunning, true)
if !isRunning {
t.Fatalf("received: '%v' but expected: '%v'", isRunning, true)
}
flags.WithBool("IS SET", &isRunning, false)
if !isRunning {
t.Fatalf("received: '%v' but expected: '%v'", isRunning, true)
}
flags["IS SET"] = true
isRunning = false
// Flag set false which will override config
flags.WithBool("IS SET", &isRunning, true)
if isRunning {
t.Fatalf("received: '%v' but expected: '%v'", isRunning, false)
}
flags.WithBool("IS SET", &isRunning, false)
if isRunning {
t.Fatalf("received: '%v' but expected: '%v'", isRunning, false)
}
}
func TestRegisterWebsocketDataHandler(t *testing.T) {
t.Parallel()
var e *Engine
err := e.RegisterWebsocketDataHandler(nil, false)
if !errors.Is(err, errNilBot) {
t.Fatalf("received: '%v' but expected: '%v'", err, errNilBot)
}
e = &Engine{WebsocketRoutineManager: &WebsocketRoutineManager{}}
err = e.RegisterWebsocketDataHandler(func(_ string, _ interface{}) error { return nil }, false)
if !errors.Is(err, nil) {
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
}
}
func TestSetDefaultWebsocketDataHandler(t *testing.T) {
t.Parallel()
var e *Engine
err := e.SetDefaultWebsocketDataHandler()
if !errors.Is(err, errNilBot) {
t.Fatalf("received: '%v' but expected: '%v'", err, errNilBot)
}
e = &Engine{WebsocketRoutineManager: &WebsocketRoutineManager{}}
err = e.SetDefaultWebsocketDataHandler()
if !errors.Is(err, nil) {
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
}
}
func TestSettingsPrint(t *testing.T) {
t.Parallel()
var s *Settings
s.PrintLoadedSettings()
s = &Settings{}
s.PrintLoadedSettings()
}