Browse Source

Change From Either Types to Exceptions (#4)

master
Taylor Bockman 6 years ago committed by GitHub
parent
commit
de32c1049c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 85
      README.md
  2. 1
      build.gradle
  3. 2
      src/main/java/com/sigmaflare/binancej/BaseBinanceApi.java
  4. 64
      src/main/java/com/sigmaflare/binancej/GeneralUtilities.java
  5. 61
      src/main/java/com/sigmaflare/binancej/HttpRequests.java
  6. 178
      src/main/java/com/sigmaflare/binancej/MarketData.java
  7. 4
      src/main/java/com/sigmaflare/binancej/entities/ServiceError.java
  8. 4
      src/main/java/com/sigmaflare/binancej/exceptions/BinanceServiceUnreachableException.java
  9. 11
      src/main/java/com/sigmaflare/binancej/exceptions/InternalServiceErrorException.java
  10. 10
      src/main/java/com/sigmaflare/binancej/exceptions/IpBannedException.java
  11. 11
      src/main/java/com/sigmaflare/binancej/exceptions/MalformedRequestException.java
  12. 10
      src/main/java/com/sigmaflare/binancej/exceptions/RateLimitExceededException.java
  13. 7
      src/main/java/com/sigmaflare/binancej/exceptions/UnexpectedErrorException.java
  14. 145
      src/test/java/com/sigmaflare/binancej/CandlestickMethodTests.java
  15. 32
      src/test/java/com/sigmaflare/binancej/ExchangeInfoMethodTests.java
  16. 42
      src/test/java/com/sigmaflare/binancej/HttpErrorThrowerTests.java
  17. 61
      src/test/java/com/sigmaflare/binancej/OrderBookDepthTests.java
  18. 32
      src/test/java/com/sigmaflare/binancej/PingMethodTests.java
  19. 59
      src/test/java/com/sigmaflare/binancej/TickerPriceTests.java
  20. 31
      src/test/java/com/sigmaflare/binancej/TimeMethodTests.java

85
README.md

@ -11,9 +11,9 @@ BinanceJ is released under the MIT license.
## Rate Limiting
BinanceJ does not perform rate limiting, so you are responsible for limiting your requests. Pay attention to the errors
you receive from Binance and check to see if they are a 429. If they are, you need to back off or face a temporary
ban.
BinanceJ will throw a `RateLimitExceededException` when a HTTP 429 error code comes back. This is an important
exception to catch and handle because if you do not back off they will issue a temporary ban. If you don't listen,
you'll be receiving `IpBannedException`s.
## API Coverage
@ -26,110 +26,77 @@ The following endpoints are currently covered:
5. `GET /api/v1/klines`
6. `GET /api/v3/ticker/price`
More will be added in future PRs as they become necessary to me or the people using the library.
More will be added in future PRs as they become necessary to me or the people using the library.
## Return Types
## HTTP Exceptions
All functions return an `Either` type. For those of you not in the know - an Either type represents a disjoint union.
Typically this means a "success" and "failure" case that must be handled uniquely. They work well with stream
processing and are a very natural way to delineate success and failure cleanly. Additionally it eliminates the pattern
of throwing exceptions on errors, and allows exceptions to be reserved for truly exceptional behavior as intended.
### Checked Exceptions
By convention I adapted the Haskell Either type style to this code. What this means is that the Either type's Right
value is the correct one (mnemonic: "right" as in correct) and the Left value is the `ServiceError`.
Any function in the API can throw a handful of exceptions related to HTTP:
### How do I extract the raw value from the Either type?
1. `IpBannedException`: Your IP has been banned after ignoring rate limit exceeded messages
2. `RateLimitExceededException`: Your IP has exceeded the rate limit and needs to slow down
3. `InternalServiceErrorException`: An error occurred on Binance's server side (see note below)
4. `MalformedRequestException`: A malformed request was sent, the error exists on the sender's side (check error code and message)
There are a few ways to do this using Ambivalent, but the most prevalent way in BinanceJ's tests is:
**Note**: A 504 error in general should not be treated as an error (though it is "exceptional" behavior). In the case of `InternalServiceErrorException`s it is critical to check the `code` member of the exception.
```java
Either<TypeA, TypeB> val = clazz.getThing();
// Good path case
if(val.isRight()) {
TypeB thing = val.right().join(Function.identity(), Function.identity());
}
The "more general" HTTP exceptions (`MalformedRequestException` and `InternalServiceErrorException`) have a code
component so you can check the specifics against the [Binance Error Documentation](https://github.com/binance-exchange/binance-official-api-docs/blob/master/errors.md).
```
## Standard Exceptions
Alternatively you are welcome to use `Helpers.extractEitherValueSafely` method to make your code cleaner.
### Runtime Exceptions
In the event an unknown error occurs, the code will throw an `UnexpectedErrorException`. These are generally complete showstoppers.
## Examples
### Server alive check with ping
If the ping method does not throw an exception, the ping was successful.
```java
GeneralUtilities generalUtilities = GeneralUtilities.builder().apiKey("KEY").secretKey("KEY").build();
Either<ServiceError, Ping> res = generalUtilities.ping();
if(res.isRight()) {
// Successful ping
}
generalUtilities.ping();
```
### Getting current server time
```java
GeneralUtilities generalUtilities = GeneralUtilities.builder().apiKey("KEY").secretKey("KEY").build();
Either<ServiceError, ServerTime> res = generalUtilities.getServerTime();
if(res.isRight()) {
ServerTime response = Helpers.extractEitherValueSafely(res.right());
//...
}
ServerTime = generalUtilities.getServerTime();
```
### Getting Exchange Information
```java
GeneralUtilities generalUtilities = GeneralUtilities.builder().apiKey("KEY").secretKey("KEY").build();
Either<ServiceError, ExchangeInfo> res = generalUtilities.getExchangeInfo();
if(res.isRight()) {
ExchangeInfo response = Helpers.extractEitherValueSafely(res.right());
//...
}
ExchangeInfo res = generalUtilities.getExchangeInfo();
```
### Getting Candlestick data
```java
MarketData marketData = MarketData.builder().apiKey("KEY").secretKey("KEY").build();
Either<ServiceError, List<Candlestick>> res = marketData.getCandlestickData("ETHBTC", Interval.ONE_MINUTE);
if(res.isRight()) {
List<Candlestick> data = Helpers.extractEitherValueSafely(res.right());
//...
}
List<Candlestick> res = marketData.getCandlestickData("ETHBTC", Interval.ONE_MINUTE);
```
### Getting market depth
```java
MarketData marketData = MarketData.builder().apiKey("KEY").secretKey("KEY").build();
Either<ServiceError, OrderBookDepth> res = marketData.getOrderBookDepth("ETHBTC", 1000);
if(res.isRight()) {
OrderBookDepth orderBookDepth = Helpers.extractEitherValueSafely(res.right());
//...
}
OrderBookDepth res = marketData.getOrderBookDepth("ETHBTC", 1000);
```
### Getting ticker price for an instrument
```java
MarketData marketData = MarketData.builder().apiKey("KEY").secretKey("KEY").build();
Either<ServiceError, TickerPrice> res = marketData.getTickerPriceForSymbol("ETHBTC");
if(res.isRight()) {
TickerPrice tickerPrice = Helpers.extractEitherValueSafely(res.right());
//...
}
TickerPrice res = marketData.getTickerPriceForSymbol("ETHBTC");
```
## Contributing
Head over to our [CONTRIBUTING.md](CONTRIBUTING.md) to get started. All features are welcome as long as they are
Head over to our [CONTRIBUTING.md](CONTRIBUTING.md) to get started. All features are welcome as long as they are
in scope of the API and following the contributing guide.

1
build.gradle

@ -10,7 +10,6 @@ sourceCompatibility = 1.8
dependencies {
compile group: 'org.projectlombok', name: 'lombok', version: '1.16.20'
compile group: 'com.codepoetics', name: 'ambivalence', version: '0.2'
compile group: 'com.fasterxml.jackson.core', name: 'jackson-core', version: '2.9.5'
compile group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.9.5'

2
src/main/java/com/sigmaflare/binancej/BaseBinanceApi.java

@ -12,7 +12,7 @@ public abstract class BaseBinanceApi {
protected final String secretKey;
protected final CloseableHttpClient closeableHttpClient;
protected static final ObjectMapper mapper = Helpers.objectMapperBuilder();
protected static final ObjectMapper mapper = HttpRequests.objectMapperBuilder();
public BaseBinanceApi(String apiKey, String secretKey) {
this.apiKey = apiKey;

64
src/main/java/com/sigmaflare/binancej/GeneralUtilities.java

@ -1,11 +1,12 @@
package com.sigmaflare.binancej;
import com.codepoetics.ambivalence.Either;
import com.sigmaflare.binancej.entities.ExchangeInfo;
import com.sigmaflare.binancej.entities.Ping;
import com.sigmaflare.binancej.entities.ServiceError;
import com.sigmaflare.binancej.entities.Time;
import com.sigmaflare.binancej.exceptions.BinanceServiceException;
import com.sigmaflare.binancej.exceptions.BinanceServiceUnreachableException;
import com.sigmaflare.binancej.exceptions.UnexpectedErrorException;
import lombok.Builder;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntity;
@ -19,7 +20,7 @@ import java.io.IOException;
import static com.sigmaflare.binancej.Constant.NO_RESPONSE_TEXT;
import static com.sigmaflare.binancej.Constant.NO_RESPONSE_TEXT_FORMATTED;
import static com.sigmaflare.binancej.Helpers.buildGetRequestFromEndpoint;
import static com.sigmaflare.binancej.HttpRequests.buildGetRequestFromEndpoint;
/**
* GeneralUtilities is a container class for methods that interact with the infrastructure
@ -44,9 +45,15 @@ public class GeneralUtilities extends BaseBinanceApi {
/**
* Hits the ping endpoint to check if the service is alive.
*
* @return empty Ping object if it returned 200, otherwise ServiceError
* @return Empty Ping object
* @throws com.sigmaflare.binancej.exceptions.IpBannedException If the IP is banned
* @throws com.sigmaflare.binancej.exceptions.RateLimitExceededException If the rate limit is exceeded
* @throws com.sigmaflare.binancej.exceptions.MalformedRequestException If the user's request is malformed
* @throws com.sigmaflare.binancej.exceptions.InternalServiceErrorException If the error occurs on Binance's side
* @throws com.sigmaflare.binancej.exceptions.BinanceServiceUnreachableException If the service is unreachable
* @throws UnexpectedErrorException If an unexpected exception occurs
*/
public Either<ServiceError, Ping> ping() throws BinanceServiceUnreachableException {
public Ping ping() throws BinanceServiceException {
final HttpGet request = buildGetRequestFromEndpoint(PING_URL, apiKey);
try {
@ -63,24 +70,30 @@ public class GeneralUtilities extends BaseBinanceApi {
String response = EntityUtils.toString(httpEntity);
if (!Helpers.statusCodeIsOk(sl.getStatusCode())) {
return Either.ofLeft(mapper.readValue(response, ServiceError.class));
if (!HttpRequests.statusCodeIsOk(sl.getStatusCode())) {
ServiceError error = mapper.readValue(response, ServiceError.class);
throw HttpRequests.buildHttpException(sl.getStatusCode(), error);
}
return Either.ofRight(new Ping());
return new Ping();
}
} catch (IOException e) {
throw new BinanceServiceUnreachableException(e.getMessage(), e.getCause());
throw new UnexpectedErrorException(e.getMessage(), e.getCause());
}
}
/**
* Gets the current server time on Binance's servers
*
* @return A Time object if successful, otherwise an ServiceError object
* @throws BinanceServiceUnreachableException Throws when the request fails
* @return A Time object populated with the current server time
* @throws com.sigmaflare.binancej.exceptions.IpBannedException If the IP is banned
* @throws com.sigmaflare.binancej.exceptions.RateLimitExceededException If the rate limit is exceeded
* @throws com.sigmaflare.binancej.exceptions.MalformedRequestException If the user's request is malformed
* @throws com.sigmaflare.binancej.exceptions.InternalServiceErrorException If the error occurs on Binance's side
* @throws com.sigmaflare.binancej.exceptions.BinanceServiceUnreachableException If the service is unreachable
* @throws UnexpectedErrorException If an unexpected exception occurs
*/
public Either<ServiceError, Time> getServerTime() throws BinanceServiceUnreachableException {
public Time getServerTime() throws BinanceServiceException {
final HttpGet request = buildGetRequestFromEndpoint(TIME_URL, apiKey);
try {
@ -97,24 +110,30 @@ public class GeneralUtilities extends BaseBinanceApi {
String response = EntityUtils.toString(httpEntity);
if (!Helpers.statusCodeIsOk(sl.getStatusCode())) {
return Either.ofLeft(mapper.readValue(response, ServiceError.class));
if (!HttpRequests.statusCodeIsOk(sl.getStatusCode())) {
ServiceError error = mapper.readValue(response, ServiceError.class);
throw HttpRequests.buildHttpException(sl.getStatusCode(), error);
}
return Either.ofRight(mapper.readValue(response, Time.class));
return mapper.readValue(response, Time.class);
}
} catch (IOException e) {
throw new BinanceServiceUnreachableException(e.getMessage(), e.getCause());
throw new UnexpectedErrorException(e.getMessage(), e.getCause());
}
}
/**
* Retrieves exchange information
*
* @return An ExchangeInfo when successful, otherwise an ServiceError
* @throws BinanceServiceUnreachableException If the service cannot be reached
* @return An ExchangeInfo populated with exchange information
* @throws com.sigmaflare.binancej.exceptions.IpBannedException If the IP is banned
* @throws com.sigmaflare.binancej.exceptions.RateLimitExceededException If the rate limit is exceeded
* @throws com.sigmaflare.binancej.exceptions.MalformedRequestException If the user's request is malformed
* @throws com.sigmaflare.binancej.exceptions.InternalServiceErrorException If the error occurs on Binance's side
* @throws com.sigmaflare.binancej.exceptions.BinanceServiceUnreachableException If the service is unreachable
* @throws UnexpectedErrorException If an unexpected exception occurs
*/
public Either<ServiceError, ExchangeInfo> getExchangeInfo() throws BinanceServiceUnreachableException {
public ExchangeInfo getExchangeInfo() throws BinanceServiceException {
final HttpGet request = buildGetRequestFromEndpoint(EXCHANGE_INFO_URL, apiKey);
try {
@ -131,14 +150,15 @@ public class GeneralUtilities extends BaseBinanceApi {
String response = EntityUtils.toString(httpEntity);
if (!Helpers.statusCodeIsOk(sl.getStatusCode())) {
return Either.ofLeft(mapper.readValue(response, ServiceError.class));
if (!HttpRequests.statusCodeIsOk(sl.getStatusCode())) {
ServiceError error = mapper.readValue(response, ServiceError.class);
throw HttpRequests.buildHttpException(sl.getStatusCode(), error);
}
return Either.ofRight(mapper.readValue(response, ExchangeInfo.class));
return mapper.readValue(response, ExchangeInfo.class);
}
} catch (IOException e) {
throw new BinanceServiceUnreachableException(e.getMessage(), e.getCause());
throw new UnexpectedErrorException(e.getMessage(), e.getCause());
}
}

61
src/main/java/com/sigmaflare/binancej/Helpers.java → src/main/java/com/sigmaflare/binancej/HttpRequests.java

@ -1,18 +1,21 @@
package com.sigmaflare.binancej;
import com.codepoetics.ambivalence.LeftProjection;
import com.codepoetics.ambivalence.RightProjection;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.sigmaflare.binancej.entities.ServiceError;
import com.sigmaflare.binancej.exceptions.BinanceServiceException;
import com.sigmaflare.binancej.exceptions.InternalServiceErrorException;
import com.sigmaflare.binancej.exceptions.IpBannedException;
import com.sigmaflare.binancej.exceptions.MalformedRequestException;
import com.sigmaflare.binancej.exceptions.RateLimitExceededException;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import java.util.function.Function;
import static com.sigmaflare.binancej.Constant.BASE_ENDPOINT;
public final class Helpers {
private Helpers() {
public final class HttpRequests {
private HttpRequests() {
}
/**
@ -28,24 +31,26 @@ public final class Helpers {
/**
* Utilizes getBuilder to build a fully functional HttpGet request
*
* @param endpoint The endpoint AFTER the base endpoint (e.g. /api/v1/xyz)
* @param apiKey The API key to use
* @param apiKey The API key to use
* @return A configured HttpGet object
*/
public static HttpGet buildGetRequestFromEndpoint(String endpoint, String apiKey) {
final String url = String.format("%s%s", BASE_ENDPOINT, endpoint);
return Helpers.getBuilder(url, apiKey);
return HttpRequests.getBuilder(url, apiKey);
}
/**
* Utilizes postBuilder to build a fully functional HttpPost request
*
* @param endpoint The endpoint AFTER the base endpoint (e.g. /api/v1/xyz)
* @param apiKey The API key to use
* @param apiKey The API key to use
* @return A configured HttpPost object
*/
public static HttpPost buildPostRequestFromEndpoint(String endpoint, String apiKey) {
final String url = String.format("%s%s", BASE_ENDPOINT, endpoint);
return Helpers.postBuilder(url, apiKey);
return HttpRequests.postBuilder(url, apiKey);
}
/**
@ -84,28 +89,24 @@ public final class Helpers {
}
/**
* Safely extracts the value from a LeftProjection of an Either
*
* @param val The LeftProjection to perform the extraction on
* @param <L> The Left type
* @param <R> The Right type
* @return The unwrapped L type object
* A simple function to consolidate the creation of common HTTP exceptions
*/
@SuppressWarnings("unchecked")
public static <L, R> L extractEitherValueSafely(LeftProjection<L, R> val) {
return (L) val.join(Function.identity(), Function.identity());
}
public static BinanceServiceException buildHttpException(int statusCode, ServiceError error) {
if(statusCode < 400) {
throw new IllegalArgumentException(
String.format("Status codes below 400 (statusCode = %d) do not have an exception", statusCode));
}
/**
* Safely extracts the value from a RightProjection of an Either
*
* @param val The RightProjection to perform the extraction on
* @param <L> The Left type
* @param <R> The Right type
* @return The unwrapped R type object
*/
@SuppressWarnings("unchecked")
public static <L, R> R extractEitherValueSafely(RightProjection<L, R> val) {
return (R) val.join(Function.identity(), Function.identity());
if (statusCode == 418) {
return new IpBannedException(error.getMessage());
} else if (statusCode == 429) {
return new RateLimitExceededException(error.getMessage());
} else if (statusCode == 504) {
return new InternalServiceErrorException(error.getCode(), error.getMessage());
} else if (statusCode < 500) {
return new MalformedRequestException(error.getCode(), error.getMessage());
} else {
return new InternalServiceErrorException(error.getCode(), error.getMessage());
}
}
}

178
src/main/java/com/sigmaflare/binancej/MarketData.java

@ -1,13 +1,14 @@
package com.sigmaflare.binancej;
import com.codepoetics.ambivalence.Either;
import com.fasterxml.jackson.databind.JavaType;
import com.sigmaflare.binancej.entities.Candlestick;
import com.sigmaflare.binancej.entities.Interval;
import com.sigmaflare.binancej.entities.OrderBookDepth;
import com.sigmaflare.binancej.entities.ServiceError;
import com.sigmaflare.binancej.entities.TickerPrice;
import com.sigmaflare.binancej.exceptions.BinanceServiceException;
import com.sigmaflare.binancej.exceptions.BinanceServiceUnreachableException;
import com.sigmaflare.binancej.exceptions.UnexpectedErrorException;
import lombok.Builder;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntity;
@ -18,12 +19,14 @@ import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import static com.sigmaflare.binancej.Constant.NO_RESPONSE_TEXT;
import static com.sigmaflare.binancej.Constant.NO_RESPONSE_TEXT_FORMATTED;
import static com.sigmaflare.binancej.Constant.SYMBOL_AND_INTERVAL_MUST_BE_SUPPLIED;
import static com.sigmaflare.binancej.Helpers.buildGetRequestFromEndpoint;
import static com.sigmaflare.binancej.HttpRequests.buildGetRequestFromEndpoint;
@Slf4j
public class MarketData extends BaseBinanceApi {
@ -31,6 +34,9 @@ public class MarketData extends BaseBinanceApi {
private static final String CANDLESTICK_URL = "/api/v1/klines";
private static final String TICKER_PRICE_URL = "/api/v3/ticker/price";
private static final ArrayList<Integer> LIMITS = new ArrayList<>(Arrays.asList(5, 10, 20, 50, 100, 500, 1000));
private static final String LIMIT_EXCEPTION_MESSAGE = "Limit must be in [5, 10, 20, 50, 100, 500, 1000]";
@Builder
MarketData(String apiKey, String secretKey) {
super(apiKey, secretKey);
@ -40,15 +46,24 @@ public class MarketData extends BaseBinanceApi {
super(apiKey, secretKey, closeableHttpClient);
}
private void checkCandlestickLimit(int limit) {
if(limit < 0 || limit > 500) {
throw new IllegalArgumentException("Limit must be greater than 0 and less than or equal to 500");
}
}
/**
* Overloaded version of getOrderBookDepth that uses the default limit of 100
*
* @param symbol The symbol
* @return A populated OrderBookDepth if successful, otherwise an ServiceError
* @throws BinanceServiceUnreachableException In the case the service is unreachable
* @throws com.sigmaflare.binancej.exceptions.IpBannedException If the IP is banned
* @throws com.sigmaflare.binancej.exceptions.RateLimitExceededException If the rate limit is exceeded
* @throws com.sigmaflare.binancej.exceptions.MalformedRequestException If the user's request is malformed
* @throws com.sigmaflare.binancej.exceptions.InternalServiceErrorException If the error occurs on Binance's side
* @throws com.sigmaflare.binancej.exceptions.BinanceServiceUnreachableException If the service is unreachable
*/
public Either<ServiceError, OrderBookDepth> getOrderBookDepth(String symbol)
throws BinanceServiceUnreachableException {
public OrderBookDepth getOrderBookDepth(String symbol) throws BinanceServiceException {
return getOrderBookDepth(symbol, 100);
}
@ -57,11 +72,19 @@ public class MarketData extends BaseBinanceApi {
*
* @param symbol The symbol
* @param limit The record limit (default: 100, maximum 1000)
* @return A populated OrderBookDepth if successful, otherwise an ServiceError
* @throws BinanceServiceUnreachableException In the case the service is unreachable
* @return A populated OrderBookDepth
* @throws com.sigmaflare.binancej.exceptions.IpBannedException If the IP is banned
* @throws com.sigmaflare.binancej.exceptions.RateLimitExceededException If the rate limit is exceeded
* @throws com.sigmaflare.binancej.exceptions.MalformedRequestException If the user's request is malformed
* @throws com.sigmaflare.binancej.exceptions.InternalServiceErrorException If the error occurs on Binance's side
* @throws com.sigmaflare.binancej.exceptions.BinanceServiceUnreachableException If the service is unreachable
* @throws UnexpectedErrorException If an unexpected exception occurs
*/
public Either<ServiceError, OrderBookDepth> getOrderBookDepth(String symbol, int limit)
throws BinanceServiceUnreachableException {
public OrderBookDepth getOrderBookDepth(String symbol, int limit) throws BinanceServiceException {
if(!LIMITS.contains(limit)) {
throw new IllegalArgumentException(LIMIT_EXCEPTION_MESSAGE);
}
String urlWithParams = String.format("%s?symbol=%s&limit=%d", ORDER_BOOK_URL, symbol, limit);
final HttpGet request = buildGetRequestFromEndpoint(urlWithParams, apiKey);
@ -79,14 +102,15 @@ public class MarketData extends BaseBinanceApi {
String response = EntityUtils.toString(httpEntity);
if (!Helpers.statusCodeIsOk(sl.getStatusCode())) {
return Either.ofLeft(mapper.readValue(response, ServiceError.class));
if (!HttpRequests.statusCodeIsOk(sl.getStatusCode())) {
ServiceError error = mapper.readValue(response, ServiceError.class);
throw HttpRequests.buildHttpException(sl.getStatusCode(), error);
}
return Either.ofRight(mapper.readValue(response, OrderBookDepth.class));
return mapper.readValue(response, OrderBookDepth.class);
}
} catch (IOException e) {
throw new BinanceServiceUnreachableException(e.getMessage(), e.getCause());
throw new UnexpectedErrorException(e.getMessage(), e.getCause());
}
}
@ -95,12 +119,16 @@ public class MarketData extends BaseBinanceApi {
*
* @param symbol The symbol
* @param interval The interval
* @return A list of candlesticks if successful, otherwise an ServiceError
* @throws BinanceServiceUnreachableException If the service is unreachable
* @return A list of candlesticks
* @throws com.sigmaflare.binancej.exceptions.IpBannedException If the IP is banned
* @throws com.sigmaflare.binancej.exceptions.RateLimitExceededException If the rate limit is exceeded
* @throws com.sigmaflare.binancej.exceptions.MalformedRequestException If the user's request is malformed
* @throws com.sigmaflare.binancej.exceptions.InternalServiceErrorException If the error occurs on Binance's side
* @throws com.sigmaflare.binancej.exceptions.BinanceServiceUnreachableException If the service is unreachable
* @throws IllegalArgumentException If the required arguments symbol and interval are not supplied
*/
public Either<ServiceError, List<Candlestick>> getCandleStickData(String symbol, Interval interval)
throws BinanceServiceUnreachableException {
public List<Candlestick> getCandleStickData(String symbol, Interval interval)
throws BinanceServiceException {
if (symbol == null || interval == null) {
throw new IllegalArgumentException(SYMBOL_AND_INTERVAL_MUST_BE_SUPPLIED);
@ -122,11 +150,17 @@ public class MarketData extends BaseBinanceApi {
* @param interval The interval
* @param limit The output limit
* @return A list of candlesticks if successful, otherwise an ServiceError
* @throws BinanceServiceUnreachableException If the service is unreachable
* @throws com.sigmaflare.binancej.exceptions.IpBannedException If the IP is banned
* @throws com.sigmaflare.binancej.exceptions.RateLimitExceededException If the rate limit is exceeded
* @throws com.sigmaflare.binancej.exceptions.MalformedRequestException If the user's request is malformed
* @throws com.sigmaflare.binancej.exceptions.InternalServiceErrorException If the error occurs on Binance's side
* @throws com.sigmaflare.binancej.exceptions.BinanceServiceUnreachableException If the service is unreachable
* @throws IllegalArgumentException If the required arguments symbol and interval are not supplied
*/
public Either<ServiceError, List<Candlestick>> getCandleStickData(String symbol, Interval interval, int limit)
throws BinanceServiceUnreachableException {
public List<Candlestick> getCandleStickData(String symbol, Interval interval, int limit)
throws BinanceServiceException {
checkCandlestickLimit(limit);
if (symbol == null || interval == null) {
throw new IllegalArgumentException(SYMBOL_AND_INTERVAL_MUST_BE_SUPPLIED);
@ -149,13 +183,17 @@ public class MarketData extends BaseBinanceApi {
* @param interval The interval
* @param time The start/end time
* @param isStartTime Indicates whether the time is a start or end time
* @return A list of candlesticks if successful, otherwise an ServiceError
* @throws BinanceServiceUnreachableException If the service is unreachable
* @return A list of candlesticks
* @throws com.sigmaflare.binancej.exceptions.IpBannedException If the IP is banned
* @throws com.sigmaflare.binancej.exceptions.RateLimitExceededException If the rate limit is exceeded
* @throws com.sigmaflare.binancej.exceptions.MalformedRequestException If the user's request is malformed
* @throws com.sigmaflare.binancej.exceptions.InternalServiceErrorException If the error occurs on Binance's side
* @throws com.sigmaflare.binancej.exceptions.BinanceServiceUnreachableException If the service is unreachable
* @throws IllegalArgumentException If the required arguments symbol and interval are not supplied
*/
public Either<ServiceError, List<Candlestick>>
public List<Candlestick>
getCandleStickData(String symbol, Interval interval, long time, boolean isStartTime)
throws BinanceServiceUnreachableException {
throws BinanceServiceException {
if (symbol == null || interval == null) {
throw new IllegalArgumentException(SYMBOL_AND_INTERVAL_MUST_BE_SUPPLIED);
@ -190,13 +228,19 @@ public class MarketData extends BaseBinanceApi {
* @param limit The output limit
* @param time The timeframe
* @param isStartTime indicates whether time is a startTime (true) or endTime (false)
* @return A list of candlesticks if successful, otherwise an ServiceError
* @throws BinanceServiceUnreachableException If the service is unreachable
* @return A list of candlesticks
* @throws com.sigmaflare.binancej.exceptions.IpBannedException If the IP is banned
* @throws com.sigmaflare.binancej.exceptions.RateLimitExceededException If the rate limit is exceeded
* @throws com.sigmaflare.binancej.exceptions.MalformedRequestException If the user's request is malformed
* @throws com.sigmaflare.binancej.exceptions.InternalServiceErrorException If the error occurs on Binance's side
* @throws com.sigmaflare.binancej.exceptions.BinanceServiceUnreachableException If the service is unreachable
* @throws IllegalArgumentException If the required arguments symbol and interval are not supplied
*/
public Either<ServiceError, List<Candlestick>>
public List<Candlestick>
getCandleStickData(String symbol, Interval interval, int limit, long time, boolean isStartTime)
throws BinanceServiceUnreachableException {
throws BinanceServiceException {
checkCandlestickLimit(limit);
if (symbol == null || interval == null) {
throw new IllegalArgumentException(SYMBOL_AND_INTERVAL_MUST_BE_SUPPLIED);
@ -233,13 +277,19 @@ public class MarketData extends BaseBinanceApi {
* @param limit The output limit
* @param startTime The start timeframe
* @param endTime The end timeframe
* @return A list of candlesticks if successful, otherwise an ServiceError
* @throws BinanceServiceUnreachableException If the service is unreachable
* @return A list of candlesticks
* @throws com.sigmaflare.binancej.exceptions.IpBannedException If the IP is banned
* @throws com.sigmaflare.binancej.exceptions.RateLimitExceededException If the rate limit is exceeded
* @throws com.sigmaflare.binancej.exceptions.MalformedRequestException If the user's request is malformed
* @throws com.sigmaflare.binancej.exceptions.InternalServiceErrorException If the error occurs on Binance's side
* @throws com.sigmaflare.binancej.exceptions.BinanceServiceUnreachableException If the service is unreachable
* @throws IllegalArgumentException If the required arguments symbol and interval are not supplied
*/
public Either<ServiceError, List<Candlestick>>
public List<Candlestick>
getCandleStickData(String symbol, Interval interval, int limit, long startTime, long endTime)
throws BinanceServiceUnreachableException {
throws BinanceServiceException {
checkCandlestickLimit(limit);
if (symbol == null || interval == null) {
throw new IllegalArgumentException(SYMBOL_AND_INTERVAL_MUST_BE_SUPPLIED);
@ -263,11 +313,16 @@ public class MarketData extends BaseBinanceApi {
* Retrieves the current ticker price for the specified symbol
*
* @param symbol The symbol
* @return A TickerPrice if successful, otherwise an ServiceError
* @throws BinanceServiceUnreachableException If the service is unreachable
* @return A populated list of TickerPrice objects
* @throws com.sigmaflare.binancej.exceptions.IpBannedException If the IP is banned
* @throws com.sigmaflare.binancej.exceptions.RateLimitExceededException If the rate limit is exceeded
* @throws com.sigmaflare.binancej.exceptions.MalformedRequestException If the user's request is malformed
* @throws com.sigmaflare.binancej.exceptions.InternalServiceErrorException If the error occurs on Binance's side
* @throws com.sigmaflare.binancej.exceptions.BinanceServiceUnreachableException If the service is unreachable
* @throws UnexpectedErrorException If an unexpected exception occurs
*/
public Either<ServiceError, TickerPrice> getTickerPriceForSymbol(String symbol)
throws BinanceServiceUnreachableException {
public TickerPrice getTickerPriceForSymbol(String symbol)
throws BinanceServiceException {
if (symbol == null) {
throw new IllegalArgumentException("Symbol must not be null");
}
@ -289,14 +344,15 @@ public class MarketData extends BaseBinanceApi {
String response = EntityUtils.toString(httpEntity);
if (!Helpers.statusCodeIsOk(sl.getStatusCode())) {
return Either.ofLeft(mapper.readValue(response, ServiceError.class));
if (!HttpRequests.statusCodeIsOk(sl.getStatusCode())) {
ServiceError error = mapper.readValue(response, ServiceError.class);
throw HttpRequests.buildHttpException(sl.getStatusCode(), error);
}
return Either.ofRight(mapper.readValue(response, TickerPrice.class));
return mapper.readValue(response, TickerPrice.class);
}
} catch (IOException e) {
throw new BinanceServiceUnreachableException(e.getMessage(), e.getCause());
throw new UnexpectedErrorException(e.getMessage(), e.getCause());
}
}
@ -304,10 +360,15 @@ public class MarketData extends BaseBinanceApi {
* Retrieves ticker prices for all supported Binance symbols. This is good to use in terms of cost if you need
* more than one symbol's current ticker price.
*
* @return Either a list of TickerPrice objects if successful, or an ServiceError
* @throws BinanceServiceUnreachableException If the service is unreachable
* @return A populated list of TickerPrice objects
* @throws com.sigmaflare.binancej.exceptions.IpBannedException If the IP is banned
* @throws com.sigmaflare.binancej.exceptions.RateLimitExceededException If the rate limit is exceeded
* @throws com.sigmaflare.binancej.exceptions.MalformedRequestException If the user's request is malformed
* @throws com.sigmaflare.binancej.exceptions.InternalServiceErrorException If the error occurs on Binance's side
* @throws com.sigmaflare.binancej.exceptions.BinanceServiceUnreachableException If the service is unreachable
* @throws UnexpectedErrorException If an unexpected exception occurs
*/
public Either<ServiceError, List<TickerPrice>> getTickerPrices() throws BinanceServiceUnreachableException {
public List<TickerPrice> getTickerPrices() throws BinanceServiceException {
final HttpGet request = buildGetRequestFromEndpoint(TICKER_PRICE_URL, apiKey);
try {
@ -324,15 +385,16 @@ public class MarketData extends BaseBinanceApi {
String response = EntityUtils.toString(httpEntity);
if (!Helpers.statusCodeIsOk(sl.getStatusCode())) {
return Either.ofLeft(mapper.readValue(response, ServiceError.class));
if (!HttpRequests.statusCodeIsOk(sl.getStatusCode())) {
ServiceError error = mapper.readValue(response, ServiceError.class);
throw HttpRequests.buildHttpException(sl.getStatusCode(), error);
}
JavaType type = mapper.getTypeFactory().constructCollectionType(List.class, TickerPrice.class);
return Either.ofRight(mapper.readValue(response, type));
return mapper.readValue(response, type);
}
} catch (IOException e) {
throw new BinanceServiceUnreachableException(e.getMessage(), e.getCause());
throw new UnexpectedErrorException(e.getMessage(), e.getCause());
}
}
@ -341,11 +403,16 @@ public class MarketData extends BaseBinanceApi {
* as glorified URL builders.
*
* @param endpoint The endpoint to use that has the associated parameters populated
* @return A list of candlesticks if successful, otherwise an ServiceError
* @throws BinanceServiceUnreachableException If the service is unreachable
* @return A list of candlesticks
* @throws com.sigmaflare.binancej.exceptions.IpBannedException If the IP is banned
* @throws com.sigmaflare.binancej.exceptions.RateLimitExceededException If the rate limit is exceeded
* @throws com.sigmaflare.binancej.exceptions.MalformedRequestException If the user's request is malformed
* @throws com.sigmaflare.binancej.exceptions.InternalServiceErrorException If the error occurs on Binance's side
* @throws com.sigmaflare.binancej.exceptions.BinanceServiceUnreachableException If the service is unreachable
* @throws UnexpectedErrorException If an unexpected exception occurs
*/
private Either<ServiceError, List<Candlestick>>
getCandleStickDataFromUrl(String endpoint) throws BinanceServiceUnreachableException {
private List<Candlestick>
getCandleStickDataFromUrl(String endpoint) throws BinanceServiceException {
final HttpGet request = buildGetRequestFromEndpoint(endpoint, apiKey);
try {
@ -362,15 +429,16 @@ public class MarketData extends BaseBinanceApi {
String response = EntityUtils.toString(httpEntity);
if (!Helpers.statusCodeIsOk(sl.getStatusCode())) {
return Either.ofLeft(mapper.readValue(response, ServiceError.class));
if (!HttpRequests.statusCodeIsOk(sl.getStatusCode())) {
ServiceError error = mapper.readValue(response, ServiceError.class);
throw HttpRequests.buildHttpException(sl.getStatusCode(), error);
}
JavaType type = mapper.getTypeFactory().constructCollectionType(List.class, Candlestick.class);
return Either.ofRight(mapper.readValue(response, type));
return mapper.readValue(response, type);
}
} catch (IOException e) {
throw new BinanceServiceUnreachableException(e.getMessage(), e.getCause());
throw new UnexpectedErrorException(e.getMessage(), e.getCause());
}
}
}

4
src/main/java/com/sigmaflare/binancej/entities/ServiceError.java

@ -13,9 +13,9 @@ import lombok.NonNull;
@NoArgsConstructor
public class ServiceError {
@JsonProperty("code")
public int code;
private int code;
@NonNull
@JsonProperty("msg")
public String message;
private String message;
}

4
src/main/java/com/sigmaflare/binancej/exceptions/BinanceServiceUnreachableException.java

@ -6,6 +6,6 @@ import lombok.Getter;
@Getter
@AllArgsConstructor
public class BinanceServiceUnreachableException extends BinanceServiceException {
public String message;
public Throwable cause;
private final String message;
private final Throwable cause;
}

11
src/main/java/com/sigmaflare/binancej/exceptions/InternalServiceErrorException.java

@ -0,0 +1,11 @@
package com.sigmaflare.binancej.exceptions;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public class InternalServiceErrorException extends BinanceServiceException {
private final int code;
private final String message;
}

10
src/main/java/com/sigmaflare/binancej/exceptions/IpBannedException.java

@ -0,0 +1,10 @@
package com.sigmaflare.binancej.exceptions;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public class IpBannedException extends BinanceServiceException {
private final String message;
}

11
src/main/java/com/sigmaflare/binancej/exceptions/MalformedRequestException.java

@ -0,0 +1,11 @@
package com.sigmaflare.binancej.exceptions;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public class MalformedRequestException extends BinanceServiceException {
private final int code;
private final String message;
}

10
src/main/java/com/sigmaflare/binancej/exceptions/RateLimitExceededException.java

@ -0,0 +1,10 @@
package com.sigmaflare.binancej.exceptions;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public class RateLimitExceededException extends BinanceServiceException {
private final String message;
}

7
src/main/java/com/sigmaflare/binancej/exceptions/UnexpectedErrorException.java

@ -0,0 +1,7 @@
package com.sigmaflare.binancej.exceptions;
public class UnexpectedErrorException extends RuntimeException {
public UnexpectedErrorException(String message, Throwable cause) {
super(message, cause);
}
}

145
src/test/java/com/sigmaflare/binancej/CandlestickMethodTests.java

@ -1,12 +1,13 @@
package com.sigmaflare.binancej;
import com.codepoetics.ambivalence.Either;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.sigmaflare.binancej.entities.Candlestick;
import com.sigmaflare.binancej.entities.ServiceError;
import com.sigmaflare.binancej.entities.Interval;
import com.sigmaflare.binancej.exceptions.BinanceServiceException;
import com.sigmaflare.binancej.exceptions.BinanceServiceUnreachableException;
import com.sigmaflare.binancej.exceptions.MalformedRequestException;
import com.sigmaflare.binancej.matchers.GetMatcher;
import org.apache.commons.io.FileUtils;
import org.apache.http.ProtocolVersion;
@ -26,15 +27,15 @@ import java.util.List;
import static com.sigmaflare.binancej.Constant.BASE_ENDPOINT;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class CandlestickMethodTests {
private static ObjectMapper mapper = Helpers.objectMapperBuilder();
private static ObjectMapper mapper = HttpRequests.objectMapperBuilder();
private final String url = String.format("%s%s", BASE_ENDPOINT, "/api/v1/klines?symbol=TEST&interval=8h");
private final String urlWithLimit = String.format("%s%s", url, "&limit=1000");
private final String urlWithLimit = String.format("%s%s", url, "&limit=250");
private final String urlWithStartTime = String.format("%s%s", url, "&startTime=1234");
private final String urlWithEndTime = String.format("%s%s", url, "&endTime=1234");
private final String urlWithAllParameters = String.format("%s%s", urlWithLimit, "&startTime=1234&endTime=1234");
@ -82,7 +83,7 @@ public class CandlestickMethodTests {
}
@Test
public void testCandlestickServiceReturnsSuccessfully() throws IOException, BinanceServiceUnreachableException {
public void testCandlestickServiceReturnsSuccessfully() throws Exception {
CloseableHttpClient mockedCloseableHttpClient = mock(CloseableHttpClient.class);
CloseableHttpResponse mockedCloseableHttpResponse = mock(CloseableHttpResponse.class);
@ -100,16 +101,38 @@ public class CandlestickMethodTests {
MarketData marketData = new MarketData("1234", "abcd", mockedCloseableHttpClient);
Either<ServiceError, List<Candlestick>> res =
marketData.getCandleStickData("TEST", Interval.EIGHT_HOURS);
List<Candlestick> res = marketData.getCandleStickData("TEST", Interval.EIGHT_HOURS);
assertTrue(res.isRight());
assertEquals(candlesticks, Helpers.extractEitherValueSafely(res.right()));
assertEquals(candlesticks, res);
}
@Test
public void testCandlestickServiceWithLimitReturnsSuccessfully()
throws IOException, BinanceServiceUnreachableException {
public void testCandlestickServiceWithLimitReturnsSuccessfully() throws Exception {
CloseableHttpClient mockedCloseableHttpClient = mock(CloseableHttpClient.class);
CloseableHttpResponse mockedCloseableHttpResponse = mock(CloseableHttpResponse.class);
when(mockedCloseableHttpResponse.getStatusLine())
.thenReturn(new BasicStatusLine(new ProtocolVersion("TEST", 1, 0),
200, "test"));
when(mockedCloseableHttpClient.execute(argThat(new GetMatcher(urlWithLimit, "1234"))))
.thenReturn(mockedCloseableHttpResponse);
BasicHttpEntity httpEntity = new BasicHttpEntity();
httpEntity.setContent(new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8)));
when(mockedCloseableHttpResponse.getEntity()).thenReturn(httpEntity);
MarketData marketData = new MarketData("1234", "abcd", mockedCloseableHttpClient);
List<Candlestick> res =
marketData.getCandleStickData("TEST", Interval.EIGHT_HOURS, 250);
assertEquals(candlesticks, res);
}
@Test(expected = IllegalArgumentException.class)
public void testCandlestickServiceWithLimitOutOfBoundsThrows() throws Exception {
CloseableHttpClient mockedCloseableHttpClient = mock(CloseableHttpClient.class);
CloseableHttpResponse mockedCloseableHttpResponse = mock(CloseableHttpResponse.class);
@ -127,16 +150,14 @@ public class CandlestickMethodTests {
MarketData marketData = new MarketData("1234", "abcd", mockedCloseableHttpClient);
Either<ServiceError, List<Candlestick>> res =
marketData.getCandleStickData("TEST", Interval.EIGHT_HOURS, 1000);
List<Candlestick> res =
marketData.getCandleStickData("TEST", Interval.EIGHT_HOURS, 60000);
assertTrue(res.isRight());
assertEquals(candlesticks, Helpers.extractEitherValueSafely(res.right()));
assertEquals(candlesticks, res);
}
@Test
public void testCandlestickServiceWithLimitAndStartTimeReturnsSuccessfully()
throws IOException, BinanceServiceUnreachableException {
public void testCandlestickServiceWithLimitAndStartTimeReturnsSuccessfully() throws Exception {
CloseableHttpClient mockedCloseableHttpClient = mock(CloseableHttpClient.class);
CloseableHttpResponse mockedCloseableHttpResponse = mock(CloseableHttpResponse.class);
@ -154,16 +175,39 @@ public class CandlestickMethodTests {
MarketData marketData = new MarketData("1234", "abcd", mockedCloseableHttpClient);
Either<ServiceError, List<Candlestick>> res =
marketData.getCandleStickData("TEST", Interval.EIGHT_HOURS, 1000, 1234L,true);
List<Candlestick> res =
marketData.getCandleStickData("TEST", Interval.EIGHT_HOURS, 250, 1234L, true);
assertTrue(res.isRight());
assertEquals(candlesticks, Helpers.extractEitherValueSafely(res.right()));
assertEquals(candlesticks, res);
}
@Test(expected = IllegalArgumentException.class)
public void testCandlestickServiceWithLimitOutOfBoundsAndStartTimeThrows() throws Exception {
CloseableHttpClient mockedCloseableHttpClient = mock(CloseableHttpClient.class);
CloseableHttpResponse mockedCloseableHttpResponse = mock(CloseableHttpResponse.class);
when(mockedCloseableHttpResponse.getStatusLine())
.thenReturn(new BasicStatusLine(new ProtocolVersion("TEST", 1, 0),
200, "test"));
when(mockedCloseableHttpClient.execute(argThat(new GetMatcher(urlWithLimitAndStartTime, "1234"))))
.thenReturn(mockedCloseableHttpResponse);
BasicHttpEntity httpEntity = new BasicHttpEntity();
httpEntity.setContent(new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8)));
when(mockedCloseableHttpResponse.getEntity()).thenReturn(httpEntity);
MarketData marketData = new MarketData("1234", "abcd", mockedCloseableHttpClient);
List<Candlestick> res =
marketData.getCandleStickData("TEST", Interval.EIGHT_HOURS, 750, 1234L, true);
assertEquals(candlesticks, res);
}
@Test
public void testCandlestickServiceWithLimitAndEndTimeReturnsSuccessfully()
throws IOException, BinanceServiceUnreachableException {
public void testCandlestickServiceWithLimitAndEndTimeReturnsSuccessfully() throws Exception {
CloseableHttpClient mockedCloseableHttpClient = mock(CloseableHttpClient.class);
CloseableHttpResponse mockedCloseableHttpResponse = mock(CloseableHttpResponse.class);
@ -181,16 +225,14 @@ public class CandlestickMethodTests {
MarketData marketData = new MarketData("1234", "abcd", mockedCloseableHttpClient);
Either<ServiceError, List<Candlestick>> res =
marketData.getCandleStickData("TEST", Interval.EIGHT_HOURS, 1000, 1234L,false);
List<Candlestick> res =
marketData.getCandleStickData("TEST", Interval.EIGHT_HOURS, 250, 1234L, false);
assertTrue(res.isRight());
assertEquals(candlesticks, Helpers.extractEitherValueSafely(res.right()));
assertEquals(candlesticks, res);
}
@Test
public void testCandlestickServiceWithAllParametersReturnsSuccessfully()
throws IOException, BinanceServiceUnreachableException {
public void testCandlestickServiceWithAllParametersReturnsSuccessfully() throws Exception {
CloseableHttpClient mockedCloseableHttpClient = mock(CloseableHttpClient.class);
CloseableHttpResponse mockedCloseableHttpResponse = mock(CloseableHttpResponse.class);
@ -208,17 +250,15 @@ public class CandlestickMethodTests {
MarketData marketData = new MarketData("1234", "abcd", mockedCloseableHttpClient);
Either<ServiceError, List<Candlestick>> res =
List<Candlestick> res =
marketData.getCandleStickData("TEST", Interval.EIGHT_HOURS,
1000, 1234L,1234L);
250, 1234L, 1234L);
assertTrue(res.isRight());
assertEquals(candlesticks, Helpers.extractEitherValueSafely(res.right()));
assertEquals(candlesticks, res);
}
@Test
public void testCandlestickServiceWithStartTimeReturnsSuccessfully()
throws IOException, BinanceServiceUnreachableException {
public void testCandlestickServiceWithStartTimeReturnsSuccessfully() throws Exception {
CloseableHttpClient mockedCloseableHttpClient = mock(CloseableHttpClient.class);
CloseableHttpResponse mockedCloseableHttpResponse = mock(CloseableHttpResponse.class);
@ -236,16 +276,14 @@ public class CandlestickMethodTests {
MarketData marketData = new MarketData("1234", "abcd", mockedCloseableHttpClient);
Either<ServiceError, List<Candlestick>> res =
marketData.getCandleStickData("TEST", Interval.EIGHT_HOURS, 1234L,true);
List<Candlestick> res =
marketData.getCandleStickData("TEST", Interval.EIGHT_HOURS, 1234L, true);
assertTrue(res.isRight());
assertEquals(candlesticks, Helpers.extractEitherValueSafely(res.right()));
assertEquals(candlesticks, res);
}
@Test
public void testCandlestickServiceWithEndTimeReturnsSuccessfully()
throws IOException, BinanceServiceUnreachableException {
public void testCandlestickServiceWithEndTimeReturnsSuccessfully() throws Exception {
CloseableHttpClient mockedCloseableHttpClient = mock(CloseableHttpClient.class);
CloseableHttpResponse mockedCloseableHttpResponse = mock(CloseableHttpResponse.class);
@ -263,15 +301,14 @@ public class CandlestickMethodTests {
MarketData marketData = new MarketData("1234", "abcd", mockedCloseableHttpClient);
Either<ServiceError, List<Candlestick>> res =
marketData.getCandleStickData("TEST", Interval.EIGHT_HOURS, 1234L,false);
List<Candlestick> res =
marketData.getCandleStickData("TEST", Interval.EIGHT_HOURS, 1234L, false);
assertTrue(res.isRight());
assertEquals(candlesticks, Helpers.extractEitherValueSafely(res.right()));
assertEquals(candlesticks, res);
}
@Test(expected = BinanceServiceUnreachableException.class)
public void testCandlestickServiceWithoutHttpEntityThrows() throws IOException, BinanceServiceUnreachableException {
public void testCandlestickServiceWithoutHttpEntityThrows() throws Exception {
CloseableHttpClient mockedCloseableHttpClient = mock(CloseableHttpClient.class);
CloseableHttpResponse mockedCloseableHttpResponse = mock(CloseableHttpResponse.class);
@ -288,7 +325,7 @@ public class CandlestickMethodTests {
}
@Test
public void testCandlestickServiceBadApiKeyReturns400() throws IOException, BinanceServiceUnreachableException {
public void testCandlestickServiceBadApiKeyReturns400() throws Exception {
CloseableHttpClient mockedCloseableHttpClient = mock(CloseableHttpClient.class);
CloseableHttpResponse mockedCloseableHttpResponse = mock(CloseableHttpResponse.class);
@ -309,10 +346,18 @@ public class CandlestickMethodTests {
MarketData marketData = new MarketData("1234", "abcd", mockedCloseableHttpClient);
Either<ServiceError, List<Candlestick>> res =
try {
marketData.getCandleStickData("TEST", Interval.EIGHT_HOURS);
fail();
} catch (BinanceServiceException e) {
if (!(e instanceof MalformedRequestException)) {
fail();
}
assertEquals(400, ((MalformedRequestException) e).getCode());
assertEquals("Bad API Key", e.getMessage());
}
assertTrue(res.isLeft());
assertEquals(serviceError, Helpers.extractEitherValueSafely(res.left()));
}
}

32
src/test/java/com/sigmaflare/binancej/ExchangeInfoMethodTests.java

@ -1,11 +1,12 @@
package com.sigmaflare.binancej;
import com.codepoetics.ambivalence.Either;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.sigmaflare.binancej.entities.ExchangeInfo;
import com.sigmaflare.binancej.entities.ServiceError;
import com.sigmaflare.binancej.exceptions.BinanceServiceException;
import com.sigmaflare.binancej.exceptions.BinanceServiceUnreachableException;
import com.sigmaflare.binancej.exceptions.MalformedRequestException;
import com.sigmaflare.binancej.matchers.GetMatcher;
import org.apache.commons.io.FileUtils;
import org.apache.http.ProtocolVersion;
@ -23,13 +24,13 @@ import java.nio.charset.StandardCharsets;
import static com.sigmaflare.binancej.Constant.BASE_ENDPOINT;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class ExchangeInfoMethodTests {
private static ObjectMapper mapper = Helpers.objectMapperBuilder();
private static ObjectMapper mapper = HttpRequests.objectMapperBuilder();
private String url = String.format("%s%s", BASE_ENDPOINT, "/api/v1/exchangeInfo");
private ExchangeInfo exchangeInfo;
@ -44,7 +45,7 @@ public class ExchangeInfoMethodTests {
}
@Test
public void testExchangeInfoServiceReturnsSuccessfully() throws IOException, BinanceServiceUnreachableException {
public void testExchangeInfoServiceReturnsSuccessfully() throws Exception {
CloseableHttpClient mockedCloseableHttpClient = mock(CloseableHttpClient.class);
CloseableHttpResponse mockedCloseableHttpResponse = mock(CloseableHttpResponse.class);
@ -65,14 +66,13 @@ public class ExchangeInfoMethodTests {
GeneralUtilities generalUtilities =
new GeneralUtilities("1234", "abcd", mockedCloseableHttpClient);
Either<ServiceError, ExchangeInfo> res = generalUtilities.getExchangeInfo();
ExchangeInfo res = generalUtilities.getExchangeInfo();
assertTrue(res.isRight());
assertEquals(exchangeInfo, Helpers.extractEitherValueSafely(res.right()));
assertEquals(exchangeInfo, res);
}
@Test(expected = BinanceServiceUnreachableException.class)
public void testExchangeInfoServiceWithNoHttpEntityFails() throws IOException, BinanceServiceUnreachableException {
public void testExchangeInfoServiceWithNoHttpEntityFails() throws Exception {
CloseableHttpClient mockedCloseableHttpClient = mock(CloseableHttpClient.class);
CloseableHttpResponse mockedCloseableHttpResponse = mock(CloseableHttpResponse.class);
@ -90,7 +90,7 @@ public class ExchangeInfoMethodTests {
}
@Test
public void testExchangeInfoServiceBadApiKeyReturns400() throws IOException, BinanceServiceUnreachableException {
public void testExchangeInfoServiceBadApiKeyReturns400() throws Exception {
CloseableHttpClient mockedCloseableHttpClient = mock(CloseableHttpClient.class);
CloseableHttpResponse mockedCloseableHttpResponse = mock(CloseableHttpResponse.class);
@ -112,9 +112,17 @@ public class ExchangeInfoMethodTests {
GeneralUtilities generalUtilities =
new GeneralUtilities("1234", "abcd", mockedCloseableHttpClient);
Either<ServiceError, ExchangeInfo> res = generalUtilities.getExchangeInfo();
assertTrue(res.isLeft());
assertEquals(serviceError, Helpers.extractEitherValueSafely(res.left()));
try {
generalUtilities.getExchangeInfo();
fail();
} catch (BinanceServiceException e) {
if (!(e instanceof MalformedRequestException)) {
fail();
}
assertEquals(400, ((MalformedRequestException) e).getCode());
assertEquals("Bad API Key", e.getMessage());
}
}
}

42
src/test/java/com/sigmaflare/binancej/HttpErrorThrowerTests.java

@ -0,0 +1,42 @@
package com.sigmaflare.binancej;
import com.sigmaflare.binancej.entities.ServiceError;
import com.sigmaflare.binancej.exceptions.InternalServiceErrorException;
import com.sigmaflare.binancej.exceptions.IpBannedException;
import com.sigmaflare.binancej.exceptions.MalformedRequestException;
import com.sigmaflare.binancej.exceptions.RateLimitExceededException;
import org.junit.Test;
public class HttpErrorThrowerTests {
@Test(expected = IpBannedException.class)
public void testIpBannedOnHttpCode418() throws Exception {
throw HttpRequests.buildHttpException(418, ServiceError.builder().message("TEST").code(418).build());
}
@Test(expected = RateLimitExceededException.class)
public void testRateLimitOnHttpCode421() throws Exception {
throw HttpRequests.buildHttpException(429, ServiceError.builder().message("TEST").code(429).build());
}
@Test(expected = MalformedRequestException.class)
public void testMalformedRequestOnHttpCode400() throws Exception {
throw HttpRequests.buildHttpException(400, ServiceError.builder().message("TEST").code(400).build());
}
@Test(expected = InternalServiceErrorException.class)
public void testInternalServiceErrorOnHttpCode500() throws Exception {
throw HttpRequests.buildHttpException(500, ServiceError.builder().message("TEST").code(500).build());
}
@Test(expected = InternalServiceErrorException.class)
public void testInternalServiceErrorOnHttpCode504() throws Exception {
throw HttpRequests.buildHttpException(504, ServiceError.builder().message("TEST").code(504).build());
}
@Test(expected = IllegalArgumentException.class)
public void testInternalServiceThrowsOn200Ok() throws Exception {
throw HttpRequests.buildHttpException(200, ServiceError.builder().message("TEST").code(200).build());
}
}

61
src/test/java/com/sigmaflare/binancej/OrderBookDepthTests.java

@ -1,11 +1,12 @@
package com.sigmaflare.binancej;
import com.codepoetics.ambivalence.Either;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.sigmaflare.binancej.entities.OrderBookDepth;
import com.sigmaflare.binancej.entities.OrderBookPricing;
import com.sigmaflare.binancej.entities.ServiceError;
import com.sigmaflare.binancej.exceptions.BinanceServiceException;
import com.sigmaflare.binancej.exceptions.BinanceServiceUnreachableException;
import com.sigmaflare.binancej.exceptions.MalformedRequestException;
import com.sigmaflare.binancej.matchers.GetMatcher;
import org.apache.commons.io.FileUtils;
import org.apache.http.ProtocolVersion;
@ -24,13 +25,13 @@ import java.nio.charset.StandardCharsets;
import static com.sigmaflare.binancej.Constant.BASE_ENDPOINT;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class OrderBookDepthTests {
private static ObjectMapper mapper = Helpers.objectMapperBuilder();
private static ObjectMapper mapper = HttpRequests.objectMapperBuilder();
private final String url = String.format("%s%s", BASE_ENDPOINT, "/api/v1/depth?symbol=ETHBTC&limit=100");
private final String urlWithLimit = String.format("%s%s", BASE_ENDPOINT, "/api/v1/depth?symbol=ETHBTC&limit=1000");
@ -70,7 +71,7 @@ public class OrderBookDepthTests {
@Test
public void testOrderBookDepthReturnsSuccessfully() throws IOException, BinanceServiceUnreachableException {
public void testOrderBookDepthReturnsSuccessfully() throws Exception {
CloseableHttpClient mockedCloseableHttpClient = mock(CloseableHttpClient.class);
CloseableHttpResponse mockedCloseableHttpResponse = mock(CloseableHttpResponse.class);
@ -92,15 +93,43 @@ public class OrderBookDepthTests {
MarketData marketData = new MarketData("1234", "abcd", mockedCloseableHttpClient);
Either<ServiceError, OrderBookDepth> res = marketData.getOrderBookDepth("ETHBTC");
OrderBookDepth res = marketData.getOrderBookDepth("ETHBTC");
assertTrue(res.isRight());
assertEquals(orderBookDepth, Helpers.extractEitherValueSafely(res.right()));
assertEquals(orderBookDepth, res);
}
@Test(expected = IllegalArgumentException.class)
public void testOrderBookDepthWithLimitOutOfBounds() throws Exception {
CloseableHttpClient mockedCloseableHttpClient = mock(CloseableHttpClient.class);
CloseableHttpResponse mockedCloseableHttpResponse = mock(CloseableHttpResponse.class);
when(mockedCloseableHttpResponse.getStatusLine())
.thenReturn(new BasicStatusLine(new ProtocolVersion("TEST", 1, 0),
200, "test"));
when(mockedCloseableHttpClient.execute(argThat(new GetMatcher(url, "1234"))))
.thenReturn(mockedCloseableHttpResponse);
BasicHttpEntity httpEntity = new BasicHttpEntity();
// This changes from the usual way we do this because we do post-processing in the deserializer and the JSON
// will no longer match the stringified OrderBookDepth.
httpEntity.setContent(new ByteArrayInputStream(orderBookDepthJson.getBytes()));
when(mockedCloseableHttpResponse.getEntity()).thenReturn(httpEntity);
MarketData marketData = new MarketData("1234", "abcd", mockedCloseableHttpClient);
OrderBookDepth res = marketData.getOrderBookDepth("ETHBTC", 1500);
assertEquals(orderBookDepth, res);
}
@Test(expected = BinanceServiceUnreachableException.class)
public void testOrderBookDepthWithNoHttpEntityFails() throws IOException, BinanceServiceUnreachableException {
public void testOrderBookDepthWithNoHttpEntityFails() throws Exception {
CloseableHttpClient mockedCloseableHttpClient = mock(CloseableHttpClient.class);
CloseableHttpResponse mockedCloseableHttpResponse = mock(CloseableHttpResponse.class);
@ -117,7 +146,7 @@ public class OrderBookDepthTests {
}
@Test
public void testOrderBookDepthWithBadApiKeyReturns400() throws IOException, BinanceServiceUnreachableException {
public void testOrderBookDepthWithBadApiKeyReturns400() throws Exception {
CloseableHttpClient mockedCloseableHttpClient = mock(CloseableHttpClient.class);
CloseableHttpResponse mockedCloseableHttpResponse = mock(CloseableHttpResponse.class);
@ -139,9 +168,17 @@ public class OrderBookDepthTests {
when(mockedCloseableHttpResponse.getEntity()).thenReturn(httpEntity);
Either<ServiceError, OrderBookDepth> res = marketData.getOrderBookDepth("ETHBTC", 1000);
try {
marketData.getOrderBookDepth("ETHBTC", 1000);
fail();
} catch (BinanceServiceException e) {
if (!(e instanceof MalformedRequestException)) {
fail();
}
assertEquals(400, ((MalformedRequestException) e).getCode());
assertEquals("Bad API Key", e.getMessage());
}
assertTrue(res.isLeft());
assertEquals(serviceError, Helpers.extractEitherValueSafely(res.left()));
}
}

32
src/test/java/com/sigmaflare/binancej/PingMethodTests.java

@ -1,11 +1,11 @@
package com.sigmaflare.binancej;
import com.codepoetics.ambivalence.Either;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.sigmaflare.binancej.entities.ServiceError;
import com.sigmaflare.binancej.entities.Ping;
import com.sigmaflare.binancej.exceptions.BinanceServiceException;
import com.sigmaflare.binancej.exceptions.BinanceServiceUnreachableException;
import com.sigmaflare.binancej.exceptions.MalformedRequestException;
import com.sigmaflare.binancej.matchers.GetMatcher;
import org.apache.http.ProtocolVersion;
import org.apache.http.client.methods.CloseableHttpResponse;
@ -20,7 +20,7 @@ import java.nio.charset.StandardCharsets;
import static com.sigmaflare.binancej.Constant.BASE_ENDPOINT;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@ -30,7 +30,7 @@ public class PingMethodTests {
private final String url = String.format("%s%s", BASE_ENDPOINT, "/api/v1/ping");
@Test
public void testServiceAliveReturnsSuccess() throws IOException, BinanceServiceUnreachableException {
public void testServiceAliveReturnsSuccess() throws Exception {
CloseableHttpClient mockedCloseableHttpClient = mock(CloseableHttpClient.class);
CloseableHttpResponse mockedCloseableHttpResponse = mock(CloseableHttpResponse.class);
@ -50,14 +50,12 @@ public class PingMethodTests {
GeneralUtilities generalUtilities =
new GeneralUtilities("1234", "abcd", mockedCloseableHttpClient);
Either<ServiceError, Ping> res = generalUtilities.ping();
assertTrue(res.isRight());
// Ping returns an object that is an empty Ping - all we care is that is doesn't throw
generalUtilities.ping();
}
@Test(expected = BinanceServiceUnreachableException.class)
public void testServiceFailsToReturnHttpEntity() throws IOException, BinanceServiceUnreachableException {
public void testServiceFailsToReturnHttpEntity() throws Exception {
CloseableHttpClient mockedCloseableHttpClient = mock(CloseableHttpClient.class);
CloseableHttpResponse mockedCloseableHttpResponse = mock(CloseableHttpResponse.class);
@ -75,7 +73,7 @@ public class PingMethodTests {
}
@Test
public void testServiceBadApiKeyReturns400() throws IOException, BinanceServiceUnreachableException {
public void testServiceBadApiKeyReturns400() throws Exception {
CloseableHttpClient mockedCloseableHttpClient = mock(CloseableHttpClient.class);
CloseableHttpResponse mockedCloseableHttpResponse = mock(CloseableHttpResponse.class);
@ -97,9 +95,15 @@ public class PingMethodTests {
GeneralUtilities generalUtilities
= new GeneralUtilities("1234", "abcd", mockedCloseableHttpClient);
Either<ServiceError, Ping> res = generalUtilities.ping();
assertTrue(res.isLeft());
assertEquals(serviceError, Helpers.extractEitherValueSafely(res.left()));
try {
generalUtilities.ping();
fail();
} catch (BinanceServiceException e) {
if (!(e instanceof MalformedRequestException)) {
fail();
}
assertEquals(400, ((MalformedRequestException) e).getCode());
assertEquals("Bad API Key", e.getMessage());
}
}
}

59
src/test/java/com/sigmaflare/binancej/TickerPriceTests.java

@ -1,11 +1,12 @@
package com.sigmaflare.binancej;
import com.codepoetics.ambivalence.Either;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.sigmaflare.binancej.entities.ServiceError;
import com.sigmaflare.binancej.entities.TickerPrice;
import com.sigmaflare.binancej.exceptions.BinanceServiceException;
import com.sigmaflare.binancej.exceptions.BinanceServiceUnreachableException;
import com.sigmaflare.binancej.exceptions.MalformedRequestException;
import com.sigmaflare.binancej.matchers.GetMatcher;
import org.apache.commons.io.FileUtils;
import org.apache.http.ProtocolVersion;
@ -24,13 +25,13 @@ import java.util.List;
import static com.sigmaflare.binancej.Constant.BASE_ENDPOINT;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class TickerPriceTests {
private static final ObjectMapper mapper = Helpers.objectMapperBuilder();
private static final ObjectMapper mapper = HttpRequests.objectMapperBuilder();
private final String url = String.format("%s%s", BASE_ENDPOINT, "/api/v3/ticker/price");
private final String specificUrl = String.format("%s%s", url, "?symbol=TEST");
@ -61,7 +62,7 @@ public class TickerPriceTests {
}
@Test
public void testTickerPriceForSymbolReturnsSuccessfully() throws IOException, BinanceServiceUnreachableException {
public void testTickerPriceForSymbolReturnsSuccessfully() throws Exception {
CloseableHttpClient mockedCloseableHttpClient = mock(CloseableHttpClient.class);
CloseableHttpResponse mockedCloseableHttpResponse = mock(CloseableHttpResponse.class);
@ -80,14 +81,13 @@ public class TickerPriceTests {
MarketData marketData = new MarketData("1234", "abcd", mockedCloseableHttpClient);
Either<ServiceError, TickerPrice> res = marketData.getTickerPriceForSymbol("TEST");
TickerPrice res = marketData.getTickerPriceForSymbol("TEST");
assertTrue(res.isRight());
assertEquals(tickerPrice, Helpers.extractEitherValueSafely(res.right()));
assertEquals(tickerPrice, res);
}
@Test
public void testTickerPricesSuccessfully() throws IOException, BinanceServiceUnreachableException {
public void testTickerPricesSuccessfully() throws Exception {
CloseableHttpClient mockedCloseableHttpClient = mock(CloseableHttpClient.class);
CloseableHttpResponse mockedCloseableHttpResponse = mock(CloseableHttpResponse.class);
@ -106,15 +106,13 @@ public class TickerPriceTests {
MarketData marketData = new MarketData("1234", "abcd", mockedCloseableHttpClient);
Either<ServiceError, List<TickerPrice>> res = marketData.getTickerPrices();
List<TickerPrice> res = marketData.getTickerPrices();
assertTrue(res.isRight());
assertEquals(tickerPrices, Helpers.extractEitherValueSafely(res.right()));
assertEquals(tickerPrices, res);
}
@Test(expected = BinanceServiceUnreachableException.class)
public void testTickerPriceForSymbolWithoutHttpEntityThrows()
throws IOException, BinanceServiceUnreachableException {
public void testTickerPriceForSymbolWithoutHttpEntityThrows() throws Exception {
CloseableHttpClient mockedCloseableHttpClient = mock(CloseableHttpClient.class);
CloseableHttpResponse mockedCloseableHttpResponse = mock(CloseableHttpResponse.class);
@ -131,8 +129,7 @@ public class TickerPriceTests {
}
@Test(expected = BinanceServiceUnreachableException.class)
public void testTickerPricesWithoutHttpEntityThrows()
throws IOException, BinanceServiceUnreachableException {
public void testTickerPricesWithoutHttpEntityThrows() throws Exception {
CloseableHttpClient mockedCloseableHttpClient = mock(CloseableHttpClient.class);
CloseableHttpResponse mockedCloseableHttpResponse = mock(CloseableHttpResponse.class);
@ -149,8 +146,7 @@ public class TickerPriceTests {
}
@Test
public void testTickerPriceForSymbolWithBadApiKeyReturns400()
throws IOException, BinanceServiceUnreachableException {
public void testTickerPriceForSymbolWithBadApiKeyReturns400() throws Exception {
CloseableHttpClient mockedCloseableHttpClient = mock(CloseableHttpClient.class);
CloseableHttpResponse mockedCloseableHttpResponse = mock(CloseableHttpResponse.class);
@ -171,14 +167,21 @@ public class TickerPriceTests {
MarketData marketData = new MarketData("1234", "abcd", mockedCloseableHttpClient);
Either<ServiceError, TickerPrice> res = marketData.getTickerPriceForSymbol("TEST");
assertTrue(res.isLeft());
assertEquals(serviceError, Helpers.extractEitherValueSafely(res.left()));
try {
marketData.getTickerPriceForSymbol("TEST");
fail();
} catch (BinanceServiceException e) {
if (!(e instanceof MalformedRequestException)) {
fail();
}
assertEquals(400, ((MalformedRequestException) e).getCode());
assertEquals("Bad API Key", e.getMessage());
}
}
@Test
public void testTickerPricesBadApiKeyReturns400() throws IOException, BinanceServiceUnreachableException {
public void testTickerPricesBadApiKeyReturns400() throws Exception {
CloseableHttpClient mockedCloseableHttpClient = mock(CloseableHttpClient.class);
CloseableHttpResponse mockedCloseableHttpResponse = mock(CloseableHttpResponse.class);
@ -199,9 +202,15 @@ public class TickerPriceTests {
MarketData marketData = new MarketData("1234", "abcd", mockedCloseableHttpClient);
Either<ServiceError, List<TickerPrice>> res = marketData.getTickerPrices();
try {
marketData.getTickerPrices();
} catch (BinanceServiceException e) {
if (!(e instanceof MalformedRequestException)) {
fail();
}
assertTrue(res.isLeft());
assertEquals(serviceError, Helpers.extractEitherValueSafely(res.left()));
assertEquals(400, ((MalformedRequestException) e).getCode());
assertEquals("Bad API Key", e.getMessage());
}
}
}

31
src/test/java/com/sigmaflare/binancej/TimeMethodTests.java

@ -1,10 +1,11 @@
package com.sigmaflare.binancej;
import com.codepoetics.ambivalence.Either;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.sigmaflare.binancej.entities.ServiceError;
import com.sigmaflare.binancej.entities.Time;
import com.sigmaflare.binancej.exceptions.BinanceServiceException;
import com.sigmaflare.binancej.exceptions.BinanceServiceUnreachableException;
import com.sigmaflare.binancej.exceptions.MalformedRequestException;
import com.sigmaflare.binancej.matchers.GetMatcher;
import org.apache.http.ProtocolVersion;
import org.apache.http.client.methods.CloseableHttpResponse;
@ -19,7 +20,7 @@ import java.nio.charset.StandardCharsets;
import static com.sigmaflare.binancej.Constant.BASE_ENDPOINT;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@ -29,7 +30,7 @@ public class TimeMethodTests {
private final String url = String.format("%s%s", BASE_ENDPOINT, "/api/v1/time");
@Test
public void testTimeReturnsSuccessfully() throws IOException, BinanceServiceUnreachableException {
public void testTimeReturnsSuccessfully() throws Exception {
CloseableHttpClient mockedCloseableHttpClient = mock(CloseableHttpClient.class);
CloseableHttpResponse mockedCloseableHttpResponse = mock(CloseableHttpResponse.class);
@ -52,14 +53,13 @@ public class TimeMethodTests {
GeneralUtilities generalUtilities =
new GeneralUtilities("1234", "abcd", mockedCloseableHttpClient);
Either<ServiceError, Time> res = generalUtilities.getServerTime();
Time res = generalUtilities.getServerTime();
assertTrue(res.isRight());
assertEquals(time, Helpers.extractEitherValueSafely(res.right()));
assertEquals(time, res);
}
@Test(expected = BinanceServiceUnreachableException.class)
public void testTimeNoEntityReturnedFails() throws IOException, BinanceServiceUnreachableException {
public void testTimeNoEntityReturnedFails() throws Exception {
CloseableHttpClient mockedCloseableHttpClient = mock(CloseableHttpClient.class);
CloseableHttpResponse mockedCloseableHttpResponse = mock(CloseableHttpResponse.class);
@ -77,7 +77,7 @@ public class TimeMethodTests {
}
@Test
public void testTimeBadApiKeyReturns400() throws IOException, BinanceServiceUnreachableException {
public void testTimeBadApiKeyReturns400() throws Exception {
CloseableHttpClient mockedCloseableHttpClient = mock(CloseableHttpClient.class);
CloseableHttpResponse mockedCloseableHttpResponse = mock(CloseableHttpResponse.class);
@ -100,9 +100,16 @@ public class TimeMethodTests {
GeneralUtilities generalUtilities =
new GeneralUtilities("1234", "abcd", mockedCloseableHttpClient);
Either<ServiceError, Time> res = generalUtilities.getServerTime();
assertTrue(res.isLeft());
assertEquals(serviceError, Helpers.extractEitherValueSafely(res.left()));
try {
generalUtilities.getServerTime();
fail();
} catch(BinanceServiceException e) {
if(!(e instanceof MalformedRequestException)) {
fail();
}
assertEquals(400, ((MalformedRequestException) e).getCode());
assertEquals("Bad API Key", e.getMessage());
}
}
}

Loading…
Cancel
Save