Kucoin: Fix intermittent TestPushData failure (#2116)

* Testing: Fix FixtureToDataHandlerWithErrors logging []byte

* Kucoin: Fix global mutexes and maps

Highlighted by:
```
go test -count 2 -run TestPushData
```

that the second run wasn't finding a futures orderbook, because the
instance had changed but kucoin was using a global map
This commit is contained in:
Gareth Kirwan
2025-12-01 07:07:13 +07:00
committed by GitHub
parent dbf3b772b6
commit 6ae40f3688
4 changed files with 20 additions and 24 deletions

View File

@@ -31,11 +31,12 @@ import (
// Exchange implements exchange.IBotExchange and contains additional specific api methods for interacting with Kucoin // Exchange implements exchange.IBotExchange and contains additional specific api methods for interacting with Kucoin
type Exchange struct { type Exchange struct {
exchange.Base exchange.Base
obm *orderbookManager obmMutex sync.Mutex
obm *orderbookManager
fetchedFuturesOrderbookMutex sync.Mutex
fetchedFuturesOrderbook map[string]bool
} }
var locker sync.Mutex
const ( const (
kucoinAPIURL = "https://api.kucoin.com/api" kucoinAPIURL = "https://api.kucoin.com/api"
tradeBaseURL = "https://www.kucoin.com/" tradeBaseURL = "https://www.kucoin.com/"

View File

@@ -72,7 +72,6 @@ func TestMain(m *testing.M) {
asset.Margin: marginTradablePair, asset.Margin: marginTradablePair,
asset.Futures: futuresTradablePair, asset.Futures: futuresTradablePair,
} }
fetchedFuturesOrderbook = map[string]bool{}
os.Exit(m.Run()) os.Exit(m.Run())
} }
@@ -4088,13 +4087,14 @@ func TestGetCurrencyTradeURL(t *testing.T) {
// testInstance returns a local Kucoin for isolated testing // testInstance returns a local Kucoin for isolated testing
func testInstance(tb testing.TB) *Exchange { func testInstance(tb testing.TB) *Exchange {
tb.Helper() tb.Helper()
kucoin := new(Exchange) e := new(Exchange)
require.NoError(tb, testexch.Setup(kucoin), "Test instance Setup must not error") require.NoError(tb, testexch.Setup(e), "Test instance Setup must not error")
kucoin.obm = &orderbookManager{ e.obm = &orderbookManager{
state: make(map[currency.Code]map[currency.Code]map[asset.Item]*update), state: make(map[currency.Code]map[currency.Code]map[asset.Item]*update),
jobs: make(chan job, maxWSOrderbookJobs), jobs: make(chan job, maxWSOrderbookJobs),
} }
return kucoin e.fetchedFuturesOrderbook = map[string]bool{}
return e
} }
func TestGetTradingPairActualFees(t *testing.T) { func TestGetTradingPairActualFees(t *testing.T) {

View File

@@ -30,11 +30,6 @@ import (
"github.com/thrasher-corp/gocryptotrader/log" "github.com/thrasher-corp/gocryptotrader/log"
) )
var (
fetchedFuturesOrderbookMutex sync.Mutex
fetchedFuturesOrderbook map[string]bool
)
const ( const (
publicBullets = "/v1/bullet-public" publicBullets = "/v1/bullet-public"
privateBullets = "/v1/bullet-private" privateBullets = "/v1/bullet-private"
@@ -121,9 +116,9 @@ func (e *Exchange) WsConnect() error {
if !e.Websocket.IsEnabled() || !e.IsEnabled() { if !e.Websocket.IsEnabled() || !e.IsEnabled() {
return websocket.ErrWebsocketNotEnabled return websocket.ErrWebsocketNotEnabled
} }
fetchedFuturesOrderbookMutex.Lock() e.fetchedFuturesOrderbookMutex.Lock()
fetchedFuturesOrderbook = map[string]bool{} e.fetchedFuturesOrderbook = map[string]bool{}
fetchedFuturesOrderbookMutex.Unlock() e.fetchedFuturesOrderbookMutex.Unlock()
var dialer gws.Dialer var dialer gws.Dialer
dialer.HandshakeTimeout = e.Config.HTTPTimeout dialer.HandshakeTimeout = e.Config.HTTPTimeout
dialer.Proxy = http.ProxyFromEnvironment dialer.Proxy = http.ProxyFromEnvironment
@@ -503,12 +498,12 @@ func (e *Exchange) processFuturesMarkPriceAndIndexPrice(respData []byte, instrum
// ensureFuturesOrderbookSnapshotLoaded makes sure an initial futures orderbook snapshot is loaded // ensureFuturesOrderbookSnapshotLoaded makes sure an initial futures orderbook snapshot is loaded
func (e *Exchange) ensureFuturesOrderbookSnapshotLoaded(ctx context.Context, symbol string) error { func (e *Exchange) ensureFuturesOrderbookSnapshotLoaded(ctx context.Context, symbol string) error {
fetchedFuturesOrderbookMutex.Lock() e.fetchedFuturesOrderbookMutex.Lock()
defer fetchedFuturesOrderbookMutex.Unlock() defer e.fetchedFuturesOrderbookMutex.Unlock()
if fetchedFuturesOrderbook[symbol] { if e.fetchedFuturesOrderbook[symbol] {
return nil return nil
} }
fetchedFuturesOrderbook[symbol] = true e.fetchedFuturesOrderbook[symbol] = true
enabledPairs, err := e.GetEnabledPairs(asset.Futures) enabledPairs, err := e.GetEnabledPairs(asset.Futures)
if err != nil { if err != nil {
return err return err
@@ -1097,8 +1092,8 @@ type job struct {
// setupOrderbookManager sets up the orderbook manager for websocket orderbook data handling. // setupOrderbookManager sets up the orderbook manager for websocket orderbook data handling.
func (e *Exchange) setupOrderbookManager(ctx context.Context) { func (e *Exchange) setupOrderbookManager(ctx context.Context) {
locker.Lock() e.obmMutex.Lock()
defer locker.Unlock() defer e.obmMutex.Unlock()
if e.obm == nil { if e.obm == nil {
e.obm = &orderbookManager{ e.obm = &orderbookManager{
state: make(map[currency.Code]map[currency.Code]map[asset.Item]*update), state: make(map[currency.Code]map[currency.Code]map[asset.Item]*update),

View File

@@ -133,7 +133,7 @@ func MockWsInstance[T any, PT interface {
// FixtureError contains an error and the message that caused it // FixtureError contains an error and the message that caused it
type FixtureError struct { type FixtureError struct {
Err error Err error
Msg []byte Msg string
} }
// FixtureToDataHandler squirts the contents of a file to a reader function (probably e.wsHandleData) and asserts no errors are returned // FixtureToDataHandler squirts the contents of a file to a reader function (probably e.wsHandleData) and asserts no errors are returned
@@ -163,7 +163,7 @@ func FixtureToDataHandlerWithErrors(tb testing.TB, fixturePath string, reader fu
if err := reader(tb.Context(), msg); err != nil { if err := reader(tb.Context(), msg); err != nil {
errs = append(errs, FixtureError{ errs = append(errs, FixtureError{
Err: err, Err: err,
Msg: msg, Msg: string(msg),
}) })
} }
} }