mirror of
https://github.com/haveno-dex/haveno.git
synced 2025-08-05 05:04:15 -04:00
general rebase in order to update payment methods and desktop app
Co-authored-by: Alva Swanson <alvasw@protonmail.com> Co-authored-by: andyheko <haoen.ko@gmail.com> Co-authored-by: Bisq GitHub Admin <51445974+bisq-github-admin-3@users.noreply.github.com> Co-authored-by: BtcContributor <79100296+BtcContributor@users.noreply.github.com> Co-authored-by: cd2357 <cd2357@users.noreply.github.com> Co-authored-by: chimp1984 <chimp1984@gmx.com> Co-authored-by: Chris Beams <chris@beams.io> Co-authored-by: Christoph Atteneder <christoph.atteneder@gmail.com> Co-authored-by: Devin Bileck <603793+devinbileck@users.noreply.github.com> Co-authored-by: ghubstan <36207203+ghubstan@users.noreply.github.com> Co-authored-by: Huey <hueydane@gmail.com> Co-authored-by: Jakub Loucký <jakub.loucky@outlook.cz> Co-authored-by: jmacxx <47253594+jmacxx@users.noreply.github.com> Co-authored-by: KanoczTomas <tomas.kanocz@cnl.sk> Co-authored-by: m52go <735155+m52go@users.noreply.github.com> Co-authored-by: Marcus0x <marcus0x@xrhodium.org> Co-authored-by: MarnixCroes <93143998+MarnixCroes@users.noreply.github.com> Co-authored-by: Martin Harrigan <martinharrigan@gmail.com> Co-authored-by: MwithM <50149324+MwithM@users.noreply.github.com> Co-authored-by: sqrrm <sqrrm@users.noreply.github.com> Co-authored-by: Stan <36207203+ghubstan@users.noreply.github.com> Co-authored-by: Stephan Oeste <emzy@emzy.de> Co-authored-by: Steven Barclay <stejbac@gmail.com> Co-authored-by: WAT <shiido.it@gmail.com> Co-authored-by: wiz <j@wiz.biz> Co-authored-by: xyzmaker123 <84982606+xyzmaker123@users.noreply.github.com>
This commit is contained in:
parent
15a1fe8a36
commit
88578bed10
539 changed files with 27629 additions and 8178 deletions
|
@ -17,7 +17,8 @@
|
|||
|
||||
package bisq.apitest;
|
||||
|
||||
import java.io.File;
|
||||
import java.time.Duration;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
@ -29,23 +30,19 @@ import javax.annotation.Nullable;
|
|||
|
||||
import org.junit.jupiter.api.TestInfo;
|
||||
|
||||
import static bisq.apitest.config.ApiTestRateMeterInterceptorConfig.getTestRateMeterInterceptorConfig;
|
||||
import static bisq.apitest.config.HavenoAppConfig.alicedaemon;
|
||||
import static bisq.apitest.config.HavenoAppConfig.arbdaemon;
|
||||
import static bisq.apitest.config.HavenoAppConfig.bobdaemon;
|
||||
import static bisq.proto.grpc.DisputeAgentsGrpc.getRegisterDisputeAgentMethod;
|
||||
import static bisq.proto.grpc.GetVersionGrpc.getGetVersionMethod;
|
||||
import static com.google.common.util.concurrent.Uninterruptibles.sleepUninterruptibly;
|
||||
import static java.net.InetAddress.getLoopbackAddress;
|
||||
import static java.util.Arrays.stream;
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
|
||||
|
||||
|
||||
import bisq.apitest.config.ApiTestConfig;
|
||||
import bisq.apitest.method.BitcoinCliHelper;
|
||||
import bisq.cli.GrpcClient;
|
||||
import bisq.daemon.grpc.GrpcVersionService;
|
||||
import bisq.daemon.grpc.interceptor.GrpcServiceRateMeteringConfig;
|
||||
|
||||
/**
|
||||
* Base class for all test types: 'method', 'scenario' and 'e2e'.
|
||||
|
@ -90,7 +87,7 @@ public class ApiTestCase {
|
|||
throws InterruptedException, ExecutionException, IOException {
|
||||
String[] params = new String[]{
|
||||
"--supportingApps", stream(supportingApps).map(Enum::name).collect(Collectors.joining(",")),
|
||||
"--callRateMeteringConfigPath", defaultRateMeterInterceptorConfig().getAbsolutePath(),
|
||||
"--callRateMeteringConfigPath", getTestRateMeterInterceptorConfig().getAbsolutePath(),
|
||||
"--enableBisqDebugging", "false"
|
||||
};
|
||||
setUpScaffold(params);
|
||||
|
@ -136,11 +133,7 @@ public class ApiTestCase {
|
|||
}
|
||||
|
||||
protected static void sleep(long ms) {
|
||||
try {
|
||||
MILLISECONDS.sleep(ms);
|
||||
} catch (InterruptedException ignored) {
|
||||
// empty
|
||||
}
|
||||
sleepUninterruptibly(Duration.ofMillis(ms));
|
||||
}
|
||||
|
||||
protected final String testName(TestInfo testInfo) {
|
||||
|
@ -148,37 +141,4 @@ public class ApiTestCase {
|
|||
? testInfo.getTestMethod().get().getName()
|
||||
: "unknown test name";
|
||||
}
|
||||
|
||||
protected static File defaultRateMeterInterceptorConfig() {
|
||||
GrpcServiceRateMeteringConfig.Builder builder = new GrpcServiceRateMeteringConfig.Builder();
|
||||
builder.addCallRateMeter(GrpcVersionService.class.getSimpleName(),
|
||||
getGetVersionMethod().getFullMethodName(),
|
||||
1,
|
||||
SECONDS);
|
||||
// Only GrpcVersionService is @VisibleForTesting, so we need to
|
||||
// hardcode other grpcServiceClassName parameter values used in
|
||||
// builder.addCallRateMeter(...).
|
||||
builder.addCallRateMeter("GrpcDisputeAgentsService",
|
||||
getRegisterDisputeAgentMethod().getFullMethodName(),
|
||||
10, // Same as default.
|
||||
SECONDS);
|
||||
// Define rate meters for non-existent method 'disabled', to override other grpc
|
||||
// services' default rate meters -- defined in their rateMeteringInterceptor()
|
||||
// methods.
|
||||
String[] serviceClassNames = new String[]{
|
||||
"GrpcGetTradeStatisticsService",
|
||||
"GrpcHelpService",
|
||||
"GrpcOffersService",
|
||||
"GrpcPaymentAccountsService",
|
||||
"GrpcPriceService",
|
||||
"GrpcTradesService",
|
||||
"GrpcWalletsService"
|
||||
};
|
||||
for (String service : serviceClassNames) {
|
||||
builder.addCallRateMeter(service, "disabled", 1, MILLISECONDS);
|
||||
}
|
||||
File file = builder.build();
|
||||
file.deleteOnExit();
|
||||
return file;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,17 +19,35 @@ package bisq.apitest.method;
|
|||
|
||||
import bisq.core.api.model.PaymentAccountForm;
|
||||
import bisq.core.payment.F2FAccount;
|
||||
import bisq.core.payment.NationalBankAccount;
|
||||
import bisq.core.proto.CoreProtoResolver;
|
||||
|
||||
import bisq.common.util.Utilities;
|
||||
|
||||
import bisq.proto.grpc.BalancesInfo;
|
||||
|
||||
import io.grpc.Status;
|
||||
import io.grpc.StatusRuntimeException;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import static bisq.apitest.config.ApiTestConfig.BTC;
|
||||
import static bisq.apitest.config.ApiTestRateMeterInterceptorConfig.getTestRateMeterInterceptorConfig;
|
||||
import static bisq.cli.table.builder.TableType.BTC_BALANCE_TBL;
|
||||
import static bisq.core.btc.wallet.Restrictions.getDefaultBuyerSecurityDepositAsPercent;
|
||||
import static java.lang.String.format;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static java.util.Arrays.stream;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
@ -37,7 +55,9 @@ import static org.junit.jupiter.api.Assertions.fail;
|
|||
|
||||
|
||||
import bisq.apitest.ApiTestCase;
|
||||
import bisq.apitest.linux.BashCommand;
|
||||
import bisq.cli.GrpcClient;
|
||||
import bisq.cli.table.builder.TableBuilder;
|
||||
|
||||
public class MethodTest extends ApiTestCase {
|
||||
|
||||
|
@ -46,15 +66,6 @@ public class MethodTest extends ApiTestCase {
|
|||
private static final Function<Enum<?>[], String> toNameList = (enums) ->
|
||||
stream(enums).map(Enum::name).collect(Collectors.joining(","));
|
||||
|
||||
public static void startSupportingApps(File callRateMeteringConfigFile,
|
||||
boolean generateBtcBlock,
|
||||
Enum<?>... supportingApps) {
|
||||
startSupportingApps(callRateMeteringConfigFile,
|
||||
generateBtcBlock,
|
||||
false,
|
||||
supportingApps);
|
||||
}
|
||||
|
||||
public static void startSupportingApps(File callRateMeteringConfigFile,
|
||||
boolean generateBtcBlock,
|
||||
boolean startSupportingAppsInDebugMode,
|
||||
|
@ -71,19 +82,12 @@ public class MethodTest extends ApiTestCase {
|
|||
}
|
||||
}
|
||||
|
||||
public static void startSupportingApps(boolean generateBtcBlock,
|
||||
Enum<?>... supportingApps) {
|
||||
startSupportingApps(generateBtcBlock,
|
||||
false,
|
||||
supportingApps);
|
||||
}
|
||||
|
||||
public static void startSupportingApps(boolean generateBtcBlock,
|
||||
boolean startSupportingAppsInDebugMode,
|
||||
Enum<?>... supportingApps) {
|
||||
try {
|
||||
// Disable call rate metering where there is no callRateMeteringConfigFile.
|
||||
File callRateMeteringConfigFile = defaultRateMeterInterceptorConfig();
|
||||
File callRateMeteringConfigFile = getTestRateMeterInterceptorConfig();
|
||||
setUpScaffold(new String[]{
|
||||
"--supportingApps", toNameList.apply(supportingApps),
|
||||
"--callRateMeteringConfigPath", callRateMeteringConfigFile.getAbsolutePath(),
|
||||
|
@ -133,17 +137,94 @@ public class MethodTest extends ApiTestCase {
|
|||
return f2FAccount;
|
||||
}
|
||||
|
||||
|
||||
protected bisq.core.payment.PaymentAccount createDummyBRLAccount(GrpcClient grpcClient,
|
||||
String holderName,
|
||||
String nationalAccountId,
|
||||
String holderTaxId) {
|
||||
String nationalBankAccountJsonString = "{\n" +
|
||||
" \"_COMMENTS_\": [ \"Dummy Account\" ],\n" +
|
||||
" \"paymentMethodId\": \"NATIONAL_BANK\",\n" +
|
||||
" \"accountName\": \"Banco do Brasil\",\n" +
|
||||
" \"country\": \"BR\",\n" +
|
||||
" \"bankName\": \"Banco do Brasil\",\n" +
|
||||
" \"branchId\": \"456789-10\",\n" +
|
||||
" \"holderName\": \"" + holderName + "\",\n" +
|
||||
" \"accountNr\": \"456789-87\",\n" +
|
||||
" \"nationalAccountId\": \"" + nationalAccountId + "\",\n" +
|
||||
" \"holderTaxId\": \"" + holderTaxId + "\"\n" +
|
||||
"}\n";
|
||||
NationalBankAccount nationalBankAccount =
|
||||
(NationalBankAccount) createPaymentAccount(grpcClient, nationalBankAccountJsonString);
|
||||
return nationalBankAccount;
|
||||
}
|
||||
|
||||
protected final bisq.core.payment.PaymentAccount createPaymentAccount(GrpcClient grpcClient, String jsonString) {
|
||||
// Normally, we do asserts on the protos from the gRPC service, but in this
|
||||
// case we need a bisq.core.payment.PaymentAccount so it can be cast to its
|
||||
// sub type.
|
||||
// sub-type.
|
||||
var paymentAccount = grpcClient.createPaymentAccount(jsonString);
|
||||
return bisq.core.payment.PaymentAccount.fromProto(paymentAccount, CORE_PROTO_RESOLVER);
|
||||
}
|
||||
|
||||
// Static conveniences for test methods and test case fixture setups.
|
||||
public static final Supplier<Double> defaultBuyerSecurityDepositPct = () -> {
|
||||
var defaultPct = BigDecimal.valueOf(getDefaultBuyerSecurityDepositAsPercent());
|
||||
if (defaultPct.precision() != 2)
|
||||
throw new IllegalStateException(format(
|
||||
"Unexpected decimal precision, expected 2 but actual is %d%n."
|
||||
+ "Check for changes to Restrictions.getDefaultBuyerSecurityDepositAsPercent()",
|
||||
defaultPct.precision()));
|
||||
|
||||
return defaultPct.movePointRight(2).doubleValue();
|
||||
};
|
||||
|
||||
public static String formatBalancesTbls(BalancesInfo allBalances) {
|
||||
StringBuilder balances = new StringBuilder(BTC).append("\n");
|
||||
balances.append(new TableBuilder(BTC_BALANCE_TBL, allBalances.getBtc()).build());
|
||||
balances.append("\n");
|
||||
return balances.toString();
|
||||
}
|
||||
|
||||
protected static String encodeToHex(String s) {
|
||||
return Utilities.bytesAsHexString(s.getBytes(UTF_8));
|
||||
}
|
||||
|
||||
protected static Status.Code getStatusRuntimeExceptionStatusCode(Exception grpcException) {
|
||||
if (grpcException instanceof StatusRuntimeException)
|
||||
return ((StatusRuntimeException) grpcException).getStatus().getCode();
|
||||
else
|
||||
throw new IllegalArgumentException(
|
||||
format("Expected a io.grpc.StatusRuntimeException argument, but got a %s",
|
||||
grpcException.getClass().getName()));
|
||||
}
|
||||
|
||||
protected void verifyNoLoggedNodeExceptions() {
|
||||
var loggedExceptions = getNodeExceptionMessages();
|
||||
if (loggedExceptions != null) {
|
||||
String err = format("Exception(s) found in daemon log(s):%n%s", loggedExceptions);
|
||||
fail(err);
|
||||
}
|
||||
}
|
||||
|
||||
protected void printNodeExceptionMessages(Logger log) {
|
||||
var loggedExceptions = getNodeExceptionMessages();
|
||||
if (loggedExceptions != null)
|
||||
log.error("Exception(s) found in daemon log(s):\n{}", loggedExceptions);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected static String getNodeExceptionMessages() {
|
||||
var nodeLogsSpec = config.rootAppDataDir.getAbsolutePath() + "/bisq-BTC_REGTEST_*_dao/bisq.log";
|
||||
var grep = "grep Exception " + nodeLogsSpec;
|
||||
var bashCommand = new BashCommand(grep);
|
||||
try {
|
||||
bashCommand.run();
|
||||
} catch (IOException | InterruptedException ex) {
|
||||
fail("Bash command execution error: " + ex);
|
||||
}
|
||||
if (bashCommand.getError() == null)
|
||||
return bashCommand.getOutput();
|
||||
else
|
||||
throw new IllegalStateException("Bash command execution error: " + bashCommand.getError());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -61,7 +61,7 @@ public class RegisterDisputeAgentsTest extends MethodTest {
|
|||
public void testRegisterArbitratorShouldThrowException() {
|
||||
Throwable exception = assertThrows(StatusRuntimeException.class, () ->
|
||||
arbClient.registerDisputeAgent(ARBITRATOR, DEV_PRIVILEGE_PRIV_KEY));
|
||||
assertEquals("INVALID_ARGUMENT: arbitrators must be registered in a Bisq UI",
|
||||
assertEquals("UNIMPLEMENTED: arbitrators must be registered in a Bisq UI",
|
||||
exception.getMessage());
|
||||
}
|
||||
|
||||
|
|
|
@ -17,13 +17,15 @@
|
|||
|
||||
package bisq.apitest.method.offer;
|
||||
|
||||
import bisq.core.monetary.Altcoin;
|
||||
import bisq.proto.grpc.OfferInfo;
|
||||
|
||||
import protobuf.PaymentAccount;
|
||||
|
||||
import org.bitcoinj.utils.Fiat;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.MathContext;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
import lombok.Setter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
@ -32,49 +34,96 @@ import org.junit.jupiter.api.AfterAll;
|
|||
import org.junit.jupiter.api.BeforeAll;
|
||||
|
||||
import static bisq.apitest.Scaffold.BitcoinCoreApp.bitcoind;
|
||||
import static bisq.apitest.config.ApiTestConfig.XMR;
|
||||
import static bisq.apitest.config.HavenoAppConfig.alicedaemon;
|
||||
import static bisq.apitest.config.HavenoAppConfig.arbdaemon;
|
||||
import static bisq.apitest.config.HavenoAppConfig.bobdaemon;
|
||||
import static bisq.apitest.config.HavenoAppConfig.seednode;
|
||||
import static bisq.common.util.MathUtils.roundDouble;
|
||||
import static bisq.common.util.MathUtils.scaleDownByPowerOf10;
|
||||
import static bisq.core.locale.CurrencyUtil.isCryptoCurrency;
|
||||
import static java.math.RoundingMode.HALF_UP;
|
||||
import static bisq.cli.table.builder.TableType.OFFER_TBL;
|
||||
import static java.lang.String.format;
|
||||
import static java.lang.System.out;
|
||||
|
||||
|
||||
|
||||
import bisq.apitest.method.MethodTest;
|
||||
import bisq.cli.CliMain;
|
||||
import bisq.cli.table.builder.TableBuilder;
|
||||
|
||||
@Slf4j
|
||||
public abstract class AbstractOfferTest extends MethodTest {
|
||||
|
||||
protected static final int ACTIVATE_OFFER = 1;
|
||||
protected static final int DEACTIVATE_OFFER = 0;
|
||||
protected static final String NO_TRIGGER_PRICE = "0";
|
||||
|
||||
@Setter
|
||||
protected static boolean isLongRunningTest;
|
||||
|
||||
protected static PaymentAccount alicesBtcAcct;
|
||||
protected static PaymentAccount bobsBtcAcct;
|
||||
|
||||
protected static PaymentAccount alicesXmrAcct;
|
||||
protected static PaymentAccount bobsXmrAcct;
|
||||
|
||||
@BeforeAll
|
||||
public static void setUp() {
|
||||
setUp(false);
|
||||
}
|
||||
|
||||
public static void setUp(boolean startSupportingAppsInDebugMode) {
|
||||
startSupportingApps(true,
|
||||
false,
|
||||
startSupportingAppsInDebugMode,
|
||||
bitcoind,
|
||||
seednode,
|
||||
arbdaemon,
|
||||
alicedaemon,
|
||||
bobdaemon);
|
||||
initPaymentAccounts();
|
||||
}
|
||||
|
||||
protected double getScaledOfferPrice(double offerPrice, String currencyCode) {
|
||||
int precision = isCryptoCurrency(currencyCode) ? Altcoin.SMALLEST_UNIT_EXPONENT : Fiat.SMALLEST_UNIT_EXPONENT;
|
||||
return scaleDownByPowerOf10(offerPrice, precision);
|
||||
protected static final Function<OfferInfo, String> toOfferTable = (offer) ->
|
||||
new TableBuilder(OFFER_TBL, offer).build().toString();
|
||||
|
||||
protected static final Function<List<OfferInfo>, String> toOffersTable = (offers) ->
|
||||
new TableBuilder(OFFER_TBL, offers).build().toString();
|
||||
|
||||
protected static String calcPriceAsString(double base, double delta, int precision) {
|
||||
var mathContext = new MathContext(precision);
|
||||
var priceAsBigDecimal = new BigDecimal(Double.toString(base), mathContext)
|
||||
.add(new BigDecimal(Double.toString(delta), mathContext))
|
||||
.round(mathContext);
|
||||
return format("%." + precision + "f", priceAsBigDecimal.doubleValue());
|
||||
}
|
||||
|
||||
protected final double getPercentageDifference(double price1, double price2) {
|
||||
return BigDecimal.valueOf(roundDouble((1 - (price1 / price2)), 5))
|
||||
.setScale(4, HALF_UP)
|
||||
.doubleValue();
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
public static void initPaymentAccounts() {
|
||||
alicesBtcAcct = aliceClient.getPaymentAccount("BTC");
|
||||
bobsBtcAcct = bobClient.getPaymentAccount("BTC");
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
public static void createXmrPaymentAccounts() {
|
||||
alicesXmrAcct = aliceClient.createCryptoCurrencyPaymentAccount("Alice's XMR Account",
|
||||
XMR,
|
||||
"44G4jWmSvTEfifSUZzTDnJVLPvYATmq9XhhtDqUof1BGCLceG82EQsVYG9Q9GN4bJcjbAJEc1JD1m5G7iK4UPZqACubV4Mq",
|
||||
false);
|
||||
log.trace("Alices XMR Account: {}", alicesXmrAcct);
|
||||
bobsXmrAcct = bobClient.createCryptoCurrencyPaymentAccount("Bob's XMR Account",
|
||||
XMR,
|
||||
"4BDRhdSBKZqAXs3PuNTbMtaXBNqFj5idC2yMVnQj8Rm61AyKY8AxLTt9vGRJ8pwcG4EtpyD8YpGqdZWCZ2VZj6yVBN2RVKs",
|
||||
false);
|
||||
log.trace("Bob's XMR Account: {}", bobsXmrAcct);
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
public static void tearDown() {
|
||||
tearDownScaffold();
|
||||
}
|
||||
|
||||
protected static void runCliGetOffer(String offerId) {
|
||||
out.println("Alice's CLI 'getmyoffer' response:");
|
||||
CliMain.main(new String[]{"--password=xyz", "--port=9998", "getmyoffer", "--offer-id=" + offerId});
|
||||
out.println("Bob's CLI 'getoffer' response:");
|
||||
CliMain.main(new String[]{"--password=xyz", "--port=9999", "getoffer", "--offer-id=" + offerId});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,9 +32,8 @@ import org.junit.jupiter.api.Order;
|
|||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestMethodOrder;
|
||||
|
||||
import static bisq.core.btc.wallet.Restrictions.getDefaultBuyerSecurityDepositAsPercent;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static protobuf.OfferPayload.Direction.BUY;
|
||||
import static protobuf.OfferDirection.BUY;
|
||||
|
||||
@Disabled
|
||||
@Slf4j
|
||||
|
@ -51,8 +50,9 @@ public class CancelOfferTest extends AbstractOfferTest {
|
|||
10000000L,
|
||||
10000000L,
|
||||
0.00,
|
||||
getDefaultBuyerSecurityDepositAsPercent(),
|
||||
paymentAccountId);
|
||||
defaultBuyerSecurityDepositPct.get(),
|
||||
paymentAccountId,
|
||||
NO_TRIGGER_PRICE);
|
||||
};
|
||||
|
||||
@Test
|
||||
|
|
|
@ -28,14 +28,14 @@ import org.junit.jupiter.api.Test;
|
|||
import org.junit.jupiter.api.TestMethodOrder;
|
||||
|
||||
import static bisq.apitest.config.ApiTestConfig.XMR;
|
||||
import static bisq.cli.TableFormat.formatOfferTable;
|
||||
import static bisq.core.btc.wallet.Restrictions.getDefaultBuyerSecurityDepositAsPercent;
|
||||
import static java.util.Collections.singletonList;
|
||||
import static bisq.apitest.config.ApiTestConfig.EUR;
|
||||
import static bisq.apitest.config.ApiTestConfig.USD;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
||||
import static protobuf.OfferPayload.Direction.BUY;
|
||||
import static protobuf.OfferPayload.Direction.SELL;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static protobuf.OfferDirection.BUY;
|
||||
import static protobuf.OfferDirection.SELL;
|
||||
|
||||
@Disabled
|
||||
@Slf4j
|
||||
|
@ -44,35 +44,44 @@ public class CreateOfferUsingFixedPriceTest extends AbstractOfferTest {
|
|||
|
||||
@Test
|
||||
@Order(1)
|
||||
public void testCreateAUDXMRBuyOfferUsingFixedPrice16000() {
|
||||
public void testCreateAUDBTCBuyOfferUsingFixedPrice16000() {
|
||||
PaymentAccount audAccount = createDummyF2FAccount(aliceClient, "AU");
|
||||
var newOffer = aliceClient.createFixedPricedOffer(BUY.name(),
|
||||
"aud",
|
||||
10_000_000L,
|
||||
10_000_000L,
|
||||
"36000",
|
||||
getDefaultBuyerSecurityDepositAsPercent(),
|
||||
defaultBuyerSecurityDepositPct.get(),
|
||||
audAccount.getId());
|
||||
log.info("OFFER #1:\n{}", formatOfferTable(singletonList(newOffer), "AUD"));
|
||||
log.debug("Offer #1:\n{}", toOfferTable.apply(newOffer));
|
||||
assertTrue(newOffer.getIsMyOffer());
|
||||
assertFalse(newOffer.getIsActivated());
|
||||
|
||||
String newOfferId = newOffer.getId();
|
||||
assertNotEquals("", newOfferId);
|
||||
assertEquals(BUY.name(), newOffer.getDirection());
|
||||
assertFalse(newOffer.getUseMarketBasedPrice());
|
||||
assertEquals(360_000_000, newOffer.getPrice());
|
||||
assertEquals("36000.0000", newOffer.getPrice());
|
||||
assertEquals(10_000_000, newOffer.getAmount());
|
||||
assertEquals(10_000_000, newOffer.getMinAmount());
|
||||
assertEquals("3600", newOffer.getVolume());
|
||||
assertEquals("3600", newOffer.getMinVolume());
|
||||
assertEquals(1_500_000, newOffer.getBuyerSecurityDeposit());
|
||||
assertEquals(audAccount.getId(), newOffer.getPaymentAccountId());
|
||||
assertEquals(XMR, newOffer.getBaseCurrencyCode());
|
||||
assertEquals("AUD", newOffer.getCounterCurrencyCode());
|
||||
|
||||
newOffer = aliceClient.getMyOffer(newOfferId);
|
||||
newOffer = aliceClient.getOffer(newOfferId);
|
||||
assertTrue(newOffer.getIsMyOffer());
|
||||
assertTrue(newOffer.getIsActivated());
|
||||
assertEquals(newOfferId, newOffer.getId());
|
||||
assertEquals(BUY.name(), newOffer.getDirection());
|
||||
assertFalse(newOffer.getUseMarketBasedPrice());
|
||||
assertEquals(360_000_000, newOffer.getPrice());
|
||||
assertEquals("36000.0000", newOffer.getPrice());
|
||||
assertEquals(10_000_000, newOffer.getAmount());
|
||||
assertEquals(10_000_000, newOffer.getMinAmount());
|
||||
assertEquals("3600", newOffer.getVolume());
|
||||
assertEquals("3600", newOffer.getMinVolume());
|
||||
assertEquals(1_500_000, newOffer.getBuyerSecurityDeposit());
|
||||
assertEquals(audAccount.getId(), newOffer.getPaymentAccountId());
|
||||
assertEquals(XMR, newOffer.getBaseCurrencyCode());
|
||||
|
@ -81,75 +90,93 @@ public class CreateOfferUsingFixedPriceTest extends AbstractOfferTest {
|
|||
|
||||
@Test
|
||||
@Order(2)
|
||||
public void testCreateUSDXMRBuyOfferUsingFixedPrice100001234() {
|
||||
public void testCreateUSDBTCBuyOfferUsingFixedPrice100001234() {
|
||||
PaymentAccount usdAccount = createDummyF2FAccount(aliceClient, "US");
|
||||
var newOffer = aliceClient.createFixedPricedOffer(BUY.name(),
|
||||
"usd",
|
||||
10_000_000L,
|
||||
10_000_000L,
|
||||
"30000.1234",
|
||||
getDefaultBuyerSecurityDepositAsPercent(),
|
||||
defaultBuyerSecurityDepositPct.get(),
|
||||
usdAccount.getId());
|
||||
log.info("OFFER #2:\n{}", formatOfferTable(singletonList(newOffer), "USD"));
|
||||
log.debug("Offer #2:\n{}", toOfferTable.apply(newOffer));
|
||||
assertTrue(newOffer.getIsMyOffer());
|
||||
assertFalse(newOffer.getIsActivated());
|
||||
|
||||
String newOfferId = newOffer.getId();
|
||||
assertNotEquals("", newOfferId);
|
||||
assertEquals(BUY.name(), newOffer.getDirection());
|
||||
assertFalse(newOffer.getUseMarketBasedPrice());
|
||||
assertEquals(300_001_234, newOffer.getPrice());
|
||||
assertEquals("30000.1234", newOffer.getPrice());
|
||||
assertEquals(10_000_000, newOffer.getAmount());
|
||||
assertEquals(10_000_000, newOffer.getMinAmount());
|
||||
assertEquals("3000", newOffer.getVolume());
|
||||
assertEquals("3000", newOffer.getMinVolume());
|
||||
assertEquals(1_500_000, newOffer.getBuyerSecurityDeposit());
|
||||
assertEquals(usdAccount.getId(), newOffer.getPaymentAccountId());
|
||||
assertEquals(XMR, newOffer.getBaseCurrencyCode());
|
||||
assertEquals("USD", newOffer.getCounterCurrencyCode());
|
||||
assertEquals(USD, newOffer.getCounterCurrencyCode());
|
||||
|
||||
newOffer = aliceClient.getMyOffer(newOfferId);
|
||||
newOffer = aliceClient.getOffer(newOfferId);
|
||||
assertTrue(newOffer.getIsMyOffer());
|
||||
assertTrue(newOffer.getIsActivated());
|
||||
assertEquals(newOfferId, newOffer.getId());
|
||||
assertEquals(BUY.name(), newOffer.getDirection());
|
||||
assertFalse(newOffer.getUseMarketBasedPrice());
|
||||
assertEquals(300_001_234, newOffer.getPrice());
|
||||
assertEquals("30000.1234", newOffer.getPrice());
|
||||
assertEquals(10_000_000, newOffer.getAmount());
|
||||
assertEquals(10_000_000, newOffer.getMinAmount());
|
||||
assertEquals("3000", newOffer.getVolume());
|
||||
assertEquals("3000", newOffer.getMinVolume());
|
||||
assertEquals(1_500_000, newOffer.getBuyerSecurityDeposit());
|
||||
assertEquals(usdAccount.getId(), newOffer.getPaymentAccountId());
|
||||
assertEquals(XMR, newOffer.getBaseCurrencyCode());
|
||||
assertEquals("USD", newOffer.getCounterCurrencyCode());
|
||||
assertEquals(USD, newOffer.getCounterCurrencyCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(3)
|
||||
public void testCreateEURXMRSellOfferUsingFixedPrice95001234() {
|
||||
public void testCreateEURBTCSellOfferUsingFixedPrice95001234() {
|
||||
PaymentAccount eurAccount = createDummyF2FAccount(aliceClient, "FR");
|
||||
var newOffer = aliceClient.createFixedPricedOffer(SELL.name(),
|
||||
"eur",
|
||||
10_000_000L,
|
||||
5_000_000L,
|
||||
"29500.1234",
|
||||
getDefaultBuyerSecurityDepositAsPercent(),
|
||||
defaultBuyerSecurityDepositPct.get(),
|
||||
eurAccount.getId());
|
||||
log.info("OFFER #3:\n{}", formatOfferTable(singletonList(newOffer), "EUR"));
|
||||
log.debug("Offer #3:\n{}", toOfferTable.apply(newOffer));
|
||||
assertTrue(newOffer.getIsMyOffer());
|
||||
assertFalse(newOffer.getIsActivated());
|
||||
|
||||
String newOfferId = newOffer.getId();
|
||||
assertNotEquals("", newOfferId);
|
||||
assertEquals(SELL.name(), newOffer.getDirection());
|
||||
assertFalse(newOffer.getUseMarketBasedPrice());
|
||||
assertEquals(295_001_234, newOffer.getPrice());
|
||||
assertEquals("29500.1234", newOffer.getPrice());
|
||||
assertEquals(10_000_000, newOffer.getAmount());
|
||||
assertEquals(5_000_000, newOffer.getMinAmount());
|
||||
assertEquals("2950", newOffer.getVolume());
|
||||
assertEquals("1475", newOffer.getMinVolume());
|
||||
assertEquals(1_500_000, newOffer.getBuyerSecurityDeposit());
|
||||
assertEquals(eurAccount.getId(), newOffer.getPaymentAccountId());
|
||||
assertEquals(XMR, newOffer.getBaseCurrencyCode());
|
||||
assertEquals("EUR", newOffer.getCounterCurrencyCode());
|
||||
assertEquals(EUR, newOffer.getCounterCurrencyCode());
|
||||
|
||||
newOffer = aliceClient.getMyOffer(newOfferId);
|
||||
newOffer = aliceClient.getOffer(newOfferId);
|
||||
assertTrue(newOffer.getIsMyOffer());
|
||||
assertTrue(newOffer.getIsActivated());
|
||||
assertEquals(newOfferId, newOffer.getId());
|
||||
assertEquals(SELL.name(), newOffer.getDirection());
|
||||
assertFalse(newOffer.getUseMarketBasedPrice());
|
||||
assertEquals(295_001_234, newOffer.getPrice());
|
||||
assertEquals("29500.1234", newOffer.getPrice());
|
||||
assertEquals(10_000_000, newOffer.getAmount());
|
||||
assertEquals(5_000_000, newOffer.getMinAmount());
|
||||
assertEquals("2950", newOffer.getVolume());
|
||||
assertEquals("1475", newOffer.getMinVolume());
|
||||
assertEquals(1_500_000, newOffer.getBuyerSecurityDeposit());
|
||||
assertEquals(eurAccount.getId(), newOffer.getPaymentAccountId());
|
||||
assertEquals(XMR, newOffer.getBaseCurrencyCode());
|
||||
assertEquals("EUR", newOffer.getCounterCurrencyCode());
|
||||
assertEquals(EUR, newOffer.getCounterCurrencyCode());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,8 @@ import bisq.proto.grpc.OfferInfo;
|
|||
|
||||
import java.text.DecimalFormat;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
|
@ -31,20 +33,22 @@ import org.junit.jupiter.api.Order;
|
|||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestMethodOrder;
|
||||
|
||||
import static bisq.apitest.config.ApiTestConfig.XMR;
|
||||
import static bisq.cli.TableFormat.formatOfferTable;
|
||||
import static bisq.apitest.config.ApiTestConfig.BTC;
|
||||
import static bisq.apitest.config.ApiTestConfig.USD;
|
||||
import static bisq.common.util.MathUtils.roundDouble;
|
||||
import static bisq.common.util.MathUtils.scaleDownByPowerOf10;
|
||||
import static bisq.common.util.MathUtils.scaleUpByPowerOf10;
|
||||
import static bisq.core.btc.wallet.Restrictions.getDefaultBuyerSecurityDepositAsPercent;
|
||||
import static java.lang.Math.abs;
|
||||
import static java.lang.String.format;
|
||||
import static java.util.Collections.singletonList;
|
||||
import static java.math.RoundingMode.HALF_UP;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static protobuf.OfferPayload.Direction.BUY;
|
||||
import static protobuf.OfferPayload.Direction.SELL;
|
||||
import static protobuf.OfferDirection.BUY;
|
||||
import static protobuf.OfferDirection.SELL;
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@Disabled
|
||||
@Slf4j
|
||||
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
|
||||
|
@ -54,77 +58,95 @@ public class CreateOfferUsingMarketPriceMarginTest extends AbstractOfferTest {
|
|||
private static final double MKT_PRICE_MARGIN_ERROR_TOLERANCE = 0.0050; // 0.50%
|
||||
private static final double MKT_PRICE_MARGIN_WARNING_TOLERANCE = 0.0001; // 0.01%
|
||||
|
||||
private static final String MAKER_FEE_CURRENCY_CODE = BTC;
|
||||
|
||||
@Test
|
||||
@Order(1)
|
||||
public void testCreateUSDXMRBuyOffer5PctPriceMargin() {
|
||||
public void testCreateUSDBTCBuyOffer5PctPriceMargin() {
|
||||
PaymentAccount usdAccount = createDummyF2FAccount(aliceClient, "US");
|
||||
double priceMarginPctInput = 5.00;
|
||||
double priceMarginPctInput = 5.00d;
|
||||
var newOffer = aliceClient.createMarketBasedPricedOffer(BUY.name(),
|
||||
"usd",
|
||||
10_000_000L,
|
||||
10_000_000L,
|
||||
priceMarginPctInput,
|
||||
getDefaultBuyerSecurityDepositAsPercent(),
|
||||
usdAccount.getId());
|
||||
log.info("OFFER #1:\n{}", formatOfferTable(singletonList(newOffer), "usd"));
|
||||
defaultBuyerSecurityDepositPct.get(),
|
||||
usdAccount.getId(),
|
||||
NO_TRIGGER_PRICE);
|
||||
log.debug("Offer #1:\n{}", toOfferTable.apply(newOffer));
|
||||
assertTrue(newOffer.getIsMyOffer());
|
||||
assertFalse(newOffer.getIsActivated());
|
||||
|
||||
String newOfferId = newOffer.getId();
|
||||
assertNotEquals("", newOfferId);
|
||||
assertEquals(BUY.name(), newOffer.getDirection());
|
||||
assertTrue(newOffer.getUseMarketBasedPrice());
|
||||
assertEquals(priceMarginPctInput, newOffer.getMarketPriceMarginPct());
|
||||
assertEquals(10_000_000, newOffer.getAmount());
|
||||
assertEquals(10_000_000, newOffer.getMinAmount());
|
||||
assertEquals(1_500_000, newOffer.getBuyerSecurityDeposit());
|
||||
assertEquals(usdAccount.getId(), newOffer.getPaymentAccountId());
|
||||
assertEquals(XMR, newOffer.getBaseCurrencyCode());
|
||||
assertEquals("USD", newOffer.getCounterCurrencyCode());
|
||||
assertEquals(BTC, newOffer.getBaseCurrencyCode());
|
||||
assertEquals(USD, newOffer.getCounterCurrencyCode());
|
||||
|
||||
newOffer = aliceClient.getMyOffer(newOfferId);
|
||||
newOffer = aliceClient.getOffer(newOfferId);
|
||||
assertTrue(newOffer.getIsMyOffer());
|
||||
assertTrue(newOffer.getIsActivated());
|
||||
assertEquals(newOfferId, newOffer.getId());
|
||||
assertEquals(BUY.name(), newOffer.getDirection());
|
||||
assertTrue(newOffer.getUseMarketBasedPrice());
|
||||
assertEquals(priceMarginPctInput, newOffer.getMarketPriceMarginPct());
|
||||
assertEquals(10_000_000, newOffer.getAmount());
|
||||
assertEquals(10_000_000, newOffer.getMinAmount());
|
||||
assertEquals(1_500_000, newOffer.getBuyerSecurityDeposit());
|
||||
assertEquals(usdAccount.getId(), newOffer.getPaymentAccountId());
|
||||
assertEquals(XMR, newOffer.getBaseCurrencyCode());
|
||||
assertEquals("USD", newOffer.getCounterCurrencyCode());
|
||||
assertEquals(BTC, newOffer.getBaseCurrencyCode());
|
||||
assertEquals(USD, newOffer.getCounterCurrencyCode());
|
||||
|
||||
assertCalculatedPriceIsCorrect(newOffer, priceMarginPctInput);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(2)
|
||||
public void testCreateNZDXMRBuyOfferMinus2PctPriceMargin() {
|
||||
public void testCreateNZDBTCBuyOfferMinus2PctPriceMargin() {
|
||||
PaymentAccount nzdAccount = createDummyF2FAccount(aliceClient, "NZ");
|
||||
double priceMarginPctInput = -2.00;
|
||||
double priceMarginPctInput = -2.00d; // -2%
|
||||
var newOffer = aliceClient.createMarketBasedPricedOffer(BUY.name(),
|
||||
"nzd",
|
||||
10_000_000L,
|
||||
10_000_000L,
|
||||
priceMarginPctInput,
|
||||
getDefaultBuyerSecurityDepositAsPercent(),
|
||||
nzdAccount.getId());
|
||||
log.info("OFFER #2:\n{}", formatOfferTable(singletonList(newOffer), "nzd"));
|
||||
defaultBuyerSecurityDepositPct.get(),
|
||||
nzdAccount.getId(),
|
||||
NO_TRIGGER_PRICE);
|
||||
log.debug("Offer #2:\n{}", toOfferTable.apply(newOffer));
|
||||
assertTrue(newOffer.getIsMyOffer());
|
||||
assertFalse(newOffer.getIsActivated());
|
||||
|
||||
String newOfferId = newOffer.getId();
|
||||
assertNotEquals("", newOfferId);
|
||||
assertEquals(BUY.name(), newOffer.getDirection());
|
||||
assertTrue(newOffer.getUseMarketBasedPrice());
|
||||
assertEquals(priceMarginPctInput, newOffer.getMarketPriceMarginPct());
|
||||
assertEquals(10_000_000, newOffer.getAmount());
|
||||
assertEquals(10_000_000, newOffer.getMinAmount());
|
||||
assertEquals(1_500_000, newOffer.getBuyerSecurityDeposit());
|
||||
assertEquals(nzdAccount.getId(), newOffer.getPaymentAccountId());
|
||||
assertEquals(XMR, newOffer.getBaseCurrencyCode());
|
||||
assertEquals(BTC, newOffer.getBaseCurrencyCode());
|
||||
assertEquals("NZD", newOffer.getCounterCurrencyCode());
|
||||
|
||||
newOffer = aliceClient.getMyOffer(newOfferId);
|
||||
newOffer = aliceClient.getOffer(newOfferId);
|
||||
assertTrue(newOffer.getIsMyOffer());
|
||||
assertTrue(newOffer.getIsActivated());
|
||||
assertEquals(newOfferId, newOffer.getId());
|
||||
assertEquals(BUY.name(), newOffer.getDirection());
|
||||
assertTrue(newOffer.getUseMarketBasedPrice());
|
||||
assertEquals(priceMarginPctInput, newOffer.getMarketPriceMarginPct());
|
||||
assertEquals(10_000_000, newOffer.getAmount());
|
||||
assertEquals(10_000_000, newOffer.getMinAmount());
|
||||
assertEquals(1_500_000, newOffer.getBuyerSecurityDeposit());
|
||||
assertEquals(nzdAccount.getId(), newOffer.getPaymentAccountId());
|
||||
assertEquals(XMR, newOffer.getBaseCurrencyCode());
|
||||
assertEquals(BTC, newOffer.getBaseCurrencyCode());
|
||||
assertEquals("NZD", newOffer.getCounterCurrencyCode());
|
||||
|
||||
assertCalculatedPriceIsCorrect(newOffer, priceMarginPctInput);
|
||||
|
@ -132,7 +154,7 @@ public class CreateOfferUsingMarketPriceMarginTest extends AbstractOfferTest {
|
|||
|
||||
@Test
|
||||
@Order(3)
|
||||
public void testCreateGBPXMRSellOfferMinus1Point5PctPriceMargin() {
|
||||
public void testCreateGBPBTCSellOfferMinus1Point5PctPriceMargin() {
|
||||
PaymentAccount gbpAccount = createDummyF2FAccount(aliceClient, "GB");
|
||||
double priceMarginPctInput = -1.5;
|
||||
var newOffer = aliceClient.createMarketBasedPricedOffer(SELL.name(),
|
||||
|
@ -140,29 +162,37 @@ public class CreateOfferUsingMarketPriceMarginTest extends AbstractOfferTest {
|
|||
10_000_000L,
|
||||
5_000_000L,
|
||||
priceMarginPctInput,
|
||||
getDefaultBuyerSecurityDepositAsPercent(),
|
||||
gbpAccount.getId());
|
||||
log.info("OFFER #3:\n{}", formatOfferTable(singletonList(newOffer), "gbp"));
|
||||
defaultBuyerSecurityDepositPct.get(),
|
||||
gbpAccount.getId(),
|
||||
NO_TRIGGER_PRICE);
|
||||
log.debug("Offer #3:\n{}", toOfferTable.apply(newOffer));
|
||||
assertTrue(newOffer.getIsMyOffer());
|
||||
assertFalse(newOffer.getIsActivated());
|
||||
|
||||
String newOfferId = newOffer.getId();
|
||||
assertNotEquals("", newOfferId);
|
||||
assertEquals(SELL.name(), newOffer.getDirection());
|
||||
assertTrue(newOffer.getUseMarketBasedPrice());
|
||||
assertEquals(priceMarginPctInput, newOffer.getMarketPriceMarginPct());
|
||||
assertEquals(10_000_000, newOffer.getAmount());
|
||||
assertEquals(5_000_000, newOffer.getMinAmount());
|
||||
assertEquals(1_500_000, newOffer.getBuyerSecurityDeposit());
|
||||
assertEquals(gbpAccount.getId(), newOffer.getPaymentAccountId());
|
||||
assertEquals(XMR, newOffer.getBaseCurrencyCode());
|
||||
assertEquals(BTC, newOffer.getBaseCurrencyCode());
|
||||
assertEquals("GBP", newOffer.getCounterCurrencyCode());
|
||||
|
||||
newOffer = aliceClient.getMyOffer(newOfferId);
|
||||
newOffer = aliceClient.getOffer(newOfferId);
|
||||
assertTrue(newOffer.getIsMyOffer());
|
||||
assertTrue(newOffer.getIsActivated());
|
||||
assertEquals(newOfferId, newOffer.getId());
|
||||
assertEquals(SELL.name(), newOffer.getDirection());
|
||||
assertTrue(newOffer.getUseMarketBasedPrice());
|
||||
assertEquals(priceMarginPctInput, newOffer.getMarketPriceMarginPct());
|
||||
assertEquals(10_000_000, newOffer.getAmount());
|
||||
assertEquals(5_000_000, newOffer.getMinAmount());
|
||||
assertEquals(1_500_000, newOffer.getBuyerSecurityDeposit());
|
||||
assertEquals(gbpAccount.getId(), newOffer.getPaymentAccountId());
|
||||
assertEquals(XMR, newOffer.getBaseCurrencyCode());
|
||||
assertEquals(BTC, newOffer.getBaseCurrencyCode());
|
||||
assertEquals("GBP", newOffer.getCounterCurrencyCode());
|
||||
|
||||
assertCalculatedPriceIsCorrect(newOffer, priceMarginPctInput);
|
||||
|
@ -170,7 +200,7 @@ public class CreateOfferUsingMarketPriceMarginTest extends AbstractOfferTest {
|
|||
|
||||
@Test
|
||||
@Order(4)
|
||||
public void testCreateBRLXMRSellOffer6Point55PctPriceMargin() {
|
||||
public void testCreateBRLBTCSellOffer6Point55PctPriceMargin() {
|
||||
PaymentAccount brlAccount = createDummyF2FAccount(aliceClient, "BR");
|
||||
double priceMarginPctInput = 6.55;
|
||||
var newOffer = aliceClient.createMarketBasedPricedOffer(SELL.name(),
|
||||
|
@ -178,53 +208,92 @@ public class CreateOfferUsingMarketPriceMarginTest extends AbstractOfferTest {
|
|||
10_000_000L,
|
||||
5_000_000L,
|
||||
priceMarginPctInput,
|
||||
getDefaultBuyerSecurityDepositAsPercent(),
|
||||
brlAccount.getId());
|
||||
log.info("OFFER #4:\n{}", formatOfferTable(singletonList(newOffer), "brl"));
|
||||
defaultBuyerSecurityDepositPct.get(),
|
||||
brlAccount.getId(),
|
||||
NO_TRIGGER_PRICE);
|
||||
log.debug("Offer #4:\n{}", toOfferTable.apply(newOffer));
|
||||
assertTrue(newOffer.getIsMyOffer());
|
||||
assertFalse(newOffer.getIsActivated());
|
||||
|
||||
String newOfferId = newOffer.getId();
|
||||
assertNotEquals("", newOfferId);
|
||||
assertEquals(SELL.name(), newOffer.getDirection());
|
||||
assertTrue(newOffer.getUseMarketBasedPrice());
|
||||
assertEquals(priceMarginPctInput, newOffer.getMarketPriceMarginPct());
|
||||
assertEquals(10_000_000, newOffer.getAmount());
|
||||
assertEquals(5_000_000, newOffer.getMinAmount());
|
||||
assertEquals(1_500_000, newOffer.getBuyerSecurityDeposit());
|
||||
assertEquals(brlAccount.getId(), newOffer.getPaymentAccountId());
|
||||
assertEquals(XMR, newOffer.getBaseCurrencyCode());
|
||||
assertEquals(BTC, newOffer.getBaseCurrencyCode());
|
||||
assertEquals("BRL", newOffer.getCounterCurrencyCode());
|
||||
|
||||
newOffer = aliceClient.getMyOffer(newOfferId);
|
||||
newOffer = aliceClient.getOffer(newOfferId);
|
||||
assertTrue(newOffer.getIsMyOffer());
|
||||
assertTrue(newOffer.getIsActivated());
|
||||
assertEquals(newOfferId, newOffer.getId());
|
||||
assertEquals(SELL.name(), newOffer.getDirection());
|
||||
assertTrue(newOffer.getUseMarketBasedPrice());
|
||||
assertEquals(priceMarginPctInput, newOffer.getMarketPriceMarginPct());
|
||||
assertEquals(10_000_000, newOffer.getAmount());
|
||||
assertEquals(5_000_000, newOffer.getMinAmount());
|
||||
assertEquals(1_500_000, newOffer.getBuyerSecurityDeposit());
|
||||
assertEquals(brlAccount.getId(), newOffer.getPaymentAccountId());
|
||||
assertEquals(XMR, newOffer.getBaseCurrencyCode());
|
||||
assertEquals(BTC, newOffer.getBaseCurrencyCode());
|
||||
assertEquals("BRL", newOffer.getCounterCurrencyCode());
|
||||
|
||||
assertCalculatedPriceIsCorrect(newOffer, priceMarginPctInput);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(5)
|
||||
public void testCreateUSDBTCBuyOfferWithTriggerPrice() {
|
||||
PaymentAccount usdAccount = createDummyF2FAccount(aliceClient, "US");
|
||||
double mktPriceAsDouble = aliceClient.getBtcPrice("usd");
|
||||
String triggerPrice = calcPriceAsString(mktPriceAsDouble, Double.parseDouble("1000.9999"), 4);
|
||||
var newOffer = aliceClient.createMarketBasedPricedOffer(BUY.name(),
|
||||
"usd",
|
||||
10_000_000L,
|
||||
5_000_000L,
|
||||
0.0,
|
||||
defaultBuyerSecurityDepositPct.get(),
|
||||
usdAccount.getId(),
|
||||
triggerPrice);
|
||||
assertTrue(newOffer.getIsMyOffer());
|
||||
assertFalse(newOffer.getIsActivated());
|
||||
|
||||
genBtcBlocksThenWait(1, 4000); // give time to add to offer book
|
||||
newOffer = aliceClient.getOffer(newOffer.getId());
|
||||
log.debug("Offer #5:\n{}", toOfferTable.apply(newOffer));
|
||||
assertTrue(newOffer.getIsMyOffer());
|
||||
assertTrue(newOffer.getIsActivated());
|
||||
assertEquals(triggerPrice, newOffer.getTriggerPrice());
|
||||
}
|
||||
|
||||
private void assertCalculatedPriceIsCorrect(OfferInfo offer, double priceMarginPctInput) {
|
||||
assertTrue(() -> {
|
||||
String counterCurrencyCode = offer.getCounterCurrencyCode();
|
||||
double mktPrice = aliceClient.getBtcPrice(counterCurrencyCode);
|
||||
double scaledOfferPrice = getScaledOfferPrice(offer.getPrice(), counterCurrencyCode);
|
||||
double priceAsDouble = Double.parseDouble(offer.getPrice());
|
||||
double expectedDiffPct = scaleDownByPowerOf10(priceMarginPctInput, 2);
|
||||
double actualDiffPct = offer.getDirection().equals(BUY.name())
|
||||
? getPercentageDifference(scaledOfferPrice, mktPrice)
|
||||
: getPercentageDifference(mktPrice, scaledOfferPrice);
|
||||
? getPercentageDifference(priceAsDouble, mktPrice)
|
||||
: getPercentageDifference(mktPrice, priceAsDouble);
|
||||
double pctDiffDelta = abs(expectedDiffPct) - abs(actualDiffPct);
|
||||
return isCalculatedPriceWithinErrorTolerance(pctDiffDelta,
|
||||
expectedDiffPct,
|
||||
actualDiffPct,
|
||||
mktPrice,
|
||||
scaledOfferPrice,
|
||||
priceAsDouble,
|
||||
offer);
|
||||
});
|
||||
}
|
||||
|
||||
private double getPercentageDifference(double price1, double price2) {
|
||||
return BigDecimal.valueOf(roundDouble((1 - (price1 / price2)), 5))
|
||||
.setScale(4, HALF_UP)
|
||||
.doubleValue();
|
||||
}
|
||||
|
||||
private boolean isCalculatedPriceWithinErrorTolerance(double delta,
|
||||
double expectedDiffPct,
|
||||
double actualDiffPct,
|
||||
|
@ -245,7 +314,7 @@ public class CreateOfferUsingMarketPriceMarginTest extends AbstractOfferTest {
|
|||
actualDiffPct,
|
||||
mktPrice,
|
||||
scaledOfferPrice);
|
||||
log.warn(offer.toString());
|
||||
log.trace(offer.toString());
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
|
@ -0,0 +1,265 @@
|
|||
/*
|
||||
* This file is part of Haveno.
|
||||
*
|
||||
* Haveno 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.
|
||||
*
|
||||
* Haveno 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 Haveno. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.apitest.method.offer;
|
||||
|
||||
import bisq.proto.grpc.OfferInfo;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
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.ApiTestConfig.BTC;
|
||||
import static bisq.apitest.config.ApiTestConfig.XMR;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static protobuf.OfferDirection.BUY;
|
||||
import static protobuf.OfferDirection.SELL;
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@Disabled
|
||||
@Slf4j
|
||||
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
|
||||
public class CreateXMROffersTest extends AbstractOfferTest {
|
||||
|
||||
private static final String MAKER_FEE_CURRENCY_CODE = BTC;
|
||||
|
||||
@BeforeAll
|
||||
public static void setUp() {
|
||||
AbstractOfferTest.setUp();
|
||||
createXmrPaymentAccounts();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(1)
|
||||
public void testCreateFixedPriceBuy1BTCFor200KXMROffer() {
|
||||
// Remember alt coin trades are BTC trades. When placing an offer, you are
|
||||
// offering to buy or sell BTC, not ETH, XMR, etc. In this test case,
|
||||
// Alice places an offer to BUY BTC.
|
||||
var newOffer = aliceClient.createFixedPricedOffer(BUY.name(),
|
||||
XMR,
|
||||
100_000_000L,
|
||||
75_000_000L,
|
||||
"0.005", // FIXED PRICE IN BTC FOR 1 XMR
|
||||
defaultBuyerSecurityDepositPct.get(),
|
||||
alicesXmrAcct.getId());
|
||||
log.debug("Sell XMR (Buy BTC) offer:\n{}", toOfferTable.apply(newOffer));
|
||||
assertTrue(newOffer.getIsMyOffer());
|
||||
assertFalse(newOffer.getIsActivated());
|
||||
|
||||
String newOfferId = newOffer.getId();
|
||||
assertNotEquals("", newOfferId);
|
||||
assertEquals(BUY.name(), newOffer.getDirection());
|
||||
assertFalse(newOffer.getUseMarketBasedPrice());
|
||||
assertEquals("0.00500000", newOffer.getPrice());
|
||||
assertEquals(100_000_000L, newOffer.getAmount());
|
||||
assertEquals(75_000_000L, newOffer.getMinAmount());
|
||||
assertEquals(15_000_000, newOffer.getBuyerSecurityDeposit());
|
||||
assertEquals(alicesXmrAcct.getId(), newOffer.getPaymentAccountId());
|
||||
assertEquals(XMR, newOffer.getBaseCurrencyCode());
|
||||
assertEquals(BTC, newOffer.getCounterCurrencyCode());
|
||||
|
||||
genBtcBlockAndWaitForOfferPreparation();
|
||||
|
||||
newOffer = aliceClient.getOffer(newOfferId);
|
||||
assertTrue(newOffer.getIsMyOffer());
|
||||
assertTrue(newOffer.getIsActivated());
|
||||
assertEquals(newOfferId, newOffer.getId());
|
||||
assertEquals(BUY.name(), newOffer.getDirection());
|
||||
assertFalse(newOffer.getUseMarketBasedPrice());
|
||||
assertEquals("0.00500000", newOffer.getPrice());
|
||||
assertEquals(100_000_000L, newOffer.getAmount());
|
||||
assertEquals(75_000_000L, newOffer.getMinAmount());
|
||||
assertEquals(15_000_000, newOffer.getBuyerSecurityDeposit());
|
||||
assertEquals(alicesXmrAcct.getId(), newOffer.getPaymentAccountId());
|
||||
assertEquals(XMR, newOffer.getBaseCurrencyCode());
|
||||
assertEquals(BTC, newOffer.getCounterCurrencyCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(2)
|
||||
public void testCreateFixedPriceSell1BTCFor200KXMROffer() {
|
||||
// Alice places an offer to SELL BTC for XMR.
|
||||
var newOffer = aliceClient.createFixedPricedOffer(SELL.name(),
|
||||
XMR,
|
||||
100_000_000L,
|
||||
50_000_000L,
|
||||
"0.005", // FIXED PRICE IN BTC (satoshis) FOR 1 XMR
|
||||
defaultBuyerSecurityDepositPct.get(),
|
||||
alicesXmrAcct.getId());
|
||||
log.debug("Buy XMR (Sell BTC) offer:\n{}", toOfferTable.apply(newOffer));
|
||||
assertTrue(newOffer.getIsMyOffer());
|
||||
assertFalse(newOffer.getIsActivated());
|
||||
|
||||
String newOfferId = newOffer.getId();
|
||||
assertNotEquals("", newOfferId);
|
||||
assertEquals(SELL.name(), newOffer.getDirection());
|
||||
assertFalse(newOffer.getUseMarketBasedPrice());
|
||||
assertEquals("0.00500000", newOffer.getPrice());
|
||||
assertEquals(100_000_000L, newOffer.getAmount());
|
||||
assertEquals(50_000_000L, newOffer.getMinAmount());
|
||||
assertEquals(15_000_000, newOffer.getBuyerSecurityDeposit());
|
||||
assertEquals(alicesXmrAcct.getId(), newOffer.getPaymentAccountId());
|
||||
assertEquals(XMR, newOffer.getBaseCurrencyCode());
|
||||
assertEquals(BTC, newOffer.getCounterCurrencyCode());
|
||||
|
||||
genBtcBlockAndWaitForOfferPreparation();
|
||||
|
||||
newOffer = aliceClient.getOffer(newOfferId);
|
||||
assertTrue(newOffer.getIsMyOffer());
|
||||
assertTrue(newOffer.getIsActivated());
|
||||
assertEquals(newOfferId, newOffer.getId());
|
||||
assertEquals(SELL.name(), newOffer.getDirection());
|
||||
assertFalse(newOffer.getUseMarketBasedPrice());
|
||||
assertEquals("0.00500000", newOffer.getPrice());
|
||||
assertEquals(100_000_000L, newOffer.getAmount());
|
||||
assertEquals(50_000_000L, newOffer.getMinAmount());
|
||||
assertEquals(15_000_000, newOffer.getBuyerSecurityDeposit());
|
||||
assertEquals(alicesXmrAcct.getId(), newOffer.getPaymentAccountId());
|
||||
assertEquals(XMR, newOffer.getBaseCurrencyCode());
|
||||
assertEquals(BTC, newOffer.getCounterCurrencyCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(3)
|
||||
public void testCreatePriceMarginBasedBuy1BTCOfferWithTriggerPrice() {
|
||||
double priceMarginPctInput = 1.00;
|
||||
double mktPriceAsDouble = aliceClient.getBtcPrice(XMR);
|
||||
String triggerPrice = calcPriceAsString(mktPriceAsDouble, Double.parseDouble("-0.001"), 8);
|
||||
var newOffer = aliceClient.createMarketBasedPricedOffer(BUY.name(),
|
||||
XMR,
|
||||
100_000_000L,
|
||||
75_000_000L,
|
||||
priceMarginPctInput,
|
||||
defaultBuyerSecurityDepositPct.get(),
|
||||
alicesXmrAcct.getId(),
|
||||
triggerPrice);
|
||||
log.debug("Pending Sell XMR (Buy BTC) offer:\n{}", toOfferTable.apply(newOffer));
|
||||
assertTrue(newOffer.getIsMyOffer());
|
||||
assertFalse(newOffer.getIsActivated());
|
||||
|
||||
String newOfferId = newOffer.getId();
|
||||
assertNotEquals("", newOfferId);
|
||||
assertEquals(BUY.name(), newOffer.getDirection());
|
||||
assertTrue(newOffer.getUseMarketBasedPrice());
|
||||
|
||||
// There is no trigger price while offer is pending.
|
||||
assertEquals(NO_TRIGGER_PRICE, newOffer.getTriggerPrice());
|
||||
|
||||
assertEquals(100_000_000L, newOffer.getAmount());
|
||||
assertEquals(75_000_000L, newOffer.getMinAmount());
|
||||
assertEquals(15_000_000, newOffer.getBuyerSecurityDeposit());
|
||||
assertEquals(alicesXmrAcct.getId(), newOffer.getPaymentAccountId());
|
||||
assertEquals(XMR, newOffer.getBaseCurrencyCode());
|
||||
assertEquals(BTC, newOffer.getCounterCurrencyCode());
|
||||
|
||||
genBtcBlockAndWaitForOfferPreparation();
|
||||
|
||||
newOffer = aliceClient.getOffer(newOfferId);
|
||||
log.debug("Available Sell XMR (Buy BTC) offer:\n{}", toOfferTable.apply(newOffer));
|
||||
assertTrue(newOffer.getIsMyOffer());
|
||||
assertTrue(newOffer.getIsActivated());
|
||||
assertEquals(newOfferId, newOffer.getId());
|
||||
assertEquals(BUY.name(), newOffer.getDirection());
|
||||
assertTrue(newOffer.getUseMarketBasedPrice());
|
||||
|
||||
// The trigger price should exist on the prepared offer.
|
||||
assertEquals(triggerPrice, newOffer.getTriggerPrice());
|
||||
|
||||
assertEquals(100_000_000L, newOffer.getAmount());
|
||||
assertEquals(75_000_000L, newOffer.getMinAmount());
|
||||
assertEquals(15_000_000, newOffer.getBuyerSecurityDeposit());
|
||||
assertEquals(alicesXmrAcct.getId(), newOffer.getPaymentAccountId());
|
||||
assertEquals(XMR, newOffer.getBaseCurrencyCode());
|
||||
assertEquals(BTC, newOffer.getCounterCurrencyCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(4)
|
||||
public void testCreatePriceMarginBasedSell1BTCOffer() {
|
||||
// Alice places an offer to SELL BTC for XMR.
|
||||
double priceMarginPctInput = 0.50;
|
||||
var newOffer = aliceClient.createMarketBasedPricedOffer(SELL.name(),
|
||||
XMR,
|
||||
100_000_000L,
|
||||
50_000_000L,
|
||||
priceMarginPctInput,
|
||||
defaultBuyerSecurityDepositPct.get(),
|
||||
alicesXmrAcct.getId(),
|
||||
NO_TRIGGER_PRICE);
|
||||
log.debug("Buy XMR (Sell BTC) offer:\n{}", toOfferTable.apply(newOffer));
|
||||
assertTrue(newOffer.getIsMyOffer());
|
||||
assertFalse(newOffer.getIsActivated());
|
||||
|
||||
String newOfferId = newOffer.getId();
|
||||
assertNotEquals("", newOfferId);
|
||||
assertEquals(SELL.name(), newOffer.getDirection());
|
||||
assertTrue(newOffer.getUseMarketBasedPrice());
|
||||
assertEquals(100_000_000L, newOffer.getAmount());
|
||||
assertEquals(50_000_000L, newOffer.getMinAmount());
|
||||
assertEquals(15_000_000, newOffer.getBuyerSecurityDeposit());
|
||||
assertEquals(alicesXmrAcct.getId(), newOffer.getPaymentAccountId());
|
||||
assertEquals(XMR, newOffer.getBaseCurrencyCode());
|
||||
assertEquals(BTC, newOffer.getCounterCurrencyCode());
|
||||
|
||||
genBtcBlockAndWaitForOfferPreparation();
|
||||
|
||||
newOffer = aliceClient.getOffer(newOfferId);
|
||||
assertTrue(newOffer.getIsMyOffer());
|
||||
assertTrue(newOffer.getIsActivated());
|
||||
assertEquals(newOfferId, newOffer.getId());
|
||||
assertEquals(SELL.name(), newOffer.getDirection());
|
||||
assertTrue(newOffer.getUseMarketBasedPrice());
|
||||
assertEquals(100_000_000L, newOffer.getAmount());
|
||||
assertEquals(50_000_000L, newOffer.getMinAmount());
|
||||
assertEquals(15_000_000, newOffer.getBuyerSecurityDeposit());
|
||||
assertEquals(alicesXmrAcct.getId(), newOffer.getPaymentAccountId());
|
||||
assertEquals(XMR, newOffer.getBaseCurrencyCode());
|
||||
assertEquals(BTC, newOffer.getCounterCurrencyCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(5)
|
||||
public void testGetAllMyXMROffers() {
|
||||
List<OfferInfo> offers = aliceClient.getMyOffersSortedByDate(XMR);
|
||||
log.debug("All of Alice's XMR offers:\n{}", toOffersTable.apply(offers));
|
||||
assertEquals(4, offers.size());
|
||||
log.debug("Alice's balances\n{}", formatBalancesTbls(aliceClient.getBalances()));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(6)
|
||||
public void testGetAvailableXMROffers() {
|
||||
List<OfferInfo> offers = bobClient.getOffersSortedByDate(XMR);
|
||||
log.debug("All of Bob's available XMR offers:\n{}", toOffersTable.apply(offers));
|
||||
assertEquals(4, offers.size());
|
||||
log.debug("Bob's balances\n{}", formatBalancesTbls(bobClient.getBalances()));
|
||||
}
|
||||
|
||||
private void genBtcBlockAndWaitForOfferPreparation() {
|
||||
genBtcBlocksThenWait(1, 5000);
|
||||
}
|
||||
}
|
|
@ -30,11 +30,10 @@ import org.junit.jupiter.api.Test;
|
|||
import org.junit.jupiter.api.TestMethodOrder;
|
||||
|
||||
import static bisq.apitest.config.ApiTestConfig.BTC;
|
||||
import static bisq.core.btc.wallet.Restrictions.getDefaultBuyerSecurityDepositAsPercent;
|
||||
import static java.lang.String.format;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static protobuf.OfferPayload.Direction.BUY;
|
||||
import static protobuf.OfferDirection.BUY;
|
||||
|
||||
@Disabled
|
||||
@Slf4j
|
||||
|
@ -52,7 +51,7 @@ public class ValidateCreateOfferTest extends AbstractOfferTest {
|
|||
100000000000L, // exceeds amount limit
|
||||
100000000000L,
|
||||
"10000.0000",
|
||||
getDefaultBuyerSecurityDepositAsPercent(),
|
||||
defaultBuyerSecurityDepositPct.get(),
|
||||
usdAccount.getId()));
|
||||
assertEquals("UNKNOWN: An error occurred at task: ValidateOffer", exception.getMessage());
|
||||
}
|
||||
|
@ -68,7 +67,7 @@ public class ValidateCreateOfferTest extends AbstractOfferTest {
|
|||
10000000L,
|
||||
10000000L,
|
||||
"40000.0000",
|
||||
getDefaultBuyerSecurityDepositAsPercent(),
|
||||
defaultBuyerSecurityDepositPct.get(),
|
||||
chfAccount.getId()));
|
||||
String expectedError = format("UNKNOWN: cannot create EUR offer with payment account %s", chfAccount.getId());
|
||||
assertEquals(expectedError, exception.getMessage());
|
||||
|
@ -85,7 +84,7 @@ public class ValidateCreateOfferTest extends AbstractOfferTest {
|
|||
10000000L,
|
||||
10000000L,
|
||||
"63000.0000",
|
||||
getDefaultBuyerSecurityDepositAsPercent(),
|
||||
defaultBuyerSecurityDepositPct.get(),
|
||||
audAccount.getId()));
|
||||
String expectedError = format("UNKNOWN: cannot create CAD offer with payment account %s", audAccount.getId());
|
||||
assertEquals(expectedError, exception.getMessage());
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package bisq.apitest.method.payment;
|
||||
|
||||
import bisq.core.api.model.PaymentAccountForm;
|
||||
import bisq.core.locale.FiatCurrency;
|
||||
import bisq.core.locale.Res;
|
||||
import bisq.core.locale.TradeCurrency;
|
||||
import bisq.core.payment.PaymentAccount;
|
||||
|
@ -17,10 +18,13 @@ import java.io.IOException;
|
|||
import java.io.OutputStreamWriter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
|
@ -57,14 +61,23 @@ public class AbstractPaymentAccountTest extends MethodTest {
|
|||
static final String PROPERTY_NAME_BANK_ACCOUNT_NAME = "bankAccountName";
|
||||
static final String PROPERTY_NAME_BANK_ACCOUNT_NUMBER = "bankAccountNumber";
|
||||
static final String PROPERTY_NAME_BANK_ACCOUNT_TYPE = "bankAccountType";
|
||||
static final String PROPERTY_NAME_BANK_ADDRESS = "bankAddress";
|
||||
static final String PROPERTY_NAME_BANK_BRANCH = "bankBranch";
|
||||
static final String PROPERTY_NAME_BANK_BRANCH_CODE = "bankBranchCode";
|
||||
static final String PROPERTY_NAME_BANK_BRANCH_NAME = "bankBranchName";
|
||||
static final String PROPERTY_NAME_BANK_CODE = "bankCode";
|
||||
static final String PROPERTY_NAME_BANK_COUNTRY_CODE = "bankCountryCode";
|
||||
@SuppressWarnings("unused")
|
||||
static final String PROPERTY_NAME_BANK_ID = "bankId";
|
||||
static final String PROPERTY_NAME_BANK_NAME = "bankName";
|
||||
static final String PROPERTY_NAME_BANK_SWIFT_CODE = "bankSwiftCode";
|
||||
static final String PROPERTY_NAME_BRANCH_ID = "branchId";
|
||||
static final String PROPERTY_NAME_BIC = "bic";
|
||||
static final String PROPERTY_NAME_BENEFICIARY_NAME = "beneficiaryName";
|
||||
static final String PROPERTY_NAME_BENEFICIARY_ACCOUNT_NR = "beneficiaryAccountNr";
|
||||
static final String PROPERTY_NAME_BENEFICIARY_ADDRESS = "beneficiaryAddress";
|
||||
static final String PROPERTY_NAME_BENEFICIARY_CITY = "beneficiaryCity";
|
||||
static final String PROPERTY_NAME_BENEFICIARY_PHONE = "beneficiaryPhone";
|
||||
static final String PROPERTY_NAME_COUNTRY = "country";
|
||||
static final String PROPERTY_NAME_CITY = "city";
|
||||
static final String PROPERTY_NAME_CONTACT = "contact";
|
||||
|
@ -75,6 +88,11 @@ public class AbstractPaymentAccountTest extends MethodTest {
|
|||
static final String PROPERTY_NAME_HOLDER_NAME = "holderName";
|
||||
static final String PROPERTY_NAME_HOLDER_TAX_ID = "holderTaxId";
|
||||
static final String PROPERTY_NAME_IBAN = "iban";
|
||||
static final String PROPERTY_NAME_INTERMEDIARY_ADDRESS = "intermediaryAddress";
|
||||
static final String PROPERTY_NAME_INTERMEDIARY_BRANCH = "intermediaryBranch";
|
||||
static final String PROPERTY_NAME_INTERMEDIARY_COUNTRY_CODE = "intermediaryCountryCode";
|
||||
static final String PROPERTY_NAME_INTERMEDIARY_NAME = "intermediaryName";
|
||||
static final String PROPERTY_NAME_INTERMEDIARY_SWIFT_CODE = "intermediarySwiftCode";
|
||||
static final String PROPERTY_NAME_MOBILE_NR = "mobileNr";
|
||||
static final String PROPERTY_NAME_NATIONAL_ACCOUNT_ID = "nationalAccountId";
|
||||
static final String PROPERTY_NAME_PAY_ID = "payid";
|
||||
|
@ -83,7 +101,9 @@ public class AbstractPaymentAccountTest extends MethodTest {
|
|||
static final String PROPERTY_NAME_QUESTION = "question";
|
||||
static final String PROPERTY_NAME_REQUIREMENTS = "requirements";
|
||||
static final String PROPERTY_NAME_SALT = "salt";
|
||||
static final String PROPERTY_NAME_SELECTED_TRADE_CURRENCY = "selectedTradeCurrency";
|
||||
static final String PROPERTY_NAME_SORT_CODE = "sortCode";
|
||||
static final String PROPERTY_NAME_SPECIAL_INSTRUCTIONS = "specialInstructions";
|
||||
static final String PROPERTY_NAME_STATE = "state";
|
||||
static final String PROPERTY_NAME_TRADE_CURRENCIES = "tradeCurrencies";
|
||||
static final String PROPERTY_NAME_USERNAME = "userName";
|
||||
|
@ -110,7 +130,7 @@ public class AbstractPaymentAccountTest extends MethodTest {
|
|||
COMPLETED_FORM_MAP.clear();
|
||||
|
||||
File emptyForm = getPaymentAccountForm(aliceClient, paymentMethodId);
|
||||
// A short cut over the API:
|
||||
// A shortcut over the API:
|
||||
// File emptyForm = PAYMENT_ACCOUNT_FORM.getPaymentAccountForm(paymentMethodId);
|
||||
log.debug("{} Empty form saved to {}",
|
||||
testName(testInfo),
|
||||
|
@ -125,7 +145,13 @@ public class AbstractPaymentAccountTest extends MethodTest {
|
|||
PAYMENT_ACCOUNT_FORM.toJsonString(jsonForm),
|
||||
Object.class);
|
||||
assertNotNull(emptyForm);
|
||||
assertEquals(PROPERTY_VALUE_JSON_COMMENTS, emptyForm.get(PROPERTY_NAME_JSON_COMMENTS));
|
||||
|
||||
if (paymentMethodId.equals("SWIFT_ID")) {
|
||||
assertEquals(getSwiftFormComments(), emptyForm.get(PROPERTY_NAME_JSON_COMMENTS));
|
||||
} else {
|
||||
assertEquals(PROPERTY_VALUE_JSON_COMMENTS, emptyForm.get(PROPERTY_NAME_JSON_COMMENTS));
|
||||
}
|
||||
|
||||
assertEquals(paymentMethodId, emptyForm.get(PROPERTY_NAME_PAYMENT_METHOD_ID));
|
||||
assertEquals("your accountname", emptyForm.get(PROPERTY_NAME_ACCOUNT_NAME));
|
||||
for (String field : fields) {
|
||||
|
@ -149,6 +175,15 @@ public class AbstractPaymentAccountTest extends MethodTest {
|
|||
assertEquals(expectedCurrencyCode, paymentAccount.getSingleTradeCurrency().getCode());
|
||||
}
|
||||
|
||||
protected final void verifyAccountTradeCurrencies(Collection<FiatCurrency> expectedFiatCurrencies,
|
||||
PaymentAccount paymentAccount) {
|
||||
assertNotNull(paymentAccount.getTradeCurrencies());
|
||||
List<TradeCurrency> expectedTradeCurrencies = new ArrayList<>() {{
|
||||
addAll(expectedFiatCurrencies);
|
||||
}};
|
||||
assertArrayEquals(expectedTradeCurrencies.toArray(), paymentAccount.getTradeCurrencies().toArray());
|
||||
}
|
||||
|
||||
protected final void verifyAccountTradeCurrencies(List<TradeCurrency> expectedTradeCurrencies,
|
||||
PaymentAccount paymentAccount) {
|
||||
assertNotNull(paymentAccount.getTradeCurrencies());
|
||||
|
@ -164,14 +199,36 @@ public class AbstractPaymentAccountTest extends MethodTest {
|
|||
assertTrue(paymentAccount.isPresent());
|
||||
}
|
||||
|
||||
protected final String getCompletedFormAsJsonString() {
|
||||
File completedForm = fillPaymentAccountForm();
|
||||
protected final String getCompletedFormAsJsonString(List<String> comments) {
|
||||
File completedForm = fillPaymentAccountForm(comments);
|
||||
String jsonString = PAYMENT_ACCOUNT_FORM.toJsonString(completedForm);
|
||||
log.debug("Completed form: {}", jsonString);
|
||||
return jsonString;
|
||||
}
|
||||
|
||||
private File fillPaymentAccountForm() {
|
||||
protected final String getCompletedFormAsJsonString() {
|
||||
File completedForm = fillPaymentAccountForm(PROPERTY_VALUE_JSON_COMMENTS);
|
||||
String jsonString = PAYMENT_ACCOUNT_FORM.toJsonString(completedForm);
|
||||
log.debug("Completed form: {}", jsonString);
|
||||
return jsonString;
|
||||
}
|
||||
|
||||
protected final String getCommaDelimitedFiatCurrencyCodes(Collection<FiatCurrency> fiatCurrencies) {
|
||||
return fiatCurrencies.stream()
|
||||
.sorted(Comparator.comparing(TradeCurrency::getCode))
|
||||
.map(c -> c.getCurrency().getCurrencyCode())
|
||||
.collect(Collectors.joining(","));
|
||||
}
|
||||
|
||||
protected final List<String> getSwiftFormComments() {
|
||||
List<String> comments = new ArrayList<>();
|
||||
comments.addAll(PROPERTY_VALUE_JSON_COMMENTS);
|
||||
List<String> wrappedSwiftComments = Res.getWrappedAsList("payment.swift.info.account", 110);
|
||||
comments.addAll(wrappedSwiftComments);
|
||||
return comments;
|
||||
}
|
||||
|
||||
private File fillPaymentAccountForm(List<String> comments) {
|
||||
File tmpJsonForm = null;
|
||||
try {
|
||||
tmpJsonForm = File.createTempFile("temp_acct_form_",
|
||||
|
@ -182,7 +239,7 @@ public class AbstractPaymentAccountTest extends MethodTest {
|
|||
|
||||
writer.name(PROPERTY_NAME_JSON_COMMENTS);
|
||||
writer.beginArray();
|
||||
for (String s : PROPERTY_VALUE_JSON_COMMENTS) {
|
||||
for (String s : comments) {
|
||||
writer.value(s);
|
||||
}
|
||||
writer.endArray();
|
||||
|
|
|
@ -17,12 +17,13 @@
|
|||
|
||||
package bisq.apitest.method.payment;
|
||||
|
||||
import bisq.core.locale.FiatCurrency;
|
||||
import bisq.core.locale.TradeCurrency;
|
||||
import bisq.core.payment.AdvancedCashAccount;
|
||||
import bisq.core.payment.AliPayAccount;
|
||||
import bisq.core.payment.AustraliaPayid;
|
||||
import bisq.core.payment.AustraliaPayidAccount;
|
||||
import bisq.core.payment.CapitualAccount;
|
||||
import bisq.core.payment.CashDepositAccount;
|
||||
import bisq.core.payment.ChaseQuickPayAccount;
|
||||
import bisq.core.payment.ClearXchangeAccount;
|
||||
import bisq.core.payment.F2FAccount;
|
||||
import bisq.core.payment.FasterPaymentsAccount;
|
||||
|
@ -32,7 +33,9 @@ import bisq.core.payment.JapanBankAccount;
|
|||
import bisq.core.payment.MoneyBeamAccount;
|
||||
import bisq.core.payment.MoneyGramAccount;
|
||||
import bisq.core.payment.NationalBankAccount;
|
||||
import bisq.core.payment.PaxumAccount;
|
||||
import bisq.core.payment.PaymentAccount;
|
||||
import bisq.core.payment.PayseraAccount;
|
||||
import bisq.core.payment.PerfectMoneyAccount;
|
||||
import bisq.core.payment.PopmoneyAccount;
|
||||
import bisq.core.payment.PromptPayAccount;
|
||||
|
@ -41,6 +44,7 @@ import bisq.core.payment.SameBankAccount;
|
|||
import bisq.core.payment.SepaAccount;
|
||||
import bisq.core.payment.SepaInstantAccount;
|
||||
import bisq.core.payment.SpecificBanksAccount;
|
||||
import bisq.core.payment.SwiftAccount;
|
||||
import bisq.core.payment.SwishAccount;
|
||||
import bisq.core.payment.TransferwiseAccount;
|
||||
import bisq.core.payment.USPostalMoneyOrderAccount;
|
||||
|
@ -51,14 +55,17 @@ import bisq.core.payment.payload.BankAccountPayload;
|
|||
import bisq.core.payment.payload.CashDepositAccountPayload;
|
||||
import bisq.core.payment.payload.SameBankAccountPayload;
|
||||
import bisq.core.payment.payload.SpecificBanksAccountPayload;
|
||||
import bisq.core.payment.payload.SwiftAccountPayload;
|
||||
|
||||
import io.grpc.StatusRuntimeException;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
|
@ -70,16 +77,23 @@ import org.junit.jupiter.api.TestInfo;
|
|||
import org.junit.jupiter.api.TestMethodOrder;
|
||||
|
||||
import static bisq.apitest.Scaffold.BitcoinCoreApp.bitcoind;
|
||||
import static bisq.apitest.config.ApiTestConfig.EUR;
|
||||
import static bisq.apitest.config.ApiTestConfig.USD;
|
||||
import static bisq.apitest.config.HavenoAppConfig.alicedaemon;
|
||||
import static bisq.cli.TableFormat.formatPaymentAcctTbl;
|
||||
import static bisq.core.locale.CurrencyUtil.*;
|
||||
import static bisq.cli.table.builder.TableType.PAYMENT_ACCOUNT_TBL;
|
||||
import static bisq.core.locale.CurrencyUtil.getAllSortedFiatCurrencies;
|
||||
import static bisq.core.locale.CurrencyUtil.getTradeCurrency;
|
||||
import static bisq.core.payment.payload.PaymentMethod.*;
|
||||
import static java.util.Collections.singletonList;
|
||||
import static java.util.Comparator.comparing;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
import static org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
|
||||
|
||||
|
||||
|
||||
import bisq.cli.table.builder.TableBuilder;
|
||||
|
||||
@SuppressWarnings({"OptionalGetWithoutIsPresent", "ConstantConditions"})
|
||||
@Disabled
|
||||
@Slf4j
|
||||
|
@ -104,11 +118,18 @@ public class CreatePaymentAccountTest extends AbstractPaymentAccountTest {
|
|||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_PAYMENT_METHOD_ID, ADVANCED_CASH_ID);
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_ACCOUNT_NAME, "Advanced Cash Acct");
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_ACCOUNT_NR, "0000 1111 2222");
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_TRADE_CURRENCIES, AdvancedCashAccount.SUPPORTED_CURRENCIES
|
||||
.stream()
|
||||
.map(TradeCurrency::getCode)
|
||||
.collect(Collectors.joining(",")));
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_SELECTED_TRADE_CURRENCY, "RUB");
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_SALT, encodeToHex("Restored Advanced Cash Acct Salt"));
|
||||
String jsonString = getCompletedFormAsJsonString();
|
||||
AdvancedCashAccount paymentAccount = (AdvancedCashAccount) createPaymentAccount(aliceClient, jsonString);
|
||||
verifyUserPayloadHasPaymentAccountWithId(aliceClient, paymentAccount.getId());
|
||||
verifyAccountTradeCurrencies(getAllAdvancedCashCurrencies(), paymentAccount);
|
||||
verifyAccountTradeCurrencies(AdvancedCashAccount.SUPPORTED_CURRENCIES, paymentAccount);
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_SELECTED_TRADE_CURRENCY),
|
||||
paymentAccount.getSelectedTradeCurrency().getCode());
|
||||
verifyCommonFormEntries(paymentAccount);
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_ACCOUNT_NR), paymentAccount.getAccountNr());
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_SALT), paymentAccount.getSaltAsHex());
|
||||
|
@ -146,7 +167,7 @@ public class CreatePaymentAccountTest extends AbstractPaymentAccountTest {
|
|||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_BANK_ACCOUNT_NAME, "Credit Union Australia");
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_SALT, encodeToHex("Restored Australia Pay ID Acct Salt"));
|
||||
String jsonString = getCompletedFormAsJsonString();
|
||||
AustraliaPayid paymentAccount = (AustraliaPayid) createPaymentAccount(aliceClient, jsonString);
|
||||
AustraliaPayidAccount paymentAccount = (AustraliaPayidAccount) createPaymentAccount(aliceClient, jsonString);
|
||||
verifyUserPayloadHasPaymentAccountWithId(aliceClient, paymentAccount.getId());
|
||||
verifyAccountSingleTradeCurrency("AUD", paymentAccount);
|
||||
verifyCommonFormEntries(paymentAccount);
|
||||
|
@ -156,6 +177,33 @@ public class CreatePaymentAccountTest extends AbstractPaymentAccountTest {
|
|||
print(paymentAccount);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateCapitualAccount(TestInfo testInfo) {
|
||||
File emptyForm = getEmptyForm(testInfo, CAPITUAL_ID);
|
||||
verifyEmptyForm(emptyForm,
|
||||
CAPITUAL_ID,
|
||||
PROPERTY_NAME_ACCOUNT_NR);
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_PAYMENT_METHOD_ID, CAPITUAL_ID);
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_ACCOUNT_NAME, "Capitual Acct");
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_ACCOUNT_NR, "1111 2222 3333-4");
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_TRADE_CURRENCIES, CapitualAccount.SUPPORTED_CURRENCIES
|
||||
.stream()
|
||||
.map(TradeCurrency::getCode)
|
||||
.collect(Collectors.joining(",")));
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_SELECTED_TRADE_CURRENCY, "BRL");
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_SALT, encodeToHex("Restored Capitual Acct Salt"));
|
||||
String jsonString = getCompletedFormAsJsonString();
|
||||
CapitualAccount paymentAccount = (CapitualAccount) createPaymentAccount(aliceClient, jsonString);
|
||||
verifyUserPayloadHasPaymentAccountWithId(aliceClient, paymentAccount.getId());
|
||||
verifyAccountTradeCurrencies(CapitualAccount.SUPPORTED_CURRENCIES, paymentAccount);
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_SELECTED_TRADE_CURRENCY),
|
||||
paymentAccount.getSelectedTradeCurrency().getCode());
|
||||
verifyCommonFormEntries(paymentAccount);
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_ACCOUNT_NR), paymentAccount.getAccountNr());
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_SALT), paymentAccount.getSaltAsHex());
|
||||
print(paymentAccount);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateCashDepositAccount(TestInfo testInfo) {
|
||||
File emptyForm = getEmptyForm(testInfo, CASH_DEPOSIT_ID);
|
||||
|
@ -189,7 +237,7 @@ public class CreatePaymentAccountTest extends AbstractPaymentAccountTest {
|
|||
String jsonString = getCompletedFormAsJsonString();
|
||||
CashDepositAccount paymentAccount = (CashDepositAccount) createPaymentAccount(aliceClient, jsonString);
|
||||
verifyUserPayloadHasPaymentAccountWithId(aliceClient, paymentAccount.getId());
|
||||
verifyAccountSingleTradeCurrency("EUR", paymentAccount);
|
||||
verifyAccountSingleTradeCurrency(EUR, paymentAccount);
|
||||
verifyCommonFormEntries(paymentAccount);
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_COUNTRY),
|
||||
Objects.requireNonNull(paymentAccount.getCountry()).code);
|
||||
|
@ -253,28 +301,6 @@ public class CreatePaymentAccountTest extends AbstractPaymentAccountTest {
|
|||
print(paymentAccount);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateChaseQuickPayAccount(TestInfo testInfo) {
|
||||
File emptyForm = getEmptyForm(testInfo, CHASE_QUICK_PAY_ID);
|
||||
verifyEmptyForm(emptyForm,
|
||||
CHASE_QUICK_PAY_ID,
|
||||
PROPERTY_NAME_EMAIL,
|
||||
PROPERTY_NAME_HOLDER_NAME);
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_PAYMENT_METHOD_ID, CHASE_QUICK_PAY_ID);
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_ACCOUNT_NAME, "Quick Pay Acct");
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_EMAIL, "johndoe@quickpay.com");
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_HOLDER_NAME, "John Doe");
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_SALT, "");
|
||||
String jsonString = getCompletedFormAsJsonString();
|
||||
ChaseQuickPayAccount paymentAccount = (ChaseQuickPayAccount) createPaymentAccount(aliceClient, jsonString);
|
||||
verifyUserPayloadHasPaymentAccountWithId(aliceClient, paymentAccount.getId());
|
||||
verifyAccountSingleTradeCurrency("USD", paymentAccount);
|
||||
verifyCommonFormEntries(paymentAccount);
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_EMAIL), paymentAccount.getEmail());
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_HOLDER_NAME), paymentAccount.getHolderName());
|
||||
print(paymentAccount);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateClearXChangeAccount(TestInfo testInfo) {
|
||||
File emptyForm = getEmptyForm(testInfo, CLEAR_X_CHANGE_ID);
|
||||
|
@ -290,7 +316,7 @@ public class CreatePaymentAccountTest extends AbstractPaymentAccountTest {
|
|||
String jsonString = getCompletedFormAsJsonString();
|
||||
ClearXchangeAccount paymentAccount = (ClearXchangeAccount) createPaymentAccount(aliceClient, jsonString);
|
||||
verifyUserPayloadHasPaymentAccountWithId(aliceClient, paymentAccount.getId());
|
||||
verifyAccountSingleTradeCurrency("USD", paymentAccount);
|
||||
verifyAccountSingleTradeCurrency(USD, paymentAccount);
|
||||
verifyCommonFormEntries(paymentAccount);
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_EMAIL_OR_MOBILE_NR), paymentAccount.getEmailOrMobileNr());
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_HOLDER_NAME), paymentAccount.getHolderName());
|
||||
|
@ -363,7 +389,7 @@ public class CreatePaymentAccountTest extends AbstractPaymentAccountTest {
|
|||
String jsonString = getCompletedFormAsJsonString();
|
||||
HalCashAccount paymentAccount = (HalCashAccount) createPaymentAccount(aliceClient, jsonString);
|
||||
verifyUserPayloadHasPaymentAccountWithId(aliceClient, paymentAccount.getId());
|
||||
verifyAccountSingleTradeCurrency("EUR", paymentAccount);
|
||||
verifyAccountSingleTradeCurrency(EUR, paymentAccount);
|
||||
verifyCommonFormEntries(paymentAccount);
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_MOBILE_NR), paymentAccount.getMobileNr());
|
||||
print(paymentAccount);
|
||||
|
@ -448,7 +474,7 @@ public class CreatePaymentAccountTest extends AbstractPaymentAccountTest {
|
|||
String jsonString = getCompletedFormAsJsonString();
|
||||
MoneyBeamAccount paymentAccount = (MoneyBeamAccount) createPaymentAccount(aliceClient, jsonString);
|
||||
verifyUserPayloadHasPaymentAccountWithId(aliceClient, paymentAccount.getId());
|
||||
verifyAccountSingleTradeCurrency("EUR", paymentAccount);
|
||||
verifyAccountSingleTradeCurrency(EUR, paymentAccount);
|
||||
verifyCommonFormEntries(paymentAccount);
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_ACCOUNT_ID), paymentAccount.getAccountId());
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_SALT), paymentAccount.getSaltAsHex());
|
||||
|
@ -466,6 +492,11 @@ public class CreatePaymentAccountTest extends AbstractPaymentAccountTest {
|
|||
PROPERTY_NAME_STATE);
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_PAYMENT_METHOD_ID, MONEY_GRAM_ID);
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_ACCOUNT_NAME, "Money Gram Acct");
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_TRADE_CURRENCIES, MoneyGramAccount.SUPPORTED_CURRENCIES
|
||||
.stream()
|
||||
.map(TradeCurrency::getCode)
|
||||
.collect(Collectors.joining(",")));
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_SELECTED_TRADE_CURRENCY, "INR");
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_HOLDER_NAME, "John Doe");
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_EMAIL, "john@doe.info");
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_COUNTRY, "US");
|
||||
|
@ -474,7 +505,9 @@ public class CreatePaymentAccountTest extends AbstractPaymentAccountTest {
|
|||
String jsonString = getCompletedFormAsJsonString();
|
||||
MoneyGramAccount paymentAccount = (MoneyGramAccount) createPaymentAccount(aliceClient, jsonString);
|
||||
verifyUserPayloadHasPaymentAccountWithId(aliceClient, paymentAccount.getId());
|
||||
verifyAccountTradeCurrencies(getAllMoneyGramCurrencies(), paymentAccount);
|
||||
verifyAccountTradeCurrencies(MoneyGramAccount.SUPPORTED_CURRENCIES, paymentAccount);
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_SELECTED_TRADE_CURRENCY),
|
||||
paymentAccount.getSelectedTradeCurrency().getCode());
|
||||
verifyCommonFormEntries(paymentAccount);
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_HOLDER_NAME), paymentAccount.getFullName());
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_EMAIL), paymentAccount.getEmail());
|
||||
|
@ -497,13 +530,65 @@ public class CreatePaymentAccountTest extends AbstractPaymentAccountTest {
|
|||
String jsonString = getCompletedFormAsJsonString();
|
||||
PerfectMoneyAccount paymentAccount = (PerfectMoneyAccount) createPaymentAccount(aliceClient, jsonString);
|
||||
verifyUserPayloadHasPaymentAccountWithId(aliceClient, paymentAccount.getId());
|
||||
verifyAccountSingleTradeCurrency("USD", paymentAccount);
|
||||
verifyAccountSingleTradeCurrency(USD, paymentAccount);
|
||||
verifyCommonFormEntries(paymentAccount);
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_ACCOUNT_NR), paymentAccount.getAccountNr());
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_SALT), paymentAccount.getSaltAsHex());
|
||||
print(paymentAccount);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreatePaxumAccount(TestInfo testInfo) {
|
||||
File emptyForm = getEmptyForm(testInfo, PAXUM_ID);
|
||||
verifyEmptyForm(emptyForm,
|
||||
PAXUM_ID,
|
||||
PROPERTY_NAME_EMAIL);
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_PAYMENT_METHOD_ID, PAXUM_ID);
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_ACCOUNT_NAME, "Paxum Acct");
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_TRADE_CURRENCIES, PaxumAccount.SUPPORTED_CURRENCIES
|
||||
.stream()
|
||||
.map(TradeCurrency::getCode)
|
||||
.collect(Collectors.joining(",")));
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_SELECTED_TRADE_CURRENCY, "SEK");
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_EMAIL, "jane@doe.net");
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_SALT, "");
|
||||
String jsonString = getCompletedFormAsJsonString();
|
||||
PaxumAccount paymentAccount = (PaxumAccount) createPaymentAccount(aliceClient, jsonString);
|
||||
verifyUserPayloadHasPaymentAccountWithId(aliceClient, paymentAccount.getId());
|
||||
verifyAccountTradeCurrencies(PaxumAccount.SUPPORTED_CURRENCIES, paymentAccount);
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_SELECTED_TRADE_CURRENCY),
|
||||
paymentAccount.getSelectedTradeCurrency().getCode());
|
||||
verifyCommonFormEntries(paymentAccount);
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_EMAIL), paymentAccount.getEmail());
|
||||
print(paymentAccount);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreatePayseraAccount(TestInfo testInfo) {
|
||||
File emptyForm = getEmptyForm(testInfo, PAYSERA_ID);
|
||||
verifyEmptyForm(emptyForm,
|
||||
PAYSERA_ID,
|
||||
PROPERTY_NAME_EMAIL);
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_PAYMENT_METHOD_ID, PAYSERA_ID);
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_ACCOUNT_NAME, "Paysera Acct");
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_TRADE_CURRENCIES, PayseraAccount.SUPPORTED_CURRENCIES
|
||||
.stream()
|
||||
.map(TradeCurrency::getCode)
|
||||
.collect(Collectors.joining(",")));
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_SELECTED_TRADE_CURRENCY, "ZAR");
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_EMAIL, "jane@doe.net");
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_SALT, "");
|
||||
String jsonString = getCompletedFormAsJsonString();
|
||||
PayseraAccount paymentAccount = (PayseraAccount) createPaymentAccount(aliceClient, jsonString);
|
||||
verifyUserPayloadHasPaymentAccountWithId(aliceClient, paymentAccount.getId());
|
||||
verifyAccountTradeCurrencies(PayseraAccount.SUPPORTED_CURRENCIES, paymentAccount);
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_SELECTED_TRADE_CURRENCY),
|
||||
paymentAccount.getSelectedTradeCurrency().getCode());
|
||||
verifyCommonFormEntries(paymentAccount);
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_EMAIL), paymentAccount.getEmail());
|
||||
print(paymentAccount);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreatePopmoneyAccount(TestInfo testInfo) {
|
||||
File emptyForm = getEmptyForm(testInfo, POPMONEY_ID);
|
||||
|
@ -519,7 +604,7 @@ public class CreatePaymentAccountTest extends AbstractPaymentAccountTest {
|
|||
String jsonString = getCompletedFormAsJsonString();
|
||||
PopmoneyAccount paymentAccount = (PopmoneyAccount) createPaymentAccount(aliceClient, jsonString);
|
||||
verifyUserPayloadHasPaymentAccountWithId(aliceClient, paymentAccount.getId());
|
||||
verifyAccountSingleTradeCurrency("USD", paymentAccount);
|
||||
verifyAccountSingleTradeCurrency(USD, paymentAccount);
|
||||
verifyCommonFormEntries(paymentAccount);
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_ACCOUNT_ID), paymentAccount.getAccountId());
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_HOLDER_NAME), paymentAccount.getHolderName());
|
||||
|
@ -554,12 +639,19 @@ public class CreatePaymentAccountTest extends AbstractPaymentAccountTest {
|
|||
PROPERTY_NAME_USERNAME);
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_PAYMENT_METHOD_ID, REVOLUT_ID);
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_ACCOUNT_NAME, "Revolut Acct");
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_TRADE_CURRENCIES, RevolutAccount.SUPPORTED_CURRENCIES
|
||||
.stream()
|
||||
.map(TradeCurrency::getCode)
|
||||
.collect(Collectors.joining(",")));
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_SELECTED_TRADE_CURRENCY, "QAR");
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_USERNAME, "revolut123");
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_SALT, "");
|
||||
String jsonString = getCompletedFormAsJsonString();
|
||||
RevolutAccount paymentAccount = (RevolutAccount) createPaymentAccount(aliceClient, jsonString);
|
||||
verifyUserPayloadHasPaymentAccountWithId(aliceClient, paymentAccount.getId());
|
||||
verifyAccountTradeCurrencies(getAllRevolutCurrencies(), paymentAccount);
|
||||
verifyAccountTradeCurrencies(RevolutAccount.SUPPORTED_CURRENCIES, paymentAccount);
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_SELECTED_TRADE_CURRENCY),
|
||||
paymentAccount.getSelectedTradeCurrency().getCode());
|
||||
verifyCommonFormEntries(paymentAccount);
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_USERNAME), paymentAccount.getUserName());
|
||||
print(paymentAccount);
|
||||
|
@ -631,7 +723,7 @@ public class CreatePaymentAccountTest extends AbstractPaymentAccountTest {
|
|||
verifyUserPayloadHasPaymentAccountWithId(aliceClient, paymentAccount.getId());
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_COUNTRY),
|
||||
Objects.requireNonNull(paymentAccount.getCountry()).code);
|
||||
verifyAccountSingleTradeCurrency("EUR", paymentAccount);
|
||||
verifyAccountSingleTradeCurrency(EUR, paymentAccount);
|
||||
verifyCommonFormEntries(paymentAccount);
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_HOLDER_NAME), paymentAccount.getHolderName());
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_IBAN), paymentAccount.getIban());
|
||||
|
@ -662,7 +754,7 @@ public class CreatePaymentAccountTest extends AbstractPaymentAccountTest {
|
|||
verifyUserPayloadHasPaymentAccountWithId(aliceClient, paymentAccount.getId());
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_COUNTRY),
|
||||
Objects.requireNonNull(paymentAccount.getCountry()).code);
|
||||
verifyAccountSingleTradeCurrency("EUR", paymentAccount);
|
||||
verifyAccountSingleTradeCurrency(EUR, paymentAccount);
|
||||
verifyCommonFormEntries(paymentAccount);
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_HOLDER_NAME), paymentAccount.getHolderName());
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_IBAN), paymentAccount.getIban());
|
||||
|
@ -720,6 +812,64 @@ public class CreatePaymentAccountTest extends AbstractPaymentAccountTest {
|
|||
print(paymentAccount);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateSwiftAccount(TestInfo testInfo) {
|
||||
// https://www.theswiftcodes.com
|
||||
File emptyForm = getEmptyForm(testInfo, SWIFT_ID);
|
||||
verifyEmptyForm(emptyForm,
|
||||
SWIFT_ID,
|
||||
PROPERTY_NAME_BANK_SWIFT_CODE);
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_PAYMENT_METHOD_ID, SWIFT_ID);
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_ACCOUNT_NAME, "IT Swift Acct w/ DE Intermediary");
|
||||
Collection<FiatCurrency> swiftCurrenciesSortedByCode = getAllSortedFiatCurrencies(comparing(TradeCurrency::getCode));
|
||||
String allFiatCodes = getCommaDelimitedFiatCurrencyCodes(swiftCurrenciesSortedByCode);
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_TRADE_CURRENCIES, allFiatCodes);
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_SELECTED_TRADE_CURRENCY, EUR);
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_BANK_SWIFT_CODE, "PASCITMMFIR");
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_BANK_COUNTRY_CODE, "IT");
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_BANK_NAME, "BANCA MONTE DEI PASCHI DI SIENA S.P.A.");
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_BANK_BRANCH, "SUCC. DI FIRENZE");
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_BANK_ADDRESS, "Via dei Pecori, 8, 50123 Firenze FI, Italy");
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_BENEFICIARY_NAME, "Vito de' Medici");
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_BENEFICIARY_ACCOUNT_NR, "0000 1111 2222 3333");
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_BENEFICIARY_ADDRESS, "Via dei Pecori, 1, 50123 Firenze FI, Italy");
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_BENEFICIARY_CITY, "Firenze");
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_BENEFICIARY_PHONE, "+39 055 222222");
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_SPECIAL_INSTRUCTIONS, "N/A");
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_INTERMEDIARY_SWIFT_CODE, "DEUTDEFFXXX");
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_INTERMEDIARY_COUNTRY_CODE, "DE");
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_INTERMEDIARY_NAME, "Kosmo Krump");
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_INTERMEDIARY_ADDRESS, "TAUNUSANLAGE 12, FRANKFURT AM MAIN, 60262");
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_INTERMEDIARY_BRANCH, "Deutsche Bank Frankfurt F");
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_SALT, encodeToHex("Restored Swift Acct Salt"));
|
||||
String jsonString = getCompletedFormAsJsonString(getSwiftFormComments());
|
||||
SwiftAccount paymentAccount = (SwiftAccount) createPaymentAccount(aliceClient, jsonString);
|
||||
verifyUserPayloadHasPaymentAccountWithId(aliceClient, paymentAccount.getId());
|
||||
verifyAccountTradeCurrencies(swiftCurrenciesSortedByCode, paymentAccount);
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_SELECTED_TRADE_CURRENCY),
|
||||
paymentAccount.getSelectedTradeCurrency().getCode());
|
||||
verifyCommonFormEntries(paymentAccount);
|
||||
SwiftAccountPayload payload = (SwiftAccountPayload) paymentAccount.getPaymentAccountPayload();
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_BANK_SWIFT_CODE), payload.getBankSwiftCode());
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_BANK_COUNTRY_CODE), payload.getBankCountryCode());
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_BANK_NAME), payload.getBankName());
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_BANK_BRANCH), payload.getBankBranch());
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_BANK_ADDRESS), payload.getBankAddress());
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_BENEFICIARY_NAME), payload.getBeneficiaryName());
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_BENEFICIARY_ACCOUNT_NR), payload.getBeneficiaryAccountNr());
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_BENEFICIARY_ADDRESS), payload.getBeneficiaryAddress());
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_BENEFICIARY_CITY), payload.getBeneficiaryCity());
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_BENEFICIARY_PHONE), payload.getBeneficiaryPhone());
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_SPECIAL_INSTRUCTIONS), payload.getSpecialInstructions());
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_INTERMEDIARY_SWIFT_CODE), payload.getIntermediarySwiftCode());
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_INTERMEDIARY_COUNTRY_CODE), payload.getIntermediaryCountryCode());
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_INTERMEDIARY_NAME), payload.getIntermediaryName());
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_INTERMEDIARY_BRANCH), payload.getIntermediaryBranch());
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_INTERMEDIARY_ADDRESS), payload.getIntermediaryAddress());
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_SALT), paymentAccount.getSaltAsHex());
|
||||
print(paymentAccount);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateSwishAccount(TestInfo testInfo) {
|
||||
File emptyForm = getEmptyForm(testInfo, SWISH_ID);
|
||||
|
@ -751,17 +901,16 @@ public class CreatePaymentAccountTest extends AbstractPaymentAccountTest {
|
|||
PROPERTY_NAME_EMAIL);
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_PAYMENT_METHOD_ID, TRANSFERWISE_ID);
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_ACCOUNT_NAME, "Transferwise Acct");
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_TRADE_CURRENCIES, "eur");
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_TRADE_CURRENCIES, "NZD");
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_SELECTED_TRADE_CURRENCY, "NZD");
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_EMAIL, "jane@doe.info");
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_SALT, "");
|
||||
String jsonString = getCompletedFormAsJsonString();
|
||||
TransferwiseAccount paymentAccount = (TransferwiseAccount) createPaymentAccount(aliceClient, jsonString);
|
||||
verifyUserPayloadHasPaymentAccountWithId(aliceClient, paymentAccount.getId());
|
||||
assertEquals(1, paymentAccount.getTradeCurrencies().size());
|
||||
TradeCurrency expectedCurrency = getTradeCurrency("EUR").get();
|
||||
assertEquals(expectedCurrency, paymentAccount.getSelectedTradeCurrency());
|
||||
List<TradeCurrency> expectedTradeCurrencies = singletonList(expectedCurrency);
|
||||
verifyAccountTradeCurrencies(expectedTradeCurrencies, paymentAccount);
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_SELECTED_TRADE_CURRENCY),
|
||||
paymentAccount.getSelectedTradeCurrency().getCode());
|
||||
verifyCommonFormEntries(paymentAccount);
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_EMAIL), paymentAccount.getEmail());
|
||||
print(paymentAccount);
|
||||
|
@ -775,7 +924,8 @@ public class CreatePaymentAccountTest extends AbstractPaymentAccountTest {
|
|||
PROPERTY_NAME_EMAIL);
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_PAYMENT_METHOD_ID, TRANSFERWISE_ID);
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_ACCOUNT_NAME, "Transferwise Acct");
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_TRADE_CURRENCIES, "ars, cad, hrk, czk, eur, hkd, idr, jpy, chf, nzd");
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_TRADE_CURRENCIES, "ARS,CAD,HRK,CZK,EUR,HKD,IDR,JPY,CHF,NZD");
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_SELECTED_TRADE_CURRENCY, "CHF");
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_EMAIL, "jane@doe.info");
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_SALT, "");
|
||||
String jsonString = getCompletedFormAsJsonString();
|
||||
|
@ -787,7 +937,7 @@ public class CreatePaymentAccountTest extends AbstractPaymentAccountTest {
|
|||
add(getTradeCurrency("CAD").get());
|
||||
add(getTradeCurrency("HRK").get());
|
||||
add(getTradeCurrency("CZK").get());
|
||||
add(getTradeCurrency("EUR").get());
|
||||
add(getTradeCurrency(EUR).get());
|
||||
add(getTradeCurrency("HKD").get());
|
||||
add(getTradeCurrency("IDR").get());
|
||||
add(getTradeCurrency("JPY").get());
|
||||
|
@ -795,8 +945,34 @@ public class CreatePaymentAccountTest extends AbstractPaymentAccountTest {
|
|||
add(getTradeCurrency("NZD").get());
|
||||
}};
|
||||
verifyAccountTradeCurrencies(expectedTradeCurrencies, paymentAccount);
|
||||
TradeCurrency expectedSelectedCurrency = expectedTradeCurrencies.get(0);
|
||||
assertEquals(expectedSelectedCurrency, paymentAccount.getSelectedTradeCurrency());
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_SELECTED_TRADE_CURRENCY),
|
||||
paymentAccount.getSelectedTradeCurrency().getCode());
|
||||
verifyCommonFormEntries(paymentAccount);
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_EMAIL), paymentAccount.getEmail());
|
||||
print(paymentAccount);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateTransferwiseAccountWithSupportedTradeCurrencies(TestInfo testInfo) {
|
||||
File emptyForm = getEmptyForm(testInfo, TRANSFERWISE_ID);
|
||||
verifyEmptyForm(emptyForm,
|
||||
TRANSFERWISE_ID,
|
||||
PROPERTY_NAME_EMAIL);
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_PAYMENT_METHOD_ID, TRANSFERWISE_ID);
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_ACCOUNT_NAME, "Transferwise Acct");
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_TRADE_CURRENCIES, TransferwiseAccount.SUPPORTED_CURRENCIES
|
||||
.stream()
|
||||
.map(TradeCurrency::getCode)
|
||||
.collect(Collectors.joining(",")));
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_SELECTED_TRADE_CURRENCY, "AUD");
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_EMAIL, "jane@doe.info");
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_SALT, "");
|
||||
String jsonString = getCompletedFormAsJsonString();
|
||||
TransferwiseAccount paymentAccount = (TransferwiseAccount) createPaymentAccount(aliceClient, jsonString);
|
||||
verifyUserPayloadHasPaymentAccountWithId(aliceClient, paymentAccount.getId());
|
||||
verifyAccountTradeCurrencies(TransferwiseAccount.SUPPORTED_CURRENCIES, paymentAccount);
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_SELECTED_TRADE_CURRENCY),
|
||||
paymentAccount.getSelectedTradeCurrency().getCode());
|
||||
verifyCommonFormEntries(paymentAccount);
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_EMAIL), paymentAccount.getEmail());
|
||||
print(paymentAccount);
|
||||
|
@ -836,7 +1012,7 @@ public class CreatePaymentAccountTest extends AbstractPaymentAccountTest {
|
|||
|
||||
Throwable exception = assertThrows(StatusRuntimeException.class, () ->
|
||||
createPaymentAccount(aliceClient, jsonString));
|
||||
assertEquals("INVALID_ARGUMENT: no trade currencies defined for transferwise payment account",
|
||||
assertEquals("INVALID_ARGUMENT: no trade currency defined for transferwise payment account",
|
||||
exception.getMessage());
|
||||
}
|
||||
|
||||
|
@ -849,11 +1025,18 @@ public class CreatePaymentAccountTest extends AbstractPaymentAccountTest {
|
|||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_PAYMENT_METHOD_ID, UPHOLD_ID);
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_ACCOUNT_NAME, "Uphold Acct");
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_ACCOUNT_ID, "UA 9876");
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_TRADE_CURRENCIES, UpholdAccount.SUPPORTED_CURRENCIES
|
||||
.stream()
|
||||
.map(TradeCurrency::getCode)
|
||||
.collect(Collectors.joining(",")));
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_SELECTED_TRADE_CURRENCY, "MXN");
|
||||
COMPLETED_FORM_MAP.put(PROPERTY_NAME_SALT, encodeToHex("Restored Uphold Acct Salt"));
|
||||
String jsonString = getCompletedFormAsJsonString();
|
||||
UpholdAccount paymentAccount = (UpholdAccount) createPaymentAccount(aliceClient, jsonString);
|
||||
verifyUserPayloadHasPaymentAccountWithId(aliceClient, paymentAccount.getId());
|
||||
verifyAccountTradeCurrencies(getAllUpholdCurrencies(), paymentAccount);
|
||||
verifyAccountTradeCurrencies(UpholdAccount.SUPPORTED_CURRENCIES, paymentAccount);
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_SELECTED_TRADE_CURRENCY),
|
||||
paymentAccount.getSelectedTradeCurrency().getCode());
|
||||
verifyCommonFormEntries(paymentAccount);
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_ACCOUNT_ID), paymentAccount.getAccountId());
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_SALT), paymentAccount.getSaltAsHex());
|
||||
|
@ -875,7 +1058,7 @@ public class CreatePaymentAccountTest extends AbstractPaymentAccountTest {
|
|||
String jsonString = getCompletedFormAsJsonString();
|
||||
USPostalMoneyOrderAccount paymentAccount = (USPostalMoneyOrderAccount) createPaymentAccount(aliceClient, jsonString);
|
||||
verifyUserPayloadHasPaymentAccountWithId(aliceClient, paymentAccount.getId());
|
||||
verifyAccountSingleTradeCurrency("USD", paymentAccount);
|
||||
verifyAccountSingleTradeCurrency(USD, paymentAccount);
|
||||
verifyCommonFormEntries(paymentAccount);
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_HOLDER_NAME), paymentAccount.getHolderName());
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_POSTAL_ADDRESS), paymentAccount.getPostalAddress());
|
||||
|
@ -923,7 +1106,7 @@ public class CreatePaymentAccountTest extends AbstractPaymentAccountTest {
|
|||
String jsonString = getCompletedFormAsJsonString();
|
||||
WesternUnionAccount paymentAccount = (WesternUnionAccount) createPaymentAccount(aliceClient, jsonString);
|
||||
verifyUserPayloadHasPaymentAccountWithId(aliceClient, paymentAccount.getId());
|
||||
verifyAccountSingleTradeCurrency("USD", paymentAccount);
|
||||
verifyAccountSingleTradeCurrency(USD, paymentAccount);
|
||||
verifyCommonFormEntries(paymentAccount);
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_HOLDER_NAME), paymentAccount.getFullName());
|
||||
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_CITY), paymentAccount.getCity());
|
||||
|
@ -942,7 +1125,7 @@ public class CreatePaymentAccountTest extends AbstractPaymentAccountTest {
|
|||
private void print(PaymentAccount paymentAccount) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Deserialized {}: {}", paymentAccount.getClass().getSimpleName(), paymentAccount);
|
||||
log.debug("\n{}", formatPaymentAcctTbl(singletonList(paymentAccount.toProtoMessage())));
|
||||
log.debug("\n{}", new TableBuilder(PAYMENT_ACCOUNT_TBL, paymentAccount.toProtoMessage()).build());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,28 +2,47 @@ package bisq.apitest.method.trade;
|
|||
|
||||
import bisq.proto.grpc.TradeInfo;
|
||||
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.TestInfo;
|
||||
|
||||
import static bisq.cli.TradeFormat.format;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static bisq.cli.table.builder.TableType.TRADE_DETAIL_TBL;
|
||||
import static bisq.core.trade.Trade.Phase.DEPOSIT_UNLOCKED;
|
||||
import static bisq.core.trade.Trade.Phase.PAYMENT_SENT;
|
||||
import static bisq.core.trade.Trade.Phase.PAYOUT_PUBLISHED;
|
||||
import static bisq.core.trade.Trade.State.BUYER_SAW_ARRIVED_PAYMENT_SENT_MSG;
|
||||
import static bisq.core.trade.Trade.State.DEPOSIT_UNLOCKED_IN_BLOCK_CHAIN;
|
||||
import static bisq.core.trade.Trade.State.SELLER_RECEIVED_PAYMENT_SENT_MSG;
|
||||
import static java.lang.String.format;
|
||||
import static java.lang.System.out;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
|
||||
|
||||
import bisq.apitest.method.offer.AbstractOfferTest;
|
||||
import bisq.cli.CliMain;
|
||||
import bisq.cli.GrpcClient;
|
||||
import bisq.cli.table.builder.TableBuilder;
|
||||
|
||||
public class AbstractTradeTest extends AbstractOfferTest {
|
||||
|
||||
public static final ExpectedProtocolStatus EXPECTED_PROTOCOL_STATUS = new ExpectedProtocolStatus();
|
||||
|
||||
// A Trade ID cache for use in @Test sequences.
|
||||
@Getter
|
||||
protected static String tradeId;
|
||||
|
||||
protected final Supplier<Integer> maxTradeStateAndPhaseChecks = () -> isLongRunningTest ? 10 : 2;
|
||||
protected final Function<TradeInfo, String> toTradeDetailTable = (trade) ->
|
||||
new TableBuilder(TRADE_DETAIL_TBL, trade).build().toString();
|
||||
protected final Function<GrpcClient, String> toUserName = (client) -> client.equals(aliceClient) ? "Alice" : "Bob";
|
||||
|
||||
@BeforeAll
|
||||
public static void initStaticFixtures() {
|
||||
|
@ -32,13 +51,129 @@ public class AbstractTradeTest extends AbstractOfferTest {
|
|||
|
||||
protected final TradeInfo takeAlicesOffer(String offerId,
|
||||
String paymentAccountId) {
|
||||
return bobClient.takeOffer(offerId, paymentAccountId);
|
||||
return takeAlicesOffer(offerId,
|
||||
paymentAccountId,
|
||||
true);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
protected final TradeInfo takeBobsOffer(String offerId,
|
||||
String paymentAccountId) {
|
||||
return aliceClient.takeOffer(offerId, paymentAccountId);
|
||||
protected final TradeInfo takeAlicesOffer(String offerId,
|
||||
String paymentAccountId,
|
||||
boolean generateBtcBlock) {
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
var trade = bobClient.takeOffer(offerId,
|
||||
paymentAccountId);
|
||||
assertNotNull(trade);
|
||||
assertEquals(offerId, trade.getTradeId());
|
||||
|
||||
// Cache the trade id for the other tests.
|
||||
tradeId = trade.getTradeId();
|
||||
|
||||
if (generateBtcBlock)
|
||||
genBtcBlocksThenWait(1, 6_000);
|
||||
|
||||
return trade;
|
||||
}
|
||||
|
||||
protected final void waitForDepositConfirmation(Logger log,
|
||||
TestInfo testInfo,
|
||||
GrpcClient grpcClient,
|
||||
String tradeId) {
|
||||
Predicate<TradeInfo> isTradeInDepositUnlockedStateAndPhase = (t) ->
|
||||
t.getState().equals(DEPOSIT_UNLOCKED_IN_BLOCK_CHAIN.name())
|
||||
&& t.getPhase().equals(DEPOSIT_UNLOCKED.name());
|
||||
|
||||
String userName = toUserName.apply(grpcClient);
|
||||
for (int i = 1; i <= maxTradeStateAndPhaseChecks.get(); i++) {
|
||||
TradeInfo trade = grpcClient.getTrade(tradeId);
|
||||
if (!isTradeInDepositUnlockedStateAndPhase.test(trade)) {
|
||||
log.warn("{} still waiting on trade {} tx {}: DEPOSIT_UNLOCKED_IN_BLOCK_CHAIN, attempt # {}",
|
||||
userName,
|
||||
trade.getShortId(),
|
||||
trade.getMakerDepositTxId(),
|
||||
trade.getTakerDepositTxId(),
|
||||
i);
|
||||
genBtcBlocksThenWait(1, 4_000);
|
||||
} else {
|
||||
EXPECTED_PROTOCOL_STATUS.setState(DEPOSIT_UNLOCKED_IN_BLOCK_CHAIN)
|
||||
.setPhase(DEPOSIT_UNLOCKED)
|
||||
.setDepositPublished(true)
|
||||
.setDepositConfirmed(true);
|
||||
verifyExpectedProtocolStatus(trade);
|
||||
logTrade(log,
|
||||
testInfo,
|
||||
userName + "'s view after deposit is confirmed",
|
||||
trade);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected final void verifyTakerDepositConfirmed(TradeInfo trade) {
|
||||
if (!trade.getIsDepositUnlocked()) {
|
||||
fail(format("INVALID_PHASE for trade %s in STATE=%s PHASE=%s, deposit tx never unlocked.",
|
||||
trade.getShortId(),
|
||||
trade.getState(),
|
||||
trade.getPhase()));
|
||||
}
|
||||
}
|
||||
|
||||
protected final void waitForBuyerSeesPaymentInitiatedMessage(Logger log,
|
||||
TestInfo testInfo,
|
||||
GrpcClient grpcClient,
|
||||
String tradeId) {
|
||||
String userName = toUserName.apply(grpcClient);
|
||||
for (int i = 1; i <= maxTradeStateAndPhaseChecks.get(); i++) {
|
||||
TradeInfo trade = grpcClient.getTrade(tradeId);
|
||||
if (!trade.getIsPaymentSent()) {
|
||||
log.warn("{} still waiting for trade {} {}, attempt # {}",
|
||||
userName,
|
||||
trade.getShortId(),
|
||||
BUYER_SAW_ARRIVED_PAYMENT_SENT_MSG,
|
||||
i);
|
||||
sleep(5_000);
|
||||
} else {
|
||||
// Do not check trade.getOffer().getState() here because
|
||||
// it might be AVAILABLE, not OFFER_FEE_RESERVED.
|
||||
EXPECTED_PROTOCOL_STATUS.setState(BUYER_SAW_ARRIVED_PAYMENT_SENT_MSG)
|
||||
.setPhase(PAYMENT_SENT)
|
||||
.setPaymentStartedMessageSent(true);
|
||||
verifyExpectedProtocolStatus(trade);
|
||||
logTrade(log, testInfo, userName + "'s view after confirming trade payment sent", trade);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected final void waitForSellerSeesPaymentInitiatedMessage(Logger log,
|
||||
TestInfo testInfo,
|
||||
GrpcClient grpcClient,
|
||||
String tradeId) {
|
||||
Predicate<TradeInfo> isTradeInPaymentReceiptConfirmedStateAndPhase = (t) ->
|
||||
t.getState().equals(SELLER_RECEIVED_PAYMENT_SENT_MSG.name()) &&
|
||||
(t.getPhase().equals(PAYOUT_PUBLISHED.name()) || t.getPhase().equals(PAYMENT_SENT.name()));
|
||||
String userName = toUserName.apply(grpcClient);
|
||||
for (int i = 1; i <= maxTradeStateAndPhaseChecks.get(); i++) {
|
||||
TradeInfo trade = grpcClient.getTrade(tradeId);
|
||||
if (!isTradeInPaymentReceiptConfirmedStateAndPhase.test(trade)) {
|
||||
log.warn("INVALID_PHASE for {}'s trade {} in STATE={} PHASE={}, cannot confirm payment received yet.",
|
||||
userName,
|
||||
trade.getShortId(),
|
||||
trade.getState(),
|
||||
trade.getPhase());
|
||||
sleep(10_000);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
TradeInfo trade = grpcClient.getTrade(tradeId);
|
||||
if (!isTradeInPaymentReceiptConfirmedStateAndPhase.test(trade)) {
|
||||
fail(format("INVALID_PHASE for %s's trade %s in STATE=%s PHASE=%s, cannot confirm payment received.",
|
||||
userName,
|
||||
trade.getShortId(),
|
||||
trade.getState(),
|
||||
trade.getPhase()));
|
||||
}
|
||||
}
|
||||
|
||||
protected final void verifyExpectedProtocolStatus(TradeInfo trade) {
|
||||
|
@ -49,35 +184,54 @@ public class AbstractTradeTest extends AbstractOfferTest {
|
|||
if (!isLongRunningTest)
|
||||
assertEquals(EXPECTED_PROTOCOL_STATUS.isDepositPublished, trade.getIsDepositPublished());
|
||||
|
||||
assertEquals(EXPECTED_PROTOCOL_STATUS.isDepositUnlocked, trade.getIsDepositUnlocked());
|
||||
assertEquals(EXPECTED_PROTOCOL_STATUS.isPaymentSent, trade.getIsPaymentSent());
|
||||
assertEquals(EXPECTED_PROTOCOL_STATUS.isPaymentReceived, trade.getIsPaymentReceived());
|
||||
assertEquals(EXPECTED_PROTOCOL_STATUS.isDepositConfirmed, trade.getIsDepositUnlocked());
|
||||
assertEquals(EXPECTED_PROTOCOL_STATUS.isPaymentStartedMessageSent, trade.getIsPaymentSent());
|
||||
assertEquals(EXPECTED_PROTOCOL_STATUS.isPaymentReceivedMessageSent, trade.getIsPaymentReceived());
|
||||
assertEquals(EXPECTED_PROTOCOL_STATUS.isPayoutPublished, trade.getIsPayoutPublished());
|
||||
assertEquals(EXPECTED_PROTOCOL_STATUS.isWithdrawn, trade.getIsWithdrawn());
|
||||
assertEquals(EXPECTED_PROTOCOL_STATUS.isCompleted, trade.getIsCompleted());
|
||||
}
|
||||
|
||||
protected final void logBalances(Logger log, TestInfo testInfo) {
|
||||
var alicesBalances = aliceClient.getBalances();
|
||||
log.debug("{} Alice's Current Balances:\n{}",
|
||||
testName(testInfo),
|
||||
formatBalancesTbls(alicesBalances));
|
||||
var bobsBalances = bobClient.getBalances();
|
||||
log.debug("{} Bob's Current Balances:\n{}",
|
||||
testName(testInfo),
|
||||
formatBalancesTbls(bobsBalances));
|
||||
}
|
||||
|
||||
protected final void logTrade(Logger log,
|
||||
TestInfo testInfo,
|
||||
String description,
|
||||
TradeInfo trade) {
|
||||
logTrade(log, testInfo, description, trade, false);
|
||||
}
|
||||
|
||||
protected final void logTrade(Logger log,
|
||||
TestInfo testInfo,
|
||||
String description,
|
||||
TradeInfo trade,
|
||||
boolean force) {
|
||||
if (force)
|
||||
log.info(String.format("%s %s%n%s",
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug(format("%s %s%n%s",
|
||||
testName(testInfo),
|
||||
description.toUpperCase(),
|
||||
format(trade)));
|
||||
else if (log.isDebugEnabled()) {
|
||||
log.debug(String.format("%s %s%n%s",
|
||||
testName(testInfo),
|
||||
description.toUpperCase(),
|
||||
format(trade)));
|
||||
description,
|
||||
new TableBuilder(TRADE_DETAIL_TBL, trade).build()));
|
||||
}
|
||||
}
|
||||
|
||||
protected static void runCliGetTrade(String tradeId) {
|
||||
out.println("Alice's CLI 'gettrade' response:");
|
||||
CliMain.main(new String[]{"--password=xyz", "--port=9998", "gettrade", "--trade-id=" + tradeId});
|
||||
out.println("Bob's CLI 'gettrade' response:");
|
||||
CliMain.main(new String[]{"--password=xyz", "--port=9999", "gettrade", "--trade-id=" + tradeId});
|
||||
}
|
||||
|
||||
protected static void runCliGetOpenTrades() {
|
||||
out.println("Alice's CLI 'gettrades --category=open' response:");
|
||||
CliMain.main(new String[]{"--password=xyz", "--port=9998", "gettrades", "--category=open"});
|
||||
out.println("Bob's CLI 'gettrades --category=open' response:");
|
||||
CliMain.main(new String[]{"--password=xyz", "--port=9999", "gettrades", "--category=open"});
|
||||
}
|
||||
|
||||
protected static void runCliGetClosedTrades() {
|
||||
out.println("Alice's CLI 'gettrades --category=closed' response:");
|
||||
CliMain.main(new String[]{"--password=xyz", "--port=9998", "gettrades", "--category=closed"});
|
||||
out.println("Bob's CLI 'gettrades --category=closed' response:");
|
||||
CliMain.main(new String[]{"--password=xyz", "--port=9999", "gettrades", "--category=closed"});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,11 +10,11 @@ public class ExpectedProtocolStatus {
|
|||
Trade.State state;
|
||||
Trade.Phase phase;
|
||||
boolean isDepositPublished;
|
||||
boolean isDepositUnlocked;
|
||||
boolean isPaymentSent;
|
||||
boolean isPaymentReceived;
|
||||
boolean isDepositConfirmed;
|
||||
boolean isPaymentStartedMessageSent;
|
||||
boolean isPaymentReceivedMessageSent;
|
||||
boolean isPayoutPublished;
|
||||
boolean isWithdrawn;
|
||||
boolean isCompleted;
|
||||
|
||||
public ExpectedProtocolStatus setState(Trade.State state) {
|
||||
this.state = state;
|
||||
|
@ -31,18 +31,18 @@ public class ExpectedProtocolStatus {
|
|||
return this;
|
||||
}
|
||||
|
||||
public ExpectedProtocolStatus setDepositUnlocked(boolean depositUnlocked) {
|
||||
isDepositUnlocked = depositUnlocked;
|
||||
public ExpectedProtocolStatus setDepositConfirmed(boolean depositConfirmed) {
|
||||
isDepositConfirmed = depositConfirmed;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ExpectedProtocolStatus setFiatSent(boolean paymentSent) {
|
||||
isPaymentSent = paymentSent;
|
||||
public ExpectedProtocolStatus setPaymentStartedMessageSent(boolean paymentStartedMessageSent) {
|
||||
isPaymentStartedMessageSent = paymentStartedMessageSent;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ExpectedProtocolStatus setFiatReceived(boolean paymentReceived) {
|
||||
isPaymentReceived = paymentReceived;
|
||||
public ExpectedProtocolStatus setPaymentReceivedMessageSent(boolean paymentReceivedMessageSent) {
|
||||
isPaymentReceivedMessageSent = paymentReceivedMessageSent;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -51,8 +51,8 @@ public class ExpectedProtocolStatus {
|
|||
return this;
|
||||
}
|
||||
|
||||
public ExpectedProtocolStatus setWithdrawn(boolean withdrawn) {
|
||||
isWithdrawn = withdrawn;
|
||||
public ExpectedProtocolStatus setCompleted(boolean completed) {
|
||||
isCompleted = completed;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -60,10 +60,10 @@ public class ExpectedProtocolStatus {
|
|||
state = null;
|
||||
phase = null;
|
||||
isDepositPublished = false;
|
||||
isDepositUnlocked = false;
|
||||
isPaymentSent = false;
|
||||
isPaymentReceived = false;
|
||||
isDepositConfirmed = false;
|
||||
isPaymentStartedMessageSent = false;
|
||||
isPaymentReceivedMessageSent = false;
|
||||
isPayoutPublished = false;
|
||||
isWithdrawn = false;
|
||||
isCompleted = false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,12 +19,8 @@ package bisq.apitest.method.trade;
|
|||
|
||||
import bisq.core.payment.PaymentAccount;
|
||||
|
||||
import bisq.proto.grpc.TradeInfo;
|
||||
|
||||
import io.grpc.StatusRuntimeException;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
|
@ -34,18 +30,15 @@ import org.junit.jupiter.api.Test;
|
|||
import org.junit.jupiter.api.TestInfo;
|
||||
import org.junit.jupiter.api.TestMethodOrder;
|
||||
|
||||
import static bisq.cli.TableFormat.formatBalancesTbls;
|
||||
import static bisq.core.btc.wallet.Restrictions.getDefaultBuyerSecurityDepositAsPercent;
|
||||
import static bisq.core.trade.Trade.Phase.DEPOSIT_UNLOCKED;
|
||||
import static bisq.core.trade.Trade.Phase.PAYMENT_SENT;
|
||||
import static bisq.apitest.config.ApiTestConfig.BTC;
|
||||
import static bisq.apitest.config.ApiTestConfig.USD;
|
||||
import static bisq.core.trade.Trade.Phase.PAYOUT_PUBLISHED;
|
||||
import static bisq.core.trade.Trade.State.*;
|
||||
import static java.lang.String.format;
|
||||
import static bisq.core.trade.Trade.State.BUYER_RECEIVED_PAYOUT_TX_PUBLISHED_MSG;
|
||||
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.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
import static protobuf.Offer.State.OFFER_FEE_RESERVED;
|
||||
import static protobuf.OfferPayload.Direction.BUY;
|
||||
import static protobuf.OfferDirection.BUY;
|
||||
import static protobuf.OpenOffer.State.AVAILABLE;
|
||||
|
||||
@Disabled
|
||||
|
@ -61,62 +54,35 @@ public class TakeBuyBTCOfferTest extends AbstractTradeTest {
|
|||
try {
|
||||
PaymentAccount alicesUsdAccount = createDummyF2FAccount(aliceClient, "US");
|
||||
var alicesOffer = aliceClient.createMarketBasedPricedOffer(BUY.name(),
|
||||
"usd",
|
||||
USD,
|
||||
12_500_000L,
|
||||
12_500_000L, // min-amount = amount
|
||||
0.00,
|
||||
getDefaultBuyerSecurityDepositAsPercent(),
|
||||
alicesUsdAccount.getId());
|
||||
defaultBuyerSecurityDepositPct.get(),
|
||||
alicesUsdAccount.getId(),
|
||||
NO_TRIGGER_PRICE);
|
||||
var offerId = alicesOffer.getId();
|
||||
|
||||
// Wait for Alice's AddToOfferBook task.
|
||||
// Wait times vary; my logs show >= 2 second delay.
|
||||
sleep(3000); // TODO loop instead of hard code wait time
|
||||
var alicesUsdOffers = aliceClient.getMyOffersSortedByDate(BUY.name(), "usd");
|
||||
// Wait times vary; my logs show >= 2-second delay.
|
||||
sleep(3_000); // TODO loop instead of hard code a wait time
|
||||
var alicesUsdOffers = aliceClient.getMyOffersSortedByDate(BUY.name(), USD);
|
||||
assertEquals(1, alicesUsdOffers.size());
|
||||
|
||||
PaymentAccount bobsUsdAccount = createDummyF2FAccount(bobClient, "US");
|
||||
var trade = takeAlicesOffer(offerId, bobsUsdAccount.getId());
|
||||
assertNotNull(trade);
|
||||
assertEquals(offerId, trade.getTradeId());
|
||||
// Cache the trade id for the other tests.
|
||||
tradeId = trade.getTradeId();
|
||||
|
||||
genBtcBlocksThenWait(1, 4000);
|
||||
alicesUsdOffers = aliceClient.getMyOffersSortedByDate(BUY.name(), "usd");
|
||||
var trade = takeAlicesOffer(offerId,
|
||||
bobsUsdAccount.getId(),
|
||||
false);
|
||||
sleep(2_500); // Allow available offer to be removed from offer book.
|
||||
alicesUsdOffers = aliceClient.getMyOffersSortedByDate(BUY.name(), USD);
|
||||
assertEquals(0, alicesUsdOffers.size());
|
||||
genBtcBlocksThenWait(1, 2_500);
|
||||
waitForDepositConfirmation(log, testInfo, bobClient, trade.getTradeId());
|
||||
|
||||
genBtcBlocksThenWait(1, 2500);
|
||||
|
||||
for (int i = 1; i <= maxTradeStateAndPhaseChecks.get(); i++) {
|
||||
trade = bobClient.getTrade(trade.getTradeId());
|
||||
|
||||
if (!trade.getIsDepositUnlocked()) {
|
||||
log.warn("Bob still waiting on trade {} maker tx {} taker tx {}: DEPOSIT_UNLOCKED_IN_BLOCK_CHAIN, attempt # {}",
|
||||
trade.getShortId(),
|
||||
trade.getMakerDepositTxId(),
|
||||
trade.getTakerDepositTxId(),
|
||||
i);
|
||||
genBtcBlocksThenWait(1, 4000);
|
||||
continue;
|
||||
} else {
|
||||
EXPECTED_PROTOCOL_STATUS.setState(DEPOSIT_UNLOCKED_IN_BLOCK_CHAIN)
|
||||
.setPhase(DEPOSIT_UNLOCKED)
|
||||
.setDepositPublished(true)
|
||||
.setDepositUnlocked(true);
|
||||
verifyExpectedProtocolStatus(trade);
|
||||
logTrade(log, testInfo, "Bob's view after deposit is unlocked", trade, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!trade.getIsDepositUnlocked()) {
|
||||
fail(format("INVALID_PHASE for Bob's trade %s in STATE=%s PHASE=%s, deposit tx never unlocked.",
|
||||
trade.getShortId(),
|
||||
trade.getState(),
|
||||
trade.getPhase()));
|
||||
}
|
||||
|
||||
trade = bobClient.getTrade(tradeId);
|
||||
verifyTakerDepositConfirmed(trade);
|
||||
logTrade(log, testInfo, "Alice's Maker/Buyer View", aliceClient.getTrade(tradeId));
|
||||
logTrade(log, testInfo, "Bob's Taker/Seller View", bobClient.getTrade(tradeId));
|
||||
} catch (StatusRuntimeException e) {
|
||||
fail(e);
|
||||
}
|
||||
|
@ -127,56 +93,10 @@ public class TakeBuyBTCOfferTest extends AbstractTradeTest {
|
|||
public void testAlicesConfirmPaymentStarted(final TestInfo testInfo) {
|
||||
try {
|
||||
var trade = aliceClient.getTrade(tradeId);
|
||||
|
||||
Predicate<TradeInfo> tradeStateAndPhaseCorrect = (t) ->
|
||||
t.getState().equals(DEPOSIT_UNLOCKED_IN_BLOCK_CHAIN.name())
|
||||
&& t.getPhase().equals(DEPOSIT_UNLOCKED.name());
|
||||
|
||||
|
||||
for (int i = 1; i <= maxTradeStateAndPhaseChecks.get(); i++) {
|
||||
if (!tradeStateAndPhaseCorrect.test(trade)) {
|
||||
log.warn("INVALID_PHASE for Alice's trade {} in STATE={} PHASE={}, cannot confirm payment started yet.",
|
||||
trade.getShortId(),
|
||||
trade.getState(),
|
||||
trade.getPhase());
|
||||
// fail("Bad trade state and phase.");
|
||||
sleep(1000 * 10);
|
||||
trade = aliceClient.getTrade(tradeId);
|
||||
continue;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!tradeStateAndPhaseCorrect.test(trade)) {
|
||||
fail(format("INVALID_PHASE for Alice's trade %s in STATE=%s PHASE=%s, could not confirm payment started.",
|
||||
trade.getShortId(),
|
||||
trade.getState(),
|
||||
trade.getPhase()));
|
||||
}
|
||||
|
||||
waitForDepositConfirmation(log, testInfo, aliceClient, trade.getTradeId());
|
||||
aliceClient.confirmPaymentStarted(trade.getTradeId());
|
||||
sleep(6000);
|
||||
|
||||
for (int i = 1; i <= maxTradeStateAndPhaseChecks.get(); i++) {
|
||||
trade = aliceClient.getTrade(tradeId);
|
||||
|
||||
if (!trade.getIsPaymentSent()) {
|
||||
log.warn("Alice still waiting for trade {} BUYER_SAW_ARRIVED_PAYMENT_SENT_MSG, attempt # {}",
|
||||
trade.getShortId(),
|
||||
i);
|
||||
sleep(5000);
|
||||
continue;
|
||||
} else {
|
||||
assertEquals(OFFER_FEE_RESERVED.name(), trade.getOffer().getState());
|
||||
EXPECTED_PROTOCOL_STATUS.setState(BUYER_SAW_ARRIVED_PAYMENT_SENT_MSG)
|
||||
.setPhase(PAYMENT_SENT)
|
||||
.setFiatSent(true);
|
||||
verifyExpectedProtocolStatus(trade);
|
||||
logTrade(log, testInfo, "Alice's view after confirming fiat payment sent", trade);
|
||||
break;
|
||||
}
|
||||
}
|
||||
sleep(6_000);
|
||||
waitForBuyerSeesPaymentInitiatedMessage(log, testInfo, aliceClient, tradeId);
|
||||
} catch (StatusRuntimeException e) {
|
||||
fail(e);
|
||||
}
|
||||
|
@ -186,82 +106,19 @@ public class TakeBuyBTCOfferTest extends AbstractTradeTest {
|
|||
@Order(3)
|
||||
public void testBobsConfirmPaymentReceived(final TestInfo testInfo) {
|
||||
try {
|
||||
waitForSellerSeesPaymentInitiatedMessage(log, testInfo, bobClient, tradeId);
|
||||
var trade = bobClient.getTrade(tradeId);
|
||||
|
||||
Predicate<TradeInfo> tradeStateAndPhaseCorrect = (t) ->
|
||||
t.getState().equals(SELLER_RECEIVED_PAYMENT_SENT_MSG.name())
|
||||
&& (t.getPhase().equals(PAYOUT_PUBLISHED.name()) || t.getPhase().equals(PAYMENT_SENT.name()));
|
||||
|
||||
for (int i = 1; i <= maxTradeStateAndPhaseChecks.get(); i++) {
|
||||
if (!tradeStateAndPhaseCorrect.test(trade)) {
|
||||
log.warn("INVALID_PHASE for Bob's trade {} in STATE={} PHASE={}, cannot confirm payment received yet.",
|
||||
trade.getShortId(),
|
||||
trade.getState(),
|
||||
trade.getPhase());
|
||||
// fail("Bad trade state and phase.");
|
||||
sleep(1000 * 10);
|
||||
trade = bobClient.getTrade(tradeId);
|
||||
continue;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!tradeStateAndPhaseCorrect.test(trade)) {
|
||||
fail(format("INVALID_PHASE for Bob's trade %s in STATE=%s PHASE=%s, cannot confirm payment received.",
|
||||
trade.getShortId(),
|
||||
trade.getState(),
|
||||
trade.getPhase()));
|
||||
}
|
||||
|
||||
bobClient.confirmPaymentReceived(trade.getTradeId());
|
||||
sleep(3000);
|
||||
|
||||
sleep(3_000);
|
||||
trade = bobClient.getTrade(tradeId);
|
||||
// Note: offer.state == available
|
||||
assertEquals(AVAILABLE.name(), trade.getOffer().getState());
|
||||
EXPECTED_PROTOCOL_STATUS.setState(SELLER_SAW_ARRIVED_PAYOUT_TX_PUBLISHED_MSG)
|
||||
.setPhase(PAYOUT_PUBLISHED)
|
||||
.setPayoutPublished(true)
|
||||
.setFiatReceived(true);
|
||||
.setPaymentReceivedMessageSent(true);
|
||||
verifyExpectedProtocolStatus(trade);
|
||||
logTrade(log, testInfo, "Bob's view after confirming fiat payment received", trade);
|
||||
|
||||
} catch (StatusRuntimeException e) {
|
||||
fail(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(4)
|
||||
public void testAlicesKeepFunds(final TestInfo testInfo) {
|
||||
try {
|
||||
genBtcBlocksThenWait(1, 1000);
|
||||
|
||||
var trade = aliceClient.getTrade(tradeId);
|
||||
logTrade(log, testInfo, "Alice's view before keeping funds", trade);
|
||||
|
||||
aliceClient.keepFunds(tradeId);
|
||||
|
||||
genBtcBlocksThenWait(1, 1000);
|
||||
|
||||
trade = aliceClient.getTrade(tradeId);
|
||||
EXPECTED_PROTOCOL_STATUS.setState(BUYER_RECEIVED_PAYOUT_TX_PUBLISHED_MSG)
|
||||
.setPhase(PAYOUT_PUBLISHED);
|
||||
verifyExpectedProtocolStatus(trade);
|
||||
logTrade(log, testInfo, "Alice's view after keeping funds", trade);
|
||||
|
||||
logTrade(log, testInfo, "Alice's Maker/Buyer View (Done)", aliceClient.getTrade(tradeId), true);
|
||||
logTrade(log, testInfo, "Bob's Taker/Seller View (Done)", bobClient.getTrade(tradeId), true);
|
||||
|
||||
var alicesBalances = aliceClient.getBalances();
|
||||
log.info("{} Alice's Current Balance:\n{}",
|
||||
testName(testInfo),
|
||||
formatBalancesTbls(alicesBalances));
|
||||
var bobsBalances = bobClient.getBalances();
|
||||
log.info("{} Bob's Current Balance:\n{}",
|
||||
testName(testInfo),
|
||||
formatBalancesTbls(bobsBalances));
|
||||
} catch (StatusRuntimeException e) {
|
||||
fail(e);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,253 @@
|
|||
/*
|
||||
* This file is part of Haveno.
|
||||
*
|
||||
* Haveno 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.
|
||||
*
|
||||
* Haveno 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 Haveno. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is part of Haveno.
|
||||
*
|
||||
* Haveno 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.
|
||||
*
|
||||
* Haveno 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 Haveno. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.apitest.method.trade;
|
||||
|
||||
import bisq.core.payment.PaymentAccount;
|
||||
import bisq.core.payment.payload.NationalBankAccountPayload;
|
||||
|
||||
import io.grpc.StatusRuntimeException;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.MethodOrderer;
|
||||
import org.junit.jupiter.api.Order;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestInfo;
|
||||
import org.junit.jupiter.api.TestMethodOrder;
|
||||
|
||||
import static bisq.apitest.config.ApiTestConfig.BTC;
|
||||
import static bisq.core.trade.Trade.Phase.PAYOUT_PUBLISHED;
|
||||
import static bisq.core.trade.Trade.State.SELLER_SAW_ARRIVED_PAYOUT_TX_PUBLISHED_MSG;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static protobuf.Offer.State.OFFER_FEE_RESERVED;
|
||||
import static protobuf.OfferDirection.BUY;
|
||||
import static protobuf.OpenOffer.State.AVAILABLE;
|
||||
|
||||
/**
|
||||
* Test case verifies trade can be made with national bank payment method,
|
||||
* and json contracts exclude bank acct details until deposit tx is confirmed.
|
||||
*/
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@Disabled
|
||||
@Slf4j
|
||||
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
|
||||
public class TakeBuyBTCOfferWithNationalBankAcctTest extends AbstractTradeTest {
|
||||
|
||||
// Alice is maker/buyer, Bob is taker/seller.
|
||||
|
||||
private static final String BRL = "BRL";
|
||||
|
||||
private static PaymentAccount alicesPaymentAccount;
|
||||
private static PaymentAccount bobsPaymentAccount;
|
||||
|
||||
@BeforeAll
|
||||
public static void setUp() {
|
||||
setUp(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(1)
|
||||
public void testTakeAlicesBuyOffer(final TestInfo testInfo) {
|
||||
try {
|
||||
alicesPaymentAccount = createDummyBRLAccount(aliceClient,
|
||||
"Alicia da Silva",
|
||||
String.valueOf(System.currentTimeMillis()),
|
||||
"123.456.789-01");
|
||||
bobsPaymentAccount = createDummyBRLAccount(bobClient,
|
||||
"Roberto da Silva",
|
||||
String.valueOf(System.currentTimeMillis()),
|
||||
"123.456.789-02");
|
||||
var alicesOffer = aliceClient.createMarketBasedPricedOffer(BUY.name(),
|
||||
BRL,
|
||||
1_000_000L,
|
||||
1_000_000L, // min-amount = amount
|
||||
0.00,
|
||||
defaultBuyerSecurityDepositPct.get(),
|
||||
alicesPaymentAccount.getId(),
|
||||
NO_TRIGGER_PRICE);
|
||||
var offerId = alicesOffer.getId();
|
||||
|
||||
// Wait for Alice's AddToOfferBook task.
|
||||
// Wait times vary; my logs show >= 2 second delay.
|
||||
sleep(3_000); // TODO loop instead of hard code wait time
|
||||
var alicesOffers = aliceClient.getMyOffersSortedByDate(BUY.name(), BRL);
|
||||
assertEquals(1, alicesOffers.size());
|
||||
|
||||
|
||||
var trade = takeAlicesOffer(offerId,
|
||||
bobsPaymentAccount.getId(),
|
||||
false);
|
||||
|
||||
// Before generating a blk and confirming deposit tx, make sure there
|
||||
// are no bank acct details in the either side's contract.
|
||||
while (true) {
|
||||
try {
|
||||
var alicesContract = aliceClient.getTrade(trade.getTradeId()).getContractAsJson();
|
||||
var bobsContract = bobClient.getTrade(trade.getTradeId()).getContractAsJson();
|
||||
verifyJsonContractExcludesBankAccountDetails(alicesContract, alicesPaymentAccount);
|
||||
verifyJsonContractExcludesBankAccountDetails(alicesContract, bobsPaymentAccount);
|
||||
verifyJsonContractExcludesBankAccountDetails(bobsContract, alicesPaymentAccount);
|
||||
verifyJsonContractExcludesBankAccountDetails(bobsContract, bobsPaymentAccount);
|
||||
break;
|
||||
} catch (StatusRuntimeException ex) {
|
||||
if (ex.getMessage() == null) {
|
||||
String message = ex.getMessage().replaceFirst("^[A-Z_]+: ", "");
|
||||
if (message.contains("trade") && message.contains("not found")) {
|
||||
fail(ex);
|
||||
}
|
||||
} else {
|
||||
sleep(500);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
genBtcBlocksThenWait(1, 4000);
|
||||
alicesOffers = aliceClient.getMyOffersSortedByDate(BUY.name(), BRL);
|
||||
assertEquals(0, alicesOffers.size());
|
||||
genBtcBlocksThenWait(1, 2_500);
|
||||
waitForDepositConfirmation(log, testInfo, bobClient, trade.getTradeId());
|
||||
|
||||
trade = bobClient.getTrade(tradeId);
|
||||
verifyTakerDepositConfirmed(trade);
|
||||
logTrade(log, testInfo, "Alice's Maker/Buyer View", aliceClient.getTrade(tradeId));
|
||||
logTrade(log, testInfo, "Bob's Taker/Seller View", bobClient.getTrade(tradeId));
|
||||
} catch (StatusRuntimeException e) {
|
||||
fail(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(2)
|
||||
public void testBankAcctDetailsIncludedInContracts(final TestInfo testInfo) {
|
||||
assertNotNull(alicesPaymentAccount);
|
||||
assertNotNull(bobsPaymentAccount);
|
||||
|
||||
var alicesTrade = aliceClient.getTrade(tradeId);
|
||||
assertNotEquals("", alicesTrade.getContract().getMakerPaymentAccountPayload().getPaymentDetails());
|
||||
assertNotEquals("", alicesTrade.getContract().getTakerPaymentAccountPayload().getPaymentDetails());
|
||||
var alicesContractJson = alicesTrade.getContractAsJson();
|
||||
verifyJsonContractIncludesBankAccountDetails(alicesContractJson, alicesPaymentAccount);
|
||||
verifyJsonContractIncludesBankAccountDetails(alicesContractJson, bobsPaymentAccount);
|
||||
|
||||
var bobsTrade = bobClient.getTrade(tradeId);
|
||||
assertNotEquals("", bobsTrade.getContract().getMakerPaymentAccountPayload().getPaymentDetails());
|
||||
assertNotEquals("", bobsTrade.getContract().getTakerPaymentAccountPayload().getPaymentDetails());
|
||||
var bobsContractJson = bobsTrade.getContractAsJson();
|
||||
verifyJsonContractIncludesBankAccountDetails(bobsContractJson, alicesPaymentAccount);
|
||||
verifyJsonContractIncludesBankAccountDetails(bobsContractJson, bobsPaymentAccount);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(3)
|
||||
public void testAlicesConfirmPaymentStarted(final TestInfo testInfo) {
|
||||
try {
|
||||
var trade = aliceClient.getTrade(tradeId);
|
||||
waitForDepositConfirmation(log, testInfo, aliceClient, trade.getTradeId());
|
||||
aliceClient.confirmPaymentStarted(trade.getTradeId());
|
||||
sleep(6_000);
|
||||
waitForBuyerSeesPaymentInitiatedMessage(log, testInfo, aliceClient, tradeId);
|
||||
trade = aliceClient.getTrade(tradeId);
|
||||
assertEquals(OFFER_FEE_RESERVED.name(), trade.getOffer().getState());
|
||||
logTrade(log, testInfo, "Alice's Maker/Buyer View (Payment Sent)", aliceClient.getTrade(tradeId));
|
||||
logTrade(log, testInfo, "Bob's Taker/Seller View (Payment Sent)", bobClient.getTrade(tradeId));
|
||||
} catch (StatusRuntimeException e) {
|
||||
fail(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(4)
|
||||
public void testBobsConfirmPaymentReceived(final TestInfo testInfo) {
|
||||
try {
|
||||
waitForSellerSeesPaymentInitiatedMessage(log, testInfo, bobClient, tradeId);
|
||||
var trade = bobClient.getTrade(tradeId);
|
||||
bobClient.confirmPaymentReceived(trade.getTradeId());
|
||||
sleep(3_000);
|
||||
trade = bobClient.getTrade(tradeId);
|
||||
// Note: offer.state == available
|
||||
assertEquals(AVAILABLE.name(), trade.getOffer().getState());
|
||||
EXPECTED_PROTOCOL_STATUS.setState(SELLER_SAW_ARRIVED_PAYOUT_TX_PUBLISHED_MSG)
|
||||
.setPhase(PAYOUT_PUBLISHED)
|
||||
.setPayoutPublished(true)
|
||||
.setPaymentReceivedMessageSent(true);
|
||||
verifyExpectedProtocolStatus(trade);
|
||||
logTrade(log, testInfo, "Bob's view after confirming fiat payment received", trade);
|
||||
} catch (StatusRuntimeException e) {
|
||||
fail(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void verifyJsonContractExcludesBankAccountDetails(String jsonContract,
|
||||
PaymentAccount paymentAccount) {
|
||||
NationalBankAccountPayload nationalBankAccountPayload =
|
||||
(NationalBankAccountPayload) paymentAccount.getPaymentAccountPayload();
|
||||
// The client cannot know exactly when payment acct payloads are added to a contract,
|
||||
// so auto-failing here results in a flaky test.
|
||||
// assertFalse(jsonContract.contains(nationalBankAccountPayload.getNationalAccountId()));
|
||||
// assertFalse(jsonContract.contains(nationalBankAccountPayload.getBranchId()));
|
||||
// assertFalse(jsonContract.contains(nationalBankAccountPayload.getAccountNr()));
|
||||
// assertFalse(jsonContract.contains(nationalBankAccountPayload.getHolderName()));
|
||||
// assertFalse(jsonContract.contains(nationalBankAccountPayload.getHolderTaxId()));
|
||||
|
||||
// Log warning if bank acct details are found in json contract.
|
||||
if (jsonContract.contains(nationalBankAccountPayload.getNationalAccountId()))
|
||||
log.warn("Could not check json contract soon enough; it contains national bank acct id");
|
||||
|
||||
if (jsonContract.contains(nationalBankAccountPayload.getBranchId()))
|
||||
log.warn("Could not check json contract soon enough; it contains natl bank branch id");
|
||||
|
||||
if (jsonContract.contains(nationalBankAccountPayload.getAccountNr()))
|
||||
log.warn("Could not check json contract soon enough; it contains natl bank acct #");
|
||||
|
||||
if (jsonContract.contains(nationalBankAccountPayload.getHolderName()))
|
||||
log.warn("Could not check json contract soon enough; it contains natl bank acct holder name");
|
||||
|
||||
if (jsonContract.contains(nationalBankAccountPayload.getHolderTaxId()))
|
||||
log.warn("Could not check json contract soon enough; it contains natl bank acct holder tax id");
|
||||
}
|
||||
|
||||
private void verifyJsonContractIncludesBankAccountDetails(String jsonContract,
|
||||
PaymentAccount paymentAccount) {
|
||||
NationalBankAccountPayload nationalBankAccountPayload =
|
||||
(NationalBankAccountPayload) paymentAccount.getPaymentAccountPayload();
|
||||
assertTrue(jsonContract.contains(nationalBankAccountPayload.getNationalAccountId()));
|
||||
assertTrue(jsonContract.contains(nationalBankAccountPayload.getBranchId()));
|
||||
assertTrue(jsonContract.contains(nationalBankAccountPayload.getAccountNr()));
|
||||
assertTrue(jsonContract.contains(nationalBankAccountPayload.getHolderName()));
|
||||
assertTrue(jsonContract.contains(nationalBankAccountPayload.getHolderTaxId()));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,144 @@
|
|||
/*
|
||||
* This file is part of Haveno.
|
||||
*
|
||||
* Haveno 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.
|
||||
*
|
||||
* Haveno 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 Haveno. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.apitest.method.trade;
|
||||
|
||||
import io.grpc.StatusRuntimeException;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.MethodOrderer;
|
||||
import org.junit.jupiter.api.Order;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestInfo;
|
||||
import org.junit.jupiter.api.TestMethodOrder;
|
||||
|
||||
import static bisq.apitest.config.ApiTestConfig.BTC;
|
||||
import static bisq.apitest.config.ApiTestConfig.XMR;
|
||||
import static bisq.cli.table.builder.TableType.OFFER_TBL;
|
||||
import static bisq.core.trade.Trade.Phase.PAYOUT_PUBLISHED;
|
||||
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.fail;
|
||||
import static protobuf.Offer.State.OFFER_FEE_RESERVED;
|
||||
import static protobuf.OfferDirection.SELL;
|
||||
|
||||
|
||||
|
||||
import bisq.apitest.method.offer.AbstractOfferTest;
|
||||
import bisq.cli.table.builder.TableBuilder;
|
||||
|
||||
@Disabled
|
||||
@Slf4j
|
||||
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
|
||||
public class TakeBuyXMROfferTest extends AbstractTradeTest {
|
||||
|
||||
// Alice is maker / xmr buyer (btc seller), Bob is taker / xmr seller (btc buyer).
|
||||
|
||||
@BeforeAll
|
||||
public static void setUp() {
|
||||
AbstractOfferTest.setUp();
|
||||
createXmrPaymentAccounts();
|
||||
EXPECTED_PROTOCOL_STATUS.init();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(1)
|
||||
public void testTakeAlicesSellBTCForXMROffer(final TestInfo testInfo) {
|
||||
try {
|
||||
// Alice is going to BUY XMR, but the Offer direction = SELL because it is a
|
||||
// BTC trade; Alice will SELL BTC for XMR. Bob will send Alice XMR.
|
||||
// Confused me, but just need to remember there are only BTC offers.
|
||||
var btcTradeDirection = SELL.name();
|
||||
var alicesOffer = aliceClient.createFixedPricedOffer(btcTradeDirection,
|
||||
XMR,
|
||||
15_000_000L,
|
||||
7_500_000L,
|
||||
"0.00455500", // FIXED PRICE IN BTC (satoshis) FOR 1 XMR
|
||||
defaultBuyerSecurityDepositPct.get(),
|
||||
alicesXmrAcct.getId());
|
||||
log.debug("Alice's BUY XMR (SELL BTC) Offer:\n{}", new TableBuilder(OFFER_TBL, alicesOffer).build());
|
||||
genBtcBlocksThenWait(1, 5000);
|
||||
var offerId = alicesOffer.getId();
|
||||
|
||||
var alicesXmrOffers = aliceClient.getMyOffers(btcTradeDirection, XMR);
|
||||
assertEquals(1, alicesXmrOffers.size());
|
||||
var trade = takeAlicesOffer(offerId, bobsXmrAcct.getId());
|
||||
alicesXmrOffers = aliceClient.getMyOffersSortedByDate(XMR);
|
||||
assertEquals(0, alicesXmrOffers.size());
|
||||
genBtcBlocksThenWait(1, 2_500);
|
||||
waitForDepositConfirmation(log, testInfo, bobClient, trade.getTradeId());
|
||||
|
||||
trade = bobClient.getTrade(tradeId);
|
||||
verifyTakerDepositConfirmed(trade);
|
||||
logTrade(log, testInfo, "Alice's Maker/Buyer View", aliceClient.getTrade(tradeId));
|
||||
logTrade(log, testInfo, "Bob's Taker/Seller View", bobClient.getTrade(tradeId));
|
||||
} catch (StatusRuntimeException e) {
|
||||
fail(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(2)
|
||||
public void testBobsConfirmPaymentStarted(final TestInfo testInfo) {
|
||||
try {
|
||||
var trade = bobClient.getTrade(tradeId);
|
||||
|
||||
verifyTakerDepositConfirmed(trade);
|
||||
log.debug("Bob sends XMR payment to Alice for trade {}", trade.getTradeId());
|
||||
bobClient.confirmPaymentStarted(trade.getTradeId());
|
||||
sleep(3500);
|
||||
waitForBuyerSeesPaymentInitiatedMessage(log, testInfo, bobClient, tradeId);
|
||||
|
||||
logTrade(log, testInfo, "Alice's Maker/Buyer View (Payment Sent)", aliceClient.getTrade(tradeId));
|
||||
logTrade(log, testInfo, "Bob's Taker/Seller View (Payment Sent)", bobClient.getTrade(tradeId));
|
||||
} catch (StatusRuntimeException e) {
|
||||
fail(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(3)
|
||||
public void testAlicesConfirmPaymentReceived(final TestInfo testInfo) {
|
||||
try {
|
||||
waitForSellerSeesPaymentInitiatedMessage(log, testInfo, aliceClient, tradeId);
|
||||
|
||||
sleep(2_000);
|
||||
var trade = aliceClient.getTrade(tradeId);
|
||||
// If we were trading BSQ, Alice would verify payment has been sent to her
|
||||
// Bisq wallet, but we can do no such checks for XMR payments.
|
||||
// All XMR transfers are done outside Bisq.
|
||||
log.debug("Alice verifies XMR payment was received from Bob, for trade {}", trade.getTradeId());
|
||||
aliceClient.confirmPaymentReceived(trade.getTradeId());
|
||||
sleep(3_000);
|
||||
|
||||
trade = aliceClient.getTrade(tradeId);
|
||||
assertEquals(OFFER_FEE_RESERVED.name(), trade.getOffer().getState());
|
||||
EXPECTED_PROTOCOL_STATUS.setState(SELLER_SAW_ARRIVED_PAYOUT_TX_PUBLISHED_MSG)
|
||||
.setPhase(PAYOUT_PUBLISHED)
|
||||
.setPayoutPublished(true)
|
||||
.setPaymentReceivedMessageSent(true);
|
||||
verifyExpectedProtocolStatus(trade);
|
||||
logTrade(log, testInfo, "Alice's Maker/Buyer View (Payment Received)", aliceClient.getTrade(tradeId));
|
||||
logTrade(log, testInfo, "Bob's Taker/Seller View (Payment Received)", bobClient.getTrade(tradeId));
|
||||
} catch (StatusRuntimeException e) {
|
||||
fail(e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -19,12 +19,8 @@ package bisq.apitest.method.trade;
|
|||
|
||||
import bisq.core.payment.PaymentAccount;
|
||||
|
||||
import bisq.proto.grpc.TradeInfo;
|
||||
|
||||
import io.grpc.StatusRuntimeException;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
|
@ -35,20 +31,16 @@ import org.junit.jupiter.api.TestInfo;
|
|||
import org.junit.jupiter.api.TestMethodOrder;
|
||||
|
||||
import static bisq.apitest.config.ApiTestConfig.BTC;
|
||||
import static bisq.cli.TableFormat.formatBalancesTbls;
|
||||
import static bisq.core.btc.wallet.Restrictions.getDefaultBuyerSecurityDepositAsPercent;
|
||||
import static bisq.core.trade.Trade.Phase.DEPOSIT_UNLOCKED;
|
||||
import static bisq.core.trade.Trade.Phase.PAYMENT_SENT;
|
||||
import static bisq.apitest.config.ApiTestConfig.USD;
|
||||
import static bisq.core.trade.Trade.Phase.PAYOUT_PUBLISHED;
|
||||
import static bisq.core.trade.Trade.Phase.WITHDRAWN;
|
||||
import static bisq.core.trade.Trade.State.*;
|
||||
import static java.lang.String.format;
|
||||
import static bisq.core.trade.Trade.State.SELLER_SAW_ARRIVED_PAYOUT_TX_PUBLISHED_MSG;
|
||||
import static bisq.core.trade.Trade.State.WITHDRAW_COMPLETED;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
import static protobuf.Offer.State.OFFER_FEE_RESERVED;
|
||||
import static protobuf.OfferPayload.Direction.SELL;
|
||||
import static protobuf.OpenOffer.State.AVAILABLE;
|
||||
import static protobuf.OfferDirection.SELL;
|
||||
|
||||
@Disabled
|
||||
@Slf4j
|
||||
|
@ -57,6 +49,9 @@ public class TakeSellBTCOfferTest extends AbstractTradeTest {
|
|||
|
||||
// Alice is maker/seller, Bob is taker/buyer.
|
||||
|
||||
// Maker and Taker fees are in BTC.
|
||||
private static final String TRADE_FEE_CURRENCY_CODE = BTC;
|
||||
|
||||
private static final String WITHDRAWAL_TX_MEMO = "Bob's trade withdrawal";
|
||||
|
||||
@Test
|
||||
|
@ -65,63 +60,35 @@ public class TakeSellBTCOfferTest extends AbstractTradeTest {
|
|||
try {
|
||||
PaymentAccount alicesUsdAccount = createDummyF2FAccount(aliceClient, "US");
|
||||
var alicesOffer = aliceClient.createMarketBasedPricedOffer(SELL.name(),
|
||||
"usd",
|
||||
USD,
|
||||
12_500_000L,
|
||||
12_500_000L, // min-amount = amount
|
||||
0.00,
|
||||
getDefaultBuyerSecurityDepositAsPercent(),
|
||||
alicesUsdAccount.getId());
|
||||
defaultBuyerSecurityDepositPct.get(),
|
||||
alicesUsdAccount.getId(),
|
||||
NO_TRIGGER_PRICE);
|
||||
var offerId = alicesOffer.getId();
|
||||
|
||||
// Wait for Alice's AddToOfferBook task.
|
||||
// Wait times vary; my logs show >= 2 second delay, but taking sell offers
|
||||
// Wait times vary; my logs show >= 2-second delay, but taking sell offers
|
||||
// seems to require more time to prepare.
|
||||
sleep(3000); // TODO loop instead of hard code wait time
|
||||
var alicesUsdOffers = aliceClient.getMyOffersSortedByDate(SELL.name(), "usd");
|
||||
sleep(3_000); // TODO loop instead of hard code a wait time
|
||||
var alicesUsdOffers = aliceClient.getMyOffersSortedByDate(SELL.name(), USD);
|
||||
assertEquals(1, alicesUsdOffers.size());
|
||||
|
||||
PaymentAccount bobsUsdAccount = createDummyF2FAccount(bobClient, "US");
|
||||
var trade = takeAlicesOffer(offerId, bobsUsdAccount.getId());
|
||||
assertNotNull(trade);
|
||||
assertEquals(offerId, trade.getTradeId());
|
||||
// Cache the trade id for the other tests.
|
||||
tradeId = trade.getTradeId();
|
||||
|
||||
genBtcBlocksThenWait(1, 4000);
|
||||
var takeableUsdOffers = bobClient.getOffersSortedByDate(SELL.name(), "usd");
|
||||
var trade = takeAlicesOffer(offerId,
|
||||
bobsUsdAccount.getId(),
|
||||
false);
|
||||
sleep(2_500); // Allow available offer to be removed from offer book.
|
||||
var takeableUsdOffers = bobClient.getOffersSortedByDate(SELL.name(), USD);
|
||||
assertEquals(0, takeableUsdOffers.size());
|
||||
|
||||
genBtcBlocksThenWait(1, 2500);
|
||||
|
||||
for (int i = 1; i <= maxTradeStateAndPhaseChecks.get(); i++) {
|
||||
trade = bobClient.getTrade(trade.getTradeId());
|
||||
|
||||
if (!trade.getIsDepositUnlocked()) {
|
||||
log.warn("Bob still waiting on trade {} maker tx {} taker tx {}: DEPOSIT_UNLOCKED_IN_BLOCK_CHAIN, attempt # {}",
|
||||
trade.getShortId(),
|
||||
trade.getMakerDepositTxId(),
|
||||
trade.getTakerDepositTxId(),
|
||||
i);
|
||||
genBtcBlocksThenWait(1, 4000);
|
||||
continue;
|
||||
} else {
|
||||
EXPECTED_PROTOCOL_STATUS.setState(DEPOSIT_UNLOCKED_IN_BLOCK_CHAIN)
|
||||
.setPhase(DEPOSIT_UNLOCKED)
|
||||
.setDepositPublished(true)
|
||||
.setDepositUnlocked(true);
|
||||
verifyExpectedProtocolStatus(trade);
|
||||
logTrade(log, testInfo, "Bob's view after deposit is confirmed", trade, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!trade.getIsDepositUnlocked()) {
|
||||
fail(format("INVALID_PHASE for Bob's trade %s in STATE=%s PHASE=%s, deposit tx never unlocked.",
|
||||
trade.getShortId(),
|
||||
trade.getState(),
|
||||
trade.getPhase()));
|
||||
}
|
||||
|
||||
genBtcBlocksThenWait(1, 2_500);
|
||||
waitForDepositConfirmation(log, testInfo, bobClient, trade.getTradeId());
|
||||
trade = bobClient.getTrade(tradeId);
|
||||
verifyTakerDepositConfirmed(trade);
|
||||
logTrade(log, testInfo, "Alice's Maker/Buyer View", aliceClient.getTrade(tradeId));
|
||||
logTrade(log, testInfo, "Bob's Taker/Seller View", bobClient.getTrade(tradeId));
|
||||
} catch (StatusRuntimeException e) {
|
||||
fail(e);
|
||||
}
|
||||
|
@ -132,54 +99,10 @@ public class TakeSellBTCOfferTest extends AbstractTradeTest {
|
|||
public void testBobsConfirmPaymentStarted(final TestInfo testInfo) {
|
||||
try {
|
||||
var trade = bobClient.getTrade(tradeId);
|
||||
|
||||
Predicate<TradeInfo> tradeStateAndPhaseCorrect = (t) ->
|
||||
t.getState().equals(DEPOSIT_UNLOCKED_IN_BLOCK_CHAIN.name()) && t.getPhase().equals(DEPOSIT_UNLOCKED.name());
|
||||
for (int i = 1; i <= maxTradeStateAndPhaseChecks.get(); i++) {
|
||||
if (!tradeStateAndPhaseCorrect.test(trade)) {
|
||||
log.warn("INVALID_PHASE for Bob's trade {} in STATE={} PHASE={}, cannot confirm payment started yet.",
|
||||
trade.getShortId(),
|
||||
trade.getState(),
|
||||
trade.getPhase());
|
||||
// fail("Bad trade state and phase.");
|
||||
sleep(1000 * 10);
|
||||
trade = bobClient.getTrade(tradeId);
|
||||
continue;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!tradeStateAndPhaseCorrect.test(trade)) {
|
||||
fail(format("INVALID_PHASE for Bob's trade %s in STATE=%s PHASE=%s, could not confirm payment started.",
|
||||
trade.getShortId(),
|
||||
trade.getState(),
|
||||
trade.getPhase()));
|
||||
}
|
||||
|
||||
verifyTakerDepositConfirmed(trade);
|
||||
bobClient.confirmPaymentStarted(tradeId);
|
||||
sleep(6000);
|
||||
|
||||
for (int i = 1; i <= maxTradeStateAndPhaseChecks.get(); i++) {
|
||||
trade = bobClient.getTrade(tradeId);
|
||||
|
||||
if (!trade.getIsPaymentSent()) {
|
||||
log.warn("Bob still waiting for trade {} BUYER_SAW_ARRIVED_PAYMENT_SENT_MSG, attempt # {}",
|
||||
trade.getShortId(),
|
||||
i);
|
||||
sleep(5000);
|
||||
continue;
|
||||
} else {
|
||||
// Note: offer.state == available
|
||||
assertEquals(AVAILABLE.name(), trade.getOffer().getState());
|
||||
EXPECTED_PROTOCOL_STATUS.setState(BUYER_SAW_ARRIVED_PAYMENT_SENT_MSG)
|
||||
.setPhase(PAYMENT_SENT)
|
||||
.setFiatSent(true);
|
||||
verifyExpectedProtocolStatus(trade);
|
||||
logTrade(log, testInfo, "Bob's view after confirming fiat payment sent", trade);
|
||||
break;
|
||||
}
|
||||
}
|
||||
sleep(6_000);
|
||||
waitForBuyerSeesPaymentInitiatedMessage(log, testInfo, bobClient, tradeId);
|
||||
} catch (StatusRuntimeException e) {
|
||||
fail(e);
|
||||
}
|
||||
|
@ -189,83 +112,21 @@ public class TakeSellBTCOfferTest extends AbstractTradeTest {
|
|||
@Order(3)
|
||||
public void testAlicesConfirmPaymentReceived(final TestInfo testInfo) {
|
||||
try {
|
||||
waitForSellerSeesPaymentInitiatedMessage(log, testInfo, aliceClient, tradeId);
|
||||
|
||||
var trade = aliceClient.getTrade(tradeId);
|
||||
|
||||
Predicate<TradeInfo> tradeStateAndPhaseCorrect = (t) ->
|
||||
t.getState().equals(SELLER_RECEIVED_PAYMENT_SENT_MSG.name())
|
||||
&& (t.getPhase().equals(PAYOUT_PUBLISHED.name()) || t.getPhase().equals(PAYMENT_SENT.name()));
|
||||
for (int i = 1; i <= maxTradeStateAndPhaseChecks.get(); i++) {
|
||||
if (!tradeStateAndPhaseCorrect.test(trade)) {
|
||||
log.warn("INVALID_PHASE for Alice's trade {} in STATE={} PHASE={}, cannot confirm payment received yet.",
|
||||
trade.getShortId(),
|
||||
trade.getState(),
|
||||
trade.getPhase());
|
||||
// fail("Bad trade state and phase.");
|
||||
sleep(1000 * 10);
|
||||
trade = aliceClient.getTrade(tradeId);
|
||||
continue;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!tradeStateAndPhaseCorrect.test(trade)) {
|
||||
fail(format("INVALID_PHASE for Alice's trade %s in STATE=%s PHASE=%s, could not confirm payment received.",
|
||||
trade.getShortId(),
|
||||
trade.getState(),
|
||||
trade.getPhase()));
|
||||
}
|
||||
|
||||
aliceClient.confirmPaymentReceived(trade.getTradeId());
|
||||
sleep(3000);
|
||||
|
||||
sleep(3_000);
|
||||
trade = aliceClient.getTrade(tradeId);
|
||||
assertEquals(OFFER_FEE_RESERVED.name(), trade.getOffer().getState());
|
||||
EXPECTED_PROTOCOL_STATUS.setState(SELLER_SAW_ARRIVED_PAYOUT_TX_PUBLISHED_MSG)
|
||||
.setPhase(PAYOUT_PUBLISHED)
|
||||
.setPayoutPublished(true)
|
||||
.setFiatReceived(true);
|
||||
.setPaymentReceivedMessageSent(true);
|
||||
verifyExpectedProtocolStatus(trade);
|
||||
logTrade(log, testInfo, "Alice's view after confirming fiat payment received", trade);
|
||||
} catch (StatusRuntimeException e) {
|
||||
fail(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(4)
|
||||
public void testBobsBtcWithdrawalToExternalAddress(final TestInfo testInfo) {
|
||||
try {
|
||||
genBtcBlocksThenWait(1, 1000);
|
||||
|
||||
var trade = bobClient.getTrade(tradeId);
|
||||
logTrade(log, testInfo, "Bob's view before withdrawing funds to external wallet", trade);
|
||||
|
||||
String toAddress = bitcoinCli.getNewBtcAddress();
|
||||
bobClient.withdrawFunds(tradeId, toAddress, WITHDRAWAL_TX_MEMO);
|
||||
|
||||
genBtcBlocksThenWait(1, 1000);
|
||||
|
||||
trade = bobClient.getTrade(tradeId);
|
||||
EXPECTED_PROTOCOL_STATUS.setState(WITHDRAW_COMPLETED)
|
||||
.setPhase(WITHDRAWN)
|
||||
.setWithdrawn(true);
|
||||
verifyExpectedProtocolStatus(trade);
|
||||
logTrade(log, testInfo, "Bob's view after withdrawing BTC funds to external wallet", trade);
|
||||
|
||||
logTrade(log, testInfo, "Alice's Maker/Buyer View (Done)", aliceClient.getTrade(tradeId), true);
|
||||
logTrade(log, testInfo, "Bob's Taker/Seller View (Done)", bobClient.getTrade(tradeId), true);
|
||||
|
||||
var alicesBalances = aliceClient.getBalances();
|
||||
log.info("{} Alice's Current Balance:\n{}",
|
||||
testName(testInfo),
|
||||
formatBalancesTbls(alicesBalances));
|
||||
var bobsBalances = bobClient.getBalances();
|
||||
log.info("{} Bob's Current Balance:\n{}",
|
||||
testName(testInfo),
|
||||
formatBalancesTbls(bobsBalances));
|
||||
} catch (StatusRuntimeException e) {
|
||||
fail(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,153 @@
|
|||
/*
|
||||
* This file is part of Haveno.
|
||||
*
|
||||
* Haveno 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.
|
||||
*
|
||||
* Haveno 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 Haveno. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.apitest.method.trade;
|
||||
|
||||
import io.grpc.StatusRuntimeException;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.MethodOrderer;
|
||||
import org.junit.jupiter.api.Order;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestInfo;
|
||||
import org.junit.jupiter.api.TestMethodOrder;
|
||||
|
||||
import static bisq.apitest.config.ApiTestConfig.BTC;
|
||||
import static bisq.apitest.config.ApiTestConfig.XMR;
|
||||
import static bisq.cli.table.builder.TableType.OFFER_TBL;
|
||||
import static bisq.core.trade.Trade.Phase.PAYOUT_PUBLISHED;
|
||||
import static bisq.core.trade.Trade.Phase.WITHDRAWN;
|
||||
import static bisq.core.trade.Trade.State.SELLER_SAW_ARRIVED_PAYOUT_TX_PUBLISHED_MSG;
|
||||
import static bisq.core.trade.Trade.State.WITHDRAW_COMPLETED;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
import static protobuf.OfferDirection.BUY;
|
||||
|
||||
|
||||
|
||||
import bisq.apitest.method.offer.AbstractOfferTest;
|
||||
import bisq.cli.table.builder.TableBuilder;
|
||||
|
||||
@Disabled
|
||||
@Slf4j
|
||||
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
|
||||
public class TakeSellXMROfferTest extends AbstractTradeTest {
|
||||
|
||||
// Alice is maker / xmr seller (btc buyer), Bob is taker / xmr buyer (btc seller).
|
||||
|
||||
// Maker and Taker fees are in BTC.
|
||||
private static final String TRADE_FEE_CURRENCY_CODE = BTC;
|
||||
|
||||
private static final String WITHDRAWAL_TX_MEMO = "Bob's trade withdrawal";
|
||||
|
||||
@BeforeAll
|
||||
public static void setUp() {
|
||||
AbstractOfferTest.setUp();
|
||||
createXmrPaymentAccounts();
|
||||
EXPECTED_PROTOCOL_STATUS.init();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(1)
|
||||
public void testTakeAlicesBuyBTCForXMROffer(final TestInfo testInfo) {
|
||||
try {
|
||||
// Alice is going to SELL XMR, but the Offer direction = BUY because it is a
|
||||
// BTC trade; Alice will BUY BTC for XMR. Alice will send Bob XMR.
|
||||
// Confused me, but just need to remember there are only BTC offers.
|
||||
var btcTradeDirection = BUY.name();
|
||||
double priceMarginPctInput = 1.50;
|
||||
var alicesOffer = aliceClient.createMarketBasedPricedOffer(btcTradeDirection,
|
||||
XMR,
|
||||
20_000_000L,
|
||||
10_500_000L,
|
||||
priceMarginPctInput,
|
||||
defaultBuyerSecurityDepositPct.get(),
|
||||
alicesXmrAcct.getId(),
|
||||
NO_TRIGGER_PRICE);
|
||||
log.debug("Alice's SELL XMR (BUY BTC) Offer:\n{}", new TableBuilder(OFFER_TBL, alicesOffer).build());
|
||||
genBtcBlocksThenWait(1, 4000);
|
||||
var offerId = alicesOffer.getId();
|
||||
|
||||
var alicesXmrOffers = aliceClient.getMyOffers(btcTradeDirection, XMR);
|
||||
assertEquals(1, alicesXmrOffers.size());
|
||||
var trade = takeAlicesOffer(offerId, bobsXmrAcct.getId());
|
||||
alicesXmrOffers = aliceClient.getMyOffersSortedByDate(XMR);
|
||||
assertEquals(0, alicesXmrOffers.size());
|
||||
genBtcBlocksThenWait(1, 2_500);
|
||||
|
||||
waitForDepositConfirmation(log, testInfo, bobClient, trade.getTradeId());
|
||||
|
||||
trade = bobClient.getTrade(tradeId);
|
||||
verifyTakerDepositConfirmed(trade);
|
||||
logTrade(log, testInfo, "Alice's Maker/Seller View", aliceClient.getTrade(tradeId));
|
||||
logTrade(log, testInfo, "Bob's Taker/Buyer View", bobClient.getTrade(tradeId));
|
||||
} catch (StatusRuntimeException e) {
|
||||
fail(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(2)
|
||||
public void testAlicesConfirmPaymentStarted(final TestInfo testInfo) {
|
||||
try {
|
||||
var trade = aliceClient.getTrade(tradeId);
|
||||
waitForDepositConfirmation(log, testInfo, aliceClient, trade.getTradeId());
|
||||
log.debug("Alice sends XMR payment to Bob for trade {}", trade.getTradeId());
|
||||
aliceClient.confirmPaymentStarted(trade.getTradeId());
|
||||
sleep(3500);
|
||||
|
||||
waitForBuyerSeesPaymentInitiatedMessage(log, testInfo, aliceClient, tradeId);
|
||||
logTrade(log, testInfo, "Alice's Maker/Seller View (Payment Sent)", aliceClient.getTrade(tradeId));
|
||||
logTrade(log, testInfo, "Bob's Taker/Buyer View (Payment Sent)", bobClient.getTrade(tradeId));
|
||||
} catch (StatusRuntimeException e) {
|
||||
fail(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(3)
|
||||
public void testBobsConfirmPaymentReceived(final TestInfo testInfo) {
|
||||
try {
|
||||
waitForSellerSeesPaymentInitiatedMessage(log, testInfo, bobClient, tradeId);
|
||||
|
||||
var trade = bobClient.getTrade(tradeId);
|
||||
sleep(2_000);
|
||||
// If we were trading BTC, Bob would verify payment has been sent to his
|
||||
// Bisq wallet, but we can do no such checks for XMR payments.
|
||||
// All XMR transfers are done outside Bisq.
|
||||
log.debug("Bob verifies XMR payment was received from Alice, for trade {}", trade.getTradeId());
|
||||
bobClient.confirmPaymentReceived(trade.getTradeId());
|
||||
sleep(3_000);
|
||||
|
||||
trade = bobClient.getTrade(tradeId);
|
||||
// Warning: trade.getOffer().getState() might be AVAILABLE, not OFFER_FEE_RESERVED.
|
||||
EXPECTED_PROTOCOL_STATUS.setState(SELLER_SAW_ARRIVED_PAYOUT_TX_PUBLISHED_MSG)
|
||||
.setPhase(PAYOUT_PUBLISHED)
|
||||
.setPayoutPublished(true)
|
||||
.setPaymentReceivedMessageSent(true);
|
||||
verifyExpectedProtocolStatus(trade);
|
||||
logTrade(log, testInfo, "Alice's Maker/Seller View (Payment Received)", aliceClient.getTrade(tradeId));
|
||||
logTrade(log, testInfo, "Bob's Taker/Buyer View (Payment Received)", bobClient.getTrade(tradeId));
|
||||
} catch (StatusRuntimeException e) {
|
||||
fail(e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -56,10 +56,9 @@ public class BtcTxFeeRateTest extends MethodTest {
|
|||
@Order(2)
|
||||
public void testSetInvalidTxFeeRateShouldThrowException(final TestInfo testInfo) {
|
||||
var currentTxFeeRateInfo = TxFeeRateInfo.fromProto(aliceClient.getTxFeeRate());
|
||||
Throwable exception = assertThrows(StatusRuntimeException.class, () ->
|
||||
aliceClient.setTxFeeRate(10));
|
||||
Throwable exception = assertThrows(StatusRuntimeException.class, () -> aliceClient.setTxFeeRate(1));
|
||||
String expectedExceptionMessage =
|
||||
format("UNKNOWN: tx fee rate preference must be >= %d sats/byte",
|
||||
format("INVALID_ARGUMENT: tx fee rate preference must be >= %d sats/byte",
|
||||
currentTxFeeRateInfo.getMinFeeServiceRate());
|
||||
assertEquals(expectedExceptionMessage, exception.getMessage());
|
||||
}
|
||||
|
|
|
@ -19,9 +19,8 @@ import static bisq.apitest.config.HavenoAppConfig.bobdaemon;
|
|||
import static bisq.apitest.config.HavenoAppConfig.seednode;
|
||||
import static bisq.apitest.method.wallet.WalletTestUtil.INITIAL_BTC_BALANCES;
|
||||
import static bisq.apitest.method.wallet.WalletTestUtil.verifyBtcBalances;
|
||||
import static bisq.cli.TableFormat.formatAddressBalanceTbl;
|
||||
import static bisq.cli.TableFormat.formatBtcBalanceInfoTbl;
|
||||
import static java.util.Collections.singletonList;
|
||||
import static bisq.cli.table.builder.TableType.ADDRESS_BALANCE_TBL;
|
||||
import static bisq.cli.table.builder.TableType.BTC_BALANCE_TBL;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
@ -30,6 +29,7 @@ import static org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
|
|||
|
||||
|
||||
import bisq.apitest.method.MethodTest;
|
||||
import bisq.cli.table.builder.TableBuilder;
|
||||
|
||||
@Disabled
|
||||
@Slf4j
|
||||
|
@ -54,10 +54,14 @@ public class BtcWalletTest extends MethodTest {
|
|||
// Bob & Alice's regtest Bisq wallets were initialized with 10 BTC.
|
||||
|
||||
BtcBalanceInfo alicesBalances = aliceClient.getBtcBalances();
|
||||
log.debug("{} Alice's BTC Balances:\n{}", testName(testInfo), formatBtcBalanceInfoTbl(alicesBalances));
|
||||
log.debug("{} Alice's BTC Balances:\n{}",
|
||||
testName(testInfo),
|
||||
new TableBuilder(BTC_BALANCE_TBL, alicesBalances).build());
|
||||
|
||||
BtcBalanceInfo bobsBalances = bobClient.getBtcBalances();
|
||||
log.debug("{} Bob's BTC Balances:\n{}", testName(testInfo), formatBtcBalanceInfoTbl(bobsBalances));
|
||||
log.debug("{} Bob's BTC Balances:\n{}",
|
||||
testName(testInfo),
|
||||
new TableBuilder(BTC_BALANCE_TBL, bobsBalances).build());
|
||||
|
||||
assertEquals(INITIAL_BTC_BALANCES.getAvailableBalance(), alicesBalances.getAvailableBalance());
|
||||
assertEquals(INITIAL_BTC_BALANCES.getAvailableBalance(), bobsBalances.getAvailableBalance());
|
||||
|
@ -76,7 +80,8 @@ public class BtcWalletTest extends MethodTest {
|
|||
|
||||
log.debug("{} -> Alice's Funded Address Balance -> \n{}",
|
||||
testName(testInfo),
|
||||
formatAddressBalanceTbl(singletonList(aliceClient.getAddressBalance(newAddress))));
|
||||
new TableBuilder(ADDRESS_BALANCE_TBL,
|
||||
aliceClient.getAddressBalance(newAddress)));
|
||||
|
||||
// New balance is 12.5 BTC
|
||||
btcBalanceInfo = aliceClient.getBtcBalances();
|
||||
|
@ -88,7 +93,7 @@ public class BtcWalletTest extends MethodTest {
|
|||
verifyBtcBalances(alicesExpectedBalances, btcBalanceInfo);
|
||||
log.debug("{} -> Alice's BTC Balances After Sending 2.5 BTC -> \n{}",
|
||||
testName(testInfo),
|
||||
formatBtcBalanceInfoTbl(btcBalanceInfo));
|
||||
new TableBuilder(BTC_BALANCE_TBL, btcBalanceInfo).build());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -115,7 +120,7 @@ public class BtcWalletTest extends MethodTest {
|
|||
BtcBalanceInfo alicesBalances = aliceClient.getBtcBalances();
|
||||
log.debug("{} Alice's BTC Balances:\n{}",
|
||||
testName(testInfo),
|
||||
formatBtcBalanceInfoTbl(alicesBalances));
|
||||
new TableBuilder(BTC_BALANCE_TBL, alicesBalances).build());
|
||||
bisq.core.api.model.BtcBalanceInfo alicesExpectedBalances =
|
||||
bisq.core.api.model.BtcBalanceInfo.valueOf(700000000,
|
||||
0,
|
||||
|
@ -126,7 +131,7 @@ public class BtcWalletTest extends MethodTest {
|
|||
BtcBalanceInfo bobsBalances = bobClient.getBtcBalances();
|
||||
log.debug("{} Bob's BTC Balances:\n{}",
|
||||
testName(testInfo),
|
||||
formatBtcBalanceInfoTbl(bobsBalances));
|
||||
new TableBuilder(BTC_BALANCE_TBL, bobsBalances).build());
|
||||
// The sendbtc tx weight and size randomly varies between two distinct values
|
||||
// (876 wu, 219 bytes, OR 880 wu, 220 bytes) from test run to test run, hence
|
||||
// the assertion of an available balance range [1549978000, 1549978100].
|
||||
|
|
|
@ -48,7 +48,7 @@ public class WalletProtectionTest extends MethodTest {
|
|||
@Order(2)
|
||||
public void testGetBalanceOnEncryptedWalletShouldThrowException() {
|
||||
Throwable exception = assertThrows(StatusRuntimeException.class, () -> aliceClient.getBtcBalances());
|
||||
assertEquals("UNKNOWN: wallet is locked", exception.getMessage());
|
||||
assertEquals("FAILED_PRECONDITION: wallet is locked", exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -58,7 +58,7 @@ public class WalletProtectionTest extends MethodTest {
|
|||
aliceClient.getBtcBalances(); // should not throw 'wallet locked' exception
|
||||
sleep(4500); // let unlock timeout expire
|
||||
Throwable exception = assertThrows(StatusRuntimeException.class, () -> aliceClient.getBtcBalances());
|
||||
assertEquals("UNKNOWN: wallet is locked", exception.getMessage());
|
||||
assertEquals("FAILED_PRECONDITION: wallet is locked", exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -67,7 +67,7 @@ public class WalletProtectionTest extends MethodTest {
|
|||
aliceClient.unlockWallet("first-password", 3);
|
||||
sleep(4000); // let unlock timeout expire
|
||||
Throwable exception = assertThrows(StatusRuntimeException.class, () -> aliceClient.getBtcBalances());
|
||||
assertEquals("UNKNOWN: wallet is locked", exception.getMessage());
|
||||
assertEquals("FAILED_PRECONDITION: wallet is locked", exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -76,14 +76,14 @@ public class WalletProtectionTest extends MethodTest {
|
|||
aliceClient.unlockWallet("first-password", 60);
|
||||
aliceClient.lockWallet();
|
||||
Throwable exception = assertThrows(StatusRuntimeException.class, () -> aliceClient.getBtcBalances());
|
||||
assertEquals("UNKNOWN: wallet is locked", exception.getMessage());
|
||||
assertEquals("FAILED_PRECONDITION: wallet is locked", exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(6)
|
||||
public void testLockWalletWhenWalletAlreadyLockedShouldThrowException() {
|
||||
Throwable exception = assertThrows(StatusRuntimeException.class, () -> aliceClient.lockWallet());
|
||||
assertEquals("UNKNOWN: wallet is already locked", exception.getMessage());
|
||||
assertEquals("ALREADY_EXISTS: wallet is already locked", exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -110,7 +110,7 @@ public class WalletProtectionTest extends MethodTest {
|
|||
public void testSetNewWalletPasswordWithIncorrectNewPasswordShouldThrowException() {
|
||||
Throwable exception = assertThrows(StatusRuntimeException.class, () ->
|
||||
aliceClient.setWalletPassword("bad old password", "irrelevant"));
|
||||
assertEquals("UNKNOWN: incorrect old password", exception.getMessage());
|
||||
assertEquals("INVALID_ARGUMENT: incorrect old password", exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -9,7 +9,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
|
|||
@Slf4j
|
||||
public class WalletTestUtil {
|
||||
|
||||
// All api tests depend on the regtest environment, and Bob & Alice's wallets
|
||||
// All api tests depend on the DAO / regtest environment, and Bob & Alice's wallets
|
||||
// are initialized with 10 BTC during the scaffolding setup.
|
||||
public static final bisq.core.api.model.BtcBalanceInfo INITIAL_BTC_BALANCES =
|
||||
bisq.core.api.model.BtcBalanceInfo.valueOf(1000000000,
|
||||
|
@ -17,7 +17,6 @@ public class WalletTestUtil {
|
|||
1000000000,
|
||||
0);
|
||||
|
||||
|
||||
public static void verifyBtcBalances(bisq.core.api.model.BtcBalanceInfo expected,
|
||||
BtcBalanceInfo actual) {
|
||||
assertEquals(expected.getAvailableBalance(), actual.getAvailableBalance());
|
||||
|
|
|
@ -0,0 +1,161 @@
|
|||
/*
|
||||
* This file is part of Haveno.
|
||||
*
|
||||
* Haveno 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.
|
||||
*
|
||||
* Haveno 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 Haveno. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.apitest.scenario;
|
||||
|
||||
import bisq.core.payment.PaymentAccount;
|
||||
|
||||
import bisq.proto.grpc.OfferInfo;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.junit.jupiter.api.MethodOrderer;
|
||||
import org.junit.jupiter.api.Order;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestInfo;
|
||||
import org.junit.jupiter.api.TestMethodOrder;
|
||||
import org.junit.jupiter.api.condition.EnabledIf;
|
||||
|
||||
import static java.lang.System.getenv;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
import static protobuf.OfferDirection.BUY;
|
||||
import static protobuf.OfferDirection.SELL;
|
||||
|
||||
|
||||
|
||||
import bisq.apitest.method.offer.AbstractOfferTest;
|
||||
|
||||
/**
|
||||
* Used to verify trigger based, automatic offer deactivation works.
|
||||
* Disabled by default.
|
||||
* Set ENV or IDE-ENV LONG_RUNNING_OFFER_DEACTIVATION_TEST_ENABLED=true to run.
|
||||
*/
|
||||
@EnabledIf("envLongRunningTestEnabled")
|
||||
@Slf4j
|
||||
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
|
||||
public class LongRunningOfferDeactivationTest extends AbstractOfferTest {
|
||||
|
||||
private static final int MAX_ITERATIONS = 500;
|
||||
|
||||
@Test
|
||||
@Order(1)
|
||||
public void testSellOfferAutoDisable(final TestInfo testInfo) {
|
||||
PaymentAccount paymentAcct = createDummyF2FAccount(aliceClient, "US");
|
||||
double mktPriceAsDouble = aliceClient.getBtcPrice("USD");
|
||||
String triggerPrice = calcPriceAsString(mktPriceAsDouble, -50.0000, 4);
|
||||
log.info("Current USD mkt price = {} Trigger Price = {}", mktPriceAsDouble, triggerPrice);
|
||||
OfferInfo offer = aliceClient.createMarketBasedPricedOffer(SELL.name(),
|
||||
"USD",
|
||||
1_000_000,
|
||||
1_000_000,
|
||||
0.00,
|
||||
defaultBuyerSecurityDepositPct.get(),
|
||||
paymentAcct.getId(),
|
||||
triggerPrice);
|
||||
log.info("SELL offer {} created with margin based price {}.",
|
||||
offer.getId(),
|
||||
offer.getPrice());
|
||||
genBtcBlocksThenWait(1, 2500); // Wait for offer book entry.
|
||||
|
||||
offer = aliceClient.getOffer(offer.getId()); // Offer has trigger price now.
|
||||
log.info("SELL offer should be automatically disabled when mkt price falls below {}.", offer.getTriggerPrice());
|
||||
|
||||
int numIterations = 0;
|
||||
while (++numIterations < MAX_ITERATIONS) {
|
||||
offer = aliceClient.getOffer(offer.getId());
|
||||
|
||||
var mktPrice = aliceClient.getBtcPrice("USD");
|
||||
if (offer.getIsActivated()) {
|
||||
log.info("Offer still enabled at mkt price {} > {} trigger price",
|
||||
mktPrice,
|
||||
offer.getTriggerPrice());
|
||||
sleep(1000 * 60); // 60s
|
||||
} else {
|
||||
log.info("Successful test completion after offer disabled at mkt price {} < {} trigger price.",
|
||||
mktPrice,
|
||||
offer.getTriggerPrice());
|
||||
break;
|
||||
}
|
||||
if (numIterations == MAX_ITERATIONS)
|
||||
fail("Offer never disabled");
|
||||
|
||||
genBtcBlocksThenWait(1, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(2)
|
||||
public void testBuyOfferAutoDisable(final TestInfo testInfo) {
|
||||
PaymentAccount paymentAcct = createDummyF2FAccount(aliceClient, "US");
|
||||
double mktPriceAsDouble = aliceClient.getBtcPrice("USD");
|
||||
String triggerPrice = calcPriceAsString(mktPriceAsDouble, 50.0000, 4);
|
||||
log.info("Current USD mkt price = {} Trigger Price = {}", mktPriceAsDouble, triggerPrice);
|
||||
OfferInfo offer = aliceClient.createMarketBasedPricedOffer(BUY.name(),
|
||||
"USD",
|
||||
1_000_000,
|
||||
1_000_000,
|
||||
0.00,
|
||||
defaultBuyerSecurityDepositPct.get(),
|
||||
paymentAcct.getId(),
|
||||
triggerPrice);
|
||||
log.info("BUY offer {} created with margin based price {}.",
|
||||
offer.getId(),
|
||||
offer.getPrice());
|
||||
genBtcBlocksThenWait(1, 2500); // Wait for offer book entry.
|
||||
|
||||
offer = aliceClient.getOffer(offer.getId()); // Offer has trigger price now.
|
||||
log.info("BUY offer should be automatically disabled when mkt price rises above {}.",
|
||||
offer.getTriggerPrice());
|
||||
|
||||
int numIterations = 0;
|
||||
while (++numIterations < MAX_ITERATIONS) {
|
||||
offer = aliceClient.getOffer(offer.getId());
|
||||
|
||||
var mktPrice = aliceClient.getBtcPrice("USD");
|
||||
if (offer.getIsActivated()) {
|
||||
log.info("Offer still enabled at mkt price {} < {} trigger price",
|
||||
mktPrice,
|
||||
offer.getTriggerPrice());
|
||||
sleep(1000 * 60); // 60s
|
||||
} else {
|
||||
log.info("Successful test completion after offer disabled at mkt price {} > {} trigger price.",
|
||||
mktPrice,
|
||||
offer.getTriggerPrice());
|
||||
break;
|
||||
}
|
||||
if (numIterations == MAX_ITERATIONS)
|
||||
fail("Offer never disabled");
|
||||
|
||||
genBtcBlocksThenWait(1, 0);
|
||||
}
|
||||
}
|
||||
|
||||
protected static boolean envLongRunningTestEnabled() {
|
||||
String envName = "LONG_RUNNING_OFFER_DEACTIVATION_TEST_ENABLED";
|
||||
String envX = getenv(envName);
|
||||
if (envX != null) {
|
||||
log.info("Enabled, found {}.", envName);
|
||||
return true;
|
||||
} else {
|
||||
log.info("Skipped, no environment variable {} defined.", envName);
|
||||
log.info("To enable on Mac OS or Linux:"
|
||||
+ "\tIf running in terminal, export LONG_RUNNING_OFFER_DEACTIVATION_TEST_ENABLED=true in bash shell."
|
||||
+ "\tIf running in Intellij, set LONG_RUNNING_OFFER_DEACTIVATION_TEST_ENABLED=true in launcher's Environment variables field.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -71,7 +71,6 @@ public class LongRunningTradesTest extends AbstractTradeTest {
|
|||
test.testTakeAlicesBuyOffer(testInfo);
|
||||
test.testAlicesConfirmPaymentStarted(testInfo);
|
||||
test.testBobsConfirmPaymentReceived(testInfo);
|
||||
test.testAlicesKeepFunds(testInfo);
|
||||
}
|
||||
|
||||
public void testTakeSellBTCOffer(final TestInfo testInfo) {
|
||||
|
@ -80,7 +79,6 @@ public class LongRunningTradesTest extends AbstractTradeTest {
|
|||
test.testTakeAlicesSellOffer(testInfo);
|
||||
test.testBobsConfirmPaymentStarted(testInfo);
|
||||
test.testAlicesConfirmPaymentReceived(testInfo);
|
||||
test.testBobsBtcWithdrawalToExternalAddress(testInfo);
|
||||
}
|
||||
|
||||
protected static boolean envLongRunningTestEnabled() {
|
||||
|
|
|
@ -20,6 +20,7 @@ package bisq.apitest.scenario;
|
|||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.MethodOrderer;
|
||||
import org.junit.jupiter.api.Order;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
@ -31,15 +32,21 @@ import bisq.apitest.method.offer.AbstractOfferTest;
|
|||
import bisq.apitest.method.offer.CancelOfferTest;
|
||||
import bisq.apitest.method.offer.CreateOfferUsingFixedPriceTest;
|
||||
import bisq.apitest.method.offer.CreateOfferUsingMarketPriceMarginTest;
|
||||
import bisq.apitest.method.offer.CreateXMROffersTest;
|
||||
import bisq.apitest.method.offer.ValidateCreateOfferTest;
|
||||
|
||||
@Slf4j
|
||||
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
|
||||
public class OfferTest extends AbstractOfferTest {
|
||||
|
||||
@BeforeAll
|
||||
public static void setUp() {
|
||||
setUp(false); // Use setUp(true) for running API daemons in remote debug mode.
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(1)
|
||||
public void testAmtTooLargeShouldThrowException() {
|
||||
public void testCreateOfferValidation() {
|
||||
ValidateCreateOfferTest test = new ValidateCreateOfferTest();
|
||||
test.testAmtTooLargeShouldThrowException();
|
||||
test.testNoMatchingEURPaymentAccountShouldThrowException();
|
||||
|
@ -57,18 +64,32 @@ public class OfferTest extends AbstractOfferTest {
|
|||
@Order(3)
|
||||
public void testCreateOfferUsingFixedPrice() {
|
||||
CreateOfferUsingFixedPriceTest test = new CreateOfferUsingFixedPriceTest();
|
||||
test.testCreateAUDXMRBuyOfferUsingFixedPrice16000();
|
||||
test.testCreateUSDXMRBuyOfferUsingFixedPrice100001234();
|
||||
test.testCreateEURXMRSellOfferUsingFixedPrice95001234();
|
||||
test.testCreateAUDBTCBuyOfferUsingFixedPrice16000();
|
||||
test.testCreateUSDBTCBuyOfferUsingFixedPrice100001234();
|
||||
test.testCreateEURBTCSellOfferUsingFixedPrice95001234();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(4)
|
||||
public void testCreateOfferUsingMarketPriceMargin() {
|
||||
public void testCreateOfferUsingMarketPriceMarginPct() {
|
||||
CreateOfferUsingMarketPriceMarginTest test = new CreateOfferUsingMarketPriceMarginTest();
|
||||
test.testCreateUSDXMRBuyOffer5PctPriceMargin();
|
||||
test.testCreateNZDXMRBuyOfferMinus2PctPriceMargin();
|
||||
test.testCreateGBPXMRSellOfferMinus1Point5PctPriceMargin();
|
||||
test.testCreateBRLXMRSellOffer6Point55PctPriceMargin();
|
||||
test.testCreateUSDBTCBuyOffer5PctPriceMargin();
|
||||
test.testCreateNZDBTCBuyOfferMinus2PctPriceMargin();
|
||||
test.testCreateGBPBTCSellOfferMinus1Point5PctPriceMargin();
|
||||
test.testCreateBRLBTCSellOffer6Point55PctPriceMargin();
|
||||
test.testCreateUSDBTCBuyOfferWithTriggerPrice();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(6)
|
||||
public void testCreateXMROffers() {
|
||||
CreateXMROffersTest test = new CreateXMROffersTest();
|
||||
CreateXMROffersTest.createXmrPaymentAccounts();
|
||||
test.testCreateFixedPriceBuy1BTCFor200KXMROffer();
|
||||
test.testCreateFixedPriceSell1BTCFor200KXMROffer();
|
||||
test.testCreatePriceMarginBasedBuy1BTCOfferWithTriggerPrice();
|
||||
test.testCreatePriceMarginBasedSell1BTCOffer();
|
||||
test.testGetAllMyXMROffers();
|
||||
test.testGetAvailableXMROffers();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -49,9 +49,9 @@ public class PaymentAccountTest extends AbstractPaymentAccountTest {
|
|||
test.testCreateAdvancedCashAccount(testInfo);
|
||||
test.testCreateAliPayAccount(testInfo);
|
||||
test.testCreateAustraliaPayidAccount(testInfo);
|
||||
test.testCreateCapitualAccount(testInfo);
|
||||
test.testCreateCashDepositAccount(testInfo);
|
||||
test.testCreateBrazilNationalBankAccount(testInfo);
|
||||
test.testCreateChaseQuickPayAccount(testInfo);
|
||||
test.testCreateClearXChangeAccount(testInfo);
|
||||
test.testCreateF2FAccount(testInfo);
|
||||
test.testCreateFasterPaymentsAccount(testInfo);
|
||||
|
@ -61,6 +61,8 @@ public class PaymentAccountTest extends AbstractPaymentAccountTest {
|
|||
test.testCreateMoneyBeamAccount(testInfo);
|
||||
test.testCreateMoneyGramAccount(testInfo);
|
||||
test.testCreatePerfectMoneyAccount(testInfo);
|
||||
test.testCreatePaxumAccount(testInfo);
|
||||
test.testCreatePayseraAccount(testInfo);
|
||||
test.testCreatePopmoneyAccount(testInfo);
|
||||
test.testCreatePromptPayAccount(testInfo);
|
||||
test.testCreateRevolutAccount(testInfo);
|
||||
|
@ -68,12 +70,12 @@ public class PaymentAccountTest extends AbstractPaymentAccountTest {
|
|||
test.testCreateSepaInstantAccount(testInfo);
|
||||
test.testCreateSepaAccount(testInfo);
|
||||
test.testCreateSpecificBanksAccount(testInfo);
|
||||
test.testCreateSwiftAccount(testInfo);
|
||||
test.testCreateSwishAccount(testInfo);
|
||||
|
||||
// TransferwiseAccount is only PaymentAccount with a
|
||||
// tradeCurrencies field in the json form.
|
||||
test.testCreateTransferwiseAccountWith1TradeCurrency(testInfo);
|
||||
test.testCreateTransferwiseAccountWith10TradeCurrencies(testInfo);
|
||||
test.testCreateTransferwiseAccountWithSupportedTradeCurrencies(testInfo);
|
||||
test.testCreateTransferwiseAccountWithInvalidBrlTradeCurrencyShouldThrowException(testInfo);
|
||||
test.testCreateTransferwiseAccountWithoutTradeCurrenciesShouldThrowException(testInfo);
|
||||
|
||||
|
|
|
@ -30,6 +30,7 @@ import org.junit.jupiter.api.Test;
|
|||
import org.junit.jupiter.api.TestMethodOrder;
|
||||
|
||||
import static bisq.apitest.Scaffold.BitcoinCoreApp.bitcoind;
|
||||
import static bisq.apitest.config.ApiTestRateMeterInterceptorConfig.getTestRateMeterInterceptorConfig;
|
||||
import static bisq.apitest.config.HavenoAppConfig.alicedaemon;
|
||||
import static bisq.apitest.config.HavenoAppConfig.arbdaemon;
|
||||
import static bisq.apitest.config.HavenoAppConfig.seednode;
|
||||
|
@ -54,7 +55,7 @@ public class StartupTest extends MethodTest {
|
|||
@BeforeAll
|
||||
public static void setUp() {
|
||||
try {
|
||||
callRateMeteringConfigFile = defaultRateMeterInterceptorConfig();
|
||||
callRateMeteringConfigFile = getTestRateMeterInterceptorConfig();
|
||||
startSupportingApps(callRateMeteringConfigFile,
|
||||
false,
|
||||
false,
|
||||
|
|
|
@ -30,7 +30,10 @@ import org.junit.jupiter.api.TestMethodOrder;
|
|||
|
||||
import bisq.apitest.method.trade.AbstractTradeTest;
|
||||
import bisq.apitest.method.trade.TakeBuyBTCOfferTest;
|
||||
import bisq.apitest.method.trade.TakeBuyBTCOfferWithNationalBankAcctTest;
|
||||
import bisq.apitest.method.trade.TakeBuyXMROfferTest;
|
||||
import bisq.apitest.method.trade.TakeSellBTCOfferTest;
|
||||
import bisq.apitest.method.trade.TakeSellXMROfferTest;
|
||||
|
||||
|
||||
@Slf4j
|
||||
|
@ -49,7 +52,6 @@ public class TradeTest extends AbstractTradeTest {
|
|||
test.testTakeAlicesBuyOffer(testInfo);
|
||||
test.testAlicesConfirmPaymentStarted(testInfo);
|
||||
test.testBobsConfirmPaymentReceived(testInfo);
|
||||
test.testAlicesKeepFunds(testInfo);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -59,6 +61,35 @@ public class TradeTest extends AbstractTradeTest {
|
|||
test.testTakeAlicesSellOffer(testInfo);
|
||||
test.testBobsConfirmPaymentStarted(testInfo);
|
||||
test.testAlicesConfirmPaymentReceived(testInfo);
|
||||
test.testBobsBtcWithdrawalToExternalAddress(testInfo);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(4)
|
||||
public void testTakeBuyBTCOfferWithNationalBankAcct(final TestInfo testInfo) {
|
||||
TakeBuyBTCOfferWithNationalBankAcctTest test = new TakeBuyBTCOfferWithNationalBankAcctTest();
|
||||
test.testTakeAlicesBuyOffer(testInfo);
|
||||
test.testBankAcctDetailsIncludedInContracts(testInfo);
|
||||
test.testAlicesConfirmPaymentStarted(testInfo);
|
||||
test.testBobsConfirmPaymentReceived(testInfo);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(6)
|
||||
public void testTakeBuyXMROffer(final TestInfo testInfo) {
|
||||
TakeBuyXMROfferTest test = new TakeBuyXMROfferTest();
|
||||
TakeBuyXMROfferTest.createXmrPaymentAccounts();
|
||||
test.testTakeAlicesSellBTCForXMROffer(testInfo);
|
||||
test.testBobsConfirmPaymentStarted(testInfo);
|
||||
test.testAlicesConfirmPaymentReceived(testInfo);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(7)
|
||||
public void testTakeSellXMROffer(final TestInfo testInfo) {
|
||||
TakeSellXMROfferTest test = new TakeSellXMROfferTest();
|
||||
TakeBuyXMROfferTest.createXmrPaymentAccounts();
|
||||
test.testTakeAlicesBuyBTCForXMROffer(testInfo);
|
||||
test.testAlicesConfirmPaymentStarted(testInfo);
|
||||
test.testBobsConfirmPaymentReceived(testInfo);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ import lombok.extern.slf4j.Slf4j;
|
|||
|
||||
import static bisq.core.locale.CountryUtil.findCountryByCode;
|
||||
import static bisq.core.payment.payload.PaymentMethod.CLEAR_X_CHANGE_ID;
|
||||
import static bisq.core.payment.payload.PaymentMethod.getPaymentMethodById;
|
||||
import static bisq.core.payment.payload.PaymentMethod.getPaymentMethod;
|
||||
import static java.lang.String.format;
|
||||
import static java.lang.System.getProperty;
|
||||
import static java.nio.file.Files.readAllBytes;
|
||||
|
@ -74,7 +74,7 @@ public abstract class AbstractBotTest extends MethodTest {
|
|||
} else {
|
||||
throw new UnsupportedOperationException(
|
||||
format("This test harness bot does not work with %s payment accounts yet.",
|
||||
getPaymentMethodById(paymentMethodId).getDisplayString()));
|
||||
getPaymentMethod(paymentMethodId).getDisplayString()));
|
||||
}
|
||||
} else {
|
||||
String countryCode = botScript.getCountryCode();
|
||||
|
|
|
@ -8,7 +8,7 @@ import lombok.extern.slf4j.Slf4j;
|
|||
|
||||
import static bisq.core.locale.CountryUtil.findCountryByCode;
|
||||
import static bisq.core.payment.payload.PaymentMethod.CLEAR_X_CHANGE_ID;
|
||||
import static bisq.core.payment.payload.PaymentMethod.getPaymentMethodById;
|
||||
import static bisq.core.payment.payload.PaymentMethod.getPaymentMethod;
|
||||
import static java.lang.String.format;
|
||||
import static java.util.concurrent.TimeUnit.MINUTES;
|
||||
|
||||
|
@ -62,7 +62,7 @@ class Bot {
|
|||
} else {
|
||||
throw new UnsupportedOperationException(
|
||||
format("This bot test does not work with %s payment accounts yet.",
|
||||
getPaymentMethodById(paymentMethodId).getDisplayString()));
|
||||
getPaymentMethod(paymentMethodId).getDisplayString()));
|
||||
}
|
||||
} else {
|
||||
Country country = findCountry(botScript.getCountryCode());
|
||||
|
|
|
@ -39,11 +39,6 @@ import bisq.cli.GrpcClient;
|
|||
|
||||
/**
|
||||
* Convenience GrpcClient wrapper for bots using gRPC services.
|
||||
*
|
||||
* TODO Consider if the duplication smell is bad enough to force a BotClient user
|
||||
* to use the GrpcClient instead (and delete this class). But right now, I think it is
|
||||
* OK because moving some of the non-gRPC related methods to GrpcClient is even smellier.
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings({"JavaDoc", "unused"})
|
||||
@Slf4j
|
||||
|
@ -124,6 +119,8 @@ public class BotClient {
|
|||
* @param minAmountInSatoshis
|
||||
* @param priceMarginAsPercent
|
||||
* @param securityDepositAsPercent
|
||||
* @param feeCurrency
|
||||
* @param triggerPrice
|
||||
* @return OfferInfo
|
||||
*/
|
||||
public OfferInfo createOfferAtMarketBasedPrice(PaymentAccount paymentAccount,
|
||||
|
@ -132,14 +129,16 @@ public class BotClient {
|
|||
long amountInSatoshis,
|
||||
long minAmountInSatoshis,
|
||||
double priceMarginAsPercent,
|
||||
double securityDepositAsPercent) {
|
||||
double securityDepositAsPercent,
|
||||
String triggerPrice) {
|
||||
return grpcClient.createMarketBasedPricedOffer(direction,
|
||||
currencyCode,
|
||||
amountInSatoshis,
|
||||
minAmountInSatoshis,
|
||||
priceMarginAsPercent,
|
||||
securityDepositAsPercent,
|
||||
paymentAccount.getId());
|
||||
paymentAccount.getId(),
|
||||
triggerPrice);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -151,6 +150,7 @@ public class BotClient {
|
|||
* @param minAmountInSatoshis
|
||||
* @param fixedOfferPriceAsString
|
||||
* @param securityDepositAsPercent
|
||||
* @param feeCurrency
|
||||
* @return OfferInfo
|
||||
*/
|
||||
public OfferInfo createOfferAtFixedPrice(PaymentAccount paymentAccount,
|
||||
|
@ -225,11 +225,11 @@ public class BotClient {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns true if the trade's taker deposit fee transaction is unlocked.
|
||||
* Returns true if the trade's taker deposit fee transaction has been confirmed.
|
||||
* @param tradeId a valid trade id
|
||||
* @return boolean
|
||||
*/
|
||||
public boolean isTakerDepositFeeTxUnlocked(String tradeId) {
|
||||
public boolean isTakerDepositFeeTxConfirmed(String tradeId) {
|
||||
return grpcClient.getTrade(tradeId).getIsDepositUnlocked();
|
||||
}
|
||||
|
||||
|
@ -278,15 +278,6 @@ public class BotClient {
|
|||
grpcClient.confirmPaymentReceived(tradeId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a 'keep funds in wallet message' for a trade with the given tradeId,
|
||||
* or throws an exception.
|
||||
* @param tradeId
|
||||
*/
|
||||
public void sendKeepFundsMessage(String tradeId) {
|
||||
grpcClient.keepFunds(tradeId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and save a new PaymentAccount with details in the given json.
|
||||
* @param json
|
||||
|
|
|
@ -33,10 +33,10 @@ import java.util.function.Supplier;
|
|||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import static bisq.cli.CurrencyFormat.formatMarketPrice;
|
||||
import static bisq.apitest.method.offer.AbstractOfferTest.defaultBuyerSecurityDepositPct;
|
||||
import static bisq.cli.CurrencyFormat.formatInternalFiatPrice;
|
||||
import static bisq.cli.CurrencyFormat.formatSatoshis;
|
||||
import static bisq.common.util.MathUtils.scaleDownByPowerOf10;
|
||||
import static bisq.core.btc.wallet.Restrictions.getDefaultBuyerSecurityDepositAsPercent;
|
||||
import static bisq.core.payment.payload.PaymentMethod.F2F_ID;
|
||||
import static java.lang.String.format;
|
||||
import static java.math.RoundingMode.HALF_UP;
|
||||
|
@ -124,7 +124,8 @@ public class RandomOffer {
|
|||
amount,
|
||||
minAmount,
|
||||
priceMargin,
|
||||
getDefaultBuyerSecurityDepositAsPercent());
|
||||
defaultBuyerSecurityDepositPct.get(),
|
||||
"0" /*no trigger price*/);
|
||||
} else {
|
||||
this.offer = botClient.createOfferAtFixedPrice(paymentAccount,
|
||||
direction,
|
||||
|
@ -132,7 +133,7 @@ public class RandomOffer {
|
|||
amount,
|
||||
minAmount,
|
||||
fixedOfferPrice,
|
||||
getDefaultBuyerSecurityDepositAsPercent());
|
||||
defaultBuyerSecurityDepositPct.get());
|
||||
}
|
||||
this.id = offer.getId();
|
||||
return this;
|
||||
|
@ -162,11 +163,11 @@ public class RandomOffer {
|
|||
log.info(description);
|
||||
if (useMarketBasedPrice) {
|
||||
log.info("Offer Price Margin = {}%", priceMargin);
|
||||
log.info("Expected Offer Price = {} {}", formatMarketPrice(Double.parseDouble(fixedOfferPrice)), currencyCode);
|
||||
log.info("Expected Offer Price = {} {}", formatInternalFiatPrice(Double.parseDouble(fixedOfferPrice)), currencyCode);
|
||||
} else {
|
||||
|
||||
log.info("Fixed Offer Price = {} {}", fixedOfferPrice, currencyCode);
|
||||
}
|
||||
log.info("Current Market Price = {} {}", formatMarketPrice(currentMarketPrice), currencyCode);
|
||||
log.info("Current Market Price = {} {}", formatInternalFiatPrice(currentMarketPrice), currencyCode);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ import lombok.extern.slf4j.Slf4j;
|
|||
|
||||
import static bisq.apitest.scenario.bot.protocol.ProtocolStep.DONE;
|
||||
import static bisq.apitest.scenario.bot.shutdown.ManualShutdown.isShutdownCalled;
|
||||
import static bisq.cli.TableFormat.formatBalancesTbls;
|
||||
import static bisq.cli.table.builder.TableType.BTC_BALANCE_TBL;
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
|
||||
|
||||
|
@ -34,6 +34,7 @@ import bisq.apitest.scenario.bot.protocol.TakerBotProtocol;
|
|||
import bisq.apitest.scenario.bot.script.BashScriptGenerator;
|
||||
import bisq.apitest.scenario.bot.script.BotScript;
|
||||
import bisq.apitest.scenario.bot.shutdown.ManualBotShutdownException;
|
||||
import bisq.cli.table.builder.TableBuilder;
|
||||
|
||||
@Slf4j
|
||||
public
|
||||
|
@ -74,10 +75,14 @@ class RobotBob extends Bot {
|
|||
throw new IllegalStateException(botProtocol.getClass().getSimpleName() + " failed to complete.");
|
||||
}
|
||||
|
||||
StringBuilder balancesBuilder = new StringBuilder();
|
||||
balancesBuilder.append("BTC").append("\n");
|
||||
balancesBuilder.append(new TableBuilder(BTC_BALANCE_TBL, botClient.getBalance().getBtc()).build().toString()).append("\n");
|
||||
|
||||
log.info("Completed {} successful trade{}. Current Balance:\n{}",
|
||||
++numTrades,
|
||||
numTrades == 1 ? "" : "s",
|
||||
formatBalancesTbls(botClient.getBalance()));
|
||||
balancesBuilder);
|
||||
|
||||
if (numTrades < actions.length) {
|
||||
try {
|
||||
|
|
|
@ -39,6 +39,7 @@ import lombok.extern.slf4j.Slf4j;
|
|||
|
||||
import static bisq.apitest.scenario.bot.protocol.ProtocolStep.*;
|
||||
import static bisq.apitest.scenario.bot.shutdown.ManualShutdown.checkIfShutdownCalled;
|
||||
import static bisq.cli.table.builder.TableType.TRADE_DETAIL_TBL;
|
||||
import static java.lang.String.format;
|
||||
import static java.lang.System.currentTimeMillis;
|
||||
import static java.util.Arrays.stream;
|
||||
|
@ -50,7 +51,7 @@ import bisq.apitest.method.BitcoinCliHelper;
|
|||
import bisq.apitest.scenario.bot.BotClient;
|
||||
import bisq.apitest.scenario.bot.script.BashScriptGenerator;
|
||||
import bisq.apitest.scenario.bot.shutdown.ManualBotShutdownException;
|
||||
import bisq.cli.TradeFormat;
|
||||
import bisq.cli.table.builder.TableBuilder;
|
||||
|
||||
@Slf4j
|
||||
public abstract class BotProtocol {
|
||||
|
@ -110,7 +111,7 @@ public abstract class BotProtocol {
|
|||
log.info("Starting protocol step {}. Bot will shutdown if step not completed within {} minutes.",
|
||||
currentProtocolStep.name(), MILLISECONDS.toMinutes(protocolStepTimeLimitInMs));
|
||||
|
||||
if (currentProtocolStep.equals(WAIT_FOR_TAKER_DEPOSIT_TX_UNLOCKED)) {
|
||||
if (currentProtocolStep.equals(WAIT_FOR_TAKER_DEPOSIT_TX_CONFIRMED)) {
|
||||
log.info("Generate a btc block to trigger taker's deposit fee tx confirmation.");
|
||||
createGenerateBtcBlockScript();
|
||||
}
|
||||
|
@ -133,7 +134,8 @@ public abstract class BotProtocol {
|
|||
try {
|
||||
var t = this.getBotClient().getTrade(trade.getTradeId());
|
||||
if (t.getIsPaymentSent()) {
|
||||
log.info("Buyer has started payment for trade:\n{}", TradeFormat.format(t));
|
||||
log.info("Buyer has started payment for trade:\n{}",
|
||||
new TableBuilder(TRADE_DETAIL_TBL, t).build().toString());
|
||||
return t;
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
|
@ -167,7 +169,8 @@ public abstract class BotProtocol {
|
|||
try {
|
||||
var t = this.getBotClient().getTrade(trade.getTradeId());
|
||||
if (t.getIsPaymentReceived()) {
|
||||
log.info("Seller has received payment for trade:\n{}", TradeFormat.format(t));
|
||||
log.info("Seller has received payment for trade:\n{}",
|
||||
new TableBuilder(TRADE_DETAIL_TBL, t).build().toString());
|
||||
return t;
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
|
@ -202,7 +205,7 @@ public abstract class BotProtocol {
|
|||
if (t.getIsPayoutPublished()) {
|
||||
log.info("Payout tx {} has been published for trade:\n{}",
|
||||
t.getPayoutTxId(),
|
||||
TradeFormat.format(t));
|
||||
new TableBuilder(TRADE_DETAIL_TBL, t).build().toString());
|
||||
return t;
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
|
@ -219,21 +222,6 @@ public abstract class BotProtocol {
|
|||
}
|
||||
};
|
||||
|
||||
protected final Function<TradeInfo, TradeInfo> keepFundsFromTrade = (trade) -> {
|
||||
initProtocolStep.accept(KEEP_FUNDS);
|
||||
var isBuy = trade.getOffer().getDirection().equalsIgnoreCase(BUY);
|
||||
var isSell = trade.getOffer().getDirection().equalsIgnoreCase(SELL);
|
||||
var cliUserIsSeller = (this instanceof MakerBotProtocol && isBuy) || (this instanceof TakerBotProtocol && isSell);
|
||||
if (cliUserIsSeller) {
|
||||
createKeepFundsScript(trade);
|
||||
} else {
|
||||
createGetBalanceScript();
|
||||
}
|
||||
checkIfShutdownCalled("Interrupted before closing trade with 'keep funds' command.");
|
||||
this.getBotClient().sendKeepFundsMessage(trade.getTradeId());
|
||||
return trade;
|
||||
};
|
||||
|
||||
protected void createPaymentStartedScript(TradeInfo trade) {
|
||||
File script = bashScriptGenerator.createPaymentStartedScript(trade);
|
||||
printCliHintAndOrScript(script, "The manual CLI side can send a 'payment started' message");
|
||||
|
@ -281,12 +269,12 @@ public abstract class BotProtocol {
|
|||
}
|
||||
|
||||
private void waitForTakerFeeTxConfirmed(String tradeId) {
|
||||
waitForTakerDepositFee(tradeId, WAIT_FOR_TAKER_DEPOSIT_TX_UNLOCKED);
|
||||
waitForTakerDepositFee(tradeId, WAIT_FOR_TAKER_DEPOSIT_TX_CONFIRMED);
|
||||
}
|
||||
|
||||
private void waitForTakerDepositFee(String tradeId, ProtocolStep depositTxProtocolStep) {
|
||||
initProtocolStep.accept(depositTxProtocolStep);
|
||||
validateCurrentProtocolStep(WAIT_FOR_TAKER_DEPOSIT_TX_PUBLISHED, WAIT_FOR_TAKER_DEPOSIT_TX_UNLOCKED);
|
||||
validateCurrentProtocolStep(WAIT_FOR_TAKER_DEPOSIT_TX_PUBLISHED, WAIT_FOR_TAKER_DEPOSIT_TX_CONFIRMED);
|
||||
try {
|
||||
log.info(waitingForDepositFeeTxMsg(tradeId));
|
||||
while (isWithinProtocolStepTimeLimit()) {
|
||||
|
@ -316,8 +304,8 @@ public abstract class BotProtocol {
|
|||
if (currentProtocolStep.equals(WAIT_FOR_TAKER_DEPOSIT_TX_PUBLISHED) && trade.getIsDepositPublished()) {
|
||||
log.info("Taker deposit fee tx {} has been published.", trade.getTakerDepositTxId());
|
||||
return true;
|
||||
} else if (currentProtocolStep.equals(WAIT_FOR_TAKER_DEPOSIT_TX_UNLOCKED) && trade.getIsDepositUnlocked()) {
|
||||
log.info("Taker deposit fee tx {} is unlocked.", trade.getTakerDepositTxId());
|
||||
} else if (currentProtocolStep.equals(WAIT_FOR_TAKER_DEPOSIT_TX_CONFIRMED) && trade.getIsDepositUnlocked()) {
|
||||
log.info("Taker deposit fee tx {} has been confirmed.", trade.getTakerDepositTxId());
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
|
|
|
@ -16,8 +16,8 @@ import lombok.extern.slf4j.Slf4j;
|
|||
import static bisq.apitest.scenario.bot.protocol.ProtocolStep.DONE;
|
||||
import static bisq.apitest.scenario.bot.protocol.ProtocolStep.WAIT_FOR_OFFER_TAKER;
|
||||
import static bisq.apitest.scenario.bot.shutdown.ManualShutdown.checkIfShutdownCalled;
|
||||
import static bisq.cli.TableFormat.formatOfferTable;
|
||||
import static java.util.Collections.singletonList;
|
||||
import static bisq.cli.table.builder.TableType.OFFER_TBL;
|
||||
import static bisq.cli.table.builder.TableType.TRADE_DETAIL_TBL;
|
||||
|
||||
|
||||
|
||||
|
@ -26,7 +26,7 @@ import bisq.apitest.scenario.bot.BotClient;
|
|||
import bisq.apitest.scenario.bot.RandomOffer;
|
||||
import bisq.apitest.scenario.bot.script.BashScriptGenerator;
|
||||
import bisq.apitest.scenario.bot.shutdown.ManualBotShutdownException;
|
||||
import bisq.cli.TradeFormat;
|
||||
import bisq.cli.table.builder.TableBuilder;
|
||||
|
||||
@Slf4j
|
||||
public class MakerBotProtocol extends BotProtocol {
|
||||
|
@ -56,16 +56,13 @@ public class MakerBotProtocol extends BotProtocol {
|
|||
: waitForPaymentStartedMessage.andThen(sendPaymentReceivedMessage);
|
||||
completeFiatTransaction.apply(trade);
|
||||
|
||||
Function<TradeInfo, TradeInfo> closeTrade = waitForPayoutTx.andThen(keepFundsFromTrade);
|
||||
closeTrade.apply(trade);
|
||||
|
||||
currentProtocolStep = DONE;
|
||||
}
|
||||
|
||||
private final Supplier<OfferInfo> randomOffer = () -> {
|
||||
checkIfShutdownCalled("Interrupted before creating random offer.");
|
||||
OfferInfo offer = new RandomOffer(botClient, paymentAccount).create().getOffer();
|
||||
log.info("Created random {} offer\n{}", currencyCode, formatOfferTable(singletonList(offer), currencyCode));
|
||||
log.info("Created random {} offer\n{}", currencyCode, new TableBuilder(OFFER_TBL, offer).build());
|
||||
return offer;
|
||||
};
|
||||
|
||||
|
@ -98,7 +95,9 @@ public class MakerBotProtocol extends BotProtocol {
|
|||
private Optional<TradeInfo> getNewTrade(String offerId) {
|
||||
try {
|
||||
var trade = botClient.getTrade(offerId);
|
||||
log.info("Offer {} was taken, new trade:\n{}", offerId, TradeFormat.format(trade));
|
||||
log.info("Offer {} was taken, new trade:\n{}",
|
||||
offerId,
|
||||
new TableBuilder(TRADE_DETAIL_TBL, trade).build().toString());
|
||||
return Optional.of(trade);
|
||||
} catch (Exception ex) {
|
||||
// Get trade will throw a non-fatal gRPC exception if not found.
|
||||
|
|
|
@ -6,12 +6,12 @@ public enum ProtocolStep {
|
|||
TAKE_OFFER,
|
||||
WAIT_FOR_OFFER_TAKER,
|
||||
WAIT_FOR_TAKER_DEPOSIT_TX_PUBLISHED,
|
||||
WAIT_FOR_TAKER_DEPOSIT_TX_UNLOCKED,
|
||||
WAIT_FOR_TAKER_DEPOSIT_TX_CONFIRMED,
|
||||
SEND_PAYMENT_STARTED_MESSAGE,
|
||||
WAIT_FOR_PAYMENT_STARTED_MESSAGE,
|
||||
SEND_PAYMENT_RECEIVED_CONFIRMATION_MESSAGE,
|
||||
WAIT_FOR_PAYMENT_RECEIVED_CONFIRMATION_MESSAGE,
|
||||
WAIT_FOR_PAYOUT_TX,
|
||||
KEEP_FUNDS,
|
||||
CLOSE_TRADE,
|
||||
DONE
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ import static bisq.apitest.scenario.bot.protocol.ProtocolStep.DONE;
|
|||
import static bisq.apitest.scenario.bot.protocol.ProtocolStep.FIND_OFFER;
|
||||
import static bisq.apitest.scenario.bot.protocol.ProtocolStep.TAKE_OFFER;
|
||||
import static bisq.apitest.scenario.bot.shutdown.ManualShutdown.checkIfShutdownCalled;
|
||||
import static bisq.cli.TableFormat.formatOfferTable;
|
||||
import static bisq.cli.table.builder.TableType.OFFER_TBL;
|
||||
import static bisq.core.payment.payload.PaymentMethod.F2F_ID;
|
||||
|
||||
|
||||
|
@ -26,6 +26,7 @@ import bisq.apitest.method.BitcoinCliHelper;
|
|||
import bisq.apitest.scenario.bot.BotClient;
|
||||
import bisq.apitest.scenario.bot.script.BashScriptGenerator;
|
||||
import bisq.apitest.scenario.bot.shutdown.ManualBotShutdownException;
|
||||
import bisq.cli.table.builder.TableBuilder;
|
||||
|
||||
@Slf4j
|
||||
public class TakerBotProtocol extends BotProtocol {
|
||||
|
@ -55,16 +56,13 @@ public class TakerBotProtocol extends BotProtocol {
|
|||
: sendPaymentStartedMessage.andThen(waitForPaymentReceivedConfirmation);
|
||||
completeFiatTransaction.apply(trade);
|
||||
|
||||
Function<TradeInfo, TradeInfo> closeTrade = waitForPayoutTx.andThen(keepFundsFromTrade);
|
||||
closeTrade.apply(trade);
|
||||
|
||||
currentProtocolStep = DONE;
|
||||
}
|
||||
|
||||
private final Supplier<Optional<OfferInfo>> firstOffer = () -> {
|
||||
var offers = botClient.getOffers(currencyCode);
|
||||
if (offers.size() > 0) {
|
||||
log.info("Offers found:\n{}", formatOfferTable(offers, currencyCode));
|
||||
log.info("Offers found:\n{}", new TableBuilder(OFFER_TBL, offers).build());
|
||||
OfferInfo offer = offers.get(0);
|
||||
log.info("Will take first offer {}", offer.getId());
|
||||
return Optional.of(offer);
|
||||
|
@ -107,7 +105,6 @@ public class TakerBotProtocol extends BotProtocol {
|
|||
|
||||
private void createMakeOfferScript() {
|
||||
String direction = RANDOM.nextBoolean() ? "BUY" : "SELL";
|
||||
String feeCurrency = "BTC";
|
||||
boolean createMarginPricedOffer = RANDOM.nextBoolean();
|
||||
// If not using an F2F account, don't go over possible 0.01 BTC
|
||||
// limit if account is not signed.
|
||||
|
@ -120,15 +117,13 @@ public class TakerBotProtocol extends BotProtocol {
|
|||
currencyCode,
|
||||
amount,
|
||||
"0.0",
|
||||
"15.0",
|
||||
feeCurrency);
|
||||
"15.0");
|
||||
} else {
|
||||
script = bashScriptGenerator.createMakeFixedPricedOfferScript(direction,
|
||||
currencyCode,
|
||||
amount,
|
||||
botClient.getCurrentBTCMarketPriceAsIntegerString(currencyCode),
|
||||
"15.0",
|
||||
feeCurrency);
|
||||
"15.0");
|
||||
}
|
||||
printCliHintAndOrScript(script, "The manual CLI side can create an offer");
|
||||
}
|
||||
|
|
|
@ -67,8 +67,7 @@ public class BashScriptGenerator {
|
|||
String currencyCode,
|
||||
String amount,
|
||||
String marketPriceMargin,
|
||||
String securityDeposit,
|
||||
String feeCurrency) {
|
||||
String securityDeposit) {
|
||||
String makeOfferCmd = format("%s createoffer --payment-account=%s "
|
||||
+ " --direction=%s"
|
||||
+ " --currency-code=%s"
|
||||
|
@ -82,8 +81,7 @@ public class BashScriptGenerator {
|
|||
currencyCode,
|
||||
amount,
|
||||
marketPriceMargin,
|
||||
securityDeposit,
|
||||
feeCurrency);
|
||||
securityDeposit);
|
||||
String getOffersCmd = format("%s getmyoffers --direction=%s --currency-code=%s",
|
||||
cliBase,
|
||||
direction,
|
||||
|
@ -98,8 +96,7 @@ public class BashScriptGenerator {
|
|||
String currencyCode,
|
||||
String amount,
|
||||
String fixedPrice,
|
||||
String securityDeposit,
|
||||
String feeCurrency) {
|
||||
String securityDeposit) {
|
||||
String makeOfferCmd = format("%s createoffer --payment-account=%s "
|
||||
+ " --direction=%s"
|
||||
+ " --currency-code=%s"
|
||||
|
@ -113,8 +110,7 @@ public class BashScriptGenerator {
|
|||
currencyCode,
|
||||
amount,
|
||||
fixedPrice,
|
||||
securityDeposit,
|
||||
feeCurrency);
|
||||
securityDeposit);
|
||||
String getOffersCmd = format("%s getmyoffers --direction=%s --currency-code=%s",
|
||||
cliBase,
|
||||
direction,
|
||||
|
@ -167,10 +163,10 @@ public class BashScriptGenerator {
|
|||
}
|
||||
|
||||
public File createKeepFundsScript(TradeInfo trade) {
|
||||
String paymentStartedCmd = format("%s keepfunds --trade-id=%s", cliBase, trade.getTradeId());
|
||||
String paymentStartedCmd = format("%s closetrade --trade-id=%s", cliBase, trade.getTradeId());
|
||||
String getTradeCmd = format("%s gettrade --trade-id=%s", cliBase, trade.getTradeId());
|
||||
String getBalanceCmd = format("%s getbalance", cliBase);
|
||||
return createCliScript("keepfunds.sh",
|
||||
return createCliScript("closetrade.sh",
|
||||
paymentStartedCmd,
|
||||
"sleep 2",
|
||||
getTradeCmd,
|
||||
|
|
|
@ -17,8 +17,9 @@
|
|||
|
||||
package bisq.apitest.scenario.bot.script;
|
||||
|
||||
import bisq.core.util.JsonUtil;
|
||||
|
||||
import bisq.common.file.JsonFileManager;
|
||||
import bisq.common.util.Utilities;
|
||||
|
||||
import joptsimple.BuiltinHelpFormatter;
|
||||
import joptsimple.OptionParser;
|
||||
|
@ -214,7 +215,7 @@ public class BotScriptGenerator {
|
|||
}
|
||||
|
||||
private String generateBotScriptTemplate() {
|
||||
return Utilities.objectToJson(new BotScript(
|
||||
return JsonUtil.objectToJson(new BotScript(
|
||||
useTestHarness,
|
||||
botPaymentMethodId,
|
||||
countryCode,
|
||||
|
|
20
apitest/src/test/resources/logback.xml
Normal file
20
apitest/src/test/resources/logback.xml
Normal file
|
@ -0,0 +1,20 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration>
|
||||
<!--
|
||||
The :daemon & :cli jars contain their own logback.xml config files, which causes chatty logback startup.
|
||||
To avoid chatty logback msgs during its configuration, pass logback.configurationFile as a system property:
|
||||
-Dlogback.configurationFile=apitest/build/resources/main/logback.xml
|
||||
The gradle build file takes care of adding this system property to the bisq-apitest script.
|
||||
-->
|
||||
<appender name="CONSOLE_APPENDER" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%highlight(%d{MMM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{30}: %msg %xEx%n)</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<root level="INFO">
|
||||
<appender-ref ref="CONSOLE_APPENDER"/>
|
||||
</root>
|
||||
|
||||
<logger name="io.grpc.netty" level="WARN"/>
|
||||
</configuration>
|
Loading…
Add table
Add a link
Reference in a new issue