From 3c07308ae7507e9f355b34b2f0110982f5f7ee7c Mon Sep 17 00:00:00 2001 From: Konstantin Silaev Date: Mon, 7 Oct 2019 16:57:28 +0300 Subject: [PATCH 01/38] Implement MongoDbContainer --- .travis.yml | 11 + docs/modules/databases/mongodb.md | 135 ++++++++++ mkdocs.yml | 1 + modules/mongodb/build.gradle | 47 ++++ .../containers/MongoDbContainer.java | 253 ++++++++++++++++++ .../containers/MongoDbContainerTest.java | 85 ++++++ .../containers/core/IntegrationTest.java | 14 + .../containers/integration/ITTest.java | 29 ++ .../integration/InitializationITTest.java | 101 +++++++ .../integration/TransactionITTest.java | 77 ++++++ .../util/SubscriberHelperUtils.java | 154 +++++++++++ .../src/test/resources/logback-test.xml | 16 ++ .../org.mockito.plugins.MockMaker | 1 + 13 files changed, 924 insertions(+) create mode 100644 docs/modules/databases/mongodb.md create mode 100644 modules/mongodb/build.gradle create mode 100644 modules/mongodb/src/main/java/org/testcontainers/containers/MongoDbContainer.java create mode 100644 modules/mongodb/src/test/java/org/testcontainers/containers/MongoDbContainerTest.java create mode 100644 modules/mongodb/src/test/java/org/testcontainers/containers/core/IntegrationTest.java create mode 100644 modules/mongodb/src/test/java/org/testcontainers/containers/integration/ITTest.java create mode 100644 modules/mongodb/src/test/java/org/testcontainers/containers/integration/InitializationITTest.java create mode 100644 modules/mongodb/src/test/java/org/testcontainers/containers/integration/TransactionITTest.java create mode 100644 modules/mongodb/src/test/java/org/testcontainers/containers/util/SubscriberHelperUtils.java create mode 100644 modules/mongodb/src/test/resources/logback-test.xml create mode 100644 modules/mongodb/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker diff --git a/.travis.yml b/.travis.yml index d7d5f94270d..7532d96c1d3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -51,6 +51,17 @@ jobs: openjdk:8-jdk-alpine \ ./gradlew --no-daemon testcontainers:test --tests '*GenericContainerRuleTest' --scan + - env: [ NAME=mongodb ] + script: + - | + DOCKER_HOST=unix:///var/run/docker.sock DOCKER_TLS_VERIFY= docker run -t --rm \ + -v "$HOME/.gradle":/root/.gradle/ \ + -v /var/run/docker.sock:/var/run/docker.sock \ + -v "$(pwd)":"$(pwd)" \ + -w "$(pwd)" \ + openjdk:8-jdk-alpine \ + ./gradlew mongodb:integrationTest --scan --no-daemon + - stage: deploy sudo: false services: [] diff --git a/docs/modules/databases/mongodb.md b/docs/modules/databases/mongodb.md new file mode 100644 index 00000000000..bba47a57b23 --- /dev/null +++ b/docs/modules/databases/mongodb.md @@ -0,0 +1,135 @@ +# Java8 MongoDbContainer to constructs a single node MongoDB replica set + +#### Prerequisite +- Java 8+ +- Docker Desktop +- Chart shows support for local and remote Docker + + local docker host | local docker host running tests from inside a container with mapping the Docker socket | remote docker daemon | + |:---: | :---: | :---: | + | + | + | + | + +Tips: +- Adding this Testcontainers library JAR will not automatically add a database driver JAR to your project. You should ensure that your project also has a suitable database driver as a dependency +- To construct a multi-node MongoDB cluster, consider [mongodb-replica-set project](https://github.com/silaev/mongodb-replica-set/) +- To use remote Docker daemon: + - check that [.testcontainers.properties](https://www.testcontainers.org/features/configuration/) file has `docker.client.strategy=org.testcontainers.dockerclient.EnvironmentAndSystemPropertyClientProviderStrategy` + - set your DOCKER_HOST environment variable + +#### Getting it +```groovy tab='Gradle' +testCompile "org.testcontainers:mongodb:{{latest_version}}" +``` + +```xml tab='Maven' + + org.testcontainers + mongodb + {{latest_version}} + test + +``` +To see logs, consider adding a slf4j implementation (Logback is recommended) if you don't have it in your application. + +#### MongoDB versions that MongoDbContainer is constantly tested against +version | +---------- | +4.0.12 | +4.2.0 | + +#### Example usage (note that the MongoDbContainer is test framework agnostic) +The example of a JUnit5 test class: +```java +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.testcontainers.mongodb.MongoDbContainer; + +import static org.junit.jupiter.api.Assertions.assertNotNull; + +class ITTest { + private final MongoDbContainer mongoDbContainer = new MongoDbContainer( + //"mongo:4.2.0" + ); + + @BeforeEach + void setUp() { + mongoDbContainer.start(); + } + + @AfterEach + void tearDown() { + mongoDbContainer.stop(); + } + + @Test + void shouldTestReplicaSetUrl() { + assertNotNull(mongoDbContainer.getReplicaSetUrl()); + } +} +``` +The example of a JUnit5 test class in a Spring Boot + Spring Data application: +```java +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.util.TestPropertyValues; +import org.springframework.context.ApplicationContextInitializer; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.test.context.ContextConfiguration; +import org.testcontainers.mongodb.MongoDbContainer; + +import static org.junit.jupiter.api.Assertions.assertNotNull; + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +//@DataMongoTest +@ContextConfiguration(initializers = ITTest.Initializer.class) +class ITTest { + private static final MongoDbContainer MONGO_DB_CONTAINER = new MongoDbContainer( + //"mongo:4.2.0" + ); + + @BeforeAll + static void setUp() { + MONGO_DB_CONTAINER.start(); + } + + @AfterAll + static void tearDown() { + MONGO_DB_CONTAINER.stop(); + } + + @Test + void shouldTestReplicaSetUrl() { + assertNotNull(MONGO_DB_CONTAINER.getReplicaSetUrl()); + } + + static class Initializer implements ApplicationContextInitializer { + @Override + public void initialize(ConfigurableApplicationContext configurableApplicationContext) { + TestPropertyValues.of( + String.format("spring.data.mongodb.uri: %s", MONGO_DB_CONTAINER.getReplicaSetUrl()) + ).applyTo(configurableApplicationContext); + } + } +} +``` + +#### Motivation +Implement a reusable, cross-platform, simple to install solution that doesn't depend on +fixed ports to test MongoDB transactions. The solution should work with local and remote Docker daemon. + +#### General info +MongoDB starting form version 4 supports multi-document transactions only for a replica set. +For instance, to initialize a single and simple node replica set on fixed ports via Docker, one has to do the following: +1) Run a MongoDB container of version 4 and up specifying --replSet command +2) Initializing a single replica set via executing a proper command (depending on local or remote Docker daemon usage) +3) Waiting for the initialization to complete +4) Providing a special url (without the need to modify the OS host file) for a user to employ with a MongoDB driver without specifying replicaSet + +As we can see, there is a lot of operations to execute and we even haven't touched a non-fixed port approach. +That's where the MongoDbContainer might come in handy. + +#### Copyright +Copyright (c) 2019 Konstantin Silaev diff --git a/mkdocs.yml b/mkdocs.yml index 976c1b7c95f..e8ab9994571 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -52,6 +52,7 @@ nav: - modules/databases/neo4j.md - modules/databases/oraclexe.md - modules/databases/postgres.md + - modules/databases/mongodb.md - modules/docker_compose.md - modules/elasticsearch.md - modules/kafka.md diff --git a/modules/mongodb/build.gradle b/modules/mongodb/build.gradle new file mode 100644 index 00000000000..23623cb2875 --- /dev/null +++ b/modules/mongodb/build.gradle @@ -0,0 +1,47 @@ +description = "Testcontainers :: MongoDB" + +buildscript { + ext { + junitVersion = '5.5.1' + } +} + +dependencies { + compile project(':testcontainers') + + testCompile("org.junit.jupiter:junit-jupiter-api:${junitVersion}") + testCompile('org.mockito:mockito-core:3.0.0') + testCompile('ch.qos.logback:logback-classic:1.2.3') + testRuntime("org.junit.jupiter:junit-jupiter-engine:${junitVersion}") + testCompile("org.mongodb:mongodb-driver-reactivestreams:1.12.0") + testCompile("org.mongodb:mongodb-driver-sync:3.11.0") + testCompile("org.mockito:mockito-junit-jupiter:3.0.0") +} + +test { + failFast = true +} + +tasks.withType(Test) { Test task -> + task.useJUnitPlatform { JUnitPlatformOptions ops -> + ops.excludeTags("integration-test") + } + + testLogging.showStandardStreams = true + testLogging.exceptionFormat = 'full' +} + +task integrationTest(type: Test) { Test task -> + task.useJUnitPlatform { JUnitPlatformOptions ops -> + ops.includeTags("integration-test") + } + + testLogging.showStandardStreams = true + testLogging.exceptionFormat = 'full' + + task.minHeapSize('32m') + task.maxHeapSize('256m') + + check.dependsOn integrationTest + integrationTest.mustRunAfter test +} diff --git a/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDbContainer.java b/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDbContainer.java new file mode 100644 index 00000000000..aded1f42a22 --- /dev/null +++ b/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDbContainer.java @@ -0,0 +1,253 @@ +package org.testcontainers.containers; + +import lombok.NonNull; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import lombok.val; +import org.testcontainers.containers.wait.strategy.Wait; + +import java.io.IOException; + +/** + * Constructs a single node MongoDB replica set for testing transactions. + *

To construct a multi-node MongoDB cluster, consider mongodb-replica-set project on GitHub + * + *

+ * + * + * + * + * + * + * + * + * + *
Chart shows a pattern for local and remote Docker support
local docker host + * local docker host running tests from inside a container with mapping the Docker socket + * remote docker daemon + *
+ + * + + * + + *
+ *
+ *

+ * Tested on a Mongo DB version 4.0.10 (that is the default version if not specified) and up. + * + *

Example usage (note that the MongoDbContainer is test framework agnostic)

+ *

The example of a JUnit5 test class: + *

+ * import org.junit.jupiter.api.AfterEach;
+ * import org.junit.jupiter.api.BeforeEach;
+ * import org.junit.jupiter.api.Test;
+ * import org.testcontainers.mongodb.MongoDbContainer;
+ *
+ * import static org.junit.jupiter.api.Assertions.assertNotNull;
+ *
+ * class ITTest {
+ *     private final MongoDbContainer mongoDbContainer = new MongoDbContainer(
+ *         //"mongo:4.2.0"
+ *     );
+ *
+ *     {@literal @}BeforeEach
+ *     void setUp() {
+ *         mongoDbContainer.start();
+ *     }
+ *
+ *     {@literal @}AfterEach
+ *     void tearDown() {
+ *         mongoDbContainer.stop();
+ *     }
+ *
+ *     {@literal @}Test
+ *     void shouldTestReplicaSetUrl() {
+ *         assertNotNull(mongoDbContainer.getReplicaSetUrl());
+ *     }
+ * }
+ * 
+ * + *

The example of a SpringBoot+SpringData test with JUnit5: + *

+ * import org.junit.jupiter.api.AfterAll;
+ * import org.junit.jupiter.api.BeforeAll;
+ * import org.junit.jupiter.api.Test;
+ * import org.springframework.boot.test.context.SpringBootTest;
+ * import org.springframework.boot.test.util.TestPropertyValues;
+ * import org.springframework.context.ApplicationContextInitializer;
+ * import org.springframework.context.ConfigurableApplicationContext;
+ * import org.springframework.test.context.ContextConfiguration;
+ * import org.testcontainers.mongodb.MongoDbContainer;
+ *
+ * import static org.junit.jupiter.api.Assertions.assertNotNull;
+ *
+ * {@literal @}SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
+ * //@DataMongoTest
+ * {@literal @}ContextConfiguration(initializers = ITTest.Initializer.class)
+ * class ITTest {
+ *     private static final MongoDbContainer MONGO_DB_CONTAINER = new MongoDbContainer(
+ *             //"mongo:4.2.0"
+ *     );
+ *
+ *     {@literal @}BeforeAll
+ *     static void setUp() {
+ *         MONGO_DB_CONTAINER.start();
+ *     }
+ *
+ *     {@literal @}AfterAll
+ *     static void tearDown() {
+ *         MONGO_DB_CONTAINER.stop();
+ *     }
+ *
+ *     {@literal @}Test
+ *     void shouldTestReplicaSetUrl() {
+ *         assertNotNull(MONGO_DB_CONTAINER.getReplicaSetUrl());
+ *     }
+ *
+ *     static class Initializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
+ *        {@literal @}@Override
+ *         public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
+ *             TestPropertyValues.of(
+ *                     String.format("spring.data.mongodb.uri: %s", MONGO_DB_CONTAINER.getReplicaSetUrl())
+ *             ).applyTo(configurableApplicationContext);
+ *         }
+ *     }
+ * }
+ * 
+ * + * @author Konstantin Silaev on 9/30/2019 + */ +@Slf4j +public class MongoDbContainer extends GenericContainer { + static final int ERROR_CONTAINER_EXIT_CODE = 1; + static final int MONGO_DB_INTERNAL_PORT = 27017; + private static final int AWAIT_INIT_REPLICA_SET_ATTEMPTS = 30; + private static final String MONGODB_VERSION_DEFAULT = "4.0.10"; + private static final String LOCALHOST = "localhost"; + + public MongoDbContainer() { + super("mongo:" + MONGODB_VERSION_DEFAULT); + } + + public MongoDbContainer(@NonNull final String dockerImageName) { + super(dockerImageName); + } + + public String getReplicaSetUrl() { + if (!isRunning()) { + throw new IllegalStateException( + String.format( + "Please, start %s first", MongoDbContainer.class.getCanonicalName()) + ); + } + return String.format( + "mongodb://%s:%d/test", + getContainerIpAddress(), getMappedPort(MONGO_DB_INTERNAL_PORT) + ); + } + + @Override + public void start() { + super.start(); + initReplicaSet(); + logReplicaSetStatus(); + } + + @SneakyThrows(value = {IOException.class, InterruptedException.class}) + private void logReplicaSetStatus() { + log.debug( + "REPLICA SET STATUS:\n{}", + execInContainer(buildMongoEvalCommand("rs.status()")).getStdout() + ); + } + + @Override + protected void configure() { + withExposedPorts(MONGO_DB_INTERNAL_PORT); + withCommand("--replSet", "docker-rs"); + waitingFor( + Wait.forLogMessage(".*waiting for connections on port.*", 1) + ); + } + + private String getMongoReplicaSetInitializer() { + val containerIpAddress = getContainerIpAddress(); + val containerPort = LOCALHOST.equals(containerIpAddress) + ? MONGO_DB_INTERNAL_PORT + : getMappedPort(MONGO_DB_INTERNAL_PORT); + val initializer = String.format( + "rs.initiate({\n" + + " \"_id\": \"docker-rs\",\n" + + " \"members\": [\n" + + " {\"_id\": %d, \"host\": \"%s:%d\"}\n ]\n});", + 0, containerIpAddress, containerPort + ); + log.debug(initializer); + return initializer; + } + + private String[] buildMongoEvalCommand(final String command) { + return new String[]{"mongo", "--eval", command}; + } + + private void checkMongoNodeExitCode(final Container.ExecResult execResult) { + if (execResult.getExitCode() == ERROR_CONTAINER_EXIT_CODE) { + val errorMessage = String.format("An error occurred: %s", execResult.getStderr()); + log.error(errorMessage); + throw new ReplicaSetInitializationException(errorMessage); + } + } + + private String buildMongoWaitCommand() { + return String.format( + "var attempt = 0; " + + "while" + + "(%s) " + + "{ " + + "if (attempt > %d) {quit(1);} " + + "print('%s ' + attempt); sleep(1000); attempt++; " + + " }", + "db.runCommand( { isMaster: 1 } ).ismaster==false", + AWAIT_INIT_REPLICA_SET_ATTEMPTS, + "An attempt to await for a single node replica set initialization:" + ); + } + + private void checkMongoNodeExitCodeAfterWaiting( + final Container.ExecResult execResultWaitForMaster + ) { + if (execResultWaitForMaster.getExitCode() == ERROR_CONTAINER_EXIT_CODE) { + val errorMessage = String.format( + "A single node replica set was not initialized in a set timeout: %d attempts", + AWAIT_INIT_REPLICA_SET_ATTEMPTS + ); + log.error(errorMessage); + throw new ReplicaSetInitializationException(errorMessage); + } + } + + @SneakyThrows(value = {IOException.class, InterruptedException.class}) + void initReplicaSet() { + log.debug("Initializing a single node node replica set..."); + val execResultInitRs = execInContainer( + buildMongoEvalCommand(getMongoReplicaSetInitializer()) + ); + log.debug(execResultInitRs.getStdout()); + checkMongoNodeExitCode(execResultInitRs); + + log.debug( + "Awaiting for a single node replica set initialization up to {} attempts", + AWAIT_INIT_REPLICA_SET_ATTEMPTS + ); + val execResultWaitForMaster = execInContainer( + buildMongoEvalCommand(buildMongoWaitCommand()) + ); + log.debug(execResultWaitForMaster.getStdout()); + + checkMongoNodeExitCodeAfterWaiting(execResultWaitForMaster); + } + + public static class ReplicaSetInitializationException extends RuntimeException { + ReplicaSetInitializationException(final String errorMessage) { + super(errorMessage); + } + } +} diff --git a/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDbContainerTest.java b/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDbContainerTest.java new file mode 100644 index 00000000000..2c7972a70b9 --- /dev/null +++ b/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDbContainerTest.java @@ -0,0 +1,85 @@ +package org.testcontainers.containers; + +import lombok.val; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.function.Executable; +import org.mockito.Spy; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.io.IOException; + +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.lenient; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +/** + * Has an overhead of connecting to Docker and running Ryuk + * only once thanks to a static class member variable MONGODB_CONTAINER. + * + * @author Konstantin Silaev on 9/30/2019 + */ +@ExtendWith(MockitoExtension.class) +class MongoDbContainerTest { + @Spy + private static MongoDbContainer MONGODB_CONTAINER = new MongoDbContainer(); + + @Test + void shouldNotGetReplicaSetUrlBecauseOfNotStartedContainer() { + //WHEN + Executable executable = () -> MONGODB_CONTAINER.getReplicaSetUrl(); + + //THEN + assertThrows(IllegalStateException.class, executable); + } + + @Test + void shouldNotInitReplicaSetBecauseOfExecInitReplicaSet() + throws IOException, InterruptedException { + //GIVEN + val mockExecResult = mock(Container.ExecResult.class); + when(mockExecResult.getExitCode()) + .thenReturn(MongoDbContainer.ERROR_CONTAINER_EXIT_CODE); + doReturn(mockExecResult).when(MONGODB_CONTAINER).execInContainer(any()); + val mappedPort = 37723; + lenient().doReturn(mappedPort).when(MONGODB_CONTAINER) + .getMappedPort(MongoDbContainer.MONGO_DB_INTERNAL_PORT); + + //WHEN + Executable executable = () -> MONGODB_CONTAINER.initReplicaSet(); + + //THEN + assertThrows( + MongoDbContainer.ReplicaSetInitializationException.class, + executable + ); + } + + @Test + void shouldNotInitReplicaSetBecauseOfWaitCommand() throws IOException, InterruptedException { + //GIVEN + val mockExecResult1 = mock(Container.ExecResult.class); + val mockExecResult2 = mock(Container.ExecResult.class); + when(mockExecResult1.getExitCode()) + .thenReturn(0); + when(mockExecResult2.getExitCode()) + .thenReturn(MongoDbContainer.ERROR_CONTAINER_EXIT_CODE); + doReturn(mockExecResult1, mockExecResult2) + .when(MONGODB_CONTAINER).execInContainer(any()); + val mappedPort = 37723; + lenient().doReturn(mappedPort).when(MONGODB_CONTAINER) + .getMappedPort(MongoDbContainer.MONGO_DB_INTERNAL_PORT); + + //WHEN + Executable executable = () -> MONGODB_CONTAINER.initReplicaSet(); + + //THEN + assertThrows( + MongoDbContainer.ReplicaSetInitializationException.class, + executable + ); + } +} diff --git a/modules/mongodb/src/test/java/org/testcontainers/containers/core/IntegrationTest.java b/modules/mongodb/src/test/java/org/testcontainers/containers/core/IntegrationTest.java new file mode 100644 index 00000000000..2c7c17d9d09 --- /dev/null +++ b/modules/mongodb/src/test/java/org/testcontainers/containers/core/IntegrationTest.java @@ -0,0 +1,14 @@ +package org.testcontainers.containers.core; + +import org.junit.jupiter.api.Tag; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +@Tag("integration-test") +public @interface IntegrationTest { +} diff --git a/modules/mongodb/src/test/java/org/testcontainers/containers/integration/ITTest.java b/modules/mongodb/src/test/java/org/testcontainers/containers/integration/ITTest.java new file mode 100644 index 00000000000..8d6a99283f1 --- /dev/null +++ b/modules/mongodb/src/test/java/org/testcontainers/containers/integration/ITTest.java @@ -0,0 +1,29 @@ +package org.testcontainers.containers.integration; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.testcontainers.containers.MongoDbContainer; + +import static org.junit.jupiter.api.Assertions.assertNotNull; + +class ITTest { + private final MongoDbContainer mongoDbContainer = new MongoDbContainer( + //"mongo:4.2.0" + ); + + @BeforeEach + void setUp() { + mongoDbContainer.start(); + } + + @AfterEach + void tearDown() { + mongoDbContainer.stop(); + } + + @Test + void shouldTestReplicaSetUrl() { + assertNotNull(mongoDbContainer.getReplicaSetUrl()); + } +} diff --git a/modules/mongodb/src/test/java/org/testcontainers/containers/integration/InitializationITTest.java b/modules/mongodb/src/test/java/org/testcontainers/containers/integration/InitializationITTest.java new file mode 100644 index 00000000000..306b32e9239 --- /dev/null +++ b/modules/mongodb/src/test/java/org/testcontainers/containers/integration/InitializationITTest.java @@ -0,0 +1,101 @@ +package org.testcontainers.containers.integration; + +import com.mongodb.reactivestreams.client.MongoClients; +import lombok.SneakyThrows; +import lombok.val; +import org.bson.Document; +import org.jetbrains.annotations.NotNull; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.reactivestreams.Publisher; +import org.testcontainers.containers.MongoDbContainer; +import org.testcontainers.containers.core.IntegrationTest; +import org.testcontainers.containers.util.SubscriberHelperUtils; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +@IntegrationTest +class InitializationITTest { + private final MongoDbContainer mongoDbContainer = + new MongoDbContainer("mongo:4.2.0"); + + @BeforeEach + void setUp() { + mongoDbContainer.start(); + } + + @AfterEach + void tearDown() { + mongoDbContainer.stop(); + } + + @Test + void shouldTestRsStatus() { + // GIVEN + val replicaSetUrl = mongoDbContainer.getReplicaSetUrl(); + assertNotNull(replicaSetUrl); + + val mongoReactiveClient = MongoClients.create(replicaSetUrl); + val db = mongoReactiveClient.getDatabase("admin"); + + // WHEN + THEN + try { + val subscriber = getSubscriber( + db.runCommand(new Document("replSetGetStatus", 1)) + ); + val document = getDocument(subscriber.getReceived()); + assertEquals(Double.valueOf("1"), document.get("ok", Double.class)); + val mongoNodesActual = document.getList("members", Document.class); + assertEquals(1, mongoNodesActual.size()); + assertEquals(1, mongoNodesActual.get(0).getInteger("state")); + } finally { + mongoReactiveClient.close(); + } + } + + @NotNull + private Document getDocument(List documents) { + return documents.get(0); + } + + @Test + void shouldTestVersionAndDockerImageName() { + // GIVEN + val replicaSetUrl = mongoDbContainer.getReplicaSetUrl(); + assertNotNull(replicaSetUrl); + val dockerImageName = mongoDbContainer.getDockerImageName(); + assertNotNull(dockerImageName); + val mongoClient = MongoClients.create(replicaSetUrl); + val db = mongoClient.getDatabase("test"); + + // WHEN + THEN + try { + val subscriber = getSubscriber( + db.runCommand(new Document("buildInfo", 1)) + ); + val version = getDocument(subscriber.getReceived()).getString("version"); + val versionExpected = + dockerImageName.substring(dockerImageName.indexOf(":") + 1); + assertEquals( + versionExpected, + version + ); + } finally { + mongoClient.close(); + } + } + + @SneakyThrows + private SubscriberHelperUtils.PrintDocumentSubscriber getSubscriber( + Publisher command + ) { + val subscriber = new SubscriberHelperUtils.PrintDocumentSubscriber(); + command.subscribe(subscriber); + subscriber.await(); + return subscriber; + } +} diff --git a/modules/mongodb/src/test/java/org/testcontainers/containers/integration/TransactionITTest.java b/modules/mongodb/src/test/java/org/testcontainers/containers/integration/TransactionITTest.java new file mode 100644 index 00000000000..64b04e7d727 --- /dev/null +++ b/modules/mongodb/src/test/java/org/testcontainers/containers/integration/TransactionITTest.java @@ -0,0 +1,77 @@ +package org.testcontainers.containers.integration; + +import com.mongodb.ReadConcern; +import com.mongodb.ReadPreference; +import com.mongodb.TransactionOptions; +import com.mongodb.WriteConcern; +import com.mongodb.client.TransactionBody; +import lombok.val; +import org.bson.Document; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.platform.commons.JUnitException; +import org.testcontainers.containers.MongoDbContainer; +import org.testcontainers.containers.core.IntegrationTest; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +@IntegrationTest +class TransactionITTest { + private final MongoDbContainer mongoDbContainer = new MongoDbContainer(); + + @BeforeEach + void setUp() { + mongoDbContainer.start(); + } + + @AfterEach + void tearDown() { + mongoDbContainer.stop(); + } + + /** + * Taken from https://docs.mongodb.com + */ + @Test + void shouldExecuteTransactions() { + //GIVEN + val mongoRsUrl = mongoDbContainer.getReplicaSetUrl(); + assertNotNull(mongoRsUrl); + val mongoSyncClient = com.mongodb.client.MongoClients.create(mongoRsUrl); + mongoSyncClient.getDatabase("mydb1").getCollection("foo") + .withWriteConcern(WriteConcern.MAJORITY).insertOne(new Document("abc", 0)); + mongoSyncClient.getDatabase("mydb2").getCollection("bar") + .withWriteConcern(WriteConcern.MAJORITY).insertOne(new Document("xyz", 0)); + + val clientSession = mongoSyncClient.startSession(); + val txnOptions = TransactionOptions.builder() + .readPreference(ReadPreference.primary()) + .readConcern(ReadConcern.LOCAL) + .writeConcern(WriteConcern.MAJORITY) + .build(); + + val trxResult = "Inserted into collections in different databases"; + + //WHEN + THEN + TransactionBody txnBody = () -> { + val coll1 = mongoSyncClient.getDatabase("mydb1").getCollection("foo"); + val coll2 = mongoSyncClient.getDatabase("mydb2").getCollection("bar"); + + coll1.insertOne(clientSession, new Document("abc", 1)); + coll2.insertOne(clientSession, new Document("xyz", 999)); + return trxResult; + }; + + try { + val trxResultActual = clientSession.withTransaction(txnBody, txnOptions); + assertEquals(trxResult, trxResultActual); + } catch (RuntimeException re) { + throw new JUnitException(re.getMessage(), re); + } finally { + clientSession.close(); + mongoSyncClient.close(); + } + } +} diff --git a/modules/mongodb/src/test/java/org/testcontainers/containers/util/SubscriberHelperUtils.java b/modules/mongodb/src/test/java/org/testcontainers/containers/util/SubscriberHelperUtils.java new file mode 100644 index 00000000000..ea26f3c3f55 --- /dev/null +++ b/modules/mongodb/src/test/java/org/testcontainers/containers/util/SubscriberHelperUtils.java @@ -0,0 +1,154 @@ +package org.testcontainers.containers.util; + +import com.mongodb.MongoTimeoutException; +import lombok.extern.slf4j.Slf4j; +import org.bson.Document; +import org.reactivestreams.Subscriber; +import org.reactivestreams.Subscription; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import static java.lang.String.format; + +/** + * Subscriber helper utility class taken from: + * + * @see https://github.com/mongodb/mongo-java-driver-reactivestreams + */ +@Slf4j +public final class SubscriberHelperUtils { + + private SubscriberHelperUtils() { + } + + /** + * A Subscriber that stores the publishers results and provides a latch so can block on completion. + * + * @param The publishers result type + */ + public static class ObservableSubscriber implements Subscriber { + private final List received; + private final List errors; + private final CountDownLatch latch; + private volatile Subscription subscription; + private volatile boolean completed; + + ObservableSubscriber() { + this.received = new ArrayList(); + this.errors = new ArrayList(); + this.latch = new CountDownLatch(1); + } + + @Override + public void onSubscribe(final Subscription s) { + subscription = s; + } + + @Override + public void onNext(final T t) { + received.add(t); + } + + @Override + public void onError(final Throwable t) { + errors.add(t); + onComplete(); + } + + @Override + public void onComplete() { + completed = true; + latch.countDown(); + } + + public Subscription getSubscription() { + return subscription; + } + + public List getReceived() { + return received; + } + + public Throwable getError() { + if (errors.size() > 0) { + return errors.get(0); + } + return null; + } + + public boolean isCompleted() { + return completed; + } + + public List get(final long timeout, final TimeUnit unit) throws Throwable { + return await(timeout, unit).getReceived(); + } + + public ObservableSubscriber await() throws Throwable { + return await(Long.MAX_VALUE, TimeUnit.MILLISECONDS); + } + + public ObservableSubscriber await(final long timeout, final TimeUnit unit) throws Throwable { + subscription.request(Integer.MAX_VALUE); + if (!latch.await(timeout, unit)) { + throw new MongoTimeoutException("Publisher onComplete timed out"); + } + if (!errors.isEmpty()) { + throw errors.get(0); + } + return this; + } + } + + /** + * A Subscriber that immediately requests Integer.MAX_VALUE onSubscribe + * + * @param The publishers result type + */ + public static class OperationSubscriber extends ObservableSubscriber { + + @Override + public void onSubscribe(final Subscription s) { + super.onSubscribe(s); + s.request(Integer.MAX_VALUE); + } + } + + /** + * A Subscriber that prints a message including the received items on completion + * + * @param The publishers result type + */ + public static class PrintSubscriber extends OperationSubscriber { + private final String message; + + /** + * A Subscriber that outputs a message onComplete. + * + * @param message the message to output onComplete + */ + public PrintSubscriber(final String message) { + this.message = message; + } + + @Override + public void onComplete() { + log.debug(format(message, getReceived())); + super.onComplete(); + } + } + + /** + * A Subscriber that prints the json version of each document + */ + public static class PrintDocumentSubscriber extends OperationSubscriber { + @Override + public void onNext(final Document document) { + super.onNext(document); + log.debug(document.toJson()); + } + } +} diff --git a/modules/mongodb/src/test/resources/logback-test.xml b/modules/mongodb/src/test/resources/logback-test.xml new file mode 100644 index 00000000000..285197eeead --- /dev/null +++ b/modules/mongodb/src/test/resources/logback-test.xml @@ -0,0 +1,16 @@ + + + + + + %d{HH:mm:ss.SSS} %-5level %logger - %msg%n + + + + + + + + + diff --git a/modules/mongodb/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker b/modules/mongodb/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker new file mode 100644 index 00000000000..ca6ee9cea8e --- /dev/null +++ b/modules/mongodb/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker @@ -0,0 +1 @@ +mock-maker-inline \ No newline at end of file From a2c5c69d1a8be2dc91f0a09f900511aa327c96e7 Mon Sep 17 00:00:00 2001 From: Konstantin Silaev Date: Mon, 7 Oct 2019 17:19:19 +0300 Subject: [PATCH 02/38] change oraclejdk8 with openjdk11, delete obsolete ITTest --- .travis.yml | 2 +- .../containers/integration/ITTest.java | 29 ------------------- 2 files changed, 1 insertion(+), 30 deletions(-) delete mode 100644 modules/mongodb/src/test/java/org/testcontainers/containers/integration/ITTest.java diff --git a/.travis.yml b/.travis.yml index 7532d96c1d3..382e4678e32 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,7 @@ if: branch = master OR tag is present language: java jdk: - - oraclejdk8 + - openjdk11 sudo: required services: diff --git a/modules/mongodb/src/test/java/org/testcontainers/containers/integration/ITTest.java b/modules/mongodb/src/test/java/org/testcontainers/containers/integration/ITTest.java deleted file mode 100644 index 8d6a99283f1..00000000000 --- a/modules/mongodb/src/test/java/org/testcontainers/containers/integration/ITTest.java +++ /dev/null @@ -1,29 +0,0 @@ -package org.testcontainers.containers.integration; - -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.testcontainers.containers.MongoDbContainer; - -import static org.junit.jupiter.api.Assertions.assertNotNull; - -class ITTest { - private final MongoDbContainer mongoDbContainer = new MongoDbContainer( - //"mongo:4.2.0" - ); - - @BeforeEach - void setUp() { - mongoDbContainer.start(); - } - - @AfterEach - void tearDown() { - mongoDbContainer.stop(); - } - - @Test - void shouldTestReplicaSetUrl() { - assertNotNull(mongoDbContainer.getReplicaSetUrl()); - } -} From 8ddd6b4447c8e450c718325f2cf50b6fde4f1660 Mon Sep 17 00:00:00 2001 From: Konstantin Silaev Date: Mon, 7 Oct 2019 18:06:30 +0300 Subject: [PATCH 03/38] Small fixes --- .travis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 382e4678e32..4f43d91b01b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,7 @@ if: branch = master OR tag is present language: java jdk: - - openjdk11 + - oraclejdk8 sudo: required services: @@ -51,7 +51,7 @@ jobs: openjdk:8-jdk-alpine \ ./gradlew --no-daemon testcontainers:test --tests '*GenericContainerRuleTest' --scan - - env: [ NAME=mongodb ] + - env: [ NAME=docker-in-mongodb ] script: - | DOCKER_HOST=unix:///var/run/docker.sock DOCKER_TLS_VERIFY= docker run -t --rm \ @@ -60,7 +60,7 @@ jobs: -v "$(pwd)":"$(pwd)" \ -w "$(pwd)" \ openjdk:8-jdk-alpine \ - ./gradlew mongodb:integrationTest --scan --no-daemon + ./gradlew mongodb:integrationTest --scan --no-daemon --no-build-cache - stage: deploy sudo: false From 1795235c4bddfb1eab2f8badfc814834ef2c578f Mon Sep 17 00:00:00 2001 From: Konstantin Silaev Date: Tue, 8 Oct 2019 09:47:00 +0300 Subject: [PATCH 04/38] Fix docs --- docs/modules/databases/mongodb.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/modules/databases/mongodb.md b/docs/modules/databases/mongodb.md index bba47a57b23..52ff64f2844 100644 --- a/docs/modules/databases/mongodb.md +++ b/docs/modules/databases/mongodb.md @@ -1,4 +1,6 @@ -# Java8 MongoDbContainer to constructs a single node MongoDB replica set +#### This module is INCUBATING. While it is ready for use and operational in the current version of Testcontainers, it is possible that it may receive breaking changes in the future. + +# Java8 MongoDbContainer for constructing a single node MongoDB replica set. To construct a multi-node MongoDB cluster, consider [mongodb-replica-set project](https://github.com/silaev/mongodb-replica-set/) #### Prerequisite - Java 8+ @@ -10,8 +12,7 @@ | + | + | + | Tips: -- Adding this Testcontainers library JAR will not automatically add a database driver JAR to your project. You should ensure that your project also has a suitable database driver as a dependency -- To construct a multi-node MongoDB cluster, consider [mongodb-replica-set project](https://github.com/silaev/mongodb-replica-set/) +- Adding this Testcontainers library JAR will not automatically add a database driver JAR to your project. You should ensure that your project also has a suitable database driver as a dependency - To use remote Docker daemon: - check that [.testcontainers.properties](https://www.testcontainers.org/features/configuration/) file has `docker.client.strategy=org.testcontainers.dockerclient.EnvironmentAndSystemPropertyClientProviderStrategy` - set your DOCKER_HOST environment variable From e06749d99a6a2c74b949f34f8be3517c5d7ca4f1 Mon Sep 17 00:00:00 2001 From: Konstantin Silaev Date: Wed, 9 Oct 2019 12:52:36 +0300 Subject: [PATCH 05/38] fix mongodb.md --- docs/modules/databases/mongodb.md | 35 ++++++++++++++++++------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/docs/modules/databases/mongodb.md b/docs/modules/databases/mongodb.md index 52ff64f2844..e6408c23710 100644 --- a/docs/modules/databases/mongodb.md +++ b/docs/modules/databases/mongodb.md @@ -1,23 +1,21 @@ +# Mongo DB Module + #### This module is INCUBATING. While it is ready for use and operational in the current version of Testcontainers, it is possible that it may receive breaking changes in the future. # Java8 MongoDbContainer for constructing a single node MongoDB replica set. To construct a multi-node MongoDB cluster, consider [mongodb-replica-set project](https://github.com/silaev/mongodb-replica-set/) #### Prerequisite -- Java 8+ -- Docker Desktop -- Chart shows support for local and remote Docker + +* Java 8+ +* Docker Desktop +* Chart shows support for local and remote Docker local docker host | local docker host running tests from inside a container with mapping the Docker socket | remote docker daemon | |:---: | :---: | :---: | | + | + | + | - -Tips: -- Adding this Testcontainers library JAR will not automatically add a database driver JAR to your project. You should ensure that your project also has a suitable database driver as a dependency -- To use remote Docker daemon: - - check that [.testcontainers.properties](https://www.testcontainers.org/features/configuration/) file has `docker.client.strategy=org.testcontainers.dockerclient.EnvironmentAndSystemPropertyClientProviderStrategy` - - set your DOCKER_HOST environment variable - + #### Getting it + ```groovy tab='Gradle' testCompile "org.testcontainers:mongodb:{{latest_version}}" ``` @@ -30,9 +28,10 @@ testCompile "org.testcontainers:mongodb:{{latest_version}}" test ``` + To see logs, consider adding a slf4j implementation (Logback is recommended) if you don't have it in your application. -#### MongoDB versions that MongoDbContainer is constantly tested against +#### MongoDB versions that MongoDbContainer is constantly tested against: version | ---------- | 4.0.12 | @@ -124,13 +123,19 @@ fixed ports to test MongoDB transactions. The solution should work with local an #### General info MongoDB starting form version 4 supports multi-document transactions only for a replica set. For instance, to initialize a single and simple node replica set on fixed ports via Docker, one has to do the following: -1) Run a MongoDB container of version 4 and up specifying --replSet command -2) Initializing a single replica set via executing a proper command (depending on local or remote Docker daemon usage) -3) Waiting for the initialization to complete -4) Providing a special url (without the need to modify the OS host file) for a user to employ with a MongoDB driver without specifying replicaSet + +* Run a MongoDB container of version 4 and up specifying --replSet command +* Initializing a single replica set via executing a proper command (depending on local or remote Docker daemon usage) +* Waiting for the initialization to complete +* Providing a special url (without the need to modify the OS host file) for a user to employ with a MongoDB driver without specifying replicaSet As we can see, there is a lot of operations to execute and we even haven't touched a non-fixed port approach. That's where the MongoDbContainer might come in handy. +!!! hint +* Adding this Testcontainers library JAR will not automatically add a database driver JAR to your project. You should ensure that your project also has a suitable database driver as a dependency +* To use remote Docker daemon check that [.testcontainers.properties](https://www.testcontainers.org/features/configuration/) file has `docker.client.strategy=org.testcontainers.dockerclient.EnvironmentAndSystemPropertyClientProviderStrategy` +and set your DOCKER_HOST environment variable + #### Copyright Copyright (c) 2019 Konstantin Silaev From 8f68d711198682f4c70ce3939625b9ee9c6ea5b1 Mon Sep 17 00:00:00 2001 From: Konstantin Silaev Date: Mon, 14 Oct 2019 09:57:57 +0300 Subject: [PATCH 06/38] Code review remarks adjusted --- .travis.yml | 11 -- docs/modules/databases/mongodb.md | 138 ++++------------- modules/mongodb/build.gradle | 18 +-- .../containers/MongoDbContainer.java | 139 +++--------------- .../containers/MongoDbContainerTest.java | 15 +- .../integration/BaseInitializationITTest.java | 85 +++++++++++ .../integration/CommunicationITTest.java | 90 ++++++++++++ .../integration/InitializationITTest.java | 78 +--------- .../integration/TransactionITTest.java | 23 +-- 9 files changed, 258 insertions(+), 339 deletions(-) create mode 100644 modules/mongodb/src/test/java/org/testcontainers/containers/integration/BaseInitializationITTest.java create mode 100644 modules/mongodb/src/test/java/org/testcontainers/containers/integration/CommunicationITTest.java diff --git a/.travis.yml b/.travis.yml index f588bebd8b9..810b5827757 100644 --- a/.travis.yml +++ b/.travis.yml @@ -51,17 +51,6 @@ jobs: openjdk:8-jdk-alpine \ ./gradlew --no-daemon testcontainers:test --tests '*GenericContainerRuleTest' --scan -i - - env: [ NAME=docker-in-mongodb ] - script: - - | - DOCKER_HOST=unix:///var/run/docker.sock DOCKER_TLS_VERIFY= docker run -t --rm \ - -v "$HOME/.gradle":/root/.gradle/ \ - -v /var/run/docker.sock:/var/run/docker.sock \ - -v "$(pwd)":"$(pwd)" \ - -w "$(pwd)" \ - openjdk:8-jdk-alpine \ - ./gradlew mongodb:integrationTest --scan --no-daemon --no-build-cache - - stage: deploy sudo: false services: [] diff --git a/docs/modules/databases/mongodb.md b/docs/modules/databases/mongodb.md index e6408c23710..a49c4aba386 100644 --- a/docs/modules/databases/mongodb.md +++ b/docs/modules/databases/mongodb.md @@ -1,35 +1,9 @@ # Mongo DB Module -#### This module is INCUBATING. While it is ready for use and operational in the current version of Testcontainers, it is possible that it may receive breaking changes in the future. +!!! note + This module is INCUBATING. While it is ready for use and operational in the current version of Testcontainers, it is possible that it may receive breaking changes in the future. See [our contributing guidelines](/contributing/#incubating-modules) for more information on our incubating modules policy. -# Java8 MongoDbContainer for constructing a single node MongoDB replica set. To construct a multi-node MongoDB cluster, consider [mongodb-replica-set project](https://github.com/silaev/mongodb-replica-set/) - -#### Prerequisite - -* Java 8+ -* Docker Desktop -* Chart shows support for local and remote Docker - - local docker host | local docker host running tests from inside a container with mapping the Docker socket | remote docker daemon | - |:---: | :---: | :---: | - | + | + | + | - -#### Getting it - -```groovy tab='Gradle' -testCompile "org.testcontainers:mongodb:{{latest_version}}" -``` - -```xml tab='Maven' - - org.testcontainers - mongodb - {{latest_version}} - test - -``` - -To see logs, consider adding a slf4j implementation (Logback is recommended) if you don't have it in your application. +# Java8 MongoDbContainer for constructing a single node MongoDB replica set. To construct a multi-node MongoDB cluster, consider the [mongodb-replica-set project](https://github.com/silaev/mongodb-replica-set/) #### MongoDB versions that MongoDbContainer is constantly tested against: version | @@ -37,88 +11,21 @@ version | 4.0.12 | 4.2.0 | -#### Example usage (note that the MongoDbContainer is test framework agnostic) -The example of a JUnit5 test class: -```java -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.testcontainers.mongodb.MongoDbContainer; - -import static org.junit.jupiter.api.Assertions.assertNotNull; - -class ITTest { - private final MongoDbContainer mongoDbContainer = new MongoDbContainer( - //"mongo:4.2.0" - ); +## Usage example - @BeforeEach - void setUp() { - mongoDbContainer.start(); - } +The following example shows how to create a MongoDbContainer - @AfterEach - void tearDown() { - mongoDbContainer.stop(); - } - - @Test - void shouldTestReplicaSetUrl() { - assertNotNull(mongoDbContainer.getReplicaSetUrl()); - } -} -``` -The example of a JUnit5 test class in a Spring Boot + Spring Data application: -```java -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.util.TestPropertyValues; -import org.springframework.context.ApplicationContextInitializer; -import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.test.context.ContextConfiguration; -import org.testcontainers.mongodb.MongoDbContainer; + +[Creating a MongoDB container](../../../modules/mongodb/src/test/java/org/testcontainers/containers/integration/InitializationITTest.java) inside_block:creatingMongoDbContainer + -import static org.junit.jupiter.api.Assertions.assertNotNull; - -@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) -//@DataMongoTest -@ContextConfiguration(initializers = ITTest.Initializer.class) -class ITTest { - private static final MongoDbContainer MONGO_DB_CONTAINER = new MongoDbContainer( - //"mongo:4.2.0" - ); - - @BeforeAll - static void setUp() { - MONGO_DB_CONTAINER.start(); - } - - @AfterAll - static void tearDown() { - MONGO_DB_CONTAINER.stop(); - } - - @Test - void shouldTestReplicaSetUrl() { - assertNotNull(MONGO_DB_CONTAINER.getReplicaSetUrl()); - } - - static class Initializer implements ApplicationContextInitializer { - @Override - public void initialize(ConfigurableApplicationContext configurableApplicationContext) { - TestPropertyValues.of( - String.format("spring.data.mongodb.uri: %s", MONGO_DB_CONTAINER.getReplicaSetUrl()) - ).applyTo(configurableApplicationContext); - } - } -} -``` + +[Start and stop the MongoDB container](../../../modules/mongodb/src/test/java/org/testcontainers/containers/integration/InitializationITTest.java) inside_block:startingStoppingMongoDbContainer + #### Motivation Implement a reusable, cross-platform, simple to install solution that doesn't depend on -fixed ports to test MongoDB transactions. The solution should work with local and remote Docker daemon. +fixed ports to test MongoDB transactions. #### General info MongoDB starting form version 4 supports multi-document transactions only for a replica set. @@ -132,10 +39,25 @@ For instance, to initialize a single and simple node replica set on fixed ports As we can see, there is a lot of operations to execute and we even haven't touched a non-fixed port approach. That's where the MongoDbContainer might come in handy. +## Adding this module to your project dependencies + +Add the following dependency to your `pom.xml`/`build.gradle` file: + +```groovy tab='Gradle' +testCompile "org.testcontainers:mongodb:{{latest_version}}" +``` + +```xml tab='Maven' + + org.testcontainers + mongodb + {{latest_version}} + test + +``` + !!! hint -* Adding this Testcontainers library JAR will not automatically add a database driver JAR to your project. You should ensure that your project also has a suitable database driver as a dependency -* To use remote Docker daemon check that [.testcontainers.properties](https://www.testcontainers.org/features/configuration/) file has `docker.client.strategy=org.testcontainers.dockerclient.EnvironmentAndSystemPropertyClientProviderStrategy` -and set your DOCKER_HOST environment variable +Adding this Testcontainers library JAR will not automatically add a database driver JAR to your project. You should ensure that your project also has a suitable database driver as a dependency #### Copyright Copyright (c) 2019 Konstantin Silaev diff --git a/modules/mongodb/build.gradle b/modules/mongodb/build.gradle index 23623cb2875..e544f166300 100644 --- a/modules/mongodb/build.gradle +++ b/modules/mongodb/build.gradle @@ -1,32 +1,24 @@ description = "Testcontainers :: MongoDB" -buildscript { - ext { - junitVersion = '5.5.1' - } -} - dependencies { compile project(':testcontainers') - testCompile("org.junit.jupiter:junit-jupiter-api:${junitVersion}") + testCompile("org.junit.jupiter:junit-jupiter-api:5.5.1") testCompile('org.mockito:mockito-core:3.0.0') testCompile('ch.qos.logback:logback-classic:1.2.3') - testRuntime("org.junit.jupiter:junit-jupiter-engine:${junitVersion}") + testRuntime("org.junit.jupiter:junit-jupiter-engine:5.5.1") testCompile("org.mongodb:mongodb-driver-reactivestreams:1.12.0") testCompile("org.mongodb:mongodb-driver-sync:3.11.0") testCompile("org.mockito:mockito-junit-jupiter:3.0.0") } -test { - failFast = true -} - tasks.withType(Test) { Test task -> task.useJUnitPlatform { JUnitPlatformOptions ops -> ops.excludeTags("integration-test") } + task.failFast = true + testLogging.showStandardStreams = true testLogging.exceptionFormat = 'full' } @@ -36,6 +28,8 @@ task integrationTest(type: Test) { Test task -> ops.includeTags("integration-test") } + task.failFast = true + testLogging.showStandardStreams = true testLogging.exceptionFormat = 'full' diff --git a/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDbContainer.java b/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDbContainer.java index aded1f42a22..b27f0a9af35 100644 --- a/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDbContainer.java +++ b/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDbContainer.java @@ -3,125 +3,25 @@ import lombok.NonNull; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; -import lombok.val; import org.testcontainers.containers.wait.strategy.Wait; import java.io.IOException; /** * Constructs a single node MongoDB replica set for testing transactions. - *

To construct a multi-node MongoDB cluster, consider mongodb-replica-set project on GitHub - * - *

- * - * - * - * - * - * - * - * - * - *
Chart shows a pattern for local and remote Docker support
local docker host - * local docker host running tests from inside a container with mapping the Docker socket - * remote docker daemon - *
+ - * + - * + - *
- *
- *

- * Tested on a Mongo DB version 4.0.10 (that is the default version if not specified) and up. - * - *

Example usage (note that the MongoDbContainer is test framework agnostic)

- *

The example of a JUnit5 test class: - *

- * import org.junit.jupiter.api.AfterEach;
- * import org.junit.jupiter.api.BeforeEach;
- * import org.junit.jupiter.api.Test;
- * import org.testcontainers.mongodb.MongoDbContainer;
- *
- * import static org.junit.jupiter.api.Assertions.assertNotNull;
- *
- * class ITTest {
- *     private final MongoDbContainer mongoDbContainer = new MongoDbContainer(
- *         //"mongo:4.2.0"
- *     );
- *
- *     {@literal @}BeforeEach
- *     void setUp() {
- *         mongoDbContainer.start();
- *     }
- *
- *     {@literal @}AfterEach
- *     void tearDown() {
- *         mongoDbContainer.stop();
- *     }
- *
- *     {@literal @}Test
- *     void shouldTestReplicaSetUrl() {
- *         assertNotNull(mongoDbContainer.getReplicaSetUrl());
- *     }
- * }
- * 
- * - *

The example of a SpringBoot+SpringData test with JUnit5: - *

- * import org.junit.jupiter.api.AfterAll;
- * import org.junit.jupiter.api.BeforeAll;
- * import org.junit.jupiter.api.Test;
- * import org.springframework.boot.test.context.SpringBootTest;
- * import org.springframework.boot.test.util.TestPropertyValues;
- * import org.springframework.context.ApplicationContextInitializer;
- * import org.springframework.context.ConfigurableApplicationContext;
- * import org.springframework.test.context.ContextConfiguration;
- * import org.testcontainers.mongodb.MongoDbContainer;
- *
- * import static org.junit.jupiter.api.Assertions.assertNotNull;
- *
- * {@literal @}SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
- * //@DataMongoTest
- * {@literal @}ContextConfiguration(initializers = ITTest.Initializer.class)
- * class ITTest {
- *     private static final MongoDbContainer MONGO_DB_CONTAINER = new MongoDbContainer(
- *             //"mongo:4.2.0"
- *     );
- *
- *     {@literal @}BeforeAll
- *     static void setUp() {
- *         MONGO_DB_CONTAINER.start();
- *     }
- *
- *     {@literal @}AfterAll
- *     static void tearDown() {
- *         MONGO_DB_CONTAINER.stop();
- *     }
- *
- *     {@literal @}Test
- *     void shouldTestReplicaSetUrl() {
- *         assertNotNull(MONGO_DB_CONTAINER.getReplicaSetUrl());
- *     }
- *
- *     static class Initializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
- *        {@literal @}@Override
- *         public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
- *             TestPropertyValues.of(
- *                     String.format("spring.data.mongodb.uri: %s", MONGO_DB_CONTAINER.getReplicaSetUrl())
- *             ).applyTo(configurableApplicationContext);
- *         }
- *     }
- * }
- * 
+ *

To construct a multi-node MongoDB cluster, consider the mongodb-replica-set project on GitHub + *

Tested on a Mongo DB version 4.0.10+ (that is the default version if not specified). * * @author Konstantin Silaev on 9/30/2019 */ @Slf4j public class MongoDbContainer extends GenericContainer { static final int ERROR_CONTAINER_EXIT_CODE = 1; - static final int MONGO_DB_INTERNAL_PORT = 27017; + static final int MONGODB_INTERNAL_PORT = 27017; private static final int AWAIT_INIT_REPLICA_SET_ATTEMPTS = 30; private static final String MONGODB_VERSION_DEFAULT = "4.0.10"; private static final String LOCALHOST = "localhost"; + private static final String MONGODB_DATABASE_NAME_DEFAULT = "test"; public MongoDbContainer() { super("mongo:" + MONGODB_VERSION_DEFAULT); @@ -133,14 +33,13 @@ public MongoDbContainer(@NonNull final String dockerImageName) { public String getReplicaSetUrl() { if (!isRunning()) { - throw new IllegalStateException( - String.format( - "Please, start %s first", MongoDbContainer.class.getCanonicalName()) - ); + throw new IllegalStateException("MongoDbContainer should be started first"); } return String.format( - "mongodb://%s:%d/test", - getContainerIpAddress(), getMappedPort(MONGO_DB_INTERNAL_PORT) + "mongodb://%s:%d/%s", + getContainerIpAddress(), + getMappedPort(MONGODB_INTERNAL_PORT), + MONGODB_DATABASE_NAME_DEFAULT ); } @@ -161,7 +60,7 @@ private void logReplicaSetStatus() { @Override protected void configure() { - withExposedPorts(MONGO_DB_INTERNAL_PORT); + withExposedPorts(MONGODB_INTERNAL_PORT); withCommand("--replSet", "docker-rs"); waitingFor( Wait.forLogMessage(".*waiting for connections on port.*", 1) @@ -169,11 +68,11 @@ protected void configure() { } private String getMongoReplicaSetInitializer() { - val containerIpAddress = getContainerIpAddress(); - val containerPort = LOCALHOST.equals(containerIpAddress) - ? MONGO_DB_INTERNAL_PORT - : getMappedPort(MONGO_DB_INTERNAL_PORT); - val initializer = String.format( + final String containerIpAddress = getContainerIpAddress(); + final int containerPort = LOCALHOST.equals(containerIpAddress) + ? MONGODB_INTERNAL_PORT + : getMappedPort(MONGODB_INTERNAL_PORT); + final String initializer = String.format( "rs.initiate({\n" + " \"_id\": \"docker-rs\",\n" + " \"members\": [\n" + @@ -190,7 +89,7 @@ private String[] buildMongoEvalCommand(final String command) { private void checkMongoNodeExitCode(final Container.ExecResult execResult) { if (execResult.getExitCode() == ERROR_CONTAINER_EXIT_CODE) { - val errorMessage = String.format("An error occurred: %s", execResult.getStderr()); + final String errorMessage = String.format("An error occurred: %s", execResult.getStderr()); log.error(errorMessage); throw new ReplicaSetInitializationException(errorMessage); } @@ -215,7 +114,7 @@ private void checkMongoNodeExitCodeAfterWaiting( final Container.ExecResult execResultWaitForMaster ) { if (execResultWaitForMaster.getExitCode() == ERROR_CONTAINER_EXIT_CODE) { - val errorMessage = String.format( + final String errorMessage = String.format( "A single node replica set was not initialized in a set timeout: %d attempts", AWAIT_INIT_REPLICA_SET_ATTEMPTS ); @@ -227,7 +126,7 @@ private void checkMongoNodeExitCodeAfterWaiting( @SneakyThrows(value = {IOException.class, InterruptedException.class}) void initReplicaSet() { log.debug("Initializing a single node node replica set..."); - val execResultInitRs = execInContainer( + final ExecResult execResultInitRs = execInContainer( buildMongoEvalCommand(getMongoReplicaSetInitializer()) ); log.debug(execResultInitRs.getStdout()); @@ -237,7 +136,7 @@ void initReplicaSet() { "Awaiting for a single node replica set initialization up to {} attempts", AWAIT_INIT_REPLICA_SET_ATTEMPTS ); - val execResultWaitForMaster = execInContainer( + final ExecResult execResultWaitForMaster = execInContainer( buildMongoEvalCommand(buildMongoWaitCommand()) ); log.debug(execResultWaitForMaster.getStdout()); diff --git a/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDbContainerTest.java b/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDbContainerTest.java index 2c7972a70b9..2e24a1faaeb 100644 --- a/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDbContainerTest.java +++ b/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDbContainerTest.java @@ -1,6 +1,5 @@ package org.testcontainers.containers; -import lombok.val; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.function.Executable; @@ -40,13 +39,13 @@ void shouldNotGetReplicaSetUrlBecauseOfNotStartedContainer() { void shouldNotInitReplicaSetBecauseOfExecInitReplicaSet() throws IOException, InterruptedException { //GIVEN - val mockExecResult = mock(Container.ExecResult.class); + Container.ExecResult mockExecResult = mock(Container.ExecResult.class); when(mockExecResult.getExitCode()) .thenReturn(MongoDbContainer.ERROR_CONTAINER_EXIT_CODE); doReturn(mockExecResult).when(MONGODB_CONTAINER).execInContainer(any()); - val mappedPort = 37723; + final int mappedPort = 37723; lenient().doReturn(mappedPort).when(MONGODB_CONTAINER) - .getMappedPort(MongoDbContainer.MONGO_DB_INTERNAL_PORT); + .getMappedPort(MongoDbContainer.MONGODB_INTERNAL_PORT); //WHEN Executable executable = () -> MONGODB_CONTAINER.initReplicaSet(); @@ -61,17 +60,17 @@ void shouldNotInitReplicaSetBecauseOfExecInitReplicaSet() @Test void shouldNotInitReplicaSetBecauseOfWaitCommand() throws IOException, InterruptedException { //GIVEN - val mockExecResult1 = mock(Container.ExecResult.class); - val mockExecResult2 = mock(Container.ExecResult.class); + final Container.ExecResult mockExecResult1 = mock(Container.ExecResult.class); + final Container.ExecResult mockExecResult2 = mock(Container.ExecResult.class); when(mockExecResult1.getExitCode()) .thenReturn(0); when(mockExecResult2.getExitCode()) .thenReturn(MongoDbContainer.ERROR_CONTAINER_EXIT_CODE); doReturn(mockExecResult1, mockExecResult2) .when(MONGODB_CONTAINER).execInContainer(any()); - val mappedPort = 37723; + final int mappedPort = 37723; lenient().doReturn(mappedPort).when(MONGODB_CONTAINER) - .getMappedPort(MongoDbContainer.MONGO_DB_INTERNAL_PORT); + .getMappedPort(MongoDbContainer.MONGODB_INTERNAL_PORT); //WHEN Executable executable = () -> MONGODB_CONTAINER.initReplicaSet(); diff --git a/modules/mongodb/src/test/java/org/testcontainers/containers/integration/BaseInitializationITTest.java b/modules/mongodb/src/test/java/org/testcontainers/containers/integration/BaseInitializationITTest.java new file mode 100644 index 00000000000..8f31b6d8f9e --- /dev/null +++ b/modules/mongodb/src/test/java/org/testcontainers/containers/integration/BaseInitializationITTest.java @@ -0,0 +1,85 @@ +package org.testcontainers.containers.integration; + +import com.mongodb.reactivestreams.client.MongoClient; +import com.mongodb.reactivestreams.client.MongoClients; +import com.mongodb.reactivestreams.client.MongoDatabase; +import lombok.SneakyThrows; +import org.bson.Document; +import org.jetbrains.annotations.NotNull; +import org.reactivestreams.Publisher; +import org.testcontainers.containers.MongoDbContainer; +import org.testcontainers.containers.util.SubscriberHelperUtils; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +abstract class BaseInitializationITTest { + + void shouldTestRsStatus( + final MongoDbContainer mongoDbContainer + ) { + // GIVEN + final String replicaSetUrl = mongoDbContainer.getReplicaSetUrl(); + assertNotNull(replicaSetUrl); + + final MongoClient mongoReactiveClient = MongoClients.create(replicaSetUrl); + final MongoDatabase db = mongoReactiveClient.getDatabase("admin"); + + // WHEN + THEN + try { + final SubscriberHelperUtils.PrintDocumentSubscriber subscriber = getSubscriber( + db.runCommand(new Document("replSetGetStatus", 1)) + ); + final Document document = getDocument(subscriber.getReceived()); + assertEquals(Double.valueOf("1"), document.get("ok", Double.class)); + final List mongoNodesActual = + document.getList("members", Document.class); + assertEquals(1, mongoNodesActual.size()); + assertEquals(1, mongoNodesActual.get(0).getInteger("state")); + } finally { + mongoReactiveClient.close(); + } + } + + @NotNull + private Document getDocument(List documents) { + return documents.get(0); + } + + void shouldTestVersionAndDockerImageName( + final MongoDbContainer mongoDbContainer + ) { + // GIVEN + final String replicaSetUrl = mongoDbContainer.getReplicaSetUrl(); + assertNotNull(replicaSetUrl); + final String dockerImageName = mongoDbContainer.getDockerImageName(); + assertNotNull(dockerImageName); + final MongoClient mongoClient = MongoClients.create(replicaSetUrl); + final MongoDatabase db = mongoClient.getDatabase("test"); + + // WHEN + THEN + try { + final SubscriberHelperUtils.PrintDocumentSubscriber subscriber = getSubscriber( + db.runCommand(new Document("buildInfo", 1)) + ); + final String version = getDocument(subscriber.getReceived()).getString("version"); + final String versionExpected = dockerImageName.substring(dockerImageName.indexOf(":") + 1); + assertEquals(versionExpected, version); + } finally { + mongoClient.close(); + } + } + + @SneakyThrows + private SubscriberHelperUtils.PrintDocumentSubscriber getSubscriber( + Publisher command + ) { + final SubscriberHelperUtils.PrintDocumentSubscriber subscriber = + new SubscriberHelperUtils.PrintDocumentSubscriber(); + command.subscribe(subscriber); + subscriber.await(); + return subscriber; + } +} diff --git a/modules/mongodb/src/test/java/org/testcontainers/containers/integration/CommunicationITTest.java b/modules/mongodb/src/test/java/org/testcontainers/containers/integration/CommunicationITTest.java new file mode 100644 index 00000000000..a370ed16006 --- /dev/null +++ b/modules/mongodb/src/test/java/org/testcontainers/containers/integration/CommunicationITTest.java @@ -0,0 +1,90 @@ +package org.testcontainers.containers.integration; + +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.testcontainers.containers.MongoDbContainer; +import org.testcontainers.containers.Network; +import org.testcontainers.containers.core.IntegrationTest; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Tests both: + *

    + *
  • the communication between 2 MongoDbContainers
  • + *
  • the accessibility of these MongoDbContainers
  • + *
+ * from a host. + *

+ * Note that s256/mongodb-net-utils is an official MongoDB Docker file with the nmap package installed. + * Its tag corresponds to an official MongoDB Docker file tag. + * + * @see dockerfile on DockerHub + * @see mongodb-net-utils on GitHub + */ +@IntegrationTest +@Slf4j +class CommunicationITTest extends BaseInitializationITTest { + private final Network network = Network.newNetwork(); + private final MongoDbContainer mongoDbContainer1 = + new MongoDbContainer("s256/mongodb-net-utils:4.0.10") + .withNetworkAliases("mongoDbContainer1") + .withNetwork(network); + private final MongoDbContainer mongoDbContainer2 = + new MongoDbContainer("s256/mongodb-net-utils:4.0.10") + .withNetworkAliases("mongoDbContainer2") + .withNetwork(network); + + @BeforeEach + void setUp() { + mongoDbContainer1.start(); + mongoDbContainer2.start(); + } + + @AfterEach + void tearDown() { + mongoDbContainer1.stop(); + mongoDbContainer2.stop(); + } + + @Test + void shouldTestCommunication() { + testCommunicationBetweenNodes(); + testAccessibilityFromHost(); + } + + @SneakyThrows + private void testCommunicationBetweenNodes() { + //GIVEN + final String successString = "Host is up"; + + //WHEN + final String stdout1 = + mongoDbContainer1.execInContainer("nmap", "mongoDbContainer2") + .getStdout(); + log.debug("Pinging mongoDbContainer2 from mongoDbContainer1: \n{}", stdout1); + + final String stdout2 = + mongoDbContainer2.execInContainer("nmap", "mongoDbContainer1") + .getStdout(); + log.debug("Pinging mongoDbContainer1 from mongoDbContainer2: \n{}", stdout2); + + final String stdout3 = + mongoDbContainer2.execInContainer("nmap", "mongoDbContainer15") + .getStdout(); + + //THEN + assertTrue(stdout1.contains(successString)); + assertTrue(stdout2.contains(successString)); + assertFalse(stdout3.contains(successString)); + } + + private void testAccessibilityFromHost() { + super.shouldTestRsStatus(mongoDbContainer1); + super.shouldTestRsStatus(mongoDbContainer2); + } +} diff --git a/modules/mongodb/src/test/java/org/testcontainers/containers/integration/InitializationITTest.java b/modules/mongodb/src/test/java/org/testcontainers/containers/integration/InitializationITTest.java index 306b32e9239..3cd90c270f2 100644 --- a/modules/mongodb/src/test/java/org/testcontainers/containers/integration/InitializationITTest.java +++ b/modules/mongodb/src/test/java/org/testcontainers/containers/integration/InitializationITTest.java @@ -1,28 +1,19 @@ package org.testcontainers.containers.integration; -import com.mongodb.reactivestreams.client.MongoClients; -import lombok.SneakyThrows; -import lombok.val; -import org.bson.Document; -import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.reactivestreams.Publisher; import org.testcontainers.containers.MongoDbContainer; import org.testcontainers.containers.core.IntegrationTest; -import org.testcontainers.containers.util.SubscriberHelperUtils; - -import java.util.List; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; @IntegrationTest -class InitializationITTest { +class InitializationITTest extends BaseInitializationITTest { + // creatingMongoDbContainer { private final MongoDbContainer mongoDbContainer = new MongoDbContainer("mongo:4.2.0"); + // } + // startingStoppingMongoDbContainer { @BeforeEach void setUp() { mongoDbContainer.start(); @@ -32,70 +23,15 @@ void setUp() { void tearDown() { mongoDbContainer.stop(); } + // } @Test void shouldTestRsStatus() { - // GIVEN - val replicaSetUrl = mongoDbContainer.getReplicaSetUrl(); - assertNotNull(replicaSetUrl); - - val mongoReactiveClient = MongoClients.create(replicaSetUrl); - val db = mongoReactiveClient.getDatabase("admin"); - - // WHEN + THEN - try { - val subscriber = getSubscriber( - db.runCommand(new Document("replSetGetStatus", 1)) - ); - val document = getDocument(subscriber.getReceived()); - assertEquals(Double.valueOf("1"), document.get("ok", Double.class)); - val mongoNodesActual = document.getList("members", Document.class); - assertEquals(1, mongoNodesActual.size()); - assertEquals(1, mongoNodesActual.get(0).getInteger("state")); - } finally { - mongoReactiveClient.close(); - } - } - - @NotNull - private Document getDocument(List documents) { - return documents.get(0); + super.shouldTestRsStatus(mongoDbContainer); } @Test void shouldTestVersionAndDockerImageName() { - // GIVEN - val replicaSetUrl = mongoDbContainer.getReplicaSetUrl(); - assertNotNull(replicaSetUrl); - val dockerImageName = mongoDbContainer.getDockerImageName(); - assertNotNull(dockerImageName); - val mongoClient = MongoClients.create(replicaSetUrl); - val db = mongoClient.getDatabase("test"); - - // WHEN + THEN - try { - val subscriber = getSubscriber( - db.runCommand(new Document("buildInfo", 1)) - ); - val version = getDocument(subscriber.getReceived()).getString("version"); - val versionExpected = - dockerImageName.substring(dockerImageName.indexOf(":") + 1); - assertEquals( - versionExpected, - version - ); - } finally { - mongoClient.close(); - } - } - - @SneakyThrows - private SubscriberHelperUtils.PrintDocumentSubscriber getSubscriber( - Publisher command - ) { - val subscriber = new SubscriberHelperUtils.PrintDocumentSubscriber(); - command.subscribe(subscriber); - subscriber.await(); - return subscriber; + super.shouldTestVersionAndDockerImageName(mongoDbContainer); } } diff --git a/modules/mongodb/src/test/java/org/testcontainers/containers/integration/TransactionITTest.java b/modules/mongodb/src/test/java/org/testcontainers/containers/integration/TransactionITTest.java index 64b04e7d727..62d99ce4e8c 100644 --- a/modules/mongodb/src/test/java/org/testcontainers/containers/integration/TransactionITTest.java +++ b/modules/mongodb/src/test/java/org/testcontainers/containers/integration/TransactionITTest.java @@ -4,8 +4,11 @@ import com.mongodb.ReadPreference; import com.mongodb.TransactionOptions; import com.mongodb.WriteConcern; +import com.mongodb.client.ClientSession; +import com.mongodb.client.MongoClient; +import com.mongodb.client.MongoClients; +import com.mongodb.client.MongoCollection; import com.mongodb.client.TransactionBody; -import lombok.val; import org.bson.Document; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -37,27 +40,29 @@ void tearDown() { @Test void shouldExecuteTransactions() { //GIVEN - val mongoRsUrl = mongoDbContainer.getReplicaSetUrl(); + final String mongoRsUrl = mongoDbContainer.getReplicaSetUrl(); assertNotNull(mongoRsUrl); - val mongoSyncClient = com.mongodb.client.MongoClients.create(mongoRsUrl); + final MongoClient mongoSyncClient = MongoClients.create(mongoRsUrl); mongoSyncClient.getDatabase("mydb1").getCollection("foo") .withWriteConcern(WriteConcern.MAJORITY).insertOne(new Document("abc", 0)); mongoSyncClient.getDatabase("mydb2").getCollection("bar") .withWriteConcern(WriteConcern.MAJORITY).insertOne(new Document("xyz", 0)); - val clientSession = mongoSyncClient.startSession(); - val txnOptions = TransactionOptions.builder() + final ClientSession clientSession = mongoSyncClient.startSession(); + final TransactionOptions txnOptions = TransactionOptions.builder() .readPreference(ReadPreference.primary()) .readConcern(ReadConcern.LOCAL) .writeConcern(WriteConcern.MAJORITY) .build(); - val trxResult = "Inserted into collections in different databases"; + final String trxResult = "Inserted into collections in different databases"; //WHEN + THEN TransactionBody txnBody = () -> { - val coll1 = mongoSyncClient.getDatabase("mydb1").getCollection("foo"); - val coll2 = mongoSyncClient.getDatabase("mydb2").getCollection("bar"); + final MongoCollection coll1 = + mongoSyncClient.getDatabase("mydb1").getCollection("foo"); + final MongoCollection coll2 = + mongoSyncClient.getDatabase("mydb2").getCollection("bar"); coll1.insertOne(clientSession, new Document("abc", 1)); coll2.insertOne(clientSession, new Document("xyz", 999)); @@ -65,7 +70,7 @@ void shouldExecuteTransactions() { }; try { - val trxResultActual = clientSession.withTransaction(txnBody, txnOptions); + final String trxResultActual = clientSession.withTransaction(txnBody, txnOptions); assertEquals(trxResult, trxResultActual); } catch (RuntimeException re) { throw new JUnitException(re.getMessage(), re); From 957cd51b2a6eadcc62ee035ea746ed20bde713f2 Mon Sep 17 00:00:00 2001 From: Konstantin Silaev Date: Mon, 14 Oct 2019 10:56:03 +0300 Subject: [PATCH 07/38] Small fix mongodb.md --- docs/modules/databases/mongodb.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/modules/databases/mongodb.md b/docs/modules/databases/mongodb.md index a49c4aba386..0f9b832076f 100644 --- a/docs/modules/databases/mongodb.md +++ b/docs/modules/databases/mongodb.md @@ -20,7 +20,7 @@ The following example shows how to create a MongoDbContainer -[Start and stop the MongoDB container](../../../modules/mongodb/src/test/java/org/testcontainers/containers/integration/InitializationITTest.java) inside_block:startingStoppingMongoDbContainer +[Starting and stoping a MongoDB container](../../../modules/mongodb/src/test/java/org/testcontainers/containers/integration/InitializationITTest.java) inside_block:startingStoppingMongoDbContainer #### Motivation From 991a5f8f2b5444e6198652b317000f81433faaf7 Mon Sep 17 00:00:00 2001 From: Konstantin Silaev Date: Mon, 14 Oct 2019 11:36:43 +0300 Subject: [PATCH 08/38] Small fix mongodb.md --- docs/modules/databases/mongodb.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/modules/databases/mongodb.md b/docs/modules/databases/mongodb.md index 0f9b832076f..ef488d6a852 100644 --- a/docs/modules/databases/mongodb.md +++ b/docs/modules/databases/mongodb.md @@ -20,7 +20,7 @@ The following example shows how to create a MongoDbContainer -[Starting and stoping a MongoDB container](../../../modules/mongodb/src/test/java/org/testcontainers/containers/integration/InitializationITTest.java) inside_block:startingStoppingMongoDbContainer +[Starting and stopping a MongoDB container](../../../modules/mongodb/src/test/java/org/testcontainers/containers/integration/InitializationITTest.java) inside_block:startingStoppingMongoDbContainer #### Motivation From 184d4e2ccbd65bf58152d4fd78a588e3bf44e89a Mon Sep 17 00:00:00 2001 From: Konstantin Silaev Date: Fri, 6 Mar 2020 17:03:28 +0300 Subject: [PATCH 09/38] Fix container exit code check --- .../org/testcontainers/containers/MongoDbContainer.java | 8 ++++---- .../testcontainers/containers/MongoDbContainerTest.java | 5 +++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDbContainer.java b/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDbContainer.java index b27f0a9af35..bfe6ab15219 100644 --- a/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDbContainer.java +++ b/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDbContainer.java @@ -16,7 +16,7 @@ */ @Slf4j public class MongoDbContainer extends GenericContainer { - static final int ERROR_CONTAINER_EXIT_CODE = 1; + private static final int CONTAINER_EXIT_CODE_OK = 0; static final int MONGODB_INTERNAL_PORT = 27017; private static final int AWAIT_INIT_REPLICA_SET_ATTEMPTS = 30; private static final String MONGODB_VERSION_DEFAULT = "4.0.10"; @@ -88,8 +88,8 @@ private String[] buildMongoEvalCommand(final String command) { } private void checkMongoNodeExitCode(final Container.ExecResult execResult) { - if (execResult.getExitCode() == ERROR_CONTAINER_EXIT_CODE) { - final String errorMessage = String.format("An error occurred: %s", execResult.getStderr()); + if (execResult.getExitCode() != CONTAINER_EXIT_CODE_OK) { + final String errorMessage = String.format("An error occurred: %s", execResult.getStdout()); log.error(errorMessage); throw new ReplicaSetInitializationException(errorMessage); } @@ -113,7 +113,7 @@ private String buildMongoWaitCommand() { private void checkMongoNodeExitCodeAfterWaiting( final Container.ExecResult execResultWaitForMaster ) { - if (execResultWaitForMaster.getExitCode() == ERROR_CONTAINER_EXIT_CODE) { + if (execResultWaitForMaster.getExitCode() != CONTAINER_EXIT_CODE_OK) { final String errorMessage = String.format( "A single node replica set was not initialized in a set timeout: %d attempts", AWAIT_INIT_REPLICA_SET_ATTEMPTS diff --git a/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDbContainerTest.java b/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDbContainerTest.java index 2e24a1faaeb..433ac6c990e 100644 --- a/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDbContainerTest.java +++ b/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDbContainerTest.java @@ -23,6 +23,7 @@ */ @ExtendWith(MockitoExtension.class) class MongoDbContainerTest { + private static final int CONTAINER_ERROR_EXIT_CODE = 252; @Spy private static MongoDbContainer MONGODB_CONTAINER = new MongoDbContainer(); @@ -41,7 +42,7 @@ void shouldNotInitReplicaSetBecauseOfExecInitReplicaSet() //GIVEN Container.ExecResult mockExecResult = mock(Container.ExecResult.class); when(mockExecResult.getExitCode()) - .thenReturn(MongoDbContainer.ERROR_CONTAINER_EXIT_CODE); + .thenReturn(CONTAINER_ERROR_EXIT_CODE); doReturn(mockExecResult).when(MONGODB_CONTAINER).execInContainer(any()); final int mappedPort = 37723; lenient().doReturn(mappedPort).when(MONGODB_CONTAINER) @@ -65,7 +66,7 @@ void shouldNotInitReplicaSetBecauseOfWaitCommand() throws IOException, Interrupt when(mockExecResult1.getExitCode()) .thenReturn(0); when(mockExecResult2.getExitCode()) - .thenReturn(MongoDbContainer.ERROR_CONTAINER_EXIT_CODE); + .thenReturn(252); doReturn(mockExecResult1, mockExecResult2) .when(MONGODB_CONTAINER).execInContainer(any()); final int mappedPort = 37723; From cfe5177d7c75c823262ca888a7f1e1c9869147dc Mon Sep 17 00:00:00 2001 From: Konstantin Silaev Date: Fri, 13 Mar 2020 15:51:35 +0300 Subject: [PATCH 10/38] Adjust tests and build.gradle to follow Testcontainers module test configuration for consistency, Move configure() to be a part of constructors --- modules/mongodb/build.gradle | 32 -------------- .../containers/MongoDbContainer.java | 20 +++++---- .../containers/MongoDbContainerTest.java | 43 ++++++++----------- .../containers/core/IntegrationTest.java | 14 ------ .../integration/BaseInitializationITTest.java | 7 +-- .../integration/CommunicationITTest.java | 25 ++++++----- .../integration/InitializationITTest.java | 22 +++++----- .../integration/TransactionITTest.java | 28 ++++++------ .../src/test/resources/logback-test.xml | 2 +- 9 files changed, 69 insertions(+), 124 deletions(-) delete mode 100644 modules/mongodb/src/test/java/org/testcontainers/containers/core/IntegrationTest.java diff --git a/modules/mongodb/build.gradle b/modules/mongodb/build.gradle index e544f166300..001fe834c52 100644 --- a/modules/mongodb/build.gradle +++ b/modules/mongodb/build.gradle @@ -3,39 +3,7 @@ description = "Testcontainers :: MongoDB" dependencies { compile project(':testcontainers') - testCompile("org.junit.jupiter:junit-jupiter-api:5.5.1") - testCompile('org.mockito:mockito-core:3.0.0') - testCompile('ch.qos.logback:logback-classic:1.2.3') - testRuntime("org.junit.jupiter:junit-jupiter-engine:5.5.1") testCompile("org.mongodb:mongodb-driver-reactivestreams:1.12.0") testCompile("org.mongodb:mongodb-driver-sync:3.11.0") testCompile("org.mockito:mockito-junit-jupiter:3.0.0") } - -tasks.withType(Test) { Test task -> - task.useJUnitPlatform { JUnitPlatformOptions ops -> - ops.excludeTags("integration-test") - } - - task.failFast = true - - testLogging.showStandardStreams = true - testLogging.exceptionFormat = 'full' -} - -task integrationTest(type: Test) { Test task -> - task.useJUnitPlatform { JUnitPlatformOptions ops -> - ops.includeTags("integration-test") - } - - task.failFast = true - - testLogging.showStandardStreams = true - testLogging.exceptionFormat = 'full' - - task.minHeapSize('32m') - task.maxHeapSize('256m') - - check.dependsOn integrationTest - integrationTest.mustRunAfter test -} diff --git a/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDbContainer.java b/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDbContainer.java index bfe6ab15219..b31361a6e4e 100644 --- a/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDbContainer.java +++ b/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDbContainer.java @@ -25,10 +25,21 @@ public class MongoDbContainer extends GenericContainer { public MongoDbContainer() { super("mongo:" + MONGODB_VERSION_DEFAULT); + configureMongoDbContainer(); + } public MongoDbContainer(@NonNull final String dockerImageName) { super(dockerImageName); + configureMongoDbContainer(); + } + + private void configureMongoDbContainer() { + withExposedPorts(MONGODB_INTERNAL_PORT); + withCommand("--replSet", "docker-rs"); + waitingFor( + Wait.forLogMessage(".*waiting for connections on port.*", 1) + ); } public String getReplicaSetUrl() { @@ -58,15 +69,6 @@ private void logReplicaSetStatus() { ); } - @Override - protected void configure() { - withExposedPorts(MONGODB_INTERNAL_PORT); - withCommand("--replSet", "docker-rs"); - waitingFor( - Wait.forLogMessage(".*waiting for connections on port.*", 1) - ); - } - private String getMongoReplicaSetInitializer() { final String containerIpAddress = getContainerIpAddress(); final int containerPort = LOCALHOST.equals(containerIpAddress) diff --git a/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDbContainerTest.java b/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDbContainerTest.java index 433ac6c990e..0e02c6a1882 100644 --- a/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDbContainerTest.java +++ b/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDbContainerTest.java @@ -1,14 +1,13 @@ package org.testcontainers.containers; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.junit.jupiter.api.function.Executable; + +import org.junit.Test; +import org.junit.runner.RunWith; import org.mockito.Spy; -import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.MockitoJUnitRunner; import java.io.IOException; -import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.lenient; @@ -21,23 +20,23 @@ * * @author Konstantin Silaev on 9/30/2019 */ -@ExtendWith(MockitoExtension.class) -class MongoDbContainerTest { +@RunWith(MockitoJUnitRunner.class) +public class MongoDbContainerTest { private static final int CONTAINER_ERROR_EXIT_CODE = 252; @Spy private static MongoDbContainer MONGODB_CONTAINER = new MongoDbContainer(); - @Test - void shouldNotGetReplicaSetUrlBecauseOfNotStartedContainer() { + @Test(expected = IllegalStateException.class) + public void shouldNotGetReplicaSetUrlBecauseOfNotStartedContainer() { //WHEN - Executable executable = () -> MONGODB_CONTAINER.getReplicaSetUrl(); + MONGODB_CONTAINER.getReplicaSetUrl(); //THEN - assertThrows(IllegalStateException.class, executable); + //expected IllegalStateException.class } - @Test - void shouldNotInitReplicaSetBecauseOfExecInitReplicaSet() + @Test(expected = MongoDbContainer.ReplicaSetInitializationException.class) + public void shouldNotInitReplicaSetBecauseOfExecInitReplicaSet() throws IOException, InterruptedException { //GIVEN Container.ExecResult mockExecResult = mock(Container.ExecResult.class); @@ -49,17 +48,14 @@ void shouldNotInitReplicaSetBecauseOfExecInitReplicaSet() .getMappedPort(MongoDbContainer.MONGODB_INTERNAL_PORT); //WHEN - Executable executable = () -> MONGODB_CONTAINER.initReplicaSet(); + MONGODB_CONTAINER.initReplicaSet(); //THEN - assertThrows( - MongoDbContainer.ReplicaSetInitializationException.class, - executable - ); + //expected = MongoDbContainer.ReplicaSetInitializationException.class } - @Test - void shouldNotInitReplicaSetBecauseOfWaitCommand() throws IOException, InterruptedException { + @Test(expected = MongoDbContainer.ReplicaSetInitializationException.class) + public void shouldNotInitReplicaSetBecauseOfWaitCommand() throws IOException, InterruptedException { //GIVEN final Container.ExecResult mockExecResult1 = mock(Container.ExecResult.class); final Container.ExecResult mockExecResult2 = mock(Container.ExecResult.class); @@ -74,12 +70,9 @@ void shouldNotInitReplicaSetBecauseOfWaitCommand() throws IOException, Interrupt .getMappedPort(MongoDbContainer.MONGODB_INTERNAL_PORT); //WHEN - Executable executable = () -> MONGODB_CONTAINER.initReplicaSet(); + MONGODB_CONTAINER.initReplicaSet(); //THEN - assertThrows( - MongoDbContainer.ReplicaSetInitializationException.class, - executable - ); + //expected = MongoDbContainer.ReplicaSetInitializationException.class } } diff --git a/modules/mongodb/src/test/java/org/testcontainers/containers/core/IntegrationTest.java b/modules/mongodb/src/test/java/org/testcontainers/containers/core/IntegrationTest.java deleted file mode 100644 index 2c7c17d9d09..00000000000 --- a/modules/mongodb/src/test/java/org/testcontainers/containers/core/IntegrationTest.java +++ /dev/null @@ -1,14 +0,0 @@ -package org.testcontainers.containers.core; - -import org.junit.jupiter.api.Tag; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.TYPE) -@Tag("integration-test") -public @interface IntegrationTest { -} diff --git a/modules/mongodb/src/test/java/org/testcontainers/containers/integration/BaseInitializationITTest.java b/modules/mongodb/src/test/java/org/testcontainers/containers/integration/BaseInitializationITTest.java index 8f31b6d8f9e..d827440de7c 100644 --- a/modules/mongodb/src/test/java/org/testcontainers/containers/integration/BaseInitializationITTest.java +++ b/modules/mongodb/src/test/java/org/testcontainers/containers/integration/BaseInitializationITTest.java @@ -12,8 +12,9 @@ import java.util.List; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + abstract class BaseInitializationITTest { @@ -37,7 +38,7 @@ void shouldTestRsStatus( final List mongoNodesActual = document.getList("members", Document.class); assertEquals(1, mongoNodesActual.size()); - assertEquals(1, mongoNodesActual.get(0).getInteger("state")); + assertEquals(Integer.valueOf(1), mongoNodesActual.get(0).getInteger("state")); } finally { mongoReactiveClient.close(); } diff --git a/modules/mongodb/src/test/java/org/testcontainers/containers/integration/CommunicationITTest.java b/modules/mongodb/src/test/java/org/testcontainers/containers/integration/CommunicationITTest.java index a370ed16006..af86cf6d884 100644 --- a/modules/mongodb/src/test/java/org/testcontainers/containers/integration/CommunicationITTest.java +++ b/modules/mongodb/src/test/java/org/testcontainers/containers/integration/CommunicationITTest.java @@ -2,15 +2,15 @@ import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; import org.testcontainers.containers.MongoDbContainer; import org.testcontainers.containers.Network; -import org.testcontainers.containers.core.IntegrationTest; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + /** * Tests both: @@ -26,9 +26,8 @@ * @see dockerfile on DockerHub * @see mongodb-net-utils on GitHub */ -@IntegrationTest @Slf4j -class CommunicationITTest extends BaseInitializationITTest { +public class CommunicationITTest extends BaseInitializationITTest { private final Network network = Network.newNetwork(); private final MongoDbContainer mongoDbContainer1 = new MongoDbContainer("s256/mongodb-net-utils:4.0.10") @@ -39,20 +38,20 @@ class CommunicationITTest extends BaseInitializationITTest { .withNetworkAliases("mongoDbContainer2") .withNetwork(network); - @BeforeEach - void setUp() { + @Before + public void setUp() { mongoDbContainer1.start(); mongoDbContainer2.start(); } - @AfterEach - void tearDown() { + @After + public void tearDown() { mongoDbContainer1.stop(); mongoDbContainer2.stop(); } @Test - void shouldTestCommunication() { + public void shouldTestCommunication() { testCommunicationBetweenNodes(); testAccessibilityFromHost(); } diff --git a/modules/mongodb/src/test/java/org/testcontainers/containers/integration/InitializationITTest.java b/modules/mongodb/src/test/java/org/testcontainers/containers/integration/InitializationITTest.java index 3cd90c270f2..78193dc5ec2 100644 --- a/modules/mongodb/src/test/java/org/testcontainers/containers/integration/InitializationITTest.java +++ b/modules/mongodb/src/test/java/org/testcontainers/containers/integration/InitializationITTest.java @@ -1,37 +1,35 @@ package org.testcontainers.containers.integration; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; import org.testcontainers.containers.MongoDbContainer; -import org.testcontainers.containers.core.IntegrationTest; -@IntegrationTest -class InitializationITTest extends BaseInitializationITTest { +public class InitializationITTest extends BaseInitializationITTest { // creatingMongoDbContainer { private final MongoDbContainer mongoDbContainer = new MongoDbContainer("mongo:4.2.0"); // } // startingStoppingMongoDbContainer { - @BeforeEach - void setUp() { + @Before + public void setUp() { mongoDbContainer.start(); } - @AfterEach - void tearDown() { + @After + public void tearDown() { mongoDbContainer.stop(); } // } @Test - void shouldTestRsStatus() { + public void shouldTestRsStatus() { super.shouldTestRsStatus(mongoDbContainer); } @Test - void shouldTestVersionAndDockerImageName() { + public void shouldTestVersionAndDockerImageName() { super.shouldTestVersionAndDockerImageName(mongoDbContainer); } } diff --git a/modules/mongodb/src/test/java/org/testcontainers/containers/integration/TransactionITTest.java b/modules/mongodb/src/test/java/org/testcontainers/containers/integration/TransactionITTest.java index 62d99ce4e8c..7bec896e4d4 100644 --- a/modules/mongodb/src/test/java/org/testcontainers/containers/integration/TransactionITTest.java +++ b/modules/mongodb/src/test/java/org/testcontainers/containers/integration/TransactionITTest.java @@ -10,27 +10,25 @@ import com.mongodb.client.MongoCollection; import com.mongodb.client.TransactionBody; import org.bson.Document; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.platform.commons.JUnitException; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; import org.testcontainers.containers.MongoDbContainer; -import org.testcontainers.containers.core.IntegrationTest; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; -@IntegrationTest -class TransactionITTest { + +public class TransactionITTest { private final MongoDbContainer mongoDbContainer = new MongoDbContainer(); - @BeforeEach - void setUp() { + @Before + public void setUp() { mongoDbContainer.start(); } - @AfterEach - void tearDown() { + @After + public void tearDown() { mongoDbContainer.stop(); } @@ -38,7 +36,7 @@ void tearDown() { * Taken from https://docs.mongodb.com */ @Test - void shouldExecuteTransactions() { + public void shouldExecuteTransactions() { //GIVEN final String mongoRsUrl = mongoDbContainer.getReplicaSetUrl(); assertNotNull(mongoRsUrl); @@ -73,7 +71,7 @@ void shouldExecuteTransactions() { final String trxResultActual = clientSession.withTransaction(txnBody, txnOptions); assertEquals(trxResult, trxResultActual); } catch (RuntimeException re) { - throw new JUnitException(re.getMessage(), re); + throw new IllegalStateException(re.getMessage(), re); } finally { clientSession.close(); mongoSyncClient.close(); diff --git a/modules/mongodb/src/test/resources/logback-test.xml b/modules/mongodb/src/test/resources/logback-test.xml index 285197eeead..535e406fc13 100644 --- a/modules/mongodb/src/test/resources/logback-test.xml +++ b/modules/mongodb/src/test/resources/logback-test.xml @@ -12,5 +12,5 @@ - + From a74d766ac47ecbf1ae7f9802dde51c2689aeea85 Mon Sep 17 00:00:00 2001 From: Konstantin Silaev Date: Fri, 13 Mar 2020 17:01:13 +0300 Subject: [PATCH 11/38] Add Javadoc explaining the use of LOCALHOST --- .../containers/MongoDbContainer.java | 35 ++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDbContainer.java b/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDbContainer.java index b31361a6e4e..7f7bba425d7 100644 --- a/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDbContainer.java +++ b/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDbContainer.java @@ -69,9 +69,42 @@ private void logReplicaSetStatus() { ); } + /** + * Gets a string to initialize MongoDB replica set. + * + * The following explains why LOCALHOST is used here. + * When it comes to a Single node replica set, it requires a proper + * port setting depending on an environment. + * The table below shows an example demonstrating such specific in detail: + * + *

+ * + * + * + * + * + * + * + * + * + * + *
Difference + * local Docker host example + * local Docker host running tests from inside a container with mapping the Docker socket or
remote Docker daemon + *
a host string to initialize a replica set + * localhost:27017
Despite the fact that Docker allocates 33538 (for instance) as a random port for a container + *
172.17.0.1:33542 + *
a url to use with a Java Mongo driver + * mongodb://localhost:33538/test + * mongodb://172.17.0.1:33542/test + *
+ *
+ * + * @return String to initialize MongoDB replica set + */ private String getMongoReplicaSetInitializer() { final String containerIpAddress = getContainerIpAddress(); - final int containerPort = LOCALHOST.equals(containerIpAddress) + final int containerPort = LOCALHOST.equalsIgnoreCase(containerIpAddress) ? MONGODB_INTERNAL_PORT : getMappedPort(MONGODB_INTERNAL_PORT); final String initializer = String.format( From 7620cb852e835e724f125f680dc23f57a842f15c Mon Sep 17 00:00:00 2001 From: Konstantin Silaev Date: Mon, 16 Mar 2020 14:26:26 +0300 Subject: [PATCH 12/38] Refactor to use a default replica set configuration --- .../containers/MongoDbContainer.java | 52 +------------------ 1 file changed, 1 insertion(+), 51 deletions(-) diff --git a/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDbContainer.java b/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDbContainer.java index 7f7bba425d7..600671f66a8 100644 --- a/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDbContainer.java +++ b/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDbContainer.java @@ -20,7 +20,6 @@ public class MongoDbContainer extends GenericContainer { static final int MONGODB_INTERNAL_PORT = 27017; private static final int AWAIT_INIT_REPLICA_SET_ATTEMPTS = 30; private static final String MONGODB_VERSION_DEFAULT = "4.0.10"; - private static final String LOCALHOST = "localhost"; private static final String MONGODB_DATABASE_NAME_DEFAULT = "test"; public MongoDbContainer() { @@ -69,55 +68,6 @@ private void logReplicaSetStatus() { ); } - /** - * Gets a string to initialize MongoDB replica set. - * - * The following explains why LOCALHOST is used here. - * When it comes to a Single node replica set, it requires a proper - * port setting depending on an environment. - * The table below shows an example demonstrating such specific in detail: - * - *
- * - * - * - * - * - * - * - * - * - * - *
Difference - * local Docker host example - * local Docker host running tests from inside a container with mapping the Docker socket or
remote Docker daemon - *
a host string to initialize a replica set - * localhost:27017
Despite the fact that Docker allocates 33538 (for instance) as a random port for a container - *
172.17.0.1:33542 - *
a url to use with a Java Mongo driver - * mongodb://localhost:33538/test - * mongodb://172.17.0.1:33542/test - *
- *
- * - * @return String to initialize MongoDB replica set - */ - private String getMongoReplicaSetInitializer() { - final String containerIpAddress = getContainerIpAddress(); - final int containerPort = LOCALHOST.equalsIgnoreCase(containerIpAddress) - ? MONGODB_INTERNAL_PORT - : getMappedPort(MONGODB_INTERNAL_PORT); - final String initializer = String.format( - "rs.initiate({\n" + - " \"_id\": \"docker-rs\",\n" + - " \"members\": [\n" + - " {\"_id\": %d, \"host\": \"%s:%d\"}\n ]\n});", - 0, containerIpAddress, containerPort - ); - log.debug(initializer); - return initializer; - } - private String[] buildMongoEvalCommand(final String command) { return new String[]{"mongo", "--eval", command}; } @@ -162,7 +112,7 @@ private void checkMongoNodeExitCodeAfterWaiting( void initReplicaSet() { log.debug("Initializing a single node node replica set..."); final ExecResult execResultInitRs = execInContainer( - buildMongoEvalCommand(getMongoReplicaSetInitializer()) + buildMongoEvalCommand("rs.initiate();") ); log.debug(execResultInitRs.getStdout()); checkMongoNodeExitCode(execResultInitRs); From aad0e7621d5885ade2bde46fdefa79b41e4becf6 Mon Sep 17 00:00:00 2001 From: Konstantin Silaev Date: Fri, 17 Apr 2020 13:25:27 +0300 Subject: [PATCH 13/38] Remove extra tests --- modules/mongodb/build.gradle | 5 +- .../{integration => }/TransactionITTest.java | 3 +- .../integration/BaseInitializationITTest.java | 86 ---------- .../integration/CommunicationITTest.java | 89 ---------- .../integration/InitializationITTest.java | 35 ---- .../util/SubscriberHelperUtils.java | 154 ------------------ 6 files changed, 3 insertions(+), 369 deletions(-) rename modules/mongodb/src/test/java/org/testcontainers/containers/{integration => }/TransactionITTest.java (96%) delete mode 100644 modules/mongodb/src/test/java/org/testcontainers/containers/integration/BaseInitializationITTest.java delete mode 100644 modules/mongodb/src/test/java/org/testcontainers/containers/integration/CommunicationITTest.java delete mode 100644 modules/mongodb/src/test/java/org/testcontainers/containers/integration/InitializationITTest.java delete mode 100644 modules/mongodb/src/test/java/org/testcontainers/containers/util/SubscriberHelperUtils.java diff --git a/modules/mongodb/build.gradle b/modules/mongodb/build.gradle index 001fe834c52..0a87bd370e4 100644 --- a/modules/mongodb/build.gradle +++ b/modules/mongodb/build.gradle @@ -3,7 +3,6 @@ description = "Testcontainers :: MongoDB" dependencies { compile project(':testcontainers') - testCompile("org.mongodb:mongodb-driver-reactivestreams:1.12.0") - testCompile("org.mongodb:mongodb-driver-sync:3.11.0") - testCompile("org.mockito:mockito-junit-jupiter:3.0.0") + testCompile("org.mongodb:mongodb-driver-sync:4.0.2") + testCompile("org.mockito:mockito-junit-jupiter:3.3.3") } diff --git a/modules/mongodb/src/test/java/org/testcontainers/containers/integration/TransactionITTest.java b/modules/mongodb/src/test/java/org/testcontainers/containers/TransactionITTest.java similarity index 96% rename from modules/mongodb/src/test/java/org/testcontainers/containers/integration/TransactionITTest.java rename to modules/mongodb/src/test/java/org/testcontainers/containers/TransactionITTest.java index 7bec896e4d4..28a5b078164 100644 --- a/modules/mongodb/src/test/java/org/testcontainers/containers/integration/TransactionITTest.java +++ b/modules/mongodb/src/test/java/org/testcontainers/containers/TransactionITTest.java @@ -1,4 +1,4 @@ -package org.testcontainers.containers.integration; +package org.testcontainers.containers; import com.mongodb.ReadConcern; import com.mongodb.ReadPreference; @@ -13,7 +13,6 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; -import org.testcontainers.containers.MongoDbContainer; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; diff --git a/modules/mongodb/src/test/java/org/testcontainers/containers/integration/BaseInitializationITTest.java b/modules/mongodb/src/test/java/org/testcontainers/containers/integration/BaseInitializationITTest.java deleted file mode 100644 index d827440de7c..00000000000 --- a/modules/mongodb/src/test/java/org/testcontainers/containers/integration/BaseInitializationITTest.java +++ /dev/null @@ -1,86 +0,0 @@ -package org.testcontainers.containers.integration; - -import com.mongodb.reactivestreams.client.MongoClient; -import com.mongodb.reactivestreams.client.MongoClients; -import com.mongodb.reactivestreams.client.MongoDatabase; -import lombok.SneakyThrows; -import org.bson.Document; -import org.jetbrains.annotations.NotNull; -import org.reactivestreams.Publisher; -import org.testcontainers.containers.MongoDbContainer; -import org.testcontainers.containers.util.SubscriberHelperUtils; - -import java.util.List; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; - - -abstract class BaseInitializationITTest { - - void shouldTestRsStatus( - final MongoDbContainer mongoDbContainer - ) { - // GIVEN - final String replicaSetUrl = mongoDbContainer.getReplicaSetUrl(); - assertNotNull(replicaSetUrl); - - final MongoClient mongoReactiveClient = MongoClients.create(replicaSetUrl); - final MongoDatabase db = mongoReactiveClient.getDatabase("admin"); - - // WHEN + THEN - try { - final SubscriberHelperUtils.PrintDocumentSubscriber subscriber = getSubscriber( - db.runCommand(new Document("replSetGetStatus", 1)) - ); - final Document document = getDocument(subscriber.getReceived()); - assertEquals(Double.valueOf("1"), document.get("ok", Double.class)); - final List mongoNodesActual = - document.getList("members", Document.class); - assertEquals(1, mongoNodesActual.size()); - assertEquals(Integer.valueOf(1), mongoNodesActual.get(0).getInteger("state")); - } finally { - mongoReactiveClient.close(); - } - } - - @NotNull - private Document getDocument(List documents) { - return documents.get(0); - } - - void shouldTestVersionAndDockerImageName( - final MongoDbContainer mongoDbContainer - ) { - // GIVEN - final String replicaSetUrl = mongoDbContainer.getReplicaSetUrl(); - assertNotNull(replicaSetUrl); - final String dockerImageName = mongoDbContainer.getDockerImageName(); - assertNotNull(dockerImageName); - final MongoClient mongoClient = MongoClients.create(replicaSetUrl); - final MongoDatabase db = mongoClient.getDatabase("test"); - - // WHEN + THEN - try { - final SubscriberHelperUtils.PrintDocumentSubscriber subscriber = getSubscriber( - db.runCommand(new Document("buildInfo", 1)) - ); - final String version = getDocument(subscriber.getReceived()).getString("version"); - final String versionExpected = dockerImageName.substring(dockerImageName.indexOf(":") + 1); - assertEquals(versionExpected, version); - } finally { - mongoClient.close(); - } - } - - @SneakyThrows - private SubscriberHelperUtils.PrintDocumentSubscriber getSubscriber( - Publisher command - ) { - final SubscriberHelperUtils.PrintDocumentSubscriber subscriber = - new SubscriberHelperUtils.PrintDocumentSubscriber(); - command.subscribe(subscriber); - subscriber.await(); - return subscriber; - } -} diff --git a/modules/mongodb/src/test/java/org/testcontainers/containers/integration/CommunicationITTest.java b/modules/mongodb/src/test/java/org/testcontainers/containers/integration/CommunicationITTest.java deleted file mode 100644 index af86cf6d884..00000000000 --- a/modules/mongodb/src/test/java/org/testcontainers/containers/integration/CommunicationITTest.java +++ /dev/null @@ -1,89 +0,0 @@ -package org.testcontainers.containers.integration; - -import lombok.SneakyThrows; -import lombok.extern.slf4j.Slf4j; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.testcontainers.containers.MongoDbContainer; -import org.testcontainers.containers.Network; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - - -/** - * Tests both: - *
    - *
  • the communication between 2 MongoDbContainers
  • - *
  • the accessibility of these MongoDbContainers
  • - *
- * from a host. - *

- * Note that s256/mongodb-net-utils is an official MongoDB Docker file with the nmap package installed. - * Its tag corresponds to an official MongoDB Docker file tag. - * - * @see dockerfile on DockerHub - * @see mongodb-net-utils on GitHub - */ -@Slf4j -public class CommunicationITTest extends BaseInitializationITTest { - private final Network network = Network.newNetwork(); - private final MongoDbContainer mongoDbContainer1 = - new MongoDbContainer("s256/mongodb-net-utils:4.0.10") - .withNetworkAliases("mongoDbContainer1") - .withNetwork(network); - private final MongoDbContainer mongoDbContainer2 = - new MongoDbContainer("s256/mongodb-net-utils:4.0.10") - .withNetworkAliases("mongoDbContainer2") - .withNetwork(network); - - @Before - public void setUp() { - mongoDbContainer1.start(); - mongoDbContainer2.start(); - } - - @After - public void tearDown() { - mongoDbContainer1.stop(); - mongoDbContainer2.stop(); - } - - @Test - public void shouldTestCommunication() { - testCommunicationBetweenNodes(); - testAccessibilityFromHost(); - } - - @SneakyThrows - private void testCommunicationBetweenNodes() { - //GIVEN - final String successString = "Host is up"; - - //WHEN - final String stdout1 = - mongoDbContainer1.execInContainer("nmap", "mongoDbContainer2") - .getStdout(); - log.debug("Pinging mongoDbContainer2 from mongoDbContainer1: \n{}", stdout1); - - final String stdout2 = - mongoDbContainer2.execInContainer("nmap", "mongoDbContainer1") - .getStdout(); - log.debug("Pinging mongoDbContainer1 from mongoDbContainer2: \n{}", stdout2); - - final String stdout3 = - mongoDbContainer2.execInContainer("nmap", "mongoDbContainer15") - .getStdout(); - - //THEN - assertTrue(stdout1.contains(successString)); - assertTrue(stdout2.contains(successString)); - assertFalse(stdout3.contains(successString)); - } - - private void testAccessibilityFromHost() { - super.shouldTestRsStatus(mongoDbContainer1); - super.shouldTestRsStatus(mongoDbContainer2); - } -} diff --git a/modules/mongodb/src/test/java/org/testcontainers/containers/integration/InitializationITTest.java b/modules/mongodb/src/test/java/org/testcontainers/containers/integration/InitializationITTest.java deleted file mode 100644 index 78193dc5ec2..00000000000 --- a/modules/mongodb/src/test/java/org/testcontainers/containers/integration/InitializationITTest.java +++ /dev/null @@ -1,35 +0,0 @@ -package org.testcontainers.containers.integration; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.testcontainers.containers.MongoDbContainer; - -public class InitializationITTest extends BaseInitializationITTest { - // creatingMongoDbContainer { - private final MongoDbContainer mongoDbContainer = - new MongoDbContainer("mongo:4.2.0"); - // } - - // startingStoppingMongoDbContainer { - @Before - public void setUp() { - mongoDbContainer.start(); - } - - @After - public void tearDown() { - mongoDbContainer.stop(); - } - // } - - @Test - public void shouldTestRsStatus() { - super.shouldTestRsStatus(mongoDbContainer); - } - - @Test - public void shouldTestVersionAndDockerImageName() { - super.shouldTestVersionAndDockerImageName(mongoDbContainer); - } -} diff --git a/modules/mongodb/src/test/java/org/testcontainers/containers/util/SubscriberHelperUtils.java b/modules/mongodb/src/test/java/org/testcontainers/containers/util/SubscriberHelperUtils.java deleted file mode 100644 index ea26f3c3f55..00000000000 --- a/modules/mongodb/src/test/java/org/testcontainers/containers/util/SubscriberHelperUtils.java +++ /dev/null @@ -1,154 +0,0 @@ -package org.testcontainers.containers.util; - -import com.mongodb.MongoTimeoutException; -import lombok.extern.slf4j.Slf4j; -import org.bson.Document; -import org.reactivestreams.Subscriber; -import org.reactivestreams.Subscription; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; - -import static java.lang.String.format; - -/** - * Subscriber helper utility class taken from: - * - * @see https://github.com/mongodb/mongo-java-driver-reactivestreams - */ -@Slf4j -public final class SubscriberHelperUtils { - - private SubscriberHelperUtils() { - } - - /** - * A Subscriber that stores the publishers results and provides a latch so can block on completion. - * - * @param The publishers result type - */ - public static class ObservableSubscriber implements Subscriber { - private final List received; - private final List errors; - private final CountDownLatch latch; - private volatile Subscription subscription; - private volatile boolean completed; - - ObservableSubscriber() { - this.received = new ArrayList(); - this.errors = new ArrayList(); - this.latch = new CountDownLatch(1); - } - - @Override - public void onSubscribe(final Subscription s) { - subscription = s; - } - - @Override - public void onNext(final T t) { - received.add(t); - } - - @Override - public void onError(final Throwable t) { - errors.add(t); - onComplete(); - } - - @Override - public void onComplete() { - completed = true; - latch.countDown(); - } - - public Subscription getSubscription() { - return subscription; - } - - public List getReceived() { - return received; - } - - public Throwable getError() { - if (errors.size() > 0) { - return errors.get(0); - } - return null; - } - - public boolean isCompleted() { - return completed; - } - - public List get(final long timeout, final TimeUnit unit) throws Throwable { - return await(timeout, unit).getReceived(); - } - - public ObservableSubscriber await() throws Throwable { - return await(Long.MAX_VALUE, TimeUnit.MILLISECONDS); - } - - public ObservableSubscriber await(final long timeout, final TimeUnit unit) throws Throwable { - subscription.request(Integer.MAX_VALUE); - if (!latch.await(timeout, unit)) { - throw new MongoTimeoutException("Publisher onComplete timed out"); - } - if (!errors.isEmpty()) { - throw errors.get(0); - } - return this; - } - } - - /** - * A Subscriber that immediately requests Integer.MAX_VALUE onSubscribe - * - * @param The publishers result type - */ - public static class OperationSubscriber extends ObservableSubscriber { - - @Override - public void onSubscribe(final Subscription s) { - super.onSubscribe(s); - s.request(Integer.MAX_VALUE); - } - } - - /** - * A Subscriber that prints a message including the received items on completion - * - * @param The publishers result type - */ - public static class PrintSubscriber extends OperationSubscriber { - private final String message; - - /** - * A Subscriber that outputs a message onComplete. - * - * @param message the message to output onComplete - */ - public PrintSubscriber(final String message) { - this.message = message; - } - - @Override - public void onComplete() { - log.debug(format(message, getReceived())); - super.onComplete(); - } - } - - /** - * A Subscriber that prints the json version of each document - */ - public static class PrintDocumentSubscriber extends OperationSubscriber { - @Override - public void onNext(final Document document) { - super.onNext(document); - log.debug(document.toJson()); - } - } -} From b92d690f815dbee37b6ef58db35a6ecd6e2a9f98 Mon Sep 17 00:00:00 2001 From: Konstantin Silaev Date: Sat, 18 Apr 2020 12:44:28 +0300 Subject: [PATCH 14/38] Remove extra unit tests --- modules/mongodb/build.gradle | 1 - .../containers/MongoDbContainerTest.java | 119 +++++++++--------- .../containers/TransactionITTest.java | 79 ------------ 3 files changed, 60 insertions(+), 139 deletions(-) delete mode 100644 modules/mongodb/src/test/java/org/testcontainers/containers/TransactionITTest.java diff --git a/modules/mongodb/build.gradle b/modules/mongodb/build.gradle index 0a87bd370e4..57db1be21de 100644 --- a/modules/mongodb/build.gradle +++ b/modules/mongodb/build.gradle @@ -4,5 +4,4 @@ dependencies { compile project(':testcontainers') testCompile("org.mongodb:mongodb-driver-sync:4.0.2") - testCompile("org.mockito:mockito-junit-jupiter:3.3.3") } diff --git a/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDbContainerTest.java b/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDbContainerTest.java index 0e02c6a1882..3dfda19ff0d 100644 --- a/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDbContainerTest.java +++ b/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDbContainerTest.java @@ -1,78 +1,79 @@ package org.testcontainers.containers; - +import com.mongodb.ReadConcern; +import com.mongodb.ReadPreference; +import com.mongodb.TransactionOptions; +import com.mongodb.WriteConcern; +import com.mongodb.client.ClientSession; +import com.mongodb.client.MongoClient; +import com.mongodb.client.MongoClients; +import com.mongodb.client.MongoCollection; +import com.mongodb.client.TransactionBody; +import org.bson.Document; +import org.junit.After; +import org.junit.Before; import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Spy; -import org.mockito.junit.MockitoJUnitRunner; -import java.io.IOException; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.lenient; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; -/** - * Has an overhead of connecting to Docker and running Ryuk - * only once thanks to a static class member variable MONGODB_CONTAINER. - * - * @author Konstantin Silaev on 9/30/2019 - */ -@RunWith(MockitoJUnitRunner.class) public class MongoDbContainerTest { - private static final int CONTAINER_ERROR_EXIT_CODE = 252; - @Spy - private static MongoDbContainer MONGODB_CONTAINER = new MongoDbContainer(); + private final MongoDbContainer mongoDbContainer = new MongoDbContainer(); - @Test(expected = IllegalStateException.class) - public void shouldNotGetReplicaSetUrlBecauseOfNotStartedContainer() { - //WHEN - MONGODB_CONTAINER.getReplicaSetUrl(); + @Before + public void setUp() { + mongoDbContainer.start(); + } - //THEN - //expected IllegalStateException.class + @After + public void tearDown() { + mongoDbContainer.stop(); } - @Test(expected = MongoDbContainer.ReplicaSetInitializationException.class) - public void shouldNotInitReplicaSetBecauseOfExecInitReplicaSet() - throws IOException, InterruptedException { + /** + * Taken from https://docs.mongodb.com + */ + @Test + public void shouldExecuteTransactions() { //GIVEN - Container.ExecResult mockExecResult = mock(Container.ExecResult.class); - when(mockExecResult.getExitCode()) - .thenReturn(CONTAINER_ERROR_EXIT_CODE); - doReturn(mockExecResult).when(MONGODB_CONTAINER).execInContainer(any()); - final int mappedPort = 37723; - lenient().doReturn(mappedPort).when(MONGODB_CONTAINER) - .getMappedPort(MongoDbContainer.MONGODB_INTERNAL_PORT); + final String mongoRsUrl = mongoDbContainer.getReplicaSetUrl(); + assertNotNull(mongoRsUrl); + final MongoClient mongoSyncClient = MongoClients.create(mongoRsUrl); + mongoSyncClient.getDatabase("mydb1").getCollection("foo") + .withWriteConcern(WriteConcern.MAJORITY).insertOne(new Document("abc", 0)); + mongoSyncClient.getDatabase("mydb2").getCollection("bar") + .withWriteConcern(WriteConcern.MAJORITY).insertOne(new Document("xyz", 0)); - //WHEN - MONGODB_CONTAINER.initReplicaSet(); + final ClientSession clientSession = mongoSyncClient.startSession(); + final TransactionOptions txnOptions = TransactionOptions.builder() + .readPreference(ReadPreference.primary()) + .readConcern(ReadConcern.LOCAL) + .writeConcern(WriteConcern.MAJORITY) + .build(); - //THEN - //expected = MongoDbContainer.ReplicaSetInitializationException.class - } + final String trxResult = "Inserted into collections in different databases"; - @Test(expected = MongoDbContainer.ReplicaSetInitializationException.class) - public void shouldNotInitReplicaSetBecauseOfWaitCommand() throws IOException, InterruptedException { - //GIVEN - final Container.ExecResult mockExecResult1 = mock(Container.ExecResult.class); - final Container.ExecResult mockExecResult2 = mock(Container.ExecResult.class); - when(mockExecResult1.getExitCode()) - .thenReturn(0); - when(mockExecResult2.getExitCode()) - .thenReturn(252); - doReturn(mockExecResult1, mockExecResult2) - .when(MONGODB_CONTAINER).execInContainer(any()); - final int mappedPort = 37723; - lenient().doReturn(mappedPort).when(MONGODB_CONTAINER) - .getMappedPort(MongoDbContainer.MONGODB_INTERNAL_PORT); + //WHEN + THEN + TransactionBody txnBody = () -> { + final MongoCollection coll1 = + mongoSyncClient.getDatabase("mydb1").getCollection("foo"); + final MongoCollection coll2 = + mongoSyncClient.getDatabase("mydb2").getCollection("bar"); - //WHEN - MONGODB_CONTAINER.initReplicaSet(); + coll1.insertOne(clientSession, new Document("abc", 1)); + coll2.insertOne(clientSession, new Document("xyz", 999)); + return trxResult; + }; - //THEN - //expected = MongoDbContainer.ReplicaSetInitializationException.class + try { + final String trxResultActual = clientSession.withTransaction(txnBody, txnOptions); + assertEquals(trxResult, trxResultActual); + } catch (RuntimeException re) { + throw new IllegalStateException(re.getMessage(), re); + } finally { + clientSession.close(); + mongoSyncClient.close(); + } } } diff --git a/modules/mongodb/src/test/java/org/testcontainers/containers/TransactionITTest.java b/modules/mongodb/src/test/java/org/testcontainers/containers/TransactionITTest.java deleted file mode 100644 index 28a5b078164..00000000000 --- a/modules/mongodb/src/test/java/org/testcontainers/containers/TransactionITTest.java +++ /dev/null @@ -1,79 +0,0 @@ -package org.testcontainers.containers; - -import com.mongodb.ReadConcern; -import com.mongodb.ReadPreference; -import com.mongodb.TransactionOptions; -import com.mongodb.WriteConcern; -import com.mongodb.client.ClientSession; -import com.mongodb.client.MongoClient; -import com.mongodb.client.MongoClients; -import com.mongodb.client.MongoCollection; -import com.mongodb.client.TransactionBody; -import org.bson.Document; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; - - -public class TransactionITTest { - private final MongoDbContainer mongoDbContainer = new MongoDbContainer(); - - @Before - public void setUp() { - mongoDbContainer.start(); - } - - @After - public void tearDown() { - mongoDbContainer.stop(); - } - - /** - * Taken from https://docs.mongodb.com - */ - @Test - public void shouldExecuteTransactions() { - //GIVEN - final String mongoRsUrl = mongoDbContainer.getReplicaSetUrl(); - assertNotNull(mongoRsUrl); - final MongoClient mongoSyncClient = MongoClients.create(mongoRsUrl); - mongoSyncClient.getDatabase("mydb1").getCollection("foo") - .withWriteConcern(WriteConcern.MAJORITY).insertOne(new Document("abc", 0)); - mongoSyncClient.getDatabase("mydb2").getCollection("bar") - .withWriteConcern(WriteConcern.MAJORITY).insertOne(new Document("xyz", 0)); - - final ClientSession clientSession = mongoSyncClient.startSession(); - final TransactionOptions txnOptions = TransactionOptions.builder() - .readPreference(ReadPreference.primary()) - .readConcern(ReadConcern.LOCAL) - .writeConcern(WriteConcern.MAJORITY) - .build(); - - final String trxResult = "Inserted into collections in different databases"; - - //WHEN + THEN - TransactionBody txnBody = () -> { - final MongoCollection coll1 = - mongoSyncClient.getDatabase("mydb1").getCollection("foo"); - final MongoCollection coll2 = - mongoSyncClient.getDatabase("mydb2").getCollection("bar"); - - coll1.insertOne(clientSession, new Document("abc", 1)); - coll2.insertOne(clientSession, new Document("xyz", 999)); - return trxResult; - }; - - try { - final String trxResultActual = clientSession.withTransaction(txnBody, txnOptions); - assertEquals(trxResult, trxResultActual); - } catch (RuntimeException re) { - throw new IllegalStateException(re.getMessage(), re); - } finally { - clientSession.close(); - mongoSyncClient.close(); - } - } -} From 5f3145b176f225c64bb96dec6248ca524c98f69d Mon Sep 17 00:00:00 2001 From: Konstantin Silaev Date: Fri, 24 Apr 2020 09:05:18 +0300 Subject: [PATCH 15/38] Remove MockMaker --- .../resources/mockito-extensions/org.mockito.plugins.MockMaker | 1 - 1 file changed, 1 deletion(-) delete mode 100644 modules/mongodb/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker diff --git a/modules/mongodb/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker b/modules/mongodb/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker deleted file mode 100644 index ca6ee9cea8e..00000000000 --- a/modules/mongodb/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker +++ /dev/null @@ -1 +0,0 @@ -mock-maker-inline \ No newline at end of file From 8b727f050590c5ced6e1c0f74fe6ab64575c02c9 Mon Sep 17 00:00:00 2001 From: Konstantin Silaev Date: Fri, 24 Apr 2020 10:00:15 +0300 Subject: [PATCH 16/38] Move initReplicaSet to containerIsStarted, set timeout delay to 100ms, use try-with-resources in test --- docs/modules/databases/mongodb.md | 4 +- .../containers/MongoDbContainer.java | 13 ++- .../containers/MongoDbContainerTest.java | 87 +++++++++---------- 3 files changed, 51 insertions(+), 53 deletions(-) diff --git a/docs/modules/databases/mongodb.md b/docs/modules/databases/mongodb.md index ef488d6a852..5fdff6a634c 100644 --- a/docs/modules/databases/mongodb.md +++ b/docs/modules/databases/mongodb.md @@ -16,11 +16,11 @@ version | The following example shows how to create a MongoDbContainer -[Creating a MongoDB container](../../../modules/mongodb/src/test/java/org/testcontainers/containers/integration/InitializationITTest.java) inside_block:creatingMongoDbContainer +[Creating a MongoDB container](../../../modules/mongodb/src/test/java/org/testcontainers/containers/MongoDbContainerTest.java) inside_block:creatingMongoDbContainer -[Starting and stopping a MongoDB container](../../../modules/mongodb/src/test/java/org/testcontainers/containers/integration/InitializationITTest.java) inside_block:startingStoppingMongoDbContainer +[Starting a MongoDB container](../../../modules/mongodb/src/test/java/org/testcontainers/containers/MongoDbContainerTest.java) inside_block:startingMongoDbContainer #### Motivation diff --git a/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDbContainer.java b/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDbContainer.java index 600671f66a8..7f527ab0431 100644 --- a/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDbContainer.java +++ b/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDbContainer.java @@ -1,5 +1,6 @@ package org.testcontainers.containers; +import com.github.dockerjava.api.command.InspectContainerResponse; import lombok.NonNull; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; @@ -17,8 +18,8 @@ @Slf4j public class MongoDbContainer extends GenericContainer { private static final int CONTAINER_EXIT_CODE_OK = 0; - static final int MONGODB_INTERNAL_PORT = 27017; - private static final int AWAIT_INIT_REPLICA_SET_ATTEMPTS = 30; + private static final int MONGODB_INTERNAL_PORT = 27017; + private static final int AWAIT_INIT_REPLICA_SET_ATTEMPTS = 60; private static final String MONGODB_VERSION_DEFAULT = "4.0.10"; private static final String MONGODB_DATABASE_NAME_DEFAULT = "test"; @@ -56,10 +57,14 @@ public String getReplicaSetUrl() { @Override public void start() { super.start(); - initReplicaSet(); logReplicaSetStatus(); } + @Override + protected void containerIsStarted(InspectContainerResponse containerInfo) { + initReplicaSet(); + } + @SneakyThrows(value = {IOException.class, InterruptedException.class}) private void logReplicaSetStatus() { log.debug( @@ -87,7 +92,7 @@ private String buildMongoWaitCommand() { "(%s) " + "{ " + "if (attempt > %d) {quit(1);} " + - "print('%s ' + attempt); sleep(1000); attempt++; " + + "print('%s ' + attempt); sleep(100); attempt++; " + " }", "db.runCommand( { isMaster: 1 } ).ismaster==false", AWAIT_INIT_REPLICA_SET_ATTEMPTS, diff --git a/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDbContainerTest.java b/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDbContainerTest.java index 3dfda19ff0d..932252f750b 100644 --- a/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDbContainerTest.java +++ b/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDbContainerTest.java @@ -10,8 +10,6 @@ import com.mongodb.client.MongoCollection; import com.mongodb.client.TransactionBody; import org.bson.Document; -import org.junit.After; -import org.junit.Before; import org.junit.Test; import static org.junit.Assert.assertEquals; @@ -19,61 +17,56 @@ public class MongoDbContainerTest { - private final MongoDbContainer mongoDbContainer = new MongoDbContainer(); - - @Before - public void setUp() { - mongoDbContainer.start(); - } - - @After - public void tearDown() { - mongoDbContainer.stop(); - } - /** * Taken from https://docs.mongodb.com */ @Test public void shouldExecuteTransactions() { - //GIVEN - final String mongoRsUrl = mongoDbContainer.getReplicaSetUrl(); - assertNotNull(mongoRsUrl); - final MongoClient mongoSyncClient = MongoClients.create(mongoRsUrl); - mongoSyncClient.getDatabase("mydb1").getCollection("foo") - .withWriteConcern(WriteConcern.MAJORITY).insertOne(new Document("abc", 0)); - mongoSyncClient.getDatabase("mydb2").getCollection("bar") - .withWriteConcern(WriteConcern.MAJORITY).insertOne(new Document("xyz", 0)); + // creatingMongoDbContainer { + try (MongoDbContainer mongoDbContainer = new MongoDbContainer()) { + // } + + // startingMongoDbContainer + mongoDbContainer.start(); + // } + + final String mongoRsUrl = mongoDbContainer.getReplicaSetUrl(); + assertNotNull(mongoRsUrl); + final MongoClient mongoSyncClient = MongoClients.create(mongoRsUrl); + mongoSyncClient.getDatabase("mydb1").getCollection("foo") + .withWriteConcern(WriteConcern.MAJORITY).insertOne(new Document("abc", 0)); + mongoSyncClient.getDatabase("mydb2").getCollection("bar") + .withWriteConcern(WriteConcern.MAJORITY).insertOne(new Document("xyz", 0)); - final ClientSession clientSession = mongoSyncClient.startSession(); - final TransactionOptions txnOptions = TransactionOptions.builder() - .readPreference(ReadPreference.primary()) - .readConcern(ReadConcern.LOCAL) - .writeConcern(WriteConcern.MAJORITY) - .build(); + final ClientSession clientSession = mongoSyncClient.startSession(); + final TransactionOptions txnOptions = TransactionOptions.builder() + .readPreference(ReadPreference.primary()) + .readConcern(ReadConcern.LOCAL) + .writeConcern(WriteConcern.MAJORITY) + .build(); - final String trxResult = "Inserted into collections in different databases"; + final String trxResult = "Inserted into collections in different databases"; - //WHEN + THEN - TransactionBody txnBody = () -> { - final MongoCollection coll1 = - mongoSyncClient.getDatabase("mydb1").getCollection("foo"); - final MongoCollection coll2 = - mongoSyncClient.getDatabase("mydb2").getCollection("bar"); + TransactionBody txnBody = () -> { + final MongoCollection coll1 = + mongoSyncClient.getDatabase("mydb1").getCollection("foo"); + final MongoCollection coll2 = + mongoSyncClient.getDatabase("mydb2").getCollection("bar"); - coll1.insertOne(clientSession, new Document("abc", 1)); - coll2.insertOne(clientSession, new Document("xyz", 999)); - return trxResult; - }; + coll1.insertOne(clientSession, new Document("abc", 1)); + coll2.insertOne(clientSession, new Document("xyz", 999)); + return trxResult; + }; - try { - final String trxResultActual = clientSession.withTransaction(txnBody, txnOptions); - assertEquals(trxResult, trxResultActual); - } catch (RuntimeException re) { - throw new IllegalStateException(re.getMessage(), re); - } finally { - clientSession.close(); - mongoSyncClient.close(); + try { + final String trxResultActual = clientSession.withTransaction(txnBody, txnOptions); + assertEquals(trxResult, trxResultActual); + } catch (RuntimeException re) { + throw new IllegalStateException(re.getMessage(), re); + } finally { + clientSession.close(); + mongoSyncClient.close(); + } } } } From e714b26dd72abbc92be08a37cbbb69bfc3068e9c Mon Sep 17 00:00:00 2001 From: Konstantin Silaev Date: Fri, 24 Apr 2020 10:01:22 +0300 Subject: [PATCH 17/38] Make initReplicaSet private --- .../java/org/testcontainers/containers/MongoDbContainer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDbContainer.java b/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDbContainer.java index 7f527ab0431..9d684976d79 100644 --- a/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDbContainer.java +++ b/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDbContainer.java @@ -114,7 +114,7 @@ private void checkMongoNodeExitCodeAfterWaiting( } @SneakyThrows(value = {IOException.class, InterruptedException.class}) - void initReplicaSet() { + private void initReplicaSet() { log.debug("Initializing a single node node replica set..."); final ExecResult execResultInitRs = execInContainer( buildMongoEvalCommand("rs.initiate();") From 5b5531dc1df8ca47b83d7fe1f0de560c1f4cc4cb Mon Sep 17 00:00:00 2001 From: Konstantin Silaev Date: Fri, 24 Apr 2020 10:09:32 +0300 Subject: [PATCH 18/38] Fix docs --- docs/modules/databases/mongodb.md | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/docs/modules/databases/mongodb.md b/docs/modules/databases/mongodb.md index 5fdff6a634c..c35305892fa 100644 --- a/docs/modules/databases/mongodb.md +++ b/docs/modules/databases/mongodb.md @@ -4,13 +4,7 @@ This module is INCUBATING. While it is ready for use and operational in the current version of Testcontainers, it is possible that it may receive breaking changes in the future. See [our contributing guidelines](/contributing/#incubating-modules) for more information on our incubating modules policy. # Java8 MongoDbContainer for constructing a single node MongoDB replica set. To construct a multi-node MongoDB cluster, consider the [mongodb-replica-set project](https://github.com/silaev/mongodb-replica-set/) - -#### MongoDB versions that MongoDbContainer is constantly tested against: -version | ----------- | -4.0.12 | -4.2.0 | - + ## Usage example The following example shows how to create a MongoDbContainer @@ -32,9 +26,9 @@ MongoDB starting form version 4 supports multi-document transactions only for a For instance, to initialize a single and simple node replica set on fixed ports via Docker, one has to do the following: * Run a MongoDB container of version 4 and up specifying --replSet command -* Initializing a single replica set via executing a proper command (depending on local or remote Docker daemon usage) -* Waiting for the initialization to complete -* Providing a special url (without the need to modify the OS host file) for a user to employ with a MongoDB driver without specifying replicaSet +* Initialize a single replica set via executing a proper command +* Wait for the initialization to complete +* Provide a special url for a user to employ with a MongoDB driver without specifying replicaSet As we can see, there is a lot of operations to execute and we even haven't touched a non-fixed port approach. That's where the MongoDbContainer might come in handy. From d89200627e9c326605526244f3009a682d6e2398 Mon Sep 17 00:00:00 2001 From: Konstantin Silaev Date: Fri, 24 Apr 2020 10:16:46 +0300 Subject: [PATCH 19/38] Fix docs --- .../org/testcontainers/containers/MongoDbContainerTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDbContainerTest.java b/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDbContainerTest.java index 932252f750b..1edd0e5fdbf 100644 --- a/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDbContainerTest.java +++ b/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDbContainerTest.java @@ -24,9 +24,9 @@ public class MongoDbContainerTest { public void shouldExecuteTransactions() { // creatingMongoDbContainer { try (MongoDbContainer mongoDbContainer = new MongoDbContainer()) { - // } + // } - // startingMongoDbContainer + // startingMongoDbContainer { mongoDbContainer.start(); // } From 7adc9f82c037fb8325d5fdf04e96e2c1529298c2 Mon Sep 17 00:00:00 2001 From: Konstantin Silaev Date: Fri, 24 Apr 2020 10:23:40 +0300 Subject: [PATCH 20/38] Fix docs --- .../testcontainers/containers/MongoDbContainerTest.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDbContainerTest.java b/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDbContainerTest.java index 1edd0e5fdbf..a06e779f818 100644 --- a/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDbContainerTest.java +++ b/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDbContainerTest.java @@ -22,9 +22,11 @@ public class MongoDbContainerTest { */ @Test public void shouldExecuteTransactions() { - // creatingMongoDbContainer { - try (MongoDbContainer mongoDbContainer = new MongoDbContainer()) { - // } + try ( + // creatingMongoDbContainer { + MongoDbContainer mongoDbContainer = new MongoDbContainer() + // } + ) { // startingMongoDbContainer { mongoDbContainer.start(); From c13659275471ab586887fde0cc296787aaf2141c Mon Sep 17 00:00:00 2001 From: Konstantin Silaev Date: Fri, 24 Apr 2020 10:25:32 +0300 Subject: [PATCH 21/38] Make mongoDbContainer final in test --- .../org/testcontainers/containers/MongoDbContainerTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDbContainerTest.java b/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDbContainerTest.java index a06e779f818..1e425f89134 100644 --- a/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDbContainerTest.java +++ b/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDbContainerTest.java @@ -24,7 +24,7 @@ public class MongoDbContainerTest { public void shouldExecuteTransactions() { try ( // creatingMongoDbContainer { - MongoDbContainer mongoDbContainer = new MongoDbContainer() + final MongoDbContainer mongoDbContainer = new MongoDbContainer() // } ) { From a773cc595e46dc5527a2ac1bdb0d92ab2f4a32f0 Mon Sep 17 00:00:00 2001 From: Konstantin Silaev Date: Fri, 24 Apr 2020 10:28:39 +0300 Subject: [PATCH 22/38] Fix docs --- docs/modules/databases/mongodb.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/modules/databases/mongodb.md b/docs/modules/databases/mongodb.md index c35305892fa..b06d7213aa7 100644 --- a/docs/modules/databases/mongodb.md +++ b/docs/modules/databases/mongodb.md @@ -23,7 +23,7 @@ fixed ports to test MongoDB transactions. #### General info MongoDB starting form version 4 supports multi-document transactions only for a replica set. -For instance, to initialize a single and simple node replica set on fixed ports via Docker, one has to do the following: +For instance, to initialize a single node replica set on fixed ports via Docker, one has to do the following: * Run a MongoDB container of version 4 and up specifying --replSet command * Initialize a single replica set via executing a proper command From fb325d48ba30239b408f92f25fc833239934ab70 Mon Sep 17 00:00:00 2001 From: Konstantin Silaev Date: Fri, 24 Apr 2020 19:31:13 +0300 Subject: [PATCH 23/38] Fix docs, change MongoDb with MongoDB, remove logReplicaSetStatus --- docs/modules/databases/mongodb.md | 15 +++++---- ...DbContainer.java => MongoDBContainer.java} | 32 +++++-------------- ...nerTest.java => MongoDBContainerTest.java} | 12 +++---- 3 files changed, 23 insertions(+), 36 deletions(-) rename modules/mongodb/src/main/java/org/testcontainers/containers/{MongoDbContainer.java => MongoDBContainer.java} (83%) rename modules/mongodb/src/test/java/org/testcontainers/containers/{MongoDbContainerTest.java => MongoDBContainerTest.java} (89%) diff --git a/docs/modules/databases/mongodb.md b/docs/modules/databases/mongodb.md index b06d7213aa7..49a9504a9c7 100644 --- a/docs/modules/databases/mongodb.md +++ b/docs/modules/databases/mongodb.md @@ -3,20 +3,23 @@ !!! note This module is INCUBATING. While it is ready for use and operational in the current version of Testcontainers, it is possible that it may receive breaking changes in the future. See [our contributing guidelines](/contributing/#incubating-modules) for more information on our incubating modules policy. -# Java8 MongoDbContainer for constructing a single node MongoDB replica set. To construct a multi-node MongoDB cluster, consider the [mongodb-replica-set project](https://github.com/silaev/mongodb-replica-set/) - ## Usage example -The following example shows how to create a MongoDbContainer +The following example shows how to create a MongoDBContainer: -[Creating a MongoDB container](../../../modules/mongodb/src/test/java/org/testcontainers/containers/MongoDbContainerTest.java) inside_block:creatingMongoDbContainer +[Creating a MongoDB container](../../../modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBContainerTest.java) inside_block:creatingMongoDBContainer +And how to start it: + -[Starting a MongoDB container](../../../modules/mongodb/src/test/java/org/testcontainers/containers/MongoDbContainerTest.java) inside_block:startingMongoDbContainer +[Starting a MongoDB container](../../../modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBContainerTest.java) inside_block:startingMongoDBContainer +!!! note + To construct a multi-node MongoDB cluster, consider the [mongodb-replica-set project](https://github.com/silaev/mongodb-replica-set/) + #### Motivation Implement a reusable, cross-platform, simple to install solution that doesn't depend on fixed ports to test MongoDB transactions. @@ -31,7 +34,7 @@ For instance, to initialize a single node replica set on fixed ports via Docker, * Provide a special url for a user to employ with a MongoDB driver without specifying replicaSet As we can see, there is a lot of operations to execute and we even haven't touched a non-fixed port approach. -That's where the MongoDbContainer might come in handy. +That's where the MongoDBContainer might come in handy. ## Adding this module to your project dependencies diff --git a/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDbContainer.java b/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDBContainer.java similarity index 83% rename from modules/mongodb/src/main/java/org/testcontainers/containers/MongoDbContainer.java rename to modules/mongodb/src/main/java/org/testcontainers/containers/MongoDBContainer.java index 9d684976d79..bc650c13f59 100644 --- a/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDbContainer.java +++ b/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDBContainer.java @@ -11,30 +11,28 @@ /** * Constructs a single node MongoDB replica set for testing transactions. *

To construct a multi-node MongoDB cluster, consider the mongodb-replica-set project on GitHub - *

Tested on a Mongo DB version 4.0.10+ (that is the default version if not specified). - * - * @author Konstantin Silaev on 9/30/2019 + *

Tested on a MongoDB version 4.0.10+ (that is the default version if not specified). */ @Slf4j -public class MongoDbContainer extends GenericContainer { +public class MongoDBContainer extends GenericContainer { private static final int CONTAINER_EXIT_CODE_OK = 0; private static final int MONGODB_INTERNAL_PORT = 27017; private static final int AWAIT_INIT_REPLICA_SET_ATTEMPTS = 60; private static final String MONGODB_VERSION_DEFAULT = "4.0.10"; private static final String MONGODB_DATABASE_NAME_DEFAULT = "test"; - public MongoDbContainer() { + public MongoDBContainer() { super("mongo:" + MONGODB_VERSION_DEFAULT); - configureMongoDbContainer(); + configureMongoDBContainer(); } - public MongoDbContainer(@NonNull final String dockerImageName) { + public MongoDBContainer(@NonNull final String dockerImageName) { super(dockerImageName); - configureMongoDbContainer(); + configureMongoDBContainer(); } - private void configureMongoDbContainer() { + private void configureMongoDBContainer() { withExposedPorts(MONGODB_INTERNAL_PORT); withCommand("--replSet", "docker-rs"); waitingFor( @@ -44,7 +42,7 @@ private void configureMongoDbContainer() { public String getReplicaSetUrl() { if (!isRunning()) { - throw new IllegalStateException("MongoDbContainer should be started first"); + throw new IllegalStateException("MongoDBContainer should be started first"); } return String.format( "mongodb://%s:%d/%s", @@ -54,25 +52,11 @@ public String getReplicaSetUrl() { ); } - @Override - public void start() { - super.start(); - logReplicaSetStatus(); - } - @Override protected void containerIsStarted(InspectContainerResponse containerInfo) { initReplicaSet(); } - @SneakyThrows(value = {IOException.class, InterruptedException.class}) - private void logReplicaSetStatus() { - log.debug( - "REPLICA SET STATUS:\n{}", - execInContainer(buildMongoEvalCommand("rs.status()")).getStdout() - ); - } - private String[] buildMongoEvalCommand(final String command) { return new String[]{"mongo", "--eval", command}; } diff --git a/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDbContainerTest.java b/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBContainerTest.java similarity index 89% rename from modules/mongodb/src/test/java/org/testcontainers/containers/MongoDbContainerTest.java rename to modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBContainerTest.java index 1e425f89134..7c4f4c12408 100644 --- a/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDbContainerTest.java +++ b/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBContainerTest.java @@ -16,23 +16,23 @@ import static org.junit.Assert.assertNotNull; -public class MongoDbContainerTest { +public class MongoDBContainerTest { /** * Taken from https://docs.mongodb.com */ @Test public void shouldExecuteTransactions() { try ( - // creatingMongoDbContainer { - final MongoDbContainer mongoDbContainer = new MongoDbContainer() + // creatingMongoDBContainer { + final MongoDBContainer mongoDBContainer = new MongoDBContainer() // } ) { - // startingMongoDbContainer { - mongoDbContainer.start(); + // startingMongoDBContainer { + mongoDBContainer.start(); // } - final String mongoRsUrl = mongoDbContainer.getReplicaSetUrl(); + final String mongoRsUrl = mongoDBContainer.getReplicaSetUrl(); assertNotNull(mongoRsUrl); final MongoClient mongoSyncClient = MongoClients.create(mongoRsUrl); mongoSyncClient.getDatabase("mydb1").getCollection("foo") From 649dae233aa404ba0f5f4b3fcd84c95a2a588447 Mon Sep 17 00:00:00 2001 From: Konstantin Silaev Date: Fri, 24 Apr 2020 19:36:29 +0300 Subject: [PATCH 24/38] Reuse secondary constructor --- .../java/org/testcontainers/containers/MongoDBContainer.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDBContainer.java b/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDBContainer.java index bc650c13f59..00c8aaf7921 100644 --- a/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDBContainer.java +++ b/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDBContainer.java @@ -22,9 +22,7 @@ public class MongoDBContainer extends GenericContainer { private static final String MONGODB_DATABASE_NAME_DEFAULT = "test"; public MongoDBContainer() { - super("mongo:" + MONGODB_VERSION_DEFAULT); - configureMongoDBContainer(); - + this("mongo:" + MONGODB_VERSION_DEFAULT); } public MongoDBContainer(@NonNull final String dockerImageName) { From cd23ddb159c6f4b8be80f2e7a70f431e4b384abc Mon Sep 17 00:00:00 2001 From: Konstantin Silaev Date: Fri, 24 Apr 2020 19:38:47 +0300 Subject: [PATCH 25/38] Adjust heading --- docs/modules/databases/mongodb.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/modules/databases/mongodb.md b/docs/modules/databases/mongodb.md index 49a9504a9c7..637a0e08984 100644 --- a/docs/modules/databases/mongodb.md +++ b/docs/modules/databases/mongodb.md @@ -1,4 +1,4 @@ -# Mongo DB Module +# MongoDB Module !!! note This module is INCUBATING. While it is ready for use and operational in the current version of Testcontainers, it is possible that it may receive breaking changes in the future. See [our contributing guidelines](/contributing/#incubating-modules) for more information on our incubating modules policy. From 47c39e7528bb486109106f3d8d161a5e0bf80c37 Mon Sep 17 00:00:00 2001 From: Konstantin Silaev Date: Fri, 24 Apr 2020 19:42:04 +0300 Subject: [PATCH 26/38] Remove configureMongoDBContainer --- .../java/org/testcontainers/containers/MongoDBContainer.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDBContainer.java b/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDBContainer.java index 00c8aaf7921..a7f57d310f2 100644 --- a/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDBContainer.java +++ b/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDBContainer.java @@ -27,10 +27,6 @@ public MongoDBContainer() { public MongoDBContainer(@NonNull final String dockerImageName) { super(dockerImageName); - configureMongoDBContainer(); - } - - private void configureMongoDBContainer() { withExposedPorts(MONGODB_INTERNAL_PORT); withCommand("--replSet", "docker-rs"); waitingFor( From 78ad147651cd7ff90ddc17f4ddf1e525c60200d1 Mon Sep 17 00:00:00 2001 From: Richard North Date: Fri, 8 May 2020 12:27:39 +0100 Subject: [PATCH 27/38] Update docs/modules/databases/mongodb.md --- docs/modules/databases/mongodb.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/modules/databases/mongodb.md b/docs/modules/databases/mongodb.md index 637a0e08984..fc2cb873f59 100644 --- a/docs/modules/databases/mongodb.md +++ b/docs/modules/databases/mongodb.md @@ -25,7 +25,7 @@ Implement a reusable, cross-platform, simple to install solution that doesn't de fixed ports to test MongoDB transactions. #### General info -MongoDB starting form version 4 supports multi-document transactions only for a replica set. +MongoDB starting from version 4 supports multi-document transactions only for a replica set. For instance, to initialize a single node replica set on fixed ports via Docker, one has to do the following: * Run a MongoDB container of version 4 and up specifying --replSet command From ccc0f402bc6d9b04576fb2df75725017eae124b9 Mon Sep 17 00:00:00 2001 From: Konstantin Silaev Date: Wed, 8 Jul 2020 10:19:21 +0300 Subject: [PATCH 28/38] Add withDatabaseName (https://github.com/testcontainers/testcontainers-java/issues/2876) --- .../containers/MongoDBContainer.java | 15 ++++++++++++++- .../containers/MongoDBContainerTest.java | 1 + 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDBContainer.java b/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDBContainer.java index a7f57d310f2..4c25154fd19 100644 --- a/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDBContainer.java +++ b/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDBContainer.java @@ -20,6 +20,7 @@ public class MongoDBContainer extends GenericContainer { private static final int AWAIT_INIT_REPLICA_SET_ATTEMPTS = 60; private static final String MONGODB_VERSION_DEFAULT = "4.0.10"; private static final String MONGODB_DATABASE_NAME_DEFAULT = "test"; + private String databaseName = MONGODB_DATABASE_NAME_DEFAULT; public MongoDBContainer() { this("mongo:" + MONGODB_VERSION_DEFAULT); @@ -42,10 +43,22 @@ public String getReplicaSetUrl() { "mongodb://%s:%d/%s", getContainerIpAddress(), getMappedPort(MONGODB_INTERNAL_PORT), - MONGODB_DATABASE_NAME_DEFAULT + databaseName ); } + /** + * Sets a database name for the {@link #getReplicaSetUrl()} method. + * Defaults to {@value #MONGODB_DATABASE_NAME_DEFAULT} if it is not called. + * + * @param databaseName a database name. + * @return this + */ + public MongoDBContainer withDatabaseName(final String databaseName) { + this.databaseName = databaseName; + return self(); + } + @Override protected void containerIsStarted(InspectContainerResponse containerInfo) { initReplicaSet(); diff --git a/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBContainerTest.java b/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBContainerTest.java index 7c4f4c12408..47eadc3e49f 100644 --- a/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBContainerTest.java +++ b/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBContainerTest.java @@ -25,6 +25,7 @@ public void shouldExecuteTransactions() { try ( // creatingMongoDBContainer { final MongoDBContainer mongoDBContainer = new MongoDBContainer() + .withDatabaseName("my-db") // } ) { From 5bed9a544cb249586fde4b748450f8fc0b5b7db5 Mon Sep 17 00:00:00 2001 From: Konstantin Silaev Date: Thu, 9 Jul 2020 09:26:22 +0300 Subject: [PATCH 29/38] Revert "Add withDatabaseName (https://github.com/testcontainers/testcontainers-java/issues/2876)" This reverts commit ccc0f402 --- .../containers/MongoDBContainer.java | 15 +-------------- .../containers/MongoDBContainerTest.java | 1 - 2 files changed, 1 insertion(+), 15 deletions(-) diff --git a/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDBContainer.java b/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDBContainer.java index 4c25154fd19..a7f57d310f2 100644 --- a/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDBContainer.java +++ b/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDBContainer.java @@ -20,7 +20,6 @@ public class MongoDBContainer extends GenericContainer { private static final int AWAIT_INIT_REPLICA_SET_ATTEMPTS = 60; private static final String MONGODB_VERSION_DEFAULT = "4.0.10"; private static final String MONGODB_DATABASE_NAME_DEFAULT = "test"; - private String databaseName = MONGODB_DATABASE_NAME_DEFAULT; public MongoDBContainer() { this("mongo:" + MONGODB_VERSION_DEFAULT); @@ -43,22 +42,10 @@ public String getReplicaSetUrl() { "mongodb://%s:%d/%s", getContainerIpAddress(), getMappedPort(MONGODB_INTERNAL_PORT), - databaseName + MONGODB_DATABASE_NAME_DEFAULT ); } - /** - * Sets a database name for the {@link #getReplicaSetUrl()} method. - * Defaults to {@value #MONGODB_DATABASE_NAME_DEFAULT} if it is not called. - * - * @param databaseName a database name. - * @return this - */ - public MongoDBContainer withDatabaseName(final String databaseName) { - this.databaseName = databaseName; - return self(); - } - @Override protected void containerIsStarted(InspectContainerResponse containerInfo) { initReplicaSet(); diff --git a/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBContainerTest.java b/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBContainerTest.java index 47eadc3e49f..7c4f4c12408 100644 --- a/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBContainerTest.java +++ b/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBContainerTest.java @@ -25,7 +25,6 @@ public void shouldExecuteTransactions() { try ( // creatingMongoDBContainer { final MongoDBContainer mongoDBContainer = new MongoDBContainer() - .withDatabaseName("my-db") // } ) { From decb5218ec1bd518fed95ff8c6089d03a91d1cc1 Mon Sep 17 00:00:00 2001 From: Konstantin Silaev Date: Thu, 9 Jul 2020 09:57:22 +0300 Subject: [PATCH 30/38] Add overloaded getReplicaSetUrl(final String databaseName) --- .../containers/MongoDBContainer.java | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDBContainer.java b/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDBContainer.java index a7f57d310f2..1d06758766d 100644 --- a/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDBContainer.java +++ b/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDBContainer.java @@ -34,7 +34,22 @@ public MongoDBContainer(@NonNull final String dockerImageName) { ); } + /** + * Gets a replica set url for the default {@value #MONGODB_DATABASE_NAME_DEFAULT} database. + * + * @return a replica set url. + */ public String getReplicaSetUrl() { + return getReplicaSetUrl(MONGODB_DATABASE_NAME_DEFAULT); + } + + /** + * Gets a replica set url for a provided databaseName. + * + * @param databaseName a database name. + * @return a replica set url. + */ + public String getReplicaSetUrl(final String databaseName) { if (!isRunning()) { throw new IllegalStateException("MongoDBContainer should be started first"); } @@ -42,7 +57,7 @@ public String getReplicaSetUrl() { "mongodb://%s:%d/%s", getContainerIpAddress(), getMappedPort(MONGODB_INTERNAL_PORT), - MONGODB_DATABASE_NAME_DEFAULT + databaseName ); } From 2086fd65fa6cd87532ac87c59550bdb68dfb857a Mon Sep 17 00:00:00 2001 From: Konstantin Silaev Date: Wed, 15 Jul 2020 11:25:37 +0300 Subject: [PATCH 31/38] Add MongoDBDatabaseNameTest --- modules/mongodb/build.gradle | 14 +++ .../containers/MongoDBDatabaseNameTest.java | 115 ++++++++++++++++++ 2 files changed, 129 insertions(+) create mode 100644 modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBDatabaseNameTest.java diff --git a/modules/mongodb/build.gradle b/modules/mongodb/build.gradle index 57db1be21de..0bf702b34b5 100644 --- a/modules/mongodb/build.gradle +++ b/modules/mongodb/build.gradle @@ -1,7 +1,21 @@ +plugins { + id 'org.springframework.boot' version '2.3.1.RELEASE' + id 'io.spring.dependency-management' version '1.0.9.RELEASE' +} + description = "Testcontainers :: MongoDB" dependencies { compile project(':testcontainers') testCompile("org.mongodb:mongodb-driver-sync:4.0.2") + testCompile("org.springframework.boot:spring-boot-starter") + testCompile("org.springframework.boot:spring-boot-starter-test") { + exclude group: 'org.junit.vintage', module: 'junit-vintage-engine' + } + testCompile("org.springframework.data:spring-data-mongodb:3.0.1.RELEASE") +} + +bootJar { + enabled = false } diff --git a/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBDatabaseNameTest.java b/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBDatabaseNameTest.java new file mode 100644 index 00000000000..03ddf5f16f3 --- /dev/null +++ b/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBDatabaseNameTest.java @@ -0,0 +1,115 @@ +package org.testcontainers.containers; + +import com.mongodb.client.MongoClient; +import com.mongodb.client.MongoClients; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import org.jetbrains.annotations.NotNull; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.test.autoconfigure.data.mongo.DataMongoTest; +import org.springframework.boot.test.util.TestPropertyValues; +import org.springframework.context.ApplicationContextInitializer; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.data.mongodb.core.MongoTemplate; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringRunner; + +import java.util.Objects; +import java.util.Spliterator; +import java.util.Spliterators; +import java.util.stream.StreamSupport; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +@Slf4j +@DataMongoTest +@ContextConfiguration(initializers = MongoDBDatabaseNameTest.Initializer.class) +@RunWith(SpringRunner.class) +public class MongoDBDatabaseNameTest { + private static final MongoDBContainer MONGO_DB_CONTAINER = new MongoDBContainer(); + private static final String DATABASE_NAME = "my-db"; + + @Autowired + private MongoTemplate mongoTemplate; + + @BeforeClass + public static void setUpAll() { + MONGO_DB_CONTAINER.start(); + } + + @AfterClass + public static void tearDownAll() { + MONGO_DB_CONTAINER.stop(); + } + + @Test + public void shouldTestDatabaseName() { + //1. Database name was already set to the MongoTemplate during auto-config. + assertEquals(DATABASE_NAME, mongoTemplate.getDb().getName()); + + try (final MongoClient mongoSyncClient = MongoClients.create(MONGO_DB_CONTAINER.getReplicaSetUrl(DATABASE_NAME))) { + //2. But the database is not created yet, because we have not performed any writing operation. + assertFalse( + "Database: " + DATABASE_NAME + " is supposed to be here", + isDatabaseInMongoDB(mongoSyncClient, DATABASE_NAME) + ); + + //3. Perform an operation to create a new collection via mongoTemplate. + mongoTemplate.createCollection(Product.class); + + //4. Now the database is created in MongoDB. + assertTrue( + "Database: " + DATABASE_NAME + " is supposed to be here", + isDatabaseInMongoDB(mongoSyncClient, DATABASE_NAME) + ); + } + } + + private boolean isDatabaseInMongoDB( + final MongoClient mongoSyncClient, + final String databaseName + ) { + Objects.requireNonNull(databaseName); + return StreamSupport.stream( + Spliterators.spliteratorUnknownSize( + mongoSyncClient.listDatabaseNames().iterator(), + Spliterator.ORDERED + ), + false + ).anyMatch(databaseName::equals); + } + + static class Initializer implements ApplicationContextInitializer { + @Override + public void initialize(@NotNull ConfigurableApplicationContext configurableApplicationContext) { + TestPropertyValues.of( + String.format("spring.data.mongodb.uri: %s", MONGO_DB_CONTAINER.getReplicaSetUrl(DATABASE_NAME)) + ).applyTo(configurableApplicationContext); + } + } + + @SpringBootApplication + static class SpringBootApp { + public static void main(String[] args) { + } + } + + @Data + @AllArgsConstructor + @NoArgsConstructor + @Setter(AccessLevel.NONE) + private static class Product { + private Long article; + } +} From 96e9c107b2199e33d2d98ce6f0ac95ff22e77db0 Mon Sep 17 00:00:00 2001 From: Konstantin Silaev Date: Wed, 15 Jul 2020 11:33:59 +0300 Subject: [PATCH 32/38] Rename MongoDBDatabaseNameTest to MongoDBContainerDatabaseNameTest --- ...aseNameTest.java => MongoDBContainerDatabaseNameTest.java} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename modules/mongodb/src/test/java/org/testcontainers/containers/{MongoDBDatabaseNameTest.java => MongoDBContainerDatabaseNameTest.java} (96%) diff --git a/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBDatabaseNameTest.java b/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBContainerDatabaseNameTest.java similarity index 96% rename from modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBDatabaseNameTest.java rename to modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBContainerDatabaseNameTest.java index 03ddf5f16f3..326b3741c67 100644 --- a/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBDatabaseNameTest.java +++ b/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBContainerDatabaseNameTest.java @@ -34,9 +34,9 @@ @Slf4j @DataMongoTest -@ContextConfiguration(initializers = MongoDBDatabaseNameTest.Initializer.class) +@ContextConfiguration(initializers = MongoDBContainerDatabaseNameTest.Initializer.class) @RunWith(SpringRunner.class) -public class MongoDBDatabaseNameTest { +public class MongoDBContainerDatabaseNameTest { private static final MongoDBContainer MONGO_DB_CONTAINER = new MongoDBContainer(); private static final String DATABASE_NAME = "my-db"; From cc3ed2a49b66eddf4cef438d05b2bc3fd0dfbf2e Mon Sep 17 00:00:00 2001 From: Konstantin Silaev Date: Wed, 15 Jul 2020 13:58:18 +0300 Subject: [PATCH 33/38] Change MongoDBContainerDatabaseNameTest to save a Product --- .../containers/MongoDBContainerDatabaseNameTest.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBContainerDatabaseNameTest.java b/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBContainerDatabaseNameTest.java index 326b3741c67..bd7fae7c755 100644 --- a/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBContainerDatabaseNameTest.java +++ b/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBContainerDatabaseNameTest.java @@ -20,6 +20,7 @@ import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.data.mongodb.core.MongoTemplate; +import org.springframework.data.mongodb.core.index.Indexed; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringRunner; @@ -33,7 +34,7 @@ import static org.junit.Assert.assertTrue; @Slf4j -@DataMongoTest +@DataMongoTest(properties = {"spring.main.banner-mode=off", "data.mongodb.auto-index-creation=true"}) @ContextConfiguration(initializers = MongoDBContainerDatabaseNameTest.Initializer.class) @RunWith(SpringRunner.class) public class MongoDBContainerDatabaseNameTest { @@ -65,8 +66,8 @@ public void shouldTestDatabaseName() { isDatabaseInMongoDB(mongoSyncClient, DATABASE_NAME) ); - //3. Perform an operation to create a new collection via mongoTemplate. - mongoTemplate.createCollection(Product.class); + //3. Perform an operation to save a new Product via mongoTemplate. + mongoTemplate.save(new Product(1L)); //4. Now the database is created in MongoDB. assertTrue( @@ -110,6 +111,7 @@ public static void main(String[] args) { @NoArgsConstructor @Setter(AccessLevel.NONE) private static class Product { + @Indexed(unique = true) private Long article; } } From 4268c937cc10df5ab1f570d8b73f4c9bd1a936f5 Mon Sep 17 00:00:00 2001 From: Konstantin Silaev Date: Wed, 15 Jul 2020 15:15:40 +0300 Subject: [PATCH 34/38] Revert "Change MongoDBContainerDatabaseNameTest to save a Product" This reverts commit cc3ed2a4 --- .../containers/MongoDBContainerDatabaseNameTest.java | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBContainerDatabaseNameTest.java b/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBContainerDatabaseNameTest.java index bd7fae7c755..326b3741c67 100644 --- a/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBContainerDatabaseNameTest.java +++ b/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBContainerDatabaseNameTest.java @@ -20,7 +20,6 @@ import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.data.mongodb.core.MongoTemplate; -import org.springframework.data.mongodb.core.index.Indexed; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringRunner; @@ -34,7 +33,7 @@ import static org.junit.Assert.assertTrue; @Slf4j -@DataMongoTest(properties = {"spring.main.banner-mode=off", "data.mongodb.auto-index-creation=true"}) +@DataMongoTest @ContextConfiguration(initializers = MongoDBContainerDatabaseNameTest.Initializer.class) @RunWith(SpringRunner.class) public class MongoDBContainerDatabaseNameTest { @@ -66,8 +65,8 @@ public void shouldTestDatabaseName() { isDatabaseInMongoDB(mongoSyncClient, DATABASE_NAME) ); - //3. Perform an operation to save a new Product via mongoTemplate. - mongoTemplate.save(new Product(1L)); + //3. Perform an operation to create a new collection via mongoTemplate. + mongoTemplate.createCollection(Product.class); //4. Now the database is created in MongoDB. assertTrue( @@ -111,7 +110,6 @@ public static void main(String[] args) { @NoArgsConstructor @Setter(AccessLevel.NONE) private static class Product { - @Indexed(unique = true) private Long article; } } From 274ce8027a6f571d9f6a26b3e984453417fe7aa8 Mon Sep 17 00:00:00 2001 From: Konstantin Silaev Date: Wed, 15 Jul 2020 15:15:46 +0300 Subject: [PATCH 35/38] Revert "Rename MongoDBDatabaseNameTest to MongoDBContainerDatabaseNameTest" This reverts commit 96e9c107 --- ...inerDatabaseNameTest.java => MongoDBDatabaseNameTest.java} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename modules/mongodb/src/test/java/org/testcontainers/containers/{MongoDBContainerDatabaseNameTest.java => MongoDBDatabaseNameTest.java} (96%) diff --git a/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBContainerDatabaseNameTest.java b/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBDatabaseNameTest.java similarity index 96% rename from modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBContainerDatabaseNameTest.java rename to modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBDatabaseNameTest.java index 326b3741c67..03ddf5f16f3 100644 --- a/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBContainerDatabaseNameTest.java +++ b/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBDatabaseNameTest.java @@ -34,9 +34,9 @@ @Slf4j @DataMongoTest -@ContextConfiguration(initializers = MongoDBContainerDatabaseNameTest.Initializer.class) +@ContextConfiguration(initializers = MongoDBDatabaseNameTest.Initializer.class) @RunWith(SpringRunner.class) -public class MongoDBContainerDatabaseNameTest { +public class MongoDBDatabaseNameTest { private static final MongoDBContainer MONGO_DB_CONTAINER = new MongoDBContainer(); private static final String DATABASE_NAME = "my-db"; From 1e41a591348b9c470661800c58f09bab2c9b98d2 Mon Sep 17 00:00:00 2001 From: Konstantin Silaev Date: Wed, 15 Jul 2020 15:15:52 +0300 Subject: [PATCH 36/38] Revert "Add MongoDBDatabaseNameTest" This reverts commit 2086fd65 --- modules/mongodb/build.gradle | 14 --- .../containers/MongoDBDatabaseNameTest.java | 115 ------------------ 2 files changed, 129 deletions(-) delete mode 100644 modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBDatabaseNameTest.java diff --git a/modules/mongodb/build.gradle b/modules/mongodb/build.gradle index 0bf702b34b5..57db1be21de 100644 --- a/modules/mongodb/build.gradle +++ b/modules/mongodb/build.gradle @@ -1,21 +1,7 @@ -plugins { - id 'org.springframework.boot' version '2.3.1.RELEASE' - id 'io.spring.dependency-management' version '1.0.9.RELEASE' -} - description = "Testcontainers :: MongoDB" dependencies { compile project(':testcontainers') testCompile("org.mongodb:mongodb-driver-sync:4.0.2") - testCompile("org.springframework.boot:spring-boot-starter") - testCompile("org.springframework.boot:spring-boot-starter-test") { - exclude group: 'org.junit.vintage', module: 'junit-vintage-engine' - } - testCompile("org.springframework.data:spring-data-mongodb:3.0.1.RELEASE") -} - -bootJar { - enabled = false } diff --git a/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBDatabaseNameTest.java b/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBDatabaseNameTest.java deleted file mode 100644 index 03ddf5f16f3..00000000000 --- a/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBDatabaseNameTest.java +++ /dev/null @@ -1,115 +0,0 @@ -package org.testcontainers.containers; - -import com.mongodb.client.MongoClient; -import com.mongodb.client.MongoClients; -import lombok.AccessLevel; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; -import lombok.Setter; -import lombok.extern.slf4j.Slf4j; -import org.jetbrains.annotations.NotNull; -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.test.autoconfigure.data.mongo.DataMongoTest; -import org.springframework.boot.test.util.TestPropertyValues; -import org.springframework.context.ApplicationContextInitializer; -import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.data.mongodb.core.MongoTemplate; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringRunner; - -import java.util.Objects; -import java.util.Spliterator; -import java.util.Spliterators; -import java.util.stream.StreamSupport; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -@Slf4j -@DataMongoTest -@ContextConfiguration(initializers = MongoDBDatabaseNameTest.Initializer.class) -@RunWith(SpringRunner.class) -public class MongoDBDatabaseNameTest { - private static final MongoDBContainer MONGO_DB_CONTAINER = new MongoDBContainer(); - private static final String DATABASE_NAME = "my-db"; - - @Autowired - private MongoTemplate mongoTemplate; - - @BeforeClass - public static void setUpAll() { - MONGO_DB_CONTAINER.start(); - } - - @AfterClass - public static void tearDownAll() { - MONGO_DB_CONTAINER.stop(); - } - - @Test - public void shouldTestDatabaseName() { - //1. Database name was already set to the MongoTemplate during auto-config. - assertEquals(DATABASE_NAME, mongoTemplate.getDb().getName()); - - try (final MongoClient mongoSyncClient = MongoClients.create(MONGO_DB_CONTAINER.getReplicaSetUrl(DATABASE_NAME))) { - //2. But the database is not created yet, because we have not performed any writing operation. - assertFalse( - "Database: " + DATABASE_NAME + " is supposed to be here", - isDatabaseInMongoDB(mongoSyncClient, DATABASE_NAME) - ); - - //3. Perform an operation to create a new collection via mongoTemplate. - mongoTemplate.createCollection(Product.class); - - //4. Now the database is created in MongoDB. - assertTrue( - "Database: " + DATABASE_NAME + " is supposed to be here", - isDatabaseInMongoDB(mongoSyncClient, DATABASE_NAME) - ); - } - } - - private boolean isDatabaseInMongoDB( - final MongoClient mongoSyncClient, - final String databaseName - ) { - Objects.requireNonNull(databaseName); - return StreamSupport.stream( - Spliterators.spliteratorUnknownSize( - mongoSyncClient.listDatabaseNames().iterator(), - Spliterator.ORDERED - ), - false - ).anyMatch(databaseName::equals); - } - - static class Initializer implements ApplicationContextInitializer { - @Override - public void initialize(@NotNull ConfigurableApplicationContext configurableApplicationContext) { - TestPropertyValues.of( - String.format("spring.data.mongodb.uri: %s", MONGO_DB_CONTAINER.getReplicaSetUrl(DATABASE_NAME)) - ).applyTo(configurableApplicationContext); - } - } - - @SpringBootApplication - static class SpringBootApp { - public static void main(String[] args) { - } - } - - @Data - @AllArgsConstructor - @NoArgsConstructor - @Setter(AccessLevel.NONE) - private static class Product { - private Long article; - } -} From bb0e08195a6a7f4c2674fcf2bc7f7b907323cc96 Mon Sep 17 00:00:00 2001 From: Konstantin Silaev Date: Wed, 15 Jul 2020 15:23:11 +0300 Subject: [PATCH 37/38] Add shouldTestDatabaseName unit test --- .../containers/MongoDBContainerTest.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBContainerTest.java b/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBContainerTest.java index 7c4f4c12408..4ec7b90fec6 100644 --- a/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBContainerTest.java +++ b/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBContainerTest.java @@ -12,8 +12,10 @@ import org.bson.Document; import org.junit.Test; +import static org.hamcrest.CoreMatchers.endsWith; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThat; public class MongoDBContainerTest { @@ -71,4 +73,15 @@ public void shouldExecuteTransactions() { } } } + + @Test + public void shouldTestDatabaseName() { + try ( + final MongoDBContainer mongoDBContainer = new MongoDBContainer() + ) { + mongoDBContainer.start(); + final String databaseName = "my-db"; + assertThat(mongoDBContainer.getReplicaSetUrl(databaseName), endsWith(databaseName)); + } + } } From d02daf7e903b7f2ab57757c047b66a53cfea8a2c Mon Sep 17 00:00:00 2001 From: Konstantin Silaev Date: Tue, 28 Jul 2020 13:15:13 +0300 Subject: [PATCH 38/38] Use new constructor as per #2839 --- .../org/testcontainers/containers/MongoDBContainerTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBContainerTest.java b/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBContainerTest.java index ff19b43c335..7ea0ed2331a 100644 --- a/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBContainerTest.java +++ b/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBContainerTest.java @@ -79,7 +79,7 @@ public void shouldExecuteTransactions() { @Test public void shouldTestDatabaseName() { try ( - final MongoDBContainer mongoDBContainer = new MongoDBContainer() + final MongoDBContainer mongoDBContainer = new MongoDBContainer(DockerImageName.parse("mongo:4.0.10")) ) { mongoDBContainer.start(); final String databaseName = "my-db";