Files
gocryptotrader/exchanges/lbank/lbank_test.go
Gareth Kirwan 5d6755b76e linters: Exclude govet shadow check on exchange instances (#2097)
* Linters: Exclude govet shadow check on e in tests

* Linters: Remove nolint rule for new(Exchange) in tests

Replay with:
```
perl -pi -e 's{(\se\s:=\s.*?)\s*//nolint:govet // Intentional shadow.*}{$1}' **/*_test.go
```
2025-10-29 09:30:23 +11:00

504 lines
16 KiB
Go

package lbank
import (
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/md5" //nolint:gosec // Used for this exchange
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/base64"
"encoding/hex"
"log"
"os"
"strings"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/currency"
"github.com/thrasher-corp/gocryptotrader/exchange/accounts"
"github.com/thrasher-corp/gocryptotrader/exchange/order/limits"
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
"github.com/thrasher-corp/gocryptotrader/exchanges/kline"
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
"github.com/thrasher-corp/gocryptotrader/exchanges/sharedtestvalues"
testexch "github.com/thrasher-corp/gocryptotrader/internal/testing/exchange"
)
// Please supply your own keys here for due diligence testing
const (
apiKey = ""
apiSecret = ""
canManipulateRealOrders = false
)
var (
e *Exchange
testPair = currency.NewBTCUSDT().Format(currency.PairFormat{Delimiter: "_"})
)
func TestMain(m *testing.M) {
e = new(Exchange)
if err := testexch.Setup(e); err != nil {
log.Fatalf("Lbank Setup error: %s", err)
}
if apiKey != "" && apiSecret != "" {
e.API.AuthenticatedSupport = true
e.SetCredentials(apiKey, apiSecret, "", "", "", "")
}
os.Exit(m.Run())
}
func TestGetTicker(t *testing.T) {
t.Parallel()
_, err := e.GetTicker(t.Context(), testPair.String())
assert.NoError(t, err, "GetTicker should not error")
}
func TestGetTimestamp(t *testing.T) {
t.Parallel()
ts, err := e.GetTimestamp(t.Context())
require.NoError(t, err, "GetTimestamp must not error")
assert.NotZero(t, ts, "GetTimestamp should return a non-zero time")
}
func TestGetTickers(t *testing.T) {
t.Parallel()
tickers, err := e.GetTickers(t.Context())
require.NoError(t, err, "GetTickers must not error")
assert.Greater(t, len(tickers), 1, "GetTickers should return more than 1 ticker")
}
func TestGetCurrencyPairs(t *testing.T) {
t.Parallel()
_, err := e.GetCurrencyPairs(t.Context())
assert.NoError(t, err, "GetCurrencyPairs should not error")
}
func TestGetMarketDepths(t *testing.T) {
t.Parallel()
d, err := e.GetMarketDepths(t.Context(), testPair.String(), 4)
require.NoError(t, err, "GetMarketDepths must not error")
require.NotEmpty(t, d, "GetMarketDepths must return a non-empty response")
assert.Len(t, d.Data.Asks, 4, "GetMarketDepths should return 4 asks")
}
func TestGetTrades(t *testing.T) {
t.Parallel()
r, err := e.GetTrades(t.Context(), testPair.String(), 420, time.Now())
require.NoError(t, err, "GetTrades must not error")
require.NotEmpty(t, r, "GetTrades must return a non-empty response")
assert.Len(t, r, 420, "GetTrades should return 420 trades")
}
func TestGetKlines(t *testing.T) {
t.Parallel()
_, err := e.GetKlines(t.Context(), testPair.String(), "600", "minute1", time.Now())
assert.NoError(t, err, "GetKlines should not error")
}
func TestUpdateOrderbook(t *testing.T) {
t.Parallel()
_, err := e.UpdateOrderbook(t.Context(), currency.EMPTYPAIR, asset.Spot)
assert.ErrorIs(t, err, currency.ErrCurrencyPairEmpty)
_, err = e.UpdateOrderbook(t.Context(), testPair, asset.Options)
assert.ErrorIs(t, err, asset.ErrNotSupported)
_, err = e.UpdateOrderbook(t.Context(), testPair, asset.Spot)
assert.NoError(t, err, "UpdateOrderbook should not error")
}
func TestGetUserInfo(t *testing.T) {
t.Parallel()
sharedtestvalues.SkipTestIfCredentialsUnset(t, e)
_, err := e.GetUserInfo(t.Context())
require.NoError(t, err, "GetUserInfo must not error")
}
func TestCreateOrder(t *testing.T) {
t.Parallel()
_, err := e.CreateOrder(t.Context(), testPair.String(), "what", 1231, 12314)
require.ErrorIs(t, err, order.ErrSideIsInvalid)
_, err = e.CreateOrder(t.Context(), testPair.String(), order.Buy.String(), 0, 0)
require.ErrorIs(t, err, limits.ErrAmountBelowMin)
_, err = e.CreateOrder(t.Context(), testPair.String(), order.Sell.String(), 1231, 0)
require.ErrorIs(t, err, limits.ErrPriceBelowMin)
sharedtestvalues.SkipTestIfCredentialsUnset(t, e, canManipulateRealOrders)
_, err = e.CreateOrder(t.Context(), testPair.String(), order.Buy.String(), 58, 681)
assert.NoError(t, err, "CreateOrder should not error")
}
func TestRemoveOrder(t *testing.T) {
t.Parallel()
sharedtestvalues.SkipTestIfCredentialsUnset(t, e, canManipulateRealOrders)
_, err := e.RemoveOrder(t.Context(), testPair.String(), "24f7ce27-af1d-4dca-a8c1-ef1cbeec1b23")
assert.NoError(t, err, "RemoveOrder should not error")
}
func TestQueryOrder(t *testing.T) {
t.Parallel()
sharedtestvalues.SkipTestIfCredentialsUnset(t, e)
_, err := e.QueryOrder(t.Context(), testPair.String(), "1")
assert.NoError(t, err, "QueryOrder should not error")
}
func TestQueryOrderHistory(t *testing.T) {
t.Parallel()
sharedtestvalues.SkipTestIfCredentialsUnset(t, e)
_, err := e.QueryOrderHistory(t.Context(), testPair.String(), "1", "100")
assert.NoError(t, err, "QueryOrderHistory should not error")
}
func TestGetPairInfo(t *testing.T) {
t.Parallel()
_, err := e.GetPairInfo(t.Context())
assert.NoError(t, err, "GetPairInfo should not error")
}
func TestOrderTransactionDetails(t *testing.T) {
t.Parallel()
sharedtestvalues.SkipTestIfCredentialsUnset(t, e)
_, err := e.OrderTransactionDetails(t.Context(), testPair.String(), "24f7ce27-af1d-4dca-a8c1-ef1cbeec1b23")
assert.NoError(t, err, "OrderTransactionDetails should not error")
}
func TestTransactionHistory(t *testing.T) {
t.Parallel()
sharedtestvalues.SkipTestIfCredentialsUnset(t, e)
_, err := e.TransactionHistory(t.Context(), testPair.String(), "", "", "", "", "", "")
assert.NoError(t, err, "TransactionHistory should not error")
}
func TestGetOpenOrders(t *testing.T) {
t.Parallel()
sharedtestvalues.SkipTestIfCredentialsUnset(t, e)
_, err := e.GetOpenOrders(t.Context(), testPair.String(), "1", "50")
assert.NoError(t, err, "GetOpenOrders should not error")
}
func TestUSD2RMBRate(t *testing.T) {
t.Parallel()
_, err := e.USD2RMBRate(t.Context())
assert.NoError(t, err, "USD2RMBRate should not error")
}
func TestGetWithdrawConfig(t *testing.T) {
t.Parallel()
c, err := e.GetWithdrawConfig(t.Context(), currency.ETH)
require.NoError(t, err, "GetWithdrawConfig must not error")
assert.NotEmpty(t, c)
}
func TestWithdraw(t *testing.T) {
t.Parallel()
sharedtestvalues.SkipTestIfCredentialsUnset(t, e, canManipulateRealOrders)
_, err := e.Withdraw(t.Context(), "", "", "", "", "", "")
require.NoError(t, err, "Withdraw must not error")
}
func TestGetWithdrawRecords(t *testing.T) {
t.Parallel()
sharedtestvalues.SkipTestIfCredentialsUnset(t, e)
_, err := e.GetWithdrawalRecords(t.Context(), currency.ETH.String(), 0, 1, 20)
assert.NoError(t, err, "GetWithdrawRecords should not error")
}
func TestLoadPrivKey(t *testing.T) {
t.Parallel()
e := new(Exchange)
e.SetDefaults()
require.ErrorIs(t, e.loadPrivKey(t.Context()), exchange.ErrCredentialsAreEmpty)
ctx := accounts.DeployCredentialsToContext(t.Context(), &accounts.Credentials{Key: "test", Secret: "errortest"})
assert.ErrorIs(t, e.loadPrivKey(ctx), errPEMBlockIsNil)
key, err := rsa.GenerateKey(rand.Reader, 2048)
require.NoError(t, err)
der := x509.MarshalPKCS1PrivateKey(key)
ctx = accounts.DeployCredentialsToContext(t.Context(), &accounts.Credentials{Key: "test", Secret: base64.StdEncoding.EncodeToString(der)})
require.ErrorIs(t, e.loadPrivKey(ctx), errUnableToParsePrivateKey)
ecdsaKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
require.NoError(t, err)
der, err = x509.MarshalPKCS8PrivateKey(ecdsaKey)
require.NoError(t, err)
ctx = accounts.DeployCredentialsToContext(t.Context(), &accounts.Credentials{Key: "test", Secret: base64.StdEncoding.EncodeToString(der)})
require.ErrorIs(t, e.loadPrivKey(ctx), common.ErrTypeAssertFailure)
key, err = rsa.GenerateKey(rand.Reader, 2048)
require.NoError(t, err)
der, err = x509.MarshalPKCS8PrivateKey(key)
require.NoError(t, err)
ctx = accounts.DeployCredentialsToContext(t.Context(), &accounts.Credentials{Key: "test", Secret: base64.StdEncoding.EncodeToString(der)})
assert.NoError(t, e.loadPrivKey(ctx), "loadPrivKey should not error")
sharedtestvalues.SkipTestIfCredentialsUnset(t, e)
assert.NoError(t, e.loadPrivKey(t.Context()), "loadPrivKey should not error")
}
func TestSign(t *testing.T) {
t.Parallel()
e := new(Exchange)
e.SetDefaults()
_, err := e.sign("hello123")
require.ErrorIs(t, err, errPrivateKeyNotLoaded)
key, err := rsa.GenerateKey(rand.Reader, 2048)
require.NoError(t, err, "GenerateKey must not error")
e.privateKey = key
targetMessage := "hello123"
msg, err := e.sign(targetMessage)
require.NoError(t, err, "sign must not error")
md5sum := md5.Sum([]byte(targetMessage)) //nolint:gosec // Used for this exchange
shasum := sha256.Sum256([]byte(strings.ToUpper(hex.EncodeToString(md5sum[:]))))
sigBytes, err := base64.StdEncoding.DecodeString(msg)
require.NoError(t, err)
err = rsa.VerifyPKCS1v15(&e.privateKey.PublicKey, crypto.SHA256, shasum[:], sigBytes)
require.NoError(t, err)
sharedtestvalues.SkipTestIfCredentialsUnset(t, e)
require.NoError(t, e.loadPrivKey(t.Context()), "loadPrivKey must not error")
_, err = e.sign("hello123")
assert.NoError(t, err, "sign should not error")
}
func TestSubmitOrder(t *testing.T) {
t.Parallel()
sharedtestvalues.SkipTestIfCannotManipulateOrders(t, e, canManipulateRealOrders)
r, err := e.SubmitOrder(t.Context(), &order.Submit{
Exchange: e.Name,
Pair: testPair,
Side: order.Buy,
Type: order.Limit,
Price: 1,
Amount: 1,
ClientID: "meowOrder",
AssetType: asset.Spot,
})
if sharedtestvalues.AreAPICredentialsSet(e) {
require.NoError(t, err, "SubmitOrder must not error")
assert.Equal(t, order.New, r.Status, "SubmitOrder should return order status New")
} else {
assert.Error(t, err, "SubmitOrder should error when credentials are not set")
}
}
func TestCancelOrder(t *testing.T) {
t.Parallel()
sharedtestvalues.SkipTestIfCredentialsUnset(t, e, canManipulateRealOrders)
err := e.CancelOrder(t.Context(), &order.Cancel{
Pair: testPair,
AssetType: asset.Spot,
OrderID: "24f7ce27-af1d-4dca-a8c1-ef1cbeec1b23",
})
assert.NoError(t, err, "CancelOrder should not error")
}
func TestGetOrderInfo(t *testing.T) {
t.Parallel()
sharedtestvalues.SkipTestIfCredentialsUnset(t, e)
_, err := e.GetOrderInfo(t.Context(), "9ead39f5-701a-400b-b635-d7349eb0f6b", currency.EMPTYPAIR, asset.Spot)
assert.NoError(t, err, "GetOrderInfo should not error")
}
func TestGetAllOpenOrderID(t *testing.T) {
t.Parallel()
sharedtestvalues.SkipTestIfCredentialsUnset(t, e)
_, err := e.getAllOpenOrderID(t.Context())
assert.NoError(t, err, "getAllOpenOrderID should not error")
}
func TestGetFeeByType(t *testing.T) {
t.Parallel()
_, err := e.GetFeeByType(t.Context(), &exchange.FeeBuilder{
Amount: 2,
FeeType: exchange.CryptocurrencyWithdrawalFee,
Pair: testPair,
})
assert.NoError(t, err, "GetFeeByType should not error")
}
func TestGetAccountInfo(t *testing.T) {
t.Parallel()
sharedtestvalues.SkipTestIfCredentialsUnset(t, e)
_, err := e.UpdateAccountBalances(t.Context(), asset.Spot)
assert.NoError(t, err, "UpdateAccountBalances should not error")
}
func TestGetActiveOrders(t *testing.T) {
t.Parallel()
sharedtestvalues.SkipTestIfCredentialsUnset(t, e)
_, err := e.GetActiveOrders(t.Context(), &order.MultiOrderRequest{
Side: order.AnySide,
AssetType: asset.Spot,
Type: order.AnyType,
})
assert.NoError(t, err, "GetActiveOrders should not error")
}
func TestGetOrderHistory(t *testing.T) {
t.Parallel()
sharedtestvalues.SkipTestIfCredentialsUnset(t, e)
_, err := e.GetOrderHistory(t.Context(), &order.MultiOrderRequest{
Side: order.AnySide,
AssetType: asset.Spot,
Type: order.AnyType,
})
assert.NoError(t, err, "GetOrderHistory should not error")
}
func TestGetHistoricCandles(t *testing.T) {
t.Parallel()
_, err := e.GetHistoricCandles(t.Context(), currency.EMPTYPAIR, asset.Spot, kline.OneMin, time.Time{}, time.Time{})
assert.ErrorIs(t, err, currency.ErrCurrencyPairEmpty)
_, err = e.GetHistoricCandles(t.Context(), testPair, asset.Spot, kline.OneMin, time.Now().Add(-24*time.Hour), time.Now())
assert.NoError(t, err, "GetHistoricCandles should not error")
}
func TestGetHistoricCandlesExtended(t *testing.T) {
t.Parallel()
_, err := e.GetHistoricCandlesExtended(t.Context(), testPair, asset.Spot, kline.OneMin, time.Now().Add(-time.Minute*2), time.Now())
assert.NoError(t, err, "GetHistoricCandlesExtended should not error")
}
func TestFormatExchangeKlineInterval(t *testing.T) {
t.Parallel()
for _, tc := range []struct {
interval kline.Interval
output string
}{
{
kline.OneMin,
"minute1",
},
{
kline.OneHour,
"hour1",
},
{
kline.OneDay,
"day1",
},
{
kline.OneWeek,
"week1",
},
{
kline.FifteenDay,
"",
},
} {
t.Run(tc.interval.String(), func(t *testing.T) {
t.Parallel()
ret := e.FormatExchangeKlineInterval(tc.interval)
assert.Equalf(t, tc.output, ret, "FormatExchangeKlineInterval(%s) should return %q", tc.interval, tc.output)
})
}
}
func TestGetRecentTrades(t *testing.T) {
t.Parallel()
_, err := e.GetRecentTrades(t.Context(), testPair, asset.Spot)
assert.NoError(t, err, "GetRecentTrades should not error")
}
func TestGetHistoricTrades(t *testing.T) {
t.Parallel()
_, err := e.GetHistoricTrades(t.Context(), testPair, asset.Spot, time.Now().AddDate(69, 0, 0), time.Now())
assert.ErrorIs(t, err, common.ErrStartAfterEnd)
_, err = e.GetHistoricTrades(t.Context(), currency.EMPTYPAIR, asset.Spot, time.Now().Add(-time.Minute*15), time.Now())
assert.ErrorIs(t, err, currency.ErrCurrencyPairEmpty)
_, err = e.GetHistoricTrades(t.Context(), testPair, asset.Spot, time.Now().Add(-time.Minute*15), time.Now())
assert.NoError(t, err, "GetHistoricTrades should not error")
}
func TestUpdateTicker(t *testing.T) {
t.Parallel()
_, err := e.UpdateTicker(t.Context(), testPair, asset.Spot)
assert.NoError(t, err, "UpdateTicker should not error")
}
func TestUpdateTickers(t *testing.T) {
t.Parallel()
err := e.UpdateTickers(t.Context(), asset.Spot)
assert.NoError(t, err, "UpdateTickers should not error")
}
func TestGetStatus(t *testing.T) {
t.Parallel()
for _, tt := range []struct {
status int64
resp order.Status
}{
{status: -1, resp: order.Cancelled},
{status: 0, resp: order.Active},
{status: 1, resp: order.PartiallyFilled},
{status: 2, resp: order.Filled},
{status: 4, resp: order.Cancelling},
{status: 5, resp: order.UnknownStatus},
} {
t.Run(tt.resp.String(), func(t *testing.T) {
t.Parallel()
assert.Equalf(t, tt.resp.String(), e.GetStatus(tt.status).String(), "GetStatus(%d) should return %s", tt.status, tt.resp)
})
}
}
func TestGetServerTime(t *testing.T) {
t.Parallel()
ts, err := e.GetServerTime(t.Context(), asset.Spot)
require.NoError(t, err, "GetServerTime must not error")
assert.NotZero(t, ts, "GetServerTime should return a non-zero time")
}
func TestGetWithdrawalsHistory(t *testing.T) {
t.Parallel()
sharedtestvalues.SkipTestIfCredentialsUnset(t, e)
_, err := e.GetWithdrawalsHistory(t.Context(), currency.BTC, asset.Spot)
assert.NoError(t, err, "GetWithdrawalsHistory should not error")
}
func TestGetCurrencyTradeURL(t *testing.T) {
t.Parallel()
testexch.UpdatePairsOnce(t, e)
for _, a := range e.GetAssetTypes(false) {
pairs, err := e.CurrencyPairs.GetPairs(a, false)
require.NoErrorf(t, err, "GetPairs must not error for asset %s", a)
require.NotEmptyf(t, pairs, "GetPairs for asset %s must return pairs", a)
resp, err := e.GetCurrencyTradeURL(t.Context(), a, pairs[0])
require.NoError(t, err)
assert.NotEmpty(t, resp)
}
}