diff --git a/exchanges/bitfinex/bitfinex_test.go b/exchanges/bitfinex/bitfinex_test.go index 8a50921fae5..7ee3778ec41 100644 --- a/exchanges/bitfinex/bitfinex_test.go +++ b/exchanges/bitfinex/bitfinex_test.go @@ -4,14 +4,12 @@ import ( "context" "errors" "log" - "net/http" "os" "sync" "testing" "time" "github.com/buger/jsonparser" - "github.com/gorilla/websocket" "github.com/stretchr/testify/assert" "github.com/thrasher-corp/gocryptotrader/common" "github.com/thrasher-corp/gocryptotrader/config" @@ -28,7 +26,7 @@ import ( "github.com/thrasher-corp/gocryptotrader/portfolio/withdraw" ) -// Please supply your own keys here to do better tests +// Please supply API keys here or in config/testdata.json to test authenticated endpoints const ( apiKey = "" apiSecret = "" @@ -36,7 +34,7 @@ const ( ) var b = &Bitfinex{} -var wsAuthExecuted bool +var wsConnected bool var btcusdPair currency.Pair func TestMain(m *testing.M) { @@ -55,7 +53,9 @@ func TestMain(m *testing.M) { if err != nil { log.Fatal("Bitfinex setup error", err) } - b.SetCredentials(apiKey, apiSecret, "", "", "", "") + if apiKey != "" { + b.SetCredentials(apiKey, apiSecret, "", "", "", "") + } if !b.Enabled || len(b.BaseCurrencies) < 1 { log.Fatal("Bitfinex Setup values not set correctly") } @@ -861,9 +861,9 @@ func TestGetOrderHistory(t *testing.T) { } } -// Any tests below this line have the ability to impact your orders on the exchange. Enable canManipulateRealOrders to run them +// Start RealTests +// Any tests in this section have the ability to impact your orders on the exchange. Enable canManipulateRealOrders to run them // ---------------------------------------------------------------------------------------------------------------------------- - func TestSubmitOrder(t *testing.T) { t.Parallel() sharedtestvalues.SkipTestIfCannotManipulateOrders(t, b, canManipulateRealOrders) @@ -916,7 +916,7 @@ func TestCancelExchangeOrder(t *testing.T) { } } -func TestCancelAllExchangeOrdera(t *testing.T) { +func TestCancelAllExchangeOrders(t *testing.T) { t.Parallel() sharedtestvalues.SkipTestIfCannotManipulateOrders(t, b, canManipulateRealOrders) @@ -1051,56 +1051,58 @@ func TestGetDepositAddress(t *testing.T) { } } -func setupWs() { - var dialer websocket.Dialer - err := b.Websocket.AuthConn.Dial(&dialer, http.Header{}) - if err != nil { - log.Fatal(err) - } - go b.wsReadData(b.Websocket.AuthConn) - go b.WsDataHandler() -} +// ---------------------------------------------------------------------------------------------------------------------------- +// End RealTests +// TestWs tests have the ability to impact your orders on the exchange. Enable canManipulateRealOrders to run them +// ---------------------------------------------------------------------------------------------------------------------------- // TestWsAuth dials websocket, sends login request. func TestWsAuth(t *testing.T) { - if !b.Websocket.IsEnabled() && !b.API.AuthenticatedWebsocketSupport { - sharedtestvalues.SkipTestIfCredentialsUnset(t, b) - } - runAuth(t) -} - -//nolint:gocritic // Only used as a testing helper function in this package -func runAuth(t *testing.T) { - t.Helper() - setupWs() - if err := b.WsSendAuth(context.Background()); err != nil { - t.Error(err) + if !b.Websocket.IsEnabled() { + t.Skip(stream.WebsocketNotEnabled) } - timer := time.NewTimer(sharedtestvalues.WebsocketResponseDefaultTimeout) - select { - case resp := <-b.Websocket.DataHandler: - if logResponse, ok := resp.(map[string]interface{}); ok { - if logResponse["event"] != "auth" && logResponse["status"] != "OK" { - t.Error("expected successful login") - } - } else { - t.Error("Unexpected response") + sharedtestvalues.SkipTestIfCredentialsUnset(t, b) + if !b.API.AuthenticatedWebsocketSupport { + t.Skip("Authentecated API support not enabled") + } + setupWs(t) + assert.True(t, b.Websocket.CanUseAuthenticatedEndpoints(), "CanUseAuthenticatedEndpoints should be turned on") + + // An upcoming PR may change this to a specific datatype + var resp map[string]interface{} + catcher := func() (ok bool) { + select { + case v := <-b.Websocket.DataHandler: + resp, ok = v.(map[string]interface{}) + default: } - case <-timer.C: - t.Error("Have not received a response") + return + } + + if assert.Eventually(t, catcher, sharedtestvalues.WebsocketResponseDefaultTimeout, time.Millisecond*10, "Auth response should arrive") { + assert.Equal(t, "auth", resp["event"], "event should be correct") + assert.Equal(t, "OK", resp["status"], "status should be correct") + assert.NotEmpty(t, resp["auth_id"], "status should be correct") } - timer.Stop() - wsAuthExecuted = true +} + +// TestWsSubscribe tests Subscribe and Unsubscribe functionality +func TestWsSubscribe(t *testing.T) { + setupWs(t) + defSubs, err := b.GenerateDefaultSubscriptions() + assert.NoError(t, err) + err = b.Subscribe([]stream.ChannelSubscription{defSubs[0]}) + assert.NoError(t, err) + s, err := b.GetSubscriptions() + assert.NoError(t, err) + err = b.Unsubscribe(s) + assert.NoError(t, err) } // TestWsPlaceOrder dials websocket, sends order request. func TestWsPlaceOrder(t *testing.T) { - if !b.Websocket.IsEnabled() && !b.API.AuthenticatedWebsocketSupport { - sharedtestvalues.SkipTestIfCredentialsUnset(t, b) - } - if !wsAuthExecuted { - runAuth(t) - } + sharedtestvalues.SkipTestIfCredentialsUnset(t, b, canManipulateRealOrders) + setupWs(t) _, err := b.WsNewOrder(&WsNewOrderRequest{ GroupID: 1, @@ -1116,12 +1118,8 @@ func TestWsPlaceOrder(t *testing.T) { // TestWsCancelOrder dials websocket, sends cancel request. func TestWsCancelOrder(t *testing.T) { - if !b.Websocket.IsEnabled() && !b.API.AuthenticatedWebsocketSupport { - sharedtestvalues.SkipTestIfCredentialsUnset(t, b) - } - if !wsAuthExecuted { - runAuth(t) - } + sharedtestvalues.SkipTestIfCredentialsUnset(t, b, canManipulateRealOrders) + setupWs(t) if err := b.WsCancelOrder(1234); err != nil { t.Error(err) } @@ -1129,12 +1127,8 @@ func TestWsCancelOrder(t *testing.T) { // TestWsCancelOrder dials websocket, sends modify request. func TestWsUpdateOrder(t *testing.T) { - if !b.Websocket.IsEnabled() && !b.API.AuthenticatedWebsocketSupport { - sharedtestvalues.SkipTestIfCredentialsUnset(t, b) - } - if !wsAuthExecuted { - runAuth(t) - } + sharedtestvalues.SkipTestIfCredentialsUnset(t, b, canManipulateRealOrders) + setupWs(t) err := b.WsModifyOrder(&WsUpdateOrderRequest{ OrderID: 1234, Price: -111, @@ -1147,12 +1141,8 @@ func TestWsUpdateOrder(t *testing.T) { // TestWsCancelAllOrders dials websocket, sends cancel all request. func TestWsCancelAllOrders(t *testing.T) { - if !b.Websocket.IsEnabled() && !b.API.AuthenticatedWebsocketSupport { - sharedtestvalues.SkipTestIfCredentialsUnset(t, b) - } - if !wsAuthExecuted { - runAuth(t) - } + sharedtestvalues.SkipTestIfCredentialsUnset(t, b, canManipulateRealOrders) + setupWs(t) if err := b.WsCancelAllOrders(); err != nil { t.Error(err) } @@ -1160,12 +1150,8 @@ func TestWsCancelAllOrders(t *testing.T) { // TestWsCancelAllOrders dials websocket, sends cancel all request. func TestWsCancelMultiOrders(t *testing.T) { - if !b.Websocket.IsEnabled() && !b.API.AuthenticatedWebsocketSupport { - sharedtestvalues.SkipTestIfCredentialsUnset(t, b) - } - if !wsAuthExecuted { - runAuth(t) - } + sharedtestvalues.SkipTestIfCredentialsUnset(t, b, canManipulateRealOrders) + setupWs(t) err := b.WsCancelMultiOrders([]int64{1, 2, 3, 4}) if err != nil { t.Error(err) @@ -1174,12 +1160,8 @@ func TestWsCancelMultiOrders(t *testing.T) { // TestWsNewOffer dials websocket, sends new offer request. func TestWsNewOffer(t *testing.T) { - if !b.Websocket.IsEnabled() && !b.API.AuthenticatedWebsocketSupport { - sharedtestvalues.SkipTestIfCredentialsUnset(t, b) - } - if !wsAuthExecuted { - runAuth(t) - } + sharedtestvalues.SkipTestIfCredentialsUnset(t, b, canManipulateRealOrders) + setupWs(t) err := b.WsNewOffer(&WsNewOfferRequest{ Type: order.Limit.String(), Symbol: "fBTC", @@ -1194,12 +1176,8 @@ func TestWsNewOffer(t *testing.T) { // TestWsCancelOffer dials websocket, sends cancel offer request. func TestWsCancelOffer(t *testing.T) { - if !b.Websocket.IsEnabled() && !b.API.AuthenticatedWebsocketSupport { - sharedtestvalues.SkipTestIfCredentialsUnset(t, b) - } - if !wsAuthExecuted { - runAuth(t) - } + sharedtestvalues.SkipTestIfCredentialsUnset(t, b, canManipulateRealOrders) + setupWs(t) if err := b.WsCancelOffer(1234); err != nil { t.Error(err) } @@ -1336,7 +1314,7 @@ func TestWsNotifications(t *testing.T) { } } -func TestWSFundingOfferSnapshotAndUpdate(t *testing.T) { +func TestWsFundingOfferSnapshotAndUpdate(t *testing.T) { pressXToJSON := `[0,"fos",[[41237920,"fETH",1573912039000,1573912039000,0.5,0.5,"LIMIT",null,null,0,"ACTIVE",null,null,null,0.0024,2,0,0,null,0,null]]]` if err := b.wsHandleData([]byte(pressXToJSON)); err != nil { t.Error(err) @@ -1348,7 +1326,7 @@ func TestWSFundingOfferSnapshotAndUpdate(t *testing.T) { } } -func TestWSFundingCreditSnapshotAndUpdate(t *testing.T) { +func TestWsFundingCreditSnapshotAndUpdate(t *testing.T) { pressXToJSON := `[0,"fcs",[[26223578,"fUST",1,1575052261000,1575296187000,350,0,"ACTIVE",null,null,null,0,30,1575052261000,1575293487000,0,0,null,0,null,0,"tBTCUST"],[26223711,"fUSD",-1,1575291961000,1575296187000,180,0,"ACTIVE",null,null,null,0.002,7,1575282446000,1575295587000,0,0,null,0,null,0,"tETHUSD"]]]` if err := b.wsHandleData([]byte(pressXToJSON)); err != nil { t.Error(err) @@ -1360,7 +1338,7 @@ func TestWSFundingCreditSnapshotAndUpdate(t *testing.T) { } } -func TestWSFundingLoanSnapshotAndUpdate(t *testing.T) { +func TestWsFundingLoanSnapshotAndUpdate(t *testing.T) { pressXToJSON := `[0,"fls",[[2995442,"fUSD",-1,1575291961000,1575295850000,820,0,"ACTIVE",null,null,null,0.002,7,1575282446000,1575295850000,0,0,null,0,null,0]]]` if err := b.wsHandleData([]byte(pressXToJSON)); err != nil { t.Error(err) @@ -1372,35 +1350,35 @@ func TestWSFundingLoanSnapshotAndUpdate(t *testing.T) { } } -func TestWSWalletSnapshot(t *testing.T) { +func TestWsWalletSnapshot(t *testing.T) { pressXToJSON := `[0,"ws",[["exchange","SAN",19.76,0,null,null,null]]]` if err := b.wsHandleData([]byte(pressXToJSON)); err != nil { t.Error(err) } } -func TestWSBalanceUpdate(t *testing.T) { +func TestWsBalanceUpdate(t *testing.T) { const pressXToJSON = `[0,"bu",[4131.85,4131.85]]` if err := b.wsHandleData([]byte(pressXToJSON)); err != nil { t.Error(err) } } -func TestWSMarginInfoUpdate(t *testing.T) { +func TestWsMarginInfoUpdate(t *testing.T) { const pressXToJSON = `[0,"miu",["base",[-13.014640000000007,0,49331.70267297,49318.68803297,27]]]` if err := b.wsHandleData([]byte(pressXToJSON)); err != nil { t.Error(err) } } -func TestWSFundingInfoUpdate(t *testing.T) { +func TestWsFundingInfoUpdate(t *testing.T) { const pressXToJSON = `[0,"fiu",["sym","tETHUSD",[149361.09689202666,149639.26293509,830.0182168075556,895.0658432466332]]]` if err := b.wsHandleData([]byte(pressXToJSON)); err != nil { t.Error(err) } } -func TestWSFundingTrade(t *testing.T) { +func TestWsFundingTrade(t *testing.T) { pressXToJSON := `[0,"fte",[636854,"fUSD",1575282446000,41238905,-1000,0.002,7,null]]` if err := b.wsHandleData([]byte(pressXToJSON)); err != nil { t.Error(err) @@ -1858,3 +1836,23 @@ func TestChanForSub(t *testing.T) { assert.Nil(t, err, "No error returned when sub found") assert.EqualValues(t, want, *s, "Correct Sub found") } + +// setupWs is a helper function to connect both auth and normal websockets +// It will skip the test if websockets are not enabled +// It's up to the test to skip if it requires creds, though +func setupWs(tb testing.TB) { + tb.Helper() + if !b.Websocket.IsEnabled() { + tb.Skip("Websocket not enabled") + } + if b.Websocket.IsConnected() { + return + } + if wsConnected { + return + } + wsConnected = true + // We don't use b.websocket.Connect() because it'd subscribe to channels + err := b.WsConnect() + assert.NoError(tb, err, "WsConnect should not error") +}