package com.mygeopay.core.exchange.shapeshift; import com.mygeopay.core.coins.BitcoinMain; import com.mygeopay.core.coins.CoinType; import com.mygeopay.core.coins.DogecoinMain; import com.mygeopay.core.coins.LitecoinMain; import com.mygeopay.core.coins.NuBitsMain; import com.mygeopay.core.coins.Value; import com.mygeopay.core.exchange.shapeshift.data.ShapeShiftAmountTx; import com.mygeopay.core.exchange.shapeshift.data.ShapeShiftCoin; import com.mygeopay.core.exchange.shapeshift.data.ShapeShiftCoins; import com.mygeopay.core.exchange.shapeshift.data.ShapeShiftEmail; import com.mygeopay.core.exchange.shapeshift.data.ShapeShiftException; import com.mygeopay.core.exchange.shapeshift.data.ShapeShiftLimit; import com.mygeopay.core.exchange.shapeshift.data.ShapeShiftMarketInfo; import com.mygeopay.core.exchange.shapeshift.data.ShapeShiftNormalTx; import com.mygeopay.core.exchange.shapeshift.data.ShapeShiftRate; import com.mygeopay.core.exchange.shapeshift.data.ShapeShiftTime; import com.mygeopay.core.exchange.shapeshift.data.ShapeShiftTxStatus; import com.squareup.okhttp.ConnectionSpec; import com.squareup.okhttp.mockwebserver.MockResponse; import com.squareup.okhttp.mockwebserver.MockWebServer; import com.squareup.okhttp.mockwebserver.RecordedRequest; import org.bitcoinj.core.Address; import org.bitcoinj.core.AddressFormatException; import org.json.JSONException; import org.json.JSONObject; import org.junit.After; import org.junit.Before; import org.junit.Test; import java.io.IOException; import java.util.Collections; import static org.junit.Assert.*; import static org.junit.Assert.assertEquals; /** * @author John L. Jegutanis */ public class ServerTest { final CoinType BTC = BitcoinMain.get(); final CoinType LTC = LitecoinMain.get(); final CoinType DOGE = DogecoinMain.get(); final CoinType NBT = NuBitsMain.get(); private MockWebServer server; private ShapeShift shapeShift; @Before public void setup() throws IOException { server = new MockWebServer(); server.start(); shapeShift = new ShapeShift(); shapeShift.baseUrl = server.getUrl("/").toString(); shapeShift.client.setConnectionSpecs(Collections.singletonList(ConnectionSpec.CLEARTEXT)); } @After public void tearDown() throws IOException { server.shutdown(); } @Test public void testGetCoins() throws ShapeShiftException, IOException, InterruptedException, JSONException { // Schedule some responses. server.enqueue(new MockResponse().setBody(GET_COINS_JSON)); ShapeShiftCoins coinsReply = shapeShift.getCoins(); assertFalse(coinsReply.isError); assertEquals(3, coinsReply.coins.size()); assertEquals(1, coinsReply.availableCoinTypes.size()); assertEquals(BTC, coinsReply.availableCoinTypes.get(0)); JSONObject coinsJson = new JSONObject(GET_COINS_JSON); for (ShapeShiftCoin coin : coinsReply.coins) { JSONObject json = coinsJson.getJSONObject(coin.symbol); assertEquals(json.getString("name"), coin.name); assertEquals(json.getString("symbol"), coin.symbol); assertEquals(json.getString("image"), coin.image.toString()); assertEquals(json.getString("status").equals("available"), coin.isAvailable); } // Optional: confirm that your app made the HTTP requests you were expecting. RecordedRequest request = server.takeRequest(); assertEquals("/getcoins", request.getPath()); } @Test public void testGetMarketInfo() throws ShapeShiftException, IOException, InterruptedException, JSONException { // Schedule some responses. server.enqueue(new MockResponse().setBody(MARKET_INFO_BTC_NBT_JSON)); ShapeShiftMarketInfo marketInfoReply = shapeShift.getMarketInfo(BTC, NBT); assertFalse(marketInfoReply.isError); assertEquals("btc_nbt", marketInfoReply.pair); assertNotNull(marketInfoReply.rate); assertNotNull(marketInfoReply.rate); assertNotNull(marketInfoReply.limit); assertNotNull(marketInfoReply.minimum); assertEquals(NBT.value("99.99"), marketInfoReply.rate.convert(BTC.value("1"))); assertEquals(BTC.value("4"), marketInfoReply.limit); assertEquals(BTC.value("0.00000104"), marketInfoReply.minimum); // Optional: confirm that your app made the HTTP requests you were expecting. RecordedRequest request = server.takeRequest(); assertEquals("/marketinfo/btc_nbt", request.getPath()); } @Test public void testGetRate() throws ShapeShiftException, IOException, InterruptedException, JSONException { // Schedule some responses. server.enqueue(new MockResponse().setBody(GET_RATE_BTC_LTC_JSON)); ShapeShiftRate rateReply = shapeShift.getRate(BTC, LTC); assertFalse(rateReply.isError); assertEquals("btc_ltc", rateReply.pair); assertNotNull(rateReply.rate); assertEquals(LTC, rateReply.rate.convert(BTC.oneCoin()).type); assertEquals(BTC, rateReply.rate.convert(LTC.oneCoin()).type); // Optional: confirm that your app made the HTTP requests you were expecting. RecordedRequest request = server.takeRequest(); assertEquals("/rate/btc_ltc", request.getPath()); } @Test public void testGetLimit() throws ShapeShiftException, IOException, InterruptedException, JSONException { // Schedule some responses. server.enqueue(new MockResponse().setBody(GET_LIMIT_BTC_LTC_JSON)); ShapeShiftLimit limitReply = shapeShift.getLimit(BTC, LTC); assertFalse(limitReply.isError); assertEquals("btc_ltc", limitReply.pair); assertNotNull(limitReply.limit); assertEquals(BTC, limitReply.limit.type); assertEquals("5", limitReply.limit.toPlainString()); // Optional: confirm that your app made the HTTP requests you were expecting. RecordedRequest request = server.takeRequest(); assertEquals("/limit/btc_ltc", request.getPath()); } @Test public void testGetTime() throws ShapeShiftException, IOException, InterruptedException, JSONException, AddressFormatException { // Schedule some responses. server.enqueue(new MockResponse().setBody(GET_TIME_PENDING_JSON)); server.enqueue(new MockResponse().setBody(GET_TIME_EXPIRED_JSON)); Address address = new Address(NuBitsMain.get(), "BPjxHqswNZB5vznbrAAxi5zGVq3ruhtBU8"); ShapeShiftTime timeReply = shapeShift.getTime(address); assertFalse(timeReply.isError); assertEquals(ShapeShiftTime.Status.PENDING, timeReply.status); assertEquals(100, timeReply.secondsRemaining); timeReply = shapeShift.getTime(address); assertFalse(timeReply.isError); assertEquals(ShapeShiftTime.Status.EXPIRED, timeReply.status); assertEquals(0, timeReply.secondsRemaining); // Optional: confirm that your app made the HTTP requests you were expecting. RecordedRequest request = server.takeRequest(); assertEquals("/timeremaining/BPjxHqswNZB5vznbrAAxi5zGVq3ruhtBU8", request.getPath()); request = server.takeRequest(); assertEquals("/timeremaining/BPjxHqswNZB5vznbrAAxi5zGVq3ruhtBU8", request.getPath()); } @Test public void testGetTxStatus() throws ShapeShiftException, IOException, InterruptedException, JSONException, AddressFormatException { // Schedule some responses. server.enqueue(new MockResponse().setBody(TX_STATUS_NO_DEPOSIT_JSON)); server.enqueue(new MockResponse().setBody(TX_STATUS_RECEIVED_JSON)); server.enqueue(new MockResponse().setBody(TX_STATUS_NEW_STATUS_JSON)); server.enqueue(new MockResponse().setBody(TX_STATUS_COMPLETE_JSON)); server.enqueue(new MockResponse().setBody(TX_STATUS_FAILED_JSON)); Address address = new Address(BTC, "1NDQPAGamGePkSZXW2CYBzXJEefB7N4bTN"); ShapeShiftTxStatus txStatusReply = shapeShift.getTxStatus(address); assertFalse(txStatusReply.isError); assertEquals(ShapeShiftTxStatus.Status.NO_DEPOSITS, txStatusReply.status); assertEquals(address, txStatusReply.address); txStatusReply = shapeShift.getTxStatus(address); assertFalse(txStatusReply.isError); assertEquals(ShapeShiftTxStatus.Status.RECEIVED, txStatusReply.status); assertEquals(address, txStatusReply.address); assertEquals("0.00297537", txStatusReply.incomingValue.toPlainString()); assertEquals(BTC, txStatusReply.incomingValue.type); txStatusReply = shapeShift.getTxStatus(address); assertFalse(txStatusReply.isError); assertEquals(ShapeShiftTxStatus.Status.UNKNOWN, txStatusReply.status); txStatusReply = shapeShift.getTxStatus(address); assertFalse(txStatusReply.isError); assertEquals(ShapeShiftTxStatus.Status.COMPLETE, txStatusReply.status); assertEquals(address, txStatusReply.address); assertEquals("0.00297537", txStatusReply.incomingValue.toPlainString()); assertEquals(BTC, txStatusReply.incomingValue.type); assertEquals("LMmeBWH17TWkQKvK7YFio2oiimPAzrHG6f", txStatusReply.withdraw.toString()); assertEquals(LTC, txStatusReply.withdraw.getParameters()); assertEquals("0.42", txStatusReply.outgoingValue.toPlainString()); assertEquals(LTC, txStatusReply.outgoingValue.type); assertEquals("66fa0b4c11227f9f05efa13d23e58c65b50acbd6395a126b5cd751064e6e79df", txStatusReply.transactionId); txStatusReply = shapeShift.getTxStatus(address); assertTrue(txStatusReply.isError); assertEquals(ShapeShiftTxStatus.Status.FAILED, txStatusReply.status); assertEquals("error", txStatusReply.errorMessage); // Optional: confirm that your app made the HTTP requests you were expecting. RecordedRequest request = server.takeRequest(); assertEquals("/txStat/1NDQPAGamGePkSZXW2CYBzXJEefB7N4bTN", request.getPath()); request = server.takeRequest(); assertEquals("/txStat/1NDQPAGamGePkSZXW2CYBzXJEefB7N4bTN", request.getPath()); request = server.takeRequest(); assertEquals("/txStat/1NDQPAGamGePkSZXW2CYBzXJEefB7N4bTN", request.getPath()); request = server.takeRequest(); assertEquals("/txStat/1NDQPAGamGePkSZXW2CYBzXJEefB7N4bTN", request.getPath()); } @Test public void testNormalTransaction() throws ShapeShiftException, IOException, InterruptedException, JSONException, AddressFormatException { // Schedule some responses. server.enqueue(new MockResponse().setBody(NORMAL_TRANSACTION_JSON)); Address withdrawal = new Address(DOGE, "DMHLQYG4j96V8cZX9WSuXxLs5RnZn6ibrV"); Address refund = new Address(BTC, "1Nz4xHJjNCnZFPjRUq8CN4BZEXTgLZfeUW"); ShapeShiftNormalTx normalTxReply = shapeShift.exchange(withdrawal, refund); assertFalse(normalTxReply.isError); assertEquals("btc_doge", normalTxReply.pair); assertEquals("18ETaXCYhJ8sxurh41vpKC3E6Tu7oJ94q8", normalTxReply.deposit.toString()); assertEquals(BTC, normalTxReply.deposit.getParameters()); assertEquals(withdrawal.toString(), normalTxReply.withdrawal.toString()); assertEquals(DOGE, normalTxReply.withdrawal.getParameters()); // Optional: confirm that your app made the HTTP requests you were expecting. RecordedRequest request = server.takeRequest(); assertEquals("/shift", request.getPath()); JSONObject reqJson = new JSONObject(request.getBody().readUtf8()); assertEquals(withdrawal.toString(), reqJson.getString("withdrawal")); assertEquals(refund.toString(), reqJson.getString("returnAddress")); assertEquals("btc_doge", reqJson.getString("pair")); } @Test public void testFixedAmountTransaction() throws ShapeShiftException, IOException, InterruptedException, JSONException, AddressFormatException { // Schedule some responses. server.enqueue(new MockResponse().setBody(FIXED_AMOUNT_TRANSACTION_JSON)); Address withdrawal = new Address(DOGE, "DMHLQYG4j96V8cZX9WSuXxLs5RnZn6ibrV"); Address refund = new Address(BTC, "1Nz4xHJjNCnZFPjRUq8CN4BZEXTgLZfeUW"); Value amount = DOGE.value("1000"); ShapeShiftAmountTx amountTxReply = shapeShift.exchangeForAmount(amount, withdrawal, refund); assertFalse(amountTxReply.isError); assertEquals("btc_doge", amountTxReply.pair); assertEquals("14gQ3xywKEUA6CfH61F8t2c6oB5nLnUjL5", amountTxReply.deposit.toString()); assertEquals(BTC, amountTxReply.deposit.getParameters()); assertEquals("0.00052379", amountTxReply.depositAmount.toPlainString()); assertEquals(BTC, amountTxReply.depositAmount.type); assertEquals(withdrawal.toString(), amountTxReply.withdrawal.toString()); assertEquals(DOGE, amountTxReply.withdrawal.getParameters()); assertEquals(amount.toPlainString(), amountTxReply.withdrawalAmount.toPlainString()); assertEquals(DOGE, amountTxReply.withdrawalAmount.type); assertEquals(1427149038191L, amountTxReply.expiration.getTime()); assertEquals(BTC.value(".00052379"), amountTxReply.rate.convert(Value.parse(DOGE, "1000"))); // Optional: confirm that your app made the HTTP requests you were expecting. RecordedRequest request = server.takeRequest(); assertEquals("/sendamount", request.getPath()); JSONObject reqJson = new JSONObject(request.getBody().readUtf8()); assertEquals(withdrawal.toString(), reqJson.getString("withdrawal")); assertEquals(refund.toString(), reqJson.getString("returnAddress")); assertEquals("btc_doge", reqJson.getString("pair")); assertEquals(amount.toPlainString(), reqJson.getString("amount")); } @Test public void testEmail() throws ShapeShiftException, IOException, InterruptedException, JSONException, AddressFormatException { // Schedule some responses. server.enqueue(new MockResponse().setBody(TX_STATUS_COMPLETE_JSON)); server.enqueue(new MockResponse().setBody(EMAIL_JSON)); ShapeShiftTxStatus txStatusReply = shapeShift.getTxStatus(BTC.address("1NDQPAGamGePkSZXW2CYBzXJEefB7N4bTN")); ShapeShiftEmail emailReply = shapeShift.requestEmailReceipt("mail@example.com", txStatusReply); assertFalse(emailReply.isError); assertEquals(ShapeShiftEmail.Status.SUCCESS, emailReply.status); assertEquals("Email receipt sent", emailReply.message); // Optional: confirm that your app made the HTTP requests you were expecting. RecordedRequest request = server.takeRequest(); assertEquals("/txStat/1NDQPAGamGePkSZXW2CYBzXJEefB7N4bTN", request.getPath()); request = server.takeRequest(); assertEquals("/mail", request.getPath()); JSONObject reqJson = new JSONObject(request.getBody().readUtf8()); assertEquals("mail@example.com", reqJson.getString("email")); assertEquals("66fa0b4c11227f9f05efa13d23e58c65b50acbd6395a126b5cd751064e6e79df", reqJson.getString("txid")); } @Test(expected = ShapeShiftException.class) public void testEmailFail() throws ShapeShiftException, IOException, InterruptedException, JSONException, AddressFormatException { server.enqueue(new MockResponse().setBody(TX_STATUS_NO_DEPOSIT_JSON)); ShapeShiftTxStatus txStatusReply = shapeShift .getTxStatus(BTC.address("1NDQPAGamGePkSZXW2CYBzXJEefB7N4bTN")); // Bad status shapeShift.requestEmailReceipt("mail@example.com", txStatusReply); } @Test(expected = ShapeShiftException.class) public void testGetMarketInfoFail() throws ShapeShiftException, IOException { server.enqueue(new MockResponse().setBody(MARKET_INFO_BTC_NBT_JSON)); // Incorrect pair shapeShift.getMarketInfo(BTC, LTC); } @Test(expected = ShapeShiftException.class) public void testGetRateFail() throws ShapeShiftException, IOException { server.enqueue(new MockResponse().setBody(GET_RATE_BTC_LTC_JSON)); // Incorrect pair shapeShift.getRate(NBT, LTC); } @Test(expected = ShapeShiftException.class) public void testGetLimitFail() throws ShapeShiftException, IOException { server.enqueue(new MockResponse().setBody(GET_LIMIT_BTC_LTC_JSON)); // Incorrect pair shapeShift.getLimit(LTC, DOGE); } @Test(expected = ShapeShiftException.class) public void testGetTxStatusFail() throws ShapeShiftException, AddressFormatException, IOException { server.enqueue(new MockResponse().setBody(TX_STATUS_COMPLETE_JSON)); // Used an incorrect address, correct is 1NDQPAGamGePkSZXW2CYBzXJEefB7N4bTN shapeShift.getTxStatus(BTC.address("18ETaXCYhJ8sxurh41vpKC3E6Tu7oJ94q8")); } @Test(expected = ShapeShiftException.class) public void testNormalTransactionFail() throws ShapeShiftException, AddressFormatException, IOException { server.enqueue(new MockResponse().setBody(NORMAL_TRANSACTION_JSON)); // Incorrect Dogecoin address, correct is DMHLQYG4j96V8cZX9WSuXxLs5RnZn6ibrV shapeShift.exchange(DOGE.address("DSntbp199h851m3Y1g3ruYCQHzWYCZQmmA"), BTC.address("1Nz4xHJjNCnZFPjRUq8CN4BZEXTgLZfeUW")); } @Test(expected = ShapeShiftException.class) public void testFixedAmountTransactionFail() throws ShapeShiftException, AddressFormatException, IOException { server.enqueue(new MockResponse().setBody(FIXED_AMOUNT_TRANSACTION_JSON)); // We withdraw Dogecoins to a Bitcoin address shapeShift.exchangeForAmount(DOGE.value("1000"), BTC.address("18ETaXCYhJ8sxurh41vpKC3E6Tu7oJ94q8"), BTC.address("1Nz4xHJjNCnZFPjRUq8CN4BZEXTgLZfeUW")); } @Test(expected = ShapeShiftException.class) public void testFixedAmountTransactionFail2() throws ShapeShiftException, AddressFormatException, IOException { server.enqueue(new MockResponse().setBody(FIXED_AMOUNT_TRANSACTION_JSON)); // Incorrect Dogecoin address, correct is DMHLQYG4j96V8cZX9WSuXxLs5RnZn6ibrV shapeShift.exchangeForAmount(DOGE.value("1000"), DOGE.address("DSntbp199h851m3Y1g3ruYCQHzWYCZQmmA"), BTC.address("1Nz4xHJjNCnZFPjRUq8CN4BZEXTgLZfeUW")); } @Test(expected = ShapeShiftException.class) public void testFixedAmountTransactionFail3() throws ShapeShiftException, AddressFormatException, IOException { server.enqueue(new MockResponse().setBody(FIXED_AMOUNT_TRANSACTION_JSON)); // Incorrect amount, correct is 1000 shapeShift.exchangeForAmount(DOGE.value("1"), DOGE.address("DMHLQYG4j96V8cZX9WSuXxLs5RnZn6ibrV"), BTC.address("1Nz4xHJjNCnZFPjRUq8CN4BZEXTgLZfeUW")); } public static final String GET_COINS_JSON = "{" + "BTC: {" + "name: \"Bitcoin\"," + "symbol: \"BTC\"," + "image: \"https://shapeshift.io/images/coins/bitcoin.png\"," + "status: \"available\"" + "}," + "LTC: {" + "name: \"Litecoin\"," + "symbol: \"LTC\"," + "image: \"https://shapeshift.io/images/coins/litecoin.png\"," + "status: \"unavailable\"" + "}," + "UNSUPPORTED: {" + "name: \"UnsupportedCoin\"," + "symbol: \"UNSUPPORTED\"," + "image: \"https://shapeshift.io/images/coins/UnsupportedCoin.png\"," + "status: \"available\"" + "}" + "}"; public static final String MARKET_INFO_BTC_NBT_JSON = "{" + "\"pair\" : \"btc_nbt\"," + "\"rate\" : \"100\"," + "\"minerFee\" : \"0.01\"," + "\"limit\" : \"4\"," + "\"minimum\" : 0.00000104" + "}"; public static final String GET_RATE_BTC_LTC_JSON = "{" + "\"pair\" : \"btc_ltc\"," + "\"rate\" : \"100\"" + "}"; public static final String GET_LIMIT_BTC_LTC_JSON = "{" + "\"pair\" : \"btc_ltc\"," + "\"limit\" : \"5\"," + "\"min\" : 0.00004008" + "}"; public static final String GET_TIME_PENDING_JSON = "{" + "status: \"pending\"," + "seconds_remaining: \"100\"" + "}"; public static final String GET_TIME_EXPIRED_JSON = "{" + "status: \"expired\"," + "seconds_remaining: \"0\"" + "}"; public static final String TX_STATUS_NO_DEPOSIT_JSON = "{" + "status: \"no_deposits\"," + "address: \"1NDQPAGamGePkSZXW2CYBzXJEefB7N4bTN\"" + "}"; public static final String TX_STATUS_RECEIVED_JSON = "{" + "status: \"received\"," + "address: \"1NDQPAGamGePkSZXW2CYBzXJEefB7N4bTN\"," + "incomingCoin: 0.00297537," + "incomingType: \"BTC\"" + "}"; public static final String TX_STATUS_NEW_STATUS_JSON = "{" + "status: \"some_new_optional_status\"," + "address: \"1NDQPAGamGePkSZXW2CYBzXJEefB7N4bTN\"," + "incomingCoin: 0.00297537," + "incomingType: \"BTC\"" + "}"; public static final String TX_STATUS_COMPLETE_JSON = "{" + "status: \"complete\"," + "address: \"1NDQPAGamGePkSZXW2CYBzXJEefB7N4bTN\"," + "withdraw: \"LMmeBWH17TWkQKvK7YFio2oiimPAzrHG6f\"," + "incomingCoin: 0.00297537," + "incomingType: \"BTC\"," + "outgoingCoin: \"0.42000000\"," + "outgoingType: \"LTC\"," + "transaction: \"66fa0b4c11227f9f05efa13d23e58c65b50acbd6395a126b5cd751064e6e79df\"" + "}"; public static final String TX_STATUS_FAILED_JSON = "{" + "status: \"failed\"," + "error: \"error\"" + "}"; public static final String NORMAL_TRANSACTION_JSON = "{" + "\"deposit\":\"18ETaXCYhJ8sxurh41vpKC3E6Tu7oJ94q8\"," + "\"depositType\":\"BTC\"," + "\"withdrawal\":\"DMHLQYG4j96V8cZX9WSuXxLs5RnZn6ibrV\"," + "\"withdrawalType\":\"DOGE\"" + "}"; public static final String FIXED_AMOUNT_TRANSACTION_JSON = "{" + "\"success\":{" + "\"pair\":\"btc_doge\"," + "\"withdrawal\":\"DMHLQYG4j96V8cZX9WSuXxLs5RnZn6ibrV\"," + "\"withdrawalAmount\":\"1000\"," + "\"minerFee\":\"1\"," + "\"deposit\":\"14gQ3xywKEUA6CfH61F8t2c6oB5nLnUjL5\"," + "\"depositAmount\":\"0.00052379\"," + "\"expiration\":1427149038191," + "\"quotedRate\":\"1911057.69230769\"," + "}" + "}"; public static final String EMAIL_JSON = "{" + "\"email\":{" + "\"status\":\"success\"," + "\"message\":\"Email receipt sent\"" + "}" + "}"; }