mirror of
https://github.com/bisq-network/bisq.git
synced 2025-02-22 14:42:37 +01:00
Move test cases into subproject test sources
This change reorganizes the ':apitest' subproject to conform to a typical gradle project, where all JUnit test cases are located in the subproject's test sources folder. This makes running tests from an IDE or gradle command line interface work as expected. It will also help keep Travis CI configuration simple. To avoid interfering in normal builds, the gradle ':apitest test' task is disable by default; API tests will only run when a '-Dforce-true' system property is passed to gradle. To run API tests, run a normal build and install dao-setup files: ./gradlew clean build :apitest:installDaoSetup Then run the tests: ./gradlew :apitest:test -Dforce=true Try to avoid adding the '-Dforce=true' property to any other gradle tasks, because this enables the ':apitest test' task, and would kick off API tests before a normal build completed. The build.gradle file was modified to support this code reorg, and the 'org.junit.jupiter' dependendency was upgraded to v5.6.2 -- only in the ':apitest:test' dependency definiitions, not anywhere else in the bisq dependency definitions. The upgrade is necessary for running ordered tests. Since the scaffolding may be set up from either test cases (under the test src folder), or a class under the main src folder, some changes were made to ensure configuration paths are correct for either use case. For example, when the 'bisq-apitest' script is run from the root project directory, the current working directory is the root project directory. When gradle or an IDE is used to run @Test cases, the current working directory is :apitest subproject directory. The main source's ApiTestMain class has been stripped down, and exists only to keep the gradle build happy -- it needs a 'mainClassName' property. But this main driver does have uses. See the class comments. The other changes in this commit were made to fix style and syntax problems.
This commit is contained in:
parent
7c974b22ac
commit
bf584c218f
16 changed files with 838 additions and 31 deletions
|
@ -29,9 +29,19 @@ import static java.lang.System.exit;
|
|||
import bisq.apitest.config.ApiTestConfig;
|
||||
|
||||
/**
|
||||
* ApiTest Application
|
||||
* ApiTestMain is a placeholder for the gradle build file, which requires a valid
|
||||
* 'mainClassName' property in the :apitest subproject configuration.
|
||||
*
|
||||
* Runs all method tests, scenario tests, and end to end tests (e2e).
|
||||
* It does has some uses:
|
||||
*
|
||||
* It can be used to print test scaffolding options: bisq-apitest --help.
|
||||
*
|
||||
* It can be used to smoke test your bitcoind environment: bisq-apitest.
|
||||
*
|
||||
* It can be used to run the regtest/dao environment for release testing:
|
||||
* bisq-test --shutdownAfterTests=false
|
||||
*
|
||||
* All method, scenario and end to end tests are found in the test sources folder.
|
||||
*
|
||||
* Requires bitcoind v0.19.x
|
||||
*/
|
||||
|
|
|
@ -96,7 +96,7 @@ public class Scaffold {
|
|||
* @param supportingApps String
|
||||
*/
|
||||
public Scaffold(String supportingApps) {
|
||||
this(new ApiTestConfig(new String[]{"--supportingApps", supportingApps}));
|
||||
this(new ApiTestConfig("--supportingApps", supportingApps));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -181,29 +181,29 @@ public class Scaffold {
|
|||
public void installDaoSetupDirectories() {
|
||||
cleanDaoSetupDirectories();
|
||||
|
||||
String srcResourcesDir = Paths.get("apitest", "src", "main", "resources", "dao-setup").toFile().getAbsolutePath();
|
||||
String daoSetupDir = Paths.get(config.baseSrcResourcesDir, "dao-setup").toFile().getAbsolutePath();
|
||||
String buildDataDir = config.rootAppDataDir.getAbsolutePath();
|
||||
try {
|
||||
if (!new File(srcResourcesDir).exists())
|
||||
if (!new File(daoSetupDir).exists())
|
||||
throw new FileNotFoundException(
|
||||
format("Dao setup dir '%s' not found. Run gradle :apitest:installDaoSetup"
|
||||
+ " to download dao-setup.zip and extract contents to resources folder",
|
||||
srcResourcesDir));
|
||||
daoSetupDir));
|
||||
|
||||
BashCommand copyBitcoinRegtestDir = new BashCommand(
|
||||
"cp -rf " + srcResourcesDir + "/Bitcoin-regtest/regtest"
|
||||
"cp -rf " + daoSetupDir + "/Bitcoin-regtest/regtest"
|
||||
+ " " + config.bitcoinDatadir);
|
||||
if (copyBitcoinRegtestDir.run().getExitStatus() != 0)
|
||||
throw new IllegalStateException("Could not install bitcoin regtest dir");
|
||||
|
||||
BashCommand copyAliceDataDir = new BashCommand(
|
||||
"cp -rf " + srcResourcesDir + "/" + alicedaemon.appName
|
||||
"cp -rf " + daoSetupDir + "/" + alicedaemon.appName
|
||||
+ " " + config.rootAppDataDir);
|
||||
if (copyAliceDataDir.run().getExitStatus() != 0)
|
||||
throw new IllegalStateException("Could not install alice data dir");
|
||||
|
||||
BashCommand copyBobDataDir = new BashCommand(
|
||||
"cp -rf " + srcResourcesDir + "/" + bobdaemon.appName
|
||||
"cp -rf " + daoSetupDir + "/" + bobdaemon.appName
|
||||
+ " " + config.rootAppDataDir);
|
||||
if (copyBobDataDir.run().getExitStatus() != 0)
|
||||
throw new IllegalStateException("Could not install bob data dir");
|
||||
|
@ -217,7 +217,7 @@ public class Scaffold {
|
|||
installBitcoinBlocknotify();
|
||||
|
||||
} catch (IOException | InterruptedException ex) {
|
||||
throw new IllegalStateException("Could not install dao-setup files from " + srcResourcesDir, ex);
|
||||
throw new IllegalStateException("Could not install dao-setup files from " + daoSetupDir, ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -254,7 +254,7 @@ public class Scaffold {
|
|||
private void installBitcoinBlocknotify() {
|
||||
// gradle is not working for this
|
||||
try {
|
||||
Path srcPath = Paths.get("apitest", "src", "main", "resources", "blocknotify");
|
||||
Path srcPath = Paths.get(config.baseSrcResourcesDir, "blocknotify");
|
||||
Path destPath = Paths.get(config.bitcoinDatadir, "blocknotify");
|
||||
Files.copy(srcPath, destPath, REPLACE_EXISTING);
|
||||
String chmod700Perms = "rwx------";
|
||||
|
@ -283,7 +283,7 @@ public class Scaffold {
|
|||
bitcoinDaemon.verifyBitcoinPathsExist(true);
|
||||
bitcoindTask = new SetupTask(bitcoinDaemon, countdownLatch);
|
||||
bitcoindTaskFuture = executor.submit(bitcoindTask);
|
||||
MILLISECONDS.sleep(3500);
|
||||
MILLISECONDS.sleep(3500); // todo make configurable
|
||||
bitcoinDaemon.verifyBitcoindRunning();
|
||||
}
|
||||
|
||||
|
|
|
@ -104,20 +104,32 @@ public class ApiTestConfig {
|
|||
public final boolean shutdownAfterTests;
|
||||
public final List<String> supportingApps;
|
||||
|
||||
// Immutable system configurations.
|
||||
// Immutable system configurations set in the constructor.
|
||||
public final String bitcoinDatadir;
|
||||
public final String userDir;
|
||||
public final boolean isRunningTest;
|
||||
public final String rootProjectDir;
|
||||
public final String baseBuildResourcesDir;
|
||||
public final String baseSrcResourcesDir;
|
||||
|
||||
|
||||
// The parser that will be used to parse both cmd line and config file options
|
||||
private final OptionParser parser = new OptionParser();
|
||||
|
||||
public ApiTestConfig(String... args) {
|
||||
this.defaultConfigFile = absoluteConfigFile(
|
||||
Paths.get("apitest", "build", "resources", "main").toFile().getAbsolutePath(),
|
||||
DEFAULT_CONFIG_FILE_NAME);
|
||||
this.bitcoinDatadir = Paths.get("apitest", "build", "resources", "main", "Bitcoin-regtest")
|
||||
.toFile().getAbsolutePath();
|
||||
this.userDir = getProperty("user.dir");
|
||||
// If running a @Test, the current working directory is the :apitest subproject
|
||||
// folder. If running ApiTestMain, the current working directory is the
|
||||
// bisq root project folder.
|
||||
this.isRunningTest = Paths.get(userDir).getFileName().toString().equals("apitest");
|
||||
this.rootProjectDir = isRunningTest
|
||||
? Paths.get(userDir).getParent().toFile().getAbsolutePath()
|
||||
: Paths.get(userDir).toFile().getAbsolutePath();
|
||||
this.baseBuildResourcesDir = Paths.get(rootProjectDir, "apitest", "build", "resources", "main").toFile().getAbsolutePath();
|
||||
this.baseSrcResourcesDir = Paths.get(rootProjectDir, "apitest", "src", "main", "resources").toFile().getAbsolutePath();
|
||||
|
||||
this.defaultConfigFile = absoluteConfigFile(baseBuildResourcesDir, DEFAULT_CONFIG_FILE_NAME);
|
||||
this.bitcoinDatadir = Paths.get(baseBuildResourcesDir, "Bitcoin-regtest").toFile().getAbsolutePath();
|
||||
|
||||
AbstractOptionSpec<Void> helpOpt =
|
||||
parser.accepts(HELP, "Print this help text")
|
||||
|
@ -134,8 +146,7 @@ public class ApiTestConfig {
|
|||
parser.accepts(ROOT_APP_DATA_DIR, "Application data directory")
|
||||
.withRequiredArg()
|
||||
.ofType(File.class)
|
||||
.defaultsTo(Paths.get("apitest", "build", "resources", "main")
|
||||
.toFile().getAbsoluteFile());
|
||||
.defaultsTo(new File(baseBuildResourcesDir));
|
||||
|
||||
ArgumentAcceptingOptionSpec<String> bashPathOpt =
|
||||
parser.accepts(BASH_PATH, "Bash path")
|
||||
|
@ -193,7 +204,7 @@ public class ApiTestConfig {
|
|||
"Run Arbitration node as desktop")
|
||||
.withRequiredArg()
|
||||
.ofType(Boolean.class)
|
||||
.defaultsTo(false); // TODO how do I register arbitrator?
|
||||
.defaultsTo(false); // TODO how do I register mediator?
|
||||
|
||||
ArgumentAcceptingOptionSpec<Boolean> runAliceNodeAsDesktopOpt =
|
||||
parser.accepts(RUN_ALICE_NODE_AS_DESKTOP,
|
||||
|
@ -309,7 +320,7 @@ public class ApiTestConfig {
|
|||
}
|
||||
|
||||
public boolean hasSupportingApp(String... supportingApp) {
|
||||
return stream(supportingApp).anyMatch(a -> this.supportingApps.contains(a));
|
||||
return stream(supportingApp).anyMatch(this.supportingApps::contains);
|
||||
}
|
||||
|
||||
public void printHelp(OutputStream sink, HelpFormatter formatter) {
|
||||
|
|
|
@ -65,11 +65,12 @@ public class BisqApp extends AbstractLinuxProcess implements LinuxProcess {
|
|||
this.fullDaoNode = true;
|
||||
this.useLocalhostForP2P = true;
|
||||
this.useDevPrivilegeKeys = true;
|
||||
this.findBisqPidScript = config.userDir + "/apitest/scripts/get-bisq-pid.sh";
|
||||
this.findBisqPidScript = (config.isRunningTest ? "." : "./apitest")
|
||||
+ "/scripts/get-bisq-pid.sh";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() throws InterruptedException, IOException {
|
||||
public void start() {
|
||||
try {
|
||||
if (config.runSubprojectJars)
|
||||
runJar(); // run subproject/build/lib/*.jar (not full build)
|
||||
|
@ -86,7 +87,7 @@ public class BisqApp extends AbstractLinuxProcess implements LinuxProcess {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() throws IOException, InterruptedException {
|
||||
public void shutdown() {
|
||||
try {
|
||||
log.info("Shutting down {} ...", bisqAppConfig.appName);
|
||||
if (!isAlive(pid))
|
||||
|
@ -156,8 +157,10 @@ public class BisqApp extends AbstractLinuxProcess implements LinuxProcess {
|
|||
// It runs a bisq-* startup script, and depends on a full build. Bisq jars
|
||||
// are loaded from the root project's lib directory.
|
||||
private void runStartupScript() throws IOException, InterruptedException {
|
||||
String startupScriptPath = config.rootProjectDir
|
||||
+ "/" + bisqAppConfig.startupScript;
|
||||
String bisqCmd = getJavaOptsSpec()
|
||||
+ " " + config.userDir + "/" + bisqAppConfig.startupScript
|
||||
+ " " + startupScriptPath
|
||||
+ " " + String.join(" ", getOptsList())
|
||||
+ " &"; // run in background without nohup
|
||||
runBashCommand(bisqCmd);
|
||||
|
@ -172,7 +175,8 @@ public class BisqApp extends AbstractLinuxProcess implements LinuxProcess {
|
|||
bashCommand.runInBackground();
|
||||
|
||||
if (bashCommand.getExitStatus() != 0)
|
||||
throw new IllegalStateException(format("Error starting BisqApp\n%s\nError: %s",
|
||||
throw new IllegalStateException(format("Error starting BisqApp%n%s%nError: %s",
|
||||
bisqAppConfig.appName,
|
||||
bashCommand.getError()));
|
||||
|
||||
// Sometimes it takes a little extra time to find the linux process id.
|
||||
|
|
|
@ -62,14 +62,14 @@ class SystemCommandExecutor {
|
|||
if (log.isDebugEnabled())
|
||||
log.debug("cmd options {}", cmdOptions.toString());
|
||||
|
||||
if (cmdOptions.isEmpty())
|
||||
throw new IllegalStateException("No command params specified.");
|
||||
|
||||
if (cmdOptions.contains("sudo")) {
|
||||
log.error("", new IllegalStateException("'sudo' commands are prohibited."));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (cmdOptions == null)
|
||||
throw new IllegalStateException("No command params specified.");
|
||||
|
||||
this.cmdOptions = cmdOptions;
|
||||
}
|
||||
|
||||
|
|
62
apitest/src/test/java/bisq/apitest/ApiTestCase.java
Normal file
62
apitest/src/test/java/bisq/apitest/ApiTestCase.java
Normal file
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.apitest;
|
||||
|
||||
import static bisq.apitest.config.BisqAppConfig.alicedaemon;
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
|
||||
|
||||
|
||||
import bisq.apitest.config.ApiTestConfig;
|
||||
import bisq.apitest.method.BitcoinCliHelper;
|
||||
|
||||
public class ApiTestCase {
|
||||
|
||||
// The gRPC service stubs are used by method & scenario tests, but not e2e tests.
|
||||
protected static GrpcStubs grpcStubs;
|
||||
|
||||
protected static Scaffold scaffold;
|
||||
protected static ApiTestConfig config;
|
||||
protected static BitcoinCliHelper bitcoinCli;
|
||||
|
||||
public static void setUpScaffold(String supportingApps) {
|
||||
// The supportingApps argument is a comma delimited string of supporting app
|
||||
// names, e.g. "bitcoind,seednode,arbdaemon,alicedaemon,bobdaemon"
|
||||
scaffold = new Scaffold(supportingApps).setUp();
|
||||
config = scaffold.config;
|
||||
bitcoinCli = new BitcoinCliHelper((config));
|
||||
grpcStubs = new GrpcStubs(alicedaemon, config).init();
|
||||
}
|
||||
|
||||
public static void setUpScaffold() {
|
||||
scaffold = new Scaffold(new String[]{}).setUp();
|
||||
config = scaffold.config;
|
||||
grpcStubs = new GrpcStubs(alicedaemon, config).init();
|
||||
}
|
||||
|
||||
public static void tearDownScaffold() {
|
||||
scaffold.tearDown();
|
||||
}
|
||||
|
||||
protected void sleep(long ms) {
|
||||
try {
|
||||
MILLISECONDS.sleep(ms);
|
||||
} catch (InterruptedException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
109
apitest/src/test/java/bisq/apitest/GrpcStubs.java
Normal file
109
apitest/src/test/java/bisq/apitest/GrpcStubs.java
Normal file
|
@ -0,0 +1,109 @@
|
|||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.apitest;
|
||||
|
||||
import bisq.proto.grpc.GetVersionGrpc;
|
||||
import bisq.proto.grpc.OffersGrpc;
|
||||
import bisq.proto.grpc.PaymentAccountsGrpc;
|
||||
import bisq.proto.grpc.WalletsGrpc;
|
||||
|
||||
import io.grpc.CallCredentials;
|
||||
import io.grpc.ManagedChannelBuilder;
|
||||
import io.grpc.Metadata;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import static io.grpc.Metadata.ASCII_STRING_MARSHALLER;
|
||||
import static io.grpc.Status.UNAUTHENTICATED;
|
||||
import static java.lang.String.format;
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
|
||||
|
||||
|
||||
import bisq.apitest.config.ApiTestConfig;
|
||||
import bisq.apitest.config.BisqAppConfig;
|
||||
|
||||
public class GrpcStubs {
|
||||
|
||||
public final CallCredentials credentials;
|
||||
public final String host;
|
||||
public final int port;
|
||||
|
||||
public GetVersionGrpc.GetVersionBlockingStub versionService;
|
||||
public OffersGrpc.OffersBlockingStub offersService;
|
||||
public PaymentAccountsGrpc.PaymentAccountsBlockingStub paymentAccountsService;
|
||||
public WalletsGrpc.WalletsBlockingStub walletsService;
|
||||
|
||||
public GrpcStubs(BisqAppConfig bisqAppConfig, ApiTestConfig config) {
|
||||
this.credentials = new PasswordCallCredentials(config.apiPassword);
|
||||
this.host = "localhost";
|
||||
this.port = bisqAppConfig.apiPort;
|
||||
}
|
||||
|
||||
public GrpcStubs init() {
|
||||
var channel = ManagedChannelBuilder.forAddress(host, port).usePlaintext().build();
|
||||
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
|
||||
try {
|
||||
channel.shutdown().awaitTermination(1, SECONDS);
|
||||
} catch (InterruptedException ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}));
|
||||
|
||||
this.versionService = GetVersionGrpc.newBlockingStub(channel).withCallCredentials(credentials);
|
||||
this.offersService = OffersGrpc.newBlockingStub(channel).withCallCredentials(credentials);
|
||||
this.paymentAccountsService = PaymentAccountsGrpc.newBlockingStub(channel).withCallCredentials(credentials);
|
||||
this.walletsService = WalletsGrpc.newBlockingStub(channel).withCallCredentials(credentials);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
static class PasswordCallCredentials extends CallCredentials {
|
||||
|
||||
public static final String PASSWORD_KEY = "password";
|
||||
private final String passwordValue;
|
||||
|
||||
public PasswordCallCredentials(String passwordValue) {
|
||||
if (passwordValue == null)
|
||||
throw new IllegalArgumentException(format("'%s' value must not be null", PASSWORD_KEY));
|
||||
this.passwordValue = passwordValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyRequestMetadata(RequestInfo requestInfo,
|
||||
Executor appExecutor,
|
||||
MetadataApplier metadataApplier) {
|
||||
appExecutor.execute(() -> {
|
||||
try {
|
||||
var headers = new Metadata();
|
||||
var passwordKey = Metadata.Key.of(PASSWORD_KEY, ASCII_STRING_MARSHALLER);
|
||||
headers.put(passwordKey, passwordValue);
|
||||
metadataApplier.apply(headers);
|
||||
} catch (Throwable ex) {
|
||||
metadataApplier.fail(UNAUTHENTICATED.withCause(ex));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void thisUsesUnstableApi() {
|
||||
// An experimental api. A noop but never called; tries to make it clearer to
|
||||
// implementors that they may break in the future.
|
||||
}
|
||||
}
|
||||
}
|
58
apitest/src/test/java/bisq/apitest/JUnitHelper.java
Normal file
58
apitest/src/test/java/bisq/apitest/JUnitHelper.java
Normal file
|
@ -0,0 +1,58 @@
|
|||
package bisq.apitest;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.junit.runner.Description;
|
||||
import org.junit.runner.JUnitCore;
|
||||
import org.junit.runner.Result;
|
||||
import org.junit.runner.notification.Failure;
|
||||
import org.junit.runner.notification.RunListener;
|
||||
|
||||
import static java.lang.String.format;
|
||||
|
||||
@Slf4j
|
||||
public class JUnitHelper {
|
||||
|
||||
private static boolean allPass;
|
||||
|
||||
public static void runTests(Class<?>... testClasses) {
|
||||
JUnitCore jUnitCore = new JUnitCore();
|
||||
jUnitCore.addListener(new RunListener() {
|
||||
public void testStarted(Description description) {
|
||||
log.info("{}", description);
|
||||
}
|
||||
|
||||
public void testIgnored(Description description) {
|
||||
log.info("Ignored {}", description);
|
||||
}
|
||||
|
||||
public void testFailure(Failure failure) {
|
||||
log.error("Failed {}", failure.getTrace());
|
||||
}
|
||||
});
|
||||
Result result = jUnitCore.run(testClasses);
|
||||
printTestResults(result);
|
||||
}
|
||||
|
||||
public static void printTestResults(Result result) {
|
||||
log.info("Total tests: {}, Failed: {}, Ignored: {}",
|
||||
result.getRunCount(),
|
||||
result.getFailureCount(),
|
||||
result.getIgnoreCount());
|
||||
|
||||
if (result.wasSuccessful()) {
|
||||
log.info("All {} tests passed", result.getRunCount());
|
||||
allPass = true;
|
||||
} else if (result.getFailureCount() > 0) {
|
||||
log.error("{} test(s) failed", result.getFailureCount());
|
||||
result.getFailures().iterator().forEachRemaining(f -> log.error(format("%s.%s()%n\t%s",
|
||||
f.getDescription().getTestClass().getName(),
|
||||
f.getDescription().getMethodName(),
|
||||
f.getTrace())));
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean allTestsPassed() {
|
||||
return allPass;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.apitest.method;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import static java.lang.String.format;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
|
||||
|
||||
import bisq.apitest.config.ApiTestConfig;
|
||||
import bisq.apitest.linux.BitcoinCli;
|
||||
|
||||
public final class BitcoinCliHelper {
|
||||
|
||||
private final ApiTestConfig config;
|
||||
|
||||
public BitcoinCliHelper(ApiTestConfig config) {
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
// Convenience methods for making bitcoin-cli calls.
|
||||
|
||||
public String getNewBtcAddress() {
|
||||
try {
|
||||
String newAddress = new BitcoinCli(config, "getnewaddress").run().getOutput();
|
||||
assertNotNull(newAddress);
|
||||
return newAddress;
|
||||
} catch (IOException | InterruptedException ex) {
|
||||
fail(ex.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public String[] generateToAddress(int blocks, String address) {
|
||||
try {
|
||||
String generateToAddressCmd = format("generatetoaddress %d \"%s\"", blocks, address);
|
||||
BitcoinCli generateToAddress = new BitcoinCli(config, generateToAddressCmd).run();
|
||||
String[] txids = generateToAddress.getOutputValueAsStringArray();
|
||||
assertNotNull(txids);
|
||||
return txids;
|
||||
} catch (IOException | InterruptedException ex) {
|
||||
fail(ex.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void generateBlocks(int blocks) {
|
||||
generateToAddress(blocks, getNewBtcAddress());
|
||||
}
|
||||
|
||||
public String sendToAddress(String address, String amount) {
|
||||
// sendtoaddress "address" amount \
|
||||
// ( "comment" "comment_to" subtractfeefromamount \
|
||||
// replaceable conf_target "estimate_mode" )
|
||||
// returns a transaction id
|
||||
try {
|
||||
String sendToAddressCmd = format("sendtoaddress \"%s\" %s \"\" \"\" false",
|
||||
address, amount);
|
||||
BitcoinCli sendToAddress = new BitcoinCli(config, sendToAddressCmd).run();
|
||||
String txid = sendToAddress.getOutput();
|
||||
assertNotNull(txid);
|
||||
return txid;
|
||||
} catch (IOException | InterruptedException ex) {
|
||||
fail(ex.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.apitest.method;
|
||||
|
||||
import bisq.proto.grpc.GetBalanceRequest;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Order;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestMethodOrder;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
import static org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
|
||||
|
||||
|
||||
@Slf4j
|
||||
@TestMethodOrder(OrderAnnotation.class)
|
||||
public class GetBalanceTest extends MethodTest {
|
||||
|
||||
@BeforeAll
|
||||
public static void setUp() {
|
||||
try {
|
||||
setUpScaffold("bitcoind,seednode,alicedaemon");
|
||||
|
||||
// Have to generate 1 regtest block for alice's wallet to show 10 BTC balance.
|
||||
bitcoinCli.generateBlocks(1);
|
||||
|
||||
// Give the alicedaemon time to parse the new block.
|
||||
MILLISECONDS.sleep(1500);
|
||||
} catch (InterruptedException ex) {
|
||||
fail(ex.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(1)
|
||||
public void testGetBalance() {
|
||||
var balance = grpcStubs.walletsService.getBalance(GetBalanceRequest.newBuilder().build()).getBalance();
|
||||
assertEquals(1000000000, balance);
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
public static void tearDown() {
|
||||
tearDownScaffold();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.apitest.method;
|
||||
|
||||
import bisq.proto.grpc.GetVersionRequest;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Order;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestMethodOrder;
|
||||
|
||||
import static bisq.apitest.config.BisqAppConfig.alicedaemon;
|
||||
import static bisq.common.app.Version.VERSION;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
|
||||
|
||||
|
||||
@Slf4j
|
||||
@TestMethodOrder(OrderAnnotation.class)
|
||||
public class GetVersionTest extends MethodTest {
|
||||
|
||||
@BeforeAll
|
||||
public static void setUp() {
|
||||
setUpScaffold(alicedaemon.name());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(1)
|
||||
public void testGetVersion() {
|
||||
var version = grpcStubs.versionService.getVersion(GetVersionRequest.newBuilder().build()).getVersion();
|
||||
assertEquals(VERSION, version);
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
public static void tearDown() {
|
||||
tearDownScaffold();
|
||||
}
|
||||
}
|
89
apitest/src/test/java/bisq/apitest/method/MethodTest.java
Normal file
89
apitest/src/test/java/bisq/apitest/method/MethodTest.java
Normal file
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.apitest.method;
|
||||
|
||||
import bisq.proto.grpc.GetBalanceRequest;
|
||||
import bisq.proto.grpc.GetFundingAddressesRequest;
|
||||
import bisq.proto.grpc.LockWalletRequest;
|
||||
import bisq.proto.grpc.RemoveWalletPasswordRequest;
|
||||
import bisq.proto.grpc.SetWalletPasswordRequest;
|
||||
import bisq.proto.grpc.UnlockWalletRequest;
|
||||
|
||||
|
||||
|
||||
import bisq.apitest.ApiTestCase;
|
||||
|
||||
public class MethodTest extends ApiTestCase {
|
||||
|
||||
// Convenience methods for building gRPC request objects
|
||||
|
||||
protected final GetBalanceRequest createBalanceRequest() {
|
||||
return GetBalanceRequest.newBuilder().build();
|
||||
}
|
||||
|
||||
protected final SetWalletPasswordRequest createSetWalletPasswordRequest(String password) {
|
||||
return SetWalletPasswordRequest.newBuilder().setPassword(password).build();
|
||||
}
|
||||
|
||||
protected final SetWalletPasswordRequest createSetWalletPasswordRequest(String oldPassword, String newPassword) {
|
||||
return SetWalletPasswordRequest.newBuilder().setPassword(oldPassword).setNewPassword(newPassword).build();
|
||||
}
|
||||
|
||||
protected final RemoveWalletPasswordRequest createRemoveWalletPasswordRequest(String password) {
|
||||
return RemoveWalletPasswordRequest.newBuilder().setPassword(password).build();
|
||||
}
|
||||
|
||||
protected final UnlockWalletRequest createUnlockWalletRequest(String password, long timeout) {
|
||||
return UnlockWalletRequest.newBuilder().setPassword(password).setTimeout(timeout).build();
|
||||
}
|
||||
|
||||
protected final LockWalletRequest createLockWalletRequest() {
|
||||
return LockWalletRequest.newBuilder().build();
|
||||
}
|
||||
|
||||
protected final GetFundingAddressesRequest createGetFundingAddressesRequest() {
|
||||
return GetFundingAddressesRequest.newBuilder().build();
|
||||
}
|
||||
|
||||
// Convenience methods for calling frequently used & thoroughly tested gRPC services.
|
||||
|
||||
protected final long getBalance() {
|
||||
return grpcStubs.walletsService.getBalance(createBalanceRequest()).getBalance();
|
||||
}
|
||||
|
||||
protected final void unlockWallet(String password, long timeout) {
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
grpcStubs.walletsService.unlockWallet(createUnlockWalletRequest(password, timeout));
|
||||
}
|
||||
|
||||
protected final void lockWallet() {
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
grpcStubs.walletsService.lockWallet(createLockWalletRequest());
|
||||
}
|
||||
|
||||
protected final String getUnusedBtcAddress() {
|
||||
return grpcStubs.walletsService.getFundingAddresses(createGetFundingAddressesRequest())
|
||||
.getAddressBalanceInfoList()
|
||||
.stream()
|
||||
.filter(a -> a.getBalance() == 0 && a.getNumConfirmations() == 0)
|
||||
.findFirst()
|
||||
.get()
|
||||
.getAddress();
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,137 @@
|
|||
package bisq.apitest.method;
|
||||
|
||||
import io.grpc.StatusRuntimeException;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Order;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestMethodOrder;
|
||||
|
||||
import static bisq.apitest.config.BisqAppConfig.alicedaemon;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
|
||||
|
||||
@SuppressWarnings("ResultOfMethodCallIgnored")
|
||||
@Slf4j
|
||||
@TestMethodOrder(OrderAnnotation.class)
|
||||
public class WalletProtectionTest extends MethodTest {
|
||||
|
||||
@BeforeAll
|
||||
public static void setUp() {
|
||||
setUpScaffold(alicedaemon.name());
|
||||
try {
|
||||
TimeUnit.MILLISECONDS.sleep(2000);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(1)
|
||||
public void testSetWalletPassword() {
|
||||
var request = createSetWalletPasswordRequest("first-password");
|
||||
grpcStubs.walletsService.setWalletPassword(request);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(2)
|
||||
public void testGetBalanceOnEncryptedWalletShouldThrowException() {
|
||||
Throwable exception = assertThrows(StatusRuntimeException.class, this::getBalance);
|
||||
assertEquals("UNKNOWN: wallet is locked", exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(3)
|
||||
public void testUnlockWalletFor4Seconds() {
|
||||
var request = createUnlockWalletRequest("first-password", 4);
|
||||
grpcStubs.walletsService.unlockWallet(request);
|
||||
getBalance(); // should not throw 'wallet locked' exception
|
||||
|
||||
sleep(4500); // let unlock timeout expire
|
||||
Throwable exception = assertThrows(StatusRuntimeException.class, this::getBalance);
|
||||
assertEquals("UNKNOWN: wallet is locked", exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(4)
|
||||
public void testGetBalanceAfterUnlockTimeExpiryShouldThrowException() {
|
||||
var request = createUnlockWalletRequest("first-password", 3);
|
||||
grpcStubs.walletsService.unlockWallet(request);
|
||||
sleep(4000); // let unlock timeout expire
|
||||
Throwable exception = assertThrows(StatusRuntimeException.class, this::getBalance);
|
||||
assertEquals("UNKNOWN: wallet is locked", exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(5)
|
||||
public void testLockWalletBeforeUnlockTimeoutExpiry() {
|
||||
unlockWallet("first-password", 60);
|
||||
var request = createLockWalletRequest();
|
||||
grpcStubs.walletsService.lockWallet(request);
|
||||
|
||||
Throwable exception = assertThrows(StatusRuntimeException.class, this::getBalance);
|
||||
assertEquals("UNKNOWN: wallet is locked", exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(6)
|
||||
public void testLockWalletWhenWalletAlreadyLockedShouldThrowException() {
|
||||
var request = createLockWalletRequest();
|
||||
|
||||
Throwable exception = assertThrows(StatusRuntimeException.class, () ->
|
||||
grpcStubs.walletsService.lockWallet(request));
|
||||
assertEquals("UNKNOWN: wallet is already locked", exception.getMessage());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(7)
|
||||
public void testUnlockWalletTimeoutOverride() {
|
||||
unlockWallet("first-password", 2);
|
||||
sleep(500); // override unlock timeout after 0.5s
|
||||
unlockWallet("first-password", 6);
|
||||
sleep(5000);
|
||||
getBalance(); // getbalance 5s after resetting unlock timeout to 6s
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(8)
|
||||
public void testSetNewWalletPassword() {
|
||||
var request = createSetWalletPasswordRequest("first-password", "second-password");
|
||||
grpcStubs.walletsService.setWalletPassword(request);
|
||||
|
||||
unlockWallet("second-password", 2);
|
||||
getBalance();
|
||||
sleep(2500); // allow time for wallet save
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(9)
|
||||
public void testSetNewWalletPasswordWithIncorrectNewPasswordShouldThrowException() {
|
||||
var request = createSetWalletPasswordRequest("bad old password", "irrelevant");
|
||||
|
||||
Throwable exception = assertThrows(StatusRuntimeException.class, () ->
|
||||
grpcStubs.walletsService.setWalletPassword(request));
|
||||
assertEquals("UNKNOWN: incorrect old password", exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(10)
|
||||
public void testRemoveNewWalletPassword() {
|
||||
var request = createRemoveWalletPasswordRequest("second-password");
|
||||
grpcStubs.walletsService.removeWalletPassword(request);
|
||||
getBalance(); // should not throw 'wallet locked' exception
|
||||
}
|
||||
|
||||
|
||||
@AfterAll
|
||||
public static void tearDown() {
|
||||
tearDownScaffold();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.apitest.scenario;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
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 java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
|
||||
@Slf4j
|
||||
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
|
||||
public class FundWalletScenarioTest extends ScenarioTest {
|
||||
|
||||
@BeforeAll
|
||||
public static void setUp() {
|
||||
try {
|
||||
setUpScaffold("bitcoind,seednode,alicedaemon");
|
||||
bitcoinCli.generateBlocks(1);
|
||||
MILLISECONDS.sleep(1500);
|
||||
} catch (InterruptedException ex) {
|
||||
fail(ex.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(1)
|
||||
public void testFundWallet() {
|
||||
long balance = getBalance(); // bisq wallet was initialized with 10 btc
|
||||
assertEquals(1000000000, balance);
|
||||
|
||||
String unusedAddress = getUnusedBtcAddress();
|
||||
bitcoinCli.sendToAddress(unusedAddress, "2.5");
|
||||
|
||||
bitcoinCli.generateBlocks(1);
|
||||
sleep(1500);
|
||||
|
||||
balance = getBalance();
|
||||
assertEquals(1250000000L, balance); // new balance is 12.5 btc
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
public static void tearDown() {
|
||||
tearDownScaffold();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.apitest.scenario;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
|
||||
|
||||
import bisq.apitest.method.MethodTest;
|
||||
|
||||
@Slf4j
|
||||
public class ScenarioTest extends MethodTest {
|
||||
}
|
24
build.gradle
24
build.gradle
|
@ -579,6 +579,14 @@ configure(project(':apitest')) {
|
|||
// ./gradlew :apitest:cleanDaoSetup -x test
|
||||
apply from: 'dao-setup.gradle'
|
||||
|
||||
// We have to disable the :apitest 'test' task by default because we do not want
|
||||
// to interfere with normal builds. To run JUnit tests in this subproject:
|
||||
// Run a normal build and install dao-setup files:
|
||||
// 'gradle clean build :apitest:installDaoSetup'
|
||||
// Run apitest tests cases
|
||||
// 'gradle :apitest:test' -Dforce=true
|
||||
test.enabled = System.getProperty("force") == "true"
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
resources {
|
||||
|
@ -588,6 +596,13 @@ configure(project(':apitest')) {
|
|||
}
|
||||
}
|
||||
|
||||
test {
|
||||
useJUnitPlatform()
|
||||
testLogging {
|
||||
events "passed", "skipped", "failed"
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile project(':proto')
|
||||
compile project(':common')
|
||||
|
@ -609,10 +624,17 @@ configure(project(':apitest')) {
|
|||
implementation "org.slf4j:slf4j-api:$slf4jVersion"
|
||||
implementation "ch.qos.logback:logback-core:$logbackVersion"
|
||||
implementation "ch.qos.logback:logback-classic:$logbackVersion"
|
||||
implementation "junit:junit:$junitVersion"
|
||||
|
||||
compileOnly "org.projectlombok:lombok:$lombokVersion"
|
||||
compileOnly "javax.annotation:javax.annotation-api:$javaxAnnotationVersion"
|
||||
annotationProcessor "org.projectlombok:lombok:$lombokVersion"
|
||||
|
||||
testCompile "org.junit.jupiter:junit-jupiter-api:5.6.2"
|
||||
testCompile "org.junit.jupiter:junit-jupiter-params:5.6.2"
|
||||
testAnnotationProcessor "org.projectlombok:lombok:$lombokVersion"
|
||||
testCompileOnly "org.projectlombok:lombok:$lombokVersion"
|
||||
testRuntime "javax.annotation:javax.annotation-api:$javaxAnnotationVersion"
|
||||
testRuntime("org.junit.jupiter:junit-jupiter-engine:5.6.2")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue