Initial implementation of HTTP mock testing framework (#310)

* Initial implementation of HTTP mock testing framework

Convert to VCR testing server. Segregate live testing via build tags.

Converted Binance to VCR server

Convert Bitstamp to VCR mocking tests

Added VCR mock testing for localbitcoins

* Add server generation for concurrent testing

* Fix linter issues

* Fix linter issue

* fix race - potentially

* revert auto assigning of host vals

* Fix requested changes

* Adds mock testing for ANX
Switch to using TestMain functionality
Added cron job usage for travis-ci to live testing
Added appveyor scheduled build check for live testing

* WOOPS

* silly correction

* Fixes fantastic linter issues

* fixed another whoopsie

* WOOO!

* Adds gemini mock testing with additional fixes

* Add docs and sharedvalue

* Added tls using httptest package

* Fixed issues

* added explicit mock recording reference to error

* Fix requested changes

* strip port from mock files as they are not needed on tls server

* Change incorrect names

* fix requested changes

* lbank update

* Fix another issue

* Updated readme
This commit is contained in:
Ryan O'Hara-Reid
2019-08-23 15:20:02 +10:00
committed by Adrian Gallagher
parent a81ddead9e
commit 6d8ba0a96a
79 changed files with 109268 additions and 1250 deletions

View File

@@ -30,7 +30,12 @@ test_script:
# test back-end
- go get github.com/golangci/golangci-lint/cmd/golangci-lint@v1.16.0
- '%GOPATH%\bin\golangci-lint.exe run --verbose'
- go test -race ./...
- ps: >-
if($env:APPVEYOR_SCHEDULED_BUILD -eq 'true') {
go test -race ./... -tags=mock_test_off
}else {
go test -race ./...
}
# test front-end
- node --version

View File

@@ -4,6 +4,7 @@ LINTPKG = github.com/golangci/golangci-lint/cmd/golangci-lint@v1.16.0
LINTBIN = $(GOPATH)/bin/golangci-lint
GCTLISTENPORT=9050
GCTPROFILERLISTENPORT=8085
CRON = $(TRAVIS_EVENT_TYPE)
get:
GO111MODULE=on go get $(GCTPKG)
@@ -16,7 +17,11 @@ linter:
check: linter test
test:
ifeq ($(CRON), cron)
go test -race -tags=mock_test_off -coverprofile=coverage.txt -covermode=atomic ./...
else
go test -race -coverprofile=coverage.txt -covermode=atomic ./...
endif
build:
GO111MODULE=on go build $(LDFLAGS)

View File

@@ -732,6 +732,7 @@ func (c *Coinmarketcap) SendHTTPRequest(method, endpoint string, v url.Values, r
false,
false,
c.Verbose,
false,
false)
}

View File

@@ -192,6 +192,7 @@ func (c *CurrencyConverter) SendHTTPRequest(endPoint string, values url.Values,
auth,
false,
c.Verbose,
false,
false)
if err != nil {
return fmt.Errorf("currency converter API SendHTTPRequest error %s with path %s",

View File

@@ -241,5 +241,6 @@ func (c *CurrencyLayer) SendHTTPRequest(endPoint string, values url.Values, resu
auth,
false,
c.Verbose,
false,
false)
}

View File

@@ -178,6 +178,7 @@ func (e *ExchangeRates) SendHTTPRequest(endPoint string, values url.Values, resu
false,
false,
e.Verbose,
false,
false)
if err != nil {
return fmt.Errorf("exchangeRatesAPI SendHTTPRequest error %s with path %s",

View File

@@ -265,5 +265,6 @@ func (f *Fixer) SendOpenHTTPRequest(endpoint string, v url.Values, result interf
auth,
false,
f.Verbose,
false,
false)
}

View File

@@ -265,5 +265,6 @@ func (o *OXR) SendHTTPRequest(endpoint string, values url.Values, result interfa
false,
false,
o.Verbose,
false,
false)
}

View File

@@ -534,7 +534,16 @@ func (a *Alphapoint) SendHTTPRequest(method, path string, data map[string]interf
return errors.New("unable to JSON request")
}
return a.SendPayload(method, path, headers, bytes.NewBuffer(PayloadJSON), result, false, false, a.Verbose, a.HTTPDebugging)
return a.SendPayload(method,
path,
headers,
bytes.NewBuffer(PayloadJSON),
result,
false,
false,
a.Verbose,
a.HTTPDebugging,
a.HTTPRecording)
}
// SendAuthenticatedHTTPRequest sends an authenticated request
@@ -559,5 +568,14 @@ func (a *Alphapoint) SendAuthenticatedHTTPRequest(method, path string, data map[
return errors.New("unable to JSON request")
}
return a.SendPayload(method, path, headers, bytes.NewBuffer(PayloadJSON), result, true, true, a.Verbose, a.HTTPDebugging)
return a.SendPayload(method,
path,
headers,
bytes.NewBuffer(PayloadJSON),
result,
true,
true,
a.Verbose,
a.HTTPDebugging,
a.HTTPRecording)
}

View File

@@ -83,7 +83,7 @@ func (a *ANX) Setup(exch *config.ExchangeConfig) {
} else {
a.Enabled = true
a.AuthenticatedAPISupport = exch.AuthenticatedAPISupport
a.SetAPIKeys(exch.APIKey, exch.APISecret, "", false)
a.SetAPIKeys(exch.APIKey, exch.APISecret, "", true)
a.SetHTTPClientTimeout(exch.HTTPTimeout)
a.SetHTTPClientUserAgent(exch.HTTPUserAgent)
a.RESTPollingDelay = exch.RESTPollingDelay
@@ -248,9 +248,13 @@ func (a *ANX) NewOrder(orderType string, buy bool, tradedCurrency string, traded
// CancelOrderByIDs cancels orders, requires already knowing order IDs
// There is no existing API call to retrieve orderIds
func (a *ANX) CancelOrderByIDs(orderIds []string) (OrderCancelResponse, error) {
var response OrderCancelResponse
if len(orderIds) == 0 {
return response, errors.New("no order ids provided")
}
req := make(map[string]interface{})
req["orderIds"] = orderIds
var response OrderCancelResponse
err := a.SendAuthenticatedHTTPRequest(anxOrderCancel, req, &response)
if response.ResultCode != "OK" {
@@ -266,7 +270,7 @@ func (a *ANX) GetOrderList(isActiveOrdersOnly bool) ([]OrderResponse, error) {
req["activeOnly"] = isActiveOrdersOnly
type OrderListResponse struct {
Timestamp int64 `json:"timestamp"`
Timestamp int64 `json:"timestamp,string"`
ResultCode string `json:"resultCode"`
Count int64 `json:"count"`
OrderResponses []OrderResponse `json:"orders"`
@@ -278,7 +282,6 @@ func (a *ANX) GetOrderList(isActiveOrdersOnly bool) ([]OrderResponse, error) {
}
if response.ResultCode != "OK" {
log.Errorf("Response code is not OK: %s\n", response.ResultCode)
return nil, errors.New(response.ResultCode)
}
@@ -304,7 +307,6 @@ func (a *ANX) OrderInfo(orderID string) (OrderResponse, error) {
}
if response.ResultCode != "OK" {
log.Errorf("Response code is not OK: %s\n", response.ResultCode)
return OrderResponse{}, errors.New(response.ResultCode)
}
return response.Order, nil
@@ -324,7 +326,7 @@ func (a *ANX) Send(currency, address, otp, amount string) (string, error) {
type SendResponse struct {
TransactionID string `json:"transactionId"`
ResultCode string `json:"resultCode"`
Timestamp int64 `json:"timestamp"`
Timestamp int64 `json:"timestamp,string"`
}
var response SendResponse
@@ -335,7 +337,6 @@ func (a *ANX) Send(currency, address, otp, amount string) (string, error) {
}
if response.ResultCode != "OK" {
log.Errorf("Response code is not OK: %s\n", response.ResultCode)
return "", errors.New(response.ResultCode)
}
return response.TransactionID, nil
@@ -361,7 +362,6 @@ func (a *ANX) CreateNewSubAccount(currency, name string) (string, error) {
}
if response.ResultCode != "OK" {
log.Errorf("Response code is not OK: %s\n", response.ResultCode)
return "", errors.New(response.ResultCode)
}
return response.SubAccount, nil
@@ -380,7 +380,7 @@ func (a *ANX) GetDepositAddressByCurrency(currency, name string, newAddr bool) (
Address string `json:"address"`
SubAccount string `json:"subAccount"`
ResultCode string `json:"resultCode"`
Timestamp int64 `json:"timestamp"`
Timestamp int64 `json:"timestamp,string"`
}
var response AddressResponse
@@ -395,7 +395,6 @@ func (a *ANX) GetDepositAddressByCurrency(currency, name string, newAddr bool) (
}
if response.ResultCode != "OK" {
log.Errorf("Response code is not OK: %s\n", response.ResultCode)
return "", errors.New(response.ResultCode)
}
@@ -404,7 +403,16 @@ func (a *ANX) GetDepositAddressByCurrency(currency, name string, newAddr bool) (
// SendHTTPRequest sends an unauthenticated HTTP request
func (a *ANX) SendHTTPRequest(path string, result interface{}) error {
return a.SendPayload(http.MethodGet, path, nil, nil, result, false, false, a.Verbose, a.HTTPDebugging)
return a.SendPayload(http.MethodGet,
path,
nil,
nil,
result,
false,
false,
a.Verbose,
a.HTTPDebugging,
a.HTTPRecording)
}
// SendAuthenticatedHTTPRequest sends a authenticated HTTP request
@@ -437,7 +445,16 @@ func (a *ANX) SendAuthenticatedHTTPRequest(path string, params map[string]interf
headers["Rest-Sign"] = common.Base64Encode(hmac)
headers["Content-Type"] = "application/json"
return a.SendPayload(http.MethodPost, a.APIUrl+path, headers, bytes.NewBuffer(PayloadJSON), result, true, true, a.Verbose, a.HTTPDebugging)
return a.SendPayload(http.MethodPost,
a.APIUrl+path,
headers,
bytes.NewBuffer(PayloadJSON),
result,
true,
true,
a.Verbose,
a.HTTPDebugging,
a.HTTPRecording)
}
// GetFee returns an estimate of fee based on type of transaction

View File

@@ -0,0 +1,32 @@
//+build mock_test_off
// This will build if build tag mock_test_off is parsed and will do live testing
// using all tests in (exchange)_test.go
package anx
import (
"log"
"os"
"testing"
"github.com/thrasher-corp/gocryptotrader/config"
"github.com/thrasher-corp/gocryptotrader/exchanges/sharedtestvalues"
)
var mockTests = false
func TestMain(m *testing.M) {
cfg := config.GetConfig()
cfg.LoadConfig("../../testdata/configtest.json")
anxConfig, err := cfg.GetExchangeConfig("ANX")
if err != nil {
log.Fatalf("Test Failed - ANX Setup() init error", err)
}
anxConfig.AuthenticatedAPISupport = true
anxConfig.APIKey = apiKey
anxConfig.APISecret = apiSecret
a.SetDefaults()
a.Setup(&anxConfig)
log.Printf(sharedtestvalues.LiveTesting, a.GetName(), a.APIUrl)
os.Exit(m.Run())
}

View File

@@ -0,0 +1,44 @@
//+build !mock_test_off
// This will build if build tag mock_test_off is not parsed and will try to mock
// all tests in _test.go
package anx
import (
"log"
"os"
"testing"
"github.com/thrasher-corp/gocryptotrader/config"
"github.com/thrasher-corp/gocryptotrader/exchanges/mock"
"github.com/thrasher-corp/gocryptotrader/exchanges/sharedtestvalues"
)
const mockFile = "../../testdata/http_mock/anx/anx.json"
var mockTests = true
func TestMain(m *testing.M) {
cfg := config.GetConfig()
cfg.LoadConfig("../../testdata/configtest.json")
anxConfig, err := cfg.GetExchangeConfig("ANX")
if err != nil {
log.Fatal("Test Failed - Mock server error", err)
}
anxConfig.AuthenticatedAPISupport = true
anxConfig.APIKey = apiKey
anxConfig.APISecret = apiSecret
a.SetDefaults()
a.Setup(&anxConfig)
serverDetails, newClient, err := mock.NewVCRServer(mockFile)
if err != nil {
log.Fatalf("Test Failed - Mock server error %s", err)
}
a.HTTPClient = newClient
a.APIUrl = serverDetails + "/"
log.Printf(sharedtestvalues.MockTesting, a.GetName(), a.APIUrl)
os.Exit(m.Run())
}

View File

@@ -4,7 +4,6 @@ import (
"testing"
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/config"
"github.com/thrasher-corp/gocryptotrader/currency"
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
)
@@ -18,70 +17,8 @@ const (
var a ANX
func TestSetDefaults(t *testing.T) {
a.SetDefaults()
if a.Name != "ANX" {
t.Error("Test Failed - ANX SetDefaults() incorrect values set")
}
if a.Enabled {
t.Error("Test Failed - ANX SetDefaults() incorrect values set")
}
if a.TakerFee != 0.02 {
t.Error("Test Failed - ANX SetDefaults() incorrect values set")
}
if a.MakerFee != 0.01 {
t.Error("Test Failed - ANX SetDefaults() incorrect values set")
}
if a.Verbose {
t.Error("Test Failed - ANX SetDefaults() incorrect values set")
}
if a.Websocket.IsEnabled() {
t.Error("Test Failed - ANX SetDefaults() incorrect values set")
}
if a.RESTPollingDelay != 10 {
t.Error("Test Failed - ANX SetDefaults() incorrect values set")
}
}
func TestSetup(t *testing.T) {
anxSetupConfig := config.GetConfig()
anxSetupConfig.LoadConfig("../../testdata/configtest.json")
anxConfig, err := anxSetupConfig.GetExchangeConfig("ANX")
anxConfig.AuthenticatedAPISupport = true
if err != nil {
t.Error("Test Failed - ANX Setup() init error")
}
a.Setup(&anxConfig)
a.APIKey = apiKey
a.APISecret = apiSecret
a.AuthenticatedAPISupport = true
if !a.Enabled {
t.Error("Test Failed - ANX Setup() incorrect values set")
}
if a.RESTPollingDelay != 10 {
t.Error("Test Failed - ANX Setup() incorrect values set")
}
if a.Verbose {
t.Error("Test Failed - ANX Setup() incorrect values set")
}
if a.Websocket.IsEnabled() {
t.Error("Test Failed - ANX Setup() incorrect values set")
}
if len(a.BaseCurrencies) == 0 {
t.Error("Test Failed - ANX Setup() incorrect values set")
}
if len(a.AvailablePairs) == 0 {
t.Error("Test Failed - ANX Setup() incorrect values set")
}
if len(a.EnabledPairs) == 0 {
t.Error("Test Failed - ANX Setup() incorrect values set")
}
}
func TestGetCurrencies(t *testing.T) {
t.Parallel()
_, err := a.GetCurrencies()
if err != nil {
t.Fatalf("Test failed. TestGetCurrencies failed. Err: %s", err)
@@ -89,6 +26,7 @@ func TestGetCurrencies(t *testing.T) {
}
func TestGetTradablePairs(t *testing.T) {
t.Parallel()
_, err := a.GetTradablePairs()
if err != nil {
t.Fatalf("Test failed. TestGetTradablePairs failed. Err: %s", err)
@@ -96,6 +34,7 @@ func TestGetTradablePairs(t *testing.T) {
}
func TestGetTicker(t *testing.T) {
t.Parallel()
ticker, err := a.GetTicker("BTCUSD")
if err != nil {
t.Errorf("Test Failed - ANX GetTicker() error: %s", err)
@@ -106,16 +45,18 @@ func TestGetTicker(t *testing.T) {
}
func TestGetDepth(t *testing.T) {
ticker, err := a.GetDepth("BTCUSD")
t.Parallel()
depth, err := a.GetDepth("BTCUSD")
if err != nil {
t.Errorf("Test Failed - ANX GetDepth() error: %s", err)
}
if ticker.Result != "success" {
if depth.Result != "success" {
t.Error("Test Failed - ANX GetDepth() unsuccessful")
}
}
func TestGetAPIKey(t *testing.T) {
t.Parallel()
apiKey, apiSecret, err := a.GetAPIKey("userName", "passWord", "", "1337")
if err == nil {
t.Error("Test Failed - ANX GetAPIKey() Incorrect")
@@ -140,6 +81,7 @@ func setFeeBuilder() *exchange.FeeBuilder {
// TestGetFeeByTypeOfflineTradeFee logic test
func TestGetFeeByTypeOfflineTradeFee(t *testing.T) {
t.Parallel()
var feeBuilder = setFeeBuilder()
a.GetFeeByType(feeBuilder)
if apiKey == "" || apiSecret == "" {
@@ -154,9 +96,7 @@ func TestGetFeeByTypeOfflineTradeFee(t *testing.T) {
}
func TestGetFee(t *testing.T) {
a.SetDefaults()
TestSetup(t)
t.Parallel()
var feeBuilder = setFeeBuilder()
// CryptocurrencyTradeFee Basic
@@ -226,7 +166,7 @@ func TestGetFee(t *testing.T) {
}
func TestFormatWithdrawPermissions(t *testing.T) {
a.SetDefaults()
t.Parallel()
expectedResult := exchange.AutoWithdrawCryptoWithSetupText + " & " + exchange.WithdrawCryptoWith2FAText + " & " +
exchange.WithdrawCryptoWithEmailText + " & " + exchange.WithdrawFiatViaWebsiteOnlyText
@@ -238,34 +178,36 @@ func TestFormatWithdrawPermissions(t *testing.T) {
}
func TestGetActiveOrders(t *testing.T) {
a.SetDefaults()
TestSetup(t)
t.Parallel()
var getOrdersRequest = exchange.GetOrdersRequest{
OrderType: exchange.AnyOrderType,
}
_, err := a.GetActiveOrders(&getOrdersRequest)
if areTestAPIKeysSet() && err != nil {
switch {
case areTestAPIKeysSet() && err != nil && !mockTests:
t.Errorf("Could not get open orders: %s", err)
} else if !areTestAPIKeysSet() && err == nil {
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Expecting an error when no keys are set")
case mockTests && err != nil:
t.Errorf("Could not get open orders: %s", err)
}
}
func TestGetOrderHistory(t *testing.T) {
a.SetDefaults()
TestSetup(t)
t.Parallel()
var getOrdersRequest = exchange.GetOrdersRequest{
OrderType: exchange.AnyOrderType,
}
_, err := a.GetOrderHistory(&getOrdersRequest)
if areTestAPIKeysSet() && err != nil {
switch {
case areTestAPIKeysSet() && err != nil && !mockTests:
t.Errorf("Could not get order history: %s", err)
} else if !areTestAPIKeysSet() && err == nil {
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Expecting an error when no keys are set")
case mockTests && err != nil:
t.Error("Test Failed - GetBalance() error", err)
}
}
@@ -281,10 +223,8 @@ func areTestAPIKeysSet() bool {
}
func TestSubmitOrder(t *testing.T) {
a.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Parallel()
if areTestAPIKeysSet() && !canManipulateRealOrders && !mockTests {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
var p = currency.Pair{
@@ -292,8 +232,9 @@ func TestSubmitOrder(t *testing.T) {
Base: currency.BTC,
Quote: currency.USD,
}
response, err := a.SubmitOrder(p, exchange.BuyOrderSide, exchange.MarketOrderType, 1, 1, "clientId")
if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) {
// TODO: QA Pass to submit order
response, err := a.SubmitOrder(p, exchange.BuyOrderSide, exchange.MarketOrderType, 1, 1, "")
if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) && !mockTests {
t.Errorf("Order failed to be placed: %v", err)
} else if !areTestAPIKeysSet() && err == nil {
t.Error("Expecting an error when no keys are set")
@@ -301,10 +242,8 @@ func TestSubmitOrder(t *testing.T) {
}
func TestCancelExchangeOrder(t *testing.T) {
a.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Parallel()
if areTestAPIKeysSet() && !canManipulateRealOrders && !mockTests {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -318,18 +257,19 @@ func TestCancelExchangeOrder(t *testing.T) {
}
err := a.CancelOrder(orderCancellation)
if areTestAPIKeysSet() && err != nil {
switch {
case areTestAPIKeysSet() && err != nil && !mockTests:
t.Errorf("Could not cancel order: %s", err)
} else if !areTestAPIKeysSet() && err == nil {
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Expecting an error when no keys are set")
case mockTests && err != nil:
t.Errorf("Could not cancel order: %s", err)
}
}
func TestCancelAllExchangeOrders(t *testing.T) {
a.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Parallel()
if areTestAPIKeysSet() && !canManipulateRealOrders && !mockTests {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -343,11 +283,13 @@ func TestCancelAllExchangeOrders(t *testing.T) {
}
resp, err := a.CancelAllOrders(orderCancellation)
if areTestAPIKeysSet() && err != nil {
switch {
case areTestAPIKeysSet() && err != nil && !mockTests:
t.Errorf("Could not cancel order: %s", err)
} else if !areTestAPIKeysSet() && err == nil {
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Expecting an error when no keys are set")
case mockTests && err == nil:
t.Errorf("QA pass needs to be completed and mock needs to be updated error cannot be nil")
}
if len(resp.OrderStatus) > 0 {
@@ -356,20 +298,20 @@ func TestCancelAllExchangeOrders(t *testing.T) {
}
func TestGetAccountInfo(t *testing.T) {
if apiKey != "" || apiSecret != "" {
_, err := a.GetAccountInfo()
if err != nil {
t.Error("test failed - GetAccountInfo() error:", err)
}
} else {
_, err := a.GetAccountInfo()
if err == nil {
t.Error("test failed - GetAccountInfo() error")
}
t.Parallel()
_, err := a.GetAccountInfo()
switch {
case areTestAPIKeysSet() && err != nil && !mockTests:
t.Error("test failed - GetAccountInfo() error:", err)
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("test failed - GetAccountInfo() error")
case mockTests && err != nil:
t.Error("test failed - GetAccountInfo() error:", err)
}
}
func TestModifyOrder(t *testing.T) {
t.Parallel()
_, err := a.ModifyOrder(&exchange.ModifyOrder{})
if err == nil {
t.Error("Test failed - ModifyOrder() error")
@@ -377,10 +319,8 @@ func TestModifyOrder(t *testing.T) {
}
func TestWithdraw(t *testing.T) {
a.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Parallel()
if areTestAPIKeysSet() && !canManipulateRealOrders && !mockTests {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -393,61 +333,47 @@ func TestWithdraw(t *testing.T) {
}
_, err := a.WithdrawCryptocurrencyFunds(&withdrawCryptoRequest)
if areTestAPIKeysSet() && err != nil {
if areTestAPIKeysSet() && err != nil && !mockTests {
t.Errorf("Withdraw failed to be placed: %v", err)
} else if !areTestAPIKeysSet() && err == nil {
} else if !areTestAPIKeysSet() && err == nil && mockTests {
t.Error("Expecting an error when no keys are set")
}
}
func TestWithdrawFiat(t *testing.T) {
a.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
t.Parallel()
var withdrawFiatRequest = exchange.WithdrawRequest{}
_, err := a.WithdrawFiatFunds(&withdrawFiatRequest)
if err != common.ErrFunctionNotSupported {
t.Errorf("Expected '%v', received: '%v'", common.ErrFunctionNotSupported, err)
t.Errorf("Expected '%v', received: '%v'",
common.ErrFunctionNotSupported,
err)
}
}
func TestWithdrawInternationalBank(t *testing.T) {
a.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
t.Parallel()
var withdrawFiatRequest = exchange.WithdrawRequest{}
_, err := a.WithdrawFiatFundsToInternationalBank(&withdrawFiatRequest)
if err != common.ErrFunctionNotSupported {
t.Errorf("Expected '%v', received: '%v'", common.ErrFunctionNotSupported, err)
t.Errorf("Expected '%v', received: '%v'",
common.ErrFunctionNotSupported,
err)
}
}
func TestGetDepositAddress(t *testing.T) {
if areTestAPIKeysSet() {
_, err := a.GetDepositAddress(currency.BTC, "")
if err != nil {
t.Error("Test Failed - GetDepositAddress() error", err)
}
} else {
_, err := a.GetDepositAddress(currency.BTC, "")
if err == nil {
t.Error("Test Failed - GetDepositAddress() error cannot be nil")
}
t.Parallel()
_, err := a.GetDepositAddress(currency.BTC, "")
if areTestAPIKeysSet() && err != nil && !mockTests {
t.Error("Test Failed - GetDepositAddress() error", err)
} else if !areTestAPIKeysSet() && err == nil {
t.Error("Test Failed - GetDepositAddress() error cannot be nil")
}
}
func TestUpdateOrderbook(t *testing.T) {
a.SetDefaults()
t.Parallel()
q := currency.Pair{
Delimiter: "_",
Base: currency.BTC,

View File

@@ -279,20 +279,10 @@ func (b *Binance) GetRecentTrades(rtr RecentTradeRequestParams) ([]RecentTrade,
// limit: Optional. Default 500; max 1000.
// fromID:
func (b *Binance) GetHistoricalTrades(symbol string, limit int, fromID int64) ([]HistoricalTrade, error) {
var resp []HistoricalTrade
if err := b.CheckLimit(limit); err != nil {
return resp, err
}
params := url.Values{}
params.Set("symbol", common.StringToUpper(symbol))
params.Set("limit", strconv.Itoa(limit))
params.Set("fromid", strconv.FormatInt(fromID, 10))
path := fmt.Sprintf("%s%s?%s", b.APIUrl, historicalTrades, params.Encode())
return resp, b.SendHTTPRequest(path, &resp)
// Dropping support due to response for market data is always
// {"code":-2014,"msg":"API-key format invalid."}
// TODO: replace with newer API vs REST endpoint
return nil, common.ErrFunctionNotSupported
}
// GetAggregatedTrades returns aggregated trade activity
@@ -617,7 +607,16 @@ func (b *Binance) GetAccount() (*Account, error) {
// SendHTTPRequest sends an unauthenticated request
func (b *Binance) SendHTTPRequest(path string, result interface{}) error {
return b.SendPayload(http.MethodGet, path, nil, nil, result, false, false, b.Verbose, b.HTTPDebugging)
return b.SendPayload(http.MethodGet,
path,
nil,
nil,
result,
false,
false,
b.Verbose,
b.HTTPDebugging,
b.HTTPRecording)
}
// SendAuthHTTPRequest sends an authenticated HTTP request
@@ -653,7 +652,16 @@ func (b *Binance) SendAuthHTTPRequest(method, path string, params url.Values, re
Message string `json:"msg"`
}{}
err := b.SendPayload(method, path, headers, bytes.NewBuffer(nil), &interim, true, false, b.Verbose, b.HTTPDebugging)
err := b.SendPayload(method,
path,
headers,
bytes.NewBuffer(nil),
&interim,
true,
false,
b.Verbose,
b.HTTPDebugging,
b.HTTPRecording)
if err != nil {
return err
}
@@ -773,7 +781,7 @@ func getCryptocurrencyWithdrawalFee(c currency.Code) float64 {
}
// WithdrawCrypto sends cryptocurrency to the address of your choosing
func (b *Binance) WithdrawCrypto(asset, address, addressTag, name, amount string) (int64, error) {
func (b *Binance) WithdrawCrypto(asset, address, addressTag, name, amount string) (string, error) {
var resp WithdrawResponse
path := fmt.Sprintf("%s%s", b.APIUrl, withdraw)
@@ -789,7 +797,7 @@ func (b *Binance) WithdrawCrypto(asset, address, addressTag, name, amount string
}
if err := b.SendAuthHTTPRequest(http.MethodPost, path, params, &resp); err != nil {
return -1, err
return "", err
}
if !resp.Success {

View File

@@ -0,0 +1,32 @@
//+build mock_test_off
// This will build if build tag mock_test_off is parsed and will do live testing
// using all tests in (exchange)_test.go
package binance
import (
"log"
"os"
"testing"
"github.com/thrasher-corp/gocryptotrader/config"
"github.com/thrasher-corp/gocryptotrader/exchanges/sharedtestvalues"
)
var mockTests = false
func TestMain(m *testing.M) {
cfg := config.GetConfig()
cfg.LoadConfig("../../testdata/configtest.json")
binanceConfig, err := cfg.GetExchangeConfig("Binance")
if err != nil {
log.Fatal("Test Failed - Binance Setup() init error", err)
}
binanceConfig.AuthenticatedAPISupport = true
binanceConfig.APIKey = apiKey
binanceConfig.APISecret = apiSecret
b.SetDefaults()
b.Setup(&binanceConfig)
log.Printf(sharedtestvalues.LiveTesting, b.GetName(), b.APIUrl)
os.Exit(m.Run())
}

View File

@@ -0,0 +1,44 @@
//+build !mock_test_off
// This will build if build tag mock_test_off is not parsed and will try to mock
// all tests in _test.go
package binance
import (
"log"
"os"
"testing"
"github.com/thrasher-corp/gocryptotrader/config"
"github.com/thrasher-corp/gocryptotrader/exchanges/mock"
"github.com/thrasher-corp/gocryptotrader/exchanges/sharedtestvalues"
)
const mockfile = "../../testdata/http_mock/binance/binance.json"
var mockTests = true
func TestMain(m *testing.M) {
cfg := config.GetConfig()
cfg.LoadConfig("../../testdata/configtest.json")
binanceConfig, err := cfg.GetExchangeConfig("Binance")
if err != nil {
log.Fatal("Test Failed - Binance Setup() init error", err)
}
binanceConfig.AuthenticatedAPISupport = true
binanceConfig.APIKey = apiKey
binanceConfig.APISecret = apiSecret
b.SetDefaults()
b.Setup(&binanceConfig)
serverDetails, newClient, err := mock.NewVCRServer(mockfile)
if err != nil {
log.Fatalf("Test Failed - Mock server error %s", err)
}
b.HTTPClient = newClient
b.APIUrl = serverDetails
log.Printf(sharedtestvalues.MockTesting, b.GetName(), b.APIUrl)
os.Exit(m.Run())
}

View File

@@ -4,7 +4,6 @@ import (
"testing"
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/config"
"github.com/thrasher-corp/gocryptotrader/currency"
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
)
@@ -18,26 +17,26 @@ const (
var b Binance
func TestSetDefaults(t *testing.T) {
b.SetDefaults()
func areTestAPIKeysSet() bool {
if b.APIKey != "" && b.APIKey != "Key" &&
b.APISecret != "" && b.APISecret != "Secret" {
return true
}
return false
}
func TestSetup(t *testing.T) {
cfg := config.GetConfig()
cfg.LoadConfig("../../testdata/configtest.json")
binanceConfig, err := cfg.GetExchangeConfig("Binance")
if err != nil {
t.Error("Test Failed - Binance Setup() init error")
func setFeeBuilder() *exchange.FeeBuilder {
return &exchange.FeeBuilder{
Amount: 1,
FeeType: exchange.CryptocurrencyTradeFee,
Pair: currency.NewPair(currency.BTC, currency.LTC),
PurchasePrice: 1,
}
binanceConfig.AuthenticatedAPISupport = true
binanceConfig.APIKey = apiKey
binanceConfig.APISecret = apiSecret
b.Setup(&binanceConfig)
}
func TestGetExchangeValidCurrencyPairs(t *testing.T) {
t.Parallel()
_, err := b.GetExchangeValidCurrencyPairs()
if err != nil {
t.Error("Test Failed - Binance GetExchangeValidCurrencyPairs() error", err)
@@ -46,6 +45,7 @@ func TestGetExchangeValidCurrencyPairs(t *testing.T) {
func TestGetOrderBook(t *testing.T) {
t.Parallel()
_, err := b.GetOrderBook(OrderBookDataRequestParams{
Symbol: "BTCUSDT",
Limit: 10,
@@ -71,14 +71,19 @@ func TestGetRecentTrades(t *testing.T) {
func TestGetHistoricalTrades(t *testing.T) {
t.Parallel()
_, err := b.GetHistoricalTrades("BTCUSDT", 5, 1337)
if err == nil {
_, err := b.GetHistoricalTrades("BTCUSDT", 5, 0)
if !mockTests && err == nil {
t.Error("Test Failed - Binance GetHistoricalTrades() expecting error")
}
if mockTests && err == nil {
t.Error("Test Failed - Binance GetHistoricalTrades() error", err)
}
}
func TestGetAggregatedTrades(t *testing.T) {
t.Parallel()
_, err := b.GetAggregatedTrades("BTCUSDT", 5)
if err != nil {
t.Error("Test Failed - Binance GetAggregatedTrades() error", err)
@@ -87,6 +92,7 @@ func TestGetAggregatedTrades(t *testing.T) {
func TestGetSpotKline(t *testing.T) {
t.Parallel()
_, err := b.GetSpotKline(KlinesRequestParams{
Symbol: "BTCUSDT",
Interval: TimeIntervalFiveMinutes,
@@ -99,6 +105,7 @@ func TestGetSpotKline(t *testing.T) {
func TestGetAveragePrice(t *testing.T) {
t.Parallel()
_, err := b.GetAveragePrice("BTCUSDT")
if err != nil {
t.Error("Test Failed - Binance GetAveragePrice() error", err)
@@ -107,6 +114,7 @@ func TestGetAveragePrice(t *testing.T) {
func TestGetPriceChangeStats(t *testing.T) {
t.Parallel()
_, err := b.GetPriceChangeStats("BTCUSDT")
if err != nil {
t.Error("Test Failed - Binance GetPriceChangeStats() error", err)
@@ -115,6 +123,7 @@ func TestGetPriceChangeStats(t *testing.T) {
func TestGetTickers(t *testing.T) {
t.Parallel()
_, err := b.GetTickers()
if err != nil {
t.Error("Test Failed - Binance TestGetTickers error", err)
@@ -123,6 +132,7 @@ func TestGetTickers(t *testing.T) {
func TestGetLatestSpotPrice(t *testing.T) {
t.Parallel()
_, err := b.GetLatestSpotPrice("BTCUSDT")
if err != nil {
t.Error("Test Failed - Binance GetLatestSpotPrice() error", err)
@@ -131,120 +141,62 @@ func TestGetLatestSpotPrice(t *testing.T) {
func TestGetBestPrice(t *testing.T) {
t.Parallel()
_, err := b.GetBestPrice("BTCUSDT")
if err != nil {
t.Error("Test Failed - Binance GetBestPrice() error", err)
}
}
func TestNewOrder(t *testing.T) {
t.Parallel()
if apiKey == "" || apiSecret == "" {
t.Skip()
}
_, err := b.NewOrder(&NewOrderRequest{
Symbol: "BTCUSDT",
Side: BinanceRequestParamsSideSell,
TradeType: BinanceRequestParamsOrderLimit,
TimeInForce: BinanceRequestParamsTimeGTC,
Quantity: 0.01,
Price: 1536.1,
})
if err == nil {
t.Error("Test Failed - Binance NewOrder() error", err)
}
}
func TestCancelExistingOrder(t *testing.T) {
t.Parallel()
if apiKey == "" || apiSecret == "" {
t.Skip()
}
_, err := b.CancelExistingOrder("BTCUSDT", 82584683, "")
if err != nil {
t.Error("Test Failed - Binance CancelExistingOrder() error", err)
}
}
func TestQueryOrder(t *testing.T) {
t.Parallel()
if apiKey == "" || apiSecret == "" {
t.Skip()
}
_, err := b.QueryOrder("BTCUSDT", "", 1337)
if err == nil {
t.Error("Test Failed - Binance QueryOrder() error", err)
switch {
case areTestAPIKeysSet() && err != nil:
t.Error("Test Failed - QueryOrder() error", err)
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Test Failed - QueryOrder() expecting an error when no keys are set")
case mockTests && err != nil:
t.Error("Test Failed - Mock QueryOrder() error", err)
}
}
func TestOpenOrders(t *testing.T) {
t.Parallel()
if apiKey == "" || apiSecret == "" {
t.Skip()
}
_, err := b.OpenOrders("BTCUSDT")
if err != nil {
t.Error("Test Failed - Binance OpenOrders() error", err)
switch {
case areTestAPIKeysSet() && err != nil:
t.Error("Test Failed - OpenOrders() error", err)
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Test Failed - OpenOrders() expecting an error when no keys are set")
case mockTests && err != nil:
t.Error("Test Failed - Mock OpenOrders() error", err)
}
}
func TestAllOrders(t *testing.T) {
t.Parallel()
if apiKey == "" || apiSecret == "" {
t.Skip()
}
_, err := b.AllOrders("BTCUSDT", "", "")
if err != nil {
t.Error("Test Failed - Binance AllOrders() error", err)
}
}
func TestGetAccount(t *testing.T) {
if apiKey == "" || apiSecret == "" {
t.Skip()
}
t.Parallel()
b.SetDefaults()
TestSetup(t)
account, err := b.GetAccount()
if err != nil {
t.Fatal("Test Failed - Binance GetAccount() error", err)
}
if account.MakerCommission <= 0 {
t.Fatalf("Test Failed. Expected > 0, Received %d", account.MakerCommission)
}
if account.TakerCommission <= 0 {
t.Fatalf("Test Failed. Expected > 0, Received %d", account.TakerCommission)
}
t.Logf("Current makerFee: %d", account.MakerCommission)
t.Logf("Current takerFee: %d", account.TakerCommission)
}
func setFeeBuilder() *exchange.FeeBuilder {
return &exchange.FeeBuilder{
Amount: 1,
FeeType: exchange.CryptocurrencyTradeFee,
Pair: currency.NewPair(currency.BTC, currency.LTC),
PurchasePrice: 1,
switch {
case areTestAPIKeysSet() && err != nil:
t.Error("Test Failed - AllOrders() error", err)
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Test Failed - AllOrders() expecting an error when no keys are set")
case mockTests && err != nil:
t.Error("Test Failed - Mock AllOrders() error", err)
}
}
// TestGetFeeByTypeOfflineTradeFee logic test
func TestGetFeeByTypeOfflineTradeFee(t *testing.T) {
t.Parallel()
var feeBuilder = setFeeBuilder()
b.GetFeeByType(feeBuilder)
if apiKey == "" || apiSecret == "" {
if !areTestAPIKeysSet() {
if feeBuilder.FeeType != exchange.OfflineTradeFee {
t.Errorf("Expected %v, received %v", exchange.OfflineTradeFee, feeBuilder.FeeType)
}
@@ -256,12 +208,11 @@ func TestGetFeeByTypeOfflineTradeFee(t *testing.T) {
}
func TestGetFee(t *testing.T) {
b.SetDefaults()
TestSetup(t)
t.Parallel()
var feeBuilder = setFeeBuilder()
if apiKey != "" || apiSecret != "" {
if areTestAPIKeysSet() || mockTests {
// CryptocurrencyTradeFee Basic
if resp, err := b.GetFee(feeBuilder); resp != float64(0.1) || err != nil {
t.Error(err)
@@ -331,19 +282,18 @@ func TestGetFee(t *testing.T) {
}
func TestFormatWithdrawPermissions(t *testing.T) {
b.SetDefaults()
t.Parallel()
expectedResult := exchange.AutoWithdrawCryptoText + " & " + exchange.NoFiatWithdrawalsText
withdrawPermissions := b.FormatWithdrawPermissions()
if withdrawPermissions != expectedResult {
t.Errorf("Expected: %s, Received: %s", expectedResult, withdrawPermissions)
}
}
func TestGetActiveOrders(t *testing.T) {
b.SetDefaults()
TestSetup(t)
t.Parallel()
var getOrdersRequest = exchange.GetOrdersRequest{
OrderType: exchange.AnyOrderType,
@@ -358,16 +308,18 @@ func TestGetActiveOrders(t *testing.T) {
}
_, err = b.GetActiveOrders(&getOrdersRequest)
if areTestAPIKeysSet() && err != nil {
t.Errorf("Could not get open orders: %s", err)
} else if !areTestAPIKeysSet() && err == nil {
t.Error("Expecting an error when no keys are set")
switch {
case areTestAPIKeysSet() && err != nil:
t.Error("Test Failed - GetActiveOrders() error", err)
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Test Failed - GetActiveOrders() expecting an error when no keys are set")
case mockTests && err != nil:
t.Error("Test Failed - Mock GetActiveOrders() error", err)
}
}
func TestGetOrderHistory(t *testing.T) {
b.SetDefaults()
TestSetup(t)
t.Parallel()
var getOrdersRequest = exchange.GetOrdersRequest{
OrderType: exchange.AnyOrderType,
@@ -383,144 +335,144 @@ func TestGetOrderHistory(t *testing.T) {
currency.BTC)}
_, err = b.GetOrderHistory(&getOrdersRequest)
if areTestAPIKeysSet() && err != nil {
t.Errorf("Could not get order history: %s", err)
} else if !areTestAPIKeysSet() && err == nil {
t.Error("Expecting an error when no keys are set")
switch {
case areTestAPIKeysSet() && err != nil:
t.Error("Test Failed - GetOrderHistory() error", err)
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Test Failed - GetOrderHistory() expecting an error when no keys are set")
case mockTests && err != nil:
t.Error("Test Failed - Mock GetOrderHistory() error", err)
}
}
// Any tests below this line have the ability to impact your orders on the exchange. Enable canManipulateRealOrders to run them
// -----------------------------------------------------------------------------------------------------------------------------
func areTestAPIKeysSet() bool {
if b.APIKey != "" && b.APIKey != "Key" &&
b.APISecret != "" && b.APISecret != "Secret" {
return true
}
return false
}
func TestSubmitOrder(t *testing.T) {
b.SetDefaults()
TestSetup(t)
t.Parallel()
var p = currency.Pair{
Delimiter: "",
Base: currency.LTC,
Quote: currency.BTC,
if areTestAPIKeysSet() && !canManipulateRealOrders && !mockTests {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
response, err := b.SubmitOrder(p, exchange.BuyOrderSide, exchange.MarketOrderType, 1, 1, "clientId")
if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) {
t.Errorf("Order failed to be placed: %v", err)
} else if !areTestAPIKeysSet() && err == nil {
t.Error("Expecting an error when no keys are set")
_, err := b.SubmitOrder(currency.NewPair(currency.LTC, currency.BTC),
exchange.BuyOrderSide,
exchange.MarketOrderType,
1,
1,
"clientId")
switch {
case areTestAPIKeysSet() && err != nil:
t.Error("Test Failed - SubmitOrder() error", err)
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Test Failed - SubmitOrder() expecting an error when no keys are set")
case mockTests && err != nil:
t.Error("Test Failed - Mock SubmitOrder() error", err)
}
}
func TestCancelExchangeOrder(t *testing.T) {
b.SetDefaults()
TestSetup(t)
t.Parallel()
currencyPair := currency.NewPair(currency.LTC, currency.BTC)
if areTestAPIKeysSet() && !canManipulateRealOrders && !mockTests {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
var orderCancellation = &exchange.OrderCancellation{
OrderID: "1",
WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB",
AccountID: "1",
CurrencyPair: currencyPair,
CurrencyPair: currency.NewPair(currency.LTC, currency.BTC),
}
err := b.CancelOrder(orderCancellation)
if !areTestAPIKeysSet() && err == nil {
t.Error("Expecting an error when no keys are set")
}
if areTestAPIKeysSet() && err != nil {
t.Errorf("Could not cancel orders: %v", err)
switch {
case areTestAPIKeysSet() && err != nil:
t.Error("Test Failed - CancelExchangeOrder() error", err)
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Test Failed - CancelExchangeOrder() expecting an error when no keys are set")
case mockTests && err != nil:
t.Error("Test Failed - Mock CancelExchangeOrder() error", err)
}
}
func TestCancelAllExchangeOrders(t *testing.T) {
b.SetDefaults()
TestSetup(t)
t.Parallel()
currencyPair := currency.NewPair(currency.LTC, currency.BTC)
if areTestAPIKeysSet() && !canManipulateRealOrders && !mockTests {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
var orderCancellation = &exchange.OrderCancellation{
OrderID: "1",
WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB",
AccountID: "1",
CurrencyPair: currencyPair,
CurrencyPair: currency.NewPair(currency.LTC, currency.BTC),
}
resp, err := b.CancelAllOrders(orderCancellation)
if !areTestAPIKeysSet() && err == nil {
t.Error("Expecting an error when no keys are set")
}
if areTestAPIKeysSet() && err != nil {
t.Errorf("Could not cancel order: %v", err)
}
if len(resp.OrderStatus) > 0 {
t.Errorf("%v orders failed to cancel", len(resp.OrderStatus))
_, err := b.CancelAllOrders(orderCancellation)
switch {
case areTestAPIKeysSet() && err != nil:
t.Error("Test Failed - CancelAllExchangeOrders() error", err)
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Test Failed - CancelAllExchangeOrders() expecting an error when no keys are set")
case mockTests && err != nil:
t.Error("Test Failed - Mock CancelAllExchangeOrders() error", err)
}
}
func TestGetAccountInfo(t *testing.T) {
b.SetDefaults()
TestSetup(t)
if apiKey == "" || apiSecret == "" {
t.Skip()
}
t.Parallel()
_, err := b.GetAccountInfo()
if err != nil {
t.Error("test failed - GetAccountInfo() error", err)
switch {
case areTestAPIKeysSet() && err != nil:
t.Error("Test Failed - GetAccountInfo() error", err)
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Test Failed - GetAccountInfo() expecting an error when no keys are set")
case mockTests && err != nil:
t.Error("Test Failed - Mock GetAccountInfo() error", err)
}
}
func TestModifyOrder(t *testing.T) {
t.Parallel()
_, err := b.ModifyOrder(&exchange.ModifyOrder{})
if err == nil {
t.Error("Test failed - ModifyOrder() error")
t.Error("Test failed - ModifyOrder() error cannot be nil")
}
}
func TestWithdraw(t *testing.T) {
b.SetDefaults()
TestSetup(t)
t.Parallel()
if areTestAPIKeysSet() && !canManipulateRealOrders {
if areTestAPIKeysSet() && !canManipulateRealOrders && !mockTests {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
var withdrawCryptoRequest = exchange.WithdrawRequest{
Amount: 100,
Amount: 0,
Currency: currency.BTC,
Address: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB",
Description: "WITHDRAW IT ALL",
}
_, err := b.WithdrawCryptocurrencyFunds(&withdrawCryptoRequest)
if !areTestAPIKeysSet() && err == nil {
t.Error("Expecting an error when no keys are set")
}
if areTestAPIKeysSet() && err != nil {
t.Errorf("Withdraw failed to be placed: %v", err)
switch {
case areTestAPIKeysSet() && err != nil:
t.Error("Test Failed - Withdraw() error", err)
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Test Failed - Withdraw() expecting an error when no keys are set")
case mockTests && err != nil:
t.Error("Test Failed - Mock Withdraw() error", err)
}
}
func TestWithdrawFiat(t *testing.T) {
b.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
var withdrawFiatRequest = exchange.WithdrawRequest{}
t.Parallel()
var withdrawFiatRequest exchange.WithdrawRequest
_, err := b.WithdrawFiatFunds(&withdrawFiatRequest)
if err != common.ErrFunctionNotSupported {
t.Errorf("Expected '%v', received: '%v'", common.ErrFunctionNotSupported, err)
@@ -528,15 +480,9 @@ func TestWithdrawFiat(t *testing.T) {
}
func TestWithdrawInternationalBank(t *testing.T) {
b.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
var withdrawFiatRequest = exchange.WithdrawRequest{}
t.Parallel()
var withdrawFiatRequest exchange.WithdrawRequest
_, err := b.WithdrawFiatFundsToInternationalBank(&withdrawFiatRequest)
if err != common.ErrFunctionNotSupported {
t.Errorf("Expected '%v', received: '%v'", common.ErrFunctionNotSupported, err)
@@ -544,15 +490,15 @@ func TestWithdrawInternationalBank(t *testing.T) {
}
func TestGetDepositAddress(t *testing.T) {
if areTestAPIKeysSet() {
_, err := b.GetDepositAddress(currency.BTC, "")
if err != nil {
t.Error("Test Failed - GetDepositAddress() error", err)
}
} else {
_, err := b.GetDepositAddress(currency.BTC, "")
if err == nil {
t.Error("Test Failed - GetDepositAddress() error cannot be nil")
}
t.Parallel()
_, err := b.GetDepositAddress(currency.BTC, "")
switch {
case areTestAPIKeysSet() && err != nil:
t.Error("Test Failed - GetDepositAddress() error", err)
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Test Failed - GetDepositAddress() error cannot be nil")
case mockTests && err != nil:
t.Error("Test Failed - Mock GetDepositAddress() error", err)
}
}

View File

@@ -314,7 +314,7 @@ type NewOrderResponse struct {
Price float64 `json:"price,string"`
Qty float64 `json:"qty,string"`
Commission float64 `json:"commission,string"`
CommissionAsset float64 `json:"commissionAsset,string"`
CommissionAsset string `json:"commissionAsset"`
} `json:"fills"`
}
@@ -621,5 +621,5 @@ var WithdrawalFees = map[currency.Code]float64{
type WithdrawResponse struct {
Success bool `json:"success"`
Msg string `json:"msg"`
ID int64 `json:"id"`
ID string `json:"id"`
}

View File

@@ -301,9 +301,10 @@ func (b *Binance) GetDepositAddress(cryptocurrency currency.Code, _ string) (str
// submitted
func (b *Binance) WithdrawCryptocurrencyFunds(withdrawRequest *exchange.WithdrawRequest) (string, error) {
amountStr := strconv.FormatFloat(withdrawRequest.Amount, 'f', -1, 64)
id, err := b.WithdrawCrypto(withdrawRequest.Currency.String(), withdrawRequest.Address, withdrawRequest.AddressTag, withdrawRequest.Description, amountStr)
return strconv.FormatInt(id, 10), err
return b.WithdrawCrypto(withdrawRequest.Currency.String(),
withdrawRequest.Address,
withdrawRequest.AddressTag,
withdrawRequest.Description, amountStr)
}
// WithdrawFiatFunds returns a withdrawal ID when a

View File

@@ -1030,7 +1030,16 @@ func (b *Bitfinex) CloseMarginFunding(swapID int64) (Offer, error) {
// SendHTTPRequest sends an unauthenticated request
func (b *Bitfinex) SendHTTPRequest(path string, result interface{}, verbose bool) error {
return b.SendPayload(http.MethodGet, path, nil, nil, result, false, false, verbose, b.HTTPDebugging)
return b.SendPayload(http.MethodGet,
path,
nil,
nil,
result,
false,
false,
verbose,
b.HTTPDebugging,
b.HTTPRecording)
}
// SendAuthenticatedHTTPRequest sends an autheticated http request and json
@@ -1076,7 +1085,8 @@ func (b *Bitfinex) SendAuthenticatedHTTPRequest(method, path string, params map[
true,
true,
b.Verbose,
b.HTTPDebugging)
b.HTTPDebugging,
b.HTTPRecording)
}
// GetFee returns an estimate of fee based on type of transaction

View File

@@ -380,7 +380,16 @@ func (b *Bitflyer) GetTradingCommission() {
// SendHTTPRequest sends an unauthenticated request
func (b *Bitflyer) SendHTTPRequest(path string, result interface{}) error {
return b.SendPayload(http.MethodGet, path, nil, nil, result, false, false, b.Verbose, b.HTTPDebugging)
return b.SendPayload(http.MethodGet,
path,
nil,
nil,
result,
false,
false,
b.Verbose,
b.HTTPDebugging,
b.HTTPRecording)
}
// SendAuthHTTPRequest sends an authenticated HTTP request

View File

@@ -531,7 +531,16 @@ func (b *Bithumb) MarketSellOrder(currency string, units float64) (MarketSell, e
// SendHTTPRequest sends an unauthenticated HTTP request
func (b *Bithumb) SendHTTPRequest(path string, result interface{}) error {
return b.SendPayload(http.MethodGet, path, nil, nil, result, false, false, b.Verbose, b.HTTPDebugging)
return b.SendPayload(http.MethodGet,
path,
nil,
nil,
result,
false,
false,
b.Verbose,
b.HTTPDebugging,
b.HTTPRecording)
}
// SendAuthenticatedHTTPRequest sends an authenticated HTTP request to bithumb
@@ -575,7 +584,8 @@ func (b *Bithumb) SendAuthenticatedHTTPRequest(path string, params url.Values, r
true,
true,
b.Verbose,
b.HTTPDebugging)
b.HTTPDebugging,
b.HTTPRecording)
if err != nil {
return err
}

View File

@@ -879,14 +879,32 @@ func (b *Bitmex) SendHTTPRequest(path string, params Parameter, result interface
if err != nil {
return err
}
err = b.SendPayload(http.MethodGet, encodedPath, nil, nil, &respCheck, false, false, b.Verbose, b.HTTPDebugging)
err = b.SendPayload(http.MethodGet,
encodedPath,
nil,
nil,
&respCheck,
false,
false,
b.Verbose,
b.HTTPDebugging,
b.HTTPRecording)
if err != nil {
return err
}
return b.CaptureError(respCheck, result)
}
}
err := b.SendPayload(http.MethodGet, path, nil, nil, &respCheck, false, false, b.Verbose, b.HTTPDebugging)
err := b.SendPayload(http.MethodGet,
path,
nil,
nil,
&respCheck,
false,
false,
b.Verbose,
b.HTTPDebugging,
b.HTTPRecording)
if err != nil {
return err
}
@@ -938,7 +956,8 @@ func (b *Bitmex) SendAuthenticatedHTTPRequest(verb, path string, params Paramete
true,
false,
b.Verbose,
b.HTTPDebugging)
b.HTTPDebugging,
b.HTTPRecording)
if err != nil {
return err
}

View File

@@ -1,6 +1,7 @@
package bitstamp
import (
"bytes"
"encoding/json"
"errors"
"fmt"
@@ -176,7 +177,6 @@ func (b *Bitstamp) GetFee(feeBuilder *exchange.FeeBuilder) (float64, error) {
switch feeBuilder.FeeType {
case exchange.CryptocurrencyTradeFee:
var err error
b.Balance, err = b.GetBalance()
if err != nil {
return 0, err
@@ -368,16 +368,15 @@ func (b *Bitstamp) GetEURUSDConversionRate() (EURUSDConversionRate, error) {
// GetBalance returns full balance of currency held on the exchange
func (b *Bitstamp) GetBalance() (Balances, error) {
balance := Balances{}
path := fmt.Sprintf("%s/%s", b.APIUrl, bitstampAPIBalance)
return balance, b.SendHTTPRequest(path, &balance)
var balance Balances
return balance,
b.SendAuthenticatedHTTPRequest(bitstampAPIBalance, true, nil, &balance)
}
// GetUserTransactions returns an array of transactions
func (b *Bitstamp) GetUserTransactions(currencyPair string) ([]UserTransactions, error) {
type Response struct {
Date int64 `json:"datetime"`
Date string `json:"datetime"`
TransID int64 `json:"id"`
Type int `json:"type,string"`
USD interface{} `json:"usd"`
@@ -390,12 +389,18 @@ func (b *Bitstamp) GetUserTransactions(currencyPair string) ([]UserTransactions,
}
var response []Response
if currencyPair != "" {
if err := b.SendAuthenticatedHTTPRequest(bitstampAPIUserTransactions, true, url.Values{}, &response); err != nil {
if currencyPair == "" {
if err := b.SendAuthenticatedHTTPRequest(bitstampAPIUserTransactions,
true,
url.Values{},
&response); err != nil {
return nil, err
}
} else {
if err := b.SendAuthenticatedHTTPRequest(bitstampAPIUserTransactions+"/"+currencyPair, true, url.Values{}, &response); err != nil {
if err := b.SendAuthenticatedHTTPRequest(bitstampAPIUserTransactions+"/"+currencyPair,
true,
url.Values{},
&response); err != nil {
return nil, err
}
}
@@ -491,10 +496,11 @@ func (b *Bitstamp) PlaceOrder(currencyPair string, price, amount float64, buy, m
orderType = bitstampAPISell
}
path := fmt.Sprintf("%s/%s", orderType, common.StringToLower(currencyPair))
var path string
if market {
path = fmt.Sprintf("%s/%s/%s", orderType, bitstampAPIMarket, common.StringToLower(currencyPair))
} else {
path = fmt.Sprintf("%s/%s", orderType, common.StringToLower(currencyPair))
}
return response,
@@ -651,28 +657,41 @@ func (b *Bitstamp) GetUnconfirmedBitcoinDeposits() ([]UnconfirmedBTCTransactions
// currency - which currency to transfer
// subaccount - name of account
// toMain - bool either to or from account
func (b *Bitstamp) TransferAccountBalance(amount float64, currency, subAccount string, toMain bool) (bool, error) {
func (b *Bitstamp) TransferAccountBalance(amount float64, currency, subAccount string, toMain bool) error {
var req = url.Values{}
req.Add("amount", strconv.FormatFloat(amount, 'f', -1, 64))
req.Add("currency", currency)
if subAccount == "" {
return errors.New("missing subAccount parameter")
}
req.Add("subAccount", subAccount)
path := bitstampAPITransferToMain
if !toMain {
var path string
if toMain {
path = bitstampAPITransferToMain
} else {
path = bitstampAPITransferFromMain
}
err := b.SendAuthenticatedHTTPRequest(path, true, req, nil)
if err != nil {
return false, err
}
var resp interface{}
return true, nil
return b.SendAuthenticatedHTTPRequest(path, true, req, &resp)
}
// SendHTTPRequest sends an unauthenticated HTTP request
func (b *Bitstamp) SendHTTPRequest(path string, result interface{}) error {
return b.SendPayload(http.MethodGet, path, nil, nil, result, false, false, b.Verbose, b.HTTPDebugging)
return b.SendPayload(http.MethodGet,
path,
nil,
nil,
result,
false,
false,
b.Verbose,
b.HTTPDebugging,
b.HTTPRecording)
}
// SendAuthenticatedHTTPRequest sends an authenticated request
@@ -706,15 +725,26 @@ func (b *Bitstamp) SendAuthenticatedHTTPRequest(path string, v2 bool, values url
headers["Content-Type"] = "application/x-www-form-urlencoded"
encodedValues := values.Encode()
readerValues := strings.NewReader(encodedValues)
readerValues := bytes.NewBufferString(encodedValues)
interim := json.RawMessage{}
errCap := struct {
Error string `json:"error"`
Error string `json:"error"`
Status string `json:"status"`
Reason interface{} `json:"reason"`
}{}
err := b.SendPayload(http.MethodPost, path, headers, readerValues, &interim, true, true, b.Verbose, b.HTTPDebugging)
err := b.SendPayload(http.MethodPost,
path,
headers,
readerValues,
&interim,
true,
true,
b.Verbose,
b.HTTPDebugging,
b.HTTPRecording)
if err != nil {
return err
}
@@ -723,6 +753,21 @@ func (b *Bitstamp) SendAuthenticatedHTTPRequest(path string, v2 bool, values url
if errCap.Error != "" {
return errors.New(errCap.Error)
}
if data, ok := errCap.Reason.(map[string][]string); ok {
var details string
for _, v := range data {
details += strings.Join(v, "")
}
return errors.New(details)
}
if data, ok := errCap.Reason.(string); ok {
return errors.New(data)
}
if errCap.Status != "" {
return errors.New(errCap.Status)
}
}
return common.JSONDecode(interim, result)

View File

@@ -0,0 +1,33 @@
//+build mock_test_off
// This will build if build tag mock_test_off is parsed and will do live testing
// using all tests in (exchange)_test.go
package bitstamp
import (
"log"
"os"
"testing"
"github.com/thrasher-corp/gocryptotrader/config"
"github.com/thrasher-corp/gocryptotrader/exchanges/sharedtestvalues"
)
var mockTests = false
func TestMain(m *testing.M) {
cfg := config.GetConfig()
cfg.LoadConfig("../../testdata/configtest.json")
bitstampConfig, err := cfg.GetExchangeConfig("Bitstamp")
if err != nil {
log.Fatal("Test Failed - Bitstamp Setup() init error", err)
}
bitstampConfig.AuthenticatedAPISupport = true
bitstampConfig.APIKey = apiKey
bitstampConfig.APISecret = apiSecret
bitstampConfig.ClientID = customerID
b.SetDefaults()
b.Setup(&bitstampConfig)
log.Printf(sharedtestvalues.LiveTesting, b.GetName(), b.APIUrl)
os.Exit(m.Run())
}

View File

@@ -0,0 +1,45 @@
//+build !mock_test_off
// This will build if build tag mock_test_off is not parsed and will try to mock
// all tests in _test.go
package bitstamp
import (
"log"
"os"
"testing"
"github.com/thrasher-corp/gocryptotrader/config"
"github.com/thrasher-corp/gocryptotrader/exchanges/mock"
"github.com/thrasher-corp/gocryptotrader/exchanges/sharedtestvalues"
)
const mockfile = "../../testdata/http_mock/bitstamp/bitstamp.json"
var mockTests = true
func TestMain(m *testing.M) {
cfg := config.GetConfig()
cfg.LoadConfig("../../testdata/configtest.json")
bitstampConfig, err := cfg.GetExchangeConfig("Bitstamp")
if err != nil {
log.Fatal("Test Failed - Bitstamp Setup() init error", err)
}
bitstampConfig.AuthenticatedAPISupport = true
bitstampConfig.APIKey = apiKey
bitstampConfig.APISecret = apiSecret
bitstampConfig.ClientID = customerID
b.SetDefaults()
b.Setup(&bitstampConfig)
serverDetails, newClient, err := mock.NewVCRServer(mockfile)
if err != nil {
log.Fatalf("Test Failed - Mock server error %s", err)
}
b.HTTPClient = newClient
b.APIUrl = serverDetails + "/api"
log.Printf(sharedtestvalues.MockTesting, b.GetName(), b.APIUrl)
os.Exit(m.Run())
}

View File

@@ -3,9 +3,7 @@ package bitstamp
import (
"net/url"
"testing"
"time"
"github.com/thrasher-corp/gocryptotrader/config"
"github.com/thrasher-corp/gocryptotrader/currency"
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
)
@@ -20,45 +18,12 @@ const (
var b Bitstamp
func TestSetDefaults(t *testing.T) {
b.SetDefaults()
if b.Name != "Bitstamp" {
t.Error("Test Failed - SetDefaults() error")
}
if b.Enabled {
t.Error("Test Failed - SetDefaults() error")
}
if b.Verbose {
t.Error("Test Failed - SetDefaults() error")
}
if b.Websocket.IsEnabled() {
t.Error("Test Failed - SetDefaults() error")
}
if b.RESTPollingDelay != 10 {
t.Error("Test Failed - SetDefaults() error")
}
}
func TestSetup(t *testing.T) {
cfg := config.GetConfig()
cfg.LoadConfig("../../testdata/configtest.json")
bConfig, err := cfg.GetExchangeConfig("Bitstamp")
if err != nil {
t.Error("Test Failed - Bitstamp Setup() init error")
}
bConfig.APIKey = apiKey
bConfig.APISecret = apiSecret
bConfig.ClientID = customerID
b.Setup(&bConfig)
b.ClientID = customerID
if !b.IsEnabled() || b.RESTPollingDelay != time.Duration(10) ||
b.Verbose || b.Websocket.IsEnabled() || len(b.BaseCurrencies) < 1 ||
len(b.AvailablePairs) < 1 || len(b.EnabledPairs) < 1 {
t.Error("Test Failed - Bitstamp Setup values not set correctly")
func areTestAPIKeysSet() bool {
if b.APIKey != "" && b.APIKey != "Key" &&
b.APISecret != "" && b.APISecret != "Secret" {
return true
}
return false
}
func setFeeBuilder() *exchange.FeeBuilder {
@@ -72,53 +37,66 @@ func setFeeBuilder() *exchange.FeeBuilder {
// TestGetFeeByTypeOfflineTradeFee logic test
func TestGetFeeByTypeOfflineTradeFee(t *testing.T) {
t.Parallel()
var feeBuilder = setFeeBuilder()
b.GetFeeByType(feeBuilder)
if apiKey == "" || apiSecret == "" {
if !areTestAPIKeysSet() || mockTests {
if feeBuilder.FeeType != exchange.OfflineTradeFee {
t.Errorf("Expected %v, received %v", exchange.OfflineTradeFee, feeBuilder.FeeType)
t.Errorf("Expected %v, received %v",
exchange.OfflineTradeFee,
feeBuilder.FeeType)
}
} else {
if feeBuilder.FeeType != exchange.CryptocurrencyTradeFee {
t.Errorf("Expected %v, received %v", exchange.CryptocurrencyTradeFee, feeBuilder.FeeType)
t.Errorf("Expected %v, received %v",
exchange.CryptocurrencyTradeFee,
feeBuilder.FeeType)
}
}
}
func TestGetFee(t *testing.T) {
b.SetDefaults()
TestSetup(t)
t.Parallel()
var feeBuilder = setFeeBuilder()
// CryptocurrencyTradeFee Basic
if resp, err := b.GetFee(feeBuilder); resp != float64(0) || err != nil {
if resp, err := b.GetFee(feeBuilder); resp != float64(0) || (areTestAPIKeysSet() && err != nil) {
t.Error(err)
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f", float64(0), resp)
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f",
float64(0),
resp)
}
// CryptocurrencyTradeFee High quantity
feeBuilder = setFeeBuilder()
feeBuilder.Amount = 1000
feeBuilder.PurchasePrice = 1000
if resp, err := b.GetFee(feeBuilder); resp != float64(0) || err != nil {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f", float64(0), resp)
if resp, err := b.GetFee(feeBuilder); resp != float64(0) || (areTestAPIKeysSet() && err != nil) {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f",
float64(0),
resp)
t.Error(err)
}
// CryptocurrencyTradeFee IsMaker
feeBuilder = setFeeBuilder()
feeBuilder.IsMaker = true
if resp, err := b.GetFee(feeBuilder); resp != float64(0) || err != nil {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f", float64(0), resp)
if resp, err := b.GetFee(feeBuilder); resp != float64(0) || (areTestAPIKeysSet() && err != nil) {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f",
float64(0),
resp)
t.Error(err)
}
// CryptocurrencyTradeFee Negative purchase price
feeBuilder = setFeeBuilder()
feeBuilder.PurchasePrice = -1000
if resp, err := b.GetFee(feeBuilder); resp != float64(0) || err != nil {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f", float64(0), resp)
if resp, err := b.GetFee(feeBuilder); resp != float64(0) || (areTestAPIKeysSet() && err != nil) {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f",
float64(0),
resp)
t.Error(err)
}
@@ -126,7 +104,9 @@ func TestGetFee(t *testing.T) {
feeBuilder = setFeeBuilder()
feeBuilder.FeeType = exchange.CryptocurrencyWithdrawalFee
if resp, err := b.GetFee(feeBuilder); resp != float64(0) || err != nil {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f", float64(0), resp)
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f",
float64(0),
resp)
t.Error(err)
}
@@ -134,7 +114,9 @@ func TestGetFee(t *testing.T) {
feeBuilder = setFeeBuilder()
feeBuilder.FeeType = exchange.CyptocurrencyDepositFee
if resp, err := b.GetFee(feeBuilder); resp != float64(0) || err != nil {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f", float64(0), resp)
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f",
float64(0),
resp)
t.Error(err)
}
@@ -143,7 +125,9 @@ func TestGetFee(t *testing.T) {
feeBuilder.FeeType = exchange.InternationalBankDepositFee
feeBuilder.FiatCurrency = currency.HKD
if resp, err := b.GetFee(feeBuilder); resp != float64(7.5) || err != nil {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f", float64(7.5), resp)
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f",
float64(7.5),
resp)
t.Error(err)
}
@@ -152,15 +136,15 @@ func TestGetFee(t *testing.T) {
feeBuilder.FeeType = exchange.InternationalBankWithdrawalFee
feeBuilder.FiatCurrency = currency.HKD
if resp, err := b.GetFee(feeBuilder); resp != float64(15) || err != nil {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f", float64(15), resp)
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f",
float64(15),
resp)
t.Error(err)
}
}
func TestCalculateTradingFee(t *testing.T) {
b.SetDefaults()
TestSetup(t)
b.Balance = Balances{}
t.Parallel()
b.Balance.BTCUSDFee = 1
b.Balance.BTCEURFee = 0
@@ -182,18 +166,16 @@ func TestCalculateTradingFee(t *testing.T) {
func TestGetTicker(t *testing.T) {
t.Parallel()
_, err := b.GetTicker(currency.BTC.String()+currency.USD.String(), false)
if err != nil {
t.Error("Test Failed - GetTicker() error", err)
}
_, err = b.GetTicker(currency.BTC.String()+currency.USD.String(), true)
if err != nil {
t.Error("Test Failed - GetTicker() error", err)
}
}
func TestGetOrderbook(t *testing.T) {
t.Parallel()
_, err := b.GetOrderbook(currency.BTC.String() + currency.USD.String())
if err != nil {
t.Error("Test Failed - GetOrderbook() error", err)
@@ -202,6 +184,7 @@ func TestGetOrderbook(t *testing.T) {
func TestGetTradingPairs(t *testing.T) {
t.Parallel()
_, err := b.GetTradingPairs()
if err != nil {
t.Error("Test Failed - GetTradingPairs() error", err)
@@ -210,6 +193,7 @@ func TestGetTradingPairs(t *testing.T) {
func TestGetTransactions(t *testing.T) {
t.Parallel()
value := url.Values{}
value.Set("time", "hour")
@@ -217,14 +201,11 @@ func TestGetTransactions(t *testing.T) {
if err != nil {
t.Error("Test Failed - GetTransactions() error", err)
}
_, err = b.GetTransactions("wigwham", value)
if err == nil {
t.Error("Test Failed - GetTransactions() error")
}
}
func TestGetEURUSDConversionRate(t *testing.T) {
t.Parallel()
_, err := b.GetEURUSDConversionRate()
if err != nil {
t.Error("Test Failed - GetEURUSDConversionRate() error", err)
@@ -233,21 +214,28 @@ func TestGetEURUSDConversionRate(t *testing.T) {
func TestGetBalance(t *testing.T) {
t.Parallel()
_, err := b.GetBalance()
if err != nil {
switch {
case areTestAPIKeysSet() && err != nil && !mockTests:
t.Error("Test Failed - GetBalance() error", err)
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Expecting an error when no keys are set")
case mockTests && err != nil:
t.Error("Test Failed - GetBalance() error", err)
}
}
func TestGetUserTransactions(t *testing.T) {
t.Parallel()
_, err := b.GetUserTransactions("")
if err == nil {
t.Error("Test Failed - GetUserTransactions() error", err)
}
_, err = b.GetUserTransactions("btcusd")
if err == nil {
_, err := b.GetUserTransactions("btcusd")
switch {
case areTestAPIKeysSet() && err != nil && !mockTests:
t.Error("Test Failed - GetUserTransactions() error", err)
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Expecting an error when no keys are set")
case mockTests && err != nil:
t.Error("Test Failed - GetUserTransactions() error", err)
}
}
@@ -256,56 +244,27 @@ func TestGetOpenOrders(t *testing.T) {
t.Parallel()
_, err := b.GetOpenOrders("btcusd")
if err == nil {
switch {
case areTestAPIKeysSet() && err != nil && !mockTests:
t.Error("Test Failed - GetOpenOrders() error", err)
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Expecting an error when no keys are set")
case mockTests && err != nil:
t.Error("Test Failed - GetOpenOrders() error", err)
}
_, err = b.GetOpenOrders("wigwham")
if err == nil {
t.Error("Test Failed - GetOpenOrders() error")
}
}
func TestGetOrderStatus(t *testing.T) {
t.Parallel()
if !areTestAPIKeysSet() {
t.Skip()
}
_, err := b.GetOrderStatus(1337)
if err == nil {
t.Error("Test Failed - GetOpenOrders() error")
}
}
func TestCancelExistingOrder(t *testing.T) {
t.Parallel()
resp, err := b.CancelExistingOrder(1337)
if err == nil || resp {
t.Error("Test Failed - CancelExistingOrder() error")
}
}
func TestCancelAllExistingOrders(t *testing.T) {
t.Parallel()
_, err := b.CancelAllExistingOrders()
if err == nil {
t.Error("Test Failed - CancelAllExistingOrders() error", err)
}
}
func TestPlaceOrder(t *testing.T) {
t.Parallel()
if !areTestAPIKeysSet() {
t.Skip()
}
_, err := b.PlaceOrder("btcusd", 0.01, 1, true, true)
if err == nil {
t.Error("Test Failed - PlaceOrder() error")
switch {
case areTestAPIKeysSet() && err != nil && !mockTests:
t.Error("Test Failed - GetOrderStatus() error", err)
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Expecting an error when no keys are set")
case mockTests && err == nil:
t.Error("Expecting an error until a QA pass can be completed")
}
}
@@ -313,34 +272,13 @@ func TestGetWithdrawalRequests(t *testing.T) {
t.Parallel()
_, err := b.GetWithdrawalRequests(0)
if err == nil {
switch {
case areTestAPIKeysSet() && err != nil && !mockTests:
t.Error("Test Failed - GetWithdrawalRequests() error", err)
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Expecting an error when no keys are set")
case mockTests && err != nil:
t.Error("Test Failed - GetWithdrawalRequests() error", err)
}
_, err = b.GetWithdrawalRequests(-1)
if err == nil {
t.Error("Test Failed - GetWithdrawalRequests() error")
}
}
func TestCryptoWithdrawal(t *testing.T) {
t.Parallel()
if !areTestAPIKeysSet() {
t.Skip()
}
_, err := b.CryptoWithdrawal(0, "bla", "btc", "", true)
if err == nil {
t.Error("Test Failed - CryptoWithdrawal() error", err)
}
}
func TestGetBitcoinDepositAddress(t *testing.T) {
t.Parallel()
_, err := b.GetCryptoDepositAddress(currency.BTC)
if err == nil {
t.Error("Test Failed - GetCryptoDepositAddress() error", err)
}
}
@@ -348,7 +286,12 @@ func TestGetUnconfirmedBitcoinDeposits(t *testing.T) {
t.Parallel()
_, err := b.GetUnconfirmedBitcoinDeposits()
if err == nil {
switch {
case areTestAPIKeysSet() && err != nil && !mockTests:
t.Error("Test Failed - GetUnconfirmedBitcoinDeposits() error", err)
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Expecting an error when no keys are set")
case mockTests && err != nil:
t.Error("Test Failed - GetUnconfirmedBitcoinDeposits() error", err)
}
}
@@ -356,78 +299,77 @@ func TestGetUnconfirmedBitcoinDeposits(t *testing.T) {
func TestTransferAccountBalance(t *testing.T) {
t.Parallel()
if !areTestAPIKeysSet() {
if !areTestAPIKeysSet() && !mockTests {
t.Skip()
}
_, err := b.TransferAccountBalance(1, "", "", true)
if err == nil {
err := b.TransferAccountBalance(0.01, "btc", "testAccount", true)
if !mockTests && err != nil {
t.Error("Test Failed - TransferAccountBalance() error", err)
}
_, err = b.TransferAccountBalance(1, "btc", "", false)
if err == nil {
t.Error("Test Failed - TransferAccountBalance() error", err)
if mockTests && err == nil {
t.Error("Expecting an error until a QA pass can be completed")
}
}
func TestFormatWithdrawPermissions(t *testing.T) {
b.SetDefaults()
expectedResult := exchange.AutoWithdrawCryptoText + " & " + exchange.AutoWithdrawFiatText
t.Parallel()
expectedResult := exchange.AutoWithdrawCryptoText +
" & " +
exchange.AutoWithdrawFiatText
withdrawPermissions := b.FormatWithdrawPermissions()
if withdrawPermissions != expectedResult {
t.Errorf("Expected: %s, Received: %s", expectedResult, withdrawPermissions)
t.Errorf("Expected: %s, Received: %s",
expectedResult,
withdrawPermissions)
}
}
func TestGetActiveOrders(t *testing.T) {
b.SetDefaults()
TestSetup(t)
t.Parallel()
var getOrdersRequest = exchange.GetOrdersRequest{
OrderType: exchange.AnyOrderType,
}
_, err := b.GetActiveOrders(&getOrdersRequest)
if areTestAPIKeysSet() && err != nil {
switch {
case areTestAPIKeysSet() && err != nil && !mockTests:
t.Errorf("Could not get open orders: %s", err)
} else if !areTestAPIKeysSet() && err == nil {
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Expecting an error when no keys are set")
case mockTests && err != nil:
t.Errorf("Could not get open orders: %s", err)
}
}
func TestGetOrderHistory(t *testing.T) {
b.SetDefaults()
TestSetup(t)
t.Parallel()
var getOrdersRequest = exchange.GetOrdersRequest{
OrderType: exchange.AnyOrderType,
}
_, err := b.GetOrderHistory(&getOrdersRequest)
if areTestAPIKeysSet() && err != nil {
switch {
case areTestAPIKeysSet() && err != nil && !mockTests:
t.Errorf("Could not get order history: %s", err)
} else if !areTestAPIKeysSet() && err == nil {
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Expecting an error when no keys are set")
case mockTests && err != nil:
t.Errorf("Could not get order history: %s", err)
}
}
// Any tests below this line have the ability to impact your orders on the exchange. Enable canManipulateRealOrders to run them
// ----------------------------------------------------------------------------------------------------------------------------
func areTestAPIKeysSet() bool {
if b.APIKey != "" && b.APIKey != "Key" &&
b.APISecret != "" && b.APISecret != "Secret" {
return true
}
return false
}
func TestSubmitOrder(t *testing.T) {
b.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Parallel()
if areTestAPIKeysSet() && !canManipulateRealOrders && !mockTests {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -436,19 +378,26 @@ func TestSubmitOrder(t *testing.T) {
Base: currency.BTC,
Quote: currency.USD,
}
response, err := b.SubmitOrder(p, exchange.BuyOrderSide, exchange.MarketOrderType, 1, 1, "clientId")
if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) {
response, err := b.SubmitOrder(p,
exchange.BuyOrderSide,
exchange.MarketOrderType,
1,
1,
"clientId")
switch {
case areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) && !mockTests:
t.Errorf("Order failed to be placed: %v", err)
} else if !areTestAPIKeysSet() && err == nil {
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Expecting an error when no keys are set")
case mockTests && err == nil:
t.Error("Expecting an error until QA pass is completed")
}
}
func TestCancelExchangeOrder(t *testing.T) {
b.SetDefaults()
TestSetup(t)
t.Parallel()
if areTestAPIKeysSet() && !canManipulateRealOrders {
if areTestAPIKeysSet() && !canManipulateRealOrders && !mockTests {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -462,19 +411,20 @@ func TestCancelExchangeOrder(t *testing.T) {
}
err := b.CancelOrder(orderCancellation)
if !areTestAPIKeysSet() && err == nil {
switch {
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Expecting an error when no keys are set")
}
if areTestAPIKeysSet() && err != nil {
case areTestAPIKeysSet() && err != nil && !mockTests:
t.Errorf("Could not cancel orders: %v", err)
case mockTests && err == nil:
t.Error("Expecting an error until QA pass is completed")
}
}
func TestCancelAllExchangeOrders(t *testing.T) {
b.SetDefaults()
TestSetup(t)
t.Parallel()
if areTestAPIKeysSet() && !canManipulateRealOrders {
if areTestAPIKeysSet() && !canManipulateRealOrders && !mockTests {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -488,11 +438,12 @@ func TestCancelAllExchangeOrders(t *testing.T) {
}
resp, err := b.CancelAllOrders(orderCancellation)
if !areTestAPIKeysSet() && err == nil {
switch {
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Expecting an error when no keys are set")
}
if areTestAPIKeysSet() && err != nil {
case areTestAPIKeysSet() && err != nil && !mockTests:
t.Errorf("Could not cancel orders: %v", err)
case mockTests && err != nil:
t.Errorf("Could not cancel orders: %v", err)
}
@@ -502,6 +453,8 @@ func TestCancelAllExchangeOrders(t *testing.T) {
}
func TestModifyOrder(t *testing.T) {
t.Parallel()
_, err := b.ModifyOrder(&exchange.ModifyOrder{})
if err == nil {
t.Error("Test failed - ModifyOrder() error")
@@ -509,8 +462,12 @@ func TestModifyOrder(t *testing.T) {
}
func TestWithdraw(t *testing.T) {
b.SetDefaults()
TestSetup(t)
t.Parallel()
if areTestAPIKeysSet() && !canManipulateRealOrders && !mockTests {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
var withdrawCryptoRequest = exchange.WithdrawRequest{
Amount: 100,
Currency: currency.BTC,
@@ -518,24 +475,21 @@ func TestWithdraw(t *testing.T) {
Description: "WITHDRAW IT ALL",
}
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
_, err := b.WithdrawCryptocurrencyFunds(&withdrawCryptoRequest)
if !areTestAPIKeysSet() && err == nil {
switch {
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Expecting an error when no keys are set")
}
if areTestAPIKeysSet() && err != nil {
case areTestAPIKeysSet() && err != nil && !mockTests:
t.Errorf("Withdraw failed to be placed: %v", err)
case mockTests && err == nil:
t.Error("Expecting an error until QA pass is completed")
}
}
func TestWithdrawFiat(t *testing.T) {
b.SetDefaults()
TestSetup(t)
t.Parallel()
if areTestAPIKeysSet() && !canManipulateRealOrders {
if areTestAPIKeysSet() && !canManipulateRealOrders && !mockTests {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -558,19 +512,20 @@ func TestWithdrawFiat(t *testing.T) {
}
_, err := b.WithdrawFiatFunds(&withdrawFiatRequest)
if !areTestAPIKeysSet() && err == nil {
switch {
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Expecting an error when no keys are set")
}
if areTestAPIKeysSet() && err != nil {
case areTestAPIKeysSet() && err != nil && !mockTests:
t.Errorf("Withdraw failed to be placed: %v", err)
case mockTests && err == nil:
t.Error("Expecting an error until QA pass is completed")
}
}
func TestWithdrawInternationalBank(t *testing.T) {
b.SetDefaults()
TestSetup(t)
t.Parallel()
if areTestAPIKeysSet() && !canManipulateRealOrders {
if areTestAPIKeysSet() && !canManipulateRealOrders && !mockTests {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -599,24 +554,26 @@ func TestWithdrawInternationalBank(t *testing.T) {
}
_, err := b.WithdrawFiatFundsToInternationalBank(&withdrawFiatRequest)
if !areTestAPIKeysSet() && err == nil {
switch {
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Expecting an error when no keys are set")
}
if areTestAPIKeysSet() && err != nil {
case areTestAPIKeysSet() && err != nil && !mockTests:
t.Errorf("Withdraw failed to be placed: %v", err)
case mockTests && err == nil:
t.Error("Expecting an error until QA pass is completed")
}
}
func TestGetDepositAddress(t *testing.T) {
if areTestAPIKeysSet() && customerID != "" {
_, err := b.GetDepositAddress(currency.BTC, "")
if err != nil {
t.Error("Test Failed - GetDepositAddress error", err)
}
} else {
_, err := b.GetDepositAddress(currency.BTC, "")
if err == nil {
t.Error("Test Failed - GetDepositAddress error cannot be nil")
}
t.Parallel()
_, err := b.GetDepositAddress(currency.BTC, "")
switch {
case areTestAPIKeysSet() && customerID != "" && err != nil && !mockTests:
t.Error("Test Failed - GetDepositAddress error", err)
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Test Failed - GetDepositAddress error cannot be nil")
case mockTests && err != nil:
t.Error("Test Failed - GetDepositAddress error", err)
}
}

View File

@@ -77,7 +77,7 @@ type Balances struct {
// UserTransactions holds user transaction information
type UserTransactions struct {
Date int64 `json:"datetime"`
Date string `json:"datetime"`
TransID int64 `json:"id"`
Type int `json:"type,string"`
USD float64 `json:"usd"`
@@ -125,15 +125,15 @@ type WithdrawalRequests struct {
// CryptoWithdrawalResponse response from a crypto withdrawal request
type CryptoWithdrawalResponse struct {
ID string `json:"id"`
Error string `json:"error"`
ID string `json:"id"`
Error map[string][]string `json:"error"`
}
// FIATWithdrawalResponse response from a fiat withdrawal request
type FIATWithdrawalResponse struct {
ID string `json:"id"`
Status string `json:"status"`
Reason string `json:"reason"`
ID string `json:"id"`
Status string `json:"status"`
Reason map[string][]string `json:"reason"`
}
// UnconfirmedBTCTransactions holds address information about unconfirmed

View File

@@ -262,8 +262,12 @@ func (b *Bitstamp) WithdrawCryptocurrencyFunds(withdrawRequest *exchange.Withdra
if err != nil {
return "", err
}
if resp.Error != "" {
return "", errors.New(resp.Error)
if len(resp.Error) != 0 {
var details string
for _, v := range resp.Error {
details += strings.Join(v, "")
}
return "", errors.New(details)
}
return resp.ID, nil
@@ -280,7 +284,11 @@ func (b *Bitstamp) WithdrawFiatFunds(withdrawRequest *exchange.WithdrawRequest)
return "", err
}
if resp.Status == errStr {
return "", errors.New(resp.Reason)
var details string
for _, v := range resp.Reason {
details += strings.Join(v, "")
}
return "", errors.New(details)
}
return resp.ID, nil
@@ -299,7 +307,11 @@ func (b *Bitstamp) WithdrawFiatFundsToInternationalBank(withdrawRequest *exchang
return "", err
}
if resp.Status == errStr {
return "", errors.New(resp.Reason)
var details string
for _, v := range resp.Reason {
details += strings.Join(v, "")
}
return "", errors.New(details)
}
return resp.ID, nil
@@ -387,7 +399,11 @@ func (b *Bitstamp) GetOrderHistory(getOrdersRequest *exchange.GetOrdersRequest)
quoteCurrency.String(),
b.ConfigCurrencyPairFormat.Delimiter)
}
orderDate := time.Unix(order.Date, 0)
orderDate, err := time.Parse("2006-01-02 15:04:05", order.Date)
if err != nil {
return nil, err
}
orders = append(orders, exchange.OrderDetail{
ID: fmt.Sprintf("%v", order.OrderID),

View File

@@ -495,7 +495,16 @@ func (b *Bittrex) GetDepositHistory(currency string) (WithdrawalHistory, error)
// SendHTTPRequest sends an unauthenticated HTTP request
func (b *Bittrex) SendHTTPRequest(path string, result interface{}) error {
return b.SendPayload(http.MethodGet, path, nil, nil, result, false, false, b.Verbose, b.HTTPDebugging)
return b.SendPayload(http.MethodGet,
path,
nil,
nil,
result,
false,
false,
b.Verbose,
b.HTTPDebugging,
b.HTTPRecording)
}
// SendAuthenticatedHTTPRequest sends an authenticated http request to a desired
@@ -516,7 +525,16 @@ func (b *Bittrex) SendAuthenticatedHTTPRequest(path string, values url.Values, r
headers := make(map[string]string)
headers["apisign"] = common.HexEncodeToString(hmac)
return b.SendPayload(http.MethodGet, rawQuery, headers, nil, result, true, true, b.Verbose, b.HTTPDebugging)
return b.SendPayload(http.MethodGet,
rawQuery,
headers,
nil,
result,
true,
true,
b.Verbose,
b.HTTPDebugging,
b.HTTPRecording)
}
// GetFee returns an estimate of fee based on type of transaction

View File

@@ -433,7 +433,16 @@ func (b *BTCMarkets) WithdrawAUD(accountName, accountNumber, bankName, bsbNumber
// SendHTTPRequest sends an unauthenticated HTTP request
func (b *BTCMarkets) SendHTTPRequest(path string, result interface{}) error {
return b.SendPayload(http.MethodGet, path, nil, nil, result, false, false, b.Verbose, b.HTTPDebugging)
return b.SendPayload(http.MethodGet,
path,
nil,
nil,
result,
false,
false,
b.Verbose,
b.HTTPDebugging,
b.HTTPRecording)
}
// SendAuthenticatedRequest sends an authenticated HTTP request
@@ -484,7 +493,8 @@ func (b *BTCMarkets) SendAuthenticatedRequest(reqType, path string, data, result
true,
true,
b.Verbose,
b.HTTPDebugging)
b.HTTPDebugging,
b.HTTPRecording)
}
// GetFee returns an estimate of fee based on type of transaction

View File

@@ -275,8 +275,16 @@ func (b *BTSE) GetFills(orderID, productID, before, after, limit string) (*Fille
// SendHTTPRequest sends an HTTP request to the desired endpoint
func (b *BTSE) SendHTTPRequest(method, endpoint string, result interface{}) error {
p := fmt.Sprintf("%s/%s", btseAPIURL, endpoint)
return b.SendPayload(method, p, nil, nil, &result, false, false, b.Verbose, b.HTTPDebugging)
return b.SendPayload(method,
fmt.Sprintf("%s/%s", btseAPIURL, endpoint),
nil,
nil,
&result,
false,
false,
b.Verbose,
b.HTTPDebugging,
b.HTTPRecording)
}
// SendAuthenticatedHTTPRequest sends an authenticated HTTP request to the desired endpoint
@@ -301,8 +309,16 @@ func (b *BTSE) SendAuthenticatedHTTPRequest(method, endpoint string, req map[str
if b.Verbose {
log.Debugf("Sending %s request to URL %s with params %s\n", method, p, string(payload))
}
return b.SendPayload(method, p, headers, strings.NewReader(string(payload)),
&result, true, false, b.Verbose, b.HTTPDebugging)
return b.SendPayload(method,
p,
headers,
strings.NewReader(string(payload)),
&result,
true,
false,
b.Verbose,
b.HTTPDebugging,
b.HTTPRecording)
}
// GetFee returns an estimate of fee based on type of transaction

View File

@@ -820,7 +820,16 @@ func (c *CoinbasePro) GetTrailingVolume() ([]Volume, error) {
// SendHTTPRequest sends an unauthenticated HTTP request
func (c *CoinbasePro) SendHTTPRequest(path string, result interface{}) error {
return c.SendPayload(http.MethodGet, path, nil, nil, result, false, false, c.Verbose, c.HTTPDebugging)
return c.SendPayload(http.MethodGet,
path,
nil,
nil,
result,
false,
false,
c.Verbose,
c.HTTPDebugging,
c.HTTPRecording)
}
// SendAuthenticatedHTTPRequest sends an authenticated HTTP reque
@@ -861,7 +870,8 @@ func (c *CoinbasePro) SendAuthenticatedHTTPRequest(method, path string, params m
true,
true,
c.Verbose,
c.HTTPDebugging)
c.HTTPDebugging,
c.HTTPRecording)
}
// GetFee returns an estimate of fee based on type of transaction

View File

@@ -405,7 +405,7 @@ func (c *COINUT) SendHTTPRequest(apiRequest string, params map[string]interface{
true,
c.Verbose,
c.HTTPDebugging,
)
c.HTTPRecording)
if err != nil {
return err
}

View File

@@ -286,6 +286,7 @@ type Base struct {
HTTPTimeout time.Duration
HTTPUserAgent string
HTTPDebugging bool
HTTPRecording bool
WebsocketURL string
APIUrl string
APIUrlDefault string

View File

@@ -373,7 +373,16 @@ func (e *EXMO) GetWalletHistory(date int64) (WalletHistory, error) {
// SendHTTPRequest sends an unauthenticated HTTP request
func (e *EXMO) SendHTTPRequest(path string, result interface{}) error {
return e.SendPayload(http.MethodGet, path, nil, nil, result, false, false, e.Verbose, e.HTTPDebugging)
return e.SendPayload(http.MethodGet,
path,
nil,
nil,
result,
false,
false,
e.Verbose,
e.HTTPDebugging,
e.HTTPRecording)
}
// SendAuthenticatedHTTPRequest sends an authenticated HTTP request
@@ -413,7 +422,8 @@ func (e *EXMO) SendAuthenticatedHTTPRequest(method, endpoint string, vals url.Va
true,
true,
e.Verbose,
e.HTTPDebugging)
e.HTTPDebugging,
e.HTTPRecording)
}
// GetFee returns an estimate of fee based on type of transaction

View File

@@ -421,7 +421,16 @@ func (g *Gateio) CancelExistingOrder(orderID int64, symbol string) (bool, error)
// SendHTTPRequest sends an unauthenticated HTTP request
func (g *Gateio) SendHTTPRequest(path string, result interface{}) error {
return g.SendPayload(http.MethodGet, path, nil, nil, result, false, false, g.Verbose, g.HTTPDebugging)
return g.SendPayload(http.MethodGet,
path,
nil,
nil,
result,
false,
false,
g.Verbose,
g.HTTPDebugging,
g.HTTPRecording)
}
// CancelAllExistingOrders all orders for a given symbol and side
@@ -521,7 +530,8 @@ func (g *Gateio) SendAuthenticatedHTTPRequest(method, endpoint, param string, re
true,
false,
g.Verbose,
g.HTTPDebugging)
g.HTTPDebugging,
g.HTTPRecording)
if err != nil {
return err
}

View File

@@ -6,7 +6,6 @@ import (
"net/http"
"net/url"
"strconv"
"strings"
"time"
"github.com/thrasher-corp/gocryptotrader/common"
@@ -56,11 +55,6 @@ const (
geminiRoleFundManager = "fundmanager"
)
var (
// Session manager
Session map[int]*Gemini
)
// Gemini is the overarching type across the Gemini package, create multiple
// instances with differing APIkeys for segregation of roles for authenticated
// requests & sessions by appending new sessions to the Session map using
@@ -73,32 +67,6 @@ type Gemini struct {
RequiresHeartBeat bool
}
// AddSession adds a new session to the gemini base
func AddSession(g *Gemini, sessionID int, apiKey, apiSecret, role string, needsHeartbeat, isSandbox bool) error {
if Session == nil {
Session = make(map[int]*Gemini)
}
_, ok := Session[sessionID]
if ok {
return errors.New("sessionID already being used")
}
g.APIKey = apiKey
g.APISecret = apiSecret
g.Role = role
g.RequiresHeartBeat = needsHeartbeat
g.APIUrl = geminiAPIURL
if isSandbox {
g.APIUrl = geminiSandboxAPIURL
}
Session[sessionID] = g
return nil
}
// SetDefaults sets package defaults for gemini exchange
func (g *Gemini) SetDefaults() {
g.Name = "Gemini"
@@ -259,9 +227,15 @@ func (g *Gemini) GetTicker(currencyPair string) (Ticker, error) {
// params - limit_bids or limit_asks [OPTIONAL] default 50, 0 returns all Values
// Type is an integer ie "params.Set("limit_asks", 30)"
func (g *Gemini) GetOrderbook(currencyPair string, params url.Values) (Orderbook, error) {
path := common.EncodeURLValues(fmt.Sprintf("%s/v%s/%s/%s", g.APIUrl, geminiAPIVersion, geminiOrderbook, currencyPair), params)
orderbook := Orderbook{}
path := common.EncodeURLValues(
fmt.Sprintf("%s/v%s/%s/%s",
g.APIUrl,
geminiAPIVersion,
geminiOrderbook,
currencyPair),
params)
var orderbook Orderbook
return orderbook, g.SendHTTPRequest(path, &orderbook)
}
@@ -307,20 +281,9 @@ func (g *Gemini) GetAuctionHistory(currencyPair string, params url.Values) ([]Au
return auctionHist, g.SendHTTPRequest(path, &auctionHist)
}
func (g *Gemini) isCorrectSession() error {
if g.Role != geminiRoleTrader {
return errors.New("incorrect role for APIKEY cannot use this function")
}
return nil
}
// NewOrder Only limit orders are supported through the API at present.
// returns order ID if successful
func (g *Gemini) NewOrder(symbol string, amount, price float64, side, orderType string) (int64, error) {
if err := g.isCorrectSession(); err != nil {
return 0, err
}
req := make(map[string]interface{})
req["symbol"] = symbol
req["amount"] = strconv.FormatFloat(amount, 'f', -1, 64)
@@ -405,6 +368,7 @@ func (g *Gemini) GetOrders() ([]Order, error) {
if err != nil {
return nil, err
}
switch r := response.(type) {
case orders:
return r.orders, nil
@@ -422,7 +386,7 @@ func (g *Gemini) GetTradeHistory(currencyPair string, timestamp int64) ([]TradeH
req := make(map[string]interface{})
req["symbol"] = currencyPair
if timestamp != 0 {
if timestamp > 0 {
req["timestamp"] = timestamp
}
@@ -511,7 +475,16 @@ func (g *Gemini) PostHeartbeat() (string, error) {
// SendHTTPRequest sends an unauthenticated request
func (g *Gemini) SendHTTPRequest(path string, result interface{}) error {
return g.SendPayload(http.MethodGet, path, nil, nil, result, false, false, g.Verbose, g.HTTPDebugging)
return g.SendPayload(http.MethodGet,
path,
nil,
nil,
result,
false,
false,
g.Verbose,
g.HTTPDebugging,
g.HTTPRecording)
}
// SendAuthenticatedHTTPRequest sends an authenticated HTTP request to the
@@ -521,7 +494,6 @@ func (g *Gemini) SendAuthenticatedHTTPRequest(method, path string, params map[st
return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, g.Name)
}
headers := make(map[string]string)
req := make(map[string]interface{})
req["request"] = fmt.Sprintf("/v%s/%s", geminiAPIVersion, path)
req["nonce"] = g.Requester.GetNonce(true).String()
@@ -542,6 +514,7 @@ func (g *Gemini) SendAuthenticatedHTTPRequest(method, path string, params map[st
PayloadBase64 := common.Base64Encode(PayloadJSON)
hmac := common.GetHMAC(common.HashSHA512_384, []byte(PayloadBase64), []byte(g.APISecret))
headers := make(map[string]string)
headers["Content-Length"] = "0"
headers["Content-Type"] = "text/plain"
headers["X-GEMINI-APIKEY"] = g.APIKey
@@ -549,7 +522,16 @@ func (g *Gemini) SendAuthenticatedHTTPRequest(method, path string, params map[st
headers["X-GEMINI-SIGNATURE"] = common.HexEncodeToString(hmac)
headers["Cache-Control"] = "no-cache"
return g.SendPayload(method, g.APIUrl+"/v1/"+path, headers, strings.NewReader(""), result, true, false, g.Verbose, g.HTTPDebugging)
return g.SendPayload(method,
g.APIUrl+"/v1/"+path,
headers,
nil,
result,
true,
false,
g.Verbose,
g.HTTPDebugging,
g.HTTPRecording)
}
// GetFee returns an estimate of fee based on type of transaction
@@ -585,9 +567,9 @@ func getOfflineTradeFee(price, amount float64) float64 {
func calculateTradingFee(notionVolume *NotionalVolume, purchasePrice, amount float64, isMaker bool) float64 {
var volumeFee float64
if isMaker {
volumeFee = (float64(notionVolume.MakerFee) / 100)
volumeFee = (float64(notionVolume.APIMakerFeeBPS) / 10000)
} else {
volumeFee = (float64(notionVolume.TakerFee) / 100)
volumeFee = (float64(notionVolume.APITakerFeeBPS) / 10000)
}
return volumeFee * amount * purchasePrice

View File

@@ -0,0 +1,33 @@
//+build mock_test_off
// This will build if build tag mock_test_off is parsed and will do live testing
// using all tests in (exchange)_test.go
package gemini
import (
"log"
"os"
"testing"
"github.com/thrasher-corp/gocryptotrader/config"
"github.com/thrasher-corp/gocryptotrader/exchanges/sharedtestvalues"
)
var mockTests = false
func TestMain(m *testing.M) {
cfg := config.GetConfig()
cfg.LoadConfig("../../testdata/configtest.json")
geminiConfig, err := cfg.GetExchangeConfig("Gemini")
if err != nil {
log.Fatal("Test Failed - Gemini Setup() init error", err)
}
geminiConfig.AuthenticatedAPISupport = true
geminiConfig.APIKey = apiKey
geminiConfig.APISecret = apiSecret
g.SetDefaults()
g.Setup(&geminiConfig)
g.APIUrl = geminiSandboxAPIURL
log.Printf(sharedtestvalues.LiveTesting, g.GetName(), g.APIUrl)
os.Exit(m.Run())
}

View File

@@ -0,0 +1,44 @@
//+build !mock_test_off
// This will build if build tag mock_test_off is not parsed and will try to mock
// all tests in _test.go
package gemini
import (
"log"
"os"
"testing"
"github.com/thrasher-corp/gocryptotrader/config"
"github.com/thrasher-corp/gocryptotrader/exchanges/mock"
"github.com/thrasher-corp/gocryptotrader/exchanges/sharedtestvalues"
)
const mockFile = "../../testdata/http_mock/gemini/gemini.json"
var mockTests = true
func TestMain(m *testing.M) {
cfg := config.GetConfig()
cfg.LoadConfig("../../testdata/configtest.json")
geminiConfig, err := cfg.GetExchangeConfig("Gemini")
if err != nil {
log.Fatal("Test Failed - Mock server error", err)
}
geminiConfig.AuthenticatedAPISupport = true
geminiConfig.APIKey = apiKey
geminiConfig.APISecret = apiSecret
g.SetDefaults()
g.Setup(&geminiConfig)
serverDetails, newClient, err := mock.NewVCRServer(mockFile)
if err != nil {
log.Fatalf("Test Failed - Mock server error %s", err)
}
g.HTTPClient = newClient
g.APIUrl = serverDetails
log.Printf(sharedtestvalues.MockTesting, g.GetName(), g.APIUrl)
os.Exit(m.Run())
}

View File

@@ -7,7 +7,6 @@ import (
"github.com/gorilla/websocket"
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/config"
"github.com/thrasher-corp/gocryptotrader/currency"
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
"github.com/thrasher-corp/gocryptotrader/exchanges/sharedtestvalues"
@@ -15,75 +14,21 @@ import (
)
// Please enter sandbox API keys & assigned roles for better testing procedures
const (
apiKey1 = ""
apiSecret1 = ""
apiKeyRole1 = ""
sessionHeartBeat1 = false
apiKey2 = ""
apiSecret2 = ""
apiKeyRole2 = ""
sessionHeartBeat2 = false
apiKey = ""
apiSecret = ""
apiKeyRole = ""
sessionHeartBeat = false
canManipulateRealOrders = false
)
func TestAddSession(t *testing.T) {
var g1 Gemini
if Session[1] == nil {
err := AddSession(&g1, 1, apiKey1, apiSecret1, apiKeyRole1, true, true)
if err != nil {
t.Error("Test failed - AddSession() error", err)
}
err = AddSession(&g1, 1, apiKey1, apiSecret1, apiKeyRole1, true, true)
if err == nil {
t.Error("Test failed - AddSession() error", err)
}
}
const testCurrency = "btcusd"
if len(Session) <= 1 {
var g2 Gemini
err := AddSession(&g2, 2, apiKey2, apiSecret2, apiKeyRole2, false, true)
if err != nil {
t.Error("Test failed - AddSession() error", err)
}
}
}
func TestSetDefaults(t *testing.T) {
Session[1].SetDefaults()
Session[2].SetDefaults()
}
func TestSetup(t *testing.T) {
cfg := config.GetConfig()
cfg.LoadConfig("../../testdata/configtest.json")
geminiConfig, err := cfg.GetExchangeConfig("Gemini")
if err != nil {
t.Error("Test Failed - Gemini Setup() init error")
}
geminiConfig.AuthenticatedWebsocketAPISupport = true
geminiConfig.AuthenticatedAPISupport = true
geminiConfig.Websocket = true
Session[1].Setup(&geminiConfig)
Session[2].Setup(&geminiConfig)
Session[1].APIKey = apiKey1
Session[1].APISecret = apiSecret1
Session[2].APIKey = apiKey2
Session[2].APISecret = apiSecret2
Session[1].APIUrl = geminiSandboxAPIURL
Session[2].APIUrl = geminiSandboxAPIURL
}
var g Gemini
func TestGetSymbols(t *testing.T) {
t.Parallel()
_, err := Session[1].GetSymbols()
_, err := g.GetSymbols()
if err != nil {
t.Error("Test Failed - GetSymbols() error", err)
}
@@ -91,11 +36,11 @@ func TestGetSymbols(t *testing.T) {
func TestGetTicker(t *testing.T) {
t.Parallel()
_, err := Session[2].GetTicker("BTCUSD")
_, err := g.GetTicker("BTCUSD")
if err != nil {
t.Error("Test Failed - GetTicker() error", err)
}
_, err = Session[1].GetTicker("bla")
_, err = g.GetTicker("bla")
if err == nil {
t.Error("Test Failed - GetTicker() error", err)
}
@@ -103,7 +48,7 @@ func TestGetTicker(t *testing.T) {
func TestGetOrderbook(t *testing.T) {
t.Parallel()
_, err := Session[1].GetOrderbook("btcusd", url.Values{})
_, err := g.GetOrderbook(testCurrency, url.Values{})
if err != nil {
t.Error("Test Failed - GetOrderbook() error", err)
}
@@ -111,25 +56,25 @@ func TestGetOrderbook(t *testing.T) {
func TestGetTrades(t *testing.T) {
t.Parallel()
_, err := Session[2].GetTrades("btcusd", url.Values{})
_, err := g.GetTrades(testCurrency, url.Values{})
if err != nil {
t.Error("Test Failed - GetTrades() error", err)
}
}
func TestGetNotionalVolume(t *testing.T) {
if apiKey2 != "" && apiSecret2 != "" {
t.Parallel()
_, err := Session[2].GetNotionalVolume()
if err != nil {
t.Error("Test Failed - GetNotionalVolume() error", err)
}
t.Parallel()
_, err := g.GetNotionalVolume()
if err != nil && mockTests {
t.Error("Test Failed - GetNotionalVolume() error", err)
} else if err == nil && !mockTests {
t.Error("Test Failed - GetNotionalVolume() error cannot be nil")
}
}
func TestGetAuction(t *testing.T) {
t.Parallel()
_, err := Session[1].GetAuction("btcusd")
_, err := g.GetAuction(testCurrency)
if err != nil {
t.Error("Test Failed - GetAuction() error", err)
}
@@ -137,7 +82,7 @@ func TestGetAuction(t *testing.T) {
func TestGetAuctionHistory(t *testing.T) {
t.Parallel()
_, err := Session[2].GetAuctionHistory("btcusd", url.Values{})
_, err := g.GetAuctionHistory(testCurrency, url.Values{})
if err != nil {
t.Error("Test Failed - GetAuctionHistory() error", err)
}
@@ -145,79 +90,87 @@ func TestGetAuctionHistory(t *testing.T) {
func TestNewOrder(t *testing.T) {
t.Parallel()
_, err := Session[1].NewOrder("btcusd", 1, 4500, "buy", "exchange limit")
if err == nil {
t.Error("Test Failed - NewOrder() error", err)
}
_, err = Session[2].NewOrder("btcusd", 1, 4500, "buy", "exchange limit")
if err == nil {
_, err := g.NewOrder(testCurrency, 1, 9000, "buy", "exchange limit")
if err != nil && mockTests {
t.Error("Test Failed - NewOrder() error", err)
} else if err == nil && !mockTests {
t.Error("Test Failed - NewOrder() error cannot be nil")
}
}
func TestCancelExistingOrder(t *testing.T) {
t.Parallel()
_, err := Session[1].CancelExistingOrder(1337)
if err == nil {
_, err := g.CancelExistingOrder(265555413)
if err != nil && mockTests {
t.Error("Test Failed - CancelExistingOrder() error", err)
} else if err == nil && !mockTests {
t.Error("Test Failed - CancelExistingOrder() error cannot be nil")
}
}
func TestCancelExistingOrders(t *testing.T) {
t.Parallel()
_, err := Session[1].CancelExistingOrders(false)
if err == nil {
t.Error("Test Failed - CancelExistingOrders() error", err)
}
_, err = Session[2].CancelExistingOrders(true)
if err == nil {
_, err := g.CancelExistingOrders(false)
if err != nil && mockTests {
t.Error("Test Failed - CancelExistingOrders() error", err)
} else if err == nil && !mockTests {
t.Error("Test Failed - CancelExistingOrders() error cannot be nil")
}
}
func TestGetOrderStatus(t *testing.T) {
t.Parallel()
_, err := Session[2].GetOrderStatus(1337)
if err == nil {
_, err := g.GetOrderStatus(265563260)
if err != nil && mockTests {
t.Error("Test Failed - GetOrderStatus() error", err)
} else if err == nil && !mockTests {
t.Error("Test Failed - GetOrderStatus() error cannot be nil")
}
}
func TestGetOrders(t *testing.T) {
t.Parallel()
_, err := Session[1].GetOrders()
if err == nil {
_, err := g.GetOrders()
if err != nil && mockTests {
t.Error("Test Failed - GetOrders() error", err)
} else if err == nil && !mockTests {
t.Error("Test Failed - GetOrders() error cannot be nil")
}
}
func TestGetTradeHistory(t *testing.T) {
t.Parallel()
_, err := Session[1].GetTradeHistory("btcusd", 0)
if err == nil {
_, err := g.GetTradeHistory(testCurrency, 0)
if err != nil && mockTests {
t.Error("Test Failed - GetTradeHistory() error", err)
} else if err == nil && !mockTests {
t.Error("Test Failed - GetTradeHistory() error cannot be nil")
}
}
func TestGetTradeVolume(t *testing.T) {
t.Parallel()
_, err := Session[2].GetTradeVolume()
if err == nil {
_, err := g.GetTradeVolume()
if err != nil && mockTests {
t.Error("Test Failed - GetTradeVolume() error", err)
} else if err == nil && !mockTests {
t.Error("Test Failed - GetTradeVolume() error cannot be nil")
}
}
func TestGetBalances(t *testing.T) {
t.Parallel()
_, err := Session[1].GetBalances()
if err == nil {
_, err := g.GetBalances()
if err != nil && mockTests {
t.Error("Test Failed - GetBalances() error", err)
} else if err == nil && !mockTests {
t.Error("Test Failed - GetBalances() error cannot be nil")
}
}
func TestGetCryptoDepositAddress(t *testing.T) {
t.Parallel()
_, err := Session[1].GetCryptoDepositAddress("LOL123", "btc")
_, err := g.GetCryptoDepositAddress("LOL123", "btc")
if err == nil {
t.Error("Test Failed - GetCryptoDepositAddress() error", err)
}
@@ -225,7 +178,7 @@ func TestGetCryptoDepositAddress(t *testing.T) {
func TestWithdrawCrypto(t *testing.T) {
t.Parallel()
_, err := Session[1].WithdrawCrypto("LOL123", "btc", 1)
_, err := g.WithdrawCrypto("LOL123", "btc", 1)
if err == nil {
t.Error("Test Failed - WithdrawCrypto() error", err)
}
@@ -233,9 +186,11 @@ func TestWithdrawCrypto(t *testing.T) {
func TestPostHeartbeat(t *testing.T) {
t.Parallel()
_, err := Session[2].PostHeartbeat()
if err == nil {
_, err := g.PostHeartbeat()
if err != nil && mockTests {
t.Error("Test Failed - PostHeartbeat() error", err)
} else if err == nil && !mockTests {
t.Error("Test Failed - PostHeartbeat() error cannot be nil")
}
}
@@ -254,61 +209,75 @@ func setFeeBuilder() *exchange.FeeBuilder {
// TestGetFeeByTypeOfflineTradeFee logic test
func TestGetFeeByTypeOfflineTradeFee(t *testing.T) {
TestAddSession(t)
TestSetDefaults(t)
TestSetup(t)
t.Parallel()
var feeBuilder = setFeeBuilder()
Session[1].GetFeeByType(feeBuilder)
if apiKey1 == "" || apiSecret1 == "" {
g.GetFeeByType(feeBuilder)
if !areTestAPIKeysSet() {
if feeBuilder.FeeType != exchange.OfflineTradeFee {
t.Errorf("Expected %v, received %v", exchange.OfflineTradeFee, feeBuilder.FeeType)
t.Errorf("Expected %v, received %v",
exchange.OfflineTradeFee,
feeBuilder.FeeType)
}
} else {
if feeBuilder.FeeType != exchange.CryptocurrencyTradeFee {
t.Errorf("Expected %v, received %v", exchange.CryptocurrencyTradeFee, feeBuilder.FeeType)
t.Errorf("Expected %v, received %v",
exchange.CryptocurrencyTradeFee,
feeBuilder.FeeType)
}
}
}
func TestGetFee(t *testing.T) {
t.Parallel()
var feeBuilder = setFeeBuilder()
if apiKey1 != "" && apiSecret1 != "" {
if areTestAPIKeysSet() || mockTests {
// CryptocurrencyTradeFee Basic
if resp, err := Session[1].GetFee(feeBuilder); resp != float64(0.01) || err != nil {
if resp, err := g.GetFee(feeBuilder); resp != float64(0.0035) || err != nil {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f",
float64(0.0035),
resp)
t.Error(err)
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f", float64(0.01), resp)
}
// CryptocurrencyTradeFee High quantity
feeBuilder = setFeeBuilder()
feeBuilder.Amount = 1000
feeBuilder.PurchasePrice = 1000
if resp, err := Session[1].GetFee(feeBuilder); resp != float64(100) || err != nil {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f", float64(100), resp)
if resp, err := g.GetFee(feeBuilder); resp != float64(3500) || err != nil {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f",
float64(3500),
resp)
t.Error(err)
}
// CryptocurrencyTradeFee IsMaker
feeBuilder = setFeeBuilder()
feeBuilder.IsMaker = true
if resp, err := Session[1].GetFee(feeBuilder); resp != float64(0.01) || err != nil {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f", float64(0.01), resp)
if resp, err := g.GetFee(feeBuilder); resp != float64(0.001) || err != nil {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f",
float64(0.001),
resp)
t.Error(err)
}
// CryptocurrencyTradeFee Negative purchase price
feeBuilder = setFeeBuilder()
feeBuilder.PurchasePrice = -1000
if resp, err := Session[1].GetFee(feeBuilder); resp != float64(0) || err != nil {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f", float64(0), resp)
if resp, err := g.GetFee(feeBuilder); resp != float64(0) || err != nil {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f",
float64(0),
resp)
t.Error(err)
}
}
// CryptocurrencyWithdrawalFee Basic
feeBuilder = setFeeBuilder()
feeBuilder.FeeType = exchange.CryptocurrencyWithdrawalFee
if resp, err := Session[1].GetFee(feeBuilder); resp != float64(0) || err != nil {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f", float64(0), resp)
if resp, err := g.GetFee(feeBuilder); resp != float64(0) || err != nil {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f",
float64(0),
resp)
t.Error(err)
}
@@ -316,24 +285,30 @@ func TestGetFee(t *testing.T) {
feeBuilder = setFeeBuilder()
feeBuilder.Pair.Base = currency.NewCode("hello")
feeBuilder.FeeType = exchange.CryptocurrencyWithdrawalFee
if resp, err := Session[1].GetFee(feeBuilder); resp != float64(0) || err != nil {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f", float64(0), resp)
if resp, err := g.GetFee(feeBuilder); resp != float64(0) || err != nil {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f",
float64(0),
resp)
t.Error(err)
}
// CyptocurrencyDepositFee Basic
feeBuilder = setFeeBuilder()
feeBuilder.FeeType = exchange.CyptocurrencyDepositFee
if resp, err := Session[1].GetFee(feeBuilder); resp != float64(0) || err != nil {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f", float64(0), resp)
if resp, err := g.GetFee(feeBuilder); resp != float64(0) || err != nil {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f",
float64(0),
resp)
t.Error(err)
}
// InternationalBankDepositFee Basic
feeBuilder = setFeeBuilder()
feeBuilder.FeeType = exchange.InternationalBankDepositFee
if resp, err := Session[1].GetFee(feeBuilder); resp != float64(0) || err != nil {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f", float64(0), resp)
if resp, err := g.GetFee(feeBuilder); resp != float64(0) || err != nil {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f",
float64(0),
resp)
t.Error(err)
}
@@ -341,78 +316,82 @@ func TestGetFee(t *testing.T) {
feeBuilder = setFeeBuilder()
feeBuilder.FeeType = exchange.InternationalBankWithdrawalFee
feeBuilder.FiatCurrency = currency.USD
if resp, err := Session[1].GetFee(feeBuilder); resp != float64(0) || err != nil {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f", float64(0), resp)
if resp, err := g.GetFee(feeBuilder); resp != float64(0) || err != nil {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f",
float64(0),
resp)
t.Error(err)
}
}
func TestFormatWithdrawPermissions(t *testing.T) {
TestAddSession(t)
TestSetDefaults(t)
TestSetup(t)
t.Parallel()
expectedResult := exchange.AutoWithdrawCryptoWithAPIPermissionText +
" & " +
exchange.AutoWithdrawCryptoWithSetupText +
" & " +
exchange.WithdrawFiatViaWebsiteOnlyText
expectedResult := exchange.AutoWithdrawCryptoWithAPIPermissionText + " & " + exchange.AutoWithdrawCryptoWithSetupText + " & " + exchange.WithdrawFiatViaWebsiteOnlyText
withdrawPermissions := Session[1].FormatWithdrawPermissions()
withdrawPermissions := g.FormatWithdrawPermissions()
if withdrawPermissions != expectedResult {
t.Errorf("Expected: %s, Received: %s", expectedResult, withdrawPermissions)
t.Errorf("Expected: %s, Received: %s",
expectedResult,
withdrawPermissions)
}
}
func TestGetActiveOrders(t *testing.T) {
TestAddSession(t)
TestSetDefaults(t)
TestSetup(t)
t.Parallel()
var getOrdersRequest = exchange.GetOrdersRequest{
OrderType: exchange.AnyOrderType,
Currencies: []currency.Pair{currency.NewPair(currency.LTC, currency.BTC)},
OrderType: exchange.AnyOrderType,
Currencies: []currency.Pair{
currency.NewPair(currency.LTC, currency.BTC),
},
}
_, err := Session[1].GetActiveOrders(&getOrdersRequest)
if areTestAPIKeysSet() && err != nil {
_, err := g.GetActiveOrders(&getOrdersRequest)
switch {
case areTestAPIKeysSet() && err != nil && !mockTests:
t.Errorf("Could not get open orders: %s", err)
} else if !areTestAPIKeysSet() && err == nil {
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Expecting an error when no keys are set")
case mockTests && err != nil:
t.Errorf("Could not get open orders: %s", err)
}
}
func TestGetOrderHistory(t *testing.T) {
TestAddSession(t)
TestSetDefaults(t)
TestSetup(t)
t.Parallel()
var getOrdersRequest = exchange.GetOrdersRequest{
OrderType: exchange.AnyOrderType,
Currencies: []currency.Pair{currency.NewPair(currency.LTC, currency.BTC)},
}
_, err := Session[1].GetOrderHistory(&getOrdersRequest)
if areTestAPIKeysSet() && err != nil {
_, err := g.GetOrderHistory(&getOrdersRequest)
switch {
case areTestAPIKeysSet() && err != nil:
t.Errorf("Could not get order history: %s", err)
} else if !areTestAPIKeysSet() && err == nil {
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Expecting an error when no keys are set")
case err != nil && mockTests:
t.Errorf("Could not get order history: %s", err)
}
}
// Any tests below this line have the ability to impact your orders on the exchange. Enable canManipulateRealOrders to run them
// ----------------------------------------------------------------------------------------------------------------------------
func areTestAPIKeysSet() bool {
if Session[1].APIKey != "" && Session[1].APIKey != "Key" &&
Session[1].APISecret != "" && Session[1].APISecret != "Secret" {
if g.APIKey != "" && g.APIKey != "Key" &&
g.APISecret != "" && g.APISecret != "Secret" {
return true
}
return false
}
func TestSubmitOrder(t *testing.T) {
TestAddSession(t)
TestSetDefaults(t)
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Parallel()
if areTestAPIKeysSet() && !canManipulateRealOrders && !mockTests {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -421,47 +400,47 @@ func TestSubmitOrder(t *testing.T) {
Base: currency.LTC,
Quote: currency.BTC,
}
response, err := Session[1].SubmitOrder(p, exchange.BuyOrderSide, exchange.MarketOrderType, 1, 10, "1234234")
if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) {
response, err := g.SubmitOrder(p,
exchange.BuyOrderSide,
exchange.LimitOrderType,
1,
10,
"1234234")
switch {
case areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced):
t.Errorf("Order failed to be placed: %v", err)
} else if !areTestAPIKeysSet() && err == nil {
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Expecting an error when no keys are set")
case mockTests && err != nil:
t.Errorf("Order failed to be placed: %v", err)
}
}
func TestCancelExchangeOrder(t *testing.T) {
TestAddSession(t)
TestSetDefaults(t)
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Parallel()
if areTestAPIKeysSet() && !canManipulateRealOrders && !mockTests {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
currencyPair := currency.NewPair(currency.LTC, currency.BTC)
var orderCancellation = &exchange.OrderCancellation{
OrderID: "1",
WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB",
AccountID: "1",
CurrencyPair: currencyPair,
OrderID: "266029865",
}
err := Session[1].CancelOrder(orderCancellation)
if !areTestAPIKeysSet() && err == nil {
err := g.CancelOrder(orderCancellation)
switch {
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Expecting an error when no keys are set")
}
if areTestAPIKeysSet() && err != nil {
case areTestAPIKeysSet() && err != nil:
t.Errorf("Could not cancel orders: %v", err)
case err != nil && mockTests:
t.Errorf("Could not cancel orders: %v", err)
}
}
func TestCancelAllExchangeOrders(t *testing.T) {
TestAddSession(t)
TestSetDefaults(t)
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Parallel()
if areTestAPIKeysSet() && !canManipulateRealOrders && !mockTests {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -474,12 +453,13 @@ func TestCancelAllExchangeOrders(t *testing.T) {
CurrencyPair: currencyPair,
}
resp, err := Session[1].CancelAllOrders(orderCancellation)
if !areTestAPIKeysSet() && err == nil {
resp, err := g.CancelAllOrders(orderCancellation)
switch {
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Expecting an error when no keys are set")
}
if areTestAPIKeysSet() && err != nil {
case areTestAPIKeysSet() && err != nil:
t.Errorf("Could not cancel orders: %v", err)
case mockTests && err != nil:
t.Errorf("Could not cancel orders: %v", err)
}
@@ -489,16 +469,15 @@ func TestCancelAllExchangeOrders(t *testing.T) {
}
func TestModifyOrder(t *testing.T) {
_, err := Session[1].ModifyOrder(&exchange.ModifyOrder{})
t.Parallel()
_, err := g.ModifyOrder(&exchange.ModifyOrder{})
if err == nil {
t.Error("Test failed - ModifyOrder() error")
}
}
func TestWithdraw(t *testing.T) {
TestAddSession(t)
TestSetDefaults(t)
TestSetup(t)
t.Parallel()
var withdrawCryptoRequest = exchange.WithdrawRequest{
Amount: 100,
Currency: currency.BTC,
@@ -506,55 +485,55 @@ func TestWithdraw(t *testing.T) {
Description: "WITHDRAW IT ALL",
}
if areTestAPIKeysSet() && !canManipulateRealOrders {
if areTestAPIKeysSet() && !canManipulateRealOrders && !mockTests {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
_, err := Session[1].WithdrawCryptocurrencyFunds(&withdrawCryptoRequest)
_, err := g.WithdrawCryptocurrencyFunds(&withdrawCryptoRequest)
if !areTestAPIKeysSet() && err == nil {
t.Error("Expecting an error when no keys are set")
}
if areTestAPIKeysSet() && err != nil {
if areTestAPIKeysSet() && err != nil && !mockTests {
t.Errorf("Withdraw failed to be placed: %v", err)
}
if areTestAPIKeysSet() && err == nil && mockTests {
t.Errorf("Withdraw failed to be placed: %v", err)
}
}
func TestWithdrawFiat(t *testing.T) {
TestAddSession(t)
TestSetDefaults(t)
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Parallel()
if areTestAPIKeysSet() && !canManipulateRealOrders && !mockTests {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
var withdrawFiatRequest = exchange.WithdrawRequest{}
_, err := Session[1].WithdrawFiatFunds(&withdrawFiatRequest)
_, err := g.WithdrawFiatFunds(&withdrawFiatRequest)
if err != common.ErrFunctionNotSupported {
t.Errorf("Expected '%v', received: '%v'", common.ErrFunctionNotSupported, err)
t.Errorf("Expected '%v', received: '%v'",
common.ErrFunctionNotSupported,
err)
}
}
func TestWithdrawInternationalBank(t *testing.T) {
TestAddSession(t)
TestSetDefaults(t)
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Parallel()
if areTestAPIKeysSet() && !canManipulateRealOrders && !mockTests {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
var withdrawFiatRequest = exchange.WithdrawRequest{}
_, err := Session[1].WithdrawFiatFundsToInternationalBank(&withdrawFiatRequest)
_, err := g.WithdrawFiatFundsToInternationalBank(&withdrawFiatRequest)
if err != common.ErrFunctionNotSupported {
t.Errorf("Expected '%v', received: '%v'", common.ErrFunctionNotSupported, err)
t.Errorf("Expected '%v', received: '%v'",
common.ErrFunctionNotSupported,
err)
}
}
func TestGetDepositAddress(t *testing.T) {
_, err := Session[1].GetDepositAddress(currency.BTC, "")
t.Parallel()
_, err := g.GetDepositAddress(currency.BTC, "")
if err == nil {
t.Error("Test Failed - GetDepositAddress error cannot be nil")
}
@@ -562,13 +541,12 @@ func TestGetDepositAddress(t *testing.T) {
// TestWsAuth dials websocket, sends login request.
func TestWsAuth(t *testing.T) {
TestAddSession(t)
TestSetDefaults(t)
TestSetup(t)
g := Session[1]
t.Parallel()
g.WebsocketURL = geminiWebsocketSandboxEndpoint
if !g.Websocket.IsEnabled() && !g.AuthenticatedWebsocketAPISupport || !areTestAPIKeysSet() {
if !g.Websocket.IsEnabled() &&
!g.AuthenticatedWebsocketAPISupport ||
!areTestAPIKeysSet() {
t.Skip(wshandler.WebsocketNotEnabled)
}
var dialer websocket.Dialer

View File

@@ -145,16 +145,23 @@ type TradeVolume struct {
SellTakerCount float64 `json:"sell_taker_count"`
}
// NotionalVolume api call for fees
// NotionalVolume api call for fees, all return fee amounts are in basis points
type NotionalVolume struct {
MakerFee int64 `json:"maker_fee_bps"`
TakerFee int64 `json:"taker_fee_bps"`
AuctionFee int64 `json:"auction_fee_bps"`
ThirtyDayVolume float64 `json:"notional_30d_volume"`
LastedUpdated int64 `json:"last_updated_ms"`
AccountID int64 `json:"account_id"`
Date string `json:"date"`
APIAuctionFeeBPS int64 `json:"api_auction_fee_bps"`
APIMakerFeeBPS int64 `json:"api_maker_fee_bps"`
APITakerFeeBPS int64 `json:"api_taker_fee_bps"`
BlockMakerFeeBPS int64 `json:"block_maker_fee_bps"`
BlockTakerFeeBPS int64 `json:"block_taker_fee_bps"`
FixAuctionFeeBPS int64 `json:"fix_auction_fee_bps"`
FixMakerFeeBPS int64 `json:"fix_maker_fee_bps"`
FixTakerFeeBPS int64 `json:"fix_taker_fee_bps"`
OneDayNotionalVolumes []OneDayNotionalVolume `json:"notional_1d_volume"`
ThirtyDayVolume float64 `json:"notional_30d_volume"`
WebAuctionFeeBPS int64 `json:"web_auction_fee_bps"`
WebMakerFeeBPS int64 `json:"web_maker_fee_bps"`
WebTakerFeeBPS int64 `json:"web_taker_fee_bps"`
LastedUpdated int64 `json:"last_updated_ms"`
Date string `json:"date"`
}
// OneDayNotionalVolume Contains the notioanl volume for a single day

View File

@@ -161,11 +161,17 @@ func (g *Gemini) GetExchangeHistory(p currency.Pair, assetType string) ([]exchan
// SubmitOrder submits a new order
func (g *Gemini) SubmitOrder(p currency.Pair, side exchange.OrderSide, orderType exchange.OrderType, amount, price float64, _ string) (exchange.SubmitOrderResponse, error) {
var submitOrderResponse exchange.SubmitOrderResponse
p = exchange.FormatExchangeCurrency(g.Name, p)
if orderType != exchange.LimitOrderType {
return submitOrderResponse, errors.New("only limit orders are enabled through this API")
}
response, err := g.NewOrder(p.String(),
amount,
price,
side.ToString(),
orderType.ToString())
"exchange limit")
if response > 0 {
submitOrderResponse.OrderID = fmt.Sprintf("%v", response)

View File

@@ -622,7 +622,16 @@ func (h *HitBTC) TransferBalance(currency, from, to string, amount float64) (boo
// SendHTTPRequest sends an unauthenticated HTTP request
func (h *HitBTC) SendHTTPRequest(path string, result interface{}) error {
return h.SendPayload(http.MethodGet, path, nil, nil, result, false, false, h.Verbose, h.HTTPDebugging)
return h.SendPayload(http.MethodGet,
path,
nil,
nil,
result,
false,
false,
h.Verbose,
h.HTTPDebugging,
h.HTTPRecording)
}
// SendAuthenticatedHTTPRequest sends an authenticated http request
@@ -644,7 +653,8 @@ func (h *HitBTC) SendAuthenticatedHTTPRequest(method, endpoint string, values ur
true,
false,
h.Verbose,
h.HTTPDebugging)
h.HTTPDebugging,
h.HTTPRecording)
}
// GetFee returns an estimate of fee based on type of transaction

View File

@@ -895,7 +895,16 @@ func (h *HUOBI) CancelWithdraw(withdrawID int64) (int64, error) {
// SendHTTPRequest sends an unauthenticated HTTP request
func (h *HUOBI) SendHTTPRequest(path string, result interface{}) error {
return h.SendPayload(http.MethodGet, path, nil, nil, result, false, false, h.Verbose, h.HTTPDebugging)
return h.SendPayload(http.MethodGet,
path,
nil,
nil,
result,
false,
false,
h.Verbose,
h.HTTPDebugging,
h.HTTPRecording)
}
// SendAuthenticatedHTTPRequest sends authenticated requests to the HUOBI API
@@ -972,7 +981,16 @@ func (h *HUOBI) SendAuthenticatedHTTPRequest(method, endpoint string, values url
body = encoded
}
return h.SendPayload(method, urlPath, headers, bytes.NewReader(body), result, true, false, h.Verbose, h.HTTPDebugging)
return h.SendPayload(method,
urlPath,
headers,
bytes.NewReader(body),
result,
true,
false,
h.Verbose,
h.HTTPDebugging,
h.HTTPRecording)
}
// GetFee returns an estimate of fee based on type of transaction

View File

@@ -890,7 +890,16 @@ func (h *HUOBIHADAX) CancelWithdraw(withdrawID int64) (int64, error) {
// SendHTTPRequest sends an unauthenticated HTTP request
func (h *HUOBIHADAX) SendHTTPRequest(path string, result interface{}) error {
return h.SendPayload(http.MethodGet, path, nil, nil, result, false, false, h.Verbose, h.HTTPDebugging)
return h.SendPayload(http.MethodGet,
path,
nil,
nil,
result,
false,
false,
h.Verbose,
h.HTTPDebugging,
h.HTTPRecording)
}
// SendAuthenticatedHTTPPostRequest sends authenticated requests to the HUOBI API
@@ -917,7 +926,16 @@ func (h *HUOBIHADAX) SendAuthenticatedHTTPPostRequest(method, endpoint, postBody
signatureParams.Set("Signature", common.Base64Encode(hmac))
urlPath := common.EncodeURLValues(fmt.Sprintf("%s%s", h.APIUrl, endpoint),
signatureParams)
return h.SendPayload(method, urlPath, headers, bytes.NewBufferString(postBodyValues), result, true, false, h.Verbose, h.HTTPDebugging)
return h.SendPayload(method,
urlPath,
headers,
bytes.NewBufferString(postBodyValues),
result,
true,
false,
h.Verbose,
h.HTTPDebugging,
h.HTTPRecording)
}
// SendAuthenticatedHTTPRequest sends authenticated requests to the HUOBI API
@@ -943,7 +961,16 @@ func (h *HUOBIHADAX) SendAuthenticatedHTTPRequest(method, endpoint string, value
urlPath := common.EncodeURLValues(fmt.Sprintf("%s%s", h.APIUrl, endpoint),
values)
return h.SendPayload(method, urlPath, headers, bytes.NewBufferString(""), result, true, false, h.Verbose, h.HTTPDebugging)
return h.SendPayload(method,
urlPath,
headers,
bytes.NewBufferString(""),
result,
true,
false,
h.Verbose,
h.HTTPDebugging,
h.HTTPRecording)
}
// GetFee returns an estimate of fee based on type of transaction

View File

@@ -345,7 +345,16 @@ func (i *ItBit) WalletTransfer(walletID, sourceWallet, destWallet string, amount
// SendHTTPRequest sends an unauthenticated HTTP request
func (i *ItBit) SendHTTPRequest(path string, result interface{}) error {
return i.SendPayload(http.MethodGet, path, nil, nil, result, false, false, i.Verbose, i.HTTPDebugging)
return i.SendPayload(http.MethodGet,
path,
nil,
nil,
result,
false,
false,
i.Verbose,
i.HTTPDebugging,
i.HTTPRecording)
}
// SendAuthenticatedHTTPRequest sends an authenticated request to itBit
@@ -405,7 +414,16 @@ func (i *ItBit) SendAuthenticatedHTTPRequest(method, path string, params map[str
RequestID string `json:"requestId"`
}{}
err = i.SendPayload(method, urlPath, headers, bytes.NewBuffer(PayloadJSON), &intermediary, true, true, i.Verbose, i.HTTPDebugging)
err = i.SendPayload(method,
urlPath,
headers,
bytes.NewBuffer(PayloadJSON),
&intermediary,
true,
true,
i.Verbose,
i.HTTPDebugging,
i.HTTPRecording)
if err != nil {
return err
}

View File

@@ -952,7 +952,16 @@ func GetError(apiErrors []string) error {
// SendHTTPRequest sends an unauthenticated HTTP requests
func (k *Kraken) SendHTTPRequest(path string, result interface{}) error {
return k.SendPayload(http.MethodGet, path, nil, nil, result, false, false, k.Verbose, k.HTTPDebugging)
return k.SendPayload(http.MethodGet,
path,
nil,
nil,
result,
false,
false,
k.Verbose,
k.HTTPDebugging,
k.HTTPRecording)
}
// SendAuthenticatedHTTPRequest sends an authenticated HTTP request
@@ -996,7 +1005,8 @@ func (k *Kraken) SendAuthenticatedHTTPRequest(method string, params url.Values,
true,
true,
k.Verbose,
k.HTTPDebugging)
k.HTTPDebugging,
k.HTTPRecording)
}
// GetFee returns an estimate of fee based on type of transaction

View File

@@ -360,7 +360,16 @@ func (l *LakeBTC) CreateWithdraw(amount float64, accountID string) (Withdraw, er
// SendHTTPRequest sends an unauthenticated http request
func (l *LakeBTC) SendHTTPRequest(path string, result interface{}) error {
return l.SendPayload(http.MethodGet, path, nil, nil, result, false, false, l.Verbose, l.HTTPDebugging)
return l.SendPayload(http.MethodGet,
path,
nil,
nil,
result,
false,
false,
l.Verbose,
l.HTTPDebugging,
l.HTTPRecording)
}
// SendAuthenticatedHTTPRequest sends an autheticated HTTP request to a LakeBTC
@@ -393,7 +402,16 @@ func (l *LakeBTC) SendAuthenticatedHTTPRequest(method, params string, result int
headers["Authorization"] = "Basic " + common.Base64Encode([]byte(l.APIKey+":"+common.HexEncodeToString(hmac)))
headers["Content-Type"] = "application/json-rpc"
return l.SendPayload(http.MethodPost, l.APIUrl, headers, strings.NewReader(string(data)), result, true, true, l.Verbose, l.HTTPDebugging)
return l.SendPayload(http.MethodPost,
l.APIUrl,
headers,
strings.NewReader(string(data)),
result,
true,
true,
l.Verbose,
l.HTTPDebugging,
l.HTTPRecording)
}
// GetFee returns an estimate of fee based on type of transaction

View File

@@ -551,7 +551,16 @@ func ErrorCapture(code int64) error {
// SendHTTPRequest sends an unauthenticated HTTP request
func (l *Lbank) SendHTTPRequest(path string, result interface{}) error {
return l.SendPayload(http.MethodGet, path, nil, nil, &result, false, false, l.Verbose, l.HTTPDebugging)
return l.SendPayload(http.MethodGet,
path,
nil,
nil,
&result,
false,
false,
l.Verbose,
l.HTTPDebugging,
l.HTTPRecording)
}
func (l *Lbank) loadPrivKey() error {
@@ -618,5 +627,6 @@ func (l *Lbank) SendAuthHTTPRequest(method, endpoint string, vals url.Values, re
true,
false,
l.Verbose,
l.HTTPDebugging)
l.HTTPDebugging,
l.HTTPRecording)
}

View File

@@ -1,6 +1,7 @@
package localbitcoins
import (
"bytes"
"errors"
"fmt"
"net/http"
@@ -204,16 +205,36 @@ func (l *LocalBitcoins) GetAccountInformation(username string, self bool) (Accou
// adID - [optional] string if omitted returns all ads
func (l *LocalBitcoins) Getads(args ...string) (AdData, error) {
var resp struct {
Data AdData `json:"data"`
Data AdData `json:"data"`
Error struct {
Message string `json:"message"`
Code int `json:"error_code"`
} `json:"error"`
}
var err error
if len(args) == 0 {
return resp.Data, l.SendAuthenticatedHTTPRequest(http.MethodGet, localbitcoinsAPIAds, nil, &resp)
err = l.SendAuthenticatedHTTPRequest(http.MethodGet,
localbitcoinsAPIAds,
nil,
&resp)
} else {
params := url.Values{"ads": {strings.Join(args, ",")}}
err = l.SendAuthenticatedHTTPRequest(http.MethodGet,
localbitcoinsAPIAdGet,
params,
&resp)
}
params := url.Values{"ads": {strings.Join(args, ",")}}
if err != nil {
return resp.Data, err
}
return resp.Data, l.SendAuthenticatedHTTPRequest(http.MethodGet, localbitcoinsAPIAdGet, params, &resp)
if resp.Error.Message != "" {
return resp.Data, errors.New(resp.Error.Message)
}
return resp.Data, nil
}
// EditAd updates set advertisements
@@ -222,12 +243,27 @@ func (l *LocalBitcoins) Getads(args ...string) (AdData, error) {
// adID - string for the ad you already created
// TODO
func (l *LocalBitcoins) EditAd(_ *AdEdit, adID string) error {
type response struct {
Data AdData `json:"data"`
resp := struct {
Data AdData `json:"data"`
Error struct {
Message string `json:"message"`
Code int `json:"error_code"`
}
}{}
err := l.SendAuthenticatedHTTPRequest(http.MethodPost,
localbitcoinsAPIAdEdit+adID+"/",
nil,
&resp)
if err != nil {
return err
}
resp := response{}
return l.SendAuthenticatedHTTPRequest(http.MethodPost, localbitcoinsAPIAdEdit+adID+"/", nil, &resp)
if resp.Error.Message != "" {
return errors.New(resp.Error.Message)
}
return nil
}
// CreateAd creates a new advertisement
@@ -254,7 +290,26 @@ func (l *LocalBitcoins) UpdatePriceEquation(adID string) error {
// adID - string of specific ad identification
// TODO
func (l *LocalBitcoins) DeleteAd(adID string) error {
return l.SendAuthenticatedHTTPRequest(http.MethodPost, localbitcoinsAPIDeleteAd+adID, nil, nil)
resp := struct {
Error struct {
Message string `json:"message"`
Code int `json:"error_code"`
} `json:"error"`
}{}
err := l.SendAuthenticatedHTTPRequest(http.MethodPost,
localbitcoinsAPIDeleteAd+adID+"/",
nil,
&resp)
if err != nil {
return err
}
if resp.Error.Message != "" {
return errors.New(resp.Error.Message)
}
return nil
}
// ReleaseFunds releases Bitcoin trades specified by ID {contact_id}. If the
@@ -579,7 +634,7 @@ func (l *LocalBitcoins) GetWalletBalance() (WalletBalanceInfo, error) {
// On success, the response returns a message indicating success. It is highly
// recommended to minimize the lifetime of access tokens with the money
// permission. Use Logout() to make the current token expire instantly.
func (l *LocalBitcoins) WalletSend(address string, amount float64, pin int64) (bool, error) {
func (l *LocalBitcoins) WalletSend(address string, amount float64, pin int64) error {
values := url.Values{}
values.Set("address", address)
values.Set("amount", strconv.FormatFloat(amount, 'f', -1, 64))
@@ -590,23 +645,34 @@ func (l *LocalBitcoins) WalletSend(address string, amount float64, pin int64) (b
path = localbitcoinsAPIWalletSendPin
}
type response struct {
resp := struct {
Error struct {
Message string `json:"message"`
Errors map[string]string `json:"errors"`
Code int `json:"error_code"`
} `json:"error"`
Data struct {
Message string `json:"message"`
} `json:"data"`
}
}{}
resp := response{}
err := l.SendAuthenticatedHTTPRequest(http.MethodPost, path, values, &resp)
if err != nil {
return false, err
return err
}
if resp.Data.Message != "Money is being sent" {
return false, errors.New("unable to send Bitcoins")
if len(resp.Error.Errors) != 0 {
var details string
for _, val := range resp.Error.Errors {
details += val
}
return errors.New(details)
}
return errors.New(resp.Data.Message)
}
return true, nil
return nil
}
// GetWalletAddress returns an unused receiving address from the token owner's
@@ -728,7 +794,16 @@ func (l *LocalBitcoins) GetOrderbook(currency string) (Orderbook, error) {
// SendHTTPRequest sends an unauthenticated HTTP request
func (l *LocalBitcoins) SendHTTPRequest(path string, result interface{}) error {
return l.SendPayload(http.MethodGet, path, nil, nil, result, false, false, l.Verbose, l.HTTPDebugging)
return l.SendPayload(http.MethodGet,
path,
nil,
nil,
result,
false,
false,
l.Verbose,
l.HTTPDebugging,
l.HTTPRecording)
}
// SendAuthenticatedHTTPRequest sends an authenticated HTTP request to
@@ -758,7 +833,16 @@ func (l *LocalBitcoins) SendAuthenticatedHTTPRequest(method, path string, params
path += "?" + encoded
}
return l.SendPayload(method, l.APIUrl+path, headers, strings.NewReader(encoded), result, true, true, l.Verbose, l.HTTPDebugging)
return l.SendPayload(method,
l.APIUrl+path,
headers,
bytes.NewBufferString(encoded),
result,
true,
true,
l.Verbose,
l.HTTPDebugging,
l.HTTPRecording)
}
// GetFee returns an estimate of fee based on type of transaction

View File

@@ -0,0 +1,32 @@
//+build mock_test_off
// This will build if build tag mock_test_off is parsed and will do live testing
// using all tests in (exchange)_test.go
package localbitcoins
import (
"log"
"os"
"testing"
"github.com/thrasher-corp/gocryptotrader/config"
"github.com/thrasher-corp/gocryptotrader/exchanges/sharedtestvalues"
)
var mockTests = false
func TestMain(m *testing.M) {
cfg := config.GetConfig()
cfg.LoadConfig("../../testdata/configtest.json")
localbitcoinsConfig, err := cfg.GetExchangeConfig("LocalBitcoins")
if err != nil {
log.Fatal("Test Failed - LocalBitcoins Setup() init error", err)
}
localbitcoinsConfig.AuthenticatedAPISupport = true
localbitcoinsConfig.APIKey = apiKey
localbitcoinsConfig.APISecret = apiSecret
l.SetDefaults()
l.Setup(&localbitcoinsConfig)
log.Printf(sharedtestvalues.LiveTesting, l.GetName(), l.APIUrl)
os.Exit(m.Run())
}

View File

@@ -0,0 +1,44 @@
//+build !mock_test_off
// This will build if build tag mock_test_off is not parsed and will try to mock
// all tests in _test.go
package localbitcoins
import (
"log"
"os"
"testing"
"github.com/thrasher-corp/gocryptotrader/config"
"github.com/thrasher-corp/gocryptotrader/exchanges/mock"
"github.com/thrasher-corp/gocryptotrader/exchanges/sharedtestvalues"
)
const mockfile = "../../testdata/http_mock/localbitcoins/localbitcoins.json"
var mockTests = true
func TestMain(m *testing.M) {
cfg := config.GetConfig()
cfg.LoadConfig("../../testdata/configtest.json")
localbitcoinsConfig, err := cfg.GetExchangeConfig("LocalBitcoins")
if err != nil {
log.Fatal("Test Failed - Localbitcoins Setup() init error", err)
}
localbitcoinsConfig.AuthenticatedAPISupport = true
localbitcoinsConfig.APIKey = apiKey
localbitcoinsConfig.APISecret = apiSecret
l.SetDefaults()
l.Setup(&localbitcoinsConfig)
serverDetails, newClient, err := mock.NewVCRServer(mockfile)
if err != nil {
log.Fatalf("Test Failed - Mock server error %s", err)
}
l.HTTPClient = newClient
l.APIUrl = serverDetails
log.Printf(sharedtestvalues.MockTesting, l.GetName(), l.APIUrl)
os.Exit(m.Run())
}

View File

@@ -4,13 +4,10 @@ import (
"testing"
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/config"
"github.com/thrasher-corp/gocryptotrader/currency"
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
)
var l LocalBitcoins
// Please supply your own APIKEYS here for due diligence testing
const (
@@ -19,26 +16,11 @@ const (
canManipulateRealOrders = false
)
func TestSetDefaults(t *testing.T) {
l.SetDefaults()
}
func TestSetup(t *testing.T) {
cfg := config.GetConfig()
cfg.LoadConfig("../../testdata/configtest.json")
localbitcoinsConfig, err := cfg.GetExchangeConfig("LocalBitcoins")
if err != nil {
t.Error("Test Failed - LakeBTC Setup() init error")
}
localbitcoinsConfig.AuthenticatedAPISupport = true
localbitcoinsConfig.APIKey = apiKey
localbitcoinsConfig.APISecret = apiSecret
l.Setup(&localbitcoinsConfig)
}
var l LocalBitcoins
func TestGetTicker(t *testing.T) {
t.Parallel()
_, err := l.GetTicker()
if err != nil {
t.Errorf("Test failed - GetTicker() returned: %s", err)
@@ -47,6 +29,7 @@ func TestGetTicker(t *testing.T) {
func TestGetTradableCurrencies(t *testing.T) {
t.Parallel()
_, err := l.GetTradableCurrencies()
if err != nil {
t.Errorf("Test failed - GetTradableCurrencies() returned: %s", err)
@@ -55,43 +38,44 @@ func TestGetTradableCurrencies(t *testing.T) {
func TestGetAccountInfo(t *testing.T) {
t.Parallel()
if l.APIKey == "" || l.APISecret == "" {
t.Skip()
}
_, err := l.GetAccountInformation("", true)
if err == nil {
t.Error("Test Failed - GetAccountInformation() error", err)
}
_, err = l.GetAccountInformation("bitcoinbaron", false)
if err != nil {
t.Error("Test Failed - GetAccountInformation() error", err)
switch {
case areTestAPIKeysSet() && err != nil && !mockTests:
t.Errorf("Could not get AccountInformation: %s", err)
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Expecting an error when no keys are set")
case mockTests && err != nil:
t.Errorf("Could not get AccountInformation: %s", err)
}
}
func TestGetads(t *testing.T) {
t.Parallel()
if l.APIKey == "" || l.APISecret == "" {
t.Skip()
}
_, err := l.Getads("")
if err == nil {
t.Error("Test Failed - Getads() - Full list, error", err)
}
_, err = l.Getads("1337")
if err == nil {
t.Error("Test Failed - Getads() error", err)
switch {
case areTestAPIKeysSet() && err != nil && !mockTests:
t.Errorf("Could not get ads: %s", err)
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Expecting an error when no keys are set")
case mockTests && err == nil:
t.Error("Expecting error until QA pass")
}
}
func TestEditAd(t *testing.T) {
t.Parallel()
if l.APIKey == "" || l.APISecret == "" {
t.Skip()
}
edit := AdEdit{}
var edit AdEdit
err := l.EditAd(&edit, "1337")
if err == nil {
t.Error("Test Failed - EditAd() error", err)
switch {
case areTestAPIKeysSet() && err != nil && !mockTests:
t.Errorf("Could not edit order: %s", err)
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Expecting an error when no keys are set")
case mockTests && err == nil:
t.Error("Expecting error until QA pass")
}
}
@@ -110,6 +94,7 @@ func setFeeBuilder() *exchange.FeeBuilder {
// TestGetFeeByTypeOfflineTradeFee logic test
func TestGetFeeByTypeOfflineTradeFee(t *testing.T) {
t.Parallel()
var feeBuilder = setFeeBuilder()
l.GetFeeByType(feeBuilder)
if apiKey == "" || apiSecret == "" {
@@ -124,7 +109,7 @@ func TestGetFeeByTypeOfflineTradeFee(t *testing.T) {
}
func TestGetFee(t *testing.T) {
l.SetDefaults()
t.Parallel()
var feeBuilder = setFeeBuilder()
// CryptocurrencyTradeFee Basic
if resp, err := l.GetFee(feeBuilder); resp != float64(0) || err != nil {
@@ -200,45 +185,53 @@ func TestGetFee(t *testing.T) {
}
func TestFormatWithdrawPermissions(t *testing.T) {
l.SetDefaults()
expectedResult := exchange.AutoWithdrawCryptoText + " & " + exchange.WithdrawFiatViaWebsiteOnlyText
t.Parallel()
expectedResult := exchange.AutoWithdrawCryptoText +
" & " +
exchange.WithdrawFiatViaWebsiteOnlyText
withdrawPermissions := l.FormatWithdrawPermissions()
if withdrawPermissions != expectedResult {
t.Errorf("Expected: %s, Received: %s", expectedResult, withdrawPermissions)
t.Errorf("Expected: %s, Received: %s",
expectedResult,
withdrawPermissions)
}
}
func TestGetActiveOrders(t *testing.T) {
l.SetDefaults()
TestSetup(t)
t.Parallel()
var getOrdersRequest = exchange.GetOrdersRequest{
OrderType: exchange.AnyOrderType,
}
_, err := l.GetActiveOrders(&getOrdersRequest)
if areTestAPIKeysSet() && err != nil {
t.Errorf("Could not get open orders: %s", err)
} else if !areTestAPIKeysSet() && err == nil {
switch {
case areTestAPIKeysSet() && err != nil && !mockTests:
t.Errorf("Could not get active orders: %s", err)
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Expecting an error when no keys are set")
case mockTests && err != nil:
t.Errorf("Could not get active orders: %s", err)
}
}
func TestGetOrderHistory(t *testing.T) {
l.SetDefaults()
TestSetup(t)
t.Parallel()
var getOrdersRequest = exchange.GetOrdersRequest{
OrderType: exchange.AnyOrderType,
}
_, err := l.GetOrderHistory(&getOrdersRequest)
if areTestAPIKeysSet() && err != nil {
switch {
case areTestAPIKeysSet() && err != nil && !mockTests:
t.Errorf("Could not get order history: %s", err)
} else if !areTestAPIKeysSet() && err == nil {
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Expecting an error when no keys are set")
case mockTests && err != nil:
t.Errorf("Could not get order history: %s", err)
}
}
@@ -253,10 +246,9 @@ func areTestAPIKeysSet() bool {
}
func TestSubmitOrder(t *testing.T) {
l.SetDefaults()
TestSetup(t)
t.Parallel()
if areTestAPIKeysSet() && !canManipulateRealOrders {
if areTestAPIKeysSet() && !canManipulateRealOrders && !mockTests {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -266,62 +258,62 @@ func TestSubmitOrder(t *testing.T) {
Quote: currency.EUR,
}
response, err := l.SubmitOrder(p, exchange.BuyOrderSide, exchange.MarketOrderType, 1, 10, "hi")
if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) {
switch {
case areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) && !mockTests:
t.Errorf("Order failed to be placed: %v", err)
} else if !areTestAPIKeysSet() && err == nil {
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Expecting an error when no keys are set")
case mockTests && err == nil:
t.Error("Expecting an error until QA pass")
}
}
func TestCancelExchangeOrder(t *testing.T) {
l.SetDefaults()
TestSetup(t)
t.Parallel()
if areTestAPIKeysSet() && !canManipulateRealOrders {
if areTestAPIKeysSet() && !canManipulateRealOrders && !mockTests {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
currencyPair := currency.NewPair(currency.LTC, currency.BTC)
var orderCancellation = &exchange.OrderCancellation{
OrderID: "1",
WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB",
AccountID: "1",
CurrencyPair: currencyPair,
CurrencyPair: currency.NewPair(currency.LTC, currency.BTC),
}
err := l.CancelOrder(orderCancellation)
if !areTestAPIKeysSet() && err == nil {
switch {
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Expecting an error when no keys are set")
}
if areTestAPIKeysSet() && err != nil {
case areTestAPIKeysSet() && err != nil && !mockTests:
t.Errorf("Could not cancel orders: %v", err)
case mockTests && err == nil:
t.Error("Expecting an error until QA pass")
}
}
func TestCancelAllExchangeOrders(t *testing.T) {
l.SetDefaults()
TestSetup(t)
t.Parallel()
if areTestAPIKeysSet() && !canManipulateRealOrders {
if areTestAPIKeysSet() && !canManipulateRealOrders && !mockTests {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
currencyPair := currency.NewPair(currency.LTC, currency.BTC)
var orderCancellation = &exchange.OrderCancellation{
OrderID: "1",
WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB",
AccountID: "1",
CurrencyPair: currencyPair,
CurrencyPair: currency.NewPair(currency.LTC, currency.BTC),
}
resp, err := l.CancelAllOrders(orderCancellation)
if !areTestAPIKeysSet() && err == nil {
switch {
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Expecting an error when no keys are set")
}
if areTestAPIKeysSet() && err != nil {
case areTestAPIKeysSet() && err != nil && !mockTests:
t.Errorf("Could not cancel orders: %v", err)
case mockTests && err != nil:
t.Errorf("Could not cancel orders: %v", err)
}
@@ -331,15 +323,17 @@ func TestCancelAllExchangeOrders(t *testing.T) {
}
func TestModifyOrder(t *testing.T) {
t.Parallel()
_, err := l.ModifyOrder(&exchange.ModifyOrder{})
if err == nil {
t.Error("Test failed - ModifyOrder() error")
if err != common.ErrFunctionNotSupported {
t.Error("Test failed - ModifyOrder() error", err)
}
}
func TestWithdraw(t *testing.T) {
l.SetDefaults()
TestSetup(t)
t.Parallel()
var withdrawCryptoRequest = exchange.WithdrawRequest{
Amount: 100,
Currency: currency.LTC,
@@ -347,61 +341,55 @@ func TestWithdraw(t *testing.T) {
Description: "WITHDRAW IT ALL",
}
if areTestAPIKeysSet() && !canManipulateRealOrders {
if areTestAPIKeysSet() && !canManipulateRealOrders && !mockTests {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
_, err := l.WithdrawCryptocurrencyFunds(&withdrawCryptoRequest)
if !areTestAPIKeysSet() && err == nil {
switch {
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Expecting an error when no keys are set")
}
if areTestAPIKeysSet() && err != nil {
case areTestAPIKeysSet() && err != nil && !mockTests:
t.Errorf("Withdraw failed to be placed: %v", err)
case mockTests && err == nil:
t.Error("Expecting an error until QA pass")
}
}
func TestWithdrawFiat(t *testing.T) {
l.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
t.Parallel()
var withdrawFiatRequest = exchange.WithdrawRequest{}
_, err := l.WithdrawFiatFunds(&withdrawFiatRequest)
if err != common.ErrFunctionNotSupported {
t.Errorf("Expected '%v', received: '%v'", common.ErrFunctionNotSupported, err)
t.Errorf("Expected '%v', received: '%v'",
common.ErrFunctionNotSupported,
err)
}
}
func TestWithdrawInternationalBank(t *testing.T) {
l.SetDefaults()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
t.Parallel()
var withdrawFiatRequest = exchange.WithdrawRequest{}
_, err := l.WithdrawFiatFundsToInternationalBank(&withdrawFiatRequest)
if err != common.ErrFunctionNotSupported {
t.Errorf("Expected '%v', received: '%v'", common.ErrFunctionNotSupported, err)
t.Errorf("Expected '%v', received: '%v'",
common.ErrFunctionNotSupported,
err)
}
}
func TestGetDepositAddress(t *testing.T) {
if apiKey != "" || apiSecret != "" {
_, err := l.GetDepositAddress(currency.BTC, "")
if err != nil {
t.Error("Test Failed - GetDepositAddress() error", err)
}
} else {
_, err := l.GetDepositAddress(currency.BTC, "")
if err == nil {
t.Error("Test Failed - GetDepositAddress() error cannot be nil")
}
t.Parallel()
_, err := l.GetDepositAddress(currency.BTC, "")
switch {
case areTestAPIKeysSet() && err != nil && !mockTests:
t.Error("Test Failed - GetDepositAddress() error", err)
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Test Failed - GetDepositAddress() expecting an error when no APIKeys are set")
case mockTests && err != nil:
t.Error("Test Failed - GetDepositAddress() error", err)
}
}

View File

@@ -280,8 +280,10 @@ func (l *LocalBitcoins) GetDepositAddress(cryptocurrency currency.Code, _ string
// WithdrawCryptocurrencyFunds returns a withdrawal ID when a withdrawal is
// submitted
func (l *LocalBitcoins) WithdrawCryptocurrencyFunds(withdrawRequest *exchange.WithdrawRequest) (string, error) {
_, err := l.WalletSend(withdrawRequest.Address, withdrawRequest.Amount, withdrawRequest.PIN)
return "", err
return "",
l.WalletSend(withdrawRequest.Address,
withdrawRequest.Amount,
withdrawRequest.PIN)
}
// WithdrawFiatFunds returns a withdrawal ID when a

174
exchanges/mock/README.md Normal file
View File

@@ -0,0 +1,174 @@
# GoCryptoTrader package Mock
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/page-logo.png?raw=true" width="350px" height="350px" hspace="70">
[![Build Status](https://travis-ci.org/thrasher-corp/gocryptotrader.svg?branch=master)](https://travis-ci.org/thrasher-corp/gocryptotrader)
[![Software License](https://img.shields.io/badge/License-MIT-orange.svg?style=flat-square)](https://github.com/thrasher-corp/gocryptotrader/blob/master/LICENSE)
[![GoDoc](https://godoc.org/github.com/thrasher-corp/gocryptotrader?status.svg)](https://godoc.org/github.com/thrasher-corp/gocryptotrader/exchanges/mock)
[![Coverage Status](http://codecov.io/github/thrasher-corp/gocryptotrader/coverage.svg?branch=master)](http://codecov.io/github/thrasher-corp/gocryptotrader?branch=master)
[![Go Report Card](https://goreportcard.com/badge/github.com/thrasher-corp/gocryptotrader)](https://goreportcard.com/report/github.com/thrasher-corp/gocryptotrader)
This Mock package is part of the GoCryptoTrader codebase.
## This is still in active development
You can track ideas, planned features and what's in progresss on this Trello board: [https://trello.com/b/ZAhMhpOy/gocryptotrader](https://trello.com/b/ZAhMhpOy/gocryptotrader).
Join our slack to discuss all things related to GoCryptoTrader! [GoCryptoTrader Slack](https://join.slack.com/t/gocryptotrader/shared_invite/enQtNTQ5NDAxMjA2Mjc5LTQyYjIxNGVhMWU5MDZlOGYzMmE0NTJmM2MzYWY5NGMzMmM4MzUwNTBjZTEzNjIwODM5NDcxODQwZDljMGQyNGY)
## Mock Testing Suite
### Current Features
+ REST recording service
+ REST mock response server
### How to enable
+ Mock testing is enabled by default in some exchanges; to disable and run live endpoint testing parse -tags=mock_test_off as a go test param.
+ To record a live endpoint create two files for an exchange.
### file one - your_current_exchange_name_live_test.go
```go
//+build mock_test_off
// This will build if build tag mock_test_off is parsed and will do live testing
// using all tests in (exchange)_test.go
package your_current_exchange_name
import (
"os"
"testing"
"log"
"github.com/thrasher-corp/gocryptotrader/config"
"github.com/thrasher-corp/gocryptotrader/exchanges/sharedtestvalues"
)
var mockTests = false
func TestMain(m *testing.M) {
cfg := config.GetConfig()
cfg.LoadConfig("../../testdata/configtest.json")
your_current_exchange_nameConfig, err := cfg.GetExchangeConfig("your_current_exchange_name")
if err != nil {
log.Fatal("Test Failed - your_current_exchange_name Setup() init error", err)
}
your_current_exchange_nameConfig.AuthenticatedAPISupport = true
your_current_exchange_nameConfig.APIKey = apiKey
your_current_exchange_nameConfig.APISecret = apiSecret
l.SetDefaults()
l.Setup(&your_current_exchange_nameConfig)
log.Printf(sharedtestvalues.LiveTesting, l.GetName(), l.APIUrl)
os.Exit(m.Run())
}
```
### file two - your_current_exchange_name_mock_test.go
```go
//+build !mock_test_off
// This will build if build tag mock_test_off is not parsed and will try to mock
// all tests in _test.go
package your_current_exchange_name
import (
"os"
"testing"
"log"
"github.com/thrasher-corp/gocryptotrader/config"
"github.com/thrasher-corp/gocryptotrader/exchanges/mock"
"github.com/thrasher-corp/gocryptotrader/exchanges/sharedtestvalues"
)
const mockfile = "../../testdata/http_mock/your_current_exchange_name/your_current_exchange_name.json"
var mockTests = true
func TestMain(m *testing.M) {
cfg := config.GetConfig()
cfg.LoadConfig("../../testdata/configtest.json")
your_current_exchange_nameConfig, err := cfg.GetExchangeConfig("your_current_exchange_name")
if err != nil {
log.Fatal("Test Failed - your_current_exchange_name Setup() init error", err)
}
your_current_exchange_nameConfig.AuthenticatedAPISupport = true
your_current_exchange_nameConfig.APIKey = apiKey
your_current_exchange_nameConfig.APISecret = apiSecret
l.SetDefaults()
l.Setup(&your_current_exchange_nameConfig)
serverDetails, newClient, err := mock.NewVCRServer(mockfile)
if err != nil {
log.Fatalf("Test Failed - Mock server error %s", err)
}
g.HTTPClient = newClient
g.APIUrl = serverDetails
log.Printf(sharedtestvalues.MockTesting, l.GetName(), l.APIUrl)
os.Exit(m.Run())
}
```
+ Once those files are completed go through each invidual test function and add
```go
var s SomeExchange
func TestDummyTest(t *testing.T) {
s.APIURL = exchangeDefaultURL // This will overwrite the current mock url at localhost
s.Verbose = true // This will show you some fancy debug output
s.HTTPRecording = true // This will record the request and response payloads
err := s.SomeExchangeEndpointFunction()
// check error
}
```
+ After this is completed it should populate a new mocktest.json file for you with the relavent payloads in testdata
+ To check if the recording was successful, comment out recording and apiurl changes, then re-run test.
```go
var s SomeExchange
func TestDummyTest(t *testing.T) {
// s.APIURL = exchangeDefaultURL // This will overwrite the current mock url at localhost
s.Verbose = true // This will show you some fancy debug output
// s.HTTPRecording = true // This will record the request and response payloads
err := s.SomeExchangeEndpointFunction()
// check error
}
```
+ The payload should be the same.
### Please click GoDocs chevron above to view current GoDoc information for this package
## Contribution
Please feel free to submit any pull requests or suggest any desired features to be added.
When submitting a PR, please abide by our coding guidelines:
+ Code must adhere to the official Go [formatting](https://golang.org/doc/effective_go.html#formatting) guidelines (i.e. uses [gofmt](https://golang.org/cmd/gofmt/)).
+ Code must be documented adhering to the official Go [commentary](https://golang.org/doc/effective_go.html#commentary) guidelines.
+ Code must adhere to our [coding style](https://github.com/thrasher-corp/gocryptotrader/blob/master/doc/coding_style.md).
+ Pull requests need to be based on and opened against the `master` branch.
## Donations
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
***1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB***

71
exchanges/mock/common.go Normal file
View File

@@ -0,0 +1,71 @@
package mock
import (
"encoding/json"
"errors"
"fmt"
"log"
"net/url"
"reflect"
"strconv"
"strings"
)
// MatchURLVals matches url.Value query strings
func MatchURLVals(v1, v2 url.Values) bool {
if len(v1) != len(v2) {
return false
}
if len(v1) == 0 && len(v2) == 0 {
return true
}
for key, val := range v1 {
if key == "nonce" || key == "signature" || key == "timestamp" || key == "tonce" || key == "key" { // delta values
if _, ok := v2[key]; !ok {
return false
}
continue
}
if val2, ok := v2[key]; ok {
if strings.Join(val2, "") == strings.Join(val, "") {
continue
}
}
return false
}
return true
}
// DeriveURLValsFromJSONMap gets url vals from a map[string]string encoded JSON body
func DeriveURLValsFromJSONMap(payload []byte) (url.Values, error) {
var vals = url.Values{}
if string(payload) == "" {
return vals, nil
}
intermediary := make(map[string]interface{})
err := json.Unmarshal(payload, &intermediary)
if err != nil {
return vals, err
}
for k, v := range intermediary {
switch val := v.(type) {
case string:
vals.Add(k, val)
case bool:
vals.Add(k, strconv.FormatBool(val))
case float64:
vals.Add(k, strconv.FormatFloat(val, 'f', -1, 64))
case map[string]interface{}, []interface{}, nil:
vals.Add(k, fmt.Sprintf("%v", val))
default:
log.Println(reflect.TypeOf(val))
return vals, errors.New("unhandled conversion type, please add as needed")
}
}
return vals, nil
}

View File

@@ -0,0 +1,140 @@
package mock
import (
"encoding/json"
"net/url"
"testing"
)
func TestMatchURLVals(t *testing.T) {
testVal, testVal2, testVal3, emptyVal := url.Values{}, url.Values{}, url.Values{}, url.Values{}
testVal.Add("test", "test")
testVal2.Add("test2", "test2")
testVal3.Add("test", "diferentValString")
nonceVal1, nonceVal2 := url.Values{}, url.Values{}
nonceVal1.Add("nonce", "012349723587")
nonceVal2.Add("nonce", "9327373874")
var expected = false
received := MatchURLVals(testVal, emptyVal)
if received != expected {
t.Errorf("Test Failed - MatchURLVals error expected %v received %v",
expected,
received)
}
received = MatchURLVals(emptyVal, testVal)
if received != expected {
t.Errorf("Test Failed - MatchURLVals error expected %v received %v",
expected,
received)
}
received = MatchURLVals(testVal, testVal2)
if received != expected {
t.Errorf("Test Failed - MatchURLVals error expected %v received %v",
expected,
received)
}
received = MatchURLVals(testVal2, testVal)
if received != expected {
t.Errorf("Test Failed - MatchURLVals error expected %v received %v",
expected,
received)
}
received = MatchURLVals(testVal, testVal3)
if received != expected {
t.Errorf("Test Failed - MatchURLVals error expected %v received %v",
expected,
received)
}
received = MatchURLVals(nonceVal1, testVal2)
if received != expected {
t.Errorf("Test Failed - MatchURLVals error expected %v received %v",
expected,
received)
}
expected = true
received = MatchURLVals(emptyVal, emptyVal)
if received != expected {
t.Errorf("Test Failed - MatchURLVals error expected %v received %v",
expected,
received)
}
received = MatchURLVals(testVal, testVal)
if received != expected {
t.Errorf("Test Failed - MatchURLVals error expected %v received %v",
expected,
received)
}
received = MatchURLVals(nonceVal1, nonceVal2)
if received != expected {
t.Errorf("Test Failed - MatchURLVals error expected %v received %v",
expected,
received)
}
}
func TestDeriveURLValsFromJSON(t *testing.T) {
test1 := struct {
Things []string `json:"things"`
Data struct {
Numbers []int `json:"numbers"`
Number float64 `json:"number"`
SomeString string `json:"somestring"`
} `json:"data"`
}{
Things: []string{"hello", "world"},
Data: struct {
Numbers []int `json:"numbers"`
Number float64 `json:"number"`
SomeString string `json:"somestring"`
}{
Numbers: []int{1, 3, 3, 7},
Number: 3.14,
SomeString: "hello, peoples",
},
}
payload, err := json.Marshal(test1)
if err != nil {
t.Error("Test Failed - marshal error", err)
}
_, err = DeriveURLValsFromJSONMap(payload)
if err != nil {
t.Error("Test Failed - DeriveURLValsFromJSON error", err)
}
test2 := map[string]string{
"val": "1",
"val2": "2",
"val3": "3",
"val4": "4",
"val5": "5",
"val6": "6",
"val7": "7",
}
payload, err = json.Marshal(test2)
if err != nil {
t.Error("Test Failed - marshal error", err)
}
vals, err := DeriveURLValsFromJSONMap(payload)
if err != nil {
t.Error("Test Failed - DeriveURLValsFromJSON error", err)
}
if vals["val"][0] != "1" {
t.Error("Test Failed - DeriveURLValsFromJSON unexpected value",
vals["val"][0])
}
}

439
exchanges/mock/recording.go Normal file
View File

@@ -0,0 +1,439 @@
package mock
import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"log"
"net/http"
"net/url"
"os"
"path/filepath"
"reflect"
"strings"
"sync"
"github.com/thrasher-corp/gocryptotrader/common"
)
// HTTPResponse defines expected response from the end point including request
// data for pathing on the VCR server
type HTTPResponse struct {
Data json.RawMessage `json:"data"`
QueryString string `json:"queryString"`
BodyParams string `json:"bodyParams"`
Headers map[string][]string `json:"headers"`
}
// HTTPRecord will record the request and response to a default JSON file for
// mocking purposes
func HTTPRecord(res *http.Response, service string, respContents []byte) error {
if res == nil {
return errors.New("http.Response cannot be nil")
}
if res.Request == nil {
return errors.New("http.Request cannot be nil")
}
if res.Request.Method == "" {
return errors.New("request method not supplied")
}
if service == "" {
return errors.New("service not supplied cannot access correct mock file")
}
service = strings.ToLower(service)
fileout := filepath.Join(DefaultDirectory, service, service+".json")
contents, err := common.ReadFile(fileout)
if err != nil {
return err
}
var m VCRMock
err = json.Unmarshal(contents, &m)
if err != nil {
return err
}
if m.Routes == nil {
m.Routes = make(map[string]map[string][]HTTPResponse)
}
var httpResponse HTTPResponse
cleanedContents, err := CheckResponsePayload(respContents)
if err != nil {
return err
}
err = json.Unmarshal(cleanedContents, &httpResponse.Data)
if err != nil {
return err
}
var body string
if res.Request.GetBody != nil {
bodycopy, bodyErr := res.Request.GetBody()
if bodyErr != nil {
return bodyErr
}
payload, bodyErr := ioutil.ReadAll(bodycopy)
if bodyErr != nil {
return bodyErr
}
body = string(payload)
}
switch res.Request.Header.Get(contentType) {
case applicationURLEncoded:
vals, urlErr := url.ParseQuery(body)
if urlErr != nil {
return urlErr
}
httpResponse.BodyParams, urlErr = GetFilteredURLVals(vals)
if urlErr != nil {
return urlErr
}
case textPlain:
payload := res.Request.Header.Get("X-Gemini-Payload")
j, dErr := common.Base64Decode(payload)
if dErr != nil {
return dErr
}
httpResponse.BodyParams = string(j)
default:
httpResponse.BodyParams = body
}
httpResponse.Headers, err = GetFilteredHeader(res)
if err != nil {
return err
}
httpResponse.QueryString, err = GetFilteredURLVals(res.Request.URL.Query())
if err != nil {
return err
}
_, ok := m.Routes[res.Request.URL.Path]
if !ok {
m.Routes[res.Request.URL.Path] = make(map[string][]HTTPResponse)
m.Routes[res.Request.URL.Path][res.Request.Method] = []HTTPResponse{httpResponse}
} else {
mockResponses, ok := m.Routes[res.Request.URL.Path][res.Request.Method]
if !ok {
m.Routes[res.Request.URL.Path][res.Request.Method] = []HTTPResponse{httpResponse}
} else {
switch res.Request.Method { // Based off method - check add or replace
case http.MethodGet:
for i := range mockResponses {
mockQuery, urlErr := url.ParseQuery(mockResponses[i].QueryString)
if urlErr != nil {
return urlErr
}
if MatchURLVals(mockQuery, res.Request.URL.Query()) {
mockResponses = append(mockResponses[:i], mockResponses[i+1:]...) // Delete Old
break
}
}
case http.MethodPost:
for i := range mockResponses {
cType, ok := mockResponses[i].Headers[contentType]
if !ok {
return errors.New("cannot find content type within mock responses")
}
jCType := strings.Join(cType, "")
var found bool
switch jCType {
case applicationURLEncoded:
respQueryVals, urlErr := url.ParseQuery(body)
if urlErr != nil {
return urlErr
}
mockRespVals, urlErr := url.ParseQuery(mockResponses[i].BodyParams)
if urlErr != nil {
log.Fatal(urlErr)
}
if MatchURLVals(respQueryVals, mockRespVals) {
// if found will delete instance and overwrite with new
// data
mockResponses = append(mockResponses[:i], mockResponses[i+1:]...)
found = true
}
case applicationJSON, textPlain:
reqVals, jErr := DeriveURLValsFromJSONMap([]byte(body))
if jErr != nil {
return jErr
}
mockVals, jErr := DeriveURLValsFromJSONMap([]byte(mockResponses[i].BodyParams))
if jErr != nil {
return jErr
}
if MatchURLVals(reqVals, mockVals) {
// if found will delete instance and overwrite with new
// data
mockResponses = append(mockResponses[:i], mockResponses[i+1:]...)
found = true
}
default:
return fmt.Errorf("unhandled content type %s", jCType)
}
if found {
break
}
}
default:
return fmt.Errorf("unhandled request method %s", res.Request.Method)
}
m.Routes[res.Request.URL.Path][res.Request.Method] = append(mockResponses, httpResponse)
}
}
payload, err := json.MarshalIndent(m, "", " ")
if err != nil {
return err
}
return common.WriteFile(fileout, payload)
}
// GetFilteredHeader filters excluded http headers for insertion into a mock
// test file
func GetFilteredHeader(res *http.Response) (http.Header, error) {
items, err := GetExcludedItems()
if err != nil {
return res.Header, err
}
for i := range items.Headers {
if res.Request.Header.Get(items.Headers[i]) != "" {
res.Request.Header.Set(items.Headers[i], "")
}
}
return res.Request.Header, nil
}
// GetFilteredURLVals filters excluded url value variables for insertion into a
// mock test file
func GetFilteredURLVals(vals url.Values) (string, error) {
items, err := GetExcludedItems()
if err != nil {
return "", err
}
for key, val := range vals {
for i := range items.Variables {
if strings.EqualFold(items.Variables[i], val[0]) {
vals.Set(key, "")
}
}
}
return vals.Encode(), nil
}
// CheckResponsePayload checks to see if there are any response body variables
// that should not be there.
func CheckResponsePayload(data []byte) ([]byte, error) {
items, err := GetExcludedItems()
if err != nil {
return nil, err
}
var intermediary interface{}
err = json.Unmarshal(data, &intermediary)
if err != nil {
return nil, err
}
payload, err := CheckJSON(intermediary, &items)
if err != nil {
return nil, err
}
return json.MarshalIndent(payload, "", " ")
}
// Reflection consts
const (
Int64 = "int64"
Float64 = "float64"
Slice = "slice"
String = "string"
Bool = "bool"
Invalid = "invalid"
)
// CheckJSON recursively parses json data to retract keywords, quite intensive.
func CheckJSON(data interface{}, excluded *Exclusion) (interface{}, error) {
var context map[string]interface{}
if reflect.TypeOf(data).String() == "[]interface {}" {
var sData []interface{}
for i := range data.([]interface{}) {
checkedData, err := CheckJSON(data.([]interface{})[i], excluded)
if err != nil {
return nil, err
}
sData = append(sData, checkedData)
}
return sData, nil
}
conv, err := json.Marshal(data)
if err != nil {
return nil, err
}
err = json.Unmarshal(conv, &context)
if err != nil {
return nil, err
}
if len(context) == 0 {
// Nil for some reason, should error out before in json.Unmarshal
return nil, nil
}
for key, val := range context {
switch reflect.ValueOf(val).Kind().String() {
case String:
if IsExcluded(key, excluded.Variables) {
context[key] = "" // Zero val string
}
case Int64:
if IsExcluded(key, excluded.Variables) {
context[key] = 0 // Zero val int
}
case Float64:
if IsExcluded(key, excluded.Variables) {
context[key] = 0.0 // Zero val float
}
case Slice:
slice := val.([]interface{})
if len(slice) < 1 {
// Empty slice found
context[key] = slice
} else {
if _, ok := slice[0].(map[string]interface{}); ok {
var cleanSlice []interface{}
for i := range slice {
cleanMap, sErr := CheckJSON(slice[i], excluded)
if sErr != nil {
return nil, sErr
}
cleanSlice = append(cleanSlice, cleanMap)
}
context[key] = cleanSlice
} else if IsExcluded(key, excluded.Variables) {
context[key] = nil // Zero val slice
}
}
case Bool, Invalid: // Skip these bad boys for now
default:
// Recursively check map data
contextValue, err := CheckJSON(val, excluded)
if err != nil {
return nil, err
}
context[key] = contextValue
}
}
return context, nil
}
// IsExcluded cross references the key with the excluded variables
func IsExcluded(key string, excludedVars []string) bool {
for i := range excludedVars {
if strings.EqualFold(key, excludedVars[i]) {
return true
}
}
return false
}
var excludedList Exclusion
var m sync.Mutex
var set bool
var exclusionFile = DefaultDirectory + "exclusion.json"
var defaultExcludedHeaders = []string{"Key",
"X-Mbx-Apikey",
"Rest-Key",
"Apiauth-Key"}
var defaultExcludedVariables = []string{"bsb",
"user",
"name",
"real_name",
"receiver_name",
"account_number",
"username"}
// Exclusion defines a list of items to be excluded from the main mock output
// this attempts a catch all approach and needs to be updated per exchange basis
type Exclusion struct {
Headers []string `json:"headers"`
Variables []string `json:"variables"`
}
// GetExcludedItems checks to see if the variable is in the exclusion list as to
// not display secure items in mock file generator output
func GetExcludedItems() (Exclusion, error) {
m.Lock()
defer m.Unlock()
if !set {
file, err := ioutil.ReadFile(exclusionFile)
if err != nil {
if !strings.Contains(err.Error(), "no such file or directory") {
return excludedList, err
}
excludedList.Headers = defaultExcludedHeaders
excludedList.Variables = defaultExcludedVariables
data, mErr := json.MarshalIndent(excludedList, "", " ")
if mErr != nil {
return excludedList, mErr
}
mErr = ioutil.WriteFile(exclusionFile, data, os.ModePerm)
if mErr != nil {
return excludedList, mErr
}
} else {
err = json.Unmarshal(file, &excludedList)
if err != nil {
return excludedList, err
}
if len(excludedList.Headers) == 0 || len(excludedList.Variables) == 0 {
return excludedList, errors.New("exclusion list does not have names")
}
}
set = true
}
return excludedList, nil
}

View File

@@ -0,0 +1,186 @@
package mock
import (
"encoding/json"
"net/http"
"net/url"
"strings"
"testing"
)
func TestGetFilteredHeader(t *testing.T) {
resp := http.Response{}
resp.Request = &http.Request{}
resp.Request.Header = http.Header{}
resp.Request.Header.Set("Key", "RiskyVals")
fMap, err := GetFilteredHeader(&resp)
if err != nil {
t.Error(err)
}
if fMap.Get("Key") != "" {
t.Error("Test Failed - risky vals where not replaced correctly")
}
}
func TestGetFilteredURLVals(t *testing.T) {
superSecretData := "Dr Seuss"
shadyVals := url.Values{}
shadyVals.Set("real_name", superSecretData)
cleanVals, err := GetFilteredURLVals(shadyVals)
if err != nil {
t.Error("Test Failed - GetFilteredURLVals error", err)
}
if strings.Contains(cleanVals, superSecretData) {
t.Error("Test Failed - Super secret data found")
}
}
func TestCheckResponsePayload(t *testing.T) {
testbody := struct {
SomeJSON string `json:"stuff"`
}{
SomeJSON: "REAAAAHHHHH",
}
payload, err := json.Marshal(testbody)
if err != nil {
t.Fatal("Test Failed - json marshal error", err)
}
data, err := CheckResponsePayload(payload)
if err != nil {
t.Error("Test Failed - CheckBody error", err)
}
expected := `{
"stuff": "REAAAAHHHHH"
}`
if string(data) != expected {
t.Error("unexpected returned data")
}
}
type TestStructLevel0 struct {
StringVal string `json:"stringVal"`
FloatVal float64 `json:"floatVal"`
IntVal int64 `json:"intVal"`
StructVal TestStructLevel1 `json:"structVal"`
}
type TestStructLevel1 struct {
OkayVal string `json:"okayVal"`
OkayVal2 float64 `json:"okayVal2"`
BadVal string `json:"user"`
BadVal2 int `json:"bsb"`
OtherData TestStructLevel2 `json:"otherVals"`
}
type TestStructLevel2 struct {
OkayVal string `json:"okayVal"`
OkayVal2 float64 `json:"okayVal2"`
BadVal float32 `json:"name"`
BadVal2 int32 `json:"real_name"`
OtherData TestStructLevel3 `json:"moreOtherVals"`
}
type TestStructLevel3 struct {
OkayVal string `json:"okayVal"`
OkayVal2 float64 `json:"okayVal2"`
BadVal int64 `json:"receiver_name"`
BadVal2 string `json:"account_number"`
}
func TestCheckJSON(t *testing.T) {
level3 := TestStructLevel3{
OkayVal: "stuff",
OkayVal2: 129219,
BadVal: 1337,
BadVal2: "Super Secret Password",
}
level2 := TestStructLevel2{
OkayVal: "stuff",
OkayVal2: 129219,
BadVal: 0.222,
BadVal2: 1337888888,
OtherData: level3,
}
level1 := TestStructLevel1{
OkayVal: "stuff",
OkayVal2: 120938,
BadVal: "CritcalBankingStuff",
BadVal2: 1337,
OtherData: level2,
}
testVal := TestStructLevel0{
StringVal: "somestringstuff",
FloatVal: 3.14,
IntVal: 1337,
StructVal: level1,
}
exclusionList, err := GetExcludedItems()
if err != nil {
t.Error("Test Failed - GetExcludedItems error", err)
}
vals, err := CheckJSON(testVal, &exclusionList)
if err != nil {
t.Error("Test Failed - Check JSON error", err)
}
payload, err := json.Marshal(vals)
if err != nil {
t.Fatal("Test Failed - json marshal error", err)
}
newStruct := TestStructLevel0{}
err = json.Unmarshal(payload, &newStruct)
if err != nil {
t.Fatal("Test Failed - Umarshal error", err)
}
if newStruct.StructVal.BadVal != "" {
t.Error("Value not wiped correctly")
}
if newStruct.StructVal.BadVal2 != 0 {
t.Error("Value not wiped correctly")
}
if newStruct.StructVal.OtherData.BadVal != 0 {
t.Error("Value not wiped correctly")
}
if newStruct.StructVal.OtherData.BadVal2 != 0 {
t.Error("Value not wiped correctly")
}
if newStruct.StructVal.OtherData.OtherData.BadVal != 0 {
t.Error("Value not wiped correctly")
}
if newStruct.StructVal.OtherData.OtherData.BadVal2 != "" {
t.Error("Value not wiped correctly")
}
}
func TestGetExcludedItems(t *testing.T) {
exclusionList, err := GetExcludedItems()
if err != nil {
t.Error("Test Failed - GetExcludedItems error", err)
}
if len(exclusionList.Headers) == 0 {
t.Error("Test Failed - Header exclusion list not popoulated")
}
if len(exclusionList.Variables) == 0 {
t.Error("Test Failed - Variable exclusion list not popoulated")
}
}

282
exchanges/mock/server.go Normal file
View File

@@ -0,0 +1,282 @@
package mock
import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"log"
"net/http"
"net/http/httptest"
"net/url"
"reflect"
"strconv"
"strings"
"github.com/thrasher-corp/gocryptotrader/common"
)
// DefaultDirectory defines the main mock directory
const DefaultDirectory = "../../testdata/http_mock/"
const (
contentType = "Content-Type"
applicationURLEncoded = "application/x-www-form-urlencoded"
applicationJSON = "application/json"
textPlain = "text/plain"
)
// VCRMock defines the main mock JSON file and attributes
type VCRMock struct {
Routes map[string]map[string][]HTTPResponse `json:"routes"`
}
// NewVCRServer starts a new VCR server for replaying HTTP requests for testing
// purposes and returns the server connection details
func NewVCRServer(path string) (string, *http.Client, error) {
if path == "" {
return "", nil, errors.New("no path to json mock file found")
}
var mockFile VCRMock
contents, err := ioutil.ReadFile(path)
if err != nil {
pathing := common.SplitStrings(path, "/")
dirPathing := pathing[:len(pathing)-1]
dir := common.JoinStrings(dirPathing, "/")
err = common.CreateDir(dir)
if err != nil {
return "", nil, err
}
data, jErr := json.MarshalIndent(mockFile, "", " ")
if jErr != nil {
return "", nil, jErr
}
err = common.WriteFile(path, data)
if err != nil {
return "", nil, err
}
contents = data
}
if !json.Valid(contents) {
return "",
nil,
fmt.Errorf("contents of file %s are not valid JSON", path)
}
// Get mocking data for the specific service
err = json.Unmarshal(contents, &mockFile)
if err != nil {
return "", nil, err
}
newMux := http.NewServeMux()
// Range over routes and assign responses to explicit paths and http
// methods
if len(mockFile.Routes) != 0 {
for pattern, mockResponses := range mockFile.Routes {
RegisterHandler(pattern, mockResponses, newMux)
}
} else {
newMux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNotFound)
err := json.NewEncoder(w).Encode("There is no mock data available in file please record a new HTTP response. Please follow README.md in the mock package.")
if err != nil {
panic(err)
}
})
}
tlsServer := httptest.NewTLSServer(newMux)
return tlsServer.URL, tlsServer.Client(), nil
}
// RegisterHandler registers a generalised mock response logic for specific
// routes
func RegisterHandler(pattern string, mock map[string][]HTTPResponse, mux *http.ServeMux) {
mux.HandleFunc(pattern, func(w http.ResponseWriter, r *http.Request) {
httpResponses, ok := mock[r.Method]
if !ok {
log.Fatalf("Mock Test Failure - Method %s not present in mock file",
r.Method)
}
switch r.Method {
case http.MethodGet:
vals, err := url.ParseRequestURI(r.RequestURI)
if err != nil {
log.Fatal("Mock Test Failure - Parse request URI error", err)
}
payload, err := MatchAndGetResponse(httpResponses, vals.Query(), true)
if err != nil {
log.Fatalf("Mock Test Failure - MatchAndGetResponse error %s for %s",
err, r.RequestURI)
}
MessageWriteJSON(w, http.StatusOK, payload)
return
case http.MethodPost:
switch r.Header.Get(contentType) {
case applicationURLEncoded:
readBody, err := ioutil.ReadAll(r.Body)
if err != nil {
log.Fatal("Mock Test Failure - ReadAll error", err)
}
vals, err := url.ParseQuery(string(readBody))
if err != nil {
log.Fatal("Mock Test Failure - parse query error", err)
}
payload, err := MatchAndGetResponse(httpResponses, vals, false)
if err != nil {
log.Fatal("Mock Test Failure - MatchAndGetResponse error ", err)
}
MessageWriteJSON(w, http.StatusOK, payload)
return
case "":
payload, err := MatchAndGetResponse(httpResponses, r.URL.Query(), true)
if err != nil {
log.Fatal("Mock Test Failure - MatchAndGetResponse error ", err)
}
MessageWriteJSON(w, http.StatusOK, payload)
return
case applicationJSON:
readBody, err := ioutil.ReadAll(r.Body)
if err != nil {
log.Fatalf("Mock Test Failure - %v", err)
}
reqVals, err := DeriveURLValsFromJSONMap(readBody)
if err != nil {
log.Fatalf("Mock Test Failure - %v", err)
}
payload, err := MatchAndGetResponse(httpResponses, reqVals, false)
if err != nil {
log.Fatal("Mock Test Failure - MatchAndGetResponse error ", err)
}
MessageWriteJSON(w, http.StatusOK, payload)
return
case textPlain:
headerData, ok := r.Header["X-Gemini-Payload"]
if !ok {
log.Fatal("Mock Test Failure - Cannot find header in request")
}
base64data := strings.Join(headerData, "")
jsonThings, err := common.Base64Decode(base64data)
if err != nil {
log.Fatal("Mock Test Failure - ", err)
}
reqVals, err := DeriveURLValsFromJSONMap(jsonThings)
if err != nil {
log.Fatalf("Mock Test Failure - %v", err)
}
payload, err := MatchAndGetResponse(httpResponses, reqVals, false)
if err != nil {
log.Fatal("Mock Test Failure - MatchAndGetResponse error ", err)
}
MessageWriteJSON(w, http.StatusOK, payload)
return
default:
log.Fatalf("Mock Test Failure - Unhandled content type %v",
r.Header.Get(contentType))
}
case http.MethodDelete:
payload, err := MatchAndGetResponse(httpResponses, r.URL.Query(), true)
if err != nil {
log.Println(r.URL.Query())
log.Fatal("Mock Test Failure - MatchAndGetResponse error ", err)
}
MessageWriteJSON(w, http.StatusOK, payload)
return
default:
log.Fatal("Mock Test Failure - Unhandled HTTP method:",
r.Header.Get(contentType))
}
})
}
// MessageWriteJSON writes JSON to a connection
func MessageWriteJSON(w http.ResponseWriter, status int, data interface{}) {
w.Header().Set(contentType, applicationJSON)
w.WriteHeader(status)
if data != nil {
err := json.NewEncoder(w).Encode(data)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte(err.Error()))
log.Fatal("Mock Test Failure - JSON encode error", err)
}
}
}
// MatchAndGetResponse matches incoming request values with mockdata response
// values and returns the payload
func MatchAndGetResponse(mockData []HTTPResponse, requestVals url.Values, isQueryData bool) (json.RawMessage, error) {
for i := range mockData {
var data string
if isQueryData {
data = mockData[i].QueryString
} else {
data = mockData[i].BodyParams
}
var mockVals = url.Values{}
var err error
if json.Valid([]byte(data)) {
something := make(map[string]interface{})
err = json.Unmarshal([]byte(data), &something)
if err != nil {
return nil, err
}
for k, v := range something {
switch val := v.(type) {
case string:
mockVals.Add(k, val)
case bool:
mockVals.Add(k, strconv.FormatBool(val))
case float64:
mockVals.Add(k, strconv.FormatFloat(val, 'f', -1, 64))
case map[string]interface{}, []interface{}, nil:
mockVals.Add(k, fmt.Sprintf("%v", val))
default:
log.Println(reflect.TypeOf(val))
log.Fatal("unhandled type please add as needed")
}
}
} else {
mockVals, err = url.ParseQuery(data)
if err != nil {
return nil, err
}
}
if MatchURLVals(mockVals, requestVals) {
return mockData[i].Data, nil
}
}
return nil, errors.New("no data could be matched")
}

View File

@@ -0,0 +1,117 @@
package mock
import (
"bytes"
"encoding/json"
"io/ioutil"
"net/http"
"os"
"strings"
"testing"
"github.com/thrasher-corp/gocryptotrader/common"
)
type responsePayload struct {
Price float64 `json:"price"`
Amount float64 `json:"amount"`
Currency string `json:"currency"`
}
const queryString = "currency=btc&command=getprice"
const testFile = "test.json"
func TestNewVCRServer(t *testing.T) {
_, _, err := NewVCRServer("")
if err == nil {
t.Error("Test Failed - NewVCRServer error cannot be nil")
}
// Set up mock data
test1 := VCRMock{}
test1.Routes = make(map[string]map[string][]HTTPResponse)
test1.Routes["/test"] = make(map[string][]HTTPResponse)
rp, err := json.Marshal(responsePayload{Price: 8000.0,
Amount: 1,
Currency: "bitcoin"})
if err != nil {
t.Fatal("Test Failed - marshal error", err)
}
testValue := HTTPResponse{Data: rp, QueryString: queryString, BodyParams: queryString}
test1.Routes["/test"][http.MethodGet] = []HTTPResponse{testValue}
payload, err := json.Marshal(test1)
if err != nil {
t.Fatal("Test Failed - marshal error", err)
}
err = ioutil.WriteFile(testFile, payload, os.ModePerm)
if err != nil {
t.Fatal("Test Failed - marshal error", err)
}
deets, client, err := NewVCRServer(testFile)
if err != nil {
t.Error("Test Failed - NewVCRServer error", err)
}
common.HTTPClient = client // Set common package global HTTP Client
_, err = common.SendHTTPRequest(http.MethodGet,
"http://localhost:300/somethingElse?"+queryString,
nil,
bytes.NewBufferString(""))
if err == nil {
t.Error("Test Failed - Sending http request expected an error")
}
// Expected good outcome
r, err := common.SendHTTPRequest(http.MethodGet,
deets,
nil,
bytes.NewBufferString(""))
if err != nil {
t.Error("Test Failed - Sending http request error", err)
}
if !strings.Contains(r, "404 page not found") {
t.Error("Test Failed - Was not expecting any value returned:", r)
}
r, err = common.SendHTTPRequest(http.MethodGet,
deets+"/test?"+queryString,
nil,
bytes.NewBufferString(""))
if err != nil {
t.Error("Test Failed - Sending http request error", err)
}
var res responsePayload
err = json.Unmarshal([]byte(r), &res)
if err != nil {
t.Error("Test Failed - unmarshal error", err)
}
if res.Price != 8000 {
t.Error("Test Failed - response error expected 8000 but received:",
res.Price)
}
if res.Amount != 1 {
t.Error("Test Failed - response error expected 1 but received:",
res.Amount)
}
if res.Currency != "bitcoin" {
t.Error("Test Failed - response error expected \"bitcoin\" but received:",
res.Currency)
}
// clean up test.json file
err = os.Remove(testFile)
if err != nil {
t.Fatal("Test Failed - Remove error", err)
}
}

View File

@@ -643,7 +643,15 @@ func (o *OKGroup) SendHTTPRequest(httpMethod, requestType, requestPath string, d
errCap := errCapFormat{}
errCap.Result = true
err = o.SendPayload(strings.ToUpper(httpMethod), path, headers, bytes.NewBuffer(payload), &intermediary, authenticated, false, o.Verbose, o.HTTPDebugging)
err = o.SendPayload(strings.ToUpper(httpMethod),
path, headers,
bytes.NewBuffer(payload),
&intermediary,
authenticated,
false,
o.Verbose,
o.HTTPDebugging,
o.HTTPRecording)
if err != nil {
return err
}

View File

@@ -348,8 +348,8 @@ func (p *Poloniex) GetLoanOrders(currency string) (LoanOrders, error) {
// GetBalances returns balances for your account.
func (p *Poloniex) GetBalances() (Balance, error) {
var result interface{}
err := p.SendAuthenticatedHTTPRequest(http.MethodPost, poloniexBalances, url.Values{}, &result)
err := p.SendAuthenticatedHTTPRequest(http.MethodPost, poloniexBalances, url.Values{}, &result)
if err != nil {
return Balance{}, err
}
@@ -368,8 +368,8 @@ func (p *Poloniex) GetBalances() (Balance, error) {
// GetCompleteBalances returns complete balances from your account.
func (p *Poloniex) GetCompleteBalances() (CompleteBalances, error) {
var result interface{}
err := p.SendAuthenticatedHTTPRequest(http.MethodPost, poloniexBalancesComplete, url.Values{}, &result)
err := p.SendAuthenticatedHTTPRequest(http.MethodPost, poloniexBalancesComplete, url.Values{}, &result)
if err != nil {
return CompleteBalances{}, err
}
@@ -394,14 +394,18 @@ func (p *Poloniex) GetCompleteBalances() (CompleteBalances, error) {
func (p *Poloniex) GetDepositAddresses() (DepositAddresses, error) {
var result interface{}
addresses := DepositAddresses{}
err := p.SendAuthenticatedHTTPRequest(http.MethodPost, poloniexDepositAddresses, url.Values{}, &result)
err := p.SendAuthenticatedHTTPRequest(http.MethodPost, poloniexDepositAddresses, url.Values{}, &result)
if err != nil {
return addresses, err
}
addresses.Addresses = make(map[string]string)
data := result.(map[string]interface{})
data, ok := result.(map[string]interface{})
if !ok {
return addresses, errors.New("return val not map[string]interface{}")
}
for x, y := range data {
addresses.Addresses[x] = y.(string)
}
@@ -421,7 +425,6 @@ func (p *Poloniex) GenerateNewAddress(currency string) (string, error) {
values.Set("currency", currency)
err := p.SendAuthenticatedHTTPRequest(http.MethodPost, poloniexGenerateNewAddress, values, &resp)
if err != nil {
return "", err
}
@@ -622,7 +625,6 @@ func (p *Poloniex) Withdraw(currency, address string, amount float64) (bool, err
values.Set("address", address)
err := p.SendAuthenticatedHTTPRequest(http.MethodPost, poloniexWithdraw, values, &result)
if err != nil {
return false, err
}
@@ -649,7 +651,6 @@ func (p *Poloniex) GetTradableBalances() (map[string]map[string]float64, error)
result := Response{}
err := p.SendAuthenticatedHTTPRequest(http.MethodPost, poloniexTradableBalances, url.Values{}, &result.Data)
if err != nil {
return nil, err
}
@@ -677,7 +678,6 @@ func (p *Poloniex) TransferBalance(currency, from, to string, amount float64) (b
values.Set("toAccount", to)
err := p.SendAuthenticatedHTTPRequest(http.MethodPost, poloniexTransferBalance, values, &result)
if err != nil {
return false, err
}
@@ -743,7 +743,6 @@ func (p *Poloniex) CloseMarginPosition(currency string) (bool, error) {
result := GenericResponse{}
err := p.SendAuthenticatedHTTPRequest(http.MethodPost, poloniexMarginPositionClose, values, &result)
if err != nil {
return false, err
}
@@ -779,7 +778,6 @@ func (p *Poloniex) CreateLoanOffer(currency string, amount, rate float64, durati
result := Response{}
err := p.SendAuthenticatedHTTPRequest(http.MethodPost, poloniexCreateLoanOffer, values, &result)
if err != nil {
return 0, err
}
@@ -798,7 +796,6 @@ func (p *Poloniex) CancelLoanOffer(orderNumber int64) (bool, error) {
values.Set("orderID", strconv.FormatInt(orderNumber, 10))
err := p.SendAuthenticatedHTTPRequest(http.MethodPost, poloniexCancelLoanOffer, values, &result)
if err != nil {
return false, err
}
@@ -818,7 +815,6 @@ func (p *Poloniex) GetOpenLoanOffers() (map[string][]LoanOffer, error) {
result := Response{}
err := p.SendAuthenticatedHTTPRequest(http.MethodPost, poloniexOpenLoanOffers, url.Values{}, &result.Data)
if err != nil {
return nil, err
}
@@ -849,15 +845,10 @@ func (p *Poloniex) GetLendingHistory(start, end string) ([]LendingHistory, error
}
var resp []LendingHistory
err := p.SendAuthenticatedHTTPRequest(http.MethodPost,
return resp, p.SendAuthenticatedHTTPRequest(http.MethodPost,
poloniexLendingHistory,
vals,
&resp)
if err != nil {
return nil, err
}
return resp, nil
}
// ToggleAutoRenew allows for the autorenew of a contract
@@ -870,7 +861,6 @@ func (p *Poloniex) ToggleAutoRenew(orderNumber int64) (bool, error) {
poloniexAutoRenew,
values,
&result)
if err != nil {
return false, err
}
@@ -892,7 +882,8 @@ func (p *Poloniex) SendHTTPRequest(path string, result interface{}) error {
false,
false,
p.Verbose,
p.HTTPDebugging)
p.HTTPDebugging,
p.HTTPRecording)
}
// SendAuthenticatedHTTPRequest sends an authenticated HTTP request
@@ -901,13 +892,11 @@ func (p *Poloniex) SendAuthenticatedHTTPRequest(method, endpoint string, values
return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet,
p.Name)
}
headers := make(map[string]string)
headers["Content-Type"] = "application/x-www-form-urlencoded"
headers["Key"] = p.APIKey
n := p.Requester.GetNonce(true).String()
values.Set("nonce", n)
values.Set("nonce", p.Requester.GetNonce(true).String())
values.Set("command", endpoint)
hmac := common.GetHMAC(common.HashSHA512,
@@ -926,7 +915,8 @@ func (p *Poloniex) SendAuthenticatedHTTPRequest(method, endpoint string, values
true,
true,
p.Verbose,
p.HTTPDebugging)
p.HTTPDebugging,
p.HTTPRecording)
}
// GetFee returns an estimate of fee based on type of transaction

View File

@@ -0,0 +1,32 @@
//+build mock_test_off
// This will build if build tag mock_test_off is parsed and will do live testing
// using all tests in (exchange)_test.go
package poloniex
import (
"log"
"os"
"testing"
"github.com/thrasher-corp/gocryptotrader/config"
"github.com/thrasher-corp/gocryptotrader/exchanges/sharedtestvalues"
)
var mockTests = false
func TestMain(m *testing.M) {
cfg := config.GetConfig()
cfg.LoadConfig("../../testdata/configtest.json")
poloniexConfig, err := cfg.GetExchangeConfig("Poloniex")
if err != nil {
log.Fatal("Test Failed - Poloniex Setup() init error", err)
}
poloniexConfig.AuthenticatedAPISupport = true
poloniexConfig.APIKey = apiKey
poloniexConfig.APISecret = apiSecret
p.SetDefaults()
p.Setup(&poloniexConfig)
log.Printf(sharedtestvalues.LiveTesting, p.GetName(), p.APIUrl)
os.Exit(m.Run())
}

View File

@@ -0,0 +1,44 @@
//+build !mock_test_off
// This will build if build tag mock_test_off is not parsed and will try to mock
// all tests in _test.go
package poloniex
import (
"log"
"os"
"testing"
"github.com/thrasher-corp/gocryptotrader/config"
"github.com/thrasher-corp/gocryptotrader/exchanges/mock"
"github.com/thrasher-corp/gocryptotrader/exchanges/sharedtestvalues"
)
const mockfile = "../../testdata/http_mock/poloniex/poloniex.json"
var mockTests = true
func TestMain(m *testing.M) {
cfg := config.GetConfig()
cfg.LoadConfig("../../testdata/configtest.json")
poloniexConfig, err := cfg.GetExchangeConfig("Poloniex")
if err != nil {
log.Fatal("Test Failed - Poloniex Setup() init error", err)
}
poloniexConfig.AuthenticatedAPISupport = true
poloniexConfig.APIKey = apiKey
poloniexConfig.APISecret = apiSecret
p.SetDefaults()
p.Setup(&poloniexConfig)
serverDetails, newClient, err := mock.NewVCRServer(mockfile)
if err != nil {
log.Fatalf("Test Failed - Mock server error %s", err)
}
p.HTTPClient = newClient
p.APIUrl = serverDetails
log.Printf(sharedtestvalues.MockTesting, p.GetName(), p.APIUrl)
os.Exit(m.Run())
}

View File

@@ -7,15 +7,12 @@ import (
"github.com/gorilla/websocket"
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/config"
"github.com/thrasher-corp/gocryptotrader/currency"
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
"github.com/thrasher-corp/gocryptotrader/exchanges/sharedtestvalues"
"github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler"
)
var p Poloniex
// Please supply your own APIKEYS here for due diligence testing
const (
apiKey = ""
@@ -23,31 +20,21 @@ const (
canManipulateRealOrders = false
)
var isSetup bool
var p Poloniex
func TestSetup(t *testing.T) {
if !isSetup {
cfg := config.GetConfig()
cfg.LoadConfig("../../testdata/configtest.json")
poloniexConfig, err := cfg.GetExchangeConfig("Poloniex")
if err != nil {
t.Error("Test Failed - Poloniex Setup() init error")
}
poloniexConfig.AuthenticatedWebsocketAPISupport = true
poloniexConfig.AuthenticatedAPISupport = true
poloniexConfig.APIKey = apiKey
poloniexConfig.APISecret = apiSecret
p.SetDefaults()
p.Setup(&poloniexConfig)
isSetup = true
func areTestAPIKeysSet() bool {
if p.APIKey != "" && p.APIKey != "Key" &&
p.APISecret != "" && p.APISecret != "Secret" {
return true
}
return false
}
func TestGetTicker(t *testing.T) {
t.Parallel()
_, err := p.GetTicker()
if err != nil {
t.Error("Test faild - Poloniex GetTicker() error")
t.Error("Test Failed - Poloniex GetTicker() error", err)
}
}
@@ -115,37 +102,43 @@ func setFeeBuilder() *exchange.FeeBuilder {
// TestGetFeeByTypeOfflineTradeFee logic test
func TestGetFeeByTypeOfflineTradeFee(t *testing.T) {
t.Parallel()
var feeBuilder = setFeeBuilder()
p.GetFeeByType(feeBuilder)
if apiKey == "" || apiSecret == "" {
if feeBuilder.FeeType != exchange.OfflineTradeFee {
t.Errorf("Expected %v, received %v", exchange.OfflineTradeFee, feeBuilder.FeeType)
t.Errorf("Expected %v, received %v",
exchange.OfflineTradeFee,
feeBuilder.FeeType)
}
} else {
if feeBuilder.FeeType != exchange.CryptocurrencyTradeFee {
t.Errorf("Expected %v, received %v", exchange.CryptocurrencyTradeFee, feeBuilder.FeeType)
t.Errorf("Expected %v, received %v",
exchange.CryptocurrencyTradeFee,
feeBuilder.FeeType)
}
}
}
func TestGetFee(t *testing.T) {
t.Parallel()
TestSetup(t)
var feeBuilder = setFeeBuilder()
if areTestAPIKeysSet() {
if areTestAPIKeysSet() || mockTests {
// CryptocurrencyTradeFee Basic
if resp, err := p.GetFee(feeBuilder); resp != float64(0.002) || err != nil {
if resp, err := p.GetFee(feeBuilder); resp != float64(0.0025) || err != nil {
t.Error(err)
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f", float64(0.0015), resp)
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f",
float64(0.0025), resp)
}
// CryptocurrencyTradeFee High quantity
feeBuilder = setFeeBuilder()
feeBuilder.Amount = 1000
feeBuilder.PurchasePrice = 1000
if resp, err := p.GetFee(feeBuilder); resp != float64(2000) || err != nil {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f", float64(2000), resp)
if resp, err := p.GetFee(feeBuilder); resp != float64(2500) || err != nil {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f",
float64(2500), resp)
t.Error(err)
}
@@ -153,7 +146,8 @@ func TestGetFee(t *testing.T) {
feeBuilder = setFeeBuilder()
feeBuilder.PurchasePrice = -1000
if resp, err := p.GetFee(feeBuilder); resp != float64(0) || err != nil {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f", float64(0), resp)
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f",
float64(0), resp)
t.Error(err)
}
}
@@ -161,7 +155,8 @@ func TestGetFee(t *testing.T) {
feeBuilder = setFeeBuilder()
feeBuilder.FeeType = exchange.CryptocurrencyWithdrawalFee
if resp, err := p.GetFee(feeBuilder); resp != float64(0.001) || err != nil {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f", float64(0.001), resp)
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f",
float64(0.001), resp)
t.Error(err)
}
@@ -170,7 +165,8 @@ func TestGetFee(t *testing.T) {
feeBuilder.Pair.Base = currency.NewCode("hello")
feeBuilder.FeeType = exchange.CryptocurrencyWithdrawalFee
if resp, err := p.GetFee(feeBuilder); resp != float64(0) || err != nil {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f", float64(0), resp)
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f",
float64(0), resp)
t.Error(err)
}
@@ -178,7 +174,8 @@ func TestGetFee(t *testing.T) {
feeBuilder = setFeeBuilder()
feeBuilder.FeeType = exchange.CyptocurrencyDepositFee
if resp, err := p.GetFee(feeBuilder); resp != float64(0) || err != nil {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f", float64(0), resp)
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f",
float64(0), resp)
t.Error(err)
}
@@ -186,7 +183,8 @@ func TestGetFee(t *testing.T) {
feeBuilder = setFeeBuilder()
feeBuilder.FeeType = exchange.InternationalBankDepositFee
if resp, err := p.GetFee(feeBuilder); resp != float64(0) || err != nil {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f", float64(0), resp)
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f",
float64(0), resp)
t.Error(err)
}
@@ -195,70 +193,67 @@ func TestGetFee(t *testing.T) {
feeBuilder.FeeType = exchange.InternationalBankWithdrawalFee
feeBuilder.FiatCurrency = currency.USD
if resp, err := p.GetFee(feeBuilder); resp != float64(0) || err != nil {
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f", float64(0), resp)
t.Errorf("Test Failed - GetFee() error. Expected: %f, Received: %f",
float64(0), resp)
t.Error(err)
}
}
func TestFormatWithdrawPermissions(t *testing.T) {
t.Parallel()
TestSetup(t)
expectedResult := exchange.AutoWithdrawCryptoWithAPIPermissionText + " & " + exchange.NoFiatWithdrawalsText
expectedResult := exchange.AutoWithdrawCryptoWithAPIPermissionText +
" & " +
exchange.NoFiatWithdrawalsText
withdrawPermissions := p.FormatWithdrawPermissions()
if withdrawPermissions != expectedResult {
t.Errorf("Expected: %s, Received: %s", expectedResult, withdrawPermissions)
t.Errorf("Expected: %s, Received: %s",
expectedResult,
withdrawPermissions)
}
}
func TestGetActiveOrders(t *testing.T) {
t.Parallel()
TestSetup(t)
var getOrdersRequest = exchange.GetOrdersRequest{
OrderType: exchange.AnyOrderType,
}
_, err := p.GetActiveOrders(&getOrdersRequest)
if areTestAPIKeysSet() && err != nil {
t.Errorf("Could not get open orders: %s", err)
} else if !areTestAPIKeysSet() && err == nil {
t.Error("Expecting an error when no keys are set")
switch {
case areTestAPIKeysSet() && err != nil:
t.Error("Test Failed - GetActiveOrders() error", err)
case !areTestAPIKeysSet() && !mockTests && err == nil:
t.Error("Test Failed - Expecting an error when no keys are set")
case mockTests && err != nil:
t.Error("Test Failed - Mock GetActiveOrders() err", err)
}
}
func TestGetOrderHistory(t *testing.T) {
t.Parallel()
TestSetup(t)
var getOrdersRequest = exchange.GetOrdersRequest{
OrderType: exchange.AnyOrderType,
}
_, err := p.GetOrderHistory(&getOrdersRequest)
if areTestAPIKeysSet() && err != nil {
switch {
case areTestAPIKeysSet() && err != nil:
t.Errorf("Could not get order history: %s", err)
} else if !areTestAPIKeysSet() && err == nil {
case !areTestAPIKeysSet() && err == nil && !mockTests:
t.Error("Expecting an error when no keys are set")
case mockTests && err != nil:
t.Errorf("Could not mock get order history: %s", err)
}
}
// Any tests below this line have the ability to impact your orders on the exchange. Enable canManipulateRealOrders to run them
// ----------------------------------------------------------------------------------------------------------------------------
func areTestAPIKeysSet() bool {
if p.APIKey != "" && p.APIKey != "Key" &&
p.APISecret != "" && p.APISecret != "Secret" {
return true
}
return false
}
func TestSubmitOrder(t *testing.T) {
t.Parallel()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
if areTestAPIKeysSet() && !canManipulateRealOrders && !mockTests {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -274,46 +269,43 @@ func TestSubmitOrder(t *testing.T) {
1,
10,
"hi")
if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) {
switch {
case areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced):
t.Errorf("Order failed to be placed: %v", err)
} else if !areTestAPIKeysSet() && err == nil {
case !areTestAPIKeysSet() && !mockTests && err == nil:
t.Error("Expecting an error when no keys are set")
case mockTests && err != nil:
t.Error("Test Failed - Mock SubmitOrder() err", err)
}
}
func TestCancelExchangeOrder(t *testing.T) {
t.Parallel()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
if areTestAPIKeysSet() && !canManipulateRealOrders && !mockTests {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
currencyPair := currency.NewPair(currency.LTC, currency.BTC)
var orderCancellation = &exchange.OrderCancellation{
OrderID: "1",
WalletAddress: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB",
AccountID: "1",
CurrencyPair: currencyPair,
CurrencyPair: currency.NewPair(currency.LTC, currency.BTC),
}
err := p.CancelOrder(orderCancellation)
if !areTestAPIKeysSet() && err == nil {
switch {
case !areTestAPIKeysSet() && !mockTests && err == nil:
t.Error("Expecting an error when no keys are set")
}
if areTestAPIKeysSet() && err != nil {
case areTestAPIKeysSet() && err != nil:
t.Errorf("Could not cancel orders: %v", err)
case mockTests && err != nil:
t.Error("Test Failed - Mock CancelExchangeOrder() err", err)
}
}
func TestCancelAllExchangeOrders(t *testing.T) {
t.Parallel()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
if areTestAPIKeysSet() && !canManipulateRealOrders && !mockTests {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
@@ -327,14 +319,14 @@ func TestCancelAllExchangeOrders(t *testing.T) {
}
resp, err := p.CancelAllOrders(orderCancellation)
if !areTestAPIKeysSet() && err == nil {
switch {
case !areTestAPIKeysSet() && !mockTests && err == nil:
t.Error("Expecting an error when no keys are set")
}
if areTestAPIKeysSet() && err != nil {
case areTestAPIKeysSet() && err != nil:
t.Errorf("Could not cancel orders: %v", err)
case mockTests && err != nil:
t.Error("Test Failed - Mock CancelAllExchangeOrders() err", err)
}
if len(resp.OrderStatus) > 0 {
t.Errorf("%v orders failed to cancel", len(resp.OrderStatus))
}
@@ -342,87 +334,88 @@ func TestCancelAllExchangeOrders(t *testing.T) {
func TestModifyOrder(t *testing.T) {
t.Parallel()
if areTestAPIKeysSet() && !canManipulateRealOrders && !mockTests {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
_, err := p.ModifyOrder(&exchange.ModifyOrder{OrderID: "1337", Price: 1337})
if err == nil {
t.Error("Test Failed - ModifyOrder() error")
switch {
case areTestAPIKeysSet() && err != nil && mockTests:
t.Error("Test Failed - ModifyOrder() error", err)
case !areTestAPIKeysSet() && !mockTests && err == nil:
t.Error("Test Failed - ModifyOrder() error cannot be nil")
case mockTests && err != nil:
t.Error("Test Failed - Mock ModifyOrder() err", err)
}
}
func TestWithdraw(t *testing.T) {
t.Parallel()
TestSetup(t)
var withdrawCryptoRequest = exchange.WithdrawRequest{
Amount: 100,
Amount: 0,
Currency: currency.LTC,
Address: "1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB",
Description: "WITHDRAW IT ALL",
}
if areTestAPIKeysSet() && !canManipulateRealOrders {
if areTestAPIKeysSet() && !canManipulateRealOrders && !mockTests {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
_, err := p.WithdrawCryptocurrencyFunds(&withdrawCryptoRequest)
if !areTestAPIKeysSet() && err == nil {
t.Error("Expecting an error when no keys are set")
}
if areTestAPIKeysSet() && err != nil {
switch {
case areTestAPIKeysSet() && err != nil:
t.Errorf("Withdraw failed to be placed: %v", err)
case !areTestAPIKeysSet() && !mockTests && err == nil:
t.Error("Expecting an error when no keys are set")
case mockTests && err != nil:
t.Error("Test Failed - Mock Withdraw() err", err)
}
}
func TestWithdrawFiat(t *testing.T) {
t.Parallel()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
if areTestAPIKeysSet() && !canManipulateRealOrders && !mockTests {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
var withdrawFiatRequest = exchange.WithdrawRequest{}
var withdrawFiatRequest exchange.WithdrawRequest
_, err := p.WithdrawFiatFunds(&withdrawFiatRequest)
if err != common.ErrFunctionNotSupported {
t.Errorf("Expected '%v', received: '%v'", common.ErrFunctionNotSupported, err)
t.Errorf("Expected '%v', received: '%v'",
common.ErrFunctionNotSupported, err)
}
}
func TestWithdrawInternationalBank(t *testing.T) {
t.Parallel()
TestSetup(t)
if areTestAPIKeysSet() && !canManipulateRealOrders {
if areTestAPIKeysSet() && !canManipulateRealOrders && !mockTests {
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
}
var withdrawFiatRequest = exchange.WithdrawRequest{}
var withdrawFiatRequest exchange.WithdrawRequest
_, err := p.WithdrawFiatFundsToInternationalBank(&withdrawFiatRequest)
if err != common.ErrFunctionNotSupported {
t.Errorf("Expected '%v', received: '%v'", common.ErrFunctionNotSupported, err)
t.Errorf("Expected '%v', received: '%v'",
common.ErrFunctionNotSupported, err)
}
}
func TestGetDepositAddress(t *testing.T) {
t.Parallel()
TestSetup(t)
if areTestAPIKeysSet() {
_, err := p.GetDepositAddress(currency.DASH, "")
if err != nil {
t.Error("Test Failed - GetDepositAddress()", err)
}
} else {
_, err := p.GetDepositAddress(currency.DASH, "")
if err == nil {
t.Error("Test Failed - GetDepositAddress()")
}
_, err := p.GetDepositAddress(currency.DASH, "")
switch {
case areTestAPIKeysSet() && err != nil:
t.Error("Test Failed - GetDepositAddress()", err)
case !areTestAPIKeysSet() && !mockTests && err == nil:
t.Error("Test Failed - GetDepositAddress() cannot be nil")
case mockTests && err != nil:
t.Error("Test Failed - Mock GetDepositAddress() err", err)
}
}
func TestWsHandleAccountData(t *testing.T) {
t.Parallel()
TestSetup(t)
p.Websocket.DataHandler = sharedtestvalues.GetWebsocketInterfaceChannelOverride()
jsons := []string{
`[["n",225,807230187,0,"1000.00000000","0.10000000","2018-11-07 16:42:42"],["b",267,"e","-0.10000000"]]`,
@@ -442,7 +435,7 @@ func TestWsHandleAccountData(t *testing.T) {
// TestWsAuth dials websocket, sends login request.
// Will receive a message only on failure
func TestWsAuth(t *testing.T) {
TestSetup(t)
t.Parallel()
if !p.Websocket.IsEnabled() && !p.AuthenticatedWebsocketAPISupport || !areTestAPIKeysSet() {
t.Skip(wshandler.WebsocketNotEnabled)
}

View File

@@ -14,6 +14,7 @@ import (
"time"
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/exchanges/mock"
"github.com/thrasher-corp/gocryptotrader/exchanges/nonce"
log "github.com/thrasher-corp/gocryptotrader/logger"
)
@@ -70,6 +71,7 @@ type Job struct {
AuthRequest bool
Verbose bool
HTTPDebugging bool
Record bool
}
// NewRateLimit creates a new RateLimit
@@ -264,13 +266,18 @@ func (r *Requester) checkRequest(method, path string, body io.Reader, headers ma
}
// DoRequest performs a HTTP/HTTPS request with the supplied params
func (r *Requester) DoRequest(req *http.Request, path string, body io.Reader, result interface{}, authRequest, verbose, httpDebug bool) error {
func (r *Requester) DoRequest(req *http.Request, path string, body io.Reader, result interface{}, authRequest, verbose, httpDebug, httpRecord bool) error {
if verbose {
log.Debugf("%s exchange request path: %s requires rate limiter: %v", r.Name, path, r.RequiresRateLimiter())
log.Debugf("%s exchange request path: %s requires rate limiter: %v",
r.Name,
path,
r.RequiresRateLimiter())
for k, d := range req.Header {
log.Debugf("%s exchange request header [%s]: %s", r.Name, k, d)
}
log.Debug(body)
log.Debugf("%s exchange request type: %s", r.Name, req.Method)
log.Debugf("%s exchange request body: %v", r.Name, body)
}
var timeoutError error
@@ -328,6 +335,14 @@ func (r *Requester) DoRequest(req *http.Request, path string, body io.Reader, re
return err
}
if httpRecord {
// This dumps http responses for future mocking implementations
err = mock.HTTPRecord(resp, r.Name, contents)
if err != nil {
return fmt.Errorf("mock recording failure %s", err)
}
}
if resp.StatusCode != 200 && resp.StatusCode != 201 && resp.StatusCode != 202 {
err = fmt.Errorf("unsuccessful HTTP status code: %d", resp.StatusCode)
if verbose {
@@ -371,7 +386,7 @@ func (r *Requester) worker() {
if !r.IsRateLimited(x.AuthRequest) {
r.IncrementRequests(x.AuthRequest)
err := r.DoRequest(x.Request, x.Path, x.Body, x.Result, x.AuthRequest, x.Verbose, x.HTTPDebugging)
err := r.DoRequest(x.Request, x.Path, x.Body, x.Result, x.AuthRequest, x.Verbose, x.HTTPDebugging, x.Record)
x.JobResult <- &JobResult{
Error: err,
Result: x.Result,
@@ -395,7 +410,7 @@ func (r *Requester) worker() {
log.Debugf("%s request. No longer rate limited! Doing request", r.Name)
}
err := r.DoRequest(x.Request, x.Path, x.Body, x.Result, x.AuthRequest, x.Verbose, x.HTTPDebugging)
err := r.DoRequest(x.Request, x.Path, x.Body, x.Result, x.AuthRequest, x.Verbose, x.HTTPDebugging, x.Record)
x.JobResult <- &JobResult{
Error: err,
Result: x.Result,
@@ -408,7 +423,7 @@ func (r *Requester) worker() {
}
// SendPayload handles sending HTTP/HTTPS requests
func (r *Requester) SendPayload(method, path string, headers map[string]string, body io.Reader, result interface{}, authRequest, nonceEnabled, verbose, httpDebugging bool) error {
func (r *Requester) SendPayload(method, path string, headers map[string]string, body io.Reader, result interface{}, authRequest, nonceEnabled, verbose, httpDebugging, record bool) error {
if !nonceEnabled {
r.lock()
}
@@ -444,7 +459,7 @@ func (r *Requester) SendPayload(method, path string, headers map[string]string,
if !r.RequiresRateLimiter() {
r.unlock()
return r.DoRequest(req, path, body, result, authRequest, verbose, httpDebugging)
return r.DoRequest(req, path, body, result, authRequest, verbose, httpDebugging, record)
}
if len(r.Jobs) == maxRequestJobs {
@@ -473,6 +488,7 @@ func (r *Requester) SendPayload(method, path string, headers map[string]string,
AuthRequest: authRequest,
Verbose: verbose,
HTTPDebugging: httpDebugging,
Record: record,
}
if verbose {

View File

@@ -200,7 +200,7 @@ func TestCheckRequest(t *testing.T) {
func TestDoRequest(t *testing.T) {
var test = new(Requester)
err := test.SendPayload(http.MethodGet, "https://www.google.com", nil, nil, nil, false, false, true, false)
err := test.SendPayload(http.MethodGet, "https://www.google.com", nil, nil, nil, false, false, true, false, false)
if err == nil {
t.Fatal("not iniitalised")
}
@@ -211,17 +211,17 @@ func TestDoRequest(t *testing.T) {
}
r.Name = "bitfinex"
err = r.SendPayload("BLAH", "https://www.google.com", nil, nil, nil, false, false, true, false)
err = r.SendPayload("BLAH", "https://www.google.com", nil, nil, nil, false, false, true, false, false)
if err == nil {
t.Fatal("unexpected values")
}
err = r.SendPayload(http.MethodGet, "", nil, nil, nil, false, false, true, false)
err = r.SendPayload(http.MethodGet, "", nil, nil, nil, false, false, true, false, false)
if err == nil {
t.Fatal("unexpected values")
}
err = r.SendPayload(http.MethodGet, "https://www.google.com", nil, nil, nil, false, false, true, false)
err = r.SendPayload(http.MethodGet, "https://www.google.com", nil, nil, nil, false, false, true, false, false)
if err != nil {
t.Fatal("unexpected values")
}
@@ -233,7 +233,7 @@ func TestDoRequest(t *testing.T) {
r.SetRateLimit(false, time.Second, 0)
r.SetRateLimit(true, time.Second, 0)
err = r.SendPayload(http.MethodGet, "https://www.google.com", nil, nil, nil, false, false, true, false)
err = r.SendPayload(http.MethodGet, "https://www.google.com", nil, nil, nil, false, false, true, false, false)
if err != nil {
t.Fatal("unexpected values")
}
@@ -250,7 +250,7 @@ func TestDoRequest(t *testing.T) {
t.Fatal("unexepcted values")
}
err = r.SendPayload(http.MethodGet, "https://www.google.com", nil, nil, nil, false, false, true, false)
err = r.SendPayload(http.MethodGet, "https://www.google.com", nil, nil, nil, false, false, true, false, false)
if err != nil {
t.Fatal("unexpected values")
}
@@ -261,27 +261,27 @@ func TestDoRequest(t *testing.T) {
t.Fatal("unexepcted values")
}
err = r.SendPayload(http.MethodGet, "https://www.google.com", nil, nil, nil, true, false, true, false)
err = r.SendPayload(http.MethodGet, "https://www.google.com", nil, nil, nil, true, false, true, false, false)
if err != nil {
t.Fatal("unexpected values")
}
var result interface{}
err = r.SendPayload(http.MethodGet, "https://www.google.com", nil, nil, result, false, false, true, false)
err = r.SendPayload(http.MethodGet, "https://www.google.com", nil, nil, result, false, false, true, false, false)
if err != nil {
t.Fatal(err)
}
headers := make(map[string]string)
headers["content-type"] = "content/text"
err = r.SendPayload(http.MethodPost, "https://bitfinex.com", headers, nil, result, false, false, true, false)
err = r.SendPayload(http.MethodPost, "https://bitfinex.com", headers, nil, result, false, false, true, false, false)
if err != nil {
t.Fatal(err)
}
r.StartCycle()
r.UnauthLimit.SetRequests(100)
err = r.SendPayload(http.MethodGet, "https://www.google.com", nil, nil, result, false, false, false, false)
err = r.SendPayload(http.MethodGet, "https://www.google.com", nil, nil, result, false, false, false, false, false)
if err != nil {
t.Fatal("unexpected values")
}
@@ -297,7 +297,7 @@ func TestDoRequest(t *testing.T) {
}
r.HTTPClient.Timeout = 1 * time.Second
err = r.SendPayload(http.MethodPost, "https://httpstat.us/200?sleep=20000", nil, nil, nil, false, false, true, false)
err = r.SendPayload(http.MethodPost, "https://httpstat.us/200?sleep=20000", nil, nil, nil, false, false, true, false, false)
if err == nil {
t.Fatal(err)
}
@@ -327,6 +327,6 @@ func BenchmarkRequestLockMech(b *testing.B) {
var r = new(Requester)
var meep interface{}
for n := 0; n < b.N; n++ {
r.SendPayload(http.MethodGet, "127.0.0.1", nil, nil, &meep, false, false, false, false)
r.SendPayload(http.MethodGet, "127.0.0.1", nil, nil, &meep, false, false, false, false, false)
}
}

View File

@@ -13,6 +13,9 @@ const (
// WebsocketChannelOverrideCapacity used in websocket testing
// Defines channel capacity as defaults size can block tests
WebsocketChannelOverrideCapacity = 20
MockTesting = "Mock testing framework in use for %s exchange @ %s on REST endpoints only"
LiveTesting = "Mock testing bypassed; live testing of REST endpoints in use for %s exchange @ %s"
)
// GetWebsocketInterfaceChannelOverride returns a new interface based channel

View File

@@ -340,7 +340,8 @@ func (y *Yobit) SendHTTPRequest(path string, result interface{}) error {
false,
false,
y.Verbose,
y.HTTPDebugging)
y.HTTPDebugging,
y.HTTPRecording)
}
// SendAuthenticatedHTTPRequest sends an authenticated HTTP request to Yobit
@@ -384,7 +385,8 @@ func (y *Yobit) SendAuthenticatedHTTPRequest(path string, params url.Values, res
true,
true,
y.Verbose,
y.HTTPDebugging)
y.HTTPDebugging,
y.HTTPRecording)
}
// GetFee returns an estimate of fee based on type of transaction

View File

@@ -391,7 +391,16 @@ func (z *ZB) GetCryptoAddress(currency currency.Code) (UserAddress, error) {
// SendHTTPRequest sends an unauthenticated HTTP request
func (z *ZB) SendHTTPRequest(path string, result interface{}) error {
return z.SendPayload(http.MethodGet, path, nil, nil, result, false, false, z.Verbose, z.HTTPDebugging)
return z.SendPayload(http.MethodGet,
path,
nil,
nil,
result,
false,
false,
z.Verbose,
z.HTTPDebugging,
z.HTTPRecording)
}
// SendAuthenticatedHTTPRequest sends authenticated requests to the zb API
@@ -429,7 +438,8 @@ func (z *ZB) SendAuthenticatedHTTPRequest(httpMethod string, params url.Values,
true,
false,
z.Verbose,
z.HTTPDebugging)
z.HTTPDebugging,
z.HTTPRecording)
if err != nil {
return err
}

2404
testdata/http_mock/anx/anx.json vendored Normal file

File diff suppressed because it is too large Load Diff

45467
testdata/http_mock/binance/binance.json vendored Normal file

File diff suppressed because it is too large Load Diff

42842
testdata/http_mock/bitstamp/bitstamp.json vendored Normal file

File diff suppressed because it is too large Load Diff

19
testdata/http_mock/exclusion.json vendored Executable file
View File

@@ -0,0 +1,19 @@
{
"headers": [
"Key",
"X-Mbx-Apikey",
"Rest-Key",
"Apiauth-Key",
"X-Gemini-Apikey"
],
"variables": [
"bsb",
"user",
"name",
"real_name",
"receiver_name",
"account_number",
"username",
"login"
]
}

2734
testdata/http_mock/gemini/gemini.json vendored Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

9031
testdata/http_mock/poloniex/poloniex.json vendored Normal file

File diff suppressed because it is too large Load Diff