Skip to content

Commit

Permalink
Merge pull request #4679 from ghubstan/5-suggested-changes
Browse files Browse the repository at this point in the history
Changes suggested in PRs 4672,4673,4674,4675
  • Loading branch information
sqrrm authored Oct 22, 2020
2 parents 01d4341 + 1f3554e commit 0e7070a
Show file tree
Hide file tree
Showing 21 changed files with 1,332 additions and 54 deletions.
7 changes: 6 additions & 1 deletion apitest/src/test/java/bisq/apitest/ApiTestCase.java
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,12 @@ protected static GrpcStubs grpcStubs(BisqAppConfig bisqAppConfig) {
}
}

protected void sleep(long ms) {
protected static void genBtcBlocksThenWait(int numBlocks, long wait) {
bitcoinCli.generateBlocks(numBlocks);
sleep(wait);
}

protected static void sleep(long ms) {
try {
MILLISECONDS.sleep(ms);
} catch (InterruptedException ignored) {
Expand Down
36 changes: 36 additions & 0 deletions apitest/src/test/java/bisq/apitest/method/MethodTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,22 @@

package bisq.apitest.method;

import bisq.proto.grpc.ConfirmPaymentReceivedRequest;
import bisq.proto.grpc.ConfirmPaymentStartedRequest;
import bisq.proto.grpc.CreatePaymentAccountRequest;
import bisq.proto.grpc.GetBalanceRequest;
import bisq.proto.grpc.GetFundingAddressesRequest;
import bisq.proto.grpc.GetOfferRequest;
import bisq.proto.grpc.GetPaymentAccountsRequest;
import bisq.proto.grpc.GetTradeRequest;
import bisq.proto.grpc.LockWalletRequest;
import bisq.proto.grpc.MarketPriceRequest;
import bisq.proto.grpc.OfferInfo;
import bisq.proto.grpc.RegisterDisputeAgentRequest;
import bisq.proto.grpc.RemoveWalletPasswordRequest;
import bisq.proto.grpc.SetWalletPasswordRequest;
import bisq.proto.grpc.TakeOfferRequest;
import bisq.proto.grpc.TradeInfo;
import bisq.proto.grpc.UnlockWalletRequest;

import protobuf.PaymentAccount;
Expand Down Expand Up @@ -88,6 +93,22 @@ protected final GetOfferRequest createGetOfferRequest(String offerId) {
return GetOfferRequest.newBuilder().setId(offerId).build();
}

protected final TakeOfferRequest createTakeOfferRequest(String offerId, String paymentAccountId) {
return TakeOfferRequest.newBuilder().setOfferId(offerId).setPaymentAccountId(paymentAccountId).build();
}

protected final GetTradeRequest createGetTradeRequest(String tradeId) {
return GetTradeRequest.newBuilder().setTradeId(tradeId).build();
}

protected final ConfirmPaymentStartedRequest createConfirmPaymentStartedRequest(String tradeId) {
return ConfirmPaymentStartedRequest.newBuilder().setTradeId(tradeId).build();
}

protected final ConfirmPaymentReceivedRequest createConfirmPaymentReceivedRequest(String tradeId) {
return ConfirmPaymentReceivedRequest.newBuilder().setTradeId(tradeId).build();
}

// Convenience methods for calling frequently used & thoroughly tested gRPC services.

protected final long getBalance(BisqAppConfig bisqAppConfig) {
Expand Down Expand Up @@ -149,6 +170,21 @@ protected final OfferInfo getOffer(BisqAppConfig bisqAppConfig, String offerId)
return grpcStubs(bisqAppConfig).offersService.getOffer(req).getOffer();
}

protected final TradeInfo getTrade(BisqAppConfig bisqAppConfig, String tradeId) {
var req = createGetTradeRequest(tradeId);
return grpcStubs(bisqAppConfig).tradesService.getTrade(req).getTrade();
}

protected final void confirmPaymentStarted(BisqAppConfig bisqAppConfig, String tradeId) {
var req = createConfirmPaymentStartedRequest(tradeId);
grpcStubs(bisqAppConfig).tradesService.confirmPaymentStarted(req);
}

protected final void confirmPaymentReceived(BisqAppConfig bisqAppConfig, String tradeId) {
var req = createConfirmPaymentReceivedRequest(tradeId);
grpcStubs(bisqAppConfig).tradesService.confirmPaymentReceived(req);
}

// Static conveniences for test methods and test case fixture setups.

protected static RegisterDisputeAgentRequest createRegisterDisputeAgentRequest(String disputeAgentType) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,12 @@

import bisq.core.monetary.Altcoin;

import bisq.proto.grpc.CreateOfferRequest;
import bisq.proto.grpc.GetOffersRequest;
import bisq.proto.grpc.OfferInfo;

import protobuf.PaymentAccount;

import org.bitcoinj.utils.Fiat;

import java.math.BigDecimal;
Expand All @@ -36,26 +39,28 @@

import static bisq.apitest.Scaffold.BitcoinCoreApp.bitcoind;
import static bisq.apitest.config.BisqAppConfig.alicedaemon;
import static bisq.apitest.config.BisqAppConfig.arbdaemon;
import static bisq.apitest.config.BisqAppConfig.bobdaemon;
import static bisq.apitest.config.BisqAppConfig.seednode;
import static bisq.common.util.MathUtils.roundDouble;
import static bisq.common.util.MathUtils.scaleDownByPowerOf10;
import static bisq.core.btc.wallet.Restrictions.getDefaultBuyerSecurityDepositAsPercent;
import static bisq.core.locale.CurrencyUtil.isCryptoCurrency;
import static java.lang.String.format;
import static java.math.RoundingMode.HALF_UP;
import static java.util.Comparator.comparing;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static org.junit.jupiter.api.Assertions.fail;



import bisq.apitest.method.MethodTest;
import bisq.cli.GrpcStubs;


@Slf4j
abstract class AbstractCreateOfferTest extends MethodTest {
public abstract class AbstractOfferTest extends MethodTest {

protected static GrpcStubs aliceStubs;
protected static GrpcStubs bobStubs;

@BeforeAll
public static void setUp() {
Expand All @@ -64,36 +69,74 @@ public static void setUp() {

static void startSupportingApps() {
try {
// setUpScaffold(new String[]{"--supportingApps", "bitcoind,seednode,alicedaemon", "--enableBisqDebugging", "true"});
setUpScaffold(bitcoind, seednode, alicedaemon);
// setUpScaffold(new String[]{"--supportingApps", "bitcoind,seednode,arbdaemon,alicedaemon,bobdaemon", "--enableBisqDebugging", "true"});
setUpScaffold(bitcoind, seednode, arbdaemon, alicedaemon, bobdaemon);
registerDisputeAgents(arbdaemon);
aliceStubs = grpcStubs(alicedaemon);
bobStubs = grpcStubs(bobdaemon);

// Generate 1 regtest block for alice's wallet to show 10 BTC balance,
// and give alicedaemon time to parse the new block.
bitcoinCli.generateBlocks(1);
MILLISECONDS.sleep(1500);
genBtcBlocksThenWait(1, 1500);
} catch (Exception ex) {
fail(ex);
}
}

protected final OfferInfo createAliceOffer(PaymentAccount paymentAccount,
String direction,
String currencyCode,
long amount) {
return createMarketBasedPricedOffer(aliceStubs, paymentAccount, direction, currencyCode, amount);
}

protected final OfferInfo createBobOffer(PaymentAccount paymentAccount,
String direction,
String currencyCode,
long amount) {
return createMarketBasedPricedOffer(bobStubs, paymentAccount, direction, currencyCode, amount);
}

protected final OfferInfo createMarketBasedPricedOffer(GrpcStubs grpcStubs,
PaymentAccount paymentAccount,
String direction,
String currencyCode,
long amount) {
var req = CreateOfferRequest.newBuilder()
.setPaymentAccountId(paymentAccount.getId())
.setDirection(direction)
.setCurrencyCode(currencyCode)
.setAmount(amount)
.setMinAmount(amount)
.setUseMarketBasedPrice(true)
.setMarketPriceMargin(0.00)
.setPrice("0")
.setBuyerSecurityDeposit(getDefaultBuyerSecurityDepositAsPercent())
.build();
return grpcStubs.offersService.createOffer(req).getOffer();
}

protected final OfferInfo getOffer(String offerId) {
return aliceStubs.offersService.getOffer(createGetOfferRequest(offerId)).getOffer();
}

protected final OfferInfo getMostRecentOffer(String direction, String currencyCode) {
List<OfferInfo> offerInfoList = getOffersSortedByDate(direction, currencyCode);
protected final OfferInfo getMostRecentOffer(GrpcStubs grpcStubs, String direction, String currencyCode) {
List<OfferInfo> offerInfoList = getOffersSortedByDate(grpcStubs, direction, currencyCode);
if (offerInfoList.isEmpty())
fail(format("No %s offers found for currency %s", direction, currencyCode));

return offerInfoList.get(offerInfoList.size() - 1);
}

protected final List<OfferInfo> getOffersSortedByDate(String direction, String currencyCode) {
protected final int getOpenOffersCount(GrpcStubs grpcStubs, String direction, String currencyCode) {
return getOffersSortedByDate(grpcStubs, direction, currencyCode).size();
}

protected final List<OfferInfo> getOffersSortedByDate(GrpcStubs grpcStubs, String direction, String currencyCode) {
var req = GetOffersRequest.newBuilder()
.setDirection(direction)
.setCurrencyCode(currencyCode).build();
var reply = aliceStubs.offersService.getOffers(req);
var reply = grpcStubs.offersService.getOffers(req);
return sortOffersByDate(reply.getOffersList());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@

@Slf4j
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class CreateOfferUsingFixedPriceTest extends AbstractCreateOfferTest {
public class CreateOfferUsingFixedPriceTest extends AbstractOfferTest {

@Test
@Order(1)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@

@Slf4j
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class CreateOfferUsingMarketPriceMarginTest extends AbstractCreateOfferTest {
public class CreateOfferUsingMarketPriceMarginTest extends AbstractOfferTest {

private static final DecimalFormat PCT_FORMAT = new DecimalFormat("##0.00");
private static final double MKT_PRICE_MARGIN_ERROR_TOLERANCE = 0.0050; // 0.50%
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@

@Slf4j
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class ValidateCreateOfferTest extends AbstractCreateOfferTest {
public class ValidateCreateOfferTest extends AbstractOfferTest {

@Test
@Order(1)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package bisq.apitest.method.trade;

import bisq.core.trade.Trade;

import bisq.proto.grpc.TradeInfo;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;



import bisq.apitest.method.offer.AbstractOfferTest;

public class AbstractTradeTest extends AbstractOfferTest {

protected final TradeInfo takeAlicesOffer(String offerId, String paymentAccountId) {
return bobStubs.tradesService.takeOffer(createTakeOfferRequest(offerId, paymentAccountId)).getTrade();
}

protected final TradeInfo takeBobsOffer(String offerId, String paymentAccountId) {
return aliceStubs.tradesService.takeOffer(createTakeOfferRequest(offerId, paymentAccountId)).getTrade();
}

protected final void verifyExpectedTradeStateAndPhase(TradeInfo trade,
Trade.State expectedState,
Trade.Phase expectedPhase) {
assertNotNull(trade);
assertEquals(expectedState.name(), trade.getState());
assertEquals(expectedPhase.name(), trade.getPhase());
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
/*
* This file is part of Bisq.
*
* Bisq is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Bisq is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
*/

package bisq.apitest.method.trade;

import protobuf.PaymentAccount;

import io.grpc.StatusRuntimeException;

import lombok.extern.slf4j.Slf4j;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;

import static bisq.apitest.config.BisqAppConfig.alicedaemon;
import static bisq.apitest.config.BisqAppConfig.bobdaemon;
import static bisq.core.trade.Trade.Phase.DEPOSIT_CONFIRMED;
import static bisq.core.trade.Trade.Phase.DEPOSIT_PUBLISHED;
import static bisq.core.trade.Trade.Phase.FIAT_SENT;
import static bisq.core.trade.Trade.Phase.PAYOUT_PUBLISHED;
import static bisq.core.trade.Trade.State.BUYER_SAW_ARRIVED_FIAT_PAYMENT_INITIATED_MSG;
import static bisq.core.trade.Trade.State.DEPOSIT_CONFIRMED_IN_BLOCK_CHAIN;
import static bisq.core.trade.Trade.State.SELLER_PUBLISHED_DEPOSIT_TX;
import static bisq.core.trade.Trade.State.SELLER_SAW_ARRIVED_PAYOUT_TX_PUBLISHED_MSG;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.fail;
import static protobuf.Offer.State.OFFER_FEE_PAID;
import static protobuf.OpenOffer.State.AVAILABLE;

@Slf4j
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class TakeBuyBTCOfferTest extends AbstractTradeTest {

// Alice is buyer, Bob is seller.

private static String tradeId;

private PaymentAccount alicesAccount;
private PaymentAccount bobsAccount;

@BeforeEach
public void init() {
alicesAccount = getDefaultPerfectDummyPaymentAccount(alicedaemon);
bobsAccount = getDefaultPerfectDummyPaymentAccount(bobdaemon);
}

@Test
@Order(1)
public void testTakeAlicesBuyOffer() {
try {
var alicesOffer = createAliceOffer(alicesAccount, "buy", "usd", 12500000);
var offerId = alicesOffer.getId();

// Wait for Alice's AddToOfferBook task.
// Wait times vary; my logs show >= 2 second delay.
sleep(3000);
assertEquals(1, getOpenOffersCount(aliceStubs, "buy", "usd"));

var trade = takeAlicesOffer(offerId, bobsAccount.getId());
assertNotNull(trade);
assertEquals(offerId, trade.getTradeId());
// Cache the trade id for the other tests.
tradeId = trade.getTradeId();

genBtcBlocksThenWait(1, 2250);
assertEquals(0, getOpenOffersCount(aliceStubs, "buy", "usd"));

trade = getTrade(bobdaemon, trade.getTradeId());
verifyExpectedTradeStateAndPhase(trade, SELLER_PUBLISHED_DEPOSIT_TX, DEPOSIT_PUBLISHED);

genBtcBlocksThenWait(1, 2250);
trade = getTrade(bobdaemon, trade.getTradeId());
verifyExpectedTradeStateAndPhase(trade, DEPOSIT_CONFIRMED_IN_BLOCK_CHAIN, DEPOSIT_CONFIRMED);
} catch (StatusRuntimeException e) {
fail(e);
}
}

@Test
@Order(2)
public void testAlicesConfirmPaymentStarted() {
try {
var trade = getTrade(alicedaemon, tradeId);
assertNotNull(trade);

confirmPaymentStarted(alicedaemon, trade.getTradeId());
sleep(3000);

trade = getTrade(alicedaemon, tradeId);
assertEquals(OFFER_FEE_PAID.name(), trade.getOffer().getState());
verifyExpectedTradeStateAndPhase(trade, BUYER_SAW_ARRIVED_FIAT_PAYMENT_INITIATED_MSG, FIAT_SENT);
} catch (StatusRuntimeException e) {
fail(e);
}
}

@Test
@Order(3)
public void testBobsConfirmPaymentReceived() {
var trade = getTrade(bobdaemon, tradeId);
assertNotNull(trade);

confirmPaymentReceived(bobdaemon, trade.getTradeId());
sleep(3000);

trade = getTrade(bobdaemon, tradeId);
// TODO is this a bug? Why is offer.state == available?
assertEquals(AVAILABLE.name(), trade.getOffer().getState());
verifyExpectedTradeStateAndPhase(trade, SELLER_SAW_ARRIVED_PAYOUT_TX_PUBLISHED_MSG, PAYOUT_PUBLISHED);
}
}
Loading

0 comments on commit 0e7070a

Please sign in to comment.