mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-06-03 15:10:49 +00:00
exchanges/qa: Add exchange wrapper testing suite (#1159)
* initial concept of a nice validation tester for exchanges * adds some datahandler design * expand testing * more tests and fixes * minor end of day fix for bithumb * fixes implementation issues * more test coverage and improvements, but not sure if i should continue * fix more wrapper implementations * adds error type, more fixes * changes signature, fixes implementations * fixes more wrapper implementations * one more bit * more cleanup * WOW things work? * lintle 1/1337 * mini bump * fixes all linting * neaten * GetOrderInfo+ asset pair fixes+improvements * adds new websocket test * expand ws testing * fix bug, expand tests, improve implementation * code coverage of a lot of new codes * fixes everything * reverts accidental changes * minor fixes from reviewing code * removes Bitfinex cancelBatchOrder implementation * fixes dumb baby typo for babies * mini nit fixes * so many nits to address * addresses all the nits * Titlecase * switcheroo * removes websocket testing for now * fix appveyor, minor test fix * fixes typo, re-kindles killed kode * skip binance wrapper tests when running CI * expired context, huobi okx fixes * kodespull * fix ordering * time fix because why not * fix exmo, others * hopefully this fixes all of my life's problems * last thing today * huobi, more like hypotrophy * golangci-lint, more like mypooroldknee-splint * fix huobi times by removing them * should fix okx currency issues * blocks the application * adds last little contingency for pairs * addresses most nits and new problems * lovely fixed before seeing why okx sucks * fixes issues with okx websocket * the classic receieieivaier * lintle * adds test and fixes existing tests * expands error handling messages during setup * fixes dumb okx bugs introduced * quick fix for lint and exmo * fixes nixes * fix exmo deposit issue * lint * fixes issue with extra asset runs missing * fix surprise race * all the lint and merge fixes * fixes surprise bugs in OKx * fixes issues with times and chains * fixing all the merge stuff * merge fix * rm logs and a panic potential * lovely lint lament * an easy demonstration of scenario, but not of initial purpose * put it in the bin * Revert "put it in the bin" This reverts commit 15c6490f713233d43f10957367fcbf18e3818bdd. * re-add after immediate error popup * fix mini poor test design * okx okay * merge fixes * fixes issues discovered in lovely test * I FORGOT TO COMMIT THIS * nit fixaroonaboo * forgoetten test fix * revert old okx asset intrument work * fixes * revert problems I didnt understand. update bybit * fix merge bugs * test cleanup * further improvements * reshuffle and lint * rm redundant CI_TEST by rm the CI_TEST field that is redundant * path fix * move to its own section, dont run on 32 bit + appveyor * lint * fix lbank * address nits * let it rip * fix failing test time range * niteroo boogaloo * mod tidy, use common.SimpleTimeFormat
This commit is contained in:
@@ -12,18 +12,34 @@ import (
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-corp/gocryptotrader/common"
|
||||
"github.com/thrasher-corp/gocryptotrader/common/timedmutex"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/mock"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/nonce"
|
||||
"github.com/thrasher-corp/gocryptotrader/log"
|
||||
)
|
||||
|
||||
const contextVerboseFlag verbosity = "verbose"
|
||||
const (
|
||||
// UnsetRequest is an unset request authentication level
|
||||
UnsetRequest AuthType = 0
|
||||
// UnauthenticatedRequest denotes a request with no credentials
|
||||
UnauthenticatedRequest = iota << 1
|
||||
// AuthenticatedRequest denotes a request using API credentials
|
||||
AuthenticatedRequest
|
||||
|
||||
contextVerboseFlag verbosity = "verbose"
|
||||
)
|
||||
|
||||
// AuthType helps distinguish the purpose of a HTTP request
|
||||
type AuthType uint8
|
||||
|
||||
var (
|
||||
// ErrRequestSystemIsNil defines and error if the request system has not
|
||||
// been set up yet.
|
||||
ErrRequestSystemIsNil = errors.New("request system is nil")
|
||||
ErrRequestSystemIsNil = errors.New("request system is nil")
|
||||
// ErrAuthRequestFailed is a wrapping error to denote that it's an auth request that failed
|
||||
ErrAuthRequestFailed = errors.New("authenticated request failed")
|
||||
|
||||
errMaxRequestJobs = errors.New("max request jobs reached")
|
||||
errRequestFunctionIsNil = errors.New("request function is nil")
|
||||
errRequestItemNil = errors.New("request item is nil")
|
||||
@@ -32,6 +48,7 @@ var (
|
||||
errFailedToRetryRequest = errors.New("failed to retry request")
|
||||
errContextRequired = errors.New("context is required")
|
||||
errTransportNotSet = errors.New("transport not set, cannot set timeout")
|
||||
errRequestTypeUnpopulated = errors.New("request type bool is not populated")
|
||||
)
|
||||
|
||||
// New returns a new Requester
|
||||
@@ -58,7 +75,7 @@ func New(name string, httpRequester *http.Client, opts ...RequesterOption) (*Req
|
||||
}
|
||||
|
||||
// SendPayload handles sending HTTP/HTTPS requests
|
||||
func (r *Requester) SendPayload(ctx context.Context, ep EndpointLimit, newRequest Generate) error {
|
||||
func (r *Requester) SendPayload(ctx context.Context, ep EndpointLimit, newRequest Generate, requestType AuthType) error {
|
||||
if r == nil {
|
||||
return ErrRequestSystemIsNil
|
||||
}
|
||||
@@ -66,6 +83,9 @@ func (r *Requester) SendPayload(ctx context.Context, ep EndpointLimit, newReques
|
||||
if ctx == nil {
|
||||
return errContextRequired
|
||||
}
|
||||
if requestType == UnsetRequest {
|
||||
return errRequestTypeUnpopulated
|
||||
}
|
||||
|
||||
defer r.timedLock.UnlockIfLocked()
|
||||
|
||||
@@ -80,6 +100,9 @@ func (r *Requester) SendPayload(ctx context.Context, ep EndpointLimit, newReques
|
||||
atomic.AddInt32(&r.jobs, 1)
|
||||
err := r.doRequest(ctx, ep, newRequest)
|
||||
atomic.AddInt32(&r.jobs, -1)
|
||||
if err != nil && requestType == AuthenticatedRequest {
|
||||
err = common.AppendError(err, ErrAuthRequestFailed)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -236,21 +236,21 @@ func TestDoRequest(t *testing.T) {
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
err = (*Requester)(nil).SendPayload(ctx, Unset, nil)
|
||||
err = (*Requester)(nil).SendPayload(ctx, Unset, nil, UnauthenticatedRequest)
|
||||
if !errors.Is(ErrRequestSystemIsNil, err) {
|
||||
t.Fatalf("expected: %v but received: %v", ErrRequestSystemIsNil, err)
|
||||
}
|
||||
err = r.SendPayload(ctx, Unset, nil)
|
||||
err = r.SendPayload(ctx, Unset, nil, UnauthenticatedRequest)
|
||||
if !errors.Is(errRequestFunctionIsNil, err) {
|
||||
t.Fatalf("expected: %v but received: %v", errRequestFunctionIsNil, err)
|
||||
}
|
||||
|
||||
err = r.SendPayload(ctx, UnAuth, func() (*Item, error) { return nil, nil })
|
||||
err = r.SendPayload(ctx, UnAuth, func() (*Item, error) { return nil, nil }, UnauthenticatedRequest)
|
||||
if !errors.Is(errRequestItemNil, err) {
|
||||
t.Fatalf("expected: %v but received: %v", errRequestItemNil, err)
|
||||
}
|
||||
|
||||
err = r.SendPayload(ctx, UnAuth, func() (*Item, error) { return &Item{}, nil })
|
||||
err = r.SendPayload(ctx, UnAuth, func() (*Item, error) { return &Item{}, nil }, UnauthenticatedRequest)
|
||||
if !errors.Is(errInvalidPath, err) {
|
||||
t.Fatalf("expected: %v but received: %v", errInvalidPath, err)
|
||||
}
|
||||
@@ -261,7 +261,7 @@ func TestDoRequest(t *testing.T) {
|
||||
Path: testURL,
|
||||
HeaderResponse: &nilHeader,
|
||||
}, nil
|
||||
})
|
||||
}, UnauthenticatedRequest)
|
||||
if !errors.Is(errHeaderResponseMapIsNil, err) {
|
||||
t.Fatalf("expected: %v but received: %v", errHeaderResponseMapIsNil, err)
|
||||
}
|
||||
@@ -271,7 +271,7 @@ func TestDoRequest(t *testing.T) {
|
||||
return &Item{
|
||||
Path: testURL,
|
||||
}, nil
|
||||
})
|
||||
}, UnauthenticatedRequest)
|
||||
if !errors.Is(err, errEndpointLimitNotFound) {
|
||||
t.Fatalf("expected: %v but received: %v", errEndpointLimitNotFound, err)
|
||||
}
|
||||
@@ -287,7 +287,7 @@ func TestDoRequest(t *testing.T) {
|
||||
HTTPDebugging: true,
|
||||
Verbose: true,
|
||||
}, nil
|
||||
})
|
||||
}, UnauthenticatedRequest)
|
||||
if !errors.Is(err, nil) {
|
||||
t.Fatalf("received: %v but expected: %v", err, nil)
|
||||
}
|
||||
@@ -296,7 +296,7 @@ func TestDoRequest(t *testing.T) {
|
||||
newError := errors.New("request item failure")
|
||||
err = r.SendPayload(ctx, UnAuth, func() (*Item, error) {
|
||||
return nil, newError
|
||||
})
|
||||
}, UnauthenticatedRequest)
|
||||
if !errors.Is(err, newError) {
|
||||
t.Fatalf("received: %v but expected: %v", err, newError)
|
||||
}
|
||||
@@ -305,7 +305,7 @@ func TestDoRequest(t *testing.T) {
|
||||
r.jobs = MaxRequestJobs
|
||||
err = r.SendPayload(ctx, UnAuth, func() (*Item, error) {
|
||||
return &Item{Path: testURL}, nil
|
||||
})
|
||||
}, UnauthenticatedRequest)
|
||||
if !errors.Is(err, errMaxRequestJobs) {
|
||||
t.Fatalf("received: %v but expected: %v", err, errMaxRequestJobs)
|
||||
}
|
||||
@@ -324,7 +324,7 @@ func TestDoRequest(t *testing.T) {
|
||||
}
|
||||
err = r.SendPayload(ctx, UnAuth, func() (*Item, error) {
|
||||
return &Item{Path: testURL + "/timeout"}, nil
|
||||
})
|
||||
}, UnauthenticatedRequest)
|
||||
if !errors.Is(err, errFailedToRetryRequest) {
|
||||
t.Fatalf("received: %v but expected: %v", err, errFailedToRetryRequest)
|
||||
}
|
||||
@@ -348,7 +348,7 @@ func TestDoRequest(t *testing.T) {
|
||||
Result: &resp,
|
||||
HeaderResponse: &passback,
|
||||
}, nil
|
||||
})
|
||||
}, UnauthenticatedRequest)
|
||||
if !errors.Is(err, nil) {
|
||||
t.Fatalf("received: %v but expected: %v", err, nil)
|
||||
}
|
||||
@@ -371,7 +371,7 @@ func TestDoRequest(t *testing.T) {
|
||||
Path: testURL,
|
||||
Result: &respErr,
|
||||
}, nil
|
||||
})
|
||||
}, UnauthenticatedRequest)
|
||||
if !errors.Is(err, nil) {
|
||||
t.Fatalf("received: %v but expected: %v", err, nil)
|
||||
}
|
||||
@@ -391,12 +391,11 @@ func TestDoRequest(t *testing.T) {
|
||||
}
|
||||
payloadError := r.SendPayload(ctx, Auth, func() (*Item, error) {
|
||||
return &Item{
|
||||
Method: http.MethodGet,
|
||||
Path: testURL + "/rate",
|
||||
Result: &resp,
|
||||
AuthRequest: true,
|
||||
Method: http.MethodGet,
|
||||
Path: testURL + "/rate",
|
||||
Result: &resp,
|
||||
}, nil
|
||||
})
|
||||
}, AuthenticatedRequest)
|
||||
wg.Done()
|
||||
if payloadError != nil {
|
||||
atomic.StoreInt32(&failed, 1)
|
||||
@@ -436,12 +435,11 @@ func TestDoRequest_Retries(t *testing.T) {
|
||||
}
|
||||
payloadError := r.SendPayload(context.Background(), Auth, func() (*Item, error) {
|
||||
return &Item{
|
||||
Method: http.MethodGet,
|
||||
Path: testURL + "/rate-retry",
|
||||
Result: &resp,
|
||||
AuthRequest: true,
|
||||
Method: http.MethodGet,
|
||||
Path: testURL + "/rate-retry",
|
||||
Result: &resp,
|
||||
}, nil
|
||||
})
|
||||
}, AuthenticatedRequest)
|
||||
if payloadError != nil {
|
||||
atomic.StoreInt32(&failed, 1)
|
||||
log.Fatal(payloadError)
|
||||
@@ -474,7 +472,7 @@ func TestDoRequest_RetryNonRecoverable(t *testing.T) {
|
||||
Method: http.MethodGet,
|
||||
Path: testURL + "/always-retry",
|
||||
}, nil
|
||||
})
|
||||
}, UnauthenticatedRequest)
|
||||
if !errors.Is(err, errFailedToRetryRequest) {
|
||||
t.Fatalf("received: %v but expected: %v", err, errFailedToRetryRequest)
|
||||
}
|
||||
@@ -499,7 +497,7 @@ func TestDoRequest_NotRetryable(t *testing.T) {
|
||||
Method: http.MethodGet,
|
||||
Path: testURL + "/always-retry",
|
||||
}, nil
|
||||
})
|
||||
}, UnauthenticatedRequest)
|
||||
if !errors.Is(err, notRetryErr) {
|
||||
t.Fatalf("received: %v but expected: %v", err, notRetryErr)
|
||||
}
|
||||
@@ -586,11 +584,11 @@ func TestBasicLimiter(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
tn := time.Now()
|
||||
err = r.SendPayload(ctx, Unset, func() (*Item, error) { return &i, nil })
|
||||
err = r.SendPayload(ctx, Unset, func() (*Item, error) { return &i, nil }, UnauthenticatedRequest)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = r.SendPayload(ctx, Unset, func() (*Item, error) { return &i, nil })
|
||||
err = r.SendPayload(ctx, Unset, func() (*Item, error) { return &i, nil }, UnauthenticatedRequest)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -600,7 +598,7 @@ func TestBasicLimiter(t *testing.T) {
|
||||
|
||||
ctx, cancel := context.WithDeadline(ctx, tn.Add(time.Nanosecond))
|
||||
defer cancel()
|
||||
err = r.SendPayload(ctx, Unset, func() (*Item, error) { return &i, nil })
|
||||
err = r.SendPayload(ctx, Unset, func() (*Item, error) { return &i, nil }, UnauthenticatedRequest)
|
||||
if !errors.Is(err, context.DeadlineExceeded) {
|
||||
t.Fatalf("received: %v but expected: %v", err, context.DeadlineExceeded)
|
||||
}
|
||||
@@ -618,12 +616,11 @@ func TestEnableDisableRateLimit(t *testing.T) {
|
||||
var resp interface{}
|
||||
err = r.SendPayload(ctx, Auth, func() (*Item, error) {
|
||||
return &Item{
|
||||
Method: http.MethodGet,
|
||||
Path: testURL,
|
||||
Result: &resp,
|
||||
AuthRequest: true,
|
||||
Method: http.MethodGet,
|
||||
Path: testURL,
|
||||
Result: &resp,
|
||||
}, nil
|
||||
})
|
||||
}, AuthenticatedRequest)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -640,12 +637,11 @@ func TestEnableDisableRateLimit(t *testing.T) {
|
||||
|
||||
err = r.SendPayload(ctx, Auth, func() (*Item, error) {
|
||||
return &Item{
|
||||
Method: http.MethodGet,
|
||||
Path: testURL,
|
||||
Result: &resp,
|
||||
AuthRequest: true,
|
||||
Method: http.MethodGet,
|
||||
Path: testURL,
|
||||
Result: &resp,
|
||||
}, nil
|
||||
})
|
||||
}, AuthenticatedRequest)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -665,12 +661,11 @@ func TestEnableDisableRateLimit(t *testing.T) {
|
||||
go func(c chan struct{}) {
|
||||
err = r.SendPayload(ctx, Auth, func() (*Item, error) {
|
||||
return &Item{
|
||||
Method: http.MethodGet,
|
||||
Path: testURL,
|
||||
Result: &resp,
|
||||
AuthRequest: true,
|
||||
Method: http.MethodGet,
|
||||
Path: testURL,
|
||||
Result: &resp,
|
||||
}, nil
|
||||
})
|
||||
}, AuthenticatedRequest)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -49,7 +49,6 @@ type Item struct {
|
||||
Headers map[string]string
|
||||
Body io.Reader
|
||||
Result interface{}
|
||||
AuthRequest bool
|
||||
NonceEnabled bool
|
||||
Verbose bool
|
||||
HTTPDebugging bool
|
||||
@@ -69,9 +68,9 @@ type RetryPolicy func(resp *http.Response, err error) (bool, error)
|
||||
// RequesterOption is a function option that can be applied to configure a Requester when creating it.
|
||||
type RequesterOption func(*Requester)
|
||||
|
||||
// Generate defines a closure for functionality outside of the requester to
|
||||
// Generate defines a closure for functionality outside the requester to
|
||||
// generate a new *http.Request on every attempt. This minimizes the chance of
|
||||
// being outside of the receive window if application rate limiting reduces outbound
|
||||
// being outside the receive window if application rate limiting reduces outbound
|
||||
// requests.
|
||||
type Generate func() (*Item, error)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user