diff --git a/api/README.md b/api/README.md
index 415ce6159..6ad04cb70 100644
--- a/api/README.md
+++ b/api/README.md
@@ -100,3 +100,19 @@ Some features will be not sufficiently tested and will only be enabled if you ad
--seedNodes=localhost:8000 --btcNodes=localhost:18445 --baseCurrencyNetwork=BTC_REGTEST --logLevel=info
--useDevPrivilegeKeys=true --bitcoinRegtestHost=NONE --myAddress=172.17.0.1:8003
--enableHttpApiExperimentalFeatures"
+
+## Integration tests
+
+Integration tests leverage Docker and run in headless mode. First you need to build docker images for the api:
+
+ cd api
+ docker-compose build
+ ../gradlew testIntegration
+
+IntelliJ Idea has awesome integration so just right click on `api/src/testIntegration` directory and select
+`Debug All Tests`.
+
+### Integration tests logging
+
+Due to Travis log length limitations the log level is set to WARN, but if you need to see more details locally
+go to `ContainerFactory` class and set `ENV_LOG_LEVEL_VALUE` property to `debug`.
diff --git a/api/src/main/java/bisq/api/http/service/endpoint/UserEndpoint.java b/api/src/main/java/bisq/api/http/service/endpoint/UserEndpoint.java
index bb16b0899..9bda23b95 100644
--- a/api/src/main/java/bisq/api/http/service/endpoint/UserEndpoint.java
+++ b/api/src/main/java/bisq/api/http/service/endpoint/UserEndpoint.java
@@ -29,6 +29,7 @@
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import javax.validation.Valid;
+import javax.validation.constraints.NotNull;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
@@ -54,7 +55,7 @@ public UserEndpoint(ApiPasswordManager apiPasswordManager) {
@Operation(summary = "Change password")
@POST
@Path("/password")
- public void changePassword(@Suspended AsyncResponse asyncResponse, @Valid ChangePassword data) {
+ public void changePassword(@Suspended AsyncResponse asyncResponse, @NotNull @Valid ChangePassword data) {
UserThread.execute(() -> {
try {
apiPasswordManager.changePassword(data.oldPassword, data.newPassword);
diff --git a/api/src/testIntegration/java/bisq/api/http/ApiTestHelper.java b/api/src/testIntegration/java/bisq/api/http/ApiTestHelper.java
new file mode 100644
index 000000000..42788b38c
--- /dev/null
+++ b/api/src/testIntegration/java/bisq/api/http/ApiTestHelper.java
@@ -0,0 +1,30 @@
+/*
+ * 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 .
+ */
+
+package bisq.api.http;
+
+@SuppressWarnings("WeakerAccess")
+public final class ApiTestHelper {
+
+ public static void waitForAllServicesToBeReady() throws InterruptedException {
+// TODO it would be nice to expose endpoint that would respond with 200
+ // PaymentMethod initializes it's static values after all services get initialized
+ int ALL_SERVICES_INITIALIZED_DELAY = 5000;
+ Thread.sleep(ALL_SERVICES_INITIALIZED_DELAY);
+ }
+
+}
diff --git a/api/src/testIntegration/java/bisq/api/http/ContainerFactory.java b/api/src/testIntegration/java/bisq/api/http/ContainerFactory.java
new file mode 100644
index 000000000..d7a67612d
--- /dev/null
+++ b/api/src/testIntegration/java/bisq/api/http/ContainerFactory.java
@@ -0,0 +1,101 @@
+/*
+ * 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 .
+ */
+
+package bisq.api.http;
+
+import org.arquillian.cube.docker.impl.client.config.Await;
+import org.arquillian.cube.docker.impl.client.containerobject.dsl.Container;
+import org.arquillian.cube.docker.impl.client.containerobject.dsl.ContainerBuilder;
+
+@SuppressWarnings("WeakerAccess")
+public final class ContainerFactory {
+
+ public static final String BITCOIN_NODE_CONTAINER_NAME = "bisq-api-bitcoin-node";
+ public static final String BITCOIN_NODE_HOST_NAME = "bitcoin";
+ public static final String SEED_NODE_CONTAINER_NAME = "bisq-seednode";
+ public static final String SEED_NODE_HOST_NAME = SEED_NODE_CONTAINER_NAME;
+ public static final String SEED_NODE_ADDRESS = SEED_NODE_HOST_NAME + ":8000";
+ public static final String CONTAINER_NAME_PREFIX = "bisq-api-";
+ public static final String API_IMAGE = "bisq/api";
+ public static final String ENV_NODE_PORT_KEY = "NODE_PORT";
+ public static final String ENV_ENABLE_HTTP_API_EXPERIMENTAL_FEATURES_KEY = "ENABLE_HTTP_API_EXPERIMENTAL_FEATURES";
+ public static final String ENV_HTTP_API_HOST_KEY = "HTTP_API_HOST";
+ public static final String ENV_HTTP_API_HOST_VALUE = "0.0.0.0";
+ public static final String ENV_USE_DEV_PRIVILEGE_KEYS_KEY = "USE_DEV_PRIVILEGE_KEYS";
+ public static final String ENV_USE_DEV_PRIVILEGE_KEYS_VALUE = "true";
+ public static final String ENV_USE_LOCALHOST_FOR_P2P_KEY = "USE_LOCALHOST_FOR_P2P";
+ public static final String ENV_USE_LOCALHOST_FOR_P2P_VALUE = "true";
+ public static final String ENV_BASE_CURRENCY_NETWORK_KEY = "BASE_CURRENCY_NETWORK";
+ public static final String ENV_BASE_CURRENCY_NETWORK_VALUE = "BTC_REGTEST";
+ public static final String ENV_BITCOIN_REGTEST_HOST_KEY = "BITCOIN_REGTEST_HOST";
+ public static final String ENV_BITCOIN_REGTEST_HOST_VALUE = "LOCALHOST";
+ public static final String ENV_BTC_NODES_KEY = "BTC_NODES";
+ public static final String ENV_BTC_NODES_VALUE = "bitcoin:18444";
+ public static final String ENV_SEED_NODES_KEY = "SEED_NODES";
+ public static final String ENV_SEED_NODES_VALUE = SEED_NODE_ADDRESS;
+ public static final String ENV_LOG_LEVEL_KEY = "LOG_LEVEL";
+ public static final String ENV_LOG_LEVEL_VALUE = "warn";
+
+ @SuppressWarnings("WeakerAccess")
+ public static ContainerBuilder.ContainerOptionsBuilder createApiContainerBuilder(String nameSuffix, String portBinding, int nodePort, boolean linkToSeedNode, boolean linkToBitcoin, boolean enableExperimentalFeatures) {
+ ContainerBuilder.ContainerOptionsBuilder containerOptionsBuilder = Container.withContainerName(CONTAINER_NAME_PREFIX + nameSuffix)
+ .fromImage(API_IMAGE)
+ .withPortBinding(portBinding)
+ .withEnvironment(ENV_NODE_PORT_KEY, nodePort)
+ .withEnvironment(ENV_HTTP_API_HOST_KEY, ENV_HTTP_API_HOST_VALUE)
+ .withEnvironment(ENV_ENABLE_HTTP_API_EXPERIMENTAL_FEATURES_KEY, enableExperimentalFeatures)
+ .withEnvironment(ENV_USE_DEV_PRIVILEGE_KEYS_KEY, ENV_USE_DEV_PRIVILEGE_KEYS_VALUE)
+ .withAwaitStrategy(getAwaitStrategy());
+ if (linkToSeedNode) {
+ containerOptionsBuilder.withLink(SEED_NODE_CONTAINER_NAME);
+ }
+ if (linkToBitcoin) {
+ containerOptionsBuilder.withLink(BITCOIN_NODE_CONTAINER_NAME, BITCOIN_NODE_HOST_NAME);
+ }
+ return withRegtestEnv(containerOptionsBuilder);
+ }
+
+ public static Await getAwaitStrategy() {
+ Await awaitStrategy = new Await();
+ awaitStrategy.setStrategy("polling");
+ int sleepPollingTime = 250;
+ awaitStrategy.setIterations(60000 / sleepPollingTime);
+ awaitStrategy.setSleepPollingTime(sleepPollingTime);
+ return awaitStrategy;
+ }
+
+ public static Container createApiContainer(String nameSuffix, String portBinding, int nodePort, boolean linkToSeedNode, boolean linkToBitcoin, boolean enableExperimentalFeatures) {
+ Container container = createApiContainerBuilder(nameSuffix, portBinding, nodePort, linkToSeedNode, linkToBitcoin, enableExperimentalFeatures).build();
+ container.getCubeContainer().setKillContainer(true);
+ return container;
+ }
+
+ public static Container createApiContainer(String nameSuffix, String portBinding, int nodePort, boolean linkToSeedNode, boolean linkToBitcoin) {
+ return createApiContainer(nameSuffix, portBinding, nodePort, linkToSeedNode, linkToBitcoin, true);
+ }
+
+ public static ContainerBuilder.ContainerOptionsBuilder withRegtestEnv(ContainerBuilder.ContainerOptionsBuilder builder) {
+ return builder
+ .withEnvironment(ENV_USE_LOCALHOST_FOR_P2P_KEY, ENV_USE_LOCALHOST_FOR_P2P_VALUE)
+ .withEnvironment(ENV_BASE_CURRENCY_NETWORK_KEY, ENV_BASE_CURRENCY_NETWORK_VALUE)
+ .withEnvironment(ENV_BITCOIN_REGTEST_HOST_KEY, ENV_BITCOIN_REGTEST_HOST_VALUE)
+ .withEnvironment(ENV_BTC_NODES_KEY, ENV_BTC_NODES_VALUE)
+ .withEnvironment(ENV_SEED_NODES_KEY, ENV_SEED_NODES_VALUE)
+ .withEnvironment(ENV_LOG_LEVEL_KEY, ENV_LOG_LEVEL_VALUE);
+ }
+
+}
diff --git a/api/src/testIntegration/java/bisq/api/http/SwaggerIT.java b/api/src/testIntegration/java/bisq/api/http/SwaggerIT.java
new file mode 100644
index 000000000..9cbf9c35b
--- /dev/null
+++ b/api/src/testIntegration/java/bisq/api/http/SwaggerIT.java
@@ -0,0 +1,73 @@
+/*
+ * 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 .
+ */
+
+package bisq.api.http;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static io.restassured.RestAssured.given;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.equalTo;
+
+
+
+import org.arquillian.cube.docker.impl.client.containerobject.dsl.Container;
+import org.arquillian.cube.docker.impl.client.containerobject.dsl.DockerContainer;
+import org.jboss.arquillian.junit.Arquillian;
+import org.jboss.arquillian.junit.InSequence;
+
+@RunWith(Arquillian.class)
+public class SwaggerIT {
+
+ @DockerContainer
+ private Container alice = ContainerFactory.createApiContainer("alice", "8081->8080", 3333, false, false);
+
+ @InSequence(1)
+ @Test
+ public void getDocs_always_returns200() {
+ given().
+ port(getAlicePort()).
+//
+ when().
+ get("/docs").
+//
+ then().
+ statusCode(200).
+ and().body(containsString("Swagger UI"))
+ ;
+ }
+
+ @InSequence(1)
+ @Test
+ public void getOpenApiJson_always_returns200() {
+ given().
+ port(getAlicePort()).
+//
+ when().
+ get("/openapi.json").
+//
+ then().
+ statusCode(200).
+ and().body("info.title", equalTo("Bisq HTTP API"));
+ }
+
+ private int getAlicePort() {
+ return alice.getBindPort(8080);
+ }
+
+}
diff --git a/api/src/testIntegration/java/bisq/api/http/UserEndpointIT.java b/api/src/testIntegration/java/bisq/api/http/UserEndpointIT.java
new file mode 100644
index 000000000..0e109d431
--- /dev/null
+++ b/api/src/testIntegration/java/bisq/api/http/UserEndpointIT.java
@@ -0,0 +1,214 @@
+/*
+ * 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 .
+ */
+
+package bisq.api.http;
+
+import bisq.api.http.model.ChangePassword;
+
+import bisq.common.util.Base64;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static io.restassured.RestAssured.given;
+import static org.hamcrest.Matchers.isEmptyString;
+
+
+
+import com.github.javafaker.Faker;
+import io.restassured.http.ContentType;
+import io.restassured.response.Response;
+import org.arquillian.cube.docker.impl.client.containerobject.dsl.Container;
+import org.arquillian.cube.docker.impl.client.containerobject.dsl.DockerContainer;
+import org.jboss.arquillian.junit.Arquillian;
+import org.jboss.arquillian.junit.InSequence;
+
+@RunWith(Arquillian.class)
+public class UserEndpointIT {
+
+ private static String validPassword = new Faker().internet().password();
+ private static String invalidPassword = getRandomPasswordDifferentThan(validPassword);
+ @DockerContainer
+ Container alice = ContainerFactory.createApiContainer("alice", "8081->8080", 3333, false, false);
+
+ private static String getRandomPasswordDifferentThan(String otherPassword) {
+ String newPassword;
+ do {
+ newPassword = new Faker().internet().password();
+ } while (otherPassword.equals(newPassword));
+ return newPassword;
+ }
+
+ @InSequence
+ @Test
+ public void waitForAllServicesToBeReady() throws InterruptedException {
+ ApiTestHelper.waitForAllServicesToBeReady();
+ verifyThatAuthenticationIsDisabled();
+ }
+
+ @InSequence(2)
+ @Test
+ public void changePassword_missingPayload_returns400() {
+ int alicePort = getAlicePort();
+ given().
+ port(alicePort).
+ contentType(ContentType.JSON).
+ accept(ContentType.JSON).
+//
+ when().
+ post("/api/v1/user/password").
+//
+ then().
+ statusCode(400).
+ and().body(isEmptyString());
+
+ verifyThatAuthenticationIsDisabled();
+ }
+
+ @InSequence(3)
+ @Test
+ public void changePassword_settingFirstPassword_enablesAuthentication() {
+ given().
+ port(getAlicePort()).
+ body(new ChangePassword(validPassword, null)).
+ contentType(ContentType.JSON).
+//
+ when().
+ post("/api/v1/user/password").
+//
+ then().
+ statusCode(204).
+ and().body(isEmptyString());
+
+ verifyThatAuthenticationIsEnabled();
+ verifyThatPasswordIsValid(validPassword);
+ }
+
+ @InSequence(4)
+ @Test
+ public void changePassword_invalidOldPassword_returns401() {
+ String newPassword = getRandomPasswordDifferentThan(validPassword);
+ given().
+ port(getAlicePort()).
+ body(new ChangePassword(newPassword, invalidPassword)).
+ contentType(ContentType.JSON).
+//
+ when().
+ post("/api/v1/user/password").
+//
+ then().
+ statusCode(401);
+
+ verifyThatAuthenticationIsEnabled();
+ verifyThatPasswordIsValid(validPassword);
+ verifyThatPasswordIsInvalid(newPassword);
+ }
+
+ @InSequence(4)
+ @Test
+ public void changePassword_emptyOldPassword_returns401() {
+ String newPassword = getRandomPasswordDifferentThan(validPassword);
+ given().
+ port(getAlicePort()).
+ body(new ChangePassword(newPassword, null)).
+ contentType(ContentType.JSON).
+//
+ when().
+ post("/api/v1/user/password").
+//
+ then().
+ statusCode(401);
+
+ verifyThatAuthenticationIsEnabled();
+ verifyThatPasswordIsValid(validPassword);
+ verifyThatPasswordIsInvalid(newPassword);
+ }
+
+ @InSequence(5)
+ @Test
+ public void changePassword_settingAnotherPassword_keepsAuthenticationEnabled() {
+ String oldPassword = validPassword;
+ String newPassword = getRandomPasswordDifferentThan(validPassword);
+ validPassword = newPassword;
+ invalidPassword = getRandomPasswordDifferentThan(validPassword);
+ given().
+ port(getAlicePort()).
+ body(new ChangePassword(newPassword, oldPassword)).
+ contentType(ContentType.JSON).
+//
+ when().
+ post("/api/v1/user/password").
+//
+ then().
+ statusCode(204);
+
+ verifyThatPasswordIsInvalid(oldPassword);
+ verifyThatPasswordIsValid(newPassword);
+ verifyThatAuthenticationIsEnabled();
+ }
+
+ @InSequence(6)
+ @Test
+ public void changePassword_validOldPasswordAndNoNewPassword_disablesAuthentication() {
+ given().
+ port(getAlicePort()).
+ body(new ChangePassword(null, validPassword)).
+ contentType(ContentType.JSON).
+//
+ when().
+ post("/api/v1/user/password").
+//
+ then().
+ statusCode(204);
+
+ verifyThatAuthenticationIsDisabled();
+ }
+
+ private void verifyThatAuthenticationIsDisabled() {
+ authenticationVerificationRequest().then().statusCode(200);
+ }
+
+ private void verifyThatAuthenticationIsEnabled() {
+ authenticationVerificationRequest().then().statusCode(401);
+ }
+
+ private void verifyThatPasswordIsInvalid(String password) {
+ passwordVerificationRequest(password).then().statusCode(401);
+ }
+
+ private void verifyThatPasswordIsValid(String password) {
+ passwordVerificationRequest(password).then().statusCode(200);
+ }
+
+ private Response authenticationVerificationRequest() {
+ return given().port(getAlicePort()).when().get("/api/v1/version");
+ }
+
+ private Response passwordVerificationRequest(String password) {
+ String authHeader = "Basic " + Base64.encode((":" + password).getBytes());
+ return given().port(getAlicePort()).
+//
+ when().
+ header("authorization", authHeader).
+ get("/api/v1/version");
+ }
+
+ private int getAlicePort() {
+ return alice.getBindPort(8080);
+ }
+
+}
diff --git a/api/src/testIntegration/java/bisq/api/http/VersionEndpointIT.java b/api/src/testIntegration/java/bisq/api/http/VersionEndpointIT.java
new file mode 100644
index 000000000..0ab143347
--- /dev/null
+++ b/api/src/testIntegration/java/bisq/api/http/VersionEndpointIT.java
@@ -0,0 +1,70 @@
+/*
+ * 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 .
+ */
+
+package bisq.api.http;
+
+import bisq.common.app.Version;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static io.restassured.RestAssured.given;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.isA;
+
+
+
+import org.arquillian.cube.docker.impl.client.containerobject.dsl.Container;
+import org.arquillian.cube.docker.impl.client.containerobject.dsl.DockerContainer;
+import org.jboss.arquillian.junit.Arquillian;
+import org.jboss.arquillian.junit.InSequence;
+
+@RunWith(Arquillian.class)
+public class VersionEndpointIT {
+
+ @DockerContainer
+ private Container alice = ContainerFactory.createApiContainer("alice", "8081->8080", 3333, false, false);
+
+ @InSequence
+ @Test
+ public void waitForAllServicesToBeReady() throws InterruptedException {
+ ApiTestHelper.waitForAllServicesToBeReady();
+ }
+
+ @InSequence(1)
+ @Test
+ public void getVersionDetails_always_returns200() {
+ given().
+ port(getAlicePort()).
+//
+ when().
+ get("/api/v1/version").
+//
+ then().
+ statusCode(200).
+ and().body("application", equalTo(Version.VERSION)).
+ and().body("network", equalTo(Version.P2P_NETWORK_VERSION)).
+ and().body("p2PMessage", isA(Integer.class)).
+ and().body("localDB", equalTo(Version.LOCAL_DB_VERSION)).
+ and().body("tradeProtocol", equalTo(Version.TRADE_PROTOCOL_VERSION));
+ }
+
+ private int getAlicePort() {
+ return alice.getBindPort(8080);
+ }
+
+}
diff --git a/api/src/testIntegration/java/bisq/api/http/arquillian/CubeLogger.java b/api/src/testIntegration/java/bisq/api/http/arquillian/CubeLogger.java
new file mode 100644
index 000000000..5d1b9f53e
--- /dev/null
+++ b/api/src/testIntegration/java/bisq/api/http/arquillian/CubeLogger.java
@@ -0,0 +1,47 @@
+/*
+ * 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 .
+ */
+
+package bisq.api.http.arquillian;
+
+import org.arquillian.cube.CubeController;
+import org.arquillian.cube.spi.event.lifecycle.BeforeStop;
+import org.jboss.arquillian.config.descriptor.api.ArquillianDescriptor;
+import org.jboss.arquillian.core.api.annotation.Observes;
+import org.jboss.arquillian.test.spi.TestClass;
+
+public class CubeLogger {
+
+ private static boolean isExtensionEnabled(ArquillianDescriptor arquillianDescriptor) {
+ String dumpContainerLogs = arquillianDescriptor.extension("cubeLogger").getExtensionProperty("enable");
+ return Boolean.parseBoolean(dumpContainerLogs);
+ }
+
+ @SuppressWarnings({"unused", "UnusedParameters"})
+ public void beforeContainerStop(@Observes BeforeStop event, CubeController cubeController, ArquillianDescriptor arquillianDescriptor, TestClass testClass) {
+ if (isExtensionEnabled(arquillianDescriptor)) {
+ String cubeId = event.getCubeId();
+ System.out.println("=====================================================================================");
+ System.out.println("Start of container logs: " + cubeId + " from " + testClass.getName());
+ System.out.println("=====================================================================================");
+ cubeController.copyLog(cubeId, false, true, true, true, -1, System.out);
+ System.out.println("=====================================================================================");
+ System.out.println("End of container logs: " + cubeId + " from " + testClass.getName());
+ System.out.println("=====================================================================================");
+ }
+ }
+
+}
diff --git a/api/src/testIntegration/java/bisq/api/http/arquillian/CubeLoggerExtension.java b/api/src/testIntegration/java/bisq/api/http/arquillian/CubeLoggerExtension.java
new file mode 100644
index 000000000..a7f342709
--- /dev/null
+++ b/api/src/testIntegration/java/bisq/api/http/arquillian/CubeLoggerExtension.java
@@ -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 .
+ */
+
+package bisq.api.http.arquillian;
+
+import org.jboss.arquillian.core.spi.LoadableExtension;
+
+public class CubeLoggerExtension implements LoadableExtension {
+
+ @Override
+ public void register(ExtensionBuilder extensionBuilder) {
+ extensionBuilder.observer(CubeLogger.class);
+ }
+}
diff --git a/api/src/testIntegration/resources/META-INF/services/org.jboss.arquillian.core.spi.LoadableExtension b/api/src/testIntegration/resources/META-INF/services/org.jboss.arquillian.core.spi.LoadableExtension
new file mode 100644
index 000000000..2f2f5d333
--- /dev/null
+++ b/api/src/testIntegration/resources/META-INF/services/org.jboss.arquillian.core.spi.LoadableExtension
@@ -0,0 +1 @@
+bisq.api.http.arquillian.CubeLoggerExtension
diff --git a/api/src/testIntegration/resources/arquillian.xml b/api/src/testIntegration/resources/arquillian.xml
new file mode 100644
index 000000000..45de142b0
--- /dev/null
+++ b/api/src/testIntegration/resources/arquillian.xml
@@ -0,0 +1,13 @@
+
+
+
+
+ CUBE
+ unix:///var/run/docker.sock
+ false
+
+
+ true
+
+
diff --git a/api/src/testIntegration/resources/logback-test.xml b/api/src/testIntegration/resources/logback-test.xml
new file mode 100644
index 000000000..d26ff1e63
--- /dev/null
+++ b/api/src/testIntegration/resources/logback-test.xml
@@ -0,0 +1,15 @@
+
+
+
+
+ %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
+
+
+
+
+
+
+
+
+
+
diff --git a/build.gradle b/build.gradle
index 29f5553c8..270750602 100644
--- a/build.gradle
+++ b/build.gradle
@@ -305,8 +305,47 @@ configure(project(':api')) {
testAnnotationProcessor 'org.projectlombok:lombok:1.18.2'
testCompile "com.github.javafaker:javafaker:0.14"
testCompile "org.apache.commons:commons-lang3:$langVersion"
+ testCompile "org.arquillian.universe:arquillian-junit:1.2.0.1"
+ testCompile "org.arquillian.universe:arquillian-cube-docker:1.2.0.1"
+ testCompile "org.arquillian.cube:arquillian-cube-docker:1.15.3"
+ testCompile "io.rest-assured:rest-assured:3.0.2"
testCompile 'io.swagger.core.v3:swagger-jaxrs2:2.0.6' //needed to generate documentation using SwaggerGenerator
}
+
+ sourceSets {
+ testIntegration {
+ java.srcDir 'src/testIntegration/java'
+ resources.srcDir 'src/testIntegration/resources'
+ compileClasspath += sourceSets.main.output + configurations.testRuntimeClasspath
+ runtimeClasspath += output + compileClasspath
+ }
+ }
+
+ task testIntegration(type: Test) {
+ group = LifecycleBasePlugin.VERIFICATION_GROUP
+ description = 'Runs the integration tests.'
+
+ maxHeapSize = '1024m'
+
+ testClassesDir = sourceSets.testIntegration.output.classesDir
+ classpath = sourceSets.testIntegration.runtimeClasspath
+
+ binResultsDir = file("$buildDir/api/integration-test-results/binary/testIntegration")
+
+ reports {
+ html.destination = "$buildDir/api/reports/integration-test"
+ junitXml.destination = "$buildDir/api/integration-test-results"
+ }
+
+ systemProperties = [
+ CUBE_LOGGER_ENABLE: System.getenv('CUBE_LOGGER_ENABLE')
+ ]
+
+ testLogging.showStandardStreams = true
+ testLogging.exceptionFormat = 'full'
+
+ mustRunAfter tasks.test
+ }
}