From f66a6d256fa5e5f7b9b2c358a41953b5ca5699ea Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 3 Feb 2022 14:14:38 +0000 Subject: [PATCH 0001/3231] Bump mysql-connector-java from 8.0.27 to 8.0.28 in /modules/jdbc-test Bumps [mysql-connector-java](https://github.com/mysql/mysql-connector-j) from 8.0.27 to 8.0.28. - [Release notes](https://github.com/mysql/mysql-connector-j/releases) - [Changelog](https://github.com/mysql/mysql-connector-j/blob/release/8.0/CHANGES) - [Commits](https://github.com/mysql/mysql-connector-j/compare/8.0.27...8.0.28) --- updated-dependencies: - dependency-name: mysql:mysql-connector-java dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/jdbc-test/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jdbc-test/build.gradle b/modules/jdbc-test/build.gradle index 98570722e6a..2ea623f75a1 100644 --- a/modules/jdbc-test/build.gradle +++ b/modules/jdbc-test/build.gradle @@ -16,5 +16,5 @@ dependencies { api 'org.apache.tomcat:tomcat-jdbc:10.0.16' api 'org.vibur:vibur-dbcp:25.0' - api 'mysql:mysql-connector-java:8.0.27' + api 'mysql:mysql-connector-java:8.0.28' } From c5bec3cee6de41c89c0270da0b42fc126141ec7f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 3 Feb 2022 14:14:43 +0000 Subject: [PATCH 0002/3231] Bump cucumber-junit from 7.2.2 to 7.2.3 in /examples Bumps [cucumber-junit](https://github.com/cucumber/cucumber-jvm) from 7.2.2 to 7.2.3. - [Release notes](https://github.com/cucumber/cucumber-jvm/releases) - [Changelog](https://github.com/cucumber/cucumber-jvm/blob/main/CHANGELOG.md) - [Commits](https://github.com/cucumber/cucumber-jvm/compare/v7.2.2...v7.2.3) --- updated-dependencies: - dependency-name: io.cucumber:cucumber-junit dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/cucumber/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cucumber/build.gradle b/examples/cucumber/build.gradle index 9a402b51b47..78d042b95fd 100644 --- a/examples/cucumber/build.gradle +++ b/examples/cucumber/build.gradle @@ -11,6 +11,6 @@ dependencies { implementation 'org.seleniumhq.selenium:selenium-firefox-driver:3.141.59' implementation 'org.seleniumhq.selenium:selenium-chrome-driver:3.141.59' testImplementation 'io.cucumber:cucumber-java:7.2.3' - testImplementation 'io.cucumber:cucumber-junit:7.2.2' + testImplementation 'io.cucumber:cucumber-junit:7.2.3' testImplementation 'org.testcontainers:selenium' } From 8de57a2c31db71a3375a70580f73a2c7d702f7ee Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 3 Feb 2022 14:14:47 +0000 Subject: [PATCH 0003/3231] Bump mysql-connector-java from 8.0.27 to 8.0.28 in /modules/mysql Bumps [mysql-connector-java](https://github.com/mysql/mysql-connector-j) from 8.0.27 to 8.0.28. - [Release notes](https://github.com/mysql/mysql-connector-j/releases) - [Changelog](https://github.com/mysql/mysql-connector-j/blob/release/8.0/CHANGES) - [Commits](https://github.com/mysql/mysql-connector-j/compare/8.0.27...8.0.28) --- updated-dependencies: - dependency-name: mysql:mysql-connector-java dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/mysql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mysql/build.gradle b/modules/mysql/build.gradle index 88a4473f56c..1796ff17ef0 100644 --- a/modules/mysql/build.gradle +++ b/modules/mysql/build.gradle @@ -10,7 +10,7 @@ dependencies { compileOnly 'dev.miku:r2dbc-mysql:0.8.2.RELEASE' testImplementation project(':jdbc-test') - testImplementation 'mysql:mysql-connector-java:8.0.27' + testImplementation 'mysql:mysql-connector-java:8.0.28' testImplementation testFixtures(project(':r2dbc')) testImplementation 'dev.miku:r2dbc-mysql:0.8.2.RELEASE' From 71a073bc5ddfce38b2b5348d9d7d4eb63f296290 Mon Sep 17 00:00:00 2001 From: Maciej Walkowiak Date: Thu, 17 Feb 2022 14:40:19 +0100 Subject: [PATCH 0004/3231] Polish code samples on container logs so that they are easier to copy & paste (#5038) --- docs/features/container_logs.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/features/container_logs.md b/docs/features/container_logs.md index d603981059a..a063e5329e4 100644 --- a/docs/features/container_logs.md +++ b/docs/features/container_logs.md @@ -40,19 +40,19 @@ By default both standard out and standard error will both be emitted at INFO lev Standard error may be emitted at ERROR level, if desired: ```java -Slf4jLogConsumer logConsumer = new Slf4jLogConsumer(log).withSeparateOutputStreams() +Slf4jLogConsumer logConsumer = new Slf4jLogConsumer(LOGGER).withSeparateOutputStreams(); ``` The [Mapped Diagnostic Context (MDC)](http://logback.qos.ch/manual/mdc.html) for emitted messages may be configured using the `withMdc(...)` option: ```java -Slf4jLogConsumer logConsumer = new Slf4jLogConsumer(log).withMdc("key", "value") +Slf4jLogConsumer logConsumer = new Slf4jLogConsumer(LOGGER).withMdc("key", "value"); ``` or using an existing map of key-value pairs: ```java -Slf4jLogConsumer logConsumer = new Slf4jLogConsumer(log).withMdc(map) +Slf4jLogConsumer logConsumer = new Slf4jLogConsumer(LOGGER).withMdc(map); ``` ### Capturing container output as a String From bfb8cecd69f852eb2ffa8ee93edbfb0edc54762a Mon Sep 17 00:00:00 2001 From: Yannick Weber Date: Thu, 17 Feb 2022 14:54:27 +0100 Subject: [PATCH 0005/3231] New Module: HiveMQ (#4797) Co-authored-by: Jochen Mader --- .github/dependabot.yml | 5 + .../strategy/MultiLogMessageWaitStrategy.java | 59 ++ docs/modules/hivemq.md | 235 +++++++ docs/modules_logos/hivemq-module.png | Bin 0 -> 165340 bytes mkdocs.yml | 1 + modules/hivemq/build.gradle | 37 ++ .../hivemq/HiveMQContainer.java | 573 ++++++++++++++++++ .../hivemq/HiveMQExtension.java | 287 +++++++++ .../org/testcontainers/hivemq/PathUtil.java | 26 + .../hivemq/ContainerWithControlCenterIT.java | 34 ++ .../hivemq/ContainerWithCustomConfigIT.java | 43 ++ ...ContainerWithExtensionFromDirectoryIT.java | 43 ++ .../hivemq/ContainerWithExtensionIT.java | 41 ++ .../ContainerWithExtensionSubclassIT.java | 35 ++ .../ContainerWithFileInExtensionHomeIT.java | 77 +++ .../hivemq/ContainerWithFileInHomeIT.java | 75 +++ .../hivemq/ContainerWithLicenseIT.java | 78 +++ .../ContainerWithoutPlatformExtensionsIT.java | 135 +++++ .../hivemq/CreateFileInCopiedDirectoryIT.java | 92 +++ .../CreateFileInExtensionDirectoryIT.java | 76 +++ ...DisableEnableExtensionFromDirectoryIT.java | 37 ++ .../hivemq/DisableEnableExtensionIT.java | 46 ++ .../hivemq/HiveMQExtensionTest.java | 13 + .../hivemq/HiveMQTestContainerCore.java | 82 +++ .../testcontainers/hivemq/PathUtilTest.java | 43 ++ .../hivemq/docs/DemoDisableExtensionsIT.java | 63 ++ .../hivemq/docs/DemoExtensionTestsIT.java | 57 ++ .../hivemq/docs/DemoFilesIT.java | 62 ++ .../hivemq/docs/DemoHiveMQContainerIT.java | 61 ++ .../hivemq/util/MyExtension.java | 37 ++ .../util/MyExtensionWithSubclasses.java | 26 + .../hivemq/util/PublishModifier.java | 16 + .../hivemq/util/TestPublishModifiedUtil.java | 48 ++ .../src/test/resources/additionalFile.txt | 0 modules/hivemq/src/test/resources/config.xml | 15 + .../src/test/resources/inMemoryConfig.xml | 6 + .../src/test/resources/logback-test.xml | 18 + .../hivemq-extension.xml | 8 + .../modifier-extension-1.0-SNAPSHOT.jar | Bin 0 -> 102179 bytes .../modifier-extension/hivemq-extension.xml | 8 + .../modifier-extension-1.0-SNAPSHOT.jar | Bin 0 -> 102179 bytes .../test/resources/myExtensionLicense.elic | 0 .../hivemq/src/test/resources/myLicense.lic | 0 43 files changed, 2598 insertions(+) create mode 100644 core/src/main/java/org/testcontainers/containers/wait/strategy/MultiLogMessageWaitStrategy.java create mode 100644 docs/modules/hivemq.md create mode 100644 docs/modules_logos/hivemq-module.png create mode 100644 modules/hivemq/build.gradle create mode 100644 modules/hivemq/src/main/java/org/testcontainers/hivemq/HiveMQContainer.java create mode 100644 modules/hivemq/src/main/java/org/testcontainers/hivemq/HiveMQExtension.java create mode 100755 modules/hivemq/src/main/java/org/testcontainers/hivemq/PathUtil.java create mode 100644 modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithControlCenterIT.java create mode 100755 modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithCustomConfigIT.java create mode 100755 modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithExtensionFromDirectoryIT.java create mode 100755 modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithExtensionIT.java create mode 100755 modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithExtensionSubclassIT.java create mode 100755 modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithFileInExtensionHomeIT.java create mode 100755 modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithFileInHomeIT.java create mode 100755 modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithLicenseIT.java create mode 100644 modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithoutPlatformExtensionsIT.java create mode 100755 modules/hivemq/src/test/java/org/testcontainers/hivemq/CreateFileInCopiedDirectoryIT.java create mode 100755 modules/hivemq/src/test/java/org/testcontainers/hivemq/CreateFileInExtensionDirectoryIT.java create mode 100644 modules/hivemq/src/test/java/org/testcontainers/hivemq/DisableEnableExtensionFromDirectoryIT.java create mode 100644 modules/hivemq/src/test/java/org/testcontainers/hivemq/DisableEnableExtensionIT.java create mode 100644 modules/hivemq/src/test/java/org/testcontainers/hivemq/HiveMQExtensionTest.java create mode 100644 modules/hivemq/src/test/java/org/testcontainers/hivemq/HiveMQTestContainerCore.java create mode 100755 modules/hivemq/src/test/java/org/testcontainers/hivemq/PathUtilTest.java create mode 100644 modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoDisableExtensionsIT.java create mode 100644 modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoExtensionTestsIT.java create mode 100644 modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoFilesIT.java create mode 100644 modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoHiveMQContainerIT.java create mode 100755 modules/hivemq/src/test/java/org/testcontainers/hivemq/util/MyExtension.java create mode 100755 modules/hivemq/src/test/java/org/testcontainers/hivemq/util/MyExtensionWithSubclasses.java create mode 100755 modules/hivemq/src/test/java/org/testcontainers/hivemq/util/PublishModifier.java create mode 100755 modules/hivemq/src/test/java/org/testcontainers/hivemq/util/TestPublishModifiedUtil.java create mode 100755 modules/hivemq/src/test/resources/additionalFile.txt create mode 100755 modules/hivemq/src/test/resources/config.xml create mode 100644 modules/hivemq/src/test/resources/inMemoryConfig.xml create mode 100644 modules/hivemq/src/test/resources/logback-test.xml create mode 100755 modules/hivemq/src/test/resources/modifier-extension-wrong-name/hivemq-extension.xml create mode 100644 modules/hivemq/src/test/resources/modifier-extension-wrong-name/modifier-extension-1.0-SNAPSHOT.jar create mode 100755 modules/hivemq/src/test/resources/modifier-extension/hivemq-extension.xml create mode 100644 modules/hivemq/src/test/resources/modifier-extension/modifier-extension-1.0-SNAPSHOT.jar create mode 100755 modules/hivemq/src/test/resources/myExtensionLicense.elic create mode 100755 modules/hivemq/src/test/resources/myLicense.lic diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 355d44f2bb4..5d67f278219 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -57,6 +57,11 @@ updates: schedule: interval: "monthly" open-pull-requests-limit: 10 + - package-ecosystem: "gradle" + directory: "/modules/hivemq" + schedule: + interval: "monthly" + open-pull-requests-limit: 10 - package-ecosystem: "gradle" directory: "/modules/influxdb" schedule: diff --git a/core/src/main/java/org/testcontainers/containers/wait/strategy/MultiLogMessageWaitStrategy.java b/core/src/main/java/org/testcontainers/containers/wait/strategy/MultiLogMessageWaitStrategy.java new file mode 100644 index 00000000000..0d342f3050f --- /dev/null +++ b/core/src/main/java/org/testcontainers/containers/wait/strategy/MultiLogMessageWaitStrategy.java @@ -0,0 +1,59 @@ +package org.testcontainers.containers.wait.strategy; + +import org.testcontainers.DockerClientFactory; +import org.testcontainers.containers.ContainerLaunchException; +import org.testcontainers.containers.output.OutputFrame; +import org.testcontainers.containers.output.WaitingConsumer; +import org.testcontainers.utility.LogUtils; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.function.Predicate; + +/** + * This is a wait strategy to wait for multiple log patterns. + * The wait strategy will continue when every log pattern is matched at least once. + */ +public class MultiLogMessageWaitStrategy extends AbstractWaitStrategy { + + private final ConcurrentHashMap regexes = new ConcurrentHashMap<>(); + + @Override + protected void waitUntilReady() { + WaitingConsumer waitingConsumer = new WaitingConsumer(); + LogUtils.followOutput(DockerClientFactory.instance().client(), waitStrategyTarget.getContainerId(), waitingConsumer); + + Predicate waitPredicate = outputFrame -> { + if (regexes.isEmpty()) { + return true; + } + regexes.entrySet().forEach(regexHasMatched -> { + final boolean matched = outputFrame.getUtf8String().matches("(?s)" + regexHasMatched.getKey()); + if (matched) { + regexHasMatched.setValue(true); + } + }); + return regexes.values().stream().reduce(Boolean::logicalAnd).orElse(true); + }; + + try { + waitingConsumer.waitUntil(waitPredicate, startupTimeout.getSeconds(), TimeUnit.SECONDS, 1); + } catch (TimeoutException e) { + throw new ContainerLaunchException("Timed out waiting for log output matching '" + regexes + "'"); + } + } + + public MultiLogMessageWaitStrategy withRegEx(final String regEx) { + regexes.put(regEx, false); + return this; + } + + public MultiLogMessageWaitStrategy reset() { + for (final Map.Entry stringBooleanEntry : regexes.entrySet()) { + stringBooleanEntry.setValue(false); + } + return this; + } +} diff --git a/docs/modules/hivemq.md b/docs/modules/hivemq.md new file mode 100644 index 00000000000..1e09bac4fc0 --- /dev/null +++ b/docs/modules/hivemq.md @@ -0,0 +1,235 @@ +# HiveMQ Module + +drawing + +Automatic starting HiveMQ docker containers for JUnit4 and JUnit5 tests. +This enables testing MQTT client applications and integration testing of custom HiveMQ extensions. + +- Community forum: https://community.hivemq.com/ +- HiveMQ website: https://www.hivemq.com/ +- MQTT resources: + - [MQTT Essentials](https://www.hivemq.com/mqtt-essentials/) + - [MQTT 5 Essentials](https://www.hivemq.com/mqtt-5/) + +Please make sure to check out the hivemq-docs for the [Community Edition](https://github.com/hivemq/hivemq-community-edition/wiki/) +and the [Enterprise Edition](https://www.hivemq.com/docs/hivemq/4.7/user-guide/). + +## Using HiveMQ CE/EE + +HiveMQ provides different editions of on [Docker Hub](https://hub.docker.com/u/hivemq): + +- the open source [Community Edition](https://github.com/hivemq/hivemq-community-edition) which +is published as *hivemq/hivemq-ce*. +- the [Enterprise Edition](https://www.hivemq.com/docs/hivemq/4.7/user-guide/) which is published as *hivemq/hivemq4*. + +Both editions can be used directly: + +Using the Community Edition: + +[Community Edition HiveMQ image](../../modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoHiveMQContainerIT.java) inside_block:ceVersion + + +Using the Enterprise Edition: + +[Enterprise Edition HiveMQ image](../../modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoHiveMQContainerIT.java) inside_block:eeVersion + + +Using a specifc version is possible by using the tag: + +[Specific HiveMQ Version](../../modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoHiveMQContainerIT.java) inside_block:specificVersion + + +## Test your MQTT 3 and MQTT 5 client application + +Using an Mqtt-client (e.g. the [HiveMQ-Mqtt-Client](https://github.com/hivemq/hivemq-mqtt-client)) you can start +testing directly. + + +[MQTT5 Client](../../modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoHiveMQContainerIT.java) inside_block:mqtt5client + + +## Settings + +There are several things that can be adjusted before container setup. +The following example shows how to enable the Control Center (this is an enterprise feature), set the log level to DEBUG +and load a HiveMQ-config-file from the classpath. + + +[Config Examples](../../modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoHiveMQContainerIT.java) inside_block:eeVersionWithControlCenter + + +--- +**Note:** +The Control Center of HiveMQ can be accessed via the URL presented in the output of the starting container: + +``` +2021-09-10 10:35:53,511 INFO - The HiveMQ Control Center is reachable under: http://localhost:55032 +``` + +Please be aware that the Control Center is a feature of the enterprise edition of HiveMQ and thus only available with +the enterprise image. + +--- + +## Testing HiveMQ extensions + +Using the [Extension SDK](https://github.com/hivemq/hivemq-extension-sdk) the functionality of all editions of HiveMQ +can be extended. +The HiveMQ module also supports testing your own custom extensions. + +### Wait Strategy + +The raw HiveMQ module is built to wait for certain startup log messages to signal readiness. +Since extensions are loaded dynamically they can be available a short while after the main container has started. +We therefore provide custom wait conditions for HiveMQ Extensions: + +The following will specify an extension to be loaded from **src/test/resources/modifier-extension** into the container and +wait for an extension named **'My Extension Name'** to be started: + + +[Custom Wait Strategy](../../modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoExtensionTestsIT.java) inside_block:waitStrategy + + +Next up we have an example for using an extension directly from the classpath and waiting directly on the extension: + + +[Extension from Classpath](../../modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoExtensionTestsIT.java) inside_block:extensionClasspath + + +--- +**Note** Debugging extensions + +Both examples contain ```.withDebugging()``` which enables remote debugging on the container. +With debugging enabled you can start putting breakpoints right into your extensions. + +--- + +### Testing extensions using Gradle + +In a Gradle based HiveMQ Extension project, testing is supported using the dedicated [HiveMQ Extension Gradle Plugin](https://github.com/hivemq/hivemq-extension-gradle-plugin/README.md). + +The plugin adds an `integrationTest` task which executes tests from the `integrationTest` source set. +- Integration test source files are defined in `src/integrationTest`. +- Integration test dependencies are defined via the `integrationTestImplementation`, `integrationTestRuntimeOnly`, etc. configurations. + +The `integrationTest` task builds the extension and unzips it to the `build/hivemq-extension-test` directory. +The tests can then load the built extension into the HiveMQ Testcontainer. + + +[Extension from filesystem](../../modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoDisableExtensionsIT.java) inside_block:startFromFilesystem + + +### Enable/Disable an extension + +It is possible to enable and disable HiveMQ extensions during runtime. Extensions can also be disabled on startup. + +--- +**Note**: that disabling or enabling of extension during runtime is only supported in HiveMQ 4 Enterprise Edition Containers. + +--- + +The following example shows how to start a HiveMQ container with the extension called **my-extension** being disabled. + + + +[Disable Extension at startup](../../modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoDisableExtensionsIT.java) inside_block:startDisabled + + +The following test then proceeds to enable and then disable the extension: + + +[Enable/Disable extension at runtime](../../modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoDisableExtensionsIT.java) inside_block:runtimeEnable + + +## Enable/Disable an extension loaded from a folder + +Extensions loaded from an extension folder during runtime can also be enabled/disabled on the fly. +If the extension folder contains a DISABLED file, the extension will be disabled during startup. + +--- +**Note**: that disabling or enabling of extension during runtime is only supported in HiveMQ 4 Enterprise Edition Containers. + +--- + +We first load the extension from the filesytem: + +[Extension from filesystem](../../modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoDisableExtensionsIT.java) inside_block:startFromFilesystem + + +Now we can enable/disable the extension using its name: + + +[Enable/Disable extension at runtime](../../modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoDisableExtensionsIT.java) inside_block:runtimeEnableFilesystem + + +### Remove prepackaged HiveMQ Extensions + +Since HiveMQ's 4.4 release, HiveMQ Docker images come with the HiveMQ Extension for Kafka, the HiveMQ Enterprise Bridge Extension +and the HiveMQ Enterprise Security Extension. +These Extensions are disabled by default, but sometimes you my need to remove them before the container starts. + +Removing all extension is as simple as: + + +[Remove all extensions](../../modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoDisableExtensionsIT.java) inside_block:noExtensions + + +A single extension (e.g. Kafka) can be removed as easily: + +[Remove a specific extension](../../modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoDisableExtensionsIT.java) inside_block:noKafkaExtension + + +## Put files into the container + +### Put a file into HiveMQ home + + +[Put file into HiveMQ home](../../modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoFilesIT.java) inside_block:hivemqHome + + +### Put files into extension home + + +[Put file into HiveMQ-Extension home](../../modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoFilesIT.java) inside_block:extensionHome + + +### Put license files into the container + + +[Put license file into the HiveMQ container](../../modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoFilesIT.java) inside_block:withLicenses + + + +### Customize the Container further + +Since the `HiveMQContainer` extends from [Testcontainer's](https://github.com/testcontainers) `GenericContainer` the container +can be customized as desired. + +## Add to your project + +### Gradle + +Add to `build.gradle`: + +````groovy +testImplementation 'org.testcontainers:hivemq:{{latest_version}}' +```` + +Add to `build.gradle.kts`: + +````kotlin +testImplementation("org.testcontainers:hivemq:{{latest_version}}") +```` + +### Maven + +Add to `pom.xml`: + +```xml + + org.testcontainers + hivemq + {{latest_version}} + test + +``` diff --git a/docs/modules_logos/hivemq-module.png b/docs/modules_logos/hivemq-module.png new file mode 100644 index 0000000000000000000000000000000000000000..55c0ee1644ae05a5ff5b36f90ebdb1006b296f42 GIT binary patch literal 165340 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ⅈLGoj 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 E0rq9^#Q*>R literal 0 HcmV?d00001 diff --git a/mkdocs.yml b/mkdocs.yml index e4115625a7f..7ace7382e41 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -67,6 +67,7 @@ nav: - modules/docker_compose.md - modules/elasticsearch.md - modules/gcloud.md + - modules/hivemq.md - modules/k3s.md - modules/kafka.md - modules/localstack.md diff --git a/modules/hivemq/build.gradle b/modules/hivemq/build.gradle new file mode 100644 index 00000000000..779ac2ec9d8 --- /dev/null +++ b/modules/hivemq/build.gradle @@ -0,0 +1,37 @@ +description = "TestContainers :: HiveMQ" + +dependencies { + api(project(":testcontainers")) + api("org.jetbrains:annotations:23.0.0") + + shaded("org.apache.commons:commons-lang3:3.12.0") + shaded("commons-io:commons-io:2.11.0") + shaded("org.javassist:javassist:3.28.0-GA") + shaded("org.jboss.shrinkwrap:shrinkwrap-api:1.2.6") + shaded("org.jboss.shrinkwrap:shrinkwrap-impl-base:1.2.6") + shaded("net.lingala.zip4j:zip4j:2.9.1") + + testImplementation("org.junit.jupiter:junit-jupiter-api:5.8.2") + testImplementation(project(":junit-jupiter")) + testImplementation("com.hivemq:hivemq-extension-sdk:4.7.3") + testImplementation("com.hivemq:hivemq-mqtt-client:1.3.0") + testImplementation("org.apache.httpcomponents:httpclient:4.5.13") + testImplementation("ch.qos.logback:logback-classic:1.2.10") + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2") +} + +test { + useJUnitPlatform() + testLogging { + events "passed", "skipped", "failed" + } + javaLauncher = javaToolchains.launcherFor { + languageVersion = JavaLanguageVersion.of(11) + } +} + +compileTestJava { + javaCompiler = javaToolchains.compilerFor { + languageVersion = JavaLanguageVersion.of(11) + } +} diff --git a/modules/hivemq/src/main/java/org/testcontainers/hivemq/HiveMQContainer.java b/modules/hivemq/src/main/java/org/testcontainers/hivemq/HiveMQContainer.java new file mode 100644 index 00000000000..37f9a9259d1 --- /dev/null +++ b/modules/hivemq/src/main/java/org/testcontainers/hivemq/HiveMQContainer.java @@ -0,0 +1,573 @@ +package org.testcontainers.hivemq; + +import com.github.dockerjava.api.command.InspectContainerResponse; +import org.apache.commons.io.FileUtils; +import org.jetbrains.annotations.NotNull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.event.Level; +import org.testcontainers.containers.ContainerLaunchException; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.wait.strategy.MultiLogMessageWaitStrategy; +import org.testcontainers.utility.DockerImageName; +import org.testcontainers.utility.MountableFile; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.time.Duration; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +public class HiveMQContainer extends GenericContainer { + + private static final Logger LOGGER = LoggerFactory.getLogger(HiveMQContainer.class); + + private static final DockerImageName DEFAULT_HIVEMQ_EE_IMAGE_NAME = DockerImageName.parse("hivemq/hivemq4"); + private static final DockerImageName DEFAULT_HIVEMQ_CE_IMAGE_NAME = DockerImageName.parse("hivemq/hivemq-ce"); + + private static final int DEBUGGING_PORT = 9000; + private static final int MQTT_PORT = 1883; + private static final int CONTROL_CENTER_PORT = 8080; + @SuppressWarnings("OctalInteger") + private static final int MODE = 0777; + private static final @NotNull Pattern EXTENSION_ID_PATTERN = Pattern.compile("(.+?)"); + + private final @NotNull ConcurrentHashMap containerOutputLatches = new ConcurrentHashMap<>(); + private boolean controlCenterEnabled = false; + private boolean debugging = false; + + private final @NotNull Set prepackagedExtensionsToRemove = new HashSet<>(); + private boolean removeAllPrepackagedExtensions = false; + + private final @NotNull MultiLogMessageWaitStrategy waitStrategy = new MultiLogMessageWaitStrategy(); + + public HiveMQContainer(final @NotNull DockerImageName dockerImageName) { + super(dockerImageName); + + dockerImageName.assertCompatibleWith(DEFAULT_HIVEMQ_CE_IMAGE_NAME, DEFAULT_HIVEMQ_EE_IMAGE_NAME); + + addExposedPort(MQTT_PORT); + + waitStrategy.withRegEx("(.*)Started HiveMQ in(.*)"); + waitingFor(waitStrategy); + + withLogConsumer(outputFrame -> { + final String utf8String = outputFrame.getUtf8String(); + if (debugging && utf8String.startsWith("Listening for transport dt_socket at address:")) { + System.out.println("Listening for transport dt_socket at address: " + getMappedPort(DEBUGGING_PORT)); + } + if (!containerOutputLatches.isEmpty()) { + containerOutputLatches.forEach((regEx, latch) -> { + if (outputFrame.getUtf8String().matches("(?s)" + regEx)) { + LOGGER.debug("Container Output '{}' matched RegEx '{}'", utf8String, regEx); + latch.countDown(); + } else { + LOGGER.debug("Container Output '{}' did not match RegEx '{}'", utf8String, regEx); + } + }); + } + }); + } + + @Override + protected void configure() { + final String removeCommand; + withCreateContainerCmdModifier(it -> it.withEntrypoint("/bin/sh")); + + if (removeAllPrepackagedExtensions || !prepackagedExtensionsToRemove.isEmpty()) { + + if (removeAllPrepackagedExtensions) { + removeCommand = "rm -rf /opt/hivemq/extensions/** &&"; + } else { + removeCommand = prepackagedExtensionsToRemove.stream() + .map(extensionId -> "rm -rf /opt/hivemq/extensions/" + extensionId + "&&") + .collect(Collectors.joining()); + } + } else { + removeCommand = ""; + } + setCommand( + "-c", + removeCommand + + "cp -r '/opt/hivemq/temp-extensions/'* /opt/hivemq/extensions/ " + + "; rm -rf /opt/hivemq/temp-extensions/** " + + "&& chmod -R 777 /opt/hivemq/extensions " + + "&& /opt/docker-entrypoint.sh /opt/hivemq/bin/run.sh" + ); + } + + protected void containerIsStarted(final @NotNull InspectContainerResponse containerInfo) { + if (controlCenterEnabled) { + LOGGER.info("The HiveMQ Control Center is reachable under: http://{}:{}", getHost(), getMappedPort(CONTROL_CENTER_PORT)); + } + } + + /** + * Adds a wait condition for the extension with this name. + *

+ * Must be called before the container is started. + * + * @param extensionName the extension to wait for + * @return self + */ + public @NotNull HiveMQContainer waitForExtension(final @NotNull String extensionName) { + final String regEX = "(.*)Extension \"" + extensionName + "\" version (.*) started successfully(.*)"; + waitStrategy.withRegEx(regEX); + return self(); + } + + /** + * Adds a wait condition for this {@link HiveMQExtension} + *

+ * Must be called before the container is started. + * + * @param extension the extension to wait for + * @return self + */ + public @NotNull HiveMQContainer waitForExtension(final @NotNull HiveMQExtension extension) { + return this.waitForExtension(extension.getName()); + } + + /** + * Enables the possibility for remote debugging clients to connect. + *

+ * Must be called before the container is started. + * + * @return self + */ + public @NotNull HiveMQContainer withDebugging() { + debugging = true; + addExposedPorts(DEBUGGING_PORT); + withEnv("JAVA_OPTS", "-agentlib:jdwp=transport=dt_socket,address=0.0.0.0:" + DEBUGGING_PORT + ",server=y,suspend=y"); + return self(); + } + + /** + * Sets the logging {@link Level} inside the container. + *

+ * Must be called before the container is started. + * + * @param level the {@link Level} + * @return self + */ + public @NotNull HiveMQContainer withLogLevel(final @NotNull Level level) { + this.withEnv("HIVEMQ_LOG_LEVEL", level.name()); + return self(); + } + + /** + * Wraps the given class and all its subclasses into an extension + * and puts it into '/opt/hivemq/temp-extensions/{extension-id}' inside the container. + *

+ * Must be called before the container is started. + *

+ * The contents of the '/opt/hivemq/temp-extensions/' directory are copied to '/opt/hivemq/extensions/' before the container is started. + * + * @param hiveMQExtension the {@link HiveMQExtension} of the extension + * @return self + */ + public @NotNull HiveMQContainer withExtension(final @NotNull HiveMQExtension hiveMQExtension) { + try { + final File extension = hiveMQExtension.createExtension(hiveMQExtension); + final MountableFile mountableExtension = MountableFile.forHostPath(extension.getPath(), MODE); + withCopyFileToContainer(mountableExtension, "/opt/hivemq/temp-extensions/" + hiveMQExtension.getId()); + } catch (final Exception e) { + throw new ContainerLaunchException(e.getMessage() == null ? "" : e.getMessage(), e); + } + return self(); + } + + /** + * Puts the given extension folder into '/opt/hivemq/temp-extensions/{directory-name}' inside the container. + * It must at least contain a valid hivemq-extension.xml and a valid extension.jar in order to be executed. + * The directory-name is taken from the id defined in the hivemq-extension.xml. + *

+ * Must be called before the container is started. + *

+ * The contents of the '/opt/hivemq/temp-extensions/' directory are copied to '/opt/hivemq/extensions/' before the container is started. + * + * @param mountableExtension the extension folder on the host machine + * @return self + */ + public @NotNull HiveMQContainer withExtension(final @NotNull MountableFile mountableExtension) { + final File extensionDir = new File(mountableExtension.getResolvedPath()); + if (!extensionDir.exists()) { + throw new ContainerLaunchException("Extension '" + mountableExtension.getFilesystemPath() + "' could not be mounted. It does not exist."); + } + if (!extensionDir.isDirectory()) { + throw new ContainerLaunchException("Extension '" + mountableExtension.getFilesystemPath() + "' could not be mounted. It is not a directory."); + } + try { + final String extensionDirName = getExtensionDirectoryName(extensionDir); + final String containerPath = "/opt/hivemq/temp-extensions/" + extensionDirName; + withCopyFileToContainer(cloneWithFileMode(mountableExtension), containerPath); + LOGGER.info("Putting extension '{}' into '{}'", extensionDirName, containerPath); + } catch (final Exception e) { + throw new ContainerLaunchException(e.getMessage() == null ? "" : e.getMessage(), e); + } + return self(); + } + + private @NotNull String getExtensionDirectoryName(final @NotNull File extensionDirectory) throws IOException { + final File file = new File(extensionDirectory, "hivemq-extension.xml"); + final String xml = FileUtils.readFileToString(file, StandardCharsets.UTF_8); + final Matcher matcher = EXTENSION_ID_PATTERN.matcher(xml); + + if (!matcher.find()) { + throw new IllegalStateException("Could not parse extension id from '" + file.getAbsolutePath() + "'"); + } + return matcher.group(1); + } + + /** + * Removes the specified prepackaged extension folders from '/opt/hivemq/extensions' before the container is started. + *

+ * Must be called before the container is started. + * + * @param extensionIds the prepackaged extensions to remove + * @return self + */ + public @NotNull HiveMQContainer withoutPrepackagedExtensions(final @NotNull String... extensionIds) { + Collections.addAll(prepackagedExtensionsToRemove, extensionIds); + return self(); + } + + /** + * Removes all prepackaged extension folders from '/opt/hivemq/extensions' before the container is started. + *

+ * Must be called before the container is started. + * + * @return self + */ + public @NotNull HiveMQContainer withoutPrepackagedExtensions() { + removeAllPrepackagedExtensions = true; + return self(); + } + + /** + * Puts the given license into '/opt/hivemq/license/' inside the container. + * It must end with '.lic' or '.elic'. + *

+ * Must be called before the container is started. + * + * @param mountableLicense the license file on the host machine + * @return self + */ + public @NotNull HiveMQContainer withLicense(final @NotNull MountableFile mountableLicense) { + final File licenseFile = new File(mountableLicense.getResolvedPath()); + if (!licenseFile.exists()) { + throw new ContainerLaunchException("License file '" + mountableLicense.getFilesystemPath() + "' does not exist."); + } + if (!licenseFile.getName().endsWith(".lic") && !licenseFile.getName().endsWith(".elic")) { + throw new ContainerLaunchException("License file '" + mountableLicense.getFilesystemPath() + "' does not end wit '.lic' or '.elic'."); + } + final String containerPath = "/opt/hivemq/license/" + licenseFile.getName(); + withCopyFileToContainer(cloneWithFileMode(mountableLicense), containerPath); + LOGGER.info("Putting license '{}' into '{}'.", licenseFile.getAbsolutePath(), containerPath); + return self(); + } + + /** + * Overwrites the HiveMQ configuration in '/opt/hivemq/conf/' inside the container. + *

+ * Must be called before the container is started. + * + * @param mountableConfig the config file on the host machine + * @return self + */ + public @NotNull HiveMQContainer withHiveMQConfig(final @NotNull MountableFile mountableConfig) { + final File config = new File(mountableConfig.getResolvedPath()); + if (!config.exists()) { + throw new ContainerLaunchException("HiveMQ config file '" + mountableConfig.getFilesystemPath() + "' does not exist."); + } + final String containerPath = "/opt/hivemq/conf/config.xml"; + withCopyFileToContainer(cloneWithFileMode(mountableConfig), containerPath); + LOGGER.info("Putting '{}' into '{}'.", config.getAbsolutePath(), containerPath); + return self(); + } + + /** + * Puts the given file into the root of the extension's home '/opt/hivemq/temp-extensions/{extensionId}/'. + *

+ * Must be called before the container is started. + *

+ * The contents of the '/opt/hivemq/temp-extensions/' directory are copied to '/opt/hivemq/extensions/' before the container is started. + * + * @param file the file on the host machine + * @param extensionId the extension + * @return self + */ + public @NotNull HiveMQContainer withFileInExtensionHomeFolder( + final @NotNull MountableFile file, + final @NotNull String extensionId) { + + return withFileInExtensionHomeFolder(file, extensionId, ""); + } + + /** + * Puts the given file into given subdirectory of the extensions's home '/opt/hivemq/temp-extensions/{id}/{pathInExtensionHome}/' + *

+ * Must be called before the container is started. + *

+ * The contents of the '/opt/hivemq/temp-extensions/' directory are copied to '/opt/hivemq/extensions/' before the container is started. + * + * @param file the file on the host machine + * @param extensionId the extension + * @param pathInExtensionHome the path + * @return self + */ + public @NotNull HiveMQContainer withFileInExtensionHomeFolder( + final @NotNull MountableFile file, + final @NotNull String extensionId, + final @NotNull String pathInExtensionHome) { + + return withFileInHomeFolder(file, "/temp-extensions/" + extensionId + PathUtil.prepareAppendPath(pathInExtensionHome)); + } + + /** + * Puts the given file into the given subdirectory of the HiveMQ home folder '/opt/hivemq/{pathInHomeFolder}'. + *

+ * Must be called before the container is started. + * + * @param mountableFile the file on the host machine + * @param pathInHomeFolder the path + * @return self + */ + public @NotNull HiveMQContainer withFileInHomeFolder( + final @NotNull MountableFile mountableFile, + final @NotNull String pathInHomeFolder) { + + final File file = new File(mountableFile.getResolvedPath()); + + if (pathInHomeFolder.trim().isEmpty()) { + throw new ContainerLaunchException("pathInHomeFolder must not be empty"); + } + + if (!file.exists()) { + throw new ContainerLaunchException("File '" + mountableFile.getFilesystemPath() + "‘ does not exist."); + } + final String containerPath = "/opt/hivemq" + PathUtil.prepareAppendPath(pathInHomeFolder); + withCopyFileToContainer(cloneWithFileMode(mountableFile), containerPath); + LOGGER.info("Putting file '{}' into container path '{}'.", file.getAbsolutePath(), containerPath); + return self(); + } + + /** + * Disables the extension with the given name and extension directory name. + * This method blocks until the HiveMQ log for successful disabling is consumed or it times out after {timeOut}. + * Note: Disabling Extensions is a HiveMQ Enterprise feature, it will not work when using the HiveMQ Community Edition. + *

+ * This can only be called once the container is started. + * + * @param extensionName the name of the extension to disable + * @param extensionDirectory the name of the extension's directory + * @param timeout the timeout + * @throws TimeoutException if the extension was not disabled within the configured timeout + */ + public void disableExtension( + final @NotNull String extensionName, + final @NotNull String extensionDirectory, + final @NotNull Duration timeout) throws TimeoutException { + + final String regEX = "(.*)Extension \"" + extensionName + "\" version (.*) stopped successfully(.*)"; + try { + final String containerPath = "/opt/hivemq/extensions" + PathUtil.prepareInnerPath(extensionDirectory) + "DISABLED"; + + final CountDownLatch latch = new CountDownLatch(1); + containerOutputLatches.put(regEX, latch); + + execInContainer("touch", containerPath); + LOGGER.info("Putting DISABLED file into container path '{}'", containerPath); + + final boolean await = latch.await(timeout.getSeconds(), TimeUnit.SECONDS); + if (!await) { + throw new TimeoutException("Extension disabling timed out after '" + timeout.getSeconds() + "' seconds. " + + "Maybe you are using a HiveMQ Community Edition image, " + + "which does not support disabling of extensions"); + } + } catch (final InterruptedException | IOException e) { + throw new RuntimeException(e); + } finally { + containerOutputLatches.remove(regEX); + } + } + + /** + * Disables the extension with the given name and extension directory name. + * This method blocks until the HiveMQ log for successful disabling is consumed or it times out after 60 seconds. + * Note: Disabling Extensions is a HiveMQ Enterprise feature, it will not work when using the HiveMQ Community Edition. + *

+ * This can only be called once the container is started. + * + * @param extensionName the name of the extension to disable + * @param extensionDirectory the name of the extension's directory + * @throws TimeoutException if the extension was not disabled within 60 seconds + */ + public void disableExtension( + final @NotNull String extensionName, + final @NotNull String extensionDirectory) throws TimeoutException { + disableExtension(extensionName, extensionDirectory, Duration.ofSeconds(60)); + } + + /** + * Disables the extension. + * This method blocks until the HiveMQ log for successful disabling is consumed or it times out after {timeOut}. + * Note: Disabling Extensions is a HiveMQ Enterprise feature, it will not work when using the HiveMQ Community Edition. + *

+ * This can only be called once the container is started. + * + * @param hiveMQExtension the extension + * @param timeout the timeout + * @throws TimeoutException if the extension was not disabled within the configured timeout + */ + public void disableExtension( + final @NotNull HiveMQExtension hiveMQExtension, + final @NotNull Duration timeout) throws TimeoutException { + disableExtension(hiveMQExtension.getName(), hiveMQExtension.getId(), timeout); + } + + /** + * Disables the extension. + * This method blocks until the HiveMQ log for successful disabling is consumed or it times out after 60 seconds. + * Note: Disabling Extensions is a HiveMQ Enterprise feature, it will not work when using the HiveMQ Community Edition. + *

+ * This can only be called once the container is started. + * + * @param hiveMQExtension the extension + * @throws TimeoutException if the extension was not disabled within 60 seconds + */ + public void disableExtension(final @NotNull HiveMQExtension hiveMQExtension) throws TimeoutException { + disableExtension(hiveMQExtension, Duration.ofSeconds(60)); + } + + /** + * Enables the extension with the given name and extension directory name. + * This method blocks until the HiveMQ log for successful enabling is consumed or it times out after {timeOut}. + * Note: Enabling Extensions is a HiveMQ Enterprise feature, it will not work when using the HiveMQ Community Edition. + *

+ * This can only be called once the container is started. + * + * @param extensionName the name of the extension to disable + * @param extensionDirectory the name of the extension's directory + * @param timeout the timeout + * @throws TimeoutException if the extension was not enabled within the configured timeout + */ + public void enableExtension( + final @NotNull String extensionName, + final @NotNull String extensionDirectory, + final @NotNull Duration timeout) throws TimeoutException { + + final String regEX = "(.*)Extension \"" + extensionName + "\" version (.*) started successfully(.*)"; + try { + final String containerPath = "/opt/hivemq/extensions" + PathUtil.prepareInnerPath(extensionDirectory) + "DISABLED"; + + final CountDownLatch latch = new CountDownLatch(1); + containerOutputLatches.put(regEX, latch); + + execInContainer("rm", "-rf", containerPath); + LOGGER.info("Removing DISABLED file in container path '{}'", containerPath); + + final boolean await = latch.await(timeout.getSeconds(), TimeUnit.SECONDS); + if (!await) { + throw new TimeoutException("Extension enabling timed out after '" + timeout.getSeconds() + "' seconds. " + + "Maybe you are using a HiveMQ Community Edition image, " + + "which does not support disabling of extensions"); + } + } catch (final InterruptedException | IOException e) { + throw new RuntimeException(e); + } finally { + containerOutputLatches.remove(regEX); + } + } + + /** + * Enables the extension with the given name and extension directory name. + * This method blocks until the HiveMQ log for successful enabling is consumed or it times out after 60 seconds. + * Note: Enabling Extensions is a HiveMQ Enterprise feature, it will not work when using the HiveMQ Community Edition. + *

+ * This can only be called once the container is started. + * + * @param extensionName the name of the extension to disable + * @param extensionDirectory the name of the extension's directory + * @throws TimeoutException if the extension was not enabled within 60 seconds + */ + public void enableExtension( + final @NotNull String extensionName, + final @NotNull String extensionDirectory) throws TimeoutException { + enableExtension(extensionName, extensionDirectory, Duration.ofSeconds(60)); + } + + /** + * Enables the extension. + * This method blocks until the HiveMQ log for successful enabling is consumed or it times out after {timeOut}. + * Note: Enabling Extensions is a HiveMQ Enterprise feature, it will not work when using the HiveMQ Community Edition. + *

+ * This can only be called once the container is started. + * + * @param hiveMQExtension the extension + * @param timeout the timeout + * @throws TimeoutException if the extension was not enabled within the configured timeout + */ + public void enableExtension( + final @NotNull HiveMQExtension hiveMQExtension, + final @NotNull Duration timeout) throws TimeoutException { + enableExtension(hiveMQExtension.getName(), hiveMQExtension.getId(), timeout); + } + + /** + * Enables the extension. + * This method blocks until the HiveMQ log for successful enabling is consumed or it times out after {timeOut}. + * Note: Enabling Extensions is a HiveMQ Enterprise feature, it will not work when using the HiveMQ Community Edition. + *

+ * This can only be called once the container is started. + * + * @param hiveMQExtension the extension + * @throws TimeoutException if the extension was not enabled within 60 seconds + */ + public void enableExtension(final @NotNull HiveMQExtension hiveMQExtension) throws TimeoutException { + enableExtension(hiveMQExtension, Duration.ofSeconds(60)); + } + + /** + * Enables connection to the HiveMQ Control Center on host port 8080. + * Note: the control center is a HiveMQ 4 Enterprise feature. + *

+ * Must be called before the container is started. + * + * @return self + */ + public @NotNull HiveMQContainer withControlCenter() { + addExposedPorts(CONTROL_CENTER_PORT); + controlCenterEnabled = true; + return self(); + } + + /** + * Get the mapped port for the MQTT port of the container. + *

+ * Must be called after the container is started. + * + * @return the port on the host machine for mqtt clients to connect + */ + public int getMqttPort() { + return this.getMappedPort(MQTT_PORT); + } + + @Override + protected void containerIsStopping(final @NotNull InspectContainerResponse containerInfo) { + waitStrategy.reset(); + } + + private @NotNull MountableFile cloneWithFileMode(final @NotNull MountableFile mountableFile) { + return MountableFile.forHostPath(mountableFile.getResolvedPath(), HiveMQContainer.MODE); + } +} diff --git a/modules/hivemq/src/main/java/org/testcontainers/hivemq/HiveMQExtension.java b/modules/hivemq/src/main/java/org/testcontainers/hivemq/HiveMQExtension.java new file mode 100644 index 00000000000..a975439f4b3 --- /dev/null +++ b/modules/hivemq/src/main/java/org/testcontainers/hivemq/HiveMQExtension.java @@ -0,0 +1,287 @@ +package org.testcontainers.hivemq; + +import javassist.ClassPool; +import javassist.NotFoundException; +import lombok.Getter; +import org.apache.commons.io.FileUtils; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.exporter.ZipExporter; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testcontainers.containers.ContainerLaunchException; + +import java.io.File; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +public class HiveMQExtension { + + private static final String VALID_EXTENSION_XML = + "" + // + " %s" + // + " %s" + // + " %s" + // + " %s" + // + " %s" + // + ""; + private static final String EXTENSION_MAIN_CLASS_NAME = "com.hivemq.extension.sdk.api.ExtensionMain"; + private static final Logger LOGGER = LoggerFactory.getLogger(HiveMQExtension.class); + + @Getter + private final @NotNull String id; + + @Getter + private final @NotNull String name; + + @Getter + private final @NotNull String version; + + @Getter + private final int priority; + + @Getter + private final int startPriority; + + @Getter + private final boolean disabledOnStartup; + + @Getter + private final @NotNull Class mainClass; + + private final @NotNull List> additionalClasses; + + private HiveMQExtension( + final @NotNull String id, + final @NotNull String name, + final @NotNull String version, + final int priority, + final int startPriority, + final boolean disabledOnStartup, + final @NotNull Class mainClass, + final @NotNull List> additionalClasses) { + + this.id = id; + this.name = name; + this.version = version; + this.priority = priority; + this.startPriority = startPriority; + this.disabledOnStartup = disabledOnStartup; + this.mainClass = mainClass; + this.additionalClasses = additionalClasses; + } + + @NotNull File createExtension(final @NotNull HiveMQExtension hiveMQExtension) + throws Exception { + + final File tempDir = Files.createTempDirectory("").toFile(); + + final File extensionDir = new File(tempDir, hiveMQExtension.getId()); + FileUtils.writeStringToFile(new File(extensionDir, "hivemq-extension.xml"), + String.format( + VALID_EXTENSION_XML, + hiveMQExtension.getId(), + hiveMQExtension.getName(), + hiveMQExtension.getVersion(), + hiveMQExtension.getPriority(), + hiveMQExtension.getStartPriority()), + Charset.defaultCharset()); + + if (hiveMQExtension.isDisabledOnStartup()) { + final File disabled = new File(extensionDir, "DISABLED"); + final boolean newFile = disabled.createNewFile(); + if (!newFile) { + throw new ContainerLaunchException("Could not create DISABLED file '" + disabled.getAbsolutePath() + "' on host machine."); + } + } + + final JavaArchive javaArchive = + ShrinkWrap.create(JavaArchive.class) + .addAsServiceProvider(EXTENSION_MAIN_CLASS_NAME, hiveMQExtension.getMainClass().getName()); + + putSubclassesIntoJar(hiveMQExtension.getId(), hiveMQExtension.getMainClass(), javaArchive); + for (final Class additionalClass : hiveMQExtension.getAdditionalClasses()) { + javaArchive.addClass(additionalClass); + putSubclassesIntoJar(hiveMQExtension.getId(), additionalClass, javaArchive); + } + + javaArchive.as(ZipExporter.class).exportTo(new File(extensionDir, "extension.jar")); + + return extensionDir; + } + + private void putSubclassesIntoJar( + final @NotNull String extensionId, + final @Nullable Class clazz, + final @NotNull JavaArchive javaArchive) throws NotFoundException { + + if (clazz != null) { + final Set subClassNames = + ClassPool.getDefault().get(clazz.getName()).getClassFile().getConstPool().getClassNames(); + for (final String subClassName : subClassNames) { + final String className = subClassName.replaceAll("/", "."); + + if (!className.startsWith("[L")) { + LOGGER.debug("Trying to package subclass '{}' into extension '{}'.", className, extensionId); + javaArchive.addClass(className); + } else { + LOGGER.debug("Class '{}' will be ignored.", className); + } + } + } + } + + public @NotNull List> getAdditionalClasses() { + return Collections.unmodifiableList(additionalClasses); + } + + public static @NotNull Builder builder() { + return new Builder(); + } + + public static final class Builder { + private @Nullable String id; + private @Nullable String name; + private @Nullable String version; + private int priority = 0; + private int startPriority = 0; + private boolean disabledOnStartup = false; + private @Nullable Class mainClass; + private final @NotNull LinkedList> additionalClasses = new LinkedList<>(); + + /** + * Builds the {@link HiveMQExtension} with the provided values or default values. + * @return the HiveMQ Extension + */ + public @NotNull HiveMQExtension build() { + if (id == null || id.isEmpty()) { + throw new IllegalArgumentException("extension id must not be null or empty"); + } + if (name == null || name.isEmpty()) { + throw new IllegalArgumentException("extension name must not be null or empty"); + } + if (version == null || version.isEmpty()) { + throw new IllegalArgumentException("extension version must not be null or empty"); + } + if (mainClass == null) { + throw new IllegalArgumentException("extension main class must not be null"); + } + return new HiveMQExtension( + id, + name, + version, + priority, + startPriority, + disabledOnStartup, + mainClass, + additionalClasses + ); + } + + /** + * Sets the identifier of the {@link HiveMQExtension}. + * + * @param id the identifier, must not be empty + * @return the {@link Builder} + */ + public @NotNull Builder id(final @NotNull String id) { + this.id = id; + return this; + } + + /** + * Sets the name of the {@link HiveMQExtension}. + * + * @param name the identifier, must not be empty + * @return the {@link Builder} + */ + public @NotNull Builder name(final @NotNull String name) { + this.name = name; + return this; + } + + /** + * Sets the version of the {@link HiveMQExtension}. + * + * @param version the version, must not be empty + * @return the {@link Builder} + */ + public @NotNull Builder version(final @NotNull String version) { + this.version = version; + return this; + } + + /** + * Sets the priority of the {@link HiveMQExtension}. + * + * @param priority the priority + * @return the {@link Builder} + */ + public @NotNull Builder priority(final int priority) { + this.priority = priority; + return this; + } + + /** + * Sets the start-priority of the {@link HiveMQExtension}. + * + * @param startPriority the start-priority + * @return the {@link Builder} + */ + public @NotNull Builder startPriority(final int startPriority) { + this.startPriority = startPriority; + return this; + } + + /** + * Flag, that indicates whether the {@link HiveMQExtension} should be disabled when HiveMQ starts. + * Disabling on startup is achieved by placing a DISABLED file in the {@link HiveMQExtension}'s directory before coping it to the container. + * + * @param disabledOnStartup if the {@link HiveMQExtension} should be disabled when HiveMQ starts + * @return the {@link Builder} + */ + public @NotNull Builder disabledOnStartup(final boolean disabledOnStartup) { + this.disabledOnStartup = disabledOnStartup; + return this; + } + + /** + * The main class of the {@link HiveMQExtension}. + * This class MUST implement com.hivemq.extension.sdk.api.ExtensionMain. + * + * @param mainClass the main class + * @return the {@link Builder} + * @throws IllegalArgumentException if the provides class does not implement com.hivemq.extension.sdk.api.ExtensionMain} + * @throws IllegalStateException if com.hivemq.extension.sdk.api.ExtensionMain is not found in the classpath + */ + public @NotNull Builder mainClass(final @NotNull Class mainClass) { + try { + final Class extensionMain = Class.forName(EXTENSION_MAIN_CLASS_NAME); + if (!extensionMain.isAssignableFrom(mainClass)) { + throw new IllegalArgumentException("The provided class does not implement '" + EXTENSION_MAIN_CLASS_NAME + "'"); + } + this.mainClass = mainClass; + return this; + } catch (final ClassNotFoundException e) { + throw new IllegalStateException("The class '" + EXTENSION_MAIN_CLASS_NAME + "' was not found in the classpath."); + } + } + + /** + * Adds an additional class to the .jar file of the {@link HiveMQExtension}. + * + * @param clazz the additional class + * @return the {@link Builder} + */ + public @NotNull Builder addAdditionalClass(final @NotNull Class clazz) { + this.additionalClasses.add(clazz); + return this; + } + } +} diff --git a/modules/hivemq/src/main/java/org/testcontainers/hivemq/PathUtil.java b/modules/hivemq/src/main/java/org/testcontainers/hivemq/PathUtil.java new file mode 100755 index 00000000000..245052f46b8 --- /dev/null +++ b/modules/hivemq/src/main/java/org/testcontainers/hivemq/PathUtil.java @@ -0,0 +1,26 @@ +package org.testcontainers.hivemq; + +import org.jetbrains.annotations.NotNull; + +class PathUtil { + + static @NotNull String prepareInnerPath(@NotNull String innerPath) { + if ("/".equals(innerPath) || innerPath.isEmpty()) { + return "/"; + } + if (!innerPath.startsWith("/")) { + innerPath = "/" + innerPath; + } + if (!innerPath.endsWith("/")) { + innerPath += "/"; + } + return innerPath; + } + + static @NotNull String prepareAppendPath(@NotNull String appendPath) { + if (!appendPath.startsWith("/")) { + appendPath = "/" + appendPath; + } + return appendPath; + } +} diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithControlCenterIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithControlCenterIT.java new file mode 100644 index 00000000000..f88d60b22ed --- /dev/null +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithControlCenterIT.java @@ -0,0 +1,34 @@ +package org.testcontainers.hivemq; + +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpUriRequest; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; +import org.testcontainers.utility.DockerImageName; + +import java.util.concurrent.TimeUnit; + +public class ContainerWithControlCenterIT { + + public static final int CONTROL_CENTER_PORT = 8080; + + @Test + @Timeout(value = 3, unit = TimeUnit.MINUTES) + public void test() throws Exception { + + try (final HiveMQContainer hivemq = + new HiveMQContainer(DockerImageName.parse("hivemq/hivemq4").withTag("4.7.4")) + .withControlCenter()) { + + hivemq.start(); + + try (final CloseableHttpClient httpClient = HttpClientBuilder.create().build()) { + final HttpUriRequest request = new HttpGet("http://" + hivemq.getHost() + ":" + hivemq.getMappedPort(CONTROL_CENTER_PORT)); + httpClient.execute(request); + } + } + + } +} diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithCustomConfigIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithCustomConfigIT.java new file mode 100755 index 00000000000..17f09d86adf --- /dev/null +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithCustomConfigIT.java @@ -0,0 +1,43 @@ +package org.testcontainers.hivemq; + +import com.hivemq.client.mqtt.datatypes.MqttQos; +import com.hivemq.client.mqtt.exceptions.MqttSessionExpiredException; +import com.hivemq.client.mqtt.mqtt5.Mqtt5BlockingClient; +import com.hivemq.client.mqtt.mqtt5.Mqtt5Client; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; +import org.testcontainers.utility.DockerImageName; +import org.testcontainers.utility.MountableFile; + +import java.util.concurrent.TimeUnit; + +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class ContainerWithCustomConfigIT { + + @Test + @Timeout(value = 3, unit = TimeUnit.MINUTES) + void test() throws Exception { + + try (final HiveMQContainer hivemq = new HiveMQContainer(DockerImageName.parse("hivemq/hivemq4").withTag("4.7.4")) + .withHiveMQConfig(MountableFile.forClasspathResource("/config.xml"))) { + + hivemq.start(); + + final Mqtt5BlockingClient publisher = Mqtt5Client.builder() + .identifier("publisher") + .serverPort(hivemq.getMqttPort()) + .buildBlocking(); + + publisher.connect(); + + assertThrows(MqttSessionExpiredException.class, () -> { + // this should fail since only QoS 0 is allowed by the configuration + publisher.publishWith() + .topic("test/topic") + .qos(MqttQos.EXACTLY_ONCE) + .send(); + }); + } + } +} diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithExtensionFromDirectoryIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithExtensionFromDirectoryIT.java new file mode 100755 index 00000000000..7672c70925d --- /dev/null +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithExtensionFromDirectoryIT.java @@ -0,0 +1,43 @@ +package org.testcontainers.hivemq; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; +import org.slf4j.event.Level; +import org.testcontainers.hivemq.util.TestPublishModifiedUtil; +import org.testcontainers.utility.DockerImageName; +import org.testcontainers.utility.MountableFile; + +import java.util.concurrent.TimeUnit; + +public class ContainerWithExtensionFromDirectoryIT { + + @Test + @Timeout(value = 3, unit = TimeUnit.MINUTES) + void test() throws Exception { + try ( + final HiveMQContainer hivemq = + new HiveMQContainer(DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3")) + .withExtension(MountableFile.forClasspathResource("/modifier-extension")) + .waitForExtension("Modifier Extension") + .withHiveMQConfig(MountableFile.forClasspathResource("/inMemoryConfig.xml")) + .withLogLevel(Level.DEBUG)) { + + hivemq.start(); + TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort()); + } + } + + @Test + @Timeout(value = 3, unit = TimeUnit.MINUTES) + void test_wrongDirectoryName() throws Exception { + try (final HiveMQContainer hivemq = + new HiveMQContainer(DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3")) + .withExtension(MountableFile.forClasspathResource("/modifier-extension-wrong-name")) + .waitForExtension("Modifier Extension") + .withLogLevel(Level.DEBUG)) { + + hivemq.start(); + TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort()); + } + } +} diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithExtensionIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithExtensionIT.java new file mode 100755 index 00000000000..3ad7b26148a --- /dev/null +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithExtensionIT.java @@ -0,0 +1,41 @@ +package org.testcontainers.hivemq; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; +import org.testcontainers.hivemq.util.MyExtension; +import org.testcontainers.hivemq.util.TestPublishModifiedUtil; +import org.testcontainers.utility.DockerImageName; +import org.testcontainers.utility.MountableFile; + +import java.util.concurrent.TimeUnit; + +public class ContainerWithExtensionIT { + + @Test + @Timeout(value = 3, unit = TimeUnit.MINUTES) + void test() throws Exception { + final HiveMQExtension hiveMQExtension = HiveMQExtension.builder() + .id("extension-1") + .name("my-extension") + .version("1.0") + .mainClass(MyExtension.class).build(); + + try (final HiveMQContainer hivemq = + new HiveMQContainer(DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3")) + .withHiveMQConfig(MountableFile.forClasspathResource("/inMemoryConfig.xml")) + .waitForExtension(hiveMQExtension) + .withExtension(hiveMQExtension)) { + + hivemq.start(); + TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort()); + hivemq.stop(); + + hivemq.start(); + TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort()); + hivemq.stop(); + + hivemq.start(); + TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort()); + } + } +} diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithExtensionSubclassIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithExtensionSubclassIT.java new file mode 100755 index 00000000000..33880a800c9 --- /dev/null +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithExtensionSubclassIT.java @@ -0,0 +1,35 @@ +package org.testcontainers.hivemq; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; +import org.slf4j.event.Level; +import org.testcontainers.hivemq.util.MyExtensionWithSubclasses; +import org.testcontainers.hivemq.util.TestPublishModifiedUtil; +import org.testcontainers.utility.DockerImageName; +import org.testcontainers.utility.MountableFile; + +import java.util.concurrent.TimeUnit; + +public class ContainerWithExtensionSubclassIT { + + @Test + @Timeout(value = 3, unit = TimeUnit.MINUTES) + void test() throws Exception { + final HiveMQExtension hiveMQExtension = HiveMQExtension.builder() + .id("extension-1") + .name("my-extension") + .version("1.0") + .mainClass(MyExtensionWithSubclasses.class).build(); + + try (final HiveMQContainer hivemq = + new HiveMQContainer(DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3")) + .waitForExtension(hiveMQExtension) + .withExtension(hiveMQExtension) + .withHiveMQConfig(MountableFile.forClasspathResource("/inMemoryConfig.xml")) + .withLogLevel(Level.DEBUG)) { + + hivemq.start(); + TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort()); + } + } +} diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithFileInExtensionHomeIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithFileInExtensionHomeIT.java new file mode 100755 index 00000000000..1d2b1705eae --- /dev/null +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithFileInExtensionHomeIT.java @@ -0,0 +1,77 @@ +package org.testcontainers.hivemq; + +import com.hivemq.extension.sdk.api.ExtensionMain; +import com.hivemq.extension.sdk.api.interceptor.publish.PublishInboundInterceptor; +import com.hivemq.extension.sdk.api.parameter.ExtensionStartInput; +import com.hivemq.extension.sdk.api.parameter.ExtensionStartOutput; +import com.hivemq.extension.sdk.api.parameter.ExtensionStopInput; +import com.hivemq.extension.sdk.api.parameter.ExtensionStopOutput; +import com.hivemq.extension.sdk.api.services.Services; +import com.hivemq.extension.sdk.api.services.intializer.ClientInitializer; +import org.jetbrains.annotations.NotNull; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; +import org.testcontainers.hivemq.util.TestPublishModifiedUtil; +import org.testcontainers.utility.DockerImageName; +import org.testcontainers.utility.MountableFile; + +import java.io.File; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.concurrent.TimeUnit; + +public class ContainerWithFileInExtensionHomeIT { + + + @Test + @Timeout(value = 3, unit = TimeUnit.MINUTES) + void test() throws Exception { + final HiveMQExtension hiveMQExtension = HiveMQExtension.builder() + .id("extension-1") + .name("my-extension") + .version("1.0") + .mainClass(FileCheckerExtension.class).build(); + + try (final HiveMQContainer hivemq = + new HiveMQContainer(DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3")) + .withHiveMQConfig(MountableFile.forClasspathResource("/inMemoryConfig.xml")) + .withExtension(hiveMQExtension) + .waitForExtension(hiveMQExtension) + .withFileInExtensionHomeFolder( + MountableFile.forClasspathResource("/additionalFile.txt"), + "extension-1", + "/additionalFiles/my-file.txt")) { + + hivemq.start(); + TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort()); + } + } + + public static class FileCheckerExtension implements ExtensionMain { + + @Override + public void extensionStart(final @NotNull ExtensionStartInput extensionStartInput, final @NotNull ExtensionStartOutput extensionStartOutput) { + + final PublishInboundInterceptor publishInboundInterceptor = (publishInboundInput, publishInboundOutput) -> { + + final File extensionHomeFolder = extensionStartInput.getExtensionInformation().getExtensionHomeFolder(); + + final File additionalFile = new File(extensionHomeFolder, "additionalFiles/my-file.txt"); + + if (additionalFile.exists()) { + publishInboundOutput.getPublishPacket().setPayload(ByteBuffer.wrap("modified".getBytes(StandardCharsets.UTF_8))); + } + }; + + final ClientInitializer clientInitializer = (initializerInput, clientContext) -> clientContext.addPublishInboundInterceptor(publishInboundInterceptor); + + Services.initializerRegistry().setClientInitializer(clientInitializer); + } + + @Override + public void extensionStop(final @NotNull ExtensionStopInput extensionStopInput, final @NotNull ExtensionStopOutput extensionStopOutput) { + + } + } + +} diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithFileInHomeIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithFileInHomeIT.java new file mode 100755 index 00000000000..542c54789e8 --- /dev/null +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithFileInHomeIT.java @@ -0,0 +1,75 @@ +package org.testcontainers.hivemq; + +import com.hivemq.extension.sdk.api.ExtensionMain; +import com.hivemq.extension.sdk.api.interceptor.publish.PublishInboundInterceptor; +import com.hivemq.extension.sdk.api.parameter.ExtensionStartInput; +import com.hivemq.extension.sdk.api.parameter.ExtensionStartOutput; +import com.hivemq.extension.sdk.api.parameter.ExtensionStopInput; +import com.hivemq.extension.sdk.api.parameter.ExtensionStopOutput; +import com.hivemq.extension.sdk.api.services.Services; +import com.hivemq.extension.sdk.api.services.intializer.ClientInitializer; +import org.jetbrains.annotations.NotNull; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; +import org.testcontainers.hivemq.util.TestPublishModifiedUtil; +import org.testcontainers.utility.DockerImageName; +import org.testcontainers.utility.MountableFile; + +import java.io.File; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.concurrent.TimeUnit; + +public class ContainerWithFileInHomeIT { + + @Test + @Timeout(value = 3, unit = TimeUnit.MINUTES) + void test() throws Exception { + final HiveMQExtension hiveMQExtension = HiveMQExtension.builder() + .id("extension-1") + .name("my-extension") + .version("1.0") + .mainClass(FileCheckerExtension.class).build(); + + try (final HiveMQContainer hivemq = + new HiveMQContainer(DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3")) + .withHiveMQConfig(MountableFile.forClasspathResource("/inMemoryConfig.xml")) + .withExtension(hiveMQExtension) + .waitForExtension(hiveMQExtension) + .withFileInHomeFolder(MountableFile.forClasspathResource("/additionalFile.txt"), + "/additionalFiles/my-file.txt")) { + + + hivemq.start(); + TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort()); + } + } + + public static class FileCheckerExtension implements ExtensionMain { + + @Override + public void extensionStart(@NotNull ExtensionStartInput extensionStartInput, @NotNull ExtensionStartOutput extensionStartOutput) { + + final PublishInboundInterceptor publishInboundInterceptor = (publishInboundInput, publishInboundOutput) -> { + + final File homeFolder = extensionStartInput.getServerInformation().getHomeFolder(); + + final File additionalFile = new File(homeFolder, "additionalFiles/my-file.txt"); + + if (additionalFile.exists()) { + publishInboundOutput.getPublishPacket().setPayload(ByteBuffer.wrap("modified".getBytes(StandardCharsets.UTF_8))); + } + }; + + final ClientInitializer clientInitializer = (initializerInput, clientContext) -> clientContext.addPublishInboundInterceptor(publishInboundInterceptor); + + Services.initializerRegistry().setClientInitializer(clientInitializer); + } + + @Override + public void extensionStop(@NotNull ExtensionStopInput extensionStopInput, @NotNull ExtensionStopOutput extensionStopOutput) { + + } + } + +} diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithLicenseIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithLicenseIT.java new file mode 100755 index 00000000000..4fc27adf8c2 --- /dev/null +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithLicenseIT.java @@ -0,0 +1,78 @@ +package org.testcontainers.hivemq; + +import com.hivemq.extension.sdk.api.ExtensionMain; +import com.hivemq.extension.sdk.api.interceptor.publish.PublishInboundInterceptor; +import com.hivemq.extension.sdk.api.parameter.ExtensionStartInput; +import com.hivemq.extension.sdk.api.parameter.ExtensionStartOutput; +import com.hivemq.extension.sdk.api.parameter.ExtensionStopInput; +import com.hivemq.extension.sdk.api.parameter.ExtensionStopOutput; +import com.hivemq.extension.sdk.api.services.Services; +import com.hivemq.extension.sdk.api.services.intializer.ClientInitializer; +import org.jetbrains.annotations.NotNull; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; +import org.testcontainers.hivemq.util.TestPublishModifiedUtil; +import org.testcontainers.utility.DockerImageName; +import org.testcontainers.utility.MountableFile; + +import java.io.File; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.concurrent.TimeUnit; + +public class ContainerWithLicenseIT { + + @Test + @Timeout(value = 3, unit = TimeUnit.MINUTES) + void test() throws Exception { + final HiveMQExtension hiveMQExtension = HiveMQExtension.builder() + .id("extension-1") + .name("my-extension") + .version("1.0") + .mainClass(LicenceCheckerExtension.class).build(); + + try (final HiveMQContainer hivemq = + new HiveMQContainer(DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3")) + .withHiveMQConfig(MountableFile.forClasspathResource("/inMemoryConfig.xml")) + .withExtension(hiveMQExtension) + .waitForExtension(hiveMQExtension) + .withLicense(MountableFile.forClasspathResource("/myLicense.lic")) + .withLicense(MountableFile.forClasspathResource("/myExtensionLicense.elic"))) { + + + hivemq.start(); + TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort()); + } + } + + @SuppressWarnings("CodeBlock2Expr") + public static class LicenceCheckerExtension implements ExtensionMain { + + @Override + public void extensionStart(@NotNull ExtensionStartInput extensionStartInput, @NotNull ExtensionStartOutput extensionStartOutput) { + + final PublishInboundInterceptor publishInboundInterceptor = (publishInboundInput, publishInboundOutput) -> { + + final File homeFolder = extensionStartInput.getServerInformation().getHomeFolder(); + final File myLicence = new File(homeFolder, "license/myLicense.lic"); + final File myExtensionLicence = new File(homeFolder, "license/myExtensionLicense.elic"); + + if (myLicence.exists() && myExtensionLicence.exists()) { + publishInboundOutput.getPublishPacket().setPayload(ByteBuffer.wrap("modified".getBytes(StandardCharsets.UTF_8))); + } + }; + + final ClientInitializer clientInitializer = (initializerInput, clientContext) -> { + clientContext.addPublishInboundInterceptor(publishInboundInterceptor); + }; + + Services.initializerRegistry().setClientInitializer(clientInitializer); + } + + @Override + public void extensionStop(@NotNull ExtensionStopInput extensionStopInput, @NotNull ExtensionStopOutput extensionStopOutput) { + + } + } + +} diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithoutPlatformExtensionsIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithoutPlatformExtensionsIT.java new file mode 100644 index 00000000000..37b37dc0d59 --- /dev/null +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithoutPlatformExtensionsIT.java @@ -0,0 +1,135 @@ +package org.testcontainers.hivemq; + +import com.hivemq.client.mqtt.MqttClient; +import com.hivemq.client.mqtt.MqttGlobalPublishFilter; +import com.hivemq.client.mqtt.mqtt5.Mqtt5BlockingClient; +import com.hivemq.client.mqtt.mqtt5.message.publish.Mqtt5Publish; +import com.hivemq.extension.sdk.api.ExtensionMain; +import com.hivemq.extension.sdk.api.auth.SimpleAuthenticator; +import com.hivemq.extension.sdk.api.parameter.ExtensionStartInput; +import com.hivemq.extension.sdk.api.parameter.ExtensionStartOutput; +import com.hivemq.extension.sdk.api.parameter.ExtensionStopInput; +import com.hivemq.extension.sdk.api.parameter.ExtensionStopOutput; +import com.hivemq.extension.sdk.api.services.Services; +import com.hivemq.extension.sdk.api.services.builder.Builders; +import org.jetbrains.annotations.NotNull; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; +import org.testcontainers.utility.DockerImageName; + +import java.io.File; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class ContainerWithoutPlatformExtensionsIT { + + private final @NotNull HiveMQExtension hiveMQExtension = HiveMQExtension.builder() + .name("MyExtension") + .id("my-extension") + .version("1.0.0") + .mainClass(CheckerExtension.class).build(); + + + @Test + @Timeout(value = 3, unit = TimeUnit.MINUTES) + public void removeAllPlatformExtensions() throws InterruptedException { + + try (final HiveMQContainer hivemq = new HiveMQContainer(DockerImageName.parse("hivemq/hivemq4").withTag("4.7.4")) + .withExtension(hiveMQExtension) + .waitForExtension(hiveMQExtension) + .withoutPrepackagedExtensions()) { + + hivemq.start(); + + final Mqtt5BlockingClient client = MqttClient.builder() + .serverPort(hivemq.getMqttPort()) + .useMqttVersion5() + .buildBlocking(); + + client.connect(); + final Mqtt5BlockingClient.Mqtt5Publishes publishes = client.publishes(MqttGlobalPublishFilter.ALL); + client.subscribeWith().topicFilter("extensions").send(); + + final Mqtt5Publish receive = publishes.receive(); + assertTrue(receive.getPayload().isPresent()); + final String extensionInfo = new String(receive.getPayloadAsBytes()); + + assertFalse(extensionInfo.contains("hivemq-allow-all-extension")); + assertFalse(extensionInfo.contains("hivemq-kafka-extension")); + assertFalse(extensionInfo.contains("hivemq-bridge-extension")); + assertFalse(extensionInfo.contains("hivemq-enterprise-security-extension")); + + hivemq.start(); + } + } + + @Test + @Timeout(value = 3, unit = TimeUnit.MINUTES) + public void removeKafkaExtension() throws InterruptedException { + + try (final HiveMQContainer hivemq = new HiveMQContainer(DockerImageName.parse("hivemq/hivemq4").withTag("4.7.4")) + .withExtension(hiveMQExtension) + .waitForExtension(hiveMQExtension) + .withoutPrepackagedExtensions("hivemq-kafka-extension")) { + + hivemq.start(); + + final Mqtt5BlockingClient client = MqttClient.builder() + .serverPort(hivemq.getMqttPort()) + .useMqttVersion5() + .buildBlocking(); + + client.connect(); + final Mqtt5BlockingClient.Mqtt5Publishes publishes = client.publishes(MqttGlobalPublishFilter.ALL); + client.subscribeWith().topicFilter("extensions").send(); + + final Mqtt5Publish receive = publishes.receive(); + assertTrue(receive.getPayload().isPresent()); + final String extensionInfo = new String(receive.getPayloadAsBytes()); + + assertTrue(extensionInfo.contains("hivemq-allow-all-extension")); + assertFalse(extensionInfo.contains("hivemq-kafka-extension")); + assertTrue(extensionInfo.contains("hivemq-bridge-extension")); + assertTrue(extensionInfo.contains("hivemq-enterprise-security-extension")); + } + } + + public static class CheckerExtension implements ExtensionMain { + + @Override + public void extensionStart( + final @NotNull ExtensionStartInput extensionStartInput, + final @NotNull ExtensionStartOutput extensionStartOutput) { + + final String extensionFolders = Arrays.stream(extensionStartInput.getServerInformation().getExtensionsFolder().listFiles()) + .filter(File::isDirectory) + .map(File::getName) + .collect(Collectors.joining("\n")); + + final byte[] bytes = extensionFolders.getBytes(StandardCharsets.UTF_8); + Services.publishService().publish(Builders.publish() + .topic("extensions") + .retain(true) + .payload(ByteBuffer.wrap(bytes)) + .build()); + + Services.securityRegistry().setAuthenticatorProvider(authenticatorProviderInput -> + (SimpleAuthenticator) (simpleAuthInput, simpleAuthOutput) -> + simpleAuthOutput.authenticateSuccessfully()); + + } + + @Override + public void extensionStop( + final @NotNull ExtensionStopInput extensionStopInput, + final @NotNull ExtensionStopOutput extensionStopOutput) { + + } + } +} diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/CreateFileInCopiedDirectoryIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/CreateFileInCopiedDirectoryIT.java new file mode 100755 index 00000000000..85089374624 --- /dev/null +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/CreateFileInCopiedDirectoryIT.java @@ -0,0 +1,92 @@ +package org.testcontainers.hivemq; + +import com.hivemq.extension.sdk.api.ExtensionMain; +import com.hivemq.extension.sdk.api.interceptor.publish.PublishInboundInterceptor; +import com.hivemq.extension.sdk.api.parameter.ExtensionStartInput; +import com.hivemq.extension.sdk.api.parameter.ExtensionStartOutput; +import com.hivemq.extension.sdk.api.parameter.ExtensionStopInput; +import com.hivemq.extension.sdk.api.parameter.ExtensionStopOutput; +import com.hivemq.extension.sdk.api.services.Services; +import com.hivemq.extension.sdk.api.services.intializer.ClientInitializer; +import org.jetbrains.annotations.NotNull; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; +import org.testcontainers.hivemq.util.TestPublishModifiedUtil; +import org.testcontainers.utility.DockerImageName; +import org.testcontainers.utility.MountableFile; + +import java.io.File; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.util.concurrent.TimeUnit; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class CreateFileInCopiedDirectoryIT { + + private @NotNull MountableFile createDirectory() throws IOException { + final File directory = new File(Files.createTempDirectory("").toFile(), "directory"); + assertTrue(directory.mkdir()); + final File subdirectory = new File(directory, "sub-directory"); + assertTrue(subdirectory.mkdir()); + return MountableFile.forHostPath(directory.getPath()); + } + + @Test + @Timeout(value = 3, unit = TimeUnit.MINUTES) + void test() throws Exception { + final HiveMQExtension extension = HiveMQExtension.builder() + .id("extension-1") + .name("my-extension") + .version("1.0") + .mainClass(FileCreatorExtension.class).build(); + + try (final HiveMQContainer hivemq = + new HiveMQContainer(DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3")) + .withHiveMQConfig(MountableFile.forClasspathResource("/inMemoryConfig.xml")) + .withExtension(extension) + .waitForExtension(extension) + .withFileInHomeFolder(createDirectory(), "directory")) { + + hivemq.start(); + TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort()); + } + } + + public static class FileCreatorExtension implements ExtensionMain { + + @Override + public void extensionStart(@NotNull ExtensionStartInput extensionStartInput, @NotNull ExtensionStartOutput extensionStartOutput) { + + final PublishInboundInterceptor publishInboundInterceptor = (publishInboundInput, publishInboundOutput) -> { + + final File homeFolder = extensionStartInput.getServerInformation().getHomeFolder(); + + final File dir = new File(homeFolder, "directory"); + final File dirFile = new File(dir, "file.txt"); + final File subDir = new File(dir, "sub-directory"); + final File subDirFile = new File(subDir, "file.txt"); + + try { + if (dirFile.createNewFile() && subDirFile.createNewFile()) { + publishInboundOutput.getPublishPacket().setPayload(ByteBuffer.wrap("modified".getBytes(StandardCharsets.UTF_8))); + } + } catch (IOException e) { + e.printStackTrace(); + } + }; + + final ClientInitializer clientInitializer = (initializerInput, clientContext) -> clientContext.addPublishInboundInterceptor(publishInboundInterceptor); + + Services.initializerRegistry().setClientInitializer(clientInitializer); + } + + @Override + public void extensionStop(@NotNull ExtensionStopInput extensionStopInput, @NotNull ExtensionStopOutput extensionStopOutput) { + + } + } + +} diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/CreateFileInExtensionDirectoryIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/CreateFileInExtensionDirectoryIT.java new file mode 100755 index 00000000000..a7756c69f1d --- /dev/null +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/CreateFileInExtensionDirectoryIT.java @@ -0,0 +1,76 @@ +package org.testcontainers.hivemq; + +import com.hivemq.extension.sdk.api.ExtensionMain; +import com.hivemq.extension.sdk.api.interceptor.publish.PublishInboundInterceptor; +import com.hivemq.extension.sdk.api.parameter.ExtensionStartInput; +import com.hivemq.extension.sdk.api.parameter.ExtensionStartOutput; +import com.hivemq.extension.sdk.api.parameter.ExtensionStopInput; +import com.hivemq.extension.sdk.api.parameter.ExtensionStopOutput; +import com.hivemq.extension.sdk.api.services.Services; +import com.hivemq.extension.sdk.api.services.intializer.ClientInitializer; +import org.jetbrains.annotations.NotNull; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; +import org.testcontainers.hivemq.util.TestPublishModifiedUtil; +import org.testcontainers.utility.DockerImageName; +import org.testcontainers.utility.MountableFile; + +import java.io.File; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.concurrent.TimeUnit; + +public class CreateFileInExtensionDirectoryIT { + + @Test + @Timeout(value = 3, unit = TimeUnit.MINUTES) + void test() throws Exception { + final HiveMQExtension hiveMQExtension = HiveMQExtension.builder() + .id("extension-1") + .name("my-extension") + .version("1.0") + .mainClass(FileCreatorExtension.class).build(); + + try (final HiveMQContainer hivemq = + new HiveMQContainer(DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3")) + .withHiveMQConfig(MountableFile.forClasspathResource("/inMemoryConfig.xml")) + .waitForExtension(hiveMQExtension) + .withExtension(hiveMQExtension)) { + + hivemq.start(); + TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort()); + } + } + + public static class FileCreatorExtension implements ExtensionMain { + + @Override + public void extensionStart(@NotNull ExtensionStartInput extensionStartInput, @NotNull ExtensionStartOutput extensionStartOutput) { + + final PublishInboundInterceptor publishInboundInterceptor = (publishInboundInput, publishInboundOutput) -> { + + final File extensionHomeFolder = extensionStartInput.getExtensionInformation().getExtensionHomeFolder(); + + final File file = new File(extensionHomeFolder, "myfile.txt"); + try { + if (file.createNewFile()) { + publishInboundOutput.getPublishPacket().setPayload(ByteBuffer.wrap("modified".getBytes(StandardCharsets.UTF_8))); + } + } catch (IOException e) { + e.printStackTrace(); + } + }; + + final ClientInitializer clientInitializer = (initializerInput, clientContext) -> clientContext.addPublishInboundInterceptor(publishInboundInterceptor); + + Services.initializerRegistry().setClientInitializer(clientInitializer); + } + + @Override + public void extensionStop(@NotNull ExtensionStopInput extensionStopInput, @NotNull ExtensionStopOutput extensionStopOutput) { + + } + } + +} diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/DisableEnableExtensionFromDirectoryIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/DisableEnableExtensionFromDirectoryIT.java new file mode 100644 index 00000000000..2248681a769 --- /dev/null +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/DisableEnableExtensionFromDirectoryIT.java @@ -0,0 +1,37 @@ +package org.testcontainers.hivemq; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; +import org.slf4j.event.Level; +import org.testcontainers.hivemq.util.TestPublishModifiedUtil; +import org.testcontainers.utility.DockerImageName; +import org.testcontainers.utility.MountableFile; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; + +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class DisableEnableExtensionFromDirectoryIT { + + @Test + @Timeout(value = 3, unit = TimeUnit.MINUTES) + void test() throws Exception { + try (final HiveMQContainer hivemq = + new HiveMQContainer(DockerImageName.parse("hivemq/hivemq4").withTag("4.7.4")) + .withExtension(MountableFile.forClasspathResource("/modifier-extension")) + .waitForExtension("Modifier Extension") + .withLogLevel(Level.DEBUG)) { + + hivemq.start(); + + + TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort()); + hivemq.disableExtension("Modifier Extension", "modifier-extension"); + assertThrows(ExecutionException.class, () -> TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort())); + hivemq.enableExtension("Modifier Extension", "modifier-extension"); + TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort()); + } + } + +} diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/DisableEnableExtensionIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/DisableEnableExtensionIT.java new file mode 100644 index 00000000000..7cc943c5527 --- /dev/null +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/DisableEnableExtensionIT.java @@ -0,0 +1,46 @@ +package org.testcontainers.hivemq; + +import org.jetbrains.annotations.NotNull; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; +import org.slf4j.event.Level; +import org.testcontainers.hivemq.util.MyExtension; +import org.testcontainers.hivemq.util.TestPublishModifiedUtil; +import org.testcontainers.utility.DockerImageName; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; + +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class DisableEnableExtensionIT { + + private final @NotNull HiveMQExtension hiveMQExtension = HiveMQExtension.builder() + .id("extension-1") + .name("my-extension") + .version("1.0") + .disabledOnStartup(true) + .mainClass(MyExtension.class).build(); + + @Test + @Timeout(value = 3, unit = TimeUnit.MINUTES) + void test() throws Exception { + try (final HiveMQContainer hivemq = + new HiveMQContainer(DockerImageName.parse("hivemq/hivemq4").withTag("4.7.4")) + .withExtension(hiveMQExtension) + .withLogLevel(Level.DEBUG)) { + + + hivemq.start(); + + assertThrows(ExecutionException.class, () -> TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort())); + hivemq.enableExtension(hiveMQExtension); + TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort()); + hivemq.disableExtension(hiveMQExtension); + assertThrows(ExecutionException.class, () -> TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort())); + hivemq.enableExtension(hiveMQExtension); + TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort()); + } + } + +} diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/HiveMQExtensionTest.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/HiveMQExtensionTest.java new file mode 100644 index 00000000000..d614a59bd8c --- /dev/null +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/HiveMQExtensionTest.java @@ -0,0 +1,13 @@ +package org.testcontainers.hivemq; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class HiveMQExtensionTest { + + @Test + void builder_classDoesNotImplementExtensionMain_exception() { + assertThrows(IllegalArgumentException.class, () -> HiveMQExtension.builder().mainClass(HiveMQExtensionTest.class)); + } +} diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/HiveMQTestContainerCore.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/HiveMQTestContainerCore.java new file mode 100644 index 00000000000..40820e33ded --- /dev/null +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/HiveMQTestContainerCore.java @@ -0,0 +1,82 @@ +package org.testcontainers.hivemq; + +import org.jetbrains.annotations.NotNull; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import org.testcontainers.containers.ContainerLaunchException; +import org.testcontainers.utility.DockerImageName; +import org.testcontainers.utility.MountableFile; + +import java.io.File; +import java.io.IOException; + +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class HiveMQTestContainerCore { + + final @NotNull HiveMQContainer container = new HiveMQContainer(DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3")); + + @TempDir + File tempDir; + + @Test + void withExtension_fileDoesNotExist_Exception() { + final MountableFile mountableFile = MountableFile.forHostPath("/this/does/not/exist"); + assertThrows(ContainerLaunchException.class, () -> container.withExtension(mountableFile)); + } + + @Test + void withExtension_fileNoDirectory_Exception() throws IOException { + final File extension = new File(tempDir, "extension"); + assertTrue(extension.createNewFile()); + final MountableFile mountableFile = MountableFile.forHostPath(extension.getAbsolutePath()); + assertThrows(ContainerLaunchException.class, () -> container.withExtension(mountableFile)); + } + + @Test + void withLicense_fileDoesNotExist_Exception() { + final MountableFile mountableFile = MountableFile.forHostPath("/this/does/not/exist"); + assertThrows(ContainerLaunchException.class, () -> container.withLicense(mountableFile)); + } + + @Test + void withExtension_fileEndingWrong_Exception() throws IOException { + final File extension = new File(tempDir, "extension.wrong"); + assertTrue(extension.createNewFile()); + final MountableFile mountableFile = MountableFile.forHostPath(extension.getAbsolutePath()); + assertThrows(ContainerLaunchException.class, () -> container.withLicense(mountableFile)); + } + + @Test + void withHiveMQConfig_fileDoesNotExist_Exception() { + final MountableFile mountableFile = MountableFile.forHostPath("/this/does/not/exist"); + assertThrows(ContainerLaunchException.class, () -> container.withHiveMQConfig(mountableFile)); + } + + @Test + void withFileInHomeFolder_fileDoesNotExist_Exception() { + final MountableFile mountableFile = MountableFile.forHostPath("/this/does/not/exist"); + assertThrows(ContainerLaunchException.class, () -> container.withFileInHomeFolder(mountableFile, "some/path")); + } + + @Test + void withFileInHomeFolder_pathEmpty_Exception() { + final MountableFile mountableFile = MountableFile.forHostPath("/this/does/not/exist"); + assertThrows(ContainerLaunchException.class, () -> container.withFileInHomeFolder(mountableFile, "")); + } + + @Test + void withFileInExtensionHomeFolder_withPath_fileDoesNotExist_Exception() { + final MountableFile mountableFile = MountableFile.forHostPath("/this/does/not/exist"); + assertThrows(ContainerLaunchException.class, () -> container.withFileInExtensionHomeFolder(mountableFile, "my-extension", "some/path")); + } + + @Test + void withFileInExtensionHomeFolder_fileDoesNotExist_Exception() { + final MountableFile mountableFile = MountableFile.forHostPath("/this/does/not/exist"); + assertThrows(ContainerLaunchException.class, () -> { + final HiveMQContainer hiveMQContainer = container.withFileInExtensionHomeFolder(mountableFile, "my-extension"); + }); + } +} diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/PathUtilTest.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/PathUtilTest.java new file mode 100755 index 00000000000..f1e060e68d9 --- /dev/null +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/PathUtilTest.java @@ -0,0 +1,43 @@ +package org.testcontainers.hivemq; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class PathUtilTest { + + @Test + void prepareInnerPath_emptyString() { + assertEquals("/", PathUtil.prepareInnerPath("")); + } + + @Test + void prepareInnerPath_onlyDelimiter() { + assertEquals("/", PathUtil.prepareInnerPath("/")); + } + + @Test + void prepareInnerPath_noDelimiter() { + assertEquals("/path/", PathUtil.prepareInnerPath("path")); + } + + @Test + void prepareInnerPath_delimiterAtEnd() { + assertEquals("/path/", PathUtil.prepareInnerPath("path/")); + } + + @Test + void prepareInnerPath_delimiterAtBeginning() { + assertEquals("/path/", PathUtil.prepareInnerPath("/path")); + } + + @Test + void prepareAppendPath_delimiterAtBeginning_noChange() { + assertEquals("/path", PathUtil.prepareAppendPath("/path")); + } + + @Test + void prepareAppendPath_delimiterAtBeginning_delimiterAdded() { + assertEquals("/path", PathUtil.prepareAppendPath("path")); + } +} diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoDisableExtensionsIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoDisableExtensionsIT.java new file mode 100644 index 00000000000..e9d9463653e --- /dev/null +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoDisableExtensionsIT.java @@ -0,0 +1,63 @@ +package org.testcontainers.hivemq.docs; + +import org.junit.jupiter.api.Test; +import org.testcontainers.hivemq.HiveMQContainer; +import org.testcontainers.hivemq.HiveMQExtension; +import org.testcontainers.hivemq.util.MyExtension; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; +import org.testcontainers.utility.DockerImageName; +import org.testcontainers.utility.MountableFile; + +@Testcontainers +public class DemoDisableExtensionsIT { + + // noExtensions { + @Container + final HiveMQContainer hivemqNoExtensions = new HiveMQContainer(DockerImageName.parse("hivemq/hivemq4").withTag("4.7.4")) + .withoutPrepackagedExtensions(); + // } + + // noKafkaExtension { + @Container + final HiveMQContainer hivemqNoKafkaExtension = new HiveMQContainer(DockerImageName.parse("hivemq/hivemq4").withTag("4.7.4")) + .withoutPrepackagedExtensions("hivemq-kafka-extension"); + // } + + // startDisabled { + private final HiveMQExtension hiveMQExtension = HiveMQExtension.builder() + .id("extension-1") + .name("my-extension") + .version("1.0") + .disabledOnStartup(true) + .mainClass(MyExtension.class).build(); + + @Container + final HiveMQContainer hivemq = new HiveMQContainer(DockerImageName.parse("hivemq/hivemq4").withTag("4.7.4")) + .withExtension(hiveMQExtension); + // } + + + // startFromFilesystem { + @Container + final HiveMQContainer hivemqExtensionFromFilesystem = new HiveMQContainer(DockerImageName.parse("hivemq/hivemq4").withTag("4.7.4")) + .withExtension(MountableFile.forHostPath("src/test/resources/modifier-extension")); + // } + + + // runtimeEnable { + @Test + void test_disable_enable_extension() throws Exception { + hivemq.enableExtension(hiveMQExtension); + hivemq.disableExtension(hiveMQExtension); + } + // } + + // runtimeEnableFilesystem { + @Test + void test_disable_enable_extension_from_filesystem() throws Exception { + hivemqExtensionFromFilesystem.disableExtension("Modifier Extension", "modifier-extension"); + hivemqExtensionFromFilesystem.enableExtension("Modifier Extension", "modifier-extension"); + } + // } +} diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoExtensionTestsIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoExtensionTestsIT.java new file mode 100644 index 00000000000..2627dca2028 --- /dev/null +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoExtensionTestsIT.java @@ -0,0 +1,57 @@ +package org.testcontainers.hivemq.docs; + +import com.hivemq.client.mqtt.mqtt5.Mqtt5BlockingClient; +import com.hivemq.client.mqtt.mqtt5.Mqtt5Client; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; +import org.testcontainers.hivemq.HiveMQContainer; +import org.testcontainers.hivemq.HiveMQExtension; +import org.testcontainers.hivemq.util.MyExtensionWithSubclasses; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; +import org.testcontainers.utility.DockerImageName; +import org.testcontainers.utility.MountableFile; + +import java.util.concurrent.TimeUnit; + +@Testcontainers +public class DemoExtensionTestsIT { + + // waitStrategy { + @Container + final HiveMQContainer hivemqWithWaitStrategy = + new HiveMQContainer(DockerImageName.parse("hivemq/hivemq4").withTag("4.7.4")) + .withExtension(MountableFile.forClasspathResource("/modifier-extension")) + .waitForExtension("Modifier Extension"); + // } + + // extensionClasspath { + final HiveMQExtension hiveMQEClasspathxtension = HiveMQExtension.builder() + .id("extension-1") + .name("my-extension") + .version("1.0") + .mainClass(MyExtensionWithSubclasses.class).build(); + + @Container + final HiveMQContainer hivemqWithClasspathExtension = + new HiveMQContainer(DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3")) + .waitForExtension(hiveMQEClasspathxtension) + .withExtension(hiveMQEClasspathxtension) + .withHiveMQConfig(MountableFile.forClasspathResource("/inMemoryConfig.xml")); + // } + + @Test + @Timeout(value = 3, unit = TimeUnit.MINUTES) + public void test() throws Exception { + + // mqtt5client { + final Mqtt5BlockingClient client = Mqtt5Client.builder() + .serverPort(hivemqWithClasspathExtension.getMqttPort()) + .buildBlocking(); + + client.connect(); + client.disconnect(); + // } + + } +} diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoFilesIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoFilesIT.java new file mode 100644 index 00000000000..46c24e8e51d --- /dev/null +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoFilesIT.java @@ -0,0 +1,62 @@ +package org.testcontainers.hivemq.docs; + +import com.hivemq.client.mqtt.mqtt5.Mqtt5BlockingClient; +import com.hivemq.client.mqtt.mqtt5.Mqtt5Client; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; +import org.testcontainers.hivemq.HiveMQContainer; +import org.testcontainers.hivemq.HiveMQExtension; +import org.testcontainers.hivemq.util.MyExtension; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; +import org.testcontainers.utility.DockerImageName; +import org.testcontainers.utility.MountableFile; + +import java.util.concurrent.TimeUnit; + +@Testcontainers +public class DemoFilesIT { + + // hivemqHome { + final HiveMQContainer hivemqFileInHome = new HiveMQContainer(DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3")) + .withFileInHomeFolder( + MountableFile.forHostPath("src/test/resources/additionalFile.txt"), + "/path/in/home/folder"); + // } + + // extensionHome { + @Container + final HiveMQContainer hivemqFileInExtensionHome = new HiveMQContainer(DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3")) + .withExtension(HiveMQExtension.builder() + .id("extension-1") + .name("my-extension") + .version("1.0") + .mainClass(MyExtension.class).build()) + .withFileInExtensionHomeFolder( + MountableFile.forHostPath("src/test/resources/additionalFile.txt"), + "extension-1", + "/path/in/extension/home"); + // } + + // withLicenses { + @Container + final HiveMQContainer hivemq = new HiveMQContainer(DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3")) + .withLicense(MountableFile.forHostPath("src/test/resources/myLicense.lic")) + .withLicense(MountableFile.forHostPath("src/test/resources/myExtensionLicense.elic")); + // } + + @Test + @Timeout(value = 3, unit = TimeUnit.MINUTES) + public void test() throws Exception { + + // mqtt5client { + final Mqtt5BlockingClient client = Mqtt5Client.builder() + .serverPort(hivemq.getMqttPort()) + .buildBlocking(); + + client.connect(); + client.disconnect(); + // } + + } +} diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoHiveMQContainerIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoHiveMQContainerIT.java new file mode 100644 index 00000000000..60683c8d612 --- /dev/null +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoHiveMQContainerIT.java @@ -0,0 +1,61 @@ +package org.testcontainers.hivemq.docs; + +import com.hivemq.client.mqtt.mqtt5.Mqtt5BlockingClient; +import com.hivemq.client.mqtt.mqtt5.Mqtt5Client; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; +import org.slf4j.event.Level; +import org.testcontainers.hivemq.HiveMQContainer; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; +import org.testcontainers.utility.DockerImageName; +import org.testcontainers.utility.MountableFile; + +import java.util.concurrent.TimeUnit; + +@Testcontainers +public class DemoHiveMQContainerIT { + + // ceVersion { + @Container + final HiveMQContainer hivemqCe = + new HiveMQContainer(DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3")) + .withLogLevel(Level.DEBUG); + // } + + // eeVersion { + @Container + final HiveMQContainer hivemqEe = + new HiveMQContainer(DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3")) + .withLogLevel(Level.DEBUG); + // } + + // eeVersionWithControlCenter { + @Container + final HiveMQContainer hivemqEeWithControLCenter = + new HiveMQContainer(DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3")) + .withLogLevel(Level.DEBUG) + .withHiveMQConfig(MountableFile.forClasspathResource("/inMemoryConfig.xml")) + .withControlCenter(); + // } + + // specificVersion { + @Container + final HiveMQContainer hivemqSpecificVersion = new HiveMQContainer(DockerImageName.parse("hivemq/hivemq-ce:2021.3")); + // } + + @Test + @Timeout(value = 3, unit = TimeUnit.MINUTES) + public void test() throws Exception { + + // mqtt5client { + final Mqtt5BlockingClient client = Mqtt5Client.builder() + .serverPort(hivemqCe.getMqttPort()) + .buildBlocking(); + + client.connect(); + client.disconnect(); + // } + + } +} diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/util/MyExtension.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/util/MyExtension.java new file mode 100755 index 00000000000..e707859be77 --- /dev/null +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/util/MyExtension.java @@ -0,0 +1,37 @@ +package org.testcontainers.hivemq.util; + +import com.hivemq.extension.sdk.api.ExtensionMain; +import org.jetbrains.annotations.NotNull; +import com.hivemq.extension.sdk.api.interceptor.publish.PublishInboundInterceptor; +import com.hivemq.extension.sdk.api.parameter.ExtensionStartInput; +import com.hivemq.extension.sdk.api.parameter.ExtensionStartOutput; +import com.hivemq.extension.sdk.api.parameter.ExtensionStopInput; +import com.hivemq.extension.sdk.api.parameter.ExtensionStopOutput; +import com.hivemq.extension.sdk.api.services.Services; +import com.hivemq.extension.sdk.api.services.intializer.ClientInitializer; + +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; + +@SuppressWarnings("CodeBlock2Expr") +public class MyExtension implements ExtensionMain { + + @Override + public void extensionStart(final @NotNull ExtensionStartInput extensionStartInput, final @NotNull ExtensionStartOutput extensionStartOutput) { + + final PublishInboundInterceptor publishInboundInterceptor = (publishInboundInput, publishInboundOutput) -> { + publishInboundOutput.getPublishPacket().setPayload(ByteBuffer.wrap("modified".getBytes(StandardCharsets.UTF_8))); + }; + + final ClientInitializer clientInitializer = (initializerInput, clientContext) -> { + clientContext.addPublishInboundInterceptor(publishInboundInterceptor); + }; + + Services.initializerRegistry().setClientInitializer(clientInitializer); + } + + @Override + public void extensionStop(final @NotNull ExtensionStopInput extensionStopInput, final @NotNull ExtensionStopOutput extensionStopOutput) { + + } +} diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/util/MyExtensionWithSubclasses.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/util/MyExtensionWithSubclasses.java new file mode 100755 index 00000000000..abc24585358 --- /dev/null +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/util/MyExtensionWithSubclasses.java @@ -0,0 +1,26 @@ +package org.testcontainers.hivemq.util; + +import com.hivemq.extension.sdk.api.ExtensionMain; +import org.jetbrains.annotations.NotNull; +import com.hivemq.extension.sdk.api.parameter.ExtensionStartInput; +import com.hivemq.extension.sdk.api.parameter.ExtensionStartOutput; +import com.hivemq.extension.sdk.api.parameter.ExtensionStopInput; +import com.hivemq.extension.sdk.api.parameter.ExtensionStopOutput; +import com.hivemq.extension.sdk.api.services.Services; +import com.hivemq.extension.sdk.api.services.intializer.ClientInitializer; + +public class MyExtensionWithSubclasses implements ExtensionMain { + + @Override + public void extensionStart(final @NotNull ExtensionStartInput extensionStartInput, final @NotNull ExtensionStartOutput extensionStartOutput) { + + final ClientInitializer clientInitializer = (initializerInput, clientContext) -> clientContext.addPublishInboundInterceptor(new PublishModifier()); + + Services.initializerRegistry().setClientInitializer(clientInitializer); + } + + @Override + public void extensionStop(final @NotNull ExtensionStopInput extensionStopInput, final @NotNull ExtensionStopOutput extensionStopOutput) { + + } +} diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/util/PublishModifier.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/util/PublishModifier.java new file mode 100755 index 00000000000..d33f29a9c10 --- /dev/null +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/util/PublishModifier.java @@ -0,0 +1,16 @@ +package org.testcontainers.hivemq.util; + +import org.jetbrains.annotations.NotNull; +import com.hivemq.extension.sdk.api.interceptor.publish.PublishInboundInterceptor; +import com.hivemq.extension.sdk.api.interceptor.publish.parameter.PublishInboundInput; +import com.hivemq.extension.sdk.api.interceptor.publish.parameter.PublishInboundOutput; + +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; + +public class PublishModifier implements PublishInboundInterceptor { + @Override + public void onInboundPublish(final @NotNull PublishInboundInput publishInboundInput, final @NotNull PublishInboundOutput publishInboundOutput) { + publishInboundOutput.getPublishPacket().setPayload(ByteBuffer.wrap("modified".getBytes(StandardCharsets.UTF_8))); + } +} diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/util/TestPublishModifiedUtil.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/util/TestPublishModifiedUtil.java new file mode 100755 index 00000000000..b2712ef5a1d --- /dev/null +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/util/TestPublishModifiedUtil.java @@ -0,0 +1,48 @@ +package org.testcontainers.hivemq.util; + +import com.hivemq.client.mqtt.MqttGlobalPublishFilter; +import com.hivemq.client.mqtt.mqtt5.Mqtt5BlockingClient; +import com.hivemq.client.mqtt.mqtt5.Mqtt5Client; + +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; + +public class TestPublishModifiedUtil { + + public static void testPublishModified(final int mqttPort) throws InterruptedException, ExecutionException { + final CompletableFuture publishReceived = new CompletableFuture<>(); + + final Mqtt5BlockingClient publisher = Mqtt5Client.builder() + .serverPort(mqttPort) + .identifier("publisher") + .buildBlocking(); + publisher.connect(); + + final Mqtt5BlockingClient subscriber = Mqtt5Client.builder() + .serverPort(mqttPort) + .identifier("subscriber") + .buildBlocking(); + subscriber.connect(); + subscriber.subscribeWith().topicFilter("test/topic").send(); + subscriber.toAsync().publishes(MqttGlobalPublishFilter.ALL, publish -> { + if (Arrays.equals(publish.getPayloadAsBytes(), "modified".getBytes(StandardCharsets.UTF_8))) { + publishReceived.complete(null); + } else { + publishReceived.completeExceptionally(new IllegalArgumentException("unexpected payload: " + new String(publish.getPayloadAsBytes()))); + } + }); + + publisher.publishWith().topic("test/topic").payload("unmodified".getBytes(StandardCharsets.UTF_8)).send(); + + try { + publishReceived.get(); + } finally { + publisher.disconnect(); + subscriber.disconnect(); + } + } + + +} diff --git a/modules/hivemq/src/test/resources/additionalFile.txt b/modules/hivemq/src/test/resources/additionalFile.txt new file mode 100755 index 00000000000..e69de29bb2d diff --git a/modules/hivemq/src/test/resources/config.xml b/modules/hivemq/src/test/resources/config.xml new file mode 100755 index 00000000000..128015e2fee --- /dev/null +++ b/modules/hivemq/src/test/resources/config.xml @@ -0,0 +1,15 @@ + + + + + + 1883 + 0.0.0.0 + + + + + 0 + + + diff --git a/modules/hivemq/src/test/resources/inMemoryConfig.xml b/modules/hivemq/src/test/resources/inMemoryConfig.xml new file mode 100644 index 00000000000..33373359243 --- /dev/null +++ b/modules/hivemq/src/test/resources/inMemoryConfig.xml @@ -0,0 +1,6 @@ + + + + in-memory + + diff --git a/modules/hivemq/src/test/resources/logback-test.xml b/modules/hivemq/src/test/resources/logback-test.xml new file mode 100644 index 00000000000..8a33fc398f0 --- /dev/null +++ b/modules/hivemq/src/test/resources/logback-test.xml @@ -0,0 +1,18 @@ + + + + + %d{HH:mm:ss.SSS} %-5level %logger - %msg%n + + + + + + + + + + + + + diff --git a/modules/hivemq/src/test/resources/modifier-extension-wrong-name/hivemq-extension.xml b/modules/hivemq/src/test/resources/modifier-extension-wrong-name/hivemq-extension.xml new file mode 100755 index 00000000000..6ca9c899df4 --- /dev/null +++ b/modules/hivemq/src/test/resources/modifier-extension-wrong-name/hivemq-extension.xml @@ -0,0 +1,8 @@ + + + modifier-extension + 1.0-SNAPSHOT + Modifier Extension + HiveMQ GmbH + 1000 + diff --git a/modules/hivemq/src/test/resources/modifier-extension-wrong-name/modifier-extension-1.0-SNAPSHOT.jar b/modules/hivemq/src/test/resources/modifier-extension-wrong-name/modifier-extension-1.0-SNAPSHOT.jar new file mode 100644 index 0000000000000000000000000000000000000000..18a7ea85f53dcf856d5280676386448b43d2d537 GIT binary patch literal 102179 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
= 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 z zJBI)~>!^Hzn|fb+!&&T#z&MGxG($b+m>VW*?v=Lj#kh*&`qW@o@mwBR71o=wT^n8P zZ${lOEi3C*m~Vy;;?CLAUx*LlC%Ol5x~Y0LK9+vcrWk2D_qBBWhAW4RulTWVNbB&P zPh>3~+Sj9H<$?1Mi#sdDSLxrk+9n}7P;K+&%&3^4^;g9>BCC|!j2qcxvYv_$ za@rcXb(U*r^cTIc|2(5Hz-W!E#?gcS9yDHvuTgih51G20kkOxbT2)cwQI||-k)zhj z^7DcDcY3De&a>^kIrASsH|xt~>o5DqWNvEC>&rc^uz3FSH#WwtIU$d<4~{)9IXx7q zX=k;6MYEWAXV9AsRl^-aEbOmuR2cJbgrw~aChSzWs+FnHj;_3AD&k=qw)w57exVUu7So&6 z_i6r?Blm0hQbRjWyD97KPGoO6@NmJ(qbuhoIi#{z6n#(~4S%%DepstF-$v_D-y`w< z1zhDLYGJ~}*@w5U(oeYiVN;9l#^;qg@+*4s>O$YmFRh&=_~7f8{*&dUX0@BX@XU0w z>DDb$-COsiAiXxE+huQedxpW?eP5UJ+@D2btY2iL5@@l%&T{GPHQikk_gA+Lo$T+rUYR;N>jJ-c-emFo^*-TD z=|LP@mbZRfSi57B6kbKCGaj=toY23#u6WlYomy$tqc-&iEQgr-^7jOOEI)8x(oNf^ zcGh0Wyi}!+OIy{BS8jIf^_X>EljB0HK(2I(wWoE@*t?vBH1H{j5kcLHn6 zwhq^`rxOxt6r3WHpLeD%-FTLF`OQaM7WV5zS5?2>N93zD5#?OtA;a;|Ahp7GS-sEf z)1@)V-8WlrVwc3>YSvHY3=8ysA?)wd@a$<7Q7VYKL@;`1byuoWS76*Kk`+#PSc-|j zcN)z(sMx5G7iHZO)#PZIar}!Q2+@UD?kij1A;3Sk}75_ccm>^f8Wb zit1zb6&sa1FZz*1o9kgob{c!nt>c7ceT~JLm0UQBG`4}z#8mbWyOggU@@vDTw#^Aj zi9V6hD`FjTxaD8xECC#k)-wJc#m4zdOq-%FTJb(;N_#24!BkBv&AL_FMMeC?bcpT%c$-%n=So4q6kV!niaY|R$4ztQd7Z|3L!w3BHLSl?pJCr4B zUcJ8;Dxn#6eE#-&!Qo=Jd!Zsz6B=b@)=Y=-nC9 z(26a58u3D1IQiVv)d}g)3#lB{3#wwdvvfYGaj&yh&Ao)K``1TU-{`h zgsGe0bT1^r8$fW!2jIMXA9N+vIC0FHnVlUnuAu_z0C1o2DE?>Cb-RM2Bb>Rt~W;HNpKy95Gr2k35& zIg4gL*fVPh1|vr)66P4gnBJgIs;)*hOFfIiAORqKppp-qn=V}oP}_?lE*aXJMD)gy z{2+A*J%xx(!^71X=$=#>#S`US04We3f+e}g|4b@O4^kNciGkt55abVNIcFjXhtPLS zl9)->z6`bPfEy2W6$0PsKjG6>kO#1B>W|Yu2mZng#wPjftm(2Idf?x;2F23UJsar} zUAVvR4Wg%JNH9ZDl7HOsEB!OQTj75xN(c3=w2ZuJUOx9RP>CSC1wsxaULG2Ine?#E z?l_mVSbw)4%P`bprSmSER}8|igSyGbM@^U6ytFXg{R!J}E~|ZgiOzVeA9!yQBEjw3 z$F4s&MpCm_JQG%!1(GX)V???-0lxphqDPhh-6>}!u{W=0#bE3fVKC~`je)Befxl-$ z=P;>L-y{S2dI6Q3_ItYMNYmo*Bsy;s-2ig|d3Tu}`8RZIOxwzPzJOZ{GYiuDG{7VCCvXh>{BqNfY)M>2V8Te!DRh93fkW?&GS79Flqz?0HuK$rJpTh5JKxR*SU%OcQ1 z6lkFl3BE*69ChLkYB{%l5)nQEExdpJFf(gNF6vYQR z4!RzYlA~+n+Fll$cc9<%(BH|^`*aasOB2^0_Se#|S8?lDBiZ#HfGRQ#23M{|^zh(Q zbaVlGrYct*f!nV`MK)aBm=>UKfC1hU2SEtQZV8cuC|0 zy9ZATEXbQkz!CiLSWnO3H8^K%Fmy1g-AmL%Ed4=Oq$Ic*ACOxGA+v!$4H^S{5YEL6 z3qF#OE*F(hUq|*lWREF?AZQJwK|n{vH4)t2TR_}c5LXm|#u7{e%@FVFM|2}$z0mc% z!Qv>v7&-zG<~7-cy-uu*F*47k;zXz{|@f_dt!90h6s#i zZ2+w{fL5(3bc(_B#WKKQMGlGlJ^vT==4N=U9|H+U(9+Ea391wRza%gRuK>@r&h+(- zUw5Fg8w@=HhDPc_1AP1+4b6})=u42n(@^jRe92Alpeqr&NKO79BtSR)F{A4{4WYRl z5EvLC$VM|6AhUKS;jk_SSZ5ei!HD5Rzm2R_n?t3*EBMT%j>^vG{4Fhd!&u`MpV$wL z8UO|%M8mYt(qMoLm9%08-(iCE9Y9$NesL)Rjq5x;G@J{%bg^&(unXXjpb8`K)JkaM zk?_vwaAhpH)n=ehTNuE|Ir1faxYd4sB)pTqAB~3@Cf+rcOfW11VUCnQh#7x@u9y(q z(9|VHv!oyxx||4%Fc}1Be>GiDOPm|b4$#k((6!(&q2wL{z;eFWDHmh9axgXRh=A+lOrc& zpfh%zF<=;F(2swFk+e)HE${tQ4CKH28;wcxf1#ma*t?>H*Mra+yWeOYwEPzu8fHFm z(La3zl|~Mn3hJElT}!`uJW^>ijPCWUjh~#-F9h7)GQ_}&YX1*3=p8^@ELWBd+R^|O zxxO*g@i!z^{@&i6u*8hc(ylqf@GS?d2tXf`LNvwaK0_?%uygg^WO{&x?tni-E@qUz zVhGj*91xBGAFS$PeQEg2en(L;cTi3M_)R5*=sVy36$g3~7PVAdD+gG9pb$mC8x7Kh zx5EAS+>|=BZ7(ouiU+0agF%4w#}-305G~>1iuf(?7yWjLE-F8dMnfiuX$_hXLx{S3 zF;LnKsPm2NK=KgH6!S1;b zI3Q!qn;#g)CO=)eAR<9@<58v&mz{0}T&?`fC>c63W!_xmm?NZNNeBGVwC3(Yl4(#xgdR?oF07&)RT>MhW + + modifier-extension + 1.0-SNAPSHOT + Modifier Extension + HiveMQ GmbH + 1000 + diff --git a/modules/hivemq/src/test/resources/modifier-extension/modifier-extension-1.0-SNAPSHOT.jar b/modules/hivemq/src/test/resources/modifier-extension/modifier-extension-1.0-SNAPSHOT.jar new file mode 100644 index 0000000000000000000000000000000000000000..18a7ea85f53dcf856d5280676386448b43d2d537 GIT binary patch literal 102179 zcmbTd19&A{vp*c$wrx9^*tX4yZQHhOn-kl%J#jL@B$NCzufKEeIp4i^KYKmT?%lnr zt5)gPRjcKtfI(0IARr(Bg1ubj0si9y2>=2hBcd!oDn zCjY0CQ9iHu*JK$1SqV`QC1pAp(R-Q6327->x;a=WTB_;EnMOs1MdsZj2O7!gQ5q>) zK?v}}BGn``>RuAJ_N+)nWGO`@r);Wf6dY)zL}o=)FXRcudz7djlMhj!=OvjPC3_o^ zkIE$oa0%j3bZV2V@<>HJz&t{_w7azb#_c-!{JYgBFaDq7-`s}$LDb*enp?P-SliQ@ zxH*~FI9k}+(79P#nJDVmW-_37pQvr-^I9oBi|1A(ml#@f6sQP8jaBe**#S!FBX1-L zFFbB|zKAcCbz$r{dR?`Tz+J^SgVvU@r0y^0boRhfD5l#hY_4}+20B^~Z1qF+aKwOq z7*J*=?u%ILj_2wLrEvY6WeUEj-2Q@;f6|LAr(iyrhl)wXKq|Oemy`mT-j`cXtliG% z^&RpNWNpV5EIQFD-;^9-p1(3)xGf}04Z=;jp0O9SsKJJ6S~z0XUI{Lm4AT|CJze;4 zn_-`sYi^eAV-&=6x@V-8rQ~JLU1z1;%I;E9#cgSHd0VMTduVK&@Z`jiTOofr?`GCP z#OPYC7t0}~f%m0a9b6HR8U@MJ__|AJQ@)>hmWdtri2c-98++)fx;^abBJub`JntqA zfLoeC(&`zLOo0hoIj8XQz@s(z#I6zYz`99NzuW(at0%T)j;mw~IOrDoEoT1}lK@mK z`ewDv2aIX_Rh|(WSsH;}`p|);7+czXS}8*og_artN}^VLR(>|GqW!8?Epf!F+Mt9xVkm=?(RJV4#?g$v$1xyC6^wZO^;O_)S(Z+4wsso{FfsM|+T*FG!9h zu}~crq|b`eD^PvNPjQ24%Hj@!Xwm|iRdFknR42#Mq0OgXy`DRo{!eQ^v47k6JYDu@|iNiM;oi) zc8QR|3unk#dN{5>8pCE^6{eMODfEGEvyWWCm;rYP#?&ZnKnM073yo#d=(L;2`b^L0 zG=>}vlZbKz1qK+?A~M~Ok9yOS$8v5`et68@GQLys7d$!fd&C1qjLBULWonV#5VPKD zj+2#-sUB?wOH^iC4tgd<@DWZ?TG6G)5rCm@9l(vcce)Jl9 zM`R{z^a!5@-V3WaHT~uV1BID!I}>t7#tT%fHlwzVLUT+}sG%y-?&PLy=ZKc{mN7PR zsGb8RV=Tvsh-$}Vage%i6Do1|Iuln%r%6$Ri(I}xUw_bT-B0?DH(Wd z#e8J*t$%O^Q{BRbbIFE0s_RMFsxQ8Nm7BqiXS!&0vRvTYElLG8C5FceHYNrr$K1}@El&sE+66_)j!J%D|FMBF)0@U22+KCDB6>UT zMgc`3!jL@&DbXkt6Y7450a?!Jk)MiH;q_w?XAv$}p^q9jVDBX6v5U<8>knn#1*{1{ z4uUmR)^7KD8i^817^SK678RZ|53-0;QF6D_`o|!>R)dfYnTT1r|NT6LhkPB zJ%A428>NP;{MS)xEdApL>Wq=2Q7K_jqVK0?g28qWh#fs*g9j*Mm<0?#chDvQaoW+M zb_{kL0j>67v)w_y;Gy9i_>4Gx3Wiw4KLng8M;Z0;p>5n!eL6cKylBE2y>JI^8P;P@ zSE`S{wz?CB9h$q_6C;VvbE8DM|Lqc4NQ4=I~4P8>x2Sw6=qdD{H(Hyq%MJZZuM z3II@m`2QEt|Akr{O&nY-j7%K=GNS(xN$`9|^#4l1$kv+f@A!i5Ux|Xw(b$sCz|MkB zv=YTRbIW0Mc+2OuAi;S z!4wA0emjMgTFwH?7fwAvr;oqJ+$S8$%4@wps}?ja<35j9N5C(xm*Yqv6zB%=yrKWX zDTLo7?qrcuxPy|HiSO+D!F*6}N||xM8j&tsPS6_!#ZrhUV0;uWOoT-oJCzu=ze;Q_ zVUh+pzLtiWIKzp|mW*+OL4l+`buJ!cQr`9sT`XNgW7)2q!J)Ka%u`1Mi@vj$FpQ{6G>izx;m(qk*b2C=oInbkU=JdMn^d}6W9WDe>We_zsze2^Pq}QcTYFr2sb4fB1<|vU zxOVJt=WG6tejc@#48mH~fE07o_M>|kS9c#D18=PDc7Bc#u;^grm-)-QeRzmkVHb~) zF6nP*igSE}lFuFsqjqFfow8(;In24j?%UjJ<<_i^Tv+IKDP+Jq{ueFPhWW7!sZJrh zkl6f?+vJ-B)R1@}9vc<7mR`E6aEr}*F&@{O8?WKkv>A832MF{M;dYX>tE6ho!;bqf zjuvHl5*vOO1w-X9n64St1+wYDRGKtAlHSJ>!&8#4UnZsQfi5=e)#DqK$;w&0$Foa! zN-%!HAMIFIw&)muO0|ng?R8(hufm)KOyaW%>TniCl!K`PvIl)QbDxqP6-YOqP%X9Knp#(=ZIrLE$yg{KSd%MY z@#9*HFLX{#g_O(VisYYGjJ;eidQtB*7AWH4_W*9r4!%~~y6v{VIFy1*=8f!Q!Ybw@ ze>}Q}%t7vHYooN+gOfADhfUhO%vifG1Nz75a%;bczoQa9j+IY$%Qv_0v>S_bv3!5^ zGdV11P@)b8N6rp~DAO#{YZ6#*FWzXTsfNEI! z6jA+{v0B7mk^DSE3%wPpVir+_t+El!io+w03<~0U+eU0Llz!>lp?C-weP?KPd1XBk zCY|W8pW5W%o|G8rnJ(`r@tL`X9a?IpozV__>*rE@TKc@VjuEx>_f=w{%E{d9br#0+ z^8K`W46E61u?ynII+|i9^=O(rHfGc=!uEkRTPaHu5gzqb-yjaPu&wfcj##|KkKdq$ z{Mh@l@k{?6O_y=g8GDC=cadP7Pne~w1)p@?bvt3K;1R{eJ#Q}sWCZJ)txl!}jST0i zkMzB1u872PE)08Bv3$U~t%!3uMrs$*y1fXpu_%b|a1(|P_SFmmDaNjR`;>DYGxDuK zDnHKd4&>)oiAC8kxe^NoV}`lTU$>#}>aa{hve2(`FPk`2@3?v1L(Ptp<+)bQu~Vfi zm2+_8#U36LXy>GxXK&!YpWGnjA-GRI^L#Ppe`dx%W@(oHRR*zhu(dOBaI!FQRC1A< zlA@KQk&sakuTZ{`p^=(8P<))40cMIsiHbsnk*SWFs)B(<&C(GT8v0o?Ao7vtl?8GrKaYT;qvU`%Ii;^bgq^w&`4kNKbJe@gTp z#A%Ie9Zdc*kw>PGN`Dgl*N6Y}*B{`)f6w;+=MKM<`oC2|J1zy)&wv0h>n+PT+bo1l zW1df~FxQNpMz+8;OJ$&+7XrO3df@w~oI4L0HcJv#_5CCOA>fHj^J===vRn_zH~tGQWX= zQ#oRhN|pLYtOxFOi~-WBKqvI-uLoR$)$oEc?b)7}sjk+}xSi|_ERQ??q059!r$hiw z;uM{BDW6|mdcKlZ5h~jr?j*I z&w}s+ad7x#S1yh1WA7LEKITS&mM(+^%tbo>tp-T=gZf#5zgA$jiDmny_dWI%b;yUH z9>%cil|OQxbflMfJd3MMYeeMerezZ4JmR={~Q%# zQ5Z7&$_qc$h+a&+^9{l|(`>>aCXv=%^?HhJ|LpQ`(?Dp*$8`68POQD=+ui;& ztJ>m0BPqQOCF+Dtw;*bUDHfQ(YX~p@I8e+tB;Qa;V2l@(Lkc{5VOM=cIT5tmhOV;g znnKbL2NC|~5~BJXoC`YiuNx~f(TxYgQq37GaTzVlQT$G<0`Xsih=&lsIe|6#gfQ9U zVW@rnR2Ld=>MP#DZ0dSEQ7qLQyvuX$(+Y077F5IjV~&xnv4OdP)nB{ZADtTYkCXo|k>NM2|IPKkIQSUQltSRlA>iJ_-1ug*439?m?) zzJ2`qZVRwIl&aAc`qYTg2dNJ*}njT|M-n3Reg3kNl_oAnxeFiTcY6~a}cEhwE}=3hv(TNWu- z);vzx{0Wx2jfH`A6G6t^)1%=HE4Kll!R3NS`m~;Foe_(HNYyaT7(p=rP z>vZm(i`exNYh89;LH+`V)mPG5&EPJ+v%cnp-RivRBrzEEDvuFC3#c-RJ0NLb_2r>e z>!WW5CsL~f44S51o#QJ@Es?aSXzO;WPm?^4_?vP*hD44{hd`El1Dg5CoK80?^R$ia zPlHMY6I!@3c`pJiq>#J8GAP&aAML3V^j$byZN=W+W|lsy&YM~}ez6ybrm7X`W$Mo; zD$wW(Nh3PYwFb%tKbK@ntuz{MxQ3-S8*ecATJ6wm)hYI@q1kHGIYRHzdibXVb?}}9 zDDOvPqLuZmRQKNYw(uSuntf@jLtdldv;;0<96iLgm8L~(1oMd|hKOTBa4dagT;DF= z!M5cR7iv9$2Du&*AKVeb+KM}`97+|Czo?WcN5Drx`l!nWwnBsXF6Q8uGZUFNDN5B) z6A?LX7iIT?`gp>e^G%D##MctzkFI~?KdQMRx<&3LxkEDIYvUgHh1_%(CFO*q#BZ!a z6cAB0k#@X+xvq+nF6>{ak7`tP9hRKH^+=cSn2d|@=vdSlEjPxqYok z;Fio;BgbgC#0zoJU;8rflkr@lEjwbd4GejOF$x>8lyBI4(!x|vcrh)SOKAXjH7M(c zdQK?~>E$%i$m!l8l3HV^5;b!BwJ$%_i}YJKPA=xwzzuRyl$qSeixIzh)~i2f5#x-J zLWvtYJKI>A7z;Q$+8S9HIDNKHq-|}?1RNX;+({V! z?*7S9GQX2^@XoVZbpUSw^or;ZbQlFh1hgE&LVolxNCK4`D>`1UP&ndK0Q00LjF;EU z$}d3u5SsuJfzY_Cts!b%i-DlNg05BMlY4}$`G$G5Z4Rx;?$p*f1zpnoZ%yNEdDitu z6;AE}Nen7iG)WUCl(3P^YPyvKKBFPXg0|MQufg$zb_B-w-x%i63|%=IDj*Jgo7Vt> zuzHkYBcEAkqqg{|O}TZkFnr#ZFDOY7Au}B6GD(GpqV5?JTHEbZ= zFu&v>3X+78>EgY~52tTQLR*Aa(>rpWOxn_MU)Jt)z5+Nj z_YxJ>v9v|lZkJq)TSLPf<$TQ&KmP<9^O^HX=!_RZjj~yZGG&$I@h-#j3wAi(X3}g$ zcjzohemblWQ#7gw6MIl((&ml~#Yz(siE{IDm1@)TisS}-%Oi`4ii!@!g_UN-CG{*e z8576Vo(Uz5@%0STBuL!c^u3PFMWP=9upXCSq&!6+w}wxYOzy|+Z1XfsK<&B+EV3dh ztP@z%MU6m-x4K%iWDzo|DH^`nbE@R2vLA9OAhx27hK5I;X1G)wTbg!}%)`^lGZI;` zSL^1OeQnXXmCG_Jo)!0xBC&f>G}PZCqLAZEg6tJUzi5#&5)h9X#)&|bs!L0`kuYp) zl>-ULCRb1l8=k-PH^%tbN$11;F99D&oqnB9TRDc*X<^};aV|g zW^iK!)Jp(Hk5RZ)2oBZk45ejgX;$qBetxtXxlf8KVY7fn>qf*>0;8QO z0xgBVF>n&m6r%3C==Fx7@ri$6{%&U55?R}Tatzpx4fdu1l31YFI+*L}ySW|Indl8N z*N^pq$U&D~E*l{@_?b0MNKDU@x6u~d!sk%N`CnzUjvTi1J&`^ZN*rR9_(xU(ANaqW zP2qr$9{4i`F@E-y|J>Qw{twRfcQ&o;w<@^YzeY!heV%X2Ch-#Y>@i)&Zh{#2C_6h%9+mlWhVjR;kBx z5~E9lymUv&MaEW)FTcNp8Hk6VzdeKXP}1z&8R6?L-dcaDsMiTeHO6?{GkNLRAE>66j^y*oAn0dm1zM? zmF7XD`bi?#nvAtdmbQ%qg$|x#;*`1+_{S&p@?=xFENT(lu`oZ`>$i_vV1gE>E10h7B`fAQDGr>3&9apIbqedeQTD~oOn6a zzy}Dl$(>?YB~E831N38Z8-{(UeChC(H=R`pR!xw`3AQgHHqJEo2mvt<3@WzLZ?QqDO0 zw@vGYcnzc7l+;uGr0FPHC5*@p94>R|gJ6PrLoBDff%R^Sk`@p-gAT>rq-*?Iil$@| zEf&b<2D#1XG8VMTnl)uKPgGGY_9P~t#sl$xJ>G^6fZNu zr=G|8nDa@Rvz#5uywVV*T+@)1oz>5rR9&2HX%JOPC`MEtWSe?9`;~8(sx+|R=##}g zr4@SL#O3@3#Kf}8Tfdk_J~G0Ov}zIAms-DcCIps;HFJ=W%DZEsAB(%OvgDM12TRcp zhG}wMFQ)?nu5O#>=SIVB1DXQsiEL#}$|<>EWy#h77>v5ceCFF`y8-Sp+SS4I344O+ zqHm0rr@_Z~M)avVAP4iIs`@f81N$ZzqBnk{p>4UV_B`7Qr2Um3cmT{NdI#f~c6<4j z8~ED@KFk~9_1SZx|Ca-LtP)>I6U=7_-{d{pu4Z)A@r&!MC3>bCcoLXRvW73Q^cZb` zmN0L~2EclQxL_?~>z5GUpHuyP58$?!I8muR*fbGUe|0g7TF`wzJY+KMhaowzA6rM( z3a^e>>c}JWI#yv^u}Q_I8UK+P4?&&pVRO*sNLgREIM`O@PGp&twzKL+7ocK`m+D*( zL07|x32_vQ8SOJpH{m-DSql%i(!jR1d(3anQz~`J0fR-p&gSFR{?xtaMXY%)e zixW{f8iSG+ms_TYhx`QiyB9BiPevh47&GQNgeh%nf1VY3;%;*wZ_bD!M+aS!?xgS5 zUw=`}TUBcO{EBijAv>8|oW)vR`ehdy_1FN#ou0bJ1n1Wq(9tX-GHwVui_W;Pb`B(7yyww8V7C#axj=lyE;}J`_}{?o+S$PbZ#fSLjld~HXLP#@M;+HFeaPS^L_&->;B#euLlla%iUIx*0Kx#Q9~x zrLqiRuHA>Dw+EaPxDGd1!dovTn3J9ywK^YE-yRusm0@?IdjC+!5oYCBRYmq1Y^Hp?N*&Xt~p z;Svre-;LWV>Vso~o{FkTp+!$~fk!rU*i)pWE+dC~02+Ev%+|+BIs!IFy2GmMj1uWi zM6`z|H_&Mjqk;&+q-t9`!ORegX=B=`32%=uSbeW>E5U~(cdOgLHCw^_X!3D3y_wL06n`(c1E1db z5J)|go}uRU*swQxUr(sIS31CsQ17??7BBonSMC`e{yO|aPfzfxVWBpB_ydKDK}WZs zmdimtMPfG?@LLXFh>PJ+H_6~z&5&}a+`cK8oK$UeCWa~P&gci#_p^$b7}xrNwxu(} z&~$|Fi>P;i?hDc(y633`o+@$0@f)7$7rG&ym6v!PC@)dXTemTJG}jbw$fu^^ua|3D zK7ii}7Hq*o;a8BaU(C|bXJp$>eys@}$-#?BMx9DlmX(n{zh(A-2fu+BugkaJOPpR2 zw&}D_@2V8!%(wvIO#vzFZAvjE4%?j6qu1nphiboQHCeK8SX4b)lyzPlPgZVsd@7~z zF$mbNsQTOY05x?z;$>r8p)0jb>Ot_x#8ZDUg*B<|tf%gp}ExXB<@$F0XTY(iimz$cVO9H~3s2mY9oG>}4rjW642m^-U9Iaj0l(Q_qx5 zjTmLpEZ}8m62eE5F4ce%LuyiRYQP# zPeIaTL}3BUEIjF>;zjph@P2$?w=ogc&O_ z7@N4Qqs~UV&QO+Mx9*#QbA_SLMHMRS*<&(Zr`wCle~_55ZY0MAyXs)QiAB8w%^BQM0KQ(%m7Ff~m(D+K4&i)VCH_*OJN_hG*tJxPFc1_58Shg$M974@v9u2!ri{+bcxdmGSfNlM(z;sPfV-tGr@- zcg(>IoQzzM$fDxD0%4g9TPKp{T}1p0&b2f|XwcE@DR9%V#*awFn_cl#STv(K$G)j? zVWOA@9dD^7m!WH4O`E@*sDU(LMNkv0U|$fSErp$m{0P~XOf}t%Jvdkd(O-BY z8a0N~gYL(dxTh)bN@)kDsWSMeF>~sE?lg6owQwpmkWyJAacrGg!>=Lb^uCNK@A_^= zV7i8w)X28gUP>ON-%>Vrwz(2jrM9tj?Q{aO~9Os-2fW6gDpn2hZoYZlchk zi$gMBel6Fjfih8F-h5UBe<4nzgBYl8zEBel?)8uJ&YGw2OnFrr>L8T!QXN!Lat!z~- z*9B38_oPfiVuB))&1(`2a0ZKZ6hVpb>{%2ca~yLCEyba25~!A>aYce|f$P?j$98Yq z8GpRaho+MU@CUIUPNt+$$Wq};soC)^kC|)fzf7NUeLvo?dojJRM`Wdk>Y-Z=i?(9+ zWV(jdvpGs`*(|RIZ|SZY*pNp&XEidT31fjQ1DZLPkB!v*`qqiypWG;h6#~XGiVZ1a zbh~Q%RTWfcEh60uFIK6cem1yQCXH)>dJE7;C`)j(#M777NO7KJ!cv~rX`78XqdXX+ z|1jY-Q7_MIG2r4HLGuWQjZn=&KymT}t1&`R(s+qA-MDA*mh0QrceThNZIm6M2)CFZ zNxO7rP?3s?VCn|NbrhXLYzZL#ZtWqB zRZo4(f2cE309vlE*8Ubi(I%pR@q^Z}UzhC;$IRBf&lzgj`+jXPDBZD&6m+~PARDx> zYG$5m<^VNxSYriIIiuP+$02|naRT$cqq0127@{9d6*>%be4(rCQEQO-T4vI z^@Xzd8EvB#>QIq-gVp$}f-h$ge#5$#z7)`f_agW@#L!afQGmy%w)*oPNBZ>sWeeoX}m#;yn9OUr8H9KMAme8 z-1^cg?wUyJHPOKn$QfBH9rl|l!rGFZA#5Zu@k5R2>i9f!$`tv#8FUzq;44k#CPRi6 zS5bh+#nkb-lw%*vINRdgE(?4hie%$N8R8+$$~1HZ!49bYt|qos95G2^vF|m0J6f@(mW}=_yU{hQZiF?k(~4S z9qnV3CNho_XNdOX24YDTgWDfF%`Mmp6(_-oh>jgvMK705nm|_{TYJ2rwHCI`AMIZr zdS7aTu7+cTqBM@$u7>JtxA+9nRc%;!?THf$Icj3f8V{Ohgs@$}B*&AjQnrO>bc(7C z;x3p`00C9F(n$eXgA)?&-pZn6>U_AFT5EqF!t z5_%k76|nyxb7D@GurxrEk^o6bXrm^bQG z9qgBT_&ZNYEn33bp;8kGc!jyldq@k_dZKS$Ayl%k87d>vEfw2bmg)h~JoHTQ~p?QOmq!xfTCgd{1x)c?*;h3>hQZWHV3ZoS8zs-t{IySzT&270xOv~U|2z< zV)cT#30d|zfUuzg4P%P>@!VMBvyX$HIi z2YO`$soDYO77Zl_u+jovl>>QnguqBl=#$-N%`mECKkxG(QMbfhr3ZPmgh(4Xw4Y~9 z*v?&GwfQEy&bl9teh0osT`ox8BZyLw zC*~f3JL9@aDBzO{=q@|s0qE=x;XUA*Lik;^MHY@H_|AChe8Ay{z)V3)Hk`D$V57^z z%y^M@dZ7!z*C5(O6wh+rLAz{pC+GLS?aI>sNlcjhxhu;Z;je-|)&DT){~h35YI=I1 zsQvh2#=e$r1qQJfPlEsvtlvNqBrga7xXuEJFj^_$R;<}^A)639WwV(rliw17ruXdA zq** zXP$B#XWCD;J*TCoyFF%r0qXE_@B10uGy>GRb_3K*+IE8vAl&U>;-S&eZ6F=H;zHXu zZ|r0eG8eV#QTS$ zbo!%_AXD#7O_);d9NVMNCE5ZisZD4S?NHV9$c@^AP_=*D&n~&EhpRBv#MiAuSK3vh zTYj!H+QCqi#>}B6COboQj3=8%Js!J#wZBEC$!333^=6y!uvGipX!L}!)xKzB6fKg? zy&y7kZuEx9W<0!9{U9`TS!jf6$aRu^D*8rUHa0Gki$h@~cIw3HR5YH=!+2XJwr5+t z+OYTL>P$2rX6^3q_x4*b%-X|YKpSmRBR_2LcElvcLD*#MUrcazMzheUtK&nOm;-NB ziMI$}TF2eea-*3hBcj44L32o<%Van8=J5jitvqhz^#R@C;O99qglnYz;ki zCx~kfg1s7pY~yb*0_L%zD{f1~3F76EA?8iS>`01+5lva0T^^iTnsHV));BwW0kzuc z(PYg1%UZ}0Br0qiI_W^&+V!@sPSUH@@i_s!muI`AVGr>Q3>Y5 ziGSS_cg{Q|{%R998j6_G1ZpI-)Td6tARcs1s1g|(7=BJxf;5Q@Wq#hG?nS|$fOa0m z@*_-j%jyxC>f0BzYgO=mNLn8h1J`!m8JAn>JuE65lEj+&u=Q%Vk^KxTJqz`aqC=J; z!J_G`7CYrjaX4J%gKq!8j)1DA9Dbfj>q4cNOk}Fed9P8!%z?SO7F7GSMV2~NuaTq|Ny~B4)RrMG5cii($3KW-4Pg;dWD1C^bLA6u z_tcf~chArx2&>Wp9!VrwS%k}SN@&fINx%{{DO{XcM9!lg&Fc-;%e^6n^6dh)=!>MR z7Rz|j7TqFeFwI1^Wif3$oj8s;}MCD&a_usg)aT~LVQKZC#FcFpWnu<=W?OvX>31n$qlvcB%o7k^i za!TzMFPZljiYkdYny14SVv<-vYSS%NYD$h-nE9;Ut@w^Trf1g|P7aJO&m0|E9v|G- zv^;-$(OAx$&jIxfu&Ck;AoZZ$=3R#5K0J%ZIFmL$jKcNHhX!jOd-W*KpR!te`8>JH-O z*pPZcGoY6d<64yhSF9s#yMnb0OaWV; zP(YqsZ3vd@Nw2oobI%*93!g8q7kp2MWSLGx?S`XA#~*$`48;q)CoyZYfvk|kD>dMH z7UH3Z)665trX7|WONREeJE~7IfA3)FT?Jc^1G1Fd|T^_&BU}N zI$XCeMZJwTMmq5>#Lvy>)MZdvfg{dNwTOfpg`g__zJa;a&zLQ%W~g1LK9Tau8Smu# zH}kZ`CiyT*PIx>;U>3=Iq2wfQT>-6omt9cgsL!ljWxMEUbCQW;aads~4lR>W%hehy zdKEFV#}dk^a}{2Q*YY<~ZlytbrhIw|H+6u7p@J1kIf2AB^O{1xtCdTo!*bQjNT)AiEs2WHzf<5f6~w)d|L z>CGqid^O_?ngPmFhlo14BKgN@PCS!q{R4S88}oe0;ac}qZGXkbdHmgMD86vlH3H{r zhB%D}rEuJTpSp~BdGbd;hhOAz(njJfjHlaQ*$ z_KPUN4VirxxNZ5DGNMxjtM{3O;|emz@0OP@C>r~3fdvGYJf_%tMUGqu$)Ov+X=gsj zq0akF2N5;iMP#FBre34ip8SlSL8e|cZ-;m#R_ODR6NND{Z4H7V z*9-}1E(BT{u}$D?Ayguqq^%wG7~rNZ4BRzXfJHa$Rl1TeD(d4e2!s*;DKcc_itRc&Lb0`%Yv6R_#v5LIJJ2yn(Ib`-6e!4bRK?|f z$DE1eWiyyP9b#*+7*m;pr%VKoUfe6IN^}V^Pe1-x7A`Z3_Oh$Ma`njMY-%dESE$s) z+}33^u9Jie!~$*{i@w_dUm+%k;POfHyemCDvVw<4W~SD2H_(hQG;1X>OIf8$_4WeY z)t9UC%B5;GwMtl*@+1ur)_BeK&U08vZ#I)cfweS|a(0Rz=}9;R2<7r|!*jjX@w=wv^#{0FP{d0qD)|s09eClu`yBq+A z{X}l)UDyb+@)Mxf{(`c<%5+E9a@`bxC~}fc=9uyCb_?x*qub&?6aXE{z;?UQwO@a6 zfL(QhSP^Ay(h35*+aDANj#)bsH@?s@_j^ausk8QNY-px_QOO{`q~v#BDv%c&0d z#h*$tm#ueZh^1yuq_1=r4M5=lGQX^Tole1MXsckj;v{UnbdTAXrp6o1hIFazk+VXg z;L*Je)>xqvS2jUV`xVG59_4r)_lA^dBfA8%?POx{9@QYCR&htiPLe(JHI*cIxKq1o zubaX03-6Ia8ZVfmXuf{p$n`yn6=q=j;w5Hq|B{&jUILWoui$&=in3fTG=i+)V2DhT zTqm@MIH#&TO~!tq-3^j^`CKnFG<(lm3YuHT${;PN5&g9>=veGYYS`oK_kP8`uK5)A zA>+vr{g*LlIc(|5Doj0+G}S5dVd`Q1Vpb!)m_F~^Z*|gkCpnmPdaPizQbxI!_4F%i zC(~ixL!aIBN6grzi#5*vM%z#*DqmQU8zP#(onESET;&0&L$MD~@0%SvjCb&u{*V6V z8_zbp_gtQr*d~z=5ThG2H<))Ixmhw#r0InmH_)6=weSH@WrR8qM72Wbl{{?Axxm!C zpbdrLfwa(-0(3cjz8?6OXROMK-fpW^+06Eb*X6tt%?k|UuR&jplpVv@F)P|dE@_q=gfH(yY& z=jmLbj1Sn-bDDn&ql)-^D4>H+P5zf~4Q?>3Sze8qZ;USrn`W;E;HEJkHjZB=s9hxJ z<~cx@FaUkCU$jfF(lKgXn4Bbye{ma1%K;O{uwolz>cD_o{7p)5$M~`P8-y2nj~JN! ztrW*WK-k<*hKi`C;5grO0}LZ%)@VUyY;zRNN6i)gIr49h*lopx_JpAZDoP;iGZo_aB%wj1Y-I~<5tn(&ocF+-`v_bzm#Z1e>(Q6lGL);C%ELpPtB|qA3Gh;s2_#~T=T!u( z+bV+7R2-FH>!Yo;Mr=C4R`q^0A8fV*u-xU+24_DQvO`-LXjF&x9fWmySTcaM8I-L; zx74brmHBF2{)hvLEAI6hRWK*)?mxBu)a=kD*x3ap6|Q7mwQ0;V$gK7mUAGM--#kZz z=3v=Vjn(X34bt*n1?rgtEPK`31ZWt@#t04yUmSkbofmZBB6R+OQy;el$G=5d+v3-W z>(}9mSMT?V@wwBr{J+OZh&v7UYzSm2VC0wJ5QJXA$Tid2Jk7YmrVk4cX}>pap4Wx({e%p z-MPCZ1-Vlt-V9y-)qxF<`<@6f5*GUA!gj)~Lt5mPd<3t&>6uV!l;`9Z*mwZAU*mf>}@B zO0~1iXN~@!-543$1%8B(ooL(*dmXGEP4mQ%`IM3peMl8;esOCOI2Au31=5(zA5>f8 z_z6b$wHyQjRQ`kTyJCcYN+qP}nUAAr8w$)|Zw%vt3 zwf5f6|L^lX-&!~4BHx?Lyoi|*Ip>TqVvI42=Wos+5nP0}h`%_Jf^oX^{m&DNDq(u* z#5)D_@k@}vCq)uUzhV8yml^a)0Zhq|>>^Z+d*R7tldf(HhsOvYkN08v1(<(E_6&132MQ}7ZP74wR zDhk&8K9Pf{mDSOz5avGTE@uvxXJW34B1?{Cit=LS#5-%fRO@>E<6lU}NGQ1K?Kjrb z@Q)KfME{sS7Pe;pP9Ux)yA@tgP*5aLSyxb3S5R0{(3rPwI%QMc4qa744ML(oYe(N2Uq@d_Ls3K73SV1G2?bNfdR}P+qQD>G z>Atdtx*a`!imH~55LiS&L_kPD;PeeYs`d2&>e1ExiIIS?zVjapyng0s^sh0u*zech z*Tdg8@tj;mEv!xC9qn8#{y&)ce+ldPJFI_)00fg;HPj&dIqB^a_md?kH8Cx<22Wct zDLvJ;I=td%ygwCtZxcBON`c-`dmHo+B>6bqgtP?BJP?JcvAo3e%#z%+oW!K~q{PyU z3=qmn%aDSM81-tjv&Y%1p=-597OaA@t|6cq~ zuKyFH3;qvCr`MFB3E~w*SOXKlMh;0UJc~5sdu??fwIfM0B(50pwkv^8X8*OHR>JLsCN?{Szt`QbZ&SZ+!synkzk9~$6;RLdrjooTWUO1IhPs9k(->NZWLFa!@B)*Sc@@wmN4xc*=cT&uDCy`p zAtjT6ZL&)Gxaek0m6xM(J-E+W89%{=h7tZ$+9qSev|)E+%~B z*?Bq9qJaIP@N(|)h-vg0gckdUdW{hZw;ZRhd_5aTiv2xF^GDRcf3;SL?h^fIhV zmKetX3)h7dbxi_D}PuVu$%d&emQZssaQdxS= zAh@AJx;;ZMdt5*t!uD6mkw{!Ez46RAS&Ur{cTd>^8 zZ-<@4Ou{AoTu3~4a6+<>>}zByUzC2UXVXsmFr{vX!JQ!+ptZb}z2sBPXqpTyPlA`b z=&A~>@pu}jbXsHxzi-LNrwz!39RMd+om#Z%dSa{?C8=4)v&(}qw5^E;LUP>@k z-oR@jMqNsfRhCjzpeYw=CjMdRavslomUs=y^BH0430e}(!;B>)(uD4p-y9Y;SB^4g z`r&Vmz+kyeAy}G^B+|5?B!=K1gzPYHPEoMn9$0h4lSNtZ-h7`_ba8nq}t610; z%x+r5+5?gp2)-)FN3gQwV?Uiu-=v&&Z!qbWv9U=i+DNHy~T zbmyP$OXiZV904j`o*xV2^fES}gSLqWlB@7^Wr!`pbteS$+Xf+9`A>^KMJn`3`&EVX zy}O`0r7{(PP%8sWAX1?G@3!<;)os#rK{g0hZNaPfer^>KRRCC32Kb15!(#g0UKr0m z@Kyqv)%tz$S7Qn2d+wpJ`UW-Y+)*yc0=gmn>uu4W!4I1Cw^ZtKA)ckcqlQQ7Af!R- zSemFRDPM*G|Ao#DxiA z6JDfuodT>gJaQPCLQ7UqvMM7M)Eda02#d0HS#bM}iWX5PE7F~a&Ey3w3l4>Mc!)17 za^UUW|25=-(yHh7@;&Ms1N%=q=ig7&{{Mfe|AD?Vyu26HlK03eZ9hJxpk zx-8~WeLcA4QrX$D-rT~gYGpFh-kYvxngAgo`NGGSJK27c<8{sP4S!v?pL{uf)yjE; z^l5VL3BvhS?}4D*-}1qJj0Ed;of_}kfpih?ngH}7-Fm_J+3&`}e$+xe+>sG{uKBAK zeMSP+a^9*5(6K`Z(~cKdwBu};2A8O>_p+$_4%R+q`jWdXI+J}dh~`&uqgD2nzxuL@?9 z*do#Aim|qrXUfXaEN)i%Q#IPP9lpSd#nPxSaYx|Fc3sAcxno*nMJZI&i>1Rqk4=P7 zw5o;;J{HR%poIkE8U}+wP{K#(;CO55>iq+zleN{x*3O2=J;MtS$bWpBzo>FPH!PS) zr#Wb#!<;w=To};{uv=nKfDQ3-s#>d+wG3iTUCpGc)yYQA#Hp!rBJ2*>j2xROAl&>C z-XMd50lV57`UR8Q{3aR{s5_*XDjodgUO9hkv26}748)x_AOBZXEBrAlSfNjHzd8Br zlytHWC>=F=ZB?Q|jv{gtT-c>f{VBitmGv4sQp20Er5k%rnCmd0PxPF+9=?D*QzJs7 zXSltdXe|?_6;p?Mg!Scm!=|RDa^`WR4)3+*deoPBR5Fc(N6?BrPlR*mGtJBG47Y(7?PL0uF-}gA`daD(lyXJ#p5murfpYk{Op>)!;~o6sR0MCIqPw z15rYmwHep7=@b&I-p~S?7Cl`o4M&W#U)=^ciC47zU><_BqxoC(DnGmV;&rxm7WK|x zj)Z*~JgVI3l;)ijetwIPI(@(In|nyYByNo8B*e39Gt$6O;wo(=qoNj}vaG{HkjCA$ zF(EP!x&&Oa=tsCYU+HCcS6tdUe10pnP@Ud0x+TQp%9kJf@GMA|dH87nXk&JI{}{Iv zvIb#0RorDl5GzwDIjanPMpRvL+OP?Z)Ai~S1e!38t!P$&eZWOxwVhd~F2h1NQ?wkh4it-^ z=ERlE7}>cBkOjbFy{xr{3Ql~bh6<0OB$dwZud6Q^W3LQax);hh@-HDbPf%;{OHF$u z=559aSZ?hM+JTbL@nuM>qj90-cBXMS=ulg0PL7Zu-n0m3Om#C5!E-Ls|KdH&%!-<{ zkg0|V6e!e-prSzUNLSJ}yf`|~cn7-A*pparsuC<@Gk?v;j{A{nvZs~qr?IyBL^pG% zT_C{={iUiIxd?$LR;eO<2vdVtr`d-Ipa!WWtS{Tu~5B;2M2#sSm+8xv|)^n4V`|Qv^GqV(-ZYgcfc7fg} zfD_1GbfAho>wvXgcHr6-E;SdAUv>do=K@C@Egq+q*vd?tr&RjmYQ+Hz*1m=bbd~IG zyx0LJGJ64 z=AA%RPGQahg#>kryK~$*GydcJU1CU#-9Ck^D%wOO=k%Uc7B^i^t&2B?9)?_Mhm>VtF(u~MPM;<;+hEm6D0Q`hWmV%&~E zSS#mJ_?@Z+9p_eAJA`|lfNqt(4d5=FzHY#6f?ZyK8)SPvoaHl}fNrfmFW|0J+}Sf9 zXnSC}hvvlfH)K{OMhnQ}1UpW~aIIt?7SCQ;vC}eHyttoUaj->&G+Y;rPz#I8#sz%o zyZTIkK4<+L>E+Cc0x>H>m^@VrU}r+}Z*@Oo;Z0*>HsYBg^zJPrk}e*>eUwXM!}D>A zV;`F9nR}QE7RXK$IdKZ*&K@oHvUmy0y17t!$cGzcW9Q5mSrZ)K&cyM@gkb8`nOko0 zYja)bq=nJ8T{}f{m(H9XJ!+Il$H_5yY8KE=+4UsbBU_wRW##axbJ&6IpEP7|qp+Vz zNi+5KAEkkiGRQmzWfa6NR~8SM|%aFOyeoB z)gukeWgu%FWL!}~RS^hJ8W~g;qMyrNrV4B9db&)y%#7{RlVS^^NvvW-I$gZUd74~n z#MbG@mj-teBEgwd))3I+CuioyePJZ$N@IG`I=x-IRG%wiiRVBI(zq75GBqqXD+3&= z7?r#v+69voUa~>w(Evzx41xv*R0iHYDg|Dt{ZCTF2B?$LmD3^{ae=DYG7seI;}YgEVe%VGVI(xlLd#Ew{lKpIUL5zFcHQR1W;#H|{53XMhsoERdL%DGQ> z&SS=wvI`legjy;om5?(EduAW+1LEB*kb&NV#8KHJ0!k@nNz}>Es1%Ap76QCy4uOec zgzyj`hWVV0GXapUt!&o^4b4p+&`uHIOS&9{JP!etIDRo@xx9>2Pu_?XJhMA?!fn1) zX^V-bhdW5H(`XSxte2@9?QDb$nL=UaXVRY=#$s#6xNPk?0qtG3Y7uYh*ImN(M%&5v zHzn*`4fODkZ;79u3`DqWtCCKPZ00)mma)Tyf8K^e-}UiFRLiFglIx|$K0mT~G%bzr zpJtdj_ANxD8fnqrUT|-$C;1NQz=TdweEPs|=I=A1SYKtVsra4p?LMvq>byIr=xd}G_OzT^rjA~1mYN8-dar_AwniXNdRqdA7$Km-?V&~V0CX(Q?&g~Z32W0#A(-;DukXVAnw zh3C^p+aE6vkk8nUH&pD7h_XL6x!I0|Cc?BDHdsJ6+)e6IJO-QXvLmpV2GBd}4MqxykqsIB!ixxd zD7_VUUeYc8I7nyIt;%Gz!*5du^sIjFvuQMIe|@OX`qS0m zo8;c*x5!Wjj+t8b>W5TlBZ`@qb=}IJCWCE)z{p*y049U#(}2XR)|9N69F#wE3&3ZL zV0=SNKd`G`>3Cmy^>6+pe7;ioK7L`&?1IGP?ChF!i3h*Zpd6TJM()hXe&o~)<5jh3 z45se`xrcRQHP#`aK09I_v|C`luS7Jla!0{ch+7SYqE9HThGI;pt%{*HQd}a8K*|fd zypr>d#6OeE-5i=TJo#e z=oh%;R^i*clXp@L_k|}7 z!V4SHNSUZxAC4mq=cR3IL)$q}k|Am20W54KK?(~jDPgCk%Fv}j@*;YbZ_8JuKyo5F zm1oP-q(HJF*!mlz>cbyU%?Y^77)}OE)CJY@62|3)Cg%8gFAGqt;~brpKoFY{L(36g z<|tv4hS>!x#R7ibWHrUdXo_-LVw{oZ93GAd^tq{Xf`1y;YCS~Wy1 z5wMJ_SBo1aG<9uMLLwz;u8LEh6{Wl=dSdLz(R`5P(%p!5255^Hs5sp`OG;h7FZ2Y} zjC?{zpgjx~Ov;`#$+}X-D~e~st?Vr}l-1|u^>%wV>4qis586XG{R<@k_pVOTvPL+& z=ni)?(`<(|-izkCHsC8YPLs~uz^%JBaQo?vw{BS?Tz_2M+*vm0pR@X02HSkmaad$4 z93URFYagheU7-&^XQGALB&0nS;J4|6e00esRt{55XFa0cVQsnDGQB~@(V^xM#z7SN ztndcDF1l(;={F{Vg>TW%EjM*jS9pLA(3eZilck+j=%2yXgsroJ+xvUv=KnkvwtrKh?OUrX1L;RrJHr_32NOVu^a$d&-bQO+cE7a z$?0!hLmPgDNc95a&^BvazHtmc>XRZroWIdfiRd49uVcimiUOmBnF@f_#>bWCKzs$G zMZP*ezX*#%uSk3tV7~nUrZd;hU{f42cKq*IHEw#yeUgrs*Y_Da+()Z^nqx z<<6vKTD>1zrNM}=rRreRXQ&1Y({tBf52n99cv69hddb(0xsd*VrX8LBu3ppxBr7*# zvFV7YGnz~#hRQn``P;5JU5}suQJnX;v1omu1y5K*N4qy0uesaTzj`MwX)R-?-zeNB z)j#!4{uXr*HgU9Y{g*h=f2)!y%E*1sM)_o>d9%~b62lx71~>6%<)kP7uYuOwaat-|m|G_ zo!Hz5pvlB7W+P~(xrnZib+Dy9tZl9klo#Y=sAE34=M@nS8)1F(u1xV7l=j7wJL#O8 z)^aWwoAGkSj0Yhf`%XM&$mpQiA^}e=RycIVP~O*?tG=o8m&Cc&8pWf?;(@L-qDtdj z_aTw3$L5WeUbfVpsBn`pYgmFQX7DDSFWdmY8-rQA6CRqhqU4UBl}n5%!E=_TS4-2Z zl%+Z!)c!0APWgz64`Z6puz6o!k3V1psHHknT?`E7q7&#u-R~x{kmx^b!*MKFJk_!r znLvdiC7<#G$S&&-(2~Cd3w?Fvd!$i94MPgHyMU~4H}M}LWi_g1{n80<6K+im%k-0F zn~`Of7~_m7yiCq(INPaYlJpcruSYvdk<;hRS!?6l#ZvZA*Jr|AY1?(An4^(7maC*y zD#By-{i-c2>yWx-e$QJ&*(JO_|7*T#30ZA>`5SY){zuFW@9$t65j7cs{{Xfs7fM(v z$bWS1VWr%E-IB`(Sx7aIQ<$p&Q~48I;^1id2WdL&(Z)d(qB#nt4N1dHzH12yaM)@UUWSdsj)Bnhi*<` zzP#qpU&<|Uzfg=UKvN7)H5#is25CxvO@&+|KG8!TB3Wgwx%=8t#cxa$HEaX}Z^d}2 zHB={H1Z1uWZxDK;2PQ~=YfgEDY#UQ*h!Z!YVmef&Hkcj17@k!>rRm@3H26Gh_cvq&9pV0<(j^Jd|vQSUis>jIB6v4eg#Ij-!Mp?ELvskihITmczSB9XCZ^{mrF-3JZ%eDw8wOK0-8KrrP2~yTNXeZou3}O-TGzzl{ zaT(eN2M?HK7H4(s#otaqj73{mc-_OU#4vue=~UEKwbCr?xiw>zwukMtL_E=1nPOGy zZ`&?1^H_w10nF`Wfe+Jr()@rVOuNr7;vbC&5b>_zA5IF;5I~KWzXjk`rVphE+4y7S zgDmWz)(F;?y9?DR)|r7ShP)m4_fd53-LoKz-R*9a6+IJ?kL;5jbnmab&y!))=lL~(294AFwc=s`<9vZq| z3$Z_V3*_(yc@hY3$703*Sk+~xo?-@FkZ_;eei8Rl=fXeDLr7xxm9DhE)wpQkx6af! zJ?LGuNGGL~T)9HBodKxui1E9yASesTI9Jx@UuG4ShR1Q9r*jJ3SRnbFOCx>K5vvn! z%cL_(AdHkHIRuF_MSubS=1fi;NC=Zs6-d1crj|>NV;G~^<<(AjN2I!qV-4Mza_PalielA&rYNWDZ)N20@sfQT)>K9N4&D96Q_#MLEM|Z-5x)raPWBs zo!fmuFD>+%D~xduJBy8S9~Z;SzXkGK4C5b^X$u3?ZurTC4DBura+wF$i9Uj)Syu1xkr#lFAF;GYVv&RA(SUN0vr6Xyd^9??zU%&jDnKJ^{rlHSJ zOpE%T$m#2#L*|gVXoPVF$@Qwq-Z!?taMs(alpWn~P~i3-)%X5Vjs0H{W)XLLJ6jW5 zXA1*sYY$-)BLk0bu7IM+U#10i7XM+L6stqHe~Vvz>6wz+ncgEl$JL1-lR%qS6z~i7 z#|;CAe+~R&E>KSA%^yr?2X;@x^92@I@xska=O9F@i~e2`EhJ%!f89M=Y6&NW@nquiXIe_ z8Q2E0A>6?dX0lpyFd^_-&%-~Z=IqT97Vx-hHNQzZwNl)!Ga7?GvI~oGne^!v;hfsH zMKjMczey!^4YlvfjW30pWSYv{S?nP`nu9++izdtmT7;4G1P8`|6dQ)-RqUZWYE-aB z7Wg^OO`Dk;cPxRk13$$U$c`cha!bQa1MAHteulyDURCuwCeK%mJ|}T_=E7az_YDXG zo}9$PC!lsHp7?{|&#w>p47{-fpoWLhfV%|XeKLWrD7B)ua%ex)p-RH;2(_U{E5j!j z9KMx1C>T*29iOLh`-$QBk!BanCQ+DR7FWWfGzha~jGG4TpDR)(raCUKT_KjVRZH|G z?X-$YI5nMZ#r}$e5%ezWiZE|m4GhHwvag7Z8&#){hIbKV*RO#f&7zYr)9fgsMuY|=R{N%uB(qyLK%NGkHk;B- zDvRqn7YC{;?3Ql!dX;OoBh?YG6A~i@tAUjK}H*=k4FRTU;A50Y!$X_tHIOTgmp!NhhF8IGseO)n%$WtFlcJ zQEBN#3y2rH&jUftW8uOc0gtO_GViP;FK3qf7G(EmBZ%Ke3Q)PXDwW6L5!<6P;x&!hK>%dQO*y?NNUL%(oSy+<3ul^~of%0GMx$(rPn6r7KN9?9G%3au z4njyLESohw>VmcgAH<)Tk()GRM+$~aRBcZfrA~+4Q}GfWM}&t6jWm@gwH3X1t}RHk z6=RWIR)9JrYnecf*39p)bz3&M4Zwja6D1neBtu)ExwbJe2H5)zBrm4QjL2A!;Y!8r z%tA;T9rupP21QgjeTxm8)dc5t?Z`5jYEYwM-LV1rp$-1&AQ3SbE8 zA|&)woN>%Vzm*WxVg$5J66^Zxh`AArH?$qbYgo{Ag|i@H2&=51cQ<~_N`#9|L4I=B zC|X#Fa%Pp460}VPJA=#v4A)J>Q>wax>RnUT==eO{LB{&Rd`^?srq)N@V026}v&xn% z=kKKn{U%MU$(ptU*ixJ;$?+~SPP@94H0mdL!z0{hqxQ2Vr7Z-hHneBOrAjhHmfH~5`wBa-Bq@lQqEdZ}yQcD)5-g;XNCL4|r@W(ya4F_(fT?K>5<+A&B{ zS3!@fDs8#2(#FV(mW@*}uyCV)YKayI7215@c_D%HiN{u;X ztT}ntB;mG;#-=zmqTSUsHOU;3ggzsi^CW@lqT;y6bLK?UM8xrVrBOV2-7j@lQC=64(jjk>+xiudwQ%l*foF9k>uesk6hryb^*A%Q2Mg@=IQhr0^)oB9Fh!`y@hf*j3@9P;fuhn%q)s*tZ}hbQV}IhoP-W8~RD zJ8Wa*#UNll+4EhY3kykxMa^+_7+ywdsH?1>e39|km7b0DpZJY@Xv=Vux><2Fsyi)653)rio+6j&O2K&teC1>3AG!ig)q4dkDakdlQ? zh~*z8G`CkGYZ(oQq)$PT-9v0)XP@kIy1R0ne)C=W4)Qo=7p@^5Z~-J=0+ZxR?+*9p z*=1-le6k+cOjOZ|g?L9KUzxTd!*#)pIU~#pzr0T^qHsFQPV(7KXD+kpDWOEjX*PQ! z!R{X#O|fvcL#`@b$_nWSI{V$dG=O~X{kYrx03YZxgEFM1>5dI%%;c9D(7TCmQ0J|w z0_~9$g(db~*(r^uHoIezWE`yFk0hNHPKZ(hZ6D(j8ry9XHQ7>^mL`5e2D%ytb`8Qb zh^H#v?=p@z?r{l&IpbukT+AsP6U1(hNaHZU#XI8`gPpr*U!M@v{z^F`#Z0A48=s9L z4WBX46TGIT$n0^!0?cnX14?V8!U{EPZVTPEF%0<;KU66 z;GS@ol!G;lP&@S`;l1{po03@LCK^oWW_OX{)F_3?Bcy&y|J-#vJH9-@nPZI<>y2%eL0AF7hRlT z5rU7@9^!t(bW=cGfQs#Se$Feq%!m$TfAB|uUxB5_39#0Mx|Kkw=6)-X4Lp)S7IV0E z!S${oHuX%`X8dHoFuhD<%s83Q6=Gf?sxZtguA-fldMpsg@`0*lK>6jt<*=qZ={DG* z|06lsf1aPOf-7=?aY^3&~7-qfzPS}u(V%b9zi-)kz3l$V6pkJU><30 zvHK!1uyuYg@5+yKha@c+$rZ=Y*BSnm6ex(L0J)|#sPN6%V_(oKGKAA+=~#Hx+W|9k zWIB!sQ=1pGGa`}?ADXFz7Ev=uLgH?qkAdd5p0EY#quw|x-GQJwl1)*6VB?#4Rf=`z zPiT-$B%gj0&{Z7wlzH{_Ek72z9&UXAzmqw2m3iC`o|nNJ~_nbFgK@9BU|sGuWqnYQUlG90PtY2>3!= zF4GzuEPYibrTCeukSC-6hw@zlyz5`K540T}Fq+>wSSbIfgT?fB2Y68ncN1e-gKr6} z|BV=%tYjs(Adl`l&AMEJiVoC|#c8}xTUSs}AQWZz+LQ-j3ZW<7@obDeUoEvkb6z`7 zr$rdf*N11~EvH3?MYNy#^2K{|m370{;|o+5oQioYFK;i`pAkX~iP_LbS6mR0aA*Pp zp{!h0xjHK!{B4ym3`D`Jd@J6c5- ztKsOf!k8U$22nmbqHIs-4O(d1ZZqg;^DyMxHh1h#M!mC&hS%n!-}08Tvx5ffchc@- z;8Z)3*W`{b1D!eiyL^oeW)E{-K9YI#vMEX^;H=5AcXS+5JNzbG5Y2XgD zOg65aT}Db{Qq@#B(zcxTL`Z{z4*R-le|$bi#4mbponzf_lyrw4EE29DvUQ%D80M)) z`yqV_IA%NZ`BI2wQ;9v3nFHCk0F{miR?!d<)rm7;qcNxvmI{Ap54!9_v)MDr@|Ls_ z-+f>piX}4<$A*X}uYjdt;%6&`LFd87zj23f;p`t5ZlbG6w$@?)erSv4N{Ua2C9io3 zDL@)(OwLN_Wc-93n)_UeW|`M02u!}o3S z?}^#?f0u~=)fn}O;7v0(#4639>qi8=E zWVi)*l?QL74kHA;Sd)|E)B1Y#_5!mHF^Jeg_M&)(_*5Lo(m@{RH|N&5r#gx8N#~Wbfyat|6g?=Azd3GT-$NDb>kI(m=sO&dOTTY$)*4B zA=!J)WkLeXFKjNJDWuJh%#jV3#VI=Xb@PE<&M?qonHz$=1^H{?LgI!7{?Z}(OL6P( z7moPv7Eav4$=S}#(ZJ?EEyIKhStNmPg7d;g%LJr403A3D{ygnCCGxO2b4U<)dD_o} zSBF*E@wo1|D_XnVgEaF267zkaSNRd9g<^Q6MLMSMoAmUJwxjRY?`I%>pc!fPG%AB1 zEI=+uYc&R$&XZIR{XcMB^`Op=(A|x9>-|N5q(CvDi^ZLVozcg`^4Fb(b{qy#cMfvw zy%EIbuh_LtaAz>?ZPS1m2DP1X@f^v*i^Tx^Glk>94MRus>2yjlK(AJ+Q{)kX(HP_I zq(aiydvBj`LN+Z|5ut@-Fv6yWc2MTLdM@G>Z9`~ZT-}M2vm@Hwp1KcX*(47m1t zD2X`C@6>}AmsN5;2wo5w*E$kdlZIH)BWw_AU9~2c&xxVq>TqrYr**~Gm8-Y;mfE3A z7+VL49fvcNL13RP)#ZU2=>trl2HU3T-}R81&`Y+#%j4y9~`ErX~ zqrcTY#`nwMWEDCEO)Dgk+XpRb6-BuOI$*i|_*5J-2Qr^_TR^UzM-JinIs*=dDl0#N zge=nyz~D*qhmbC*lZ4zLR#!za#v||(oFp=)G$S{4=*L;aWr?U&Vt6KTZ6BpY>a18( zN|RlSJRCfwbzLi-;V~4rkjgN2@lixnx=0*hV~+i;D3aZ8{CW>&aD>?Z?ZeT$^11U@ zx_0@Z*z0`PdDK7FxzyjKtAwqSv!jd6chAVgSj64L$i>;t@ms&uu&f<;4kR-x#yRkNC`RAaLwG#d}1vot2(YU zPo?ZNm*KRQR&cgvsjC%5K?}H{iaJHxJcI6peP$w6awFv#U4vzBSlwkk@T^zQo$(${ z*2+NQYr9l`*lTUE%Iadi8fIL9slb?>KKWGcH4m94W@L?j0tBNW+prYQNpDp^vubii zVyPQtI|)|e;x~{5C1Ke{mPlaKaa5*;tI6EV3-G@#S(?t7Qp~>09sQ%j7`ccjoB811q!=Y=bzbTf?sP5 zvbikJY!5~R_^l2f2}slXfV!%d+SZrfYZuPH;kZ3eb;4T}QlqZ3j_Yr(m>qll#Ax^+ zGm+4f%f2?{H(dRv?W(+4sKGXP8@JWXj)yCLBc>U;P^`bL-hqA~1##$_PiyHTp=c^FaU6RZVFXt;Zi$9-=@N7j5=#y%X20~MF zQ<)Uf`R>%3Kzrgkv`JC}$JQG(;U{*y^9qx2u02JB4tA#CgVxYp%x|wt*g=&9)5~w- z_o8w+IP8bvg?dACu)XqGAn?%pudIq!wVEOF$g@&-I(V(Vl8WWJA2ebtzY#M*NIB8q zdpi#c)tYIb=sR%bzUEv%V62*vl<=sA1Zgc!vH9#j#3#PxQR4c{vii_^)*>H2f(WKZ zIrNS-s_v{uNj&k4REHPxs@9Qbh>HSMl-AaIb(r<~?)-t?(kYG9lsEC`e3=4)m zFf*W=WOV~zv%f5e0m6-B)97|BqIr5YL05@2*cru)~3fq z3f(Mj96B&--o=*m;oD0jk|04fT2PzCal9ho(L2GOWVbIaMIC9NeKk|Pg;o;YrgCg_RdEgv{z~wpajtmE_~18O`{bu54+(OI^Qkr`s&*~NLAWXA!H!1hFW|Zrevh~nHR`-g zwyyyH(_-Z`_HLb8Y_4-k<;I^P)z8nTK(h%4SeU#VYMnB^fWIy*nNkwtusAyf{gfcn4BZXb^8Vtvsm|5a6(Ganw zIl(hlt6VqB&FY9iB5O0uPlJzk@Zn#_V;`5M==bk!^A+@;YVL0>M*e$i{2$xqzt4Tc z|3O)9j1$tObAG1jYZ$}OuZgPzLohIx03as*kl+tQ@lN-U!JX`q*y-}uw@Pl&KMQH5 zYH5yaT3&9+OF%DQzOL4}GS%w5dGGn{qch!pGnFuzIVQY&F*@39cg-{5H_h`6v^)&? z;d(&#adIm~M7x8*-5l=eaCdEldI6G;Q1ERJK6rWDZU121_4#<<#oflsYlZ`!4O8m! z>V?C5EM)l!w>QY0bv(@E+jJKT_OW_C%KGI!8yoWjcR&MA6iEGNJ)`x&Aa~TmZ5Z4M zpn5>;ZLHYDEjsV`Vr*kW?A`jP$15;g?%|#iuQ%sUOz#j0KCKX=S8O=B*V{Kn>|wOD z<4A7r@KCykTNK=!$GuAH84A3fPK4G;VPz(2n#~GP!gGFMB>8n1A*OG~ zQ|_8!GKD8%=)wGR+cV3Jl@1>@H$AOb{yL{#Px)#&bz9Tr3jHHd7sEE zPNR`sQUE{oNb|r75kb{9r{-w${2&x2FP4Wq-1)Fl<{b2}b5sI&^;wJvk#O!R*m8tw z-U^Kp+=vx7ixgAD@)_c&4hHhN+3Y%&6ng7<#dW+GbHxfZxJC_*pqDd+u{}64>7{w2 z5Ppe7+f&s>emB?+GOc(d)+wI#JXu}du5wE@0gLEhi1A6;t=SHwIe@9lMjzfzCR9bD zP8Fq-o#}!V%=xpl<+jKf?k?-r-!fvgefI&o5Sz{f)y*Oz%0_iPsjTGOMV}#<$;+l! z^JkWda5j3lldEzg#fLAM<>WMOh53z*gA)4Ho$zi*t;AJ96ovI_c+ARQ1a-c@5i=?> zCB!qSj7HX<{XaE-Wgtq2mk%Q$SQYeQ>-uvb2_4_sGg?Rd{%!7Mrle=Ys?|uvZ1~F# z6`{^xii40?!=R+;msR;45MX2pJdu2QE+K@uoi5RX6PmF@xh-}S&a5UO0%cITp_WlR z_*sD`!?)6t2wy!)W3~e?ks548y{G6ir1q8Min3A|4m8s&E8=`eI@$Fg8%T`YRq9z~ zy0zKDBCN6XhGSdxV0QM!9W7p{v9gXX&UH6UybDB<9V+WSML$tJ=KAxB6Ra0#!q{7HSqFy?9I> z)>;<*PBW_3#6-bDoX>Rtx#}iOmv-j4gbH_)%P)2}L{d zz`4~_H{WF}RV+K8^iBsP^G+LNXYl3i1P=KFp3ks_7lfuQq`2V|m&c4WlxnvAj%%`eg+LX}TsPV8?KY?p{vrDh%b>b4IS`LnTV9 zF$oySf%FapMiy7)NQCVQjRsl8)+YSb_}D4C;Y$)v4Uk5Na6=7%8D9TrT6;V4A;9a5 zLTzz!Ky8`nKc)V0I`^l|+U!mp(x^^*xjDzi1i7hLXw78`OGPiT?~P^6?KyouGn%T` zYFObkZk;b)8U~-@hc~b~jwyUGnKkic(hE^rJ zO(GZRgFQv)y=pDtQuK*13F;ttCl3Z-^;uUOIF!m$n6|JSx6eOHsD!6+9Bc5utmY() z&^{Fv0thqER8|&qgdF~$OPaom?)m*FE(DJ%utpw7r{xeL#&upm&%;pn4^|JqdTd@b z9fiFFK13TtxUU537M?o)jqM_bU{5!7cg`?bR>KjQzSQipEyhsUYyMo^4QKRBPRkW} z|7aN8&3-~1F&~*)N-pAg$ML-XZO>SOc2bPWV}X=1AZG*wpb@QDMWD1i-|KQ%O65Sb zJU9euq1g<>Vxq}yqPfWs-Ro+=*3N%Ra*W=NHSB1o`y@n;H|(8Znll*ib)LbX%@7le zTU0+8cWp)5YCWRODYwiudMtaE9qM~YuoiG5zHWrW^X5W*zx!B3v?0maa+K^>c(_XS zbm{asBo0TEbyHI6iX9nQR8Rmd!X-X89krU4tH`{gFr>$(aOy$Nzawc$B22{^RtKEV zfj>|)LK}~t5@VE31|`I2u$nlyil!e|RLb%&E77=9e0%_~9}o`mIyJWxQd1=QI9P=r0~3v|r66%)R6wcf*MLGz%`a zE-g-Wg>T>-2gPV}?Y{Fvt9tu+y~$rnZWqQ=iNeF0Y`H=*z0tnV%{q@NWV1SKF)Dy$ z^+~+vAh=>eg{`WjOWL)Q6-&65}fBzCc?E5YbI0dTPuM*wb_@Q zi+5!q#Rz(J<@VH*ruQ{`@B6WEqMl$yr~eOaUl|-{mMv*ZvY2Htqs0ssGcz-@#mtgL zC1z%3W|k~wW=4ydSzFybZ{E&EOz+#>w?C>P>R(kwetGkpdrl_Eg^{YV(J!ijG&N?1 zE=(IceOk)!r~EP#S^Y7W?F!nl+)pMFHBALXRw?0R*{3+Ha(RyP{^Z=mxLIi3%g@df z_tp|{)~#H%<`%hbpU1~9UlIOjmhgLK0=HN{eagrEJJqN3Uul*o{t;^HWch#6HEB>$ zSDlqZd@ro%15^(c?(`VUHxhhtK|?Cf*6S`PIKFyBobe!tCQGLd{g9YM(!#r4*WIR* zKPW8sOW3`h8XL>tWZGnl^O&XE;Y%ukQq}|MIXDjU5NJL$!a`hV^_&-QFx)kCgY8#B^gwhHpR6r7QQvPVmx*r8 z{|qV3J~&m8d!9P8p%yOg9FPdy3YbNzr&vX^UYOyQy{v@riR7x${cJ6{YAsi#!sfQLOp5ayRUtn*!8fOY!xC1MowUP0AQ z_AkP4Q4HeAZ%q!Q%Qc+r#9!obMH0k-?r$JbbaIIU&s3EBdoYUktb(AettV;%`^XKP}Py8hB}Cy zk3Ida$}s7o(qkueQ%B;A*z(vFJOOP&v8N=_13V@SKGhSYadm0LTacBH~lt<8zTi#D@<@DBIgewaLC4P z%ZcBOETS}~e=O$Qb!NU5T=bY$&W|Jmngyu$VgVysGJ*y0^$O@5hp@=;eE1ptPK`lJ zc!nOSXI7d0aG4btq96zCWqTzx<&PwjBp<1Kn=NWW{24ty-^BPssYX@;GU47k#@chW z`E<#dzM~F^xH=};#*!L#v-kP@>C57dcT=DYY@qc2Z39K?zwNP>1I~}z7?~UXYe%I) zWnB^IW%7|>6qjJkA0B`O6P&7n-1l=v;n$=>Mc>Z_zS4#j9cLSThGk>cwuK}f=jXMQ z`-Ujz<1{AHH`n5eciDu@HYj|if|@)*6Z;GH2hX<&_6I}V_SYM_Pbg|9d~{^6`B#~_ z8*KC;2wz!@^{|-0CwBdEX|b9EMl%k3Q9<(_<)BOwwunJh?WE=L%eLJ?#KzOUgSreSqHc*~bQ=XMY6`+D=>L$Ju4=Pl~9pElP z(IUTYvt8DqpBgwYz@}4rI8{tNEQ{+;mkg7_v&WSo)~in@2KbW>Ccx-hsXvE}Bt}!Z zB^Ij$T#$D*6qm$9y|odphf?mUY3f7mr=LjpG*TWr4X?Q9@K#v1@s&D{H_}%}qY&YE z2oD4_q!w1~i>XeHr&)CK3 z#dl0K?Xif?6F>MUaOXv*xnjSOfk(4lp+z1x8}Fd3vBq@E;~Jxcsm;hUT$Ri;`?qoM zFxeqwr45xNytwM^zzBOm-uGpp;y#4m2L-z`tSOIeggI3)sJJ z%(xR^y>Y|7Yl^$cbkeebm~8HGaKXP6qS0d({m?8u|C|X`1XyKXb`q#}->MKDj9f)( z`dukBajr^q!)O*KMq;eTvdz2p*+Is4*?{b;gAabRwo5Y(RcA3nYxg~tXMf;HBczc7 zlPZ1Rq%KyUjW!O2ljx;GqQWBR)NGzL!oJ}v{x1RxT*FduIPpxTrx-Z7cecLkA~wWH zQ{+MHDb{FLv=mt3NM=0UT+p;bvMorY$QubBjma&rjgXB=sjuJB@kn7(?3@n-cYXQJ0+?KkR|K!sHn?hwpy z+cyJSTf*Eq^pRPWjM+z|BKa+iNCvhqXEt}^jHOogH-;SUDd0yKn?@N%VF&h64ppoz z{F;=dEv9)_IJNR|hV4Ox?5^PjNw(m)AAizMagLSj9gu$Pfy(0F-x&V)sfz!Fc9bRm z(a0@VXC(myK|?|!#a%)YgmMXy`hk3=5%5hcGG;PHi`D*7$NwH5Im@jp`>@zC%Vg8p<@rSq(Lv|YR$c_IM<8@4I9F1H%{^d_iWt!O z<8hiU4-OQhndB!wW|Ez7Pjo;TG60ige9T0dxL+PoQI7vZm~Mjpk=k%v!u%U4H#Dxrx{m#f|ZrFwx_X4Q;F5YZryLj zoP}G*GgX7c@0KMEjUfe>U6Zl{_Yq)w^yF#lsSx&M07EG(G)YvFM0YozCRWG|7!`cC zAIz4*25;KIWJ%gJa=N&56ifh-qghkg$Y!8UR(yk6uO|(UMy8{L%^8tLzoTvvAf>wA zlUylnWG$-XOcBo}WjEiaiI6=aN*d~#Iz<&$Mii>>ri__9b(yWQ%CZE!_K zM$(76)|2n&U|=45Jp)2Fndv1ZXYp%@_K_qU;oiz1l!=j zi9Vb7)ZFa!IF(+qRa$t3(;$Y=GTfV&elGUA!;VRImHN499Aeus*HLhFcb4vv>pM7) zjBtqb?tAeeLnt(5-FptR>+v$%TFPk_+VpYi->bMeC-Cu z-e1BI0&pN;Ai2hejLy@lLGA+BVyUF-(sjHm4RB~);Hvc=*yl8Zm&_3?@UQSX?cbh2 zC!!a&zF+N9r+yYiCe+%C<*3+uv>Lv$zol4maE1OZA72U`jZD(2yG0_X`g!A}OL{L? zN&DwDScSuj-+EOOxfJ&o>>NbvATzpB^H)gBEXXIRC3ueNFg(w0#Ln2?@Y--~$%I}| znr)KhB6(R)SU*RL>;OUf#Ty zO1Vu{*VUN1J39=ton!r~&5xx4 zRZr1jy*gYIcPML5PWUo|Y>!vsw4FzJoioy{h({JT*huTqd)Tes1j67l#x#5+~BkuI9#Jxm6Q`!~T&i z4F#T{&9BU7jS!^VK~pT0_d`i9#A9x`v{D!(wTlxCUNi3-q`2d#c7-OZ_EMvhrs_qZ zweRsnSb}~5gNa$Uh!4qW)DefYnNqlz*bQ4_s@Trs~BAA^{f;judRJ3k8| zOsTIlaVf8f&@G-a{=zGz^hDTzb0D*SG2)es4~ftX$VfJ_61HGzj>Ws#F50}D6Bgx> zkO~X}B(*emH4QS1rr^!to17d=fjcOJHbcGGcN)GbG)B|$umLw~xEJv>qJtJqTm`uC zDZu;|kFQ@}v%c~-6%ZJJ(zdWX8Pl5bk*Du4YMmW z6*evjlP(ni+-|xd)8$1mvGIl3DSwKN=1XBB2@gyZxZ6}tOhwyZ3FMi%WolO zk#)|ts*fLMnLVswH|cX^jTM3ph^K!N*Qdns#et>_lp^N{whuRgQIZ0cvEIys3qS=R zHrv9Znw4nFnt__D&}1f?Ols(K#huJxba#Ix&pcdy0qksOv)s)kZgiTcoRlVZI?>Kr ztvl5mv!yP|=9RN}B#v1yv6G!gRt%U}1=L2T7`hbdEU7a!j){va`W0!yCvzHos}I{p zUc4^eY3^BNF`QCbCfkmH9|-3&qBCAO{SmqI>ME*gMEAv^0lOfsCo4etn^R-H!cRLd zjru{jYX1tvGv&Kzk?p=TG2dF%>2TMmmJ}rw*)opd7+CkvK5|d5G?CY1#K{PW9zj5Z zGmr0)7F7mR2I>ulgrh9@`?>|C2fyS6(i7WcW~?}lo}z@PI3Qi7LQ6Yhz;B>W%WAT~ zn26cep-{<$2sAYs>4SyBRCe&bFJ-beoktc1LG-@3qMP8Q+`?0n1cStCNIzAv*#(}? zJ$_bu1k(iP+r5RaqGF^)$*9qC%KrRp+xS?2VaQa;LQqS3%77Hpuy7;hMJh~#$q2Dv z!)_*WVYnJ+Ls4@=j8NpS7Kd6-6If@Q^&e9CsZ{jWKI+JdsJcWZU@E4F;V49v1j`-Z zu`18OQ47i?v_4P=$%}^M@7$X4gg5oXP~ylsz9$D9KcYwy#7avrGmO!SWK!$9U%qzf zMu?Q9rlx{Tl<|aOi;p`d^_RyKf^A%dFY-dTUP+qZ90s(2gz6^k@>!&69asDg%5eC3 z+_~tk`O5!da&i*VcWBVAz6U8n3_|hVV;CHyIksH=rC$i)|i@htircO z^x|KKFc4vkYs7GfcX4*epoL?oL;DSUpWrusT>B^wv;eJRet?!aExLeVzP&o-l>RxS^JyILat-H(ikMVrYoef4NM{hK-!p2n+= z*!_Bt2B1c1h))}1M}dCPXUs@D*k;rYp*qwY8&-CygLyFs(cByvhD{<*7-qclov}oX zc2{PNbl33lJ9^W|<5!;X{xe5>$-O2OZPj}_0n;G~b)^v0S~c;VeoA>7IUZxOc;SfR z1UvkDnDVhxrnFxaqL#H#)|O|VK;W33w}`5l33D@U3h}TRU6Z{Ekkk?svPpx$n?)+c z+E8Y>hgq!O`zqqjKZis*=kkfn93^JM!j-%d7Ib$c_3=KEI4v*x)y{#?oGvP}YFm7_ z1^h^6>w@a3hE)9^2aeT44&^dSIOUIqOX@efLYMLMhjss8UzM z1yp+U(_WuWO7G%w3gIAIXM6V}8eui-&Cz*80dvO>M;Letj&;AbC=&-s7cBNtT!+?G z?%_q#wA=bYZ4eqMr%+-}kp@X6CXAIMUq4^oRFx)&-}C9!RGF7fqgspKfznx)h zQ&zCn7Ab+J)YqL_7*rZOQGK=s#`~8ggS37lh@3fd?^)wkpS{@SR2yi6p_-hg3*wiC zJGe~I>S|t(>D){RyD@TTgk17zbxS8&p5Sz$gPMt%)mTqTnPTqg0bJ<^-~5J5~qbnvAC# zHRau~l2U5@s&GFpH!vOnJ^^}G1+R`J1seDAl#eInN9KVc{H_^dGESZT^+IsSkL6|4 zJZK)m73hi#a^nVDyGfQ!*rI5?D>t^gX@=Q-CUJ$lU+Zxs+NrUl2}dN%q>{~y1z;Ro zD=UuC^Gnu@lEr2igIAd(nD~yy49n^en5uCd0m}wyrn_Eq_i~dgw?(sc5s2A$hO3Fl z0@Hh+FbAR3#i#8+E8Zf=OlnwWyQxWOad1P#wlXa;#2%wVJToG22#xl&SU)d2cRbsb z56k#b?2R# zb6)xptoy41PgjaY;!MBu*m{6wx=WLsuE86PUlude#t)SC5ZD+=CuL~A6Z>R+@yHZ9 z*o@ENiqb4HLoTMEfjeYmhmZ8}clhU7 z(v`><(^GE8Uc&lY&C@~KW3(R?=!d!a_59IUbVUQ^AW=C^8ATgo1f+OOftF)E_naG^ zCGR{AsC0}a)y<~jH3xG32Q~0?Q^UK$rh^sT0!(qX`>R{Ut=%e4Jdxjyvzn3u&?m5b zD!4Zkrm*h5U^z9JkCZRUlWgNbJ#+$;8&h?`F5q9zEZ5nbD9uLg&qNny+NDaiP~*4e znU8+0LBX_t?S5Frh`5jZWs%9`W=QF51P;q8r z#PV7&9yRt`7%W*F8_y|F+DPYtJO$Zu;GVIqBj8v0*@mb|3F0yI^GLjqu5x2j9X(IW zaI+|72|JO_XAC|6X(N$?)?aY~^Xbzl&}H-A7w+Q!mAXR7%-&evNJQTdI6vw3FO!r1 zTCG$qz8C{*b07lXgvJGVNr)8F$%Ff4aIkVf*w@o%j>*1%SXMALAp zhPtD?&mIeQPr1Cfxgz%$B0}?~L2@PQ6Sj3@%gu-jJRGmYw=D@78q=3;6O^kKpW4NPx8k68WrDy;^F3@A5j z6ZN)Y6>7<<4(vKZOE-jswO9?_yz#xyixT~6&Co*Nd{QlC!D&`fJohweERRQ$Mq_UW z8X5sFV*~kDso@#Wl6Xt-=VRyyae<@lVGofLjB#;d9}p8yKZEjg0%x2rGI1Hfjf>I0 zIG(VZfu4X!;<5V{e0#%GUvu*T$4Fg8>LON{_}x6j-k~si{hfLN+X(s29n*>=JOtXf z0S8_9C~+d`;5Ge3(qR~+FY!|}JxvTjjEV9&!B)rnjI1>%M{`Fc6o*IY1vvudrO9zn zW15nxu#i`-uU|VkBUQ;=hM4^Zzk?|Eh&6RIF67l@YzIk(b76 z)xztQQ$=W|$b6$e-4*yLWBGy$7W2uMH`1nAsI}5HFRQ)yzn)7%QHuKf^vTaV7K zuHR1gZgIFUGRZXEpI963`gprT_?h-d7)g@YS4+KJ=|?WXs1oq1MLr5GQcW3&T2oGu z-&V9mDY3IiM^ZkDp<3-LU9*NhLW9ZHw=Ic@38hQX9B}qI|FXdk;!&ihiO5L2M-VEC z#B-2dtM(*wwYvH2>e#)vu>m)ZshD(i-+FrP*h#0z&{*H;O5X0w+t|S_gHU^8)_d+6 zZj%0{)$jyvk@TZ|pFJ_>0NqpG`E&)xU+Xtnl{sEN4$Q;Knp2C4_Kf4CT~=&R{%pz+ zJ;`XRVT`%*>bz8DQ}Y3qJ9pOz_c^p6&?Uh+f)LeO%@NC4uQa+EmJ@Q7*JcUel9uBs zf;Y*;5eGKxq{CF~DSN=E30sq^Sinpd50|EjwE*wJ=5IhY5?Ps|nQ9U2Ac=Y73|DGN z$mRJ^kxm_b+9-!P;6XHac{y*41;)tImbjUXe;E6=W;K4z#C=C4xVA3~LU5c86!&&* zBTd$T2?+?ZB{QGXfj09xw%IiRf~hrfgrsdw{BEfZt6+G-jt4f zgT4T4kI2FO@wojj^q&epHNy8jN3l#39p-Dm?X4O z){--7v9uEX_AA|w6lE_G=#30@v6BvrEd6eex|v7TZmJw^5Uzhn;uoNufX6M^kB3)= zfvzQ`2HSr#Z$^DOj6BpB`f%k7CfeUAw&QM7^|g2I36d*mb!7r?ksDw^hfjYjJ6*+k zXG)99C|aac!HHC1y>v?rO$+1fl;ZJzS2cx_qT?2mcFOJ>YC`U{5=|}Qk?aQ0By_~T zjr~k`tDfs}Q~)FW)KAxrH-^?Lbup3Qrp5Dc8^|NWY)ouAo8L4!vo^>KEB?$l3!59o zQ^SmP8z>treo4yGMrVHSnQ}GxW|Y+rRuShobBIIwY+$f?y=OA+EA)nT&y0Pax*SvpRti!Zwc~Ag15bU7;j*1BW9TiC%|7R$Q@}G$S@8FPeqqa11 z`~kV70csKG@fve}T9iWtU%(oaIZ04(#7*c^^v>SAbG&YIfL>S@S*=k(*L>Y^R&SQ< z(MCHDwXG_1B@U-YGj>o;w6K3`wY4do>NV>NwTuNxj_pT$t0yQAy#r@W@b`Qj87mxo>{J3$*cNf#kSC0F=$&LnE$Br-?u!f|GT zEY?|~E5|fkXu;OrgfR<4Hxm;RxdA0JHI@6=!8c#KI(UW6OL21w<;&pMWorWUo;~0< ziR0iz>TqAZRYhdJf=DDmPJ7-#x-iq|YIk_%w=^w9L=U20!IEGmVwNVBEi>vH%knXy zO-0h9juVu{k#}?y#h49>%**sv8UvPuZ3cU2cL}hvsYPe`nd&nRg{N=5SOxt z+cmXTLs|RohSYfn#|AQ1QqKNdQ25;psYxtx;;cmn7uO%Plj(tRKfdSbSYhy5to8T>TH$R$7*utq_F&yMTG{rN)QWqsztO=^4c0K{_f*mb*#;OkM>7 z=&lj~cGD|;i_jdDY;cMP&+*e0^L=Y0(!|3cPAUsY0ZBpFcK|*~O!JOtyH_jK@{eJ~ zX&t*?%YyeZ;vH|D5_<0*a#^$u;VwB89c|t1ObHdWv&9r5ikfP&ZW*GVt!93;W-^JC z&-tB#)|8aIQ!)PB$nYeT6dhTR--r%7{l21xfuE6vtGOkV=p*8+6vXAz9toTR8KlQVgiy)n>M3-Z9S;W%?Et*_V5MVf2Pihl8GgBf>tG$Qrm(;pZ1mmj=PWDI_nbq}wEIDVS925Y#oMVjQoGxcM@Sw=Or zqL7&=jEW`|Xn5@Vu6bCype;mY{CRm2kQfvf47B@4GCk%U8i?}_GbnrA=+j2* zO-H=2d98#F*kpZe5QbBH1#)LguxZd6SmwzQD>$f?FT|3=gQ(V_U5n;GHl_Qz)eoez zP5ocoWFqshfV=J?S%eR?gEH*{*;bPv^#`PdZ5)l1 z8`^!gfusX4vcny!?~@leWHkX4TEBTB)Y+ zb27=w2NO?E)V$+-KG^V#{z3NkGYz>Z)u(AZ)x4w=r29#6fJ2D!hk4L_LTA=d-{{xa!1-4v5#m55=ur- ziU|V_&wOXwR(v*M7B}Dtb8#%5cc>NWUoC>JXZ@4^!-qJ$}y@BCytr0rAJ+suMJlpAZOaCriqKRNYfGDZ3lvq z>RPaR#tPA`X8qYy0`39@_fLapne11&3s80qJC?JSN2rP>$pQkN1e?^Ro|^dyl=hV5 z<0gw2A)S~% z{LjfvQ}iO>WZ;l`Mtb0}Qn1v2-d2b6VnP}LzmPrf_3x{Of9stHJgR|TncorkZs6qj zkA1+1_!E^sV|)Z@9gqR^;XhHY6bh2FF{*_i10;WDn|(Ur`|(4QBLGRjp@uMLLFe>s zhxiuhRmo7Z?es@dmRA6e0Ddko$p=R}+w&*F%g0B?D9_8-^4_wK>^nGpz{_XX8mvAD zTSu+w8niy)z3=5W;Z*e0wp3!y{%(o{w9%$*r1X@RgsXnPlKEn+#0d!HOguF8w6=&F zQGKC&ts0em#eK0>aeXO#qn0<2#GpNU?kj)Q|0=TNMM*|_Og}>hsRMaf@}`JJehfWZ z1aSb_tbS7&biv|+8OFwPpKY;cNTsUcOq1!3KwJMonzb59D<-W%Q8{0Fo(z5=K-n_X zwombV_Ir6*32>=l`E%Dc+^`)<)F^xwFO;M+r68f-81A`e>-cWZK2%$Zy@vjA4g3-8 zcZX6;S+|4@JEu(jRy=Di>%78Bw#03w6k|thElYZf+tl$Jr%cYiwMB3MF~r7RMvQQM z;SH%vjf$27)#!wVuZUxc)lni~`mzjF1U_|BBFRC9a98O%UFG+EVmfg1KZfpi846a< zOFDhmWX>(d#<59eB}VlPHffPYnOth*+i_mOGeFa7t&$sO`GPo1s4|=KGQe$cs8YE^ zp(6SF63sVq)hXIVN_H38$p;G`^L{rO3%$&byh@BmJYKUycCjCzl8@6rBeGz%dW=i! z4%hT`Sm|Mp`E911B*1r0x%;`6L$=EBS! zcz{j>7xwkdenR#n$yT}oUmy+#PGh5!cgq8suF7Vm?wux7#1OUeW|>)5TPTJ}3lP)^ zRdL9<`iAk|`M#OW>ls5VRL6&I%pPjuII5MwJcy~f=e49AtIlY@xav3qXQb>UufVgs z_&{}=B*|G*#+)Vb?GoOamDK{lEZ!#T31y?4luKoc!7plxdiqj>23ZhU(<_mpMmqO* z6TG-((o&9Q1~P)Ar?i?ViL*G@kLL>O;Xx97Hm8$-y|`Fiwm>I|Yx7}llV9$F_EmdK zCR5Ookz(geX%%&`lj7%Wa3}41#e1LY`my3Y`LEMVhOlCBm^43fkz{elofkAkhbEqn z&FbpjIabcfbq4i0sg4h8>&W16iF0b=NSIwyN#sQ7pc-TY@>?PuNJMbuV-q=OqXUkE zm!q<|J*U-X>3=8L6fAJaNYBX1W@Njxgrd1eAU2xq(^j-ZvA#;dy?zS11UqvDz`qKf zUD9=!#<{<{TxHHZd_ep`tPPOGZ8E@uLiKM)`o8@=u>zyz{#jRKC~GSIF?=hdsYzZk zB`_U|9$jjsJR&XEZGi-gBbuAW+>n{|hszh2RuFlQL*STr{N>O4>xzy2TpYy8y!=qm z`Qdc=Vl;D|dpJrrX!A3*0hMrRG)Np#fB;St1F^7t=)@s=@q zZIy8mc@~TI2`70=$iW$-kaD_@9WjR}WY1}tuR^`gB--%?4olGnBeUbO_=lAVV`g|BHyg9|| zD|Vm|O0%}jCQGpX&SgK^YG7-?UFKQ|E*dS6^3)LofSX}KQ^nyd71sGtX`O9_0H zuX&;g5l~_I^^>1ZT&GS}?mV>DQA<$r`oM08(9DrEwka)u#;cQcLV*zrO2C!`oB;VM(a1P6fTB)Immt;wZ01RyRe;S zr~n1zhlGe2lZgB#D%2Pn_9SdC)7EvHmY9?bjrW3y;4sUu)8+xUFyWH$@_ zSiRL*#!tIgr?JznGKBz;(I_82+dKnZxQX!VnWe0?(Q`YW5(D>HvVHVEgn2T_z ze2;%-N+Ar74sSgNizPv&1EUCU1vB)$l%wNX{v!KwdBx}2jGlS!mjxWc%EONREV5&t z?3v0U5J-A#vW-oY1)@&Y0U6cbKpWeyb%IPwEEKNlhdG&UB<0Ld_(CrO&LkqsNY;v> z=!J1~{}?WrFBn^N&}WxqwYKWSzNHcC&-0@>)I7^@B%-JgN5S_B6;yXk{U{%r_{6VD ze^#4`Yf#cs4r}rECJNG8b~4WOt?R+3Td-K-271zEb5AC=oU!jM@zP!r*1`XfDv*URuxsLykvDok zeT~wRQK(GDpDEu$;JPT|p4CM+d6iYXmSvFfOnMge2R?KjB`n~{w>*I&JYcV?%>a<% zxqacTTtxgvAT7RaB{s=c(2v@gl``LhW?X`6FMAirDD^RVk3=MoE^Ypdn?zmIIunGe z&!&WMxf0o8iq%MwHc)qlYyDI->>^gT!29Tg>b-u{jOIhFR4FPOOGAlXEEkn;%$XT2 z?Y?f0g+<9>y&D+`J8^;`q9KY##X)PBs$ZKW4(H;e)y*B4kv)M^TnL-8p+z4rn*j^> zJq?XgiocZT_I{zp}!2a2M03Ck7NjF4_eY6(4&jl=;0C9 zr)FCo)l2rz(`9>*n)}dXzW!<0@`%u>UHe8UF*(|FBxJcFHq!fTSD| z#;AqM|4Duf8G;x=qKIPp$oFc$z_L!LEx7`}i%-PBAcE3;2(fGG6Mx06Eu753x^kViCq=DG{VNCIXHln@X|n?N}WkFo!dB(c;(LF z+RA=q^otxuoId^7y^&Q)kJ!qZZH1bB`+0)v>@S4%$(r7>K zh$52_{t2{k@?+MWp-?TQRN4y-5YnqN5sB5uwJ>8Nw3`R1{Z@(|I;$*>U0LBl%oe7B zU9>22P8yZgTxg7&f<;GY`6x=|qK($Ids} zb;*N1FKGt6AahH+Eh`#h)@D5EsU33?!j8T*(eyeZNGMGVTp(g%1)|)QAy2SQKJ=%r z|C;uDEobx@`yvX_YrB1cMtQCmE0a%Bd>U+2SL+Cbv?#}z!R;QskPY4R4-_OJ^NIL< z_-=`tpx}5O;%6YFVP}&G2yA~MVTpQlbbQ;#+%g z5HrI_hLE&ANAgWFR6ZL+7{5k@LZi}30m`-MRHs6HSB%&j$RFxQ=aIq89SCWf{}xCy z{R7fU+BP#l_b_2@l>!>3_iuE} zc1-U2?V7dNA2C=rB!>#diNw}U0UQ3jKP*0tY#}TS`Sk21Y1nL
= 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 z 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 Date: Thu, 17 Feb 2022 13:54:54 +0000 Subject: [PATCH 0006/3231] Bump spock-core from 2.0-groovy-3.0 to 2.1-groovy-3.0 in /modules/spock Bumps spock-core from 2.0-groovy-3.0 to 2.1-groovy-3.0. --- updated-dependencies: - dependency-name: org.spockframework:spock-core dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- modules/spock/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle index 26158b6db57..e745bc23ed6 100644 --- a/modules/spock/build.gradle +++ b/modules/spock/build.gradle @@ -6,7 +6,7 @@ description = "Testcontainers :: Spock-Extension" dependencies { api project(':testcontainers') - api 'org.spockframework:spock-core:2.0-groovy-3.0' + api 'org.spockframework:spock-core:2.1-groovy-3.0' testImplementation project(':selenium') testImplementation project(':mysql') From 40f3e3f0125e1307a6f0af844e36f4a8c783b55a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 17 Feb 2022 13:54:55 +0000 Subject: [PATCH 0007/3231] Bump postgresql from 42.3.1 to 42.3.3 in /modules/postgresql Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.3.1 to 42.3.3. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.3.1...REL42.3.3) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/postgresql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/postgresql/build.gradle b/modules/postgresql/build.gradle index a8a6495bfc1..754d5dde289 100644 --- a/modules/postgresql/build.gradle +++ b/modules/postgresql/build.gradle @@ -11,7 +11,7 @@ dependencies { testImplementation project(':jdbc-test') testImplementation project(':test-support') - testImplementation 'org.postgresql:postgresql:42.3.1' + testImplementation 'org.postgresql:postgresql:42.3.3' testImplementation testFixtures(project(':r2dbc')) testImplementation 'io.r2dbc:r2dbc-postgresql:0.8.11.RELEASE' From b5575455cba038b579289c18d966921835e90676 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 17 Feb 2022 13:54:55 +0000 Subject: [PATCH 0008/3231] Bump postgresql from 42.3.2 to 42.3.3 in /modules/cockroachdb Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.3.2 to 42.3.3. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.3.2...REL42.3.3) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/cockroachdb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/cockroachdb/build.gradle b/modules/cockroachdb/build.gradle index 71011c6534b..4d15e4df1c1 100644 --- a/modules/cockroachdb/build.gradle +++ b/modules/cockroachdb/build.gradle @@ -4,5 +4,5 @@ dependencies { api project(':jdbc') testImplementation project(':jdbc-test') - testImplementation 'org.postgresql:postgresql:42.3.2' + testImplementation 'org.postgresql:postgresql:42.3.3' } From 08d1c9c180f7fd38d0c0e3d67dd90ec36c10f81a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 17 Feb 2022 13:54:55 +0000 Subject: [PATCH 0009/3231] Bump postgresql from 42.3.2 to 42.3.3 in /modules/junit-jupiter Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.3.2 to 42.3.3. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.3.2...REL42.3.3) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index ba0083a25a7..749bf72690d 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -15,7 +15,7 @@ dependencies { testImplementation 'org.assertj:assertj-core:3.22.0' testImplementation 'org.junit.jupiter:junit-jupiter-params:5.8.2' - testRuntimeOnly 'org.postgresql:postgresql:42.3.2' + testRuntimeOnly 'org.postgresql:postgresql:42.3.3' testRuntimeOnly 'mysql:mysql-connector-java:8.0.27' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.2' } From bb76a74e71b5a102cc1082b654b6a27a23872a87 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 17 Feb 2022 13:54:55 +0000 Subject: [PATCH 0010/3231] Bump hivemq-extension-sdk from 4.7.3 to 4.7.4 in /modules/hivemq Bumps [hivemq-extension-sdk](https://github.com/hivemq/hivemq-extension-sdk) from 4.7.3 to 4.7.4. - [Release notes](https://github.com/hivemq/hivemq-extension-sdk/releases) - [Commits](https://github.com/hivemq/hivemq-extension-sdk/compare/4.7.3...4.7.4) --- updated-dependencies: - dependency-name: com.hivemq:hivemq-extension-sdk dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/hivemq/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/hivemq/build.gradle b/modules/hivemq/build.gradle index 779ac2ec9d8..f0c3aaa2e69 100644 --- a/modules/hivemq/build.gradle +++ b/modules/hivemq/build.gradle @@ -13,7 +13,7 @@ dependencies { testImplementation("org.junit.jupiter:junit-jupiter-api:5.8.2") testImplementation(project(":junit-jupiter")) - testImplementation("com.hivemq:hivemq-extension-sdk:4.7.3") + testImplementation("com.hivemq:hivemq-extension-sdk:4.7.4") testImplementation("com.hivemq:hivemq-mqtt-client:1.3.0") testImplementation("org.apache.httpcomponents:httpclient:4.5.13") testImplementation("ch.qos.logback:logback-classic:1.2.10") From 80e64855928f4a70b95fbfbc68377700aedafe53 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 17 Feb 2022 13:54:55 +0000 Subject: [PATCH 0011/3231] Bump mongodb-driver-sync from 4.4.1 to 4.5.0 in /modules/mongodb Bumps [mongodb-driver-sync](https://github.com/mongodb/mongo-java-driver) from 4.4.1 to 4.5.0. - [Release notes](https://github.com/mongodb/mongo-java-driver/releases) - [Commits](https://github.com/mongodb/mongo-java-driver/compare/r4.4.1...r4.5.0) --- updated-dependencies: - dependency-name: org.mongodb:mongodb-driver-sync dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/mongodb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mongodb/build.gradle b/modules/mongodb/build.gradle index 95919c931d4..73dba45dbbf 100644 --- a/modules/mongodb/build.gradle +++ b/modules/mongodb/build.gradle @@ -3,5 +3,5 @@ description = "Testcontainers :: MongoDB" dependencies { api project(':testcontainers') - testImplementation("org.mongodb:mongodb-driver-sync:4.4.1") + testImplementation("org.mongodb:mongodb-driver-sync:4.5.0") } From 30b09ba593698c1a8934bf0b6068a29839efebdf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 17 Feb 2022 13:54:56 +0000 Subject: [PATCH 0012/3231] Bump rest-assured from 4.4.0 to 4.5.1 in /modules/vault Bumps [rest-assured](https://github.com/rest-assured/rest-assured) from 4.4.0 to 4.5.1. - [Release notes](https://github.com/rest-assured/rest-assured/releases) - [Changelog](https://github.com/rest-assured/rest-assured/blob/master/changelog.txt) - [Commits](https://github.com/rest-assured/rest-assured/compare/rest-assured-4.4.0...rest-assured-4.5.1) --- updated-dependencies: - dependency-name: io.rest-assured:rest-assured dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/vault/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/vault/build.gradle b/modules/vault/build.gradle index 8aa0a2d689e..ce000685a10 100644 --- a/modules/vault/build.gradle +++ b/modules/vault/build.gradle @@ -4,7 +4,7 @@ dependencies { api project(':testcontainers') testImplementation 'com.bettercloud:vault-java-driver:5.1.0' - testImplementation 'io.rest-assured:rest-assured:4.4.0' + testImplementation 'io.rest-assured:rest-assured:4.5.1' testImplementation 'org.assertj:assertj-core:3.22.0' } From 232771603089399249f4436cbac505feb3232cb7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 17 Feb 2022 13:54:56 +0000 Subject: [PATCH 0013/3231] Bump reactor-core from 3.4.14 to 3.4.15 in /modules/r2dbc Bumps [reactor-core](https://github.com/reactor/reactor-core) from 3.4.14 to 3.4.15. - [Release notes](https://github.com/reactor/reactor-core/releases) - [Commits](https://github.com/reactor/reactor-core/compare/v3.4.14...v3.4.15) --- updated-dependencies: - dependency-name: io.projectreactor:reactor-core dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/r2dbc/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/r2dbc/build.gradle b/modules/r2dbc/build.gradle index 74ab1c9f7f0..2fa066c21b3 100644 --- a/modules/r2dbc/build.gradle +++ b/modules/r2dbc/build.gradle @@ -15,6 +15,6 @@ dependencies { testImplementation 'io.r2dbc:r2dbc-postgresql:0.8.11.RELEASE' testImplementation project(':postgresql') - testFixturesImplementation 'io.projectreactor:reactor-core:3.4.14' + testFixturesImplementation 'io.projectreactor:reactor-core:3.4.15' testFixturesImplementation 'org.assertj:assertj-core:3.14.0' } From ca2dbdd74ec0f61895c7af69c7af1f2731db66fe Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 17 Feb 2022 13:54:57 +0000 Subject: [PATCH 0014/3231] Bump amqp-client from 5.14.1 to 5.14.2 in /modules/rabbitmq Bumps [amqp-client](https://github.com/rabbitmq/rabbitmq-java-client) from 5.14.1 to 5.14.2. - [Release notes](https://github.com/rabbitmq/rabbitmq-java-client/releases) - [Commits](https://github.com/rabbitmq/rabbitmq-java-client/compare/v5.14.1...v5.14.2) --- updated-dependencies: - dependency-name: com.rabbitmq:amqp-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/rabbitmq/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/rabbitmq/build.gradle b/modules/rabbitmq/build.gradle index 9b680b12968..967b9eb4ed9 100644 --- a/modules/rabbitmq/build.gradle +++ b/modules/rabbitmq/build.gradle @@ -2,7 +2,7 @@ description = "TestContainers :: RabbitMQ" dependencies { api project(":testcontainers") - testImplementation 'com.rabbitmq:amqp-client:5.14.1' + testImplementation 'com.rabbitmq:amqp-client:5.14.2' testImplementation 'org.assertj:assertj-core:3.22.0' compileOnly 'org.jetbrains:annotations:23.0.0' } From cd9d2b46dde52a3cf4fe42fa54ebe487dc08f124 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 17 Feb 2022 13:55:00 +0000 Subject: [PATCH 0015/3231] Bump azure-cosmos from 4.24.0 to 4.26.0 in /modules/azure Bumps [azure-cosmos](https://github.com/Azure/azure-sdk-for-java) from 4.24.0 to 4.26.0. - [Release notes](https://github.com/Azure/azure-sdk-for-java/releases) - [Commits](https://github.com/Azure/azure-sdk-for-java/compare/azure-cosmos_4.24.0...azure-cosmos_4.26.0) --- updated-dependencies: - dependency-name: com.azure:azure-cosmos dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/azure/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/azure/build.gradle b/modules/azure/build.gradle index 1dbad6a6698..7e5bc336526 100644 --- a/modules/azure/build.gradle +++ b/modules/azure/build.gradle @@ -4,5 +4,5 @@ dependencies { api project(':testcontainers') testImplementation 'org.assertj:assertj-core:3.22.0' - testImplementation 'com.azure:azure-cosmos:4.24.0' + testImplementation 'com.azure:azure-cosmos:4.26.0' } From 9202c67fe321c9e6ca96d9eb4cc86c157e2e1ff5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 17 Feb 2022 13:55:01 +0000 Subject: [PATCH 0016/3231] Bump amqp-client from 5.14.1 to 5.14.2 in /core Bumps [amqp-client](https://github.com/rabbitmq/rabbitmq-java-client) from 5.14.1 to 5.14.2. - [Release notes](https://github.com/rabbitmq/rabbitmq-java-client/releases) - [Commits](https://github.com/rabbitmq/rabbitmq-java-client/compare/v5.14.1...v5.14.2) --- updated-dependencies: - dependency-name: com.rabbitmq:amqp-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 0d704cecd81..1b54ca4ff1b 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -90,7 +90,7 @@ dependencies { testImplementation 'org.apache.httpcomponents:httpclient:4.5.9' testImplementation 'redis.clients:jedis:4.0.1' - testImplementation 'com.rabbitmq:amqp-client:5.14.1' + testImplementation 'com.rabbitmq:amqp-client:5.14.2' testImplementation 'org.mongodb:mongo-java-driver:3.12.10' testImplementation ('org.mockito:mockito-core:4.3.1') { From cc3e076f546c53629e62f0c3b266d705d79c4881 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 17 Feb 2022 13:55:04 +0000 Subject: [PATCH 0017/3231] Bump release-drafter/release-drafter from 5.17.6 to 5.18.1 Bumps [release-drafter/release-drafter](https://github.com/release-drafter/release-drafter) from 5.17.6 to 5.18.1. - [Release notes](https://github.com/release-drafter/release-drafter/releases) - [Commits](https://github.com/release-drafter/release-drafter/compare/436492609a0c75979acf131d57f61b5321571950...2f7ebf8ab5ef7f9835ee4b0b1eebaa2a14ca1669) --- updated-dependencies: - dependency-name: release-drafter/release-drafter dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/release-drafter.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml index aec34acb8ff..05485ac57fa 100644 --- a/.github/workflows/release-drafter.yml +++ b/.github/workflows/release-drafter.yml @@ -12,6 +12,6 @@ jobs: if: github.repository == 'testcontainers/testcontainers-java' runs-on: ubuntu-latest steps: - - uses: release-drafter/release-drafter@436492609a0c75979acf131d57f61b5321571950 # v5.15.0 + - uses: release-drafter/release-drafter@2f7ebf8ab5ef7f9835ee4b0b1eebaa2a14ca1669 # v5.15.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From a78133cc8dbe6dcd839d4d264d3d5dae647edf22 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 17 Feb 2022 13:55:08 +0000 Subject: [PATCH 0018/3231] Bump postgresql from 42.3.2 to 42.3.3 in /examples Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.3.2 to 42.3.3. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.3.2...REL42.3.3) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/linked-container/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/linked-container/build.gradle b/examples/linked-container/build.gradle index 37fc1b71226..1cb3a0285f2 100644 --- a/examples/linked-container/build.gradle +++ b/examples/linked-container/build.gradle @@ -9,7 +9,7 @@ dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.32' implementation 'com.squareup.okhttp3:okhttp:4.9.3' implementation 'org.json:json:20211205' - testImplementation 'org.postgresql:postgresql:42.3.2' + testImplementation 'org.postgresql:postgresql:42.3.3' testImplementation 'ch.qos.logback:logback-classic:1.2.10' testImplementation 'org.testcontainers:postgresql' } From 5e016c9432c71f263493d144214f5fb047268d84 Mon Sep 17 00:00:00 2001 From: Yannick Weber Date: Fri, 18 Feb 2022 13:30:23 +0100 Subject: [PATCH 0019/3231] Use WaitAllStrategy for HiveMQ (#5093) Also, remove MultiLogMessageWaitStrategy. --- .../strategy/MultiLogMessageWaitStrategy.java | 59 ------------------- .../hivemq/HiveMQContainer.java | 14 ++--- 2 files changed, 5 insertions(+), 68 deletions(-) delete mode 100644 core/src/main/java/org/testcontainers/containers/wait/strategy/MultiLogMessageWaitStrategy.java diff --git a/core/src/main/java/org/testcontainers/containers/wait/strategy/MultiLogMessageWaitStrategy.java b/core/src/main/java/org/testcontainers/containers/wait/strategy/MultiLogMessageWaitStrategy.java deleted file mode 100644 index 0d342f3050f..00000000000 --- a/core/src/main/java/org/testcontainers/containers/wait/strategy/MultiLogMessageWaitStrategy.java +++ /dev/null @@ -1,59 +0,0 @@ -package org.testcontainers.containers.wait.strategy; - -import org.testcontainers.DockerClientFactory; -import org.testcontainers.containers.ContainerLaunchException; -import org.testcontainers.containers.output.OutputFrame; -import org.testcontainers.containers.output.WaitingConsumer; -import org.testcontainers.utility.LogUtils; - -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -import java.util.function.Predicate; - -/** - * This is a wait strategy to wait for multiple log patterns. - * The wait strategy will continue when every log pattern is matched at least once. - */ -public class MultiLogMessageWaitStrategy extends AbstractWaitStrategy { - - private final ConcurrentHashMap regexes = new ConcurrentHashMap<>(); - - @Override - protected void waitUntilReady() { - WaitingConsumer waitingConsumer = new WaitingConsumer(); - LogUtils.followOutput(DockerClientFactory.instance().client(), waitStrategyTarget.getContainerId(), waitingConsumer); - - Predicate waitPredicate = outputFrame -> { - if (regexes.isEmpty()) { - return true; - } - regexes.entrySet().forEach(regexHasMatched -> { - final boolean matched = outputFrame.getUtf8String().matches("(?s)" + regexHasMatched.getKey()); - if (matched) { - regexHasMatched.setValue(true); - } - }); - return regexes.values().stream().reduce(Boolean::logicalAnd).orElse(true); - }; - - try { - waitingConsumer.waitUntil(waitPredicate, startupTimeout.getSeconds(), TimeUnit.SECONDS, 1); - } catch (TimeoutException e) { - throw new ContainerLaunchException("Timed out waiting for log output matching '" + regexes + "'"); - } - } - - public MultiLogMessageWaitStrategy withRegEx(final String regEx) { - regexes.put(regEx, false); - return this; - } - - public MultiLogMessageWaitStrategy reset() { - for (final Map.Entry stringBooleanEntry : regexes.entrySet()) { - stringBooleanEntry.setValue(false); - } - return this; - } -} diff --git a/modules/hivemq/src/main/java/org/testcontainers/hivemq/HiveMQContainer.java b/modules/hivemq/src/main/java/org/testcontainers/hivemq/HiveMQContainer.java index 37f9a9259d1..342903e24af 100644 --- a/modules/hivemq/src/main/java/org/testcontainers/hivemq/HiveMQContainer.java +++ b/modules/hivemq/src/main/java/org/testcontainers/hivemq/HiveMQContainer.java @@ -8,7 +8,8 @@ import org.slf4j.event.Level; import org.testcontainers.containers.ContainerLaunchException; import org.testcontainers.containers.GenericContainer; -import org.testcontainers.containers.wait.strategy.MultiLogMessageWaitStrategy; +import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy; +import org.testcontainers.containers.wait.strategy.WaitAllStrategy; import org.testcontainers.utility.DockerImageName; import org.testcontainers.utility.MountableFile; @@ -48,7 +49,7 @@ public class HiveMQContainer extends GenericContainer { private final @NotNull Set prepackagedExtensionsToRemove = new HashSet<>(); private boolean removeAllPrepackagedExtensions = false; - private final @NotNull MultiLogMessageWaitStrategy waitStrategy = new MultiLogMessageWaitStrategy(); + private final @NotNull WaitAllStrategy waitStrategy = new WaitAllStrategy(); public HiveMQContainer(final @NotNull DockerImageName dockerImageName) { super(dockerImageName); @@ -57,7 +58,7 @@ public HiveMQContainer(final @NotNull DockerImageName dockerImageName) { addExposedPort(MQTT_PORT); - waitStrategy.withRegEx("(.*)Started HiveMQ in(.*)"); + waitStrategy.withStrategy(new LogMessageWaitStrategy().withRegEx("(.*)Started HiveMQ in(.*)")); waitingFor(waitStrategy); withLogConsumer(outputFrame -> { @@ -121,7 +122,7 @@ protected void containerIsStarted(final @NotNull InspectContainerResponse contai */ public @NotNull HiveMQContainer waitForExtension(final @NotNull String extensionName) { final String regEX = "(.*)Extension \"" + extensionName + "\" version (.*) started successfully(.*)"; - waitStrategy.withRegEx(regEX); + waitStrategy.withStrategy(new LogMessageWaitStrategy().withRegEx(regEX)); return self(); } @@ -562,11 +563,6 @@ public int getMqttPort() { return this.getMappedPort(MQTT_PORT); } - @Override - protected void containerIsStopping(final @NotNull InspectContainerResponse containerInfo) { - waitStrategy.reset(); - } - private @NotNull MountableFile cloneWithFileMode(final @NotNull MountableFile mountableFile) { return MountableFile.forHostPath(mountableFile.getResolvedPath(), HiveMQContainer.MODE); } From 3ff0bb229b204c0839c256b44d15430080b55dc0 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Mon, 21 Feb 2022 07:30:16 -0500 Subject: [PATCH 0020/3231] Fix tests on Windows (#5096) * Fix tests on Windows * Fix race condition in `StartupCheckStrategyTest` --- azure-pipelines.yml | 7 +- .../DockerRegistryContainer.java | 68 +++++++++++++++++++ .../images/ImagePullPolicyTest.java | 7 +- .../utility/AuthenticatedImagePullTest.java | 40 ++--------- .../StartupCheckStrategyTest.java | 4 +- 5 files changed, 82 insertions(+), 44 deletions(-) create mode 100644 core/src/test/java/org/testcontainers/DockerRegistryContainer.java diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 0f01bb7bcd2..9279b3233f8 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -1,7 +1,7 @@ jobs: -- job: azure_pipeline_tests - timeoutInMinutes: 120 +- job: core_tests + timeoutInMinutes: 60 steps: # Run all core tests when running the Windows CI tests @@ -19,6 +19,9 @@ jobs: publishJUnitResults: true testResultsFiles: '**/TEST-*.xml' +- job: other_tests + timeoutInMinutes: 120 + steps: # Run all non-core tests when running the Windows CI tests - task: Gradle@2 condition: eq(variables['Agent.OS'], 'Windows_NT') diff --git a/core/src/test/java/org/testcontainers/DockerRegistryContainer.java b/core/src/test/java/org/testcontainers/DockerRegistryContainer.java new file mode 100644 index 00000000000..641b1553d72 --- /dev/null +++ b/core/src/test/java/org/testcontainers/DockerRegistryContainer.java @@ -0,0 +1,68 @@ +package org.testcontainers; + +import com.github.dockerjava.api.command.InspectContainerResponse; +import lombok.Getter; +import lombok.NonNull; +import lombok.SneakyThrows; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.output.FrameConsumerResultCallback; +import org.testcontainers.containers.output.OutputFrame; +import org.testcontainers.containers.output.WaitingConsumer; + +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class DockerRegistryContainer extends GenericContainer { + + @Getter + String endpoint; + + public DockerRegistryContainer() { + super(TestImages.DOCKER_REGISTRY_IMAGE); + } + + public DockerRegistryContainer(@NonNull Future image) { + super(image); + } + + @Override + protected void configure() { + super.configure(); + withEnv("REGISTRY_HTTP_ADDR", "127.0.0.1:0"); + withCreateContainerCmdModifier(cmd -> { + cmd.getHostConfig().withNetworkMode("host"); + }); + } + + @Override + @SneakyThrows + protected void containerIsStarting(InspectContainerResponse containerInfo) { + AtomicInteger port = new AtomicInteger(-1); + try (FrameConsumerResultCallback resultCallback = new FrameConsumerResultCallback()) { + WaitingConsumer waitingConsumer = new WaitingConsumer(); + resultCallback.addConsumer(OutputFrame.OutputType.STDERR, waitingConsumer); + + dockerClient.logContainerCmd(containerInfo.getId()) + .withStdErr(true) + .withFollowStream(true) + .exec(resultCallback); + + Pattern pattern = Pattern.compile(".*listening on .*:(\\d+).*", Pattern.DOTALL | Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); + waitingConsumer.waitUntil(it -> { + String s = it.getUtf8String(); + Matcher matcher = pattern.matcher(s); + if (matcher.matches()) { + port.set(Integer.parseInt(matcher.group(1))); + return true; + } else { + return false; + } + }, 10, TimeUnit.SECONDS); + } + + endpoint = getHost() + ":" + port.get(); + } +} diff --git a/core/src/test/java/org/testcontainers/images/ImagePullPolicyTest.java b/core/src/test/java/org/testcontainers/images/ImagePullPolicyTest.java index 9b22f5e6166..85c2e785652 100644 --- a/core/src/test/java/org/testcontainers/images/ImagePullPolicyTest.java +++ b/core/src/test/java/org/testcontainers/images/ImagePullPolicyTest.java @@ -12,6 +12,7 @@ import org.mockito.Mockito; import org.rnorth.visibleassertions.VisibleAssertions; import org.testcontainers.DockerClientFactory; +import org.testcontainers.DockerRegistryContainer; import org.testcontainers.containers.ContainerLaunchException; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.startupcheck.OneShotStartupCheckStrategy; @@ -22,19 +23,17 @@ import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; -import static org.testcontainers.TestImages.DOCKER_REGISTRY_IMAGE; public class ImagePullPolicyTest { @ClassRule - public static GenericContainer registry = new GenericContainer<>(DOCKER_REGISTRY_IMAGE) - .withExposedPorts(5000); + public static DockerRegistryContainer registry = new DockerRegistryContainer(); private static DockerImageName imageName; @BeforeClass public static void beforeClass() throws Exception { - String testRegistryAddress = registry.getHost() + ":" + registry.getFirstMappedPort(); + String testRegistryAddress = registry.getEndpoint(); String testImageName = testRegistryAddress + "/image-pull-policy-test"; String tag = UUID.randomUUID().toString(); imageName = DockerImageName.parse(testImageName).withTag(tag); diff --git a/core/src/test/java/org/testcontainers/utility/AuthenticatedImagePullTest.java b/core/src/test/java/org/testcontainers/utility/AuthenticatedImagePullTest.java index 3243d219cc0..c57c1d7c0b6 100644 --- a/core/src/test/java/org/testcontainers/utility/AuthenticatedImagePullTest.java +++ b/core/src/test/java/org/testcontainers/utility/AuthenticatedImagePullTest.java @@ -13,12 +13,10 @@ import org.junit.Test; import org.mockito.Mockito; import org.testcontainers.DockerClientFactory; +import org.testcontainers.DockerRegistryContainer; import org.testcontainers.containers.ContainerState; import org.testcontainers.containers.DockerComposeContainer; import org.testcontainers.containers.GenericContainer; -import org.testcontainers.containers.output.FrameConsumerResultCallback; -import org.testcontainers.containers.output.OutputFrame; -import org.testcontainers.containers.output.WaitingConsumer; import org.testcontainers.images.builder.ImageFromDockerfile; import java.io.IOException; @@ -26,9 +24,6 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; @@ -51,18 +46,14 @@ public class AuthenticatedImagePullTest { * Containerised docker image registry, with simple hardcoded credentials */ @ClassRule - public static GenericContainer authenticatedRegistry = new GenericContainer<>(new ImageFromDockerfile() + public static DockerRegistryContainer authenticatedRegistry = new DockerRegistryContainer(new ImageFromDockerfile() .withDockerfileFromBuilder(builder -> { builder.from(DOCKER_REGISTRY_IMAGE.asCanonicalNameString()) .run("htpasswd -Bbn testuser notasecret > /htpasswd") .env("REGISTRY_AUTH", "htpasswd") .env("REGISTRY_AUTH_HTPASSWD_PATH", "/htpasswd") .env("REGISTRY_AUTH_HTPASSWD_REALM", "Test"); - })) - .withEnv("REGISTRY_HTTP_ADDR", "127.0.0.1:0") - .withCreateContainerCmdModifier(cmd -> { - cmd.getHostConfig().withNetworkMode("host"); - }); + })); private static RegistryAuthLocator originalAuthLocatorSingleton; private static DockerClient client; @@ -75,30 +66,7 @@ public static void setUp() throws Exception { originalAuthLocatorSingleton = RegistryAuthLocator.instance(); client = DockerClientFactory.instance().client(); - AtomicInteger port = new AtomicInteger(-1); - try (FrameConsumerResultCallback resultCallback = new FrameConsumerResultCallback()) { - WaitingConsumer waitingConsumer = new WaitingConsumer(); - resultCallback.addConsumer(OutputFrame.OutputType.STDERR, waitingConsumer); - - client.logContainerCmd(authenticatedRegistry.getContainerId()) - .withStdErr(true) - .withFollowStream(true) - .exec(resultCallback); - - Pattern pattern = Pattern.compile(".*listening on .*:(\\d+).*", Pattern.DOTALL | Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); - waitingConsumer.waitUntil(it -> { - String s = it.getUtf8String(); - Matcher matcher = pattern.matcher(s); - if (matcher.matches()) { - port.set(Integer.parseInt(matcher.group(1))); - return true; - } else { - return false; - } - }, 10, TimeUnit.SECONDS); - } - - String testRegistryAddress = authenticatedRegistry.getHost() + ":" + port.get(); + String testRegistryAddress = authenticatedRegistry.getEndpoint(); testImageName = testRegistryAddress + "/alpine"; final DockerImageName expectedName = DockerImageName.parse(testImageName); diff --git a/docs/examples/junit4/generic/src/test/java/org/testcontainers/containers/startupcheck/StartupCheckStrategyTest.java b/docs/examples/junit4/generic/src/test/java/org/testcontainers/containers/startupcheck/StartupCheckStrategyTest.java index 0e72efb5723..b60225010a1 100644 --- a/docs/examples/junit4/generic/src/test/java/org/testcontainers/containers/startupcheck/StartupCheckStrategyTest.java +++ b/docs/examples/junit4/generic/src/test/java/org/testcontainers/containers/startupcheck/StartupCheckStrategyTest.java @@ -84,9 +84,9 @@ public static class MinimumDurationStrategyTest { @SneakyThrows @Test public void testCommandIsExecuted() { - waitForHello(bboxWithMinimumDuration); - assertThat(bboxWithMinimumDuration.isRunning()).isTrue(); + + waitForHello(bboxWithMinimumDuration); } } } From 257c89bea35901e4318704e883d27c08437d8911 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Mon, 21 Feb 2022 08:39:53 -0500 Subject: [PATCH 0021/3231] Start Ryuk lazily when in the reusable mode (#4938) * Lazy Ryuk * Add `ResourceReaper#init` --- core/build.gradle | 7 +- .../testcontainers/DockerClientFactory.java | 56 +++---- .../containers/GenericContainer.java | 5 +- .../testcontainers/containers/Network.java | 2 + .../images/builder/ImageFromDockerfile.java | 2 + .../utility/JVMHookResourceReaper.java | 57 +++++++ .../utility/ResourceReaper.java | 156 +++++++----------- .../utility/RyukResourceReaper.java | 43 ++++- .../DockerClientFactoryTest.java | 20 +-- 9 files changed, 201 insertions(+), 147 deletions(-) create mode 100644 core/src/main/java/org/testcontainers/utility/JVMHookResourceReaper.java diff --git a/core/build.gradle b/core/build.gradle index 0d704cecd81..7b014d1ad24 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -40,7 +40,12 @@ tasks.japicmp { classExcludes = [] - methodExcludes = [] + methodExcludes = [ + "org.testcontainers.utility.ResourceReaper#start(java.lang.String,com.github.dockerjava.api.DockerClient)", + "org.testcontainers.utility.ResourceReaper#start(com.github.dockerjava.api.DockerClient)", + "org.testcontainers.utility.ResourceReaper#registerNetworkForCleanup(java.lang.String)", + "org.testcontainers.utility.ResourceReaper#removeNetworks(java.lang.String)", + ] fieldExcludes = [] } diff --git a/core/src/main/java/org/testcontainers/DockerClientFactory.java b/core/src/main/java/org/testcontainers/DockerClientFactory.java index 1ad7a0bec7e..d0a4e4a4fb3 100644 --- a/core/src/main/java/org/testcontainers/DockerClientFactory.java +++ b/core/src/main/java/org/testcontainers/DockerClientFactory.java @@ -31,6 +31,7 @@ import org.testcontainers.utility.ImageNameSubstitutor; import org.testcontainers.utility.MountableFile; import org.testcontainers.utility.ResourceReaper; +import org.testcontainers.utility.RyukResourceReaper; import org.testcontainers.utility.TestcontainersConfiguration; import java.io.ByteArrayOutputStream; @@ -38,6 +39,7 @@ import java.io.InputStream; import java.net.URI; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; @@ -61,8 +63,7 @@ public class DockerClientFactory { public static final String SESSION_ID = UUID.randomUUID().toString(); public static final Map DEFAULT_LABELS = ImmutableMap.of( - TESTCONTAINERS_LABEL, "true", - TESTCONTAINERS_SESSION_ID_LABEL, SESSION_ID + TESTCONTAINERS_LABEL, "true" ); private static final DockerImageName TINY_IMAGE = DockerImageName.parse("alpine:3.14"); @@ -73,7 +74,7 @@ public class DockerClientFactory { DockerClientProviderStrategy strategy; @VisibleForTesting - DockerClient dockerClient; + DockerClient client; @VisibleForTesting RuntimeException cachedClientFailure; @@ -174,21 +175,20 @@ public String getRemoteDockerUnixSocketPath() { */ @Synchronized public DockerClient client() { - - if (dockerClient != null) { - return dockerClient; - } - // fail-fast if checks have failed previously if (cachedClientFailure != null) { log.debug("There is a cached checks failure - throwing", cachedClientFailure); throw cachedClientFailure; } + if (client != null) { + return client; + } + final DockerClientProviderStrategy strategy = getOrInitializeStrategy(); log.info("Docker host IP address is {}", strategy.getDockerHostIpAddress()); - final DockerClient client = new DockerClientDelegate() { + client = new DockerClientDelegate() { @Getter final DockerClient dockerClient = strategy.getDockerClient(); @@ -209,25 +209,14 @@ public void close() { " Operating System: " + dockerInfo.getOperatingSystem() + "\n" + " Total Memory: " + dockerInfo.getMemTotal() / (1024 * 1024) + " MB"); - final String ryukContainerId; - - boolean useRyuk = !Boolean.parseBoolean(System.getenv("TESTCONTAINERS_RYUK_DISABLED")); - if (useRyuk) { - log.debug("Ryuk is enabled"); - try { - //noinspection deprecation - ryukContainerId = ResourceReaper.start(client); - } catch (RuntimeException e) { - cachedClientFailure = e; - throw e; - } - log.info("Ryuk started - will monitor and terminate Testcontainers containers on JVM exit"); - } else { - log.debug("Ryuk is disabled"); - ryukContainerId = null; - // best-efforts cleanup at JVM shutdown, without using the Ryuk container + final ResourceReaper resourceReaper; + try { + resourceReaper = ResourceReaper.instance(); //noinspection deprecation - ResourceReaper.instance().setHook(); + resourceReaper.init(); + } catch (RuntimeException e) { + cachedClientFailure = e; + throw e; } boolean checksEnabled = !TestcontainersConfiguration.getInstance().isDisableChecks(); @@ -237,6 +226,12 @@ public void close() { try { log.info("Checking the system..."); checkDockerVersion(version.getVersion()); + + //noinspection deprecation + String ryukContainerId = resourceReaper instanceof RyukResourceReaper + ? ((RyukResourceReaper) resourceReaper).getContainerId() + : null; + if (ryukContainerId != null) { checkDiskSpace(client, ryukContainerId); } else { @@ -261,8 +256,7 @@ public void close() { log.debug("Checks are disabled"); } - dockerClient = client; - return dockerClient; + return client; } private void checkDockerVersion(String dockerVersion) { @@ -378,8 +372,10 @@ private T runInsideDocker(DockerClient client, Consumer final String tinyImage = ImageNameSubstitutor.instance().apply(TINY_IMAGE).asCanonicalNameString(); checkAndPullImage(client, tinyImage); + HashMap labels = new HashMap<>(DEFAULT_LABELS); + labels.putAll(ResourceReaper.instance().getLabels()); CreateContainerCmd createContainerCmd = client.createContainerCmd(tinyImage) - .withLabels(DEFAULT_LABELS); + .withLabels(labels); createContainerCmdConsumer.accept(createContainerCmd); String id = createContainerCmd.exec().getId(); diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index 3851cf42fca..aca543ef7ec 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -364,7 +364,7 @@ private void tryStart(Instant startedAt) { CreateContainerCmd createCommand = dockerClient.createContainerCmd(dockerImageName); applyConfiguration(createCommand); - createCommand.getLabels().put(DockerClientFactory.TESTCONTAINERS_LABEL, "true"); + createCommand.getLabels().putAll(DockerClientFactory.DEFAULT_LABELS); boolean reused = false; final boolean reusable; @@ -406,7 +406,8 @@ private void tryStart(Instant startedAt) { } if (!reusable) { - createCommand.getLabels().put(DockerClientFactory.TESTCONTAINERS_SESSION_ID_LABEL, DockerClientFactory.SESSION_ID); + //noinspection deprecation + createCommand.getLabels().putAll(ResourceReaper.instance().getLabels()); } if (!reused) { diff --git a/core/src/main/java/org/testcontainers/containers/Network.java b/core/src/main/java/org/testcontainers/containers/Network.java index 2fda5ad1a8d..046133815b6 100644 --- a/core/src/main/java/org/testcontainers/containers/Network.java +++ b/core/src/main/java/org/testcontainers/containers/Network.java @@ -87,6 +87,8 @@ private String create() { Map labels = createNetworkCmd.getLabels(); labels = new HashMap<>(labels != null ? labels : Collections.emptyMap()); labels.putAll(DockerClientFactory.DEFAULT_LABELS); + //noinspection deprecation + labels.putAll(ResourceReaper.instance().getLabels()); createNetworkCmd.withLabels(labels); return createNetworkCmd.exec().getId(); diff --git a/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java b/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java index 05bb6911c3e..a434dee2b41 100644 --- a/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java +++ b/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java @@ -112,6 +112,8 @@ public void onNext(BuildResponseItem item) { labels.putAll(buildImageCmd.getLabels()); } labels.putAll(DockerClientFactory.DEFAULT_LABELS); + //noinspection deprecation + labels.putAll(ResourceReaper.instance().getLabels()); buildImageCmd.withLabels(labels); prePullDependencyImages(dependencyImageNames); diff --git a/core/src/main/java/org/testcontainers/utility/JVMHookResourceReaper.java b/core/src/main/java/org/testcontainers/utility/JVMHookResourceReaper.java new file mode 100644 index 00000000000..173cf28f97c --- /dev/null +++ b/core/src/main/java/org/testcontainers/utility/JVMHookResourceReaper.java @@ -0,0 +1,57 @@ +package org.testcontainers.utility; + +import com.github.dockerjava.api.model.Container; +import com.github.dockerjava.api.model.PruneType; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +/** + * A {@link ResourceReaper} implementation that uses {@link Runtime#addShutdownHook(Thread)} + * to cleanup containers. + */ +class JVMHookResourceReaper extends ResourceReaper { + + @Override + public void init() { + setHook(); + } + + @Override + public synchronized void performCleanup() { + super.performCleanup(); + synchronized (DEATH_NOTE) { + DEATH_NOTE.forEach(filters -> prune(PruneType.CONTAINERS, filters)); + DEATH_NOTE.forEach(filters -> prune(PruneType.NETWORKS, filters)); + DEATH_NOTE.forEach(filters -> prune(PruneType.VOLUMES, filters)); + DEATH_NOTE.forEach(filters -> prune(PruneType.IMAGES, filters)); + } + } + + private void prune(PruneType pruneType, List> filters) { + String[] labels = filters.stream() + .filter(it -> "label".equals(it.getKey())) + .map(Map.Entry::getValue) + .toArray(String[]::new); + switch (pruneType) { + // Docker only prunes stopped containers, so we have to do it manually + case CONTAINERS: + List containers = dockerClient.listContainersCmd() + .withFilter("label", Arrays.asList(labels)) + .withShowAll(true) + .exec(); + + containers.parallelStream().forEach(container -> { + dockerClient.removeContainerCmd(container.getId()) + .withForce(true) + .withRemoveVolumes(true) + .exec(); + }); + break; + default: + dockerClient.pruneCmd(pruneType).withLabelFilter(labels).exec(); + break; + } + } +} diff --git a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java index 69c849b6c82..44f9456ce4c 100644 --- a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java +++ b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java @@ -3,9 +3,7 @@ import com.github.dockerjava.api.DockerClient; import com.github.dockerjava.api.command.InspectContainerResponse; import com.github.dockerjava.api.exception.NotFoundException; -import com.github.dockerjava.api.model.Container; import com.github.dockerjava.api.model.Network; -import com.github.dockerjava.api.model.PruneType; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Throwables; import com.google.common.collect.Sets; @@ -24,62 +22,62 @@ import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Collectors; +import java.util.stream.Stream; /** * Component that responsible for container removal and automatic cleanup of dead containers at JVM shutdown. */ @Slf4j -public final class ResourceReaper { +public class ResourceReaper { private static final Logger LOGGER = LoggerFactory.getLogger(ResourceReaper.class); + private static final Map MARKER_LABELS = Collections.singletonMap( + DockerClientFactory.TESTCONTAINERS_SESSION_ID_LABEL, + DockerClientFactory.SESSION_ID + ); + static final List>> DEATH_NOTE = new ArrayList<>( Arrays.asList( - DockerClientFactory.DEFAULT_LABELS.entrySet().stream() + Stream + .concat( + DockerClientFactory.DEFAULT_LABELS.entrySet().stream(), + MARKER_LABELS.entrySet().stream() + ) .>map(it -> new SimpleEntry<>("label", it.getKey() + "=" + it.getValue())) .collect(Collectors.toList()) ) ); private static ResourceReaper instance; - private static AtomicBoolean ryukStarted = new AtomicBoolean(false); - private final DockerClient dockerClient = DockerClientFactory.lazyClient(); + final DockerClient dockerClient = DockerClientFactory.lazyClient(); private Map registeredContainers = new ConcurrentHashMap<>(); private Set registeredNetworks = Sets.newConcurrentHashSet(); private Set registeredImages = Sets.newConcurrentHashSet(); private AtomicBoolean hookIsSet = new AtomicBoolean(false); - /** - * - * @deprecated internal API + * Internal constructor to avoid custom implementations */ - @Deprecated - public static String start(String hostIpAddress, DockerClient client) { - return start(client); - } - - /** - * - * @deprecated internal API - */ - @Deprecated - public static String start(DockerClient client) { - RyukResourceReaper ryuk = new RyukResourceReaper(client); - String containerId = ryuk.getContainerId(); - ryukStarted.set(true); - return containerId; + ResourceReaper() { } public synchronized static ResourceReaper instance() { if (instance == null) { - instance = new ResourceReaper(); + boolean useRyuk = !Boolean.parseBoolean(System.getenv("TESTCONTAINERS_RYUK_DISABLED")); + if (useRyuk) { + //noinspection deprecation + instance = new RyukResourceReaper(); + } else { + instance = new JVMHookResourceReaper(); + } } return instance; @@ -87,8 +85,10 @@ public synchronized static ResourceReaper instance() { /** * Perform a cleanup. + * @deprecated no longer supported API, use {@link DockerClient} directly */ - public synchronized void performCleanup() { + @Deprecated + public void performCleanup() { registeredContainers.forEach(this::removeContainer); registeredNetworks.forEach(this::removeNetwork); registeredImages.forEach(this::removeImage); @@ -126,7 +126,9 @@ public void registerLabelsFilterForCleanup(Map labels) { * * @param containerId the ID of the container * @param imageName the image name of the container (used for logging) + * @deprecated no longer supported API */ + @Deprecated public void registerContainerForCleanup(String containerId, String imageName) { setHook(); registeredContainers.put(containerId, imageName); @@ -136,7 +138,9 @@ public void registerContainerForCleanup(String containerId, String imageName) { * Stop a potentially running container and remove it, including associated volumes. * * @param containerId the ID of the container + * @deprecated use {@link DockerClient} directly */ + @Deprecated public void stopAndRemoveContainer(String containerId) { removeContainer(containerId, registeredContainers.get(containerId)); @@ -148,7 +152,9 @@ public void stopAndRemoveContainer(String containerId) { * * @param containerId the ID of the container * @param imageName the image name of the container (used for logging) + * @deprecated use {@link DockerClient} directly */ + @Deprecated public void stopAndRemoveContainer(String containerId, String imageName) { removeContainer(containerId, imageName); @@ -204,46 +210,24 @@ private void removeContainer(String containerId, String imageName) { * Register a network to be cleaned up at JVM shutdown. * * @param id the ID of the network + * @deprecated no longer supported API */ + @Deprecated public void registerNetworkIdForCleanup(String id) { setHook(); registeredNetworks.add(id); } - /** - * @param networkName the name of the network - * @deprecated see {@link ResourceReaper#registerNetworkIdForCleanup(String)} - */ - @Deprecated - public void registerNetworkForCleanup(String networkName) { - try { - // Try to find the network by name, so that we can register its ID for later deletion - dockerClient.listNetworksCmd() - .withNameFilter(networkName) - .exec() - .forEach(network -> registerNetworkIdForCleanup(network.getId())); - } catch (Exception e) { - LOGGER.trace("Error encountered when looking up network (name: {})", networkName); - } - } - /** * Removes a network by ID. * @param id + * @deprecated use {@link DockerClient} directly */ + @Deprecated public void removeNetworkById(String id) { removeNetwork(id); } - /** - * Removes a network by ID. - * @param identifier - * @deprecated see {@link ResourceReaper#removeNetworkById(String)} - */ - @Deprecated - public void removeNetworks(String identifier) { - removeNetworkById(identifier); - } private void removeNetwork(String id) { try { @@ -273,14 +257,26 @@ private void removeNetwork(String id) { } } + /** + * @deprecated no longer supported API + */ + @Deprecated public void unregisterNetwork(String identifier) { registeredNetworks.remove(identifier); } + /** + * @deprecated no longer supported API + */ + @Deprecated public void unregisterContainer(String identifier) { registeredContainers.remove(identifier); } + /** + * @deprecated no longer supported API + */ + @Deprecated public void registerImageForCleanup(String dockerImageName) { setHook(); registeredImages.add(dockerImageName); @@ -295,53 +291,27 @@ private void removeImage(String dockerImageName) { } } - private void prune(PruneType pruneType, List> filters) { - String[] labels = filters.stream() - .filter(it -> "label".equals(it.getKey())) - .map(Map.Entry::getValue) - .toArray(String[]::new); - switch (pruneType) { - // Docker only prunes stopped containers, so we have to do it manually - case CONTAINERS: - List containers = dockerClient.listContainersCmd() - .withFilter("label", Arrays.asList(labels)) - .withShowAll(true) - .exec(); - - containers.parallelStream().forEach(container -> { - removeContainer(container.getId(), container.getImage()); - }); - break; - default: - dockerClient.pruneCmd(pruneType).withLabelFilter(labels).exec(); - break; + void setHook() { + if (hookIsSet.compareAndSet(false, true)) { + // If the JVM stops without containers being stopped, try and stop the container. + Runtime.getRuntime().addShutdownHook(new Thread(DockerClientFactory.TESTCONTAINERS_THREAD_GROUP, this::performCleanup)); } } /** - * @deprecated internal API, not intended for public usage + * + * @deprecated internal API */ @Deprecated - public void setHook() { - if (hookIsSet.compareAndSet(false, true)) { - // If the JVM stops without containers being stopped, try and stop the container. - Runtime.getRuntime().addShutdownHook( - new Thread(DockerClientFactory.TESTCONTAINERS_THREAD_GROUP, - () -> { - performCleanup(); - - if (!ryukStarted.get()) { - synchronized (DEATH_NOTE) { - DEATH_NOTE.forEach(filters -> prune(PruneType.CONTAINERS, filters)); - DEATH_NOTE.forEach(filters -> prune(PruneType.NETWORKS, filters)); - DEATH_NOTE.forEach(filters -> prune(PruneType.VOLUMES, filters)); - DEATH_NOTE.forEach(filters -> prune(PruneType.IMAGES, filters)); - } - } - } - ) - ); - } + public Map getLabels() { + return MARKER_LABELS; + } + + /** + * @deprecated internal API + */ + @Deprecated + public void init() { } static class FilterRegistry { diff --git a/core/src/main/java/org/testcontainers/utility/RyukResourceReaper.java b/core/src/main/java/org/testcontainers/utility/RyukResourceReaper.java index e8aed92fa9d..743b33856cf 100644 --- a/core/src/main/java/org/testcontainers/utility/RyukResourceReaper.java +++ b/core/src/main/java/org/testcontainers/utility/RyukResourceReaper.java @@ -29,13 +29,21 @@ import java.util.Objects; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Collectors; import java.util.stream.Stream; import static org.awaitility.Awaitility.await; +/** + * Ryuk-based {@link ResourceReaper} implementation. + * + * @see
moby-ryuk + * @deprecated internal API + */ +@Deprecated @Slf4j -class RyukResourceReaper { +public class RyukResourceReaper extends ResourceReaper { private static final RateLimiter RYUK_ACK_RATE_LIMITER = RateLimiterBuilder .newBuilder() @@ -43,11 +51,40 @@ class RyukResourceReaper { .withConstantThroughput() .build(); + private final AtomicBoolean started = new AtomicBoolean(false); + @Getter - private final String containerId; + private String containerId = null; + + @Override + public void init() { + if (!TestcontainersConfiguration.getInstance().environmentSupportsReuse()) { + log.debug("Ryuk is enabled"); + maybeStart(); + log.info("Ryuk started - will monitor and terminate Testcontainers containers on JVM exit"); + } else { + log.debug("Ryuk is enabled but will be started on demand"); + } + } + + @Override + public void registerLabelsFilterForCleanup(Map labels) { + maybeStart(); + super.registerLabelsFilterForCleanup(labels); + } + + @Override + public Map getLabels() { + maybeStart(); + return super.getLabels(); + } @SneakyThrows(InterruptedException.class) - RyukResourceReaper(DockerClient client) { + private synchronized void maybeStart() { + if (!started.compareAndSet(false, true)) { + return; + } + DockerClient client = DockerClientFactory.lazyClient(); String ryukImage = ImageNameSubstitutor.instance() .apply(DockerImageName.parse("testcontainers/ryuk:0.3.3")) .asCanonicalNameString(); diff --git a/core/src/test/java/org/testcontainers/DockerClientFactoryTest.java b/core/src/test/java/org/testcontainers/DockerClientFactoryTest.java index d484cc92eaf..16d25b2f625 100644 --- a/core/src/test/java/org/testcontainers/DockerClientFactoryTest.java +++ b/core/src/test/java/org/testcontainers/DockerClientFactoryTest.java @@ -4,17 +4,13 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.testcontainers.TestImages.TINY_IMAGE; -import com.github.dockerjava.api.DockerClient; -import com.github.dockerjava.api.exception.DockerException; import com.github.dockerjava.api.exception.NotFoundException; import org.junit.Rule; import org.junit.Test; -import org.mockito.Mockito; import org.rnorth.visibleassertions.VisibleAssertions; import org.testcontainers.DockerClientFactory.DiskSpaceUsage; import org.testcontainers.dockerclient.LogToStringContainerCallback; import org.testcontainers.utility.MockTestcontainersConfigurationRule; -import org.testcontainers.utility.TestcontainersConfiguration; /** * Test for {@link DockerClientFactory}. @@ -65,27 +61,15 @@ public void dockerHostIpAddress() { @Test public void failedChecksFailFast() { - Mockito.doReturn(false).when(TestcontainersConfiguration.getInstance()).isDisableChecks(); - - // Make sure that Ryuk is started - assertThat(DockerClientFactory.instance().client()).isNotNull(); - - DockerClientFactory instance = new DockerClientFactory(); - DockerClient dockerClient = instance.dockerClient; + DockerClientFactory instance = DockerClientFactory.instance(); + assertThat(instance.client()).isNotNull(); assertThat(instance.cachedClientFailure).isNull(); try { - // Remove cached client to force the initialization logic - instance.dockerClient = null; - - // Ryuk should fail to start twice due to the name conflict (equal to the session id) - assertThatThrownBy(instance::client).isInstanceOf(DockerException.class); - RuntimeException failure = new IllegalStateException("Boom!"); instance.cachedClientFailure = failure; // Fail fast assertThatThrownBy(instance::client).isEqualTo(failure); } finally { - instance.dockerClient = dockerClient; instance.cachedClientFailure = null; } } From 55a131545b758c04afa1bb0af4319d505ac3abde Mon Sep 17 00:00:00 2001 From: Yannick Weber Date: Tue, 22 Feb 2022 13:15:20 +0100 Subject: [PATCH 0022/3231] Every HiveMQ IT now uses `getHost()` (#5106) Ensures better portability across different Docker environments. --- .../hivemq/ContainerWithCustomConfigIT.java | 1 + .../ContainerWithExtensionFromDirectoryIT.java | 4 ++-- .../hivemq/ContainerWithExtensionIT.java | 6 +++--- .../ContainerWithExtensionSubclassIT.java | 2 +- .../ContainerWithFileInExtensionHomeIT.java | 2 +- .../hivemq/ContainerWithFileInHomeIT.java | 2 +- .../hivemq/ContainerWithLicenseIT.java | 2 +- .../ContainerWithoutPlatformExtensionsIT.java | 2 ++ .../hivemq/CreateFileInCopiedDirectoryIT.java | 2 +- .../CreateFileInExtensionDirectoryIT.java | 2 +- .../DisableEnableExtensionFromDirectoryIT.java | 6 +++--- .../hivemq/DisableEnableExtensionIT.java | 8 ++++---- .../hivemq/docs/DemoExtensionTestsIT.java | 1 + .../hivemq/docs/DemoFilesIT.java | 1 + .../hivemq/docs/DemoHiveMQContainerIT.java | 1 + .../hivemq/util/TestPublishModifiedUtil.java | 18 ++++++++++-------- 16 files changed, 34 insertions(+), 26 deletions(-) diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithCustomConfigIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithCustomConfigIT.java index 17f09d86adf..57d3b75bc24 100755 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithCustomConfigIT.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithCustomConfigIT.java @@ -27,6 +27,7 @@ void test() throws Exception { final Mqtt5BlockingClient publisher = Mqtt5Client.builder() .identifier("publisher") .serverPort(hivemq.getMqttPort()) + .serverHost(hivemq.getHost()) .buildBlocking(); publisher.connect(); diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithExtensionFromDirectoryIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithExtensionFromDirectoryIT.java index 7672c70925d..4b89e418b05 100755 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithExtensionFromDirectoryIT.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithExtensionFromDirectoryIT.java @@ -23,7 +23,7 @@ void test() throws Exception { .withLogLevel(Level.DEBUG)) { hivemq.start(); - TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort()); + TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost()); } } @@ -37,7 +37,7 @@ void test_wrongDirectoryName() throws Exception { .withLogLevel(Level.DEBUG)) { hivemq.start(); - TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort()); + TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost()); } } } diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithExtensionIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithExtensionIT.java index 3ad7b26148a..8979d2c17b0 100755 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithExtensionIT.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithExtensionIT.java @@ -27,15 +27,15 @@ void test() throws Exception { .withExtension(hiveMQExtension)) { hivemq.start(); - TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort()); + TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost()); hivemq.stop(); hivemq.start(); - TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort()); + TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost()); hivemq.stop(); hivemq.start(); - TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort()); + TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost()); } } } diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithExtensionSubclassIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithExtensionSubclassIT.java index 33880a800c9..53714123aff 100755 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithExtensionSubclassIT.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithExtensionSubclassIT.java @@ -29,7 +29,7 @@ void test() throws Exception { .withLogLevel(Level.DEBUG)) { hivemq.start(); - TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort()); + TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost()); } } } diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithFileInExtensionHomeIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithFileInExtensionHomeIT.java index 1d2b1705eae..559737c8868 100755 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithFileInExtensionHomeIT.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithFileInExtensionHomeIT.java @@ -43,7 +43,7 @@ void test() throws Exception { "/additionalFiles/my-file.txt")) { hivemq.start(); - TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort()); + TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost()); } } diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithFileInHomeIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithFileInHomeIT.java index 542c54789e8..efdf6ad298d 100755 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithFileInHomeIT.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithFileInHomeIT.java @@ -41,7 +41,7 @@ void test() throws Exception { hivemq.start(); - TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort()); + TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost()); } } diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithLicenseIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithLicenseIT.java index 4fc27adf8c2..ba2dc472cdb 100755 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithLicenseIT.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithLicenseIT.java @@ -41,7 +41,7 @@ void test() throws Exception { hivemq.start(); - TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort()); + TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost()); } } diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithoutPlatformExtensionsIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithoutPlatformExtensionsIT.java index 37b37dc0d59..eaec72cb038 100644 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithoutPlatformExtensionsIT.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithoutPlatformExtensionsIT.java @@ -49,6 +49,7 @@ public void removeAllPlatformExtensions() throws InterruptedException { final Mqtt5BlockingClient client = MqttClient.builder() .serverPort(hivemq.getMqttPort()) + .serverHost(hivemq.getHost()) .useMqttVersion5() .buildBlocking(); @@ -82,6 +83,7 @@ public void removeKafkaExtension() throws InterruptedException { final Mqtt5BlockingClient client = MqttClient.builder() .serverPort(hivemq.getMqttPort()) + .serverHost(hivemq.getHost()) .useMqttVersion5() .buildBlocking(); diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/CreateFileInCopiedDirectoryIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/CreateFileInCopiedDirectoryIT.java index 85089374624..58b70c3d1d9 100755 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/CreateFileInCopiedDirectoryIT.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/CreateFileInCopiedDirectoryIT.java @@ -51,7 +51,7 @@ void test() throws Exception { .withFileInHomeFolder(createDirectory(), "directory")) { hivemq.start(); - TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort()); + TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost()); } } diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/CreateFileInExtensionDirectoryIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/CreateFileInExtensionDirectoryIT.java index a7756c69f1d..c41137f58c3 100755 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/CreateFileInExtensionDirectoryIT.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/CreateFileInExtensionDirectoryIT.java @@ -39,7 +39,7 @@ void test() throws Exception { .withExtension(hiveMQExtension)) { hivemq.start(); - TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort()); + TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost()); } } diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/DisableEnableExtensionFromDirectoryIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/DisableEnableExtensionFromDirectoryIT.java index 2248681a769..f6b1b9b5cd7 100644 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/DisableEnableExtensionFromDirectoryIT.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/DisableEnableExtensionFromDirectoryIT.java @@ -26,11 +26,11 @@ void test() throws Exception { hivemq.start(); - TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort()); + TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost()); hivemq.disableExtension("Modifier Extension", "modifier-extension"); - assertThrows(ExecutionException.class, () -> TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort())); + assertThrows(ExecutionException.class, () -> TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost())); hivemq.enableExtension("Modifier Extension", "modifier-extension"); - TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort()); + TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost()); } } diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/DisableEnableExtensionIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/DisableEnableExtensionIT.java index 7cc943c5527..5b3c6ab107f 100644 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/DisableEnableExtensionIT.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/DisableEnableExtensionIT.java @@ -33,13 +33,13 @@ void test() throws Exception { hivemq.start(); - assertThrows(ExecutionException.class, () -> TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort())); + assertThrows(ExecutionException.class, () -> TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost())); hivemq.enableExtension(hiveMQExtension); - TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort()); + TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost()); hivemq.disableExtension(hiveMQExtension); - assertThrows(ExecutionException.class, () -> TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort())); + assertThrows(ExecutionException.class, () -> TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost())); hivemq.enableExtension(hiveMQExtension); - TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort()); + TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost()); } } diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoExtensionTestsIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoExtensionTestsIT.java index 2627dca2028..3a8ccc1bebe 100644 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoExtensionTestsIT.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoExtensionTestsIT.java @@ -47,6 +47,7 @@ public void test() throws Exception { // mqtt5client { final Mqtt5BlockingClient client = Mqtt5Client.builder() .serverPort(hivemqWithClasspathExtension.getMqttPort()) + .serverHost(hivemqWithClasspathExtension.getHost()) .buildBlocking(); client.connect(); diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoFilesIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoFilesIT.java index 46c24e8e51d..7ec777d02b8 100644 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoFilesIT.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoFilesIT.java @@ -52,6 +52,7 @@ public void test() throws Exception { // mqtt5client { final Mqtt5BlockingClient client = Mqtt5Client.builder() .serverPort(hivemq.getMqttPort()) + .serverHost(hivemq.getHost()) .buildBlocking(); client.connect(); diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoHiveMQContainerIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoHiveMQContainerIT.java index 60683c8d612..51e3a43d9e5 100644 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoHiveMQContainerIT.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoHiveMQContainerIT.java @@ -51,6 +51,7 @@ public void test() throws Exception { // mqtt5client { final Mqtt5BlockingClient client = Mqtt5Client.builder() .serverPort(hivemqCe.getMqttPort()) + .serverHost(hivemqCe.getHost()) .buildBlocking(); client.connect(); diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/util/TestPublishModifiedUtil.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/util/TestPublishModifiedUtil.java index b2712ef5a1d..f2f1cfd692f 100755 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/util/TestPublishModifiedUtil.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/util/TestPublishModifiedUtil.java @@ -3,27 +3,29 @@ import com.hivemq.client.mqtt.MqttGlobalPublishFilter; import com.hivemq.client.mqtt.mqtt5.Mqtt5BlockingClient; import com.hivemq.client.mqtt.mqtt5.Mqtt5Client; +import org.jetbrains.annotations.NotNull; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; public class TestPublishModifiedUtil { - public static void testPublishModified(final int mqttPort) throws InterruptedException, ExecutionException { + public static void testPublishModified(final int mqttPort, final @NotNull String host) throws Exception { final CompletableFuture publishReceived = new CompletableFuture<>(); final Mqtt5BlockingClient publisher = Mqtt5Client.builder() - .serverPort(mqttPort) - .identifier("publisher") - .buildBlocking(); + .serverPort(mqttPort) + .serverHost(host) + .identifier("publisher") + .buildBlocking(); publisher.connect(); final Mqtt5BlockingClient subscriber = Mqtt5Client.builder() - .serverPort(mqttPort) - .identifier("subscriber") - .buildBlocking(); + .serverPort(mqttPort) + .serverHost(host) + .identifier("subscriber") + .buildBlocking(); subscriber.connect(); subscriber.subscribeWith().topicFilter("test/topic").send(); subscriber.toAsync().publishes(MqttGlobalPublishFilter.ALL, publish -> { From fdb58fb3d3dd82dbbd7cf6a2be52ecbbcdbc25db Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Tue, 22 Feb 2022 08:33:06 -0500 Subject: [PATCH 0023/3231] Do not mount Selenium's SHM on Windows (#5105) It looks like mounting /dev/shm did not play well on WSL2. This change uses Docker's SHM support on Windows. --- .../containers/BrowserWebDriverContainer.java | 9 ++++++++- .../junit/BrowserWebDriverContainerTest.java | 3 +++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java b/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java index bd38dead8ac..e47c832e2ff 100644 --- a/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java +++ b/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java @@ -16,6 +16,9 @@ import java.util.Optional; import java.util.Set; import java.util.concurrent.TimeUnit; + +import org.apache.commons.io.FileUtils; +import org.apache.commons.lang.SystemUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.openqa.selenium.Capabilities; @@ -205,7 +208,11 @@ protected void configure() { setCommand("/opt/bin/entry_point.sh"); if (getShmSize() == null) { - this.getBinds().add(new Bind("/dev/shm", new Volume("/dev/shm"), AccessMode.rw)); + if (SystemUtils.IS_OS_WINDOWS) { + withSharedMemorySize(512 * FileUtils.ONE_MB); + } else { + this.getBinds().add(new Bind("/dev/shm", new Volume("/dev/shm"), AccessMode.rw)); + } } /* diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/BrowserWebDriverContainerTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/BrowserWebDriverContainerTest.java index 7b7d3a46733..0589d764573 100644 --- a/modules/selenium/src/test/java/org/testcontainers/junit/BrowserWebDriverContainerTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/junit/BrowserWebDriverContainerTest.java @@ -2,6 +2,8 @@ import com.github.dockerjava.api.command.InspectContainerResponse; import org.apache.commons.io.FileUtils; +import org.apache.commons.lang.SystemUtils; +import org.junit.Assume; import org.junit.Test; import org.openqa.selenium.chrome.ChromeOptions; import org.openqa.selenium.firefox.FirefoxOptions; @@ -50,6 +52,7 @@ public void provideDefaultNoProxyEnvironmentIfNotSet() { @Test public void createContainerWithShmVolume() { + Assume.assumeFalse("SHM isn't mounted on Windows", SystemUtils.IS_OS_WINDOWS); try ( BrowserWebDriverContainer webDriverContainer = new BrowserWebDriverContainer() .withCapabilities(new FirefoxOptions()) From 14eff07a7cae8d48b07b80e309bba601f164ae66 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Tue, 22 Feb 2022 09:50:27 -0500 Subject: [PATCH 0024/3231] Update to `docker-java` 3.2.13 (#5045) * Try `docker-java` 3.2.13 * Fix imports * Update build.gradle * Update SimplePostgreSQLTest.java * Use `withCgroupnsMode` * fix `BrowserWebDriverContainer` * FML --- core/build.gradle | 10 +++------- .../java/org/testcontainers/DockerClientFactory.java | 4 ++-- .../org/testcontainers/containers/ContainerState.java | 2 +- .../containers/DockerComposeContainer.java | 4 ++-- .../testcontainers/containers/GenericContainer.java | 4 ++-- .../dockerclient/DockerClientConfigUtils.java | 2 +- .../dockerclient/DockerClientProviderStrategy.java | 2 +- .../NpipeSocketClientProviderStrategy.java | 2 +- .../RootlessDockerClientProviderStrategy.java | 4 ++-- .../dockerclient/UnixSocketClientProviderStrategy.java | 2 +- .../images/builder/ImageFromDockerfile.java | 2 +- .../images/builder/traits/StringsTrait.java | 2 +- .../java/org/testcontainers/utility/AuditLogger.java | 2 +- .../testcontainers/utility/DockerMachineClient.java | 2 +- .../java/org/testcontainers/utility/MountableFile.java | 2 +- .../testcontainers/utility/RegistryAuthLocator.java | 6 +++--- .../utility/TestcontainersConfiguration.java | 2 +- .../containers/output/ToStringConsumerTest.java | 2 +- .../dockerfile/statement/AbstractStatementTest.java | 6 +++--- .../utility/RegistryAuthLocatorTest.java | 2 +- .../main/java/org/testcontainers/ext/ScriptUtils.java | 2 +- .../testcontainers/jdbc/AbstractJDBCDriverTest.java | 2 +- .../containers/JdbcDatabaseContainer.java | 2 +- .../main/java/org/testcontainers/k3s/K3sContainer.java | 4 +--- .../junit/mariadb/SimpleMariaDBTest.java | 2 +- .../mssqlserver/CustomPasswordMSSQLServerTest.java | 2 +- .../testcontainers/junit/mysql/SimpleMySQLTest.java | 2 +- .../org/testcontainers/containers/OracleContainer.java | 10 +++++----- .../containers/BrowserWebDriverContainer.java | 2 +- .../junit/BrowserWebDriverContainerTest.java | 2 +- .../junit/LocalServerWebDriverContainerTest.java | 2 +- .../org/testcontainers/containers/SolrContainer.java | 2 +- 32 files changed, 46 insertions(+), 52 deletions(-) diff --git a/core/build.gradle b/core/build.gradle index 7b014d1ad24..a471aa4816e 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -73,17 +73,13 @@ dependencies { shaded 'org.awaitility:awaitility:4.1.1' - api platform('com.github.docker-java:docker-java-bom:3.2.12') - shaded platform('com.github.docker-java:docker-java-bom:3.2.12') + api platform('com.github.docker-java:docker-java-bom:3.2.13') + shaded platform('com.github.docker-java:docker-java-bom:3.2.13') api "com.github.docker-java:docker-java-api" shaded 'com.github.docker-java:docker-java-core' - - shaded 'com.github.docker-java:docker-java-transport-okhttp', { - // TODO remove unused `net.java.dev.jna:jna-platform` from `docker-java-transport-okhttp` - exclude(group: 'net.java.dev.jna', module: 'jna-platform') - } + shaded 'com.github.docker-java:docker-java-transport-okhttp' api 'com.github.docker-java:docker-java-transport-zerodep' diff --git a/core/src/main/java/org/testcontainers/DockerClientFactory.java b/core/src/main/java/org/testcontainers/DockerClientFactory.java index d0a4e4a4fb3..9a6165ef55f 100644 --- a/core/src/main/java/org/testcontainers/DockerClientFactory.java +++ b/core/src/main/java/org/testcontainers/DockerClientFactory.java @@ -20,8 +20,8 @@ import lombok.SneakyThrows; import lombok.Synchronized; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang.StringUtils; -import org.apache.commons.lang.SystemUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.SystemUtils; import org.testcontainers.dockerclient.DockerClientProviderStrategy; import org.testcontainers.dockerclient.DockerMachineClientProviderStrategy; import org.testcontainers.dockerclient.TransportConfig; diff --git a/core/src/main/java/org/testcontainers/containers/ContainerState.java b/core/src/main/java/org/testcontainers/containers/ContainerState.java index 5de9ef7c566..7fe90bd57a4 100644 --- a/core/src/main/java/org/testcontainers/containers/ContainerState.java +++ b/core/src/main/java/org/testcontainers/containers/ContainerState.java @@ -12,7 +12,7 @@ import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream; import org.apache.commons.compress.utils.IOUtils; -import org.apache.commons.lang.math.NumberUtils; +import org.apache.commons.lang3.math.NumberUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.testcontainers.DockerClientFactory; diff --git a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java index b56c70cb959..b8913e8ac7c 100644 --- a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java +++ b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java @@ -12,8 +12,8 @@ import com.google.common.util.concurrent.Uninterruptibles; import lombok.NonNull; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang.StringUtils; -import org.apache.commons.lang.SystemUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.SystemUtils; import org.junit.runner.Description; import org.junit.runners.model.Statement; import org.slf4j.Logger; diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index aca543ef7ec..22a4f0ba739 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -27,8 +27,8 @@ import lombok.Setter; import lombok.SneakyThrows; import org.apache.commons.io.FileUtils; -import org.apache.commons.lang.StringUtils; -import org.apache.commons.lang.SystemUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.SystemUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.junit.runner.Description; diff --git a/core/src/main/java/org/testcontainers/dockerclient/DockerClientConfigUtils.java b/core/src/main/java/org/testcontainers/dockerclient/DockerClientConfigUtils.java index b494eb087f6..e393e59419f 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/DockerClientConfigUtils.java +++ b/core/src/main/java/org/testcontainers/dockerclient/DockerClientConfigUtils.java @@ -2,7 +2,7 @@ import lombok.Getter; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.testcontainers.DockerClientFactory; import java.io.File; diff --git a/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java index b595ddba6ca..89383698adf 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java @@ -14,7 +14,7 @@ import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.IOUtils; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.Nullable; import org.rnorth.ducttape.TimeoutException; import org.rnorth.ducttape.ratelimits.RateLimiter; diff --git a/core/src/main/java/org/testcontainers/dockerclient/NpipeSocketClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/NpipeSocketClientProviderStrategy.java index fc3c65fd3dd..7f73e1d7675 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/NpipeSocketClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/NpipeSocketClientProviderStrategy.java @@ -1,6 +1,6 @@ package org.testcontainers.dockerclient; -import org.apache.commons.lang.SystemUtils; +import org.apache.commons.lang3.SystemUtils; import java.net.URI; diff --git a/core/src/main/java/org/testcontainers/dockerclient/RootlessDockerClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/RootlessDockerClientProviderStrategy.java index 3ee58f4910a..36d6b5f6123 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/RootlessDockerClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/RootlessDockerClientProviderStrategy.java @@ -4,8 +4,8 @@ import com.sun.jna.Native; import lombok.Getter; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang.StringUtils; -import org.apache.commons.lang.SystemUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.SystemUtils; import org.jetbrains.annotations.Nullable; import java.net.URI; diff --git a/core/src/main/java/org/testcontainers/dockerclient/UnixSocketClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/UnixSocketClientProviderStrategy.java index 6f030ada279..4a85394155d 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/UnixSocketClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/UnixSocketClientProviderStrategy.java @@ -1,6 +1,6 @@ package org.testcontainers.dockerclient; -import org.apache.commons.lang.SystemUtils; +import org.apache.commons.lang3.SystemUtils; import java.io.IOException; import java.net.URI; diff --git a/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java b/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java index a434dee2b41..abf13f8ef02 100644 --- a/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java +++ b/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java @@ -9,7 +9,7 @@ import lombok.extern.slf4j.Slf4j; import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream; import org.apache.commons.io.FileUtils; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.testcontainers.DockerClientFactory; import org.testcontainers.images.ParsedDockerfile; diff --git a/core/src/main/java/org/testcontainers/images/builder/traits/StringsTrait.java b/core/src/main/java/org/testcontainers/images/builder/traits/StringsTrait.java index 51423068ec9..2b23551ee58 100644 --- a/core/src/main/java/org/testcontainers/images/builder/traits/StringsTrait.java +++ b/core/src/main/java/org/testcontainers/images/builder/traits/StringsTrait.java @@ -1,7 +1,7 @@ package org.testcontainers.images.builder.traits; import lombok.Getter; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.testcontainers.images.builder.Transferable; /** diff --git a/core/src/main/java/org/testcontainers/utility/AuditLogger.java b/core/src/main/java/org/testcontainers/utility/AuditLogger.java index 567132da05d..f6c4559485d 100644 --- a/core/src/main/java/org/testcontainers/utility/AuditLogger.java +++ b/core/src/main/java/org/testcontainers/utility/AuditLogger.java @@ -5,7 +5,7 @@ import com.github.dockerjava.api.command.DockerCmd; import lombok.experimental.UtilityClass; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.slf4j.MDC; diff --git a/core/src/main/java/org/testcontainers/utility/DockerMachineClient.java b/core/src/main/java/org/testcontainers/utility/DockerMachineClient.java index 021baf270d9..6f1e2c9d50b 100644 --- a/core/src/main/java/org/testcontainers/utility/DockerMachineClient.java +++ b/core/src/main/java/org/testcontainers/utility/DockerMachineClient.java @@ -1,7 +1,7 @@ package org.testcontainers.utility; import lombok.NonNull; -import org.apache.commons.lang.SystemUtils; +import org.apache.commons.lang3.SystemUtils; import org.slf4j.Logger; import java.util.List; diff --git a/core/src/main/java/org/testcontainers/utility/MountableFile.java b/core/src/main/java/org/testcontainers/utility/MountableFile.java index 071d36631b0..19dca4c134c 100644 --- a/core/src/main/java/org/testcontainers/utility/MountableFile.java +++ b/core/src/main/java/org/testcontainers/utility/MountableFile.java @@ -7,7 +7,7 @@ import org.apache.commons.compress.archivers.tar.TarArchiveEntry; import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream; import org.apache.commons.compress.archivers.tar.TarConstants; -import org.apache.commons.lang.SystemUtils; +import org.apache.commons.lang3.SystemUtils; import org.jetbrains.annotations.NotNull; import org.testcontainers.DockerClientFactory; import org.testcontainers.UnstableAPI; diff --git a/core/src/main/java/org/testcontainers/utility/RegistryAuthLocator.java b/core/src/main/java/org/testcontainers/utility/RegistryAuthLocator.java index b373eedaa03..d88bdccd76a 100644 --- a/core/src/main/java/org/testcontainers/utility/RegistryAuthLocator.java +++ b/core/src/main/java/org/testcontainers/utility/RegistryAuthLocator.java @@ -4,8 +4,8 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.github.dockerjava.api.model.AuthConfig; import com.google.common.annotations.VisibleForTesting; -import org.apache.commons.lang.StringUtils; -import org.apache.commons.lang.SystemUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.SystemUtils; import org.slf4j.Logger; import org.zeroturnaround.exec.InvalidResultException; import org.zeroturnaround.exec.ProcessExecutor; @@ -22,7 +22,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import static org.apache.commons.lang.StringUtils.isBlank; +import static org.apache.commons.lang3.StringUtils.isBlank; import static org.slf4j.LoggerFactory.getLogger; import static org.testcontainers.utility.AuthConfigUtil.toSafeString; diff --git a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java index bb3febd6b3f..c6ce7496dd9 100644 --- a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java +++ b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java @@ -8,7 +8,7 @@ import lombok.SneakyThrows; import lombok.Synchronized; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang.exception.ExceptionUtils; +import org.apache.commons.lang3.exception.ExceptionUtils; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; diff --git a/core/src/test/java/org/testcontainers/containers/output/ToStringConsumerTest.java b/core/src/test/java/org/testcontainers/containers/output/ToStringConsumerTest.java index 8da67e897e9..1760a8031b2 100644 --- a/core/src/test/java/org/testcontainers/containers/output/ToStringConsumerTest.java +++ b/core/src/test/java/org/testcontainers/containers/output/ToStringConsumerTest.java @@ -2,7 +2,7 @@ import static org.assertj.core.api.Assertions.assertThat; -import org.apache.commons.lang.RandomStringUtils; +import org.apache.commons.lang3.RandomStringUtils; import org.assertj.core.api.Assertions; import org.junit.Test; import org.testcontainers.containers.Container.ExecResult; diff --git a/core/src/test/java/org/testcontainers/images/builder/dockerfile/statement/AbstractStatementTest.java b/core/src/test/java/org/testcontainers/images/builder/dockerfile/statement/AbstractStatementTest.java index 35d325c5f40..15c73cadb5f 100644 --- a/core/src/test/java/org/testcontainers/images/builder/dockerfile/statement/AbstractStatementTest.java +++ b/core/src/test/java/org/testcontainers/images/builder/dockerfile/statement/AbstractStatementTest.java @@ -1,8 +1,8 @@ package org.testcontainers.images.builder.dockerfile.statement; import org.apache.commons.io.IOUtils; -import org.apache.commons.lang.StringUtils; -import org.apache.commons.lang.exception.ExceptionUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.exception.ExceptionUtils; import org.junit.Rule; import org.junit.rules.TestName; import org.rnorth.ducttape.Preconditions; @@ -31,7 +31,7 @@ protected void assertStatement(Statement statement) { IOUtils.closeQuietly(inputStream); expectedLines = StringUtils.chomp(content.replaceAll("\r\n", "\n").trim()).split("\n"); } catch (Exception e) { - fail("can't load fixture '" + testName.getMethodName() + "'\n" + ExceptionUtils.getFullStackTrace(e)); + fail("can't load fixture '" + testName.getMethodName() + "'\n" + ExceptionUtils.getStackTrace(e)); } StringBuilder builder = new StringBuilder(); diff --git a/core/src/test/java/org/testcontainers/utility/RegistryAuthLocatorTest.java b/core/src/test/java/org/testcontainers/utility/RegistryAuthLocatorTest.java index a5e5ab4260d..5db2583958a 100644 --- a/core/src/test/java/org/testcontainers/utility/RegistryAuthLocatorTest.java +++ b/core/src/test/java/org/testcontainers/utility/RegistryAuthLocatorTest.java @@ -2,7 +2,7 @@ import com.github.dockerjava.api.model.AuthConfig; import com.google.common.io.Resources; -import org.apache.commons.lang.SystemUtils; +import org.apache.commons.lang3.SystemUtils; import org.jetbrains.annotations.NotNull; import org.junit.Test; diff --git a/modules/database-commons/src/main/java/org/testcontainers/ext/ScriptUtils.java b/modules/database-commons/src/main/java/org/testcontainers/ext/ScriptUtils.java index d30064af28e..76f0cb90ea0 100644 --- a/modules/database-commons/src/main/java/org/testcontainers/ext/ScriptUtils.java +++ b/modules/database-commons/src/main/java/org/testcontainers/ext/ScriptUtils.java @@ -17,7 +17,7 @@ package org.testcontainers.ext; import org.apache.commons.io.IOUtils; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.testcontainers.delegate.DatabaseDelegate; diff --git a/modules/jdbc-test/src/main/java/org/testcontainers/jdbc/AbstractJDBCDriverTest.java b/modules/jdbc-test/src/main/java/org/testcontainers/jdbc/AbstractJDBCDriverTest.java index 580aad513e3..4e5eb03bb49 100644 --- a/modules/jdbc-test/src/main/java/org/testcontainers/jdbc/AbstractJDBCDriverTest.java +++ b/modules/jdbc-test/src/main/java/org/testcontainers/jdbc/AbstractJDBCDriverTest.java @@ -3,7 +3,7 @@ import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariDataSource; import org.apache.commons.dbutils.QueryRunner; -import org.apache.commons.lang.SystemUtils; +import org.apache.commons.lang3.SystemUtils; import org.junit.AfterClass; import org.junit.Test; import org.junit.runners.Parameterized.Parameter; diff --git a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java index cf5ae150575..c4bacf3c8ff 100644 --- a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java +++ b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java @@ -4,7 +4,7 @@ import java.sql.Statement; import lombok.NonNull; import lombok.SneakyThrows; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.NotNull; import org.testcontainers.containers.traits.LinkableContainer; import org.testcontainers.delegate.DatabaseDelegate; diff --git a/modules/k3s/src/main/java/org/testcontainers/k3s/K3sContainer.java b/modules/k3s/src/main/java/org/testcontainers/k3s/K3sContainer.java index 511b309e0d5..54cb2ff500f 100644 --- a/modules/k3s/src/main/java/org/testcontainers/k3s/K3sContainer.java +++ b/modules/k3s/src/main/java/org/testcontainers/k3s/K3sContainer.java @@ -27,9 +27,7 @@ public K3sContainer(DockerImageName dockerImageName) { addExposedPorts(6443, 8443); setPrivilegedMode(true); withCreateContainerCmdModifier(it -> { - DockerObjectAccessor.overrideRawValue( - it.getHostConfig(), "CgroupnsMode", "host" - ); + it.getHostConfig().withCgroupnsMode("host"); }); addFileSystemBind("/sys/fs/cgroup", "/sys/fs/cgroup", BindMode.READ_WRITE); diff --git a/modules/mariadb/src/test/java/org/testcontainers/junit/mariadb/SimpleMariaDBTest.java b/modules/mariadb/src/test/java/org/testcontainers/junit/mariadb/SimpleMariaDBTest.java index 4ea98bbe1fe..2dcdbafd9e3 100644 --- a/modules/mariadb/src/test/java/org/testcontainers/junit/mariadb/SimpleMariaDBTest.java +++ b/modules/mariadb/src/test/java/org/testcontainers/junit/mariadb/SimpleMariaDBTest.java @@ -1,6 +1,6 @@ package org.testcontainers.junit.mariadb; -import org.apache.commons.lang.SystemUtils; +import org.apache.commons.lang3.SystemUtils; import org.junit.Test; import org.testcontainers.containers.MariaDBContainer; import org.testcontainers.db.AbstractContainerDatabaseTest; diff --git a/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/CustomPasswordMSSQLServerTest.java b/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/CustomPasswordMSSQLServerTest.java index 3df8ff0fa3d..6ebd3624577 100644 --- a/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/CustomPasswordMSSQLServerTest.java +++ b/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/CustomPasswordMSSQLServerTest.java @@ -1,6 +1,6 @@ package org.testcontainers.junit.mssqlserver; -import org.apache.commons.lang.RandomStringUtils; +import org.apache.commons.lang3.RandomStringUtils; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; diff --git a/modules/mysql/src/test/java/org/testcontainers/junit/mysql/SimpleMySQLTest.java b/modules/mysql/src/test/java/org/testcontainers/junit/mysql/SimpleMySQLTest.java index 1b6143b3b34..90973bc4b56 100644 --- a/modules/mysql/src/test/java/org/testcontainers/junit/mysql/SimpleMySQLTest.java +++ b/modules/mysql/src/test/java/org/testcontainers/junit/mysql/SimpleMySQLTest.java @@ -1,6 +1,6 @@ package org.testcontainers.junit.mysql; -import org.apache.commons.lang.SystemUtils; +import org.apache.commons.lang3.SystemUtils; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java b/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java index c833689e692..689646f0006 100644 --- a/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java +++ b/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java @@ -1,7 +1,7 @@ package org.testcontainers.containers; import com.google.common.collect.Sets; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.NotNull; import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy; import org.testcontainers.utility.DockerImageName; @@ -34,7 +34,7 @@ public class OracleContainer extends JdbcDatabaseContainer { static final String DEFAULT_SID = "xe"; static final String DEFAULT_SYSTEM_USER = "system"; static final String DEFAULT_SYS_USER = "sys"; - + // Test container defaults static final String APP_USER = "test"; static final String APP_USER_PASSWORD = "test"; @@ -75,7 +75,7 @@ private void preconfigure() { .withRegEx(".*DATABASE IS READY TO USE!.*\\s") .withTimes(1) .withStartupTimeout(Duration.of(DEFAULT_STARTUP_TIMEOUT_SECONDS, SECONDS)); - + withConnectTimeoutSeconds(DEFAULT_CONNECT_TIMEOUT_SECONDS); addExposedPorts(ORACLE_PORT, APEX_HTTP_PORT); } @@ -98,7 +98,7 @@ public String getDriverClassName() { @Override public String getJdbcUrl() { - return isUsingSid() ? + return isUsingSid() ? "jdbc:oracle:thin:" + "@" + getHost() + ":" + getOraclePort() + ":" + getSid() : "jdbc:oracle:thin:" + "@" + getHost() + ":" + getOraclePort() + "/" + getDatabaseName(); } @@ -195,7 +195,7 @@ protected void configure() { if(databaseName != DEFAULT_DATABASE_NAME) { withEnv("ORACLE_DATABASE", databaseName); } - + withEnv("APP_USER", username); withEnv("APP_USER_PASSWORD", password); } diff --git a/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java b/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java index e47c832e2ff..12bbbc07de5 100644 --- a/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java +++ b/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java @@ -18,7 +18,7 @@ import java.util.concurrent.TimeUnit; import org.apache.commons.io.FileUtils; -import org.apache.commons.lang.SystemUtils; +import org.apache.commons.lang3.SystemUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.openqa.selenium.Capabilities; diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/BrowserWebDriverContainerTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/BrowserWebDriverContainerTest.java index 0589d764573..a53fa532f99 100644 --- a/modules/selenium/src/test/java/org/testcontainers/junit/BrowserWebDriverContainerTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/junit/BrowserWebDriverContainerTest.java @@ -2,7 +2,7 @@ import com.github.dockerjava.api.command.InspectContainerResponse; import org.apache.commons.io.FileUtils; -import org.apache.commons.lang.SystemUtils; +import org.apache.commons.lang3.SystemUtils; import org.junit.Assume; import org.junit.Test; import org.openqa.selenium.chrome.ChromeOptions; diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/LocalServerWebDriverContainerTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/LocalServerWebDriverContainerTest.java index f7d626c15b5..772193e2d43 100644 --- a/modules/selenium/src/test/java/org/testcontainers/junit/LocalServerWebDriverContainerTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/junit/LocalServerWebDriverContainerTest.java @@ -10,7 +10,7 @@ import org.testcontainers.containers.BrowserWebDriverContainer; import org.testcontainers.utility.TestEnvironment; -import static org.apache.commons.lang.SystemUtils.IS_OS_MAC_OSX; +import static org.apache.commons.lang3.SystemUtils.IS_OS_MAC_OSX; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; /** diff --git a/modules/solr/src/main/java/org/testcontainers/containers/SolrContainer.java b/modules/solr/src/main/java/org/testcontainers/containers/SolrContainer.java index 4bc2f19ec86..4d0b5cd1ae1 100644 --- a/modules/solr/src/main/java/org/testcontainers/containers/SolrContainer.java +++ b/modules/solr/src/main/java/org/testcontainers/containers/SolrContainer.java @@ -2,7 +2,7 @@ import com.github.dockerjava.api.command.InspectContainerResponse; import lombok.SneakyThrows; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy; import org.testcontainers.utility.DockerImageName; From 244c77c618bfdd9ba5dad464a22b4b56789550c2 Mon Sep 17 00:00:00 2001 From: Gerrit Meier Date: Fri, 25 Feb 2022 12:07:11 +0100 Subject: [PATCH 0025/3231] [Neo4j] Include code from tests in docs (#5120) --- docs/modules/databases/neo4j.md | 149 ++++-------------- examples/neo4j-container/build.gradle | 14 ++ .../containers/Neo4jExampleTest.java | 77 +++++++++ examples/settings.gradle | 1 + .../Neo4jContainerJUnitIntegrationTest.java | 2 +- .../containers/Neo4jContainerTest.java | 27 +++- .../containers/Neo4jTestImages.java | 7 - 7 files changed, 143 insertions(+), 134 deletions(-) create mode 100644 examples/neo4j-container/build.gradle create mode 100644 examples/neo4j-container/src/test/java/org/testcontainers/containers/Neo4jExampleTest.java delete mode 100644 modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jTestImages.java diff --git a/docs/modules/databases/neo4j.md b/docs/modules/databases/neo4j.md index 17e1e154fc8..87a5d9157d1 100644 --- a/docs/modules/databases/neo4j.md +++ b/docs/modules/databases/neo4j.md @@ -6,68 +6,18 @@ Note that it's based on the [official Docker image](https://hub.docker.com/_/neo ## Usage example -Declare your Testcontainer as a `@ClassRule` or `@Rule` in a JUnit 4 test or as static or member attribute of a JUnit 5 test annotated with `@Container` as you would with other Testcontainers. -You can either use call `getHttpUrl()` or `getBoltUrl()` on the Neo4j container. -`getHttpUrl()` gives you the HTTP-address of the transactional HTTP endpoint while `getBoltUrl()` is meant to be used with one of the [official Bolt drivers](https://neo4j.com/developer/language-guides/). +Declare your Testcontainers as a `@ClassRule` or `@Rule` in a JUnit 4 test or as static or member attribute of a JUnit 5 test annotated with `@Container` as you would with other Testcontainers. +You can either use call `getBoltUrl()` or `getHttpUrl()` on the Neo4j container. +`getBoltUrl()` is meant to be used with one of the [official Bolt drivers](https://neo4j.com/developer/language-guides/) while `getHttpUrl()` gives you the HTTP-address of the transactional HTTP endpoint. On the JVM you would most likely use the [Java driver](https://github.com/neo4j/neo4j-java-driver). The following example uses the JUnit 5 extension `@Testcontainers` and demonstrates both the usage of the Java Driver and the REST endpoint: -=== "JUnit 5 example" - ```java - @Testcontainers - public class ExampleTest { - - @Container - private static Neo4jContainer neo4jContainer = new Neo4jContainer() - .withAdminPassword(null); // Disable password - - @Test - void testSomethingUsingBolt() { - - // Retrieve the Bolt URL from the container - String boltUrl = neo4jContainer.getBoltUrl(); - try ( - Driver driver = GraphDatabase.driver(boltUrl, AuthTokens.none()); - Session session = driver.session() - ) { - long one = session.run("RETURN 1", Collections.emptyMap()).next().get(0).asLong(); - assertThat(one, is(1L)); - } catch (Exception e) { - fail(e.getMessage()); - } - } - - @Test - void testSomethingUsingHttp() throws IOException { - - // Retrieve the HTTP URL from the container - String httpUrl = neo4jContainer.getHttpUrl(); - - URL url = new URL(httpUrl + "/db/data/transaction/commit"); - HttpURLConnection con = (HttpURLConnection) url.openConnection(); - - con.setRequestMethod("POST"); - con.setRequestProperty("Content-Type", "application/json"); - con.setDoOutput(true); - - try (Writer out = new OutputStreamWriter(con.getOutputStream())) { - out.write("{\"statements\":[{\"statement\":\"RETURN 1\"}]}"); - out.flush(); - } - - assertThat(con.getResponseCode(), is(HttpURLConnection.HTTP_OK)); - try (BufferedReader buffer = new BufferedReader(new InputStreamReader(con.getInputStream()))) { - String expectedResponse = - "{\"results\":[{\"columns\":[\"1\"],\"data\":[{\"row\":[1],\"meta\":[null]}]}],\"errors\":[]}"; - String response = buffer.lines().collect(Collectors.joining("\n")); - assertThat(response, is(expectedResponse)); - } - } - } - ``` + +[JUnit 5 example](../../../examples/neo4j-container/src/test/java/org/testcontainers/containers/Neo4jExampleTest.java) inside_block:junitExample + -You are not limited to Unit tests and can of course use an instance of the Neo4j Testcontainer in vanilla Java code as well. +You are not limited to Unit tests and can of course use an instance of the Neo4j Testcontainers in vanilla Java code as well. ## Additional features @@ -75,70 +25,40 @@ You are not limited to Unit tests and can of course use an instance of the Neo4j Authentication can be disabled: -```java -@Testcontainers -public class ExampleTest { - - @Container - Neo4jContainer neo4jContainer = new Neo4jContainer() - .withoutAuthentication(); -} -``` + +[Disable authentication](../../../modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerTest.java) inside_block:withoutAuthentication + ### Neo4j-Configuration Neo4j's Docker image needs Neo4j configuration options in a dedicated format. -The container takes care of that and you can configure the database with standard options like the following: - -```java -@Testcontainers -public class ExampleTest { +The container takes care of that, and you can configure the database with standard options like the following: - @Container - Neo4jContainer neo4jContainer = new Neo4jContainer() - .withNeo4jConfig("dbms.security.procedures.unrestricted", "apoc.*,algo.*"); -} -``` + +[Neo4j configuration](../../../modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerTest.java) inside_block:neo4jConfiguration + ### Add custom plugins Custom plugins, like APOC, can be copied over to the container from any classpath or host resource like this: -```java -@Testcontainers -public class ExampleTest { - - @Container - Neo4jContainer neo4jContainer = new Neo4jContainer() - .withPlugins(MountableFile.forClasspathResource("/apoc-3.5.0.1-all.jar")); -} -``` + +[Plugin jar](../../../modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerTest.java) inside_block:registerPluginsJar + Whole directories work as well: -```java -@Testcontainers -public class ExampleTest { - - @Container - Neo4jContainer neo4jContainer = new Neo4jContainer() - .withPlugins(MountableFile.forClasspathResource("/my-plugins")); -} -``` + +[Plugin folder](../../../modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerTest.java) inside_block:registerPluginsPath + ### Start the container with a predefined database If you have an existing database (`graph.db`) you want to work with, copy it over to the container like this: -```java -@Testcontainers -public class ExampleTest { - - @Container - Neo4jContainer neo4jContainer = new Neo4jContainer() - .withDatabase(MountableFile.forClasspathResource("/test-graph.db")); -} -``` + +[Copy database](../../../modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerTest.java) inside_block:copyDatabase + !!! note The `withDatabase` method will only work with Neo4j 3.5 and throw an exception if used in combination with a newer version. @@ -147,16 +67,11 @@ The `withDatabase` method will only work with Neo4j 3.5 and throw an exception i If you need the Neo4j enterprise license, you can declare your Neo4j container like this: -```java -@Testcontainers -public class ExampleTest { - @ClassRule - public static Neo4jContainer neo4jContainer = new Neo4jContainer() - .withEnterpriseEdition(); -} -``` + +[Enterprise edition](../../../modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerTest.java) inside_block:enterpriseEdition + -This creates a Testcontainer based on the Docker image build with the Enterprise version of Neo4j. +This creates a Testcontainers based on the Docker image build with the Enterprise version of Neo4j. The call to `withEnterpriseEdition` adds the required environment variable that you accepted the terms and condition of the enterprise version. You accept those by adding a file named `container-license-acceptance.txt` to the root of your classpath containing the text `neo4j:3.5.0-enterprise` in one line. You'll find more information about licensing Neo4j here: [About Neo4j Licenses](https://neo4j.com/licensing/). @@ -181,11 +96,11 @@ Add the following dependency to your `pom.xml`/`build.gradle` file: ``` !!! hint - Add the Neo4j Java driver if you plan to access the Testcontainer via Bolt: + Add the Neo4j Java driver if you plan to access the Testcontainers via Bolt: === "Gradle" ```groovy - compile "org.neo4j.driver:neo4j-java-driver:1.7.1" + compile "org.neo4j.driver:neo4j-java-driver:4.4.3" ``` === "Maven" @@ -193,10 +108,6 @@ Add the following dependency to your `pom.xml`/`build.gradle` file: org.neo4j.driver neo4j-java-driver - 1.7.1 + 4.4.3 ``` - - - - diff --git a/examples/neo4j-container/build.gradle b/examples/neo4j-container/build.gradle new file mode 100644 index 00000000000..718adc3183a --- /dev/null +++ b/examples/neo4j-container/build.gradle @@ -0,0 +1,14 @@ +plugins { + id 'java' +} + +repositories { + mavenCentral() +} + +dependencies { + testImplementation 'org.assertj:assertj-core:3.22.0' + testImplementation 'org.neo4j.driver:neo4j-java-driver:4.4.3' + testImplementation 'org.testcontainers:neo4j' + testImplementation 'org.testcontainers:junit-jupiter' +} diff --git a/examples/neo4j-container/src/test/java/org/testcontainers/containers/Neo4jExampleTest.java b/examples/neo4j-container/src/test/java/org/testcontainers/containers/Neo4jExampleTest.java new file mode 100644 index 00000000000..7730d4fa8d0 --- /dev/null +++ b/examples/neo4j-container/src/test/java/org/testcontainers/containers/Neo4jExampleTest.java @@ -0,0 +1,77 @@ +package org.testcontainers.containers; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.Writer; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.Collections; +import java.util.stream.Collectors; + +import org.junit.jupiter.api.Test; +import org.neo4j.driver.AuthTokens; +import org.neo4j.driver.Driver; +import org.neo4j.driver.GraphDatabase; +import org.neo4j.driver.Session; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; +import org.testcontainers.utility.DockerImageName; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.fail; + +// junitExample { +@Testcontainers +public class Neo4jExampleTest { + + @Container + private static Neo4jContainer neo4jContainer = + new Neo4jContainer<>(DockerImageName.parse("neo4j:4.4")) + .withAdminPassword(null); // Disable password + + @Test + void testSomethingUsingBolt() { + + // Retrieve the Bolt URL from the container + String boltUrl = neo4jContainer.getBoltUrl(); + try ( + Driver driver = GraphDatabase.driver(boltUrl, AuthTokens.none()); + Session session = driver.session() + ) { + long one = session.run("RETURN 1", Collections.emptyMap()).next().get(0).asLong(); + assertThat(one).isEqualTo(1L); + } catch (Exception e) { + fail(e.getMessage()); + } + } + + @Test + void testSomethingUsingHttp() throws IOException { + + // Retrieve the HTTP URL from the container + String httpUrl = neo4jContainer.getHttpUrl(); + + URL url = new URL(httpUrl + "/db/data/transaction/commit"); + HttpURLConnection con = (HttpURLConnection) url.openConnection(); + + con.setRequestMethod("POST"); + con.setRequestProperty("Content-Type", "application/json"); + con.setDoOutput(true); + + try (Writer out = new OutputStreamWriter(con.getOutputStream())) { + out.write("{\"statements\":[{\"statement\":\"RETURN 1\"}]}"); + out.flush(); + } + + assertThat(con.getResponseCode()).isEqualTo(HttpURLConnection.HTTP_OK); + try (BufferedReader buffer = new BufferedReader(new InputStreamReader(con.getInputStream()))) { + String expectedResponse = + "{\"results\":[{\"columns\":[\"1\"],\"data\":[{\"row\":[1],\"meta\":[null]}]}],\"errors\":[]}"; + String response = buffer.lines().collect(Collectors.joining("\n")); + assertThat(response).isEqualTo(expectedResponse); + } + } +} +// } diff --git a/examples/settings.gradle b/examples/settings.gradle index f58ea83fb9b..75f739e9005 100644 --- a/examples/settings.gradle +++ b/examples/settings.gradle @@ -23,6 +23,7 @@ include 'disque-job-queue' include 'kafka-cluster' include 'linked-container' include 'mongodb-container' +include 'neo4j-container' include 'redis-backed-cache' include 'redis-backed-cache-testng' include 'selenium-container' diff --git a/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerJUnitIntegrationTest.java b/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerJUnitIntegrationTest.java index ad6ccad8281..6832dfff0c7 100644 --- a/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerJUnitIntegrationTest.java +++ b/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerJUnitIntegrationTest.java @@ -20,7 +20,7 @@ public class Neo4jContainerJUnitIntegrationTest { @ClassRule - public static Neo4jContainer neo4jContainer = new Neo4jContainer<>(Neo4jTestImages.NEO4J_TEST_IMAGE); + public static Neo4jContainer neo4jContainer = new Neo4jContainer<>("neo4j:4.4"); @Test public void shouldStart() { diff --git a/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerTest.java b/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerTest.java index fcfda6f6873..25924fdd6b4 100644 --- a/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerTest.java +++ b/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerTest.java @@ -9,6 +9,7 @@ import org.neo4j.driver.Record; import org.neo4j.driver.Result; import org.neo4j.driver.Session; +import org.testcontainers.utility.DockerImageName; import org.testcontainers.utility.MountableFile; import java.util.Collections; @@ -31,7 +32,10 @@ public class Neo4jContainerTest { public void shouldDisableAuthentication() { try ( - Neo4jContainer neo4jContainer = new Neo4jContainer<>(Neo4jTestImages.NEO4J_TEST_IMAGE).withoutAuthentication() + // withoutAuthentication { + Neo4jContainer neo4jContainer = new Neo4jContainer<>("neo4j:4.4") + .withoutAuthentication() + // } ) { neo4jContainer.start(); try (Driver driver = getDriver(neo4jContainer); @@ -46,8 +50,10 @@ public void shouldDisableAuthentication() { @Test public void shouldCopyDatabase() { try ( + // copyDatabase { Neo4jContainer neo4jContainer = new Neo4jContainer<>("neo4j:3.5.30") .withDatabase(MountableFile.forClasspathResource("/test-graph.db")) + // } ) { neo4jContainer.start(); try ( @@ -88,8 +94,10 @@ public void shouldFailOnCopyDatabaseForCustomNonSemverNeo4j4Image() { @Test public void shouldCopyPlugins() { try ( - Neo4jContainer neo4jContainer = new Neo4jContainer<>(Neo4jTestImages.NEO4J_TEST_IMAGE) + // registerPluginsPath { + Neo4jContainer neo4jContainer = new Neo4jContainer<>("neo4j:4.4") .withPlugins(MountableFile.forClasspathResource("/custom-plugins")) + // } ) { neo4jContainer.start(); try ( @@ -104,8 +112,10 @@ public void shouldCopyPlugins() { @Test public void shouldCopyPlugin() { try ( - Neo4jContainer neo4jContainer = new Neo4jContainer<>(Neo4jTestImages.NEO4J_TEST_IMAGE) + // registerPluginsJar { + Neo4jContainer neo4jContainer = new Neo4jContainer<>("neo4j:4.4") .withPlugins(MountableFile.forClasspathResource("/custom-plugins/hello-world.jar")) + // } ) { neo4jContainer.start(); try ( @@ -131,7 +141,7 @@ public void shouldCheckEnterpriseLicense() { String expectedImageName = "neo4j:4.4-enterprise"; assertThatExceptionOfType(IllegalStateException.class) - .isThrownBy(() -> new Neo4jContainer<>(Neo4jTestImages.NEO4J_TEST_IMAGE).withEnterpriseEdition()) + .isThrownBy(() -> new Neo4jContainer<>("neo4j:4.4").withEnterpriseEdition()) .withMessageContaining("The image " + expectedImageName + " requires you to accept a license agreement."); } @@ -140,8 +150,10 @@ public void shouldRunEnterprise() { assumeThat(Neo4jContainerTest.class.getResource(ACCEPTANCE_FILE_LOCATION)).isNotNull(); try ( - Neo4jContainer neo4jContainer = new Neo4jContainer<>(Neo4jTestImages.NEO4J_TEST_IMAGE) + // enterpriseEdition { + Neo4jContainer neo4jContainer = new Neo4jContainer<>("neo4j:4.4") .withEnterpriseEdition() + // } .withAdminPassword("Picard123") ) { neo4jContainer.start(); @@ -159,10 +171,11 @@ public void shouldRunEnterprise() { @Test public void shouldAddConfigToEnvironment() { - - Neo4jContainer neo4jContainer = new Neo4jContainer<>(Neo4jTestImages.NEO4J_TEST_IMAGE) + // neo4jConfiguration { + Neo4jContainer neo4jContainer = new Neo4jContainer<>("neo4j:4.4") .withNeo4jConfig("dbms.security.procedures.unrestricted", "apoc.*,algo.*") .withNeo4jConfig("dbms.tx_log.rotation.size", "42M"); + // } assertThat(neo4jContainer.getEnvMap()) .containsEntry("NEO4J_dbms_security_procedures_unrestricted", "apoc.*,algo.*"); diff --git a/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jTestImages.java b/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jTestImages.java deleted file mode 100644 index aee6f29fc5f..00000000000 --- a/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jTestImages.java +++ /dev/null @@ -1,7 +0,0 @@ -package org.testcontainers.containers; - -import org.testcontainers.utility.DockerImageName; - -public interface Neo4jTestImages { - DockerImageName NEO4J_TEST_IMAGE = DockerImageName.parse("neo4j:4.4.1"); -} From cbccd499fbc9f882a8d4da0f7254796a1ea9d28f Mon Sep 17 00:00:00 2001 From: hellozepp Date: Tue, 1 Mar 2022 00:54:49 +0800 Subject: [PATCH 0026/3231] Add the using organization `Byzer` (#5125) --- docs/index.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/index.md b/docs/index.md index 998722af50f..fdbad94b851 100644 --- a/docs/index.md +++ b/docs/index.md @@ -198,6 +198,7 @@ A huge thank you to our sponsors: * [Elastic](https://www.elastic.co) - Integration testing of the Java APM agent * [Alkira](https://www.alkira.com/) - Testing of multiple micro-services using Kafka, PostgreSQL, Apache Zookeeper, Etcd and so on. * [Togglz](https://www.togglz.org/) - Feature Flags for the Java platform +* [Byzer](https://www.byzer.org/home) - Integration tests for Data and AI platforms are based on multiple versions of Byzer, Ray and Apache Spark. ## License From d21a29e7a4de2c0d82cf2ae4b6c9eb7d85533103 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Mar 2022 22:08:33 +0000 Subject: [PATCH 0027/3231] Bump aws-java-sdk-s3 from 1.12.150 to 1.12.169 in /modules/localstack Bumps [aws-java-sdk-s3](https://github.com/aws/aws-sdk-java) from 1.12.150 to 1.12.169. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.150...1.12.169) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-s3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 8dc6dc2671f..7a076881a96 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -3,8 +3,8 @@ description = "Testcontainers :: Localstack" dependencies { api project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.150' - testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.137' + compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.169' + testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.169' testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.150' testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.139' testImplementation 'software.amazon.awssdk:s3:2.17.108' From 43f807dc8ba58c5cbab61880e24c0ce2fd3595a9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Mar 2022 22:08:50 +0000 Subject: [PATCH 0028/3231] Bump neo4j from 3.5.29 to 3.5.31 in /modules/neo4j Bumps [neo4j](https://github.com/neo4j/neo4j) from 3.5.29 to 3.5.31. - [Release notes](https://github.com/neo4j/neo4j/releases) - [Commits](https://github.com/neo4j/neo4j/commits) --- updated-dependencies: - dependency-name: org.neo4j:neo4j dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/neo4j/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/neo4j/build.gradle b/modules/neo4j/build.gradle index 1d5cffc1955..27bf38b9f23 100644 --- a/modules/neo4j/build.gradle +++ b/modules/neo4j/build.gradle @@ -29,7 +29,7 @@ task customNeo4jPluginJar(type: Jar) { test.dependsOn customNeo4jPluginJar dependencies { - customNeo4jPluginCompileOnly "org.neo4j:neo4j:3.5.29" + customNeo4jPluginCompileOnly "org.neo4j:neo4j:3.5.31" api project(":testcontainers") From 0343dacda34bccd074700810c4f7c53d0ab74c53 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Mar 2022 22:08:51 +0000 Subject: [PATCH 0029/3231] Bump aws-java-sdk-logs from 1.12.139 to 1.12.169 in /modules/localstack Bumps [aws-java-sdk-logs](https://github.com/aws/aws-sdk-java) from 1.12.139 to 1.12.169. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.139...1.12.169) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-logs dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 8dc6dc2671f..fda140b12da 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -6,7 +6,7 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.150' testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.137' testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.150' - testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.139' + testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.169' testImplementation 'software.amazon.awssdk:s3:2.17.108' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } From 7c75b0a3040721b35b592a747c2097fa1abf977b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Mar 2022 22:09:32 +0000 Subject: [PATCH 0030/3231] Bump guava from 31.0.1-jre to 31.1-jre in /modules/jdbc-test Bumps [guava](https://github.com/google/guava) from 31.0.1-jre to 31.1-jre. - [Release notes](https://github.com/google/guava/releases) - [Commits](https://github.com/google/guava/commits) --- updated-dependencies: - dependency-name: com.google.guava:guava dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- modules/jdbc-test/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jdbc-test/build.gradle b/modules/jdbc-test/build.gradle index 98570722e6a..314b3ab6a2d 100644 --- a/modules/jdbc-test/build.gradle +++ b/modules/jdbc-test/build.gradle @@ -2,7 +2,7 @@ dependencies { api project(':jdbc') api project(':test-support') - api 'com.google.guava:guava:31.0.1-jre' + api 'com.google.guava:guava:31.1-jre' api 'org.apache.commons:commons-lang3:3.12.0' api 'com.zaxxer:HikariCP-java6:2.3.13' api 'commons-dbutils:commons-dbutils:1.7' From e17bc43e70dc3eaee7224eb1b4ace75c905194a2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Mar 2022 22:09:45 +0000 Subject: [PATCH 0031/3231] Bump transport from 7.17.0 to 7.17.1 in /modules/elasticsearch Bumps [transport](https://github.com/elastic/elasticsearch) from 7.17.0 to 7.17.1. - [Release notes](https://github.com/elastic/elasticsearch/releases) - [Changelog](https://github.com/elastic/elasticsearch/blob/master/CHANGELOG.md) - [Commits](https://github.com/elastic/elasticsearch/compare/v7.17.0...v7.17.1) --- updated-dependencies: - dependency-name: org.elasticsearch.client:transport dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/elasticsearch/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/elasticsearch/build.gradle b/modules/elasticsearch/build.gradle index fca8ecfe9fb..d669f0fbd05 100644 --- a/modules/elasticsearch/build.gradle +++ b/modules/elasticsearch/build.gradle @@ -3,6 +3,6 @@ description = "TestContainers :: elasticsearch" dependencies { api project(':testcontainers') testImplementation "org.elasticsearch.client:elasticsearch-rest-client:7.16.2" - testImplementation "org.elasticsearch.client:transport:7.17.0" + testImplementation "org.elasticsearch.client:transport:7.17.1" testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } From d7cb81c9a97deb39cd6d7c100853cb7ccb358476 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Mar 2022 22:09:46 +0000 Subject: [PATCH 0032/3231] Bump org.springframework.boot from 2.6.3 to 2.6.4 in /examples Bumps [org.springframework.boot](https://github.com/spring-projects/spring-boot) from 2.6.3 to 2.6.4. - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v2.6.3...v2.6.4) --- updated-dependencies: - dependency-name: org.springframework.boot dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/selenium-container/build.gradle | 2 +- examples/spring-boot-kotlin-redis/build.gradle | 2 +- examples/spring-boot/build.gradle | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/selenium-container/build.gradle b/examples/selenium-container/build.gradle index d728c13e3c0..7677e82eefb 100644 --- a/examples/selenium-container/build.gradle +++ b/examples/selenium-container/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id 'org.springframework.boot' version '2.6.3' + id 'org.springframework.boot' version '2.6.4' } apply plugin: 'io.spring.dependency-management' diff --git a/examples/spring-boot-kotlin-redis/build.gradle b/examples/spring-boot-kotlin-redis/build.gradle index 5a9495d86bf..45cee0ec20b 100644 --- a/examples/spring-boot-kotlin-redis/build.gradle +++ b/examples/spring-boot-kotlin-redis/build.gradle @@ -1,5 +1,5 @@ plugins { - id("org.springframework.boot") version "2.6.3" + id("org.springframework.boot") version "2.6.4" id("org.jetbrains.kotlin.jvm") version "1.6.10" id("org.jetbrains.kotlin.plugin.spring") version "1.6.10" } diff --git a/examples/spring-boot/build.gradle b/examples/spring-boot/build.gradle index ace47d12ebe..e3b3376e28f 100644 --- a/examples/spring-boot/build.gradle +++ b/examples/spring-boot/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id 'org.springframework.boot' version '2.6.3' + id 'org.springframework.boot' version '2.6.4' } apply plugin: 'io.spring.dependency-management' From 99578c4b2a2e45b1b7efe4638eb1af4449cbb667 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Mar 2022 22:10:10 +0000 Subject: [PATCH 0033/3231] Bump aws-java-sdk-dynamodb in /modules/dynalite Bumps [aws-java-sdk-dynamodb](https://github.com/aws/aws-sdk-java) from 1.12.150 to 1.12.169. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.150...1.12.169) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-dynamodb dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/dynalite/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/dynalite/build.gradle b/modules/dynalite/build.gradle index ce09dde17e6..2fa5978772e 100644 --- a/modules/dynalite/build.gradle +++ b/modules/dynalite/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Dynalite" dependencies { api project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.12.150' + compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.12.169' testImplementation 'com.amazonaws:aws-java-sdk-dynamodb:1.12.150' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' From 6904d8e21b5a248420c2373d0f8efcefd6a7000d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Mar 2022 22:10:28 +0000 Subject: [PATCH 0034/3231] Bump actions/setup-java from 2.5.0 to 3 Bumps [actions/setup-java](https://github.com/actions/setup-java) from 2.5.0 to 3. - [Release notes](https://github.com/actions/setup-java/releases) - [Commits](https://github.com/actions/setup-java/compare/v2.5.0...v3) --- updated-dependencies: - dependency-name: actions/setup-java dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/ci-examples.yml | 4 ++-- .github/workflows/ci.yml | 4 ++-- .github/workflows/release.yml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci-examples.yml b/.github/workflows/ci-examples.yml index d7351605e6d..3fb2467a295 100644 --- a/.github/workflows/ci-examples.yml +++ b/.github/workflows/ci-examples.yml @@ -17,7 +17,7 @@ jobs: matrix: ${{ steps.set-matrix.outputs.matrix }} steps: - uses: actions/checkout@v2 - - uses: actions/setup-java@v2.5.0 + - uses: actions/setup-java@v3 with: java-version: '8.0.302' distribution: temurin @@ -45,7 +45,7 @@ jobs: runs-on: ubuntu-18.04 steps: - uses: actions/checkout@v2 - - uses: actions/setup-java@v2.5.0 + - uses: actions/setup-java@v3 with: java-version: '8.0.302' distribution: temurin diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0fd59f892f0..db7232fffa8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,7 +25,7 @@ jobs: matrix: ${{ steps.set-matrix.outputs.matrix }} steps: - uses: actions/checkout@v2 - - uses: actions/setup-java@v2.5.0 + - uses: actions/setup-java@v3 with: java-version: '8.0.302' distribution: temurin @@ -51,7 +51,7 @@ jobs: runs-on: ubuntu-18.04 steps: - uses: actions/checkout@v2 - - uses: actions/setup-java@v2.5.0 + - uses: actions/setup-java@v3 with: java-version: '8.0.302' distribution: temurin diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d9026eb2257..f6df0dd46f4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-18.04 steps: - uses: actions/checkout@v2 - - uses: actions/setup-java@v2.5.0 + - uses: actions/setup-java@v3 with: java-version: '8.0.302' distribution: temurin From 5d2806f3bc4a01660cc9f550a37a9213946e62f6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Mar 2022 22:18:26 +0000 Subject: [PATCH 0035/3231] Bump google-cloud-spanner from 6.17.4 to 6.20.0 in /modules/gcloud Bumps [google-cloud-spanner](https://github.com/googleapis/java-spanner) from 6.17.4 to 6.20.0. - [Release notes](https://github.com/googleapis/java-spanner/releases) - [Changelog](https://github.com/googleapis/java-spanner/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/java-spanner/compare/v6.17.4...v6.20.0) --- updated-dependencies: - dependency-name: com.google.cloud:google-cloud-spanner dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/gcloud/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle index 72e304029ce..0e5ebee97dd 100644 --- a/modules/gcloud/build.gradle +++ b/modules/gcloud/build.gradle @@ -6,7 +6,7 @@ dependencies { testImplementation 'com.google.cloud:google-cloud-datastore:2.2.2' testImplementation 'com.google.cloud:google-cloud-firestore:3.0.11' testImplementation 'com.google.cloud:google-cloud-pubsub:1.115.1' - testImplementation 'com.google.cloud:google-cloud-spanner:6.17.4' + testImplementation 'com.google.cloud:google-cloud-spanner:6.20.0' testImplementation 'com.google.cloud:google-cloud-bigtable:2.5.2' testImplementation 'org.assertj:assertj-core:3.22.0' } From 03b5a75d391600f716c5a514d58eb79c985baa42 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Mar 2022 22:18:30 +0000 Subject: [PATCH 0036/3231] Bump google-cloud-firestore from 3.0.11 to 3.0.14 in /modules/gcloud Bumps [google-cloud-firestore](https://github.com/googleapis/java-firestore) from 3.0.11 to 3.0.14. - [Release notes](https://github.com/googleapis/java-firestore/releases) - [Changelog](https://github.com/googleapis/java-firestore/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/java-firestore/compare/v3.0.11...v3.0.14) --- updated-dependencies: - dependency-name: com.google.cloud:google-cloud-firestore dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/gcloud/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle index 72e304029ce..7d17a689ecf 100644 --- a/modules/gcloud/build.gradle +++ b/modules/gcloud/build.gradle @@ -4,7 +4,7 @@ dependencies { api project(':testcontainers') testImplementation 'com.google.cloud:google-cloud-datastore:2.2.2' - testImplementation 'com.google.cloud:google-cloud-firestore:3.0.11' + testImplementation 'com.google.cloud:google-cloud-firestore:3.0.14' testImplementation 'com.google.cloud:google-cloud-pubsub:1.115.1' testImplementation 'com.google.cloud:google-cloud-spanner:6.17.4' testImplementation 'com.google.cloud:google-cloud-bigtable:2.5.2' From 30e8635e7e5b8160e66f014d4055a20b628e8499 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Thu, 3 Mar 2022 06:55:50 -0500 Subject: [PATCH 0037/3231] Add the session header to every Docker request (#5145) To improve tracing and other operations, we add current session's id to every Docker request that we are making. --- .../DockerClientProviderStrategy.java | 7 ++++- .../HeadersAddingDockerHttpClient.java | 27 +++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 core/src/main/java/org/testcontainers/dockerclient/HeadersAddingDockerHttpClient.java diff --git a/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java index 89383698adf..c65887b6af6 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java @@ -20,10 +20,12 @@ import org.rnorth.ducttape.ratelimits.RateLimiter; import org.rnorth.ducttape.ratelimits.RateLimiterBuilder; import org.rnorth.ducttape.unreliables.Unreliables; +import org.testcontainers.DockerClientFactory; import org.testcontainers.utility.TestcontainersConfiguration; import java.net.URI; import java.util.ArrayList; +import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import java.util.List; @@ -282,7 +284,10 @@ public static DockerClient getClientForConfig(TransportConfig transportConfig) { .withDockerHost(transportConfig.getDockerHost().toString()) .build() ), - dockerHttpClient + new HeadersAddingDockerHttpClient( + dockerHttpClient, + Collections.singletonMap("x-tc-sid", DockerClientFactory.SESSION_ID) + ) ); } diff --git a/core/src/main/java/org/testcontainers/dockerclient/HeadersAddingDockerHttpClient.java b/core/src/main/java/org/testcontainers/dockerclient/HeadersAddingDockerHttpClient.java new file mode 100644 index 00000000000..59a51e19a80 --- /dev/null +++ b/core/src/main/java/org/testcontainers/dockerclient/HeadersAddingDockerHttpClient.java @@ -0,0 +1,27 @@ +package org.testcontainers.dockerclient; + +import com.github.dockerjava.transport.DockerHttpClient; +import lombok.RequiredArgsConstructor; +import lombok.ToString; +import lombok.experimental.Delegate; + +import java.io.Closeable; +import java.util.Map; + +@RequiredArgsConstructor +@ToString +class HeadersAddingDockerHttpClient implements DockerHttpClient { + + @Delegate(types = Closeable.class) + final DockerHttpClient delegate; + + final Map headers; + + @Override + public Response execute(Request request) { + request = Request.builder().from(request) + .putAllHeaders(headers) + .build(); + return delegate.execute(request); + } +} From 71a0679a675f5474f8f6c869827de6402d1819b9 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Thu, 3 Mar 2022 14:45:15 -0500 Subject: [PATCH 0038/3231] Run tests using shaded dependencies (#5146) * Test shaded JARs * Find shaded projects recursively * Add missing space * fix core tests * fix HiveMQ module * Simplify the HiveMQ workaround * Add a comment that explains why we pin Jackson version in k3s --- ...temPropertyClientProviderStrategyTest.java | 26 +++++++++---------- gradle/shading.gradle | 12 +++++++++ .../hivemq/HiveMQExtension.java | 16 +++++++++--- modules/k3s/build.gradle | 5 +++- 4 files changed, 42 insertions(+), 17 deletions(-) diff --git a/core/src/test/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategyTest.java b/core/src/test/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategyTest.java index c8eec1d38d8..a6bd68be745 100644 --- a/core/src/test/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategyTest.java +++ b/core/src/test/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategyTest.java @@ -1,8 +1,8 @@ package org.testcontainers.dockerclient; import com.github.dockerjava.core.DefaultDockerClientConfig; -import com.github.dockerjava.core.LocalDirectorySSLConfig; import com.github.dockerjava.transport.SSLConfig; +import org.assertj.core.api.Assertions; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -16,15 +16,12 @@ import java.net.URI; import java.nio.file.Files; import java.nio.file.Path; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; +import java.util.Properties; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isNull; @@ -93,21 +90,24 @@ public void testWhenDockerHostAndSSLConfigPresent() throws IOException { assertEquals("tcp://1.2.3.4:2375", transportConfig.getDockerHost().toString()); SSLConfig sslConfig = transportConfig.getSslConfig(); - assertNotNull(sslConfig); - assertTrue(sslConfig instanceof LocalDirectorySSLConfig); - assertEquals(tempDirPath, ((LocalDirectorySSLConfig) sslConfig).getDockerCertPath()); + Assertions.assertThat(sslConfig) + .extracting("dockerCertPath") + .isEqualTo(tempDirPath); } @Test public void applicableWhenIgnoringUserPropertiesAndConfigured() { Mockito.doReturn("autoIgnoringUserProperties").when(TestcontainersConfiguration.getInstance()).getEnvVarOrProperty(eq("dockerconfig.source"), anyString()); - DefaultDockerClientConfig.Builder configBuilder = DefaultDockerClientConfig.createDefaultConfigBuilder() - .withDockerHost("tcp://1.2.3.4:2375"); + Properties oldProperties = System.getProperties(); + try { + System.setProperty("DOCKER_HOST", "tcp://1.2.3.4:2375"); + EnvironmentAndSystemPropertyClientProviderStrategy strategy = new EnvironmentAndSystemPropertyClientProviderStrategy(); - EnvironmentAndSystemPropertyClientProviderStrategy strategy = new EnvironmentAndSystemPropertyClientProviderStrategy(configBuilder); - - assertTrue(strategy.isApplicable()); + assertTrue(strategy.isApplicable()); + } finally { + System.setProperties(oldProperties); + } } @Test diff --git a/gradle/shading.gradle b/gradle/shading.gradle index 909989caf84..fc7f6587b3e 100644 --- a/gradle/shading.gradle +++ b/gradle/shading.gradle @@ -55,4 +55,16 @@ project.afterEvaluate { tasks.shadowJar.relocate(pkg, "org.testcontainers.shaded.${pkg}") } + + // Add shaded JARs first + tasks.test.classpath = findShadedProjects(project) + .plus(project) + .sum { it.tasks.findByName("shadowJar").outputs.files } + .plus(sourceSets.test.runtimeClasspath) +} + +static Set findShadedProjects(Project project) { + Set dependencies = project.configurations.api.dependencies.withType(ProjectDependency)*.dependencyProject + + return dependencies + dependencies.collectMany { it -> findShadedProjects(it) } } diff --git a/modules/hivemq/src/main/java/org/testcontainers/hivemq/HiveMQExtension.java b/modules/hivemq/src/main/java/org/testcontainers/hivemq/HiveMQExtension.java index a975439f4b3..934dfd0c29c 100644 --- a/modules/hivemq/src/main/java/org/testcontainers/hivemq/HiveMQExtension.java +++ b/modules/hivemq/src/main/java/org/testcontainers/hivemq/HiveMQExtension.java @@ -4,9 +4,12 @@ import javassist.NotFoundException; import lombok.Getter; import org.apache.commons.io.FileUtils; +import org.jboss.shrinkwrap.api.ExtensionLoader; import org.jboss.shrinkwrap.api.ShrinkWrap; import org.jboss.shrinkwrap.api.exporter.ZipExporter; import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.jboss.shrinkwrap.impl.base.exporter.zip.ZipExporterImpl; +import org.jboss.shrinkwrap.impl.base.spec.JavaArchiveImpl; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; @@ -101,9 +104,16 @@ private HiveMQExtension( } } - final JavaArchive javaArchive = - ShrinkWrap.create(JavaArchive.class) - .addAsServiceProvider(EXTENSION_MAIN_CLASS_NAME, hiveMQExtension.getMainClass().getName()); + // Shadow Gradle plugin doesn't know how to handle ShrinkWrap's SPI definitions + // This workaround creates the mappings programmatically + // TODO write a custom Gradle Shadow transformer? + ExtensionLoader extensionLoader = ShrinkWrap.getDefaultDomain().getConfiguration().getExtensionLoader(); + extensionLoader.addOverride(JavaArchive.class, JavaArchiveImpl.class); + extensionLoader.addOverride(ZipExporter.class, ZipExporterImpl.class); + + final JavaArchive javaArchive = ShrinkWrap + .create(JavaArchive.class) + .addAsServiceProvider(EXTENSION_MAIN_CLASS_NAME, hiveMQExtension.getMainClass().getName()); putSubclassesIntoJar(hiveMQExtension.getId(), hiveMQExtension.getMainClass(), javaArchive); for (final Class additionalClass : hiveMQExtension.getAdditionalClasses()) { diff --git a/modules/k3s/build.gradle b/modules/k3s/build.gradle index e36dc67bd9c..5007c832e7f 100644 --- a/modules/k3s/build.gradle +++ b/modules/k3s/build.gradle @@ -3,7 +3,10 @@ description = "Testcontainers :: K3S" dependencies { api project(":testcontainers") - shaded 'com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.13.1' + // https://youtu.be/otCpCn0l4Wo + // The core module depends on jackson-databind 2.8.x for backward compatibility. + // Any >2.8 version here is not compatible with jackson-databind 2.8.x. + shaded 'com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.8.8' testImplementation 'io.fabric8:kubernetes-client:5.11.0' testImplementation 'io.kubernetes:client-java:14.0.0' From 947b37e296e883a6eaf62423eb6981aa5c7cfa6a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 3 Mar 2022 19:46:16 +0000 Subject: [PATCH 0039/3231] Bump kubernetes-client from 5.11.0 to 5.12.1 in /modules/k3s Bumps [kubernetes-client](https://github.com/fabric8io/kubernetes-client) from 5.11.0 to 5.12.1. - [Release notes](https://github.com/fabric8io/kubernetes-client/releases) - [Changelog](https://github.com/fabric8io/kubernetes-client/blob/master/CHANGELOG.md) - [Commits](https://github.com/fabric8io/kubernetes-client/compare/v5.11.0...v5.12.1) --- updated-dependencies: - dependency-name: io.fabric8:kubernetes-client dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/k3s/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/k3s/build.gradle b/modules/k3s/build.gradle index 5007c832e7f..075078f36d9 100644 --- a/modules/k3s/build.gradle +++ b/modules/k3s/build.gradle @@ -8,7 +8,7 @@ dependencies { // Any >2.8 version here is not compatible with jackson-databind 2.8.x. shaded 'com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.8.8' - testImplementation 'io.fabric8:kubernetes-client:5.11.0' + testImplementation 'io.fabric8:kubernetes-client:5.12.1' testImplementation 'io.kubernetes:client-java:14.0.0' testImplementation 'org.assertj:assertj-core:3.22.0' } From 6dea9789db1f970da20fc04d11eb3c5323a8bd4f Mon Sep 17 00:00:00 2001 From: Richard North Date: Fri, 4 Mar 2022 10:51:32 +0000 Subject: [PATCH 0040/3231] Fix formatting in docs for image override configuration (#5147) --- docs/features/image_name_substitution.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/features/image_name_substitution.md b/docs/features/image_name_substitution.md index 7ead811a250..f3c78c1f309 100644 --- a/docs/features/image_name_substitution.md +++ b/docs/features/image_name_substitution.md @@ -112,9 +112,10 @@ To do this, create or modify a file on the classpath named `testcontainers.prope For example: -```text tab="src/test/resources/testcontainers.properties" -image.substitutor=com.mycompany.testcontainers.ExampleImageNameSubstitutor -``` +=== "`src/test/resources/testcontainers.properties`" + ```text + image.substitutor=com.mycompany.testcontainers.ExampleImageNameSubstitutor + ``` Note that it is also possible to provide this same configuration property: @@ -130,7 +131,7 @@ Please see [the documentation on configuration mechanisms](./configuration.md) f !!! note This approach is discouraged and deprecated, but is documented for completeness. Please consider one of the other approaches outlined in this page instead. - Overriding individual image names via configuration may be removed in 2021. + Overriding individual image names via configuration may be removed in the future. Consider this if: From e12b63692d9f3922b53705041d81a9f529bc479f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 8 Mar 2022 13:59:20 +0000 Subject: [PATCH 0041/3231] Bump actions/checkout from 2 to 3 Bumps [actions/checkout](https://github.com/actions/checkout) from 2 to 3. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v2...v3) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/ci-docker-wormhole.yml | 2 +- .github/workflows/ci-examples.yml | 4 ++-- .github/workflows/ci-rootless.yml | 2 +- .github/workflows/ci.yml | 6 +++--- .github/workflows/release.yml | 2 +- .github/workflows/update-docs-version.yml | 2 +- .github/workflows/update-gradle-wrapper.yml | 2 +- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/ci-docker-wormhole.yml b/.github/workflows/ci-docker-wormhole.yml index d750d8b39e0..b93d26d98bb 100644 --- a/.github/workflows/ci-docker-wormhole.yml +++ b/.github/workflows/ci-docker-wormhole.yml @@ -8,7 +8,7 @@ jobs: in-docker_test: runs-on: ubuntu-18.04 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Build with Gradle run: | docker run -i --rm \ diff --git a/.github/workflows/ci-examples.yml b/.github/workflows/ci-examples.yml index 3fb2467a295..a594f936a84 100644 --- a/.github/workflows/ci-examples.yml +++ b/.github/workflows/ci-examples.yml @@ -16,7 +16,7 @@ jobs: outputs: matrix: ${{ steps.set-matrix.outputs.matrix }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: actions/setup-java@v3 with: java-version: '8.0.302' @@ -44,7 +44,7 @@ jobs: matrix: ${{ fromJson(needs.find_gradle_jobs.outputs.matrix) }} runs-on: ubuntu-18.04 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: actions/setup-java@v3 with: java-version: '8.0.302' diff --git a/.github/workflows/ci-rootless.yml b/.github/workflows/ci-rootless.yml index 71ded85cf53..45c58c5e2d0 100644 --- a/.github/workflows/ci-rootless.yml +++ b/.github/workflows/ci-rootless.yml @@ -16,7 +16,7 @@ jobs: env: XDG_RUNTIME_DIR: ${{ matrix.XDG_RUNTIME_DIR }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: debug run: id -u; whoami - name: uninstall rootful Docker diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index db7232fffa8..d0f57d88667 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,7 +16,7 @@ jobs: outputs: thundra_agent_testrun_id: ${{ steps.thundra_test_initializer.outputs.thundra_agent_testrun_id }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - id: thundra_test_initializer uses: thundra-io/thundra-test-init-action@v1 find_gradle_jobs: @@ -24,7 +24,7 @@ jobs: outputs: matrix: ${{ steps.set-matrix.outputs.matrix }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: actions/setup-java@v3 with: java-version: '8.0.302' @@ -50,7 +50,7 @@ jobs: matrix: ${{ fromJson(needs.find_gradle_jobs.outputs.matrix) }} runs-on: ubuntu-18.04 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: actions/setup-java@v3 with: java-version: '8.0.302' diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f6df0dd46f4..5c786d440c5 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,7 +12,7 @@ jobs: release: runs-on: ubuntu-18.04 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: actions/setup-java@v3 with: java-version: '8.0.302' diff --git a/.github/workflows/update-docs-version.yml b/.github/workflows/update-docs-version.yml index 2c7626c7ec7..ef039182317 100644 --- a/.github/workflows/update-docs-version.yml +++ b/.github/workflows/update-docs-version.yml @@ -9,7 +9,7 @@ jobs: if: github.repository == 'testcontainers/testcontainers-java' runs-on: ubuntu-18.04 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: ref: master - name: Update latest_version property in mkdocs.yml diff --git a/.github/workflows/update-gradle-wrapper.yml b/.github/workflows/update-gradle-wrapper.yml index 253e37c23eb..e2013d0f1b7 100644 --- a/.github/workflows/update-gradle-wrapper.yml +++ b/.github/workflows/update-gradle-wrapper.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Update Gradle Wrapper uses: gradle-update/update-gradle-wrapper-action@5b865a8b4d09fdf6afaaa20397ba68455bb48f0d # v1.0.13 From 5f7ded3ac202fd379f5b92bbf196b2c4e30cc6b4 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Tue, 8 Mar 2022 09:08:19 -0500 Subject: [PATCH 0042/3231] Deprecate `getTestHostIpAddress` and `getContainerIpAddress()` (#5149) * Deprecate `getTestHostIpAddress` * Update docs/modules/webdriver_containers.md Co-authored-by: Richard North Co-authored-by: Richard North --- .../testcontainers/containers/Container.java | 2 ++ .../containers/ContainerState.java | 1 + .../containers/GenericContainer.java | 1 + docs/modules/webdriver_containers.md | 3 +-- .../LocalServerWebDriverContainerTest.java | 20 ++++++------------- 5 files changed, 11 insertions(+), 16 deletions(-) diff --git a/core/src/main/java/org/testcontainers/containers/Container.java b/core/src/main/java/org/testcontainers/containers/Container.java index 9c1cb7f8034..d9291fc9fc8 100644 --- a/core/src/main/java/org/testcontainers/containers/Container.java +++ b/core/src/main/java/org/testcontainers/containers/Container.java @@ -365,7 +365,9 @@ default SELF withClasspathResourceMapping(final String resourcePath, final Strin * from inside the container is not going to work, since the container has its own IP address. * * @return the IP address of the host machine + * @deprecated use {@link org.testcontainers.Testcontainers#exposeHostPorts(int...)} */ + @Deprecated String getTestHostIpAddress(); /** diff --git a/core/src/main/java/org/testcontainers/containers/ContainerState.java b/core/src/main/java/org/testcontainers/containers/ContainerState.java index 7fe90bd57a4..e5903b0b806 100644 --- a/core/src/main/java/org/testcontainers/containers/ContainerState.java +++ b/core/src/main/java/org/testcontainers/containers/ContainerState.java @@ -44,6 +44,7 @@ public interface ContainerState { * Get the IP address that this container may be reached on (may not be the local machine). * * @return an IP address + * @deprecated use {@link #getHost()} * @see #getHost() */ default String getContainerIpAddress() { diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index 22a4f0ba739..70e080711a7 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -1332,6 +1332,7 @@ public String getDockerImageName() { * {@inheritDoc} */ @Override + @Deprecated public String getTestHostIpAddress() { if (DockerMachineClient.instance().isInstalled()) { try { diff --git a/docs/modules/webdriver_containers.md b/docs/modules/webdriver_containers.md index d589aa18098..ec1dc2a407f 100644 --- a/docs/modules/webdriver_containers.md +++ b/docs/modules/webdriver_containers.md @@ -36,8 +36,7 @@ test methods: You can then use this driver instance like a regular WebDriver. Note that, if you want to test a **web application running on the host machine** (the machine the JUnit tests are -running on - which is quite likely), you'll need to replace any references to `localhost` with an IP address that the -Docker container can reach. Use the `getTestHostIpAddress()` method, e.g.: +running on - which is quite likely), you'll need to use [the host exposing](../features/networking.md#exposing-host-ports-to-the-container) feature of Testcontainers, e.g.: [Open Web Page](../../modules/selenium/src/test/java/org/testcontainers/junit/LocalServerWebDriverContainerTest.java) inside_block:getPage diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/LocalServerWebDriverContainerTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/LocalServerWebDriverContainerTest.java index 772193e2d43..3c071984dfb 100644 --- a/modules/selenium/src/test/java/org/testcontainers/junit/LocalServerWebDriverContainerTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/junit/LocalServerWebDriverContainerTest.java @@ -7,10 +7,9 @@ import org.openqa.selenium.By; import org.openqa.selenium.chrome.ChromeOptions; import org.openqa.selenium.remote.RemoteWebDriver; +import org.testcontainers.Testcontainers; import org.testcontainers.containers.BrowserWebDriverContainer; -import org.testcontainers.utility.TestEnvironment; -import static org.apache.commons.lang3.SystemUtils.IS_OS_MAC_OSX; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; /** @@ -20,18 +19,11 @@ public class LocalServerWebDriverContainerTest { @Rule - public BrowserWebDriverContainer chrome = new BrowserWebDriverContainer().withCapabilities(new ChromeOptions()); + public BrowserWebDriverContainer chrome = new BrowserWebDriverContainer<>() + .withAccessToHost(true) + .withCapabilities(new ChromeOptions()); private int localPort; - /** - * The getTestHostIpAddress() method is only implemented for OS X running docker-machine. Skip JUnit execution elsewhere. - */ - @BeforeClass - public static void checkOS() { - Assume.assumeTrue("These tests are currently only applicable to OS X", IS_OS_MAC_OSX); - Assume.assumeTrue("These tests are only applicable to docker machine", TestEnvironment.dockerIsDockerMachine()); - } - @Before public void setupLocalServer() throws Exception { @@ -55,8 +47,8 @@ public void testConnection() { // Construct a URL that the browser container can access // getPage { - String hostIpAddress = chrome.getTestHostIpAddress(); - driver.get("http://" + hostIpAddress + ":" + localPort); + Testcontainers.exposeHostPorts(localPort); + driver.get("http://host.testcontainers.internal:" + localPort); // } String headingText = driver.findElement(By.cssSelector("h1")).getText().trim(); From affaf194f08bb1409e23996f03ad05cb3eabe943 Mon Sep 17 00:00:00 2001 From: Yannick Weber Date: Tue, 8 Mar 2022 16:24:15 +0100 Subject: [PATCH 0043/3231] replaced non-ascii single quote with ascii single quote (#5102) --- .../main/java/org/testcontainers/hivemq/HiveMQContainer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/hivemq/src/main/java/org/testcontainers/hivemq/HiveMQContainer.java b/modules/hivemq/src/main/java/org/testcontainers/hivemq/HiveMQContainer.java index 342903e24af..996caad1eb8 100644 --- a/modules/hivemq/src/main/java/org/testcontainers/hivemq/HiveMQContainer.java +++ b/modules/hivemq/src/main/java/org/testcontainers/hivemq/HiveMQContainer.java @@ -354,7 +354,7 @@ protected void containerIsStarted(final @NotNull InspectContainerResponse contai } if (!file.exists()) { - throw new ContainerLaunchException("File '" + mountableFile.getFilesystemPath() + "‘ does not exist."); + throw new ContainerLaunchException("File '" + mountableFile.getFilesystemPath() + "' does not exist."); } final String containerPath = "/opt/hivemq" + PathUtil.prepareAppendPath(pathInHomeFolder); withCopyFileToContainer(cloneWithFileMode(mountableFile), containerPath); From 40341d3912a2fe623adb83651c52a490734f3ab9 Mon Sep 17 00:00:00 2001 From: Gerrit Meier Date: Fri, 11 Mar 2022 14:16:15 +0100 Subject: [PATCH 0044/3231] Mention that Spring Data Neo4j uses Testcontainers in the docs (#5169) * Fix Neo4j-OGM usage description. * Add Spring Data Neo4j to the "who is using"-part. --- docs/index.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/index.md b/docs/index.md index fdbad94b851..8749e3678cf 100644 --- a/docs/index.md +++ b/docs/index.md @@ -176,7 +176,8 @@ A huge thank you to our sponsors: * [Instana](https://www.instana.com) - Testing agents and stream processing backends * [eBay Marketing](https://www.ebay.com) - Testing for MySQL, Cassandra, Redis, Couchbase, Kafka, etc. * [Skyscanner](https://www.skyscanner.net/) - Integration testing against HTTP service mocks and various data stores -* [Neo4j-OGM](https://neo4j.com/developer/neo4j-ogm/) - Testing new, reactive client implementations +* [Neo4j-OGM](https://neo4j.com/developer/neo4j-ogm/) - Testing with Neo4j +* [Spring Data Neo4j](https://github.com/spring-projects/spring-data-neo4j/) - Testing imperative and reactive implementations with Neo4j * [Lightbend](https://www.lightbend.com/) - Testing [Alpakka Kafka](https://doc.akka.io/docs/alpakka-kafka/current/) and support in [Alpakka Kafka Testkit](https://doc.akka.io/docs/alpakka-kafka/current/testing.html#testing-with-kafka-in-docker) * [Zalando SE](https://corporate.zalando.com/en) - Testing core business services * [Europace AG](https://tech.europace.de/) - Integration testing for databases and micro services From eea61bd9a794842aae7865e505be72a938e2a7e0 Mon Sep 17 00:00:00 2001 From: Joseph Petersen Date: Thu, 17 Mar 2022 12:48:25 +0100 Subject: [PATCH 0045/3231] Use collapse-after for dependencies category (#5157) Bump release-drafter.yml --- .github/release-drafter.yml | 1 + .github/workflows/release-drafter.yml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml index 4da341055c2..07376824bf7 100644 --- a/.github/release-drafter.yml +++ b/.github/release-drafter.yml @@ -23,3 +23,4 @@ categories: - type/test-improvement - title: 📦 Dependency updates label: dependencies + collapse-after: 5 diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml index 05485ac57fa..839fb43df4d 100644 --- a/.github/workflows/release-drafter.yml +++ b/.github/workflows/release-drafter.yml @@ -12,6 +12,6 @@ jobs: if: github.repository == 'testcontainers/testcontainers-java' runs-on: ubuntu-latest steps: - - uses: release-drafter/release-drafter@2f7ebf8ab5ef7f9835ee4b0b1eebaa2a14ca1669 # v5.15.0 + - uses: release-drafter/release-drafter@e9ee02fbac03d922bac6212003695e8358dd88b0 # v5.19.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From e450586648f613abae00e29cb8d290a987e43a25 Mon Sep 17 00:00:00 2001 From: Gerrit Meier Date: Fri, 25 Mar 2022 13:28:48 +0100 Subject: [PATCH 0046/3231] Enable Neo4j labs plugin definition. (#5035) --- docs/modules/databases/neo4j.md | 17 +++++- .../containers/Neo4jContainer.java | 41 +++++++++++++ .../containers/Neo4jLabsPlugin.java | 21 +++++++ .../containers/Neo4jContainerTest.java | 58 ++++++++++++++++++- 4 files changed, 135 insertions(+), 2 deletions(-) create mode 100644 modules/neo4j/src/main/java/org/testcontainers/containers/Neo4jLabsPlugin.java diff --git a/docs/modules/databases/neo4j.md b/docs/modules/databases/neo4j.md index 87a5d9157d1..b6a04d2195c 100644 --- a/docs/modules/databases/neo4j.md +++ b/docs/modules/databases/neo4j.md @@ -52,6 +52,21 @@ Whole directories work as well: [Plugin folder](../../../modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerTest.java) inside_block:registerPluginsPath +### Add Neo4j Docker Labs plugins + +Add any Neo4j Labs plugin from the [Neo4j Docker Labs plugin list](https://neo4j.com/docs/operations-manual/4.4/docker/operations/#docker-neo4jlabs-plugins). + +!!! note + At the moment only the plugins available from the list Neo4j Docker 4.4 are supported by type. + If you want to register another supported Neo4j Labs plugin, you have to add it manually + by using the method `withLabsPlugins(String... neo4jLabsPlugins)`. + Please refer to the list of [supported Docker image plugins](https://neo4j.com/docs/operations-manual/current/docker/operations/#docker-neo4jlabs-plugins). + + +[Configure Neo4j Labs Plugins](../../../modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerTest.java) inside_block:configureLabsPlugins + + + ### Start the container with a predefined database If you have an existing database (`graph.db`) you want to work with, copy it over to the container like this: @@ -61,7 +76,7 @@ If you have an existing database (`graph.db`) you want to work with, copy it ove !!! note -The `withDatabase` method will only work with Neo4j 3.5 and throw an exception if used in combination with a newer version. + The `withDatabase` method will only work with Neo4j 3.5 and throw an exception if used in combination with a newer version. ## Choose your Neo4j license diff --git a/modules/neo4j/src/main/java/org/testcontainers/containers/Neo4jContainer.java b/modules/neo4j/src/main/java/org/testcontainers/containers/Neo4jContainer.java index f844df3f945..6f0cf10ded7 100644 --- a/modules/neo4j/src/main/java/org/testcontainers/containers/Neo4jContainer.java +++ b/modules/neo4j/src/main/java/org/testcontainers/containers/Neo4jContainer.java @@ -4,7 +4,11 @@ import static java.util.stream.Collectors.toSet; import java.time.Duration; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; import java.util.Set; +import java.util.stream.Collectors; import java.util.stream.Stream; import org.testcontainers.containers.wait.strategy.HttpWaitStrategy; import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy; @@ -60,6 +64,8 @@ public class Neo4jContainer> extends GenericContaine private String adminPassword = DEFAULT_ADMIN_PASSWORD; + private final Set labsPlugins = new HashSet<>(); + /** * Creates a Neo4jContainer using the official Neo4j docker image. * @deprecated use {@link Neo4jContainer(DockerImageName)} instead @@ -119,6 +125,15 @@ protected void configure() { boolean emptyAdminPassword = this.adminPassword == null || this.adminPassword.isEmpty(); String neo4jAuth = emptyAdminPassword ? "none" : String.format(AUTH_FORMAT, this.adminPassword); addEnv("NEO4J_AUTH", neo4jAuth); + + if (!this.labsPlugins.isEmpty()) { + String enabledPlugins = this.labsPlugins.stream() + .map(pluginName -> "\"" + pluginName + "\"") + .collect(Collectors.joining(",")); + + addEnv("NEO4JLABS_PLUGINS", "[" + enabledPlugins + "]"); + } + } /** @@ -251,6 +266,32 @@ public String getAdminPassword() { return adminPassword; } + /** + * Registers one or more {@link Neo4jLabsPlugin} for download and server startup. + + * @param neo4jLabsPlugins The Neo4j plugins that should get started with the server. + * @return This container. + */ + public S withLabsPlugins(Neo4jLabsPlugin... neo4jLabsPlugins) { + List pluginNames = Arrays.stream(neo4jLabsPlugins) + .map(plugin -> plugin.pluginName) + .collect(Collectors.toList()); + + this.labsPlugins.addAll(pluginNames); + return self(); + } + + /** + * Registers one or more {@link Neo4jLabsPlugin} for download and server startup. + + * @param neo4jLabsPlugins The Neo4j plugins that should get started with the server. + * @return This container. + */ + public S withLabsPlugins(String... neo4jLabsPlugins) { + this.labsPlugins.addAll(Arrays.asList(neo4jLabsPlugins)); + return self(); + } + private static String formatConfigurationKey(String plainConfigKey) { final String prefix = "NEO4J_"; diff --git a/modules/neo4j/src/main/java/org/testcontainers/containers/Neo4jLabsPlugin.java b/modules/neo4j/src/main/java/org/testcontainers/containers/Neo4jLabsPlugin.java new file mode 100644 index 00000000000..96c8e987c85 --- /dev/null +++ b/modules/neo4j/src/main/java/org/testcontainers/containers/Neo4jLabsPlugin.java @@ -0,0 +1,21 @@ +package org.testcontainers.containers; + +/** + * Reflects a plugin from the official Neo4j 4.4. + * Neo4j Labs Plugin list. + * There might be plugins not supported by your selected version of Neo4j. + */ +public enum Neo4jLabsPlugin { + APOC("apoc"), + APOC_CORE("apoc-core"), + BLOOM("bloom"), + STREAMS("streams"), + GRAPH_DATA_SCIENCE("graph-data-science"), + NEO_SEMANTICS("n10s"); + + final String pluginName; + + Neo4jLabsPlugin(String pluginName) { + this.pluginName = pluginName; + } +} diff --git a/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerTest.java b/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerTest.java index 25924fdd6b4..0d3f04ef570 100644 --- a/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerTest.java +++ b/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerTest.java @@ -9,7 +9,6 @@ import org.neo4j.driver.Record; import org.neo4j.driver.Result; import org.neo4j.driver.Session; -import org.testcontainers.utility.DockerImageName; import org.testcontainers.utility.MountableFile; import java.util.Collections; @@ -183,6 +182,63 @@ public void shouldAddConfigToEnvironment() { .containsEntry("NEO4J_dbms_tx__log_rotation_size", "42M"); } + @Test + public void shouldConfigureSingleLabsPlugin() { + try (Neo4jContainer neo4jContainer = new Neo4jContainer<>("neo4j:4.4") + .withLabsPlugins(Neo4jLabsPlugin.APOC)) { + + + // needs to get called explicitly for setup + neo4jContainer.configure(); + + assertThat(neo4jContainer.getEnvMap()) + .containsEntry("NEO4JLABS_PLUGINS", "[\"apoc\"]"); + } + } + + @Test + public void shouldConfigureMultipleLabsPlugins() { + try( + // configureLabsPlugins { + Neo4jContainer neo4jContainer = new Neo4jContainer<>("neo4j:4.4") + .withLabsPlugins(Neo4jLabsPlugin.APOC, Neo4jLabsPlugin.BLOOM); + // } + ) { + // needs to get called explicitly for setup + neo4jContainer.configure(); + + assertThat(neo4jContainer.getEnvMap().get("NEO4JLABS_PLUGINS")) + .containsAnyOf("[\"apoc\",\"bloom\"]", "[\"bloom\",\"apoc\"]"); + } + } + + @Test + public void shouldConfigureSingleLabsPluginWithString() { + try (Neo4jContainer neo4jContainer = new Neo4jContainer<>("neo4j:4.4") + .withLabsPlugins("myApoc")) { + + // needs to get called explicitly for setup + neo4jContainer.configure(); + + assertThat(neo4jContainer.getEnvMap()) + .containsEntry("NEO4JLABS_PLUGINS", "[\"myApoc\"]"); + } + } + + @Test + public void shouldConfigureMultipleLabsPluginsWithString() { + + try (Neo4jContainer neo4jContainer = new Neo4jContainer<>("neo4j:4.4") + .withLabsPlugins("myApoc", "myBloom")) { + + // needs to get called explicitly for setup + neo4jContainer.configure(); + + assertThat(neo4jContainer.getEnvMap().get("NEO4JLABS_PLUGINS")) + .containsAnyOf("[\"myApoc\",\"myBloom\"]", "[\"myBloom\",\"myApoc\"]"); + } + } + private static Driver getDriver(Neo4jContainer container) { AuthToken authToken = AuthTokens.none(); From 0545c0e1c61ba87d7bf7350ab4f9d719c46150d4 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Mon, 28 Mar 2022 11:01:43 -0400 Subject: [PATCH 0047/3231] Remove OkHttp transport (#5113) * Remove OkHttp transport * Fix OkHttp dependencies --- core/build.gradle | 1 - .../dockerclient/DockerClientProviderStrategy.java | 7 ------- modules/azure/build.gradle | 2 ++ modules/couchbase/build.gradle | 2 ++ modules/solr/build.gradle | 3 +++ 5 files changed, 7 insertions(+), 8 deletions(-) diff --git a/core/build.gradle b/core/build.gradle index 89028d0f9c3..558584a0d7c 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -79,7 +79,6 @@ dependencies { api "com.github.docker-java:docker-java-api" shaded 'com.github.docker-java:docker-java-core' - shaded 'com.github.docker-java:docker-java-transport-okhttp' api 'com.github.docker-java:docker-java-transport-zerodep' diff --git a/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java index c65887b6af6..2ae066d897d 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java @@ -6,7 +6,6 @@ import com.github.dockerjava.core.DefaultDockerClientConfig; import com.github.dockerjava.core.DockerClientImpl; import com.github.dockerjava.core.RemoteApiVersion; -import com.github.dockerjava.okhttp.OkDockerHttpClient; import com.github.dockerjava.transport.DockerHttpClient; import com.github.dockerjava.zerodep.ZerodepDockerHttpClient; import com.google.common.annotations.VisibleForTesting; @@ -257,12 +256,6 @@ public static DockerClient getClientForConfig(TransportConfig transportConfig) { String transportType = TestcontainersConfiguration.getInstance().getTransportType(); switch (transportType) { - case "okhttp": - dockerHttpClient = new OkDockerHttpClient.Builder() - .dockerHost(transportConfig.getDockerHost()) - .sslConfig(transportConfig.getSslConfig()) - .build(); - break; case "httpclient5": dockerHttpClient = new ZerodepDockerHttpClient.Builder() .dockerHost(transportConfig.getDockerHost()) diff --git a/modules/azure/build.gradle b/modules/azure/build.gradle index 7e5bc336526..b4dab636eb9 100644 --- a/modules/azure/build.gradle +++ b/modules/azure/build.gradle @@ -2,6 +2,8 @@ description = "Testcontainers :: Azure" dependencies { api project(':testcontainers') + // TODO use JDK's HTTP client and/or Apache HttpClient5 + shaded 'com.squareup.okhttp3:okhttp:3.14.9' testImplementation 'org.assertj:assertj-core:3.22.0' testImplementation 'com.azure:azure-cosmos:4.26.0' diff --git a/modules/couchbase/build.gradle b/modules/couchbase/build.gradle index 2c68f163a57..0245a5935cb 100644 --- a/modules/couchbase/build.gradle +++ b/modules/couchbase/build.gradle @@ -2,6 +2,8 @@ description = "Testcontainers :: Couchbase" dependencies { api project(':testcontainers') + // TODO use JDK's HTTP client and/or Apache HttpClient5 + shaded 'com.squareup.okhttp3:okhttp:3.14.9' testImplementation 'com.couchbase.client:java-client:3.2.5' testImplementation 'org.awaitility:awaitility:4.1.1' diff --git a/modules/solr/build.gradle b/modules/solr/build.gradle index b3dfc9b3f92..49b2a9970f3 100644 --- a/modules/solr/build.gradle +++ b/modules/solr/build.gradle @@ -2,6 +2,9 @@ description = "Testcontainers :: Solr" dependencies { api project(':testcontainers') + // TODO use JDK's HTTP client and/or Apache HttpClient5 + shaded 'com.squareup.okhttp3:okhttp:3.14.9' + testImplementation 'org.apache.solr:solr-solrj:8.11.1' } From 6414df1288b0ec9c1e707aeb34c200a191cca74e Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Mon, 28 Mar 2022 17:12:09 +0200 Subject: [PATCH 0048/3231] Update mkdocs to 1.3.0 (#5186) --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 8f4a1a5f100..f77d057b599 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -mkdocs==1.2.3 +mkdocs==1.3.0 mkdocs-codeinclude-plugin==0.2.0 mkdocs-material==8.1.3 mkdocs-markdownextradata-plugin==0.2.5 From 02782d9cb711baebaa504bc00669acdd84e10e51 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Mon, 28 Mar 2022 11:13:47 -0400 Subject: [PATCH 0049/3231] Replace deprecated `checkAndPullImage` with `RemoteDockerImage` (#5148) * Replace deprecated `checkAndPullImage` with `RemoteDockerImage` * fix `runCommandInsideDockerShouldNotFailIfImageDoesNotExistsLocally` * Remove `LoggerFactory` usage * avoid name substitution in `DockerComposeContainer`/`ImageFromDockerfile` * fix `AuthenticatedImagePullTest` * remove accidential `@Ignore` * fix indentation * put `clearCache` after `catch()` --- .../testcontainers/DockerClientFactory.java | 22 +++++++++---------- .../containers/DockerComposeContainer.java | 6 ++++- .../images/LocalImagesCache.java | 3 ++- .../images/RemoteDockerImage.java | 9 +++++--- .../images/builder/ImageFromDockerfile.java | 7 +++++- .../utility/ImageNameSubstitutor.java | 17 ++++++++++++++ .../utility/RyukResourceReaper.java | 8 +++---- .../DockerClientFactoryTest.java | 10 +++++++-- .../images/LocalImagesCacheAccessor.java | 9 ++++++++ .../utility/AuthenticatedImagePullTest.java | 2 ++ .../containers/localstack/LegacyModeTest.java | 9 ++------ 11 files changed, 70 insertions(+), 32 deletions(-) create mode 100644 core/src/test/java/org/testcontainers/images/LocalImagesCacheAccessor.java diff --git a/core/src/main/java/org/testcontainers/DockerClientFactory.java b/core/src/main/java/org/testcontainers/DockerClientFactory.java index 9a6165ef55f..4cbe81f9e2d 100644 --- a/core/src/main/java/org/testcontainers/DockerClientFactory.java +++ b/core/src/main/java/org/testcontainers/DockerClientFactory.java @@ -25,10 +25,10 @@ import org.testcontainers.dockerclient.DockerClientProviderStrategy; import org.testcontainers.dockerclient.DockerMachineClientProviderStrategy; import org.testcontainers.dockerclient.TransportConfig; +import org.testcontainers.images.RemoteDockerImage; import org.testcontainers.images.TimeLimitedLoggedPullImageResultCallback; import org.testcontainers.utility.ComparableVersion; import org.testcontainers.utility.DockerImageName; -import org.testcontainers.utility.ImageNameSubstitutor; import org.testcontainers.utility.MountableFile; import org.testcontainers.utility.ResourceReaper; import org.testcontainers.utility.RyukResourceReaper; @@ -236,7 +236,6 @@ public void close() { checkDiskSpace(client, ryukContainerId); } else { runInsideDocker( - client, createContainerCmd -> { createContainerCmd.withName("testcontainers-checks-" + SESSION_ID); createContainerCmd.getHostConfig().withAutoRemove(true); @@ -335,9 +334,12 @@ private boolean checkMountableFile() { } /** - * Check whether the image is available locally and pull it otherwise - */ + * Check whether the image is available locally and pull it otherwise + * + * @deprecated use {@link RemoteDockerImage} + */ @SneakyThrows + @Deprecated public void checkAndPullImage(DockerClient client, String image) { try { client.inspectImageCmd(image).exec(); @@ -363,18 +365,14 @@ public String dockerHostIpAddress() { } public T runInsideDocker(Consumer createContainerCmdConsumer, BiFunction block) { - // We can't use client() here because it might create an infinite loop - return runInsideDocker(getOrInitializeStrategy().getDockerClient(), createContainerCmdConsumer, block); + return runInsideDocker(TINY_IMAGE, createContainerCmdConsumer, block); } - private T runInsideDocker(DockerClient client, Consumer createContainerCmdConsumer, BiFunction block) { - - final String tinyImage = ImageNameSubstitutor.instance().apply(TINY_IMAGE).asCanonicalNameString(); - - checkAndPullImage(client, tinyImage); + T runInsideDocker(DockerImageName imageName, Consumer createContainerCmdConsumer, BiFunction block) { + RemoteDockerImage dockerImage = new RemoteDockerImage(imageName); HashMap labels = new HashMap<>(DEFAULT_LABELS); labels.putAll(ResourceReaper.instance().getLabels()); - CreateContainerCmd createContainerCmd = client.createContainerCmd(tinyImage) + CreateContainerCmd createContainerCmd = client.createContainerCmd(dockerImage.get()) .withLabels(labels); createContainerCmdConsumer.accept(createContainerCmd); String id = createContainerCmd.exec().getId(); diff --git a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java index b8913e8ac7c..4db9361a618 100644 --- a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java +++ b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java @@ -25,12 +25,14 @@ import org.testcontainers.containers.wait.strategy.WaitAllStrategy; import org.testcontainers.containers.wait.strategy.WaitStrategy; import org.testcontainers.dockerclient.TransportConfig; +import org.testcontainers.images.RemoteDockerImage; import org.testcontainers.lifecycle.Startable; import org.testcontainers.utility.AuditLogger; import org.testcontainers.utility.Base58; import org.testcontainers.utility.CommandLine; import org.testcontainers.utility.DockerImageName; import org.testcontainers.utility.DockerLoggerFactory; +import org.testcontainers.utility.ImageNameSubstitutor; import org.testcontainers.utility.LogUtils; import org.testcontainers.utility.MountableFile; import org.testcontainers.utility.PathUtils; @@ -186,7 +188,9 @@ private void pullImages() { .forEach(imageName -> { try { log.info("Preemptively checking local images for '{}', referenced via a compose file or transitive Dockerfile. If not available, it will be pulled.", imageName); - DockerClientFactory.instance().checkAndPullImage(dockerClient, imageName); + new RemoteDockerImage(DockerImageName.parse(imageName)) + .withImageNameSubstitutor(ImageNameSubstitutor.noop()) + .get(); } catch (Exception e) { log.warn("Unable to pre-fetch an image ({}) depended upon by Docker Compose build - startup will continue but may fail. Exception message was: {}", imageName, e.getMessage()); } diff --git a/core/src/main/java/org/testcontainers/images/LocalImagesCache.java b/core/src/main/java/org/testcontainers/images/LocalImagesCache.java index ec371ef830e..ec38721a229 100644 --- a/core/src/main/java/org/testcontainers/images/LocalImagesCache.java +++ b/core/src/main/java/org/testcontainers/images/LocalImagesCache.java @@ -21,7 +21,8 @@ enum LocalImagesCache { INSTANCE; - private final AtomicBoolean initialized = new AtomicBoolean(false); + @VisibleForTesting + final AtomicBoolean initialized = new AtomicBoolean(false); @VisibleForTesting final Map cache = new ConcurrentHashMap<>(); diff --git a/core/src/main/java/org/testcontainers/images/RemoteDockerImage.java b/core/src/main/java/org/testcontainers/images/RemoteDockerImage.java index 4ce38124fe7..3bf2243549a 100644 --- a/core/src/main/java/org/testcontainers/images/RemoteDockerImage.java +++ b/core/src/main/java/org/testcontainers/images/RemoteDockerImage.java @@ -10,7 +10,7 @@ import lombok.NonNull; import lombok.SneakyThrows; import lombok.ToString; -import lombok.experimental.Wither; +import lombok.With; import org.slf4j.Logger; import org.testcontainers.DockerClientFactory; import org.testcontainers.containers.ContainerFetchException; @@ -34,9 +34,12 @@ public class RemoteDockerImage extends LazyFuture { @ToString.Exclude private Future imageNameFuture; - @Wither + @With private ImagePullPolicy imagePullPolicy = PullPolicy.defaultPolicy(); + @With + private ImageNameSubstitutor imageNameSubstitutor = ImageNameSubstitutor.instance(); + @ToString.Exclude private DockerClient dockerClient = DockerClientFactory.lazyClient(); @@ -115,7 +118,7 @@ private DockerImageName getImageName() throws InterruptedException, ExecutionExc final DockerImageName specifiedImageName = imageNameFuture.get(); // Allow the image name to be substituted - return ImageNameSubstitutor.instance().apply(specifiedImageName); + return imageNameSubstitutor.apply(specifiedImageName); } @ToString.Include(name = "imageName", rank = 1) diff --git a/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java b/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java index abf13f8ef02..281a74feaba 100644 --- a/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java +++ b/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java @@ -13,13 +13,16 @@ import org.slf4j.Logger; import org.testcontainers.DockerClientFactory; import org.testcontainers.images.ParsedDockerfile; +import org.testcontainers.images.RemoteDockerImage; import org.testcontainers.images.builder.traits.BuildContextBuilderTrait; import org.testcontainers.images.builder.traits.ClasspathTrait; import org.testcontainers.images.builder.traits.DockerfileTrait; import org.testcontainers.images.builder.traits.FilesTrait; import org.testcontainers.images.builder.traits.StringsTrait; import org.testcontainers.utility.Base58; +import org.testcontainers.utility.DockerImageName; import org.testcontainers.utility.DockerLoggerFactory; +import org.testcontainers.utility.ImageNameSubstitutor; import org.testcontainers.utility.LazyFuture; import org.testcontainers.utility.ResourceReaper; @@ -172,7 +175,9 @@ private void prePullDependencyImages(Set imagesToPull) { imagesToPull.forEach(imageName -> { try { log.info("Pre-emptively checking local images for '{}', referenced via a Dockerfile. If not available, it will be pulled.", imageName); - DockerClientFactory.instance().checkAndPullImage(dockerClient, imageName); + new RemoteDockerImage(DockerImageName.parse(imageName)) + .withImageNameSubstitutor(ImageNameSubstitutor.noop()) + .get(); } catch (Exception e) { log.warn("Unable to pre-fetch an image ({}) depended upon by Dockerfile - image build will continue but may fail. Exception message was: {}", imageName, e.getMessage()); } diff --git a/core/src/main/java/org/testcontainers/utility/ImageNameSubstitutor.java b/core/src/main/java/org/testcontainers/utility/ImageNameSubstitutor.java index 645b717a562..a1cb2492204 100644 --- a/core/src/main/java/org/testcontainers/utility/ImageNameSubstitutor.java +++ b/core/src/main/java/org/testcontainers/utility/ImageNameSubstitutor.java @@ -53,6 +53,10 @@ public synchronized static ImageNameSubstitutor instance() { return instance; } + public static ImageNameSubstitutor noop() { + return new NoopImageNameSubstitutor(); + } + private static ImageNameSubstitutor wrapWithLogging(final ImageNameSubstitutor wrappedInstance) { return new LogWrappedImageNameSubstitutor(wrappedInstance); } @@ -127,4 +131,17 @@ protected String getDescription() { ); } } + + private static class NoopImageNameSubstitutor extends ImageNameSubstitutor { + + @Override + public DockerImageName apply(DockerImageName original) { + return original; + } + + @Override + protected String getDescription() { + return "No-op substitutor"; + } + } } diff --git a/core/src/main/java/org/testcontainers/utility/RyukResourceReaper.java b/core/src/main/java/org/testcontainers/utility/RyukResourceReaper.java index 743b33856cf..9efbf5ed972 100644 --- a/core/src/main/java/org/testcontainers/utility/RyukResourceReaper.java +++ b/core/src/main/java/org/testcontainers/utility/RyukResourceReaper.java @@ -17,6 +17,7 @@ import org.rnorth.ducttape.ratelimits.RateLimiterBuilder; import org.testcontainers.DockerClientFactory; import org.testcontainers.containers.ContainerState; +import org.testcontainers.images.RemoteDockerImage; import java.io.IOException; import java.net.InetSocketAddress; @@ -85,16 +86,13 @@ private synchronized void maybeStart() { return; } DockerClient client = DockerClientFactory.lazyClient(); - String ryukImage = ImageNameSubstitutor.instance() - .apply(DockerImageName.parse("testcontainers/ryuk:0.3.3")) - .asCanonicalNameString(); - DockerClientFactory.instance().checkAndPullImage(client, ryukImage); + RemoteDockerImage ryukImage = new RemoteDockerImage(DockerImageName.parse("testcontainers/ryuk:0.3.3")); List binds = new ArrayList<>(); binds.add(new Bind(DockerClientFactory.instance().getRemoteDockerUnixSocketPath(), new Volume("/var/run/docker.sock"))); ExposedPort ryukExposedPort = ExposedPort.tcp(8080); - containerId = client.createContainerCmd(ryukImage) + containerId = client.createContainerCmd(ryukImage.get()) .withHostConfig( new HostConfig() .withAutoRemove(true) diff --git a/core/src/test/java/org/testcontainers/DockerClientFactoryTest.java b/core/src/test/java/org/testcontainers/DockerClientFactoryTest.java index 16d25b2f625..4dcfc26349f 100644 --- a/core/src/test/java/org/testcontainers/DockerClientFactoryTest.java +++ b/core/src/test/java/org/testcontainers/DockerClientFactoryTest.java @@ -2,7 +2,6 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.testcontainers.TestImages.TINY_IMAGE; import com.github.dockerjava.api.exception.NotFoundException; import org.junit.Rule; @@ -10,6 +9,8 @@ import org.rnorth.visibleassertions.VisibleAssertions; import org.testcontainers.DockerClientFactory.DiskSpaceUsage; import org.testcontainers.dockerclient.LogToStringContainerCallback; +import org.testcontainers.images.LocalImagesCacheAccessor; +import org.testcontainers.utility.DockerImageName; import org.testcontainers.utility.MockTestcontainersConfigurationRule; /** @@ -24,16 +25,21 @@ public class DockerClientFactoryTest { public void runCommandInsideDockerShouldNotFailIfImageDoesNotExistsLocally() { final DockerClientFactory dockFactory = DockerClientFactory.instance(); + + DockerImageName imageName = DockerImageName.parse("testcontainers/helloworld:1.1.0"); + try { //remove tiny image, so it will be pulled during next command run dockFactory.client() - .removeImageCmd(TINY_IMAGE.asCanonicalNameString()) + .removeImageCmd(imageName.asCanonicalNameString()) .withForce(true).exec(); } catch (NotFoundException ignored) { // Do not fail if it's not pulled yet } + LocalImagesCacheAccessor.clearCache(); dockFactory.runInsideDocker( + imageName, cmd -> cmd.withCmd("sh", "-c", "echo 'SUCCESS'"), (client, id) -> client.logContainerCmd(id) diff --git a/core/src/test/java/org/testcontainers/images/LocalImagesCacheAccessor.java b/core/src/test/java/org/testcontainers/images/LocalImagesCacheAccessor.java new file mode 100644 index 00000000000..4e7a28c535a --- /dev/null +++ b/core/src/test/java/org/testcontainers/images/LocalImagesCacheAccessor.java @@ -0,0 +1,9 @@ +package org.testcontainers.images; + +public final class LocalImagesCacheAccessor { + + public static synchronized void clearCache() { + LocalImagesCache.INSTANCE.cache.clear(); + LocalImagesCache.INSTANCE.initialized.set(false); + } +} diff --git a/core/src/test/java/org/testcontainers/utility/AuthenticatedImagePullTest.java b/core/src/test/java/org/testcontainers/utility/AuthenticatedImagePullTest.java index c57c1d7c0b6..412a945b204 100644 --- a/core/src/test/java/org/testcontainers/utility/AuthenticatedImagePullTest.java +++ b/core/src/test/java/org/testcontainers/utility/AuthenticatedImagePullTest.java @@ -17,6 +17,7 @@ import org.testcontainers.containers.ContainerState; import org.testcontainers.containers.DockerComposeContainer; import org.testcontainers.containers.GenericContainer; +import org.testcontainers.images.LocalImagesCacheAccessor; import org.testcontainers.images.builder.ImageFromDockerfile; import java.io.IOException; @@ -93,6 +94,7 @@ public void removeImageFromLocalDocker() { } catch (NotFoundException ignored) { } + LocalImagesCacheAccessor.clearCache(); } @AfterClass diff --git a/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LegacyModeTest.java b/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LegacyModeTest.java index b8f2ec1aae0..b34f43f79ed 100644 --- a/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LegacyModeTest.java +++ b/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LegacyModeTest.java @@ -8,6 +8,7 @@ import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.testcontainers.DockerClientFactory; +import org.testcontainers.images.RemoteDockerImage; import org.testcontainers.utility.DockerImageName; import java.util.Arrays; @@ -72,15 +73,9 @@ public static class On { @BeforeClass public static void createCustomTag() { DockerClient dockerClient = DockerClientFactory.instance().client(); - DockerClientFactory - .instance() - .checkAndPullImage( - dockerClient, - LOCALSTACK_0_12_IMAGE.asCanonicalNameString() - ); dockerClient .tagImageCmd( - LOCALSTACK_0_12_IMAGE.asCanonicalNameString(), + new RemoteDockerImage(LOCALSTACK_0_12_IMAGE).get(), LOCALSTACK_CUSTOM_TAG.getRepository(), LOCALSTACK_CUSTOM_TAG.getVersionPart() ) From 5bbffb6c9962807fc0c54180d29f2aa4d3deaa86 Mon Sep 17 00:00:00 2001 From: Yannick Weber Date: Tue, 29 Mar 2022 14:08:46 +0200 Subject: [PATCH 0050/3231] Improved jitpack dependencies page (#5189) --- docs/jitpack_dependencies.md | 61 ++++++++++++++++++++++++++---------- 1 file changed, 45 insertions(+), 16 deletions(-) diff --git a/docs/jitpack_dependencies.md b/docs/jitpack_dependencies.md index 7689772de92..23a4c7353ad 100644 --- a/docs/jitpack_dependencies.md +++ b/docs/jitpack_dependencies.md @@ -3,22 +3,51 @@ If you like to live on the bleeding edge, [jitpack.io](https://jitpack.io) can be used to obtain SNAPSHOT versions. Use the following dependency description instead: -```xml - - com.github.testcontainers.testcontainers-java - --artifact name-- - -SNAPSHOT - -``` +=== "Gradle" + ```groovy + testImplementation "com.github.testcontainers.testcontainers-java:--artifact name--:master-SNAPSHOT" + ``` +=== "Maven" + ```xml + + com.github.testcontainers.testcontainers-java + --artifact name-- + master-SNAPSHOT + + ``` -A specific git revision (such as `093a3a4628`) can be used as a fixed version instead. The JitPack maven repository must also be declared, e.g.: +A specific git revision (such as `02782d9`) can be used as a fixed version instead: -```xml - - - jitpack.io - https://jitpack.io - - -``` +=== "Gradle" + ```groovy + testImplementation "com.github.testcontainers.testcontainers-java:--artifact name--:02782d9" + ``` +=== "Maven" + ```xml + + com.github.testcontainers.testcontainers-java + --artifact name-- + 02782d9 + + ``` + +The JitPack maven repository must also be declared, e.g.: + +=== "Gradle" + ```groovy + repositories { + maven { + url "https://jitpack.io" + } + } + ``` +=== "Maven" + ```xml + + + jitpack.io + https://jitpack.io + + + ``` From 04ecb68363596d8feb37fd2d4fcf3233031582eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Prinet?= Date: Wed, 30 Mar 2022 19:36:41 +0200 Subject: [PATCH 0051/3231] Improve Gradle build cacheability (#5188) * Bump up Gradle plugins version * Allow cache relocatability for Delombok task * Declare Delombok task inputs/outputs inside DelombokArgumentProvider --- build.gradle | 12 +++------ .../build/DelombokArgumentProvider.groovy | 26 +++++++++++++++++++ settings.gradle | 4 +-- 3 files changed, 32 insertions(+), 10 deletions(-) create mode 100644 buildSrc/src/main/groovy/org/testcontainers/build/DelombokArgumentProvider.groovy diff --git a/build.gradle b/build.gradle index 8fb15a6566f..309743477c1 100644 --- a/build.gradle +++ b/build.gradle @@ -43,18 +43,14 @@ subprojects { version = '1.18.20' } + task delombok(type: io.franzbecker.gradle.lombok.task.DelombokTask) { - def outputDir = file("$buildDir/delombok") - outputs.dir(outputDir) outputs.cacheIf { true } - for (srcDir in project.sourceSets.main.java.srcDirs) { - // TODO: named input and relative path for `srcDir` to make task more cacheable - inputs.dir(srcDir) - // TODO: `outputDir` as relative path to make task more cacheable - args(srcDir, "-d", outputDir, "-f", "generateDelombokComment:skip") - } + argumentProviders.addAll( + new org.testcontainers.build.DelombokArgumentProvider(srcDirs: project.sourceSets.main.java.srcDirs, outputDir: file("$buildDir/delombok")) + ) } delombok.onlyIf { project.sourceSets.main.java.srcDirs.find { it.exists() } diff --git a/buildSrc/src/main/groovy/org/testcontainers/build/DelombokArgumentProvider.groovy b/buildSrc/src/main/groovy/org/testcontainers/build/DelombokArgumentProvider.groovy new file mode 100644 index 00000000000..2cb4931eb58 --- /dev/null +++ b/buildSrc/src/main/groovy/org/testcontainers/build/DelombokArgumentProvider.groovy @@ -0,0 +1,26 @@ +package org.testcontainers.build + +import org.gradle.api.tasks.InputFiles +import org.gradle.api.tasks.OutputDirectory +import org.gradle.api.tasks.PathSensitive +import org.gradle.api.tasks.PathSensitivity +import org.gradle.process.CommandLineArgumentProvider + +/** + * Allows build cache relocatability for Delombok task + */ +class DelombokArgumentProvider implements CommandLineArgumentProvider { + + @InputFiles + @PathSensitive(PathSensitivity.RELATIVE) + Set srcDirs + + @OutputDirectory + File outputDir + + @Override + Iterable asArguments() { + return [srcDirs.collect { it.absolutePath }.join(" "), "-d", outputDir.absolutePath, "-f", "generateDelombokComment:skip"] + } + +} diff --git a/settings.gradle b/settings.gradle index e4c65186fb8..64f2aa5d70a 100644 --- a/settings.gradle +++ b/settings.gradle @@ -5,8 +5,8 @@ buildscript { } } dependencies { - classpath "com.gradle.enterprise:com.gradle.enterprise.gradle.plugin:3.8.1" - classpath "com.gradle:common-custom-user-data-gradle-plugin:1.6.2" + classpath "com.gradle.enterprise:com.gradle.enterprise.gradle.plugin:3.9" + classpath "com.gradle:common-custom-user-data-gradle-plugin:1.6.5" } } From 457b583df82bc5e778b95276aaf6ad1fc0297dfe Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 31 Mar 2022 11:29:13 +0200 Subject: [PATCH 0052/3231] Update Gradle Wrapper to 7.4.1 (#5168) Signed-off-by: gradle-update-robot Co-authored-by: gradle-update-robot --- examples/gradle/wrapper/gradle-wrapper.properties | 4 ++-- gradle/wrapper/gradle-wrapper.properties | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/gradle/wrapper/gradle-wrapper.properties b/examples/gradle/wrapper/gradle-wrapper.properties index 1debed0d8fd..ffe61b45c92 100644 --- a/examples/gradle/wrapper/gradle-wrapper.properties +++ b/examples/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=8cc27038d5dbd815759851ba53e70cf62e481b87494cc97cfd97982ada5ba634 -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip +distributionSha256Sum=e5444a57cda4a95f90b0c9446a9e1b47d3d7f69057765bfb54bd4f482542d548 +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index dcf5e2cb7b3..db3bccc1f9b 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=cd5c2958a107ee7f0722004a12d0f8559b4564c34daad7df06cffd4d12a426d0 -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip +distributionSha256Sum=a9a7b7baba105f6557c9dcf9c3c6e8f7e57e6b49889c5f1d133f015d0727e4be +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.1-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists From 0408db806f6d57b8117881adcd10b6bd6c9dcd9c Mon Sep 17 00:00:00 2001 From: Joseph Petersen Date: Thu, 31 Mar 2022 11:30:10 +0200 Subject: [PATCH 0053/3231] Add virtual environment to git ignore (#5156) GitHub Codespaces auto detects Pipfile and starts installing dependencies into .venv/ Signed-off-by: Joseph Petersen --- .gitignore | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index d6a9a697796..95fc6e52000 100644 --- a/.gitignore +++ b/.gitignore @@ -66,4 +66,7 @@ src/pip-delete-this-directory.txt .DS_Store # Codespaces / VSCode -/.vscode/ \ No newline at end of file +/.vscode/ + +# Python virtual environment +/.venv/ From 9712d25aa899bc306287daffe77bdf28b64c57b4 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 1 Apr 2022 08:47:22 +0200 Subject: [PATCH 0054/3231] Update Gradle Wrapper to 7.4.2 (#5193) Signed-off-by: gradle-update-robot Co-authored-by: gradle-update-robot --- examples/gradle/wrapper/gradle-wrapper.properties | 4 ++-- gradle/wrapper/gradle-wrapper.properties | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/gradle/wrapper/gradle-wrapper.properties b/examples/gradle/wrapper/gradle-wrapper.properties index ffe61b45c92..5c51a4acc9d 100644 --- a/examples/gradle/wrapper/gradle-wrapper.properties +++ b/examples/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=e5444a57cda4a95f90b0c9446a9e1b47d3d7f69057765bfb54bd4f482542d548 -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.1-bin.zip +distributionSha256Sum=29e49b10984e585d8118b7d0bc452f944e386458df27371b49b4ac1dec4b7fda +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index db3bccc1f9b..e1e0c8dc42b 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=a9a7b7baba105f6557c9dcf9c3c6e8f7e57e6b49889c5f1d133f015d0727e4be -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.1-all.zip +distributionSha256Sum=e6d864e3b5bc05cc62041842b306383fc1fefcec359e70cebb1d470a6094ca82 +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists From 297621fe4ab0a775d8dc2bf4f869fbed61dac95e Mon Sep 17 00:00:00 2001 From: Yannick Weber Date: Fri, 1 Apr 2022 13:50:18 +0200 Subject: [PATCH 0055/3231] Added tmpFs usage for HiveMQ module (#5109) Improves performance for IO heavy operations --- .../org/testcontainers/hivemq/HiveMQContainer.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/modules/hivemq/src/main/java/org/testcontainers/hivemq/HiveMQContainer.java b/modules/hivemq/src/main/java/org/testcontainers/hivemq/HiveMQContainer.java index 996caad1eb8..5bb8935c39e 100644 --- a/modules/hivemq/src/main/java/org/testcontainers/hivemq/HiveMQContainer.java +++ b/modules/hivemq/src/main/java/org/testcontainers/hivemq/HiveMQContainer.java @@ -18,6 +18,7 @@ import java.nio.charset.StandardCharsets; import java.time.Duration; import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; @@ -77,6 +78,16 @@ public HiveMQContainer(final @NotNull DockerImageName dockerImageName) { }); } }); + + final HashMap tmpFs = new HashMap<>(); + if (dockerImageName.isCompatibleWith(DEFAULT_HIVEMQ_EE_IMAGE_NAME)) { + tmpFs.put("/opt/hivemq/audit", "rw"); + tmpFs.put("/opt/hivemq/backup", "rw"); + } + + tmpFs.put("/opt/hivemq/log", "rw"); + tmpFs.put("/opt/hivemq/data", "rw"); + withTmpFs(tmpFs); } @Override From 498dac062d628a63c9755492bcdbc13afbddc040 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Apr 2022 22:00:38 +0000 Subject: [PATCH 0056/3231] Bump tomcat-jdbc from 10.0.16 to 10.0.20 in /modules/jdbc-test Bumps tomcat-jdbc from 10.0.16 to 10.0.20. --- updated-dependencies: - dependency-name: org.apache.tomcat:tomcat-jdbc dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/jdbc-test/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jdbc-test/build.gradle b/modules/jdbc-test/build.gradle index bf90d9603a2..5c10742b7f7 100644 --- a/modules/jdbc-test/build.gradle +++ b/modules/jdbc-test/build.gradle @@ -14,7 +14,7 @@ dependencies { exclude(group: "net.java.dev.jna", module: "jna") } - api 'org.apache.tomcat:tomcat-jdbc:10.0.16' + api 'org.apache.tomcat:tomcat-jdbc:10.0.20' api 'org.vibur:vibur-dbcp:25.0' api 'mysql:mysql-connector-java:8.0.28' } From 913efe9e73ecf7a7a332e4f1ef33cd6214b56ced Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Apr 2022 22:00:39 +0000 Subject: [PATCH 0057/3231] Bump tomcat-jdbc from 10.0.16 to 10.0.20 in /modules/jdbc Bumps tomcat-jdbc from 10.0.16 to 10.0.20. --- updated-dependencies: - dependency-name: org.apache.tomcat:tomcat-jdbc dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/jdbc/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jdbc/build.gradle b/modules/jdbc/build.gradle index 71940e0026a..84b927ac4a9 100644 --- a/modules/jdbc/build.gradle +++ b/modules/jdbc/build.gradle @@ -7,7 +7,7 @@ dependencies { compileOnly 'org.jetbrains:annotations:23.0.0' testImplementation 'commons-dbutils:commons-dbutils:1.7' testImplementation 'org.vibur:vibur-dbcp:25.0' - testImplementation 'org.apache.tomcat:tomcat-jdbc:10.0.16' + testImplementation 'org.apache.tomcat:tomcat-jdbc:10.0.20' testImplementation 'com.zaxxer:HikariCP-java6:2.3.13' testImplementation 'com.googlecode.junit-toolbox:junit-toolbox:2.4' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' From 95fec38a2cac0f4fe9e5cbdfdad6dbb46e68e6d8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Apr 2022 22:00:41 +0000 Subject: [PATCH 0058/3231] Bump aws-java-sdk-s3 from 1.12.169 to 1.12.191 in /modules/localstack Bumps [aws-java-sdk-s3](https://github.com/aws/aws-sdk-java) from 1.12.169 to 1.12.191. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.169...1.12.191) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-s3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index c16baad9a22..ccd322f0952 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Localstack" dependencies { api project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.169' + compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.191' testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.169' testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.150' testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.169' From a4587abe04ce78e3f54abe28b95927d69ceb23de Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Apr 2022 22:00:41 +0000 Subject: [PATCH 0059/3231] Bump neo4j-java-driver from 4.4.3 to 4.4.5 in /modules/neo4j Bumps [neo4j-java-driver](https://github.com/neo4j/neo4j-java-driver) from 4.4.3 to 4.4.5. - [Release notes](https://github.com/neo4j/neo4j-java-driver/releases) - [Commits](https://github.com/neo4j/neo4j-java-driver/compare/4.4.3...4.4.5) --- updated-dependencies: - dependency-name: org.neo4j.driver:neo4j-java-driver dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/neo4j/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/neo4j/build.gradle b/modules/neo4j/build.gradle index 27bf38b9f23..6187d665098 100644 --- a/modules/neo4j/build.gradle +++ b/modules/neo4j/build.gradle @@ -33,6 +33,6 @@ dependencies { api project(":testcontainers") - testImplementation 'org.neo4j.driver:neo4j-java-driver:4.4.3' + testImplementation 'org.neo4j.driver:neo4j-java-driver:4.4.5' testImplementation 'org.assertj:assertj-core:3.22.0' } From 9216980323e5fbc4bd81f82058025a876d4240c2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Apr 2022 22:00:42 +0000 Subject: [PATCH 0060/3231] Bump rest-assured from 4.5.1 to 5.0.0 in /modules/vault Bumps [rest-assured](https://github.com/rest-assured/rest-assured) from 4.5.1 to 5.0.0. - [Release notes](https://github.com/rest-assured/rest-assured/releases) - [Changelog](https://github.com/rest-assured/rest-assured/blob/master/changelog.txt) - [Commits](https://github.com/rest-assured/rest-assured/compare/rest-assured-4.5.1...rest-assured-5.0.0) --- updated-dependencies: - dependency-name: io.rest-assured:rest-assured dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- modules/vault/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/vault/build.gradle b/modules/vault/build.gradle index ce000685a10..68daf4ed26f 100644 --- a/modules/vault/build.gradle +++ b/modules/vault/build.gradle @@ -4,7 +4,7 @@ dependencies { api project(':testcontainers') testImplementation 'com.bettercloud:vault-java-driver:5.1.0' - testImplementation 'io.rest-assured:rest-assured:4.5.1' + testImplementation 'io.rest-assured:rest-assured:5.0.0' testImplementation 'org.assertj:assertj-core:3.22.0' } From e7afcec5a09789bade7e4a808edf6f6eaf1dbc5b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Apr 2022 22:00:43 +0000 Subject: [PATCH 0061/3231] Bump java-client from 3.2.5 to 3.2.6 in /modules/couchbase Bumps [java-client](https://github.com/couchbase/couchbase-jvm-clients) from 3.2.5 to 3.2.6. - [Release notes](https://github.com/couchbase/couchbase-jvm-clients/releases) - [Commits](https://github.com/couchbase/couchbase-jvm-clients/compare/java-client-3.2.5...java-client-3.2.6) --- updated-dependencies: - dependency-name: com.couchbase.client:java-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/couchbase/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/couchbase/build.gradle b/modules/couchbase/build.gradle index 0245a5935cb..a3cb0fcbc70 100644 --- a/modules/couchbase/build.gradle +++ b/modules/couchbase/build.gradle @@ -5,6 +5,6 @@ dependencies { // TODO use JDK's HTTP client and/or Apache HttpClient5 shaded 'com.squareup.okhttp3:okhttp:3.14.9' - testImplementation 'com.couchbase.client:java-client:3.2.5' + testImplementation 'com.couchbase.client:java-client:3.2.6' testImplementation 'org.awaitility:awaitility:4.1.1' } From 3ff87f5bdeee8256195fb7b87a801a3eef7a35c9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Apr 2022 22:00:44 +0000 Subject: [PATCH 0062/3231] Bump jedis from 4.1.1 to 4.2.1 in /examples Bumps [jedis](https://github.com/redis/jedis) from 4.1.1 to 4.2.1. - [Release notes](https://github.com/redis/jedis/releases) - [Commits](https://github.com/redis/jedis/compare/v4.1.1...v4.2.1) --- updated-dependencies: - dependency-name: redis.clients:jedis dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- examples/redis-backed-cache-testng/build.gradle | 2 +- examples/redis-backed-cache/build.gradle | 2 +- examples/singleton-container/build.gradle | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/redis-backed-cache-testng/build.gradle b/examples/redis-backed-cache-testng/build.gradle index ec3d0bd134c..38a8715364a 100644 --- a/examples/redis-backed-cache-testng/build.gradle +++ b/examples/redis-backed-cache-testng/build.gradle @@ -8,7 +8,7 @@ repositories { dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.32' - implementation 'redis.clients:jedis:4.1.1' + implementation 'redis.clients:jedis:4.2.1' implementation 'com.google.code.gson:gson:2.8.9' implementation 'com.google.guava:guava:23.0' testImplementation 'org.testcontainers:testcontainers' diff --git a/examples/redis-backed-cache/build.gradle b/examples/redis-backed-cache/build.gradle index 32e35f07766..9583fe99147 100644 --- a/examples/redis-backed-cache/build.gradle +++ b/examples/redis-backed-cache/build.gradle @@ -8,7 +8,7 @@ repositories { dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.32' - implementation 'redis.clients:jedis:4.1.1' + implementation 'redis.clients:jedis:4.2.1' implementation 'com.google.code.gson:gson:2.8.9' implementation 'com.google.guava:guava:23.0' testImplementation 'org.testcontainers:testcontainers' diff --git a/examples/singleton-container/build.gradle b/examples/singleton-container/build.gradle index 2ebaba5be50..e8d470f0f23 100644 --- a/examples/singleton-container/build.gradle +++ b/examples/singleton-container/build.gradle @@ -8,7 +8,7 @@ repositories { dependencies { - implementation 'redis.clients:jedis:4.1.1' + implementation 'redis.clients:jedis:4.2.1' implementation 'com.google.code.gson:gson:2.8.9' implementation 'com.google.guava:guava:23.0' compileOnly 'org.slf4j:slf4j-api:1.7.32' From cce236c67ff09ca88aacf4435bd740ebac4f166b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Apr 2022 22:00:45 +0000 Subject: [PATCH 0063/3231] Bump mockito-core from 4.3.1 to 4.4.0 in /modules/junit-jupiter Bumps [mockito-core](https://github.com/mockito/mockito) from 4.3.1 to 4.4.0. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v4.3.1...v4.4.0) --- updated-dependencies: - dependency-name: org.mockito:mockito-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 749bf72690d..89882481dc1 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -9,7 +9,7 @@ dependencies { testImplementation 'com.zaxxer:HikariCP:4.0.3' testImplementation 'redis.clients:jedis:4.1.1' testImplementation 'org.apache.httpcomponents:httpclient:4.5.13' - testImplementation ('org.mockito:mockito-core:4.3.1') { + testImplementation ('org.mockito:mockito-core:4.4.0') { exclude(module: 'hamcrest-core') } testImplementation 'org.assertj:assertj-core:3.22.0' From 156971e69538d73d6a3d4ec1c0bd992b636ee3ea Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Apr 2022 22:00:46 +0000 Subject: [PATCH 0064/3231] Bump transport from 7.17.1 to 7.17.2 in /modules/elasticsearch Bumps [transport](https://github.com/elastic/elasticsearch) from 7.17.1 to 7.17.2. - [Release notes](https://github.com/elastic/elasticsearch/releases) - [Changelog](https://github.com/elastic/elasticsearch/blob/master/CHANGELOG.md) - [Commits](https://github.com/elastic/elasticsearch/compare/v7.17.1...v7.17.2) --- updated-dependencies: - dependency-name: org.elasticsearch.client:transport dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/elasticsearch/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/elasticsearch/build.gradle b/modules/elasticsearch/build.gradle index d669f0fbd05..a8d718af3c4 100644 --- a/modules/elasticsearch/build.gradle +++ b/modules/elasticsearch/build.gradle @@ -3,6 +3,6 @@ description = "TestContainers :: elasticsearch" dependencies { api project(':testcontainers') testImplementation "org.elasticsearch.client:elasticsearch-rest-client:7.16.2" - testImplementation "org.elasticsearch.client:transport:7.17.1" + testImplementation "org.elasticsearch.client:transport:7.17.2" testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } From 03fa998e2ea9cfabe5d9921f76c96dc82c0be2e6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Apr 2022 22:00:47 +0000 Subject: [PATCH 0065/3231] Bump logback-classic from 1.2.10 to 1.2.11 in /examples Bumps logback-classic from 1.2.10 to 1.2.11. --- updated-dependencies: - dependency-name: ch.qos.logback:logback-classic dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/disque-job-queue/build.gradle | 2 +- examples/kafka-cluster/build.gradle | 2 +- examples/linked-container/build.gradle | 2 +- examples/redis-backed-cache-testng/build.gradle | 2 +- examples/redis-backed-cache/build.gradle | 2 +- examples/singleton-container/build.gradle | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/disque-job-queue/build.gradle b/examples/disque-job-queue/build.gradle index 1639a091e91..2352eace899 100644 --- a/examples/disque-job-queue/build.gradle +++ b/examples/disque-job-queue/build.gradle @@ -12,7 +12,7 @@ dependencies { implementation 'biz.paluch.redis:spinach:0.3' implementation 'com.google.code.gson:gson:2.8.9' implementation 'com.google.guava:guava:23.0' - testImplementation 'ch.qos.logback:logback-classic:1.2.10' + testImplementation 'ch.qos.logback:logback-classic:1.2.11' testImplementation 'org.mockito:mockito-all:1.10.19' testImplementation 'org.testcontainers:testcontainers' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' diff --git a/examples/kafka-cluster/build.gradle b/examples/kafka-cluster/build.gradle index 2e37410bcea..7ba974d3a6d 100644 --- a/examples/kafka-cluster/build.gradle +++ b/examples/kafka-cluster/build.gradle @@ -13,5 +13,5 @@ dependencies { testImplementation 'org.apache.kafka:kafka-clients:3.1.0' testImplementation 'org.assertj:assertj-core:3.22.0' testImplementation 'com.google.guava:guava:23.0' - testImplementation 'ch.qos.logback:logback-classic:1.2.10' + testImplementation 'ch.qos.logback:logback-classic:1.2.11' } diff --git a/examples/linked-container/build.gradle b/examples/linked-container/build.gradle index 1cb3a0285f2..c61ce36e473 100644 --- a/examples/linked-container/build.gradle +++ b/examples/linked-container/build.gradle @@ -10,7 +10,7 @@ dependencies { implementation 'com.squareup.okhttp3:okhttp:4.9.3' implementation 'org.json:json:20211205' testImplementation 'org.postgresql:postgresql:42.3.3' - testImplementation 'ch.qos.logback:logback-classic:1.2.10' + testImplementation 'ch.qos.logback:logback-classic:1.2.11' testImplementation 'org.testcontainers:postgresql' } diff --git a/examples/redis-backed-cache-testng/build.gradle b/examples/redis-backed-cache-testng/build.gradle index ec3d0bd134c..bc1d30d9599 100644 --- a/examples/redis-backed-cache-testng/build.gradle +++ b/examples/redis-backed-cache-testng/build.gradle @@ -12,7 +12,7 @@ dependencies { implementation 'com.google.code.gson:gson:2.8.9' implementation 'com.google.guava:guava:23.0' testImplementation 'org.testcontainers:testcontainers' - testImplementation 'ch.qos.logback:logback-classic:1.2.10' + testImplementation 'ch.qos.logback:logback-classic:1.2.11' testImplementation 'org.testng:testng:7.5' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } diff --git a/examples/redis-backed-cache/build.gradle b/examples/redis-backed-cache/build.gradle index 32e35f07766..f2d5830404d 100644 --- a/examples/redis-backed-cache/build.gradle +++ b/examples/redis-backed-cache/build.gradle @@ -13,6 +13,6 @@ dependencies { implementation 'com.google.guava:guava:23.0' testImplementation 'org.testcontainers:testcontainers' testImplementation 'junit:junit:4.13.2' - testImplementation 'ch.qos.logback:logback-classic:1.2.10' + testImplementation 'ch.qos.logback:logback-classic:1.2.11' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } diff --git a/examples/singleton-container/build.gradle b/examples/singleton-container/build.gradle index 2ebaba5be50..2879ddb4fdd 100644 --- a/examples/singleton-container/build.gradle +++ b/examples/singleton-container/build.gradle @@ -13,7 +13,7 @@ dependencies { implementation 'com.google.guava:guava:23.0' compileOnly 'org.slf4j:slf4j-api:1.7.32' - testImplementation 'ch.qos.logback:logback-classic:1.2.10' + testImplementation 'ch.qos.logback:logback-classic:1.2.11' testImplementation 'org.testcontainers:testcontainers' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } From cb58298c4ef96968ce6e0f654d9f87d2a7b56126 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Apr 2022 22:00:47 +0000 Subject: [PATCH 0066/3231] Bump org.jetbrains.kotlin.plugin.spring in /examples Bumps org.jetbrains.kotlin.plugin.spring from 1.6.10 to 1.6.20. --- updated-dependencies: - dependency-name: org.jetbrains.kotlin.plugin.spring dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/spring-boot-kotlin-redis/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/spring-boot-kotlin-redis/build.gradle b/examples/spring-boot-kotlin-redis/build.gradle index 45cee0ec20b..1d98c3e3eff 100644 --- a/examples/spring-boot-kotlin-redis/build.gradle +++ b/examples/spring-boot-kotlin-redis/build.gradle @@ -1,7 +1,7 @@ plugins { id("org.springframework.boot") version "2.6.4" id("org.jetbrains.kotlin.jvm") version "1.6.10" - id("org.jetbrains.kotlin.plugin.spring") version "1.6.10" + id("org.jetbrains.kotlin.plugin.spring") version "1.6.20" } apply plugin: 'io.spring.dependency-management' From 58f70290f0a77c11fa3538cab671dd94b149afdb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Apr 2022 22:00:48 +0000 Subject: [PATCH 0067/3231] Bump awaitility from 4.1.1 to 4.2.0 in /core Bumps [awaitility](https://github.com/awaitility/awaitility) from 4.1.1 to 4.2.0. - [Release notes](https://github.com/awaitility/awaitility/releases) - [Changelog](https://github.com/awaitility/awaitility/blob/master/changelog.txt) - [Commits](https://github.com/awaitility/awaitility/compare/awaitility-4.1.1...awaitility-4.2.0) --- updated-dependencies: - dependency-name: org.awaitility:awaitility dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 558584a0d7c..c17169977cc 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -71,7 +71,7 @@ dependencies { exclude(group: 'org.jetbrains', module: 'annotations') } - shaded 'org.awaitility:awaitility:4.1.1' + shaded 'org.awaitility:awaitility:4.2.0' api platform('com.github.docker-java:docker-java-bom:3.2.13') shaded platform('com.github.docker-java:docker-java-bom:3.2.13') From 02dec3edf4408a43e5060ce0a639f5ab0043dc54 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Apr 2022 22:00:51 +0000 Subject: [PATCH 0068/3231] Bump s3 from 2.17.108 to 2.17.162 in /modules/localstack Bumps s3 from 2.17.108 to 2.17.162. --- updated-dependencies: - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index c16baad9a22..401230dbb61 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -7,6 +7,6 @@ dependencies { testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.169' testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.150' testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.169' - testImplementation 'software.amazon.awssdk:s3:2.17.108' + testImplementation 'software.amazon.awssdk:s3:2.17.162' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } From 285ce0efe024f2c33d62069d2e89eeb277b7a272 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Apr 2022 22:00:52 +0000 Subject: [PATCH 0069/3231] Bump neo4j-java-driver from 4.4.3 to 4.4.5 in /examples Bumps [neo4j-java-driver](https://github.com/neo4j/neo4j-java-driver) from 4.4.3 to 4.4.5. - [Release notes](https://github.com/neo4j/neo4j-java-driver/releases) - [Commits](https://github.com/neo4j/neo4j-java-driver/compare/4.4.3...4.4.5) --- updated-dependencies: - dependency-name: org.neo4j.driver:neo4j-java-driver dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/neo4j-container/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/neo4j-container/build.gradle b/examples/neo4j-container/build.gradle index 718adc3183a..652937d8b33 100644 --- a/examples/neo4j-container/build.gradle +++ b/examples/neo4j-container/build.gradle @@ -8,7 +8,7 @@ repositories { dependencies { testImplementation 'org.assertj:assertj-core:3.22.0' - testImplementation 'org.neo4j.driver:neo4j-java-driver:4.4.3' + testImplementation 'org.neo4j.driver:neo4j-java-driver:4.4.5' testImplementation 'org.testcontainers:neo4j' testImplementation 'org.testcontainers:junit-jupiter' } From 16fb27f651a7d1cd66881238cfa99e2433e483eb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Apr 2022 22:01:23 +0000 Subject: [PATCH 0070/3231] Bump actions/cache from 2.1.7 to 3.0.1 Bumps [actions/cache](https://github.com/actions/cache) from 2.1.7 to 3.0.1. - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/v2.1.7...v3.0.1) --- updated-dependencies: - dependency-name: actions/cache dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/ci-examples.yml | 4 ++-- .github/workflows/ci.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci-examples.yml b/.github/workflows/ci-examples.yml index 3fb2467a295..7b9a9e5c3a1 100644 --- a/.github/workflows/ci-examples.yml +++ b/.github/workflows/ci-examples.yml @@ -22,7 +22,7 @@ jobs: java-version: '8.0.302' distribution: temurin - name: Cache Gradle Home files - uses: actions/cache@v2.1.7 + uses: actions/cache@v3.0.1 continue-on-error: true with: path: ~/.gradle/caches @@ -50,7 +50,7 @@ jobs: java-version: '8.0.302' distribution: temurin - name: Cache Gradle Home files - uses: actions/cache@v2.1.7 + uses: actions/cache@v3.0.1 continue-on-error: true with: path: ~/.gradle/caches diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index db7232fffa8..4fad892230d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,7 +30,7 @@ jobs: java-version: '8.0.302' distribution: temurin - name: Cache Gradle Home files - uses: actions/cache@v2.1.7 + uses: actions/cache@v3.0.1 with: path: ~/.gradle/caches key: ${{ runner.os }}-gradle-home-testmatrix-${{ hashFiles('**/*.gradle') }} @@ -56,7 +56,7 @@ jobs: java-version: '8.0.302' distribution: temurin - name: Cache Gradle Home files - uses: actions/cache@v2.1.7 + uses: actions/cache@v3.0.1 continue-on-error: true with: path: ~/.gradle/caches From 6f2eb5c6105d2cf5fb9b0e927223f594fdcf9ab8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Apr 2022 22:01:29 +0000 Subject: [PATCH 0071/3231] Bump peter-evans/create-pull-request from 3.12.1 to 4.0.1 Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 3.12.1 to 4.0.1. - [Release notes](https://github.com/peter-evans/create-pull-request/releases) - [Commits](https://github.com/peter-evans/create-pull-request/compare/f22a7da129c901513876a2380e2dae9f8e145330...f1a7646cead32c950d90344a4fb5d4e926972a8f) --- updated-dependencies: - dependency-name: peter-evans/create-pull-request dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/update-docs-version.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/update-docs-version.yml b/.github/workflows/update-docs-version.yml index 2c7626c7ec7..ceb2385e986 100644 --- a/.github/workflows/update-docs-version.yml +++ b/.github/workflows/update-docs-version.yml @@ -17,7 +17,7 @@ jobs: sed -i "s/latest_version: .*/latest_version: ${GITHUB_REF##*/}/g" mkdocs.yml git diff - name: Create Pull Request - uses: peter-evans/create-pull-request@f22a7da129c901513876a2380e2dae9f8e145330 # v3.10.1 + uses: peter-evans/create-pull-request@f1a7646cead32c950d90344a4fb5d4e926972a8f # v3.10.1 with: title: Update docs version to ${GITHUB_REF##*/} body: | From a4695120eb1c3f49a030f686e5747c7506251e69 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Apr 2022 22:04:58 +0000 Subject: [PATCH 0072/3231] Bump r2dbc-postgresql in /modules/r2dbc Bumps [r2dbc-postgresql](https://github.com/r2dbc/r2dbc-postgresql) from 0.8.11.RELEASE to 0.8.12.RELEASE. - [Release notes](https://github.com/r2dbc/r2dbc-postgresql/releases) - [Changelog](https://github.com/pgjdbc/r2dbc-postgresql/blob/v0.8.12.RELEASE/CHANGELOG) - [Commits](https://github.com/r2dbc/r2dbc-postgresql/compare/v0.8.11.RELEASE...v0.8.12.RELEASE) --- updated-dependencies: - dependency-name: io.r2dbc:r2dbc-postgresql dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/r2dbc/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/r2dbc/build.gradle b/modules/r2dbc/build.gradle index 2fa066c21b3..ee5f5844e56 100644 --- a/modules/r2dbc/build.gradle +++ b/modules/r2dbc/build.gradle @@ -12,7 +12,7 @@ dependencies { api 'io.r2dbc:r2dbc-spi:0.8.1.RELEASE' testImplementation 'org.assertj:assertj-core:3.22.0' - testImplementation 'io.r2dbc:r2dbc-postgresql:0.8.11.RELEASE' + testImplementation 'io.r2dbc:r2dbc-postgresql:0.8.12.RELEASE' testImplementation project(':postgresql') testFixturesImplementation 'io.projectreactor:reactor-core:3.4.15' From b00d7c2ced538f9b1ff004a4f67e6a01ac25a32d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Apr 2022 22:04:59 +0000 Subject: [PATCH 0073/3231] Bump trino-jdbc from 369 to 375 in /modules/trino Bumps trino-jdbc from 369 to 375. --- updated-dependencies: - dependency-name: io.trino:trino-jdbc dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- modules/trino/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/trino/build.gradle b/modules/trino/build.gradle index 9bf0e24f06b..f99951dc7fa 100644 --- a/modules/trino/build.gradle +++ b/modules/trino/build.gradle @@ -4,6 +4,6 @@ dependencies { api project(':jdbc') testImplementation project(':jdbc-test') - testImplementation 'io.trino:trino-jdbc:369' + testImplementation 'io.trino:trino-jdbc:375' compileOnly 'org.jetbrains:annotations:23.0.0' } From 0bc42f61c149e5d79cdbccec95ad19638cc056d7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Apr 2022 22:04:59 +0000 Subject: [PATCH 0074/3231] Bump okhttp from 3.14.9 to 4.9.3 in /modules/solr Bumps [okhttp](https://github.com/square/okhttp) from 3.14.9 to 4.9.3. - [Release notes](https://github.com/square/okhttp/releases) - [Changelog](https://github.com/square/okhttp/blob/master/CHANGELOG.md) - [Commits](https://github.com/square/okhttp/compare/parent-3.14.9...parent-4.9.3) --- updated-dependencies: - dependency-name: com.squareup.okhttp3:okhttp dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- modules/solr/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/solr/build.gradle b/modules/solr/build.gradle index 49b2a9970f3..5a6326f3844 100644 --- a/modules/solr/build.gradle +++ b/modules/solr/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Solr" dependencies { api project(':testcontainers') // TODO use JDK's HTTP client and/or Apache HttpClient5 - shaded 'com.squareup.okhttp3:okhttp:3.14.9' + shaded 'com.squareup.okhttp3:okhttp:4.9.3' testImplementation 'org.apache.solr:solr-solrj:8.11.1' From 1cdac6a748e3a80802d3c0432efc512c45491b77 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Apr 2022 22:05:00 +0000 Subject: [PATCH 0075/3231] Bump client-java from 14.0.0 to 15.0.0 in /modules/k3s Bumps [client-java](https://github.com/kubernetes-client/java) from 14.0.0 to 15.0.0. - [Release notes](https://github.com/kubernetes-client/java/releases) - [Changelog](https://github.com/kubernetes-client/java/blob/master/CHANGELOG.md) - [Commits](https://github.com/kubernetes-client/java/compare/client-java-parent-14.0.0...v15.0.0) --- updated-dependencies: - dependency-name: io.kubernetes:client-java dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- modules/k3s/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/k3s/build.gradle b/modules/k3s/build.gradle index 075078f36d9..a373d8115d7 100644 --- a/modules/k3s/build.gradle +++ b/modules/k3s/build.gradle @@ -9,6 +9,6 @@ dependencies { shaded 'com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.8.8' testImplementation 'io.fabric8:kubernetes-client:5.12.1' - testImplementation 'io.kubernetes:client-java:14.0.0' + testImplementation 'io.kubernetes:client-java:15.0.0' testImplementation 'org.assertj:assertj-core:3.22.0' } From 5a3881995369330b216773af9fcc1ebf9d98e9ce Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Apr 2022 22:05:01 +0000 Subject: [PATCH 0076/3231] Bump google-cloud-bigtable from 2.5.2 to 2.6.1 in /modules/gcloud Bumps [google-cloud-bigtable](https://github.com/googleapis/java-bigtable) from 2.5.2 to 2.6.1. - [Release notes](https://github.com/googleapis/java-bigtable/releases) - [Changelog](https://github.com/googleapis/java-bigtable/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/java-bigtable/compare/v2.5.2...v2.6.1) --- updated-dependencies: - dependency-name: com.google.cloud:google-cloud-bigtable dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/gcloud/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle index e0b232ac949..93111ef3f16 100644 --- a/modules/gcloud/build.gradle +++ b/modules/gcloud/build.gradle @@ -7,6 +7,6 @@ dependencies { testImplementation 'com.google.cloud:google-cloud-firestore:3.0.14' testImplementation 'com.google.cloud:google-cloud-pubsub:1.115.1' testImplementation 'com.google.cloud:google-cloud-spanner:6.20.0' - testImplementation 'com.google.cloud:google-cloud-bigtable:2.5.2' + testImplementation 'com.google.cloud:google-cloud-bigtable:2.6.1' testImplementation 'org.assertj:assertj-core:3.22.0' } From 0cc5b67ef8ce32ec0978eaffbf94628824fd7476 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Apr 2022 22:05:01 +0000 Subject: [PATCH 0077/3231] Bump mongodb-driver-sync from 4.5.0 to 4.5.1 in /modules/mongodb Bumps [mongodb-driver-sync](https://github.com/mongodb/mongo-java-driver) from 4.5.0 to 4.5.1. - [Release notes](https://github.com/mongodb/mongo-java-driver/releases) - [Commits](https://github.com/mongodb/mongo-java-driver/compare/r4.5.0...r4.5.1) --- updated-dependencies: - dependency-name: org.mongodb:mongodb-driver-sync dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/mongodb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mongodb/build.gradle b/modules/mongodb/build.gradle index 73dba45dbbf..6bca2cb892b 100644 --- a/modules/mongodb/build.gradle +++ b/modules/mongodb/build.gradle @@ -3,5 +3,5 @@ description = "Testcontainers :: MongoDB" dependencies { api project(':testcontainers') - testImplementation("org.mongodb:mongodb-driver-sync:4.5.0") + testImplementation("org.mongodb:mongodb-driver-sync:4.5.1") } From b9adf8717210a5bc42e850f03722970fbe692409 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Apr 2022 22:05:02 +0000 Subject: [PATCH 0078/3231] Bump reactor-core from 3.4.15 to 3.4.16 in /modules/r2dbc Bumps [reactor-core](https://github.com/reactor/reactor-core) from 3.4.15 to 3.4.16. - [Release notes](https://github.com/reactor/reactor-core/releases) - [Commits](https://github.com/reactor/reactor-core/compare/v3.4.15...v3.4.16) --- updated-dependencies: - dependency-name: io.projectreactor:reactor-core dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/r2dbc/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/r2dbc/build.gradle b/modules/r2dbc/build.gradle index 2fa066c21b3..56e910910d1 100644 --- a/modules/r2dbc/build.gradle +++ b/modules/r2dbc/build.gradle @@ -15,6 +15,6 @@ dependencies { testImplementation 'io.r2dbc:r2dbc-postgresql:0.8.11.RELEASE' testImplementation project(':postgresql') - testFixturesImplementation 'io.projectreactor:reactor-core:3.4.15' + testFixturesImplementation 'io.projectreactor:reactor-core:3.4.16' testFixturesImplementation 'org.assertj:assertj-core:3.14.0' } From 8973288c81fb249f80c308e1ec6fbe466b52d237 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Apr 2022 22:05:12 +0000 Subject: [PATCH 0079/3231] Bump google-cloud-firestore from 3.0.14 to 3.0.21 in /modules/gcloud Bumps [google-cloud-firestore](https://github.com/googleapis/java-firestore) from 3.0.14 to 3.0.21. - [Release notes](https://github.com/googleapis/java-firestore/releases) - [Changelog](https://github.com/googleapis/java-firestore/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/java-firestore/compare/v3.0.14...v3.0.21) --- updated-dependencies: - dependency-name: com.google.cloud:google-cloud-firestore dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/gcloud/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle index e0b232ac949..cdc07d77c37 100644 --- a/modules/gcloud/build.gradle +++ b/modules/gcloud/build.gradle @@ -4,7 +4,7 @@ dependencies { api project(':testcontainers') testImplementation 'com.google.cloud:google-cloud-datastore:2.2.2' - testImplementation 'com.google.cloud:google-cloud-firestore:3.0.14' + testImplementation 'com.google.cloud:google-cloud-firestore:3.0.21' testImplementation 'com.google.cloud:google-cloud-pubsub:1.115.1' testImplementation 'com.google.cloud:google-cloud-spanner:6.20.0' testImplementation 'com.google.cloud:google-cloud-bigtable:2.5.2' From fe7782c028875eb166982387a2011520aae5ebf7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Apr 2022 22:05:33 +0000 Subject: [PATCH 0080/3231] Bump hivemq-extension-sdk from 4.7.4 to 4.7.5 in /modules/hivemq Bumps [hivemq-extension-sdk](https://github.com/hivemq/hivemq-extension-sdk) from 4.7.4 to 4.7.5. - [Release notes](https://github.com/hivemq/hivemq-extension-sdk/releases) - [Commits](https://github.com/hivemq/hivemq-extension-sdk/compare/4.7.4...4.7.5) --- updated-dependencies: - dependency-name: com.hivemq:hivemq-extension-sdk dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/hivemq/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/hivemq/build.gradle b/modules/hivemq/build.gradle index f0c3aaa2e69..17be5658992 100644 --- a/modules/hivemq/build.gradle +++ b/modules/hivemq/build.gradle @@ -13,7 +13,7 @@ dependencies { testImplementation("org.junit.jupiter:junit-jupiter-api:5.8.2") testImplementation(project(":junit-jupiter")) - testImplementation("com.hivemq:hivemq-extension-sdk:4.7.4") + testImplementation("com.hivemq:hivemq-extension-sdk:4.7.5") testImplementation("com.hivemq:hivemq-mqtt-client:1.3.0") testImplementation("org.apache.httpcomponents:httpclient:4.5.13") testImplementation("ch.qos.logback:logback-classic:1.2.10") From 64f96781c518b75fddc98bbad05b234faec7169f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Apr 2022 22:05:35 +0000 Subject: [PATCH 0081/3231] Bump zip4j from 2.9.1 to 2.10.0 in /modules/hivemq Bumps [zip4j](https://github.com/srikanth-lingala/zip4j) from 2.9.1 to 2.10.0. - [Release notes](https://github.com/srikanth-lingala/zip4j/releases) - [Commits](https://github.com/srikanth-lingala/zip4j/compare/v2.9.1...v2.10.0) --- updated-dependencies: - dependency-name: net.lingala.zip4j:zip4j dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/hivemq/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/hivemq/build.gradle b/modules/hivemq/build.gradle index f0c3aaa2e69..79c59bba0d2 100644 --- a/modules/hivemq/build.gradle +++ b/modules/hivemq/build.gradle @@ -9,7 +9,7 @@ dependencies { shaded("org.javassist:javassist:3.28.0-GA") shaded("org.jboss.shrinkwrap:shrinkwrap-api:1.2.6") shaded("org.jboss.shrinkwrap:shrinkwrap-impl-base:1.2.6") - shaded("net.lingala.zip4j:zip4j:2.9.1") + shaded("net.lingala.zip4j:zip4j:2.10.0") testImplementation("org.junit.jupiter:junit-jupiter-api:5.8.2") testImplementation(project(":junit-jupiter")) From 06fc95f5233b0c11d2ba319b4b02587db0b5028b Mon Sep 17 00:00:00 2001 From: Falko Modler Date: Wed, 6 Apr 2022 17:01:03 +0200 Subject: [PATCH 0082/3231] Fix ScriptUtils.runInitScript() in hierarchical classloader setup by using TCCL (#5167) --- .../main/java/org/testcontainers/ext/ScriptUtils.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/modules/database-commons/src/main/java/org/testcontainers/ext/ScriptUtils.java b/modules/database-commons/src/main/java/org/testcontainers/ext/ScriptUtils.java index 76f0cb90ea0..1b94b84f368 100644 --- a/modules/database-commons/src/main/java/org/testcontainers/ext/ScriptUtils.java +++ b/modules/database-commons/src/main/java/org/testcontainers/ext/ScriptUtils.java @@ -293,10 +293,13 @@ public static boolean containsSqlScriptDelimiters(String script, String delim) { */ public static void runInitScript(DatabaseDelegate databaseDelegate, String initScriptPath) { try { - URL resource = ScriptUtils.class.getClassLoader().getResource(initScriptPath); + URL resource = Thread.currentThread().getContextClassLoader().getResource(initScriptPath); if (resource == null) { - LOGGER.warn("Could not load classpath init script: {}", initScriptPath); - throw new ScriptLoadException("Could not load classpath init script: " + initScriptPath + ". Resource not found."); + resource = ScriptUtils.class.getClassLoader().getResource(initScriptPath); + if (resource == null) { + LOGGER.warn("Could not load classpath init script: {}", initScriptPath); + throw new ScriptLoadException("Could not load classpath init script: " + initScriptPath + ". Resource not found."); + } } String scripts = IOUtils.toString(resource, StandardCharsets.UTF_8); executeDatabaseScript(databaseDelegate, initScriptPath, scripts); From 734678f4be9e9a439b4391265d5f369ed1bab4b5 Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Thu, 7 Apr 2022 09:29:04 +0200 Subject: [PATCH 0083/3231] Deperecate `getContainerIpAddress` (#5247) --- .../main/java/org/testcontainers/containers/ContainerState.java | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/main/java/org/testcontainers/containers/ContainerState.java b/core/src/main/java/org/testcontainers/containers/ContainerState.java index e5903b0b806..ef2c19a06c9 100644 --- a/core/src/main/java/org/testcontainers/containers/ContainerState.java +++ b/core/src/main/java/org/testcontainers/containers/ContainerState.java @@ -47,6 +47,7 @@ public interface ContainerState { * @deprecated use {@link #getHost()} * @see #getHost() */ + @Deprecated default String getContainerIpAddress() { return getHost(); } From 53e80aa4115b60eee11823b5aa7d3616065a9421 Mon Sep 17 00:00:00 2001 From: Matthew de Detrich Date: Thu, 7 Apr 2022 18:03:27 +0200 Subject: [PATCH 0084/3231] Fix incorrect path for RABBITMQ_CONFIG_FILE (#5184) Co-authored-by: Kevin Wittek --- .../containers/RabbitMQContainer.java | 2 +- .../containers/RabbitMQContainerTest.java | 24 +++++++------------ .../src/test/resources/logback-test.xml | 16 +++++++++++++ .../src/test/resources/rabbitmq-custom.conf | 3 +-- .../src/test/resources/rabbitmq-custom.config | 18 ++++++-------- 5 files changed, 34 insertions(+), 29 deletions(-) create mode 100644 modules/rabbitmq/src/test/resources/logback-test.xml diff --git a/modules/rabbitmq/src/main/java/org/testcontainers/containers/RabbitMQContainer.java b/modules/rabbitmq/src/main/java/org/testcontainers/containers/RabbitMQContainer.java index edbffcf3174..37dc2fbe7ca 100644 --- a/modules/rabbitmq/src/main/java/org/testcontainers/containers/RabbitMQContainer.java +++ b/modules/rabbitmq/src/main/java/org/testcontainers/containers/RabbitMQContainer.java @@ -396,7 +396,7 @@ public RabbitMQContainer withRabbitMQConfig(MountableFile rabbitMQConf) { * @return This container. */ public RabbitMQContainer withRabbitMQConfigSysctl(MountableFile rabbitMQConf) { - withEnv("RABBITMQ_CONFIG_FILE", "/etc/rabbitmq/rabbitmq-custom"); + withEnv("RABBITMQ_CONFIG_FILE", "/etc/rabbitmq/rabbitmq-custom.conf"); return withCopyFileToContainer(rabbitMQConf, "/etc/rabbitmq/rabbitmq-custom.conf"); } diff --git a/modules/rabbitmq/src/test/java/org/testcontainers/containers/RabbitMQContainerTest.java b/modules/rabbitmq/src/test/java/org/testcontainers/containers/RabbitMQContainerTest.java index 8be97deb710..474d613cb8b 100644 --- a/modules/rabbitmq/src/test/java/org/testcontainers/containers/RabbitMQContainerTest.java +++ b/modules/rabbitmq/src/test/java/org/testcontainers/containers/RabbitMQContainerTest.java @@ -14,6 +14,7 @@ import java.io.File; import java.io.FileInputStream; import java.io.IOException; +import java.io.InputStream; import java.security.KeyManagementException; import java.security.KeyStore; import java.security.KeyStoreException; @@ -21,6 +22,8 @@ import java.security.UnrecoverableKeyException; import java.security.cert.CertificateException; import java.util.Collections; +import java.util.Objects; +import java.util.Scanner; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatCode; @@ -115,43 +118,34 @@ public void shouldCreateRabbitMQContainerWithQueues() throws IOException, Interr } @Test - public void shouldMountConfigurationFile() - { + public void shouldMountConfigurationFile() { try (RabbitMQContainer container = new RabbitMQContainer(RabbitMQTestImages.RABBITMQ_IMAGE)) { - container.withRabbitMQConfig(MountableFile.forClasspathResource("/rabbitmq-custom.conf")); container.start(); - assertThat(container.getLogs()).contains("config file(s) : /etc/rabbitmq/rabbitmq-custom.conf"); - assertThat(container.getLogs()).doesNotContain(" (not found)"); + assertThat(container.getLogs()).contains("debug"); // config file changes log level to `debug` } } @Test - public void shouldMountConfigurationFileErlang() - { + public void shouldMountConfigurationFileErlang() { try (RabbitMQContainer container = new RabbitMQContainer(RabbitMQTestImages.RABBITMQ_IMAGE)) { - container.withRabbitMQConfigErlang(MountableFile.forClasspathResource("/rabbitmq-custom.config")); container.start(); - assertThat(container.getLogs()).contains("config file(s) : /etc/rabbitmq/rabbitmq-custom.config"); - assertThat(container.getLogs()).doesNotContain(" (not found)"); + assertThat(container.getLogs()).contains("debug"); // config file changes log level to `debug` } } @Test - public void shouldMountConfigurationFileSysctl() - { + public void shouldMountConfigurationFileSysctl() { try (RabbitMQContainer container = new RabbitMQContainer(RabbitMQTestImages.RABBITMQ_IMAGE)) { - container.withRabbitMQConfigSysctl(MountableFile.forClasspathResource("/rabbitmq-custom.conf")); container.start(); - assertThat(container.getLogs()).contains("config file(s) : /etc/rabbitmq/rabbitmq-custom.conf"); - assertThat(container.getLogs()).doesNotContain(" (not found)"); + assertThat(container.getLogs()).contains("debug"); // config file changes log level to `debug` } } diff --git a/modules/rabbitmq/src/test/resources/logback-test.xml b/modules/rabbitmq/src/test/resources/logback-test.xml new file mode 100644 index 00000000000..535e406fc13 --- /dev/null +++ b/modules/rabbitmq/src/test/resources/logback-test.xml @@ -0,0 +1,16 @@ + + + + + + %d{HH:mm:ss.SSS} %-5level %logger - %msg%n + + + + + + + + + diff --git a/modules/rabbitmq/src/test/resources/rabbitmq-custom.conf b/modules/rabbitmq/src/test/resources/rabbitmq-custom.conf index 293829a353b..a2994c78602 100644 --- a/modules/rabbitmq/src/test/resources/rabbitmq-custom.conf +++ b/modules/rabbitmq/src/test/resources/rabbitmq-custom.conf @@ -1,2 +1 @@ -loopback_users.guest = false -listeners.tcp.default = 5555 +log.console.level = debug diff --git a/modules/rabbitmq/src/test/resources/rabbitmq-custom.config b/modules/rabbitmq/src/test/resources/rabbitmq-custom.config index a1c581542ce..353a854fc42 100644 --- a/modules/rabbitmq/src/test/resources/rabbitmq-custom.config +++ b/modules/rabbitmq/src/test/resources/rabbitmq-custom.config @@ -1,13 +1,9 @@ [ -{ rabbit, [ -{ loopback_users, [ ] }, - { tcp_listeners, [ 5555 ] }, - { ssl_listeners, [ ] }, - { default_vhost, <<"vhost">> }, - { hipe_compile, false } - ] }, - { rabbitmq_management, [ { listener, [ - { port, 15672 }, - { ssl, false } - ] } ] } +{rabbit, + [ + {log, + [{console, [{level, debug}]}] + } + ] +} ]. From c7449edda49ff780921067c4a03014ce68b2aa41 Mon Sep 17 00:00:00 2001 From: Alexander Reelsen Date: Fri, 8 Apr 2022 09:16:53 +0200 Subject: [PATCH 0085/3231] Elasticsearch: Ensure Elasticsearch 8 works OOTB secure as default (#5099) Since Elasticsearch 8.0 the default is to enable security, meaning TLS and authentication. This adds a check for Elasticsearch 8.0 to change the default behaviour to properly support this change, but you can still run Elasticsearch with security features disabled, if you want. Co-authored-by: Kevin Wittek Co-authored-by: Sergei Egorov --- .../elasticsearch/ElasticsearchContainer.java | 96 +++++++++++++++---- .../ElasticsearchContainerTest.java | 38 ++++++-- 2 files changed, 111 insertions(+), 23 deletions(-) diff --git a/modules/elasticsearch/src/main/java/org/testcontainers/elasticsearch/ElasticsearchContainer.java b/modules/elasticsearch/src/main/java/org/testcontainers/elasticsearch/ElasticsearchContainer.java index acbca15aaf5..84881b2e87d 100644 --- a/modules/elasticsearch/src/main/java/org/testcontainers/elasticsearch/ElasticsearchContainer.java +++ b/modules/elasticsearch/src/main/java/org/testcontainers/elasticsearch/ElasticsearchContainer.java @@ -1,21 +1,33 @@ package org.testcontainers.elasticsearch; -import static java.net.HttpURLConnection.HTTP_OK; -import static java.net.HttpURLConnection.HTTP_UNAUTHORIZED; - -import java.net.InetSocketAddress; -import java.time.Duration; +import com.github.dockerjava.api.command.InspectContainerResponse; +import org.apache.commons.io.IOUtils; import org.testcontainers.containers.GenericContainer; -import org.testcontainers.containers.wait.strategy.HttpWaitStrategy; +import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy; import org.testcontainers.utility.Base58; +import org.testcontainers.utility.ComparableVersion; import org.testcontainers.utility.DockerImageName; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManagerFactory; +import java.io.ByteArrayInputStream; +import java.net.InetSocketAddress; +import java.security.KeyStore; +import java.security.cert.Certificate; +import java.security.cert.CertificateFactory; +import java.util.Optional; + /** * Represents an elasticsearch docker instance which exposes by default port 9200 and 9300 (transport.tcp.port) * The docker image is by default fetched from docker.elastic.co/elasticsearch/elasticsearch */ public class ElasticsearchContainer extends GenericContainer { + /** + * Elasticsearch Default Password for Elasticsearch >= 8 + */ + public static final String ELASTICSEARCH_DEFAULT_PASSWORD = "changeme"; + /** * Elasticsearch Default HTTP port */ @@ -39,7 +51,10 @@ public class ElasticsearchContainer extends GenericContainer caCertAsBytes = Optional.empty(); /** * @deprecated use {@link ElasticsearchContainer(DockerImageName)} instead @@ -65,23 +80,67 @@ public ElasticsearchContainer(final DockerImageName dockerImageName) { super(dockerImageName); dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME, DEFAULT_OSS_IMAGE_NAME); - - if (dockerImageName.isCompatibleWith(DEFAULT_OSS_IMAGE_NAME)) { - this.isOss = true; - } + this.isOss = dockerImageName.isCompatibleWith(DEFAULT_OSS_IMAGE_NAME); logger().info("Starting an elasticsearch container using [{}]", dockerImageName); withNetworkAliases("elasticsearch-" + Base58.randomString(6)); withEnv("discovery.type", "single-node"); addExposedPorts(ELASTICSEARCH_DEFAULT_PORT, ELASTICSEARCH_DEFAULT_TCP_PORT); - setWaitStrategy(new HttpWaitStrategy() - .forPort(ELASTICSEARCH_DEFAULT_PORT) - .forStatusCodeMatching(response -> response == HTTP_OK || response == HTTP_UNAUTHORIZED) - .withStartupTimeout(Duration.ofMinutes(2))); + this.isAtLeastMajorVersion8 = new ComparableVersion(dockerImageName.getVersionPart()).isGreaterThanOrEqualTo("8.0.0"); + // regex that + // matches 8.0 JSON logging with no whitespace between message field and content + // matches 7.x JSON logging with whitespace between message field and content + // matches 6.x text logging with node name in brackets and just a 'started' message till the end of the line + String regex = ".*(\"message\":\\s?\"started\".*|] started\n$)"; + setWaitStrategy(new LogMessageWaitStrategy().withRegEx(regex)); + if (isAtLeastMajorVersion8) { + withPassword(ELASTICSEARCH_DEFAULT_PASSWORD); + } + } + + @Override + protected void containerIsStarted(InspectContainerResponse containerInfo) { + if (isAtLeastMajorVersion8) { + byte[] bytes = copyFileFromContainer("/usr/share/elasticsearch/config/certs/http_ca.crt", IOUtils::toByteArray); + if (bytes.length > 0) { + this.caCertAsBytes = Optional.of(bytes); + } + } + } + + /** + * If this is running above Elasticsearch 8, this will return the probably self-signed CA cert that has been extracted + * + * @return byte array optional containing the CA cert extracted from the docker container + */ + public Optional caCertAsBytes() { + return caCertAsBytes; } /** - * Define the Elasticsearch password to set. It enables security behind the scene. + * A SSL context based on the self signed CA, so that using this SSL Context allows to connect to the Elasticsearch service + * @return a customized SSL Context + */ + public SSLContext createSslContextFromCa() { + try { + CertificateFactory factory = CertificateFactory.getInstance("X.509"); + Certificate trustedCa = factory.generateCertificate(new ByteArrayInputStream(caCertAsBytes.get())); + KeyStore trustStore = KeyStore.getInstance("pkcs12"); + trustStore.load(null, null); + trustStore.setCertificateEntry("ca", trustedCa); + + final SSLContext sslContext = SSLContext.getInstance("TLSv1.3"); + TrustManagerFactory tmfactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + tmfactory.init(trustStore); + sslContext.init(null, tmfactory.getTrustManagers(), null); + return sslContext; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * Define the Elasticsearch password to set. It enables security behind the scene for major version below 8.0.0. * It's not possible to use security with the oss image. * @param password Password to set * @return this @@ -92,7 +151,10 @@ public ElasticsearchContainer withPassword(String password) { "Please switch to the default distribution"); } withEnv("ELASTIC_PASSWORD", password); - withEnv("xpack.security.enabled", "true"); + if (!isAtLeastMajorVersion8) { + // major version 8 is secure by default and does not need this to enable authentication + withEnv("xpack.security.enabled", "true"); + } return this; } diff --git a/modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java b/modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java index 859587645d7..956c794135b 100644 --- a/modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java +++ b/modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java @@ -1,11 +1,5 @@ package org.testcontainers.elasticsearch; -import static org.hamcrest.CoreMatchers.containsString; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.rnorth.visibleassertions.VisibleAssertions.assertThrows; - -import java.io.IOException; import org.apache.http.HttpHost; import org.apache.http.auth.AuthScope; import org.apache.http.auth.UsernamePasswordCredentials; @@ -25,6 +19,13 @@ import org.junit.Test; import org.testcontainers.utility.DockerImageName; +import java.io.IOException; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.rnorth.visibleassertions.VisibleAssertions.assertThrows; + public class ElasticsearchContainerTest { /** @@ -249,6 +250,31 @@ public void incompatibleSettingsTest() { ); } + @Test + public void testElasticsearch8SecureByDefault() throws Exception { + try (ElasticsearchContainer container = new ElasticsearchContainer("docker.elastic.co/elasticsearch/elasticsearch:8.0.0")) { + // Start the container. This step might take some time... + container.start(); + + // Create the secured client. + final CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); + credentialsProvider.setCredentials(AuthScope.ANY, + new UsernamePasswordCredentials(ELASTICSEARCH_USERNAME, ElasticsearchContainer.ELASTICSEARCH_DEFAULT_PASSWORD)); + + client = RestClient.builder(HttpHost.create("https://" + container.getHttpHostAddress())) + .setHttpClientConfigCallback(httpClientBuilder -> { + httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider); + httpClientBuilder.setSSLContext(container.createSslContextFromCa()); + return httpClientBuilder; + }) + .build(); + + Response response = client.performRequest(new Request("GET", "/_cluster/health")); + assertThat(response.getStatusLine().getStatusCode(), is(200)); + assertThat(EntityUtils.toString(response.getEntity()), containsString("cluster_name")); + } + } + private RestClient getClient(ElasticsearchContainer container) { if (client == null) { final CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); From d93dceae969b63645b07881e80a61c57e3ca564b Mon Sep 17 00:00:00 2001 From: Thomas Darimont Date: Fri, 8 Apr 2022 16:02:00 +0200 Subject: [PATCH 0086/3231] Allow to create a file in container from a Transferable (#3814) (#3815) Co-authored-by: Alex Stockinger Co-authored-by: Sergei Egorov Co-authored-by: Kevin Wittek --- core/build.gradle | 2 + .../testcontainers/containers/Container.java | 15 +++- .../containers/GenericContainer.java | 53 ++++++----- .../images/builder/Transferable.java | 15 ++++ .../testcontainers/utility/MountableFile.java | 26 ++++++ .../containers/GenericContainerTest.java | 36 ++++++++ .../containers/ReusabilityUnitTests.java | 87 +++++++++++++++---- 7 files changed, 193 insertions(+), 41 deletions(-) diff --git a/core/build.gradle b/core/build.gradle index c17169977cc..3f1ad71731d 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -45,6 +45,8 @@ tasks.japicmp { "org.testcontainers.utility.ResourceReaper#start(com.github.dockerjava.api.DockerClient)", "org.testcontainers.utility.ResourceReaper#registerNetworkForCleanup(java.lang.String)", "org.testcontainers.utility.ResourceReaper#removeNetworks(java.lang.String)", + "org.testcontainers.images.builder.Transferable#of(java.lang.String)", + "org.testcontainers.images.builder.Transferable#updateChecksum(java.util.zip.Checksum)" ] fieldExcludes = [] diff --git a/core/src/main/java/org/testcontainers/containers/Container.java b/core/src/main/java/org/testcontainers/containers/Container.java index d9291fc9fc8..8fd53fc5a0e 100644 --- a/core/src/main/java/org/testcontainers/containers/Container.java +++ b/core/src/main/java/org/testcontainers/containers/Container.java @@ -12,6 +12,7 @@ import org.testcontainers.containers.startupcheck.StartupCheckStrategy; import org.testcontainers.containers.traits.LinkableContainer; import org.testcontainers.containers.wait.strategy.WaitStrategy; +import org.testcontainers.images.builder.Transferable; import org.testcontainers.utility.LogUtils; import org.testcontainers.utility.MountableFile; @@ -169,11 +170,23 @@ default SELF withFileSystemBind(String hostPath, String containerPath) { * Set the file to be copied before starting a created container * * @param mountableFile a Mountable file with path of source file / folder on host machine - * @param containerPath a destination path on conatiner to which the files / folders to be copied + * @param containerPath a destination path on container to which the files / folders to be copied * @return this + * + * @deprecated Use {@link #withCopyToContainer(Transferable, String)} instead */ + @Deprecated SELF withCopyFileToContainer(MountableFile mountableFile, String containerPath); + /** + * Set the content to be copied before starting a created container + * + * @param transferable a Transferable + * @param containerPath a destination path on container to which the files / folders to be copied + * @return this + */ + SELF withCopyToContainer(Transferable transferable, String containerPath); + /** * Add an environment variable to be passed to the container. * diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index 70e080711a7..d47bc6cfc5e 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -23,6 +23,7 @@ import com.google.common.hash.Hashing; import lombok.AccessLevel; import lombok.Data; +import lombok.Getter; import lombok.NonNull; import lombok.Setter; import lombok.SneakyThrows; @@ -49,6 +50,7 @@ import org.testcontainers.containers.wait.strategy.WaitStrategyTarget; import org.testcontainers.images.ImagePullPolicy; import org.testcontainers.images.RemoteDockerImage; +import org.testcontainers.images.builder.Transferable; import org.testcontainers.lifecycle.Startable; import org.testcontainers.lifecycle.Startables; import org.testcontainers.lifecycle.TestDescription; @@ -186,8 +188,15 @@ public class GenericContainer> private Long shmSize; // Maintain order in which entries are added, as earlier target location may be a prefix of a later location. + @Deprecated private Map copyToFileContainerPathMap = new LinkedHashMap<>(); + // Maintain order in which entries are added, as earlier target location may be a prefix of a later location. + @Setter(AccessLevel.NONE) + @Getter(AccessLevel.MODULE) + @VisibleForTesting + private Map copyToTransferableContainerPathMap = new LinkedHashMap<>(); + protected final Set dependencies = new HashSet<>(); /** @@ -415,6 +424,8 @@ private void tryStart(Instant startedAt) { // TODO use single "copy" invocation (and calculate an hash of the resulting tar archive) copyToFileContainerPathMap.forEach(this::copyFileToContainer); + + copyToTransferableContainerPathMap.forEach(this::copyFileToContainer); } connectToPortForwardingNetwork(createCommand.getNetworkMode()); @@ -530,33 +541,18 @@ private void tryStart(Instant startedAt) { @VisibleForTesting Checksum hashCopiedFiles() { Checksum checksum = new Adler32(); - copyToFileContainerPathMap.entrySet().stream().sorted(Entry.comparingByValue()).forEach(entry -> { - byte[] pathBytes = entry.getValue().getBytes(); - // Add path to the hash - checksum.update(pathBytes, 0, pathBytes.length); - - File file = new File(entry.getKey().getResolvedPath()); - checksumFile(file, checksum); - }); + Stream.of(copyToFileContainerPathMap, copyToTransferableContainerPathMap) + .flatMap(it -> it.entrySet().stream()) + .sorted(Entry.comparingByValue()).forEach(entry -> { + byte[] pathBytes = entry.getValue().getBytes(); + // Add path to the hash + checksum.update(pathBytes, 0, pathBytes.length); + + entry.getKey().updateChecksum(checksum); + }); return checksum; } - @VisibleForTesting - @SneakyThrows(IOException.class) - void checksumFile(File file, Checksum checksum) { - Path path = file.toPath(); - checksum.update(MountableFile.getUnixFileMode(path)); - if (file.isDirectory()) { - try (Stream stream = Files.walk(path)) { - stream.filter(it -> it != path).forEach(it -> { - checksumFile(it.toFile(), checksum); - }); - } - } else { - FileUtils.checksum(file, checksum); - } - } - @UnstableAPI @SneakyThrows(JsonProcessingException.class) final String hash(CreateContainerCmd createCommand) { @@ -1296,6 +1292,15 @@ public SELF withCopyFileToContainer(MountableFile mountableFile, String containe return self(); } + /** + * {@inheritDoc} + */ + @Override + public SELF withCopyToContainer(Transferable transferable, String containerPath) { + copyToTransferableContainerPathMap.put(transferable, containerPath); + return self(); + } + /** * Get the IP address that this container may be reached on (may not be the local machine). * diff --git a/core/src/main/java/org/testcontainers/images/builder/Transferable.java b/core/src/main/java/org/testcontainers/images/builder/Transferable.java index 3f068f98a18..8ec26c8c3da 100644 --- a/core/src/main/java/org/testcontainers/images/builder/Transferable.java +++ b/core/src/main/java/org/testcontainers/images/builder/Transferable.java @@ -5,12 +5,18 @@ import org.apache.commons.io.IOUtils; import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.zip.Checksum; public interface Transferable { int DEFAULT_FILE_MODE = 0100644; int DEFAULT_DIR_MODE = 040755; + static Transferable of(String string) { + return of(string.getBytes(StandardCharsets.UTF_8)); + } + static Transferable of(byte[] bytes) { return of(bytes, DEFAULT_FILE_MODE); } @@ -27,6 +33,11 @@ public byte[] getBytes() { return bytes; } + @Override + public void updateChecksum(Checksum checksum) { + checksum.update(bytes, 0, bytes.length); + } + @Override public int getFileMode() { return fileMode; @@ -78,4 +89,8 @@ default byte[] getBytes() { default String getDescription() { return ""; } + + default void updateChecksum(Checksum checksum) { + throw new UnsupportedOperationException("Provide implementation in subclass"); + } } diff --git a/core/src/main/java/org/testcontainers/utility/MountableFile.java b/core/src/main/java/org/testcontainers/utility/MountableFile.java index 19dca4c134c..81cc37adeab 100644 --- a/core/src/main/java/org/testcontainers/utility/MountableFile.java +++ b/core/src/main/java/org/testcontainers/utility/MountableFile.java @@ -1,12 +1,15 @@ package org.testcontainers.utility; +import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Charsets; import lombok.Getter; import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.apache.commons.compress.archivers.tar.TarArchiveEntry; import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream; import org.apache.commons.compress.archivers.tar.TarConstants; +import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.SystemUtils; import org.jetbrains.annotations.NotNull; import org.testcontainers.DockerClientFactory; @@ -28,6 +31,8 @@ import java.util.Set; import java.util.jar.JarEntry; import java.util.jar.JarFile; +import java.util.stream.Stream; +import java.util.zip.Checksum; import static lombok.AccessLevel.PACKAGE; import static org.testcontainers.utility.PathUtils.recursiveDeleteDir; @@ -361,6 +366,27 @@ public String getDescription() { return this.getResolvedPath(); } + @Override + public void updateChecksum(Checksum checksum) { + File file = new File(getResolvedPath()); + checksumFile(file, checksum); + } + + @SneakyThrows(IOException.class) + private void checksumFile(File file, Checksum checksum) { + Path path = file.toPath(); + checksum.update(MountableFile.getUnixFileMode(path)); + if (file.isDirectory()) { + try (Stream stream = Files.walk(path)) { + stream.filter(it -> it != path).forEach(it -> { + checksumFile(it.toFile(), checksum); + }); + } + } else { + FileUtils.checksum(file, checksum); + } + } + @Override public int getFileMode() { return getUnixFileMode(this.getResolvedPath()); diff --git a/core/src/test/java/org/testcontainers/containers/GenericContainerTest.java b/core/src/test/java/org/testcontainers/containers/GenericContainerTest.java index 93a9cd96479..0c30d522cd9 100644 --- a/core/src/test/java/org/testcontainers/containers/GenericContainerTest.java +++ b/core/src/test/java/org/testcontainers/containers/GenericContainerTest.java @@ -20,7 +20,11 @@ import org.testcontainers.containers.startupcheck.OneShotStartupCheckStrategy; import org.testcontainers.containers.startupcheck.StartupCheckStrategy; import org.testcontainers.containers.wait.strategy.AbstractWaitStrategy; +import org.testcontainers.images.builder.Transferable; + +import java.nio.charset.StandardCharsets; import org.testcontainers.images.builder.ImageFromDockerfile; +import org.testcontainers.utility.MountableFile; import java.util.Arrays; import java.util.List; @@ -75,6 +79,38 @@ public void shouldReportErrorAfterWait() { } } + @Test + public void shouldCopyTransferableAsFile() { + try ( + GenericContainer container = new GenericContainer<>(TestImages.TINY_IMAGE) + .withStartupCheckStrategy(new NoopStartupCheckStrategy()) + .withCopyToContainer(Transferable.of("test"), "/tmp/test") + .waitingFor(new WaitForExitedState(state -> state.getExitCodeLong() > 0)) + .withCommand("sh", "-c", "grep -q test /tmp/test && exit 100") + ) { + assertThatThrownBy(container::start) + .hasStackTraceContaining("Container exited with code 100"); + } + } + + @Test + public void shouldCopyTransferableAfterMountableFile() { + try ( + GenericContainer container = new GenericContainer<>(TestImages.TINY_IMAGE) + .withStartupCheckStrategy(new NoopStartupCheckStrategy()) + .withCopyFileToContainer( + MountableFile.forClasspathResource("test_copy_to_container.txt"), + "/tmp/test" + ) + .withCopyToContainer(Transferable.of("test"), "/tmp/test") + .waitingFor(new WaitForExitedState(state -> state.getExitCodeLong() > 0)) + .withCommand("sh", "-c", "grep -q test /tmp/test && exit 100") + ) { + assertThatThrownBy(container::start) + .hasStackTraceContaining("Container exited with code 100"); + } + } + @Test public void shouldOnlyPublishExposedPorts() { ImageFromDockerfile image = new ImageFromDockerfile("publish-multiple") diff --git a/core/src/test/java/org/testcontainers/containers/ReusabilityUnitTests.java b/core/src/test/java/org/testcontainers/containers/ReusabilityUnitTests.java index 0540fcdba9d..9aad97d7e7b 100644 --- a/core/src/test/java/org/testcontainers/containers/ReusabilityUnitTests.java +++ b/core/src/test/java/org/testcontainers/containers/ReusabilityUnitTests.java @@ -10,7 +10,6 @@ import com.github.dockerjava.api.command.ListContainersCmd; import com.github.dockerjava.api.command.StartContainerCmd; import com.github.dockerjava.api.model.Container; -import com.github.dockerjava.api.model.NetworkSettings; import com.github.dockerjava.core.command.CreateContainerCmdImpl; import com.github.dockerjava.core.command.InspectContainerCmdImpl; import com.github.dockerjava.core.command.ListContainersCmdImpl; @@ -24,13 +23,13 @@ import org.junit.runners.BlockJUnit4ClassRunner; import org.junit.runners.Parameterized; import org.mockito.Answers; -import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.stubbing.Answer; import org.rnorth.visibleassertions.VisibleAssertions; import org.testcontainers.DockerClientFactory; import org.testcontainers.containers.startupcheck.StartupCheckStrategy; import org.testcontainers.containers.wait.strategy.AbstractWaitStrategy; +import org.testcontainers.images.builder.Transferable; import org.testcontainers.utility.MockTestcontainersConfigurationRule; import org.testcontainers.utility.MountableFile; import org.testcontainers.utility.TestcontainersConfiguration; @@ -39,12 +38,14 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.UUID; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; +import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -297,11 +298,65 @@ public void shouldHashCopiedFiles() { } } - @RunWith(BlockJUnit4ClassRunner.class) + @RunWith(Parameterized.class) @FieldDefaults(makeFinal = true) public static class CopyFilesHashTest { + private final TestStrategy strategy; + + interface TestStrategy { + void withCopyFileToContainer(MountableFile mountableFile, String path); + + void clear(); + } + + private static class MountableFileTestStrategy implements TestStrategy { + private final GenericContainer container; + + private MountableFileTestStrategy(GenericContainer container) { + this.container = container; + } + + @Override + public void withCopyFileToContainer(MountableFile mountableFile, String path) { + container.withCopyFileToContainer(mountableFile, path); + } + + @Override + public void clear() { + container.getCopyToFileContainerPathMap().clear(); + } + } + + private static class TransferableTestStrategy implements TestStrategy { + private final GenericContainer container; + + private TransferableTestStrategy(GenericContainer container) { + this.container = container; + } + + @Override + public void withCopyFileToContainer(MountableFile mountableFile, String path) { + container.withCopyToContainer(mountableFile, path); + } + + @Override + public void clear() { + container.getCopyToTransferableContainerPathMap().clear(); + } + } + + @Parameterized.Parameters + public static List, TestStrategy>> strategies() { + return Arrays.asList(MountableFileTestStrategy::new, TransferableTestStrategy::new); + } + + GenericContainer container = new GenericContainer<>(TINY_IMAGE); + public CopyFilesHashTest(Function, TestStrategy> strategyFactory) { + this.strategy = strategyFactory.apply(container); + } + @Test public void empty() { assertThat(container.hashCopiedFiles()).isNotNull(); @@ -311,7 +366,7 @@ public void empty() { public void oneFile() { long emptyHash = container.hashCopiedFiles().getValue(); - container.withCopyFileToContainer( + strategy.withCopyFileToContainer( MountableFile.forClasspathResource("test_copy_to_container.txt"), "/foo/bar" ); @@ -322,13 +377,13 @@ public void oneFile() { @Test public void differentPath() { MountableFile mountableFile = MountableFile.forClasspathResource("test_copy_to_container.txt"); - container.withCopyFileToContainer(mountableFile, "/foo/bar"); + strategy.withCopyFileToContainer(mountableFile, "/foo/bar"); long hash1 = container.hashCopiedFiles().getValue(); - container.getCopyToFileContainerPathMap().clear(); + strategy.clear(); - container.withCopyFileToContainer(mountableFile, "/foo/baz"); + strategy.withCopyFileToContainer(mountableFile, "/foo/baz"); assertThat(container.hashCopiedFiles().getValue()).isNotEqualTo(hash1); } @@ -337,7 +392,7 @@ public void differentPath() { public void detectsChangesInFile() throws Exception { Path path = File.createTempFile("reusable_test", ".txt").toPath(); MountableFile mountableFile = MountableFile.forHostPath(path); - container.withCopyFileToContainer(mountableFile, "/foo/bar"); + strategy.withCopyFileToContainer(mountableFile, "/foo/bar"); long hash1 = container.hashCopiedFiles().getValue(); @@ -348,13 +403,13 @@ public void detectsChangesInFile() throws Exception { @Test public void multipleFiles() { - container.withCopyFileToContainer( + strategy.withCopyFileToContainer( MountableFile.forClasspathResource("test_copy_to_container.txt"), "/foo/bar" ); long hash1 = container.hashCopiedFiles().getValue(); - container.withCopyFileToContainer( + strategy.withCopyFileToContainer( MountableFile.forClasspathResource("mappable-resource/test-resource.txt"), "/foo/baz" ); @@ -368,7 +423,7 @@ public void folder() throws Exception { Path tempDirectory = Files.createTempDirectory("reusable_test"); MountableFile mountableFile = MountableFile.forHostPath(tempDirectory); - container.withCopyFileToContainer(mountableFile, "/foo/bar/"); + strategy.withCopyFileToContainer(mountableFile, "/foo/bar/"); assertThat(container.hashCopiedFiles().getValue()).isNotEqualTo(emptyHash); } @@ -378,7 +433,7 @@ public void changesInFolder() throws Exception { Path tempDirectory = Files.createTempDirectory("reusable_test"); MountableFile mountableFile = MountableFile.forHostPath(tempDirectory); assertThat(new File(mountableFile.getResolvedPath())).isDirectory(); - container.withCopyFileToContainer(mountableFile, "/foo/bar/"); + strategy.withCopyFileToContainer(mountableFile, "/foo/bar/"); long hash1 = container.hashCopiedFiles().getValue(); @@ -397,11 +452,11 @@ public void folderAndFile() throws Exception { Path tempDirectory = Files.createTempDirectory("reusable_test"); MountableFile mountableFile = MountableFile.forHostPath(tempDirectory); assertThat(new File(mountableFile.getResolvedPath())).isDirectory(); - container.withCopyFileToContainer(mountableFile, "/foo/bar/"); + strategy.withCopyFileToContainer(mountableFile, "/foo/bar/"); long hash1 = container.hashCopiedFiles().getValue(); - container.withCopyFileToContainer( + strategy.withCopyFileToContainer( MountableFile.forClasspathResource("test_copy_to_container.txt"), "/foo/baz" ); @@ -414,7 +469,7 @@ public void filePermissions() throws Exception { Path path = File.createTempFile("reusable_test", ".txt").toPath(); path.toFile().setExecutable(false); MountableFile mountableFile = MountableFile.forHostPath(path); - container.withCopyFileToContainer(mountableFile, "/foo/bar"); + strategy.withCopyFileToContainer(mountableFile, "/foo/bar"); long hash1 = container.hashCopiedFiles().getValue(); @@ -432,7 +487,7 @@ public void folderPermissions() throws Exception { Path subDir = Files.createDirectory(tempDirectory.resolve("sub")); subDir.toFile().setWritable(false); assumeThat(subDir.toFile().canWrite()).isFalse(); - container.withCopyFileToContainer(mountableFile, "/foo/bar/"); + strategy.withCopyFileToContainer(mountableFile, "/foo/bar/"); long hash1 = container.hashCopiedFiles().getValue(); From 17407eff6ef3343ee0c2d52e9306d40708909c43 Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Fri, 8 Apr 2022 16:45:17 +0200 Subject: [PATCH 0087/3231] Increase default client ping timeout from 5 to 10 seconds (#5254) --- .../org/testcontainers/utility/TestcontainersConfiguration.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java index c6ce7496dd9..c9dfd543ad6 100644 --- a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java +++ b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java @@ -198,7 +198,7 @@ public String getImageSubstitutorClassName() { } public Integer getClientPingTimeout() { - return Integer.parseInt(getEnvVarOrProperty("client.ping.timeout", "5")); + return Integer.parseInt(getEnvVarOrProperty("client.ping.timeout", "10")); } @Nullable From af835694bb617fb1ef8d5eb1aa438ced68bcab7b Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Tue, 12 Apr 2022 19:27:32 +0200 Subject: [PATCH 0088/3231] Update images and theme for testcontainers.org (#5258) --- docs/css/extra.css | 14 +++---- docs/favicon.ico | Bin 15086 -> 15406 bytes docs/logo.png | Bin 51964 -> 84934 bytes docs/logo.svg | 99 +++++++++++++++++++++++++++++++++++++++++---- mkdocs.yml | 7 ++-- 5 files changed, 102 insertions(+), 18 deletions(-) diff --git a/docs/css/extra.css b/docs/css/extra.css index d7e7e6b68a3..4c11e3df748 100644 --- a/docs/css/extra.css +++ b/docs/css/extra.css @@ -1,10 +1,10 @@ -@import url('https://fonts.googleapis.com/css?family=Inconsolata|Lato|Merriweather+Sans|Open+Sans|Source+Sans+Pro'); -body { - font-family: 'Lato', sans-serif; +@import url('https://fonts.googleapis.com/css?family=Rubik'); +h1, h2, h3, h4, h5, h6 { + font-family: 'Rubik', sans-serif; } -.md-typeset pre { - font-family: 'Inconsolata', monospace; - font-size: 100%; -} \ No newline at end of file +[data-md-color-scheme="testcontainers"] { + --md-primary-fg-color: #291A3F; + --md-accent-fg-color: #291A3F; +} diff --git a/docs/favicon.ico b/docs/favicon.ico index 004282385c3e9b51ece573d194b64693145dc2e1..311a0acaa373f197360673f8ac6bc1b3c38c7e7f 100644 GIT binary patch literal 15406 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 EAH!e_G5`Po diff --git a/docs/logo.png b/docs/logo.png index 696e03ad6ff360ce03647928910996169e329b4f..3d8f03fa0cf0fb98578e83d9b2ff71de3ead11c0 100644 GIT binary patch literal 84934 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 Gg8vr^t7`)Q diff --git a/docs/logo.svg b/docs/logo.svg index 3a8db707fc8..bac0c391a59 100644 --- a/docs/logo.svg +++ b/docs/logo.svg @@ -1,9 +1,92 @@ - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mkdocs.yml b/mkdocs.yml index 7ace7382e41..16b2106bb3f 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -7,9 +7,10 @@ theme: name: 'material' custom_dir: 'docs/theme' palette: - primary: 'blue' - accent: 'teal' - font: false + scheme: testcontainers + font: + text: Roboto + code: Roboto Mono logo: 'logo.svg' favicon: 'favicon.ico' extra_css: From cec49348c70615a3a0b5243d10b8f5c30d3b5697 Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Wed, 13 Apr 2022 13:07:28 +0200 Subject: [PATCH 0089/3231] Update release docs to contain milestone rename step --- RELEASING.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/RELEASING.md b/RELEASING.md index 4ad91b56477..f04839d9d41 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -16,7 +16,8 @@ Testcontainers' release process is semi-automated through GitHub Actions. This d 7. Close the release in Sonatype. This will evaluate the release based on given Sonatype rules. 8. After successful closing, the release button needs to be clicked and afterwards it is automatically synced to Maven Central. 9. Handcraft and polish some of the release notes (e.g. substitute combinded dependency PRs and highlight certain features). -10. When available through Maven Central, poke [Richard North](https://github.com/rnorth) to announce the release on Twitter! +10. Rename existing milestone corresponding to new release and close it. Then create a new `next` milstestone. +11. When available through Maven Central, poke [Richard North](https://github.com/rnorth) to announce the release on Twitter! ## Internal details From 66e4be2817f82aa80490c21e8df9e8f8a87fa792 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Wed, 13 Apr 2022 07:27:38 -0400 Subject: [PATCH 0090/3231] Fix `StackOverflowError` in `DockerClientFactory#client` (#5262) --- core/src/main/java/org/testcontainers/DockerClientFactory.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/testcontainers/DockerClientFactory.java b/core/src/main/java/org/testcontainers/DockerClientFactory.java index 4cbe81f9e2d..6cf8f716704 100644 --- a/core/src/main/java/org/testcontainers/DockerClientFactory.java +++ b/core/src/main/java/org/testcontainers/DockerClientFactory.java @@ -187,7 +187,6 @@ public DockerClient client() { final DockerClientProviderStrategy strategy = getOrInitializeStrategy(); - log.info("Docker host IP address is {}", strategy.getDockerHostIpAddress()); client = new DockerClientDelegate() { @Getter @@ -198,6 +197,7 @@ public void close() { throw new IllegalStateException("You should never close the global DockerClient!"); } }; + log.info("Docker host IP address is {}", strategy.getDockerHostIpAddress()); Info dockerInfo = client.infoCmd().exec(); Version version = client.versionCmd().exec(); From 1113cd4cd4a7356c6e745a59c4a6b6711ac55458 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Tue, 19 Apr 2022 18:02:53 -0500 Subject: [PATCH 0091/3231] Rely on `TransportConfig` to read `DOCKER_HOST` env variable for `DockerComposeContainer` (#5276) `TransportConfig` is already aware of `DOCKER_HOST`. --- .../containers/DockerComposeContainer.java | 21 ++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java index 4db9361a618..c0f84307c88 100644 --- a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java +++ b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java @@ -726,20 +726,17 @@ public void invoke() { final Map environment = Maps.newHashMap(env); environment.put(ENV_PROJECT_NAME, identifier); - String dockerHost = System.getenv("DOCKER_HOST"); - if (dockerHost == null) { - TransportConfig transportConfig = DockerClientFactory.instance().getTransportConfig(); - SSLConfig sslConfig = transportConfig.getSslConfig(); - if (sslConfig != null) { - if (sslConfig instanceof LocalDirectorySSLConfig) { - environment.put("DOCKER_CERT_PATH", ((LocalDirectorySSLConfig) sslConfig).getDockerCertPath()); - environment.put("DOCKER_TLS_VERIFY", "true"); - } else { - logger().warn("Couldn't set DOCKER_CERT_PATH. `sslConfig` is present but it's not LocalDirectorySSLConfig."); - } + TransportConfig transportConfig = DockerClientFactory.instance().getTransportConfig(); + SSLConfig sslConfig = transportConfig.getSslConfig(); + if (sslConfig != null) { + if (sslConfig instanceof LocalDirectorySSLConfig) { + environment.put("DOCKER_CERT_PATH", ((LocalDirectorySSLConfig) sslConfig).getDockerCertPath()); + environment.put("DOCKER_TLS_VERIFY", "true"); + } else { + logger().warn("Couldn't set DOCKER_CERT_PATH. `sslConfig` is present but it's not LocalDirectorySSLConfig."); } - dockerHost = transportConfig.getDockerHost().toString(); } + String dockerHost = transportConfig.getDockerHost().toString(); environment.put("DOCKER_HOST", dockerHost); final Stream absoluteDockerComposeFilePaths = composeFiles.stream() From bb6b5c5489825398461bf9e0e8d180fde9c15eec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Wed, 20 Apr 2022 08:13:04 -0500 Subject: [PATCH 0092/3231] Use `getHost` instead of `getContainerIpAddress()` (#5277) `getContainerIpAddress` is deprecated. --- .../src/test/java/DisqueBackedMailSenderTest.java | 2 +- .../src/test/java/SingleDisqueInstanceTest.java | 2 +- .../java/com/example/linkedcontainer/RedmineContainer.java | 2 +- .../org/testcontainers/containers/MongoDbContainerTest.java | 2 +- .../src/test/java/RedisBackedCacheTest.java | 2 +- .../src/test/java/RedisBackedCacheTest.java | 2 +- .../src/test/java/com/example/BarConcreteTestClass.java | 2 +- .../src/test/java/com/example/FooConcreteTestClass.java | 2 +- .../src/test/java/com/example/SolrQueryTest.java | 2 +- .../testcontainers/containers/DatastoreEmulatorContainer.java | 2 +- .../testcontainers/containers/FirestoreEmulatorContainer.java | 2 +- .../testcontainers/containers/PubSubEmulatorContainer.java | 2 +- .../testcontainers/containers/SpannerEmulatorContainer.java | 4 ++-- .../java/org/testcontainers/containers/MongoDBContainer.java | 2 +- .../org/testcontainers/containers/PostgreSQLContainer.java | 2 +- .../java/org/testcontainers/containers/SolrContainer.java | 4 ++-- .../java/org/testcontainers/containers/SolrContainerTest.java | 4 ++-- 17 files changed, 20 insertions(+), 20 deletions(-) diff --git a/examples/disque-job-queue/src/test/java/DisqueBackedMailSenderTest.java b/examples/disque-job-queue/src/test/java/DisqueBackedMailSenderTest.java index add59fd15ba..4da6201eecb 100644 --- a/examples/disque-job-queue/src/test/java/DisqueBackedMailSenderTest.java +++ b/examples/disque-job-queue/src/test/java/DisqueBackedMailSenderTest.java @@ -36,7 +36,7 @@ public class DisqueBackedMailSenderTest { @Before public void setup() { context(""); - disqueClient = new DisqueClient(DisqueURI.create(container.getContainerIpAddress(), container.getMappedPort(7711))); + disqueClient = new DisqueClient(DisqueURI.create(container.getHost(), container.getMappedPort(7711))); mockMailApiClient = mock(MailApiClient.class); service = new DisqueBackedMailSenderService(disqueClient, mockMailApiClient); diff --git a/examples/disque-job-queue/src/test/java/SingleDisqueInstanceTest.java b/examples/disque-job-queue/src/test/java/SingleDisqueInstanceTest.java index 3fa115bea20..7ded5d8da43 100644 --- a/examples/disque-job-queue/src/test/java/SingleDisqueInstanceTest.java +++ b/examples/disque-job-queue/src/test/java/SingleDisqueInstanceTest.java @@ -30,7 +30,7 @@ public class SingleDisqueInstanceTest { @Before public void setup() { context(""); - DisqueClient client = new DisqueClient(DisqueURI.create(container.getContainerIpAddress(), container.getMappedPort(7711))); + DisqueClient client = new DisqueClient(DisqueURI.create(container.getHost(), container.getMappedPort(7711))); connection = client.connect().sync(); retryAfter1Second = AddJobArgs.builder().retry(1, TimeUnit.SECONDS).build(); diff --git a/examples/linked-container/src/test/java/com/example/linkedcontainer/RedmineContainer.java b/examples/linked-container/src/test/java/com/example/linkedcontainer/RedmineContainer.java index a75867205ab..4303bbee682 100644 --- a/examples/linked-container/src/test/java/com/example/linkedcontainer/RedmineContainer.java +++ b/examples/linked-container/src/test/java/com/example/linkedcontainer/RedmineContainer.java @@ -29,7 +29,7 @@ public RedmineContainer withLinkToContainer(LinkableContainer otherContainer, St public String getRedmineUrl() { return String.format("http://%s:%d", - this.getContainerIpAddress(), + this.getHost(), this.getMappedPort(REDMINE_PORT)); } } diff --git a/examples/mongodb-container/src/test/java/org/testcontainers/containers/MongoDbContainerTest.java b/examples/mongodb-container/src/test/java/org/testcontainers/containers/MongoDbContainerTest.java index 7c87e46bccd..5581c76e675 100644 --- a/examples/mongodb-container/src/test/java/org/testcontainers/containers/MongoDbContainerTest.java +++ b/examples/mongodb-container/src/test/java/org/testcontainers/containers/MongoDbContainerTest.java @@ -19,7 +19,7 @@ public void containerStartsAndPublicPortIsAvailable() { private void assertThatPortIsAvailable(MongoDbContainer container) { try { - new Socket(container.getContainerIpAddress(), container.getPort()); + new Socket(container.getHost(), container.getPort()); } catch (IOException e) { throw new AssertionError("The expected port " + container.getPort() + " is not available!"); } diff --git a/examples/redis-backed-cache-testng/src/test/java/RedisBackedCacheTest.java b/examples/redis-backed-cache-testng/src/test/java/RedisBackedCacheTest.java index 5d204cf99c4..927f097aa5f 100644 --- a/examples/redis-backed-cache-testng/src/test/java/RedisBackedCacheTest.java +++ b/examples/redis-backed-cache-testng/src/test/java/RedisBackedCacheTest.java @@ -35,7 +35,7 @@ public static void stopContainer() { @BeforeMethod public void setUp() { - Jedis jedis = new Jedis(redis.getContainerIpAddress(), redis.getMappedPort(6379)); + Jedis jedis = new Jedis(redis.getHost(), redis.getMappedPort(6379)); cache = new RedisBackedCache(jedis, "test"); } diff --git a/examples/redis-backed-cache/src/test/java/RedisBackedCacheTest.java b/examples/redis-backed-cache/src/test/java/RedisBackedCacheTest.java index d9d93b8255e..aa4199684e8 100644 --- a/examples/redis-backed-cache/src/test/java/RedisBackedCacheTest.java +++ b/examples/redis-backed-cache/src/test/java/RedisBackedCacheTest.java @@ -25,7 +25,7 @@ public class RedisBackedCacheTest { @Before public void setUp() throws Exception { - Jedis jedis = new Jedis(redis.getContainerIpAddress(), redis.getMappedPort(6379)); + Jedis jedis = new Jedis(redis.getHost(), redis.getMappedPort(6379)); cache = new RedisBackedCache(jedis, "test"); } diff --git a/examples/singleton-container/src/test/java/com/example/BarConcreteTestClass.java b/examples/singleton-container/src/test/java/com/example/BarConcreteTestClass.java index 9923b08f1e0..0f4e6d672d8 100644 --- a/examples/singleton-container/src/test/java/com/example/BarConcreteTestClass.java +++ b/examples/singleton-container/src/test/java/com/example/BarConcreteTestClass.java @@ -17,7 +17,7 @@ public class BarConcreteTestClass extends AbstractIntegrationTest { @Before public void setUp() { - Jedis jedis = new Jedis(redis.getContainerIpAddress(), redis.getMappedPort(6379)); + Jedis jedis = new Jedis(redis.getHost(), redis.getMappedPort(6379)); cache = new RedisBackedCache(jedis, "bar"); } diff --git a/examples/singleton-container/src/test/java/com/example/FooConcreteTestClass.java b/examples/singleton-container/src/test/java/com/example/FooConcreteTestClass.java index a57b0d5d4a0..3a9e4e84f04 100644 --- a/examples/singleton-container/src/test/java/com/example/FooConcreteTestClass.java +++ b/examples/singleton-container/src/test/java/com/example/FooConcreteTestClass.java @@ -17,7 +17,7 @@ public class FooConcreteTestClass extends AbstractIntegrationTest { @Before public void setUp() throws Exception { - Jedis jedis = new Jedis(redis.getContainerIpAddress(), redis.getMappedPort(6379)); + Jedis jedis = new Jedis(redis.getHost(), redis.getMappedPort(6379)); cache = new RedisBackedCache(jedis, "foo"); } diff --git a/examples/solr-container/src/test/java/com/example/SolrQueryTest.java b/examples/solr-container/src/test/java/com/example/SolrQueryTest.java index 63ac67ac1cd..49f8cf99f33 100644 --- a/examples/solr-container/src/test/java/com/example/SolrQueryTest.java +++ b/examples/solr-container/src/test/java/com/example/SolrQueryTest.java @@ -30,7 +30,7 @@ public class SolrQueryTest { @BeforeClass public static void setUp() throws IOException, SolrServerException { solrContainer.start(); - solrClient = new Http2SolrClient.Builder("http://" + solrContainer.getContainerIpAddress() + ":" + solrContainer.getSolrPort() + "/solr").build(); + solrClient = new Http2SolrClient.Builder("http://" + solrContainer.getHost() + ":" + solrContainer.getSolrPort() + "/solr").build(); // Add Sample Data solrClient.add(COLLECTION_NAME, Collections.singletonList( diff --git a/modules/gcloud/src/main/java/org/testcontainers/containers/DatastoreEmulatorContainer.java b/modules/gcloud/src/main/java/org/testcontainers/containers/DatastoreEmulatorContainer.java index fe0a9959f2d..58e5c023a0e 100644 --- a/modules/gcloud/src/main/java/org/testcontainers/containers/DatastoreEmulatorContainer.java +++ b/modules/gcloud/src/main/java/org/testcontainers/containers/DatastoreEmulatorContainer.java @@ -33,6 +33,6 @@ public DatastoreEmulatorContainer(final DockerImageName dockerImageName) { * com.google.cloud.ServiceOptions.Builder#setHost(java.lang.String) method. */ public String getEmulatorEndpoint() { - return getContainerIpAddress() + ":" + getMappedPort(8081); + return getHost() + ":" + getMappedPort(8081); } } diff --git a/modules/gcloud/src/main/java/org/testcontainers/containers/FirestoreEmulatorContainer.java b/modules/gcloud/src/main/java/org/testcontainers/containers/FirestoreEmulatorContainer.java index 6353a679130..c36082dfeb6 100644 --- a/modules/gcloud/src/main/java/org/testcontainers/containers/FirestoreEmulatorContainer.java +++ b/modules/gcloud/src/main/java/org/testcontainers/containers/FirestoreEmulatorContainer.java @@ -34,6 +34,6 @@ public FirestoreEmulatorContainer(final DockerImageName dockerImageName) { * com.google.cloud.ServiceOptions.Builder#setHost(java.lang.String) method. */ public String getEmulatorEndpoint() { - return getContainerIpAddress() + ":" + getMappedPort(8080); + return getHost() + ":" + getMappedPort(8080); } } diff --git a/modules/gcloud/src/main/java/org/testcontainers/containers/PubSubEmulatorContainer.java b/modules/gcloud/src/main/java/org/testcontainers/containers/PubSubEmulatorContainer.java index 90d77c837cf..231f7626044 100644 --- a/modules/gcloud/src/main/java/org/testcontainers/containers/PubSubEmulatorContainer.java +++ b/modules/gcloud/src/main/java/org/testcontainers/containers/PubSubEmulatorContainer.java @@ -34,6 +34,6 @@ public PubSubEmulatorContainer(final DockerImageName dockerImageName) { * io.grpc.ManagedChannelBuilder#forTarget(java.lang.String) method. */ public String getEmulatorEndpoint() { - return getContainerIpAddress() + ":" + getMappedPort(PORT); + return getHost() + ":" + getMappedPort(PORT); } } diff --git a/modules/gcloud/src/main/java/org/testcontainers/containers/SpannerEmulatorContainer.java b/modules/gcloud/src/main/java/org/testcontainers/containers/SpannerEmulatorContainer.java index 8603b1f3437..0ae8e89dc06 100644 --- a/modules/gcloud/src/main/java/org/testcontainers/containers/SpannerEmulatorContainer.java +++ b/modules/gcloud/src/main/java/org/testcontainers/containers/SpannerEmulatorContainer.java @@ -31,7 +31,7 @@ public SpannerEmulatorContainer(final DockerImageName dockerImageName) { * com.google.cloud.spanner.SpannerOptions.Builder#setEmulatorHost(java.lang.String) method. */ public String getEmulatorGrpcEndpoint() { - return getContainerIpAddress() + ":" + getMappedPort(GRPC_PORT); + return getHost() + ":" + getMappedPort(GRPC_PORT); } /** @@ -39,6 +39,6 @@ public String getEmulatorGrpcEndpoint() { * HTTP REST endpoint is reachable from the test host machine. */ public String getEmulatorHttpEndpoint() { - return getContainerIpAddress() + ":" + getMappedPort(HTTP_PORT); + return getHost() + ":" + getMappedPort(HTTP_PORT); } } 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 662c4b575f7..53336d4fbec 100644 --- a/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDBContainer.java +++ b/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDBContainer.java @@ -69,7 +69,7 @@ public String getReplicaSetUrl(final String databaseName) { } return String.format( "mongodb://%s:%d/%s", - getContainerIpAddress(), + getHost(), getMappedPort(MONGODB_INTERNAL_PORT), databaseName ); diff --git a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java index 3bfb508aa47..741a8c4139a 100644 --- a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java +++ b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java @@ -81,7 +81,7 @@ public String getDriverClassName() { @Override public String getJdbcUrl() { String additionalUrlParams = constructUrlParameters("?", "&"); - return "jdbc:postgresql://" + getContainerIpAddress() + ":" + getMappedPort(POSTGRESQL_PORT) + return "jdbc:postgresql://" + getHost() + ":" + getMappedPort(POSTGRESQL_PORT) + "/" + databaseName + additionalUrlParams; } diff --git a/modules/solr/src/main/java/org/testcontainers/containers/SolrContainer.java b/modules/solr/src/main/java/org/testcontainers/containers/SolrContainer.java index 4d0b5cd1ae1..e583c1f54ee 100644 --- a/modules/solr/src/main/java/org/testcontainers/containers/SolrContainer.java +++ b/modules/solr/src/main/java/org/testcontainers/containers/SolrContainer.java @@ -136,7 +136,7 @@ protected void containerIsStarted(InspectContainerResponse containerInfo) { if (StringUtils.isNotEmpty(configuration.getConfigurationName())) { SolrClientUtils.uploadConfiguration( - getContainerIpAddress(), + getHost(), getSolrPort(), configuration.getConfigurationName(), configuration.getSolrConfiguration(), @@ -144,7 +144,7 @@ protected void containerIsStarted(InspectContainerResponse containerInfo) { } SolrClientUtils.createCollection( - getContainerIpAddress(), + getHost(), getSolrPort(), configuration.getCollectionName(), configuration.getConfigurationName()); diff --git a/modules/solr/src/test/java/org/testcontainers/containers/SolrContainerTest.java b/modules/solr/src/test/java/org/testcontainers/containers/SolrContainerTest.java index c2d19a3e61c..9f82e2c3af9 100644 --- a/modules/solr/src/test/java/org/testcontainers/containers/SolrContainerTest.java +++ b/modules/solr/src/test/java/org/testcontainers/containers/SolrContainerTest.java @@ -60,7 +60,7 @@ public void solrCloudPingTest() throws IOException, SolrServerException { container.start(); // Do whatever you want with the client ... - SolrClient client = new Http2SolrClient.Builder("http://" + container.getContainerIpAddress() + ":" + container.getSolrPort() + "/solr").build(); + SolrClient client = new Http2SolrClient.Builder("http://" + container.getHost() + ":" + container.getSolrPort() + "/solr").build(); SolrPingResponse response = client.ping("dummy"); // Stop the container. @@ -70,7 +70,7 @@ public void solrCloudPingTest() throws IOException, SolrServerException { private SolrClient getClient(SolrContainer container) { if (client == null) { - client = new Http2SolrClient.Builder("http://" + container.getContainerIpAddress() + ":" + container.getSolrPort() + "/solr").build(); + client = new Http2SolrClient.Builder("http://" + container.getHost() + ":" + container.getSolrPort() + "/solr").build(); } return client; } From 600d235d220d5e507405921b4e241aa73c067a2a Mon Sep 17 00:00:00 2001 From: David Boreham Date: Thu, 21 Apr 2022 06:45:04 -0600 Subject: [PATCH 0093/3231] Use latest version of alpine/socat container image (#5280) --- .../main/java/org/testcontainers/containers/SocatContainer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/testcontainers/containers/SocatContainer.java b/core/src/main/java/org/testcontainers/containers/SocatContainer.java index 7592d9f4d1f..bc9ced57845 100644 --- a/core/src/main/java/org/testcontainers/containers/SocatContainer.java +++ b/core/src/main/java/org/testcontainers/containers/SocatContainer.java @@ -15,7 +15,7 @@ public class SocatContainer extends GenericContainer { private final Map targets = new HashMap<>(); public SocatContainer() { - this(DockerImageName.parse("alpine/socat:1.7.3.4-r0")); + this(DockerImageName.parse("alpine/socat:1.7.4.3-r0")); } public SocatContainer(final DockerImageName dockerImageName) { From 96601379e0d1e01ab9743566545e8413e7155e81 Mon Sep 17 00:00:00 2001 From: Roman Pylypaka Date: Mon, 25 Apr 2022 08:24:22 +0100 Subject: [PATCH 0094/3231] Fix connection leak in JDBC waitUntilContainerStarted (#5281) Only the Statement was auto-closed, but not the Connection from which the Statement was created. Aside from resources leaking, this can cause problems when database rename transactions are executed by clients. --- .../org/testcontainers/containers/JdbcDatabaseContainer.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java index c4bacf3c8ff..5e5342f1ee1 100644 --- a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java +++ b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java @@ -1,7 +1,6 @@ package org.testcontainers.containers; import com.github.dockerjava.api.command.InspectContainerResponse; -import java.sql.Statement; import lombok.NonNull; import lombok.SneakyThrows; import org.apache.commons.lang3.StringUtils; @@ -16,6 +15,7 @@ import java.sql.Connection; import java.sql.Driver; import java.sql.SQLException; +import java.sql.Statement; import java.util.HashMap; import java.util.Map; import java.util.Properties; @@ -144,7 +144,8 @@ protected void waitUntilContainerStarted() { if (!isRunning()) { Thread.sleep(100L); } else { - try (Statement statement = createConnection("").createStatement()) { + try (Connection connection = createConnection(""); + Statement statement = connection.createStatement()) { boolean testQuerySucceeded = statement.execute(this.getTestQueryString()); if (testQuerySucceeded) { logger().info("Container is started (JDBC URL: {})", this.getJdbcUrl()); From 42fa611a18d3433b820c60a1274ccc7217026016 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 May 2022 22:00:44 +0000 Subject: [PATCH 0095/3231] Bump mssql-jdbc in /modules/mssqlserver Bumps [mssql-jdbc](https://github.com/Microsoft/mssql-jdbc) from 10.2.0.jre8 to 11.1.1.jre8-preview. - [Release notes](https://github.com/Microsoft/mssql-jdbc/releases) - [Changelog](https://github.com/microsoft/mssql-jdbc/blob/main/CHANGELOG.md) - [Commits](https://github.com/Microsoft/mssql-jdbc/commits) --- updated-dependencies: - dependency-name: com.microsoft.sqlserver:mssql-jdbc dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- modules/mssqlserver/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mssqlserver/build.gradle b/modules/mssqlserver/build.gradle index 1f754303ae1..dfa15ed1c2a 100644 --- a/modules/mssqlserver/build.gradle +++ b/modules/mssqlserver/build.gradle @@ -10,7 +10,7 @@ dependencies { compileOnly 'io.r2dbc:r2dbc-mssql:0.8.7.RELEASE' testImplementation project(':jdbc-test') - testImplementation 'com.microsoft.sqlserver:mssql-jdbc:10.2.0.jre8' + testImplementation 'com.microsoft.sqlserver:mssql-jdbc:11.1.1.jre8-preview' testImplementation project(':r2dbc') testImplementation 'io.r2dbc:r2dbc-mssql:0.8.7.RELEASE' From 52cf6a3505e13607a98e512f032a5ea436c2438a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 May 2022 22:00:44 +0000 Subject: [PATCH 0096/3231] Bump postgresql from 42.3.3 to 42.3.4 in /modules/junit-jupiter Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.3.3 to 42.3.4. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.3.3...REL42.3.4) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 89882481dc1..009f62a8a16 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -15,7 +15,7 @@ dependencies { testImplementation 'org.assertj:assertj-core:3.22.0' testImplementation 'org.junit.jupiter:junit-jupiter-params:5.8.2' - testRuntimeOnly 'org.postgresql:postgresql:42.3.3' + testRuntimeOnly 'org.postgresql:postgresql:42.3.4' testRuntimeOnly 'mysql:mysql-connector-java:8.0.27' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.2' } From a43ff9b9be66b1131638801083f7ca017c71a332 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 May 2022 22:00:44 +0000 Subject: [PATCH 0097/3231] Bump s3 from 2.17.162 to 2.17.181 in /modules/localstack Bumps s3 from 2.17.162 to 2.17.181. --- updated-dependencies: - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index c3f2374e23f..59a5173fe73 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -7,6 +7,6 @@ dependencies { testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.169' testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.150' testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.169' - testImplementation 'software.amazon.awssdk:s3:2.17.162' + testImplementation 'software.amazon.awssdk:s3:2.17.181' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } From 244159750e6ba3da0fd59c61b352ea627915bead Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 May 2022 22:00:44 +0000 Subject: [PATCH 0098/3231] Bump neo4j from 3.5.31 to 3.5.32 in /modules/neo4j Bumps [neo4j](https://github.com/neo4j/neo4j) from 3.5.31 to 3.5.32. - [Release notes](https://github.com/neo4j/neo4j/releases) - [Commits](https://github.com/neo4j/neo4j/compare/3.5.31...3.5.32) --- updated-dependencies: - dependency-name: org.neo4j:neo4j dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/neo4j/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/neo4j/build.gradle b/modules/neo4j/build.gradle index 6187d665098..0bdd9ba960c 100644 --- a/modules/neo4j/build.gradle +++ b/modules/neo4j/build.gradle @@ -29,7 +29,7 @@ task customNeo4jPluginJar(type: Jar) { test.dependsOn customNeo4jPluginJar dependencies { - customNeo4jPluginCompileOnly "org.neo4j:neo4j:3.5.31" + customNeo4jPluginCompileOnly "org.neo4j:neo4j:3.5.32" api project(":testcontainers") From bda535fa419cd6abf7891af00932b58f46b87dc2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 May 2022 22:00:44 +0000 Subject: [PATCH 0099/3231] Bump mysql-connector-java from 8.0.28 to 8.0.29 in /modules/mysql Bumps [mysql-connector-java](https://github.com/mysql/mysql-connector-j) from 8.0.28 to 8.0.29. - [Release notes](https://github.com/mysql/mysql-connector-j/releases) - [Changelog](https://github.com/mysql/mysql-connector-j/blob/release/8.0/CHANGES) - [Commits](https://github.com/mysql/mysql-connector-j/compare/8.0.28...8.0.29) --- updated-dependencies: - dependency-name: mysql:mysql-connector-java dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/mysql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mysql/build.gradle b/modules/mysql/build.gradle index 1796ff17ef0..e23920eaae9 100644 --- a/modules/mysql/build.gradle +++ b/modules/mysql/build.gradle @@ -10,7 +10,7 @@ dependencies { compileOnly 'dev.miku:r2dbc-mysql:0.8.2.RELEASE' testImplementation project(':jdbc-test') - testImplementation 'mysql:mysql-connector-java:8.0.28' + testImplementation 'mysql:mysql-connector-java:8.0.29' testImplementation testFixtures(project(':r2dbc')) testImplementation 'dev.miku:r2dbc-mysql:0.8.2.RELEASE' From 6eddddb462011a1b178726c74798d55c495d498f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 May 2022 22:00:45 +0000 Subject: [PATCH 0100/3231] Bump postgresql from 42.3.3 to 42.3.4 in /modules/postgresql Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.3.3 to 42.3.4. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.3.3...REL42.3.4) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/postgresql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/postgresql/build.gradle b/modules/postgresql/build.gradle index 754d5dde289..f2c50c30754 100644 --- a/modules/postgresql/build.gradle +++ b/modules/postgresql/build.gradle @@ -11,7 +11,7 @@ dependencies { testImplementation project(':jdbc-test') testImplementation project(':test-support') - testImplementation 'org.postgresql:postgresql:42.3.3' + testImplementation 'org.postgresql:postgresql:42.3.4' testImplementation testFixtures(project(':r2dbc')) testImplementation 'io.r2dbc:r2dbc-postgresql:0.8.11.RELEASE' From 00e366f4eb27425b3e37012f341c55efb828f365 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 May 2022 22:00:46 +0000 Subject: [PATCH 0101/3231] Bump java-client from 3.2.6 to 3.3.0 in /modules/couchbase Bumps [java-client](https://github.com/couchbase/couchbase-jvm-clients) from 3.2.6 to 3.3.0. - [Release notes](https://github.com/couchbase/couchbase-jvm-clients/releases) - [Commits](https://github.com/couchbase/couchbase-jvm-clients/compare/java-client-3.2.6...java-client-3.3.0) --- updated-dependencies: - dependency-name: com.couchbase.client:java-client dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/couchbase/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/couchbase/build.gradle b/modules/couchbase/build.gradle index a3cb0fcbc70..f1e41fcbe11 100644 --- a/modules/couchbase/build.gradle +++ b/modules/couchbase/build.gradle @@ -5,6 +5,6 @@ dependencies { // TODO use JDK's HTTP client and/or Apache HttpClient5 shaded 'com.squareup.okhttp3:okhttp:3.14.9' - testImplementation 'com.couchbase.client:java-client:3.2.6' + testImplementation 'com.couchbase.client:java-client:3.3.0' testImplementation 'org.awaitility:awaitility:4.1.1' } From 6b52e87ca78f61bd996391b4cbafa50917ee25a7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 May 2022 22:00:46 +0000 Subject: [PATCH 0102/3231] Bump mysql-connector-java from 8.0.28 to 8.0.29 in /modules/spock Bumps [mysql-connector-java](https://github.com/mysql/mysql-connector-j) from 8.0.28 to 8.0.29. - [Release notes](https://github.com/mysql/mysql-connector-j/releases) - [Changelog](https://github.com/mysql/mysql-connector-j/blob/release/8.0/CHANGES) - [Commits](https://github.com/mysql/mysql-connector-j/compare/8.0.28...8.0.29) --- updated-dependencies: - dependency-name: mysql:mysql-connector-java dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/spock/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle index e745bc23ed6..396c0071042 100644 --- a/modules/spock/build.gradle +++ b/modules/spock/build.gradle @@ -16,7 +16,7 @@ dependencies { testImplementation 'org.apache.httpcomponents:httpclient:4.5.13' testRuntimeOnly 'org.postgresql:postgresql:42.3.1' - testRuntimeOnly 'mysql:mysql-connector-java:8.0.28' + testRuntimeOnly 'mysql:mysql-connector-java:8.0.29' testCompileClasspath 'org.jetbrains:annotations:23.0.0' } From 92d1b1b29377a18c8c7652bd39589463b27d8a4c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 May 2022 22:00:47 +0000 Subject: [PATCH 0103/3231] Bump jedis from 4.2.1 to 4.2.2 in /examples Bumps [jedis](https://github.com/redis/jedis) from 4.2.1 to 4.2.2. - [Release notes](https://github.com/redis/jedis/releases) - [Commits](https://github.com/redis/jedis/compare/v4.2.1...v4.2.2) --- updated-dependencies: - dependency-name: redis.clients:jedis dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/redis-backed-cache-testng/build.gradle | 2 +- examples/redis-backed-cache/build.gradle | 2 +- examples/singleton-container/build.gradle | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/redis-backed-cache-testng/build.gradle b/examples/redis-backed-cache-testng/build.gradle index 9092cc86903..4faead13f3a 100644 --- a/examples/redis-backed-cache-testng/build.gradle +++ b/examples/redis-backed-cache-testng/build.gradle @@ -8,7 +8,7 @@ repositories { dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.32' - implementation 'redis.clients:jedis:4.2.1' + implementation 'redis.clients:jedis:4.2.2' implementation 'com.google.code.gson:gson:2.8.9' implementation 'com.google.guava:guava:23.0' testImplementation 'org.testcontainers:testcontainers' diff --git a/examples/redis-backed-cache/build.gradle b/examples/redis-backed-cache/build.gradle index 2df4ba7e6d6..b0d64f0524b 100644 --- a/examples/redis-backed-cache/build.gradle +++ b/examples/redis-backed-cache/build.gradle @@ -8,7 +8,7 @@ repositories { dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.32' - implementation 'redis.clients:jedis:4.2.1' + implementation 'redis.clients:jedis:4.2.2' implementation 'com.google.code.gson:gson:2.8.9' implementation 'com.google.guava:guava:23.0' testImplementation 'org.testcontainers:testcontainers' diff --git a/examples/singleton-container/build.gradle b/examples/singleton-container/build.gradle index 9a0af108840..2b7c3b7dda4 100644 --- a/examples/singleton-container/build.gradle +++ b/examples/singleton-container/build.gradle @@ -8,7 +8,7 @@ repositories { dependencies { - implementation 'redis.clients:jedis:4.2.1' + implementation 'redis.clients:jedis:4.2.2' implementation 'com.google.code.gson:gson:2.8.9' implementation 'com.google.guava:guava:23.0' compileOnly 'org.slf4j:slf4j-api:1.7.32' From f4ef7e7d28e6bc8d62c732325fa7ad76c71edfc5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 May 2022 22:00:47 +0000 Subject: [PATCH 0104/3231] Bump testcontainers from 1.16.3 to 1.17.1 in /core Bumps [testcontainers](https://github.com/testcontainers/testcontainers-java) from 1.16.3 to 1.17.1. - [Release notes](https://github.com/testcontainers/testcontainers-java/releases) - [Changelog](https://github.com/testcontainers/testcontainers-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/testcontainers/testcontainers-java/compare/1.16.3...1.17.1) --- updated-dependencies: - dependency-name: org.testcontainers:testcontainers dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 3f1ad71731d..91ee725184f 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -60,7 +60,7 @@ configurations.all { } dependencies { - baseline 'org.testcontainers:testcontainers:1.16.3', { + baseline 'org.testcontainers:testcontainers:1.17.1', { exclude group: "*", module: "*" } From 338e9bde40b3b857b5403c42c59cfb583670ce08 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 May 2022 22:00:47 +0000 Subject: [PATCH 0105/3231] Bump rest-assured from 5.0.0 to 5.0.1 in /modules/vault Bumps [rest-assured](https://github.com/rest-assured/rest-assured) from 5.0.0 to 5.0.1. - [Release notes](https://github.com/rest-assured/rest-assured/releases) - [Changelog](https://github.com/rest-assured/rest-assured/blob/master/changelog.txt) - [Commits](https://github.com/rest-assured/rest-assured/compare/rest-assured-5.0.0...rest-assured-5.0.1) --- updated-dependencies: - dependency-name: io.rest-assured:rest-assured dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/vault/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/vault/build.gradle b/modules/vault/build.gradle index 68daf4ed26f..80b416ce235 100644 --- a/modules/vault/build.gradle +++ b/modules/vault/build.gradle @@ -4,7 +4,7 @@ dependencies { api project(':testcontainers') testImplementation 'com.bettercloud:vault-java-driver:5.1.0' - testImplementation 'io.rest-assured:rest-assured:5.0.0' + testImplementation 'io.rest-assured:rest-assured:5.0.1' testImplementation 'org.assertj:assertj-core:3.22.0' } From c3583f45bb5931e21cff1a51409b82deeddc2a00 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 May 2022 22:00:48 +0000 Subject: [PATCH 0106/3231] Bump mysql-connector-java from 8.0.28 to 8.0.29 in /modules/jdbc-test Bumps [mysql-connector-java](https://github.com/mysql/mysql-connector-j) from 8.0.28 to 8.0.29. - [Release notes](https://github.com/mysql/mysql-connector-j/releases) - [Changelog](https://github.com/mysql/mysql-connector-j/blob/release/8.0/CHANGES) - [Commits](https://github.com/mysql/mysql-connector-j/compare/8.0.28...8.0.29) --- updated-dependencies: - dependency-name: mysql:mysql-connector-java dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/jdbc-test/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jdbc-test/build.gradle b/modules/jdbc-test/build.gradle index 5c10742b7f7..d77465c5616 100644 --- a/modules/jdbc-test/build.gradle +++ b/modules/jdbc-test/build.gradle @@ -16,5 +16,5 @@ dependencies { api 'org.apache.tomcat:tomcat-jdbc:10.0.20' api 'org.vibur:vibur-dbcp:25.0' - api 'mysql:mysql-connector-java:8.0.28' + api 'mysql:mysql-connector-java:8.0.29' } From dd899533f03c4f7b1df821833e3504cf22c6f559 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 May 2022 22:00:49 +0000 Subject: [PATCH 0107/3231] Bump transport from 7.17.2 to 7.17.3 in /modules/elasticsearch Bumps [transport](https://github.com/elastic/elasticsearch) from 7.17.2 to 7.17.3. - [Release notes](https://github.com/elastic/elasticsearch/releases) - [Changelog](https://github.com/elastic/elasticsearch/blob/master/CHANGELOG.md) - [Commits](https://github.com/elastic/elasticsearch/compare/v7.17.2...v7.17.3) --- updated-dependencies: - dependency-name: org.elasticsearch.client:transport dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/elasticsearch/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/elasticsearch/build.gradle b/modules/elasticsearch/build.gradle index a8d718af3c4..3d8f7bff12d 100644 --- a/modules/elasticsearch/build.gradle +++ b/modules/elasticsearch/build.gradle @@ -3,6 +3,6 @@ description = "TestContainers :: elasticsearch" dependencies { api project(':testcontainers') testImplementation "org.elasticsearch.client:elasticsearch-rest-client:7.16.2" - testImplementation "org.elasticsearch.client:transport:7.17.2" + testImplementation "org.elasticsearch.client:transport:7.17.3" testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } From e70ca03da3577b8cefc52d3a06b88c2dcd057276 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 May 2022 22:00:50 +0000 Subject: [PATCH 0108/3231] Bump aws-java-sdk-dynamodb in /modules/dynalite Bumps [aws-java-sdk-dynamodb](https://github.com/aws/aws-sdk-java) from 1.12.169 to 1.12.210. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.169...1.12.210) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-dynamodb dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/dynalite/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/dynalite/build.gradle b/modules/dynalite/build.gradle index 2fa5978772e..6dabdec119b 100644 --- a/modules/dynalite/build.gradle +++ b/modules/dynalite/build.gradle @@ -3,8 +3,8 @@ description = "Testcontainers :: Dynalite" dependencies { api project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.12.169' - testImplementation 'com.amazonaws:aws-java-sdk-dynamodb:1.12.150' + compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.12.210' + testImplementation 'com.amazonaws:aws-java-sdk-dynamodb:1.12.210' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } From 7425a05dbec152c741e2e35b16018f08bc998c36 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 May 2022 22:00:51 +0000 Subject: [PATCH 0109/3231] Bump jedis from 4.1.1 to 4.2.2 in /modules/junit-jupiter Bumps [jedis](https://github.com/redis/jedis) from 4.1.1 to 4.2.2. - [Release notes](https://github.com/redis/jedis/releases) - [Commits](https://github.com/redis/jedis/compare/v4.1.1...v4.2.2) --- updated-dependencies: - dependency-name: redis.clients:jedis dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 89882481dc1..2685e7b7e40 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -7,7 +7,7 @@ dependencies { testImplementation project(':mysql') testImplementation project(':postgresql') testImplementation 'com.zaxxer:HikariCP:4.0.3' - testImplementation 'redis.clients:jedis:4.1.1' + testImplementation 'redis.clients:jedis:4.2.2' testImplementation 'org.apache.httpcomponents:httpclient:4.5.13' testImplementation ('org.mockito:mockito-core:4.4.0') { exclude(module: 'hamcrest-core') From e62826489090b6485e4668797eda3ffddf8075c2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 May 2022 22:00:55 +0000 Subject: [PATCH 0110/3231] Bump jedis from 4.0.1 to 4.2.2 in /core Bumps [jedis](https://github.com/redis/jedis) from 4.0.1 to 4.2.2. - [Release notes](https://github.com/redis/jedis/releases) - [Commits](https://github.com/redis/jedis/compare/v4.0.1...v4.2.2) --- updated-dependencies: - dependency-name: redis.clients:jedis dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 3f1ad71731d..527d8bd6153 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -91,7 +91,7 @@ dependencies { shaded 'org.zeroturnaround:zt-exec:1.12' testImplementation 'org.apache.httpcomponents:httpclient:4.5.9' - testImplementation 'redis.clients:jedis:4.0.1' + testImplementation 'redis.clients:jedis:4.2.2' testImplementation 'com.rabbitmq:amqp-client:5.14.2' testImplementation 'org.mongodb:mongo-java-driver:3.12.10' From 93504fdbef0d27cec7b7810704c99270ecf3e743 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 May 2022 22:00:55 +0000 Subject: [PATCH 0111/3231] Bump toxiproxy-java from 2.1.5 to 2.1.7 in /modules/toxiproxy Bumps [toxiproxy-java](https://github.com/trekawek/toxiproxy-java) from 2.1.5 to 2.1.7. - [Release notes](https://github.com/trekawek/toxiproxy-java/releases) - [Changelog](https://github.com/trekawek/toxiproxy-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/trekawek/toxiproxy-java/compare/toxiproxy-java-2.1.5...toxiproxy-java-2.1.7) --- updated-dependencies: - dependency-name: eu.rekawek.toxiproxy:toxiproxy-java dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/toxiproxy/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/toxiproxy/build.gradle b/modules/toxiproxy/build.gradle index cad4b6b71f3..a1e5f58551a 100644 --- a/modules/toxiproxy/build.gradle +++ b/modules/toxiproxy/build.gradle @@ -2,7 +2,7 @@ description = "Testcontainers :: Toxiproxy" dependencies { api project(':testcontainers') - api 'eu.rekawek.toxiproxy:toxiproxy-java:2.1.5' + api 'eu.rekawek.toxiproxy:toxiproxy-java:2.1.7' testImplementation 'redis.clients:jedis:3.0.1' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' From e54b4a3db1f8de8b80cfb0f3aed494ff21ba900e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 May 2022 22:00:56 +0000 Subject: [PATCH 0112/3231] Bump com.gradle.enterprise.gradle.plugin from 3.8.1 to 3.10 in /examples Bumps com.gradle.enterprise.gradle.plugin from 3.8.1 to 3.10. --- updated-dependencies: - dependency-name: com.gradle.enterprise:com.gradle.enterprise.gradle.plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- examples/settings.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/settings.gradle b/examples/settings.gradle index 75f739e9005..ceb39498854 100644 --- a/examples/settings.gradle +++ b/examples/settings.gradle @@ -6,7 +6,7 @@ buildscript { } dependencies { classpath "gradle.plugin.ch.myniva.gradle:s3-build-cache:0.10.0" - classpath "com.gradle.enterprise:com.gradle.enterprise.gradle.plugin:3.8.1" + classpath "com.gradle.enterprise:com.gradle.enterprise.gradle.plugin:3.10" classpath "com.gradle:common-custom-user-data-gradle-plugin:1.6.2" } } From ef3e752393d1720352bd72ad4c0e8b7064f6538f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 May 2022 22:00:59 +0000 Subject: [PATCH 0113/3231] Bump aws-java-sdk-sqs from 1.12.150 to 1.12.210 in /modules/localstack Bumps [aws-java-sdk-sqs](https://github.com/aws/aws-sdk-java) from 1.12.150 to 1.12.210. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.150...1.12.210) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-sqs dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index c3f2374e23f..704a16204ae 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -5,7 +5,7 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.191' testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.169' - testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.150' + testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.210' testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.169' testImplementation 'software.amazon.awssdk:s3:2.17.162' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' From dcdad30f9e91cb87ab3c45a73b5fb9e264405399 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 May 2022 22:00:59 +0000 Subject: [PATCH 0114/3231] Bump lombok from 1.18.22 to 1.18.24 in /examples Bumps [lombok](https://github.com/projectlombok/lombok) from 1.18.22 to 1.18.24. - [Release notes](https://github.com/projectlombok/lombok/releases) - [Changelog](https://github.com/projectlombok/lombok/blob/master/doc/changelog.markdown) - [Commits](https://github.com/projectlombok/lombok/compare/v1.18.22...v1.18.24) --- updated-dependencies: - dependency-name: org.projectlombok:lombok dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/disque-job-queue/build.gradle | 4 ++-- examples/kafka-cluster/build.gradle | 2 +- examples/solr-container/build.gradle | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/disque-job-queue/build.gradle b/examples/disque-job-queue/build.gradle index 2352eace899..bfb89f9cef2 100644 --- a/examples/disque-job-queue/build.gradle +++ b/examples/disque-job-queue/build.gradle @@ -7,8 +7,8 @@ repositories { } dependencies { - compileOnly "org.projectlombok:lombok:1.18.22" - annotationProcessor "org.projectlombok:lombok:1.18.20" + compileOnly "org.projectlombok:lombok:1.18.24" + annotationProcessor "org.projectlombok:lombok:1.18.24" implementation 'biz.paluch.redis:spinach:0.3' implementation 'com.google.code.gson:gson:2.8.9' implementation 'com.google.guava:guava:23.0' diff --git a/examples/kafka-cluster/build.gradle b/examples/kafka-cluster/build.gradle index 7ba974d3a6d..ee8035ea51c 100644 --- a/examples/kafka-cluster/build.gradle +++ b/examples/kafka-cluster/build.gradle @@ -7,7 +7,7 @@ repositories { } dependencies { - testCompileOnly "org.projectlombok:lombok:1.18.22" + testCompileOnly "org.projectlombok:lombok:1.18.24" testAnnotationProcessor "org.projectlombok:lombok:1.18.22" testImplementation 'org.testcontainers:kafka' testImplementation 'org.apache.kafka:kafka-clients:3.1.0' diff --git a/examples/solr-container/build.gradle b/examples/solr-container/build.gradle index cec4f59625b..3f3366f9d46 100644 --- a/examples/solr-container/build.gradle +++ b/examples/solr-container/build.gradle @@ -7,7 +7,7 @@ repositories { } dependencies { - compileOnly "org.projectlombok:lombok:1.18.22" + compileOnly "org.projectlombok:lombok:1.18.24" annotationProcessor "org.projectlombok:lombok:1.18.22" implementation 'org.apache.solr:solr-solrj:8.11.1' From d3350c2e9de0a19d56a8d24e3750167e379d4ff9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 May 2022 22:01:06 +0000 Subject: [PATCH 0115/3231] Bump actions/cache from 3.0.1 to 3.0.2 Bumps [actions/cache](https://github.com/actions/cache) from 3.0.1 to 3.0.2. - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/v3.0.1...v3.0.2) --- updated-dependencies: - dependency-name: actions/cache dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/ci-examples.yml | 4 ++-- .github/workflows/ci.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci-examples.yml b/.github/workflows/ci-examples.yml index c1b4ac7148e..90b493df481 100644 --- a/.github/workflows/ci-examples.yml +++ b/.github/workflows/ci-examples.yml @@ -22,7 +22,7 @@ jobs: java-version: '8.0.302' distribution: temurin - name: Cache Gradle Home files - uses: actions/cache@v3.0.1 + uses: actions/cache@v3.0.2 continue-on-error: true with: path: ~/.gradle/caches @@ -50,7 +50,7 @@ jobs: java-version: '8.0.302' distribution: temurin - name: Cache Gradle Home files - uses: actions/cache@v3.0.1 + uses: actions/cache@v3.0.2 continue-on-error: true with: path: ~/.gradle/caches diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 16397815990..0c0e5eaf601 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,7 +30,7 @@ jobs: java-version: '8.0.302' distribution: temurin - name: Cache Gradle Home files - uses: actions/cache@v3.0.1 + uses: actions/cache@v3.0.2 with: path: ~/.gradle/caches key: ${{ runner.os }}-gradle-home-testmatrix-${{ hashFiles('**/*.gradle') }} @@ -56,7 +56,7 @@ jobs: java-version: '8.0.302' distribution: temurin - name: Cache Gradle Home files - uses: actions/cache@v3.0.1 + uses: actions/cache@v3.0.2 continue-on-error: true with: path: ~/.gradle/caches From 0bce9aac0621ab988552698a8c6111cc99a0e358 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 May 2022 22:02:21 +0000 Subject: [PATCH 0116/3231] Bump peter-evans/create-pull-request from 4.0.1 to 4.0.2 Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 4.0.1 to 4.0.2. - [Release notes](https://github.com/peter-evans/create-pull-request/releases) - [Commits](https://github.com/peter-evans/create-pull-request/compare/f1a7646cead32c950d90344a4fb5d4e926972a8f...bd72e1b7922d417764d27d30768117ad7da78a0e) --- updated-dependencies: - dependency-name: peter-evans/create-pull-request dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/update-docs-version.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/update-docs-version.yml b/.github/workflows/update-docs-version.yml index 3fbbda7153c..b5d672c092d 100644 --- a/.github/workflows/update-docs-version.yml +++ b/.github/workflows/update-docs-version.yml @@ -17,7 +17,7 @@ jobs: sed -i "s/latest_version: .*/latest_version: ${GITHUB_REF##*/}/g" mkdocs.yml git diff - name: Create Pull Request - uses: peter-evans/create-pull-request@f1a7646cead32c950d90344a4fb5d4e926972a8f # v3.10.1 + uses: peter-evans/create-pull-request@bd72e1b7922d417764d27d30768117ad7da78a0e # v3.10.1 with: title: Update docs version to ${GITHUB_REF##*/} body: | From 69c73c20274055ce92f39e3f29ffba41bddccf5e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 May 2022 22:02:29 +0000 Subject: [PATCH 0117/3231] Bump org.jetbrains.kotlin.jvm from 1.6.10 to 1.6.21 in /examples Bumps org.jetbrains.kotlin.jvm from 1.6.10 to 1.6.21. --- updated-dependencies: - dependency-name: org.jetbrains.kotlin.jvm dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/spring-boot-kotlin-redis/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/spring-boot-kotlin-redis/build.gradle b/examples/spring-boot-kotlin-redis/build.gradle index 1d98c3e3eff..b6dd68b33cd 100644 --- a/examples/spring-boot-kotlin-redis/build.gradle +++ b/examples/spring-boot-kotlin-redis/build.gradle @@ -1,6 +1,6 @@ plugins { id("org.springframework.boot") version "2.6.4" - id("org.jetbrains.kotlin.jvm") version "1.6.10" + id("org.jetbrains.kotlin.jvm") version "1.6.21" id("org.jetbrains.kotlin.plugin.spring") version "1.6.20" } From 9ecd399cb1c23d3130f4613ca6888d3e67dca9ab Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 May 2022 22:03:47 +0000 Subject: [PATCH 0118/3231] Bump trino-jdbc from 375 to 379 in /modules/trino Bumps trino-jdbc from 375 to 379. --- updated-dependencies: - dependency-name: io.trino:trino-jdbc dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- modules/trino/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/trino/build.gradle b/modules/trino/build.gradle index f99951dc7fa..d0b56af633f 100644 --- a/modules/trino/build.gradle +++ b/modules/trino/build.gradle @@ -4,6 +4,6 @@ dependencies { api project(':jdbc') testImplementation project(':jdbc-test') - testImplementation 'io.trino:trino-jdbc:375' + testImplementation 'io.trino:trino-jdbc:379' compileOnly 'org.jetbrains:annotations:23.0.0' } From b353a96a8ac2c260aa4ba30180e3f1495dc9835a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 May 2022 22:03:58 +0000 Subject: [PATCH 0119/3231] Bump mongodb-driver-sync from 4.5.1 to 4.6.0 in /modules/mongodb Bumps [mongodb-driver-sync](https://github.com/mongodb/mongo-java-driver) from 4.5.1 to 4.6.0. - [Release notes](https://github.com/mongodb/mongo-java-driver/releases) - [Commits](https://github.com/mongodb/mongo-java-driver/compare/r4.5.1...r4.6.0) --- updated-dependencies: - dependency-name: org.mongodb:mongodb-driver-sync dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/mongodb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mongodb/build.gradle b/modules/mongodb/build.gradle index 6bca2cb892b..d8ef34fbb40 100644 --- a/modules/mongodb/build.gradle +++ b/modules/mongodb/build.gradle @@ -3,5 +3,5 @@ description = "Testcontainers :: MongoDB" dependencies { api project(':testcontainers') - testImplementation("org.mongodb:mongodb-driver-sync:4.5.1") + testImplementation("org.mongodb:mongodb-driver-sync:4.6.0") } From e3525704d983221c94d7af7a87b3e8452ca81933 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 May 2022 22:04:00 +0000 Subject: [PATCH 0120/3231] Bump azure-cosmos from 4.26.0 to 4.29.1 in /modules/azure Bumps [azure-cosmos](https://github.com/Azure/azure-sdk-for-java) from 4.26.0 to 4.29.1. - [Release notes](https://github.com/Azure/azure-sdk-for-java/releases) - [Commits](https://github.com/Azure/azure-sdk-for-java/compare/azure-cosmos_4.26.0...azure-cosmos_4.29.1) --- updated-dependencies: - dependency-name: com.azure:azure-cosmos dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/azure/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/azure/build.gradle b/modules/azure/build.gradle index b4dab636eb9..8ab7612c18b 100644 --- a/modules/azure/build.gradle +++ b/modules/azure/build.gradle @@ -6,5 +6,5 @@ dependencies { shaded 'com.squareup.okhttp3:okhttp:3.14.9' testImplementation 'org.assertj:assertj-core:3.22.0' - testImplementation 'com.azure:azure-cosmos:4.26.0' + testImplementation 'com.azure:azure-cosmos:4.29.1' } From 9d1f038121cb0f8bf42611a9092af1f2b3857748 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 May 2022 22:04:05 +0000 Subject: [PATCH 0121/3231] Bump kubernetes-client from 5.12.1 to 5.12.2 in /modules/k3s Bumps [kubernetes-client](https://github.com/fabric8io/kubernetes-client) from 5.12.1 to 5.12.2. - [Release notes](https://github.com/fabric8io/kubernetes-client/releases) - [Changelog](https://github.com/fabric8io/kubernetes-client/blob/master/CHANGELOG.md) - [Commits](https://github.com/fabric8io/kubernetes-client/compare/v5.12.1...v5.12.2) --- updated-dependencies: - dependency-name: io.fabric8:kubernetes-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/k3s/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/k3s/build.gradle b/modules/k3s/build.gradle index a373d8115d7..0ab368d60c2 100644 --- a/modules/k3s/build.gradle +++ b/modules/k3s/build.gradle @@ -8,7 +8,7 @@ dependencies { // Any >2.8 version here is not compatible with jackson-databind 2.8.x. shaded 'com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.8.8' - testImplementation 'io.fabric8:kubernetes-client:5.12.1' + testImplementation 'io.fabric8:kubernetes-client:5.12.2' testImplementation 'io.kubernetes:client-java:15.0.0' testImplementation 'org.assertj:assertj-core:3.22.0' } From 3cd4f25f6825def6c6dfeeadf40c09fe95be01d5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 May 2022 22:04:12 +0000 Subject: [PATCH 0122/3231] Bump postgresql from 42.3.3 to 42.3.4 in /modules/cockroachdb Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.3.3 to 42.3.4. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.3.3...REL42.3.4) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/cockroachdb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/cockroachdb/build.gradle b/modules/cockroachdb/build.gradle index 4d15e4df1c1..ad1007a4744 100644 --- a/modules/cockroachdb/build.gradle +++ b/modules/cockroachdb/build.gradle @@ -4,5 +4,5 @@ dependencies { api project(':jdbc') testImplementation project(':jdbc-test') - testImplementation 'org.postgresql:postgresql:42.3.3' + testImplementation 'org.postgresql:postgresql:42.3.4' } From 9035c934cd87b8bcb051d4f1533fa0fc1d9fee0c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 May 2022 22:04:16 +0000 Subject: [PATCH 0123/3231] Bump google-cloud-pubsub from 1.115.1 to 1.116.4 in /modules/gcloud Bumps [google-cloud-pubsub](https://github.com/googleapis/java-pubsub) from 1.115.1 to 1.116.4. - [Release notes](https://github.com/googleapis/java-pubsub/releases) - [Changelog](https://github.com/googleapis/java-pubsub/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/java-pubsub/compare/v1.115.1...v1.116.4) --- updated-dependencies: - dependency-name: com.google.cloud:google-cloud-pubsub dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/gcloud/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle index 6a51c854552..475cfb56c45 100644 --- a/modules/gcloud/build.gradle +++ b/modules/gcloud/build.gradle @@ -5,7 +5,7 @@ dependencies { testImplementation 'com.google.cloud:google-cloud-datastore:2.2.2' testImplementation 'com.google.cloud:google-cloud-firestore:3.0.21' - testImplementation 'com.google.cloud:google-cloud-pubsub:1.115.1' + testImplementation 'com.google.cloud:google-cloud-pubsub:1.116.4' testImplementation 'com.google.cloud:google-cloud-spanner:6.20.0' testImplementation 'com.google.cloud:google-cloud-bigtable:2.6.1' testImplementation 'org.assertj:assertj-core:3.22.0' From f9abd766159b2d1f595f106fae9fb6325ef0e1b4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 May 2022 22:04:26 +0000 Subject: [PATCH 0124/3231] Bump reactor-core from 3.4.16 to 3.4.17 in /modules/r2dbc Bumps [reactor-core](https://github.com/reactor/reactor-core) from 3.4.16 to 3.4.17. - [Release notes](https://github.com/reactor/reactor-core/releases) - [Commits](https://github.com/reactor/reactor-core/compare/v3.4.16...v3.4.17) --- updated-dependencies: - dependency-name: io.projectreactor:reactor-core dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/r2dbc/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/r2dbc/build.gradle b/modules/r2dbc/build.gradle index d1ddfc0455a..2a4e3958af8 100644 --- a/modules/r2dbc/build.gradle +++ b/modules/r2dbc/build.gradle @@ -15,6 +15,6 @@ dependencies { testImplementation 'io.r2dbc:r2dbc-postgresql:0.8.12.RELEASE' testImplementation project(':postgresql') - testFixturesImplementation 'io.projectreactor:reactor-core:3.4.16' + testFixturesImplementation 'io.projectreactor:reactor-core:3.4.17' testFixturesImplementation 'org.assertj:assertj-core:3.14.0' } From 6a56e04b436b14e406df2ebb2e571dcce87f6bfa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 May 2022 22:04:29 +0000 Subject: [PATCH 0125/3231] Bump google-cloud-datastore from 2.2.2 to 2.4.0 in /modules/gcloud Bumps [google-cloud-datastore](https://github.com/googleapis/java-datastore) from 2.2.2 to 2.4.0. - [Release notes](https://github.com/googleapis/java-datastore/releases) - [Changelog](https://github.com/googleapis/java-datastore/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/java-datastore/compare/v2.2.2...v2.4.0) --- updated-dependencies: - dependency-name: com.google.cloud:google-cloud-datastore dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/gcloud/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle index 6a51c854552..ab571447bcc 100644 --- a/modules/gcloud/build.gradle +++ b/modules/gcloud/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: GCloud" dependencies { api project(':testcontainers') - testImplementation 'com.google.cloud:google-cloud-datastore:2.2.2' + testImplementation 'com.google.cloud:google-cloud-datastore:2.4.0' testImplementation 'com.google.cloud:google-cloud-firestore:3.0.21' testImplementation 'com.google.cloud:google-cloud-pubsub:1.115.1' testImplementation 'com.google.cloud:google-cloud-spanner:6.20.0' From bf9a26680a6a4a1394cf1fc230895112e0d53e45 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 May 2022 22:04:34 +0000 Subject: [PATCH 0126/3231] Bump google-cloud-bigtable from 2.6.1 to 2.6.2 in /modules/gcloud Bumps [google-cloud-bigtable](https://github.com/googleapis/java-bigtable) from 2.6.1 to 2.6.2. - [Release notes](https://github.com/googleapis/java-bigtable/releases) - [Changelog](https://github.com/googleapis/java-bigtable/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/java-bigtable/compare/v2.6.1...v2.6.2) --- updated-dependencies: - dependency-name: com.google.cloud:google-cloud-bigtable dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/gcloud/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle index 6a51c854552..2aea93f27e5 100644 --- a/modules/gcloud/build.gradle +++ b/modules/gcloud/build.gradle @@ -7,6 +7,6 @@ dependencies { testImplementation 'com.google.cloud:google-cloud-firestore:3.0.21' testImplementation 'com.google.cloud:google-cloud-pubsub:1.115.1' testImplementation 'com.google.cloud:google-cloud-spanner:6.20.0' - testImplementation 'com.google.cloud:google-cloud-bigtable:2.6.1' + testImplementation 'com.google.cloud:google-cloud-bigtable:2.6.2' testImplementation 'org.assertj:assertj-core:3.22.0' } From f63f9619329a2d6e25c9832c51bba3af06c7c86a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 May 2022 22:05:33 +0000 Subject: [PATCH 0127/3231] Bump hivemq-extension-sdk from 4.7.5 to 4.8.0 in /modules/hivemq Bumps [hivemq-extension-sdk](https://github.com/hivemq/hivemq-extension-sdk) from 4.7.5 to 4.8.0. - [Release notes](https://github.com/hivemq/hivemq-extension-sdk/releases) - [Commits](https://github.com/hivemq/hivemq-extension-sdk/compare/4.7.5...4.8.0) --- updated-dependencies: - dependency-name: com.hivemq:hivemq-extension-sdk dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/hivemq/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/hivemq/build.gradle b/modules/hivemq/build.gradle index dfd01981894..bfdfecb1761 100644 --- a/modules/hivemq/build.gradle +++ b/modules/hivemq/build.gradle @@ -13,7 +13,7 @@ dependencies { testImplementation("org.junit.jupiter:junit-jupiter-api:5.8.2") testImplementation(project(":junit-jupiter")) - testImplementation("com.hivemq:hivemq-extension-sdk:4.7.5") + testImplementation("com.hivemq:hivemq-extension-sdk:4.8.0") testImplementation("com.hivemq:hivemq-mqtt-client:1.3.0") testImplementation("org.apache.httpcomponents:httpclient:4.5.13") testImplementation("ch.qos.logback:logback-classic:1.2.10") From 9635a350d34035c204e1a2e451c77e617e778055 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Prinet?= Date: Mon, 2 May 2022 16:51:33 +0200 Subject: [PATCH 0128/3231] Manage Gradle Enterprise plugins with Dependabot (#5342) * Add dependabot configuration to monitor Gradle plugins * Restrict registries to Gradle Plugin Portal --- .github/dependabot.yml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 5d67f278219..3e2d53fed65 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,10 +1,25 @@ version: 2 +registries: + gradle-plugin-portal: + type: maven-repository + url: https://plugins.gradle.org/m2 + username: dummy # Required by dependabot + password: dummy # Required by dependabot updates: - package-ecosystem: "gradle" directory: "/core" schedule: interval: "monthly" open-pull-requests-limit: 10 + - package-ecosystem: "gradle" + directory: "/" + allow: + - dependency-name: "com.gradle*" + registries: + - gradle-plugin-portal + schedule: + interval: "monthly" + open-pull-requests-limit: 10 # Explicit entry for each module - package-ecosystem: "gradle" From e21164a893141b5a7be817a4f6c43485a9f930e4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 May 2022 20:10:23 +0200 Subject: [PATCH 0129/3231] Bump com.gradle.enterprise.gradle.plugin from 3.9 to 3.10 (#5343) Bumps com.gradle.enterprise.gradle.plugin from 3.9 to 3.10. --- updated-dependencies: - dependency-name: com.gradle.enterprise:com.gradle.enterprise.gradle.plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- settings.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.gradle b/settings.gradle index 64f2aa5d70a..6b80183bc34 100644 --- a/settings.gradle +++ b/settings.gradle @@ -5,7 +5,7 @@ buildscript { } } dependencies { - classpath "com.gradle.enterprise:com.gradle.enterprise.gradle.plugin:3.9" + classpath "com.gradle.enterprise:com.gradle.enterprise.gradle.plugin:3.10" classpath "com.gradle:common-custom-user-data-gradle-plugin:1.6.5" } } From 46ba812ab2315626ca3d90c7432a2a0bbb544f5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Fri, 6 May 2022 16:10:29 -0500 Subject: [PATCH 0130/3231] Add testcontainers/java-team in CODEOWNERS (#5353) --- .github/CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index add6048a437..effc21c4f1e 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -3,7 +3,7 @@ # These owners will be the default reviewers for everything in # the repo. -* @rnorth @bsideup @kiview +* @testcontainers/java-team # The last matching pattern takes the most # precedence. From b3b2a4694f2f919b2d1a7b3fa447e4e03125d56d Mon Sep 17 00:00:00 2001 From: Alexander Reelsen Date: Sun, 8 May 2022 10:02:27 +0200 Subject: [PATCH 0131/3231] Elasticsearch: Don't throw exception on missing CA cert file (#5265) The current check throws an exception if the version is supposed to be greater than 8 and the ca cert file is missing. This posed two problems: 1. A custom docker image might have a 'latest' version, and thus is newer than version 8 2. A user may have configured their own CA in a different path In order to accomodate for this, a missing copy of a the ca crt file will log a warning, but not throw an exception and leave the container unstarted. There is also another public method, allowing to read the cert, called `extractCert`. Closes #5264 --- .../elasticsearch/ElasticsearchContainer.java | 31 +++++++- .../ElasticsearchContainerTest.java | 79 +++++++++++++++---- .../src/test/resources/http_ca.crt | 31 ++++++++ 3 files changed, 123 insertions(+), 18 deletions(-) create mode 100644 modules/elasticsearch/src/test/resources/http_ca.crt diff --git a/modules/elasticsearch/src/main/java/org/testcontainers/elasticsearch/ElasticsearchContainer.java b/modules/elasticsearch/src/main/java/org/testcontainers/elasticsearch/ElasticsearchContainer.java index 84881b2e87d..ac0b367f403 100644 --- a/modules/elasticsearch/src/main/java/org/testcontainers/elasticsearch/ElasticsearchContainer.java +++ b/modules/elasticsearch/src/main/java/org/testcontainers/elasticsearch/ElasticsearchContainer.java @@ -1,7 +1,10 @@ package org.testcontainers.elasticsearch; import com.github.dockerjava.api.command.InspectContainerResponse; +import com.github.dockerjava.api.exception.NotFoundException; +import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.StringUtils; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy; import org.testcontainers.utility.Base58; @@ -21,6 +24,7 @@ * Represents an elasticsearch docker instance which exposes by default port 9200 and 9300 (transport.tcp.port) * The docker image is by default fetched from docker.elastic.co/elasticsearch/elasticsearch */ +@Slf4j public class ElasticsearchContainer extends GenericContainer { /** @@ -55,6 +59,7 @@ public class ElasticsearchContainer extends GenericContainer caCertAsBytes = Optional.empty(); + private String certPath = "/usr/share/elasticsearch/config/certs/http_ca.crt"; /** * @deprecated use {@link ElasticsearchContainer(DockerImageName)} instead @@ -100,10 +105,17 @@ public ElasticsearchContainer(final DockerImageName dockerImageName) { @Override protected void containerIsStarted(InspectContainerResponse containerInfo) { - if (isAtLeastMajorVersion8) { - byte[] bytes = copyFileFromContainer("/usr/share/elasticsearch/config/certs/http_ca.crt", IOUtils::toByteArray); - if (bytes.length > 0) { - this.caCertAsBytes = Optional.of(bytes); + if (isAtLeastMajorVersion8 && StringUtils.isNotEmpty(certPath)) { + try { + byte[] bytes = copyFileFromContainer(certPath, IOUtils::toByteArray); + if (bytes.length > 0) { + this.caCertAsBytes = Optional.of(bytes); + } + } catch (NotFoundException e) { + // just emit an error message, but do not throw an exception + // this might be ok, if the docker image is accidentally looking like version 8 or latest + // can happen if Elasticsearch is repackaged, i.e. with custom plugins + log.warn("CA cert under " + certPath + " not found."); } } } @@ -158,6 +170,17 @@ public ElasticsearchContainer withPassword(String password) { return this; } + /** + * Configure a CA cert path that is not the default + * + * @param certPath Path to the CA certificate within the Docker container to extract it from after start up + * @return this + */ + public ElasticsearchContainer withCertPath(String certPath) { + this.certPath = certPath; + return this; + } + public String getHttpHostAddress() { return getHost() + ":" + getMappedPort(ELASTICSEARCH_DEFAULT_PORT); } diff --git a/modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java b/modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java index 956c794135b..f8131268e61 100644 --- a/modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java +++ b/modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java @@ -1,5 +1,6 @@ package org.testcontainers.elasticsearch; +import com.github.dockerjava.api.DockerClient; import org.apache.http.HttpHost; import org.apache.http.auth.AuthScope; import org.apache.http.auth.UsernamePasswordCredentials; @@ -17,8 +18,12 @@ import org.elasticsearch.transport.client.PreBuiltTransportClient; import org.junit.After; import org.junit.Test; +import org.testcontainers.DockerClientFactory; +import org.testcontainers.images.RemoteDockerImage; import org.testcontainers.utility.DockerImageName; +import org.testcontainers.utility.MountableFile; +import javax.net.ssl.SSLHandshakeException; import java.io.IOException; import static org.hamcrest.CoreMatchers.containsString; @@ -252,29 +257,75 @@ public void incompatibleSettingsTest() { @Test public void testElasticsearch8SecureByDefault() throws Exception { - try (ElasticsearchContainer container = new ElasticsearchContainer("docker.elastic.co/elasticsearch/elasticsearch:8.0.0")) { + try (ElasticsearchContainer container = new ElasticsearchContainer("docker.elastic.co/elasticsearch/elasticsearch:8.1.2")) { // Start the container. This step might take some time... container.start(); - // Create the secured client. - final CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); - credentialsProvider.setCredentials(AuthScope.ANY, - new UsernamePasswordCredentials(ELASTICSEARCH_USERNAME, ElasticsearchContainer.ELASTICSEARCH_DEFAULT_PASSWORD)); - - client = RestClient.builder(HttpHost.create("https://" + container.getHttpHostAddress())) - .setHttpClientConfigCallback(httpClientBuilder -> { - httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider); - httpClientBuilder.setSSLContext(container.createSslContextFromCa()); - return httpClientBuilder; - }) - .build(); + Response response = getClusterHealth(container); + assertThat(response.getStatusLine().getStatusCode(), is(200)); + assertThat(EntityUtils.toString(response.getEntity()), containsString("cluster_name")); + } + } - Response response = client.performRequest(new Request("GET", "/_cluster/health")); + @Test + public void testElasticsearch8SecureByDefaultCustomCaCertFails() throws Exception { + final MountableFile mountableFile = MountableFile.forClasspathResource("http_ca.crt"); + String caPath = "/tmp/http_ca.crt"; + try (ElasticsearchContainer container = new ElasticsearchContainer("docker.elastic.co/elasticsearch/elasticsearch:8.1.2") + .withCopyToContainer(mountableFile, caPath) + .withCertPath(caPath)) { + + container.start(); + + // this is expected, as a different cert is used for creating the SSL context + assertThrows("PKIX path validation failed: java.security.cert.CertPathValidatorException: Path does not chain with any of the trust anchors", SSLHandshakeException.class, () -> getClusterHealth(container)); + } + } + + @Test + public void testElasticsearch8SecureByDefaultFailsSilentlyOnLatestImages() throws Exception { + // this test exists for custom images by users that use the `latest` tag + // even though the version might be older than version 8 + // this tags an old 7.x version as :latest + tagImage("docker.elastic.co/elasticsearch/elasticsearch:7.9.2", "elasticsearch-tc-older-release", "latest"); + DockerImageName image = DockerImageName.parse("elasticsearch-tc-older-release:latest").asCompatibleSubstituteFor("docker.elastic.co/elasticsearch/elasticsearch"); + + try (ElasticsearchContainer container = new ElasticsearchContainer(image)) { + container.start(); + + Response response = getClient(container).performRequest(new Request("GET", "/_cluster/health")); assertThat(response.getStatusLine().getStatusCode(), is(200)); assertThat(EntityUtils.toString(response.getEntity()), containsString("cluster_name")); } } + private void tagImage(String sourceImage, String targetImage, String targetTag) throws InterruptedException { + DockerClient dockerClient = DockerClientFactory.instance().client(); + dockerClient.tagImageCmd( + new RemoteDockerImage(DockerImageName.parse(sourceImage)).get(), + targetImage, + targetTag + ) + .exec(); + } + + private Response getClusterHealth(ElasticsearchContainer container) throws IOException { + // Create the secured client. + final CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); + credentialsProvider.setCredentials(AuthScope.ANY, + new UsernamePasswordCredentials(ELASTICSEARCH_USERNAME, ElasticsearchContainer.ELASTICSEARCH_DEFAULT_PASSWORD)); + + client = RestClient.builder(HttpHost.create("https://" + container.getHttpHostAddress())) + .setHttpClientConfigCallback(httpClientBuilder -> { + httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider); + httpClientBuilder.setSSLContext(container.createSslContextFromCa()); + return httpClientBuilder; + }) + .build(); + + return client.performRequest(new Request("GET", "/_cluster/health")); + } + private RestClient getClient(ElasticsearchContainer container) { if (client == null) { final CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); diff --git a/modules/elasticsearch/src/test/resources/http_ca.crt b/modules/elasticsearch/src/test/resources/http_ca.crt new file mode 100644 index 00000000000..704e5a37aa6 --- /dev/null +++ b/modules/elasticsearch/src/test/resources/http_ca.crt @@ -0,0 +1,31 @@ +-----BEGIN CERTIFICATE----- +MIIFWjCCA0KgAwIBAgIVAPVLz0Dwvzl66ZVpyJY/ntos+qQZMA0GCSqGSIb3DQEB +CwUAMDwxOjA4BgNVBAMTMUVsYXN0aWNzZWFyY2ggc2VjdXJpdHkgYXV0by1jb25m +aWd1cmF0aW9uIEhUVFAgQ0EwHhcNMjIwNDE0MDcyOTA4WhcNMjUwNDEzMDcyOTA4 +WjA8MTowOAYDVQQDEzFFbGFzdGljc2VhcmNoIHNlY3VyaXR5IGF1dG8tY29uZmln +dXJhdGlvbiBIVFRQIENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA +ufHmvM04dPKNF6AulX3HrjNfcYy62pBO2oJIKhScetDzuRupv/qiXkO1gzGT3/jk +8uk57rzylDFoCgFNUyWdYQAXD+qsy5vbAytGVNCHOGSuWlNm1bbDYwZNTXjZTK6C +CKIY31lbFn9a4oM+Jp1kvIr8GSMQAYDisq+yrVloDLkRs1SPImnoaXsq8epxloBf +En0vo5V8PtOh+xQFpPP21pd5QohCSB+jMaxaizScWX+k7BijEaS1LCsc2w2PNvwn +/bEvtW7w9w+HnzRGZW2nVlt8eji1PHMmfM5Zugn4HAxsSvdI9VRFGAeT2moNiSLN +zxOWmEvQVl2MxRWiTkM1EM7CFDN40hLHtHej8UddeXTDDXyLoUjY3FmaB45rLKdE +k/rszepc3lmptEMh74iUowKaYZTS5jRqT0yIDzevP5je3JP+pe4aNkx7lWMhTReQ +6fs97nd71PfJBuMcHPEA14zwSzSRb/8mNqqaQLBb5H1DpDcZtzbBxb4wFSAa7Dd0 +pVl4A04iB4PS3DaWg/im2C5a83nUTld7Lvy+I6cO1MTaXdkzn6EWXtxkHj7P4VXX +sFTr+Z2A6g/novqderirQzq8aD87MBp2hLBgG59lVB3IXA+CJTzBRBZipU+FOSs2 +1enMlaEa84d8cb+GSpDkmsvamPMhhLeMw6QLmhQXoWMCAwEAAaNTMFEwHQYDVR0O +BBYEFOt9JC+RiqfFdGf/UT2vfmnV8f+kMB8GA1UdIwQYMBaAFOt9JC+RiqfFdGf/ +UT2vfmnV8f+kMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggIBALFX +r2CKtu2DcLFQaZft9LeRK67xU0rjN8w1+0MN9otSkU9avd8atPOI9p6r67HZyoTv +LDA47ajitzPo4zAXiy4GUXFVCjx3iPOQ4TTCm0YEf9IudLHLqGTbK+Pup6XlfAMb +RejkztcXkxw3Cy6SjIRq99xU8J6Y1jAggB162hqnp3u42nA1PgOJgo/biUYeVtu7 +Y01gKPdCEkiqmSFxfLiRPv5Z4WyYoKge6UyDYFHu0zyMY7zQ2hzrPMFsEhI4+g5D +W7ihilcOijhvfeeWIxcP5lRn2pGbf2GtwqtA7Bt9YKp+NQIBPzK7D1ymS0v/CAWh +3Bv1rqkqDK8TlBybZitTTB6MgGtXOBccouTPmBFBXSWydvW4GW6Dag/ogHHE2vG2 +xlXY6EC1QEzExcM5FZNJI6SOaK0nl+WKAv060U/1ZqcRIkhyctYdkrK4449n1JMy +wjtwcDW7QxhQspHp8GEXztLctokqGjnuMcgPjVoFdiF3w/IV0UUvVeFK4Oms0YbH +uFr3q44Fu/Fol68/1CUk1ytgLUS5anf0Q0WlJsmMUX156ATA29dVBfloJN63EYd7 +01uwbjoMJce7MiwTaLIetW75fxxZHlQK9TMNhaQwKUO8SRaNuE4wKURFoKPg/Dqu +yPhx9adseStlJ3oV6ziEWMwjOK2JmJf0bmIqQ7KR +-----END CERTIFICATE----- From 84a635efad8a2dc1726ee263950e013de9b51e58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Mon, 9 May 2022 09:11:20 -0500 Subject: [PATCH 0132/3231] Fix docs: use getHost (#5288) --- .../src/test/java/com/example/AbstractIntegrationTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/spring-boot/src/test/java/com/example/AbstractIntegrationTest.java b/examples/spring-boot/src/test/java/com/example/AbstractIntegrationTest.java index b6859563c69..dd1b7b6fb5a 100644 --- a/examples/spring-boot/src/test/java/com/example/AbstractIntegrationTest.java +++ b/examples/spring-boot/src/test/java/com/example/AbstractIntegrationTest.java @@ -23,7 +23,7 @@ public abstract class AbstractIntegrationTest { @DynamicPropertySource static void redisProperties(DynamicPropertyRegistry registry) { redis.start(); - registry.add("spring.redis.host", redis::getContainerIpAddress); + registry.add("spring.redis.host", redis::getHost); registry.add("spring.redis.port", redis::getFirstMappedPort); } } From 0b03b91c22fe79af25c8576c5c13195b76f3f619 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Mon, 9 May 2022 20:40:29 -0400 Subject: [PATCH 0133/3231] Fix deadlock in `LocalImagesCache` (#5356) Fixes #5282 --- .../java/org/testcontainers/images/LocalImagesCache.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/org/testcontainers/images/LocalImagesCache.java b/core/src/main/java/org/testcontainers/images/LocalImagesCache.java index ec38721a229..8791ecb6986 100644 --- a/core/src/main/java/org/testcontainers/images/LocalImagesCache.java +++ b/core/src/main/java/org/testcontainers/images/LocalImagesCache.java @@ -27,15 +27,14 @@ enum LocalImagesCache { @VisibleForTesting final Map cache = new ConcurrentHashMap<>(); - DockerClient dockerClient = DockerClientFactory.lazyClient(); - public ImageData get(DockerImageName imageName) { - maybeInitCache(); + maybeInitCache(DockerClientFactory.instance().client()); return cache.get(imageName); } public Optional refreshCache(DockerImageName imageName) { - if (!maybeInitCache()) { + DockerClient dockerClient = DockerClientFactory.instance().client(); + if (!maybeInitCache(dockerClient)) { // Cache may be stale, trying inspectImageCmd... InspectImageResponse response = null; @@ -57,7 +56,7 @@ public Optional refreshCache(DockerImageName imageName) { return Optional.ofNullable(cache.get(imageName)); } - private synchronized boolean maybeInitCache() { + private synchronized boolean maybeInitCache(DockerClient dockerClient) { if (!initialized.compareAndSet(false, true)) { return false; } From 3164d4ec1081a8f43982dcadc9db887ea6ac79b3 Mon Sep 17 00:00:00 2001 From: Felix Seifert Date: Tue, 10 May 2022 02:40:56 +0200 Subject: [PATCH 0134/3231] Correct usage of deprecated method `createSubscription()` (#5350) Call non-deprecated `createSubscription` method in `PubSubEmulatorTest` --- .../testcontainers/containers/PubSubEmulatorContainerTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/gcloud/src/test/java/org/testcontainers/containers/PubSubEmulatorContainerTest.java b/modules/gcloud/src/test/java/org/testcontainers/containers/PubSubEmulatorContainerTest.java index 8db49eb3649..61d56a279d7 100644 --- a/modules/gcloud/src/test/java/org/testcontainers/containers/PubSubEmulatorContainerTest.java +++ b/modules/gcloud/src/test/java/org/testcontainers/containers/PubSubEmulatorContainerTest.java @@ -20,6 +20,7 @@ import com.google.pubsub.v1.PullRequest; import com.google.pubsub.v1.PullResponse; import com.google.pubsub.v1.PushConfig; +import com.google.pubsub.v1.SubscriptionName; import com.google.pubsub.v1.TopicName; import io.grpc.ManagedChannel; import io.grpc.ManagedChannelBuilder; @@ -104,7 +105,7 @@ private void createSubscription(String subscriptionId, String topicId, Transport .setCredentialsProvider(credentialsProvider) .build(); SubscriptionAdminClient subscriptionAdminClient = SubscriptionAdminClient.create(subscriptionAdminSettings); - ProjectSubscriptionName subscriptionName = ProjectSubscriptionName.of(PROJECT_ID, subscriptionId); + SubscriptionName subscriptionName = SubscriptionName.of(PROJECT_ID, subscriptionId); subscriptionAdminClient.createSubscription(subscriptionName, TopicName.of(PROJECT_ID, topicId), PushConfig.getDefaultInstance(), 10); } // } From d4e7671a09f6312f96044f937f2d0973078860e3 Mon Sep 17 00:00:00 2001 From: Matthew de Detrich Date: Tue, 10 May 2022 08:11:31 +0200 Subject: [PATCH 0135/3231] Add getConnectionString() for MongoDBContainer (#5271) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Eddú Meléndez Gonzales Co-authored-by: Kevin Wittek --- .../containers/MongoDBContainer.java | 20 +++++++++++++------ .../containers/MongoDBContainerTest.java | 4 ++++ 2 files changed, 18 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 53336d4fbec..87db80846a8 100644 --- a/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDBContainer.java +++ b/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDBContainer.java @@ -48,6 +48,19 @@ public MongoDBContainer(final DockerImageName dockerImageName) { ); } + /** + * Gets a connection string url, unlike {@link #getReplicaSetUrl} this does point to a + * database + * @return a connection url pointing to a mongodb instance + */ + public String getConnectionString() { + return String.format( + "mongodb://%s:%d", + getHost(), + getMappedPort(MONGODB_INTERNAL_PORT) + ); + } + /** * Gets a replica set url for the default {@value #MONGODB_DATABASE_NAME_DEFAULT} database. * @@ -67,12 +80,7 @@ public String getReplicaSetUrl(final String databaseName) { if (!isRunning()) { throw new IllegalStateException("MongoDBContainer should be started first"); } - return String.format( - "mongodb://%s:%d/%s", - getHost(), - getMappedPort(MONGODB_INTERNAL_PORT), - databaseName - ); + return getConnectionString() + "/" + databaseName; } @Override 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 ff5243acea7..532a86208c1 100644 --- a/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBContainerTest.java +++ b/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBContainerTest.java @@ -38,11 +38,15 @@ public void shouldExecuteTransactions() { final String mongoRsUrl = mongoDBContainer.getReplicaSetUrl(); assertNotNull(mongoRsUrl); + final String connectionString = mongoDBContainer.getConnectionString(); + final MongoClient mongoSyncClientBase = MongoClients.create(connectionString); 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)); + mongoSyncClientBase.getDatabase("mydb3").getCollection("baz") + .withWriteConcern(WriteConcern.MAJORITY).insertOne(new Document("def", 0)); final ClientSession clientSession = mongoSyncClient.startSession(); final TransactionOptions txnOptions = TransactionOptions.builder() From 87126060b0e76ace2b4b9b344d92908fe4fb7cf5 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Tue, 10 May 2022 10:19:57 -0400 Subject: [PATCH 0136/3231] Test `DOCKER_HOST` in `DockerClientProviderStrategy` (#5357) * Test `DOCKER_HOST` in `DockerClientProviderStrategy` * Update DockerClientProviderStrategy.java --- .../DockerClientProviderStrategy.java | 101 +++++++++++++++--- 1 file changed, 88 insertions(+), 13 deletions(-) diff --git a/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java index 2ae066d897d..cf091e94328 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java @@ -7,6 +7,9 @@ import com.github.dockerjava.core.DockerClientImpl; import com.github.dockerjava.core.RemoteApiVersion; import com.github.dockerjava.transport.DockerHttpClient; +import com.github.dockerjava.transport.NamedPipeSocket; +import com.github.dockerjava.transport.SSLConfig; +import com.github.dockerjava.transport.UnixSocket; import com.github.dockerjava.zerodep.ZerodepDockerHttpClient; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Throwables; @@ -14,15 +17,28 @@ import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; +import org.awaitility.Awaitility; import org.jetbrains.annotations.Nullable; import org.rnorth.ducttape.TimeoutException; import org.rnorth.ducttape.ratelimits.RateLimiter; import org.rnorth.ducttape.ratelimits.RateLimiterBuilder; import org.rnorth.ducttape.unreliables.Unreliables; import org.testcontainers.DockerClientFactory; +import org.testcontainers.UnstableAPI; import org.testcontainers.utility.TestcontainersConfiguration; +import javax.net.SocketFactory; +import java.io.File; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.SocketAddress; +import java.net.SocketTimeoutException; import java.net.URI; +import java.security.KeyManagementException; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.UnrecoverableKeyException; +import java.time.Duration; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -31,6 +47,7 @@ import java.util.Objects; import java.util.Optional; import java.util.Set; +import java.util.concurrent.Callable; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Predicate; @@ -104,6 +121,71 @@ public DockerClient getClient() { return dockerClient; } + /** + * TODO we should consider moving this to docker-java at some point + */ + @UnstableAPI + protected boolean test() { + TransportConfig transportConfig = getTransportConfig(); + URI dockerHost = transportConfig.getDockerHost(); + + Callable socketProvider; + SocketAddress socketAddress; + switch (dockerHost.getScheme()) { + case "tcp": + case "http": + case "https": + SocketFactory socketFactory = SocketFactory.getDefault(); + SSLConfig sslConfig = transportConfig.getSslConfig(); + if (sslConfig != null) { + try { + socketFactory = sslConfig.getSSLContext().getSocketFactory(); + } catch (KeyManagementException | UnrecoverableKeyException | NoSuchAlgorithmException | KeyStoreException e) { + log.warn("Exception while creating SSLSocketFactory", e); + return false; + } + } + socketProvider = socketFactory::createSocket; + socketAddress = new InetSocketAddress(dockerHost.getHost(), dockerHost.getPort()); + break; + case "unix": + case "npipe": + if (!new File(dockerHost.getPath()).exists()) { + log.debug("DOCKER_HOST socket file '{}' does not exist", dockerHost.getPath()); + return false; + } + socketProvider = () -> { + switch (dockerHost.getScheme()) { + case "unix": + return UnixSocket.get(dockerHost.getPath()); + case "npipe": + return new NamedPipeSocket(dockerHost.getPath()); + default: + throw new IllegalStateException("Unexpected scheme " + dockerHost.getScheme()); + } + }; + socketAddress = new InetSocketAddress("localhost", 2375); + break; + default: + log.warn("Unknown DOCKER_HOST scheme {}, skipping the strategy test...", dockerHost.getScheme()); + return true; + } + + try (Socket socket = socketProvider.call()) { + Duration timeout = Duration.ofMillis(200); + Awaitility.await() + .atMost(TestcontainersConfiguration.getInstance().getClientPingTimeout(), TimeUnit.SECONDS) + .pollInterval(timeout) + .pollDelay(Duration.ofSeconds(0)) // start checking immediately + .ignoreExceptionsInstanceOf(SocketTimeoutException.class) + .untilAsserted(() -> socket.connect(socketAddress, (int) timeout.toMillis())); + return true; + } catch (Exception e) { + log.warn("DOCKER_HOST {} is not listening", dockerHost); + return false; + } + } + /** * Determine the right DockerClientConfig to use for building clients by trial-and-error. * @@ -161,20 +243,13 @@ public boolean test(DockerClientProviderStrategy dockerClientProviderStrategy) { private static boolean tryOutStrategy(List configurationFailures, DockerClientProviderStrategy strategy) { try { log.debug("Trying out strategy: {}", strategy.getClass().getSimpleName()); - DockerClient dockerClient = strategy.getDockerClient(); - - Info info; - try { - info = Unreliables.retryUntilSuccess(TestcontainersConfiguration.getInstance().getClientPingTimeout(), TimeUnit.SECONDS, () -> { - return strategy.PING_RATE_LIMITER.getWhenReady(() -> { - log.debug("Pinging docker daemon..."); - return dockerClient.infoCmd().exec(); - }); - }); - } catch (TimeoutException e) { - IOUtils.closeQuietly(dockerClient); - throw e; + + if (!strategy.test()) { + log.debug("strategy {} did not pass the test", strategy.getClass().getSimpleName()); + return false; } + + Info info = strategy.getDockerClient().infoCmd().exec(); log.info("Found Docker environment with {}", strategy.getDescription()); log.debug( "Transport type: '{}', Docker host: '{}'", From 7d54ff90a4e9514ded0ea0a09d234ae5d76cc10f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Wed, 11 May 2022 13:22:18 -0500 Subject: [PATCH 0137/3231] Allow to create exchanges, queues, bindings in the same vhost (#5348) --- .../containers/RabbitMQContainer.java | 39 +++++++++++++++++++ .../containers/RabbitMQContainerTest.java | 16 +++++--- 2 files changed, 49 insertions(+), 6 deletions(-) diff --git a/modules/rabbitmq/src/main/java/org/testcontainers/containers/RabbitMQContainer.java b/modules/rabbitmq/src/main/java/org/testcontainers/containers/RabbitMQContainer.java index 37dc2fbe7ca..f5c63a267a9 100644 --- a/modules/rabbitmq/src/main/java/org/testcontainers/containers/RabbitMQContainer.java +++ b/modules/rabbitmq/src/main/java/org/testcontainers/containers/RabbitMQContainer.java @@ -220,8 +220,25 @@ public RabbitMQContainer withBinding(String source, String destination) { return self(); } + public RabbitMQContainer withBinding(String vhost, String source, String destination) { + values.add(asList("rabbitmqadmin", "--vhost=" + vhost, "declare", "binding", + "source=" + source, + "destination=" + destination)); + return self(); + } + public RabbitMQContainer withBinding(String source, String destination, Map arguments, String routingKey, String destinationType) { values.add(asList("rabbitmqadmin", "declare", "binding", + "source=" + source, + "destination=" + destination, + "routing_key=" + routingKey, + "destination_type=" + destinationType, + "arguments=" + toJson(arguments))); + return self(); + } + + public RabbitMQContainer withBinding(String vhost, String source, String destination, Map arguments, String routingKey, String destinationType) { + values.add(asList("rabbitmqadmin", "--vhost=" + vhost, "declare", "binding", "source=" + source, "destination=" + destination, "routing_key=" + routingKey, @@ -332,6 +349,12 @@ public RabbitMQContainer withVhostLimit(String vhost, String name, int value) { public RabbitMQContainer withQueue(String name) { values.add(asList("rabbitmqadmin", "declare", "queue", + "name=" + name)); + return self(); + } + + public RabbitMQContainer withQueue(String vhost, String name) { + values.add(asList("rabbitmqadmin", "--vhost=" + vhost, "declare", "queue", "name=" + name)); return self(); } @@ -345,6 +368,15 @@ public RabbitMQContainer withQueue(String name, boolean autoDelete, boolean dura return self(); } + public RabbitMQContainer withQueue(String vhost, String name, boolean autoDelete, boolean durable, Map arguments) { + values.add(asList("rabbitmqadmin", "--vhost=" + vhost, "declare", "queue", + "name=" + name, + "auto_delete=" + autoDelete, + "durable=" + durable, + "arguments=" + toJson(arguments))); + return self(); + } + public RabbitMQContainer withExchange(String name, String type) { values.add(asList("rabbitmqadmin", "declare", "exchange", "name=" + name, @@ -352,6 +384,13 @@ public RabbitMQContainer withExchange(String name, String type) { return self(); } + public RabbitMQContainer withExchange(String vhost, String name, String type) { + values.add(asList("rabbitmqadmin", "--vhost=" + vhost, "declare", "exchange", + "name=" + name, + "type=" + type)); + return self(); + } + public RabbitMQContainer withExchange(String name, String type, boolean autoDelete, boolean internal, boolean durable, Map arguments) { values.add(asList("rabbitmqadmin", "declare", "exchange", "name=" + name, diff --git a/modules/rabbitmq/src/test/java/org/testcontainers/containers/RabbitMQContainerTest.java b/modules/rabbitmq/src/test/java/org/testcontainers/containers/RabbitMQContainerTest.java index 474d613cb8b..d82051a94db 100644 --- a/modules/rabbitmq/src/test/java/org/testcontainers/containers/RabbitMQContainerTest.java +++ b/modules/rabbitmq/src/test/java/org/testcontainers/containers/RabbitMQContainerTest.java @@ -14,7 +14,6 @@ import java.io.File; import java.io.FileInputStream; import java.io.IOException; -import java.io.InputStream; import java.security.KeyManagementException; import java.security.KeyStore; import java.security.KeyStoreException; @@ -22,8 +21,6 @@ import java.security.UnrecoverableKeyException; import java.security.cert.CertificateException; import java.util.Collections; -import java.util.Objects; -import java.util.Scanner; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatCode; @@ -159,9 +156,16 @@ public void shouldStartTheWholeEnchilada() throws IOException, InterruptedExcept .withVhost("vhost2", true) .withExchange("direct-exchange", "direct") .withExchange("topic-exchange", "topic") + .withExchange("vhost1", "topic-exchange-2", "topic", false, false, true, Collections.emptyMap()) + .withExchange("vhost2", "topic-exchange-3", "topic") + .withExchange("topic-exchange-4", "topic", false, false, true, Collections.emptyMap()) .withQueue("queue1") .withQueue("queue2", true, false, ImmutableMap.of("x-message-ttl", 1000)) + .withQueue("vhost1", "queue3", true, false, ImmutableMap.of("x-message-ttl", 1000)) + .withQueue("vhost2", "queue4") .withBinding("direct-exchange", "queue1") + .withBinding("vhost1", "topic-exchange-2", "queue3") + .withBinding("vhost2", "topic-exchange-3", "queue4", Collections.emptyMap(), "ss7", "queue") .withUser("user1", "password1") .withUser("user2", "password2", ImmutableSet.of("administrator")) .withPermission("vhost1", "user1", ".*", ".*", ".*") @@ -175,15 +179,15 @@ public void shouldStartTheWholeEnchilada() throws IOException, InterruptedExcept assertThat(container.execInContainer("rabbitmqadmin", "list", "queues") .getStdout()) - .contains("queue1", "queue2"); + .contains("queue1", "queue2", "queue3", "queue4"); assertThat(container.execInContainer("rabbitmqadmin", "list", "exchanges") .getStdout()) - .contains("direct-exchange", "topic-exchange"); + .contains("direct-exchange", "topic-exchange", "topic-exchange-2", "topic-exchange-3", "topic-exchange-4"); assertThat(container.execInContainer("rabbitmqadmin", "list", "bindings") .getStdout()) - .contains("direct-exchange"); + .contains("direct-exchange", "topic-exchange-2", "topic-exchange-3"); assertThat(container.execInContainer("rabbitmqadmin", "list", "users") .getStdout()) From f41bdb2cda1b45e86db07507b18e8acbf8641cc6 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Fri, 13 May 2022 21:05:43 -0400 Subject: [PATCH 0138/3231] Read `indexServerAddress` from Docker's `/info` (#5347) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Read `indexServerAddress` from Docker's `/info` * remove `setIndexServerAddress` * Update fallback default registry name to `https://index.docker.io/v1/` * Fix build Co-authored-by: Kevin Wittek Co-authored-by: Eddú Meléndez Gonzales --- .../java/org/testcontainers/DockerClientFactory.java | 7 ++++++- .../dockerclient/DockerClientProviderStrategy.java | 7 +++++-- .../testcontainers/utility/RegistryAuthLocator.java | 12 ++++++++++-- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/org/testcontainers/DockerClientFactory.java b/core/src/main/java/org/testcontainers/DockerClientFactory.java index 6cf8f716704..cc85645b2b4 100644 --- a/core/src/main/java/org/testcontainers/DockerClientFactory.java +++ b/core/src/main/java/org/testcontainers/DockerClientFactory.java @@ -199,7 +199,7 @@ public void close() { }; log.info("Docker host IP address is {}", strategy.getDockerHostIpAddress()); - Info dockerInfo = client.infoCmd().exec(); + Info dockerInfo = strategy.getInfo(); Version version = client.versionCmd().exec(); activeApiVersion = version.getApiVersion(); activeExecutionDriver = dockerInfo.getExecutionDriver(); @@ -434,4 +434,9 @@ public String getActiveExecutionDriver() { public boolean isUsing(Class providerStrategyClass) { return strategy != null && providerStrategyClass.isAssignableFrom(this.strategy.getClass()); } + + @UnstableAPI + public Info getInfo() { + return getOrInitializeStrategy().getInfo(); + } } diff --git a/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java index cf091e94328..834e670438a 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java @@ -65,6 +65,9 @@ public abstract class DockerClientProviderStrategy { private String dockerHostIpAddress; + @Getter + private Info info; + private final RateLimiter PING_RATE_LIMITER = RateLimiterBuilder.newBuilder() .withRate(10, TimeUnit.SECONDS) .withConstantThroughput() @@ -249,7 +252,7 @@ private static boolean tryOutStrategy(List configurationFailures, Docker return false; } - Info info = strategy.getDockerClient().infoCmd().exec(); + strategy.info = strategy.getDockerClient().infoCmd().exec(); log.info("Found Docker environment with {}", strategy.getDescription()); log.debug( "Transport type: '{}', Docker host: '{}'", @@ -258,7 +261,7 @@ private static boolean tryOutStrategy(List configurationFailures, Docker ); log.debug("Checking Docker OS type for {}", strategy.getDescription()); - String osType = info.getOsType(); + String osType = strategy.getInfo().getOsType(); if (StringUtils.isBlank(osType)) { log.warn("Could not determine Docker OS type"); } else if (!osType.equals("linux")) { diff --git a/core/src/main/java/org/testcontainers/utility/RegistryAuthLocator.java b/core/src/main/java/org/testcontainers/utility/RegistryAuthLocator.java index d88bdccd76a..e487bda686b 100644 --- a/core/src/main/java/org/testcontainers/utility/RegistryAuthLocator.java +++ b/core/src/main/java/org/testcontainers/utility/RegistryAuthLocator.java @@ -7,6 +7,7 @@ import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.SystemUtils; import org.slf4j.Logger; +import org.testcontainers.DockerClientFactory; import org.zeroturnaround.exec.InvalidResultException; import org.zeroturnaround.exec.ProcessExecutor; @@ -32,7 +33,7 @@ public class RegistryAuthLocator { private static final Logger log = getLogger(RegistryAuthLocator.class); - private static final String DEFAULT_REGISTRY_NAME = "index.docker.io"; + private static final String DEFAULT_REGISTRY_NAME = "https://index.docker.io/v1/"; private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); private static RegistryAuthLocator instance; @@ -282,7 +283,14 @@ private String getCredentialProgramName(String credHelper) { } private String effectiveRegistryName(DockerImageName dockerImageName) { - return StringUtils.defaultIfEmpty(dockerImageName.getRegistry(), DEFAULT_REGISTRY_NAME); + final String registry = dockerImageName.getRegistry(); + if (!StringUtils.isEmpty(registry)) { + return registry; + } + return StringUtils.defaultString( + DockerClientFactory.instance().getInfo().getIndexServerAddress(), + DEFAULT_REGISTRY_NAME + ); } private String getGenericCredentialsNotFoundMsg(String credentialHelperName) { From bf70b4093fd31850fe58d6d29af2039c1026ce23 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Sun, 15 May 2022 11:00:06 -0400 Subject: [PATCH 0139/3231] run `core` tests in parallel (#5365) * run `core` tests in parallel * ooops * Dummy change * dummy change (2) --- core/build.gradle | 2 + .../DockerClientFactoryTest.java | 38 +++++------- .../DockerRegistryContainer.java | 36 +++++++++++ .../dockerclient/AmbiguousImagePullTest.java | 31 ++++------ .../images/ImagePullPolicyTest.java | 59 +++--------------- .../utility/AuthenticatedImagePullTest.java | 62 +++---------------- 6 files changed, 79 insertions(+), 149 deletions(-) diff --git a/core/build.gradle b/core/build.gradle index 3f1ad71731d..b519673de4b 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -6,6 +6,8 @@ sourceSets { jarFileTest } +test.maxParallelForks = 4 + idea.module.testSourceDirs += sourceSets.jarFileTest.allSource.srcDirs shadowJar { diff --git a/core/src/test/java/org/testcontainers/DockerClientFactoryTest.java b/core/src/test/java/org/testcontainers/DockerClientFactoryTest.java index 4dcfc26349f..247a16a76a6 100644 --- a/core/src/test/java/org/testcontainers/DockerClientFactoryTest.java +++ b/core/src/test/java/org/testcontainers/DockerClientFactoryTest.java @@ -1,18 +1,16 @@ package org.testcontainers; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; - -import com.github.dockerjava.api.exception.NotFoundException; import org.junit.Rule; import org.junit.Test; import org.rnorth.visibleassertions.VisibleAssertions; import org.testcontainers.DockerClientFactory.DiskSpaceUsage; import org.testcontainers.dockerclient.LogToStringContainerCallback; -import org.testcontainers.images.LocalImagesCacheAccessor; import org.testcontainers.utility.DockerImageName; import org.testcontainers.utility.MockTestcontainersConfigurationRule; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + /** * Test for {@link DockerClientFactory}. */ @@ -23,30 +21,22 @@ public class DockerClientFactoryTest { @Test public void runCommandInsideDockerShouldNotFailIfImageDoesNotExistsLocally() { + try (DockerRegistryContainer registryContainer = new DockerRegistryContainer()) { + registryContainer.start(); + DockerImageName imageName = registryContainer.createImage(); - final DockerClientFactory dockFactory = DockerClientFactory.instance(); + DockerClientFactory dockFactory = DockerClientFactory.instance(); - DockerImageName imageName = DockerImageName.parse("testcontainers/helloworld:1.1.0"); - - try { - //remove tiny image, so it will be pulled during next command run - dockFactory.client() - .removeImageCmd(imageName.asCanonicalNameString()) - .withForce(true).exec(); - } catch (NotFoundException ignored) { - // Do not fail if it's not pulled yet - } - LocalImagesCacheAccessor.clearCache(); - - dockFactory.runInsideDocker( + dockFactory.runInsideDocker( imageName, cmd -> cmd.withCmd("sh", "-c", "echo 'SUCCESS'"), (client, id) -> - client.logContainerCmd(id) - .withStdOut(true) - .exec(new LogToStringContainerCallback()) - .toString() - ); + client.logContainerCmd(id) + .withStdOut(true) + .exec(new LogToStringContainerCallback()) + .toString() + ); + } } @Test diff --git a/core/src/test/java/org/testcontainers/DockerRegistryContainer.java b/core/src/test/java/org/testcontainers/DockerRegistryContainer.java index 641b1553d72..4018b2b954d 100644 --- a/core/src/test/java/org/testcontainers/DockerRegistryContainer.java +++ b/core/src/test/java/org/testcontainers/DockerRegistryContainer.java @@ -1,6 +1,9 @@ package org.testcontainers; +import com.github.dockerjava.api.DockerClient; +import com.github.dockerjava.api.async.ResultCallback; import com.github.dockerjava.api.command.InspectContainerResponse; +import com.github.dockerjava.api.command.PullImageResultCallback; import lombok.Getter; import lombok.NonNull; import lombok.SneakyThrows; @@ -8,7 +11,10 @@ import org.testcontainers.containers.output.FrameConsumerResultCallback; import org.testcontainers.containers.output.OutputFrame; import org.testcontainers.containers.output.WaitingConsumer; +import org.testcontainers.utility.Base58; +import org.testcontainers.utility.DockerImageName; +import java.util.UUID; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; @@ -65,4 +71,34 @@ protected void containerIsStarting(InspectContainerResponse containerInfo) { endpoint = getHost() + ":" + port.get(); } + + public DockerImageName createImage() { + return createImage(UUID.randomUUID().toString()); + } + + public DockerImageName createImage(String tag) { + return createImage("testcontainers/helloworld:latest", tag); + } + + @SneakyThrows(InterruptedException.class) + public DockerImageName createImage(String originalImage, String tag) { + DockerClient client = getDockerClient(); + client.pullImageCmd(originalImage).exec(new PullImageResultCallback()).awaitCompletion(); + + String dummyImageId = client.inspectImageCmd(originalImage).exec().getId(); + + DockerImageName imageName = DockerImageName.parse(getEndpoint() + "/" + Base58.randomString(6).toLowerCase()).withTag(tag); + + // push the image to the registry + client.tagImageCmd(dummyImageId, imageName.asCanonicalNameString(), tag).exec(); + + client.pushImageCmd(imageName.asCanonicalNameString()) + .exec(new ResultCallback.Adapter<>()) + .awaitCompletion(1, TimeUnit.MINUTES); + + // Remove from local cache, tests should pull the image themselves + client.removeImageCmd(imageName.asCanonicalNameString()).exec(); + + return imageName; + } } diff --git a/core/src/test/java/org/testcontainers/dockerclient/AmbiguousImagePullTest.java b/core/src/test/java/org/testcontainers/dockerclient/AmbiguousImagePullTest.java index 9e62148c3bb..2e39b367c46 100644 --- a/core/src/test/java/org/testcontainers/dockerclient/AmbiguousImagePullTest.java +++ b/core/src/test/java/org/testcontainers/dockerclient/AmbiguousImagePullTest.java @@ -1,32 +1,25 @@ package org.testcontainers.dockerclient; -import com.github.dockerjava.api.DockerClient; -import com.github.dockerjava.api.model.Image; import org.junit.Test; -import org.testcontainers.DockerClientFactory; +import org.testcontainers.DockerRegistryContainer; import org.testcontainers.containers.GenericContainer; import org.testcontainers.utility.DockerImageName; -import java.util.List; - public class AmbiguousImagePullTest { @Test(timeout = 30_000) public void testNotUsingParse() { - DockerClient client = DockerClientFactory.instance().client(); - List alpineImages = client.listImagesCmd() - .withImageNameFilter("testcontainers/helloworld:latest") - .exec(); - for (Image alpineImage : alpineImages) { - client.removeImageCmd(alpineImage.getId()).exec(); - } - - try ( - final GenericContainer container = new GenericContainer<>(DockerImageName.parse("testcontainers/helloworld")) - .withExposedPorts(8080) - ) { - container.start(); - // do nothing other than start and stop + try (DockerRegistryContainer registryContainer = new DockerRegistryContainer()) { + registryContainer.start(); + DockerImageName imageName = registryContainer.createImage("latest"); + String imageNameWithoutTag = imageName.getRegistry() + "/" + imageName.getRepository(); + try ( + final GenericContainer container = new GenericContainer<>(imageNameWithoutTag) + .withExposedPorts(8080) + ) { + container.start(); + // do nothing other than start and stop + } } } } diff --git a/core/src/test/java/org/testcontainers/images/ImagePullPolicyTest.java b/core/src/test/java/org/testcontainers/images/ImagePullPolicyTest.java index 85c2e785652..9d5ed400d32 100644 --- a/core/src/test/java/org/testcontainers/images/ImagePullPolicyTest.java +++ b/core/src/test/java/org/testcontainers/images/ImagePullPolicyTest.java @@ -1,12 +1,6 @@ package org.testcontainers.images; -import com.github.dockerjava.api.DockerClient; -import com.github.dockerjava.api.async.ResultCallback; -import com.github.dockerjava.api.command.PullImageResultCallback; import com.github.dockerjava.api.exception.NotFoundException; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; import org.junit.ClassRule; import org.junit.Test; import org.mockito.Mockito; @@ -18,9 +12,6 @@ import org.testcontainers.containers.startupcheck.OneShotStartupCheckStrategy; import org.testcontainers.utility.DockerImageName; -import java.util.UUID; -import java.util.concurrent.TimeUnit; - import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; @@ -29,47 +20,13 @@ public class ImagePullPolicyTest { @ClassRule public static DockerRegistryContainer registry = new DockerRegistryContainer(); - private static DockerImageName imageName; - - @BeforeClass - public static void beforeClass() throws Exception { - String testRegistryAddress = registry.getEndpoint(); - String testImageName = testRegistryAddress + "/image-pull-policy-test"; - String tag = UUID.randomUUID().toString(); - imageName = DockerImageName.parse(testImageName).withTag(tag); - - DockerClient client = DockerClientFactory.instance().client(); - String dummySourceImage = "hello-world:latest"; - client.pullImageCmd(dummySourceImage).exec(new PullImageResultCallback()).awaitCompletion(); - - String dummyImageId = client.inspectImageCmd(dummySourceImage).exec().getId(); - - // push the image to the registry - client.tagImageCmd(dummyImageId, testImageName, tag).exec(); - - client.pushImageCmd(imageName.asCanonicalNameString()) - .exec(new ResultCallback.Adapter<>()) - .awaitCompletion(1, TimeUnit.MINUTES); - } - - @AfterClass - public static void afterClass() { - removeImage(); - } - - @Before - public void setUp() { - // Clean up local cache - removeImage(); - - LocalImagesCache.INSTANCE.cache.remove(imageName); - } + private final DockerImageName imageName = registry.createImage(); @Test public void pullsByDefault() { try ( GenericContainer container = new GenericContainer<>(imageName) - .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) + .withExposedPorts(8080) ) { container.start(); } @@ -79,7 +36,7 @@ public void pullsByDefault() { public void shouldAlwaysPull() { try ( GenericContainer container = new GenericContainer<>(imageName) - .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) + .withExposedPorts(8080) ) { container.start(); } @@ -88,7 +45,7 @@ public void shouldAlwaysPull() { try ( GenericContainer container = new GenericContainer<>(imageName) - .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) + .withExposedPorts(8080) ) { expectToFailWithNotFoundException(container); } @@ -99,7 +56,7 @@ public void shouldAlwaysPull() { .withImagePullPolicy(PullPolicy.alwaysPull()) // } ) { - container.withStartupCheckStrategy(new OneShotStartupCheckStrategy()); + container.withExposedPorts(8080); container.start(); } } @@ -117,7 +74,7 @@ protected boolean shouldPullCached(DockerImageName imageName, ImageData localIma }) // } ) { - container.withStartupCheckStrategy(new OneShotStartupCheckStrategy()); + container.withExposedPorts(8080); container.start(); } } @@ -133,7 +90,7 @@ protected boolean shouldPullCached(DockerImageName imageName, ImageData localIma try ( GenericContainer container = new GenericContainer<>(imageName) .withImagePullPolicy(policy) - .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) + .withExposedPorts(8080) ) { container.start(); @@ -169,7 +126,7 @@ private void expectToFailWithNotFoundException(GenericContainer container) { } } - private static void removeImage() { + private void removeImage() { try { DockerClientFactory.instance().client() .removeImageCmd(imageName.asCanonicalNameString()) diff --git a/core/src/test/java/org/testcontainers/utility/AuthenticatedImagePullTest.java b/core/src/test/java/org/testcontainers/utility/AuthenticatedImagePullTest.java index 412a945b204..a911675dfb3 100644 --- a/core/src/test/java/org/testcontainers/utility/AuthenticatedImagePullTest.java +++ b/core/src/test/java/org/testcontainers/utility/AuthenticatedImagePullTest.java @@ -1,37 +1,28 @@ package org.testcontainers.utility; -import com.github.dockerjava.api.DockerClient; -import com.github.dockerjava.api.async.ResultCallback; -import com.github.dockerjava.api.command.PullImageResultCallback; -import com.github.dockerjava.api.exception.NotFoundException; import com.github.dockerjava.api.model.AuthConfig; import org.intellij.lang.annotations.Language; import org.junit.AfterClass; -import org.junit.Before; import org.junit.BeforeClass; import org.junit.ClassRule; import org.junit.Test; import org.mockito.Mockito; -import org.testcontainers.DockerClientFactory; import org.testcontainers.DockerRegistryContainer; import org.testcontainers.containers.ContainerState; import org.testcontainers.containers.DockerComposeContainer; import org.testcontainers.containers.GenericContainer; -import org.testcontainers.images.LocalImagesCacheAccessor; import org.testcontainers.images.builder.ImageFromDockerfile; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.concurrent.TimeUnit; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.Mockito.when; import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; import static org.testcontainers.TestImages.DOCKER_REGISTRY_IMAGE; -import static org.testcontainers.TestImages.TINY_IMAGE; /** * This test checks the integration between Testcontainers and an authenticated registry, but uses @@ -57,20 +48,15 @@ public class AuthenticatedImagePullTest { })); private static RegistryAuthLocator originalAuthLocatorSingleton; - private static DockerClient client; - private static String testImageName; - private static RegistryAuthLocator mockAuthLocator; + private final DockerImageName testImageName = authenticatedRegistry.createImage(); @BeforeClass - public static void setUp() throws Exception { + public static void beforeClass() throws Exception { originalAuthLocatorSingleton = RegistryAuthLocator.instance(); - client = DockerClientFactory.instance().client(); String testRegistryAddress = authenticatedRegistry.getEndpoint(); - testImageName = testRegistryAddress + "/alpine"; - final DockerImageName expectedName = DockerImageName.parse(testImageName); final AuthConfig authConfig = new AuthConfig() .withUsername("testuser") .withPassword("notasecret") @@ -79,22 +65,8 @@ public static void setUp() throws Exception { // Replace the RegistryAuthLocator singleton with our mock, for the duration of this test final RegistryAuthLocator mockAuthLocator = Mockito.mock(RegistryAuthLocator.class); RegistryAuthLocator.setInstance(mockAuthLocator); - when(mockAuthLocator.lookupAuthConfig(eq(expectedName), any())) + when(mockAuthLocator.lookupAuthConfig(argThat(argument -> testRegistryAddress.equals(argument.getRegistry())), any())) .thenReturn(authConfig); - - // a push will use the auth locator for authentication, although that isn't the goal of this test - putImageInRegistry(); - } - - @Before - public void removeImageFromLocalDocker() { - // remove the image tag from local docker so that it must be pulled before use - try { - client.removeImageCmd(testImageName).withForce(true).exec(); - } catch (NotFoundException ignored) { - - } - LocalImagesCacheAccessor.clearCache(); } @AfterClass @@ -105,7 +77,7 @@ public static void tearDown() { @Test public void testThatAuthLocatorIsUsedForContainerCreation() { // actually start a container, which will require an authenticated pull - try (final GenericContainer container = new GenericContainer<>(DockerImageName.parse(testImageName)) + try (final GenericContainer container = new GenericContainer<>(testImageName) .withCommand("/bin/sh", "-c", "sleep 10")) { container.start(); @@ -117,7 +89,7 @@ public void testThatAuthLocatorIsUsedForContainerCreation() { public void testThatAuthLocatorIsUsedForDockerfileBuild() throws IOException { // Prepare a simple temporary Dockerfile which requires our custom private image Path tempFile = getLocalTempFile(".Dockerfile"); - String dockerFileContent = "FROM " + testImageName; + String dockerFileContent = "FROM " + testImageName.asCanonicalNameString(); Files.write(tempFile, dockerFileContent.getBytes()); // Start a container built from a derived image, which will require an authenticated pull @@ -141,7 +113,7 @@ public void testThatAuthLocatorIsUsedForDockerComposePull() throws IOException { "services:\n" + " privateservice:\n" + " command: /bin/sh -c 'sleep 60'\n" + - " image: " + testImageName; + " image: " + testImageName.asCanonicalNameString(); Files.write(tempFile, composeFileContent.getBytes()); // Start the docker compose project, which will require an authenticated pull @@ -168,24 +140,4 @@ private Path getLocalTempFile(String s) throws IOException { return tempFile; } - - private static void putImageInRegistry() throws InterruptedException { - // It doesn't matter which image we use for this test, but use one that's likely to have been pulled already - final String dummySourceImage = TINY_IMAGE.asCanonicalNameString(); - - client.pullImageCmd(dummySourceImage) - .exec(new PullImageResultCallback()) - .awaitCompletion(1, TimeUnit.MINUTES); - - final String id = client.inspectImageCmd(dummySourceImage) - .exec() - .getId(); - - // push the image to the registry - client.tagImageCmd(id, testImageName, "").exec(); - - client.pushImageCmd(testImageName) - .exec(new ResultCallback.Adapter<>()) - .awaitCompletion(1, TimeUnit.MINUTES); - } } From c0183f1d81ae2637d8ed70634ce643e3c75263b0 Mon Sep 17 00:00:00 2001 From: Andriy Redko Date: Mon, 16 May 2022 11:57:23 -0400 Subject: [PATCH 0140/3231] Allow insecure HTTPS/TLS connection for HttpWaitStrategy (#4951) Signed-off-by: Andriy Redko --- .../wait/strategy/HttpWaitStrategy.java | 63 ++++++++++++++++++- .../wait/strategy/HttpWaitStrategyTest.java | 19 ++++++ .../ElasticsearchContainerTest.java | 23 +++++++ 3 files changed, 104 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/testcontainers/containers/wait/strategy/HttpWaitStrategy.java b/core/src/main/java/org/testcontainers/containers/wait/strategy/HttpWaitStrategy.java index c4751725707..d5d08ce6fd0 100644 --- a/core/src/main/java/org/testcontainers/containers/wait/strategy/HttpWaitStrategy.java +++ b/core/src/main/java/org/testcontainers/containers/wait/strategy/HttpWaitStrategy.java @@ -12,15 +12,26 @@ import java.io.IOException; import java.io.InputStreamReader; import java.net.HttpURLConnection; +import java.net.MalformedURLException; import java.net.URI; import java.net.URL; import java.time.Duration; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.security.cert.X509Certificate; import java.util.HashSet; import java.util.Optional; import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.function.Predicate; +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.KeyManager; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; + import static org.rnorth.ducttape.unreliables.Unreliables.retryUntilSuccess; @Slf4j @@ -47,6 +58,7 @@ public class HttpWaitStrategy extends AbstractWaitStrategy { private Predicate statusCodePredicate = null; private Optional livenessPort = Optional.empty(); private Duration readTimeout = Duration.ofSeconds(1); + private boolean allowInsecure; /** * Waits for the given status code. @@ -112,6 +124,16 @@ public HttpWaitStrategy withMethod(String method) { return this; } + /** + * Indicates that HTTPS connection could use untrusted (self signed) certificate chains. + * + * @return this + */ + public HttpWaitStrategy allowInsecure() { + this.allowInsecure = true; + return this; + } + /** * Authenticate with HTTP Basic Authorization credentials. * @@ -206,7 +228,7 @@ protected void waitUntilReady() { retryUntilSuccess((int) startupTimeout.getSeconds(), TimeUnit.SECONDS, () -> { getRateLimiter().doWhenReady(() -> { try { - final HttpURLConnection connection = (HttpURLConnection) new URL(uri).openConnection(); + final HttpURLConnection connection = openConnection(uri); connection.setReadTimeout(Math.toIntExact(readTimeout.toMillis())); // authenticate @@ -267,6 +289,45 @@ protected void waitUntilReady() { } } + private HttpURLConnection openConnection(final String uri) throws IOException, MalformedURLException { + if (tlsEnabled) { + final HttpsURLConnection connection = (HttpsURLConnection)new URL(uri).openConnection(); + if (allowInsecure) { + // Create a trust manager that does not validate certificate chains + // and trust all certificates + final TrustManager[] trustAllCerts = new TrustManager[] { + new X509TrustManager() { + @Override + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[0]; + } + + @Override + public void checkClientTrusted(final X509Certificate[] certs, final String authType) { + } + + @Override + public void checkServerTrusted(final X509Certificate[] certs, final String authType) { + } + } + }; + + try { + // Create custom SSL context and set the "trust all certificates" trust manager + final SSLContext sc = SSLContext.getInstance("SSL"); + sc.init(new KeyManager[0], trustAllCerts, new SecureRandom()); + connection.setSSLSocketFactory(sc.getSocketFactory()); + } catch (final NoSuchAlgorithmException | KeyManagementException ex) { + throw new IOException("Unable to create custom SSL factory instance", ex); + } + } + + return connection; + } else { + return (HttpURLConnection) new URL(uri).openConnection(); + } + } + /** * Build the URI on which to check if the container is ready. * diff --git a/core/src/test/java/org/testcontainers/junit/wait/strategy/HttpWaitStrategyTest.java b/core/src/test/java/org/testcontainers/junit/wait/strategy/HttpWaitStrategyTest.java index c5a39a4538c..82207b4537e 100644 --- a/core/src/test/java/org/testcontainers/junit/wait/strategy/HttpWaitStrategyTest.java +++ b/core/src/test/java/org/testcontainers/junit/wait/strategy/HttpWaitStrategyTest.java @@ -57,6 +57,17 @@ public void testWaitUntilReadyWithSuccessWithCustomHeaders() { assertThat(logs, containsString("baz: boo")); } + /** + * Ensures that HTTPS requests made with the HttpWaitStrategy can skip the + * certificate validation chains (to support self-signed certificates for example). + */ + @Test + public void testWaitUntilReadyWithTlsAndAllowUnsecure() { + waitUntilReadyAndSucceed(startContainerWithCommand(createHttpsShellCommand("200 OK", GOOD_RESPONSE_BODY, 8080), + createHttpWaitStrategy(ready).usingTls().allowInsecure() + )); + } + /** * Expects that the WaitStrategy returns successfully after receiving an HTTP 401 response from the container. * This 401 response is checked with a lambda using {@link HttpWaitStrategy#forStatusCodeMatching(Predicate)} @@ -206,4 +217,12 @@ private String createShellCommand(String header, String responseBody, int port) "Content-Length: " + length + NEWLINE + "\";" + " echo \"" + responseBody + "\";} | nc -lp " + port + "; done"; } + + private String createHttpsShellCommand(String header, String responseBody, int port) { + int length = responseBody.getBytes().length; + return "apk add nmap-ncat; while true; do { echo -e \"HTTP/1.1 " + header + NEWLINE + + "Content-Type: text/html" + NEWLINE + + "Content-Length: " + length + NEWLINE + "\";" + + " echo \"" + responseBody + "\";} | ncat -lp " + port + " --ssl; done"; + } } diff --git a/modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java b/modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java index f8131268e61..5b6b5ce3194 100644 --- a/modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java +++ b/modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java @@ -19,6 +19,8 @@ import org.junit.After; import org.junit.Test; import org.testcontainers.DockerClientFactory; +import org.testcontainers.containers.wait.strategy.HttpWaitStrategy; +import org.testcontainers.containers.wait.strategy.Wait; import org.testcontainers.images.RemoteDockerImage; import org.testcontainers.utility.DockerImageName; import org.testcontainers.utility.MountableFile; @@ -282,6 +284,27 @@ public void testElasticsearch8SecureByDefaultCustomCaCertFails() throws Exceptio } } + @Test + public void testElasticsearch8SecureByDefaultHttpWaitStrategy() throws Exception { + final HttpWaitStrategy httpsWaitStrategy = Wait.forHttps("/") + .forPort(9200) + .forStatusCode(200) + .withBasicCredentials(ELASTICSEARCH_USERNAME, ELASTICSEARCH_PASSWORD) + // trusting self-signed certificate + .allowInsecure(); + + try (ElasticsearchContainer container = new ElasticsearchContainer("docker.elastic.co/elasticsearch/elasticsearch:8.1.2") + .waitingFor(httpsWaitStrategy)) { + + // Start the container. This step might take some time... + container.start(); + + Response response = getClusterHealth(container); + assertThat(response.getStatusLine().getStatusCode(), is(200)); + assertThat(EntityUtils.toString(response.getEntity()), containsString("cluster_name")); + } + } + @Test public void testElasticsearch8SecureByDefaultFailsSilentlyOnLatestImages() throws Exception { // this test exists for custom images by users that use the `latest` tag From 98b60fbf87f4f65f96651bee919c8f8027fe0cc5 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Mon, 16 May 2022 13:27:39 -0400 Subject: [PATCH 0141/3231] Use `GenericContainer` in `RyukResourceReaper` (#5358) * Experiment: Use `GenericContainer` in `RyukResourceReaper` * reduce global `DockerClient` usage * revert indentation * Avoid bind check * Fix `japicmp` * Add `ResourceReaperTest` --- core/build.gradle | 10 +- .../ComposeServiceWaitStrategyTarget.java | 8 +- .../testcontainers/containers/Container.java | 6 +- .../containers/ContainerState.java | 16 ++- .../containers/DockerComposeContainer.java | 9 +- .../containers/ExecInContainerPattern.java | 36 ++++- .../containers/GenericContainer.java | 4 +- .../IsRunningStartupCheckStrategy.java | 17 ++- .../startupcheck/StartupCheckStrategy.java | 10 ++ .../InternalCommandPortListeningCheck.java | 2 +- .../wait/strategy/LogMessageWaitStrategy.java | 2 +- .../utility/ResourceReaper.java | 12 ++ .../testcontainers/utility/RyukContainer.java | 27 ++++ .../utility/RyukResourceReaper.java | 135 ++++-------------- .../utility/ResourceReaperTest.java | 109 ++++++++++++++ 15 files changed, 257 insertions(+), 146 deletions(-) create mode 100644 core/src/main/java/org/testcontainers/utility/RyukContainer.java create mode 100644 core/src/test/java/org/testcontainers/utility/ResourceReaperTest.java diff --git a/core/build.gradle b/core/build.gradle index b519673de4b..1988063489e 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -43,12 +43,8 @@ tasks.japicmp { classExcludes = [] methodExcludes = [ - "org.testcontainers.utility.ResourceReaper#start(java.lang.String,com.github.dockerjava.api.DockerClient)", - "org.testcontainers.utility.ResourceReaper#start(com.github.dockerjava.api.DockerClient)", - "org.testcontainers.utility.ResourceReaper#registerNetworkForCleanup(java.lang.String)", - "org.testcontainers.utility.ResourceReaper#removeNetworks(java.lang.String)", - "org.testcontainers.images.builder.Transferable#of(java.lang.String)", - "org.testcontainers.images.builder.Transferable#updateChecksum(java.util.zip.Checksum)" + "org.testcontainers.containers.Container#getDockerClient()", + "org.testcontainers.containers.ContainerState#getDockerClient()", ] fieldExcludes = [] @@ -62,7 +58,7 @@ configurations.all { } dependencies { - baseline 'org.testcontainers:testcontainers:1.16.3', { + baseline 'org.testcontainers:testcontainers:1.17.1', { exclude group: "*", module: "*" } diff --git a/core/src/main/java/org/testcontainers/containers/ComposeServiceWaitStrategyTarget.java b/core/src/main/java/org/testcontainers/containers/ComposeServiceWaitStrategyTarget.java index 2fb953b4aee..fbb2a7cee6e 100644 --- a/core/src/main/java/org/testcontainers/containers/ComposeServiceWaitStrategyTarget.java +++ b/core/src/main/java/org/testcontainers/containers/ComposeServiceWaitStrategyTarget.java @@ -1,11 +1,11 @@ package org.testcontainers.containers; +import com.github.dockerjava.api.DockerClient; import com.github.dockerjava.api.command.InspectContainerResponse; import com.github.dockerjava.api.model.Container; import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.NonNull; -import org.testcontainers.DockerClientFactory; import org.testcontainers.containers.wait.strategy.WaitStrategyTarget; import java.util.ArrayList; @@ -22,13 +22,15 @@ class ComposeServiceWaitStrategyTarget implements WaitStrategyTarget { private final Container container; private final GenericContainer proxyContainer; + private final DockerClient dockerClient; @NonNull private Map mappedPorts; @Getter(lazy=true) - private final InspectContainerResponse containerInfo = DockerClientFactory.instance().client().inspectContainerCmd(getContainerId()).exec(); + private final InspectContainerResponse containerInfo = dockerClient.inspectContainerCmd(getContainerId()).exec(); - ComposeServiceWaitStrategyTarget(Container container, GenericContainer proxyContainer, + ComposeServiceWaitStrategyTarget(DockerClient dockerClient, Container container, GenericContainer proxyContainer, @NonNull Map mappedPorts) { + this.dockerClient = dockerClient; this.container = container; this.proxyContainer = proxyContainer; this.mappedPorts = new HashMap<>(mappedPorts); diff --git a/core/src/main/java/org/testcontainers/containers/Container.java b/core/src/main/java/org/testcontainers/containers/Container.java index 8fd53fc5a0e..88fe4802945 100644 --- a/core/src/main/java/org/testcontainers/containers/Container.java +++ b/core/src/main/java/org/testcontainers/containers/Container.java @@ -389,7 +389,7 @@ default SELF withClasspathResourceMapping(final String resourcePath, final Strin * @param consumer consumer that the frames should be sent to */ default void followOutput(Consumer consumer) { - LogUtils.followOutput(DockerClientFactory.instance().client(), getContainerId(), consumer); + LogUtils.followOutput(getDockerClient(), getContainerId(), consumer); } /** @@ -400,7 +400,7 @@ default void followOutput(Consumer consumer) { * @param types types that should be followed (one or both of STDOUT, STDERR) */ default void followOutput(Consumer consumer, OutputFrame.OutputType... types) { - LogUtils.followOutput(DockerClientFactory.instance().client(), getContainerId(), consumer, types); + LogUtils.followOutput(getDockerClient(), getContainerId(), consumer, types); } @@ -439,8 +439,6 @@ default void followOutput(Consumer consumer, OutputFrame.OutputType @Deprecated Map getLinkedContainers(); - DockerClient getDockerClient(); - void setExposedPorts(List exposedPorts); void setPortBindings(List portBindings); diff --git a/core/src/main/java/org/testcontainers/containers/ContainerState.java b/core/src/main/java/org/testcontainers/containers/ContainerState.java index ef2c19a06c9..51eb4daddde 100644 --- a/core/src/main/java/org/testcontainers/containers/ContainerState.java +++ b/core/src/main/java/org/testcontainers/containers/ContainerState.java @@ -52,6 +52,10 @@ default String getContainerIpAddress() { return getHost(); } + default DockerClient getDockerClient() { + return DockerClientFactory.lazyClient(); + } + /** * Get the host that this container may be reached on (may not be the local machine). * @@ -115,7 +119,7 @@ default boolean isHealthy() { } default InspectContainerResponse getCurrentContainerInfo() { - return DockerClientFactory.instance().client().inspectContainerCmd(getContainerId()).exec(); + return getDockerClient().inspectContainerCmd(getContainerId()).exec(); } /** @@ -195,7 +199,7 @@ default List getBoundPortNumbers() { * @return all log output from the container from start until the current instant (both stdout and stderr) */ default String getLogs() { - return LogUtils.getOutput(DockerClientFactory.instance().client(), getContainerId()); + return LogUtils.getOutput(getDockerClient(), getContainerId()); } /** @@ -203,7 +207,7 @@ default String getLogs() { * @return all log output from the container from start until the current instant */ default String getLogs(OutputFrame.OutputType... types) { - return LogUtils.getOutput(DockerClientFactory.instance().client(), getContainerId(), types); + return LogUtils.getOutput(getDockerClient(), getContainerId(), types); } /** @@ -234,7 +238,7 @@ default Container.ExecResult execInContainer(String... command) throws Unsupport * @see ExecInContainerPattern#execInContainer(com.github.dockerjava.api.command.InspectContainerResponse, Charset, String...) */ default Container.ExecResult execInContainer(Charset outputCharset, String... command) throws UnsupportedOperationException, IOException, InterruptedException { - return ExecInContainerPattern.execInContainer(getContainerInfo(), outputCharset, command); + return ExecInContainerPattern.execInContainer(getDockerClient(), getContainerInfo(), outputCharset, command); } /** @@ -279,7 +283,7 @@ default void copyFileToContainer(Transferable transferable, String containerPath transferable.transferTo(tarArchive, containerPath); tarArchive.finish(); - DockerClientFactory.instance().client() + getDockerClient() .copyArchiveToContainerCmd(getContainerId()) .withTarInputStream(new ByteArrayInputStream(byteArrayOutputStream.toByteArray())) .withRemotePath("/") @@ -316,7 +320,7 @@ default T copyFileFromContainer(String containerPath, ThrowingFunction composeFiles) { - this.composeFiles = composeFiles; this.dockerComposeFiles = new DockerComposeFiles(composeFiles); @@ -132,7 +131,7 @@ public DockerComposeContainer(String identifier, List composeFiles) { this.identifier = identifier; this.project = randomProjectId(); - this.dockerClient = DockerClientFactory.instance().client(); + this.dockerClient = DockerClientFactory.lazyClient(); } @Override @@ -269,7 +268,7 @@ private void waitUntilServiceStarted() { private void createServiceInstance(Container container) { String serviceName = getServiceNameFromContainer(container); - final ComposeServiceWaitStrategyTarget containerInstance = new ComposeServiceWaitStrategyTarget(container, + final ComposeServiceWaitStrategyTarget containerInstance = new ComposeServiceWaitStrategyTarget(dockerClient, container, ambassadorContainer, ambassadorPortMappings.getOrDefault(serviceName, new HashMap<>())); String containerId = containerInstance.getContainerId(); @@ -559,7 +558,7 @@ public Optional getContainerByServiceName(String serviceName) { } private void followLogs(String containerId, Consumer consumer) { - LogUtils.followOutput(DockerClientFactory.instance().client(), containerId, consumer); + LogUtils.followOutput(dockerClient, containerId, consumer); } private SELF self() { @@ -659,7 +658,7 @@ public void invoke() { AuditLogger.doComposeLog(this.getCommandParts(), this.getEnv()); - final Integer exitCode = this.dockerClient.inspectContainerCmd(getContainerId()) + final Integer exitCode = getDockerClient().inspectContainerCmd(getContainerId()) .exec() .getState() .getExitCode(); diff --git a/core/src/main/java/org/testcontainers/containers/ExecInContainerPattern.java b/core/src/main/java/org/testcontainers/containers/ExecInContainerPattern.java index 9cd68d48ffb..0858476880f 100644 --- a/core/src/main/java/org/testcontainers/containers/ExecInContainerPattern.java +++ b/core/src/main/java/org/testcontainers/containers/ExecInContainerPattern.java @@ -14,6 +14,7 @@ import java.io.IOException; import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; /** * Provides utility methods for executing commands in containers @@ -22,17 +23,41 @@ @Slf4j public class ExecInContainerPattern { + + /** + * + * @deprecated use {@link #execInContainer(DockerClient, InspectContainerResponse, String...)} + */ + @Deprecated + public Container.ExecResult execInContainer(InspectContainerResponse containerInfo, String... command) + throws UnsupportedOperationException, IOException, InterruptedException { + DockerClient dockerClient = DockerClientFactory.instance().client(); + return execInContainer(dockerClient, containerInfo, command); + } + + /** + * + * @deprecated use {@link #execInContainer(DockerClient, InspectContainerResponse, Charset, String...)} + */ + @Deprecated + public Container.ExecResult execInContainer(InspectContainerResponse containerInfo, Charset outputCharset, String... command) + throws UnsupportedOperationException, IOException, InterruptedException { + DockerClient dockerClient = DockerClientFactory.instance().client(); + return execInContainer(dockerClient, containerInfo, outputCharset, command); + } + /** * Run a command inside a running container, as though using "docker exec", and interpreting * the output as UTF8. *

+ * @param dockerClient the {@link DockerClient} * @param containerInfo the container info * @param command the command to execute - * @see #execInContainer(InspectContainerResponse, Charset, String...) + * @see #execInContainer(DockerClient, InspectContainerResponse, Charset, String...) */ - public Container.ExecResult execInContainer(InspectContainerResponse containerInfo, String... command) + public Container.ExecResult execInContainer(DockerClient dockerClient, InspectContainerResponse containerInfo, String... command) throws UnsupportedOperationException, IOException, InterruptedException { - return execInContainer(containerInfo, Charset.forName("UTF-8"), command); + return execInContainer(dockerClient, containerInfo, StandardCharsets.UTF_8, command); } /** @@ -40,6 +65,7 @@ public Container.ExecResult execInContainer(InspectContainerResponse containerIn *

* This functionality is not available on a docker daemon running the older "lxc" execution driver. At * the time of writing, CircleCI was using this driver. + * @param dockerClient the {@link DockerClient} * @param containerInfo the container info * @param outputCharset the character set used to interpret the output. * @param command the parts of the command to run @@ -48,7 +74,7 @@ public Container.ExecResult execInContainer(InspectContainerResponse containerIn * @throws InterruptedException if the thread waiting for the response is interrupted * @throws UnsupportedOperationException if the docker daemon you're connecting to doesn't support "exec". */ - public Container.ExecResult execInContainer(InspectContainerResponse containerInfo, Charset outputCharset, String... command) + public Container.ExecResult execInContainer(DockerClient dockerClient, InspectContainerResponse containerInfo, Charset outputCharset, String... command) throws UnsupportedOperationException, IOException, InterruptedException { if (!TestEnvironment.dockerExecutionDriverSupportsExec()) { // at time of writing, this is the expected result in CircleCI. @@ -64,8 +90,6 @@ public Container.ExecResult execInContainer(InspectContainerResponse containerIn String containerId = containerInfo.getId(); String containerName = containerInfo.getName(); - DockerClient dockerClient = DockerClientFactory.instance().client(); - log.debug("{}: Running \"exec\" command: {}", containerName, String.join(" ", command)); final ExecCreateCmdResponse execCreateCmdResponse = dockerClient.execCreateCmd(containerId) .withAttachStdout(true).withAttachStderr(true).withCmd(command).exec(); diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index d47bc6cfc5e..4e5e682738d 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -416,7 +416,7 @@ private void tryStart(Instant startedAt) { if (!reusable) { //noinspection deprecation - createCommand.getLabels().putAll(ResourceReaper.instance().getLabels()); + createCommand = ResourceReaper.instance().register(this, createCommand); } if (!reused) { @@ -469,7 +469,7 @@ private void tryStart(Instant startedAt) { containerIsStarting(containerInfo, reused); // Wait until the container has reached the desired running state - if (!this.startupCheckStrategy.waitUntilStartupSuccessful(dockerClient, containerId)) { + if (!this.startupCheckStrategy.waitUntilStartupSuccessful(this)) { // Bail out, don't wait for the port to start listening. // (Exception thrown here will be caught below and wrapped) throw new IllegalStateException("Container did not start correctly."); diff --git a/core/src/main/java/org/testcontainers/containers/startupcheck/IsRunningStartupCheckStrategy.java b/core/src/main/java/org/testcontainers/containers/startupcheck/IsRunningStartupCheckStrategy.java index 6a296e0329c..b4b20aad897 100644 --- a/core/src/main/java/org/testcontainers/containers/startupcheck/IsRunningStartupCheckStrategy.java +++ b/core/src/main/java/org/testcontainers/containers/startupcheck/IsRunningStartupCheckStrategy.java @@ -2,6 +2,7 @@ import com.github.dockerjava.api.DockerClient; import com.github.dockerjava.api.command.InspectContainerResponse; +import org.testcontainers.containers.GenericContainer; import org.testcontainers.utility.DockerStatus; /** @@ -10,10 +11,24 @@ */ public class IsRunningStartupCheckStrategy extends StartupCheckStrategy { + @SuppressWarnings("deprecation") + @Override + public boolean waitUntilStartupSuccessful(GenericContainer container) { + // Optimization: container already has the initial "after start" state, check it first + if (checkState(container.getContainerInfo().getState()) == StartupStatus.SUCCESSFUL) { + return true; + } + return super.waitUntilStartupSuccessful(container); + } + @Override public StartupStatus checkStartupState(DockerClient dockerClient, String containerId) { InspectContainerResponse.ContainerState state = getCurrentState(dockerClient, containerId); - if (state.getRunning()) { + return checkState(state); + } + + private StartupStatus checkState(InspectContainerResponse.ContainerState state) { + if (Boolean.TRUE.equals(state.getRunning())) { return StartupStatus.SUCCESSFUL; } else if (!DockerStatus.isContainerExitCodeSuccess(state)) { return StartupStatus.FAILED; diff --git a/core/src/main/java/org/testcontainers/containers/startupcheck/StartupCheckStrategy.java b/core/src/main/java/org/testcontainers/containers/startupcheck/StartupCheckStrategy.java index 1425123473b..d5931454212 100644 --- a/core/src/main/java/org/testcontainers/containers/startupcheck/StartupCheckStrategy.java +++ b/core/src/main/java/org/testcontainers/containers/startupcheck/StartupCheckStrategy.java @@ -5,6 +5,7 @@ import org.rnorth.ducttape.ratelimits.RateLimiter; import org.rnorth.ducttape.ratelimits.RateLimiterBuilder; import org.rnorth.ducttape.unreliables.Unreliables; +import org.testcontainers.containers.GenericContainer; import java.time.Duration; import java.util.concurrent.TimeUnit; @@ -30,6 +31,15 @@ public SELF withTimeout(Duration timeout) { return (SELF) this; } + /** + * + * @deprecated internal API + */ + @Deprecated + public boolean waitUntilStartupSuccessful(GenericContainer container) { + return waitUntilStartupSuccessful(container.getDockerClient(), container.getContainerId()); + } + public boolean waitUntilStartupSuccessful(DockerClient dockerClient, String containerId) { final Boolean[] startedOK = {null}; Unreliables.retryUntilTrue((int) timeout.toMillis(), TimeUnit.MILLISECONDS, () -> { diff --git a/core/src/main/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheck.java b/core/src/main/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheck.java index 379840a9d7e..1eba8acbbd1 100644 --- a/core/src/main/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheck.java +++ b/core/src/main/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheck.java @@ -40,7 +40,7 @@ public Boolean call() { Instant before = Instant.now(); try { - ExecResult result = ExecInContainerPattern.execInContainer(waitStrategyTarget.getContainerInfo(), "/bin/sh", "-c", command.toString()); + ExecResult result = ExecInContainerPattern.execInContainer(waitStrategyTarget.getDockerClient(), waitStrategyTarget.getContainerInfo(), "/bin/sh", "-c", command.toString()); log.trace("Check for {} took {}. Result code '{}', stdout message: '{}'", internalPorts, Duration.between(before, Instant.now()), result.getExitCode(), result.getStdout()); int exitCode = result.getExitCode(); if (exitCode != 0 && exitCode != 1) { diff --git a/core/src/main/java/org/testcontainers/containers/wait/strategy/LogMessageWaitStrategy.java b/core/src/main/java/org/testcontainers/containers/wait/strategy/LogMessageWaitStrategy.java index 9f7a0078f00..65370e51bfa 100644 --- a/core/src/main/java/org/testcontainers/containers/wait/strategy/LogMessageWaitStrategy.java +++ b/core/src/main/java/org/testcontainers/containers/wait/strategy/LogMessageWaitStrategy.java @@ -27,7 +27,7 @@ public class LogMessageWaitStrategy extends AbstractWaitStrategy { protected void waitUntilReady() { WaitingConsumer waitingConsumer = new WaitingConsumer(); - LogContainerCmd cmd = DockerClientFactory.instance().client().logContainerCmd(waitStrategyTarget.getContainerId()) + LogContainerCmd cmd = waitStrategyTarget.getDockerClient().logContainerCmd(waitStrategyTarget.getContainerId()) .withFollowStream(true) .withSince(0) .withStdOut(true) diff --git a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java index 44f9456ce4c..d916579db35 100644 --- a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java +++ b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java @@ -1,6 +1,7 @@ package org.testcontainers.utility; import com.github.dockerjava.api.DockerClient; +import com.github.dockerjava.api.command.CreateContainerCmd; import com.github.dockerjava.api.command.InspectContainerResponse; import com.github.dockerjava.api.exception.NotFoundException; import com.github.dockerjava.api.model.Network; @@ -11,6 +12,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.testcontainers.DockerClientFactory; +import org.testcontainers.containers.GenericContainer; import java.io.BufferedReader; import java.io.IOException; @@ -307,6 +309,16 @@ public Map getLabels() { return MARKER_LABELS; } + /** + * + * @deprecated internal API + */ + @Deprecated + public CreateContainerCmd register(GenericContainer container, CreateContainerCmd cmd) { + cmd.getLabels().putAll(getLabels()); + return cmd; + } + /** * @deprecated internal API */ diff --git a/core/src/main/java/org/testcontainers/utility/RyukContainer.java b/core/src/main/java/org/testcontainers/utility/RyukContainer.java new file mode 100644 index 00000000000..1e18c14157a --- /dev/null +++ b/core/src/main/java/org/testcontainers/utility/RyukContainer.java @@ -0,0 +1,27 @@ +package org.testcontainers.utility; + +import com.github.dockerjava.api.model.Bind; +import com.github.dockerjava.api.model.Volume; +import org.testcontainers.DockerClientFactory; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.wait.strategy.Wait; + +class RyukContainer extends GenericContainer { + + RyukContainer() { + super("testcontainers/ryuk:0.3.3"); + + withExposedPorts(8080); + withCreateContainerCmdModifier(cmd -> { + cmd.withName("testcontainers-ryuk-" + DockerClientFactory.SESSION_ID); + cmd.withHostConfig( + cmd.getHostConfig() + .withAutoRemove(true) + .withPrivileged(TestcontainersConfiguration.getInstance().isRyukPrivileged()) + .withBinds(new Bind(DockerClientFactory.instance().getRemoteDockerUnixSocketPath(), new Volume("/var/run/docker.sock"))) + ); + }); + + waitingFor(Wait.forLogMessage(".*Started.*", 1)); + } +} diff --git a/core/src/main/java/org/testcontainers/utility/RyukResourceReaper.java b/core/src/main/java/org/testcontainers/utility/RyukResourceReaper.java index 9efbf5ed972..c2e2cf4ef97 100644 --- a/core/src/main/java/org/testcontainers/utility/RyukResourceReaper.java +++ b/core/src/main/java/org/testcontainers/utility/RyukResourceReaper.java @@ -1,40 +1,21 @@ package org.testcontainers.utility; -import com.github.dockerjava.api.DockerClient; -import com.github.dockerjava.api.async.ResultCallback; -import com.github.dockerjava.api.command.InspectContainerResponse; -import com.github.dockerjava.api.model.Bind; -import com.github.dockerjava.api.model.ExposedPort; -import com.github.dockerjava.api.model.Frame; -import com.github.dockerjava.api.model.HostConfig; -import com.github.dockerjava.api.model.PortBinding; -import com.github.dockerjava.api.model.Ports; -import com.github.dockerjava.api.model.Volume; -import lombok.Getter; +import com.github.dockerjava.api.command.CreateContainerCmd; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.rnorth.ducttape.ratelimits.RateLimiter; import org.rnorth.ducttape.ratelimits.RateLimiterBuilder; import org.testcontainers.DockerClientFactory; -import org.testcontainers.containers.ContainerState; -import org.testcontainers.images.RemoteDockerImage; +import org.testcontainers.containers.GenericContainer; import java.io.IOException; import java.net.InetSocketAddress; import java.net.Socket; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import static org.awaitility.Awaitility.await; /** * Ryuk-based {@link ResourceReaper} implementation. @@ -54,8 +35,11 @@ public class RyukResourceReaper extends ResourceReaper { private final AtomicBoolean started = new AtomicBoolean(false); - @Getter - private String containerId = null; + private final RyukContainer ryukContainer = new RyukContainer(); + + public String getContainerId() { + return ryukContainer.getContainerId(); + } @Override public void init() { @@ -80,91 +64,29 @@ public Map getLabels() { return super.getLabels(); } + @Override + public CreateContainerCmd register(GenericContainer container, CreateContainerCmd cmd) { + if (container == ryukContainer) { + // Do not register Ryuk container to avoid self-pruning + return cmd; + } + + maybeStart(); + return super.register(container, cmd); + } + @SneakyThrows(InterruptedException.class) private synchronized void maybeStart() { if (!started.compareAndSet(false, true)) { return; } - DockerClient client = DockerClientFactory.lazyClient(); - RemoteDockerImage ryukImage = new RemoteDockerImage(DockerImageName.parse("testcontainers/ryuk:0.3.3")); - - List binds = new ArrayList<>(); - binds.add(new Bind(DockerClientFactory.instance().getRemoteDockerUnixSocketPath(), new Volume("/var/run/docker.sock"))); - - ExposedPort ryukExposedPort = ExposedPort.tcp(8080); - containerId = client.createContainerCmd(ryukImage.get()) - .withHostConfig( - new HostConfig() - .withAutoRemove(true) - .withPortBindings(new PortBinding(Ports.Binding.empty(), ryukExposedPort)) - ) - .withExposedPorts(ryukExposedPort) - .withName("testcontainers-ryuk-" + DockerClientFactory.SESSION_ID) - .withLabels(Collections.singletonMap(DockerClientFactory.TESTCONTAINERS_LABEL, "true")) - .withBinds(binds) - .withPrivileged(TestcontainersConfiguration.getInstance().isRyukPrivileged()) - .exec() - .getId(); - - client.startContainerCmd(containerId).exec(); - - StringBuilder ryukLog = new StringBuilder(); - - ResultCallback.Adapter logCallback = client.logContainerCmd(containerId) - .withSince(0) - .withFollowStream(true) - .withStdOut(true) - .withStdErr(true) - .exec(new ResultCallback.Adapter() { - @Override - public void onNext(Frame frame) { - ryukLog.append(new String(frame.getPayload(), StandardCharsets.UTF_8)); - } - }); - - InspectContainerResponse inspectedContainer; - try { - // inspect container response might initially not contain the mapped port - inspectedContainer = await() - .atMost(5, TimeUnit.SECONDS) - .pollInterval(DynamicPollInterval.ofMillis(50)) - .pollInSameThread() - .until( - () -> client.inspectContainerCmd(containerId).exec(), - inspectContainerResponse -> { - return inspectContainerResponse - .getNetworkSettings() - .getPorts() - .getBindings() - .values() - .stream() - .anyMatch(Objects::nonNull); - } - ); - } catch (Exception e) { - log.warn("Ryuk container cannot be inspected and probably had a problem starting. Ryuk's logs:\n{}", ryukLog); - throw new IllegalStateException("Ryuk failed to start", e); - } - - ContainerState containerState = new ContainerState() { - - @Override - public List getExposedPorts() { - return Stream.of(getContainerInfo().getConfig().getExposedPorts()) - .map(ExposedPort::getPort) - .collect(Collectors.toList()); - } - @Override - public InspectContainerResponse getContainerInfo() { - return inspectedContainer; - } - }; + ryukContainer.start(); CountDownLatch ryukScheduledLatch = new CountDownLatch(1); - String host = containerState.getHost(); - Integer ryukPort = containerState.getFirstMappedPort(); + String host = ryukContainer.getHost(); + Integer ryukPort = ryukContainer.getFirstMappedPort(); Thread kiraThread = new Thread( DockerClientFactory.TESTCONTAINERS_THREAD_GROUP, () -> { @@ -207,17 +129,10 @@ public InspectContainerResponse getContainerInfo() { ); kiraThread.setDaemon(true); kiraThread.start(); - try { - // We need to wait before we can start any containers to make sure that we delete them - if (!ryukScheduledLatch.await(TestcontainersConfiguration.getInstance().getRyukTimeout(), TimeUnit.SECONDS)) { - log.error("Timed out waiting for Ryuk container to start. Ryuk's logs:\n{}", ryukLog); - throw new IllegalStateException(String.format("Could not connect to Ryuk at %s:%s", host, ryukPort)); - } - } finally { - try { - logCallback.close(); - } catch (IOException ignored) { - } + // We need to wait before we can start any containers to make sure that we delete them + if (!ryukScheduledLatch.await(TestcontainersConfiguration.getInstance().getRyukTimeout(), TimeUnit.SECONDS)) { + log.error("Timed out waiting for Ryuk container to start. Ryuk's logs:\n{}", ryukContainer.getLogs()); + throw new IllegalStateException(String.format("Could not connect to Ryuk at %s:%s", host, ryukPort)); } } } diff --git a/core/src/test/java/org/testcontainers/utility/ResourceReaperTest.java b/core/src/test/java/org/testcontainers/utility/ResourceReaperTest.java new file mode 100644 index 00000000000..214b465c297 --- /dev/null +++ b/core/src/test/java/org/testcontainers/utility/ResourceReaperTest.java @@ -0,0 +1,109 @@ +package org.testcontainers.utility; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.dockerjava.api.DockerClient; +import lombok.SneakyThrows; +import org.awaitility.Awaitility; +import org.awaitility.core.ConditionFactory; +import org.junit.Test; +import org.testcontainers.DockerClientFactory; +import org.testcontainers.containers.GenericContainer; +import org.zeroturnaround.exec.ProcessExecutor; +import org.zeroturnaround.exec.ProcessResult; + +import java.io.File; +import java.time.Duration; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.assertEquals; + +public class ResourceReaperTest { + + @Test + public void shouldCleanupWithRyuk() { + Map labels = runProcess(processExecutor -> {}); + + assertCleanup(labels); + } + + @Test + public void shouldCleanupWithJVM() { + Map labels = runProcess(processExecutor -> { + processExecutor.environment("TESTCONTAINERS_RYUK_DISABLED", "true"); + }); + + assertCleanup(labels); + } + + private void assertCleanup(Map labels) { + DockerClient client = DockerClientFactory.instance().client(); + ConditionFactory awaitFactory = Awaitility.await().atMost(Duration.ofMinutes(1)).pollInterval(Duration.ofSeconds(1)); + + List labelValues = labels.entrySet().stream() + .map(it -> it.getKey() + "=" + it.getValue()) + .collect(Collectors.toList()); + + awaitFactory.untilAsserted(() -> { + assertThat(client.listContainersCmd().withFilter("label", labelValues).withShowAll(true).exec()) + .isEmpty(); + }); + + awaitFactory.untilAsserted(() -> { + assertThat(client.listNetworksCmd().withFilter("label", labelValues).exec()) + .isEmpty(); + }); + + awaitFactory.untilAsserted(() -> { + assertThat(client.listVolumesCmd().withFilter("label", labelValues).exec().getVolumes()) + .isEmpty(); + }); + } + + @SneakyThrows + private Map runProcess(Consumer processExecutorConsumer) { + ProcessExecutor processExecutor = new ProcessExecutor( + new File(System.getProperty("java.home")).toPath().resolve("bin").resolve("java").toString(), + "-ea", + "-classpath", + System.getProperty("java.class.path"), + SimpleUsage.class.getName() + ); + processExecutor.readOutput(true); + processExecutor.redirectOutput(System.out); + processExecutor.redirectError(System.err); + processExecutorConsumer.accept(processExecutor); + + ProcessResult result = processExecutor.execute(); + assertEquals(0, result.getExitValue()); + + String labelsJson = Stream.of(result.outputUTF8().split("\n")) + .filter(it -> it.startsWith(SimpleUsage.LABELS_MARKER)) + .map(it -> it.substring(SimpleUsage.LABELS_MARKER.length())) + .findFirst() + .get(); + + return new ObjectMapper().readValue(labelsJson, Map.class); + } + + public static class SimpleUsage { + + static final String LABELS_MARKER = "LABELS:"; + + @SneakyThrows + @SuppressWarnings("deprecation") + public static void main(String[] args) { + System.out.println(LABELS_MARKER + new ObjectMapper().writeValueAsString(ResourceReaper.instance().getLabels())); + + GenericContainer container = new GenericContainer<>("testcontainers/helloworld:1.1.0") + .withNetwork(org.testcontainers.containers.Network.newNetwork()) + .withExposedPorts(8080); + + container.start(); + } + } +} From b517804acfdfe0c59f484054e2b66a37c057dd5d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 May 2022 21:07:23 +0000 Subject: [PATCH 0142/3231] Bump cucumber-junit from 7.2.3 to 7.3.4 in /examples Bumps [cucumber-junit](https://github.com/cucumber/cucumber-jvm) from 7.2.3 to 7.3.4. - [Release notes](https://github.com/cucumber/cucumber-jvm/releases) - [Changelog](https://github.com/cucumber/cucumber-jvm/blob/main/CHANGELOG.md) - [Commits](https://github.com/cucumber/cucumber-jvm/compare/v7.2.3...v7.3.4) --- updated-dependencies: - dependency-name: io.cucumber:cucumber-junit dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- examples/cucumber/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cucumber/build.gradle b/examples/cucumber/build.gradle index 78d042b95fd..f23a2d9b7af 100644 --- a/examples/cucumber/build.gradle +++ b/examples/cucumber/build.gradle @@ -11,6 +11,6 @@ dependencies { implementation 'org.seleniumhq.selenium:selenium-firefox-driver:3.141.59' implementation 'org.seleniumhq.selenium:selenium-chrome-driver:3.141.59' testImplementation 'io.cucumber:cucumber-java:7.2.3' - testImplementation 'io.cucumber:cucumber-junit:7.2.3' + testImplementation 'io.cucumber:cucumber-junit:7.3.4' testImplementation 'org.testcontainers:selenium' } From b3143fd8009ef82a4ebd9206b73e21da97e868de Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 May 2022 21:07:29 +0000 Subject: [PATCH 0143/3231] Bump r2dbc-mssql in /modules/mssqlserver Bumps [r2dbc-mssql](https://github.com/r2dbc/r2dbc-mssql) from 0.8.7.RELEASE to 0.9.0.RELEASE. - [Release notes](https://github.com/r2dbc/r2dbc-mssql/releases) - [Changelog](https://github.com/r2dbc/r2dbc-mssql/blob/main/CHANGELOG) - [Commits](https://github.com/r2dbc/r2dbc-mssql/compare/v0.8.7.RELEASE...v0.9.0.RELEASE) --- updated-dependencies: - dependency-name: io.r2dbc:r2dbc-mssql dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/mssqlserver/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mssqlserver/build.gradle b/modules/mssqlserver/build.gradle index dfa15ed1c2a..e7a654e12f1 100644 --- a/modules/mssqlserver/build.gradle +++ b/modules/mssqlserver/build.gradle @@ -7,7 +7,7 @@ dependencies { api project(':jdbc') compileOnly project(':r2dbc') - compileOnly 'io.r2dbc:r2dbc-mssql:0.8.7.RELEASE' + compileOnly 'io.r2dbc:r2dbc-mssql:0.9.0.RELEASE' testImplementation project(':jdbc-test') testImplementation 'com.microsoft.sqlserver:mssql-jdbc:11.1.1.jre8-preview' From e9e602b3aac9a72c6c7b4d93af62e4cc7d3acd04 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 May 2022 21:07:47 +0000 Subject: [PATCH 0144/3231] Bump awaitility from 4.1.1 to 4.2.0 in /modules/couchbase Bumps [awaitility](https://github.com/awaitility/awaitility) from 4.1.1 to 4.2.0. - [Release notes](https://github.com/awaitility/awaitility/releases) - [Changelog](https://github.com/awaitility/awaitility/blob/master/changelog.txt) - [Commits](https://github.com/awaitility/awaitility/compare/awaitility-4.1.1...awaitility-4.2.0) --- updated-dependencies: - dependency-name: org.awaitility:awaitility dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/couchbase/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/couchbase/build.gradle b/modules/couchbase/build.gradle index f1e41fcbe11..71844a30d20 100644 --- a/modules/couchbase/build.gradle +++ b/modules/couchbase/build.gradle @@ -6,5 +6,5 @@ dependencies { shaded 'com.squareup.okhttp3:okhttp:3.14.9' testImplementation 'com.couchbase.client:java-client:3.3.0' - testImplementation 'org.awaitility:awaitility:4.1.1' + testImplementation 'org.awaitility:awaitility:4.2.0' } From 07543ffad63bb842279abaf5dfecb9974ed2d667 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 May 2022 21:07:49 +0000 Subject: [PATCH 0145/3231] Bump org.jetbrains.kotlin.plugin.spring in /examples Bumps org.jetbrains.kotlin.plugin.spring from 1.6.20 to 1.6.21. --- updated-dependencies: - dependency-name: org.jetbrains.kotlin.plugin.spring dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/spring-boot-kotlin-redis/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/spring-boot-kotlin-redis/build.gradle b/examples/spring-boot-kotlin-redis/build.gradle index b6dd68b33cd..4f3a50de21a 100644 --- a/examples/spring-boot-kotlin-redis/build.gradle +++ b/examples/spring-boot-kotlin-redis/build.gradle @@ -1,7 +1,7 @@ plugins { id("org.springframework.boot") version "2.6.4" id("org.jetbrains.kotlin.jvm") version "1.6.21" - id("org.jetbrains.kotlin.plugin.spring") version "1.6.20" + id("org.jetbrains.kotlin.plugin.spring") version "1.6.21" } apply plugin: 'io.spring.dependency-management' From e94eaa11e65b984b6af7e21e98b1e02aade21282 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 May 2022 21:07:56 +0000 Subject: [PATCH 0146/3231] Bump r2dbc-postgresql in /modules/postgresql Bumps [r2dbc-postgresql](https://github.com/r2dbc/r2dbc-postgresql) from 0.8.11.RELEASE to 0.8.12.RELEASE. - [Release notes](https://github.com/r2dbc/r2dbc-postgresql/releases) - [Changelog](https://github.com/pgjdbc/r2dbc-postgresql/blob/v0.8.12.RELEASE/CHANGELOG) - [Commits](https://github.com/r2dbc/r2dbc-postgresql/compare/v0.8.11.RELEASE...v0.8.12.RELEASE) --- updated-dependencies: - dependency-name: io.r2dbc:r2dbc-postgresql dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/postgresql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/postgresql/build.gradle b/modules/postgresql/build.gradle index f2c50c30754..c0628afc63a 100644 --- a/modules/postgresql/build.gradle +++ b/modules/postgresql/build.gradle @@ -7,7 +7,7 @@ dependencies { api project(':jdbc') compileOnly project(':r2dbc') - compileOnly 'io.r2dbc:r2dbc-postgresql:0.8.11.RELEASE' + compileOnly 'io.r2dbc:r2dbc-postgresql:0.8.12.RELEASE' testImplementation project(':jdbc-test') testImplementation project(':test-support') From 7a957d5819a3fa6964db285a46eb77c0b9360163 Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Tue, 17 May 2022 18:02:08 -0400 Subject: [PATCH 0147/3231] Remove the disk space check (#5381) --- core/build.gradle | 4 +- .../testcontainers/DockerClientFactory.java | 102 +----------------- .../utility/RyukResourceReaper.java | 8 +- .../DockerClientFactoryTest.java | 11 -- 4 files changed, 8 insertions(+), 117 deletions(-) diff --git a/core/build.gradle b/core/build.gradle index 2a81eb8568a..efb08991f8d 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -40,7 +40,9 @@ tasks.japicmp { "org.testcontainers.shaded.*", ] - classExcludes = [] + classExcludes = [ + "org.testcontainers.utility.RyukResourceReaper", + ] methodExcludes = [ "org.testcontainers.containers.Container#getDockerClient()", diff --git a/core/src/main/java/org/testcontainers/DockerClientFactory.java b/core/src/main/java/org/testcontainers/DockerClientFactory.java index cc85645b2b4..c1e999d392a 100644 --- a/core/src/main/java/org/testcontainers/DockerClientFactory.java +++ b/core/src/main/java/org/testcontainers/DockerClientFactory.java @@ -2,7 +2,6 @@ import com.github.dockerjava.api.DockerClient; import com.github.dockerjava.api.DockerClientDelegate; -import com.github.dockerjava.api.async.ResultCallback; import com.github.dockerjava.api.command.CreateContainerCmd; import com.github.dockerjava.api.command.PullImageCmd; import com.github.dockerjava.api.exception.DockerClientException; @@ -10,7 +9,6 @@ import com.github.dockerjava.api.exception.NotFoundException; import com.github.dockerjava.api.model.AccessMode; import com.github.dockerjava.api.model.Bind; -import com.github.dockerjava.api.model.Frame; import com.github.dockerjava.api.model.Info; import com.github.dockerjava.api.model.Version; import com.github.dockerjava.api.model.Volume; @@ -31,18 +29,14 @@ import org.testcontainers.utility.DockerImageName; import org.testcontainers.utility.MountableFile; import org.testcontainers.utility.ResourceReaper; -import org.testcontainers.utility.RyukResourceReaper; import org.testcontainers.utility.TestcontainersConfiguration; -import java.io.ByteArrayOutputStream; -import java.io.IOException; import java.io.InputStream; import java.net.URI; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Optional; import java.util.ServiceLoader; import java.util.UUID; import java.util.function.BiFunction; @@ -80,16 +74,10 @@ public class DockerClientFactory { RuntimeException cachedClientFailure; private String activeApiVersion; - private String activeExecutionDriver; @Getter(lazy = true) private final boolean fileMountingSupported = checkMountableFile(); - - static { - System.setProperty("org.testcontainers.shaded.io.netty.packagePrefix", "org.testcontainers.shaded."); - } - @VisibleForTesting DockerClientFactory() { @@ -200,20 +188,19 @@ public void close() { log.info("Docker host IP address is {}", strategy.getDockerHostIpAddress()); Info dockerInfo = strategy.getInfo(); + log.debug("Docker info: {}", dockerInfo.getRawValues()); Version version = client.versionCmd().exec(); + log.debug("Docker version: {}", version.getRawValues()); activeApiVersion = version.getApiVersion(); - activeExecutionDriver = dockerInfo.getExecutionDriver(); log.info("Connected to docker: \n" + " Server Version: " + dockerInfo.getServerVersion() + "\n" + " API Version: " + activeApiVersion + "\n" + " Operating System: " + dockerInfo.getOperatingSystem() + "\n" + " Total Memory: " + dockerInfo.getMemTotal() / (1024 * 1024) + " MB"); - final ResourceReaper resourceReaper; try { - resourceReaper = ResourceReaper.instance(); //noinspection deprecation - resourceReaper.init(); + ResourceReaper.instance().init(); } catch (RuntimeException e) { cachedClientFailure = e; throw e; @@ -226,27 +213,6 @@ public void close() { try { log.info("Checking the system..."); checkDockerVersion(version.getVersion()); - - //noinspection deprecation - String ryukContainerId = resourceReaper instanceof RyukResourceReaper - ? ((RyukResourceReaper) resourceReaper).getContainerId() - : null; - - if (ryukContainerId != null) { - checkDiskSpace(client, ryukContainerId); - } else { - runInsideDocker( - createContainerCmd -> { - createContainerCmd.withName("testcontainers-checks-" + SESSION_ID); - createContainerCmd.getHostConfig().withAutoRemove(true); - createContainerCmd.withCmd("tail", "-f", "/dev/null"); - }, - (__, containerId) -> { - checkDiskSpace(client, containerId); - return ""; - } - ); - } } catch (RuntimeException e) { cachedClientFailure = e; throw e; @@ -263,43 +229,6 @@ private void checkDockerVersion(String dockerVersion) { check("Docker server version should be at least 1.6.0", versionIsSufficient); } - private void checkDiskSpace(DockerClient dockerClient, String id) { - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - - try { - dockerClient - .execStartCmd(dockerClient.execCreateCmd(id).withAttachStdout(true).withCmd("df", "-P").exec().getId()) - .exec(new ResultCallback.Adapter() { - @Override - public void onNext(Frame frame) { - if (frame == null) { - return; - } - switch (frame.getStreamType()) { - case RAW: - case STDOUT: - try { - outputStream.write(frame.getPayload()); - outputStream.flush(); - } catch (IOException e) { - onError(e); - } - } - } - }) - .awaitCompletion(); - } catch (Exception e) { - log.debug("Can't exec disk checking command", e); - } - - DiskSpaceUsage df = parseAvailableDiskSpace(outputStream.toString()); - - check( - "Docker environment should have more than 2GB free disk space", - df.availableMB.map(it -> it >= 2048).orElse(true) - ); - } - private void check(String message, boolean isSuccessful) { if (isSuccessful) { log.info("\u2714\ufe0e {}", message); @@ -389,28 +318,6 @@ T runInsideDocker(DockerImageName imageName, Consumer cr } } - @VisibleForTesting - static class DiskSpaceUsage { - Optional availableMB = Optional.empty(); - Optional usedPercent = Optional.empty(); - } - - @VisibleForTesting - DiskSpaceUsage parseAvailableDiskSpace(String dfOutput) { - DiskSpaceUsage df = new DiskSpaceUsage(); - String[] lines = dfOutput.split("\n"); - for (String line : lines) { - String[] fields = line.split("\\s+"); - if (fields.length > 5 && fields[5].equals("/")) { - long availableKB = Long.parseLong(fields[3]); - df.availableMB = Optional.of(availableKB / 1024L); - df.usedPercent = Optional.of(Integer.valueOf(fields[4].replace("%", ""))); - break; - } - } - return df; - } - /** * @return the docker API version of the daemon that we have connected to */ @@ -423,8 +330,7 @@ public String getActiveApiVersion() { * @return the docker execution driver of the daemon that we have connected to */ public String getActiveExecutionDriver() { - client(); - return activeExecutionDriver; + return getInfo().getExecutionDriver(); } /** diff --git a/core/src/main/java/org/testcontainers/utility/RyukResourceReaper.java b/core/src/main/java/org/testcontainers/utility/RyukResourceReaper.java index c2e2cf4ef97..6ce9c372fd2 100644 --- a/core/src/main/java/org/testcontainers/utility/RyukResourceReaper.java +++ b/core/src/main/java/org/testcontainers/utility/RyukResourceReaper.java @@ -21,11 +21,9 @@ * Ryuk-based {@link ResourceReaper} implementation. * * @see moby-ryuk - * @deprecated internal API */ -@Deprecated @Slf4j -public class RyukResourceReaper extends ResourceReaper { +class RyukResourceReaper extends ResourceReaper { private static final RateLimiter RYUK_ACK_RATE_LIMITER = RateLimiterBuilder .newBuilder() @@ -37,10 +35,6 @@ public class RyukResourceReaper extends ResourceReaper { private final RyukContainer ryukContainer = new RyukContainer(); - public String getContainerId() { - return ryukContainer.getContainerId(); - } - @Override public void init() { if (!TestcontainersConfiguration.getInstance().environmentSupportsReuse()) { diff --git a/core/src/test/java/org/testcontainers/DockerClientFactoryTest.java b/core/src/test/java/org/testcontainers/DockerClientFactoryTest.java index 247a16a76a6..a889764579f 100644 --- a/core/src/test/java/org/testcontainers/DockerClientFactoryTest.java +++ b/core/src/test/java/org/testcontainers/DockerClientFactoryTest.java @@ -2,8 +2,6 @@ import org.junit.Rule; import org.junit.Test; -import org.rnorth.visibleassertions.VisibleAssertions; -import org.testcontainers.DockerClientFactory.DiskSpaceUsage; import org.testcontainers.dockerclient.LogToStringContainerCallback; import org.testcontainers.utility.DockerImageName; import org.testcontainers.utility.MockTestcontainersConfigurationRule; @@ -39,15 +37,6 @@ public void runCommandInsideDockerShouldNotFailIfImageDoesNotExistsLocally() { } } - @Test - public void shouldHandleBigDiskSize() throws Exception { - String dfOutput = "/dev/disk1 2982480572 1491240286 2982480572 31% /"; - DiskSpaceUsage usage = DockerClientFactory.instance().parseAvailableDiskSpace(dfOutput); - - VisibleAssertions.assertEquals("Available MB is correct", 2982480572L / 1024L, usage.availableMB.orElse(0L)); - VisibleAssertions.assertEquals("Available percentage is correct", 31, usage.usedPercent.orElse(0)); - } - @Test public void dockerHostIpAddress() { DockerClientFactory instance = new DockerClientFactory(); From d9fa5130435d6bcd5112b864349924ad5d2cdbae Mon Sep 17 00:00:00 2001 From: Wenjun Ruan Date: Wed, 18 May 2022 06:56:00 +0800 Subject: [PATCH 0148/3231] Add SeaTunnel to who is using testcontainers (#5272) --- docs/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/index.md b/docs/index.md index 8749e3678cf..b2c11d6d8e8 100644 --- a/docs/index.md +++ b/docs/index.md @@ -200,7 +200,7 @@ A huge thank you to our sponsors: * [Alkira](https://www.alkira.com/) - Testing of multiple micro-services using Kafka, PostgreSQL, Apache Zookeeper, Etcd and so on. * [Togglz](https://www.togglz.org/) - Feature Flags for the Java platform * [Byzer](https://www.byzer.org/home) - Integration tests for Data and AI platforms are based on multiple versions of Byzer, Ray and Apache Spark. - +* [Apache SeaTunnel](https://github.com/apache/incubator-seatunnel) - Integration testing with different datasource. ## License From 0073001e603ac8bd583a82f81c2a077251156c5d Mon Sep 17 00:00:00 2001 From: Denis <13610290+cac03@users.noreply.github.com> Date: Wed, 18 May 2022 02:24:11 +0300 Subject: [PATCH 0149/3231] Remove "Linux - Travis CI" from docs (#4374) --- docs/supported_docker_environment/index.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/supported_docker_environment/index.md b/docs/supported_docker_environment/index.md index 62a2bbe0af2..3b82cea2165 100644 --- a/docs/supported_docker_environment/index.md +++ b/docs/supported_docker_environment/index.md @@ -5,7 +5,6 @@ | Host Operating System / Environment | Minimum recommended docker versions | Known issues / tips | |-------------------------------------|-----------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | Linux - general | Docker v17.09 | | -| Linux - Travis CI | Docker v17.09 | See [example .travis.yml](https://raw.githubusercontent.com/testcontainers/testcontainers-java/master/.travis.yml) for baseline Travis CI configuration | | Linux - CircleCI (LXC driver) | Docker v17.09 | The `exec` feature is not compatible with CircleCI. See CircleCI configuration [example](./continuous_integration/circle_ci.md) | | Linux - within a Docker container | Docker v17.09 | See [Running inside Docker](continuous_integration/dind_patterns.md) for Docker-in-Docker and Docker wormhole patterns | | Mac OS X - Docker Toolbox | Docker Machine v0.8.0 | | From 55334479422b97fea8110a0a59bf64127e29a6ae Mon Sep 17 00:00:00 2001 From: John Athanaselis Date: Wed, 18 May 2022 12:53:27 +0300 Subject: [PATCH 0150/3231] K3sContainer: expose kubeConfig for docker network access (#5097) Co-authored-by: Sergei Egorov --- .../org/testcontainers/k3s/K3sContainer.java | 63 ++++++++++++++----- .../k3s/KubectlContainerTest.java | 45 +++++++++++++ 2 files changed, 93 insertions(+), 15 deletions(-) create mode 100644 modules/k3s/src/test/java/org/testcontainers/k3s/KubectlContainerTest.java diff --git a/modules/k3s/src/main/java/org/testcontainers/k3s/K3sContainer.java b/modules/k3s/src/main/java/org/testcontainers/k3s/K3sContainer.java index 54cb2ff500f..7f21ca7dbb8 100644 --- a/modules/k3s/src/main/java/org/testcontainers/k3s/K3sContainer.java +++ b/modules/k3s/src/main/java/org/testcontainers/k3s/K3sContainer.java @@ -8,23 +8,29 @@ import com.github.dockerjava.api.command.InspectContainerResponse; import com.github.dockerjava.api.model.DockerObjectAccessor; import lombok.SneakyThrows; +import org.apache.commons.io.IOUtils; import org.testcontainers.containers.BindMode; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy; import org.testcontainers.utility.DockerImageName; +import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map; public class K3sContainer extends GenericContainer { + public static int KUBE_SECURE_PORT = 6443; + + public static int RANCHER_WEBHOOK_PORT = 8443; + private String kubeConfigYaml; public K3sContainer(DockerImageName dockerImageName) { super(dockerImageName); dockerImageName.assertCompatibleWith(DockerImageName.parse("rancher/k3s")); - addExposedPorts(6443, 8443); + addExposedPorts(KUBE_SECURE_PORT, RANCHER_WEBHOOK_PORT); setPrivilegedMode(true); withCreateContainerCmdModifier(it -> { it.getHostConfig().withCgroupnsMode("host"); @@ -44,30 +50,57 @@ public K3sContainer(DockerImageName dockerImageName) { setWaitStrategy(new LogMessageWaitStrategy().withRegEx(".*Node controller sync successful.*")); } - @SneakyThrows @Override protected void containerIsStarted(InspectContainerResponse containerInfo) { + String rawKubeConfig = copyFileFromContainer("/etc/rancher/k3s/k3s.yaml", + is -> IOUtils.toString(is, StandardCharsets.UTF_8) + ); + String serverUrl = "https://" + this.getHost() + ":" + this.getMappedPort(KUBE_SECURE_PORT); + kubeConfigYaml = kubeConfigWithServerUrl(rawKubeConfig, serverUrl); + } + + /** + * Return the kubernetes client configuration to access k3s from the host machine. + * + * @return the kubeConfig yaml. + */ + public String getKubeConfigYaml() { + return kubeConfigYaml; + } + + /** + * Generate a kubernetes client configuration for use on a docker internal network. The kubeConfig can be used by + * another docker container running in the same network as the k3s container. For access from the host, use + * the {@link #getKubeConfigYaml()} method instead. + * + * @param networkAlias a valid network alias of the k3s container. + * @return the kubeConfig yaml. + */ + public String generateInternalKubeConfigYaml(String networkAlias) { + if (this.getNetworkAliases().contains(networkAlias)) { + String serverUrl = "https://" + networkAlias + ":" + KUBE_SECURE_PORT; + return kubeConfigWithServerUrl(kubeConfigYaml, serverUrl); + } else { + throw new IllegalArgumentException(networkAlias + " is not a network alias for k3s container"); + } + } + + @SneakyThrows + private String kubeConfigWithServerUrl(String kubeConfigYaml, String serverUrl) { ObjectMapper objectMapper = new ObjectMapper(new YAMLFactory()); - ObjectNode rawKubeConfig = copyFileFromContainer( - "/etc/rancher/k3s/k3s.yaml", - is -> objectMapper.readValue(is, ObjectNode.class) - ); + ObjectNode kubeConfigObjectNode = objectMapper.readValue(kubeConfigYaml, ObjectNode.class); - JsonNode clusterNode = rawKubeConfig.at("/clusters/0/cluster"); + JsonNode clusterNode = kubeConfigObjectNode.at("/clusters/0/cluster"); if (!clusterNode.isObject()) { throw new IllegalStateException("'/clusters/0/cluster' expected to be an object"); } ObjectNode clusterConfig = (ObjectNode) clusterNode; + clusterConfig.replace("server", new TextNode(serverUrl)); - clusterConfig.replace("server", new TextNode("https://" + this.getHost() + ":" + this.getMappedPort(6443))); + kubeConfigObjectNode.set("current-context", new TextNode("default")); - rawKubeConfig.set("current-context", new TextNode("default")); - - kubeConfigYaml = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(rawKubeConfig); - } - - public String getKubeConfigYaml() { - return kubeConfigYaml; + return objectMapper.writerWithDefaultPrettyPrinter() + .writeValueAsString(kubeConfigObjectNode); } } diff --git a/modules/k3s/src/test/java/org/testcontainers/k3s/KubectlContainerTest.java b/modules/k3s/src/test/java/org/testcontainers/k3s/KubectlContainerTest.java new file mode 100644 index 00000000000..5c0d44ff0f1 --- /dev/null +++ b/modules/k3s/src/test/java/org/testcontainers/k3s/KubectlContainerTest.java @@ -0,0 +1,45 @@ +package org.testcontainers.k3s; + +import org.junit.ClassRule; +import org.junit.Test; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.Network; +import org.testcontainers.containers.startupcheck.OneShotStartupCheckStrategy; +import org.testcontainers.images.builder.Transferable; +import org.testcontainers.utility.DockerImageName; + +import java.time.Duration; + +import static org.assertj.core.api.Assertions.assertThat; + +public class KubectlContainerTest { + + public static Network network = Network.SHARED; + + @ClassRule + public static K3sContainer k3s = new K3sContainer(DockerImageName.parse("rancher/k3s:v1.21.3-k3s1")) + .withNetwork(network) + .withNetworkAliases("k3s"); + + @Test + public void shouldExposeKubeConfigForNetworkAlias() throws Exception { + String kubeConfigYaml = k3s.generateInternalKubeConfigYaml("k3s"); + + try ( + GenericContainer kubectlContainer = new GenericContainer<>("rancher/kubectl:v1.23.3") + .withNetwork(network) + .withCopyToContainer(Transferable.of(kubeConfigYaml), "/.kube/config") + .withCommand("get namespaces") + .withStartupCheckStrategy(new OneShotStartupCheckStrategy().withTimeout(Duration.ofSeconds(30))) + ) { + kubectlContainer.start(); + + assertThat(kubectlContainer.getLogs()).contains("kube-system"); + } + } + + @Test(expected = IllegalArgumentException.class) + public void shouldThrowAnExceptionForUnknownNetworkAlias() { + k3s.generateInternalKubeConfigYaml("not-set-network-alias"); + } +} From ada816c136d6a46c77d301b1a85968ae5d4ae430 Mon Sep 17 00:00:00 2001 From: edee111 <4160602+edee111@users.noreply.github.com> Date: Wed, 18 May 2022 11:59:44 +0200 Subject: [PATCH 0151/3231] Removal of containers from the cache is not synchronized (#4598) Co-authored-by: Eduard Tomek --- .../org/testcontainers/jdbc/ContainerDatabaseDriver.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/modules/jdbc/src/main/java/org/testcontainers/jdbc/ContainerDatabaseDriver.java b/modules/jdbc/src/main/java/org/testcontainers/jdbc/ContainerDatabaseDriver.java index c1b4fc29067..4b3a7ba98f7 100644 --- a/modules/jdbc/src/main/java/org/testcontainers/jdbc/ContainerDatabaseDriver.java +++ b/modules/jdbc/src/main/java/org/testcontainers/jdbc/ContainerDatabaseDriver.java @@ -162,8 +162,10 @@ private Connection wrapConnection(final Connection connection, final JdbcDatabas return new ConnectionWrapper(connection, () -> { finalConnections.remove(connection); if (!isDaemon && finalConnections.isEmpty()) { - container.stop(); - jdbcUrlContainerCache.remove(connectionUrl.getUrl()); + synchronized (jdbcUrlContainerCache) { + container.stop(); + jdbcUrlContainerCache.remove(connectionUrl.getUrl()); + } } }); } From b8d75b153b61af01127b0fb8a09405d6624a8ab8 Mon Sep 17 00:00:00 2001 From: Jurrian Fahner Date: Wed, 18 May 2022 18:30:34 +0200 Subject: [PATCH 0152/3231] Add docs for using Gitlab CI with Docker wormhole executor (#4770) --- .../continuous_integration/gitlab_ci.md | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/docs/supported_docker_environment/continuous_integration/gitlab_ci.md b/docs/supported_docker_environment/continuous_integration/gitlab_ci.md index 5e701007ab0..b4571403654 100644 --- a/docs/supported_docker_environment/continuous_integration/gitlab_ci.md +++ b/docs/supported_docker_environment/continuous_integration/gitlab_ci.md @@ -1,5 +1,36 @@ # GitLab CI +## Example using Docker socket +This applies if you have your own GitlabCI runner installed, use the Docker executor and you have `/var/run/docker.sock` mounted in the runner configuration. + +See below for an example runner configuration: +```toml +[[runners]] + name = "MACHINE_NAME" + url = "https://gitlab.com/" + token = "GENERATED_GITLAB_RUNNER_TOKEN" + executor = "docker" + [runners.docker] + tls_verify = false + image = "docker:latest" + privileged = false + disable_entrypoint_overwrite = false + oom_kill_disable = false + disable_cache = false + volumes = ["/var/run/docker.sock:/var/run/docker.sock", "/cache"] + shm_size = 0 +``` + +Please also include the following in your GitlabCI pipeline definitions (`.gitlab-ci.yml`) that use Testcontainers: +```yml +variables: + TESTCONTAINERS_HOST_OVERRIDE: "host.docker.internal" +``` + +The environment variable `TESTCONTAINERS_HOST_OVERRIDE` needs to be configured, otherwise, a wrong IP address would be used to resolve the Docker host, which will likely lead to failing tests. + +## Example using DinD (Docker-in-Docker) + In order to use Testcontainers in a Gitlab CI pipeline, you need to run the job as a Docker container (see [Patterns for running inside Docker](dind_patterns.md)). So edit your `.gitlab-ci.yml` to include the [Docker-In-Docker service](https://docs.gitlab.com/ee/ci/docker/using_docker_build.html#use-docker-in-docker-workflow-with-docker-executor) (`docker:dind`) and set the `DOCKER_HOST` variable to `tcp://docker:2375` and `DOCKER_TLS_CERTDIR` to empty string. From 6dc05d30cccd8211728e6082f3aa11c6964d0967 Mon Sep 17 00:00:00 2001 From: Tobias Stadler Date: Wed, 18 May 2022 18:52:15 +0200 Subject: [PATCH 0153/3231] Reset driver and vncRecordingContainer when Selenium container stops (#5116) --- .../testcontainers/containers/BrowserWebDriverContainer.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java b/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java index 12bbbc07de5..fc3778385cd 100644 --- a/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java +++ b/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java @@ -318,6 +318,7 @@ public void stop() { } catch (Exception e) { LOGGER.debug("Failed to quit the driver", e); } + driver = null; } if (vncRecordingContainer != null) { @@ -326,6 +327,7 @@ public void stop() { } catch (Exception e) { LOGGER.debug("Failed to stop vncRecordingContainer", e); } + vncRecordingContainer = null; } super.stop(); From ea7b29baac0422c0c7a58554a49b393b7ebcc8b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Thu, 19 May 2022 12:58:16 -0500 Subject: [PATCH 0154/3231] Upgrade lombok to be compatible against java 17 (#5388) --- build.gradle | 4 ++-- examples/kafka-cluster/build.gradle | 2 +- examples/solr-container/build.gradle | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build.gradle b/build.gradle index 309743477c1..96e1940eba5 100644 --- a/build.gradle +++ b/build.gradle @@ -10,7 +10,7 @@ buildscript { } plugins { - id 'io.franzbecker.gradle-lombok' version '4.0.0' + id 'io.franzbecker.gradle-lombok' version '5.0.0' id 'com.github.johnrengelman.shadow' version '7.0.0' id 'me.champeau.gradle.japicmp' version '0.2.9' apply false } @@ -40,7 +40,7 @@ subprojects { } lombok { - version = '1.18.20' + version = '1.18.24' } diff --git a/examples/kafka-cluster/build.gradle b/examples/kafka-cluster/build.gradle index ee8035ea51c..db89b4335fe 100644 --- a/examples/kafka-cluster/build.gradle +++ b/examples/kafka-cluster/build.gradle @@ -8,7 +8,7 @@ repositories { dependencies { testCompileOnly "org.projectlombok:lombok:1.18.24" - testAnnotationProcessor "org.projectlombok:lombok:1.18.22" + testAnnotationProcessor "org.projectlombok:lombok:1.18.24" testImplementation 'org.testcontainers:kafka' testImplementation 'org.apache.kafka:kafka-clients:3.1.0' testImplementation 'org.assertj:assertj-core:3.22.0' diff --git a/examples/solr-container/build.gradle b/examples/solr-container/build.gradle index 3f3366f9d46..abb7c12e0ed 100644 --- a/examples/solr-container/build.gradle +++ b/examples/solr-container/build.gradle @@ -8,7 +8,7 @@ repositories { dependencies { compileOnly "org.projectlombok:lombok:1.18.24" - annotationProcessor "org.projectlombok:lombok:1.18.22" + annotationProcessor "org.projectlombok:lombok:1.18.24" implementation 'org.apache.solr:solr-solrj:8.11.1' From fffab559a25d2e0501ab721182bc9cbdcb722506 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Thu, 19 May 2022 13:32:08 -0500 Subject: [PATCH 0155/3231] Fix TestcontainersTestDescription filename (#5387) --- ...nerTestDescription.java => TestcontainersTestDescription.java} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/{TestcontainerTestDescription.java => TestcontainersTestDescription.java} (100%) diff --git a/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainerTestDescription.java b/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersTestDescription.java similarity index 100% rename from modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainerTestDescription.java rename to modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersTestDescription.java From b04540b2cafbb25a580595d2d7832781a1de9825 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Fri, 20 May 2022 18:14:30 -0500 Subject: [PATCH 0156/3231] Add spotless and checkstyle (#5383) Co-authored-by: Sergei Egorov --- build.gradle | 31 +++++++++++++++++++++ config/checkstyle/checkstyle.xml | 47 ++++++++++++++++++++++++++++++++ docs/contributing.md | 1 + gradle/ci-support.gradle | 2 +- modules/neo4j/build.gradle | 2 +- 5 files changed, 81 insertions(+), 2 deletions(-) create mode 100644 config/checkstyle/checkstyle.xml diff --git a/build.gradle b/build.gradle index 96e1940eba5..adbd1a47c4a 100644 --- a/build.gradle +++ b/build.gradle @@ -13,6 +13,7 @@ plugins { id 'io.franzbecker.gradle-lombok' version '5.0.0' id 'com.github.johnrengelman.shadow' version '7.0.0' id 'me.champeau.gradle.japicmp' version '0.2.9' apply false + id 'com.diffplug.spotless' version '6.3.0' apply false } apply from: "$rootDir/gradle/ci-support.gradle" @@ -22,6 +23,8 @@ subprojects { apply plugin: 'idea' apply plugin: 'io.franzbecker.gradle-lombok' apply from: "$rootDir/gradle/shading.gradle" + apply plugin: 'com.diffplug.spotless' + apply plugin: 'checkstyle' group = "org.testcontainers" @@ -105,4 +108,32 @@ subprojects { dependencies { testImplementation 'ch.qos.logback:logback-classic:1.2.3' } + + spotless { + enforceCheck false + java { + removeUnusedImports() + trimTrailingWhitespace() + endWithNewline() + + prettier(['prettier': '2.5.1', 'prettier-plugin-java': '1.6.1']) + .config([ + 'parser' : 'java', + 'tabWidth' : 4, + 'printWidth': 120 + ]) + + importOrder('', 'java', 'javax', '\\#') + } + groovyGradle { + target '**/*.groovy' + greclipse('4.19.0') + } + } + + checkstyle { + toolVersion = "9.3" + configFile = rootProject.file('config/checkstyle/checkstyle.xml') + ignoreFailures = true + } } diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml new file mode 100644 index 00000000000..39598dae2e4 --- /dev/null +++ b/config/checkstyle/checkstyle.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + diff --git a/docs/contributing.md b/docs/contributing.md index 6cd3e2719f8..01c4fce57b6 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -8,6 +8,7 @@ * follow the style, naming and structure conventions of the rest of the project. * make commits atomic and easy to merge. * when updating documentation, please see [our guidance for documentation contributions](contributing_docs.md). + * apply format running `./gradlew spotlessApply` * verify all tests are passing. Build the project with `./gradlew check` to do this. **N.B.** Gradle's Build Cache is enabled by default, but you can add `--no-build-cache` flag to disable it. diff --git a/gradle/ci-support.gradle b/gradle/ci-support.gradle index b878c107796..5b85837361e 100644 --- a/gradle/ci-support.gradle +++ b/gradle/ci-support.gradle @@ -17,7 +17,7 @@ task testMatrix { } } -// If we're executing the `taskMatrix` task, disable tests and other slow tasks +// If we're executing the `testMatrix` task, disable tests and other slow tasks // so that we can get a result quickly. gradle.taskGraph.whenReady { if (it.hasTask(tasks.testMatrix)) { diff --git a/modules/neo4j/build.gradle b/modules/neo4j/build.gradle index 0bdd9ba960c..efba3dd0db5 100644 --- a/modules/neo4j/build.gradle +++ b/modules/neo4j/build.gradle @@ -26,7 +26,7 @@ task customNeo4jPluginJar(type: Jar) { outputs.dir(customNeo4jPluginDestinationDir) } -test.dependsOn customNeo4jPluginJar +processTestResources.dependsOn customNeo4jPluginJar dependencies { customNeo4jPluginCompileOnly "org.neo4j:neo4j:3.5.32" From f60051c0b7f89bed30ddeffd09b8feed0500e342 Mon Sep 17 00:00:00 2001 From: Tobias Stadler Date: Tue, 24 May 2022 20:43:27 +0200 Subject: [PATCH 0157/3231] Use `TestInstances` API from JUnit Jupiter (#2719) Co-authored-by: Richard North --- .../jupiter/TestcontainersExtension.java | 27 +++++-------------- 1 file changed, 6 insertions(+), 21 deletions(-) diff --git a/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersExtension.java b/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersExtension.java index 1ed5e954e44..990940be14d 100644 --- a/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersExtension.java +++ b/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersExtension.java @@ -12,7 +12,6 @@ import org.junit.jupiter.api.extension.ExtensionContext.Namespace; import org.junit.jupiter.api.extension.ExtensionContext.Store; import org.junit.jupiter.api.extension.ExtensionContext.Store.CloseableResource; -import org.junit.jupiter.api.extension.TestInstancePostProcessor; import org.junit.platform.commons.support.AnnotationSupport; import org.junit.platform.commons.util.AnnotationUtils; import org.junit.platform.commons.util.Preconditions; @@ -23,7 +22,8 @@ import org.testcontainers.lifecycle.TestLifecycleAware; import java.lang.reflect.Field; -import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collections; import java.util.LinkedHashSet; import java.util.List; import java.util.Optional; @@ -34,20 +34,13 @@ import static java.util.stream.Collectors.toList; import static org.testcontainers.junit.jupiter.FilesystemFriendlyNameGenerator.filesystemFriendlyNameOf; -class TestcontainersExtension implements BeforeEachCallback, BeforeAllCallback, AfterEachCallback, AfterAllCallback, ExecutionCondition, TestInstancePostProcessor { +class TestcontainersExtension implements BeforeEachCallback, BeforeAllCallback, AfterEachCallback, AfterAllCallback, ExecutionCondition { private static final Namespace NAMESPACE = Namespace.create(TestcontainersExtension.class); - private static final String TEST_INSTANCE = "testInstance"; private static final String SHARED_LIFECYCLE_AWARE_CONTAINERS = "sharedLifecycleAwareContainers"; private static final String LOCAL_LIFECYCLE_AWARE_CONTAINERS = "localLifecycleAwareContainers"; - @Override - public void postProcessTestInstance(final Object testInstance, final ExtensionContext context) { - Store store = context.getStore(NAMESPACE); - store.put(TEST_INSTANCE, testInstance); - } - @Override public void beforeAll(ExtensionContext context) { Class testClass = context.getTestClass() @@ -156,17 +149,9 @@ boolean isDockerAvailable() { } private Set collectParentTestInstances(final ExtensionContext context) { - Set testInstances = new LinkedHashSet<>(); - Optional current = Optional.of(context); - while (current.isPresent()) { - ExtensionContext ctx = current.get(); - Object testInstance = ctx.getStore(NAMESPACE).remove(TEST_INSTANCE); - if (testInstance != null) { - testInstances.add(testInstance); - } - current = ctx.getParent(); - } - return testInstances; + List allInstances = new ArrayList<>(context.getRequiredTestInstances().getAllInstances()); + Collections.reverse(allInstances); + return new LinkedHashSet<>(allInstances); } private List findSharedContainers(Class testClass) { From 2bdbc8cd6d42cb5dc12165617e72abfa0dee2fec Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Tue, 24 May 2022 21:07:58 +0200 Subject: [PATCH 0158/3231] Don't delete images if `deleteOnExit` is false (#5391) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Eddú Meléndez Gonzales --- .../images/builder/ImageFromDockerfile.java | 10 +++++----- .../builder/ImageFromDockerfileTest.java | 19 +++++++++++++++++++ 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java b/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java index 281a74feaba..000487bfa4d 100644 --- a/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java +++ b/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java @@ -87,9 +87,6 @@ protected final String resolve() { DockerClient dockerClient = DockerClientFactory.instance().client(); try { - if (deleteOnExit) { - ResourceReaper.instance().registerImageForCleanup(dockerImageName); - } BuildImageResultCallback resultCallback = new BuildImageResultCallback() { @Override @@ -114,9 +111,12 @@ public void onNext(BuildResponseItem item) { if (buildImageCmd.getLabels() != null) { labels.putAll(buildImageCmd.getLabels()); } + labels.putAll(DockerClientFactory.DEFAULT_LABELS); - //noinspection deprecation - labels.putAll(ResourceReaper.instance().getLabels()); + if (deleteOnExit) { + //noinspection deprecation + labels.putAll(ResourceReaper.instance().getLabels()); + } buildImageCmd.withLabels(labels); prePullDependencyImages(dependencyImageNames); diff --git a/core/src/test/java/org/testcontainers/images/builder/ImageFromDockerfileTest.java b/core/src/test/java/org/testcontainers/images/builder/ImageFromDockerfileTest.java index 93ec5cb5cd1..da3509cea7b 100644 --- a/core/src/test/java/org/testcontainers/images/builder/ImageFromDockerfileTest.java +++ b/core/src/test/java/org/testcontainers/images/builder/ImageFromDockerfileTest.java @@ -4,6 +4,7 @@ import com.github.dockerjava.api.command.InspectImageResponse; import org.junit.Test; import org.testcontainers.DockerClientFactory; +import org.testcontainers.utility.Base58; import static org.assertj.core.api.Assertions.assertThat; @@ -24,4 +25,22 @@ public void shouldAddDefaultLabels() { .containsAllEntriesOf(DockerClientFactory.DEFAULT_LABELS); } + @Test + public void shouldNotAddSessionLabelIfDeleteOnExitIsFalse() { + ImageFromDockerfile image = new ImageFromDockerfile("localhost/testcontainers/" + Base58.randomString(16).toLowerCase(), false) + .withDockerfileFromBuilder(it -> it.from("scratch")); + String imageId = image.resolve(); + DockerClient dockerClient = DockerClientFactory.instance().client(); + + try { + InspectImageResponse inspectImageResponse = dockerClient.inspectImageCmd(imageId).exec(); + assertThat(inspectImageResponse.getConfig().getLabels()) + .doesNotContainKey(DockerClientFactory.TESTCONTAINERS_SESSION_ID_LABEL); + } finally { + // ensure the image is deleted, even if the test fails + dockerClient.removeImageCmd(imageId).exec(); + } + + } + } From de1324ed2800eff4da326d0c23d281399d006bc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Tue, 24 May 2022 23:31:37 -0500 Subject: [PATCH 0159/3231] Apply spotless and checkstyle (#5390) --- .github/workflows/ci-docker-wormhole.yml | 1 + build.gradle | 11 +- config/checkstyle/checkstyle.xml | 8 +- .../testcontainers/AbstractJarFileTest.java | 5 +- .../testcontainers/JarFileShadingTest.java | 24 +- .../testcontainers/PublicBinaryAPITest.java | 76 +- .../testcontainers/DockerClientFactory.java | 93 ++- .../org/testcontainers/Testcontainers.java | 15 +- .../java/org/testcontainers/UnstableAPI.java | 6 +- .../testcontainers/containers/BindMode.java | 3 +- .../ComposeServiceWaitStrategyTarget.java | 15 +- .../testcontainers/containers/Container.java | 22 +- .../containers/ContainerFetchException.java | 1 + .../containers/ContainerState.java | 42 +- .../containers/DockerComposeContainer.java | 236 ++++-- .../containers/DockerComposeFiles.java | 4 +- .../containers/ExecInContainerPattern.java | 38 +- .../FailureDetectingExternalResource.java | 16 +- .../FixedHostPortGenericContainer.java | 5 +- .../containers/FutureContainer.java | 1 + .../containers/GenericContainer.java | 323 ++++---- .../containers/InternetProtocol.java | 1 - .../testcontainers/containers/Network.java | 1 - .../containers/ParsedDockerComposeFile.java | 65 +- .../containers/PortForwardingContainer.java | 24 +- .../containers/SelinuxContext.java | 1 - .../containers/SocatContainer.java | 16 +- .../containers/VncRecordingContainer.java | 55 +- .../containers/output/BaseConsumer.java | 1 + .../output/FrameConsumerResultCallback.java | 4 +- .../containers/output/OutputFrame.java | 19 +- .../containers/output/Slf4jLogConsumer.java | 10 +- .../containers/output/ToStringConsumer.java | 1 + .../containers/output/WaitingConsumer.java | 4 +- ...finiteWaitOneShotStartupCheckStrategy.java | 1 + ...umDurationRunningStartupCheckStrategy.java | 1 - .../startupcheck/StartupCheckStrategy.java | 51 +- .../containers/traits/LinkableContainer.java | 1 - .../internal/ExternalPortListeningCheck.java | 20 +- .../InternalCommandPortListeningCheck.java | 25 +- .../wait/strategy/AbstractWaitStrategy.java | 21 +- .../DockerHealthcheckWaitStrategy.java | 7 +- .../wait/strategy/HostPortWaitStrategy.java | 98 ++- .../wait/strategy/HttpWaitStrategy.java | 207 ++--- .../wait/strategy/LogMessageWaitStrategy.java | 18 +- .../containers/wait/strategy/Wait.java | 9 +- .../wait/strategy/WaitAllStrategy.java | 29 +- .../wait/strategy/WaitStrategy.java | 1 - .../wait/strategy/WaitStrategyTarget.java | 8 +- .../AuditLoggingDockerClient.java | 127 +-- .../AuthDelegatingDockerClientConfig.java | 14 +- .../dockerclient/DockerClientConfigUtils.java | 27 +- .../DockerClientProviderStrategy.java | 139 ++-- .../DockerMachineClientProviderStrategy.java | 21 +- ...dSystemPropertyClientProviderStrategy.java | 11 +- .../HeadersAddingDockerHttpClient.java | 4 +- .../InvalidConfigurationException.java | 2 +- .../LogToStringContainerCallback.java | 1 + .../NpipeSocketClientProviderStrategy.java | 5 +- .../RootlessDockerClientProviderStrategy.java | 20 +- .../UnixSocketClientProviderStrategy.java | 11 +- .../images/AbstractImagePullPolicy.java | 3 +- .../org/testcontainers/images/ImageData.java | 7 +- .../images/ImagePullPolicy.java | 2 - .../images/LocalImagesCache.java | 8 +- .../images/LoggedPullImageResultCallback.java | 38 +- .../images/ParsedDockerfile.java | 17 +- .../org/testcontainers/images/PullPolicy.java | 5 +- .../images/RemoteDockerImage.java | 24 +- ...eLimitedLoggedPullImageResultCallback.java | 39 +- .../images/builder/ImageFromDockerfile.java | 54 +- .../images/builder/Transferable.java | 1 - .../builder/dockerfile/DockerfileBuilder.java | 5 +- .../dockerfile/statement/Statement.java | 2 +- .../dockerfile/traits/AddStatementTrait.java | 1 - .../dockerfile/traits/CmdStatementTrait.java | 1 - .../dockerfile/traits/CopyStatementTrait.java | 1 - .../traits/DockerfileBuilderTrait.java | 1 - .../traits/EntryPointStatementTrait.java | 1 - .../dockerfile/traits/EnvStatementTrait.java | 1 - .../traits/ExposeStatementTrait.java | 8 +- .../dockerfile/traits/FromStatementTrait.java | 1 - .../traits/LabelStatementTrait.java | 1 - .../dockerfile/traits/RunStatementTrait.java | 1 - .../dockerfile/traits/UserStatementTrait.java | 1 - .../traits/VolumeStatementTrait.java | 1 - .../traits/WorkdirStatementTrait.java | 1 - .../traits/BuildContextBuilderTrait.java | 1 - .../images/builder/traits/ClasspathTrait.java | 1 - .../builder/traits/DockerfileTrait.java | 38 +- .../images/builder/traits/FilesTrait.java | 1 - .../images/builder/traits/StringsTrait.java | 29 +- .../testcontainers/lifecycle/Startable.java | 1 - .../testcontainers/lifecycle/Startables.java | 36 +- .../lifecycle/TestDescription.java | 1 - .../lifecycle/TestLifecycleAware.java | 9 +- .../testcontainers/utility/AuditLogger.java | 45 +- .../utility/AuthConfigUtil.java | 9 +- .../org/testcontainers/utility/Base58.java | 3 +- .../utility/ClasspathScanner.java | 6 +- .../testcontainers/utility/CommandLine.java | 14 +- .../utility/ComparableVersion.java | 10 +- ...ConfigurationFileImageNameSubstitutor.java | 3 +- .../utility/DefaultImageNameSubstitutor.java | 13 +- .../utility/DockerImageName.java | 68 +- .../utility/DockerLoggerFactory.java | 1 - .../utility/DockerMachineClient.java | 45 +- .../testcontainers/utility/DockerStatus.java | 22 +- .../utility/DynamicPollInterval.java | 1 + .../utility/ImageNameSubstitutor.java | 40 +- .../utility/JVMHookResourceReaper.java | 21 +- .../testcontainers/utility/LazyFuture.java | 2 +- .../utility/LicenseAcceptance.java | 17 +- .../org/testcontainers/utility/LogUtils.java | 40 +- .../testcontainers/utility/MountableFile.java | 115 ++- .../org/testcontainers/utility/PathUtils.java | 34 +- .../PrefixingImageNameSubstitutor.java | 6 +- .../utility/RegistryAuthLocator.java | 141 ++-- .../utility/ResourceReaper.java | 66 +- .../testcontainers/utility/RyukContainer.java | 11 +- .../utility/RyukResourceReaper.java | 5 +- .../utility/TestEnvironment.java | 4 +- .../utility/TestcontainersConfiguration.java | 95 ++- .../utility/ThrowingFunction.java | 1 - .../testcontainers/utility/Versioning.java | 5 + .../OutOfPackageImagePullPolicyTest.java | 19 +- .../java/org/testcontainers/DaemonTest.java | 10 +- .../DockerClientFactoryTest.java | 8 +- .../DockerRegistryContainer.java | 39 +- .../containers/ContainerStateTest.java | 22 +- ...ockerComposeContainerWithServicesTest.java | 36 +- .../containers/DockerComposeFilesTest.java | 22 +- .../DockerComposeOverridesTest.java | 81 +- .../containers/ExposedHostTest.java | 36 +- .../FailureDetectingExternalResourceTest.java | 1 - .../containers/GenericContainerTest.java | 84 +- .../containers/NetworkTest.java | 73 +- ...ParsedDockerComposeFileValidationTest.java | 69 +- .../containers/ReusabilityUnitTests.java | 170 ++-- .../containers/output/ContainerLogsTest.java | 15 +- .../FrameConsumerResultCallbackTest.java | 100 ++- .../output/ToStringConsumerTest.java | 27 +- .../ExternalPortListeningCheckTest.java | 32 +- ...InternalCommandPortListeningCheckTest.java | 44 +- .../DockerHealthcheckWaitStrategyTest.java | 14 +- .../wait/strategy/WaitAllStrategyTest.java | 61 +- .../dockerclient/AmbiguousImagePullTest.java | 3 +- .../DockerClientConfigUtilsTest.java | 24 +- ...temPropertyClientProviderStrategyTest.java | 77 +- .../dockerclient/EventStreamTest.java | 30 +- .../dockerclient/ImagePullTest.java | 10 +- .../images/AgeBasedPullPolicyTest.java | 12 +- .../testcontainers/images/ImageDataTest.java | 8 +- .../images/ImagePullPolicyTest.java | 46 +- .../images/ParsedDockerfileTest.java | 90 ++- .../images/RemoteDockerImageTest.java | 10 +- .../images/builder/DockerfileBuildTest.java | 47 +- .../images/builder/DockerignoreTest.java | 30 +- .../builder/ImageFromDockerfileTest.java | 10 +- .../statement/AbstractStatementTest.java | 6 +- .../statement/KeyValuesStatementTest.java | 24 +- .../statement/MultiArgsStatementTest.java | 2 +- .../statement/RawStatementTest.java | 3 +- .../SingleArgumentStatementTest.java | 2 +- .../junit/BaseDockerComposeTest.java | 29 +- .../junit/CopyFileToContainerTest.java | 13 +- .../junit/DependenciesTest.java | 26 +- .../DockerComposeContainerScalingTest.java | 17 +- .../junit/DockerComposeContainerTest.java | 39 +- .../DockerComposeContainerWithBuildTest.java | 84 +- ...DockerComposeContainerWithOptionsTest.java | 53 +- .../junit/DockerComposeErrorHandlingTest.java | 14 +- .../junit/DockerComposeLocalImageTest.java | 4 +- .../junit/DockerComposeLogConsumerTest.java | 18 +- .../junit/DockerComposePassthroughTest.java | 20 +- .../junit/DockerComposeServiceTest.java | 13 +- .../junit/DockerComposeV2FormatTest.java | 6 +- .../junit/DockerComposeV2WithNetworkTest.java | 13 +- .../junit/DockerComposeWaitStrategyTest.java | 62 +- .../junit/DockerNetworkModeTest.java | 6 +- .../junit/DockerfileContainerTest.java | 31 +- .../testcontainers/junit/DockerfileTest.java | 68 +- .../junit/FileOperationsTest.java | 32 +- .../junit/FixedHostPortContainerTest.java | 32 +- .../junit/GenericContainerRuleTest.java | 269 ++++--- .../junit/NonExistentImagePullTest.java | 12 +- .../junit/OutputStreamTest.java | 57 +- .../junit/OutputStreamWithTTYTest.java | 43 +- .../ParameterizedDockerfileContainerTest.java | 30 +- .../junit/WorkingDirectoryTest.java | 11 +- .../strategy/AbstractWaitStrategyTest.java | 28 +- .../strategy/HostPortWaitStrategyTest.java | 3 +- .../wait/strategy/HttpWaitStrategyTest.java | 154 ++-- .../strategy/LogMessageWaitStrategyTest.java | 29 +- .../utility/AuthenticatedImagePullTest.java | 65 +- .../utility/ClasspathScannerTest.java | 83 +- .../utility/ComparableVersionTest.java | 24 +- .../DefaultImageNameSubstitutorTest.java | 2 + .../utility/DirectoryTarResourceTest.java | 81 +- .../DockerImageNameCompatibilityTest.java | 13 +- .../utility/DockerImageNameTest.java | 95 ++- .../utility/DockerStatusTest.java | 18 +- .../utility/FakeImageSubstitutor.java | 1 + .../utility/FilterRegistryTest.java | 12 +- .../utility/ImageNameSubstitutorTest.java | 12 +- .../utility/LazyFutureTest.java | 16 +- .../utility/MountableFileTest.java | 33 +- .../PrefixingImageNameSubstitutorTest.java | 51 +- .../utility/RegistryAuthLocatorTest.java | 76 +- .../utility/ResourceReaperTest.java | 27 +- .../TestcontainersConfigurationTest.java | 69 +- .../test/java/generic/CmdModifierTest.java | 15 +- .../src/test/java/generic/CommandsTest.java | 3 +- .../java/generic/ContainerCreationTest.java | 24 +- .../test/java/generic/ContainerLabelTest.java | 2 +- .../java/generic/HostPortExposedTest.java | 24 +- .../generic/ImageNameSubstitutionTest.java | 9 +- .../generic/MultiplePortsExposedTest.java | 13 +- .../test/java/generic/WaitStrategiesTest.java | 29 +- .../StartupCheckStrategyTest.java | 38 +- .../quickstart/RedisBackedCacheIntTest.java | 5 +- .../RedisBackedCacheIntTestStep0.java | 2 +- .../quickstart/RedisBackedCacheIntTest.java | 5 +- .../RedisBackedCacheIntTestStep0.java | 2 +- .../quickstart/RedisBackedCacheIntTest.groovy | 38 +- .../RedisBackedCacheIntTestStep0.groovy | 26 +- docs/modules/hivemq.md | 4 +- .../containers/CosmosDBEmulatorContainer.java | 5 +- .../containers/KeyStoreBuilder.java | 46 +- .../CosmosDBEmulatorContainerTest.java | 22 +- .../containers/CassandraContainer.java | 34 +- .../delegate/CassandraDatabaseDelegate.java | 11 +- .../wait/CassandraQueryWaitStrategy.java | 22 +- .../containers/CassandraContainerTest.java | 24 +- .../containers/ClickHouseContainer.java | 7 +- .../containers/ClickHouseProvider.java | 1 + .../clickhouse/ClickhouseJDBCDriverTest.java | 12 +- .../clickhouse/SimpleClickhouseTest.java | 4 +- .../containers/CockroachContainer.java | 32 +- .../CockroachContainerProvider.java | 1 + .../CockroachDBJDBCDriverTest.java | 12 +- .../cockroachdb/SimpleCockroachDBTest.java | 15 +- .../couchbase/BucketDefinition.java | 6 +- .../couchbase/CouchbaseContainer.java | 333 +++++--- .../couchbase/CouchbaseService.java | 2 - .../couchbase/CouchbaseContainerTest.java | 85 +- .../delegate/AbstractDatabaseDelegate.java | 7 +- .../delegate/DatabaseDelegate.java | 9 +- .../org/testcontainers/ext/ScriptUtils.java | 736 ++++++++++-------- .../ext/ScriptSplittingTest.java | 143 ++-- .../containers/Db2Container.java | 16 +- .../containers/Db2ContainerProvider.java | 1 + .../jdbc/db2/DB2JDBCDriverTest.java | 12 +- .../junit/db2/SimpleDb2Test.java | 17 +- .../dynamodb/DynaliteContainer.java | 22 +- .../dynamodb/DynaliteContainerTest.java | 30 +- .../elasticsearch/ElasticsearchContainer.java | 26 +- .../ElasticsearchContainerTest.java | 215 +++-- .../containers/BigtableEmulatorContainer.java | 9 +- .../DatastoreEmulatorContainer.java | 9 +- .../FirestoreEmulatorContainer.java | 9 +- .../containers/PubSubEmulatorContainer.java | 9 +- .../containers/SpannerEmulatorContainer.java | 9 +- .../BigtableEmulatorContainerTest.java | 61 +- .../DatastoreEmulatorContainerTest.java | 18 +- .../FirestoreEmulatorContainerTest.java | 24 +- .../PubSubEmulatorContainerTest.java | 85 +- .../SpannerEmulatorContainerTest.java | 68 +- .../hivemq/HiveMQContainer.java | 138 ++-- .../hivemq/HiveMQExtension.java | 98 ++- .../hivemq/ContainerWithControlCenterIT.java | 14 +- .../hivemq/ContainerWithCustomConfigIT.java | 25 +- ...ContainerWithExtensionFromDirectoryIT.java | 25 +- .../hivemq/ContainerWithExtensionIT.java | 22 +- .../ContainerWithExtensionSubclassIT.java | 22 +- .../ContainerWithFileInExtensionHomeIT.java | 55 +- .../hivemq/ContainerWithFileInHomeIT.java | 52 +- .../hivemq/ContainerWithLicenseIT.java | 46 +- .../ContainerWithoutPlatformExtensionsIT.java | 71 +- .../hivemq/CreateFileInCopiedDirectoryIT.java | 47 +- .../CreateFileInExtensionDirectoryIT.java | 45 +- ...DisableEnableExtensionFromDirectoryIT.java | 19 +- .../hivemq/DisableEnableExtensionIT.java | 29 +- .../hivemq/HiveMQExtensionTest.java | 5 +- .../hivemq/HiveMQTestContainerCore.java | 20 +- .../hivemq/docs/DemoDisableExtensionsIT.java | 27 +- .../hivemq/docs/DemoExtensionTestsIT.java | 32 +- .../hivemq/docs/DemoFilesIT.java | 35 +- .../hivemq/docs/DemoHiveMQContainerIT.java | 31 +- .../hivemq/util/MyExtension.java | 19 +- .../util/MyExtensionWithSubclasses.java | 19 +- .../hivemq/util/PublishModifier.java | 12 +- .../hivemq/util/TestPublishModifiedUtil.java | 31 +- .../containers/InfluxDBContainer.java | 14 +- .../containers/InfluxDBContainerTest.java | 8 +- .../InfluxDBContainerWithUserTest.java | 15 +- .../db/AbstractContainerDatabaseTest.java | 3 +- .../jdbc/AbstractJDBCDriverTest.java | 136 ++-- .../containers/JdbcDatabaseContainer.java | 49 +- .../JdbcDatabaseContainerProvider.java | 16 +- .../jdbc/ConnectionDelegate.java | 2 +- .../testcontainers/jdbc/ConnectionUrl.java | 89 +-- .../jdbc/ConnectionWrapper.java | 2 +- .../jdbc/ContainerDatabaseDriver.java | 63 +- .../jdbc/JdbcDatabaseDelegate.java | 18 +- .../testcontainers/jdbc/ext/ScriptUtils.java | 62 +- .../containers/JdbcDatabaseContainerTest.java | 5 +- .../jdbc/ConnectionUrlDriversTests.java | 109 ++- .../jdbc/ConnectionUrlTest.java | 41 +- .../jdbc/ContainerDatabaseDriverTest.java | 37 +- .../jdbc/MissingJdbcDriverTest.java | 2 +- .../FilesystemFriendlyNameGenerator.java | 10 +- .../junit/jupiter/Testcontainers.java | 1 - .../jupiter/TestcontainersExtension.java | 69 +- .../TestcontainersTestDescription.java | 2 + .../junit/jupiter/ComposeContainerTests.java | 3 +- .../FilesystemFriendlyNameGeneratorTest.java | 6 +- .../junit/jupiter/MixedLifecycleTests.java | 8 - .../junit/jupiter/PostgresContainerTests.java | 12 +- .../TestLifecycleAwareContainerMock.java | 10 +- ...tLifecycleAwareExceptionCapturingTest.java | 1 + .../jupiter/TestLifecycleAwareMethodTest.java | 17 +- .../jupiter/TestcontainersExtensionTests.java | 10 +- ...ntainersNestedRestartedContainerTests.java | 16 +- ...tcontainersNestedSharedContainerTests.java | 5 +- .../TestcontainersRestartBetweenTests.java | 10 +- .../TestcontainersSharedContainerTests.java | 6 +- .../jupiter/WrongAnnotationUsageTests.java | 3 - .../jupiter/inheritance/AbstractTestBase.java | 1 - .../org/testcontainers/k3s/K3sContainer.java | 13 +- .../k3s/Fabric8K3sContainerTest.java | 10 +- .../containers/KafkaContainer.java | 18 +- .../containers/KafkaContainerTest.java | 80 +- .../localstack/LocalStackContainer.java | 30 +- .../containers/localstack/LegacyModeTest.java | 70 +- .../localstack/LocalstackContainerTest.java | 160 ++-- .../containers/MariaDBContainer.java | 19 +- .../containers/MariaDBContainerProvider.java | 3 +- .../MariaDBR2DBCDatabaseContainer.java | 6 +- .../MariaDBR2DBCDatabaseContainerTest.java | 1 - .../jdbc/mariadb/MariaDBJDBCDriverTest.java | 49 +- .../junit/mariadb/SimpleMariaDBTest.java | 37 +- .../containers/MockServerContainer.java | 2 +- .../MockServerContainerRuleTest.java | 12 +- .../containers/MockServerContainerTest.java | 19 +- .../containers/SimpleHttpClient.java | 4 +- .../containers/MongoDBContainer.java | 45 +- .../containers/MongoDBContainerTest.java | 42 +- .../MSSQLR2DBCDatabaseContainer.java | 6 +- .../containers/MSSQLServerContainer.java | 21 +- .../MSSQLServerContainerProvider.java | 1 + .../MSSQLServerJDBCDriverTest.java | 15 +- .../CustomPasswordMSSQLServerTest.java | 60 +- .../CustomizableMSSQLServerTest.java | 9 +- .../mssqlserver/SimpleMSSQLServerTest.java | 25 +- .../containers/MySQLContainer.java | 17 +- .../containers/MySQLContainerProvider.java | 1 - .../MySQLR2DBCDatabaseContainer.java | 6 +- .../org/testcontainers/MySQLTestImages.java | 3 + .../MySQLR2DBCDatabaseContainerTest.java | 1 - .../containers/MySQLRootAccountTest.java | 7 +- .../jdbc/mysql/JDBCDriverWithPoolTest.java | 54 +- .../jdbc/mysql/MySQLJDBCDriverTest.java | 51 +- .../junit/mysql/CustomizableMysqlTest.java | 16 +- .../junit/mysql/MultiVersionMySQLTest.java | 20 +- .../junit/mysql/SimpleMySQLTest.java | 104 +-- .../neo4j/demos/plugins/HelloWorld.java | 1 - .../containers/Neo4jContainer.java | 71 +- .../Neo4jContainerJUnitIntegrationTest.java | 5 +- .../containers/Neo4jContainerTest.java | 93 +-- .../containers/NginxContainer.java | 7 +- .../testcontainers/junit/SimpleNginxTest.java | 14 +- .../containers/OracleContainer.java | 36 +- .../containers/OracleContainerProvider.java | 1 + .../containers/jdbc/OracleJDBCDriverTest.java | 24 +- .../junit/oracle/SimpleOracleTest.java | 29 +- .../containers/OrientDBContainer.java | 16 +- .../containers/OrientDBContainerTest.java | 13 +- .../containers/PostgisContainerProvider.java | 9 +- .../containers/PostgreSQLContainer.java | 29 +- .../PostgreSQLContainerProvider.java | 2 +- .../PostgreSQLR2DBCDatabaseContainer.java | 6 +- .../TimescaleDBContainerProvider.java | 9 +- .../PostgreSQLConnectionURLTest.java | 19 +- .../PostgreSQLR2DBCDatabaseContainerTest.java | 2 + .../containers/TimescaleDBContainerTest.java | 29 +- .../jdbc/DatabaseDriverShutdownTest.java | 1 + .../jdbc/DatabaseDriverTmpfsTest.java | 1 - .../jdbc/postgis/PostgisJDBCDriverTest.java | 20 +- .../postgresql/PostgreSQLJDBCDriverTest.java | 15 +- .../TimescaleDBJDBCDriverTest.java | 20 +- .../CustomizablePostgreSQLTest.java | 10 +- .../postgresql/SimplePostgreSQLTest.java | 30 +- .../containers/PrestoContainer.java | 26 +- .../containers/PrestoContainerProvider.java | 2 +- .../containers/PrestoContainerTest.java | 98 ++- .../jdbc/presto/PrestoJDBCDriverTest.java | 12 +- .../containers/PulsarContainer.java | 4 +- .../containers/PulsarContainerTest.java | 28 +- .../r2dbc/CancellableSubscription.java | 3 +- .../r2dbc/ConnectionPublisher.java | 10 +- .../r2dbc/EmptySubscription.java | 8 +- .../java/org/testcontainers/r2dbc/Hidden.java | 4 +- .../r2dbc/R2DBCDatabaseContainer.java | 1 - .../r2dbc/R2DBCDatabaseContainerProvider.java | 1 - .../TestcontainersR2DBCConnectionFactory.java | 45 +- ...tcontainersR2DBCConnectionFactoryTest.java | 19 +- .../AbstractR2DBCDatabaseContainerTest.java | 4 +- .../containers/RabbitMQContainer.java | 373 ++++++--- .../containers/RabbitMQContainerTest.java | 248 +++--- .../containers/BrowserWebDriverContainer.java | 125 ++- .../DefaultRecordingFileFactory.java | 23 +- .../containers/RecordingFileFactory.java | 15 +- .../containers/SeleniumUtils.java | 13 +- .../DefaultRecordingFileFactoryTest.java | 37 +- .../junit/BaseWebDriverContainerTest.java | 22 +- .../junit/BrowserWebDriverContainerTest.java | 18 +- ...ChromeRecordingWebDriverContainerTest.java | 102 +-- .../junit/ChromeWebDriverContainerTest.java | 2 +- .../ContainerWithoutCapabilitiesTest.java | 7 +- ...stomWaitTimeoutWebDriverContainerTest.java | 5 +- .../junit/FirefoxWebDriverContainerTest.java | 4 +- .../LocalServerWebDriverContainerTest.java | 8 +- .../junit/SeleniumStartTest.java | 14 +- .../junit/SeleniumUtilsTest.java | 2 - ...ecificImageNameWebDriverContainerTest.java | 5 +- .../containers/SolrClientUtils.java | 49 +- .../containers/SolrClientUtilsException.java | 1 + .../containers/SolrContainer.java | 23 +- .../SolrContainerConfiguration.java | 8 +- .../containers/SolrContainerTest.java | 15 +- .../spock/SpockTestDescription.groovy | 36 +- .../spock/Testcontainers.groovy | 1 - .../spock/TestcontainersExtension.groovy | 42 +- .../TestcontainersMethodInterceptor.groovy | 224 +++--- .../spock/ComposeContainerIT.groovy | 34 +- .../spock/MySqlContainerIT.groovy | 14 +- .../spock/PostgresContainerIT.groovy | 56 +- .../spock/SharedComposeContainerIT.groovy | 36 +- .../spock/SpockTestImages.groovy | 8 +- .../spock/TestHierarchyIT.groovy | 22 +- .../TestLifecycleAwareContainerMock.java | 10 +- .../spock/TestLifecycleAwareIT.groovy | 57 +- ...TestcontainersRestartBetweenTestsIT.groovy | 29 +- .../TestcontainersSharedContainerIT.groovy | 61 +- .../containers/ToxiproxyContainer.java | 50 +- .../containers/ToxiproxyTest.java | 78 +- .../containers/TrinoContainer.java | 17 +- .../containers/TrinoContainerProvider.java | 1 + .../containers/TrinoContainerTest.java | 38 +- .../jdbc/trino/TrinoJDBCDriverTest.java | 12 +- .../testcontainers/vault/VaultContainer.java | 46 +- .../testcontainers/vault/VaultLogLevel.java | 6 +- .../testcontainers/vault/VaultClientTest.java | 20 +- .../vault/VaultContainerTest.java | 74 +- .../org/testcontainers/testsupport/Flaky.java | 10 +- .../testsupport/FlakyTestJUnit4RetryRule.java | 14 +- .../testsupport/FlakyRuleTest.java | 60 +- 458 files changed, 8766 insertions(+), 5973 deletions(-) diff --git a/.github/workflows/ci-docker-wormhole.yml b/.github/workflows/ci-docker-wormhole.yml index b93d26d98bb..6d0df9a3c21 100644 --- a/.github/workflows/ci-docker-wormhole.yml +++ b/.github/workflows/ci-docker-wormhole.yml @@ -16,5 +16,6 @@ jobs: -v "$HOME:$HOME" \ -v "$PWD:$PWD" \ -w "$PWD" \ + -e AUTO_APPLY_GIT_HOOKS=false \ openjdk:8-jdk-alpine \ ./gradlew --no-daemon --continue --scan testcontainers:test --tests '*GenericContainerRuleTest' diff --git a/build.gradle b/build.gradle index adbd1a47c4a..1458b3db027 100644 --- a/build.gradle +++ b/build.gradle @@ -1,11 +1,13 @@ buildscript { repositories { mavenCentral() + maven { url 'https://jitpack.io' } } dependencies { // https://github.com/melix/japicmp-gradle-plugin/issues/36 classpath 'com.google.guava:guava:30.1.1-jre' + classpath 'com.github.kiview:captain-hook:76a1c11a16' } } @@ -17,6 +19,12 @@ plugins { } apply from: "$rootDir/gradle/ci-support.gradle" +apply plugin: 'com.github.tjni.captainhook' + +captainHook { + autoApplyGitHooks = Boolean.valueOf(System.getenv("AUTO_APPLY_GIT_HOOKS")) + preCommit = './gradlew spotlessApply' +} subprojects { apply plugin: 'java-library' @@ -110,8 +118,8 @@ subprojects { } spotless { - enforceCheck false java { + toggleOffOn() removeUnusedImports() trimTrailingWhitespace() endWithNewline() @@ -134,6 +142,5 @@ subprojects { checkstyle { toolVersion = "9.3" configFile = rootProject.file('config/checkstyle/checkstyle.xml') - ignoreFailures = true } } diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml index 39598dae2e4..ccb8367404c 100644 --- a/config/checkstyle/checkstyle.xml +++ b/config/checkstyle/checkstyle.xml @@ -11,8 +11,12 @@ LITERAL_FOR, LITERAL_IF, LITERAL_WHILE, - LITERAL_CASE, - LITERAL_DEFAULT, + " + /> + + + diff --git a/core/src/jarFileTest/java/org/testcontainers/AbstractJarFileTest.java b/core/src/jarFileTest/java/org/testcontainers/AbstractJarFileTest.java index d6043422640..ae92aa371b0 100644 --- a/core/src/jarFileTest/java/org/testcontainers/AbstractJarFileTest.java +++ b/core/src/jarFileTest/java/org/testcontainers/AbstractJarFileTest.java @@ -5,8 +5,7 @@ import java.nio.file.FileSystems; import java.nio.file.Path; import java.nio.file.Paths; - -import static java.util.Collections.emptyMap; +import java.util.Collections; public abstract class AbstractJarFileTest { @@ -16,7 +15,7 @@ public abstract class AbstractJarFileTest { try { Path jarFilePath = Paths.get(System.getProperty("jarFile")); URI jarFileUri = new URI("jar", jarFilePath.toUri().toString(), null); - FileSystem fileSystem = FileSystems.newFileSystem(jarFileUri, emptyMap()); + FileSystem fileSystem = FileSystems.newFileSystem(jarFileUri, Collections.emptyMap()); root = fileSystem.getPath("/"); } catch (Exception e) { throw new RuntimeException(e); diff --git a/core/src/jarFileTest/java/org/testcontainers/JarFileShadingTest.java b/core/src/jarFileTest/java/org/testcontainers/JarFileShadingTest.java index 4cf7b3a9e9f..99fd1359c85 100644 --- a/core/src/jarFileTest/java/org/testcontainers/JarFileShadingTest.java +++ b/core/src/jarFileTest/java/org/testcontainers/JarFileShadingTest.java @@ -13,34 +13,26 @@ public class JarFileShadingTest extends AbstractJarFileTest { @Test public void testPackages() throws Exception { - assertThatFileList(root).containsOnly( - "org", - "META-INF" - ); - - assertThatFileList(root.resolve("org")).containsOnly( - "testcontainers" - ); + assertThatFileList(root).containsOnly("org", "META-INF"); + + assertThatFileList(root.resolve("org")).containsOnly("testcontainers"); } @Test public void testMetaInf() throws Exception { - assertThatFileList(root.resolve("META-INF")).containsOnly( - "MANIFEST.MF", - "services" - ); + assertThatFileList(root.resolve("META-INF")).containsOnly("MANIFEST.MF", "services"); } @Test public void testMetaInfServices() throws Exception { assertThatFileList(root.resolve("META-INF").resolve("services")) - .allMatch(it -> it.startsWith("org.testcontainers.")); + .allMatch(it -> it.startsWith("org.testcontainers.")); } private ListAssert assertThatFileList(Path path) throws IOException { return (ListAssert) assertThat(Files.list(path)) - .extracting(Path::getFileName) - .extracting(Path::toString) - .extracting(it -> it.endsWith("/") ? it.substring(0, it.length() - 1) : it); + .extracting(Path::getFileName) + .extracting(Path::toString) + .extracting(it -> it.endsWith("/") ? it.substring(0, it.length() - 1) : it); } } diff --git a/core/src/jarFileTest/java/org/testcontainers/PublicBinaryAPITest.java b/core/src/jarFileTest/java/org/testcontainers/PublicBinaryAPITest.java index 208c8702654..77156e77636 100644 --- a/core/src/jarFileTest/java/org/testcontainers/PublicBinaryAPITest.java +++ b/core/src/jarFileTest/java/org/testcontainers/PublicBinaryAPITest.java @@ -37,6 +37,7 @@ public class PublicBinaryAPITest extends AbstractJarFileTest { private static String SHADED_PACKAGE = "org.testcontainers.shaded."; + private static String SHADED_PACKAGE_PATH = SHADED_PACKAGE.replaceAll("\\.", "/"); static { @@ -49,36 +50,38 @@ public class PublicBinaryAPITest extends AbstractJarFileTest { public static List data() throws Exception { List result = new ArrayList<>(); - Files.walkFileTree(root, new SimpleFileVisitor() { - - @Override - public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) throws IOException { - String fileName = path.toString(); + Files.walkFileTree( + root, + new SimpleFileVisitor() { + @Override + public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) throws IOException { + String fileName = path.toString(); - if (!fileName.endsWith(".class")) { - return super.visitFile(path, attrs); - } + if (!fileName.endsWith(".class")) { + return super.visitFile(path, attrs); + } - if (!fileName.startsWith("/org/testcontainers/") ) { - return super.visitFile(path, attrs); - } + if (!fileName.startsWith("/org/testcontainers/")) { + return super.visitFile(path, attrs); + } - if (fileName.startsWith("/" + SHADED_PACKAGE_PATH)) { - return super.visitFile(path, attrs); - } + if (fileName.startsWith("/" + SHADED_PACKAGE_PATH)) { + return super.visitFile(path, attrs); + } - try(InputStream inputStream = Files.newInputStream(path)) { - ClassReader reader = new ClassReader(inputStream); - ClassNode node = new ClassNode(); - reader.accept(node, ClassReader.SKIP_CODE); - if ((node.access & Opcodes.ACC_PUBLIC) != 0) { - result.add(new Object[]{ fileName, node }); + try (InputStream inputStream = Files.newInputStream(path)) { + ClassReader reader = new ClassReader(inputStream); + ClassNode node = new ClassNode(); + reader.accept(node, ClassReader.SKIP_CODE); + if ((node.access & Opcodes.ACC_PUBLIC) != 0) { + result.add(new Object[] { fileName, node }); + } } - } - return super.visitFile(path, attrs); + return super.visitFile(path, attrs); + } } - }); + ); return result; } @@ -100,39 +103,36 @@ public void setUp() { @Test public void testSuperClass() { - assertThat(classNode.superName) - .doesNotStartWith(SHADED_PACKAGE_PATH); + assertThat(classNode.superName).doesNotStartWith(SHADED_PACKAGE_PATH); } @Test public void testInterfaces() { - assertThat(classNode.interfaces) - .allSatisfy(it -> assertThat(it).doesNotStartWith(SHADED_PACKAGE_PATH)); + assertThat(classNode.interfaces).allSatisfy(it -> assertThat(it).doesNotStartWith(SHADED_PACKAGE_PATH)); } @Test public void testMethodReturnTypes() { assertThat(classNode.methods) - .filteredOn(it -> (it.access & (Opcodes.ACC_PUBLIC | Opcodes.ACC_PROTECTED)) != 0) - .allSatisfy(it -> assertThat(Type.getReturnType(it.desc).getClassName()).doesNotStartWith(SHADED_PACKAGE)); + .filteredOn(it -> (it.access & (Opcodes.ACC_PUBLIC | Opcodes.ACC_PROTECTED)) != 0) + .allSatisfy(it -> assertThat(Type.getReturnType(it.desc).getClassName()).doesNotStartWith(SHADED_PACKAGE)); } @Test public void testMethodArguments() { assertThat(classNode.methods) - .filteredOn(it -> (it.access & (Opcodes.ACC_PUBLIC | Opcodes.ACC_PROTECTED)) != 0) - .allSatisfy(method -> assertThat(Arrays.asList(Type.getArgumentTypes(method.desc))) - .extracting(Type::getClassName) - .allSatisfy(it -> assertThat(it).doesNotStartWith(SHADED_PACKAGE)) - ); + .filteredOn(it -> (it.access & (Opcodes.ACC_PUBLIC | Opcodes.ACC_PROTECTED)) != 0) + .allSatisfy(method -> { + assertThat(Arrays.asList(Type.getArgumentTypes(method.desc))) + .extracting(Type::getClassName) + .allSatisfy(it -> assertThat(it).doesNotStartWith(SHADED_PACKAGE)); + }); } @Test public void testFields() { assertThat(classNode.fields) - .filteredOn(it -> (it.access & (Opcodes.ACC_PUBLIC | Opcodes.ACC_PROTECTED)) != 0) - .allSatisfy(it -> assertThat(Type.getType(it.desc).getClassName()) - .doesNotStartWith(SHADED_PACKAGE) - ); + .filteredOn(it -> (it.access & (Opcodes.ACC_PUBLIC | Opcodes.ACC_PROTECTED)) != 0) + .allSatisfy(it -> assertThat(Type.getType(it.desc).getClassName()).doesNotStartWith(SHADED_PACKAGE)); } } diff --git a/core/src/main/java/org/testcontainers/DockerClientFactory.java b/core/src/main/java/org/testcontainers/DockerClientFactory.java index c1e999d392a..04c20236763 100644 --- a/core/src/main/java/org/testcontainers/DockerClientFactory.java +++ b/core/src/main/java/org/testcontainers/DockerClientFactory.java @@ -51,16 +51,17 @@ public class DockerClientFactory { public static final ThreadGroup TESTCONTAINERS_THREAD_GROUP = new ThreadGroup("testcontainers"); + public static final String TESTCONTAINERS_LABEL = DockerClientFactory.class.getPackage().getName(); + public static final String TESTCONTAINERS_SESSION_ID_LABEL = TESTCONTAINERS_LABEL + ".sessionId"; public static final String SESSION_ID = UUID.randomUUID().toString(); - public static final Map DEFAULT_LABELS = ImmutableMap.of( - TESTCONTAINERS_LABEL, "true" - ); + public static final Map DEFAULT_LABELS = ImmutableMap.of(TESTCONTAINERS_LABEL, "true"); private static final DockerImageName TINY_IMAGE = DockerImageName.parse("alpine:3.14"); + private static DockerClientFactory instance; // Cached client configuration @@ -79,9 +80,7 @@ public class DockerClientFactory { private final boolean fileMountingSupported = checkMountableFile(); @VisibleForTesting - DockerClientFactory() { - - } + DockerClientFactory() {} public static DockerClient lazyClient() { return new DockerClientDelegate() { @@ -102,7 +101,7 @@ public String toString() { * * @return the singleton instance of DockerClientFactory */ - public synchronized static DockerClientFactory instance() { + public static synchronized DockerClientFactory instance() { if (instance == null) { instance = new DockerClientFactory(); } @@ -112,6 +111,7 @@ public synchronized static DockerClientFactory instance() { /** * Checks whether Docker is accessible and {@link #client()} is able to produce a client. + * * @return true if Docker is available, false if not. */ public synchronized boolean isDockerAvailable() { @@ -149,16 +149,11 @@ public String getRemoteDockerUnixSocketPath() { } URI dockerHost = getTransportConfig().getDockerHost(); - String path = "unix".equals(dockerHost.getScheme()) - ? dockerHost.getRawPath() - : "/var/run/docker.sock"; - return SystemUtils.IS_OS_WINDOWS - ? "/" + path - : path; + String path = "unix".equals(dockerHost.getScheme()) ? dockerHost.getRawPath() : "/var/run/docker.sock"; + return SystemUtils.IS_OS_WINDOWS ? "/" + path : path; } /** - * * @return a new initialized Docker client */ @Synchronized @@ -175,16 +170,16 @@ public DockerClient client() { final DockerClientProviderStrategy strategy = getOrInitializeStrategy(); - client = new DockerClientDelegate() { - - @Getter - final DockerClient dockerClient = strategy.getDockerClient(); + client = + new DockerClientDelegate() { + @Getter + final DockerClient dockerClient = strategy.getDockerClient(); - @Override - public void close() { - throw new IllegalStateException("You should never close the global DockerClient!"); - } - }; + @Override + public void close() { + throw new IllegalStateException("You should never close the global DockerClient!"); + } + }; log.info("Docker host IP address is {}", strategy.getDockerHostIpAddress()); Info dockerInfo = strategy.getInfo(); @@ -192,11 +187,22 @@ public void close() { Version version = client.versionCmd().exec(); log.debug("Docker version: {}", version.getRawValues()); activeApiVersion = version.getApiVersion(); - log.info("Connected to docker: \n" + - " Server Version: " + dockerInfo.getServerVersion() + "\n" + - " API Version: " + activeApiVersion + "\n" + - " Operating System: " + dockerInfo.getOperatingSystem() + "\n" + - " Total Memory: " + dockerInfo.getMemTotal() / (1024 * 1024) + " MB"); + log.info( + "Connected to docker: \n" + + " Server Version: " + + dockerInfo.getServerVersion() + + "\n" + + " API Version: " + + activeApiVersion + + "\n" + + " Operating System: " + + dockerInfo.getOperatingSystem() + + "\n" + + " Total Memory: " + + dockerInfo.getMemTotal() / + (1024 * 1024) + + " MB" + ); try { //noinspection deprecation @@ -225,7 +231,8 @@ public void close() { } private void checkDockerVersion(String dockerVersion) { - boolean versionIsSufficient = new ComparableVersion(dockerVersion).compareTo(new ComparableVersion("1.6.0")) >= 0; + boolean versionIsSufficient = new ComparableVersion(dockerVersion).compareTo(new ComparableVersion("1.6.0")) >= + 0; check("Docker server version should be at least 1.6.0", versionIsSufficient); } @@ -241,14 +248,22 @@ private void check(String message, boolean isSuccessful) { private boolean checkMountableFile() { DockerClient dockerClient = client(); - MountableFile mountableFile = MountableFile.forClasspathResource(ResourceReaper.class.getName().replace(".", "/") + ".class"); + MountableFile mountableFile = MountableFile.forClasspathResource( + ResourceReaper.class.getName().replace(".", "/") + ".class" + ); Volume volume = new Volume("/dummy"); try { return runInsideDocker( - createContainerCmd -> createContainerCmd.withBinds(new Bind(mountableFile.getResolvedPath(), volume, AccessMode.ro)), + createContainerCmd -> { + createContainerCmd.withBinds(new Bind(mountableFile.getResolvedPath(), volume, AccessMode.ro)); + }, (__, containerId) -> { - try (InputStream stream = dockerClient.copyArchiveFromContainerCmd(containerId, volume.getPath()).exec()) { + try ( + InputStream stream = dockerClient + .copyArchiveFromContainerCmd(containerId, volume.getPath()) + .exec() + ) { stream.read(); return true; } catch (Exception e) { @@ -293,16 +308,22 @@ public String dockerHostIpAddress() { return getOrInitializeStrategy().getDockerHostIpAddress(); } - public T runInsideDocker(Consumer createContainerCmdConsumer, BiFunction block) { + public T runInsideDocker( + Consumer createContainerCmdConsumer, + BiFunction block + ) { return runInsideDocker(TINY_IMAGE, createContainerCmdConsumer, block); } - T runInsideDocker(DockerImageName imageName, Consumer createContainerCmdConsumer, BiFunction block) { + T runInsideDocker( + DockerImageName imageName, + Consumer createContainerCmdConsumer, + BiFunction block + ) { RemoteDockerImage dockerImage = new RemoteDockerImage(imageName); HashMap labels = new HashMap<>(DEFAULT_LABELS); labels.putAll(ResourceReaper.instance().getLabels()); - CreateContainerCmd createContainerCmd = client.createContainerCmd(dockerImage.get()) - .withLabels(labels); + CreateContainerCmd createContainerCmd = client.createContainerCmd(dockerImage.get()).withLabels(labels); createContainerCmdConsumer.accept(createContainerCmd); String id = createContainerCmd.exec().getId(); diff --git a/core/src/main/java/org/testcontainers/Testcontainers.java b/core/src/main/java/org/testcontainers/Testcontainers.java index 0f83725c55b..ab567e1fda7 100644 --- a/core/src/main/java/org/testcontainers/Testcontainers.java +++ b/core/src/main/java/org/testcontainers/Testcontainers.java @@ -1,12 +1,11 @@ package org.testcontainers; -import java.util.Map; -import java.util.Map.Entry; - import lombok.experimental.UtilityClass; - import org.testcontainers.containers.PortForwardingContainer; +import java.util.Map; +import java.util.Map.Entry; + @UtilityClass public class Testcontainers { @@ -15,10 +14,10 @@ public void exposeHostPorts(int... ports) { PortForwardingContainer.INSTANCE.exposeHostPort(port); } } - + public void exposeHostPorts(Map ports) { - for(Entry entry : ports.entrySet()) { - PortForwardingContainer.INSTANCE.exposeHostPort(entry.getKey(), entry.getValue()); - } + for (Entry entry : ports.entrySet()) { + PortForwardingContainer.INSTANCE.exposeHostPort(entry.getKey(), entry.getValue()); + } } } diff --git a/core/src/main/java/org/testcontainers/UnstableAPI.java b/core/src/main/java/org/testcontainers/UnstableAPI.java index 21fd2c3176c..01221738dd4 100644 --- a/core/src/main/java/org/testcontainers/UnstableAPI.java +++ b/core/src/main/java/org/testcontainers/UnstableAPI.java @@ -11,11 +11,7 @@ * a stable API. */ @Retention(RetentionPolicy.SOURCE) -@Target({ - ElementType.TYPE, - ElementType.METHOD, - ElementType.FIELD, -}) +@Target({ ElementType.TYPE, ElementType.METHOD, ElementType.FIELD }) @Documented public @interface UnstableAPI { } diff --git a/core/src/main/java/org/testcontainers/containers/BindMode.java b/core/src/main/java/org/testcontainers/containers/BindMode.java index 579dc4d5eca..5fc9c610165 100644 --- a/core/src/main/java/org/testcontainers/containers/BindMode.java +++ b/core/src/main/java/org/testcontainers/containers/BindMode.java @@ -6,7 +6,8 @@ * Possible modes for binding storage volumes. */ public enum BindMode { - READ_ONLY(AccessMode.ro), READ_WRITE(AccessMode.rw); + READ_ONLY(AccessMode.ro), + READ_WRITE(AccessMode.rw); public final AccessMode accessMode; diff --git a/core/src/main/java/org/testcontainers/containers/ComposeServiceWaitStrategyTarget.java b/core/src/main/java/org/testcontainers/containers/ComposeServiceWaitStrategyTarget.java index fbb2a7cee6e..81525710700 100644 --- a/core/src/main/java/org/testcontainers/containers/ComposeServiceWaitStrategyTarget.java +++ b/core/src/main/java/org/testcontainers/containers/ComposeServiceWaitStrategyTarget.java @@ -13,7 +13,6 @@ import java.util.List; import java.util.Map; - /** * Class to provide a wait strategy target for services started through docker-compose */ @@ -21,15 +20,23 @@ class ComposeServiceWaitStrategyTarget implements WaitStrategyTarget { private final Container container; + private final GenericContainer proxyContainer; + private final DockerClient dockerClient; + @NonNull private Map mappedPorts; - @Getter(lazy=true) + + @Getter(lazy = true) private final InspectContainerResponse containerInfo = dockerClient.inspectContainerCmd(getContainerId()).exec(); - ComposeServiceWaitStrategyTarget(DockerClient dockerClient, Container container, GenericContainer proxyContainer, - @NonNull Map mappedPorts) { + ComposeServiceWaitStrategyTarget( + DockerClient dockerClient, + Container container, + GenericContainer proxyContainer, + @NonNull Map mappedPorts + ) { this.dockerClient = dockerClient; this.container = container; this.proxyContainer = proxyContainer; diff --git a/core/src/main/java/org/testcontainers/containers/Container.java b/core/src/main/java/org/testcontainers/containers/Container.java index 88fe4802945..7e6f492c284 100644 --- a/core/src/main/java/org/testcontainers/containers/Container.java +++ b/core/src/main/java/org/testcontainers/containers/Container.java @@ -1,17 +1,15 @@ package org.testcontainers.containers; -import com.github.dockerjava.api.DockerClient; import com.github.dockerjava.api.model.Bind; import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.NonNull; import lombok.Value; -import org.testcontainers.DockerClientFactory; -import org.testcontainers.images.ImagePullPolicy; import org.testcontainers.containers.output.OutputFrame; import org.testcontainers.containers.startupcheck.StartupCheckStrategy; import org.testcontainers.containers.traits.LinkableContainer; import org.testcontainers.containers.wait.strategy.WaitStrategy; +import org.testcontainers.images.ImagePullPolicy; import org.testcontainers.images.builder.Transferable; import org.testcontainers.utility.LogUtils; import org.testcontainers.utility.MountableFile; @@ -25,7 +23,6 @@ import java.util.function.Function; public interface Container> extends LinkableContainer, ContainerState { - /** * @return a reference to this container instance, cast to the expected generic type. */ @@ -40,8 +37,11 @@ default SELF self() { @Value @AllArgsConstructor(access = AccessLevel.MODULE) class ExecResult { + int exitCode; + String stdout; + String stderr; } @@ -298,7 +298,11 @@ default SELF withEnv(String key, Function, String> mapper) { * @param mode access mode for the file * @return this */ - default SELF withClasspathResourceMapping(final String resourcePath, final String containerPath, final BindMode mode) { + default SELF withClasspathResourceMapping( + final String resourcePath, + final String containerPath, + final BindMode mode + ) { withClasspathResourceMapping(resourcePath, containerPath, mode, SelinuxContext.NONE); return self(); } @@ -313,7 +317,12 @@ default SELF withClasspathResourceMapping(final String resourcePath, final Strin * @param selinuxContext selinux context argument to use for this file * @return this */ - SELF withClasspathResourceMapping(String resourcePath, String containerPath, BindMode mode, SelinuxContext selinuxContext); + SELF withClasspathResourceMapping( + String resourcePath, + String containerPath, + BindMode mode, + SelinuxContext selinuxContext + ); /** * Set the duration of waiting time until container treated as started. @@ -403,7 +412,6 @@ default void followOutput(Consumer consumer, OutputFrame.OutputType LogUtils.followOutput(getDockerClient(), getContainerId(), consumer, types); } - /** * Attach an output consumer at container startup, enabling stdout and stderr to be followed, waited on, etc. *

diff --git a/core/src/main/java/org/testcontainers/containers/ContainerFetchException.java b/core/src/main/java/org/testcontainers/containers/ContainerFetchException.java index 66aa1a493b0..66414434786 100644 --- a/core/src/main/java/org/testcontainers/containers/ContainerFetchException.java +++ b/core/src/main/java/org/testcontainers/containers/ContainerFetchException.java @@ -4,6 +4,7 @@ * Created by rnorth on 15/10/2015. */ public class ContainerFetchException extends RuntimeException { + public ContainerFetchException(String s, Exception e) { super(s, e); } diff --git a/core/src/main/java/org/testcontainers/containers/ContainerState.java b/core/src/main/java/org/testcontainers/containers/ContainerState.java index 51eb4daddde..b82b5448d2f 100644 --- a/core/src/main/java/org/testcontainers/containers/ContainerState.java +++ b/core/src/main/java/org/testcontainers/containers/ContainerState.java @@ -37,7 +37,6 @@ import java.util.stream.Collectors; public interface ContainerState { - String STATE_HEALTHY = "healthy"; /** @@ -109,7 +108,9 @@ default boolean isHealthy() { InspectContainerResponse inspectContainerResponse = getCurrentContainerInfo(); HealthState health = inspectContainerResponse.getState().getHealth(); if (health == null) { - throw new RuntimeException("This container's image does not have a healthcheck declared, so health cannot be determined. Either amend the image or use another approach to determine whether containers are healthy."); + throw new RuntimeException( + "This container's image does not have a healthcheck declared, so health cannot be determined. Either amend the image or use another approach to determine whether containers are healthy." + ); } return STATE_HEALTHY.equals(health.getStatus()); @@ -145,7 +146,10 @@ default Integer getFirstMappedPort() { * @return the port that the exposed port is mapped to, or null if it is not exposed */ default Integer getMappedPort(int originalPort) { - Preconditions.checkState(this.getContainerId() != null, "Mapped port can only be obtained after the container is started"); + Preconditions.checkState( + this.getContainerId() != null, + "Mapped port can only be obtained after the container is started" + ); Ports.Binding[] binding = new Ports.Binding[0]; final InspectContainerResponse containerInfo = this.getContainerInfo(); @@ -183,7 +187,8 @@ default List getPortBindings() { * @return the bound port numbers */ default List getBoundPortNumbers() { - return getPortBindings().stream() + return getPortBindings() + .stream() .map(PortBinding::parse) .map(PortBinding::getBinding) .map(Ports.Binding::getHostPortSpec) @@ -194,7 +199,6 @@ default List getBoundPortNumbers() { .collect(Collectors.toList()); } - /** * @return all log output from the container from start until the current instant (both stdout and stderr) */ @@ -228,7 +232,8 @@ default String getContainerId() { *

* @see ExecInContainerPattern#execInContainer(com.github.dockerjava.api.command.InspectContainerResponse, String...) */ - default Container.ExecResult execInContainer(String... command) throws UnsupportedOperationException, IOException, InterruptedException { + default Container.ExecResult execInContainer(String... command) + throws UnsupportedOperationException, IOException, InterruptedException { return execInContainer(StandardCharsets.UTF_8, command); } @@ -237,7 +242,8 @@ default Container.ExecResult execInContainer(String... command) throws Unsupport *

* @see ExecInContainerPattern#execInContainer(com.github.dockerjava.api.command.InspectContainerResponse, Charset, String...) */ - default Container.ExecResult execInContainer(Charset outputCharset, String... command) throws UnsupportedOperationException, IOException, InterruptedException { + default Container.ExecResult execInContainer(Charset outputCharset, String... command) + throws UnsupportedOperationException, IOException, InterruptedException { return ExecInContainerPattern.execInContainer(getDockerClient(), getContainerInfo(), outputCharset, command); } @@ -253,7 +259,9 @@ default void copyFileToContainer(MountableFile mountableFile, String containerPa if (containerPath.endsWith("/") && sourceFile.isFile()) { final Logger logger = LoggerFactory.getLogger(GenericContainer.class); - logger.warn("folder-like containerPath in copyFileToContainer is deprecated, please explicitly specify a file path"); + logger.warn( + "folder-like containerPath in copyFileToContainer is deprecated, please explicitly specify a file path" + ); copyFileToContainer((Transferable) mountableFile, containerPath + sourceFile.getName()); } else { copyFileToContainer((Transferable) mountableFile, containerPath); @@ -299,13 +307,17 @@ default void copyFileToContainer(Transferable transferable, String containerPath * @throws IOException if there's an issue communicating with Docker or receiving entry from TarArchiveInputStream * @throws InterruptedException if the thread waiting for the response is interrupted */ - default void copyFileFromContainer(String containerPath, String destinationPath) throws IOException, InterruptedException { - copyFileFromContainer(containerPath, inputStream -> { - try(FileOutputStream output = new FileOutputStream(destinationPath)) { - IOUtils.copy(inputStream, output); - return null; + default void copyFileFromContainer(String containerPath, String destinationPath) + throws IOException, InterruptedException { + copyFileFromContainer( + containerPath, + inputStream -> { + try (FileOutputStream output = new FileOutputStream(destinationPath)) { + IOUtils.copy(inputStream, output); + return null; + } } - }); + ); } /** @@ -315,7 +327,7 @@ default void copyFileFromContainer(String containerPath, String destinationPath) * @param function function that takes InputStream of the copied file */ @SneakyThrows - default T copyFileFromContainer(String containerPath, ThrowingFunction function) { + default T copyFileFromContainer(String containerPath, ThrowingFunction function) { if (getContainerId() == null) { throw new IllegalStateException("copyFileFromContainer can only be used when the Container is created."); } diff --git a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java index 6f0a3a086b6..86a98586c7f 100644 --- a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java +++ b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java @@ -6,7 +6,9 @@ import com.github.dockerjava.transport.SSLConfig; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Joiner; +import com.google.common.base.Preconditions; import com.google.common.base.Splitter; +import com.google.common.base.Strings; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.google.common.util.concurrent.Uninterruptibles; @@ -57,42 +59,52 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; +import java.util.stream.Collectors; import java.util.stream.Stream; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.base.Strings.isNullOrEmpty; -import static java.util.stream.Collectors.joining; -import static java.util.stream.Collectors.toList; -import static org.testcontainers.containers.BindMode.READ_WRITE; - /** * Container which launches Docker Compose, for the purposes of launching a defined set of containers. */ @Slf4j -public class DockerComposeContainer> extends FailureDetectingExternalResource implements Startable { +public class DockerComposeContainer> + extends FailureDetectingExternalResource + implements Startable { /** * Random identifier which will become part of spawned containers names, so we can shut them down */ private final String identifier; + private final List composeFiles; + private DockerComposeFiles dockerComposeFiles; + private final Map scalingPreferences = new HashMap<>(); + private DockerClient dockerClient; + private boolean localCompose; + private boolean pull = true; + private boolean build = false; + private Set options = new HashSet<>(); + private boolean tailChildContainers; private String project; private final AtomicInteger nextAmbassadorPort = new AtomicInteger(2000); + private final Map> ambassadorPortMappings = new ConcurrentHashMap<>(); + private final Map serviceInstanceMap = new ConcurrentHashMap<>(); + private final Map waitStrategyMap = new ConcurrentHashMap<>(); + private final SocatContainer ambassadorContainer = new SocatContainer(); + private final Map>> logConsumers = new ConcurrentHashMap<>(); private static final Object MUTEX = new Object(); @@ -104,6 +116,7 @@ public class DockerComposeContainer> e * necessarily to containers that are spawned by Compose itself) */ private Map env = new HashMap<>(); + private RemoveImages removeImages; @Deprecated @@ -148,13 +161,11 @@ public void starting(Description description) { @Override @Deprecated - protected void succeeded(Description description) { - } + protected void succeeded(Description description) {} @Override @Deprecated - protected void failed(Throwable e, Description description) { - } + protected void failed(Throwable e, Description description) {} @Override @Deprecated @@ -183,15 +194,23 @@ private void pullImages() { // Pull images using our docker client rather than compose itself, // (a) as a workaround for https://github.com/docker/compose/issues/5854, which prevents authenticated image pulls being possible when credential helpers are in use // (b) so that credential helper-based auth still works when compose is running from within a container - dockerComposeFiles.getDependencyImages() + dockerComposeFiles + .getDependencyImages() .forEach(imageName -> { try { - log.info("Preemptively checking local images for '{}', referenced via a compose file or transitive Dockerfile. If not available, it will be pulled.", imageName); + log.info( + "Preemptively checking local images for '{}', referenced via a compose file or transitive Dockerfile. If not available, it will be pulled.", + imageName + ); new RemoteDockerImage(DockerImageName.parse(imageName)) .withImageNameSubstitutor(ImageNameSubstitutor.noop()) .get(); } catch (Exception e) { - log.warn("Unable to pre-fetch an image ({}) depended upon by Docker Compose build - startup will continue but may fail. Exception message was: {}", imageName, e.getMessage()); + log.warn( + "Unable to pre-fetch an image ({}) depended upon by Docker Compose build - startup will continue but may fail. Exception message was: {}", + imageName, + e.getMessage() + ); } }); } @@ -203,18 +222,21 @@ public SELF withServices(@NonNull String... services) { private void createServices() { // services that have been explicitly requested to be started. If empty, all services should be started. - final String serviceNameArgs = Stream.concat( - services.stream(), // services that have been specified with `withServices` - scalingPreferences.keySet().stream() // services that are implicitly needed via `withScaledService` - ) + final String serviceNameArgs = Stream + .concat( + services.stream(), // services that have been specified with `withServices` + scalingPreferences.keySet().stream() // services that are implicitly needed via `withScaledService` + ) .distinct() - .collect(joining(" ")); + .collect(Collectors.joining(" ")); // Apply scaling for the services specified using `withScaledService` - final String scalingOptions = scalingPreferences.entrySet().stream() + final String scalingOptions = scalingPreferences + .entrySet() + .stream() .map(entry -> "--scale " + entry.getKey() + "=" + entry.getValue()) .distinct() - .collect(joining(" ")); + .collect(Collectors.joining(" ")); String command = optionsAsString() + "up -d"; @@ -222,11 +244,11 @@ private void createServices() { command += " --build"; } - if (!isNullOrEmpty(scalingOptions)) { + if (!Strings.isNullOrEmpty(scalingOptions)) { command += " " + scalingOptions; } - if (!isNullOrEmpty(serviceNameArgs)) { + if (!Strings.isNullOrEmpty(serviceNameArgs)) { command += " " + serviceNameArgs; } @@ -235,10 +257,8 @@ private void createServices() { } private String optionsAsString() { - String optionsString = options - .stream() - .collect(joining(" ")); - if (optionsString.length() !=0 ) { + String optionsString = options.stream().collect(Collectors.joining(" ")); + if (optionsString.length() != 0) { // ensures that there is a space between the options and 'up' if options are passed. return optionsString + " "; } else { @@ -252,15 +272,17 @@ private void waitUntilServiceStarted() { Set servicesToWaitFor = waitStrategyMap.keySet(); Set instantiatedServices = serviceInstanceMap.keySet(); - Sets.SetView missingServiceInstances = - Sets.difference(servicesToWaitFor, instantiatedServices); + Sets.SetView missingServiceInstances = Sets.difference(servicesToWaitFor, instantiatedServices); if (!missingServiceInstances.isEmpty()) { throw new IllegalStateException( - "Services named " + missingServiceInstances + " " + - "do not exist, but wait conditions have been defined " + - "for them. This might mean that you misspelled " + - "the service name when defining the wait condition."); + "Services named " + + missingServiceInstances + + " " + + "do not exist, but wait conditions have been defined " + + "for them. This might mean that you misspelled " + + "the service name when defining the wait condition." + ); } serviceInstanceMap.forEach(this::waitUntilServiceStarted); @@ -268,15 +290,21 @@ private void waitUntilServiceStarted() { private void createServiceInstance(Container container) { String serviceName = getServiceNameFromContainer(container); - final ComposeServiceWaitStrategyTarget containerInstance = new ComposeServiceWaitStrategyTarget(dockerClient, container, - ambassadorContainer, ambassadorPortMappings.getOrDefault(serviceName, new HashMap<>())); + final ComposeServiceWaitStrategyTarget containerInstance = new ComposeServiceWaitStrategyTarget( + dockerClient, + container, + ambassadorContainer, + ambassadorPortMappings.getOrDefault(serviceName, new HashMap<>()) + ); String containerId = containerInstance.getContainerId(); if (tailChildContainers) { followLogs(containerId, new Slf4jLogConsumer(log).withPrefix(container.getNames()[0])); } //follow logs using registered consumers for this service - logConsumers.getOrDefault(serviceName, Collections.emptyList()).forEach(consumer -> followLogs(containerId, consumer)); + logConsumers + .getOrDefault(serviceName, Collections.emptyList()) + .forEach(consumer -> followLogs(containerId, consumer)); serviceInstanceMap.putIfAbsent(serviceName, containerInstance); } @@ -294,8 +322,8 @@ private String getServiceNameFromContainer(Container container) { } private void runWithCompose(String cmd) { - checkNotNull(composeFiles); - checkArgument(!composeFiles.isEmpty(), "No docker compose file have been provided"); + Preconditions.checkNotNull(composeFiles); + Preconditions.checkArgument(!composeFiles.isEmpty(), "No docker compose file have been provided"); final DockerCompose dockerCompose; if (localCompose) { @@ -304,26 +332,24 @@ private void runWithCompose(String cmd) { dockerCompose = new ContainerisedDockerCompose(composeFiles, project); } - dockerCompose - .withCommand(cmd) - .withEnv(env) - .invoke(); + dockerCompose.withCommand(cmd).withEnv(env).invoke(); } private void registerContainersForShutdown() { - ResourceReaper.instance().registerLabelsFilterForCleanup(Collections.singletonMap( - "com.docker.compose.project", project - )); + ResourceReaper + .instance() + .registerLabelsFilterForCleanup(Collections.singletonMap("com.docker.compose.project", project)); } @VisibleForTesting List listChildContainers() { - return dockerClient.listContainersCmd() + return dockerClient + .listContainersCmd() .withShowAll(true) - .exec().stream() - .filter(container -> Arrays.stream(container.getNames()).anyMatch(name -> - name.startsWith("/" + project))) - .collect(toList()); + .exec() + .stream() + .filter(container -> Arrays.stream(container.getNames()).anyMatch(name -> name.startsWith("/" + project))) + .collect(Collectors.toList()); } private void startAmbassadorContainers() { @@ -345,7 +371,6 @@ public void stop() { cmd += " --rmi " + removeImages.dockerRemoveImagesType(); } runWithCompose(cmd); - } finally { project = randomProjectId(); } @@ -360,12 +385,16 @@ public DockerComposeContainer withExposedService(String serviceName, int instanc return withExposedService(serviceName + "_" + instance, servicePort); } - public DockerComposeContainer withExposedService(String serviceName, int instance, int servicePort, WaitStrategy waitStrategy) { + public DockerComposeContainer withExposedService( + String serviceName, + int instance, + int servicePort, + WaitStrategy waitStrategy + ) { return withExposedService(serviceName + "_" + instance, servicePort, waitStrategy); } public SELF withExposedService(String serviceName, int servicePort, @NonNull WaitStrategy waitStrategy) { - String serviceInstanceName = getServiceInstanceName(serviceName); /* @@ -384,7 +413,9 @@ public SELF withExposedService(String serviceName, int servicePort, @NonNull Wai // Ambassador container will be started together after docker compose has started int ambassadorPort = nextAmbassadorPort.getAndIncrement(); - ambassadorPortMappings.computeIfAbsent(serviceInstanceName, __ -> new ConcurrentHashMap<>()).put(servicePort, ambassadorPort); + ambassadorPortMappings + .computeIfAbsent(serviceInstanceName, __ -> new ConcurrentHashMap<>()) + .put(servicePort, ambassadorPort); ambassadorContainer.withTarget(ambassadorPort, serviceInstanceName, servicePort); ambassadorContainer.addLink(new FutureContainer(this.project + "_" + serviceInstanceName), serviceInstanceName); addWaitStrategy(serviceInstanceName, waitStrategy); @@ -405,8 +436,13 @@ private String getServiceInstanceName(String serviceName) { * The WaitAllStrategy uses an long timeout, because timeouts should be handled by the inner strategies. */ private void addWaitStrategy(String serviceInstanceName, @NonNull WaitStrategy waitStrategy) { - final WaitAllStrategy waitAllStrategy = waitStrategyMap.computeIfAbsent(serviceInstanceName, __ -> - new WaitAllStrategy(WaitAllStrategy.Mode.WITH_MAXIMUM_OUTER_TIMEOUT).withStartupTimeout(Duration.ofMinutes(30))); + final WaitAllStrategy waitAllStrategy = waitStrategyMap.computeIfAbsent( + serviceInstanceName, + __ -> { + return new WaitAllStrategy(WaitAllStrategy.Mode.WITH_MAXIMUM_OUTER_TIMEOUT) + .withStartupTimeout(Duration.ofMinutes(30)); + } + ); waitAllStrategy.withStrategy(waitStrategy); } @@ -452,9 +488,17 @@ public Integer getServicePort(String serviceName, Integer servicePort) { Map portMap = ambassadorPortMappings.get(getServiceInstanceName(serviceName)); if (portMap == null) { - throw new IllegalArgumentException("Could not get a port for '" + serviceName + "'. " + - "Testcontainers does not have an exposed port configured for '" + serviceName + "'. " + - "To fix, please ensure that the service '" + serviceName + "' has ports exposed using .withExposedService(...)"); + throw new IllegalArgumentException( + "Could not get a port for '" + + serviceName + + "'. " + + "Testcontainers does not have an exposed port configured for '" + + serviceName + + "'. " + + "To fix, please ensure that the service '" + + serviceName + + "' has ports exposed using .withExposedService(...)" + ); } else { return ambassadorContainer.getMappedPort(portMap.get(servicePort)); } @@ -517,7 +561,8 @@ public SELF withTailChildContainers(boolean tailChildContainers) { */ public SELF withLogConsumer(String serviceName, Consumer consumer) { String serviceInstanceName = getServiceInstanceName(serviceName); - final List> consumers = this.logConsumers.getOrDefault(serviceInstanceName, new ArrayList<>()); + final List> consumers = + this.logConsumers.getOrDefault(serviceInstanceName, new ArrayList<>()); consumers.add(consumer); this.logConsumers.putIfAbsent(serviceInstanceName, consumers); return self(); @@ -609,34 +654,39 @@ interface DockerCompose { class ContainerisedDockerCompose extends GenericContainer implements DockerCompose { public static final char UNIX_PATH_SEPERATOR = ':'; + public static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("docker/compose:1.29.2"); public ContainerisedDockerCompose(List composeFiles, String identifier) { - super(DEFAULT_IMAGE_NAME); addEnv(ENV_PROJECT_NAME, identifier); // Map the docker compose file into the container final File dockerComposeBaseFile = composeFiles.get(0); final String pwd = dockerComposeBaseFile.getAbsoluteFile().getParentFile().getAbsolutePath(); - final String containerPwd = convertToUnixFilesystemPath(pwd); + final String containerPwd = convertToUnixFilesystemPath(pwd); - final List absoluteDockerComposeFiles = composeFiles.stream() + final List absoluteDockerComposeFiles = composeFiles + .stream() .map(File::getAbsolutePath) .map(MountableFile::forHostPath) .map(MountableFile::getFilesystemPath) .map(this::convertToUnixFilesystemPath) - .collect(toList()); + .collect(Collectors.toList()); final String composeFileEnvVariableValue = Joiner.on(UNIX_PATH_SEPERATOR).join(absoluteDockerComposeFiles); // we always need the UNIX path separator logger().debug("Set env COMPOSE_FILE={}", composeFileEnvVariableValue); addEnv(ENV_COMPOSE_FILE, composeFileEnvVariableValue); - addFileSystemBind(pwd, containerPwd, READ_WRITE); + addFileSystemBind(pwd, containerPwd, BindMode.READ_WRITE); // Ensure that compose can access docker. Since the container is assumed to be running on the same machine // as the docker daemon, just mapping the docker control socket is OK. // As there seems to be a problem with mapping to the /var/run directory in certain environments (e.g. CircleCI) // we map the socket file outside of /var/run, as just /docker.sock - addFileSystemBind(DockerClientFactory.instance().getRemoteDockerUnixSocketPath(), "/docker.sock", READ_WRITE); + addFileSystemBind( + DockerClientFactory.instance().getRemoteDockerUnixSocketPath(), + "/docker.sock", + BindMode.READ_WRITE + ); addEnv("DOCKER_HOST", "unix:///docker.sock"); setStartupCheckStrategy(new IndefiniteWaitOneShotStartupCheckStrategy()); setWorkingDirectory(containerPwd); @@ -649,7 +699,8 @@ public void invoke() { this.followOutput(new Slf4jLogConsumer(logger())); // wait for the compose container to stop, which should only happen after it has spawned all the service containers - logger().info("Docker Compose container is running for command: {}", Joiner.on(" ").join(this.getCommandParts())); + logger() + .info("Docker Compose container is running for command: {}", Joiner.on(" ").join(this.getCommandParts())); while (this.isRunning()) { logger().trace("Compose container is still running"); Uninterruptibles.sleepUninterruptibly(100, TimeUnit.MILLISECONDS); @@ -658,7 +709,8 @@ public void invoke() { AuditLogger.doComposeLog(this.getCommandParts(), this.getEnv()); - final Integer exitCode = getDockerClient().inspectContainerCmd(getContainerId()) + final Integer exitCode = getDockerClient() + .inspectContainerCmd(getContainerId()) .exec() .getState() .getExitCode(); @@ -668,14 +720,13 @@ public void invoke() { "Containerised Docker Compose exited abnormally with code " + exitCode + " whilst running command: " + - StringUtils.join(this.getCommandParts(), ' ')); + StringUtils.join(this.getCommandParts(), ' ') + ); } } private String convertToUnixFilesystemPath(String path) { - return SystemUtils.IS_OS_WINDOWS - ? PathUtils.createMinGWPath(path).substring(1) - : path; + return SystemUtils.IS_OS_WINDOWS ? PathUtils.createMinGWPath(path).substring(1) : path; } } @@ -683,14 +734,20 @@ private String convertToUnixFilesystemPath(String path) { * Use local Docker Compose binary, if present. */ class LocalDockerCompose implements DockerCompose { + /** * Executable name for Docker Compose. */ - private static final String COMPOSE_EXECUTABLE = SystemUtils.IS_OS_WINDOWS ? "docker-compose.exe" : "docker-compose"; + private static final String COMPOSE_EXECUTABLE = SystemUtils.IS_OS_WINDOWS + ? "docker-compose.exe" + : "docker-compose"; private final List composeFiles; + private final String identifier; + private String cmd = ""; + private Map env = new HashMap<>(); public LocalDockerCompose(List composeFiles, String identifier) { @@ -719,7 +776,9 @@ static boolean executableExists() { public void invoke() { // bail out early if (!executableExists()) { - throw new ContainerLaunchException("Local Docker Compose not found. Is " + COMPOSE_EXECUTABLE + " on the PATH?"); + throw new ContainerLaunchException( + "Local Docker Compose not found. Is " + COMPOSE_EXECUTABLE + " on the PATH?" + ); } final Map environment = Maps.newHashMap(env); @@ -732,18 +791,23 @@ public void invoke() { environment.put("DOCKER_CERT_PATH", ((LocalDirectorySSLConfig) sslConfig).getDockerCertPath()); environment.put("DOCKER_TLS_VERIFY", "true"); } else { - logger().warn("Couldn't set DOCKER_CERT_PATH. `sslConfig` is present but it's not LocalDirectorySSLConfig."); + logger() + .warn( + "Couldn't set DOCKER_CERT_PATH. `sslConfig` is present but it's not LocalDirectorySSLConfig." + ); } } String dockerHost = transportConfig.getDockerHost().toString(); environment.put("DOCKER_HOST", dockerHost); - final Stream absoluteDockerComposeFilePaths = composeFiles.stream() + final Stream absoluteDockerComposeFilePaths = composeFiles + .stream() .map(File::getAbsolutePath) .map(Objects::toString); final String composeFileEnvVariableValue = absoluteDockerComposeFilePaths.collect( - joining(File.pathSeparator + "")); + Collectors.joining(File.pathSeparator + "") + ); logger().debug("Set env COMPOSE_FILE={}", composeFileEnvVariableValue); final File pwd = composeFiles.get(0).getAbsoluteFile().getParentFile().getAbsoluteFile(); @@ -751,12 +815,14 @@ public void invoke() { logger().info("Local Docker Compose is running command: {}", cmd); - final List command = Splitter.onPattern(" ") + final List command = Splitter + .onPattern(" ") .omitEmptyStrings() .splitToList(COMPOSE_EXECUTABLE + " " + cmd); try { - new ProcessExecutor().command(command) + new ProcessExecutor() + .command(command) .redirectOutput(Slf4jStream.of(logger()).asInfo()) .redirectError(Slf4jStream.of(logger()).asInfo()) // docker-compose will log pull information to stderr .environment(environment) @@ -765,11 +831,13 @@ public void invoke() { .executeNoTimeout(); logger().info("Docker Compose has finished running"); - } catch (InvalidExitValueException e) { - throw new ContainerLaunchException("Local Docker Compose exited abnormally with code " + - e.getExitValue() + " whilst running command: " + cmd); - + throw new ContainerLaunchException( + "Local Docker Compose exited abnormally with code " + + e.getExitValue() + + " whilst running command: " + + cmd + ); } catch (Exception e) { throw new ContainerLaunchException("Error running local Docker Compose command: " + cmd, e); } diff --git a/core/src/main/java/org/testcontainers/containers/DockerComposeFiles.java b/core/src/main/java/org/testcontainers/containers/DockerComposeFiles.java index 76754df5118..b96ebfb005e 100644 --- a/core/src/main/java/org/testcontainers/containers/DockerComposeFiles.java +++ b/core/src/main/java/org/testcontainers/containers/DockerComposeFiles.java @@ -33,7 +33,9 @@ private Map> mergeServiceDependencyImageNames() { } private Set getImageNames(Map> serviceToImageNames) { - return serviceToImageNames.values().stream() + return serviceToImageNames + .values() + .stream() .flatMap(Collection::stream) // Pass through DockerImageName to convert image names to canonical form (e.g. making implicit latest tag explicit) .map(DockerImageName::parse) diff --git a/core/src/main/java/org/testcontainers/containers/ExecInContainerPattern.java b/core/src/main/java/org/testcontainers/containers/ExecInContainerPattern.java index 0858476880f..fe641618cc4 100644 --- a/core/src/main/java/org/testcontainers/containers/ExecInContainerPattern.java +++ b/core/src/main/java/org/testcontainers/containers/ExecInContainerPattern.java @@ -23,7 +23,6 @@ @Slf4j public class ExecInContainerPattern { - /** * * @deprecated use {@link #execInContainer(DockerClient, InspectContainerResponse, String...)} @@ -40,8 +39,11 @@ public Container.ExecResult execInContainer(InspectContainerResponse containerIn * @deprecated use {@link #execInContainer(DockerClient, InspectContainerResponse, Charset, String...)} */ @Deprecated - public Container.ExecResult execInContainer(InspectContainerResponse containerInfo, Charset outputCharset, String... command) - throws UnsupportedOperationException, IOException, InterruptedException { + public Container.ExecResult execInContainer( + InspectContainerResponse containerInfo, + Charset outputCharset, + String... command + ) throws UnsupportedOperationException, IOException, InterruptedException { DockerClient dockerClient = DockerClientFactory.instance().client(); return execInContainer(dockerClient, containerInfo, outputCharset, command); } @@ -55,8 +57,11 @@ public Container.ExecResult execInContainer(InspectContainerResponse containerIn * @param command the command to execute * @see #execInContainer(DockerClient, InspectContainerResponse, Charset, String...) */ - public Container.ExecResult execInContainer(DockerClient dockerClient, InspectContainerResponse containerInfo, String... command) - throws UnsupportedOperationException, IOException, InterruptedException { + public Container.ExecResult execInContainer( + DockerClient dockerClient, + InspectContainerResponse containerInfo, + String... command + ) throws UnsupportedOperationException, IOException, InterruptedException { return execInContainer(dockerClient, containerInfo, StandardCharsets.UTF_8, command); } @@ -74,13 +79,17 @@ public Container.ExecResult execInContainer(DockerClient dockerClient, InspectCo * @throws InterruptedException if the thread waiting for the response is interrupted * @throws UnsupportedOperationException if the docker daemon you're connecting to doesn't support "exec". */ - public Container.ExecResult execInContainer(DockerClient dockerClient, InspectContainerResponse containerInfo, Charset outputCharset, String... command) - throws UnsupportedOperationException, IOException, InterruptedException { + public Container.ExecResult execInContainer( + DockerClient dockerClient, + InspectContainerResponse containerInfo, + Charset outputCharset, + String... command + ) throws UnsupportedOperationException, IOException, InterruptedException { if (!TestEnvironment.dockerExecutionDriverSupportsExec()) { // at time of writing, this is the expected result in CircleCI. throw new UnsupportedOperationException( - "Your docker daemon is running the \"lxc\" driver, which doesn't support \"docker exec\"."); - + "Your docker daemon is running the \"lxc\" driver, which doesn't support \"docker exec\"." + ); } if (!isRunning(containerInfo)) { @@ -91,8 +100,12 @@ public Container.ExecResult execInContainer(DockerClient dockerClient, InspectCo String containerName = containerInfo.getName(); log.debug("{}: Running \"exec\" command: {}", containerName, String.join(" ", command)); - final ExecCreateCmdResponse execCreateCmdResponse = dockerClient.execCreateCmd(containerId) - .withAttachStdout(true).withAttachStderr(true).withCmd(command).exec(); + final ExecCreateCmdResponse execCreateCmdResponse = dockerClient + .execCreateCmd(containerId) + .withAttachStdout(true) + .withAttachStderr(true) + .withCmd(command) + .exec(); final ToStringConsumer stdoutConsumer = new ToStringConsumer(); final ToStringConsumer stderrConsumer = new ToStringConsumer(); @@ -108,7 +121,8 @@ public Container.ExecResult execInContainer(DockerClient dockerClient, InspectCo final Container.ExecResult result = new Container.ExecResult( exitCode, stdoutConsumer.toString(outputCharset), - stderrConsumer.toString(outputCharset)); + stderrConsumer.toString(outputCharset) + ); log.trace("{}: stdout: {}", containerName, result.getStdout()); log.trace("{}: stderr: {}", containerName, result.getStderr()); diff --git a/core/src/main/java/org/testcontainers/containers/FailureDetectingExternalResource.java b/core/src/main/java/org/testcontainers/containers/FailureDetectingExternalResource.java index 29a032d320b..71344267d32 100644 --- a/core/src/main/java/org/testcontainers/containers/FailureDetectingExternalResource.java +++ b/core/src/main/java/org/testcontainers/containers/FailureDetectingExternalResource.java @@ -16,13 +16,12 @@ * figures). */ public class FailureDetectingExternalResource implements TestRule { + @Override public Statement apply(Statement base, Description description) { - return new Statement() { @Override public void evaluate() throws Throwable { - List errors = new ArrayList(); try { @@ -41,16 +40,11 @@ public void evaluate() throws Throwable { }; } - protected void starting(Description description) { - - } + protected void starting(Description description) {} - protected void succeeded(Description description) { - } + protected void succeeded(Description description) {} - protected void failed(Throwable e, Description description) { - } + protected void failed(Throwable e, Description description) {} - protected void finished(Description description) { - } + protected void finished(Description description) {} } diff --git a/core/src/main/java/org/testcontainers/containers/FixedHostPortGenericContainer.java b/core/src/main/java/org/testcontainers/containers/FixedHostPortGenericContainer.java index 6477c28042e..db610ad57c2 100644 --- a/core/src/main/java/org/testcontainers/containers/FixedHostPortGenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/FixedHostPortGenericContainer.java @@ -11,7 +11,8 @@ *

Callers are responsible for ensuring that this fixed port is actually available; failure will occur if it is * not available - which could manifest as flaky or unstable tests.

*/ -public class FixedHostPortGenericContainer> extends GenericContainer { +public class FixedHostPortGenericContainer> + extends GenericContainer { /** * @deprecated it is highly recommended that {@link FixedHostPortGenericContainer} not be used, as it risks port conflicts. @@ -28,7 +29,6 @@ public FixedHostPortGenericContainer(@NotNull String dockerImageName) { * @return this container */ public SELF withFixedExposedPort(int hostPort, int containerPort) { - return withFixedExposedPort(hostPort, containerPort, InternetProtocol.TCP); } @@ -40,7 +40,6 @@ public SELF withFixedExposedPort(int hostPort, int containerPort) { * @return this container */ public SELF withFixedExposedPort(int hostPort, int containerPort, InternetProtocol protocol) { - super.addFixedExposedPort(hostPort, containerPort, protocol); return self(); diff --git a/core/src/main/java/org/testcontainers/containers/FutureContainer.java b/core/src/main/java/org/testcontainers/containers/FutureContainer.java index b2269b534c1..28cfbfff6c9 100644 --- a/core/src/main/java/org/testcontainers/containers/FutureContainer.java +++ b/core/src/main/java/org/testcontainers/containers/FutureContainer.java @@ -8,5 +8,6 @@ */ @Data public class FutureContainer implements LinkableContainer { + private final String containerName; } diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index 4e5e682738d..7163ea40a93 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -20,6 +20,7 @@ import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Strings; import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Lists; import com.google.common.hash.Hashing; import lombok.AccessLevel; import lombok.Data; @@ -27,7 +28,6 @@ import lombok.NonNull; import lombok.Setter; import lombok.SneakyThrows; -import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.SystemUtils; import org.jetbrains.annotations.NotNull; @@ -56,6 +56,7 @@ import org.testcontainers.lifecycle.TestDescription; import org.testcontainers.lifecycle.TestLifecycleAware; import org.testcontainers.utility.Base58; +import org.testcontainers.utility.CommandLine; import org.testcontainers.utility.DockerImageName; import org.testcontainers.utility.DockerLoggerFactory; import org.testcontainers.utility.DockerMachineClient; @@ -66,12 +67,10 @@ import org.testcontainers.utility.TestcontainersConfiguration; import java.io.File; -import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.UndeclaredThrowableException; import java.nio.charset.Charset; -import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.time.Duration; @@ -101,17 +100,15 @@ import java.util.zip.Adler32; import java.util.zip.Checksum; -import static com.google.common.collect.Lists.newArrayList; import static org.awaitility.Awaitility.await; -import static org.testcontainers.utility.CommandLine.runShellCommand; /** * Base class for that allows a container to be launched and controlled. */ @Data public class GenericContainer> - extends FailureDetectingExternalResource - implements Container, AutoCloseable, WaitStrategyTarget, Startable { + extends FailureDetectingExternalResource + implements Container, AutoCloseable, WaitStrategyTarget, Startable { private static final Charset UTF8 = Charset.forName("UTF-8"); @@ -142,9 +139,7 @@ public class GenericContainer> private Network network; @NonNull - private List networkAliases = new ArrayList<>(Arrays.asList( - "tc-" + Base58.randomString(8) - )); + private List networkAliases = new ArrayList<>(Arrays.asList("tc-" + Base58.randomString(8))); @NonNull private RemoteDockerImage image; @@ -228,6 +223,7 @@ public class GenericContainer> private final Set> createContainerCmdModifiers = new LinkedHashSet<>(); private static final Set AVAILABLE_IMAGE_NAME_CACHE = new HashSet<>(); + private static final RateLimiter DOCKER_CLIENT_RATE_LIMITER = RateLimiterBuilder .newBuilder() .withRate(1, TimeUnit.SECONDS) @@ -315,7 +311,7 @@ public String getContainerId() { * Starts the container using docker, pulling an image if necessary. */ @Override - @SneakyThrows({InterruptedException.class, ExecutionException.class}) + @SneakyThrows({ InterruptedException.class, ExecutionException.class }) public void start() { if (containerId != null) { return; @@ -335,12 +331,20 @@ protected void doStart() { logger().debug("Starting container: {}", getDockerImageName()); AtomicInteger attempt = new AtomicInteger(0); - Unreliables.retryUntilSuccess(startupAttempts, () -> { - logger().debug("Trying to start container: {} (attempt {}/{})", getDockerImageName(), attempt.incrementAndGet(), startupAttempts); - tryStart(startedAt); - return true; - }); - + Unreliables.retryUntilSuccess( + startupAttempts, + () -> { + logger() + .debug( + "Trying to start container: {} (attempt {}/{})", + getDockerImageName(), + attempt.incrementAndGet(), + startupAttempts + ); + tryStart(startedAt); + return true; + } + ); } catch (Exception e) { throw new ContainerLaunchException("Container startup failed", e); } @@ -383,10 +387,9 @@ private void tryStart(Instant startedAt) { } if (TestcontainersConfiguration.getInstance().environmentSupportsReuse()) { - createCommand.getLabels().put( - COPIED_FILES_HASH_LABEL, - Long.toHexString(hashCopiedFiles().getValue()) - ); + createCommand + .getLabels() + .put(COPIED_FILES_HASH_LABEL, Long.toHexString(hashCopiedFiles().getValue())); String hash = hash(createCommand); @@ -402,12 +405,13 @@ private void tryStart(Instant startedAt) { } reusable = true; } else { - logger().warn( - "" + + logger() + .warn( + "" + "Reuse was requested but the environment does not support the reuse of containers\n" + "To enable reuse of containers, you must set 'testcontainers.reuse.enable=true' in a file located at {}", - Paths.get(System.getProperty("user.home"), ".testcontainers.properties") - ); + Paths.get(System.getProperty("user.home"), ".testcontainers.properties") + ); reusable = false; } } else { @@ -443,27 +447,28 @@ private void tryStart(Instant startedAt) { this.logConsumers.forEach(this::followOutput); // Wait until inspect container returns the mapped ports - containerInfo = await() - .atMost(5, TimeUnit.SECONDS) - .pollInterval(DynamicPollInterval.ofMillis(50)) - .pollInSameThread() - .until( - () -> dockerClient.inspectContainerCmd(containerId).exec(), - inspectContainerResponse -> { - Set exposedAndMappedPorts = inspectContainerResponse - .getNetworkSettings() - .getPorts() - .getBindings() - .entrySet() - .stream() - .filter(it -> Objects.nonNull(it.getValue())) // filter out exposed but not yet mapped - .map(Entry::getKey) - .map(ExposedPort::getPort) - .collect(Collectors.toSet()); - - return exposedAndMappedPorts.containsAll(exposedPorts); - } - ); + containerInfo = + await() + .atMost(5, TimeUnit.SECONDS) + .pollInterval(DynamicPollInterval.ofMillis(50)) + .pollInSameThread() + .until( + () -> dockerClient.inspectContainerCmd(containerId).exec(), + inspectContainerResponse -> { + Set exposedAndMappedPorts = inspectContainerResponse + .getNetworkSettings() + .getPorts() + .getBindings() + .entrySet() + .stream() + .filter(it -> Objects.nonNull(it.getValue())) // filter out exposed but not yet mapped + .map(Entry::getKey) + .map(ExposedPort::getPort) + .collect(Collectors.toSet()); + + return exposedAndMappedPorts.containsAll(exposedPorts); + } + ); // Tell subclasses that we're starting containerIsStarting(containerInfo, reused); @@ -541,9 +546,11 @@ private void tryStart(Instant startedAt) { @VisibleForTesting Checksum hashCopiedFiles() { Checksum checksum = new Adler32(); - Stream.of(copyToFileContainerPathMap, copyToTransferableContainerPathMap) + Stream + .of(copyToFileContainerPathMap, copyToTransferableContainerPathMap) .flatMap(it -> it.entrySet().stream()) - .sorted(Entry.comparingByValue()).forEach(entry -> { + .sorted(Entry.comparingByValue()) + .forEach(entry -> { byte[] pathBytes = entry.getValue().getBytes(); // Add path to the hash checksum.update(pathBytes, 0, pathBytes.length); @@ -558,7 +565,8 @@ Checksum hashCopiedFiles() { final String hash(CreateContainerCmd createCommand) { DefaultDockerClientConfig dockerClientConfig = DefaultDockerClientConfig.createDefaultConfigBuilder().build(); - byte[] commandJson = dockerClientConfig.getObjectMapper() + byte[] commandJson = dockerClientConfig + .getObjectMapper() .copy() .enable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY) .enable(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS) @@ -571,7 +579,8 @@ final String hash(CreateContainerCmd createCommand) { @VisibleForTesting Optional findContainerForReuse(String hash) { // TODO locking - return dockerClient.listContainersCmd() + return dockerClient + .listContainersCmd() .withLabelFilter(ImmutableMap.of(HASH_LABEL, hash)) .withLimit(1) .withStatusFilter(Arrays.asList("running")) @@ -596,11 +605,14 @@ private HostConfig buildHostConfig() { } private void connectToPortForwardingNetwork(String networkMode) { - PortForwardingContainer.INSTANCE.getNetwork().map(ContainerNetwork::getNetworkID).ifPresent(networkId -> { - if (!Arrays.asList(networkId, "none", "host").contains(networkMode)) { - dockerClient.connectToNetworkCmd().withContainerId(containerId).withNetworkId(networkId).exec(); - } - }); + PortForwardingContainer.INSTANCE + .getNetwork() + .map(ContainerNetwork::getNetworkID) + .ifPresent(networkId -> { + if (!Arrays.asList(networkId, "none", "host").contains(networkMode)) { + dockerClient.connectToNetworkCmd().withContainerId(containerId).withNetworkId(networkId).exec(); + } + }); } /** @@ -608,7 +620,6 @@ private void connectToPortForwardingNetwork(String networkMode) { */ @Override public void stop() { - if (containerId == null) { return; } @@ -651,35 +662,40 @@ protected Path createVolumeDirectory(boolean temporary) { Path directory = new File(".tmp-volume-" + UUID.randomUUID()).toPath(); PathUtils.mkdirp(directory); - if (temporary) Runtime.getRuntime().addShutdownHook(new Thread(DockerClientFactory.TESTCONTAINERS_THREAD_GROUP, () -> { - PathUtils.recursiveDeleteDir(directory); - })); + if (temporary) { + Runtime + .getRuntime() + .addShutdownHook( + new Thread( + DockerClientFactory.TESTCONTAINERS_THREAD_GROUP, + () -> { + PathUtils.recursiveDeleteDir(directory); + } + ) + ); + } return directory; } - protected void configure() { - } + protected void configure() {} - @SuppressWarnings({"EmptyMethod", "UnusedParameters"}) - protected void containerIsCreated(String containerId) { - } + @SuppressWarnings({ "EmptyMethod", "UnusedParameters" }) + protected void containerIsCreated(String containerId) {} - @SuppressWarnings({"EmptyMethod", "UnusedParameters"}) - protected void containerIsStarting(InspectContainerResponse containerInfo) { - } + @SuppressWarnings({ "EmptyMethod", "UnusedParameters" }) + protected void containerIsStarting(InspectContainerResponse containerInfo) {} - @SuppressWarnings({"EmptyMethod", "UnusedParameters"}) + @SuppressWarnings({ "EmptyMethod", "UnusedParameters" }) @UnstableAPI protected void containerIsStarting(InspectContainerResponse containerInfo, boolean reused) { containerIsStarting(containerInfo); } - @SuppressWarnings({"EmptyMethod", "UnusedParameters"}) - protected void containerIsStarted(InspectContainerResponse containerInfo) { - } + @SuppressWarnings({ "EmptyMethod", "UnusedParameters" }) + protected void containerIsStarted(InspectContainerResponse containerInfo) {} - @SuppressWarnings({"EmptyMethod", "UnusedParameters"}) + @SuppressWarnings({ "EmptyMethod", "UnusedParameters" }) @UnstableAPI protected void containerIsStarted(InspectContainerResponse containerInfo, boolean reused) { containerIsStarted(containerInfo); @@ -690,18 +706,16 @@ protected void containerIsStarted(InspectContainerResponse containerInfo, boolea * Warning! This hook won't be executed if the container is terminated during * the JVM's shutdown hook or by Ryuk. */ - @SuppressWarnings({"EmptyMethod", "UnusedParameters"}) - protected void containerIsStopping(InspectContainerResponse containerInfo) { - } + @SuppressWarnings({ "EmptyMethod", "UnusedParameters" }) + protected void containerIsStopping(InspectContainerResponse containerInfo) {} /** * A hook that is executed after the container is stopped with {@link #stop()}. * Warning! This hook won't be executed if the container is terminated during * the JVM's shutdown hook or by Ryuk. */ - @SuppressWarnings({"EmptyMethod", "UnusedParameters"}) - protected void containerIsStopped(InspectContainerResponse containerInfo) { - } + @SuppressWarnings({ "EmptyMethod", "UnusedParameters" }) + protected void containerIsStopped(InspectContainerResponse containerInfo) {} /** * @return the port on which to check if the container is ready @@ -772,35 +786,36 @@ private void applyConfiguration(CreateContainerCmd createCommand) { createCommand.withCmd(commandParts); } - String[] envArray = env.entrySet().stream() - .filter(it -> it.getValue() != null) - .map(it -> it.getKey() + "=" + it.getValue()) - .toArray(String[]::new); + String[] envArray = env + .entrySet() + .stream() + .filter(it -> it.getValue() != null) + .map(it -> it.getKey() + "=" + it.getValue()) + .toArray(String[]::new); createCommand.withEnv(envArray); - boolean shouldCheckFileMountingSupport = binds.size() > 0 && !TestcontainersConfiguration.getInstance().isDisableChecks(); + boolean shouldCheckFileMountingSupport = + binds.size() > 0 && !TestcontainersConfiguration.getInstance().isDisableChecks(); if (shouldCheckFileMountingSupport) { if (!DockerClientFactory.instance().isFileMountingSupported()) { - logger().warn( - "Unable to mount a file from test host into a running container. " + + logger() + .warn( + "Unable to mount a file from test host into a running container. " + "This may be a misconfiguration or limitation of your Docker environment. " + "Some features might not work." - ); + ); } } - Bind[] bindsArray = binds.stream() - .toArray(Bind[]::new); + Bind[] bindsArray = binds.stream().toArray(Bind[]::new); createCommand.withBinds(bindsArray); - VolumesFrom[] volumesFromsArray = volumesFroms.stream() - .toArray(VolumesFrom[]::new); + VolumesFrom[] volumesFromsArray = volumesFroms.stream().toArray(VolumesFrom[]::new); createCommand.withVolumesFrom(volumesFromsArray); Set allLinks = new HashSet<>(); Set allLinkedContainerNetworks = new HashSet<>(); for (Entry linkEntries : linkedContainers.entrySet()) { - String alias = linkEntries.getKey(); LinkableContainer linkableContainer = linkEntries.getValue(); @@ -808,9 +823,11 @@ private void applyConfiguration(CreateContainerCmd createCommand) { allLinks.addAll(links); if (allLinks.size() == 0) { - throw new ContainerLaunchException("Aborting attempt to link to container " + - linkableContainer.getContainerName() + - " as it is not running"); + throw new ContainerLaunchException( + "Aborting attempt to link to container " + + linkableContainer.getContainerName() + + " as it is not running" + ); } Set linkedContainerNetworks = findAllNetworksForLinkedContainers(linkableContainer); @@ -821,9 +838,12 @@ private void applyConfiguration(CreateContainerCmd createCommand) { allLinkedContainerNetworks.remove("bridge"); if (allLinkedContainerNetworks.size() > 1) { - logger().warn("Container needs to be on more than one custom network to link to other " + - "containers - this is not currently supported. Required networks are: {}", - allLinkedContainerNetworks); + logger() + .warn( + "Container needs to be on more than one custom network to link to other " + + "containers - this is not currently supported. Required networks are: {}", + allLinkedContainerNetworks + ); } Optional networkForLinks = allLinkedContainerNetworks.stream().findFirst(); @@ -835,12 +855,13 @@ private void applyConfiguration(CreateContainerCmd createCommand) { if (hostAccessible) { PortForwardingContainer.INSTANCE.start(); } - PortForwardingContainer.INSTANCE.getNetwork().ifPresent(it -> { - withExtraHost(INTERNAL_HOST_HOSTNAME, it.getIpAddress()); - }); + PortForwardingContainer.INSTANCE + .getNetwork() + .ifPresent(it -> { + withExtraHost(INTERNAL_HOST_HOSTNAME, it.getIpAddress()); + }); - String[] extraHostsArray = extraHosts.stream() - .toArray(String[]::new); + String[] extraHostsArray = extraHosts.stream().toArray(String[]::new); createCommand.withExtraHosts(extraHostsArray); if (network != null) { @@ -870,23 +891,29 @@ private void applyConfiguration(CreateContainerCmd createCommand) { } private Set findLinksFromThisContainer(String alias, LinkableContainer linkableContainer) { - return dockerClient.listContainersCmd() - .withStatusFilter(Arrays.asList("running")) - .exec().stream() - .flatMap(container -> Stream.of(container.getNames())) - .filter(name -> name.endsWith(linkableContainer.getContainerName())) - .map(name -> new Link(name, alias)) - .collect(Collectors.toSet()); + return dockerClient + .listContainersCmd() + .withStatusFilter(Arrays.asList("running")) + .exec() + .stream() + .flatMap(container -> Stream.of(container.getNames())) + .filter(name -> name.endsWith(linkableContainer.getContainerName())) + .map(name -> new Link(name, alias)) + .collect(Collectors.toSet()); } private Set findAllNetworksForLinkedContainers(LinkableContainer linkableContainer) { - return dockerClient.listContainersCmd().exec().stream() - .filter(container -> container.getNames()[0].endsWith(linkableContainer.getContainerName())) - .filter(container -> container.getNetworkSettings() != null && - container.getNetworkSettings().getNetworks() != null) - .flatMap(container -> container.getNetworkSettings().getNetworks().keySet().stream()) - .distinct() - .collect(Collectors.toSet()); + return dockerClient + .listContainersCmd() + .exec() + .stream() + .filter(container -> container.getNames()[0].endsWith(linkableContainer.getContainerName())) + .filter(container -> { + return container.getNetworkSettings() != null && container.getNetworkSettings().getNetworks() != null; + }) + .flatMap(container -> container.getNetworkSettings().getNetworks().keySet().stream()) + .distinct() + .collect(Collectors.toSet()); } /** @@ -953,19 +980,12 @@ public Map getEnvMap() { */ @Override public List getEnv() { - return env.entrySet().stream() - .map(it -> it.getKey() + "=" + it.getValue()) - .collect(Collectors.toList()); + return env.entrySet().stream().map(it -> it.getKey() + "=" + it.getValue()).collect(Collectors.toList()); } @Override public void setEnv(List env) { - this.env = env.stream() - .map(it -> it.split("=")) - .collect(Collectors.toMap( - it -> it[0], - it -> it[1] - )); + this.env = env.stream().map(it -> it.split("=")).collect(Collectors.toMap(it -> it[0], it -> it[1])); } /** @@ -980,14 +1000,25 @@ public void addEnv(String key, String value) { * {@inheritDoc} */ @Override - public void addFileSystemBind(final String hostPath, final String containerPath, final BindMode mode, final SelinuxContext selinuxContext) { + public void addFileSystemBind( + final String hostPath, + final String containerPath, + final BindMode mode, + final SelinuxContext selinuxContext + ) { if (SystemUtils.IS_OS_WINDOWS && hostPath.startsWith("/")) { // e.g. Docker socket mount binds.add(new Bind(hostPath, new Volume(containerPath), mode.accessMode, selinuxContext.selContext)); - } else { final MountableFile mountableFile = MountableFile.forHostPath(hostPath); - binds.add(new Bind(mountableFile.getResolvedPath(), new Volume(containerPath), mode.accessMode, selinuxContext.selContext)); + binds.add( + new Bind( + mountableFile.getResolvedPath(), + new Volume(containerPath), + mode.accessMode, + selinuxContext.selContext + ) + ); } } @@ -1090,9 +1121,8 @@ protected void finished(Description description) { */ @Override public SELF withExposedPorts(Integer... ports) { - this.setExposedPorts(newArrayList(ports)); + this.setExposedPorts(Lists.newArrayList(ports)); return self(); - } /** @@ -1218,7 +1248,11 @@ public SELF withImagePullPolicy(ImagePullPolicy imagePullPolicy) { * {@inheritDoc} */ @Override - public SELF withClasspathResourceMapping(final String resourcePath, final String containerPath, final BindMode mode) { + public SELF withClasspathResourceMapping( + final String resourcePath, + final String containerPath, + final BindMode mode + ) { return withClasspathResourceMapping(resourcePath, containerPath, mode, SelinuxContext.NONE); } @@ -1226,7 +1260,12 @@ public SELF withClasspathResourceMapping(final String resourcePath, final String * {@inheritDoc} */ @Override - public SELF withClasspathResourceMapping(final String resourcePath, final String containerPath, final BindMode mode, final SelinuxContext selinuxContext) { + public SELF withClasspathResourceMapping( + final String resourcePath, + final String containerPath, + final BindMode mode, + final SelinuxContext selinuxContext + ) { final MountableFile mountableFile = MountableFile.forClasspathResource(resourcePath); if (mode == BindMode.READ_ONLY && selinuxContext == SelinuxContext.NONE) { @@ -1346,23 +1385,33 @@ public String getTestHostIpAddress() { throw new IllegalStateException("Could not find a default docker-machine instance"); } - String sshConnectionString = runShellCommand("docker-machine", "ssh", defaultMachine.get(), "echo $SSH_CONNECTION").trim(); + String sshConnectionString = CommandLine + .runShellCommand("docker-machine", "ssh", defaultMachine.get(), "echo $SSH_CONNECTION") + .trim(); if (Strings.isNullOrEmpty(sshConnectionString)) { - throw new IllegalStateException("Could not obtain SSH_CONNECTION environment variable for docker machine " + defaultMachine.get()); + throw new IllegalStateException( + "Could not obtain SSH_CONNECTION environment variable for docker machine " + + defaultMachine.get() + ); } String[] sshConnectionParts = sshConnectionString.split("\\s"); if (sshConnectionParts.length != 4) { - throw new IllegalStateException("Unexpected pattern for SSH_CONNECTION for docker machine - expected 'IP PORT IP PORT' pattern but found '" + sshConnectionString + "'"); + throw new IllegalStateException( + "Unexpected pattern for SSH_CONNECTION for docker machine - expected 'IP PORT IP PORT' pattern but found '" + + sshConnectionString + + "'" + ); } return sshConnectionParts[0]; } catch (Exception e) { throw new RuntimeException(e); } - } else { - throw new UnsupportedOperationException("getTestHostIpAddress() is only implemented for docker-machine right now"); + throw new UnsupportedOperationException( + "getTestHostIpAddress() is only implemented for docker-machine right now" + ); } } diff --git a/core/src/main/java/org/testcontainers/containers/InternetProtocol.java b/core/src/main/java/org/testcontainers/containers/InternetProtocol.java index 0df0480a1bf..6574a517cdf 100644 --- a/core/src/main/java/org/testcontainers/containers/InternetProtocol.java +++ b/core/src/main/java/org/testcontainers/containers/InternetProtocol.java @@ -4,7 +4,6 @@ * The IP protocols supported by Docker. */ public enum InternetProtocol { - TCP, UDP; diff --git a/core/src/main/java/org/testcontainers/containers/Network.java b/core/src/main/java/org/testcontainers/containers/Network.java index 046133815b6..fac7beff638 100644 --- a/core/src/main/java/org/testcontainers/containers/Network.java +++ b/core/src/main/java/org/testcontainers/containers/Network.java @@ -18,7 +18,6 @@ import java.util.function.Consumer; public interface Network extends AutoCloseable, TestRule { - Network SHARED = new NetworkImpl(false, null, Collections.emptySet(), null) { @Override public void close() { diff --git a/core/src/main/java/org/testcontainers/containers/ParsedDockerComposeFile.java b/core/src/main/java/org/testcontainers/containers/ParsedDockerComposeFile.java index e9457ba92bd..abb6deaab3d 100644 --- a/core/src/main/java/org/testcontainers/containers/ParsedDockerComposeFile.java +++ b/core/src/main/java/org/testcontainers/containers/ParsedDockerComposeFile.java @@ -26,7 +26,9 @@ class ParsedDockerComposeFile { private final Map composeFileContent; + private final String composeFileName; + private final File composeFile; @Getter @@ -57,14 +59,20 @@ private void parseAndValidate() { final Map servicesMap; if (composeFileContent.containsKey("version")) { if ("2.0".equals(composeFileContent.get("version"))) { - log.warn("Testcontainers may not be able to clean up networks spawned using Docker Compose v2.0 files. " + + log.warn( + "Testcontainers may not be able to clean up networks spawned using Docker Compose v2.0 files. " + "Please see https://github.com/testcontainers/moby-ryuk/issues/2, and specify 'version: \"2.1\"' or " + - "higher in {}", composeFileName); + "higher in {}", + composeFileName + ); } final Object servicesElement = composeFileContent.get("services"); if (servicesElement == null) { - log.debug("Compose file {} has an unknown format: 'version' is set but 'services' is not defined", composeFileName); + log.debug( + "Compose file {} has an unknown format: 'version' is set but 'services' is not defined", + composeFileName + ); return; } if (!(servicesElement instanceof Map)) { @@ -81,7 +89,11 @@ private void parseAndValidate() { String serviceName = entry.getKey(); Object serviceDefinition = entry.getValue(); if (!(serviceDefinition instanceof Map)) { - log.debug("Compose file {} has an unknown format: service '{}' is not Map", composeFileName, serviceName); + log.debug( + "Compose file {} has an unknown format: service '{}' is not Map", + composeFileName, + serviceName + ); break; } @@ -95,11 +107,13 @@ private void parseAndValidate() { private void validateNoContainerNameSpecified(String serviceName, Map serviceDefinitionMap) { if (serviceDefinitionMap.containsKey("container_name")) { - throw new IllegalStateException(String.format( - "Compose file %s has 'container_name' property set for service '%s' but this property is not supported by Testcontainers, consider removing it", - composeFileName, - serviceName - )); + throw new IllegalStateException( + String.format( + "Compose file %s has 'container_name' property set for service '%s' but this property is not supported by Testcontainers, consider removing it", + composeFileName, + serviceName + ) + ); } } @@ -120,28 +134,35 @@ private void findImageNamesInDockerfile(String serviceName, Map serviceDefinitio final Object dockerfileRelativePath = buildElement.get("dockerfile"); final Object contextRelativePath = buildElement.get("context"); if (dockerfileRelativePath instanceof String && contextRelativePath instanceof String) { - dockerfilePath = composeFile + dockerfilePath = + composeFile + .getAbsoluteFile() + .getParentFile() + .toPath() + .resolve((String) contextRelativePath) + .resolve((String) dockerfileRelativePath) + .normalize(); + } + } else if (buildNode instanceof String) { + dockerfilePath = + composeFile .getAbsoluteFile() .getParentFile() .toPath() - .resolve((String) contextRelativePath) - .resolve((String) dockerfileRelativePath) + .resolve((String) buildNode) + .resolve("./Dockerfile") .normalize(); - } - } else if (buildNode instanceof String) { - dockerfilePath = composeFile - .getAbsoluteFile() - .getParentFile() - .toPath() - .resolve((String) buildNode) - .resolve("./Dockerfile") - .normalize(); } if (dockerfilePath != null && Files.exists(dockerfilePath)) { Set resolvedImageNames = new ParsedDockerfile(dockerfilePath).getDependencyImageNames(); if (!resolvedImageNames.isEmpty()) { - log.debug("Resolved Dockerfile dependency images for Docker Compose in {} -> {}: {}", composeFileName, dockerfilePath, resolvedImageNames); + log.debug( + "Resolved Dockerfile dependency images for Docker Compose in {} -> {}: {}", + composeFileName, + dockerfilePath, + resolvedImageNames + ); this.serviceNameToImageNames.put(serviceName, resolvedImageNames); } } diff --git a/core/src/main/java/org/testcontainers/containers/PortForwardingContainer.java b/core/src/main/java/org/testcontainers/containers/PortForwardingContainer.java index a854b1e55c3..22d364c0762 100644 --- a/core/src/main/java/org/testcontainers/containers/PortForwardingContainer.java +++ b/core/src/main/java/org/testcontainers/containers/PortForwardingContainer.java @@ -30,15 +30,16 @@ public enum PortForwardingContainer { @SneakyThrows private Connection createSSHSession() { String password = UUID.randomUUID().toString(); - container = new GenericContainer<>(DockerImageName.parse("testcontainers/sshd:1.0.0")) - .withExposedPorts(22) - .withEnv("PASSWORD", password) - .withCommand( - "sh", - "-c", - // Disable ipv6 & Make it listen on all interfaces, not just localhost - "echo \"root:$PASSWORD\" | chpasswd && /usr/sbin/sshd -D -o PermitRootLogin=yes -o AddressFamily=inet -o GatewayPorts=yes" - ); + container = + new GenericContainer<>(DockerImageName.parse("testcontainers/sshd:1.0.0")) + .withExposedPorts(22) + .withEnv("PASSWORD", password) + .withCommand( + "sh", + "-c", + // Disable ipv6 & Make it listen on all interfaces, not just localhost + "echo \"root:$PASSWORD\" | chpasswd && /usr/sbin/sshd -D -o PermitRootLogin=yes -o AddressFamily=inet -o GatewayPorts=yes" + ); container.start(); Connection connection = new Connection(container.getHost(), container.getMappedPort(22)); @@ -64,7 +65,7 @@ public void exposeHostPort(int port) { @SneakyThrows public void exposeHostPort(int hostPort, int containerPort) { - if (exposedPorts.add(new AbstractMap.SimpleEntry<>(hostPort, containerPort))) { + if (exposedPorts.add(new AbstractMap.SimpleEntry<>(hostPort, containerPort))) { getSshConnection().requestRemotePortForwarding("", containerPort, "localhost", hostPort); } } @@ -74,7 +75,8 @@ void start() { } Optional getNetwork() { - return Optional.ofNullable(container) + return Optional + .ofNullable(container) .map(GenericContainer::getContainerInfo) .flatMap(it -> it.getNetworkSettings().getNetworks().values().stream().findFirst()); } diff --git a/core/src/main/java/org/testcontainers/containers/SelinuxContext.java b/core/src/main/java/org/testcontainers/containers/SelinuxContext.java index 735d3e3a277..ec814d5640a 100644 --- a/core/src/main/java/org/testcontainers/containers/SelinuxContext.java +++ b/core/src/main/java/org/testcontainers/containers/SelinuxContext.java @@ -13,5 +13,4 @@ public enum SelinuxContext { NONE(SELContext.none); public final SELContext selContext; - } diff --git a/core/src/main/java/org/testcontainers/containers/SocatContainer.java b/core/src/main/java/org/testcontainers/containers/SocatContainer.java index bc9ced57845..9455ac77227 100644 --- a/core/src/main/java/org/testcontainers/containers/SocatContainer.java +++ b/core/src/main/java/org/testcontainers/containers/SocatContainer.java @@ -1,10 +1,11 @@ package org.testcontainers.containers; +import org.testcontainers.utility.Base58; +import org.testcontainers.utility.DockerImageName; + import java.util.HashMap; import java.util.Map; import java.util.stream.Collectors; -import org.testcontainers.utility.Base58; -import org.testcontainers.utility.DockerImageName; /** * A socat container is used as a TCP proxy, enabling any TCP port of another container to be exposed @@ -36,10 +37,13 @@ public SocatContainer withTarget(int exposedPort, String host, int internalPort) @Override protected void configure() { - withCommand("-c", - targets.entrySet().stream() - .map(entry -> "socat TCP-LISTEN:" + entry.getKey() + ",fork,reuseaddr TCP:" + entry.getValue()) - .collect(Collectors.joining(" & ")) + withCommand( + "-c", + targets + .entrySet() + .stream() + .map(entry -> "socat TCP-LISTEN:" + entry.getKey() + ",fork,reuseaddr TCP:" + entry.getValue()) + .collect(Collectors.joining(" & ")) ); } } diff --git a/core/src/main/java/org/testcontainers/containers/VncRecordingContainer.java b/core/src/main/java/org/testcontainers/containers/VncRecordingContainer.java index be8a4ed735f..f629a141357 100644 --- a/core/src/main/java/org/testcontainers/containers/VncRecordingContainer.java +++ b/core/src/main/java/org/testcontainers/containers/VncRecordingContainer.java @@ -15,10 +15,9 @@ import java.nio.file.Files; import java.nio.file.StandardCopyOption; import java.time.Duration; +import java.time.temporal.ChronoUnit; import java.util.Base64; -import static java.time.temporal.ChronoUnit.SECONDS; - /** * 'Sidekick container' with the sole purpose of recording the VNC screen output from another container. * @@ -48,7 +47,9 @@ public class VncRecordingContainer extends GenericContainer targetContainer) { this( targetContainer.getNetwork(), - targetContainer.getNetworkAliases().stream() + targetContainer + .getNetworkAliases() + .stream() .findFirst() .orElseThrow(() -> new IllegalStateException("Target container must have a network alias")) ); @@ -57,14 +58,16 @@ public VncRecordingContainer(@NonNull GenericContainer targetContainer) { /** * Create a sidekick container and attach it to another container. The VNC output of that container will be recorded. */ - public VncRecordingContainer(@NonNull Network network, @NonNull String targetNetworkAlias) throws IllegalStateException { + public VncRecordingContainer(@NonNull Network network, @NonNull String targetNetworkAlias) + throws IllegalStateException { super(DockerImageName.parse("testcontainers/vnc-recorder:1.2.0")); - this.targetNetworkAlias = targetNetworkAlias; withNetwork(network); - waitingFor(new LogMessageWaitStrategy() - .withRegEx(".*Connected.*") - .withStartupTimeout(Duration.of(15, SECONDS))); + waitingFor( + new LogMessageWaitStrategy() + .withRegEx(".*Connected.*") + .withStartupTimeout(Duration.of(15, ChronoUnit.SECONDS)) + ); } public VncRecordingContainer withVncPassword(@NonNull String vncPassword) { @@ -95,8 +98,17 @@ protected void configure() { String encodedPassword = Base64.getEncoder().encodeToString(vncPassword.getBytes()); setCommand( "-c", - "echo '" + encodedPassword + "' | base64 -d > /vnc_password && " + - "flvrec.py -o " + ORIGINAL_RECORDING_FILE_NAME + " -d -r " + frameRate + " -P /vnc_password " + targetNetworkAlias + " " + vncPort + "echo '" + + encodedPassword + + "' | base64 -d > /vnc_password && " + + "flvrec.py -o " + + ORIGINAL_RECORDING_FILE_NAME + + " -d -r " + + frameRate + + " -P /vnc_password " + + targetNetworkAlias + + " " + + vncPort ); } @@ -122,7 +134,8 @@ public void saveRecordingToFile(@NonNull File file) { public enum VncRecordingFormat { FLV("flv") { @Override - String reencodeRecording(@NonNull VncRecordingContainer container, @NonNull String source) throws IOException, InterruptedException { + String reencodeRecording(@NonNull VncRecordingContainer container, @NonNull String source) + throws IOException, InterruptedException { String newFileOutput = "/newScreen.flv"; container.execInContainer("ffmpeg", "-i", source, "-vcodec", "libx264", newFileOutput); return newFileOutput; @@ -130,17 +143,29 @@ String reencodeRecording(@NonNull VncRecordingContainer container, @NonNull Stri }, MP4("mp4") { @Override - String reencodeRecording(@NonNull VncRecordingContainer container, @NonNull String source) throws IOException, InterruptedException { + String reencodeRecording(@NonNull VncRecordingContainer container, @NonNull String source) + throws IOException, InterruptedException { String newFileOutput = "/newScreen.mp4"; - container.execInContainer("ffmpeg", "-i", source, "-vcodec", "libx264", "-movflags", "faststart", "-pix_fmt", "yuv420p", newFileOutput); + container.execInContainer( + "ffmpeg", + "-i", + source, + "-vcodec", + "libx264", + "-movflags", + "faststart", + "-pix_fmt", + "yuv420p", + newFileOutput + ); return newFileOutput; } }; - abstract String reencodeRecording(VncRecordingContainer container, String source) throws IOException, InterruptedException; + abstract String reencodeRecording(VncRecordingContainer container, String source) + throws IOException, InterruptedException; @Getter private final String filenameExtension; } - } diff --git a/core/src/main/java/org/testcontainers/containers/output/BaseConsumer.java b/core/src/main/java/org/testcontainers/containers/output/BaseConsumer.java index 2967eb4d33a..7679ee8ab57 100644 --- a/core/src/main/java/org/testcontainers/containers/output/BaseConsumer.java +++ b/core/src/main/java/org/testcontainers/containers/output/BaseConsumer.java @@ -6,6 +6,7 @@ import java.util.function.Consumer; public abstract class BaseConsumer> implements Consumer { + @Getter @Setter private boolean removeColorCodes = true; diff --git a/core/src/main/java/org/testcontainers/containers/output/FrameConsumerResultCallback.java b/core/src/main/java/org/testcontainers/containers/output/FrameConsumerResultCallback.java index 8c7480320a8..75c24d1ac1d 100644 --- a/core/src/main/java/org/testcontainers/containers/output/FrameConsumerResultCallback.java +++ b/core/src/main/java/org/testcontainers/containers/output/FrameConsumerResultCallback.java @@ -1,6 +1,5 @@ package org.testcontainers.containers.output; - import com.github.dockerjava.api.async.ResultCallbackTemplate; import com.github.dockerjava.api.model.Frame; import com.github.dockerjava.api.model.StreamType; @@ -79,7 +78,7 @@ public void onError(Throwable throwable) { // Sink any errors try { close(); - } catch (IOException ignored) { } + } catch (IOException ignored) {} } @Override @@ -169,7 +168,6 @@ private String processAnsiColorCodes(String utf8String, Consumer co return utf8String; } - private byte[] merge(byte[] str1, byte[] str2) { byte[] mergedString = new byte[str1.length + str2.length]; System.arraycopy(str1, 0, mergedString, 0, str1.length); diff --git a/core/src/main/java/org/testcontainers/containers/output/OutputFrame.java b/core/src/main/java/org/testcontainers/containers/output/OutputFrame.java index d5ddcada8c8..f6eb5c18cbc 100644 --- a/core/src/main/java/org/testcontainers/containers/output/OutputFrame.java +++ b/core/src/main/java/org/testcontainers/containers/output/OutputFrame.java @@ -12,6 +12,7 @@ public class OutputFrame { public static final OutputFrame END = new OutputFrame(OutputType.END, null); private final OutputType type; + private final byte[] bytes; public OutputFrame(OutputType type, byte[] bytes) { @@ -28,7 +29,6 @@ public byte[] getBytes() { } public String getUtf8String() { - if (bytes == null) { return ""; } @@ -37,14 +37,20 @@ public String getUtf8String() { } public enum OutputType { - STDOUT, STDERR, END; + STDOUT, + STDERR, + END; public static OutputType forStreamType(StreamType streamType) { switch (streamType) { - case RAW: return STDOUT; - case STDOUT: return STDOUT; - case STDERR: return STDERR; - default: return null; + case RAW: + return STDOUT; + case STDOUT: + return STDOUT; + case STDERR: + return STDERR; + default: + return null; } } } @@ -56,5 +62,4 @@ public static OutputFrame forFrame(Frame frame) { } return new OutputFrame(outputType, frame.getPayload()); } - } diff --git a/core/src/main/java/org/testcontainers/containers/output/Slf4jLogConsumer.java b/core/src/main/java/org/testcontainers/containers/output/Slf4jLogConsumer.java index a274cd63004..ece7ab0e5b7 100644 --- a/core/src/main/java/org/testcontainers/containers/output/Slf4jLogConsumer.java +++ b/core/src/main/java/org/testcontainers/containers/output/Slf4jLogConsumer.java @@ -1,18 +1,22 @@ package org.testcontainers.containers.output; -import java.util.HashMap; -import java.util.Map; - import org.slf4j.Logger; import org.slf4j.MDC; +import java.util.HashMap; +import java.util.Map; + /** * A consumer for container output that logs output to an SLF4J logger. */ public class Slf4jLogConsumer extends BaseConsumer { + private final Logger logger; + private final Map mdc = new HashMap<>(); + private boolean separateOutputStreams; + private String prefix = ""; public Slf4jLogConsumer(Logger logger) { diff --git a/core/src/main/java/org/testcontainers/containers/output/ToStringConsumer.java b/core/src/main/java/org/testcontainers/containers/output/ToStringConsumer.java index 40ec60fb744..444434fa702 100644 --- a/core/src/main/java/org/testcontainers/containers/output/ToStringConsumer.java +++ b/core/src/main/java/org/testcontainers/containers/output/ToStringConsumer.java @@ -10,6 +10,7 @@ * Created by rnorth on 26/03/2016. */ public class ToStringConsumer extends BaseConsumer { + private ByteArrayOutputStream stringBuffer = new ByteArrayOutputStream(); @Override diff --git a/core/src/main/java/org/testcontainers/containers/output/WaitingConsumer.java b/core/src/main/java/org/testcontainers/containers/output/WaitingConsumer.java index 858f5a4b487..e9b34692319 100644 --- a/core/src/main/java/org/testcontainers/containers/output/WaitingConsumer.java +++ b/core/src/main/java/org/testcontainers/containers/output/WaitingConsumer.java @@ -71,14 +71,14 @@ public void waitUntil(Predicate predicate, int limit, TimeUnit limi * @param limitUnit maximum time to wait (units) * @param times number of times the predicate has to match */ - public void waitUntil(Predicate predicate, long limit, TimeUnit limitUnit, int times) throws TimeoutException { + public void waitUntil(Predicate predicate, long limit, TimeUnit limitUnit, int times) + throws TimeoutException { long expiry = limitUnit.toMillis(limit) + System.currentTimeMillis(); waitUntil(predicate, expiry, times); } private void waitUntil(Predicate predicate, long expiry, int times) throws TimeoutException { - int numberOfMatches = 0; while (System.currentTimeMillis() < expiry) { try { diff --git a/core/src/main/java/org/testcontainers/containers/startupcheck/IndefiniteWaitOneShotStartupCheckStrategy.java b/core/src/main/java/org/testcontainers/containers/startupcheck/IndefiniteWaitOneShotStartupCheckStrategy.java index f719cb1d476..a547b7885e3 100644 --- a/core/src/main/java/org/testcontainers/containers/startupcheck/IndefiniteWaitOneShotStartupCheckStrategy.java +++ b/core/src/main/java/org/testcontainers/containers/startupcheck/IndefiniteWaitOneShotStartupCheckStrategy.java @@ -12,6 +12,7 @@ * It has to be assumed that the container will stop of its own accord, either with a success or failure exit code. */ public class IndefiniteWaitOneShotStartupCheckStrategy extends OneShotStartupCheckStrategy { + @Override public boolean waitUntilStartupSuccessful(DockerClient dockerClient, String containerId) { while (checkStartupState(dockerClient, containerId) == StartupStatus.NOT_YET_KNOWN) { diff --git a/core/src/main/java/org/testcontainers/containers/startupcheck/MinimumDurationRunningStartupCheckStrategy.java b/core/src/main/java/org/testcontainers/containers/startupcheck/MinimumDurationRunningStartupCheckStrategy.java index 3ce50446b77..ce29e0104e1 100644 --- a/core/src/main/java/org/testcontainers/containers/startupcheck/MinimumDurationRunningStartupCheckStrategy.java +++ b/core/src/main/java/org/testcontainers/containers/startupcheck/MinimumDurationRunningStartupCheckStrategy.java @@ -35,5 +35,4 @@ public StartupStatus checkStartupState(DockerClient dockerClient, String contain } return StartupStatus.NOT_YET_KNOWN; } - } diff --git a/core/src/main/java/org/testcontainers/containers/startupcheck/StartupCheckStrategy.java b/core/src/main/java/org/testcontainers/containers/startupcheck/StartupCheckStrategy.java index d5931454212..50fdcdb8b9b 100644 --- a/core/src/main/java/org/testcontainers/containers/startupcheck/StartupCheckStrategy.java +++ b/core/src/main/java/org/testcontainers/containers/startupcheck/StartupCheckStrategy.java @@ -10,20 +10,18 @@ import java.time.Duration; import java.util.concurrent.TimeUnit; -import static org.testcontainers.containers.GenericContainer.CONTAINER_RUNNING_TIMEOUT_SEC; - /** * Approach to determine whether a container has 'started up' correctly. */ public abstract class StartupCheckStrategy { private static final RateLimiter DOCKER_CLIENT_RATE_LIMITER = RateLimiterBuilder - .newBuilder() - .withRate(1, TimeUnit.SECONDS) - .withConstantThroughput() - .build(); + .newBuilder() + .withRate(1, TimeUnit.SECONDS) + .withConstantThroughput() + .build(); - private Duration timeout = Duration.ofSeconds(CONTAINER_RUNNING_TIMEOUT_SEC); + private Duration timeout = Duration.ofSeconds(GenericContainer.CONTAINER_RUNNING_TIMEOUT_SEC); @SuppressWarnings("unchecked") public SELF withTimeout(Duration timeout) { @@ -41,20 +39,27 @@ public boolean waitUntilStartupSuccessful(GenericContainer container) { } public boolean waitUntilStartupSuccessful(DockerClient dockerClient, String containerId) { - final Boolean[] startedOK = {null}; - Unreliables.retryUntilTrue((int) timeout.toMillis(), TimeUnit.MILLISECONDS, () -> { - //noinspection CodeBlock2Expr - return DOCKER_CLIENT_RATE_LIMITER.getWhenReady(() -> { - StartupStatus state = checkStartupState(dockerClient, containerId); - switch (state) { - case SUCCESSFUL: startedOK[0] = true; - return true; - case FAILED: startedOK[0] = false; - return true; - default: return false; - } - }); - }); + final Boolean[] startedOK = { null }; + Unreliables.retryUntilTrue( + (int) timeout.toMillis(), + TimeUnit.MILLISECONDS, + () -> { + //noinspection CodeBlock2Expr + return DOCKER_CLIENT_RATE_LIMITER.getWhenReady(() -> { + StartupStatus state = checkStartupState(dockerClient, containerId); + switch (state) { + case SUCCESSFUL: + startedOK[0] = true; + return true; + case FAILED: + startedOK[0] = false; + return true; + default: + return false; + } + }); + } + ); return startedOK[0]; } @@ -65,6 +70,8 @@ protected InspectContainerResponse.ContainerState getCurrentState(DockerClient d } public enum StartupStatus { - NOT_YET_KNOWN, SUCCESSFUL, FAILED + NOT_YET_KNOWN, + SUCCESSFUL, + FAILED, } } diff --git a/core/src/main/java/org/testcontainers/containers/traits/LinkableContainer.java b/core/src/main/java/org/testcontainers/containers/traits/LinkableContainer.java index 76e71378969..4973c4dc9c1 100644 --- a/core/src/main/java/org/testcontainers/containers/traits/LinkableContainer.java +++ b/core/src/main/java/org/testcontainers/containers/traits/LinkableContainer.java @@ -7,6 +7,5 @@ */ @Deprecated public interface LinkableContainer { - String getContainerName(); } diff --git a/core/src/main/java/org/testcontainers/containers/wait/internal/ExternalPortListeningCheck.java b/core/src/main/java/org/testcontainers/containers/wait/internal/ExternalPortListeningCheck.java index d543d952bbd..fbee9c2f9f4 100644 --- a/core/src/main/java/org/testcontainers/containers/wait/internal/ExternalPortListeningCheck.java +++ b/core/src/main/java/org/testcontainers/containers/wait/internal/ExternalPortListeningCheck.java @@ -14,21 +14,25 @@ */ @RequiredArgsConstructor public class ExternalPortListeningCheck implements Callable { + private final ContainerState containerState; + private final Set externalLivenessCheckPorts; @Override public Boolean call() { String address = containerState.getHost(); - externalLivenessCheckPorts.parallelStream().forEach(externalPort -> { - try (Socket socket = new Socket()) { - InetSocketAddress inetSocketAddress = new InetSocketAddress(address, externalPort); - socket.connect(inetSocketAddress, 1000); - } catch (IOException e) { - throw new IllegalStateException("Socket not listening yet: " + externalPort); - } - }); + externalLivenessCheckPorts + .parallelStream() + .forEach(externalPort -> { + try (Socket socket = new Socket()) { + InetSocketAddress inetSocketAddress = new InetSocketAddress(address, externalPort); + socket.connect(inetSocketAddress, 1000); + } catch (IOException e) { + throw new IllegalStateException("Socket not listening yet: " + externalPort); + } + }); return true; } } diff --git a/core/src/main/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheck.java b/core/src/main/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheck.java index 1eba8acbbd1..aeedbea1e3f 100644 --- a/core/src/main/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheck.java +++ b/core/src/main/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheck.java @@ -10,8 +10,6 @@ import java.time.Instant; import java.util.Set; -import static java.lang.String.format; - /** * Mechanism for testing that a socket is listening when run from the container being checked. */ @@ -20,6 +18,7 @@ public class InternalCommandPortListeningCheck implements java.util.concurrent.Callable { private final WaitStrategyTarget waitStrategyTarget; + private final Set internalPorts; @Override @@ -29,19 +28,31 @@ public Boolean call() { for (int internalPort : internalPorts) { command.append(" && "); command.append(" ("); - command.append(format("grep -i ':0*%x' /proc/net/tcp*", internalPort)); + command.append(String.format("grep -i ':0*%x' /proc/net/tcp*", internalPort)); command.append(" || "); - command.append(format("nc -vz -w 1 localhost %d", internalPort)); + command.append(String.format("nc -vz -w 1 localhost %d", internalPort)); command.append(" || "); - command.append(format("/bin/bash -c ' externalLivenessCheckPorts = getLivenessCheckPorts(); if (externalLivenessCheckPorts.isEmpty()) { if (log.isDebugEnabled()) { - log.debug("Liveness check ports of {} is empty. Not waiting.", waitStrategyTarget.getContainerInfo().getName()); + log.debug( + "Liveness check ports of {} is empty. Not waiting.", + waitStrategyTarget.getContainerInfo().getName() + ); } return; } @@ -45,58 +48,69 @@ protected void waitUntilReady() { Callable internalCheck = new InternalCommandPortListeningCheck(waitStrategyTarget, internalPorts); - Callable externalCheck = new ExternalPortListeningCheck(waitStrategyTarget, externalLivenessCheckPorts); + Callable externalCheck = new ExternalPortListeningCheck( + waitStrategyTarget, + externalLivenessCheckPorts + ); try { - List> futures = EXECUTOR.invokeAll(Arrays.asList( - // Blocking - () -> { - Instant now = Instant.now(); - Boolean result = internalCheck.call(); - log.debug( - "Internal port check {} for {} in {}", - Boolean.TRUE.equals(result) ? "passed" : "failed", - internalPorts, - Duration.between(now, Instant.now()) - ); - return result; - }, - // Polling - () -> { - Instant now = Instant.now(); - Awaitility.await() - .pollInSameThread() - .pollInterval(Duration.ofMillis(100)) - .pollDelay(Duration.ZERO) - .ignoreExceptions() - .forever() - .until(externalCheck); + List> futures = EXECUTOR.invokeAll( + Arrays.asList( + // Blocking + () -> { + Instant now = Instant.now(); + Boolean result = internalCheck.call(); + log.debug( + "Internal port check {} for {} in {}", + Boolean.TRUE.equals(result) ? "passed" : "failed", + internalPorts, + Duration.between(now, Instant.now()) + ); + return result; + }, + // Polling + () -> { + Instant now = Instant.now(); + Awaitility + .await() + .pollInSameThread() + .pollInterval(Duration.ofMillis(100)) + .pollDelay(Duration.ZERO) + .ignoreExceptions() + .forever() + .until(externalCheck); - log.debug( - "External port check passed for {} mapped as {} in {}", - internalPorts, - externalLivenessCheckPorts, - Duration.between(now, Instant.now()) - ); - return true; - } - ), startupTimeout.getSeconds(), TimeUnit.SECONDS); + log.debug( + "External port check passed for {} mapped as {} in {}", + internalPorts, + externalLivenessCheckPorts, + Duration.between(now, Instant.now()) + ); + return true; + } + ), + startupTimeout.getSeconds(), + TimeUnit.SECONDS + ); for (Future future : futures) { future.get(0, TimeUnit.SECONDS); } } catch (CancellationException | ExecutionException | TimeoutException e) { - throw new ContainerLaunchException("Timed out waiting for container port to open (" + - waitStrategyTarget.getHost() + - " ports: " + - externalLivenessCheckPorts + - " should be listening)"); + throw new ContainerLaunchException( + "Timed out waiting for container port to open (" + + waitStrategyTarget.getHost() + + " ports: " + + externalLivenessCheckPorts + + " should be listening)" + ); } } private Set getInternalPorts(Set externalLivenessCheckPorts, List exposedPorts) { - return exposedPorts.stream() - .filter(it -> externalLivenessCheckPorts.contains(waitStrategyTarget.getMappedPort(it))) - .collect(Collectors.toSet()); + return exposedPorts + .stream() + .filter(it -> externalLivenessCheckPorts.contains(waitStrategyTarget.getMappedPort(it))) + .collect(Collectors.toSet()); } } diff --git a/core/src/main/java/org/testcontainers/containers/wait/strategy/HttpWaitStrategy.java b/core/src/main/java/org/testcontainers/containers/wait/strategy/HttpWaitStrategy.java index d5d08ce6fd0..ccaef47a821 100644 --- a/core/src/main/java/org/testcontainers/containers/wait/strategy/HttpWaitStrategy.java +++ b/core/src/main/java/org/testcontainers/containers/wait/strategy/HttpWaitStrategy.java @@ -2,8 +2,6 @@ import com.google.common.base.Strings; import com.google.common.io.BaseEncoding; -import java.util.HashMap; -import java.util.Map; import lombok.extern.slf4j.Slf4j; import org.rnorth.ducttape.TimeoutException; import org.testcontainers.containers.ContainerLaunchException; @@ -15,12 +13,14 @@ import java.net.MalformedURLException; import java.net.URI; import java.net.URL; -import java.time.Duration; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.security.cert.X509Certificate; +import java.time.Duration; +import java.util.HashMap; import java.util.HashSet; +import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.concurrent.TimeUnit; @@ -48,16 +48,27 @@ public class HttpWaitStrategy extends AbstractWaitStrategy { private static final String AUTH_BASIC = "Basic "; private String path = "/"; + private String method = "GET"; + private Set statusCodes = new HashSet<>(); + private boolean tlsEnabled; + private String username; + private String password; + private final Map headers = new HashMap<>(); + private Predicate responsePredicate; + private Predicate statusCodePredicate = null; + private Optional livenessPort = Optional.empty(); + private Duration readTimeout = Duration.ofSeconds(1); + private boolean allowInsecure; /** @@ -196,14 +207,16 @@ public HttpWaitStrategy forResponsePredicate(Predicate responsePredicate protected void waitUntilReady() { final String containerName = waitStrategyTarget.getContainerInfo().getName(); - final Integer livenessCheckPort = livenessPort.map(waitStrategyTarget::getMappedPort).orElseGet(() -> { - final Set livenessCheckPorts = getLivenessCheckPorts(); - if (livenessCheckPorts == null || livenessCheckPorts.isEmpty()) { - log.warn("{}: No exposed ports or mapped ports - cannot wait for status", containerName); - return -1; - } - return livenessCheckPorts.iterator().next(); - }); + final Integer livenessCheckPort = livenessPort + .map(waitStrategyTarget::getMappedPort) + .orElseGet(() -> { + final Set livenessCheckPorts = getLivenessCheckPorts(); + if (livenessCheckPorts == null || livenessCheckPorts.isEmpty()) { + log.warn("{}: No exposed ports or mapped ports - cannot wait for status", containerName); + return -1; + } + return livenessCheckPorts.iterator().next(); + }); if (null == livenessCheckPort || -1 == livenessCheckPort) { return; @@ -213,11 +226,20 @@ protected void waitUntilReady() { try { // Un-map the port for logging - int originalPort = waitStrategyTarget.getExposedPorts().stream() + int originalPort = waitStrategyTarget + .getExposedPorts() + .stream() .filter(exposedPort -> rawUri.getPort() == waitStrategyTarget.getMappedPort(exposedPort)) .findFirst() .orElseThrow(() -> new IllegalStateException("Target port " + rawUri.getPort() + " is not exposed")); - log.info("{}: Waiting for {} seconds for URL: {} (where port {} maps to container port {})", containerName, startupTimeout.getSeconds(), uri, rawUri.getPort(), originalPort); + log.info( + "{}: Waiting for {} seconds for URL: {} (where port {} maps to container port {})", + containerName, + startupTimeout.getSeconds(), + uri, + rawUri.getPort(), + originalPort + ); } catch (RuntimeException e) { // do not allow a failure in logging to prevent progress, but log for diagnosis log.warn("Unexpected error occurred - will proceed to try to wait anyway", e); @@ -225,103 +247,114 @@ protected void waitUntilReady() { // try to connect to the URL try { - retryUntilSuccess((int) startupTimeout.getSeconds(), TimeUnit.SECONDS, () -> { - getRateLimiter().doWhenReady(() -> { - try { - final HttpURLConnection connection = openConnection(uri); - connection.setReadTimeout(Math.toIntExact(readTimeout.toMillis())); - - // authenticate - if (!Strings.isNullOrEmpty(username)) { - connection.setRequestProperty(HEADER_AUTHORIZATION, buildAuthString(username, password)); - connection.setUseCaches(false); - } - - // Add user configured headers - this.headers.forEach(connection::setRequestProperty); - connection.setRequestMethod(method); - connection.connect(); - - log.trace("Get response code {}", connection.getResponseCode()); - - // Choose the statusCodePredicate strategy depending on what we defined. - Predicate predicate; - if (statusCodes.isEmpty() && statusCodePredicate == null) { - // We have no status code and no predicate so we expect a 200 OK response code - predicate = responseCode -> HttpURLConnection.HTTP_OK == responseCode; - } else if (!statusCodes.isEmpty() && statusCodePredicate == null) { - // We use the default status predicate checker when we only have status codes - predicate = responseCode -> statusCodes.contains(responseCode); - } else if (statusCodes.isEmpty()) { - // We only have a predicate - predicate = statusCodePredicate; - } else { - // We have both predicate and status code - predicate = statusCodePredicate.or(responseCode -> statusCodes.contains(responseCode)); - } - if (!predicate.test(connection.getResponseCode())) { - throw new RuntimeException(String.format("HTTP response code was: %s", - connection.getResponseCode())); - } - - if(responsePredicate != null) { - String responseBody = getResponseBody(connection); - - log.trace("Get response {}", responseBody); - - if(!responsePredicate.test(responseBody)) { - throw new RuntimeException(String.format("Response: %s did not match predicate", - responseBody)); + retryUntilSuccess( + (int) startupTimeout.getSeconds(), + TimeUnit.SECONDS, + () -> { + getRateLimiter() + .doWhenReady(() -> { + try { + final HttpURLConnection connection = openConnection(uri); + connection.setReadTimeout(Math.toIntExact(readTimeout.toMillis())); + + // authenticate + if (!Strings.isNullOrEmpty(username)) { + connection.setRequestProperty( + HEADER_AUTHORIZATION, + buildAuthString(username, password) + ); + connection.setUseCaches(false); + } + + // Add user configured headers + this.headers.forEach(connection::setRequestProperty); + connection.setRequestMethod(method); + connection.connect(); + + log.trace("Get response code {}", connection.getResponseCode()); + + // Choose the statusCodePredicate strategy depending on what we defined. + Predicate predicate; + if (statusCodes.isEmpty() && statusCodePredicate == null) { + // We have no status code and no predicate so we expect a 200 OK response code + predicate = responseCode -> HttpURLConnection.HTTP_OK == responseCode; + } else if (!statusCodes.isEmpty() && statusCodePredicate == null) { + // We use the default status predicate checker when we only have status codes + predicate = responseCode -> statusCodes.contains(responseCode); + } else if (statusCodes.isEmpty()) { + // We only have a predicate + predicate = statusCodePredicate; + } else { + // We have both predicate and status code + predicate = + statusCodePredicate.or(responseCode -> statusCodes.contains(responseCode)); + } + if (!predicate.test(connection.getResponseCode())) { + throw new RuntimeException( + String.format("HTTP response code was: %s", connection.getResponseCode()) + ); + } + + if (responsePredicate != null) { + String responseBody = getResponseBody(connection); + + log.trace("Get response {}", responseBody); + + if (!responsePredicate.test(responseBody)) { + throw new RuntimeException( + String.format("Response: %s did not match predicate", responseBody) + ); + } + } + } catch (IOException e) { + throw new RuntimeException(e); } - } - - } catch (IOException e) { - throw new RuntimeException(e); - } - }); - return true; - }); - + }); + return true; + } + ); } catch (TimeoutException e) { - throw new ContainerLaunchException(String.format( - "Timed out waiting for URL to be accessible (%s should return HTTP %s)", uri, statusCodes.isEmpty() ? - HttpURLConnection.HTTP_OK : statusCodes)); + throw new ContainerLaunchException( + String.format( + "Timed out waiting for URL to be accessible (%s should return HTTP %s)", + uri, + statusCodes.isEmpty() ? HttpURLConnection.HTTP_OK : statusCodes + ) + ); } } private HttpURLConnection openConnection(final String uri) throws IOException, MalformedURLException { if (tlsEnabled) { - final HttpsURLConnection connection = (HttpsURLConnection)new URL(uri).openConnection(); + final HttpsURLConnection connection = (HttpsURLConnection) new URL(uri).openConnection(); if (allowInsecure) { - // Create a trust manager that does not validate certificate chains + // Create a trust manager that does not validate certificate chains // and trust all certificates - final TrustManager[] trustAllCerts = new TrustManager[] { + final TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() { @Override - public X509Certificate[] getAcceptedIssuers() { + public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; } - + @Override - public void checkClientTrusted(final X509Certificate[] certs, final String authType) { - } - + public void checkClientTrusted(final X509Certificate[] certs, final String authType) {} + @Override - public void checkServerTrusted(final X509Certificate[] certs, final String authType) { - } - } - }; + public void checkServerTrusted(final X509Certificate[] certs, final String authType) {} + }, + }; try { // Create custom SSL context and set the "trust all certificates" trust manager - final SSLContext sc = SSLContext.getInstance("SSL"); - sc.init(new KeyManager[0], trustAllCerts, new SecureRandom()); + final SSLContext sc = SSLContext.getInstance("SSL"); + sc.init(new KeyManager[0], trustAllCerts, new SecureRandom()); connection.setSSLSocketFactory(sc.getSocketFactory()); } catch (final NoSuchAlgorithmException | KeyManagementException ex) { throw new IOException("Unable to create custom SSL factory instance", ex); } } - + return connection; } else { return (HttpURLConnection) new URL(uri).openConnection(); diff --git a/core/src/main/java/org/testcontainers/containers/wait/strategy/LogMessageWaitStrategy.java b/core/src/main/java/org/testcontainers/containers/wait/strategy/LogMessageWaitStrategy.java index 65370e51bfa..7a095d888f2 100644 --- a/core/src/main/java/org/testcontainers/containers/wait/strategy/LogMessageWaitStrategy.java +++ b/core/src/main/java/org/testcontainers/containers/wait/strategy/LogMessageWaitStrategy.java @@ -2,7 +2,6 @@ import com.github.dockerjava.api.command.LogContainerCmd; import lombok.SneakyThrows; -import org.testcontainers.DockerClientFactory; import org.testcontainers.containers.ContainerLaunchException; import org.testcontainers.containers.output.FrameConsumerResultCallback; import org.testcontainers.containers.output.OutputFrame; @@ -13,9 +12,6 @@ import java.util.concurrent.TimeoutException; import java.util.function.Predicate; -import static org.testcontainers.containers.output.OutputFrame.OutputType.STDERR; -import static org.testcontainers.containers.output.OutputFrame.OutputType.STDOUT; - public class LogMessageWaitStrategy extends AbstractWaitStrategy { private String regEx; @@ -27,22 +23,24 @@ public class LogMessageWaitStrategy extends AbstractWaitStrategy { protected void waitUntilReady() { WaitingConsumer waitingConsumer = new WaitingConsumer(); - LogContainerCmd cmd = waitStrategyTarget.getDockerClient().logContainerCmd(waitStrategyTarget.getContainerId()) + LogContainerCmd cmd = waitStrategyTarget + .getDockerClient() + .logContainerCmd(waitStrategyTarget.getContainerId()) .withFollowStream(true) .withSince(0) .withStdOut(true) .withStdErr(true); try (FrameConsumerResultCallback callback = new FrameConsumerResultCallback()) { - callback.addConsumer(STDOUT, waitingConsumer); - callback.addConsumer(STDERR, waitingConsumer); + callback.addConsumer(OutputFrame.OutputType.STDOUT, waitingConsumer); + callback.addConsumer(OutputFrame.OutputType.STDERR, waitingConsumer); cmd.exec(callback); - Predicate waitPredicate = outputFrame -> + Predicate waitPredicate = outputFrame -> { // (?s) enables line terminator matching (equivalent to Pattern.DOTALL) - outputFrame.getUtf8String().matches("(?s)" + regEx); - + return outputFrame.getUtf8String().matches("(?s)" + regEx); + }; try { waitingConsumer.waitUntil(waitPredicate, startupTimeout.getSeconds(), TimeUnit.SECONDS, times); } catch (TimeoutException e) { diff --git a/core/src/main/java/org/testcontainers/containers/wait/strategy/Wait.java b/core/src/main/java/org/testcontainers/containers/wait/strategy/Wait.java index 034a83a2949..6c3b9e86aec 100644 --- a/core/src/main/java/org/testcontainers/containers/wait/strategy/Wait.java +++ b/core/src/main/java/org/testcontainers/containers/wait/strategy/Wait.java @@ -1,12 +1,11 @@ package org.testcontainers.containers.wait.strategy; -import java.net.HttpURLConnection; - /** * Convenience class with logic for building common {@link WaitStrategy} instances. * */ public class Wait { + /** * Convenience method to return the default WaitStrategy. * @@ -34,8 +33,7 @@ public static HostPortWaitStrategy forListeningPort() { * @see HttpWaitStrategy */ public static HttpWaitStrategy forHttp(String path) { - return new HttpWaitStrategy() - .forPath(path); + return new HttpWaitStrategy().forPath(path); } /** @@ -46,8 +44,7 @@ public static HttpWaitStrategy forHttp(String path) { * @see HttpWaitStrategy */ public static HttpWaitStrategy forHttps(String path) { - return forHttp(path) - .usingTls(); + return forHttp(path).usingTls(); } /** diff --git a/core/src/main/java/org/testcontainers/containers/wait/strategy/WaitAllStrategy.java b/core/src/main/java/org/testcontainers/containers/wait/strategy/WaitAllStrategy.java index cebacb375b6..5c7530fc67f 100644 --- a/core/src/main/java/org/testcontainers/containers/wait/strategy/WaitAllStrategy.java +++ b/core/src/main/java/org/testcontainers/containers/wait/strategy/WaitAllStrategy.java @@ -1,16 +1,15 @@ package org.testcontainers.containers.wait.strategy; +import org.rnorth.ducttape.timeouts.Timeouts; + import java.time.Duration; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; -import org.rnorth.ducttape.timeouts.Timeouts; - public class WaitAllStrategy implements WaitStrategy { public enum Mode { - /** * This is the default mode: The timeout of the {@link WaitAllStrategy strategy} is applied to each individual * strategy, so that the container waits maximum for @@ -30,11 +29,13 @@ public enum Mode { * This is the original mode of this strategy: The inner strategies wait with their preconfigured timeout * individually and the wait all strategy kills them, if the outer limit is reached. */ - WITH_MAXIMUM_OUTER_TIMEOUT + WITH_MAXIMUM_OUTER_TIMEOUT, } private final Mode mode; + private final List strategies = new ArrayList<>(); + private Duration timeout = Duration.ofSeconds(30); public WaitAllStrategy() { @@ -50,9 +51,13 @@ public void waitUntilReady(WaitStrategyTarget waitStrategyTarget) { if (mode == Mode.WITH_INDIVIDUAL_TIMEOUTS_ONLY) { waitUntilNestedStrategiesAreReady(waitStrategyTarget); } else { - Timeouts.doWithTimeout((int) timeout.toMillis(), TimeUnit.MILLISECONDS, () -> { - waitUntilNestedStrategiesAreReady(waitStrategyTarget); - }); + Timeouts.doWithTimeout( + (int) timeout.toMillis(), + TimeUnit.MILLISECONDS, + () -> { + waitUntilNestedStrategiesAreReady(waitStrategyTarget); + } + ); } } @@ -63,7 +68,6 @@ private void waitUntilNestedStrategiesAreReady(WaitStrategyTarget waitStrategyTa } public WaitAllStrategy withStrategy(WaitStrategy strategy) { - if (mode == Mode.WITH_OUTER_TIMEOUT) { applyStartupTimeout(strategy); } @@ -74,10 +78,13 @@ public WaitAllStrategy withStrategy(WaitStrategy strategy) { @Override public WaitAllStrategy withStartupTimeout(Duration startupTimeout) { - if (mode == Mode.WITH_INDIVIDUAL_TIMEOUTS_ONLY) { - throw new IllegalStateException(String.format( - "Changing startup timeout is not supported with mode %s", Mode.WITH_INDIVIDUAL_TIMEOUTS_ONLY)); + throw new IllegalStateException( + String.format( + "Changing startup timeout is not supported with mode %s", + Mode.WITH_INDIVIDUAL_TIMEOUTS_ONLY + ) + ); } this.timeout = startupTimeout; diff --git a/core/src/main/java/org/testcontainers/containers/wait/strategy/WaitStrategy.java b/core/src/main/java/org/testcontainers/containers/wait/strategy/WaitStrategy.java index 25703174502..ff371cf775f 100644 --- a/core/src/main/java/org/testcontainers/containers/wait/strategy/WaitStrategy.java +++ b/core/src/main/java/org/testcontainers/containers/wait/strategy/WaitStrategy.java @@ -3,7 +3,6 @@ import java.time.Duration; public interface WaitStrategy { - void waitUntilReady(WaitStrategyTarget waitStrategyTarget); WaitStrategy withStartupTimeout(Duration startupTimeout); diff --git a/core/src/main/java/org/testcontainers/containers/wait/strategy/WaitStrategyTarget.java b/core/src/main/java/org/testcontainers/containers/wait/strategy/WaitStrategyTarget.java index 49f88fb7711..778c252009b 100644 --- a/core/src/main/java/org/testcontainers/containers/wait/strategy/WaitStrategyTarget.java +++ b/core/src/main/java/org/testcontainers/containers/wait/strategy/WaitStrategyTarget.java @@ -6,13 +6,15 @@ import java.util.stream.Collectors; public interface WaitStrategyTarget extends ContainerState { - /** * @return the ports on which to check if the container is ready */ default Set getLivenessCheckPortNumbers() { - final Set result = getExposedPorts().stream() - .map(this::getMappedPort).distinct().collect(Collectors.toSet()); + final Set result = getExposedPorts() + .stream() + .map(this::getMappedPort) + .distinct() + .collect(Collectors.toSet()); result.addAll(getBoundPortNumbers()); return result; } diff --git a/core/src/main/java/org/testcontainers/dockerclient/AuditLoggingDockerClient.java b/core/src/main/java/org/testcontainers/dockerclient/AuditLoggingDockerClient.java index 2dc88cad313..2b413aafaed 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/AuditLoggingDockerClient.java +++ b/core/src/main/java/org/testcontainers/dockerclient/AuditLoggingDockerClient.java @@ -5,13 +5,12 @@ import lombok.experimental.Delegate; import lombok.extern.slf4j.Slf4j; import org.jetbrains.annotations.NotNull; +import org.testcontainers.utility.AuditLogger; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Proxy; import java.util.function.BiConsumer; -import static org.testcontainers.utility.AuditLogger.doLog; - /** * Wrapper for {@link DockerClient} to facilitate 'audit logging' of potentially destruction actions using * {@link org.testcontainers.utility.AuditLogger}. @@ -30,97 +29,117 @@ public AuditLoggingDockerClient(DockerClient wrappedClient) { @Override public CreateContainerCmd createContainerCmd(@NotNull String image) { - return wrappedCommand(CreateContainerCmd.class, - wrappedClient.createContainerCmd(image), - (cmd, res) -> doLog("CREATE", image, res.getId(), cmd), - (cmd, e) -> doLog("CREATE", image, null, cmd, e)); - + return wrappedCommand( + CreateContainerCmd.class, + wrappedClient.createContainerCmd(image), + (cmd, res) -> AuditLogger.doLog("CREATE", image, res.getId(), cmd), + (cmd, e) -> AuditLogger.doLog("CREATE", image, null, cmd, e) + ); } @Override public StartContainerCmd startContainerCmd(@NotNull String containerId) { - return wrappedCommand(StartContainerCmd.class, - wrappedClient.startContainerCmd(containerId), - (cmd, res) -> doLog("START", null, containerId, cmd), - (cmd, e) -> doLog("START", null, containerId, cmd, e)); + return wrappedCommand( + StartContainerCmd.class, + wrappedClient.startContainerCmd(containerId), + (cmd, res) -> AuditLogger.doLog("START", null, containerId, cmd), + (cmd, e) -> AuditLogger.doLog("START", null, containerId, cmd, e) + ); } @Override public RemoveContainerCmd removeContainerCmd(@NotNull String containerId) { - return wrappedCommand(RemoveContainerCmd.class, - wrappedClient.removeContainerCmd(containerId), - (cmd, res) -> doLog("REMOVE", null, containerId, cmd), - (cmd, e) -> doLog("REMOVE", null, containerId, cmd, e)); + return wrappedCommand( + RemoveContainerCmd.class, + wrappedClient.removeContainerCmd(containerId), + (cmd, res) -> AuditLogger.doLog("REMOVE", null, containerId, cmd), + (cmd, e) -> AuditLogger.doLog("REMOVE", null, containerId, cmd, e) + ); } @Override public StopContainerCmd stopContainerCmd(@NotNull String containerId) { - return wrappedCommand(StopContainerCmd.class, - wrappedClient.stopContainerCmd(containerId), - (cmd, res) -> doLog("STOP", null, containerId, cmd), - (cmd, e) -> doLog("STOP", null, containerId, cmd, e)); + return wrappedCommand( + StopContainerCmd.class, + wrappedClient.stopContainerCmd(containerId), + (cmd, res) -> AuditLogger.doLog("STOP", null, containerId, cmd), + (cmd, e) -> AuditLogger.doLog("STOP", null, containerId, cmd, e) + ); } @Override public KillContainerCmd killContainerCmd(@NotNull String containerId) { - return wrappedCommand(KillContainerCmd.class, - wrappedClient.killContainerCmd(containerId), - (cmd, res) -> doLog("KILL", null, containerId, cmd), - (cmd, e) -> doLog("KILL", null, containerId, cmd, e)); + return wrappedCommand( + KillContainerCmd.class, + wrappedClient.killContainerCmd(containerId), + (cmd, res) -> AuditLogger.doLog("KILL", null, containerId, cmd), + (cmd, e) -> AuditLogger.doLog("KILL", null, containerId, cmd, e) + ); } @Override public CreateNetworkCmd createNetworkCmd() { - return wrappedCommand(CreateNetworkCmd.class, - wrappedClient.createNetworkCmd(), - (cmd, res) -> doLog("CREATE_NETWORK", null, null, cmd), - (cmd, e) -> doLog("CREATE_NETWORK", null, null, cmd, e)); + return wrappedCommand( + CreateNetworkCmd.class, + wrappedClient.createNetworkCmd(), + (cmd, res) -> AuditLogger.doLog("CREATE_NETWORK", null, null, cmd), + (cmd, e) -> AuditLogger.doLog("CREATE_NETWORK", null, null, cmd, e) + ); } @Override public RemoveNetworkCmd removeNetworkCmd(@NotNull String networkId) { - return wrappedCommand(RemoveNetworkCmd.class, - wrappedClient.removeNetworkCmd(networkId), - (cmd, res) -> doLog("REMOVE_NETWORK", null, null, cmd), - (cmd, e) -> doLog("REMOVE_NETWORK", null, null, cmd, e)); + return wrappedCommand( + RemoveNetworkCmd.class, + wrappedClient.removeNetworkCmd(networkId), + (cmd, res) -> AuditLogger.doLog("REMOVE_NETWORK", null, null, cmd), + (cmd, e) -> AuditLogger.doLog("REMOVE_NETWORK", null, null, cmd, e) + ); } - private , R> T wrappedCommand(Class clazz, - T cmd, - BiConsumer successConsumer, - BiConsumer failureConsumer) { - + private , R> T wrappedCommand( + Class clazz, + T cmd, + BiConsumer successConsumer, + BiConsumer failureConsumer + ) { return (T) Proxy.newProxyInstance( - clazz.getClassLoader(), - new Class[]{clazz}, - (proxy, method, args) -> { - - if (method.getName().equals("exec")) { - try { - R r = (R) method.invoke(cmd, args); - successConsumer.accept(cmd, r); - return r; - } catch (Exception e) { - if (e instanceof InvocationTargetException && e.getCause() instanceof Exception) { - e = (Exception) e.getCause(); - } - failureConsumer.accept(cmd, e); - throw e; + clazz.getClassLoader(), + new Class[] { clazz }, + (proxy, method, args) -> { + if (method.getName().equals("exec")) { + try { + R r = (R) method.invoke(cmd, args); + successConsumer.accept(cmd, r); + return r; + } catch (Exception e) { + if (e instanceof InvocationTargetException && e.getCause() instanceof Exception) { + e = (Exception) e.getCause(); } - } else { - return method.invoke(cmd, args); + failureConsumer.accept(cmd, e); + throw e; } - }); + } else { + return method.invoke(cmd, args); + } + } + ); } @SuppressWarnings("unused") private interface InterceptedMethods { CreateContainerCmd createContainerCmd(String image); + StartContainerCmd startContainerCmd(String containerId); + RemoveContainerCmd removeContainerCmd(String containerId); + StopContainerCmd stopContainerCmd(String containerId); + KillContainerCmd killContainerCmd(String containerId); + CreateNetworkCmd createNetworkCmd(); + RemoveNetworkCmd removeNetworkCmd(String networkId); } } diff --git a/core/src/main/java/org/testcontainers/dockerclient/AuthDelegatingDockerClientConfig.java b/core/src/main/java/org/testcontainers/dockerclient/AuthDelegatingDockerClientConfig.java index 1f534eb59e5..5713d054e13 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/AuthDelegatingDockerClientConfig.java +++ b/core/src/main/java/org/testcontainers/dockerclient/AuthDelegatingDockerClientConfig.java @@ -4,11 +4,10 @@ import com.github.dockerjava.core.DockerClientConfig; import com.github.dockerjava.core.DockerClientConfigDelegate; import lombok.extern.slf4j.Slf4j; +import org.testcontainers.utility.AuthConfigUtil; import org.testcontainers.utility.DockerImageName; import org.testcontainers.utility.RegistryAuthLocator; -import static org.testcontainers.utility.AuthConfigUtil.toSafeString; - /** * Facade implementation for {@link DockerClientConfig} which overrides how authentication * configuration is obtained. A delegate {@link DockerClientConfig} will be called first @@ -31,18 +30,21 @@ public AuthConfig effectiveAuthConfig(String imageName) { try { fallbackAuthConfig = super.effectiveAuthConfig(imageName); } catch (Exception e) { - log.debug("Delegate call to effectiveAuthConfig failed with cause: '{}'. " + + log.debug( + "Delegate call to effectiveAuthConfig failed with cause: '{}'. " + "Resolution of auth config will continue using RegistryAuthLocator.", - e.getMessage()); + e.getMessage() + ); fallbackAuthConfig = new AuthConfig(); } // try and obtain more accurate auth config using our resolution final DockerImageName parsed = DockerImageName.parse(imageName); - final AuthConfig effectiveAuthConfig = RegistryAuthLocator.instance() + final AuthConfig effectiveAuthConfig = RegistryAuthLocator + .instance() .lookupAuthConfig(parsed, fallbackAuthConfig); - log.debug("Effective auth config [{}]", toSafeString(effectiveAuthConfig)); + log.debug("Effective auth config [{}]", AuthConfigUtil.toSafeString(effectiveAuthConfig)); return effectiveAuthConfig; } } diff --git a/core/src/main/java/org/testcontainers/dockerclient/DockerClientConfigUtils.java b/core/src/main/java/org/testcontainers/dockerclient/DockerClientConfigUtils.java index e393e59419f..7206a7e1452 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/DockerClientConfigUtils.java +++ b/core/src/main/java/org/testcontainers/dockerclient/DockerClientConfigUtils.java @@ -8,8 +8,7 @@ import java.io.File; import java.net.URI; import java.util.Optional; - -import static java.util.concurrent.TimeUnit.SECONDS; +import java.util.concurrent.TimeUnit; @Slf4j public class DockerClientConfigUtils { @@ -19,25 +18,31 @@ public class DockerClientConfigUtils { @Getter(lazy = true) private static final Optional defaultGateway = Optional - .ofNullable(DockerClientFactory.instance().runInsideDocker( + .ofNullable( + DockerClientFactory + .instance() + .runInsideDocker( cmd -> cmd.withCmd("sh", "-c", "ip route|awk '/default/ { print $3 }'"), (client, id) -> { try { LogToStringContainerCallback loggingCallback = new LogToStringContainerCallback(); - client.logContainerCmd(id).withStdOut(true) - .withFollowStream(true) - .exec(loggingCallback) - .awaitStarted(); - loggingCallback.awaitCompletion(3, SECONDS); + client + .logContainerCmd(id) + .withStdOut(true) + .withFollowStream(true) + .exec(loggingCallback) + .awaitStarted(); + loggingCallback.awaitCompletion(3, TimeUnit.SECONDS); return loggingCallback.toString(); } catch (Exception e) { log.warn("Can't parse the default gateway IP", e); return null; } } - )) - .map(StringUtils::trimToEmpty) - .filter(StringUtils::isNotBlank); + ) + ) + .map(StringUtils::trimToEmpty) + .filter(StringUtils::isNotBlank); /** * @deprecated use {@link DockerClientProviderStrategy#getDockerHostIpAddress()} diff --git a/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java index 834e670438a..0d2aa3d08ed 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java @@ -27,7 +27,6 @@ import org.testcontainers.UnstableAPI; import org.testcontainers.utility.TestcontainersConfiguration; -import javax.net.SocketFactory; import java.io.File; import java.net.InetSocketAddress; import java.net.Socket; @@ -54,6 +53,8 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import javax.net.SocketFactory; + /** * Mechanism to find a viable Docker client configuration according to the host system environment. */ @@ -68,10 +69,11 @@ public abstract class DockerClientProviderStrategy { @Getter private Info info; - private final RateLimiter PING_RATE_LIMITER = RateLimiterBuilder.newBuilder() - .withRate(10, TimeUnit.SECONDS) - .withConstantThroughput() - .build(); + private final RateLimiter PING_RATE_LIMITER = RateLimiterBuilder + .newBuilder() + .withRate(10, TimeUnit.SECONDS) + .withConstantThroughput() + .build(); private static final AtomicBoolean FAIL_FAST_ALWAYS = new AtomicBoolean(false); @@ -109,14 +111,18 @@ protected int getPriority() { public DockerClient getClient() { DockerClient dockerClient = getDockerClient(); try { - Unreliables.retryUntilSuccess(30, TimeUnit.SECONDS, () -> { - return PING_RATE_LIMITER.getWhenReady(() -> { - log.debug("Pinging docker daemon..."); - dockerClient.pingCmd().exec(); - log.debug("Pinged"); - return true; - }); - }); + Unreliables.retryUntilSuccess( + 30, + TimeUnit.SECONDS, + () -> { + return PING_RATE_LIMITER.getWhenReady(() -> { + log.debug("Pinging docker daemon..."); + dockerClient.pingCmd().exec(); + log.debug("Pinged"); + return true; + }); + } + ); } catch (TimeoutException e) { IOUtils.closeQuietly(dockerClient); throw e; @@ -143,7 +149,12 @@ protected boolean test() { if (sslConfig != null) { try { socketFactory = sslConfig.getSSLContext().getSocketFactory(); - } catch (KeyManagementException | UnrecoverableKeyException | NoSuchAlgorithmException | KeyStoreException e) { + } catch ( + KeyManagementException + | UnrecoverableKeyException + | NoSuchAlgorithmException + | KeyStoreException e + ) { log.warn("Exception while creating SSLSocketFactory", e); return false; } @@ -157,16 +168,17 @@ protected boolean test() { log.debug("DOCKER_HOST socket file '{}' does not exist", dockerHost.getPath()); return false; } - socketProvider = () -> { - switch (dockerHost.getScheme()) { - case "unix": - return UnixSocket.get(dockerHost.getPath()); - case "npipe": - return new NamedPipeSocket(dockerHost.getPath()); - default: - throw new IllegalStateException("Unexpected scheme " + dockerHost.getScheme()); - } - }; + socketProvider = + () -> { + switch (dockerHost.getScheme()) { + case "unix": + return UnixSocket.get(dockerHost.getPath()); + case "npipe": + return new NamedPipeSocket(dockerHost.getPath()); + default: + throw new IllegalStateException("Unexpected scheme " + dockerHost.getScheme()); + } + }; socketAddress = new InetSocketAddress("localhost", 2375); break; default: @@ -176,7 +188,8 @@ protected boolean test() { try (Socket socket = socketProvider.call()) { Duration timeout = Duration.ofMillis(200); - Awaitility.await() + Awaitility + .await() .atMost(TestcontainersConfiguration.getInstance().getClientPingTimeout(), TimeUnit.SECONDS) .pollInterval(timeout) .pollDelay(Duration.ofSeconds(0)) // start checking immediately @@ -195,9 +208,10 @@ protected boolean test() { * @return a working DockerClientConfig, as determined by successful execution of a ping command */ public static DockerClientProviderStrategy getFirstValidStrategy(List strategies) { - if (FAIL_FAST_ALWAYS.get()) { - throw new IllegalStateException("Previous attempts to find a Docker environment failed. Will not retry. Please see logs and check configuration"); + throw new IllegalStateException( + "Previous attempts to find a Docker environment failed. Will not retry. Please see logs and check configuration" + ); } List configurationFailures = new ArrayList<>(); @@ -215,7 +229,6 @@ public static DockerClientProviderStrategy getFirstValidStrategy(List allStrategies)); - Predicate distinctStrategyClassPredicate = new Predicate() { final Set> classes = new HashSet<>(); @@ -232,14 +245,18 @@ public boolean test(DockerClientProviderStrategy dockerClientProviderStrategy) { .filter(strategy -> tryOutStrategy(configurationFailures, strategy)) .findFirst() .orElseThrow(() -> { - log.error("Could not find a valid Docker environment. Please check configuration. Attempted configurations were:"); + log.error( + "Could not find a valid Docker environment. Please check configuration. Attempted configurations were:" + ); for (String failureMessage : configurationFailures) { log.error(" " + failureMessage); } log.error("As no valid configuration was found, execution cannot continue"); FAIL_FAST_ALWAYS.set(true); - return new IllegalStateException("Could not find a valid Docker environment. Please see logs and check configuration"); + return new IllegalStateException( + "Could not find a valid Docker environment. Please see logs and check configuration" + ); }); } @@ -270,30 +287,39 @@ private static boolean tryOutStrategy(List configurationFailures, Docker } if (strategy.isPersistable()) { - TestcontainersConfiguration.getInstance().updateUserConfig("docker.client.strategy", strategy.getClass().getName()); + TestcontainersConfiguration + .getInstance() + .updateUserConfig("docker.client.strategy", strategy.getClass().getName()); } return true; } catch (Exception | ExceptionInInitializerError | NoClassDefFoundError e) { - @Nullable String throwableMessage = e.getMessage(); + @Nullable + String throwableMessage = e.getMessage(); @SuppressWarnings("ThrowableResultOfMethodCallIgnored") Throwable rootCause = Throwables.getRootCause(e); - @Nullable String rootCauseMessage = rootCause.getMessage(); + @Nullable + String rootCauseMessage = rootCause.getMessage(); String failureDescription; if (throwableMessage != null && throwableMessage.equals(rootCauseMessage)) { - failureDescription = String.format("%s: failed with exception %s (%s)", + failureDescription = + String.format( + "%s: failed with exception %s (%s)", strategy.getClass().getSimpleName(), e.getClass().getSimpleName(), - throwableMessage); + throwableMessage + ); } else { - failureDescription = String.format("%s: failed with exception %s (%s). Root cause %s (%s)", + failureDescription = + String.format( + "%s: failed with exception %s (%s). Root cause %s (%s)", strategy.getClass().getSimpleName(), e.getClass().getSimpleName(), throwableMessage, rootCause.getClass().getSimpleName(), rootCauseMessage - ); + ); } configurationFailures.add(failureDescription); @@ -303,20 +329,28 @@ private static boolean tryOutStrategy(List configurationFailures, Docker } private static Optional loadConfiguredStrategy() { - String configuredDockerClientStrategyClassName = TestcontainersConfiguration.getInstance().getDockerClientStrategyClassName(); + String configuredDockerClientStrategyClassName = TestcontainersConfiguration + .getInstance() + .getDockerClientStrategyClassName(); return Stream .of(configuredDockerClientStrategyClassName) .filter(Objects::nonNull) .flatMap(it -> { try { - Class strategyClass = (Class) Thread.currentThread().getContextClassLoader().loadClass(it); + Class strategyClass = (Class) Thread + .currentThread() + .getContextClassLoader() + .loadClass(it); return Stream.of(strategyClass.newInstance()); } catch (ClassNotFoundException e) { - log.warn("Can't instantiate a strategy from {} (ClassNotFoundException). " + + log.warn( + "Can't instantiate a strategy from {} (ClassNotFoundException). " + "This probably means that cached configuration refers to a client provider " + "class that is not available in this version of Testcontainers. Other " + - "strategies will be tried instead.", it); + "strategies will be tried instead.", + it + ); return Stream.empty(); } catch (InstantiationException | IllegalAccessException e) { log.warn("Can't instantiate a strategy from {}", it, e); @@ -325,7 +359,12 @@ private static Optional loadConfiguredSt }) // Ignore persisted strategy if it's not persistable anymore .filter(DockerClientProviderStrategy::isPersistable) - .peek(strategy -> log.info("Loaded {} from ~/.testcontainers.properties, will try it first", strategy.getClass().getName())) + .peek(strategy -> { + log.info( + "Loaded {} from ~/.testcontainers.properties, will try it first", + strategy.getClass().getName() + ); + }) .findFirst(); } @@ -335,10 +374,11 @@ public static DockerClient getClientForConfig(TransportConfig transportConfig) { String transportType = TestcontainersConfiguration.getInstance().getTransportType(); switch (transportType) { case "httpclient5": - dockerHttpClient = new ZerodepDockerHttpClient.Builder() - .dockerHost(transportConfig.getDockerHost()) - .sslConfig(transportConfig.getSslConfig()) - .build(); + dockerHttpClient = + new ZerodepDockerHttpClient.Builder() + .dockerHost(transportConfig.getDockerHost()) + .sslConfig(transportConfig.getSslConfig()) + .build(); break; default: throw new IllegalArgumentException("Unknown transport type '" + transportType + "'"); @@ -351,9 +391,7 @@ public static DockerClient getClientForConfig(TransportConfig transportConfig) { } return DockerClientImpl.getInstance( new AuthDelegatingDockerClientConfig( - configBuilder - .withDockerHost(transportConfig.getDockerHost().toString()) - .build() + configBuilder.withDockerHost(transportConfig.getDockerHost().toString()).build() ), new HeadersAddingDockerHttpClient( dockerHttpClient, @@ -384,7 +422,8 @@ static String resolveDockerHostIpAddress(DockerClient client, URI dockerHost) { case "unix": case "npipe": if (DockerClientConfigUtils.IN_A_CONTAINER) { - return client.inspectNetworkCmd() + return client + .inspectNetworkCmd() .withNetworkId("bridge") .exec() .getIpam() diff --git a/core/src/main/java/org/testcontainers/dockerclient/DockerMachineClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/DockerMachineClientProviderStrategy.java index 4de508ea808..7c05a11c198 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/DockerMachineClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/DockerMachineClientProviderStrategy.java @@ -1,6 +1,7 @@ package org.testcontainers.dockerclient; import com.github.dockerjava.core.LocalDirectorySSLConfig; +import com.google.common.base.Preconditions; import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.testcontainers.utility.CommandLine; @@ -11,8 +12,6 @@ import java.util.Arrays; import java.util.Optional; -import static com.google.common.base.Preconditions.checkArgument; - /** * Use Docker machine (if available on the PATH) to locate a Docker environment. * @@ -27,10 +26,16 @@ public final class DockerMachineClientProviderStrategy extends DockerClientProvi private TransportConfig resolveTransportConfig() throws InvalidConfigurationException { boolean installed = DockerMachineClient.instance().isInstalled(); - checkArgument(installed, "docker-machine executable was not found on PATH (" + Arrays.toString(CommandLine.getSystemPath()) + ")"); + Preconditions.checkArgument( + installed, + "docker-machine executable was not found on PATH (" + Arrays.toString(CommandLine.getSystemPath()) + ")" + ); Optional machineNameOptional = DockerMachineClient.instance().getDefaultMachine(); - checkArgument(machineNameOptional.isPresent(), "docker-machine is installed but no default machine could be found"); + Preconditions.checkArgument( + machineNameOptional.isPresent(), + "docker-machine is installed but no default machine could be found" + ); String machineName = machineNameOptional.get(); log.info("Found docker-machine, and will use machine named {}", machineName); @@ -41,7 +46,8 @@ private TransportConfig resolveTransportConfig() throws InvalidConfigurationExce log.info("Docker daemon URL for docker machine {} is {}", machineName, dockerDaemonUrl); - return TransportConfig.builder() + return TransportConfig + .builder() .dockerHost(URI.create(dockerDaemonUrl)) .sslConfig( new LocalDirectorySSLConfig( @@ -55,7 +61,10 @@ private TransportConfig resolveTransportConfig() throws InvalidConfigurationExce protected boolean isApplicable() { boolean installed = DockerMachineClient.instance().isInstalled(); if (!installed) { - log.info("docker-machine executable was not found on PATH ({})", Arrays.toString(CommandLine.getSystemPath())); + log.info( + "docker-machine executable was not found on PATH ({})", + Arrays.toString(CommandLine.getSystemPath()) + ); return false; } diff --git a/core/src/main/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategy.java index 06fdf387d6a..969610373d1 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategy.java @@ -35,7 +35,8 @@ public EnvironmentAndSystemPropertyClientProviderStrategy() { } EnvironmentAndSystemPropertyClientProviderStrategy(DefaultDockerClientConfig.Builder configBuilder) { - String dockerConfigSource = TestcontainersConfiguration.getInstance() + String dockerConfigSource = TestcontainersConfiguration + .getInstance() .getEnvVarOrProperty("dockerconfig.source", "auto"); switch (dockerConfigSource) { @@ -62,7 +63,8 @@ private Optional getSetting(final String name) { @Override public TransportConfig getTransportConfig() { - return TransportConfig.builder() + return TransportConfig + .builder() .dockerHost(dockerClientConfig.getDockerHost()) .sslConfig(dockerClientConfig.getSSLConfig()) .build(); @@ -75,7 +77,10 @@ protected int getPriority() { @Override public String getDescription() { - return "Environment variables, system properties and defaults. Resolved dockerHost=" + dockerClientConfig.getDockerHost(); + return ( + "Environment variables, system properties and defaults. Resolved dockerHost=" + + dockerClientConfig.getDockerHost() + ); } @Override diff --git a/core/src/main/java/org/testcontainers/dockerclient/HeadersAddingDockerHttpClient.java b/core/src/main/java/org/testcontainers/dockerclient/HeadersAddingDockerHttpClient.java index 59a51e19a80..7ff1b0b620c 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/HeadersAddingDockerHttpClient.java +++ b/core/src/main/java/org/testcontainers/dockerclient/HeadersAddingDockerHttpClient.java @@ -19,9 +19,7 @@ class HeadersAddingDockerHttpClient implements DockerHttpClient { @Override public Response execute(Request request) { - request = Request.builder().from(request) - .putAllHeaders(headers) - .build(); + request = Request.builder().from(request).putAllHeaders(headers).build(); return delegate.execute(request); } } diff --git a/core/src/main/java/org/testcontainers/dockerclient/InvalidConfigurationException.java b/core/src/main/java/org/testcontainers/dockerclient/InvalidConfigurationException.java index ef834c9cec5..504378cb17e 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/InvalidConfigurationException.java +++ b/core/src/main/java/org/testcontainers/dockerclient/InvalidConfigurationException.java @@ -12,4 +12,4 @@ public InvalidConfigurationException(String s) { public InvalidConfigurationException(String message, Throwable cause) { super(message, cause); } -} \ No newline at end of file +} diff --git a/core/src/main/java/org/testcontainers/dockerclient/LogToStringContainerCallback.java b/core/src/main/java/org/testcontainers/dockerclient/LogToStringContainerCallback.java index d376b685a91..2c0716b65f0 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/LogToStringContainerCallback.java +++ b/core/src/main/java/org/testcontainers/dockerclient/LogToStringContainerCallback.java @@ -9,6 +9,7 @@ */ @Deprecated public class LogToStringContainerCallback extends ResultCallback.Adapter { + private final StringBuffer log = new StringBuffer(); @Override diff --git a/core/src/main/java/org/testcontainers/dockerclient/NpipeSocketClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/NpipeSocketClientProviderStrategy.java index 7f73e1d7675..0e1f641fe8a 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/NpipeSocketClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/NpipeSocketClientProviderStrategy.java @@ -12,15 +12,14 @@ public final class NpipeSocketClientProviderStrategy extends DockerClientProviderStrategy { protected static final String DOCKER_SOCK_PATH = "//./pipe/docker_engine"; + private static final String SOCKET_LOCATION = "npipe://" + DOCKER_SOCK_PATH; public static final int PRIORITY = EnvironmentAndSystemPropertyClientProviderStrategy.PRIORITY - 20; @Override public TransportConfig getTransportConfig() { - return TransportConfig.builder() - .dockerHost(URI.create(SOCKET_LOCATION)) - .build(); + return TransportConfig.builder().dockerHost(URI.create(SOCKET_LOCATION)).build(); } @Override diff --git a/core/src/main/java/org/testcontainers/dockerclient/RootlessDockerClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/RootlessDockerClientProviderStrategy.java index 36d6b5f6123..a032c2b324d 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/RootlessDockerClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/RootlessDockerClientProviderStrategy.java @@ -29,13 +29,15 @@ public final class RootlessDockerClientProviderStrategy extends DockerClientProv private final Path socketPath = resolveSocketPath(); private Path resolveSocketPath() { - return tryEnv().orElseGet(() -> { - Path homePath = Paths.get(System.getProperty("user.home")).resolve(".docker").resolve("run"); - return tryFolder(homePath).orElseGet(() -> { - Path implicitPath = Paths.get("/run/user/" + LibC.INSTANCE.getuid()); - return tryFolder(implicitPath).orElse(null); + return tryEnv() + .orElseGet(() -> { + Path homePath = Paths.get(System.getProperty("user.home")).resolve(".docker").resolve("run"); + return tryFolder(homePath) + .orElseGet(() -> { + Path implicitPath = Paths.get("/run/user/" + LibC.INSTANCE.getuid()); + return tryFolder(implicitPath).orElse(null); + }); }); - }); } private Optional tryEnv() { @@ -72,9 +74,7 @@ private Optional tryFolder(Path path) { @Override public TransportConfig getTransportConfig() throws InvalidConfigurationException { - return TransportConfig.builder() - .dockerHost(URI.create("unix://" + getSocketPath().toString())) - .build(); + return TransportConfig.builder().dockerHost(URI.create("unix://" + getSocketPath().toString())).build(); } @Override @@ -93,10 +93,8 @@ protected int getPriority() { } private interface LibC extends Library { - LibC INSTANCE = Native.loadLibrary("c", LibC.class); int getuid(); } - } diff --git a/core/src/main/java/org/testcontainers/dockerclient/UnixSocketClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/UnixSocketClientProviderStrategy.java index 4a85394155d..c262bda1db5 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/UnixSocketClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/UnixSocketClientProviderStrategy.java @@ -14,8 +14,11 @@ */ @Deprecated public final class UnixSocketClientProviderStrategy extends DockerClientProviderStrategy { + protected static final String DOCKER_SOCK_PATH = "/var/run/docker.sock"; + private static final String SOCKET_LOCATION = "unix://" + DOCKER_SOCK_PATH; + private static final int SOCKET_FILE_MODE_MASK = 0xc000; public static final int PRIORITY = EnvironmentAndSystemPropertyClientProviderStrategy.PRIORITY - 20; @@ -31,12 +34,12 @@ public TransportConfig getTransportConfig() throws InvalidConfigurationException } if ((mode & 0xc000) != SOCKET_FILE_MODE_MASK) { - throw new InvalidConfigurationException("Found docker unix domain socket but file mode was not as expected (expected: srwxr-xr-x). This problem is possibly due to occurrence of this issue in the past: https://github.com/docker/docker/issues/13121"); + throw new InvalidConfigurationException( + "Found docker unix domain socket but file mode was not as expected (expected: srwxr-xr-x). This problem is possibly due to occurrence of this issue in the past: https://github.com/docker/docker/issues/13121" + ); } - return TransportConfig.builder() - .dockerHost(URI.create(SOCKET_LOCATION)) - .build(); + return TransportConfig.builder().dockerHost(URI.create(SOCKET_LOCATION)).build(); } @Override diff --git a/core/src/main/java/org/testcontainers/images/AbstractImagePullPolicy.java b/core/src/main/java/org/testcontainers/images/AbstractImagePullPolicy.java index f5d75ad9617..1f9583ef4cf 100644 --- a/core/src/main/java/org/testcontainers/images/AbstractImagePullPolicy.java +++ b/core/src/main/java/org/testcontainers/images/AbstractImagePullPolicy.java @@ -36,7 +36,6 @@ public boolean shouldPull(DockerImageName imageName) { log.debug("Using locally available and not pulling image: {}", imageName); return false; } - } /** @@ -45,5 +44,5 @@ public boolean shouldPull(DockerImageName imageName) { * * @return {@code true} to update the locally available image, {@code false} to use local instead */ - abstract protected boolean shouldPullCached(DockerImageName imageName, ImageData localImageData); + protected abstract boolean shouldPullCached(DockerImageName imageName, ImageData localImageData); } diff --git a/core/src/main/java/org/testcontainers/images/ImageData.java b/core/src/main/java/org/testcontainers/images/ImageData.java index 345b5939268..e57be2dbbbd 100644 --- a/core/src/main/java/org/testcontainers/images/ImageData.java +++ b/core/src/main/java/org/testcontainers/images/ImageData.java @@ -17,14 +17,13 @@ public class ImageData { Instant createdAt; static ImageData from(InspectImageResponse inspectImageResponse) { - return ImageData.builder() + return ImageData + .builder() .createdAt(ZonedDateTime.parse(inspectImageResponse.getCreated()).toInstant()) .build(); } static ImageData from(Image image) { - return ImageData.builder() - .createdAt(Instant.ofEpochMilli(image.getCreated())) - .build(); + return ImageData.builder().createdAt(Instant.ofEpochMilli(image.getCreated())).build(); } } diff --git a/core/src/main/java/org/testcontainers/images/ImagePullPolicy.java b/core/src/main/java/org/testcontainers/images/ImagePullPolicy.java index e821e511795..a090c3f7b9c 100644 --- a/core/src/main/java/org/testcontainers/images/ImagePullPolicy.java +++ b/core/src/main/java/org/testcontainers/images/ImagePullPolicy.java @@ -3,7 +3,5 @@ import org.testcontainers.utility.DockerImageName; public interface ImagePullPolicy { - boolean shouldPull(DockerImageName imageName); - } diff --git a/core/src/main/java/org/testcontainers/images/LocalImagesCache.java b/core/src/main/java/org/testcontainers/images/LocalImagesCache.java index 8791ecb6986..c98339e9f4c 100644 --- a/core/src/main/java/org/testcontainers/images/LocalImagesCache.java +++ b/core/src/main/java/org/testcontainers/images/LocalImagesCache.java @@ -79,14 +79,12 @@ private void populateFromList(List images) { } cache.putAll( - Stream.of(repoTags) + Stream + .of(repoTags) // Protection against some edge case where local image repository tags end up with duplicates // making toMap crash at merge time. .distinct() - .collect(Collectors.toMap( - DockerImageName::new, - it -> ImageData.from(image) - )) + .collect(Collectors.toMap(DockerImageName::new, it -> ImageData.from(image))) ); } } diff --git a/core/src/main/java/org/testcontainers/images/LoggedPullImageResultCallback.java b/core/src/main/java/org/testcontainers/images/LoggedPullImageResultCallback.java index 2ba8ba6df11..32e19090b5c 100644 --- a/core/src/main/java/org/testcontainers/images/LoggedPullImageResultCallback.java +++ b/core/src/main/java/org/testcontainers/images/LoggedPullImageResultCallback.java @@ -2,6 +2,7 @@ import com.github.dockerjava.api.command.PullImageResultCallback; import com.github.dockerjava.api.model.PullResponseItem; +import org.apache.commons.io.FileUtils; import org.slf4j.Logger; import java.io.Closeable; @@ -9,21 +10,25 @@ import java.time.Instant; import java.util.*; -import static java.lang.String.format; -import static org.apache.commons.io.FileUtils.byteCountToDisplaySize; - /** * {@link PullImageResultCallback} with improved logging of pull progress. */ class LoggedPullImageResultCallback extends PullImageResultCallback { + private final Logger logger; private final Set allLayers = new HashSet<>(); + private final Set downloadedLayers = new HashSet<>(); + private final Set pulledLayers = new HashSet<>(); + private final Map totalSizes = new HashMap<>(); + private final Map currentSizes = new HashMap<>(); + private boolean completed; + private Instant start; LoggedPullImageResultCallback(final Logger logger) { @@ -69,8 +74,7 @@ public void onNext(final PullResponseItem item) { } } - if (statusLowercase.startsWith("pulling from" ) || statusLowercase.contains("complete" )) { - + if (statusLowercase.startsWith("pulling from") || statusLowercase.contains("complete")) { long totalSize = totalLayerSize(); long currentSize = downloadedLayerSize(); @@ -79,15 +83,17 @@ public void onNext(final PullResponseItem item) { if (pendingCount > 0) { friendlyTotalSize = "? MB"; } else { - friendlyTotalSize = byteCountToDisplaySize(totalSize); + friendlyTotalSize = FileUtils.byteCountToDisplaySize(totalSize); } - logger.info("Pulling image layers: {} pending, {} downloaded, {} extracted, ({}/{})", - format("%2d", pendingCount), - format("%2d", downloadedLayers.size()), - format("%2d", pulledLayers.size()), - byteCountToDisplaySize(currentSize), - friendlyTotalSize); + logger.info( + "Pulling image layers: {} pending, {} downloaded, {} extracted, ({}/{})", + String.format("%2d", pendingCount), + String.format("%2d", downloadedLayers.size()), + String.format("%2d", pulledLayers.size()), + FileUtils.byteCountToDisplaySize(currentSize), + friendlyTotalSize + ); } if (statusLowercase.contains("complete")) { @@ -103,11 +109,13 @@ public void onComplete() { final long duration = Duration.between(start, Instant.now()).getSeconds(); if (completed) { - logger.info("Pull complete. {} layers, pulled in {}s (downloaded {} at {}/s)", + logger.info( + "Pull complete. {} layers, pulled in {}s (downloaded {} at {}/s)", allLayers.size(), duration, - byteCountToDisplaySize(downloadedLayerSize), - byteCountToDisplaySize(downloadedLayerSize / duration)); + FileUtils.byteCountToDisplaySize(downloadedLayerSize), + FileUtils.byteCountToDisplaySize(downloadedLayerSize / duration) + ); } } diff --git a/core/src/main/java/org/testcontainers/images/ParsedDockerfile.java b/core/src/main/java/org/testcontainers/images/ParsedDockerfile.java index d4574d17c34..ed88c1a85ff 100644 --- a/core/src/main/java/org/testcontainers/images/ParsedDockerfile.java +++ b/core/src/main/java/org/testcontainers/images/ParsedDockerfile.java @@ -21,7 +21,10 @@ @Slf4j public class ParsedDockerfile { - private static final Pattern FROM_LINE_PATTERN = Pattern.compile("FROM (?--[^\\s]+\\s)*(?[^\\s]+).*", Pattern.CASE_INSENSITIVE); + private static final Pattern FROM_LINE_PATTERN = Pattern.compile( + "FROM (?--[^\\s]+\\s)*(?[^\\s]+).*", + Pattern.CASE_INSENSITIVE + ); private final Path dockerFilePath; @@ -54,11 +57,13 @@ private List read() { } private void parse(List lines) { - dependencyImageNames = lines.stream() - .map(FROM_LINE_PATTERN::matcher) - .filter(Matcher::matches) - .map(matcher -> matcher.group("image")) - .collect(Collectors.toSet()); + dependencyImageNames = + lines + .stream() + .map(FROM_LINE_PATTERN::matcher) + .filter(Matcher::matches) + .map(matcher -> matcher.group("image")) + .collect(Collectors.toSet()); if (!dependencyImageNames.isEmpty()) { log.debug("Found dependency images in Dockerfile {}: {}", dockerFilePath, dependencyImageNames); diff --git a/core/src/main/java/org/testcontainers/images/PullPolicy.java b/core/src/main/java/org/testcontainers/images/PullPolicy.java index 3dc2e452b8d..a8b8344a8c2 100644 --- a/core/src/main/java/org/testcontainers/images/PullPolicy.java +++ b/core/src/main/java/org/testcontainers/images/PullPolicy.java @@ -1,9 +1,9 @@ package org.testcontainers.images; -import java.time.Duration; - import lombok.experimental.UtilityClass; +import java.time.Duration; + /** * Convenience class with logic for building common {@link ImagePullPolicy} instances. * @@ -34,5 +34,4 @@ public static ImagePullPolicy alwaysPull() { public static ImagePullPolicy ageBased(Duration maxAge) { return new AgeBasedPullPolicy(maxAge); } - } diff --git a/core/src/main/java/org/testcontainers/images/RemoteDockerImage.java b/core/src/main/java/org/testcontainers/images/RemoteDockerImage.java index 3bf2243549a..7dbd7adec0b 100644 --- a/core/src/main/java/org/testcontainers/images/RemoteDockerImage.java +++ b/core/src/main/java/org/testcontainers/images/RemoteDockerImage.java @@ -62,7 +62,7 @@ public RemoteDockerImage(@NonNull Future imageFuture) { } @Override - @SneakyThrows({InterruptedException.class, ExecutionException.class}) + @SneakyThrows({ InterruptedException.class, ExecutionException.class }) protected final String resolve() { final DockerImageName imageName = getImageName(); Logger logger = DockerLoggerFactory.getLogger(imageName.toString()); @@ -72,7 +72,10 @@ protected final String resolve() { } // The image is not available locally - pull it - logger.info("Pulling docker image: {}. Please be patient; this may take some time but only needs to be done once.", imageName); + logger.info( + "Pulling docker image: {}. Please be patient; this may take some time but only needs to be done once.", + imageName + ); Exception lastFailure = null; final Instant lastRetryAllowed = Instant.now().plus(PULL_RETRY_TIME_LIMIT); @@ -84,9 +87,7 @@ protected final String resolve() { .withTag(imageName.getVersionPart()); try { - pullImageCmd - .exec(new TimeLimitedLoggedPullImageResultCallback(logger)) - .awaitCompletion(); + pullImageCmd.exec(new TimeLimitedLoggedPullImageResultCallback(logger)).awaitCompletion(); } catch (DockerClientException e) { // Try to fallback to x86 pullImageCmd @@ -101,12 +102,19 @@ protected final String resolve() { } catch (InterruptedException | InternalServerErrorException e) { // these classes of exception often relate to timeout/connection errors so should be retried lastFailure = e; - logger.warn("Retrying pull for image: {} ({}s remaining)", + logger.warn( + "Retrying pull for image: {} ({}s remaining)", imageName, - Duration.between(Instant.now(), lastRetryAllowed).getSeconds()); + Duration.between(Instant.now(), lastRetryAllowed).getSeconds() + ); } } - logger.error("Failed to pull image: {}. Please check output of `docker pull {}`", imageName, imageName, lastFailure); + logger.error( + "Failed to pull image: {}. Please check output of `docker pull {}`", + imageName, + imageName, + lastFailure + ); throw new ContainerFetchException("Failed to pull image: " + imageName, lastFailure); } catch (DockerClientException e) { diff --git a/core/src/main/java/org/testcontainers/images/TimeLimitedLoggedPullImageResultCallback.java b/core/src/main/java/org/testcontainers/images/TimeLimitedLoggedPullImageResultCallback.java index b6d8539c3e8..88b8804dd8a 100644 --- a/core/src/main/java/org/testcontainers/images/TimeLimitedLoggedPullImageResultCallback.java +++ b/core/src/main/java/org/testcontainers/images/TimeLimitedLoggedPullImageResultCallback.java @@ -3,6 +3,7 @@ import com.github.dockerjava.api.command.PullImageResultCallback; import com.github.dockerjava.api.model.PullResponseItem; import org.slf4j.Logger; +import org.testcontainers.DockerClientFactory; import org.testcontainers.utility.TestcontainersConfiguration; import java.io.Closeable; @@ -16,8 +17,6 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; -import static org.testcontainers.DockerClientFactory.TESTCONTAINERS_THREAD_GROUP; - /** * {@link PullImageResultCallback} with improved logging of pull progress and a 'watchdog' which will abort the pull * if progress is not being made, to prevent a hanging test @@ -25,14 +24,21 @@ public class TimeLimitedLoggedPullImageResultCallback extends LoggedPullImageResultCallback { private static final AtomicInteger THREAD_ID = new AtomicInteger(0); - private static final ScheduledExecutorService PROGRESS_WATCHDOG_EXECUTOR = - Executors.newScheduledThreadPool(0, runnable -> { - Thread t = new Thread(TESTCONTAINERS_THREAD_GROUP, runnable); + + private static final ScheduledExecutorService PROGRESS_WATCHDOG_EXECUTOR = Executors.newScheduledThreadPool( + 0, + runnable -> { + Thread t = new Thread(DockerClientFactory.TESTCONTAINERS_THREAD_GROUP, runnable); t.setDaemon(true); t.setName("testcontainers-pull-watchdog-" + THREAD_ID.incrementAndGet()); return t; - }); - private static final Duration PULL_PAUSE_TOLERANCE = Duration.ofSeconds(TestcontainersConfiguration.getInstance().getImagePullPauseTimeout()); + } + ); + + private static final Duration PULL_PAUSE_TOLERANCE = Duration.ofSeconds( + TestcontainersConfiguration.getInstance().getImagePullPauseTimeout() + ); + private final Logger logger; // A future which, if it ever fires, will kill the pull @@ -85,27 +91,30 @@ public void onComplete() { super.onComplete(); } - /* * This method schedules a future task which will interrupt the waiting waiting threads if ever fired. * Every time this method is called (from onStart or onNext), the task is cancelled and recreated 30s in the future, * ensuring that it will only fire if the method stops being called regularly (e.g. if the pull has hung). */ private void resetProgressWatchdog(boolean isFinished) { - if (nextCheckForProgress != null && ! nextCheckForProgress.isCancelled()) { + if (nextCheckForProgress != null && !nextCheckForProgress.isCancelled()) { nextCheckForProgress.cancel(false); } if (!isFinished) { - nextCheckForProgress = PROGRESS_WATCHDOG_EXECUTOR.schedule( - this::abortPull, - PULL_PAUSE_TOLERANCE.getSeconds(), - TimeUnit.SECONDS - ); + nextCheckForProgress = + PROGRESS_WATCHDOG_EXECUTOR.schedule( + this::abortPull, + PULL_PAUSE_TOLERANCE.getSeconds(), + TimeUnit.SECONDS + ); } } private void abortPull() { - logger.error("Docker image pull has not made progress in {}s - aborting pull", PULL_PAUSE_TOLERANCE.getSeconds()); + logger.error( + "Docker image pull has not made progress in {}s - aborting pull", + PULL_PAUSE_TOLERANCE.getSeconds() + ); // Interrupt any threads that are waiting, before closing streams, because the stream can take // an indeterminate amount of time to close waitingThreads.forEach(Thread::interrupt); diff --git a/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java b/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java index 000487bfa4d..201cf0fd058 100644 --- a/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java +++ b/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java @@ -39,7 +39,9 @@ @Slf4j @Getter -public class ImageFromDockerfile extends LazyFuture implements +public class ImageFromDockerfile + extends LazyFuture + implements BuildContextBuilderTrait, ClasspathTrait, FilesTrait, @@ -51,9 +53,13 @@ public class ImageFromDockerfile extends LazyFuture implements private boolean deleteOnExit = true; private final Map transferables = new HashMap<>(); + private final Map buildArgs = new HashMap<>(); + private Optional dockerFilePath = Optional.empty(); + private Optional dockerfile = Optional.empty(); + private Set dependencyImageNames = Collections.emptySet(); public ImageFromDockerfile() { @@ -87,7 +93,6 @@ protected final String resolve() { DockerClient dockerClient = DockerClientFactory.instance().client(); try { - BuildImageResultCallback resultCallback = new BuildImageResultCallback() { @Override public void onNext(BuildResponseItem item) { @@ -102,8 +107,10 @@ public void onNext(BuildResponseItem item) { }; // We have to use pipes to avoid high memory consumption since users might want to build really big images - @Cleanup PipedInputStream in = new PipedInputStream(); - @Cleanup PipedOutputStream out = new PipedOutputStream(in); + @Cleanup + PipedInputStream in = new PipedInputStream(); + @Cleanup + PipedOutputStream out = new PipedOutputStream(in); BuildImageCmd buildImageCmd = dockerClient.buildImageCmd(in); configure(buildImageCmd); @@ -140,14 +147,17 @@ public void onNext(BuildResponseItem item) { } log.info("Transferred {} to Docker daemon", FileUtils.byteCountToDisplaySize(bytesToDockerDaemon)); - if (bytesToDockerDaemon > FileUtils.ONE_MB * 50) // warn if >50MB sent to docker daemon - log.warn("A large amount of data was sent to the Docker daemon ({}). Consider using a .dockerignore file for better performance.", - FileUtils.byteCountToDisplaySize(bytesToDockerDaemon)); + if (bytesToDockerDaemon > FileUtils.ONE_MB * 50) { + log.warn( // warn if >50MB sent to docker daemon + "A large amount of data was sent to the Docker daemon ({}). Consider using a .dockerignore file for better performance.", + FileUtils.byteCountToDisplaySize(bytesToDockerDaemon) + ); + } exec.awaitImageId(); return dockerImageName; - } catch(IOException e) { + } catch (IOException e) { throw new RuntimeException("Can't close DockerClient", e); } } @@ -156,15 +166,15 @@ protected void configure(BuildImageCmd buildImageCmd) { buildImageCmd.withTag(this.getDockerImageName()); this.dockerFilePath.ifPresent(buildImageCmd::withDockerfilePath); this.dockerfile.ifPresent(p -> { - buildImageCmd.withDockerfile(p.toFile()); - dependencyImageNames = new ParsedDockerfile(p).getDependencyImageNames(); + buildImageCmd.withDockerfile(p.toFile()); + dependencyImageNames = new ParsedDockerfile(p).getDependencyImageNames(); - if (dependencyImageNames.size() > 0) { - // if we'll be pre-pulling images, disable the built-in pull as it is not necessary and will fail for - // authenticated registries - buildImageCmd.withPull(false); - } - }); + if (dependencyImageNames.size() > 0) { + // if we'll be pre-pulling images, disable the built-in pull as it is not necessary and will fail for + // authenticated registries + buildImageCmd.withPull(false); + } + }); this.buildArgs.forEach(buildImageCmd::withBuildArg); } @@ -174,12 +184,19 @@ private void prePullDependencyImages(Set imagesToPull) { imagesToPull.forEach(imageName -> { try { - log.info("Pre-emptively checking local images for '{}', referenced via a Dockerfile. If not available, it will be pulled.", imageName); + log.info( + "Pre-emptively checking local images for '{}', referenced via a Dockerfile. If not available, it will be pulled.", + imageName + ); new RemoteDockerImage(DockerImageName.parse(imageName)) .withImageNameSubstitutor(ImageNameSubstitutor.noop()) .get(); } catch (Exception e) { - log.warn("Unable to pre-fetch an image ({}) depended upon by Dockerfile - image build will continue but may fail. Exception message was: {}", imageName, e.getMessage()); + log.warn( + "Unable to pre-fetch an image ({}) depended upon by Dockerfile - image build will continue but may fail. Exception message was: {}", + imageName, + e.getMessage() + ); } }); } @@ -218,5 +235,4 @@ public ImageFromDockerfile withDockerfile(Path dockerfile) { this.dockerfile = Optional.of(dockerfile); return this; } - } diff --git a/core/src/main/java/org/testcontainers/images/builder/Transferable.java b/core/src/main/java/org/testcontainers/images/builder/Transferable.java index 8ec26c8c3da..e98dbc2ad11 100644 --- a/core/src/main/java/org/testcontainers/images/builder/Transferable.java +++ b/core/src/main/java/org/testcontainers/images/builder/Transferable.java @@ -9,7 +9,6 @@ import java.util.zip.Checksum; public interface Transferable { - int DEFAULT_FILE_MODE = 0100644; int DEFAULT_DIR_MODE = 040755; diff --git a/core/src/main/java/org/testcontainers/images/builder/dockerfile/DockerfileBuilder.java b/core/src/main/java/org/testcontainers/images/builder/dockerfile/DockerfileBuilder.java index 17b13b3bac0..0ce794e778f 100644 --- a/core/src/main/java/org/testcontainers/images/builder/dockerfile/DockerfileBuilder.java +++ b/core/src/main/java/org/testcontainers/images/builder/dockerfile/DockerfileBuilder.java @@ -10,7 +10,9 @@ @Data @Slf4j -public class DockerfileBuilder implements DockerfileBuilderTrait, +public class DockerfileBuilder + implements + DockerfileBuilderTrait, FromStatementTrait, AddStatementTrait, CopyStatementTrait, @@ -27,7 +29,6 @@ public class DockerfileBuilder implements DockerfileBuilderTrait statements = new ArrayList<>(); public String build() { - StringBuilder builder = new StringBuilder(); for (Statement statement : statements) { diff --git a/core/src/main/java/org/testcontainers/images/builder/dockerfile/statement/Statement.java b/core/src/main/java/org/testcontainers/images/builder/dockerfile/statement/Statement.java index b36811a974d..2b9b005a579 100644 --- a/core/src/main/java/org/testcontainers/images/builder/dockerfile/statement/Statement.java +++ b/core/src/main/java/org/testcontainers/images/builder/dockerfile/statement/Statement.java @@ -7,5 +7,5 @@ public abstract class Statement { final String type; - abstract public void appendArguments(StringBuilder dockerfileStringBuilder); + public abstract void appendArguments(StringBuilder dockerfileStringBuilder); } diff --git a/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/AddStatementTrait.java b/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/AddStatementTrait.java index 44a35d68ba2..85b6b9ad6ea 100644 --- a/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/AddStatementTrait.java +++ b/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/AddStatementTrait.java @@ -3,7 +3,6 @@ import org.testcontainers.images.builder.dockerfile.statement.MultiArgsStatement; public interface AddStatementTrait & DockerfileBuilderTrait> { - default SELF add(String source, String destination) { return ((SELF) this).withStatement(new MultiArgsStatement("ADD", source, destination)); } diff --git a/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/CmdStatementTrait.java b/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/CmdStatementTrait.java index 054656af4d1..c9cc93c96c1 100644 --- a/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/CmdStatementTrait.java +++ b/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/CmdStatementTrait.java @@ -4,7 +4,6 @@ import org.testcontainers.images.builder.dockerfile.statement.SingleArgumentStatement; public interface CmdStatementTrait & DockerfileBuilderTrait> { - default SELF cmd(String command) { return ((SELF) this).withStatement(new SingleArgumentStatement("CMD", command)); } diff --git a/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/CopyStatementTrait.java b/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/CopyStatementTrait.java index 03e0cc7b461..0bdb2ef29ab 100644 --- a/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/CopyStatementTrait.java +++ b/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/CopyStatementTrait.java @@ -3,7 +3,6 @@ import org.testcontainers.images.builder.dockerfile.statement.MultiArgsStatement; public interface CopyStatementTrait & DockerfileBuilderTrait> { - default SELF copy(String source, String destination) { return ((SELF) this).withStatement(new MultiArgsStatement("COPY", source, destination)); } diff --git a/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/DockerfileBuilderTrait.java b/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/DockerfileBuilderTrait.java index 7a422da27d5..5fdc74ce1de 100644 --- a/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/DockerfileBuilderTrait.java +++ b/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/DockerfileBuilderTrait.java @@ -5,7 +5,6 @@ import java.util.List; public interface DockerfileBuilderTrait> { - List getStatements(); default SELF withStatement(Statement statement) { diff --git a/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/EntryPointStatementTrait.java b/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/EntryPointStatementTrait.java index c974e58e384..79a6e6315a0 100644 --- a/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/EntryPointStatementTrait.java +++ b/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/EntryPointStatementTrait.java @@ -4,7 +4,6 @@ import org.testcontainers.images.builder.dockerfile.statement.SingleArgumentStatement; public interface EntryPointStatementTrait & DockerfileBuilderTrait> { - default SELF entryPoint(String command) { return ((SELF) this).withStatement(new SingleArgumentStatement("ENTRYPOINT", command)); } diff --git a/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/EnvStatementTrait.java b/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/EnvStatementTrait.java index b02c8e71ec7..c1380304a34 100644 --- a/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/EnvStatementTrait.java +++ b/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/EnvStatementTrait.java @@ -6,7 +6,6 @@ import java.util.Map; public interface EnvStatementTrait & DockerfileBuilderTrait> { - default SELF env(String key, String value) { return env(Collections.singletonMap(key, value)); } diff --git a/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/ExposeStatementTrait.java b/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/ExposeStatementTrait.java index bebe243c8de..22dcbeee32b 100644 --- a/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/ExposeStatementTrait.java +++ b/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/ExposeStatementTrait.java @@ -6,8 +6,12 @@ import java.util.stream.Stream; public interface ExposeStatementTrait & DockerfileBuilderTrait> { - default SELF expose(Integer... ports) { - return ((SELF) this).withStatement(new SingleArgumentStatement("EXPOSE", Stream.of(ports).map(Object::toString).collect(Collectors.joining(" ")))); + return ((SELF) this).withStatement( + new SingleArgumentStatement( + "EXPOSE", + Stream.of(ports).map(Object::toString).collect(Collectors.joining(" ")) + ) + ); } } diff --git a/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/FromStatementTrait.java b/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/FromStatementTrait.java index 07077259df8..1ea411d206e 100644 --- a/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/FromStatementTrait.java +++ b/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/FromStatementTrait.java @@ -4,7 +4,6 @@ import org.testcontainers.utility.DockerImageName; public interface FromStatementTrait & DockerfileBuilderTrait> { - default SELF from(String dockerImageName) { DockerImageName.parse(dockerImageName).assertValid(); diff --git a/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/LabelStatementTrait.java b/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/LabelStatementTrait.java index 0b423453b33..4ed527051dd 100644 --- a/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/LabelStatementTrait.java +++ b/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/LabelStatementTrait.java @@ -6,7 +6,6 @@ import java.util.Map; public interface LabelStatementTrait & DockerfileBuilderTrait> { - default SELF label(String key, String value) { return label(Collections.singletonMap(key, value)); } diff --git a/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/RunStatementTrait.java b/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/RunStatementTrait.java index 74dfe1b00ab..f8fb064fb32 100644 --- a/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/RunStatementTrait.java +++ b/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/RunStatementTrait.java @@ -4,7 +4,6 @@ import org.testcontainers.images.builder.dockerfile.statement.SingleArgumentStatement; public interface RunStatementTrait & DockerfileBuilderTrait> { - default SELF run(String... commandParts) { return ((SELF) this).withStatement(new MultiArgsStatement("RUN", commandParts)); } diff --git a/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/UserStatementTrait.java b/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/UserStatementTrait.java index 57419ad7c5d..ca44d2d03be 100644 --- a/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/UserStatementTrait.java +++ b/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/UserStatementTrait.java @@ -3,7 +3,6 @@ import org.testcontainers.images.builder.dockerfile.statement.SingleArgumentStatement; public interface UserStatementTrait & DockerfileBuilderTrait> { - default SELF user(String user) { return ((SELF) this).withStatement(new SingleArgumentStatement("USER", user)); } diff --git a/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/VolumeStatementTrait.java b/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/VolumeStatementTrait.java index 2709adf32da..a8cd8621ee6 100644 --- a/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/VolumeStatementTrait.java +++ b/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/VolumeStatementTrait.java @@ -3,7 +3,6 @@ import org.testcontainers.images.builder.dockerfile.statement.MultiArgsStatement; public interface VolumeStatementTrait & DockerfileBuilderTrait> { - default SELF volume(String... volumes) { return ((SELF) this).withStatement(new MultiArgsStatement("VOLUME", volumes)); } diff --git a/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/WorkdirStatementTrait.java b/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/WorkdirStatementTrait.java index c4fd5386af8..ea2aaa1412c 100644 --- a/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/WorkdirStatementTrait.java +++ b/core/src/main/java/org/testcontainers/images/builder/dockerfile/traits/WorkdirStatementTrait.java @@ -3,7 +3,6 @@ import org.testcontainers.images.builder.dockerfile.statement.SingleArgumentStatement; public interface WorkdirStatementTrait & DockerfileBuilderTrait> { - default SELF workDir(String workdir) { return ((SELF) this).withStatement(new SingleArgumentStatement("WORKDIR", workdir)); } diff --git a/core/src/main/java/org/testcontainers/images/builder/traits/BuildContextBuilderTrait.java b/core/src/main/java/org/testcontainers/images/builder/traits/BuildContextBuilderTrait.java index 5ddee327cee..74ddf68b365 100644 --- a/core/src/main/java/org/testcontainers/images/builder/traits/BuildContextBuilderTrait.java +++ b/core/src/main/java/org/testcontainers/images/builder/traits/BuildContextBuilderTrait.java @@ -7,6 +7,5 @@ * */ public interface BuildContextBuilderTrait> { - SELF withFileFromTransferable(String path, Transferable transferable); } diff --git a/core/src/main/java/org/testcontainers/images/builder/traits/ClasspathTrait.java b/core/src/main/java/org/testcontainers/images/builder/traits/ClasspathTrait.java index 5617365f2cd..aadc3ccf7b9 100644 --- a/core/src/main/java/org/testcontainers/images/builder/traits/ClasspathTrait.java +++ b/core/src/main/java/org/testcontainers/images/builder/traits/ClasspathTrait.java @@ -9,7 +9,6 @@ * */ public interface ClasspathTrait & BuildContextBuilderTrait & FilesTrait> { - default SELF withFileFromClasspath(String path, String resourcePath) { final MountableFile mountableFile = MountableFile.forClasspathResource(resourcePath); diff --git a/core/src/main/java/org/testcontainers/images/builder/traits/DockerfileTrait.java b/core/src/main/java/org/testcontainers/images/builder/traits/DockerfileTrait.java index 07ed13ed0fb..293b76a7c5c 100644 --- a/core/src/main/java/org/testcontainers/images/builder/traits/DockerfileTrait.java +++ b/core/src/main/java/org/testcontainers/images/builder/traits/DockerfileTrait.java @@ -10,29 +10,31 @@ * BuildContextBuilder's trait for Dockerfile-based resources. * */ -public interface DockerfileTrait & BuildContextBuilderTrait & StringsTrait> { - +public interface DockerfileTrait< + SELF extends DockerfileTrait & BuildContextBuilderTrait & StringsTrait +> { default SELF withDockerfileFromBuilder(Consumer builderConsumer) { - DockerfileBuilder builder = new DockerfileBuilder(); builderConsumer.accept(builder); // return Transferable because we want to build Dockerfile's content lazily - return ((SELF) this).withFileFromTransferable("Dockerfile", new Transferable() { - - @Getter(lazy = true) - private final byte[] bytes = builder.build().getBytes(); - - @Override - public long getSize() { - return getBytes().length; - } - - @Override - public String getDescription() { - return "Dockerfile: " + builder; - } - }); + return ((SELF) this).withFileFromTransferable( + "Dockerfile", + new Transferable() { + @Getter(lazy = true) + private final byte[] bytes = builder.build().getBytes(); + + @Override + public long getSize() { + return getBytes().length; + } + + @Override + public String getDescription() { + return "Dockerfile: " + builder; + } + } + ); } } diff --git a/core/src/main/java/org/testcontainers/images/builder/traits/FilesTrait.java b/core/src/main/java/org/testcontainers/images/builder/traits/FilesTrait.java index 61524b59f8a..4c1204f752a 100644 --- a/core/src/main/java/org/testcontainers/images/builder/traits/FilesTrait.java +++ b/core/src/main/java/org/testcontainers/images/builder/traits/FilesTrait.java @@ -10,7 +10,6 @@ * */ public interface FilesTrait & BuildContextBuilderTrait> { - /** * Adds file to tarball copied into container. * @param path in tarball diff --git a/core/src/main/java/org/testcontainers/images/builder/traits/StringsTrait.java b/core/src/main/java/org/testcontainers/images/builder/traits/StringsTrait.java index 2b23551ee58..68de3d67c14 100644 --- a/core/src/main/java/org/testcontainers/images/builder/traits/StringsTrait.java +++ b/core/src/main/java/org/testcontainers/images/builder/traits/StringsTrait.java @@ -9,22 +9,23 @@ * */ public interface StringsTrait & BuildContextBuilderTrait> { - default SELF withFileFromString(String path, String content) { - return ((SELF) this).withFileFromTransferable(path, new Transferable() { - - @Getter - byte[] bytes = content.getBytes(); + return ((SELF) this).withFileFromTransferable( + path, + new Transferable() { + @Getter + byte[] bytes = content.getBytes(); - @Override - public long getSize() { - return bytes.length; - } + @Override + public long getSize() { + return bytes.length; + } - @Override - public String getDescription() { - return "String: " + StringUtils.abbreviate(content, 100); - } - }); + @Override + public String getDescription() { + return "String: " + StringUtils.abbreviate(content, 100); + } + } + ); } } diff --git a/core/src/main/java/org/testcontainers/lifecycle/Startable.java b/core/src/main/java/org/testcontainers/lifecycle/Startable.java index 3e73968d06e..2ffa423d2e3 100644 --- a/core/src/main/java/org/testcontainers/lifecycle/Startable.java +++ b/core/src/main/java/org/testcontainers/lifecycle/Startable.java @@ -4,7 +4,6 @@ import java.util.Set; public interface Startable extends AutoCloseable { - default Set getDependencies() { return Collections.emptySet(); } diff --git a/core/src/main/java/org/testcontainers/lifecycle/Startables.java b/core/src/main/java/org/testcontainers/lifecycle/Startables.java index 5ec46f1edb9..daa63931a88 100644 --- a/core/src/main/java/org/testcontainers/lifecycle/Startables.java +++ b/core/src/main/java/org/testcontainers/lifecycle/Startables.java @@ -1,8 +1,8 @@ package org.testcontainers.lifecycle; -import java.util.Arrays; import lombok.experimental.UtilityClass; +import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.Map; @@ -17,17 +17,18 @@ @UtilityClass public class Startables { - private static final Executor EXECUTOR = Executors.newCachedThreadPool(new ThreadFactory() { - - private final AtomicLong COUNTER = new AtomicLong(0); + private static final Executor EXECUTOR = Executors.newCachedThreadPool( + new ThreadFactory() { + private final AtomicLong COUNTER = new AtomicLong(0); - @Override - public Thread newThread(Runnable r) { - Thread thread = new Thread(r, "testcontainers-lifecycle-" + COUNTER.getAndIncrement()); - thread.setDaemon(true); - return thread; + @Override + public Thread newThread(Runnable r) { + Thread thread = new Thread(r, "testcontainers-lifecycle-" + COUNTER.getAndIncrement()); + thread.setDaemon(true); + return thread; + } } - }); + ); /** * @see #deepStart(Stream) @@ -77,15 +78,22 @@ public CompletableFuture deepStart(Stream startables) * @param started an intermediate storage for already started {@link Startable}s to prevent multiple starts. * @param startables a {@link Stream} of {@link Startable}s to start and scan for transitive dependencies. */ - private CompletableFuture deepStart(Map> started, Stream startables) { + private CompletableFuture deepStart( + Map> started, + Stream startables + ) { CompletableFuture[] futures = startables .sequential() .map(it -> { // avoid a recursive update in `computeIfAbsent` Map> subStarted = new HashMap<>(started); - CompletableFuture future = started.computeIfAbsent(it, startable -> { - return deepStart(subStarted, startable.getDependencies().stream()).thenRunAsync(startable::start, EXECUTOR); - }); + CompletableFuture future = started.computeIfAbsent( + it, + startable -> { + return deepStart(subStarted, startable.getDependencies().stream()) + .thenRunAsync(startable::start, EXECUTOR); + } + ); started.putAll(subStarted); return future; }) diff --git a/core/src/main/java/org/testcontainers/lifecycle/TestDescription.java b/core/src/main/java/org/testcontainers/lifecycle/TestDescription.java index c27f59fb0fc..3787707509a 100644 --- a/core/src/main/java/org/testcontainers/lifecycle/TestDescription.java +++ b/core/src/main/java/org/testcontainers/lifecycle/TestDescription.java @@ -1,7 +1,6 @@ package org.testcontainers.lifecycle; public interface TestDescription { - String getTestId(); String getFilesystemFriendlyName(); diff --git a/core/src/main/java/org/testcontainers/lifecycle/TestLifecycleAware.java b/core/src/main/java/org/testcontainers/lifecycle/TestLifecycleAware.java index 178e081c083..1e5a0258f14 100644 --- a/core/src/main/java/org/testcontainers/lifecycle/TestLifecycleAware.java +++ b/core/src/main/java/org/testcontainers/lifecycle/TestLifecycleAware.java @@ -3,12 +3,7 @@ import java.util.Optional; public interface TestLifecycleAware { + default void beforeTest(TestDescription description) {} - default void beforeTest(TestDescription description) { - - } - - default void afterTest(TestDescription description, Optional throwable) { - - } + default void afterTest(TestDescription description, Optional throwable) {} } diff --git a/core/src/main/java/org/testcontainers/utility/AuditLogger.java b/core/src/main/java/org/testcontainers/utility/AuditLogger.java index f6c4559485d..518144c4302 100644 --- a/core/src/main/java/org/testcontainers/utility/AuditLogger.java +++ b/core/src/main/java/org/testcontainers/utility/AuditLogger.java @@ -3,6 +3,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.github.dockerjava.api.command.DockerCmd; +import com.google.common.base.Strings; import lombok.experimental.UtilityClass; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; @@ -12,8 +13,6 @@ import java.util.List; -import static com.google.common.base.Strings.nullToEmpty; - /** * Logger for tracking potentially destructive actions, intended for usage in a shared Docker environment where * traceability is needed. This class uses SLF4J, logging at TRACE level and capturing common fields as MDC fields. @@ -26,34 +25,36 @@ public class AuditLogger { private static final ObjectMapper objectMapper = new ObjectMapper(); - public static final String MDC_PREFIX = AuditLogger.class.getCanonicalName(); - public static void doLog(@NotNull String action, - @Nullable String image, - @Nullable String containerId, - @NotNull DockerCmd cmd) { + public static final String MDC_PREFIX = AuditLogger.class.getCanonicalName(); + public static void doLog( + @NotNull String action, + @Nullable String image, + @Nullable String containerId, + @NotNull DockerCmd cmd + ) { doLog(action, image, containerId, cmd, null); } - public static void doLog(@NotNull String action, - @Nullable String image, - @Nullable String containerId, - @NotNull DockerCmd cmd, - @Nullable Exception e) { - - if (! log.isTraceEnabled()) { + public static void doLog( + @NotNull String action, + @Nullable String image, + @Nullable String containerId, + @NotNull DockerCmd cmd, + @Nullable Exception e + ) { + if (!log.isTraceEnabled()) { return; } - MDC.put(MDC_PREFIX + ".Action", nullToEmpty(action)); - MDC.put(MDC_PREFIX + ".Image", nullToEmpty(image)); - MDC.put(MDC_PREFIX + ".ContainerId", nullToEmpty(containerId)); + MDC.put(MDC_PREFIX + ".Action", Strings.nullToEmpty(action)); + MDC.put(MDC_PREFIX + ".Image", Strings.nullToEmpty(image)); + MDC.put(MDC_PREFIX + ".ContainerId", Strings.nullToEmpty(containerId)); try { MDC.put(MDC_PREFIX + ".Command", objectMapper.writeValueAsString(cmd)); - } catch (JsonProcessingException ignored) { - } + } catch (JsonProcessingException ignored) {} if (e != null) { MDC.put(MDC_PREFIX + ".Exception", e.getLocalizedMessage()); @@ -69,10 +70,8 @@ public static void doLog(@NotNull String action, MDC.remove(MDC_PREFIX + ".Exception"); } - public static void doComposeLog(@NotNull String[] commandParts, - @Nullable List env) { - - if (! log.isTraceEnabled()) { + public static void doComposeLog(@NotNull String[] commandParts, @Nullable List env) { + if (!log.isTraceEnabled()) { return; } diff --git a/core/src/main/java/org/testcontainers/utility/AuthConfigUtil.java b/core/src/main/java/org/testcontainers/utility/AuthConfigUtil.java index 77b670d374b..8ed91a5e34e 100644 --- a/core/src/main/java/org/testcontainers/utility/AuthConfigUtil.java +++ b/core/src/main/java/org/testcontainers/utility/AuthConfigUtil.java @@ -2,22 +2,23 @@ import com.github.dockerjava.api.model.AuthConfig; import com.google.common.base.MoreObjects; +import com.google.common.base.Strings; import lombok.experimental.UtilityClass; import org.jetbrains.annotations.NotNull; -import static com.google.common.base.Strings.isNullOrEmpty; - /** * TODO: Javadocs */ @UtilityClass public class AuthConfigUtil { + public static String toSafeString(AuthConfig authConfig) { if (authConfig == null) { return "null"; } - return MoreObjects.toStringHelper(authConfig) + return MoreObjects + .toStringHelper(authConfig) .add("username", authConfig.getUsername()) .add("password", obfuscated(authConfig.getPassword())) .add("auth", obfuscated(authConfig.getAuth())) @@ -29,6 +30,6 @@ public static String toSafeString(AuthConfig authConfig) { @NotNull private static String obfuscated(String value) { - return isNullOrEmpty(value) ? "blank" : "hidden non-blank value"; + return Strings.isNullOrEmpty(value) ? "blank" : "hidden non-blank value"; } } diff --git a/core/src/main/java/org/testcontainers/utility/Base58.java b/core/src/main/java/org/testcontainers/utility/Base58.java index 1f56978c7cc..2cbfc751f4d 100644 --- a/core/src/main/java/org/testcontainers/utility/Base58.java +++ b/core/src/main/java/org/testcontainers/utility/Base58.java @@ -7,8 +7,7 @@ */ public class Base58 { - private static final char[] ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" - .toCharArray(); + private static final char[] ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz".toCharArray(); private static final SecureRandom RANDOM = new SecureRandom(); diff --git a/core/src/main/java/org/testcontainers/utility/ClasspathScanner.java b/core/src/main/java/org/testcontainers/utility/ClasspathScanner.java index 76716cfdb7c..0c8addc2874 100644 --- a/core/src/main/java/org/testcontainers/utility/ClasspathScanner.java +++ b/core/src/main/java/org/testcontainers/utility/ClasspathScanner.java @@ -40,11 +40,7 @@ private static Integer filesFileSchemeFirst(final URL t) { * context classloader. Results are currently alphabetically sorted. */ static Stream scanFor(final String name) { - return scanFor( - name, - ClasspathScanner.class.getClassLoader(), - Thread.currentThread().getContextClassLoader() - ); + return scanFor(name, ClasspathScanner.class.getClassLoader(), Thread.currentThread().getContextClassLoader()); } @Nullable diff --git a/core/src/main/java/org/testcontainers/utility/CommandLine.java b/core/src/main/java/org/testcontainers/utility/CommandLine.java index a217823c40f..1bb8f827148 100644 --- a/core/src/main/java/org/testcontainers/utility/CommandLine.java +++ b/core/src/main/java/org/testcontainers/utility/CommandLine.java @@ -2,6 +2,7 @@ import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.zeroturnaround.exec.InvalidExitValueException; import org.zeroturnaround.exec.ProcessExecutor; import org.zeroturnaround.exec.ProcessResult; @@ -14,14 +15,12 @@ import java.util.concurrent.TimeoutException; import java.util.regex.Pattern; -import static org.slf4j.LoggerFactory.getLogger; - /** * Process execution utility methods. */ public class CommandLine { - private static final Logger LOGGER = getLogger(CommandLine.class); + private static final Logger LOGGER = LoggerFactory.getLogger(CommandLine.class); /** * Run a shell command synchronously. @@ -30,16 +29,11 @@ public class CommandLine { * @return the stdout output of the command */ public static String runShellCommand(String... command) { - String joinedCommand = String.join(" ", command); LOGGER.debug("Executing shell command: `{}`", joinedCommand); try { - ProcessResult result = new ProcessExecutor() - .command(command) - .readOutput(true) - .exitValueNormal() - .execute(); + ProcessResult result = new ProcessExecutor().command(command).readOutput(true).exitValueNormal().execute(); return result.outputUTF8().trim(); } catch (IOException | InterruptedException | TimeoutException | InvalidExitValueException e) { @@ -55,7 +49,6 @@ public static String runShellCommand(String... command) { * @return whether the executable exists and is executable */ public static boolean executableExists(String executable) { - // First check if we've been given the full path already File directFile = new File(executable); if (directFile.exists() && directFile.canExecute()) { @@ -78,6 +71,7 @@ public static String[] getSystemPath() { } private static class ShellCommandException extends RuntimeException { + public ShellCommandException(String message, Exception e) { super(message, e); } diff --git a/core/src/main/java/org/testcontainers/utility/ComparableVersion.java b/core/src/main/java/org/testcontainers/utility/ComparableVersion.java index d3e3d9eb958..999c4b40b3b 100644 --- a/core/src/main/java/org/testcontainers/utility/ComparableVersion.java +++ b/core/src/main/java/org/testcontainers/utility/ComparableVersion.java @@ -1,12 +1,11 @@ package org.testcontainers.utility; -import java.util.ArrayList; -import java.util.List; - import com.google.common.annotations.VisibleForTesting; - import org.jetbrains.annotations.NotNull; +import java.util.ArrayList; +import java.util.List; + public final class ComparableVersion implements Comparable { private final int[] parts; @@ -19,7 +18,7 @@ public ComparableVersion(String version) { @Override public int compareTo(@NotNull ComparableVersion other) { - for (int i=0; i otherPart) { @@ -71,5 +70,4 @@ static int[] parseVersion(final String version) { return ret; } - } diff --git a/core/src/main/java/org/testcontainers/utility/ConfigurationFileImageNameSubstitutor.java b/core/src/main/java/org/testcontainers/utility/ConfigurationFileImageNameSubstitutor.java index 530d5a76118..ee7ab1d95b6 100644 --- a/core/src/main/java/org/testcontainers/utility/ConfigurationFileImageNameSubstitutor.java +++ b/core/src/main/java/org/testcontainers/utility/ConfigurationFileImageNameSubstitutor.java @@ -28,7 +28,8 @@ public DockerImageName apply(final DockerImageName original) { .asCompatibleSubstituteFor(original); if (!result.equals(original)) { - log.warn("Image name {} was substituted by configuration to {}. This approach is deprecated and will be removed in the future", + log.warn( + "Image name {} was substituted by configuration to {}. This approach is deprecated and will be removed in the future", original, result ); diff --git a/core/src/main/java/org/testcontainers/utility/DefaultImageNameSubstitutor.java b/core/src/main/java/org/testcontainers/utility/DefaultImageNameSubstitutor.java index 840b8a77b40..a7755f8eea8 100644 --- a/core/src/main/java/org/testcontainers/utility/DefaultImageNameSubstitutor.java +++ b/core/src/main/java/org/testcontainers/utility/DefaultImageNameSubstitutor.java @@ -11,6 +11,7 @@ final class DefaultImageNameSubstitutor extends ImageNameSubstitutor { private final ConfigurationFileImageNameSubstitutor configurationFileImageNameSubstitutor; + private final PrefixingImageNameSubstitutor prefixingImageNameSubstitutor; public DefaultImageNameSubstitutor() { @@ -29,13 +30,17 @@ public DefaultImageNameSubstitutor() { @Override public DockerImageName apply(final DockerImageName original) { - return configurationFileImageNameSubstitutor - .andThen(prefixingImageNameSubstitutor) - .apply(original); + return configurationFileImageNameSubstitutor.andThen(prefixingImageNameSubstitutor).apply(original); } @Override protected String getDescription() { - return "DefaultImageNameSubstitutor (composite of '" + configurationFileImageNameSubstitutor.getDescription() + "' and '" + prefixingImageNameSubstitutor.getDescription() + "')"; + return ( + "DefaultImageNameSubstitutor (composite of '" + + configurationFileImageNameSubstitutor.getDescription() + + "' and '" + + prefixingImageNameSubstitutor.getDescription() + + "')" + ); } } diff --git a/core/src/main/java/org/testcontainers/utility/DockerImageName.java b/core/src/main/java/org/testcontainers/utility/DockerImageName.java index b9d266a41ca..9fca02aad6f 100644 --- a/core/src/main/java/org/testcontainers/utility/DockerImageName.java +++ b/core/src/main/java/org/testcontainers/utility/DockerImageName.java @@ -1,6 +1,5 @@ package org.testcontainers.utility; - import com.google.common.net.HostAndPort; import lombok.AccessLevel; import lombok.AllArgsConstructor; @@ -20,16 +19,29 @@ public final class DockerImageName { /* Regex patterns used for validation */ private static final String ALPHA_NUMERIC = "[a-z0-9]+"; + private static final String SEPARATOR = "([.]|_{1,2}|-+)"; + private static final String REPO_NAME_PART = ALPHA_NUMERIC + "(" + SEPARATOR + ALPHA_NUMERIC + ")*"; + private static final Pattern REPO_NAME = Pattern.compile(REPO_NAME_PART + "(/" + REPO_NAME_PART + ")*"); private final String rawName; - @With @Getter private final String registry; - @With @Getter private final String repository; - @NotNull @With(AccessLevel.PRIVATE) + + @With + @Getter + private final String registry; + + @With + @Getter + private final String repository; + + @NotNull + @With(AccessLevel.PRIVATE) private final Versioning versioning; - @Nullable @With(AccessLevel.PRIVATE) + + @Nullable + @With(AccessLevel.PRIVATE) private final DockerImageName compatibleSubstituteFor; /** @@ -57,10 +69,14 @@ public DockerImageName(String fullImageName) { final int slashIndex = fullImageName.indexOf('/'); String remoteName; - if (slashIndex == -1 || - (!fullImageName.substring(0, slashIndex).contains(".") && + if ( + slashIndex == -1 || + ( + !fullImageName.substring(0, slashIndex).contains(".") && !fullImageName.substring(0, slashIndex).contains(":") && - !fullImageName.substring(0, slashIndex).equals("localhost"))) { + !fullImageName.substring(0, slashIndex).equals("localhost") + ) + ) { registry = ""; remoteName = fullImageName; } else { @@ -99,10 +115,14 @@ public DockerImageName(String nameWithoutTag, @NotNull String version) { final int slashIndex = nameWithoutTag.indexOf('/'); String remoteName; - if (slashIndex == -1 || - (!nameWithoutTag.substring(0, slashIndex).contains(".") && + if ( + slashIndex == -1 || + ( + !nameWithoutTag.substring(0, slashIndex).contains(".") && !nameWithoutTag.substring(0, slashIndex).contains(":") && - !nameWithoutTag.substring(0, slashIndex).equals("localhost"))) { + !nameWithoutTag.substring(0, slashIndex).equals("localhost") + ) + ) { registry = ""; remoteName = nameWithoutTag; } else { @@ -163,7 +183,9 @@ public void assertValid() { throw new IllegalArgumentException(repository + " is not a valid Docker image name (in " + rawName + ")"); } if (!versioning.isValid()) { - throw new IllegalArgumentException(versioning + " is not a valid image versioning identifier (in " + rawName + ")"); + throw new IllegalArgumentException( + versioning + " is not a valid image versioning identifier (in " + rawName + ")" + ); } } @@ -246,18 +268,16 @@ public void assertCompatibleWith(DockerImageName... anyOthers) { throw new IllegalStateException( String.format( - "Failed to verify that image '%s' is a compatible substitute for '%s'. This generally means that " - + - "you are trying to use an image that Testcontainers has not been designed to use. If this is " - + - "deliberate, and if you are confident that the image is compatible, you should declare " - + - "compatibility in code using the `asCompatibleSubstituteFor` method. For example:\n" - + - " DockerImageName myImage = DockerImageName.parse(\"%s\").asCompatibleSubstituteFor(\"%s\");\n" - + - "and then use `myImage` instead.", - this.rawName, exampleOther.rawName, this.rawName, exampleOther.rawName + "Failed to verify that image '%s' is a compatible substitute for '%s'. This generally means that " + + "you are trying to use an image that Testcontainers has not been designed to use. If this is " + + "deliberate, and if you are confident that the image is compatible, you should declare " + + "compatibility in code using the `asCompatibleSubstituteFor` method. For example:\n" + + " DockerImageName myImage = DockerImageName.parse(\"%s\").asCompatibleSubstituteFor(\"%s\");\n" + + "and then use `myImage` instead.", + this.rawName, + exampleOther.rawName, + this.rawName, + exampleOther.rawName ) ); } diff --git a/core/src/main/java/org/testcontainers/utility/DockerLoggerFactory.java b/core/src/main/java/org/testcontainers/utility/DockerLoggerFactory.java index 69f7b998da2..c372c3cea3e 100644 --- a/core/src/main/java/org/testcontainers/utility/DockerLoggerFactory.java +++ b/core/src/main/java/org/testcontainers/utility/DockerLoggerFactory.java @@ -9,7 +9,6 @@ public final class DockerLoggerFactory { public static Logger getLogger(String dockerImageName) { - final String abbreviatedName; if (dockerImageName.contains("@sha256")) { abbreviatedName = dockerImageName.substring(0, dockerImageName.indexOf("@sha256") + 14) + "..."; diff --git a/core/src/main/java/org/testcontainers/utility/DockerMachineClient.java b/core/src/main/java/org/testcontainers/utility/DockerMachineClient.java index 6f1e2c9d50b..1a2b4aafe74 100644 --- a/core/src/main/java/org/testcontainers/utility/DockerMachineClient.java +++ b/core/src/main/java/org/testcontainers/utility/DockerMachineClient.java @@ -3,44 +3,42 @@ import lombok.NonNull; import org.apache.commons.lang3.SystemUtils; import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import java.util.Arrays; import java.util.List; import java.util.Optional; -import static java.util.Arrays.asList; -import static org.slf4j.LoggerFactory.getLogger; -import static org.testcontainers.utility.CommandLine.executableExists; -import static org.testcontainers.utility.CommandLine.runShellCommand; - /** * Created by rnorth on 27/10/2015. */ public class DockerMachineClient { private static DockerMachineClient instance; - private static final Logger LOGGER = getLogger(DockerMachineClient.class); + + private static final Logger LOGGER = LoggerFactory.getLogger(DockerMachineClient.class); private static final String executableName; + static { - if(SystemUtils.IS_OS_WINDOWS) { - executableName = "docker-machine.exe"; - } else { - executableName = "docker-machine"; - } + if (SystemUtils.IS_OS_WINDOWS) { + executableName = "docker-machine.exe"; + } else { + executableName = "docker-machine"; + } } /** * Private constructor */ - private DockerMachineClient() { - } + private DockerMachineClient() {} /** * Obtain an instance of the DockerMachineClient wrapper. * * @return the singleton instance of DockerMachineClient */ - public synchronized static DockerMachineClient instance() { + public static synchronized DockerMachineClient instance() { if (instance == null) { instance = new DockerMachineClient(); } @@ -49,12 +47,12 @@ public synchronized static DockerMachineClient instance() { } public boolean isInstalled() { - return executableExists(executableName); + return CommandLine.executableExists(executableName); } public Optional getDefaultMachine() { - String ls = runShellCommand(executableName, "ls", "-q"); - List machineNames = asList(ls.split("\n")); + String ls = CommandLine.runShellCommand(executableName, "ls", "-q"); + List machineNames = Arrays.asList(ls.split("\n")); String envMachineName = System.getenv("DOCKER_MACHINE_NAME"); @@ -65,7 +63,10 @@ public Optional getDefaultMachine() { LOGGER.debug("DOCKER_MACHINE_NAME is not set; Using 'default' docker-machine", envMachineName); return Optional.of("default"); } else if (machineNames.size() > 0) { - LOGGER.debug("DOCKER_MACHINE_NAME is not set and no machine named 'default' found; Using first machine found with `docker-machine ls`: {}", machineNames.get(0)); + LOGGER.debug( + "DOCKER_MACHINE_NAME is not set and no machine named 'default' found; Using first machine found with `docker-machine ls`: {}", + machineNames.get(0) + ); return Optional.of(machineNames.get(0)); } else { return Optional.empty(); @@ -75,7 +76,7 @@ public Optional getDefaultMachine() { public void ensureMachineRunning(@NonNull String machineName) { if (!isMachineRunning(machineName)) { LOGGER.info("Docker-machine '{}' is not running. Will start it now", machineName); - runShellCommand("docker-machine", "start", machineName); + CommandLine.runShellCommand("docker-machine", "start", machineName); } } @@ -84,15 +85,15 @@ public void ensureMachineRunning(@NonNull String machineName) { */ @Deprecated public String getDockerDaemonIpAddress(@NonNull String machineName) { - return runShellCommand(executableName, "ip", machineName); + return CommandLine.runShellCommand(executableName, "ip", machineName); } public String getDockerDaemonUrl(@NonNull String machineName) { - return runShellCommand(executableName, "url", machineName); + return CommandLine.runShellCommand(executableName, "url", machineName); } public boolean isMachineRunning(String machineName) { - String status = runShellCommand("docker-machine", "status", machineName); + String status = CommandLine.runShellCommand("docker-machine", "status", machineName); return status.trim().equalsIgnoreCase("running"); } diff --git a/core/src/main/java/org/testcontainers/utility/DockerStatus.java b/core/src/main/java/org/testcontainers/utility/DockerStatus.java index a410597dcfb..00f71249dd1 100644 --- a/core/src/main/java/org/testcontainers/utility/DockerStatus.java +++ b/core/src/main/java/org/testcontainers/utility/DockerStatus.java @@ -33,15 +33,16 @@ public class DockerStatus { * @param now the time to consider as the current time * @return true if we can conclude that the container is running, false otherwise */ - public static boolean isContainerRunning(InspectContainerResponse.ContainerState state, - Duration minimumRunningDuration, - Instant now) { + public static boolean isContainerRunning( + InspectContainerResponse.ContainerState state, + Duration minimumRunningDuration, + Instant now + ) { if (state.getRunning()) { if (minimumRunningDuration == null) { return true; } - Instant startedAt = DateTimeFormatter.ISO_OFFSET_DATE_TIME.parse( - state.getStartedAt(), Instant::from); + Instant startedAt = DateTimeFormatter.ISO_OFFSET_DATE_TIME.parse(state.getStartedAt(), Instant::from); if (startedAt.isBefore(now.minus(minimumRunningDuration))) { return true; @@ -57,7 +58,6 @@ public static boolean isContainerRunning(InspectContainerResponse.ContainerState * @return true if we can conclude that the container has started but is now stopped, false otherwise. */ public static boolean isContainerStopped(InspectContainerResponse.ContainerState state) { - // get some preconditions out of the way if (state.getRunning() || state.getPaused()) { return false; @@ -72,10 +72,12 @@ public static boolean isContainerStopped(InspectContainerResponse.ContainerState public static boolean isDockerTimestampNonEmpty(String dockerTimestamp) { // This is a defensive approach. Current versions of Docker use the DOCKER_TIMESTAMP_ZERO value, but // that could change. - return dockerTimestamp != null - && !dockerTimestamp.isEmpty() - && !dockerTimestamp.equals(DOCKER_TIMESTAMP_ZERO) - && DateTimeFormatter.ISO_OFFSET_DATE_TIME.parse(dockerTimestamp, Instant::from).getEpochSecond() >= 0L; + return ( + dockerTimestamp != null && + !dockerTimestamp.isEmpty() && + !dockerTimestamp.equals(DOCKER_TIMESTAMP_ZERO) && + DateTimeFormatter.ISO_OFFSET_DATE_TIME.parse(dockerTimestamp, Instant::from).getEpochSecond() >= 0L + ); } public static boolean isContainerExitCodeSuccess(InspectContainerResponse.ContainerState state) { diff --git a/core/src/main/java/org/testcontainers/utility/DynamicPollInterval.java b/core/src/main/java/org/testcontainers/utility/DynamicPollInterval.java index 1252fb2c3a1..043befe04bb 100644 --- a/core/src/main/java/org/testcontainers/utility/DynamicPollInterval.java +++ b/core/src/main/java/org/testcontainers/utility/DynamicPollInterval.java @@ -15,6 +15,7 @@ public class DynamicPollInterval implements PollInterval { final Duration interval; + Instant lastTimestamp; private DynamicPollInterval(Duration interval) { diff --git a/core/src/main/java/org/testcontainers/utility/ImageNameSubstitutor.java b/core/src/main/java/org/testcontainers/utility/ImageNameSubstitutor.java index a1cb2492204..dae3f178230 100644 --- a/core/src/main/java/org/testcontainers/utility/ImageNameSubstitutor.java +++ b/core/src/main/java/org/testcontainers/utility/ImageNameSubstitutor.java @@ -24,7 +24,7 @@ public abstract class ImageNameSubstitutor implements Function> filters) { - String[] labels = filters.stream() + String[] labels = filters + .stream() .filter(it -> "label".equals(it.getKey())) .map(Map.Entry::getValue) .toArray(String[]::new); switch (pruneType) { // Docker only prunes stopped containers, so we have to do it manually case CONTAINERS: - List containers = dockerClient.listContainersCmd() + List containers = dockerClient + .listContainersCmd() .withFilter("label", Arrays.asList(labels)) .withShowAll(true) .exec(); - containers.parallelStream().forEach(container -> { - dockerClient.removeContainerCmd(container.getId()) - .withForce(true) - .withRemoveVolumes(true) - .exec(); - }); + containers + .parallelStream() + .forEach(container -> { + dockerClient + .removeContainerCmd(container.getId()) + .withForce(true) + .withRemoveVolumes(true) + .exec(); + }); break; default: dockerClient.pruneCmd(pruneType).withLabelFilter(labels).exec(); diff --git a/core/src/main/java/org/testcontainers/utility/LazyFuture.java b/core/src/main/java/org/testcontainers/utility/LazyFuture.java index 758082f5185..bde9feb69ec 100644 --- a/core/src/main/java/org/testcontainers/utility/LazyFuture.java +++ b/core/src/main/java/org/testcontainers/utility/LazyFuture.java @@ -17,7 +17,7 @@ public abstract class LazyFuture implements Future { @Getter(value = AccessLevel.MODULE, lazy = true) private final T resolvedValue = resolve(); - abstract protected T resolve(); + protected abstract T resolve(); @Override public boolean cancel(boolean mayInterruptIfRunning) { diff --git a/core/src/main/java/org/testcontainers/utility/LicenseAcceptance.java b/core/src/main/java/org/testcontainers/utility/LicenseAcceptance.java index 55037d8978c..9fe8d9db8e6 100644 --- a/core/src/main/java/org/testcontainers/utility/LicenseAcceptance.java +++ b/core/src/main/java/org/testcontainers/utility/LicenseAcceptance.java @@ -27,10 +27,17 @@ public static void assertLicenseAccepted(final String imageName) { // suppressed } - throw new IllegalStateException("The image " + imageName + " requires you to accept a license agreement. " + - "Please place a file at the root of the classpath named " + ACCEPTANCE_FILE_NAME + ", e.g. at " + - "src/test/resources/" + ACCEPTANCE_FILE_NAME + ". This file should contain the line:\n " + - imageName); - + throw new IllegalStateException( + "The image " + + imageName + + " requires you to accept a license agreement. " + + "Please place a file at the root of the classpath named " + + ACCEPTANCE_FILE_NAME + + ", e.g. at " + + "src/test/resources/" + + ACCEPTANCE_FILE_NAME + + ". This file should contain the line:\n " + + imageName + ); } } diff --git a/core/src/main/java/org/testcontainers/utility/LogUtils.java b/core/src/main/java/org/testcontainers/utility/LogUtils.java index ce27387490f..e5912563cd9 100644 --- a/core/src/main/java/org/testcontainers/utility/LogUtils.java +++ b/core/src/main/java/org/testcontainers/utility/LogUtils.java @@ -13,9 +13,6 @@ import java.io.IOException; import java.util.function.Consumer; -import static org.testcontainers.containers.output.OutputFrame.OutputType.STDERR; -import static org.testcontainers.containers.output.OutputFrame.OutputType.STDOUT; - /** * Provides utility methods for logging. */ @@ -31,11 +28,12 @@ public class LogUtils { * @param consumer a consumer of {@link OutputFrame}s * @param types types of {@link OutputFrame} to receive */ - public void followOutput(DockerClient dockerClient, - String containerId, - Consumer consumer, - OutputFrame.OutputType... types) { - + public void followOutput( + DockerClient dockerClient, + String containerId, + Consumer consumer, + OutputFrame.OutputType... types + ) { attachConsumer(dockerClient, containerId, consumer, true, types); } @@ -47,11 +45,8 @@ public void followOutput(DockerClient dockerClient, * @param containerId container ID to attach to * @param consumer a consumer of {@link OutputFrame}s */ - public void followOutput(DockerClient dockerClient, - String containerId, - Consumer consumer) { - - followOutput(dockerClient, containerId, consumer, STDOUT, STDERR); + public void followOutput(DockerClient dockerClient, String containerId, Consumer consumer) { + followOutput(dockerClient, containerId, consumer, OutputFrame.OutputType.STDOUT, OutputFrame.OutputType.STDERR); } /** @@ -63,16 +58,13 @@ public void followOutput(DockerClient dockerClient, * @return all previous output frames (stdout/stderr being separated by newline characters) */ @SneakyThrows(IOException.class) - public String getOutput(DockerClient dockerClient, - String containerId, - OutputFrame.OutputType... types) { - + public String getOutput(DockerClient dockerClient, String containerId, OutputFrame.OutputType... types) { if (containerId == null) { return ""; } if (types.length == 0) { - types = new OutputFrame.OutputType[] { STDOUT, STDERR }; + types = new OutputFrame.OutputType[] { OutputFrame.OutputType.STDOUT, OutputFrame.OutputType.STDERR }; } final ToStringConsumer consumer = new ToStringConsumer(); @@ -90,16 +82,20 @@ private static Closeable attachConsumer( boolean followStream, OutputFrame.OutputType... types ) { - - final LogContainerCmd cmd = dockerClient.logContainerCmd(containerId) + final LogContainerCmd cmd = dockerClient + .logContainerCmd(containerId) .withFollowStream(followStream) .withSince(0); final FrameConsumerResultCallback callback = new FrameConsumerResultCallback(); for (OutputFrame.OutputType type : types) { callback.addConsumer(type, consumer); - if (type == STDOUT) cmd.withStdOut(true); - if (type == STDERR) cmd.withStdErr(true); + if (type == OutputFrame.OutputType.STDOUT) { + cmd.withStdOut(true); + } + if (type == OutputFrame.OutputType.STDERR) { + cmd.withStdErr(true); + } } return cmd.exec(callback); diff --git a/core/src/main/java/org/testcontainers/utility/MountableFile.java b/core/src/main/java/org/testcontainers/utility/MountableFile.java index 81cc37adeab..a8a9f465a19 100644 --- a/core/src/main/java/org/testcontainers/utility/MountableFile.java +++ b/core/src/main/java/org/testcontainers/utility/MountableFile.java @@ -1,7 +1,7 @@ package org.testcontainers.utility; -import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Charsets; +import lombok.AccessLevel; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; @@ -34,23 +34,24 @@ import java.util.stream.Stream; import java.util.zip.Checksum; -import static lombok.AccessLevel.PACKAGE; -import static org.testcontainers.utility.PathUtils.recursiveDeleteDir; - /** * An abstraction over files and classpath resources aimed at encapsulating all the complexity of generating * a path that the Docker daemon is about to create a volume mount for. */ -@RequiredArgsConstructor(access = PACKAGE) +@RequiredArgsConstructor(access = AccessLevel.PACKAGE) @Slf4j public class MountableFile implements Transferable { private static final String TESTCONTAINERS_TMP_DIR_PREFIX = ".testcontainers-tmp-"; + private static final String OS_MAC_TMP_DIR = "/tmp"; + private static final int BASE_FILE_MODE = 0100000; + private static final int BASE_DIR_MODE = 0040000; private final String path; + private final Integer forcedFileMode; @Getter(lazy = true) @@ -124,10 +125,11 @@ public static MountableFile forHostPath(final Path path, Integer mode) { return new MountableFile(path.toAbsolutePath().toString(), mode); } - @NotNull - private static URL getClasspathResource(@NotNull final String resourcePath, @NotNull final Set classLoaders) { - + private static URL getClasspathResource( + @NotNull final String resourcePath, + @NotNull final Set classLoaders + ) { final Set classLoadersToSearch = new HashSet<>(classLoaders); // try context and system classloaders as well classLoadersToSearch.add(Thread.currentThread().getContextClassLoader()); @@ -153,16 +155,22 @@ private static URL getClasspathResource(@NotNull final String resourcePath, @Not } } - throw new IllegalArgumentException("Resource with path " + resourcePath + " could not be found on any of these classloaders: " + classLoadersToSearch); + throw new IllegalArgumentException( + "Resource with path " + + resourcePath + + " could not be found on any of these classloaders: " + + classLoadersToSearch + ); } private static String unencodeResourceURIToFilePath(@NotNull final String resource) { try { // Convert any url-encoded characters (e.g. spaces) back into unencoded form - return URLDecoder.decode(resource.replaceAll("\\+", "%2B"), Charsets.UTF_8.name()) - .replaceFirst("jar:", "") - .replaceFirst("file:", "") - .replaceAll("!.*", ""); + return URLDecoder + .decode(resource.replaceAll("\\+", "%2B"), Charsets.UTF_8.name()) + .replaceFirst("jar:", "") + .replaceFirst("file:", "") + .replaceAll("!.*", ""); } catch (UnsupportedEncodingException e) { throw new IllegalStateException(e); } @@ -237,15 +245,19 @@ private String extractClassPathResourceToTempLocation(final String hostPath) { JarEntry entry = entries.nextElement(); final String name = entry.getName(); if (name.startsWith(internalPath)) { - log.debug("Copying classpath resource(s) from {} to {} to permit Docker to bind", - hostPath, - tmpLocation); + log.debug( + "Copying classpath resource(s) from {} to {} to permit Docker to bind", + hostPath, + tmpLocation + ); copyFromJarToLocation(jarFile, entry, internalPath, tmpLocation); } } - } catch (IOException e) { - throw new IllegalStateException("Failed to process JAR file when extracting classpath resource: " + hostPath, e); + throw new IllegalStateException( + "Failed to process JAR file when extracting classpath resource: " + hostPath, + e + ); } // Mark temporary files/dirs for deletion at JVM shutdown @@ -264,23 +276,22 @@ private File createTempDirectory() { return Files.createTempDirectory(Paths.get(OS_MAC_TMP_DIR), TESTCONTAINERS_TMP_DIR_PREFIX).toFile(); } return Files.createTempDirectory(TESTCONTAINERS_TMP_DIR_PREFIX).toFile(); - } catch (IOException e) { + } catch (IOException e) { return new File(TESTCONTAINERS_TMP_DIR_PREFIX + Base58.randomString(5)); } } @SuppressWarnings("ResultOfMethodCallIgnored") - private void copyFromJarToLocation(final JarFile jarFile, - final JarEntry entry, - final String fromRoot, - final File toRoot) throws IOException { - + private void copyFromJarToLocation( + final JarFile jarFile, + final JarEntry entry, + final String fromRoot, + final File toRoot + ) throws IOException { String destinationName = entry.getName().replaceFirst(fromRoot, ""); File newFile = new File(toRoot, destinationName); - log.debug("Copying resource {} from JAR file {}", - fromRoot, - jarFile.getName()); + log.debug("Copying resource {} from JAR file {}", fromRoot, jarFile.getName()); if (!entry.isDirectory()) { // Create parent directories @@ -291,14 +302,21 @@ private void copyFromJarToLocation(final JarFile jarFile, try (InputStream is = jarFile.getInputStream(entry)) { Files.copy(is, newFile.toPath()); } catch (IOException e) { - log.error("Failed to extract classpath resource " + entry.getName() + " from JAR file " + jarFile.getName(), e); + log.error( + "Failed to extract classpath resource " + entry.getName() + " from JAR file " + jarFile.getName(), + e + ); throw e; } } } private void deleteOnExit(final Path path) { - Runtime.getRuntime().addShutdownHook(new Thread(DockerClientFactory.TESTCONTAINERS_THREAD_GROUP, () -> recursiveDeleteDir(path))); + Runtime + .getRuntime() + .addShutdownHook( + new Thread(DockerClientFactory.TESTCONTAINERS_THREAD_GROUP, () -> PathUtils.recursiveDeleteDir(path)) + ); } /** @@ -312,11 +330,20 @@ public void transferTo(final TarArchiveOutputStream outputStream, String destina /* * Recursively copies a file/directory into a TarArchiveOutputStream */ - private void recursiveTar(String entryFilename, String rootPath, String itemPath, TarArchiveOutputStream tarArchive) { + private void recursiveTar( + String entryFilename, + String rootPath, + String itemPath, + TarArchiveOutputStream tarArchive + ) { try { - final File sourceFile = new File(itemPath).getCanonicalFile(); // e.g. /foo/bar/baz - final File sourceRootFile = new File(rootPath).getCanonicalFile(); // e.g. /foo - final String relativePathToSourceFile = sourceRootFile.toPath().relativize(sourceFile.toPath()).toFile().toString(); // e.g. /bar/baz + final File sourceFile = new File(itemPath).getCanonicalFile(); // e.g. /foo/bar/baz + final File sourceRootFile = new File(rootPath).getCanonicalFile(); // e.g. /foo + final String relativePathToSourceFile = sourceRootFile + .toPath() + .relativize(sourceFile.toPath()) + .toFile() + .toString(); // e.g. /bar/baz final String tarEntryFilename; if (relativePathToSourceFile.isEmpty()) { @@ -341,7 +368,12 @@ private void recursiveTar(String entryFilename, String rootPath, String itemPath if (children != null) { // recurse into child files/directories for (final File child : children) { - recursiveTar(entryFilename, sourceRootFile.getCanonicalPath(), child.getCanonicalPath(), tarArchive); + recursiveTar( + entryFilename, + sourceRootFile.getCanonicalPath(), + child.getCanonicalPath(), + tarArchive + ); } } } catch (IOException e) { @@ -352,7 +384,6 @@ private void recursiveTar(String entryFilename, String rootPath, String itemPath @Override public long getSize() { - final File file = new File(this.getResolvedPath()); if (file.isFile()) { return file.length(); @@ -378,9 +409,11 @@ private void checksumFile(File file, Checksum checksum) { checksum.update(MountableFile.getUnixFileMode(path)); if (file.isDirectory()) { try (Stream stream = Files.walk(path)) { - stream.filter(it -> it != path).forEach(it -> { - checksumFile(it.toFile(), checksum); - }); + stream + .filter(it -> it != path) + .forEach(it -> { + checksumFile(it.toFile(), checksum); + }); } } else { FileUtils.checksum(file, checksum); @@ -405,9 +438,11 @@ public static int getUnixFileMode(final Path path) { try { int unixMode = (int) Files.readAttributes(path, "unix:mode").get("mode"); // Truncate mode bits for z/OS - if ("OS/390".equals(SystemUtils.OS_NAME) || + if ( + "OS/390".equals(SystemUtils.OS_NAME) || "z/OS".equals(SystemUtils.OS_NAME) || - "zOS".equals(SystemUtils.OS_NAME) ) { + "zOS".equals(SystemUtils.OS_NAME) + ) { unixMode &= TarConstants.MAXID; unixMode |= Files.isDirectory(path) ? 040000 : 0100000; } diff --git a/core/src/main/java/org/testcontainers/utility/PathUtils.java b/core/src/main/java/org/testcontainers/utility/PathUtils.java index 2dcf9db8a8d..7f8b5925689 100644 --- a/core/src/main/java/org/testcontainers/utility/PathUtils.java +++ b/core/src/main/java/org/testcontainers/utility/PathUtils.java @@ -23,21 +23,23 @@ public class PathUtils { */ public static void recursiveDeleteDir(final @NonNull Path directory) { try { - Files.walkFileTree(directory, new SimpleFileVisitor() { - @Override - public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { - Files.delete(dir); - return FileVisitResult.CONTINUE; - } + Files.walkFileTree( + directory, + new SimpleFileVisitor() { + @Override + public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { + Files.delete(dir); + return FileVisitResult.CONTINUE; + } - @Override - public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { - Files.delete(file); - return FileVisitResult.CONTINUE; + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + Files.delete(file); + return FileVisitResult.CONTINUE; + } } - }); - } catch (IOException ignored) { - } + ); + } catch (IOException ignored) {} } /** @@ -66,8 +68,10 @@ public static String createMinGWPath(String path) { } // drive-letter must be lower case - mingwPath = "//" + Character.toLowerCase(mingwPath.charAt(driveLetterIndex)) + - mingwPath.substring(driveLetterIndex + 1); + mingwPath = + "//" + + Character.toLowerCase(mingwPath.charAt(driveLetterIndex)) + + mingwPath.substring(driveLetterIndex + 1); mingwPath = mingwPath.replace(":", ""); return mingwPath; } diff --git a/core/src/main/java/org/testcontainers/utility/PrefixingImageNameSubstitutor.java b/core/src/main/java/org/testcontainers/utility/PrefixingImageNameSubstitutor.java index c720416deac..7cf082000a4 100644 --- a/core/src/main/java/org/testcontainers/utility/PrefixingImageNameSubstitutor.java +++ b/core/src/main/java/org/testcontainers/utility/PrefixingImageNameSubstitutor.java @@ -38,11 +38,7 @@ public DockerImageName apply(DockerImageName original) { return original; } - log.debug( - "Applying changes to image name {}: applying prefix '{}'", - original, - configuredPrefix - ); + log.debug("Applying changes to image name {}: applying prefix '{}'", original, configuredPrefix); DockerImageName prefixAsImage = DockerImageName.parse(configuredPrefix); diff --git a/core/src/main/java/org/testcontainers/utility/RegistryAuthLocator.java b/core/src/main/java/org/testcontainers/utility/RegistryAuthLocator.java index e487bda686b..0001b926059 100644 --- a/core/src/main/java/org/testcontainers/utility/RegistryAuthLocator.java +++ b/core/src/main/java/org/testcontainers/utility/RegistryAuthLocator.java @@ -7,6 +7,7 @@ import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.SystemUtils; import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.testcontainers.DockerClientFactory; import org.zeroturnaround.exec.InvalidResultException; import org.zeroturnaround.exec.ProcessExecutor; @@ -23,23 +24,23 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import static org.apache.commons.lang3.StringUtils.isBlank; -import static org.slf4j.LoggerFactory.getLogger; -import static org.testcontainers.utility.AuthConfigUtil.toSafeString; - /** * Utility to look up registry authentication information for an image. */ public class RegistryAuthLocator { - private static final Logger log = getLogger(RegistryAuthLocator.class); + private static final Logger log = LoggerFactory.getLogger(RegistryAuthLocator.class); + private static final String DEFAULT_REGISTRY_NAME = "https://index.docker.io/v1/"; + private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); private static RegistryAuthLocator instance; private final String commandPathPrefix; + private final String commandExtension; + private final File configFile; private final Map> cache = new ConcurrentHashMap<>(); @@ -51,8 +52,12 @@ public class RegistryAuthLocator { private final Map CREDENTIALS_HELPERS_NOT_FOUND_MESSAGE_CACHE; @VisibleForTesting - RegistryAuthLocator(File configFile, String commandPathPrefix, String commandExtension, - Map notFoundMessageHolderReference) { + RegistryAuthLocator( + File configFile, + String commandPathPrefix, + String commandExtension, + Map notFoundMessageHolderReference + ) { this.configFile = configFile; this.commandPathPrefix = commandPathPrefix; this.commandExtension = commandExtension; @@ -63,8 +68,9 @@ public class RegistryAuthLocator { /** */ protected RegistryAuthLocator() { - final String dockerConfigLocation = System.getenv().getOrDefault("DOCKER_CONFIG", - System.getProperty("user.home") + "/.docker"); + final String dockerConfigLocation = System + .getenv() + .getOrDefault("DOCKER_CONFIG", System.getProperty("user.home") + "/.docker"); this.configFile = new File(dockerConfigLocation + "/config.json"); this.commandPathPrefix = ""; this.commandExtension = ""; @@ -72,7 +78,7 @@ protected RegistryAuthLocator() { this.CREDENTIALS_HELPERS_NOT_FOUND_MESSAGE_CACHE = new HashMap<>(); } - public synchronized static RegistryAuthLocator instance() { + public static synchronized RegistryAuthLocator instance() { if (instance == null) { instance = new RegistryAuthLocator(); } @@ -106,23 +112,31 @@ public AuthConfig lookupAuthConfig(DockerImageName dockerImageName, AuthConfig d final String registryName = effectiveRegistryName(dockerImageName); log.debug("Looking up auth config for image: {} at registry: {}", dockerImageName, registryName); - final Optional cachedAuth = cache.computeIfAbsent(registryName, __ -> lookupUncachedAuthConfig(registryName, dockerImageName)); + final Optional cachedAuth = cache.computeIfAbsent( + registryName, + __ -> lookupUncachedAuthConfig(registryName, dockerImageName) + ); if (cachedAuth.isPresent()) { - log.debug("Cached auth found: [{}]", toSafeString(cachedAuth.get())); + log.debug("Cached auth found: [{}]", AuthConfigUtil.toSafeString(cachedAuth.get())); return cachedAuth.get(); } else { - log.debug("No matching Auth Configs - falling back to defaultAuthConfig [{}]", toSafeString(defaultAuthConfig)); + log.debug( + "No matching Auth Configs - falling back to defaultAuthConfig [{}]", + AuthConfigUtil.toSafeString(defaultAuthConfig) + ); // otherwise, defaultAuthConfig should already contain any credentials available return defaultAuthConfig; } } private Optional lookupUncachedAuthConfig(String registryName, DockerImageName dockerImageName) { - log.debug("RegistryAuthLocator has configFile: {} ({}) and commandPathPrefix: {}", + log.debug( + "RegistryAuthLocator has configFile: {} ({}) and commandPathPrefix: {}", configFile, configFile.exists() ? "exists" : "does not exist", - commandPathPrefix); + commandPathPrefix + ); try { final JsonNode config = OBJECT_MAPPER.readTree(configFile); @@ -131,32 +145,33 @@ private Optional lookupUncachedAuthConfig(String registryName, Docke // use helper preferentially (per https://docs.docker.com/engine/reference/commandline/cli/) final AuthConfig helperAuthConfig = authConfigUsingHelper(config, registryName); if (helperAuthConfig != null) { - log.debug("found helper auth config [{}]", toSafeString(helperAuthConfig)); + log.debug("found helper auth config [{}]", AuthConfigUtil.toSafeString(helperAuthConfig)); return Optional.of(helperAuthConfig); } // no credsHelper to use, using credsStore: final AuthConfig storeAuthConfig = authConfigUsingStore(config, registryName); if (storeAuthConfig != null) { - log.debug("found creds store auth config [{}]", toSafeString(storeAuthConfig)); + log.debug("found creds store auth config [{}]", AuthConfigUtil.toSafeString(storeAuthConfig)); return Optional.of(storeAuthConfig); } // fall back to base64 encoded auth hardcoded in config file final AuthConfig existingAuthConfig = findExistingAuthConfig(config, registryName); if (existingAuthConfig != null) { - log.debug("found existing auth config [{}]", toSafeString(existingAuthConfig)); + log.debug("found existing auth config [{}]", AuthConfigUtil.toSafeString(existingAuthConfig)); return Optional.of(existingAuthConfig); } } catch (Exception e) { - log.info("Failure when attempting to lookup auth config. Please ignore if you don't have images in an authenticated registry. Details: (dockerImageName: {}, configFile: {}. Falling back to docker-java default behaviour. Exception message: {}", + log.info( + "Failure when attempting to lookup auth config. Please ignore if you don't have images in an authenticated registry. Details: (dockerImageName: {}, configFile: {}. Falling back to docker-java default behaviour. Exception message: {}", dockerImageName, configFile, - e.getMessage()); + e.getMessage() + ); } return Optional.empty(); } private AuthConfig findExistingAuthConfig(final JsonNode config, final String reposName) throws Exception { - final Map.Entry entry = findAuthNode(config, reposName); if (entry != null && entry.getValue() != null && entry.getValue().size() > 0) { @@ -164,10 +179,11 @@ private AuthConfig findExistingAuthConfig(final JsonNode config, final String re .treeToValue(entry.getValue(), AuthConfig.class) .withRegistryAddress(entry.getKey()); - if (isBlank(deserializedAuth.getUsername()) && - isBlank(deserializedAuth.getPassword()) && - !isBlank(deserializedAuth.getAuth())) { - + if ( + StringUtils.isBlank(deserializedAuth.getUsername()) && + StringUtils.isBlank(deserializedAuth.getPassword()) && + !StringUtils.isBlank(deserializedAuth.getAuth()) + ) { final String rawAuth = new String(Base64.getDecoder().decode(deserializedAuth.getAuth())); final String[] splitRawAuth = rawAuth.split(":", 2); @@ -198,7 +214,7 @@ private AuthConfig authConfigUsingStore(final JsonNode config, final String repo final JsonNode credsStoreNode = config.get("credsStore"); if (credsStoreNode != null && !credsStoreNode.isMissingNode() && credsStoreNode.isTextual()) { final String credsStore = credsStoreNode.asText(); - if (isBlank(credsStore)) { + if (StringUtils.isBlank(credsStore)) { log.warn("Docker auth config credsStore field will be ignored, because value is blank"); return null; } @@ -222,8 +238,7 @@ private Map.Entry findAuthNode(final JsonNode config, final St } private AuthConfig runCredentialProvider(String hostName, String helperOrStoreName) throws Exception { - - if (isBlank(hostName)) { + if (StringUtils.isBlank(hostName)) { log.debug("There is no point in locating AuthConfig for blank hostName. Returning NULL to allow fallback"); return null; } @@ -231,28 +246,34 @@ private AuthConfig runCredentialProvider(String hostName, String helperOrStoreNa final String credentialProgramName = getCredentialProgramName(helperOrStoreName); final String data; - log.debug("Executing docker credential provider: {} to locate auth config for: {}", - credentialProgramName, hostName); + log.debug( + "Executing docker credential provider: {} to locate auth config for: {}", + credentialProgramName, + hostName + ); try { data = runCredentialProgram(hostName, credentialProgramName); } catch (InvalidResultException e) { - final String responseErrorMsg = extractCredentialProviderErrorMessage(e); - if (!isBlank(responseErrorMsg)) { + if (!StringUtils.isBlank(responseErrorMsg)) { String credentialsNotFoundMsg = getGenericCredentialsNotFoundMsg(credentialProgramName); if (credentialsNotFoundMsg != null && credentialsNotFoundMsg.equals(responseErrorMsg)) { - log.info("Credential helper/store ({}) does not have credentials for {}", + log.info( + "Credential helper/store ({}) does not have credentials for {}", credentialProgramName, - hostName); + hostName + ); return null; } - log.debug("Failure running docker credential helper/store ({}) with output '{}'", - credentialProgramName, responseErrorMsg); - + log.debug( + "Failure running docker credential helper/store ({}) with output '{}'", + credentialProgramName, + responseErrorMsg + ); } else { log.debug("Failure running docker credential helper/store ({})", credentialProgramName); } @@ -296,7 +317,7 @@ private String effectiveRegistryName(DockerImageName dockerImageName) { private String getGenericCredentialsNotFoundMsg(String credentialHelperName) { if (!CREDENTIALS_HELPERS_NOT_FOUND_MESSAGE_CACHE.containsKey(credentialHelperName)) { String credentialsNotFoundMsg = discoverCredentialsHelperNotFoundMessage(credentialHelperName); - if (!isBlank(credentialsNotFoundMsg)) { + if (!StringUtils.isBlank(credentialsNotFoundMsg)) { CREDENTIALS_HELPERS_NOT_FOUND_MESSAGE_CACHE.put(credentialHelperName, credentialsNotFoundMsg); } } @@ -317,19 +338,26 @@ private String discoverCredentialsHelperNotFoundMessage(String credentialHelperN runCredentialProgram(notExistentFakeHostName, credentialHelperName); // should not reach here - log.warn("Failure running docker credential helper ({}) with fake call, expected 'credentials not found' response", - credentialHelperName); - } catch(Exception e) { + log.warn( + "Failure running docker credential helper ({}) with fake call, expected 'credentials not found' response", + credentialHelperName + ); + } catch (Exception e) { if (e instanceof InvalidResultException) { - credentialsNotFoundMsg = extractCredentialProviderErrorMessage((InvalidResultException)e); + credentialsNotFoundMsg = extractCredentialProviderErrorMessage((InvalidResultException) e); } - if (isBlank(credentialsNotFoundMsg)) { - log.warn("Failure running docker credential helper ({}) with fake call, expected 'credentials not found' response. Exception message: {}", + if (StringUtils.isBlank(credentialsNotFoundMsg)) { + log.warn( + "Failure running docker credential helper ({}) with fake call, expected 'credentials not found' response. Exception message: {}", credentialHelperName, - e.getMessage()); + e.getMessage() + ); } else { - log.debug("Got credentials not found error message from docker credential helper - {}", credentialsNotFoundMsg); + log.debug( + "Got credentials not found error message from docker credential helper - {}", + credentialsNotFoundMsg + ); } } @@ -345,16 +373,17 @@ private String extractCredentialProviderErrorMessage(InvalidResultException inva private String runCredentialProgram(String hostName, String credentialHelperName) throws InvalidResultException, InterruptedException, TimeoutException, IOException { - - String[] command = SystemUtils.IS_OS_WINDOWS ? new String[] {"cmd", "/c", credentialHelperName, "get"} : new String[]{credentialHelperName, "get"}; + String[] command = SystemUtils.IS_OS_WINDOWS + ? new String[] { "cmd", "/c", credentialHelperName, "get" } + : new String[] { credentialHelperName, "get" }; return new ProcessExecutor() - .command(command) - .redirectInput(new ByteArrayInputStream(hostName.getBytes())) - .readOutput(true) - .exitValueNormal() - .timeout(30, TimeUnit.SECONDS) - .execute() - .outputUTF8() - .trim(); + .command(command) + .redirectInput(new ByteArrayInputStream(hostName.getBytes())) + .readOutput(true) + .exitValueNormal() + .timeout(30, TimeUnit.SECONDS) + .execute() + .outputUTF8() + .trim(); } } diff --git a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java index d916579db35..ad0c949f6e7 100644 --- a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java +++ b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java @@ -49,29 +49,30 @@ public class ResourceReaper { static final List>> DEATH_NOTE = new ArrayList<>( Arrays.asList( Stream - .concat( - DockerClientFactory.DEFAULT_LABELS.entrySet().stream(), - MARKER_LABELS.entrySet().stream() - ) + .concat(DockerClientFactory.DEFAULT_LABELS.entrySet().stream(), MARKER_LABELS.entrySet().stream()) .>map(it -> new SimpleEntry<>("label", it.getKey() + "=" + it.getValue())) .collect(Collectors.toList()) ) ); private static ResourceReaper instance; + final DockerClient dockerClient = DockerClientFactory.lazyClient(); + private Map registeredContainers = new ConcurrentHashMap<>(); + private Set registeredNetworks = Sets.newConcurrentHashSet(); + private Set registeredImages = Sets.newConcurrentHashSet(); + private AtomicBoolean hookIsSet = new AtomicBoolean(false); /** * Internal constructor to avoid custom implementations */ - ResourceReaper() { - } + ResourceReaper() {} - public synchronized static ResourceReaper instance() { + public static synchronized ResourceReaper instance() { if (instance == null) { boolean useRyuk = !Boolean.parseBoolean(System.getenv("TESTCONTAINERS_RYUK_DISABLED")); if (useRyuk) { @@ -117,7 +118,9 @@ public void registerFilterForCleanup(List> filter) { */ public void registerLabelsFilterForCleanup(Map labels) { registerFilterForCleanup( - labels.entrySet().stream() + labels + .entrySet() + .stream() .map(it -> new SimpleEntry<>("label", it.getKey() + "=" + it.getValue())) .collect(Collectors.toList()) ); @@ -172,9 +175,11 @@ private void removeContainer(String containerId, String imageName) { LOGGER.trace("Was going to stop container but it apparently no longer exists: {}", containerId); return; } catch (Exception e) { - LOGGER.trace("Error encountered when checking container for shutdown (ID: {}) - it may not have been stopped, or may already be stopped. Root cause: {}", + LOGGER.trace( + "Error encountered when checking container for shutdown (ID: {}) - it may not have been stopped, or may already be stopped. Root cause: {}", containerId, - Throwables.getRootCause(e).getMessage()); + Throwables.getRootCause(e).getMessage() + ); return; } @@ -184,9 +189,11 @@ private void removeContainer(String containerId, String imageName) { dockerClient.killContainerCmd(containerId).exec(); LOGGER.trace("Stopped container: {}", imageName); } catch (Exception e) { - LOGGER.trace("Error encountered shutting down container (ID: {}) - it may not have been stopped, or may already be stopped. Root cause: {}", + LOGGER.trace( + "Error encountered shutting down container (ID: {}) - it may not have been stopped, or may already be stopped. Root cause: {}", containerId, - Throwables.getRootCause(e).getMessage()); + Throwables.getRootCause(e).getMessage() + ); } } @@ -202,9 +209,11 @@ private void removeContainer(String containerId, String imageName) { dockerClient.removeContainerCmd(containerId).withRemoveVolumes(true).withForce(true).exec(); LOGGER.debug("Removed container and associated volume(s): {}", imageName); } catch (Exception e) { - LOGGER.trace("Error encountered shutting down container (ID: {}) - it may not have been stopped, or may already be stopped. Root cause: {}", + LOGGER.trace( + "Error encountered shutting down container (ID: {}) - it may not have been stopped, or may already be stopped. Root cause: {}", containerId, - Throwables.getRootCause(e).getMessage()); + Throwables.getRootCause(e).getMessage() + ); } } @@ -227,10 +236,9 @@ public void registerNetworkIdForCleanup(String id) { */ @Deprecated public void removeNetworkById(String id) { - removeNetwork(id); + removeNetwork(id); } - private void removeNetwork(String id) { try { List networks; @@ -239,7 +247,10 @@ private void removeNetwork(String id) { // Listing by ID first prevents docker-java logging an error if we just go blindly into removeNetworkCmd networks = dockerClient.listNetworksCmd().withIdFilter(id).exec(); } catch (Exception e) { - LOGGER.trace("Error encountered when looking up network for removal (name: {}) - it may not have been removed", id); + LOGGER.trace( + "Error encountered when looking up network for removal (name: {}) - it may not have been removed", + id + ); return; } @@ -251,7 +262,10 @@ private void removeNetwork(String id) { registeredNetworks.remove(network.getId()); LOGGER.debug("Removed network: {}", id); } catch (Exception e) { - LOGGER.trace("Error encountered removing network (name: {}) - it may not have been removed", network.getName()); + LOGGER.trace( + "Error encountered removing network (name: {}) - it may not have been removed", + network.getName() + ); } } } finally { @@ -296,7 +310,9 @@ private void removeImage(String dockerImageName) { void setHook() { if (hookIsSet.compareAndSet(false, true)) { // If the JVM stops without containers being stopped, try and stop the container. - Runtime.getRuntime().addShutdownHook(new Thread(DockerClientFactory.TESTCONTAINERS_THREAD_GROUP, this::performCleanup)); + Runtime + .getRuntime() + .addShutdownHook(new Thread(DockerClientFactory.TESTCONTAINERS_THREAD_GROUP, this::performCleanup)); } } @@ -323,8 +339,7 @@ public CreateContainerCmd register(GenericContainer container, CreateContaine * @deprecated internal API */ @Deprecated - public void init() { - } + public void init() {} static class FilterRegistry { @@ -332,6 +347,7 @@ static class FilterRegistry { static final String ACKNOWLEDGMENT = "ACK"; private final BufferedReader in; + private final OutputStream out; FilterRegistry(InputStream ryukInputStream, OutputStream ryukOutputStream) { @@ -347,10 +363,13 @@ static class FilterRegistry { * @throws IOException if communication with Ryuk fails */ protected boolean register(List> filters) throws IOException { - String query = filters.stream() + String query = filters + .stream() .map(it -> { try { - return URLEncoder.encode(it.getKey(), "UTF-8") + "=" + URLEncoder.encode(it.getValue(), "UTF-8"); + return ( + URLEncoder.encode(it.getKey(), "UTF-8") + "=" + URLEncoder.encode(it.getValue(), "UTF-8") + ); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } @@ -372,6 +391,5 @@ private static boolean waitForAcknowledgment(BufferedReader in) throws IOExcepti } return ACKNOWLEDGMENT.equalsIgnoreCase(line); } - } } diff --git a/core/src/main/java/org/testcontainers/utility/RyukContainer.java b/core/src/main/java/org/testcontainers/utility/RyukContainer.java index 1e18c14157a..511e6ccac17 100644 --- a/core/src/main/java/org/testcontainers/utility/RyukContainer.java +++ b/core/src/main/java/org/testcontainers/utility/RyukContainer.java @@ -10,15 +10,20 @@ class RyukContainer extends GenericContainer { RyukContainer() { super("testcontainers/ryuk:0.3.3"); - withExposedPorts(8080); withCreateContainerCmdModifier(cmd -> { cmd.withName("testcontainers-ryuk-" + DockerClientFactory.SESSION_ID); cmd.withHostConfig( - cmd.getHostConfig() + cmd + .getHostConfig() .withAutoRemove(true) .withPrivileged(TestcontainersConfiguration.getInstance().isRyukPrivileged()) - .withBinds(new Bind(DockerClientFactory.instance().getRemoteDockerUnixSocketPath(), new Volume("/var/run/docker.sock"))) + .withBinds( + new Bind( + DockerClientFactory.instance().getRemoteDockerUnixSocketPath(), + new Volume("/var/run/docker.sock") + ) + ) ); }); diff --git a/core/src/main/java/org/testcontainers/utility/RyukResourceReaper.java b/core/src/main/java/org/testcontainers/utility/RyukResourceReaper.java index 6ce9c372fd2..999b1cbbffb 100644 --- a/core/src/main/java/org/testcontainers/utility/RyukResourceReaper.java +++ b/core/src/main/java/org/testcontainers/utility/RyukResourceReaper.java @@ -90,7 +90,10 @@ private synchronized void maybeStart() { // not set the read timeout, as Ryuk would not send anything unless a new filter is submitted, meaning that we would get a timeout exception pretty quick try (Socket clientSocket = new Socket()) { clientSocket.connect(new InetSocketAddress(host, ryukPort), 5 * 1000); - ResourceReaper.FilterRegistry registry = new ResourceReaper.FilterRegistry(clientSocket.getInputStream(), clientSocket.getOutputStream()); + ResourceReaper.FilterRegistry registry = new ResourceReaper.FilterRegistry( + clientSocket.getInputStream(), + clientSocket.getOutputStream() + ); synchronized (ResourceReaper.DEATH_NOTE) { while (true) { diff --git a/core/src/main/java/org/testcontainers/utility/TestEnvironment.java b/core/src/main/java/org/testcontainers/utility/TestEnvironment.java index dbd8e45a98f..ee58199540b 100644 --- a/core/src/main/java/org/testcontainers/utility/TestEnvironment.java +++ b/core/src/main/java/org/testcontainers/utility/TestEnvironment.java @@ -8,8 +8,7 @@ */ public class TestEnvironment { - private TestEnvironment() { - } + private TestEnvironment() {} public static boolean dockerApiAtLeast(String minimumVersion) { ComparableVersion min = new ComparableVersion(minimumVersion); @@ -29,4 +28,3 @@ public static boolean dockerIsDockerMachine() { return DockerClientFactory.instance().isUsing(DockerMachineClientProviderStrategy.class); } } - diff --git a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java index c9dfd543ad6..e647a8d50b3 100644 --- a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java +++ b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java @@ -50,18 +50,29 @@ public class TestcontainersConfiguration { private static File USER_CONFIG_FILE = new File(System.getProperty("user.home"), "." + PROPERTIES_FILE_NAME); private static final String AMBASSADOR_IMAGE = "richnorth/ambassador"; + private static final String SOCAT_IMAGE = "alpine/socat"; + private static final String VNC_RECORDER_IMAGE = "testcontainers/vnc-recorder"; + private static final String COMPOSE_IMAGE = "docker/compose"; + private static final String ALPINE_IMAGE = "alpine"; + private static final String RYUK_IMAGE = "testcontainers/ryuk"; + private static final String KAFKA_IMAGE = "confluentinc/cp-kafka"; + private static final String PULSAR_IMAGE = "apachepulsar/pulsar"; + private static final String LOCALSTACK_IMAGE = "localstack/localstack"; + private static final String SSHD_IMAGE = "testcontainers/sshd"; + private static final String ORACLE_IMAGE = "gvenzl/oracle-xe"; - private static final ImmutableMap CONTAINER_MAPPING = ImmutableMap.builder() + private static final ImmutableMap CONTAINER_MAPPING = ImmutableMap + .builder() .put(DockerImageName.parse(AMBASSADOR_IMAGE), "ambassador.container.image") .put(DockerImageName.parse(SOCAT_IMAGE), "socat.container.image") .put(DockerImageName.parse(VNC_RECORDER_IMAGE), "vncrecorder.container.image") @@ -78,7 +89,7 @@ public class TestcontainersConfiguration { @Getter(lazy = true) private static final TestcontainersConfiguration instance = loadConfiguration(); - @SuppressWarnings({"ConstantConditions", "unchecked", "rawtypes"}) + @SuppressWarnings({ "ConstantConditions", "unchecked", "rawtypes" }) @VisibleForTesting static AtomicReference getInstanceField() { // Lazy Getter from Lombok changes the field's type to AtomicReference @@ -86,10 +97,16 @@ static AtomicReference getInstanceField() { } private final Properties userProperties; + private final Properties classpathProperties; + private final Map environment; - TestcontainersConfiguration(Properties userProperties, Properties classpathProperties, final Map environment) { + TestcontainersConfiguration( + Properties userProperties, + Properties classpathProperties, + final Map environment + ) { this.userProperties = userProperties; this.classpathProperties = classpathProperties; this.environment = environment; @@ -121,8 +138,7 @@ public String getTinyImage() { } public boolean isRyukPrivileged() { - return Boolean - .parseBoolean(getEnvVarOrProperty("ryuk.container.privileged", "false")); + return Boolean.parseBoolean(getEnvVarOrProperty("ryuk.container.privileged", "false")); } @Deprecated @@ -203,7 +219,11 @@ public Integer getClientPingTimeout() { @Nullable @Contract("_, !null, _ -> !null") - private String getConfigurable(@NotNull final String propertyName, @Nullable final String defaultValue, Properties... propertiesSources) { + private String getConfigurable( + @NotNull final String propertyName, + @Nullable final String defaultValue, + Properties... propertiesSources + ) { String envVarName = propertyName.replaceAll("\\.", "_").toUpperCase(); if (!envVarName.startsWith("TESTCONTAINERS_") && !envVarName.startsWith("DOCKER_")) { envVarName = "TESTCONTAINERS_" + envVarName; @@ -279,11 +299,15 @@ public String getUserProperty(@NotNull final String propertyName, @Nullable fina */ @Deprecated public Properties getProperties() { - return Stream.of(userProperties, classpathProperties) - .reduce(new Properties(), (a, b) -> { - a.putAll(b); - return a; - }); + return Stream + .of(userProperties, classpathProperties) + .reduce( + new Properties(), + (a, b) -> { + a.putAll(b); + return a; + } + ); } @Deprecated @@ -318,15 +342,20 @@ public boolean updateUserConfig(@NonNull String prop, @NonNull String value) { private static TestcontainersConfiguration loadConfiguration() { return new TestcontainersConfiguration( readProperties(USER_CONFIG_FILE.toURI().toURL()), - ClasspathScanner.scanFor(PROPERTIES_FILE_NAME) + ClasspathScanner + .scanFor(PROPERTIES_FILE_NAME) .map(TestcontainersConfiguration::readProperties) - .reduce(new Properties(), (a, b) -> { - // first-write-wins merging - URLs appearing first on the classpath alphabetically will take priority. - // Note that this means that file: URLs will always take priority over jar: URLs. - b.putAll(a); - return b; - }), - System.getenv()); + .reduce( + new Properties(), + (a, b) -> { + // first-write-wins merging - URLs appearing first on the classpath alphabetically will take priority. + // Note that this means that file: URLs will always take priority over jar: URLs. + b.putAll(a); + return b; + } + ), + System.getenv() + ); } private static Properties readProperties(URL url) { @@ -335,9 +364,17 @@ private static Properties readProperties(URL url) { try (InputStream inputStream = url.openStream()) { properties.load(inputStream); } catch (FileNotFoundException e) { - log.warn("Attempted to read Testcontainers configuration file at {} but the file was not found. Exception message: {}", url, ExceptionUtils.getRootCauseMessage(e)); + log.warn( + "Attempted to read Testcontainers configuration file at {} but the file was not found. Exception message: {}", + url, + ExceptionUtils.getRootCauseMessage(e) + ); } catch (IOException e) { - log.warn("Attempted to read Testcontainers configuration file at {} but could it not be loaded. Exception message: {}", url, ExceptionUtils.getRootCauseMessage(e)); + log.warn( + "Attempted to read Testcontainers configuration file at {} but could it not be loaded. Exception message: {}", + url, + ExceptionUtils.getRootCauseMessage(e) + ); } return properties; } @@ -349,14 +386,14 @@ private DockerImageName getImage(final String defaultValue) { DockerImageName getConfiguredSubstituteImage(DockerImageName original) { for (final Map.Entry entry : CONTAINER_MAPPING.entrySet()) { if (original.isCompatibleWith(entry.getKey())) { - return - Optional.ofNullable(entry.getValue()) - .map(propertyName -> getEnvVarOrProperty(propertyName, null)) - .map(String::valueOf) - .map(String::trim) - .map(DockerImageName::parse) - .orElse(original) - .asCompatibleSubstituteFor(original); + return Optional + .ofNullable(entry.getValue()) + .map(propertyName -> getEnvVarOrProperty(propertyName, null)) + .map(String::valueOf) + .map(String::trim) + .map(DockerImageName::parse) + .orElse(original) + .asCompatibleSubstituteFor(original); } } return original; diff --git a/core/src/main/java/org/testcontainers/utility/ThrowingFunction.java b/core/src/main/java/org/testcontainers/utility/ThrowingFunction.java index 6b2cef907e1..a8138f9dda0 100644 --- a/core/src/main/java/org/testcontainers/utility/ThrowingFunction.java +++ b/core/src/main/java/org/testcontainers/utility/ThrowingFunction.java @@ -1,6 +1,5 @@ package org.testcontainers.utility; public interface ThrowingFunction { - R apply(T t) throws Exception; } diff --git a/core/src/main/java/org/testcontainers/utility/Versioning.java b/core/src/main/java/org/testcontainers/utility/Versioning.java index 4b1da407440..372a105518f 100644 --- a/core/src/main/java/org/testcontainers/utility/Versioning.java +++ b/core/src/main/java/org/testcontainers/utility/Versioning.java @@ -45,8 +45,11 @@ public int hashCode() { @EqualsAndHashCode class TagVersioning implements Versioning { + public static final String TAG_REGEX = "[\\w][\\w.\\-]{0,127}"; + static final TagVersioning LATEST = new TagVersioning("latest"); + private final String tag; TagVersioning(String tag) { @@ -71,7 +74,9 @@ public String toString() { @EqualsAndHashCode class Sha256Versioning implements Versioning { + public static final String HASH_REGEX = "[0-9a-fA-F]{32,}"; + private final String hash; Sha256Versioning(String hash) { diff --git a/core/src/test/java/alt/testcontainers/images/OutOfPackageImagePullPolicyTest.java b/core/src/test/java/alt/testcontainers/images/OutOfPackageImagePullPolicyTest.java index e4197c7c1f6..168932106fb 100644 --- a/core/src/test/java/alt/testcontainers/images/OutOfPackageImagePullPolicyTest.java +++ b/core/src/test/java/alt/testcontainers/images/OutOfPackageImagePullPolicyTest.java @@ -1,29 +1,30 @@ package alt.testcontainers.images; import org.junit.Test; +import org.testcontainers.TestImages; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.startupcheck.OneShotStartupCheckStrategy; import org.testcontainers.images.AbstractImagePullPolicy; import org.testcontainers.images.ImageData; import org.testcontainers.utility.DockerImageName; -import static org.testcontainers.TestImages.TINY_IMAGE; - public class OutOfPackageImagePullPolicyTest { + @Test public void shouldSupportCustomPoliciesOutOfTestContainersPackage() { try ( - GenericContainer container = new GenericContainer<>(TINY_IMAGE) - .withImagePullPolicy(new AbstractImagePullPolicy() { - @Override - protected boolean shouldPullCached(DockerImageName imageName, ImageData localImageData) { - return false; + GenericContainer container = new GenericContainer<>(TestImages.TINY_IMAGE) + .withImagePullPolicy( + new AbstractImagePullPolicy() { + @Override + protected boolean shouldPullCached(DockerImageName imageName, ImageData localImageData) { + return false; + } } - }) + ) ) { container.withStartupCheckStrategy(new OneShotStartupCheckStrategy()); container.start(); } } - } diff --git a/core/src/test/java/org/testcontainers/DaemonTest.java b/core/src/test/java/org/testcontainers/DaemonTest.java index f08614c8cdc..c1a1c20d60a 100644 --- a/core/src/test/java/org/testcontainers/DaemonTest.java +++ b/core/src/test/java/org/testcontainers/DaemonTest.java @@ -10,7 +10,6 @@ import java.util.stream.Collectors; import static org.junit.Assert.assertEquals; -import static org.testcontainers.TestImages.TINY_IMAGE; /** * This test forks a new JVM, otherwise it's not possible to reliably diff the threads @@ -23,7 +22,7 @@ public static void main(String[] args) { GenericContainer genericContainer = null; try { - genericContainer = new GenericContainer<>(TINY_IMAGE).withCommand("top"); + genericContainer = new GenericContainer<>(TestImages.TINY_IMAGE).withCommand("top"); genericContainer.start(); Set threads = new HashSet<>(Thread.getAllStackTraces().keySet()); @@ -34,11 +33,14 @@ public static void main(String[] args) { if (nonDaemonThreads.isEmpty()) { VisibleAssertions.pass("All threads marked as daemon"); } else { - String nonDaemonThreadNames = nonDaemonThreads.stream() + String nonDaemonThreadNames = nonDaemonThreads + .stream() .map(Thread::getName) .collect(Collectors.joining("\n", "\n", "")); - VisibleAssertions.fail("Expected all threads to be daemons but the following are not:\n" + nonDaemonThreadNames); + VisibleAssertions.fail( + "Expected all threads to be daemons but the following are not:\n" + nonDaemonThreadNames + ); } } finally { if (genericContainer != null) { diff --git a/core/src/test/java/org/testcontainers/DockerClientFactoryTest.java b/core/src/test/java/org/testcontainers/DockerClientFactoryTest.java index a889764579f..794bbbc37c9 100644 --- a/core/src/test/java/org/testcontainers/DockerClientFactoryTest.java +++ b/core/src/test/java/org/testcontainers/DockerClientFactoryTest.java @@ -28,11 +28,13 @@ public void runCommandInsideDockerShouldNotFailIfImageDoesNotExistsLocally() { dockFactory.runInsideDocker( imageName, cmd -> cmd.withCmd("sh", "-c", "echo 'SUCCESS'"), - (client, id) -> - client.logContainerCmd(id) + (client, id) -> { + return client + .logContainerCmd(id) .withStdOut(true) .exec(new LogToStringContainerCallback()) - .toString() + .toString(); + } ); } } diff --git a/core/src/test/java/org/testcontainers/DockerRegistryContainer.java b/core/src/test/java/org/testcontainers/DockerRegistryContainer.java index 4018b2b954d..a907c674386 100644 --- a/core/src/test/java/org/testcontainers/DockerRegistryContainer.java +++ b/core/src/test/java/org/testcontainers/DockerRegistryContainer.java @@ -51,22 +51,30 @@ protected void containerIsStarting(InspectContainerResponse containerInfo) { WaitingConsumer waitingConsumer = new WaitingConsumer(); resultCallback.addConsumer(OutputFrame.OutputType.STDERR, waitingConsumer); - dockerClient.logContainerCmd(containerInfo.getId()) + dockerClient + .logContainerCmd(containerInfo.getId()) .withStdErr(true) .withFollowStream(true) .exec(resultCallback); - Pattern pattern = Pattern.compile(".*listening on .*:(\\d+).*", Pattern.DOTALL | Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); - waitingConsumer.waitUntil(it -> { - String s = it.getUtf8String(); - Matcher matcher = pattern.matcher(s); - if (matcher.matches()) { - port.set(Integer.parseInt(matcher.group(1))); - return true; - } else { - return false; - } - }, 10, TimeUnit.SECONDS); + Pattern pattern = Pattern.compile( + ".*listening on .*:(\\d+).*", + Pattern.DOTALL | Pattern.CASE_INSENSITIVE | Pattern.MULTILINE + ); + waitingConsumer.waitUntil( + it -> { + String s = it.getUtf8String(); + Matcher matcher = pattern.matcher(s); + if (matcher.matches()) { + port.set(Integer.parseInt(matcher.group(1))); + return true; + } else { + return false; + } + }, + 10, + TimeUnit.SECONDS + ); } endpoint = getHost() + ":" + port.get(); @@ -87,12 +95,15 @@ public DockerImageName createImage(String originalImage, String tag) { String dummyImageId = client.inspectImageCmd(originalImage).exec().getId(); - DockerImageName imageName = DockerImageName.parse(getEndpoint() + "/" + Base58.randomString(6).toLowerCase()).withTag(tag); + DockerImageName imageName = DockerImageName + .parse(getEndpoint() + "/" + Base58.randomString(6).toLowerCase()) + .withTag(tag); // push the image to the registry client.tagImageCmd(dummyImageId, imageName.asCanonicalNameString(), tag).exec(); - client.pushImageCmd(imageName.asCanonicalNameString()) + client + .pushImageCmd(imageName.asCanonicalNameString()) .exec(new ResultCallback.Adapter<>()) .awaitCompletion(1, TimeUnit.MINUTES); diff --git a/core/src/test/java/org/testcontainers/containers/ContainerStateTest.java b/core/src/test/java/org/testcontainers/containers/ContainerStateTest.java index 52296678fd4..4909e908713 100644 --- a/core/src/test/java/org/testcontainers/containers/ContainerStateTest.java +++ b/core/src/test/java/org/testcontainers/containers/ContainerStateTest.java @@ -5,10 +5,9 @@ import org.junit.runner.RunWith; import org.junit.runners.Parameterized; +import java.util.Collections; import java.util.List; -import static java.util.Collections.emptyList; -import static java.util.Collections.singletonList; import static org.mockito.Mockito.doCallRealMethod; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -18,21 +17,22 @@ public class ContainerStateTest { @Parameterized.Parameters(name = "{0} ({1} -> {2})") public static Object[][] params() { - return new Object[][]{ - new Object[]{"regular mapping", "80:8080/tcp", singletonList(80),}, - new Object[]{"regular mapping with host", "127.0.0.1:80:8080/tcp", singletonList(80),}, - new Object[]{"zero port without host", ":0:8080/tcp", emptyList(),}, - new Object[]{"missing port with host", "0.0.0.0:0:8080/tcp", emptyList(),}, - new Object[]{"zero port (synthetic case)", "0:8080/tcp", emptyList(),}, - new Object[]{"missing port", ":8080/tcp", emptyList(), - } + return new Object[][] { + new Object[] { "regular mapping", "80:8080/tcp", Collections.singletonList(80) }, + new Object[] { "regular mapping with host", "127.0.0.1:80:8080/tcp", Collections.singletonList(80) }, + new Object[] { "zero port without host", ":0:8080/tcp", Collections.emptyList() }, + new Object[] { "missing port with host", "0.0.0.0:0:8080/tcp", Collections.emptyList() }, + new Object[] { "zero port (synthetic case)", "0:8080/tcp", Collections.emptyList() }, + new Object[] { "missing port", ":8080/tcp", Collections.emptyList() }, }; } @Parameterized.Parameter(0) public String name; + @Parameterized.Parameter(1) public String testSet; + @Parameterized.Parameter(2) public List expectedResult; @@ -41,7 +41,7 @@ public void test() { ContainerState containerState = mock(ContainerState.class); doCallRealMethod().when(containerState).getBoundPortNumbers(); - when(containerState.getPortBindings()).thenReturn(singletonList(testSet)); + when(containerState.getPortBindings()).thenReturn(Collections.singletonList(testSet)); List result = containerState.getBoundPortNumbers(); Assertions.assertThat(result).hasSameElementsAs(expectedResult); diff --git a/core/src/test/java/org/testcontainers/containers/DockerComposeContainerWithServicesTest.java b/core/src/test/java/org/testcontainers/containers/DockerComposeContainerWithServicesTest.java index 8927972e642..8f056de98fa 100644 --- a/core/src/test/java/org/testcontainers/containers/DockerComposeContainerWithServicesTest.java +++ b/core/src/test/java/org/testcontainers/containers/DockerComposeContainerWithServicesTest.java @@ -9,17 +9,20 @@ import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; - public class DockerComposeContainerWithServicesTest { - public static final File SIMPLE_COMPOSE_FILE = new File("src/test/resources/compose-scaling-multiple-containers.yml"); - public static final File COMPOSE_FILE_WITH_INLINE_SCALE = new File("src/test/resources/compose-with-inline-scale-test.yml"); + public static final File SIMPLE_COMPOSE_FILE = new File( + "src/test/resources/compose-scaling-multiple-containers.yml" + ); + + public static final File COMPOSE_FILE_WITH_INLINE_SCALE = new File( + "src/test/resources/compose-with-inline-scale-test.yml" + ); @Test public void testDesiredSubsetOfServicesAreStarted() { try ( - DockerComposeContainer compose = new DockerComposeContainer<>(SIMPLE_COMPOSE_FILE) - .withServices("redis") + DockerComposeContainer compose = new DockerComposeContainer<>(SIMPLE_COMPOSE_FILE).withServices("redis") ) { compose.start(); @@ -67,9 +70,7 @@ public void testDesiredSubsetOfSpecifiedOrScaledServicesAreStarted() { @Test public void testAllServicesAreStartedIfNotSpecified() { - try ( - DockerComposeContainer compose = new DockerComposeContainer<>(SIMPLE_COMPOSE_FILE) - ) { + try (DockerComposeContainer compose = new DockerComposeContainer<>(SIMPLE_COMPOSE_FILE)) { compose.start(); verifyStartedContainers(compose, "redis_1", "other_1"); @@ -78,9 +79,7 @@ public void testAllServicesAreStartedIfNotSpecified() { @Test public void testScaleInComposeFileIsRespected() { - try ( - DockerComposeContainer compose = new DockerComposeContainer<>(COMPOSE_FILE_WITH_INLINE_SCALE) - ) { + try (DockerComposeContainer compose = new DockerComposeContainer<>(COMPOSE_FILE_WITH_INLINE_SCALE)) { compose.start(); // the compose file includes `scale: 3` for the redis container @@ -89,17 +88,20 @@ public void testScaleInComposeFileIsRespected() { } private void verifyStartedContainers(final DockerComposeContainer compose, final String... names) { - final List containerNames = compose.listChildContainers().stream() + final List containerNames = compose + .listChildContainers() + .stream() .flatMap(container -> Stream.of(container.getNames())) .collect(Collectors.toList()); - assertEquals("number of running services of docker-compose is the same as length of listOfServices", - names.length, containerNames.size()); + assertEquals( + "number of running services of docker-compose is the same as length of listOfServices", + names.length, + containerNames.size() + ); for (final String expectedName : names) { - final long matches = containerNames.stream() - .filter(foundName -> foundName.endsWith(expectedName)) - .count(); + final long matches = containerNames.stream().filter(foundName -> foundName.endsWith(expectedName)).count(); assertEquals("container with name starting '" + expectedName + "' should be running", 1L, matches); } diff --git a/core/src/test/java/org/testcontainers/containers/DockerComposeFilesTest.java b/core/src/test/java/org/testcontainers/containers/DockerComposeFilesTest.java index c47db0c4b66..6e1630f005f 100644 --- a/core/src/test/java/org/testcontainers/containers/DockerComposeFilesTest.java +++ b/core/src/test/java/org/testcontainers/containers/DockerComposeFilesTest.java @@ -1,25 +1,33 @@ package org.testcontainers.containers; - import com.google.common.collect.Lists; -import java.io.File; import org.assertj.core.api.Assertions; import org.junit.Test; +import java.io.File; + public class DockerComposeFilesTest { + @Test public void shouldGetDependencyImages() { - DockerComposeFiles dockerComposeFiles = new DockerComposeFiles(Lists.newArrayList(new File("src/test/resources/docker-compose-imagename-parsing-v2.yml"))); - Assertions.assertThat(dockerComposeFiles.getDependencyImages()) + DockerComposeFiles dockerComposeFiles = new DockerComposeFiles( + Lists.newArrayList(new File("src/test/resources/docker-compose-imagename-parsing-v2.yml")) + ); + Assertions + .assertThat(dockerComposeFiles.getDependencyImages()) .containsExactlyInAnyOrder("postgres:latest", "redis:latest", "mysql:latest"); } @Test public void shouldGetDependencyImagesWhenOverriding() { DockerComposeFiles dockerComposeFiles = new DockerComposeFiles( - Lists.newArrayList(new File("src/test/resources/docker-compose-imagename-overriding-a.yml"), - new File("src/test/resources/docker-compose-imagename-overriding-b.yml"))); - Assertions.assertThat(dockerComposeFiles.getDependencyImages()) + Lists.newArrayList( + new File("src/test/resources/docker-compose-imagename-overriding-a.yml"), + new File("src/test/resources/docker-compose-imagename-overriding-b.yml") + ) + ); + Assertions + .assertThat(dockerComposeFiles.getDependencyImages()) .containsExactlyInAnyOrder("alpine:3.14", "redis:b", "mysql:b", "aservice:latest"); } } diff --git a/core/src/test/java/org/testcontainers/containers/DockerComposeOverridesTest.java b/core/src/test/java/org/testcontainers/containers/DockerComposeOverridesTest.java index 9e9eb27b509..6207ae5ff45 100644 --- a/core/src/test/java/org/testcontainers/containers/DockerComposeOverridesTest.java +++ b/core/src/test/java/org/testcontainers/containers/DockerComposeOverridesTest.java @@ -8,7 +8,6 @@ import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; import org.rnorth.ducttape.unreliables.Unreliables; -import org.testcontainers.containers.DockerComposeContainer; import java.io.BufferedReader; import java.io.File; @@ -24,14 +23,23 @@ public class DockerComposeOverridesTest { private static final File BASE_COMPOSE_FILE = new File("src/test/resources/docker-compose-base.yml"); + private static final String BASE_ENV_VAR = "bar=base"; - private static final File OVERRIDE_COMPOSE_FILE = new File("src/test/resources/docker-compose-non-default-override.yml"); + + private static final File OVERRIDE_COMPOSE_FILE = new File( + "src/test/resources/docker-compose-non-default-override.yml" + ); + private static final String OVERRIDE_ENV_VAR = "bar=overwritten"; + private static final int SERVICE_PORT = 3000; + private static final String SERVICE_NAME = "alpine_1"; private final boolean localMode; + private final String expectedEnvVar; + private final File[] composeFiles; public DockerComposeOverridesTest(boolean localMode, String expectedEnvVar, File... composeFiles) { @@ -42,18 +50,21 @@ public DockerComposeOverridesTest(boolean localMode, String expectedEnvVar, File @Parameters(name = "{index}: local[{0}], composeFiles[{2}], expectedEnvVar[{1}]") public static Iterable data() { - return Arrays.asList(new Object[][]{ - {true, BASE_ENV_VAR, new File[]{BASE_COMPOSE_FILE}}, - {true, OVERRIDE_ENV_VAR, new File[]{BASE_COMPOSE_FILE, OVERRIDE_COMPOSE_FILE}}, - {false, BASE_ENV_VAR, new File[]{BASE_COMPOSE_FILE}}, - {false, OVERRIDE_ENV_VAR, new File[]{BASE_COMPOSE_FILE, OVERRIDE_COMPOSE_FILE}}, - }); + return Arrays.asList( + new Object[][] { + { true, BASE_ENV_VAR, new File[] { BASE_COMPOSE_FILE } }, + { true, OVERRIDE_ENV_VAR, new File[] { BASE_COMPOSE_FILE, OVERRIDE_COMPOSE_FILE } }, + { false, BASE_ENV_VAR, new File[] { BASE_COMPOSE_FILE } }, + { false, OVERRIDE_ENV_VAR, new File[] { BASE_COMPOSE_FILE, OVERRIDE_COMPOSE_FILE } }, + } + ); } @Before public void setUp() { if (localMode) { - Assumptions.assumeThat(LocalDockerCompose.executableExists()) + Assumptions + .assumeThat(LocalDockerCompose.executableExists()) .as("docker-compose executable exists") .isTrue(); } @@ -61,33 +72,43 @@ public void setUp() { @Test public void test() { - try (DockerComposeContainer compose = - new DockerComposeContainer(composeFiles) - .withLocalCompose(localMode) - .withExposedService(SERVICE_NAME, SERVICE_PORT)) { - + try ( + DockerComposeContainer compose = new DockerComposeContainer(composeFiles) + .withLocalCompose(localMode) + .withExposedService(SERVICE_NAME, SERVICE_PORT) + ) { compose.start(); - BufferedReader br = Unreliables.retryUntilSuccess(10, TimeUnit.SECONDS, () -> { - Uninterruptibles.sleepUninterruptibly(1, TimeUnit.SECONDS); + BufferedReader br = Unreliables.retryUntilSuccess( + 10, + TimeUnit.SECONDS, + () -> { + Uninterruptibles.sleepUninterruptibly(1, TimeUnit.SECONDS); - Socket socket = new Socket(compose.getServiceHost(SERVICE_NAME, SERVICE_PORT), compose.getServicePort(SERVICE_NAME, SERVICE_PORT)); - return new BufferedReader(new InputStreamReader(socket.getInputStream())); - }); + Socket socket = new Socket( + compose.getServiceHost(SERVICE_NAME, SERVICE_PORT), + compose.getServicePort(SERVICE_NAME, SERVICE_PORT) + ); + return new BufferedReader(new InputStreamReader(socket.getInputStream())); + } + ); - Unreliables.retryUntilTrue(10, TimeUnit.SECONDS, () -> { - while (br.ready()) { - String line = br.readLine(); - if (line.contains(expectedEnvVar)) { - pass("Mapped environment variable was found"); - return true; + Unreliables.retryUntilTrue( + 10, + TimeUnit.SECONDS, + () -> { + while (br.ready()) { + String line = br.readLine(); + if (line.contains(expectedEnvVar)) { + pass("Mapped environment variable was found"); + return true; + } } + info("Mapped environment variable was not found yet - process probably not ready"); + Uninterruptibles.sleepUninterruptibly(100, TimeUnit.MILLISECONDS); + return false; } - info("Mapped environment variable was not found yet - process probably not ready"); - Uninterruptibles.sleepUninterruptibly(100, TimeUnit.MILLISECONDS); - return false; - }); + ); } } - } diff --git a/core/src/test/java/org/testcontainers/containers/ExposedHostTest.java b/core/src/test/java/org/testcontainers/containers/ExposedHostTest.java index 38005014662..4afddeaa5dd 100644 --- a/core/src/test/java/org/testcontainers/containers/ExposedHostTest.java +++ b/core/src/test/java/org/testcontainers/containers/ExposedHostTest.java @@ -7,13 +7,13 @@ import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; +import org.testcontainers.TestImages; import org.testcontainers.Testcontainers; import java.io.OutputStream; import java.net.InetSocketAddress; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.testcontainers.TestImages.TINY_IMAGE; public class ExposedHostTest { @@ -22,14 +22,17 @@ public class ExposedHostTest { @BeforeClass public static void setUpClass() throws Exception { server = HttpServer.create(new InetSocketAddress(0), 0); - server.createContext("/", exchange -> { - byte[] content = "Hello World!".getBytes(); - exchange.sendResponseHeaders(200, content.length); - try (OutputStream responseBody = exchange.getResponseBody()) { - responseBody.write(content); - responseBody.flush(); + server.createContext( + "/", + exchange -> { + byte[] content = "Hello World!".getBytes(); + exchange.sendResponseHeaders(200, content.length); + try (OutputStream responseBody = exchange.getResponseBody()) { + responseBody.write(content); + responseBody.flush(); + } } - }); + ); server.start(); } @@ -47,7 +50,7 @@ public void tearDown() { @Test public void testExposedHostAfterContainerIsStarted() { try ( - GenericContainer container = new GenericContainer<>(TINY_IMAGE) + GenericContainer container = new GenericContainer<>(TestImages.TINY_IMAGE) .withCommand("top") .withAccessToHost(true) ) { @@ -60,14 +63,17 @@ public void testExposedHostAfterContainerIsStarted() { @Test public void testExposedHost() throws Exception { Testcontainers.exposeHostPorts(server.getAddress().getPort()); - assertResponse(new GenericContainer<>(TINY_IMAGE).withCommand("top"), server.getAddress().getPort()); + assertResponse(new GenericContainer<>(TestImages.TINY_IMAGE).withCommand("top"), server.getAddress().getPort()); } @Test public void testExposedHostWithNetwork() throws Exception { Testcontainers.exposeHostPorts(server.getAddress().getPort()); try (Network network = Network.newNetwork()) { - assertResponse(new GenericContainer<>(TINY_IMAGE).withNetwork(network).withCommand("top"), server.getAddress().getPort()); + assertResponse( + new GenericContainer<>(TestImages.TINY_IMAGE).withNetwork(network).withCommand("top"), + server.getAddress().getPort() + ); } } @@ -76,8 +82,8 @@ public void testExposedHostPortOnFixedInternalPorts() throws Exception { Testcontainers.exposeHostPorts(ImmutableMap.of(server.getAddress().getPort(), 80)); Testcontainers.exposeHostPorts(ImmutableMap.of(server.getAddress().getPort(), 81)); - assertResponse(new GenericContainer<>(TINY_IMAGE).withCommand("top"), 80); - assertResponse(new GenericContainer<>(TINY_IMAGE).withCommand("top"), 81); + assertResponse(new GenericContainer<>(TestImages.TINY_IMAGE).withCommand("top"), 80); + assertResponse(new GenericContainer<>(TestImages.TINY_IMAGE).withCommand("top"), 81); } @SneakyThrows @@ -85,7 +91,9 @@ protected void assertResponse(GenericContainer container, int port) { try { container.start(); - String response = container.execInContainer("wget", "-O", "-", "http://host.testcontainers.internal:" + port).getStdout(); + String response = container + .execInContainer("wget", "-O", "-", "http://host.testcontainers.internal:" + port) + .getStdout(); assertEquals("received response", "Hello World!", response); } finally { diff --git a/core/src/test/java/org/testcontainers/containers/FailureDetectingExternalResourceTest.java b/core/src/test/java/org/testcontainers/containers/FailureDetectingExternalResourceTest.java index 63993bc24ff..c743fcf6a43 100644 --- a/core/src/test/java/org/testcontainers/containers/FailureDetectingExternalResourceTest.java +++ b/core/src/test/java/org/testcontainers/containers/FailureDetectingExternalResourceTest.java @@ -25,5 +25,4 @@ public void finishedIsCalledForCleanupIfStartingThrows() throws Throwable { verify(res).starting(any()); verify(res).finished(any()); } - } diff --git a/core/src/test/java/org/testcontainers/containers/GenericContainerTest.java b/core/src/test/java/org/testcontainers/containers/GenericContainerTest.java index 0c30d522cd9..fdd8ac1b146 100644 --- a/core/src/test/java/org/testcontainers/containers/GenericContainerTest.java +++ b/core/src/test/java/org/testcontainers/containers/GenericContainerTest.java @@ -17,13 +17,10 @@ import org.rnorth.ducttape.unreliables.Unreliables; import org.testcontainers.DockerClientFactory; import org.testcontainers.TestImages; -import org.testcontainers.containers.startupcheck.OneShotStartupCheckStrategy; import org.testcontainers.containers.startupcheck.StartupCheckStrategy; import org.testcontainers.containers.wait.strategy.AbstractWaitStrategy; -import org.testcontainers.images.builder.Transferable; - -import java.nio.charset.StandardCharsets; import org.testcontainers.images.builder.ImageFromDockerfile; +import org.testcontainers.images.builder.Transferable; import org.testcontainers.utility.MountableFile; import java.util.Arrays; @@ -34,11 +31,9 @@ import java.util.stream.Collectors; import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.hamcrest.CoreMatchers.equalTo; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; import static org.rnorth.visibleassertions.VisibleAssertions.assertThrows; import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; -import static org.testcontainers.TestImages.TINY_IMAGE; public class GenericContainerTest { @@ -52,7 +47,8 @@ public void shouldReportOOMAfterWait() { .withStartupCheckStrategy(new NoopStartupCheckStrategy()) .waitingFor(new WaitForExitedState(ContainerState::getOOMKilled)) .withCreateContainerCmdModifier(it -> { - it.getHostConfig() + it + .getHostConfig() .withMemory(20 * FileUtils.ONE_MB) .withMemorySwappiness(0L) .withMemorySwap(0L) @@ -61,8 +57,7 @@ public void shouldReportOOMAfterWait() { }) .withCommand("sh", "-c", "A='0123456789'; for i in $(seq 0 32); do A=$A$A; done; sleep 10m") ) { - assertThatThrownBy(container::start) - .hasStackTraceContaining("Container crashed with out-of-memory"); + assertThatThrownBy(container::start).hasStackTraceContaining("Container crashed with out-of-memory"); } } @@ -74,8 +69,7 @@ public void shouldReportErrorAfterWait() { .waitingFor(new WaitForExitedState(state -> state.getExitCode() > 0)) .withCommand("sh", "-c", "usleep 100; exit 123") ) { - assertThatThrownBy(container::start) - .hasStackTraceContaining("Container exited with code 123"); + assertThatThrownBy(container::start).hasStackTraceContaining("Container exited with code 123"); } } @@ -88,8 +82,7 @@ public void shouldCopyTransferableAsFile() { .waitingFor(new WaitForExitedState(state -> state.getExitCodeLong() > 0)) .withCommand("sh", "-c", "grep -q test /tmp/test && exit 100") ) { - assertThatThrownBy(container::start) - .hasStackTraceContaining("Container exited with code 100"); + assertThatThrownBy(container::start).hasStackTraceContaining("Container exited with code 100"); } } @@ -98,34 +91,31 @@ public void shouldCopyTransferableAfterMountableFile() { try ( GenericContainer container = new GenericContainer<>(TestImages.TINY_IMAGE) .withStartupCheckStrategy(new NoopStartupCheckStrategy()) - .withCopyFileToContainer( - MountableFile.forClasspathResource("test_copy_to_container.txt"), - "/tmp/test" - ) + .withCopyFileToContainer(MountableFile.forClasspathResource("test_copy_to_container.txt"), "/tmp/test") .withCopyToContainer(Transferable.of("test"), "/tmp/test") .waitingFor(new WaitForExitedState(state -> state.getExitCodeLong() > 0)) .withCommand("sh", "-c", "grep -q test /tmp/test && exit 100") ) { - assertThatThrownBy(container::start) - .hasStackTraceContaining("Container exited with code 100"); + assertThatThrownBy(container::start).hasStackTraceContaining("Container exited with code 100"); } } @Test public void shouldOnlyPublishExposedPorts() { ImageFromDockerfile image = new ImageFromDockerfile("publish-multiple") - .withDockerfileFromBuilder(builder -> + .withDockerfileFromBuilder(builder -> { builder - .from("testcontainers/helloworld:1.1.0") + .from("testcontainers/helloworld:1.1.0") // .expose(8080, 8081) - .build() - ); + .build(); + }); try (GenericContainer container = new GenericContainer<>(image).withExposedPorts(8080)) { container.start(); InspectContainerResponse inspectedContainer = container.getContainerInfo(); - List exposedPorts = Arrays.stream(inspectedContainer.getConfig().getExposedPorts()) + List exposedPorts = Arrays + .stream(inspectedContainer.getConfig().getExposedPorts()) .map(ExposedPort::getPort) .collect(Collectors.toList()); @@ -135,18 +125,14 @@ public void shouldOnlyPublishExposedPorts() { exposedPorts ); - Map hostBindings = inspectedContainer.getHostConfig().getPortBindings().getBindings(); - assertEquals( - "only 1 port is bound on the host (published)", - 1, - hostBindings.size() - ); + Map hostBindings = inspectedContainer + .getHostConfig() + .getPortBindings() + .getBindings(); + assertEquals("only 1 port is bound on the host (published)", 1, hostBindings.size()); Integer mappedPort = container.getMappedPort(8080); - assertTrue( - "port 8080 is bound to a different port on the host", - mappedPort != 8080 - ); + assertTrue("port 8080 is bound to a different port on the host", mappedPort != 8080); assertThrows( "trying to get a non-bound port mapping fails", @@ -160,21 +146,19 @@ public void shouldOnlyPublishExposedPorts() { @Test public void shouldWaitUntilExposedPortIsMapped() { - ImageFromDockerfile image = new ImageFromDockerfile("publish-multiple") - .withDockerfileFromBuilder(builder -> + .withDockerfileFromBuilder(builder -> { builder .from("testcontainers/helloworld:1.1.0") .expose(8080, 8081) // one additional port exposed in image - .build() - ); + .build(); + }); try ( GenericContainer container = new GenericContainer<>(image) .withExposedPorts(8080) .withCreateContainerCmdModifier(it -> it.withExposedPorts(ExposedPort.tcp(8082))) // another port exposed by modifier ) { - container.start(); assertEquals("Only withExposedPort should be exposed", 1, container.getExposedPorts().size()); @@ -200,16 +184,20 @@ static class WaitForExitedState extends AbstractWaitStrategy { @Override @SneakyThrows protected void waitUntilReady() { - Unreliables.retryUntilTrue(5, TimeUnit.SECONDS, () -> { - ContainerState state = waitStrategyTarget.getCurrentContainerInfo().getState(); - - log.debug("Current state: {}", state); - if (!"exited".equalsIgnoreCase(state.getStatus())) { - Thread.sleep(100); - return false; + Unreliables.retryUntilTrue( + 5, + TimeUnit.SECONDS, + () -> { + ContainerState state = waitStrategyTarget.getCurrentContainerInfo().getState(); + + log.debug("Current state: {}", state); + if (!"exited".equalsIgnoreCase(state.getStatus())) { + Thread.sleep(100); + return false; + } + return predicate.test(state); } - return predicate.test(state); - }); + ); throw new IllegalStateException("Nope!"); } diff --git a/core/src/test/java/org/testcontainers/containers/NetworkTest.java b/core/src/test/java/org/testcontainers/containers/NetworkTest.java index a39f0552a5a..d783f95e6d1 100644 --- a/core/src/test/java/org/testcontainers/containers/NetworkTest.java +++ b/core/src/test/java/org/testcontainers/containers/NetworkTest.java @@ -10,7 +10,6 @@ import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; import static org.rnorth.visibleassertions.VisibleAssertions.assertNotEquals; import static org.rnorth.visibleassertions.VisibleAssertions.assertNotNull; -import static org.testcontainers.containers.Network.newNetwork; @RunWith(Enclosed.class) public class NetworkTest { @@ -18,18 +17,18 @@ public class NetworkTest { public static class WithRules { @Rule - public Network network = newNetwork(); + public Network network = Network.newNetwork(); @Rule public GenericContainer foo = new GenericContainer<>(TestImages.TINY_IMAGE) - .withNetwork(network) - .withNetworkAliases("foo") - .withCommand("/bin/sh", "-c", "while true ; do printf 'HTTP/1.1 200 OK\\n\\nyay' | nc -l -p 8080; done"); + .withNetwork(network) + .withNetworkAliases("foo") + .withCommand("/bin/sh", "-c", "while true ; do printf 'HTTP/1.1 200 OK\\n\\nyay' | nc -l -p 8080; done"); @Rule public GenericContainer bar = new GenericContainer<>(TestImages.TINY_IMAGE) - .withNetwork(network) - .withCommand("top"); + .withNetwork(network) + .withCommand("top"); @Test public void testNetworkSupport() throws Exception { @@ -44,16 +43,18 @@ public static class WithoutRules { public void testNetworkSupport() throws Exception { // useCustomNetwork { try ( - Network network = Network.newNetwork(); - - GenericContainer foo = new GenericContainer<>(TestImages.TINY_IMAGE) - .withNetwork(network) - .withNetworkAliases("foo") - .withCommand("/bin/sh", "-c", "while true ; do printf 'HTTP/1.1 200 OK\\n\\nyay' | nc -l -p 8080; done"); - - GenericContainer bar = new GenericContainer<>(TestImages.TINY_IMAGE) - .withNetwork(network) - .withCommand("top") + Network network = Network.newNetwork(); + GenericContainer foo = new GenericContainer<>(TestImages.TINY_IMAGE) + .withNetwork(network) + .withNetworkAliases("foo") + .withCommand( + "/bin/sh", + "-c", + "while true ; do printf 'HTTP/1.1 200 OK\\n\\nyay' | nc -l -p 8080; done" + ); + GenericContainer bar = new GenericContainer<>(TestImages.TINY_IMAGE) + .withNetwork(network) + .withCommand("top") ) { foo.start(); bar.start(); @@ -66,16 +67,12 @@ public void testNetworkSupport() throws Exception { @Test public void testBuilder() { - try ( - Network network = Network.builder() - .driver("macvlan") - .build() - ) { + try (Network network = Network.builder().driver("macvlan").build()) { String id = network.getId(); assertEquals( - "Flag is set", - "macvlan", - DockerClientFactory.instance().client().inspectNetworkCmd().withNetworkId(id).exec().getDriver() + "Flag is set", + "macvlan", + DockerClientFactory.instance().client().inspectNetworkCmd().withNetworkId(id).exec().getDriver() ); } } @@ -83,15 +80,13 @@ public void testBuilder() { @Test public void testModifiers() { try ( - Network network = Network.builder() - .createNetworkCmdModifier(cmd -> cmd.withDriver("macvlan")) - .build() + Network network = Network.builder().createNetworkCmdModifier(cmd -> cmd.withDriver("macvlan")).build() ) { String id = network.getId(); assertEquals( - "Flag is set", - "macvlan", - DockerClientFactory.instance().client().inspectNetworkCmd().withNetworkId(id).exec().getDriver() + "Flag is set", + "macvlan", + DockerClientFactory.instance().client().inspectNetworkCmd().withNetworkId(id).exec().getDriver() ); } } @@ -101,16 +96,22 @@ public void testReusability() { try (Network network = Network.newNetwork()) { String firstId = network.getId(); assertNotNull( - "Network exists", - DockerClientFactory.instance().client().inspectNetworkCmd().withNetworkId(firstId).exec() + "Network exists", + DockerClientFactory.instance().client().inspectNetworkCmd().withNetworkId(firstId).exec() ); network.close(); assertNotEquals( - "New network created", - firstId, - DockerClientFactory.instance().client().inspectNetworkCmd().withNetworkId(network.getId()).exec().getId() + "New network created", + firstId, + DockerClientFactory + .instance() + .client() + .inspectNetworkCmd() + .withNetworkId(network.getId()) + .exec() + .getId() ); } } diff --git a/core/src/test/java/org/testcontainers/containers/ParsedDockerComposeFileValidationTest.java b/core/src/test/java/org/testcontainers/containers/ParsedDockerComposeFileValidationTest.java index ef70d06ada1..61955de98ba 100644 --- a/core/src/test/java/org/testcontainers/containers/ParsedDockerComposeFileValidationTest.java +++ b/core/src/test/java/org/testcontainers/containers/ParsedDockerComposeFileValidationTest.java @@ -6,9 +6,9 @@ import org.junit.Test; import java.io.File; +import java.util.Collections; -import static java.util.Collections.emptyList; -import static java.util.Collections.emptyMap; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.assertj.core.api.Assertions.entry; public class ParsedDockerComposeFileValidationTest { @@ -16,8 +16,7 @@ public class ParsedDockerComposeFileValidationTest { @Test public void shouldValidate() { File file = new File("src/test/resources/docker-compose-container-name-v1.yml"); - Assertions - .assertThatThrownBy(() -> { + assertThatThrownBy(() -> { new ParsedDockerComposeFile(file); }) .hasMessageContaining(file.getAbsolutePath()) @@ -26,25 +25,23 @@ public void shouldValidate() { @Test public void shouldRejectContainerNameV1() { - Assertions - .assertThatThrownBy(() -> { - new ParsedDockerComposeFile(ImmutableMap.of( - "redis", ImmutableMap.of("container_name", "redis") - )); + assertThatThrownBy(() -> { + new ParsedDockerComposeFile(ImmutableMap.of("redis", ImmutableMap.of("container_name", "redis"))); }) .hasMessageContaining("'container_name' property set for service 'redis'"); } @Test public void shouldRejectContainerNameV2() { - Assertions - .assertThatThrownBy(() -> { - new ParsedDockerComposeFile(ImmutableMap.of( - "version", "2", - "services", ImmutableMap.of( - "redis", ImmutableMap.of("container_name", "redis") + assertThatThrownBy(() -> { + new ParsedDockerComposeFile( + ImmutableMap.of( + "version", + "2", + "services", + ImmutableMap.of("redis", ImmutableMap.of("container_name", "redis")) ) - )); + ); }) .hasMessageContaining("'container_name' property set for service 'redis'"); } @@ -52,35 +49,29 @@ public void shouldRejectContainerNameV2() { @Test public void shouldIgnoreUnknownStructure() { // Everything is a list - new ParsedDockerComposeFile(emptyMap()); + new ParsedDockerComposeFile(Collections.emptyMap()); // services is not a map but List - new ParsedDockerComposeFile(ImmutableMap.of( - "version", "2", - "services", emptyList() - )); + new ParsedDockerComposeFile(ImmutableMap.of("version", "2", "services", Collections.emptyList())); // services is not a collection - new ParsedDockerComposeFile(ImmutableMap.of( - "version", "2", - "services", true - )); + new ParsedDockerComposeFile(ImmutableMap.of("version", "2", "services", true)); // no services while version is defined - new ParsedDockerComposeFile(ImmutableMap.of( - "version", "9000" - )); + new ParsedDockerComposeFile(ImmutableMap.of("version", "9000")); } @Test public void shouldObtainImageNamesV1() { File file = new File("src/test/resources/docker-compose-imagename-parsing-v1.yml"); ParsedDockerComposeFile parsedFile = new ParsedDockerComposeFile(file); - Assertions.assertThat(parsedFile.getServiceNameToImageNames()) + Assertions + .assertThat(parsedFile.getServiceNameToImageNames()) .contains( entry("mysql", Sets.newHashSet("mysql")), entry("redis", Sets.newHashSet("redis")), - entry("custom", Sets.newHashSet("postgres"))) + entry("custom", Sets.newHashSet("postgres")) + ) .as("all defined images are found"); // redis, mysql from compose file, postgres from Dockerfile build } @@ -88,11 +79,13 @@ public void shouldObtainImageNamesV1() { public void shouldObtainImageNamesV2() { File file = new File("src/test/resources/docker-compose-imagename-parsing-v2.yml"); ParsedDockerComposeFile parsedFile = new ParsedDockerComposeFile(file); - Assertions.assertThat(parsedFile.getServiceNameToImageNames()) + Assertions + .assertThat(parsedFile.getServiceNameToImageNames()) .contains( entry("mysql", Sets.newHashSet("mysql")), entry("redis", Sets.newHashSet("redis")), - entry("custom", Sets.newHashSet("postgres"))) + entry("custom", Sets.newHashSet("postgres")) + ) .as("all defined images are found"); } @@ -100,11 +93,13 @@ public void shouldObtainImageNamesV2() { public void shouldObtainImageFromDockerfileBuild() { File file = new File("src/test/resources/docker-compose-imagename-parsing-dockerfile.yml"); ParsedDockerComposeFile parsedFile = new ParsedDockerComposeFile(file); - Assertions.assertThat(parsedFile.getServiceNameToImageNames()) + Assertions + .assertThat(parsedFile.getServiceNameToImageNames()) .contains( entry("mysql", Sets.newHashSet("mysql")), entry("redis", Sets.newHashSet("redis")), - entry("custom", Sets.newHashSet("alpine:3.14"))) + entry("custom", Sets.newHashSet("alpine:3.14")) + ) .as("all defined images are found"); // r/ redis, mysql from compose file, alpine:3.14 from Dockerfile build } @@ -112,11 +107,13 @@ public void shouldObtainImageFromDockerfileBuild() { public void shouldObtainImageFromDockerfileBuildWithContext() { File file = new File("src/test/resources/docker-compose-imagename-parsing-dockerfile-with-context.yml"); ParsedDockerComposeFile parsedFile = new ParsedDockerComposeFile(file); - Assertions.assertThat(parsedFile.getServiceNameToImageNames()) + Assertions + .assertThat(parsedFile.getServiceNameToImageNames()) .contains( entry("mysql", Sets.newHashSet("mysql")), entry("redis", Sets.newHashSet("redis")), - entry("custom", Sets.newHashSet("alpine:3.14"))) + entry("custom", Sets.newHashSet("alpine:3.14")) + ) .as("all defined images are found"); // redis, mysql from compose file, alpine:3.14 from Dockerfile build } } diff --git a/core/src/test/java/org/testcontainers/containers/ReusabilityUnitTests.java b/core/src/test/java/org/testcontainers/containers/ReusabilityUnitTests.java index 9aad97d7e7b..7aafb0149bd 100644 --- a/core/src/test/java/org/testcontainers/containers/ReusabilityUnitTests.java +++ b/core/src/test/java/org/testcontainers/containers/ReusabilityUnitTests.java @@ -27,9 +27,9 @@ import org.mockito.stubbing.Answer; import org.rnorth.visibleassertions.VisibleAssertions; import org.testcontainers.DockerClientFactory; +import org.testcontainers.TestImages; import org.testcontainers.containers.startupcheck.StartupCheckStrategy; import org.testcontainers.containers.wait.strategy.AbstractWaitStrategy; -import org.testcontainers.images.builder.Transferable; import org.testcontainers.utility.MockTestcontainersConfigurationRule; import org.testcontainers.utility.MountableFile; import org.testcontainers.utility.TestcontainersConfiguration; @@ -53,7 +53,6 @@ import static org.assertj.core.api.Assumptions.assumeThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.when; -import static org.testcontainers.TestImages.TINY_IMAGE; @RunWith(Enclosed.class) public class ReusabilityUnitTests { @@ -66,8 +65,8 @@ public static class CanBeReusedTest { @Parameterized.Parameters(name = "{0}") public static Object[][] data() { return new Object[][] { - { "generic", new GenericContainer<>(TINY_IMAGE), true }, - { "anonymous generic", new GenericContainer(TINY_IMAGE) {}, true }, + { "generic", new GenericContainer<>(TestImages.TINY_IMAGE), true }, + { "anonymous generic", new GenericContainer(TestImages.TINY_IMAGE) {}, true }, { "custom", new CustomContainer(), true }, { "anonymous custom", new CustomContainer() {}, true }, { "custom with containerIsCreated", new CustomContainerWithContainerIsCreated(), false }, @@ -90,15 +89,17 @@ public void shouldBeReusable() { } static class CustomContainer extends GenericContainer { + CustomContainer() { - super(TINY_IMAGE); + super(TestImages.TINY_IMAGE); } } - static class CustomContainerWithContainerIsCreated extends GenericContainer { + static class CustomContainerWithContainerIsCreated + extends GenericContainer { CustomContainerWithContainerIsCreated() { - super(TINY_IMAGE); + super(TestImages.TINY_IMAGE); } @Override @@ -114,29 +115,30 @@ public static class HooksTest extends AbstractReusabilityTest { List script = new ArrayList<>(); - GenericContainer container = makeReusable(new GenericContainer(TINY_IMAGE) { - - @Override - protected boolean canBeReused() { - // Because we override "containerIsCreated" - return true; - } + GenericContainer container = makeReusable( + new GenericContainer(TestImages.TINY_IMAGE) { + @Override + protected boolean canBeReused() { + // Because we override "containerIsCreated" + return true; + } - @Override - protected void containerIsCreated(String containerId) { - script.add("containerIsCreated"); - } + @Override + protected void containerIsCreated(String containerId) { + script.add("containerIsCreated"); + } - @Override - protected void containerIsStarting(InspectContainerResponse containerInfo, boolean reused) { - script.add("containerIsStarting(reused=" + reused + ")"); - } + @Override + protected void containerIsStarting(InspectContainerResponse containerInfo, boolean reused) { + script.add("containerIsStarting(reused=" + reused + ")"); + } - @Override - protected void containerIsStarted(InspectContainerResponse containerInfo, boolean reused) { - script.add("containerIsStarted(reused=" + reused + ")"); + @Override + protected void containerIsStarted(InspectContainerResponse containerInfo, boolean reused) { + script.add("containerIsStarted(reused=" + reused + ")"); + } } - }); + ); @Test public void shouldSetLabelsIfEnvironmentDoesNotSupportReuse() { @@ -149,11 +151,12 @@ public void shouldSetLabelsIfEnvironmentDoesNotSupportReuse() { when(client.inspectContainerCmd(containerId)).then(inspectContainerAnswer()); container.start(); - assertThat(script).containsExactly( - "containerIsCreated", - "containerIsStarting(reused=false)", - "containerIsStarted(reused=false)" - ); + assertThat(script) + .containsExactly( + "containerIsCreated", + "containerIsStarting(reused=false)", + "containerIsStarted(reused=false)" + ); } @Test @@ -166,10 +169,7 @@ public void shouldCallHookIfReused() { when(client.inspectContainerCmd(existingContainerId)).then(inspectContainerAnswer()); container.start(); - assertThat(script).containsExactly( - "containerIsStarting(reused=true)", - "containerIsStarted(reused=true)" - ); + assertThat(script).containsExactly("containerIsStarting(reused=true)", "containerIsStarted(reused=true)"); } @Test @@ -181,11 +181,12 @@ public void shouldNotCallHookIfNotReused() { when(client.inspectContainerCmd(containerId)).then(inspectContainerAnswer()); container.start(); - assertThat(script).containsExactly( - "containerIsCreated", - "containerIsStarting(reused=false)", - "containerIsStarted(reused=false)" - ); + assertThat(script) + .containsExactly( + "containerIsCreated", + "containerIsStarting(reused=false)", + "containerIsStarted(reused=false)" + ); } } @@ -193,12 +194,14 @@ public void shouldNotCallHookIfNotReused() { @FieldDefaults(makeFinal = true) public static class HashTest extends AbstractReusabilityTest { - protected GenericContainer container = makeReusable(new GenericContainer(TINY_IMAGE) { - @Override - public void copyFileToContainer(MountableFile mountableFile, String containerPath) { - // NOOP + protected GenericContainer container = makeReusable( + new GenericContainer(TestImages.TINY_IMAGE) { + @Override + public void copyFileToContainer(MountableFile mountableFile, String containerPath) { + // NOOP + } } - }); + ); @Test public void shouldStartIfListReturnsEmpty() { @@ -260,8 +263,7 @@ public void shouldSetCopiedFilesHashLabel() { container.start(); assertThat(commandRef).isNotNull(); - assertThat(commandRef.get().getLabels()) - .containsKeys(GenericContainer.COPIED_FILES_HASH_LABEL); + assertThat(commandRef.get().getLabels()).containsKeys(GenericContainer.COPIED_FILES_HASH_LABEL); } @Test @@ -292,15 +294,20 @@ public void shouldHashCopiedFiles() { ); container.start(); - assertThat(commandRef.get().getLabels()).hasEntrySatisfying(GenericContainer.COPIED_FILES_HASH_LABEL, newHash -> { - assertThat(newHash).as("new hash").isNotEqualTo(oldHash); - }); + assertThat(commandRef.get().getLabels()) + .hasEntrySatisfying( + GenericContainer.COPIED_FILES_HASH_LABEL, + newHash -> { + assertThat(newHash).as("new hash").isNotEqualTo(oldHash); + } + ); } } @RunWith(Parameterized.class) @FieldDefaults(makeFinal = true) public static class CopyFilesHashTest { + private final TestStrategy strategy; interface TestStrategy { @@ -310,6 +317,7 @@ interface TestStrategy { } private static class MountableFileTestStrategy implements TestStrategy { + private final GenericContainer container; private MountableFileTestStrategy(GenericContainer container) { @@ -328,6 +336,7 @@ public void clear() { } private static class TransferableTestStrategy implements TestStrategy { + private final GenericContainer container; private TransferableTestStrategy(GenericContainer container) { @@ -350,8 +359,7 @@ public static List, TestStrategy>> strategies() { return Arrays.asList(MountableFileTestStrategy::new, TransferableTestStrategy::new); } - - GenericContainer container = new GenericContainer<>(TINY_IMAGE); + GenericContainer container = new GenericContainer<>(TestImages.TINY_IMAGE); public CopyFilesHashTest(Function, TestStrategy> strategyFactory) { this.strategy = strategyFactory.apply(container); @@ -499,7 +507,7 @@ public void folderPermissions() throws Exception { } @FieldDefaults(makeFinal = true) - public static abstract class AbstractReusabilityTest { + public abstract static class AbstractReusabilityTest { @Rule public MockTestcontainersConfigurationRule configurationMock = new MockTestcontainersConfigurationRule(); @@ -509,25 +517,26 @@ public static abstract class AbstractReusabilityTest { protected > T makeReusable(T container) { container.dockerClient = client; container.withNetworkMode("none"); // to disable the port forwarding - container.withStartupCheckStrategy(new StartupCheckStrategy() { - - @Override - public boolean waitUntilStartupSuccessful(DockerClient dockerClient, String containerId) { - // Skip DockerClient rate limiter - return true; + container.withStartupCheckStrategy( + new StartupCheckStrategy() { + @Override + public boolean waitUntilStartupSuccessful(DockerClient dockerClient, String containerId) { + // Skip DockerClient rate limiter + return true; + } + + @Override + public StartupStatus checkStartupState(DockerClient dockerClient, String containerId) { + return StartupStatus.SUCCESSFUL; + } } - - @Override - public StartupStatus checkStartupState(DockerClient dockerClient, String containerId) { - return StartupStatus.SUCCESSFUL; - } - }); - container.waitingFor(new AbstractWaitStrategy() { - @Override - protected void waitUntilReady() { - + ); + container.waitingFor( + new AbstractWaitStrategy() { + @Override + protected void waitUntilReady() {} } - }); + ); container.withReuse(true); return container; } @@ -539,12 +548,11 @@ protected String randomContainerId() { protected Answer listContainersAnswer(String... ids) { return invocation -> { ListContainersCmd.Exec exec = command -> { - return new ObjectMapper().convertValue( - Stream.of(ids) - .map(id -> Collections.singletonMap("Id", id)) - .collect(Collectors.toList()), - new TypeReference>() {} - ); + return new ObjectMapper() + .convertValue( + Stream.of(ids).map(id -> Collections.singletonMap("Id", id)).collect(Collectors.toList()), + new TypeReference>() {} + ); }; return new ListContainersCmdImpl(exec); }; @@ -554,7 +562,10 @@ protected Answer createContainerAnswer(String containerId) { return createContainerAnswer(containerId, command -> {}); } - protected Answer createContainerAnswer(String containerId, Consumer cmdConsumer) { + protected Answer createContainerAnswer( + String containerId, + Consumer cmdConsumer + ) { return invocation -> { CreateContainerCmd.Exec exec = command -> { cmdConsumer.accept(command); @@ -578,7 +589,10 @@ protected Answer startContainerAnswer() { protected Answer inspectContainerAnswer() { return invocation -> { InspectContainerCmd.Exec exec = command -> { - InspectContainerResponse stubResponse = Mockito.mock(InspectContainerResponse.class, Answers.RETURNS_DEEP_STUBS); + InspectContainerResponse stubResponse = Mockito.mock( + InspectContainerResponse.class, + Answers.RETURNS_DEEP_STUBS + ); when(stubResponse.getNetworkSettings().getPorts().getBindings()).thenReturn(Collections.emptyMap()); return stubResponse; }; diff --git a/core/src/test/java/org/testcontainers/containers/output/ContainerLogsTest.java b/core/src/test/java/org/testcontainers/containers/output/ContainerLogsTest.java index 05ef7373293..a860b7802ec 100644 --- a/core/src/test/java/org/testcontainers/containers/output/ContainerLogsTest.java +++ b/core/src/test/java/org/testcontainers/containers/output/ContainerLogsTest.java @@ -2,14 +2,12 @@ import org.junit.Ignore; import org.junit.Test; +import org.testcontainers.TestImages; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.startupcheck.OneShotStartupCheckStrategy; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; -import static org.testcontainers.TestImages.ALPINE_IMAGE; -import static org.testcontainers.containers.output.OutputFrame.OutputType.STDERR; -import static org.testcontainers.containers.output.OutputFrame.OutputType.STDOUT; public class ContainerLogsTest { @@ -43,7 +41,7 @@ public void getLogsReturnsStdOutToDate() { container.start(); // docsGetStdOut { - final String logs = container.getLogs(STDOUT); + final String logs = container.getLogs(OutputFrame.OutputType.STDOUT); // } assertTrue("stdout is reflected in the returned logs", logs.contains("stdout")); } @@ -55,7 +53,7 @@ public void getLogsReturnsStdErrToDate() { container.start(); // docsGetStdErr { - final String logs = container.getLogs(STDERR); + final String logs = container.getLogs(OutputFrame.OutputType.STDERR); // } assertTrue("stderr is reflected in the returned logs", logs.contains("stderr")); } @@ -68,19 +66,18 @@ public void getLogsForLongRunningContainer() throws InterruptedException { Thread.sleep(1000L); - final String logs = container.getLogs(STDOUT); + final String logs = container.getLogs(OutputFrame.OutputType.STDOUT); assertTrue("stdout is reflected in the returned logs for a running container", logs.contains("seq=0")); } } private static GenericContainer shortLivedContainer() { - return new GenericContainer<>(ALPINE_IMAGE) + return new GenericContainer<>(TestImages.ALPINE_IMAGE) .withCommand("/bin/sh", "-c", "echo -n 'stdout' && echo -n 'stderr' 1>&2") .withStartupCheckStrategy(new OneShotStartupCheckStrategy()); } private static GenericContainer longRunningContainer() { - return new GenericContainer<>(ALPINE_IMAGE) - .withCommand("ping -c 100 127.0.0.1"); + return new GenericContainer<>(TestImages.ALPINE_IMAGE).withCommand("ping -c 100 127.0.0.1"); } } diff --git a/core/src/test/java/org/testcontainers/containers/output/FrameConsumerResultCallbackTest.java b/core/src/test/java/org/testcontainers/containers/output/FrameConsumerResultCallbackTest.java index 325cccdd378..5282712b733 100644 --- a/core/src/test/java/org/testcontainers/containers/output/FrameConsumerResultCallbackTest.java +++ b/core/src/test/java/org/testcontainers/containers/output/FrameConsumerResultCallbackTest.java @@ -12,17 +12,19 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; -import static org.testcontainers.containers.output.OutputFrame.OutputType; public class FrameConsumerResultCallbackTest { - private static final String FRAME_PAYLOAD = "\u001B[0;32mТест1\u001B[0m\n\u001B[1;33mTest2\u001B[0m\n\u001B[0;31mTest3\u001B[0m"; + + private static final String FRAME_PAYLOAD = + "\u001B[0;32mТест1\u001B[0m\n\u001B[1;33mTest2\u001B[0m\n\u001B[0;31mTest3\u001B[0m"; + private static final String LOG_RESULT = "Тест1\nTest2\nTest3"; @Test public void passStderrFrameWithoutColors() { FrameConsumerResultCallback callback = new FrameConsumerResultCallback(); ToStringConsumer consumer = new ToStringConsumer(); - callback.addConsumer(OutputType.STDERR, consumer); + callback.addConsumer(OutputFrame.OutputType.STDERR, consumer); callback.onNext(new Frame(StreamType.STDERR, FRAME_PAYLOAD.getBytes())); assertEquals(LOG_RESULT, consumer.toUtf8String()); } @@ -31,7 +33,7 @@ public void passStderrFrameWithoutColors() { public void passStderrFrameWithColors() { FrameConsumerResultCallback callback = new FrameConsumerResultCallback(); ToStringConsumer consumer = new ToStringConsumer().withRemoveAnsiCodes(false); - callback.addConsumer(OutputType.STDERR, consumer); + callback.addConsumer(OutputFrame.OutputType.STDERR, consumer); callback.onNext(new Frame(StreamType.STDERR, FRAME_PAYLOAD.getBytes())); assertEquals(FRAME_PAYLOAD, consumer.toUtf8String()); } @@ -40,7 +42,7 @@ public void passStderrFrameWithColors() { public void passStdoutFrameWithoutColors() { FrameConsumerResultCallback callback = new FrameConsumerResultCallback(); ToStringConsumer consumer = new ToStringConsumer(); - callback.addConsumer(OutputType.STDOUT, consumer); + callback.addConsumer(OutputFrame.OutputType.STDOUT, consumer); callback.onNext(new Frame(StreamType.STDOUT, FRAME_PAYLOAD.getBytes())); assertEquals(LOG_RESULT, consumer.toUtf8String()); } @@ -49,7 +51,7 @@ public void passStdoutFrameWithoutColors() { public void passStdoutFrameWithColors() { FrameConsumerResultCallback callback = new FrameConsumerResultCallback(); ToStringConsumer consumer = new ToStringConsumer().withRemoveAnsiCodes(false); - callback.addConsumer(OutputType.STDOUT, consumer); + callback.addConsumer(OutputFrame.OutputType.STDOUT, consumer); callback.onNext(new Frame(StreamType.STDOUT, FRAME_PAYLOAD.getBytes())); assertEquals(FRAME_PAYLOAD, consumer.toUtf8String()); } @@ -58,7 +60,7 @@ public void passStdoutFrameWithColors() { public void basicConsumer() { FrameConsumerResultCallback callback = new FrameConsumerResultCallback(); BasicConsumer consumer = new BasicConsumer(); - callback.addConsumer(OutputType.STDOUT, consumer); + callback.addConsumer(OutputFrame.OutputType.STDOUT, consumer); callback.onNext(new Frame(StreamType.STDOUT, FRAME_PAYLOAD.getBytes())); assertEquals(LOG_RESULT, consumer.toString()); } @@ -67,7 +69,7 @@ public void basicConsumer() { public void passStdoutNull() { FrameConsumerResultCallback callback = new FrameConsumerResultCallback(); ToStringConsumer consumer = new ToStringConsumer().withRemoveAnsiCodes(false); - callback.addConsumer(OutputType.STDOUT, consumer); + callback.addConsumer(OutputFrame.OutputType.STDOUT, consumer); callback.onNext(new Frame(StreamType.STDOUT, null)); assertEquals("", consumer.toUtf8String()); } @@ -77,7 +79,7 @@ public void passStdoutEmptyLine() { String payload = ""; FrameConsumerResultCallback callback = new FrameConsumerResultCallback(); ToStringConsumer consumer = new ToStringConsumer().withRemoveAnsiCodes(false); - callback.addConsumer(OutputType.STDOUT, consumer); + callback.addConsumer(OutputFrame.OutputType.STDOUT, consumer); callback.onNext(new Frame(StreamType.STDOUT, payload.getBytes())); assertEquals(payload, consumer.toUtf8String()); } @@ -87,7 +89,7 @@ public void passStdoutSingleLine() { String payload = "Test"; FrameConsumerResultCallback callback = new FrameConsumerResultCallback(); ToStringConsumer consumer = new ToStringConsumer().withRemoveAnsiCodes(false); - callback.addConsumer(OutputType.STDOUT, consumer); + callback.addConsumer(OutputFrame.OutputType.STDOUT, consumer); callback.onNext(new Frame(StreamType.STDOUT, payload.getBytes())); assertEquals(payload, consumer.toUtf8String()); } @@ -97,7 +99,7 @@ public void passStdoutSingleLineWithNewline() { String payload = "Test\n"; FrameConsumerResultCallback callback = new FrameConsumerResultCallback(); ToStringConsumer consumer = new ToStringConsumer().withRemoveAnsiCodes(false); - callback.addConsumer(OutputType.STDOUT, consumer); + callback.addConsumer(OutputFrame.OutputType.STDOUT, consumer); callback.onNext(new Frame(StreamType.STDOUT, payload.getBytes())); assertEquals(payload, consumer.toUtf8String()); } @@ -106,38 +108,90 @@ public void passStdoutSingleLineWithNewline() { public void passRawFrameWithoutColors() throws TimeoutException, IOException { FrameConsumerResultCallback callback = new FrameConsumerResultCallback(); WaitingConsumer waitConsumer = new WaitingConsumer(); - callback.addConsumer(OutputType.STDOUT, waitConsumer); + callback.addConsumer(OutputFrame.OutputType.STDOUT, waitConsumer); callback.onNext(new Frame(StreamType.RAW, FRAME_PAYLOAD.getBytes())); - waitConsumer.waitUntil(frame -> frame.getType() == OutputType.STDOUT && frame.getUtf8String().equals("Test2"), 1, TimeUnit.SECONDS); - waitConsumer.waitUntil(frame -> frame.getType() == OutputType.STDOUT && frame.getUtf8String().equals("Тест1"), 1, TimeUnit.SECONDS); + waitConsumer.waitUntil( + frame -> frame.getType() == OutputFrame.OutputType.STDOUT && frame.getUtf8String().equals("Test2"), + 1, + TimeUnit.SECONDS + ); + waitConsumer.waitUntil( + frame -> frame.getType() == OutputFrame.OutputType.STDOUT && frame.getUtf8String().equals("Тест1"), + 1, + TimeUnit.SECONDS + ); Exception exception = null; try { - waitConsumer.waitUntil(frame -> frame.getType() == OutputType.STDOUT && frame.getUtf8String().equals("Test3"), 1, TimeUnit.SECONDS); + waitConsumer.waitUntil( + frame -> frame.getType() == OutputFrame.OutputType.STDOUT && frame.getUtf8String().equals("Test3"), + 1, + TimeUnit.SECONDS + ); } catch (Exception e) { exception = e; } assertTrue(exception instanceof TimeoutException); callback.close(); - waitConsumer.waitUntil(frame -> frame.getType() == OutputType.STDOUT && frame.getUtf8String().equals("Test3"), 1, TimeUnit.SECONDS); + waitConsumer.waitUntil( + frame -> frame.getType() == OutputFrame.OutputType.STDOUT && frame.getUtf8String().equals("Test3"), + 1, + TimeUnit.SECONDS + ); } @Test public void passRawFrameWithColors() throws TimeoutException, IOException { FrameConsumerResultCallback callback = new FrameConsumerResultCallback(); WaitingConsumer waitConsumer = new WaitingConsumer().withRemoveAnsiCodes(false); - callback.addConsumer(OutputType.STDOUT, waitConsumer); + callback.addConsumer(OutputFrame.OutputType.STDOUT, waitConsumer); callback.onNext(new Frame(StreamType.RAW, FRAME_PAYLOAD.getBytes())); - waitConsumer.waitUntil(frame -> frame.getType() == OutputType.STDOUT && frame.getUtf8String().equals("\u001B[1;33mTest2\u001B[0m"), 1, TimeUnit.SECONDS); - waitConsumer.waitUntil(frame -> frame.getType() == OutputType.STDOUT && frame.getUtf8String().equals("\u001B[0;32mТест1\u001B[0m"), 1, TimeUnit.SECONDS); + waitConsumer.waitUntil( + frame -> { + return ( + frame.getType() == OutputFrame.OutputType.STDOUT && + frame.getUtf8String().equals("\u001B[1;33mTest2\u001B[0m") + ); + }, + 1, + TimeUnit.SECONDS + ); + waitConsumer.waitUntil( + frame -> { + return ( + frame.getType() == OutputFrame.OutputType.STDOUT && + frame.getUtf8String().equals("\u001B[0;32mТест1\u001B[0m") + ); + }, + 1, + TimeUnit.SECONDS + ); Exception exception = null; try { - waitConsumer.waitUntil(frame -> frame.getType() == OutputType.STDOUT && frame.getUtf8String().equals("\u001B[0;31mTest3\u001B[0m"), 1, TimeUnit.SECONDS); + waitConsumer.waitUntil( + frame -> { + return ( + frame.getType() == OutputFrame.OutputType.STDOUT && + frame.getUtf8String().equals("\u001B[0;31mTest3\u001B[0m") + ); + }, + 1, + TimeUnit.SECONDS + ); } catch (Exception e) { exception = e; } assertTrue(exception instanceof TimeoutException); callback.close(); - waitConsumer.waitUntil(frame -> frame.getType() == OutputType.STDOUT && frame.getUtf8String().equals("\u001B[0;31mTest3\u001B[0m"), 1, TimeUnit.SECONDS); + waitConsumer.waitUntil( + frame -> { + return ( + frame.getType() == OutputFrame.OutputType.STDOUT && + frame.getUtf8String().equals("\u001B[0;31mTest3\u001B[0m") + ); + }, + 1, + TimeUnit.SECONDS + ); } @Test @@ -150,7 +204,7 @@ public void reconstructBreakedUnicode() throws IOException { System.arraycopy(payloadBytes, bytes1.length, bytes2, 0, bytes2.length); FrameConsumerResultCallback callback = new FrameConsumerResultCallback(); ToStringConsumer consumer = new ToStringConsumer().withRemoveAnsiCodes(false); - callback.addConsumer(OutputType.STDOUT, consumer); + callback.addConsumer(OutputFrame.OutputType.STDOUT, consumer); callback.onNext(new Frame(StreamType.RAW, bytes1)); callback.onNext(new Frame(StreamType.RAW, bytes2)); callback.close(); @@ -158,7 +212,9 @@ public void reconstructBreakedUnicode() throws IOException { } private static class BasicConsumer implements Consumer { + private boolean firstLine = true; + private StringBuilder input = new StringBuilder(); @Override diff --git a/core/src/test/java/org/testcontainers/containers/output/ToStringConsumerTest.java b/core/src/test/java/org/testcontainers/containers/output/ToStringConsumerTest.java index 1760a8031b2..08973f2d601 100644 --- a/core/src/test/java/org/testcontainers/containers/output/ToStringConsumerTest.java +++ b/core/src/test/java/org/testcontainers/containers/output/ToStringConsumerTest.java @@ -1,7 +1,5 @@ package org.testcontainers.containers.output; -import static org.assertj.core.api.Assertions.assertThat; - import org.apache.commons.lang3.RandomStringUtils; import org.assertj.core.api.Assertions; import org.junit.Test; @@ -9,12 +7,14 @@ import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.startupcheck.OneShotStartupCheckStrategy; +import static org.assertj.core.api.Assertions.assertThat; + public class ToStringConsumerTest { private static final String LARGE_PAYLOAD; static { - StringBuilder builder = new StringBuilder(10_003 * 10);; + StringBuilder builder = new StringBuilder(10_003 * 10); for (int i = 0; i < 10; i++) { builder.append(' ').append(i).append(RandomStringUtils.randomAlphabetic(10000)); } @@ -29,9 +29,7 @@ public void newlines_are_not_added_to_exec_output() throws Exception { container.start(); ExecResult build = container.execInContainer("echo", "-n", LARGE_PAYLOAD); - Assertions.assertThat(build.getStdout()) - .doesNotContain("\n") - .isEqualTo(LARGE_PAYLOAD); + Assertions.assertThat(build.getStdout()).doesNotContain("\n").isEqualTo(LARGE_PAYLOAD); } } @@ -39,18 +37,13 @@ public void newlines_are_not_added_to_exec_output() throws Exception { public void newlines_are_not_added_to_exec_output_with_tty() throws Exception { try (GenericContainer container = new GenericContainer<>("alpine:3.14")) { container.withCreateContainerCmdModifier(cmd -> { - cmd - .withAttachStdin(true) - .withStdinOpen(true) - .withTty(true); + cmd.withAttachStdin(true).withStdinOpen(true).withTty(true); }); container.withCommand("sleep", "2m"); container.start(); ExecResult build = container.execInContainer("echo", "-n", LARGE_PAYLOAD); - assertThat(build.getStdout()) - .isEqualTo(LARGE_PAYLOAD) - .doesNotContain("\n"); + assertThat(build.getStdout()).isEqualTo(LARGE_PAYLOAD).doesNotContain("\n"); } } @@ -63,9 +56,7 @@ public void newlines_are_not_added_to_container_output() { container.getDockerClient().waitContainerCmd(container.getContainerId()).start().awaitStatusCode(); - assertThat(container.getLogs()) - .isEqualTo(LARGE_PAYLOAD) - .doesNotContain("\n"); + assertThat(container.getLogs()).isEqualTo(LARGE_PAYLOAD).doesNotContain("\n"); } } @@ -81,9 +72,7 @@ public void newlines_are_not_added_to_container_output_with_tty() { container.getDockerClient().waitContainerCmd(container.getContainerId()).start().awaitStatusCode(); - assertThat(container.getLogs()) - .isEqualTo(LARGE_PAYLOAD) - .doesNotContain("\n"); + assertThat(container.getLogs()).isEqualTo(LARGE_PAYLOAD).doesNotContain("\n"); } } } diff --git a/core/src/test/java/org/testcontainers/containers/wait/internal/ExternalPortListeningCheckTest.java b/core/src/test/java/org/testcontainers/containers/wait/internal/ExternalPortListeningCheckTest.java index 19f3a772d41..c76d2481c7c 100644 --- a/core/src/test/java/org/testcontainers/containers/wait/internal/ExternalPortListeningCheckTest.java +++ b/core/src/test/java/org/testcontainers/containers/wait/internal/ExternalPortListeningCheckTest.java @@ -16,8 +16,11 @@ public class ExternalPortListeningCheckTest { private ServerSocket listeningSocket1; + private ServerSocket listeningSocket2; + private ServerSocket nonListeningSocket; + private WaitStrategyTarget mockContainer; @Before @@ -34,8 +37,10 @@ public void setUp() throws Exception { @Test public void singleListening() { - - final ExternalPortListeningCheck check = new ExternalPortListeningCheck(mockContainer, ImmutableSet.of(listeningSocket1.getLocalPort())); + final ExternalPortListeningCheck check = new ExternalPortListeningCheck( + mockContainer, + ImmutableSet.of(listeningSocket1.getLocalPort()) + ); final Boolean result = check.call(); @@ -44,8 +49,10 @@ public void singleListening() { @Test public void multipleListening() { - - final ExternalPortListeningCheck check = new ExternalPortListeningCheck(mockContainer, ImmutableSet.of(listeningSocket1.getLocalPort(), listeningSocket2.getLocalPort())); + final ExternalPortListeningCheck check = new ExternalPortListeningCheck( + mockContainer, + ImmutableSet.of(listeningSocket1.getLocalPort(), listeningSocket2.getLocalPort()) + ); final Boolean result = check.call(); @@ -54,13 +61,16 @@ public void multipleListening() { @Test public void oneNotListening() { - - final ExternalPortListeningCheck check = new ExternalPortListeningCheck(mockContainer, ImmutableSet.of(listeningSocket1.getLocalPort(), nonListeningSocket.getLocalPort())); - - assertThrows("ExternalPortListeningCheck detects a non-listening port among many", - IllegalStateException.class, - (Runnable) check::call); - + final ExternalPortListeningCheck check = new ExternalPortListeningCheck( + mockContainer, + ImmutableSet.of(listeningSocket1.getLocalPort(), nonListeningSocket.getLocalPort()) + ); + + assertThrows( + "ExternalPortListeningCheck detects a non-listening port among many", + IllegalStateException.class, + (Runnable) check::call + ); } @After diff --git a/core/src/test/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheckTest.java b/core/src/test/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheckTest.java index 22014cd097a..6f2a5d3158c 100644 --- a/core/src/test/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheckTest.java +++ b/core/src/test/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheckTest.java @@ -11,36 +11,41 @@ import org.testcontainers.containers.GenericContainer; import org.testcontainers.images.builder.ImageFromDockerfile; +import java.util.Arrays; import java.util.concurrent.TimeUnit; -import static java.util.Arrays.asList; - @RunWith(Parameterized.class) public class InternalCommandPortListeningCheckTest { @Parameterized.Parameters(name = "{index} - {0}") public static Iterable data() { - return asList( - new Object[][]{ - {"internal-port-check-dockerfile/Dockerfile-tcp"}, - {"internal-port-check-dockerfile/Dockerfile-nc"}, - {"internal-port-check-dockerfile/Dockerfile-bash"}, - }); + return Arrays.asList( + new Object[][] { + { "internal-port-check-dockerfile/Dockerfile-tcp" }, + { "internal-port-check-dockerfile/Dockerfile-nc" }, + { "internal-port-check-dockerfile/Dockerfile-bash" }, + } + ); } @Rule public GenericContainer container; public InternalCommandPortListeningCheckTest(String dockerfile) { - container = new GenericContainer(new ImageFromDockerfile() - .withFileFromClasspath("Dockerfile", dockerfile) - .withFileFromClasspath("nginx.conf", "internal-port-check-dockerfile/nginx.conf") - ); + container = + new GenericContainer( + new ImageFromDockerfile() + .withFileFromClasspath("Dockerfile", dockerfile) + .withFileFromClasspath("nginx.conf", "internal-port-check-dockerfile/nginx.conf") + ); } @Test public void singleListening() { - final InternalCommandPortListeningCheck check = new InternalCommandPortListeningCheck(container, ImmutableSet.of(8080)); + final InternalCommandPortListeningCheck check = new InternalCommandPortListeningCheck( + container, + ImmutableSet.of(8080) + ); Unreliables.retryUntilTrue(5, TimeUnit.SECONDS, check); @@ -49,18 +54,23 @@ public void singleListening() { @Test public void nonListening() { - final InternalCommandPortListeningCheck check = new InternalCommandPortListeningCheck(container, ImmutableSet.of(8080, 1234)); + final InternalCommandPortListeningCheck check = new InternalCommandPortListeningCheck( + container, + ImmutableSet.of(8080, 1234) + ); try { Unreliables.retryUntilTrue(5, TimeUnit.SECONDS, check); VisibleAssertions.fail("expected to fail"); - } catch (TimeoutException e) { - } + } catch (TimeoutException e) {} } @Test public void lowAndHighPortListening() { - final InternalCommandPortListeningCheck check = new InternalCommandPortListeningCheck(container, ImmutableSet.of(100, 8080)); + final InternalCommandPortListeningCheck check = new InternalCommandPortListeningCheck( + container, + ImmutableSet.of(100, 8080) + ); Unreliables.retryUntilTrue(5, TimeUnit.SECONDS, check); diff --git a/core/src/test/java/org/testcontainers/containers/wait/strategy/DockerHealthcheckWaitStrategyTest.java b/core/src/test/java/org/testcontainers/containers/wait/strategy/DockerHealthcheckWaitStrategyTest.java index d867233f5d7..0d102def5a9 100644 --- a/core/src/test/java/org/testcontainers/containers/wait/strategy/DockerHealthcheckWaitStrategyTest.java +++ b/core/src/test/java/org/testcontainers/containers/wait/strategy/DockerHealthcheckWaitStrategyTest.java @@ -17,10 +17,16 @@ public class DockerHealthcheckWaitStrategyTest { @Before public void setUp() { // Using a Dockerfile here, since Dockerfile builder DSL doesn't support HEALTHCHECK - container = new GenericContainer(new ImageFromDockerfile() - .withFileFromClasspath("write_file_and_loop.sh", "health-wait-strategy-dockerfile/write_file_and_loop.sh") - .withFileFromClasspath("Dockerfile", "health-wait-strategy-dockerfile/Dockerfile")) - .waitingFor(Wait.forHealthcheck().withStartupTimeout(Duration.ofSeconds(3))); + container = + new GenericContainer( + new ImageFromDockerfile() + .withFileFromClasspath( + "write_file_and_loop.sh", + "health-wait-strategy-dockerfile/write_file_and_loop.sh" + ) + .withFileFromClasspath("Dockerfile", "health-wait-strategy-dockerfile/Dockerfile") + ) + .waitingFor(Wait.forHealthcheck().withStartupTimeout(Duration.ofSeconds(3))); } @Test diff --git a/core/src/test/java/org/testcontainers/containers/wait/strategy/WaitAllStrategyTest.java b/core/src/test/java/org/testcontainers/containers/wait/strategy/WaitAllStrategyTest.java index 897293282bd..2b8ba7c33ec 100644 --- a/core/src/test/java/org/testcontainers/containers/wait/strategy/WaitAllStrategyTest.java +++ b/core/src/test/java/org/testcontainers/containers/wait/strategy/WaitAllStrategyTest.java @@ -19,10 +19,13 @@ public class WaitAllStrategyTest { @Mock private GenericContainer container; + @Mock private WaitStrategy strategy1; + @Mock private WaitStrategy strategy2; + @Mock private WaitStrategy strategy3; @@ -36,32 +39,25 @@ public void setUp() { */ @Test public void parentTimeoutApplies() { - DummyStrategy child1 = new DummyStrategy(Duration.ofMillis(10)); child1.withStartupTimeout(Duration.ofMillis(20)); assertEquals("withStartupTimeout directly sets the timeout", 20L, child1.startupTimeout.toMillis()); - new WaitAllStrategy() - .withStrategy(child1) - .withStartupTimeout(Duration.ofMillis(30)); + new WaitAllStrategy().withStrategy(child1).withStartupTimeout(Duration.ofMillis(30)); assertEquals("WaitAllStrategy overrides a child's timeout", 30L, child1.startupTimeout.toMillis()); } @Test public void parentTimeoutAppliesToMultipleChildren() { - Duration defaultInnerWait = Duration.ofMillis(2); Duration outerWait = Duration.ofMillis(6); DummyStrategy child1 = new DummyStrategy(defaultInnerWait); DummyStrategy child2 = new DummyStrategy(defaultInnerWait); - new WaitAllStrategy() - .withStrategy(child1) - .withStrategy(child2) - .withStartupTimeout(outerWait); + new WaitAllStrategy().withStrategy(child1).withStrategy(child2).withStartupTimeout(outerWait); assertEquals("WaitAllStrategy overrides a child's timeout (1st)", 6L, child1.startupTimeout.toMillis()); assertEquals("WaitAllStrategy overrides a child's timeout (2nd)", 6L, child2.startupTimeout.toMillis()); @@ -69,20 +65,20 @@ public void parentTimeoutAppliesToMultipleChildren() { @Test public void parentTimeoutAppliesToAdditionalChildren() { - Duration defaultInnerWait = Duration.ofMillis(2); Duration outerWait = Duration.ofMillis(20); DummyStrategy child1 = new DummyStrategy(defaultInnerWait); DummyStrategy child2 = new DummyStrategy(defaultInnerWait); - new WaitAllStrategy() - .withStrategy(child1) - .withStartupTimeout(outerWait) - .withStrategy(child2); + new WaitAllStrategy().withStrategy(child1).withStartupTimeout(outerWait).withStrategy(child2); assertEquals("WaitAllStrategy overrides a child's timeout (1st)", 20L, child1.startupTimeout.toMillis()); - assertEquals("WaitAllStrategy overrides a child's timeout (2nd, additional)", 20L, child2.startupTimeout.toMillis()); + assertEquals( + "WaitAllStrategy overrides a child's timeout (2nd, additional)", + 20L, + child2.startupTimeout.toMillis() + ); } /* @@ -90,10 +86,7 @@ public void parentTimeoutAppliesToAdditionalChildren() { */ @Test public void childExecutionTest() { - - final WaitStrategy underTest = new WaitAllStrategy() - .withStrategy(strategy1) - .withStrategy(strategy2); + final WaitStrategy underTest = new WaitAllStrategy().withStrategy(strategy1).withStrategy(strategy2); doNothing().when(strategy1).waitUntilReady(eq(container)); doNothing().when(strategy2).waitUntilReady(eq(container)); @@ -107,7 +100,6 @@ public void childExecutionTest() { @Test public void withoutOuterTimeoutShouldRelyOnInnerStrategies() { - final WaitStrategy underTest = new WaitAllStrategy(WaitAllStrategy.Mode.WITH_INDIVIDUAL_TIMEOUTS_ONLY) .withStrategy(strategy1) .withStrategy(strategy2) @@ -116,9 +108,13 @@ public void withoutOuterTimeoutShouldRelyOnInnerStrategies() { doNothing().when(strategy1).waitUntilReady(eq(container)); doThrow(TimeoutException.class).when(strategy2).waitUntilReady(eq(container)); - assertThrows("The outer strategy timeout applies", TimeoutException.class, () -> { - underTest.waitUntilReady(container); - }); + assertThrows( + "The outer strategy timeout applies", + TimeoutException.class, + () -> { + underTest.waitUntilReady(container); + } + ); InOrder inOrder = inOrder(strategy1, strategy2, strategy3); inOrder.verify(strategy1).waitUntilReady(any()); @@ -128,17 +124,19 @@ public void withoutOuterTimeoutShouldRelyOnInnerStrategies() { @Test public void timeoutChangeShouldNotBePossibleWithIndividualTimeoutMode() { - final WaitStrategy underTest = new WaitAllStrategy(WaitAllStrategy.Mode.WITH_INDIVIDUAL_TIMEOUTS_ONLY); - assertThrows("Cannot change timeout for individual timeouts", IllegalStateException.class, () -> { - underTest.withStartupTimeout(Duration.ofSeconds(42)); - }); + assertThrows( + "Cannot change timeout for individual timeouts", + IllegalStateException.class, + () -> { + underTest.withStartupTimeout(Duration.ofSeconds(42)); + } + ); } @Test public void shouldNotMessWithIndividualTimeouts() { - new WaitAllStrategy(WaitAllStrategy.Mode.WITH_INDIVIDUAL_TIMEOUTS_ONLY) .withStrategy(strategy1) .withStrategy(strategy2); @@ -149,18 +147,15 @@ public void shouldNotMessWithIndividualTimeouts() { @Test public void shouldOverwriteIndividualTimeouts() { - Duration someSeconds = Duration.ofSeconds(23); - new WaitAllStrategy() - .withStartupTimeout(someSeconds) - .withStrategy(strategy1) - .withStrategy(strategy2); + new WaitAllStrategy().withStartupTimeout(someSeconds).withStrategy(strategy1).withStrategy(strategy2); verify(strategy1).withStartupTimeout(someSeconds); verify(strategy1).withStartupTimeout(someSeconds); } static class DummyStrategy extends AbstractWaitStrategy { + DummyStrategy(Duration defaultInnerWait) { super.startupTimeout = defaultInnerWait; } diff --git a/core/src/test/java/org/testcontainers/dockerclient/AmbiguousImagePullTest.java b/core/src/test/java/org/testcontainers/dockerclient/AmbiguousImagePullTest.java index 2e39b367c46..f67ad212d77 100644 --- a/core/src/test/java/org/testcontainers/dockerclient/AmbiguousImagePullTest.java +++ b/core/src/test/java/org/testcontainers/dockerclient/AmbiguousImagePullTest.java @@ -14,8 +14,7 @@ public void testNotUsingParse() { DockerImageName imageName = registryContainer.createImage("latest"); String imageNameWithoutTag = imageName.getRegistry() + "/" + imageName.getRepository(); try ( - final GenericContainer container = new GenericContainer<>(imageNameWithoutTag) - .withExposedPorts(8080) + final GenericContainer container = new GenericContainer<>(imageNameWithoutTag).withExposedPorts(8080) ) { container.start(); // do nothing other than start and stop diff --git a/core/src/test/java/org/testcontainers/dockerclient/DockerClientConfigUtilsTest.java b/core/src/test/java/org/testcontainers/dockerclient/DockerClientConfigUtilsTest.java index 442c2efd950..39e1338bc46 100644 --- a/core/src/test/java/org/testcontainers/dockerclient/DockerClientConfigUtilsTest.java +++ b/core/src/test/java/org/testcontainers/dockerclient/DockerClientConfigUtilsTest.java @@ -17,29 +17,39 @@ public class DockerClientConfigUtilsTest { @Test public void getDockerHostIpAddressShouldReturnLocalhostWhenUnixSocket() { - Assumptions.assumeThat(DockerClientConfigUtils.IN_A_CONTAINER) - .as("in a container") - .isFalse(); + Assumptions.assumeThat(DockerClientConfigUtils.IN_A_CONTAINER).as("in a container").isFalse(); - String actual = DockerClientProviderStrategy.resolveDockerHostIpAddress(client, URI.create("unix:///var/run/docker.sock")); + String actual = DockerClientProviderStrategy.resolveDockerHostIpAddress( + client, + URI.create("unix:///var/run/docker.sock") + ); assertEquals("localhost", actual); } @Test public void getDockerHostIpAddressShouldReturnDockerHostIpWhenHttpsUri() { - String actual = DockerClientProviderStrategy.resolveDockerHostIpAddress(client, URI.create("http://12.23.34.45")); + String actual = DockerClientProviderStrategy.resolveDockerHostIpAddress( + client, + URI.create("http://12.23.34.45") + ); assertEquals("12.23.34.45", actual); } @Test public void getDockerHostIpAddressShouldReturnDockerHostIpWhenTcpUri() { - String actual = DockerClientProviderStrategy.resolveDockerHostIpAddress(client, URI.create("tcp://12.23.34.45")); + String actual = DockerClientProviderStrategy.resolveDockerHostIpAddress( + client, + URI.create("tcp://12.23.34.45") + ); assertEquals("12.23.34.45", actual); } @Test public void getDockerHostIpAddressShouldReturnNullWhenUnsupportedUriScheme() { - String actual = DockerClientProviderStrategy.resolveDockerHostIpAddress(client, URI.create("gopher://12.23.34.45")); + String actual = DockerClientProviderStrategy.resolveDockerHostIpAddress( + client, + URI.create("gopher://12.23.34.45") + ); assertNull(actual); } diff --git a/core/src/test/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategyTest.java b/core/src/test/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategyTest.java index a6bd68be745..b2ab27aad27 100644 --- a/core/src/test/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategyTest.java +++ b/core/src/test/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategyTest.java @@ -20,7 +20,6 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; @@ -35,7 +34,9 @@ public class EnvironmentAndSystemPropertyClientProviderStrategyTest { @Rule public MockTestcontainersConfigurationRule mockConfig = new MockTestcontainersConfigurationRule(); + private URI defaultDockerHost; + private com.github.dockerjava.core.SSLConfig defaultSSLConfig; @Before @@ -48,10 +49,22 @@ public void checkEnvironmentClear() { @Test public void testWhenConfigAbsent() { - Mockito.doReturn("auto").when(TestcontainersConfiguration.getInstance()).getEnvVarOrProperty(eq("dockerconfig.source"), anyString()); - Mockito.doReturn(null).when(TestcontainersConfiguration.getInstance()).getEnvVarOrUserProperty(eq("docker.host"), isNull()); - Mockito.doReturn(null).when(TestcontainersConfiguration.getInstance()).getEnvVarOrUserProperty(eq("docker.tls.verify"), isNull()); - Mockito.doReturn(null).when(TestcontainersConfiguration.getInstance()).getEnvVarOrUserProperty(eq("docker.cert.path"), isNull()); + Mockito + .doReturn("auto") + .when(TestcontainersConfiguration.getInstance()) + .getEnvVarOrProperty(eq("dockerconfig.source"), anyString()); + Mockito + .doReturn(null) + .when(TestcontainersConfiguration.getInstance()) + .getEnvVarOrUserProperty(eq("docker.host"), isNull()); + Mockito + .doReturn(null) + .when(TestcontainersConfiguration.getInstance()) + .getEnvVarOrUserProperty(eq("docker.tls.verify"), isNull()); + Mockito + .doReturn(null) + .when(TestcontainersConfiguration.getInstance()) + .getEnvVarOrUserProperty(eq("docker.cert.path"), isNull()); EnvironmentAndSystemPropertyClientProviderStrategy strategy = new EnvironmentAndSystemPropertyClientProviderStrategy(); @@ -62,10 +75,22 @@ public void testWhenConfigAbsent() { @Test public void testWhenDockerHostPresent() { - Mockito.doReturn("auto").when(TestcontainersConfiguration.getInstance()).getEnvVarOrProperty(eq("dockerconfig.source"), anyString()); - Mockito.doReturn("tcp://1.2.3.4:2375").when(TestcontainersConfiguration.getInstance()).getEnvVarOrUserProperty(eq("docker.host"), isNull()); - Mockito.doReturn(null).when(TestcontainersConfiguration.getInstance()).getEnvVarOrUserProperty(eq("docker.tls.verify"), isNull()); - Mockito.doReturn(null).when(TestcontainersConfiguration.getInstance()).getEnvVarOrUserProperty(eq("docker.cert.path"), isNull()); + Mockito + .doReturn("auto") + .when(TestcontainersConfiguration.getInstance()) + .getEnvVarOrProperty(eq("dockerconfig.source"), anyString()); + Mockito + .doReturn("tcp://1.2.3.4:2375") + .when(TestcontainersConfiguration.getInstance()) + .getEnvVarOrUserProperty(eq("docker.host"), isNull()); + Mockito + .doReturn(null) + .when(TestcontainersConfiguration.getInstance()) + .getEnvVarOrUserProperty(eq("docker.tls.verify"), isNull()); + Mockito + .doReturn(null) + .when(TestcontainersConfiguration.getInstance()) + .getEnvVarOrUserProperty(eq("docker.cert.path"), isNull()); EnvironmentAndSystemPropertyClientProviderStrategy strategy = new EnvironmentAndSystemPropertyClientProviderStrategy(); @@ -79,10 +104,22 @@ public void testWhenDockerHostAndSSLConfigPresent() throws IOException { Path tempDir = Files.createTempDirectory("testcontainers-test"); String tempDirPath = tempDir.toAbsolutePath().toString(); - Mockito.doReturn("auto").when(TestcontainersConfiguration.getInstance()).getEnvVarOrProperty(eq("dockerconfig.source"), anyString()); - Mockito.doReturn("tcp://1.2.3.4:2375").when(TestcontainersConfiguration.getInstance()).getEnvVarOrUserProperty(eq("docker.host"), isNull()); - Mockito.doReturn("1").when(TestcontainersConfiguration.getInstance()).getEnvVarOrUserProperty(eq("docker.tls.verify"), isNull()); - Mockito.doReturn(tempDirPath).when(TestcontainersConfiguration.getInstance()).getEnvVarOrUserProperty(eq("docker.cert.path"), isNull()); + Mockito + .doReturn("auto") + .when(TestcontainersConfiguration.getInstance()) + .getEnvVarOrProperty(eq("dockerconfig.source"), anyString()); + Mockito + .doReturn("tcp://1.2.3.4:2375") + .when(TestcontainersConfiguration.getInstance()) + .getEnvVarOrUserProperty(eq("docker.host"), isNull()); + Mockito + .doReturn("1") + .when(TestcontainersConfiguration.getInstance()) + .getEnvVarOrUserProperty(eq("docker.tls.verify"), isNull()); + Mockito + .doReturn(tempDirPath) + .when(TestcontainersConfiguration.getInstance()) + .getEnvVarOrUserProperty(eq("docker.cert.path"), isNull()); EnvironmentAndSystemPropertyClientProviderStrategy strategy = new EnvironmentAndSystemPropertyClientProviderStrategy(); @@ -90,14 +127,15 @@ public void testWhenDockerHostAndSSLConfigPresent() throws IOException { assertEquals("tcp://1.2.3.4:2375", transportConfig.getDockerHost().toString()); SSLConfig sslConfig = transportConfig.getSslConfig(); - Assertions.assertThat(sslConfig) - .extracting("dockerCertPath") - .isEqualTo(tempDirPath); + Assertions.assertThat(sslConfig).extracting("dockerCertPath").isEqualTo(tempDirPath); } @Test public void applicableWhenIgnoringUserPropertiesAndConfigured() { - Mockito.doReturn("autoIgnoringUserProperties").when(TestcontainersConfiguration.getInstance()).getEnvVarOrProperty(eq("dockerconfig.source"), anyString()); + Mockito + .doReturn("autoIgnoringUserProperties") + .when(TestcontainersConfiguration.getInstance()) + .getEnvVarOrProperty(eq("dockerconfig.source"), anyString()); Properties oldProperties = System.getProperties(); try { @@ -112,7 +150,10 @@ public void applicableWhenIgnoringUserPropertiesAndConfigured() { @Test public void notApplicableWhenIgnoringUserPropertiesAndNotConfigured() { - Mockito.doReturn("autoIgnoringUserProperties").when(TestcontainersConfiguration.getInstance()).getEnvVarOrProperty(eq("dockerconfig.source"), anyString()); + Mockito + .doReturn("autoIgnoringUserProperties") + .when(TestcontainersConfiguration.getInstance()) + .getEnvVarOrProperty(eq("dockerconfig.source"), anyString()); EnvironmentAndSystemPropertyClientProviderStrategy strategy = new EnvironmentAndSystemPropertyClientProviderStrategy(); diff --git a/core/src/test/java/org/testcontainers/dockerclient/EventStreamTest.java b/core/src/test/java/org/testcontainers/dockerclient/EventStreamTest.java index a43a001ca5b..642eb6d7390 100644 --- a/core/src/test/java/org/testcontainers/dockerclient/EventStreamTest.java +++ b/core/src/test/java/org/testcontainers/dockerclient/EventStreamTest.java @@ -8,6 +8,7 @@ import org.junit.Test; import org.junit.rules.Timeout; import org.testcontainers.DockerClientFactory; +import org.testcontainers.TestImages; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.startupcheck.OneShotStartupCheckStrategy; @@ -16,8 +17,6 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; -import static org.testcontainers.TestImages.TINY_IMAGE; - /** * Test that event streaming from the {@link DockerClient} works correctly */ @@ -34,7 +33,7 @@ public void test() throws IOException, InterruptedException { CountDownLatch latch = new CountDownLatch(1); try ( - GenericContainer container = new GenericContainer<>(TINY_IMAGE) + GenericContainer container = new GenericContainer<>(TestImages.TINY_IMAGE) .withCommand("true") .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) ) { @@ -43,24 +42,31 @@ public void test() throws IOException, InterruptedException { // Request all events between startTime and endTime for the container try ( - EventsResultCallback response = DockerClientFactory.instance().client().eventsCmd() + EventsResultCallback response = DockerClientFactory + .instance() + .client() + .eventsCmd() .withContainerFilter(container.getContainerId()) .withEventFilter("create") .withSince(Instant.parse(createdAt).getEpochSecond() + "") - .exec(new EventsResultCallback() { - @Override - public void onNext(@NotNull Event event) { - // Check that a create event for the container is received - if (event.getId().equals(container.getContainerId()) && event.getStatus().equals("create")) { - latch.countDown(); + .exec( + new EventsResultCallback() { + @Override + public void onNext(@NotNull Event event) { + // Check that a create event for the container is received + if ( + event.getId().equals(container.getContainerId()) && + event.getStatus().equals("create") + ) { + latch.countDown(); + } } } - }) + ) ) { response.awaitStarted(5, TimeUnit.SECONDS); latch.await(5, TimeUnit.SECONDS); } } } - } diff --git a/core/src/test/java/org/testcontainers/dockerclient/ImagePullTest.java b/core/src/test/java/org/testcontainers/dockerclient/ImagePullTest.java index 8691e8f5f56..730208c553f 100644 --- a/core/src/test/java/org/testcontainers/dockerclient/ImagePullTest.java +++ b/core/src/test/java/org/testcontainers/dockerclient/ImagePullTest.java @@ -22,7 +22,7 @@ public static String[] parameters() { "quay.io/testcontainers/ryuk:latest", "quay.io/testcontainers/ryuk:0.2.3", "quay.io/testcontainers/ryuk@sha256:bb5a635cac4bd96c93cc476969ce11dc56436238ec7cd028d0524462f4739dd9", -// "ibmcom/db2express-c", // Big image for testing with slow networks + // "ibmcom/db2express-c", // Big image for testing with slow networks }; } @@ -32,9 +32,11 @@ public ImagePullTest(String image) { @Test public void test() { - try (final GenericContainer container = new GenericContainer<>(DockerImageName.parse(image)) - .withCommand("/bin/sh", "-c", "sleep 0") - .withStartupCheckStrategy(new OneShotStartupCheckStrategy())) { + try ( + final GenericContainer container = new GenericContainer<>(DockerImageName.parse(image)) + .withCommand("/bin/sh", "-c", "sleep 0") + .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) + ) { container.start(); // do nothing other than start and stop } diff --git a/core/src/test/java/org/testcontainers/images/AgeBasedPullPolicyTest.java b/core/src/test/java/org/testcontainers/images/AgeBasedPullPolicyTest.java index e073fa0522a..96fdfa4010b 100644 --- a/core/src/test/java/org/testcontainers/images/AgeBasedPullPolicyTest.java +++ b/core/src/test/java/org/testcontainers/images/AgeBasedPullPolicyTest.java @@ -1,15 +1,15 @@ package org.testcontainers.images; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import org.junit.Test; +import org.testcontainers.utility.DockerImageName; import java.time.Duration; import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.UUID; -import org.junit.Test; -import org.testcontainers.utility.DockerImageName; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; public class AgeBasedPullPolicyTest { @@ -17,9 +17,7 @@ public class AgeBasedPullPolicyTest { @Test public void shouldPull() { - ImageData imageData = ImageData.builder() - .createdAt(Instant.now().minus(2, ChronoUnit.HOURS)) - .build(); + ImageData imageData = ImageData.builder().createdAt(Instant.now().minus(2, ChronoUnit.HOURS)).build(); AgeBasedPullPolicy oneHour = new AgeBasedPullPolicy(Duration.of(1L, ChronoUnit.HOURS)); assertTrue(oneHour.shouldPullCached(imageName, imageData)); diff --git a/core/src/test/java/org/testcontainers/images/ImageDataTest.java b/core/src/test/java/org/testcontainers/images/ImageDataTest.java index 3b473cf7ffd..6ff75e9e355 100644 --- a/core/src/test/java/org/testcontainers/images/ImageDataTest.java +++ b/core/src/test/java/org/testcontainers/images/ImageDataTest.java @@ -1,12 +1,11 @@ package org.testcontainers.images; -import static org.assertj.core.api.Assertions.assertThat; +import com.github.dockerjava.api.command.InspectImageResponse; +import org.junit.Test; import java.time.Instant; -import org.junit.Test; - -import com.github.dockerjava.api.command.InspectImageResponse; +import static org.assertj.core.api.Assertions.assertThat; public class ImageDataTest { @@ -23,5 +22,4 @@ public void shouldReadTimestampWithOffsetFromInspectImageResponse() { final ImageData imageData = ImageData.from(new InspectImageResponse().withCreated(timestamp)); assertThat(imageData.getCreatedAt()).isEqualTo(Instant.parse("2020-07-27T16:23:31.365190246Z")); } - } diff --git a/core/src/test/java/org/testcontainers/images/ImagePullPolicyTest.java b/core/src/test/java/org/testcontainers/images/ImagePullPolicyTest.java index 9d5ed400d32..0b0ee5f1b70 100644 --- a/core/src/test/java/org/testcontainers/images/ImagePullPolicyTest.java +++ b/core/src/test/java/org/testcontainers/images/ImagePullPolicyTest.java @@ -24,29 +24,20 @@ public class ImagePullPolicyTest { @Test public void pullsByDefault() { - try ( - GenericContainer container = new GenericContainer<>(imageName) - .withExposedPorts(8080) - ) { + try (GenericContainer container = new GenericContainer<>(imageName).withExposedPorts(8080)) { container.start(); } } @Test public void shouldAlwaysPull() { - try ( - GenericContainer container = new GenericContainer<>(imageName) - .withExposedPorts(8080) - ) { + try (GenericContainer container = new GenericContainer<>(imageName).withExposedPorts(8080)) { container.start(); } removeImage(); - try ( - GenericContainer container = new GenericContainer<>(imageName) - .withExposedPorts(8080) - ) { + try (GenericContainer container = new GenericContainer<>(imageName).withExposedPorts(8080)) { expectToFailWithNotFoundException(container); } @@ -66,12 +57,14 @@ public void shouldSupportCustomPolicies() { try ( // custom_image_pull_policy { GenericContainer container = new GenericContainer<>(imageName) - .withImagePullPolicy(new AbstractImagePullPolicy() { - @Override - protected boolean shouldPullCached(DockerImageName imageName, ImageData localImageData) { - return System.getenv("ALWAYS_PULL_IMAGE") != null; + .withImagePullPolicy( + new AbstractImagePullPolicy() { + @Override + protected boolean shouldPullCached(DockerImageName imageName, ImageData localImageData) { + return System.getenv("ALWAYS_PULL_IMAGE") != null; + } } - }) + ) // } ) { container.withExposedPorts(8080); @@ -81,12 +74,14 @@ protected boolean shouldPullCached(DockerImageName imageName, ImageData localIma @Test public void shouldCheckPolicy() { - ImagePullPolicy policy = Mockito.spy(new AbstractImagePullPolicy() { - @Override - protected boolean shouldPullCached(DockerImageName imageName, ImageData localImageData) { - return false; + ImagePullPolicy policy = Mockito.spy( + new AbstractImagePullPolicy() { + @Override + protected boolean shouldPullCached(DockerImageName imageName, ImageData localImageData) { + return false; + } } - }); + ); try ( GenericContainer container = new GenericContainer<>(imageName) .withImagePullPolicy(policy) @@ -128,11 +123,12 @@ private void expectToFailWithNotFoundException(GenericContainer container) { private void removeImage() { try { - DockerClientFactory.instance().client() + DockerClientFactory + .instance() + .client() .removeImageCmd(imageName.asCanonicalNameString()) .withForce(true) .exec(); - } catch (NotFoundException ignored) { - } + } catch (NotFoundException ignored) {} } } diff --git a/core/src/test/java/org/testcontainers/images/ParsedDockerfileTest.java b/core/src/test/java/org/testcontainers/images/ParsedDockerfileTest.java index 52a22c83b00..cd4509965e6 100644 --- a/core/src/test/java/org/testcontainers/images/ParsedDockerfileTest.java +++ b/core/src/test/java/org/testcontainers/images/ParsedDockerfileTest.java @@ -4,69 +4,119 @@ import org.junit.Test; import java.nio.file.Paths; +import java.util.Arrays; -import static java.util.Arrays.asList; import static org.junit.Assert.assertEquals; public class ParsedDockerfileTest { @Test public void doesSimpleParsing() { - final ParsedDockerfile parsedDockerfile = new ParsedDockerfile(asList("FROM someimage", "RUN something")); - assertEquals("extracts a single image name", Sets.newHashSet("someimage"), parsedDockerfile.getDependencyImageNames()); + final ParsedDockerfile parsedDockerfile = new ParsedDockerfile( + Arrays.asList("FROM someimage", "RUN something") + ); + assertEquals( + "extracts a single image name", + Sets.newHashSet("someimage"), + parsedDockerfile.getDependencyImageNames() + ); } @Test public void isCaseInsensitive() { - final ParsedDockerfile parsedDockerfile = new ParsedDockerfile(asList("from someimage", "RUN something")); - assertEquals("extracts a single image name", Sets.newHashSet("someimage"), parsedDockerfile.getDependencyImageNames()); + final ParsedDockerfile parsedDockerfile = new ParsedDockerfile( + Arrays.asList("from someimage", "RUN something") + ); + assertEquals( + "extracts a single image name", + Sets.newHashSet("someimage"), + parsedDockerfile.getDependencyImageNames() + ); } @Test public void handlesTags() { - final ParsedDockerfile parsedDockerfile = new ParsedDockerfile(asList("FROM someimage:tag", "RUN something")); - assertEquals("retains tags in image names", Sets.newHashSet("someimage:tag"), parsedDockerfile.getDependencyImageNames()); + final ParsedDockerfile parsedDockerfile = new ParsedDockerfile( + Arrays.asList("FROM someimage:tag", "RUN something") + ); + assertEquals( + "retains tags in image names", + Sets.newHashSet("someimage:tag"), + parsedDockerfile.getDependencyImageNames() + ); } @Test public void handlesDigests() { - final ParsedDockerfile parsedDockerfile = new ParsedDockerfile(asList("FROM someimage@sha256:abc123", "RUN something")); - assertEquals("retains digests in image names", Sets.newHashSet("someimage@sha256:abc123"), parsedDockerfile.getDependencyImageNames()); + final ParsedDockerfile parsedDockerfile = new ParsedDockerfile( + Arrays.asList("FROM someimage@sha256:abc123", "RUN something") + ); + assertEquals( + "retains digests in image names", + Sets.newHashSet("someimage@sha256:abc123"), + parsedDockerfile.getDependencyImageNames() + ); } @Test public void ignoringCommentedFromLines() { - final ParsedDockerfile parsedDockerfile = new ParsedDockerfile(asList("FROM someimage", "#FROM somethingelse")); - assertEquals("ignores commented from lines", Sets.newHashSet("someimage"), parsedDockerfile.getDependencyImageNames()); + final ParsedDockerfile parsedDockerfile = new ParsedDockerfile( + Arrays.asList("FROM someimage", "#FROM somethingelse") + ); + assertEquals( + "ignores commented from lines", + Sets.newHashSet("someimage"), + parsedDockerfile.getDependencyImageNames() + ); } @Test public void ignoringBuildStageNames() { - final ParsedDockerfile parsedDockerfile = new ParsedDockerfile(asList("FROM someimage --as=base", "RUN something", "FROM nextimage", "RUN something")); - assertEquals("ignores build stage names and allows multiple images to be extracted", Sets.newHashSet("someimage", "nextimage"), parsedDockerfile.getDependencyImageNames()); + final ParsedDockerfile parsedDockerfile = new ParsedDockerfile( + Arrays.asList("FROM someimage --as=base", "RUN something", "FROM nextimage", "RUN something") + ); + assertEquals( + "ignores build stage names and allows multiple images to be extracted", + Sets.newHashSet("someimage", "nextimage"), + parsedDockerfile.getDependencyImageNames() + ); } @Test public void ignoringPlatformArgs() { - final ParsedDockerfile parsedDockerfile = new ParsedDockerfile(asList("FROM --platform=linux/amd64 someimage", "RUN something")); - assertEquals("ignores platform args", Sets.newHashSet("someimage"), parsedDockerfile.getDependencyImageNames()); + final ParsedDockerfile parsedDockerfile = new ParsedDockerfile( + Arrays.asList("FROM --platform=linux/amd64 someimage", "RUN something") + ); + assertEquals("ignores platform args", Sets.newHashSet("someimage"), parsedDockerfile.getDependencyImageNames()); } @Test public void ignoringExtraPlatformArgs() { - final ParsedDockerfile parsedDockerfile = new ParsedDockerfile(asList("FROM --platform=linux/amd64 --somethingelse=value someimage", "RUN something")); - assertEquals("ignores platform args", Sets.newHashSet("someimage"), parsedDockerfile.getDependencyImageNames()); + final ParsedDockerfile parsedDockerfile = new ParsedDockerfile( + Arrays.asList("FROM --platform=linux/amd64 --somethingelse=value someimage", "RUN something") + ); + assertEquals("ignores platform args", Sets.newHashSet("someimage"), parsedDockerfile.getDependencyImageNames()); } @Test public void handlesGracefullyIfNoFromLine() { - final ParsedDockerfile parsedDockerfile = new ParsedDockerfile(asList("RUN something", "# is this even a valid Dockerfile?")); - assertEquals("handles invalid Dockerfiles gracefully", Sets.newHashSet(), parsedDockerfile.getDependencyImageNames()); + final ParsedDockerfile parsedDockerfile = new ParsedDockerfile( + Arrays.asList("RUN something", "# is this even a valid Dockerfile?") + ); + assertEquals( + "handles invalid Dockerfiles gracefully", + Sets.newHashSet(), + parsedDockerfile.getDependencyImageNames() + ); } @Test public void handlesGracefullyIfDockerfileNotFound() { final ParsedDockerfile parsedDockerfile = new ParsedDockerfile(Paths.get("nonexistent.Dockerfile")); - assertEquals("handles missing Dockerfiles gracefully", Sets.newHashSet(), parsedDockerfile.getDependencyImageNames()); + assertEquals( + "handles missing Dockerfiles gracefully", + Sets.newHashSet(), + parsedDockerfile.getDependencyImageNames() + ); } } diff --git a/core/src/test/java/org/testcontainers/images/RemoteDockerImageTest.java b/core/src/test/java/org/testcontainers/images/RemoteDockerImageTest.java index d2ea3e7188a..592d46f33d1 100644 --- a/core/src/test/java/org/testcontainers/images/RemoteDockerImageTest.java +++ b/core/src/test/java/org/testcontainers/images/RemoteDockerImageTest.java @@ -9,7 +9,7 @@ import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicBoolean; -import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.Matchers.containsString; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThat; @@ -23,7 +23,7 @@ public void toStringContainsOnlyImageName() { } @Test - public void toStringWithExceptionContainsOnlyImageNameFuture() { + public void toStringWithExceptionContainsOnlyImageNameFuture() { CompletableFuture imageNameFuture = new CompletableFuture<>(); imageNameFuture.completeExceptionally(new RuntimeException("arbitrary")); @@ -31,8 +31,8 @@ public void toStringWithExceptionContainsOnlyImageNameFuture() { assertThat(remoteDockerImage.toString(), containsString("imageName=java.lang.RuntimeException: arbitrary")); } - @Test(timeout=5000L) - public void toStringDoesntResolveImageNameFuture() { + @Test(timeout = 5000L) + public void toStringDoesntResolveImageNameFuture() { CompletableFuture imageNameFuture = new CompletableFuture<>(); // verify that we've set up the test properly @@ -49,7 +49,7 @@ public void toStringDoesntResolveImageNameFuture() { assertThat(remoteDockerImage.toString(), containsString("imageName=" + imageName)); } - @Test(timeout=5000L) + @Test(timeout = 5000L) public void toStringDoesntResolveLazyFuture() throws Exception { String imageName = Base58.randomString(8).toLowerCase(); AtomicBoolean resolved = new AtomicBoolean(false); diff --git a/core/src/test/java/org/testcontainers/images/builder/DockerfileBuildTest.java b/core/src/test/java/org/testcontainers/images/builder/DockerfileBuildTest.java index 9a9d647d71b..29cf4fcf6a7 100644 --- a/core/src/test/java/org/testcontainers/images/builder/DockerfileBuildTest.java +++ b/core/src/test/java/org/testcontainers/images/builder/DockerfileBuildTest.java @@ -6,7 +6,6 @@ import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.startupcheck.OneShotStartupCheckStrategy; -import java.io.File; import java.nio.file.Path; import java.nio.file.Paths; @@ -18,39 +17,41 @@ public class DockerfileBuildTest { static final Path RESOURCE_PATH = Paths.get("src/test/resources/dockerfile-build-test"); public String expectedFileContent; + public ImageFromDockerfile image; @Parameterized.Parameters public static Object[][] parameters() { - return new Object[][]{ + return new Object[][] { // Dockerfile build without explicit per-file inclusion - new Object[]{"test1234", + new Object[] { + "test1234", + // spotless:off // docsShowRecursiveFileInclusion { new ImageFromDockerfile() - .withFileFromPath(".", RESOURCE_PATH) + .withFileFromPath(".", RESOURCE_PATH), // } + // spotless:on }, - // Dockerfile build using a non-standard Dockerfile - new Object[]{"test4567", - new ImageFromDockerfile() - .withFileFromPath(".", RESOURCE_PATH) - .withDockerfilePath("./Dockerfile-alt") + new Object[] { + "test4567", + new ImageFromDockerfile().withFileFromPath(".", RESOURCE_PATH).withDockerfilePath("./Dockerfile-alt"), }, - // Dockerfile build using build args - new Object[]{"test7890", + new Object[] { + "test7890", new ImageFromDockerfile() .withFileFromPath(".", RESOURCE_PATH) .withDockerfilePath("./Dockerfile-buildarg") - .withBuildArg("CUSTOM_ARG", "test7890") + .withBuildArg("CUSTOM_ARG", "test7890"), }, - - // Dockerfile build using withDockerfile(File) - new Object[]{"test4567", + // Dockerfile build using withDockerfile(File) + new Object[] { + "test4567", new ImageFromDockerfile() .withFileFromPath(".", RESOURCE_PATH) - .withDockerfile(RESOURCE_PATH.resolve("Dockerfile-alt")) + .withDockerfile(RESOURCE_PATH.resolve("Dockerfile-alt")), }, }; } @@ -62,14 +63,18 @@ public DockerfileBuildTest(String expectedFileContent, ImageFromDockerfile image @Test public void performTest() { - try (final GenericContainer container = new GenericContainer(image) - .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) - .withCommand("cat", "/test.txt")) { + try ( + final GenericContainer container = new GenericContainer(image) + .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) + .withCommand("cat", "/test.txt") + ) { container.start(); final String logs = container.getLogs(); - assertTrue("expected file content indicates that dockerfile build steps have been run", logs.contains(expectedFileContent)); + assertTrue( + "expected file content indicates that dockerfile build steps have been run", + logs.contains(expectedFileContent) + ); } } - } diff --git a/core/src/test/java/org/testcontainers/images/builder/DockerignoreTest.java b/core/src/test/java/org/testcontainers/images/builder/DockerignoreTest.java index b99a70a7650..513b1ce1784 100644 --- a/core/src/test/java/org/testcontainers/images/builder/DockerignoreTest.java +++ b/core/src/test/java/org/testcontainers/images/builder/DockerignoreTest.java @@ -24,10 +24,10 @@ public void testInvalidDockerignore() throws Exception { .withDockerfile(INVALID_DOCKERIGNORE_PATH.resolve("Dockerfile")) .get(); fail("Should not be able to build an image with an invalid .dockerignore file"); - } - catch (DockerClientException e) { - if (!e.getMessage().contains("Invalid pattern")) + } catch (DockerClientException e) { + if (!e.getMessage().contains("Invalid pattern")) { throw e; + } } } @@ -35,22 +35,24 @@ public void testInvalidDockerignore() throws Exception { @Test public void testValidDockerignore() throws Exception { ImageFromDockerfile img = new ImageFromDockerfile() - .withFileFromPath(".", DockerfileBuildTest.RESOURCE_PATH) - .withDockerfile(DockerfileBuildTest.RESOURCE_PATH.resolve("Dockerfile-currentdir")); - try( + .withFileFromPath(".", DockerfileBuildTest.RESOURCE_PATH) + .withDockerfile(DockerfileBuildTest.RESOURCE_PATH.resolve("Dockerfile-currentdir")); + try ( final GenericContainer container = new GenericContainer(DockerImageName.parse(img.get())) - .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) - .withCommand("ls", "/") + .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) + .withCommand("ls", "/") ) { - container.start(); final String logs = container.getLogs(); - assertTrue("Files in the container indicated the .dockerignore was not applied. Output was: " + logs, - logs.contains("should_not_be_ignored.txt")); - assertTrue("Files in the container indicated the .dockerignore was not applied. Output was: " + logs, - !logs.contains("should_be_ignored.txt")); + assertTrue( + "Files in the container indicated the .dockerignore was not applied. Output was: " + logs, + logs.contains("should_not_be_ignored.txt") + ); + assertTrue( + "Files in the container indicated the .dockerignore was not applied. Output was: " + logs, + !logs.contains("should_be_ignored.txt") + ); } } - } diff --git a/core/src/test/java/org/testcontainers/images/builder/ImageFromDockerfileTest.java b/core/src/test/java/org/testcontainers/images/builder/ImageFromDockerfileTest.java index da3509cea7b..abc05eb567c 100644 --- a/core/src/test/java/org/testcontainers/images/builder/ImageFromDockerfileTest.java +++ b/core/src/test/java/org/testcontainers/images/builder/ImageFromDockerfileTest.java @@ -12,8 +12,7 @@ public class ImageFromDockerfileTest { @Test public void shouldAddDefaultLabels() { - ImageFromDockerfile image = new ImageFromDockerfile() - .withDockerfileFromBuilder(it -> it.from("scratch")); + ImageFromDockerfile image = new ImageFromDockerfile().withDockerfileFromBuilder(it -> it.from("scratch")); String imageId = image.resolve(); @@ -27,7 +26,10 @@ public void shouldAddDefaultLabels() { @Test public void shouldNotAddSessionLabelIfDeleteOnExitIsFalse() { - ImageFromDockerfile image = new ImageFromDockerfile("localhost/testcontainers/" + Base58.randomString(16).toLowerCase(), false) + ImageFromDockerfile image = new ImageFromDockerfile( + "localhost/testcontainers/" + Base58.randomString(16).toLowerCase(), + false + ) .withDockerfileFromBuilder(it -> it.from("scratch")); String imageId = image.resolve(); DockerClient dockerClient = DockerClientFactory.instance().client(); @@ -40,7 +42,5 @@ public void shouldNotAddSessionLabelIfDeleteOnExitIsFalse() { // ensure the image is deleted, even if the test fails dockerClient.removeImageCmd(imageId).exec(); } - } - } diff --git a/core/src/test/java/org/testcontainers/images/builder/dockerfile/statement/AbstractStatementTest.java b/core/src/test/java/org/testcontainers/images/builder/dockerfile/statement/AbstractStatementTest.java index 15c73cadb5f..69d9a1b6840 100644 --- a/core/src/test/java/org/testcontainers/images/builder/dockerfile/statement/AbstractStatementTest.java +++ b/core/src/test/java/org/testcontainers/images/builder/dockerfile/statement/AbstractStatementTest.java @@ -19,7 +19,6 @@ public abstract class AbstractStatementTest { public TestName testName = new TestName(); protected void assertStatement(Statement statement) { - String[] expectedLines = new String[0]; try { String path = "fixtures/statements/" + getClass().getSimpleName() + "/" + testName.getMethodName(); @@ -39,7 +38,9 @@ protected void assertStatement(Statement statement) { String[] resultLines = StringUtils.chomp(builder.toString().trim()).split("\n"); if (expectedLines.length != resultLines.length) { - fail("number of lines is not the same. Expected " + expectedLines.length + " but got " + resultLines.length); + fail( + "number of lines is not the same. Expected " + expectedLines.length + " but got " + resultLines.length + ); } if (!Arrays.equals(expectedLines, resultLines)) { @@ -59,7 +60,6 @@ protected void assertStatement(Statement statement) { failureBuilder.append(expectedLine); failureBuilder.append(">\n"); } - } fail(failureBuilder.toString()); diff --git a/core/src/test/java/org/testcontainers/images/builder/dockerfile/statement/KeyValuesStatementTest.java b/core/src/test/java/org/testcontainers/images/builder/dockerfile/statement/KeyValuesStatementTest.java index 6add9d462f2..f446e48b42c 100644 --- a/core/src/test/java/org/testcontainers/images/builder/dockerfile/statement/KeyValuesStatementTest.java +++ b/core/src/test/java/org/testcontainers/images/builder/dockerfile/statement/KeyValuesStatementTest.java @@ -9,11 +9,12 @@ public class KeyValuesStatementTest extends AbstractStatementTest { @Test public void multilineTest() throws Exception { - ImmutableMap pairs = ImmutableMap.builder() - .put("line1", "1") - .put("line2", "2") - .put("line3", "3") - .build(); + ImmutableMap pairs = ImmutableMap + .builder() + .put("line1", "1") + .put("line2", "2") + .put("line3", "3") + .build(); assertStatement(new KeyValuesStatement("TEST", pairs)); } @@ -35,12 +36,13 @@ public void keyWithTabsTest() throws Exception { @Test public void valueIsEscapedTest() throws Exception { - ImmutableMap pairs = ImmutableMap.builder() - .put("1", "value with spaces") - .put("2", "value\nwith\nnewlines") - .put("3", "value\twith\ttab") - .build(); + ImmutableMap pairs = ImmutableMap + .builder() + .put("1", "value with spaces") + .put("2", "value\nwith\nnewlines") + .put("3", "value\twith\ttab") + .build(); assertStatement(new KeyValuesStatement("TEST", pairs)); } -} \ No newline at end of file +} diff --git a/core/src/test/java/org/testcontainers/images/builder/dockerfile/statement/MultiArgsStatementTest.java b/core/src/test/java/org/testcontainers/images/builder/dockerfile/statement/MultiArgsStatementTest.java index 3b36af8861e..50bc8bedd96 100644 --- a/core/src/test/java/org/testcontainers/images/builder/dockerfile/statement/MultiArgsStatementTest.java +++ b/core/src/test/java/org/testcontainers/images/builder/dockerfile/statement/MultiArgsStatementTest.java @@ -13,4 +13,4 @@ public void simpleTest() throws Exception { public void multilineTest() throws Exception { assertStatement(new MultiArgsStatement("TEST", "some\nmultiline\nargument")); } -} \ No newline at end of file +} diff --git a/core/src/test/java/org/testcontainers/images/builder/dockerfile/statement/RawStatementTest.java b/core/src/test/java/org/testcontainers/images/builder/dockerfile/statement/RawStatementTest.java index a600451bd71..70724a03f09 100644 --- a/core/src/test/java/org/testcontainers/images/builder/dockerfile/statement/RawStatementTest.java +++ b/core/src/test/java/org/testcontainers/images/builder/dockerfile/statement/RawStatementTest.java @@ -8,5 +8,4 @@ public class RawStatementTest extends AbstractStatementTest { public void simpleTest() throws Exception { assertStatement(new RawStatement("TEST", "value\nas\t\\\nis")); } - -} \ No newline at end of file +} diff --git a/core/src/test/java/org/testcontainers/images/builder/dockerfile/statement/SingleArgumentStatementTest.java b/core/src/test/java/org/testcontainers/images/builder/dockerfile/statement/SingleArgumentStatementTest.java index 75d290b240d..dbdabab9fff 100644 --- a/core/src/test/java/org/testcontainers/images/builder/dockerfile/statement/SingleArgumentStatementTest.java +++ b/core/src/test/java/org/testcontainers/images/builder/dockerfile/statement/SingleArgumentStatementTest.java @@ -13,4 +13,4 @@ public void simpleTest() throws Exception { public void multilineTest() throws Exception { assertStatement(new SingleArgumentStatement("TEST", "hello\nworld")); } -} \ No newline at end of file +} diff --git a/core/src/test/java/org/testcontainers/junit/BaseDockerComposeTest.java b/core/src/test/java/org/testcontainers/junit/BaseDockerComposeTest.java index 7f8b5d79953..dd62ace1fac 100644 --- a/core/src/test/java/org/testcontainers/junit/BaseDockerComposeTest.java +++ b/core/src/test/java/org/testcontainers/junit/BaseDockerComposeTest.java @@ -15,7 +15,7 @@ import java.util.List; import java.util.stream.Collectors; -import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.Matchers.is; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; import static org.rnorth.visibleassertions.VisibleAssertions.assertThat; @@ -37,7 +37,10 @@ public static void checkVersion() { @Test public void simpleTest() { - Jedis jedis = new Jedis(getEnvironment().getServiceHost("redis_1", REDIS_PORT), getEnvironment().getServicePort("redis_1", REDIS_PORT)); + Jedis jedis = new Jedis( + getEnvironment().getServiceHost("redis_1", REDIS_PORT), + getEnvironment().getServicePort("redis_1", REDIS_PORT) + ); jedis.incr("test"); jedis.incr("test"); @@ -49,7 +52,10 @@ public void simpleTest() { @Test public void secondTest() { // used in manual checking for cleanup in between tests - Jedis jedis = new Jedis(getEnvironment().getServiceHost("redis_1", REDIS_PORT), getEnvironment().getServicePort("redis_1", REDIS_PORT)); + Jedis jedis = new Jedis( + getEnvironment().getServiceHost("redis_1", REDIS_PORT), + getEnvironment().getServicePort("redis_1", REDIS_PORT) + ); jedis.incr("test"); jedis.incr("test"); @@ -62,18 +68,23 @@ public void secondTest() { @Before public void captureNetworks() { - existingNetworks.addAll(findAllNetworks()); + existingNetworks.addAll(findAllNetworks()); } @After public void verifyNoNetworks() { - assertThat("The networks", findAllNetworks(), is(existingNetworks)); + assertThat("The networks", findAllNetworks(), is(existingNetworks)); } private List findAllNetworks() { - return DockerClientFactory.instance().client().listNetworksCmd().exec().stream() - .map(Network::getName) - .sorted() - .collect(Collectors.toList()); + return DockerClientFactory + .instance() + .client() + .listNetworksCmd() + .exec() + .stream() + .map(Network::getName) + .sorted() + .collect(Collectors.toList()); } } diff --git a/core/src/test/java/org/testcontainers/junit/CopyFileToContainerTest.java b/core/src/test/java/org/testcontainers/junit/CopyFileToContainerTest.java index b0ed90d9c9e..7041e74912a 100644 --- a/core/src/test/java/org/testcontainers/junit/CopyFileToContainerTest.java +++ b/core/src/test/java/org/testcontainers/junit/CopyFileToContainerTest.java @@ -1,6 +1,7 @@ package org.testcontainers.junit; import org.junit.Test; +import org.testcontainers.TestImages; import org.testcontainers.containers.BindMode; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.SelinuxContext; @@ -11,16 +12,17 @@ import static org.rnorth.visibleassertions.VisibleAssertions.assertFalse; import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; -import static org.testcontainers.TestImages.TINY_IMAGE; public class CopyFileToContainerTest { + private static String containerPath = "/tmp/mappable-resource/"; + private static String fileName = "test-resource.txt"; @Test public void checkFileCopied() throws IOException, InterruptedException { try ( - GenericContainer container = new GenericContainer<>(TINY_IMAGE) + GenericContainer container = new GenericContainer<>(TestImages.TINY_IMAGE) .withCommand("sleep", "3000") .withCopyFileToContainer(MountableFile.forClasspathResource("/mappable-resource/"), containerPath) ) { @@ -33,7 +35,7 @@ public void checkFileCopied() throws IOException, InterruptedException { @Test public void shouldUseCopyForReadOnlyClasspathResources() throws Exception { try ( - GenericContainer container = new GenericContainer<>(TINY_IMAGE) + GenericContainer container = new GenericContainer<>(TestImages.TINY_IMAGE) .withCommand("sleep", "3000") .withClasspathResourceMapping("/mappable-resource/", containerPath, BindMode.READ_ONLY) ) { @@ -46,10 +48,9 @@ public void shouldUseCopyForReadOnlyClasspathResources() throws Exception { @Test public void shouldUseCopyOnlyWithReadOnlyClasspathResources() { String resource = "/test_copy_to_container.txt"; - GenericContainer container = new GenericContainer<>(TINY_IMAGE) + GenericContainer container = new GenericContainer<>(TestImages.TINY_IMAGE) .withClasspathResourceMapping(resource, "/readOnly", BindMode.READ_ONLY) .withClasspathResourceMapping(resource, "/readOnlyNoSelinux", BindMode.READ_ONLY) - .withClasspathResourceMapping(resource, "/readOnlyShared", BindMode.READ_ONLY, SelinuxContext.SHARED) .withClasspathResourceMapping(resource, "/readWrite", BindMode.READ_WRITE); @@ -65,7 +66,7 @@ public void shouldUseCopyOnlyWithReadOnlyClasspathResources() { public void shouldCreateFoldersStructureWithCopy() throws Exception { String resource = "/test_copy_to_container.txt"; try ( - GenericContainer container = new GenericContainer<>(TINY_IMAGE) + GenericContainer container = new GenericContainer<>(TestImages.TINY_IMAGE) .withCommand("sleep", "3000") .withClasspathResourceMapping(resource, "/a/b/c/file", BindMode.READ_ONLY) ) { diff --git a/core/src/test/java/org/testcontainers/junit/DependenciesTest.java b/core/src/test/java/org/testcontainers/junit/DependenciesTest.java index bd82c58a7f7..78e55335d9a 100644 --- a/core/src/test/java/org/testcontainers/junit/DependenciesTest.java +++ b/core/src/test/java/org/testcontainers/junit/DependenciesTest.java @@ -3,6 +3,7 @@ import lombok.Getter; import org.junit.Test; import org.rnorth.visibleassertions.VisibleAssertions; +import org.testcontainers.TestImages; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.startupcheck.OneShotStartupCheckStrategy; import org.testcontainers.lifecycle.Startable; @@ -16,8 +17,6 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import static org.testcontainers.TestImages.TINY_IMAGE; - public class DependenciesTest { @Test @@ -25,7 +24,7 @@ public void shouldWorkWithSimpleDependency() { InvocationCountingStartable startable = new InvocationCountingStartable(); try ( - GenericContainer container = new GenericContainer<>(TINY_IMAGE) + GenericContainer container = new GenericContainer<>(TestImages.TINY_IMAGE) .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) .dependsOn(startable) ) { @@ -42,7 +41,7 @@ public void shouldWorkWithMutlipleDependencies() { InvocationCountingStartable startable2 = new InvocationCountingStartable(); try ( - GenericContainer container = new GenericContainer<>(TINY_IMAGE) + GenericContainer container = new GenericContainer<>(TestImages.TINY_IMAGE) .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) .dependsOn(startable1, startable2) ) { @@ -58,7 +57,7 @@ public void shouldStartEveryTime() { InvocationCountingStartable startable = new InvocationCountingStartable(); try ( - GenericContainer container = new GenericContainer<>(TINY_IMAGE) + GenericContainer container = new GenericContainer<>(TestImages.TINY_IMAGE) .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) .dependsOn(startable) ) { @@ -85,7 +84,7 @@ public void shouldStartTransitiveDependencies() { startable.getDependencies().add(transitiveStartable); try ( - GenericContainer container = new GenericContainer<>(TINY_IMAGE) + GenericContainer container = new GenericContainer<>(TestImages.TINY_IMAGE) .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) .dependsOn(startable) ) { @@ -94,8 +93,16 @@ public void shouldStartTransitiveDependencies() { } VisibleAssertions.assertEquals("Root started", 1, startable.getStartInvocationCount().intValue()); - VisibleAssertions.assertEquals("Transitive started", 1, transitiveStartable.getStartInvocationCount().intValue()); - VisibleAssertions.assertEquals("Transitive of transitive started", 1, transitiveOfTransitiveStartable.getStartInvocationCount().intValue()); + VisibleAssertions.assertEquals( + "Transitive started", + 1, + transitiveStartable.getStartInvocationCount().intValue() + ); + VisibleAssertions.assertEquals( + "Transitive of transitive started", + 1, + transitiveOfTransitiveStartable.getStartInvocationCount().intValue() + ); } @Test @@ -123,7 +130,8 @@ public void shouldHandleDiamondDependencies() throws Exception { @Test public void shouldHandleParallelStream() throws Exception { - List startables = Stream.generate(InvocationCountingStartable::new) + List startables = Stream + .generate(InvocationCountingStartable::new) .limit(10) .collect(Collectors.toList()); diff --git a/core/src/test/java/org/testcontainers/junit/DockerComposeContainerScalingTest.java b/core/src/test/java/org/testcontainers/junit/DockerComposeContainerScalingTest.java index a44d8de49f4..651696696c7 100644 --- a/core/src/test/java/org/testcontainers/junit/DockerComposeContainerScalingTest.java +++ b/core/src/test/java/org/testcontainers/junit/DockerComposeContainerScalingTest.java @@ -24,25 +24,26 @@ public static void checkVersion() { } @Rule - public DockerComposeContainer environment = new DockerComposeContainer(new File("src/test/resources/scaled-compose-test.yml")) - .withScaledService("redis", 3) - .withExposedService("redis", REDIS_PORT) // implicit '_1' - .withExposedService("redis_2", REDIS_PORT) // explicit service index - .withExposedService("redis", 3, REDIS_PORT); // explicit service index via parameter + public DockerComposeContainer environment = new DockerComposeContainer( + new File("src/test/resources/scaled-compose-test.yml") + ) + .withScaledService("redis", 3) + .withExposedService("redis", REDIS_PORT) // implicit '_1' + .withExposedService("redis_2", REDIS_PORT) // explicit service index + .withExposedService("redis", 3, REDIS_PORT); // explicit service index via parameter @Before public void setupClients() { for (int i = 0; i < 3; i++) { - String name = String.format("redis_%d", i + 1); - clients[i] = new Jedis(environment.getServiceHost(name, REDIS_PORT), environment.getServicePort(name, REDIS_PORT)); + clients[i] = + new Jedis(environment.getServiceHost(name, REDIS_PORT), environment.getServicePort(name, REDIS_PORT)); } } @Test public void simpleTest() { - for (int i = 0; i < 3; i++) { clients[i].incr("somekey"); diff --git a/core/src/test/java/org/testcontainers/junit/DockerComposeContainerTest.java b/core/src/test/java/org/testcontainers/junit/DockerComposeContainerTest.java index 17a9b665513..c49663a4e35 100644 --- a/core/src/test/java/org/testcontainers/junit/DockerComposeContainerTest.java +++ b/core/src/test/java/org/testcontainers/junit/DockerComposeContainerTest.java @@ -9,10 +9,9 @@ import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; +import java.util.Collections; import java.util.Optional; -import static java.lang.String.format; -import static java.util.Collections.singletonList; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; import static org.rnorth.visibleassertions.VisibleAssertions.assertFalse; import static org.rnorth.visibleassertions.VisibleAssertions.assertNotNull; @@ -24,7 +23,9 @@ public class DockerComposeContainerTest extends BaseDockerComposeTest { @Rule - public DockerComposeContainer environment = new DockerComposeContainer(new File("src/test/resources/compose-test.yml")) + public DockerComposeContainer environment = new DockerComposeContainer( + new File("src/test/resources/compose-test.yml") + ) .withExposedService("redis_1", REDIS_PORT) .withExposedService("db_1", 3306); @@ -46,34 +47,46 @@ public void testGetServicePort() { public void shouldRetrieveContainerByServiceName() { String existingServiceName = "db_1"; Optional result = environment.getContainerByServiceName(existingServiceName); - assertTrue(format("Container should be found by service name %s", existingServiceName), result.isPresent()); - assertEquals("Mapped port for result container was wrong, probably wrong container found", result.get().getExposedPorts(), singletonList(3306)); + assertTrue( + String.format("Container should be found by service name %s", existingServiceName), + result.isPresent() + ); + assertEquals( + "Mapped port for result container was wrong, probably wrong container found", + result.get().getExposedPorts(), + Collections.singletonList(3306) + ); } @Test public void shouldReturnEmptyResultOnNoneExistingService() { String notExistingServiceName = "db_256"; Optional result = environment.getContainerByServiceName(notExistingServiceName); - assertFalse(format("No container should be found under service name %s", notExistingServiceName), result.isPresent()); + assertFalse( + String.format("No container should be found under service name %s", notExistingServiceName), + result.isPresent() + ); } @Test public void shouldCreateContainerWhenFileNotPrefixedWithPath() throws IOException { String validYaml = "version: '2.2'\n" + - "services:\n" + - " http:\n" + - " build: .\n" + - " image: python:latest\n" + - " ports:\n" + - " - 8080:8080"; + "services:\n" + + " http:\n" + + " build: .\n" + + " image: python:latest\n" + + " ports:\n" + + " - 8080:8080"; File filePathNotStartWithDotSlash = new File("docker-compose-test.yml"); filePathNotStartWithDotSlash.createNewFile(); filePathNotStartWithDotSlash.deleteOnExit(); Files.write(filePathNotStartWithDotSlash.toPath(), validYaml.getBytes(StandardCharsets.UTF_8)); - final DockerComposeContainer dockerComposeContainer = new DockerComposeContainer<>(filePathNotStartWithDotSlash); + final DockerComposeContainer dockerComposeContainer = new DockerComposeContainer<>( + filePathNotStartWithDotSlash + ); assertNotNull("Container could not be created using docker compose file", dockerComposeContainer); } } diff --git a/core/src/test/java/org/testcontainers/junit/DockerComposeContainerWithBuildTest.java b/core/src/test/java/org/testcontainers/junit/DockerComposeContainerWithBuildTest.java index 1b1efc878af..e0705a2c73b 100644 --- a/core/src/test/java/org/testcontainers/junit/DockerComposeContainerWithBuildTest.java +++ b/core/src/test/java/org/testcontainers/junit/DockerComposeContainerWithBuildTest.java @@ -14,30 +14,32 @@ import java.util.stream.Stream; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.testcontainers.containers.DockerComposeContainer.RemoveImages.ALL; -import static org.testcontainers.containers.DockerComposeContainer.RemoveImages.LOCAL; @RunWith(Parameterized.class) public class DockerComposeContainerWithBuildTest { - public DockerComposeContainerWithBuildTest(final DockerComposeContainer.RemoveImages removeMode, - final boolean shouldBuiltImageBePresentAfterRunning, - final boolean shouldPulledImageBePresentAfterRunning) { + public DockerComposeContainerWithBuildTest( + final DockerComposeContainer.RemoveImages removeMode, + final boolean shouldBuiltImageBePresentAfterRunning, + final boolean shouldPulledImageBePresentAfterRunning + ) { this.removeMode = removeMode; this.shouldBuiltImageBePresentAfterRunning = shouldBuiltImageBePresentAfterRunning; this.shouldPulledImageBePresentAfterRunning = shouldPulledImageBePresentAfterRunning; } public final DockerComposeContainer.RemoveImages removeMode; + public final boolean shouldBuiltImageBePresentAfterRunning; + public final boolean shouldPulledImageBePresentAfterRunning; @Parameterized.Parameters(name = "removeMode = {0}") public static Object[][] params() { - return new Object[][]{ - {null, true, true}, - {LOCAL, false, true}, - {ALL, false, false}, + return new Object[][] { + { null, true, true }, + { DockerComposeContainer.RemoveImages.LOCAL, false, true }, + { DockerComposeContainer.RemoveImages.ALL, false, false }, }; } @@ -47,11 +49,12 @@ public void performTest() { final AtomicReference builtImageName = new AtomicReference<>(""); final AtomicReference pulledImageName = new AtomicReference<>(""); - try (DockerComposeContainer environment = new DockerComposeContainer<>(composeFile) - .withExposedService("customredis", 6379) - .withBuild(true) - .withRemoveImages(removeMode)) { - + try ( + DockerComposeContainer environment = new DockerComposeContainer<>(composeFile) + .withExposedService("customredis", 6379) + .withBuild(true) + .withRemoveImages(removeMode) + ) { environment.start(); builtImageName.set(imageNameForRunningContainer("_customredis_1")); @@ -63,21 +66,42 @@ public void performTest() { assertEquals("the pulled image is present while running", true, isPulledImagePresentWhileRunning); } - Unreliables.retryUntilSuccess(10, TimeUnit.SECONDS, () -> { - final boolean isBuiltImagePresentAfterRunning = isImagePresent(builtImageName.get()); - assertEquals("the built image is not present after running", shouldBuiltImageBePresentAfterRunning, isBuiltImagePresentAfterRunning); - return null; - }); - - Unreliables.retryUntilSuccess(10, TimeUnit.SECONDS, () -> { - final boolean isPulledImagePresentAfterRunning = isImagePresent(pulledImageName.get()); - assertEquals("the pulled image is present after running", shouldPulledImageBePresentAfterRunning, isPulledImagePresentAfterRunning); - return null; - }); + Unreliables.retryUntilSuccess( + 10, + TimeUnit.SECONDS, + () -> { + final boolean isBuiltImagePresentAfterRunning = isImagePresent(builtImageName.get()); + assertEquals( + "the built image is not present after running", + shouldBuiltImageBePresentAfterRunning, + isBuiltImagePresentAfterRunning + ); + return null; + } + ); + + Unreliables.retryUntilSuccess( + 10, + TimeUnit.SECONDS, + () -> { + final boolean isPulledImagePresentAfterRunning = isImagePresent(pulledImageName.get()); + assertEquals( + "the pulled image is present after running", + shouldPulledImageBePresentAfterRunning, + isPulledImagePresentAfterRunning + ); + return null; + } + ); } private String imageNameForRunningContainer(final String containerNameSuffix) { - return DockerClientFactory.instance().client().listContainersCmd().exec().stream() + return DockerClientFactory + .instance() + .client() + .listContainersCmd() + .exec() + .stream() .filter(it -> Stream.of(it.getNames()).anyMatch(name -> name.endsWith(containerNameSuffix))) .findFirst() .map(Container::getImage) @@ -85,7 +109,13 @@ private String imageNameForRunningContainer(final String containerNameSuffix) { } private boolean isImagePresent(final String imageName) { - return DockerClientFactory.instance().client().listImagesCmd().withImageNameFilter(imageName).exec().stream() + return DockerClientFactory + .instance() + .client() + .listImagesCmd() + .withImageNameFilter(imageName) + .exec() + .stream() .findFirst() .isPresent(); } diff --git a/core/src/test/java/org/testcontainers/junit/DockerComposeContainerWithOptionsTest.java b/core/src/test/java/org/testcontainers/junit/DockerComposeContainerWithOptionsTest.java index 375c9baa070..77e60580b39 100644 --- a/core/src/test/java/org/testcontainers/junit/DockerComposeContainerWithOptionsTest.java +++ b/core/src/test/java/org/testcontainers/junit/DockerComposeContainerWithOptionsTest.java @@ -17,7 +17,12 @@ @RunWith(Parameterized.class) public class DockerComposeContainerWithOptionsTest { - public DockerComposeContainerWithOptionsTest(final File composeFile, final boolean local, final Set options, final boolean expectError) { + public DockerComposeContainerWithOptionsTest( + final File composeFile, + final boolean local, + final Set options, + final boolean expectError + ) { this.composeFile = composeFile; this.local = local; this.options = options; @@ -25,38 +30,60 @@ public DockerComposeContainerWithOptionsTest(final File composeFile, final boole } private final File composeFile; + private final boolean local; private final Set options; - private final boolean expectError; - @Parameterized.Parameters(name = "docker-compose test [compose file: {0}, local: {1}, options: {2}, expected result: {3}]") + private final boolean expectError; + @Parameterized.Parameters( + name = "docker-compose test [compose file: {0}, local: {1}, options: {2}, expected result: {3}]" + ) public static Object[][] params() { - return new Object[][]{ + return new Object[][] { // Test the happy day case. THe compatibility option should be accepted by docker-compose. - {new File("src/test/resources/compose-options-test/with-deploy-block.yml"), false, ImmutableSet.of("--compatibility"), false}, + { + new File("src/test/resources/compose-options-test/with-deploy-block.yml"), + false, + ImmutableSet.of("--compatibility"), + false, + }, // Test with flags absent. Docker compose will warn but continue, ignoring the deploy block. - {new File("src/test/resources/compose-options-test/with-deploy-block.yml"), false, ImmutableSet.of(""), false}, + { + new File("src/test/resources/compose-options-test/with-deploy-block.yml"), + false, + ImmutableSet.of(""), + false, + }, // Test with a bad option. Compose will complain. - {new File("src/test/resources/compose-options-test/with-deploy-block.yml"), false, ImmutableSet.of("--bad-option"), true}, + { + new File("src/test/resources/compose-options-test/with-deploy-block.yml"), + false, + ImmutableSet.of("--bad-option"), + true, + }, // Local compose - {new File("src/test/resources/compose-options-test/with-deploy-block.yml"), true, ImmutableSet.of("--compatibility"), false}, + { + new File("src/test/resources/compose-options-test/with-deploy-block.yml"), + true, + ImmutableSet.of("--compatibility"), + false, + }, }; } @Test public void performTest() { - - try (DockerComposeContainer environment = new DockerComposeContainer<>(composeFile) + try ( + DockerComposeContainer environment = new DockerComposeContainer<>(composeFile) .withOptions(options.stream().toArray(String[]::new)) - .withLocalCompose(local)) { + .withLocalCompose(local) + ) { environment.start(); assertThat(expectError).isEqualTo(false); } catch (Exception e) { assertThat(expectError).isEqualTo(true); } - } - } diff --git a/core/src/test/java/org/testcontainers/junit/DockerComposeErrorHandlingTest.java b/core/src/test/java/org/testcontainers/junit/DockerComposeErrorHandlingTest.java index b8e26ffd3f1..51f09bdb095 100644 --- a/core/src/test/java/org/testcontainers/junit/DockerComposeErrorHandlingTest.java +++ b/core/src/test/java/org/testcontainers/junit/DockerComposeErrorHandlingTest.java @@ -10,11 +10,15 @@ public class DockerComposeErrorHandlingTest { @Test public void simpleTest() { - VisibleAssertions.assertThrows("starting with an invalid docker-compose file throws an exception", + VisibleAssertions.assertThrows( + "starting with an invalid docker-compose file throws an exception", IllegalArgumentException.class, - () -> { - DockerComposeContainer environment = new DockerComposeContainer(new File("src/test/resources/invalid-compose.yml")) - .withExposedService("something", 123); - }); + () -> { + DockerComposeContainer environment = new DockerComposeContainer( + new File("src/test/resources/invalid-compose.yml") + ) + .withExposedService("something", 123); + } + ); } } diff --git a/core/src/test/java/org/testcontainers/junit/DockerComposeLocalImageTest.java b/core/src/test/java/org/testcontainers/junit/DockerComposeLocalImageTest.java index 890fa34b0f8..dd2054e1e2e 100644 --- a/core/src/test/java/org/testcontainers/junit/DockerComposeLocalImageTest.java +++ b/core/src/test/java/org/testcontainers/junit/DockerComposeLocalImageTest.java @@ -14,7 +14,9 @@ public class DockerComposeLocalImageTest { public void usesLocalImageEvenWhenPullFails() throws InterruptedException { tagImage("redis:4.0.10", "redis-local", "latest"); - DockerComposeContainer composeContainer = new DockerComposeContainer(new File("src/test/resources/local-compose-test.yml")) + DockerComposeContainer composeContainer = new DockerComposeContainer( + new File("src/test/resources/local-compose-test.yml") + ) .withExposedService("redis", 6379); composeContainer.start(); } diff --git a/core/src/test/java/org/testcontainers/junit/DockerComposeLogConsumerTest.java b/core/src/test/java/org/testcontainers/junit/DockerComposeLogConsumerTest.java index 6cd90d3cd9c..97e686a9b92 100644 --- a/core/src/test/java/org/testcontainers/junit/DockerComposeLogConsumerTest.java +++ b/core/src/test/java/org/testcontainers/junit/DockerComposeLogConsumerTest.java @@ -3,26 +3,36 @@ import org.junit.Test; import org.junit.runner.Description; import org.testcontainers.containers.DockerComposeContainer; +import org.testcontainers.containers.output.OutputFrame.OutputType; import org.testcontainers.containers.output.WaitingConsumer; import java.io.File; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import static org.testcontainers.containers.output.OutputFrame.OutputType.STDOUT; - public class DockerComposeLogConsumerTest { @Test public void testLogConsumer() throws TimeoutException { WaitingConsumer logConsumer = new WaitingConsumer(); - DockerComposeContainer environment = new DockerComposeContainer(new File("src/test/resources/v2-compose-test.yml")) + DockerComposeContainer environment = new DockerComposeContainer( + new File("src/test/resources/v2-compose-test.yml") + ) .withExposedService("redis_1", 6379) .withLogConsumer("redis_1", logConsumer); try { environment.starting(Description.EMPTY); - logConsumer.waitUntil(frame -> frame.getType() == STDOUT && frame.getUtf8String().contains("Ready to accept connections"), 5, TimeUnit.SECONDS); + logConsumer.waitUntil( + frame -> { + return ( + frame.getType() == OutputType.STDOUT && + frame.getUtf8String().contains("Ready to accept connections") + ); + }, + 5, + TimeUnit.SECONDS + ); } finally { environment.finished(Description.EMPTY); } diff --git a/core/src/test/java/org/testcontainers/junit/DockerComposePassthroughTest.java b/core/src/test/java/org/testcontainers/junit/DockerComposePassthroughTest.java index cdeea00d201..c026bcaa741 100644 --- a/core/src/test/java/org/testcontainers/junit/DockerComposePassthroughTest.java +++ b/core/src/test/java/org/testcontainers/junit/DockerComposePassthroughTest.java @@ -13,7 +13,7 @@ import java.util.Arrays; import java.util.Objects; -import static org.hamcrest.CoreMatchers.hasItem; +import static org.hamcrest.Matchers.hasItem; import static org.rnorth.visibleassertions.VisibleAssertions.assertNotNull; import static org.rnorth.visibleassertions.VisibleAssertions.assertThat; @@ -30,25 +30,27 @@ public static void checkVersion() { } @Rule - public DockerComposeContainer compose = - new DockerComposeContainer(new File("src/test/resources/v2-compose-test-passthrough.yml")) - .withEnv("foo", "bar") - .withExposedService("alpine_1", 3000, waitStrategy); - + public DockerComposeContainer compose = new DockerComposeContainer( + new File("src/test/resources/v2-compose-test-passthrough.yml") + ) + .withEnv("foo", "bar") + .withExposedService("alpine_1", 3000, waitStrategy); @Test public void testContainerInstanceProperties() { final ContainerState container = waitStrategy.getContainer(); //check environment variable was set - assertThat("Environment variable set correctly", Arrays.asList(Objects.requireNonNull(container.getContainerInfo() - .getConfig().getEnv())), hasItem("bar=bar")); + assertThat( + "Environment variable set correctly", + Arrays.asList(Objects.requireNonNull(container.getContainerInfo().getConfig().getEnv())), + hasItem("bar=bar") + ); //check other container properties assertNotNull("Container id is not null", container.getContainerId()); assertNotNull("Port mapped", container.getMappedPort(3000)); assertThat("Exposed Ports", container.getExposedPorts(), hasItem(3000)); - } /* diff --git a/core/src/test/java/org/testcontainers/junit/DockerComposeServiceTest.java b/core/src/test/java/org/testcontainers/junit/DockerComposeServiceTest.java index ec8c50d42ab..5e6ba4cc30f 100644 --- a/core/src/test/java/org/testcontainers/junit/DockerComposeServiceTest.java +++ b/core/src/test/java/org/testcontainers/junit/DockerComposeServiceTest.java @@ -4,17 +4,18 @@ import org.junit.Test; import org.testcontainers.containers.DockerComposeContainer; -import static org.rnorth.visibleassertions.VisibleAssertions.assertNotNull; - import java.io.File; +import static org.rnorth.visibleassertions.VisibleAssertions.assertNotNull; + public class DockerComposeServiceTest extends BaseDockerComposeTest { @Rule - public DockerComposeContainer environment = new DockerComposeContainer(new File("src/test/resources/compose-test.yml")) - .withServices("redis") - .withExposedService("redis_1", REDIS_PORT); - + public DockerComposeContainer environment = new DockerComposeContainer( + new File("src/test/resources/compose-test.yml") + ) + .withServices("redis") + .withExposedService("redis_1", REDIS_PORT); @Override protected DockerComposeContainer getEnvironment() { diff --git a/core/src/test/java/org/testcontainers/junit/DockerComposeV2FormatTest.java b/core/src/test/java/org/testcontainers/junit/DockerComposeV2FormatTest.java index bed19bd0d09..f3dc2ee3cc8 100644 --- a/core/src/test/java/org/testcontainers/junit/DockerComposeV2FormatTest.java +++ b/core/src/test/java/org/testcontainers/junit/DockerComposeV2FormatTest.java @@ -11,8 +11,10 @@ public class DockerComposeV2FormatTest extends BaseDockerComposeTest { @Rule - public DockerComposeContainer environment = new DockerComposeContainer(new File("src/test/resources/v2-compose-test.yml")) - .withExposedService("redis_1", REDIS_PORT); + public DockerComposeContainer environment = new DockerComposeContainer( + new File("src/test/resources/v2-compose-test.yml") + ) + .withExposedService("redis_1", REDIS_PORT); @Override protected DockerComposeContainer getEnvironment() { diff --git a/core/src/test/java/org/testcontainers/junit/DockerComposeV2WithNetworkTest.java b/core/src/test/java/org/testcontainers/junit/DockerComposeV2WithNetworkTest.java index 26764476687..b28dfc3ee51 100644 --- a/core/src/test/java/org/testcontainers/junit/DockerComposeV2WithNetworkTest.java +++ b/core/src/test/java/org/testcontainers/junit/DockerComposeV2WithNetworkTest.java @@ -1,21 +1,20 @@ package org.testcontainers.junit; -import java.io.File; - import org.junit.Rule; import org.testcontainers.containers.DockerComposeContainer; +import java.io.File; + public class DockerComposeV2WithNetworkTest extends BaseDockerComposeTest { @Rule - public DockerComposeContainer environment = new DockerComposeContainer(new File("src/test/resources/v2-compose-test-with-network.yml")) - .withExposedService("redis_1", REDIS_PORT); + public DockerComposeContainer environment = new DockerComposeContainer( + new File("src/test/resources/v2-compose-test-with-network.yml") + ) + .withExposedService("redis_1", REDIS_PORT); @Override protected DockerComposeContainer getEnvironment() { return environment; } - - - } diff --git a/core/src/test/java/org/testcontainers/junit/DockerComposeWaitStrategyTest.java b/core/src/test/java/org/testcontainers/junit/DockerComposeWaitStrategyTest.java index 32465010700..a30c716964e 100644 --- a/core/src/test/java/org/testcontainers/junit/DockerComposeWaitStrategyTest.java +++ b/core/src/test/java/org/testcontainers/junit/DockerComposeWaitStrategyTest.java @@ -1,10 +1,5 @@ package org.testcontainers.junit; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.catchThrowable; - -import java.io.File; -import java.time.Duration; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -15,6 +10,12 @@ import org.testcontainers.containers.wait.strategy.Wait; import org.testcontainers.containers.wait.strategy.WaitStrategy; +import java.io.File; +import java.time.Duration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; + public class DockerComposeWaitStrategyTest { private static final int REDIS_PORT = 6379; @@ -23,8 +24,7 @@ public class DockerComposeWaitStrategyTest { @Before public final void setUp() { - environment = new DockerComposeContainer<>( - new File("src/test/resources/compose-test.yml")); + environment = new DockerComposeContainer<>(new File("src/test/resources/compose-test.yml")); } @After @@ -34,14 +34,15 @@ public final void cleanUp() { @Test public void testWaitOnListeningPort() { - environment - .withExposedService("redis_1", REDIS_PORT, Wait.forListeningPort()); + environment.withExposedService("redis_1", REDIS_PORT, Wait.forListeningPort()); try { environment.starting(Description.createTestDescription(Object.class, "name")); VisibleAssertions.pass("Docker compose should start after waiting for listening port"); } catch (RuntimeException e) { - VisibleAssertions.fail("Docker compose should start after waiting for listening port with failed with: " + e); + VisibleAssertions.fail( + "Docker compose should start after waiting for listening port with failed with: " + e + ); } } @@ -56,29 +57,45 @@ public void testWaitOnMultipleStrategiesPassing() { environment.starting(Description.createTestDescription(Object.class, "name")); VisibleAssertions.pass("Docker compose should start after waiting for listening port"); } catch (RuntimeException e) { - VisibleAssertions.fail("Docker compose should start after waiting for listening port with failed with: " + e); + VisibleAssertions.fail( + "Docker compose should start after waiting for listening port with failed with: " + e + ); } } @Test public void testWaitingFails() { - environment - .withExposedService("redis_1", REDIS_PORT, Wait.forHttp("/test").withStartupTimeout(Duration.ofSeconds(10))); - VisibleAssertions.assertThrows("waiting on an invalid http path times out", + environment.withExposedService( + "redis_1", + REDIS_PORT, + Wait.forHttp("/test").withStartupTimeout(Duration.ofSeconds(10)) + ); + VisibleAssertions.assertThrows( + "waiting on an invalid http path times out", RuntimeException.class, - () -> environment.starting(Description.createTestDescription(Object.class, "name"))); + () -> environment.starting(Description.createTestDescription(Object.class, "name")) + ); } @Test public void testWaitOnOneOfMultipleStrategiesFailing() { environment - .withExposedService("redis_1", REDIS_PORT, Wait.forListeningPort().withStartupTimeout(Duration.ofSeconds(10))) - .waitingFor("db_1", Wait.forLogMessage(".*test test test.*\\s", 1).withStartupTimeout(Duration.ofSeconds(10))) + .withExposedService( + "redis_1", + REDIS_PORT, + Wait.forListeningPort().withStartupTimeout(Duration.ofSeconds(10)) + ) + .waitingFor( + "db_1", + Wait.forLogMessage(".*test test test.*\\s", 1).withStartupTimeout(Duration.ofSeconds(10)) + ) .withTailChildContainers(true); - VisibleAssertions.assertThrows("waiting on one failing strategy to time out", + VisibleAssertions.assertThrows( + "waiting on one failing strategy to time out", RuntimeException.class, - () -> environment.starting(Description.createTestDescription(Object.class, "name"))); + () -> environment.starting(Description.createTestDescription(Object.class, "name")) + ); } @Test @@ -93,14 +110,11 @@ public void testWaitingForNonexistentServices() { .waitingFor(nonexistentServiceName1, someWaitStrategy) .waitingFor(nonexistentServiceName2, someWaitStrategy); - Throwable thrownWhenRequestedToWaitForNonexistentService = - catchThrowable(environment::start); + Throwable thrownWhenRequestedToWaitForNonexistentService = catchThrowable(environment::start); assertThat(thrownWhenRequestedToWaitForNonexistentService) .isInstanceOf(IllegalStateException.class) - .hasMessageContaining( - nonexistentServiceName1, - nonexistentServiceName2) + .hasMessageContaining(nonexistentServiceName1, nonexistentServiceName2) .hasMessageNotContaining(existentServiceName); } } diff --git a/core/src/test/java/org/testcontainers/junit/DockerNetworkModeTest.java b/core/src/test/java/org/testcontainers/junit/DockerNetworkModeTest.java index 59c2a63127c..7962b87e157 100644 --- a/core/src/test/java/org/testcontainers/junit/DockerNetworkModeTest.java +++ b/core/src/test/java/org/testcontainers/junit/DockerNetworkModeTest.java @@ -3,12 +3,12 @@ import com.github.dockerjava.api.model.NetworkSettings; import lombok.extern.slf4j.Slf4j; import org.junit.Test; +import org.testcontainers.TestImages; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.startupcheck.OneShotStartupCheckStrategy; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; -import static org.testcontainers.TestImages.TINY_IMAGE; /** * Simple tests of named network modes - more may be possible, but may not be reproducible @@ -20,7 +20,7 @@ public class DockerNetworkModeTest { @Test public void testNoNetworkContainer() { try ( - GenericContainer container = new GenericContainer<>(TINY_IMAGE) + GenericContainer container = new GenericContainer<>(TestImages.TINY_IMAGE) .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) .withCommand("true") .withNetworkMode("none") @@ -36,7 +36,7 @@ public void testNoNetworkContainer() { @Test public void testHostNetworkContainer() { try ( - GenericContainer container = new GenericContainer<>(TINY_IMAGE) + GenericContainer container = new GenericContainer<>(TestImages.TINY_IMAGE) .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) .withCommand("true") .withNetworkMode("host") diff --git a/core/src/test/java/org/testcontainers/junit/DockerfileContainerTest.java b/core/src/test/java/org/testcontainers/junit/DockerfileContainerTest.java index 6ad7c3ea26e..715ba3f64a1 100644 --- a/core/src/test/java/org/testcontainers/junit/DockerfileContainerTest.java +++ b/core/src/test/java/org/testcontainers/junit/DockerfileContainerTest.java @@ -21,13 +21,16 @@ public class DockerfileContainerTest { @Rule public GenericContainer dslContainer = new GenericContainer( - new ImageFromDockerfile("tcdockerfile/nginx", false).withDockerfileFromBuilder(builder -> { - builder - .from("alpine:3.2") - .run("apk add --update nginx") - .cmd("nginx", "-g", "daemon off;") - .build(); })) - .withExposedPorts(80); + new ImageFromDockerfile("tcdockerfile/nginx", false) + .withDockerfileFromBuilder(builder -> { + builder + .from("alpine:3.2") // + .run("apk add --update nginx") + .cmd("nginx", "-g", "daemon off;") + .build(); + }) + ) + .withExposedPorts(80); @Test public void simpleDslTest() throws IOException { @@ -37,11 +40,15 @@ public void simpleDslTest() throws IOException { HttpGet get = new HttpGet(address); try (CloseableHttpResponse response = httpClient.execute(get)) { - assertEquals("A container built from a dockerfile can run nginx as expected, and returns a good status code", - 200, - response.getStatusLine().getStatusCode()); - assertTrue("A container built from a dockerfile can run nginx as expected, and returns an expected Server header", - response.getHeaders("Server")[0].getValue().contains("nginx")); + assertEquals( + "A container built from a dockerfile can run nginx as expected, and returns a good status code", + 200, + response.getStatusLine().getStatusCode() + ); + assertTrue( + "A container built from a dockerfile can run nginx as expected, and returns an expected Server header", + response.getHeaders("Server")[0].getValue().contains("nginx") + ); } } } diff --git a/core/src/test/java/org/testcontainers/junit/DockerfileTest.java b/core/src/test/java/org/testcontainers/junit/DockerfileTest.java index 2197d7be0e7..e121e7db5a4 100644 --- a/core/src/test/java/org/testcontainers/junit/DockerfileTest.java +++ b/core/src/test/java/org/testcontainers/junit/DockerfileTest.java @@ -5,6 +5,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.output.OutputFrame; import org.testcontainers.containers.output.WaitingConsumer; import org.testcontainers.containers.startupcheck.OneShotStartupCheckStrategy; import org.testcontainers.images.builder.ImageFromDockerfile; @@ -16,7 +17,6 @@ import java.util.concurrent.TimeoutException; import static org.rnorth.visibleassertions.VisibleAssertions.pass; -import static org.testcontainers.containers.output.OutputFrame.OutputType.STDOUT; public class DockerfileTest { @@ -25,9 +25,9 @@ public class DockerfileTest { @Test public void simpleDockerfileWorks() { ImageFromDockerfile image = new ImageFromDockerfile() - .withFileFromString("folder/someFile.txt", "hello") - .withFileFromClasspath("test.txt", "mappable-resource/test-resource.txt") - .withFileFromClasspath("Dockerfile", "mappable-dockerfile/Dockerfile"); + .withFileFromString("folder/someFile.txt", "hello") + .withFileFromClasspath("test.txt", "mappable-resource/test-resource.txt") + .withFileFromClasspath("Dockerfile", "mappable-dockerfile/Dockerfile"); verifyImage(image); } @@ -40,9 +40,9 @@ protected void configure(BuildImageCmd buildImageCmd) { super.configure(buildImageCmd); List dockerfile = Arrays.asList( - "FROM alpine:3.14", - "RUN echo 'hello from Docker build process'", - "CMD yes" + "FROM alpine:3.14", + "RUN echo 'hello from Docker build process'", + "CMD yes" ); withFileFromString("Dockerfile", String.join("\n", dockerfile)); @@ -56,28 +56,30 @@ protected void configure(BuildImageCmd buildImageCmd) { @Test public void dockerfileBuilderWorks() { ImageFromDockerfile image = new ImageFromDockerfile() - .withFileFromClasspath("test.txt", "mappable-resource/test-resource.txt") - .withFileFromString("folder/someFile.txt", "hello") - .withDockerfileFromBuilder(builder -> builder - .from("alpine:3.14") - .workDir("/app") - .add("test.txt", "test file.txt") - .run("ls", "-la", "/app/test file.txt") - .copy("folder/someFile.txt", "/someFile.txt") - .expose(80, 8080) - .cmd("while true; do cat /someFile.txt | nc -l -p 80; done") - ); + .withFileFromClasspath("test.txt", "mappable-resource/test-resource.txt") + .withFileFromString("folder/someFile.txt", "hello") + .withDockerfileFromBuilder(builder -> { + builder + .from("alpine:3.14") + .workDir("/app") + .add("test.txt", "test file.txt") + .run("ls", "-la", "/app/test file.txt") + .copy("folder/someFile.txt", "/someFile.txt") + .expose(80, 8080) + .cmd("while true; do cat /someFile.txt | nc -l -p 80; done"); + }); verifyImage(image); } @Test public void filePermissions() throws TimeoutException { - WaitingConsumer consumer = new WaitingConsumer(); ImageFromDockerfile image = new ImageFromDockerfile() - .withFileFromTransferable("/someFile.txt", new Transferable() { + .withFileFromTransferable( + "/someFile.txt", + new Transferable() { @Override public long getSize() { return 0; @@ -97,23 +99,27 @@ public String getDescription() { public int getFileMode() { return 0123; } - - - }) - .withDockerfileFromBuilder(builder -> builder - .from("alpine:3.14") - .copy("someFile.txt", "/someFile.txt") - .cmd("stat -c \"%a\" /someFile.txt") - ); + } + ) + .withDockerfileFromBuilder(builder -> { + builder + .from("alpine:3.14") // + .copy("someFile.txt", "/someFile.txt") + .cmd("stat -c \"%a\" /someFile.txt"); + }); GenericContainer container = new GenericContainer(image) - .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) - .withLogConsumer(consumer); + .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) + .withLogConsumer(consumer); try { container.start(); - consumer.waitUntil(frame -> frame.getType() == STDOUT && frame.getUtf8String().contains("123"), 5, TimeUnit.SECONDS); + consumer.waitUntil( + frame -> frame.getType() == OutputFrame.OutputType.STDOUT && frame.getUtf8String().contains("123"), + 5, + TimeUnit.SECONDS + ); } finally { container.stop(); } diff --git a/core/src/test/java/org/testcontainers/junit/FileOperationsTest.java b/core/src/test/java/org/testcontainers/junit/FileOperationsTest.java index beca4a8a49f..e61721f80c4 100644 --- a/core/src/test/java/org/testcontainers/junit/FileOperationsTest.java +++ b/core/src/test/java/org/testcontainers/junit/FileOperationsTest.java @@ -7,6 +7,7 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; +import org.testcontainers.TestImages; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.startupcheck.OneShotStartupCheckStrategy; import org.testcontainers.utility.MountableFile; @@ -16,7 +17,6 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; -import static org.testcontainers.TestImages.ALPINE_IMAGE; public class FileOperationsTest { @@ -26,7 +26,7 @@ public class FileOperationsTest { @Test public void copyFileToContainerFileTest() throws Exception { try ( - GenericContainer alpineCopyToContainer = new GenericContainer(ALPINE_IMAGE) + GenericContainer alpineCopyToContainer = new GenericContainer(TestImages.ALPINE_IMAGE) // .withCommand("top") ) { alpineCopyToContainer.start(); @@ -44,7 +44,7 @@ public void copyFileToContainerFileTest() throws Exception { @Test public void copyFileToContainerFolderTest() throws Exception { try ( - GenericContainer alpineCopyToContainer = new GenericContainer(ALPINE_IMAGE) + GenericContainer alpineCopyToContainer = new GenericContainer(TestImages.ALPINE_IMAGE) // .withCommand("top") ) { alpineCopyToContainer.start(); @@ -62,10 +62,9 @@ public void copyFileToContainerFolderTest() throws Exception { @Test public void copyFolderToContainerFolderTest() throws Exception { try ( - GenericContainer alpineCopyToContainer = new GenericContainer(ALPINE_IMAGE) + GenericContainer alpineCopyToContainer = new GenericContainer(TestImages.ALPINE_IMAGE) // .withCommand("top") ) { - alpineCopyToContainer.start(); final MountableFile mountableFile = MountableFile.forClasspathResource("mappable-resource/"); alpineCopyToContainer.copyFileToContainer(mountableFile, "/home/test/"); @@ -81,7 +80,7 @@ public void copyFolderToContainerFolderTest() throws Exception { @Test(expected = NotFoundException.class) public void copyFromContainerShouldFailBecauseNoFileTest() throws NotFoundException { try ( - GenericContainer alpineCopyToContainer = new GenericContainer(ALPINE_IMAGE) + GenericContainer alpineCopyToContainer = new GenericContainer(TestImages.ALPINE_IMAGE) // .withCommand("top") ) { alpineCopyToContainer.start(); @@ -92,10 +91,9 @@ public void copyFromContainerShouldFailBecauseNoFileTest() throws NotFoundExcept @Test public void shouldCopyFileFromContainerTest() throws IOException { try ( - GenericContainer alpineCopyToContainer = new GenericContainer(ALPINE_IMAGE) + GenericContainer alpineCopyToContainer = new GenericContainer(TestImages.ALPINE_IMAGE) // .withCommand("top") ) { - alpineCopyToContainer.start(); final MountableFile mountableFile = MountableFile.forClasspathResource("test_copy_to_container.txt"); alpineCopyToContainer.copyFileToContainer(mountableFile, "/home/"); @@ -110,7 +108,7 @@ public void shouldCopyFileFromContainerTest() throws IOException { @Test public void copyFileOperationsShouldFailWhenNotStartedTest() { - try (GenericContainer container = new GenericContainer<>(ALPINE_IMAGE).withCommand("top")) { + try (GenericContainer container = new GenericContainer<>(TestImages.ALPINE_IMAGE).withCommand("top")) { Assertions .assertThatThrownBy(() -> { MountableFile mountableFile = MountableFile.forClasspathResource("test_copy_to_container.txt"); @@ -121,10 +119,7 @@ public void copyFileOperationsShouldFailWhenNotStartedTest() { Assertions .assertThatThrownBy(() -> { - container.copyFileFromContainer( - "/home/test_copy_to_container.txt", - IOUtils::toByteArray - ); + container.copyFileFromContainer("/home/test_copy_to_container.txt", IOUtils::toByteArray); }) .isInstanceOf(IllegalStateException.class) .hasMessageContaining("can only be used when the Container is created"); @@ -134,18 +129,17 @@ public void copyFileOperationsShouldFailWhenNotStartedTest() { @Test public void shouldCopyFileFromExitedContainerTest() throws IOException { try ( - GenericContainer container = new GenericContainer<>(ALPINE_IMAGE) + GenericContainer container = new GenericContainer<>(TestImages.ALPINE_IMAGE) .withCommand("sh", "-c", "echo -n 'Hello!' > /home/file_in_container.txt") .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) ) { container.start(); - assertThat(container.getDockerClient().waitContainerCmd(container.getContainerId()).start().awaitStatusCode()) + assertThat( + container.getDockerClient().waitContainerCmd(container.getContainerId()).start().awaitStatusCode() + ) .isEqualTo(0); - container.copyFileFromContainer( - "/home/file_in_container.txt", - IOUtils::toByteArray - ); + container.copyFileFromContainer("/home/file_in_container.txt", IOUtils::toByteArray); container.copyFileToContainer( MountableFile.forClasspathResource("test_copy_to_container.txt"), diff --git a/core/src/test/java/org/testcontainers/junit/FixedHostPortContainerTest.java b/core/src/test/java/org/testcontainers/junit/FixedHostPortContainerTest.java index 02fad53fb1e..a6bb13d160c 100644 --- a/core/src/test/java/org/testcontainers/junit/FixedHostPortContainerTest.java +++ b/core/src/test/java/org/testcontainers/junit/FixedHostPortContainerTest.java @@ -3,6 +3,7 @@ import com.google.common.util.concurrent.Uninterruptibles; import org.junit.Test; import org.rnorth.ducttape.unreliables.Unreliables; +import org.testcontainers.TestImages; import org.testcontainers.containers.FixedHostPortGenericContainer; import org.testcontainers.containers.GenericContainer; @@ -12,9 +13,8 @@ import java.net.Socket; import java.util.concurrent.TimeUnit; -import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; -import static org.testcontainers.TestImages.TINY_IMAGE; +import static org.hamcrest.Matchers.equalTo; /** * Test of {@link FixedHostPortGenericContainer}. Note that this is not an example of typical use (usually, a container @@ -39,15 +39,18 @@ public class FixedHostPortContainerTest { /** * *nix pipe to fire test response on test port */ - private static final String HTTP_ECHO_CMD = - String.format("while true; do echo \"%s\" | nc -l -p %d; done", TEST_RESPONSE, TEST_PORT); + private static final String HTTP_ECHO_CMD = String.format( + "while true; do echo \"%s\" | nc -l -p %d; done", + TEST_RESPONSE, + TEST_PORT + ); @Test public void testFixedHostPortMapping() throws IOException { // first find a free port on the docker host that will work for testing final Integer unusedHostPort; try ( - final GenericContainer echoServer = new GenericContainer(TINY_IMAGE) + final GenericContainer echoServer = new GenericContainer(TestImages.TINY_IMAGE) .withExposedPorts(TEST_PORT) .withCommand("/bin/sh", "-c", HTTP_ECHO_CMD) ) { @@ -58,15 +61,18 @@ public void testFixedHostPortMapping() throws IOException { // now starting echo server container mapped to known-as-free host port try ( final GenericContainer echoServer = new FixedHostPortGenericContainer(TEST_IMAGE) - // using workaround for port bind+expose - .withFixedExposedPort(unusedHostPort, TEST_PORT) - .withExposedPorts(TEST_PORT) - .withCommand("/bin/sh", "-c", HTTP_ECHO_CMD) + // using workaround for port bind+expose + .withFixedExposedPort(unusedHostPort, TEST_PORT) + .withExposedPorts(TEST_PORT) + .withCommand("/bin/sh", "-c", HTTP_ECHO_CMD) ) { echoServer.start(); - assertThat("Port mapping does not seem to match given fixed port", - echoServer.getMappedPort(TEST_PORT), equalTo(unusedHostPort)); + assertThat( + "Port mapping does not seem to match given fixed port", + echoServer.getMappedPort(TEST_PORT), + equalTo(unusedHostPort) + ); final String content = this.readResponse(echoServer, unusedHostPort); assertThat("Returned echo from fixed port does not match expected", content, equalTo(TEST_RESPONSE)); @@ -83,7 +89,9 @@ public void testFixedHostPortMapping() throws IOException { */ private String readResponse(GenericContainer container, Integer port) throws IOException { try ( - final BufferedReader reader = Unreliables.retryUntilSuccess(10, TimeUnit.SECONDS, + final BufferedReader reader = Unreliables.retryUntilSuccess( + 10, + TimeUnit.SECONDS, () -> { Uninterruptibles.sleepUninterruptibly(1, TimeUnit.SECONDS); final Socket socket = new Socket(container.getHost(), port); diff --git a/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java b/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java index c13421029e0..8c696585902 100644 --- a/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java +++ b/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java @@ -21,8 +21,11 @@ import org.junit.Test; import org.rnorth.ducttape.RetryCountExceededException; import org.rnorth.ducttape.unreliables.Unreliables; +import org.testcontainers.TestImages; +import org.testcontainers.containers.BindMode; import org.testcontainers.containers.Container; import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.SelinuxContext; import org.testcontainers.containers.startupcheck.OneShotStartupCheckStrategy; import org.testcontainers.utility.Base58; import org.testcontainers.utility.TestEnvironment; @@ -37,27 +40,19 @@ import java.net.Socket; import java.time.Duration; import java.util.Arrays; +import java.util.Collections; import java.util.Map; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.regex.Matcher; import java.util.regex.Pattern; -import static java.util.Collections.singletonMap; -import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.Matchers.equalTo; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; import static org.rnorth.visibleassertions.VisibleAssertions.assertFalse; import static org.rnorth.visibleassertions.VisibleAssertions.assertThat; import static org.rnorth.visibleassertions.VisibleAssertions.assertThrows; import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; -import static org.testcontainers.TestImages.ALPINE_IMAGE; -import static org.testcontainers.TestImages.MONGODB_IMAGE; -import static org.testcontainers.TestImages.RABBITMQ_IMAGE; -import static org.testcontainers.TestImages.REDIS_IMAGE; -import static org.testcontainers.TestImages.TINY_IMAGE; -import static org.testcontainers.containers.BindMode.READ_ONLY; -import static org.testcontainers.containers.BindMode.READ_WRITE; -import static org.testcontainers.containers.SelinuxContext.SHARED; /** * Tests for GenericContainerRules @@ -65,12 +60,16 @@ public class GenericContainerRuleTest { private static final int REDIS_PORT = 6379; + private static final String RABBIQMQ_TEST_EXCHANGE = "TestExchange"; + private static final String RABBITMQ_TEST_ROUTING_KEY = "TestRoutingKey"; + private static final String RABBITMQ_TEST_MESSAGE = "Hello world"; + private static final int RABBITMQ_PORT = 5672; - private static final int MONGO_PORT = 27017; + private static final int MONGO_PORT = 27017; /* * Test data setup @@ -86,77 +85,79 @@ public static void setupContent() throws FileNotFoundException { * Redis */ @ClassRule - public static GenericContainer redis = new GenericContainer<>(REDIS_IMAGE) - .withExposedPorts(REDIS_PORT); + public static GenericContainer redis = new GenericContainer<>(TestImages.REDIS_IMAGE) + .withExposedPorts(REDIS_PORT); /** * RabbitMQ */ @ClassRule - public static GenericContainer rabbitMq = new GenericContainer<>(RABBITMQ_IMAGE) - .withExposedPorts(RABBITMQ_PORT); + public static GenericContainer rabbitMq = new GenericContainer<>(TestImages.RABBITMQ_IMAGE) + .withExposedPorts(RABBITMQ_PORT); + /** * MongoDB */ @ClassRule - public static GenericContainer mongo = new GenericContainer<>(MONGODB_IMAGE) - .withExposedPorts(MONGO_PORT); + public static GenericContainer mongo = new GenericContainer<>(TestImages.MONGODB_IMAGE) + .withExposedPorts(MONGO_PORT); + /** * Pass an environment variable to the container, then run a shell script that exposes the variable in a quick and * dirty way for testing. */ @ClassRule - public static GenericContainer alpineEnvVar = new GenericContainer<>(ALPINE_IMAGE) - .withExposedPorts(80) - .withEnv("MAGIC_NUMBER", "4") - .withEnv("MAGIC_NUMBER", oldValue -> oldValue.orElse("") + "2") - .withCommand("/bin/sh", "-c", "while true; do echo \"$MAGIC_NUMBER\" | nc -l -p 80; done"); + public static GenericContainer alpineEnvVar = new GenericContainer<>(TestImages.ALPINE_IMAGE) + .withExposedPorts(80) + .withEnv("MAGIC_NUMBER", "4") + .withEnv("MAGIC_NUMBER", oldValue -> oldValue.orElse("") + "2") + .withCommand("/bin/sh", "-c", "while true; do echo \"$MAGIC_NUMBER\" | nc -l -p 80; done"); /** * Pass environment variables to the container, then run a shell script that exposes the variables in a quick and * dirty way for testing. */ @ClassRule - public static GenericContainer alpineEnvVarFromMap = new GenericContainer<>(ALPINE_IMAGE) - .withExposedPorts(80) - .withEnv(ImmutableMap.of( - "FIRST", "42", - "SECOND", "50" - )) - .withCommand("/bin/sh", "-c", "while true; do echo \"$FIRST and $SECOND\" | nc -l -p 80; done"); + public static GenericContainer alpineEnvVarFromMap = new GenericContainer<>(TestImages.ALPINE_IMAGE) + .withExposedPorts(80) + .withEnv(ImmutableMap.of("FIRST", "42", "SECOND", "50")) + .withCommand("/bin/sh", "-c", "while true; do echo \"$FIRST and $SECOND\" | nc -l -p 80; done"); /** * Map a file on the classpath to a file in the container, and then expose the content for testing. */ @ClassRule - public static GenericContainer alpineClasspathResource = new GenericContainer<>(ALPINE_IMAGE) - .withExposedPorts(80) - .withClasspathResourceMapping("mappable-resource/test-resource.txt", "/content.txt", READ_ONLY) - .withCommand("/bin/sh", "-c", "while true; do cat /content.txt | nc -l -p 80; done"); + public static GenericContainer alpineClasspathResource = new GenericContainer<>(TestImages.ALPINE_IMAGE) + .withExposedPorts(80) + .withClasspathResourceMapping("mappable-resource/test-resource.txt", "/content.txt", BindMode.READ_ONLY) + .withCommand("/bin/sh", "-c", "while true; do cat /content.txt | nc -l -p 80; done"); /** * Map a file on the classpath to a file in the container, and then expose the content for testing. */ @ClassRule - public static GenericContainer alpineClasspathResourceSelinux = new GenericContainer<>(ALPINE_IMAGE) - .withExposedPorts(80) - .withClasspathResourceMapping("mappable-resource/test-resource.txt", "/content.txt", READ_WRITE, SHARED) - .withCommand("/bin/sh", "-c", "while true; do cat /content.txt | nc -l -p 80; done"); + public static GenericContainer alpineClasspathResourceSelinux = new GenericContainer<>(TestImages.ALPINE_IMAGE) + .withExposedPorts(80) + .withClasspathResourceMapping( + "mappable-resource/test-resource.txt", + "/content.txt", + BindMode.READ_WRITE, + SelinuxContext.SHARED + ) + .withCommand("/bin/sh", "-c", "while true; do cat /content.txt | nc -l -p 80; done"); /** * Create a container with an extra host entry and expose the content of /etc/hosts for testing. */ @ClassRule - public static GenericContainer alpineExtrahost = new GenericContainer<>(ALPINE_IMAGE) - .withExposedPorts(80) - .withExtraHost("somehost", "192.168.1.10") - .withCommand("/bin/sh", "-c", "while true; do cat /etc/hosts | nc -l -p 80; done"); + public static GenericContainer alpineExtrahost = new GenericContainer<>(TestImages.ALPINE_IMAGE) + .withExposedPorts(80) + .withExtraHost("somehost", "192.168.1.10") + .withCommand("/bin/sh", "-c", "while true; do cat /etc/hosts | nc -l -p 80; done"); @Test public void testIsRunning() { - try ( - GenericContainer container = new GenericContainer<>(TINY_IMAGE) - .withCommand("top")) { + try (GenericContainer container = new GenericContainer<>(TestImages.TINY_IMAGE).withCommand("top")) { assertFalse("Container is not started and not running", container.isRunning()); container.start(); assertTrue("Container is started and running", container.isRunning()); @@ -166,16 +167,19 @@ public void testIsRunning() { @Test public void withTmpFsTest() throws Exception { try ( - GenericContainer container = new GenericContainer<>(TINY_IMAGE) + GenericContainer container = new GenericContainer<>(TestImages.TINY_IMAGE) .withCommand("top") - .withTmpFs(singletonMap("/testtmpfs", "rw")) + .withTmpFs(Collections.singletonMap("/testtmpfs", "rw")) ) { container.start(); // check file doesn't exist String path = "/testtmpfs/test.file"; Container.ExecResult execResult = container.execInContainer("ls", path); - assertEquals("tmpfs inside container works fine", execResult.getStderr(), - "ls: /testtmpfs/test.file: No such file or directory\n"); + assertEquals( + "tmpfs inside container works fine", + execResult.getStderr(), + "ls: /testtmpfs/test.file: No such file or directory\n" + ); // touch && check file does exist container.execInContainer("touch", path); execResult = container.execInContainer("ls", path); @@ -183,7 +187,6 @@ public void withTmpFsTest() throws Exception { } } - @Test public void simpleRabbitMqTest() throws IOException, TimeoutException { ConnectionFactory factory = new ConnectionFactory(); @@ -198,23 +201,39 @@ public void simpleRabbitMqTest() throws IOException, TimeoutException { // Set up a consumer on the queue final boolean[] messageWasReceived = new boolean[1]; - channel.basicConsume(queueName, false, new DefaultConsumer(channel) { - @Override - public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException { - messageWasReceived[0] = Arrays.equals(body, RABBITMQ_TEST_MESSAGE.getBytes()); + channel.basicConsume( + queueName, + false, + new DefaultConsumer(channel) { + @Override + public void handleDelivery( + String consumerTag, + Envelope envelope, + AMQP.BasicProperties properties, + byte[] body + ) throws IOException { + messageWasReceived[0] = Arrays.equals(body, RABBITMQ_TEST_MESSAGE.getBytes()); + } } - }); + ); // post a message channel.basicPublish(RABBIQMQ_TEST_EXCHANGE, RABBITMQ_TEST_ROUTING_KEY, null, RABBITMQ_TEST_MESSAGE.getBytes()); // check the message was received - assertTrue("The message was received", Unreliables.retryUntilSuccess(5, TimeUnit.SECONDS, () -> { - if (!messageWasReceived[0]) { - throw new IllegalStateException("Message not received yet"); - } - return true; - })); + assertTrue( + "The message was received", + Unreliables.retryUntilSuccess( + 5, + TimeUnit.SECONDS, + () -> { + if (!messageWasReceived[0]) { + throw new IllegalStateException("Message not received yet"); + } + return true; + } + ) + ); } @Test @@ -223,8 +242,7 @@ public void simpleMongoDbTest() { MongoDatabase database = mongoClient.getDatabase("test"); MongoCollection collection = database.getCollection("testCollection"); - Document doc = new Document("name", "foo") - .append("value", 1); + Document doc = new Document("name", "foo").append("value", 1); collection.insertOne(doc); Document doc2 = collection.find(new Document("name", "foo")).first(); @@ -247,10 +265,11 @@ public void environmentFromMapTest() throws IOException { @Test public void customLabelTest() { - try (final GenericContainer alpineCustomLabel = new GenericContainer<>(ALPINE_IMAGE) - .withLabel("our.custom", "label") - .withCommand("top")) { - + try ( + final GenericContainer alpineCustomLabel = new GenericContainer<>(TestImages.ALPINE_IMAGE) + .withLabel("our.custom", "label") + .withCommand("top") + ) { alpineCustomLabel.start(); Map labels = alpineCustomLabel.getCurrentContainerInfo().getConfig().getLabels(); @@ -262,11 +281,11 @@ public void customLabelTest() { @Test public void exceptionThrownWhenTryingToOverrideTestcontainersLabels() { - assertThrows("When trying to overwrite an 'org.testcontainers' label, withLabel() throws an exception", + assertThrows( + "When trying to overwrite an 'org.testcontainers' label, withLabel() throws an exception", IllegalArgumentException.class, () -> { - new GenericContainer<>(ALPINE_IMAGE) - .withLabel("org.testcontainers.foo", "false"); + new GenericContainer<>(TestImages.ALPINE_IMAGE).withLabel("org.testcontainers.foo", "false"); } ); } @@ -277,25 +296,36 @@ public void customClasspathResourceMappingTest() throws IOException { // in that case this test will fail. String line = getReaderForContainerPort80(alpineClasspathResource).readLine(); - assertEquals("Resource on the classpath can be mapped using calls to withClasspathResourceMapping", "FOOBAR", line); + assertEquals( + "Resource on the classpath can be mapped using calls to withClasspathResourceMapping", + "FOOBAR", + line + ); } @Test public void customClasspathResourceMappingWithSelinuxTest() throws IOException { String line = getReaderForContainerPort80(alpineClasspathResourceSelinux).readLine(); - assertEquals("Resource on the classpath can be mapped using calls to withClasspathResourceMappingSelinux", "FOOBAR", line); + assertEquals( + "Resource on the classpath can be mapped using calls to withClasspathResourceMappingSelinux", + "FOOBAR", + line + ); } @Test public void exceptionThrownWhenMappedPortNotFound() { - assertThrows("When the requested port is not mapped, getMappedPort() throws an exception", - IllegalArgumentException.class, - () -> { - return redis.getMappedPort(666); - }); + assertThrows( + "When the requested port is not mapped, getMappedPort() throws an exception", + IllegalArgumentException.class, + () -> { + return redis.getMappedPort(666); + } + ); } - protected static void writeStringToFile(File contentFolder, String filename, String string) throws FileNotFoundException { + protected static void writeStringToFile(File contentFolder, String filename, String string) + throws FileNotFoundException { File file = new File(contentFolder, filename); PrintStream printStream = new PrintStream(new FileOutputStream(file)); @@ -303,32 +333,34 @@ protected static void writeStringToFile(File contentFolder, String filename, Str printStream.close(); } - @Test @Ignore //TODO investigate intermittent failures + @Test + @Ignore //TODO investigate intermittent failures public void failFastWhenContainerHaltsImmediately() { - long startingTimeMs = System.currentTimeMillis(); - final GenericContainer failsImmediately = new GenericContainer<>(ALPINE_IMAGE) - .withCommand("/bin/sh", "-c", "return false") - .withMinimumRunningDuration(Duration.ofMillis(100)); + final GenericContainer failsImmediately = new GenericContainer<>(TestImages.ALPINE_IMAGE) + .withCommand("/bin/sh", "-c", "return false") + .withMinimumRunningDuration(Duration.ofMillis(100)); try { assertThrows( - "When we start a container that halts immediately, an exception is thrown", - RetryCountExceededException.class, - () -> { - failsImmediately.start(); - return null; - }); + "When we start a container that halts immediately, an exception is thrown", + RetryCountExceededException.class, + () -> { + failsImmediately.start(); + return null; + } + ); // Check how long it took, to verify that we ARE bailing out early. // Want to strike a balance here; too short and this test will fail intermittently // on slow systems and/or due to GC variation, too long and we won't properly test // what we're intending to test. - int allowedSecondsToFailure = - GenericContainer.CONTAINER_RUNNING_TIMEOUT_SEC / 2; + int allowedSecondsToFailure = GenericContainer.CONTAINER_RUNNING_TIMEOUT_SEC / 2; long completedTimeMs = System.currentTimeMillis(); - assertTrue("container should not take long to start up", - completedTimeMs - startingTimeMs < 1000L * allowedSecondsToFailure); + assertTrue( + "container should not take long to start up", + completedTimeMs - startingTimeMs < 1000L * allowedSecondsToFailure + ); } finally { failsImmediately.stop(); } @@ -336,19 +368,20 @@ public void failFastWhenContainerHaltsImmediately() { @Test public void testExecInContainer() throws Exception { - // The older "lxc" execution driver doesn't support "exec". At the time of writing (2016/03/29), // that's the case for CircleCI. // Once they resolve the issue, this clause can be removed. Assume.assumeTrue(TestEnvironment.dockerExecutionDriverSupportsExec()); final GenericContainer.ExecResult result = redis.execInContainer("redis-cli", "role"); - assertTrue("Output for \"redis-cli role\" command should start with \"master\"", result.getStdout().startsWith("master")); + assertTrue( + "Output for \"redis-cli role\" command should start with \"master\"", + result.getStdout().startsWith("master") + ); assertEquals("Stderr for \"redis-cli role\" command should be empty", "", result.getStderr()); // We expect to reach this point for modern Docker versions. } - @Test public void extraHostTest() throws IOException { BufferedReader br = getReaderForContainerPort80(alpineExtrahost); @@ -370,30 +403,37 @@ public void extraHostTest() throws IOException { public void createContainerCmdHookTest() { // Use random name to avoid the conflicts between the tests String randomName = Base58.randomString(5); - try( - GenericContainer container = new GenericContainer<>(REDIS_IMAGE) - .withCommand("redis-server", "--help") - .withCreateContainerCmdModifier(cmd -> cmd.withName("overrideMe")) - // Preserves the order - .withCreateContainerCmdModifier(cmd -> cmd.withName(randomName)) - // Allows to override pre-configured values by GenericContainer - .withCreateContainerCmdModifier(cmd -> cmd.withCmd("redis-server", "--port", "6379")) + try ( + GenericContainer container = new GenericContainer<>(TestImages.REDIS_IMAGE) + .withCommand("redis-server", "--help") + .withCreateContainerCmdModifier(cmd -> cmd.withName("overrideMe")) + // Preserves the order + .withCreateContainerCmdModifier(cmd -> cmd.withName(randomName)) + // Allows to override pre-configured values by GenericContainer + .withCreateContainerCmdModifier(cmd -> cmd.withCmd("redis-server", "--port", "6379")) ) { container.start(); assertEquals("Name is configured", "/" + randomName, container.getContainerInfo().getName()); - assertEquals("Command is configured", "[redis-server, --port, 6379]", Arrays.toString(container.getContainerInfo().getConfig().getCmd())); + assertEquals( + "Command is configured", + "[redis-server, --port, 6379]", + Arrays.toString(container.getContainerInfo().getConfig().getCmd()) + ); } } private BufferedReader getReaderForContainerPort80(GenericContainer container) { + return Unreliables.retryUntilSuccess( + 10, + TimeUnit.SECONDS, + () -> { + Uninterruptibles.sleepUninterruptibly(1, TimeUnit.SECONDS); - return Unreliables.retryUntilSuccess(10, TimeUnit.SECONDS, () -> { - Uninterruptibles.sleepUninterruptibly(1, TimeUnit.SECONDS); - - Socket socket = new Socket(container.getHost(), container.getFirstMappedPort()); - return new BufferedReader(new InputStreamReader(socket.getInputStream())); - }); + Socket socket = new Socket(container.getHost(), container.getFirstMappedPort()); + return new BufferedReader(new InputStreamReader(socket.getInputStream())); + } + ); } @Test @@ -415,10 +455,11 @@ public void addingExposedPortTwiceShouldNotFail() { @Test public void sharedMemorySetTest() { - try (GenericContainer containerWithSharedMemory = new GenericContainer<>(TINY_IMAGE) - .withSharedMemorySize(42L * FileUtils.ONE_MB) - .withStartupCheckStrategy(new OneShotStartupCheckStrategy())) { - + try ( + GenericContainer containerWithSharedMemory = new GenericContainer<>(TestImages.TINY_IMAGE) + .withSharedMemorySize(42L * FileUtils.ONE_MB) + .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) + ) { containerWithSharedMemory.start(); HostConfig hostConfig = containerWithSharedMemory.getContainerInfo().getHostConfig(); diff --git a/core/src/test/java/org/testcontainers/junit/NonExistentImagePullTest.java b/core/src/test/java/org/testcontainers/junit/NonExistentImagePullTest.java index 95047779551..33909e2ed27 100644 --- a/core/src/test/java/org/testcontainers/junit/NonExistentImagePullTest.java +++ b/core/src/test/java/org/testcontainers/junit/NonExistentImagePullTest.java @@ -14,10 +14,12 @@ public class NonExistentImagePullTest { @Test(timeout = 60_000L) public void pullingNonExistentImageFailsGracefully() { - - assertThrows("Pulling a nonexistent container will cause an exception to be thrown", - ContainerFetchException.class, () -> { - new GenericContainer<>(DockerImageName.parse("testcontainers/nonexistent:latest")).getDockerImageName(); - }); + assertThrows( + "Pulling a nonexistent container will cause an exception to be thrown", + ContainerFetchException.class, + () -> { + new GenericContainer<>(DockerImageName.parse("testcontainers/nonexistent:latest")).getDockerImageName(); + } + ); } } diff --git a/core/src/test/java/org/testcontainers/junit/OutputStreamTest.java b/core/src/test/java/org/testcontainers/junit/OutputStreamTest.java index 649a0b56fc4..104fb78fe48 100644 --- a/core/src/test/java/org/testcontainers/junit/OutputStreamTest.java +++ b/core/src/test/java/org/testcontainers/junit/OutputStreamTest.java @@ -4,6 +4,7 @@ import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.testcontainers.TestImages; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.output.OutputFrame; import org.testcontainers.containers.output.Slf4jLogConsumer; @@ -17,8 +18,6 @@ import static org.rnorth.visibleassertions.VisibleAssertions.assertFalse; import static org.rnorth.visibleassertions.VisibleAssertions.assertThrows; import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; -import static org.testcontainers.TestImages.ALPINE_IMAGE; -import static org.testcontainers.containers.output.OutputFrame.OutputType.STDOUT; /** * Simple test for following container output. @@ -26,61 +25,74 @@ public class OutputStreamTest { @Rule - public GenericContainer container = new GenericContainer(ALPINE_IMAGE) - .withCommand("ping -c 5 127.0.0.1"); + public GenericContainer container = new GenericContainer(TestImages.ALPINE_IMAGE) + .withCommand("ping -c 5 127.0.0.1"); private static final Logger LOGGER = LoggerFactory.getLogger(OutputStreamTest.class); @Test(timeout = 60_000L) public void testFetchStdout() throws TimeoutException { - WaitingConsumer consumer = new WaitingConsumer(); - container.followOutput(consumer, STDOUT); + container.followOutput(consumer, OutputFrame.OutputType.STDOUT); - consumer.waitUntil(frame -> frame.getType() == STDOUT && frame.getUtf8String().contains("seq=2"), - 30, TimeUnit.SECONDS); + consumer.waitUntil( + frame -> frame.getType() == OutputFrame.OutputType.STDOUT && frame.getUtf8String().contains("seq=2"), + 30, + TimeUnit.SECONDS + ); } @Test(timeout = 60_000L) public void testFetchStdoutWithTimeout() { - WaitingConsumer consumer = new WaitingConsumer(); - container.followOutput(consumer, STDOUT); - - assertThrows("a TimeoutException should be thrown", TimeoutException.class, () -> { - consumer.waitUntil(frame -> frame.getType() == STDOUT && frame.getUtf8String().contains("seq=5"), - 2, TimeUnit.SECONDS); - return true; - }); + container.followOutput(consumer, OutputFrame.OutputType.STDOUT); + + assertThrows( + "a TimeoutException should be thrown", + TimeoutException.class, + () -> { + consumer.waitUntil( + frame -> { + return ( + frame.getType() == OutputFrame.OutputType.STDOUT && frame.getUtf8String().contains("seq=5") + ); + }, + 2, + TimeUnit.SECONDS + ); + return true; + } + ); } @Test(timeout = 60_000L) public void testFetchStdoutWithNoLimit() throws TimeoutException { - WaitingConsumer consumer = new WaitingConsumer(); - container.followOutput(consumer, STDOUT); + container.followOutput(consumer, OutputFrame.OutputType.STDOUT); - consumer.waitUntil(frame -> frame.getType() == STDOUT && frame.getUtf8String().contains("seq=2")); + consumer.waitUntil(frame -> { + return frame.getType() == OutputFrame.OutputType.STDOUT && frame.getUtf8String().contains("seq=2"); + }); } @Test(timeout = 60_000L) public void testLogConsumer() throws TimeoutException { - WaitingConsumer waitingConsumer = new WaitingConsumer(); Slf4jLogConsumer logConsumer = new Slf4jLogConsumer(LOGGER); Consumer composedConsumer = logConsumer.andThen(waitingConsumer); container.followOutput(composedConsumer); - waitingConsumer.waitUntil(frame -> frame.getType() == STDOUT && frame.getUtf8String().contains("seq=2")); + waitingConsumer.waitUntil(frame -> { + return frame.getType() == OutputFrame.OutputType.STDOUT && frame.getUtf8String().contains("seq=2"); + }); } @Test(timeout = 60_000L) public void testToStringConsumer() throws TimeoutException { - WaitingConsumer waitingConsumer = new WaitingConsumer(); ToStringConsumer toStringConsumer = new ToStringConsumer(); @@ -95,4 +107,3 @@ public void testToStringConsumer() throws TimeoutException { assertFalse("a non-expected value was found", utf8String.contains("seq=42")); } } - diff --git a/core/src/test/java/org/testcontainers/junit/OutputStreamWithTTYTest.java b/core/src/test/java/org/testcontainers/junit/OutputStreamWithTTYTest.java index b15f63f4158..a87dbef3990 100644 --- a/core/src/test/java/org/testcontainers/junit/OutputStreamWithTTYTest.java +++ b/core/src/test/java/org/testcontainers/junit/OutputStreamWithTTYTest.java @@ -4,6 +4,7 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.Timeout; +import org.testcontainers.TestImages; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.output.OutputFrame; import org.testcontainers.containers.output.Slf4jLogConsumer; @@ -18,14 +19,12 @@ import static org.rnorth.visibleassertions.VisibleAssertions.assertFalse; import static org.rnorth.visibleassertions.VisibleAssertions.assertThrows; import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; -import static org.testcontainers.TestImages.ALPINE_IMAGE; -import static org.testcontainers.containers.output.OutputFrame.OutputType.STDOUT; @Slf4j public class OutputStreamWithTTYTest { @Rule - public GenericContainer container = new GenericContainer<>(ALPINE_IMAGE) + public GenericContainer container = new GenericContainer<>(TestImages.ALPINE_IMAGE) .withCommand("ls -1") .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) .withCreateContainerCmdModifier(command -> command.withTty(true)); @@ -37,30 +36,44 @@ public class OutputStreamWithTTYTest { public void testFetchStdout() throws TimeoutException { WaitingConsumer consumer = new WaitingConsumer(); - container.followOutput(consumer, STDOUT); + container.followOutput(consumer, OutputFrame.OutputType.STDOUT); - consumer.waitUntil(frame -> frame.getType() == STDOUT && frame.getUtf8String().contains("home"), 4, TimeUnit.SECONDS); + consumer.waitUntil( + frame -> frame.getType() == OutputFrame.OutputType.STDOUT && frame.getUtf8String().contains("home"), + 4, + TimeUnit.SECONDS + ); } @Test public void testFetchStdoutWithTimeout() { WaitingConsumer consumer = new WaitingConsumer(); - container.followOutput(consumer, STDOUT); - - assertThrows("a TimeoutException should be thrown", TimeoutException.class, () -> { - consumer.waitUntil(frame -> frame.getType() == STDOUT && frame.getUtf8String().contains("qqq"), 1, TimeUnit.SECONDS); - return true; - }); + container.followOutput(consumer, OutputFrame.OutputType.STDOUT); + + assertThrows( + "a TimeoutException should be thrown", + TimeoutException.class, + () -> { + consumer.waitUntil( + frame -> frame.getType() == OutputFrame.OutputType.STDOUT && frame.getUtf8String().contains("qqq"), + 1, + TimeUnit.SECONDS + ); + return true; + } + ); } @Test public void testFetchStdoutWithNoLimit() throws TimeoutException { WaitingConsumer consumer = new WaitingConsumer(); - container.followOutput(consumer, STDOUT); + container.followOutput(consumer, OutputFrame.OutputType.STDOUT); - consumer.waitUntil(frame -> frame.getType() == STDOUT && frame.getUtf8String().contains("home")); + consumer.waitUntil(frame -> { + return frame.getType() == OutputFrame.OutputType.STDOUT && frame.getUtf8String().contains("home"); + }); } @Test @@ -71,7 +84,9 @@ public void testLogConsumer() throws TimeoutException { Consumer composedConsumer = logConsumer.andThen(waitingConsumer); container.followOutput(composedConsumer); - waitingConsumer.waitUntil(frame -> frame.getType() == STDOUT && frame.getUtf8String().contains("home")); + waitingConsumer.waitUntil(frame -> { + return frame.getType() == OutputFrame.OutputType.STDOUT && frame.getUtf8String().contains("home"); + }); } @Test diff --git a/core/src/test/java/org/testcontainers/junit/ParameterizedDockerfileContainerTest.java b/core/src/test/java/org/testcontainers/junit/ParameterizedDockerfileContainerTest.java index 969d2836b2e..62eccc2000c 100644 --- a/core/src/test/java/org/testcontainers/junit/ParameterizedDockerfileContainerTest.java +++ b/core/src/test/java/org/testcontainers/junit/ParameterizedDockerfileContainerTest.java @@ -22,22 +22,27 @@ public class ParameterizedDockerfileContainerTest { public GenericContainer container; public ParameterizedDockerfileContainerTest(String baseImage, String expectedVersion) { - container = new GenericContainer(new ImageFromDockerfile().withDockerfileFromBuilder(builder -> { - builder - .from(baseImage) - // Could potentially customise the image here, e.g. adding files, running - // commands, etc. - .build(); - })).withCommand("top"); + container = + new GenericContainer( + new ImageFromDockerfile() + .withDockerfileFromBuilder(builder -> { + builder + .from(baseImage) + // Could potentially customise the image here, e.g. adding files, running + // commands, etc. + .build(); + }) + ) + .withCommand("top"); this.expectedVersion = expectedVersion; } @Parameterized.Parameters(name = "{0}") public static Object[][] data() { - return new Object[][] { - { "alpine:3.12", "3.12"}, - { "alpine:3.13", "3.13"}, - { "alpine:3.14", "3.14"} + return new Object[][] { // + { "alpine:3.12", "3.12" }, + { "alpine:3.13", "3.13" }, + { "alpine:3.14", "3.14" }, }; } @@ -45,7 +50,6 @@ public static Object[][] data() { public void simpleTest() throws Exception { final String release = container.execInContainer("cat", "/etc/alpine-release").getStdout(); - assertTrue("/etc/alpine-release starts with " + expectedVersion, - release.startsWith(expectedVersion)); + assertTrue("/etc/alpine-release starts with " + expectedVersion, release.startsWith(expectedVersion)); } } diff --git a/core/src/test/java/org/testcontainers/junit/WorkingDirectoryTest.java b/core/src/test/java/org/testcontainers/junit/WorkingDirectoryTest.java index b23ddf2ca1c..0be4022c171 100644 --- a/core/src/test/java/org/testcontainers/junit/WorkingDirectoryTest.java +++ b/core/src/test/java/org/testcontainers/junit/WorkingDirectoryTest.java @@ -2,11 +2,11 @@ import org.junit.ClassRule; import org.junit.Test; +import org.testcontainers.TestImages; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.startupcheck.OneShotStartupCheckStrategy; import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; -import static org.testcontainers.TestImages.ALPINE_IMAGE; /** * Created by rnorth on 26/07/2016. @@ -14,10 +14,10 @@ public class WorkingDirectoryTest { @ClassRule - public static GenericContainer container = new GenericContainer(ALPINE_IMAGE) - .withWorkingDirectory("/etc") - .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) - .withCommand("ls", "-al"); + public static GenericContainer container = new GenericContainer(TestImages.ALPINE_IMAGE) + .withWorkingDirectory("/etc") + .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) + .withCommand("ls", "-al"); @Test public void checkOutput() { @@ -27,5 +27,4 @@ public void checkOutput() { assertTrue("Directory listing contains expected /etc content", listing.contains("init.d")); assertTrue("Directory listing contains expected /etc content", listing.contains("passwd")); } - } diff --git a/core/src/test/java/org/testcontainers/junit/wait/strategy/AbstractWaitStrategyTest.java b/core/src/test/java/org/testcontainers/junit/wait/strategy/AbstractWaitStrategyTest.java index 480536a54ef..de16eb0e582 100644 --- a/core/src/test/java/org/testcontainers/junit/wait/strategy/AbstractWaitStrategyTest.java +++ b/core/src/test/java/org/testcontainers/junit/wait/strategy/AbstractWaitStrategyTest.java @@ -20,6 +20,7 @@ * @author Pete Cornish {@literal } */ public abstract class AbstractWaitStrategyTest { + static final long WAIT_TIMEOUT_MILLIS = 3000; /** @@ -65,12 +66,16 @@ protected GenericContainer startContainerWithCommand(String shellCommand, Wai return startContainerWithCommand(shellCommand, waitStrategy, 8080); } - protected GenericContainer startContainerWithCommand(String shellCommand, WaitStrategy waitStrategy, Integer... ports) { + protected GenericContainer startContainerWithCommand( + String shellCommand, + WaitStrategy waitStrategy, + Integer... ports + ) { // apply WaitStrategy to container return new GenericContainer<>(TestImages.ALPINE_IMAGE) - .withExposedPorts(ports) - .withCommand("sh", "-c", shellCommand) - .waitingFor(waitStrategy.withStartupTimeout(Duration.ofMillis(WAIT_TIMEOUT_MILLIS))); + .withExposedPorts(ports) + .withCommand("sh", "-c", shellCommand) + .waitingFor(waitStrategy.withStartupTimeout(Duration.ofMillis(WAIT_TIMEOUT_MILLIS))); } /** @@ -100,10 +105,11 @@ protected void waitUntilReadyAndTimeout(String shellCommand) { */ protected void waitUntilReadyAndTimeout(GenericContainer container) { // start() blocks until successful or timeout - VisibleAssertions.assertThrows("an exception is thrown when timeout occurs (" + WAIT_TIMEOUT_MILLIS + "ms)", - ContainerLaunchException.class, - container::start); - + VisibleAssertions.assertThrows( + "an exception is thrown when timeout occurs (" + WAIT_TIMEOUT_MILLIS + "ms)", + ContainerLaunchException.class, + container::start + ); } /** @@ -115,7 +121,9 @@ protected void waitUntilReadyAndSucceed(GenericContainer container) { // start() blocks until successful or timeout container.start(); - assertTrue(String.format("Expected container to be ready after timeout of %sms", - WAIT_TIMEOUT_MILLIS), ready.get()); + assertTrue( + String.format("Expected container to be ready after timeout of %sms", WAIT_TIMEOUT_MILLIS), + ready.get() + ); } } diff --git a/core/src/test/java/org/testcontainers/junit/wait/strategy/HostPortWaitStrategyTest.java b/core/src/test/java/org/testcontainers/junit/wait/strategy/HostPortWaitStrategyTest.java index 5c7d37a7da3..4b5f43a4d4b 100644 --- a/core/src/test/java/org/testcontainers/junit/wait/strategy/HostPortWaitStrategyTest.java +++ b/core/src/test/java/org/testcontainers/junit/wait/strategy/HostPortWaitStrategyTest.java @@ -17,7 +17,8 @@ public class HostPortWaitStrategyTest { @ClassRule - public static GenericContainer container = new GenericContainer<>(TestImages.ALPINE_IMAGE).withExposedPorts() + public static GenericContainer container = new GenericContainer<>(TestImages.ALPINE_IMAGE) + .withExposedPorts() .withCommand("sh", "-c", "while true; do nc -lp 8080; done") .withExposedPorts(8080) .waitingFor(Wait.forListeningPort().withStartupTimeout(Duration.ofSeconds(10))); diff --git a/core/src/test/java/org/testcontainers/junit/wait/strategy/HttpWaitStrategyTest.java b/core/src/test/java/org/testcontainers/junit/wait/strategy/HttpWaitStrategyTest.java index 82207b4537e..7014749115e 100644 --- a/core/src/test/java/org/testcontainers/junit/wait/strategy/HttpWaitStrategyTest.java +++ b/core/src/test/java/org/testcontainers/junit/wait/strategy/HttpWaitStrategyTest.java @@ -1,9 +1,5 @@ package org.testcontainers.junit.wait.strategy; -import static org.hamcrest.CoreMatchers.containsString; -import static org.junit.Assert.assertThat; - -import java.util.HashMap; import org.jetbrains.annotations.NotNull; import org.junit.Test; import org.rnorth.ducttape.RetryCountExceededException; @@ -11,15 +7,20 @@ import org.testcontainers.containers.wait.strategy.HttpWaitStrategy; import java.time.Duration; +import java.util.HashMap; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Predicate; +import static org.hamcrest.Matchers.containsString; +import static org.junit.Assert.assertThat; + /** * Tests for {@link HttpWaitStrategy}. * * @author Pete Cornish {@literal } */ public class HttpWaitStrategyTest extends AbstractWaitStrategyTest { + /** * newline sequence indicating end of the HTTP header. */ @@ -44,10 +45,9 @@ public void testWaitUntilReadyWithSuccess() { public void testWaitUntilReadyWithSuccessWithCustomHeaders() { HashMap headers = new HashMap<>(); headers.put("baz", "boo"); - GenericContainer container = startContainerWithCommand(createShellCommand("200 OK", GOOD_RESPONSE_BODY), - createHttpWaitStrategy(ready) - .withHeader("foo", "bar") - .withHeaders(headers) + GenericContainer container = startContainerWithCommand( + createShellCommand("200 OK", GOOD_RESPONSE_BODY), + createHttpWaitStrategy(ready).withHeader("foo", "bar").withHeaders(headers) ); waitUntilReadyAndSucceed(container); @@ -58,14 +58,17 @@ public void testWaitUntilReadyWithSuccessWithCustomHeaders() { } /** - * Ensures that HTTPS requests made with the HttpWaitStrategy can skip the - * certificate validation chains (to support self-signed certificates for example). + * Ensures that HTTPS requests made with the HttpWaitStrategy can skip the + * certificate validation chains (to support self-signed certificates for example). */ @Test public void testWaitUntilReadyWithTlsAndAllowUnsecure() { - waitUntilReadyAndSucceed(startContainerWithCommand(createHttpsShellCommand("200 OK", GOOD_RESPONSE_BODY, 8080), - createHttpWaitStrategy(ready).usingTls().allowInsecure() - )); + waitUntilReadyAndSucceed( + startContainerWithCommand( + createHttpsShellCommand("200 OK", GOOD_RESPONSE_BODY, 8080), + createHttpWaitStrategy(ready).usingTls().allowInsecure() + ) + ); } /** @@ -74,10 +77,12 @@ public void testWaitUntilReadyWithTlsAndAllowUnsecure() { */ @Test public void testWaitUntilReadyWithUnauthorizedWithLambda() { - waitUntilReadyAndSucceed(startContainerWithCommand(createShellCommand("401 UNAUTHORIZED", GOOD_RESPONSE_BODY), - createHttpWaitStrategy(ready) - .forStatusCodeMatching(it -> it >= 200 && it < 300 || it == 401) - )); + waitUntilReadyAndSucceed( + startContainerWithCommand( + createShellCommand("401 UNAUTHORIZED", GOOD_RESPONSE_BODY), + createHttpWaitStrategy(ready).forStatusCodeMatching(it -> it >= 200 && it < 300 || it == 401) + ) + ); } /** @@ -86,12 +91,12 @@ public void testWaitUntilReadyWithUnauthorizedWithLambda() { */ @Test public void testWaitUntilReadyWithManyStatusCodes() { - waitUntilReadyAndSucceed(startContainerWithCommand(createShellCommand("401 UNAUTHORIZED", GOOD_RESPONSE_BODY), - createHttpWaitStrategy(ready) - .forStatusCode(300) - .forStatusCode(401) - .forStatusCode(500) - )); + waitUntilReadyAndSucceed( + startContainerWithCommand( + createShellCommand("401 UNAUTHORIZED", GOOD_RESPONSE_BODY), + createHttpWaitStrategy(ready).forStatusCode(300).forStatusCode(401).forStatusCode(500) + ) + ); } /** @@ -101,12 +106,15 @@ public void testWaitUntilReadyWithManyStatusCodes() { */ @Test public void testWaitUntilReadyWithManyStatusCodesAndLambda() { - waitUntilReadyAndSucceed(startContainerWithCommand(createShellCommand("401 UNAUTHORIZED", GOOD_RESPONSE_BODY), - createHttpWaitStrategy(ready) - .forStatusCode(300) - .forStatusCode(500) - .forStatusCodeMatching(it -> it == 401) - )); + waitUntilReadyAndSucceed( + startContainerWithCommand( + createShellCommand("401 UNAUTHORIZED", GOOD_RESPONSE_BODY), + createHttpWaitStrategy(ready) + .forStatusCode(300) + .forStatusCode(500) + .forStatusCodeMatching(it -> it == 401) + ) + ); } /** @@ -116,11 +124,12 @@ public void testWaitUntilReadyWithManyStatusCodesAndLambda() { */ @Test public void testWaitUntilReadyWithTimeoutAndWithManyStatusCodesAndLambda() { - waitUntilReadyAndTimeout(startContainerWithCommand(createShellCommand("401 UNAUTHORIZED", GOOD_RESPONSE_BODY), - createHttpWaitStrategy(ready) - .forStatusCode(300) - .forStatusCodeMatching(it -> it == 500) - )); + waitUntilReadyAndTimeout( + startContainerWithCommand( + createShellCommand("401 UNAUTHORIZED", GOOD_RESPONSE_BODY), + createHttpWaitStrategy(ready).forStatusCode(300).forStatusCodeMatching(it -> it == 500) + ) + ); } /** @@ -132,10 +141,12 @@ public void testWaitUntilReadyWithTimeoutAndWithManyStatusCodesAndLambda() { */ @Test public void testWaitUntilReadyWithTimeoutAndWithLambdaShouldNotMatchOk() { - waitUntilReadyAndTimeout(startContainerWithCommand(createShellCommand("200 OK", GOOD_RESPONSE_BODY), - createHttpWaitStrategy(ready) - .forStatusCodeMatching(it -> it >= 300) - )); + waitUntilReadyAndTimeout( + startContainerWithCommand( + createShellCommand("200 OK", GOOD_RESPONSE_BODY), + createHttpWaitStrategy(ready).forStatusCodeMatching(it -> it >= 300) + ) + ); } /** @@ -156,27 +167,31 @@ public void testWaitUntilReadyWithTimeoutAndBadResponseBody() { waitUntilReadyAndTimeout(createShellCommand("200 OK", "Bad Response")); } - /** * Expects the WaitStrategy probing the right port. */ @Test public void testWaitUntilReadyWithSpecificPort() { - waitUntilReadyAndSucceed(startContainerWithCommand( - createShellCommand("200 OK", GOOD_RESPONSE_BODY, 9090), - createHttpWaitStrategy(ready) - .forPort(9090), - 7070, 8080, 9090 - )); + waitUntilReadyAndSucceed( + startContainerWithCommand( + createShellCommand("200 OK", GOOD_RESPONSE_BODY, 9090), + createHttpWaitStrategy(ready).forPort(9090), + 7070, + 8080, + 9090 + ) + ); } @Test public void testWaitUntilReadyWithTimoutCausedByReadTimeout() { waitUntilReadyAndTimeout( - startContainerWithCommand(createShellCommand("0 Connection Refused", GOOD_RESPONSE_BODY, 9090), + startContainerWithCommand( + createShellCommand("0 Connection Refused", GOOD_RESPONSE_BODY, 9090), createHttpWaitStrategy(ready).forPort(9090).withReadTimeout(Duration.ofMillis(1)), 9090 - )); + ) + ); } /** @@ -185,8 +200,7 @@ public void testWaitUntilReadyWithTimoutCausedByReadTimeout() { */ @NotNull protected HttpWaitStrategy buildWaitStrategy(final AtomicBoolean ready) { - return createHttpWaitStrategy(ready) - .forResponsePredicate(s -> s.equals(GOOD_RESPONSE_BODY)); + return createHttpWaitStrategy(ready).forResponsePredicate(s -> s.equals(GOOD_RESPONSE_BODY)); } /** @@ -212,17 +226,41 @@ private String createShellCommand(String header, String responseBody) { private String createShellCommand(String header, String responseBody, int port) { int length = responseBody.getBytes().length; - return "while true; do { echo -e \"HTTP/1.1 " + header + NEWLINE + - "Content-Type: text/html" + NEWLINE + - "Content-Length: " + length + NEWLINE + "\";" - + " echo \"" + responseBody + "\";} | nc -lp " + port + "; done"; + return ( + "while true; do { echo -e \"HTTP/1.1 " + + header + + NEWLINE + + "Content-Type: text/html" + + NEWLINE + + "Content-Length: " + + length + + NEWLINE + + "\";" + + " echo \"" + + responseBody + + "\";} | nc -lp " + + port + + "; done" + ); } - + private String createHttpsShellCommand(String header, String responseBody, int port) { int length = responseBody.getBytes().length; - return "apk add nmap-ncat; while true; do { echo -e \"HTTP/1.1 " + header + NEWLINE + - "Content-Type: text/html" + NEWLINE + - "Content-Length: " + length + NEWLINE + "\";" - + " echo \"" + responseBody + "\";} | ncat -lp " + port + " --ssl; done"; + return ( + "apk add nmap-ncat; while true; do { echo -e \"HTTP/1.1 " + + header + + NEWLINE + + "Content-Type: text/html" + + NEWLINE + + "Content-Length: " + + length + + NEWLINE + + "\";" + + " echo \"" + + responseBody + + "\";} | ncat -lp " + + port + + " --ssl; done" + ); } } diff --git a/core/src/test/java/org/testcontainers/junit/wait/strategy/LogMessageWaitStrategyTest.java b/core/src/test/java/org/testcontainers/junit/wait/strategy/LogMessageWaitStrategyTest.java index a6ef9f11ba4..5dd9623107b 100644 --- a/core/src/test/java/org/testcontainers/junit/wait/strategy/LogMessageWaitStrategyTest.java +++ b/core/src/test/java/org/testcontainers/junit/wait/strategy/LogMessageWaitStrategyTest.java @@ -19,9 +19,9 @@ public class LogMessageWaitStrategyTest extends AbstractWaitStrategyTest { - builder.from(DOCKER_REGISTRY_IMAGE.asCanonicalNameString()) - .run("htpasswd -Bbn testuser notasecret > /htpasswd") - .env("REGISTRY_AUTH", "htpasswd") - .env("REGISTRY_AUTH_HTPASSWD_PATH", "/htpasswd") - .env("REGISTRY_AUTH_HTPASSWD_REALM", "Test"); - })); + public static DockerRegistryContainer authenticatedRegistry = new DockerRegistryContainer( + new ImageFromDockerfile() + .withDockerfileFromBuilder(builder -> { + builder + .from(TestImages.DOCKER_REGISTRY_IMAGE.asCanonicalNameString()) + .run("htpasswd -Bbn testuser notasecret > /htpasswd") + .env("REGISTRY_AUTH", "htpasswd") + .env("REGISTRY_AUTH_HTPASSWD_PATH", "/htpasswd") + .env("REGISTRY_AUTH_HTPASSWD_REALM", "Test"); + }) + ); private static RegistryAuthLocator originalAuthLocatorSingleton; @@ -65,7 +68,12 @@ public static void beforeClass() throws Exception { // Replace the RegistryAuthLocator singleton with our mock, for the duration of this test final RegistryAuthLocator mockAuthLocator = Mockito.mock(RegistryAuthLocator.class); RegistryAuthLocator.setInstance(mockAuthLocator); - when(mockAuthLocator.lookupAuthConfig(argThat(argument -> testRegistryAddress.equals(argument.getRegistry())), any())) + when( + mockAuthLocator.lookupAuthConfig( + argThat(argument -> testRegistryAddress.equals(argument.getRegistry())), + any() + ) + ) .thenReturn(authConfig); } @@ -77,8 +85,10 @@ public static void tearDown() { @Test public void testThatAuthLocatorIsUsedForContainerCreation() { // actually start a container, which will require an authenticated pull - try (final GenericContainer container = new GenericContainer<>(testImageName) - .withCommand("/bin/sh", "-c", "sleep 10")) { + try ( + final GenericContainer container = new GenericContainer<>(testImageName) + .withCommand("/bin/sh", "-c", "sleep 10") + ) { container.start(); assertTrue("container started following an authenticated pull", container.isRunning()); @@ -93,11 +103,12 @@ public void testThatAuthLocatorIsUsedForDockerfileBuild() throws IOException { Files.write(tempFile, dockerFileContent.getBytes()); // Start a container built from a derived image, which will require an authenticated pull - try (final GenericContainer container = new GenericContainer<>( - new ImageFromDockerfile() - .withDockerfile(tempFile) - ) - .withCommand("/bin/sh", "-c", "sleep 10")) { + try ( + final GenericContainer container = new GenericContainer<>( + new ImageFromDockerfile().withDockerfile(tempFile) + ) + .withCommand("/bin/sh", "-c", "sleep 10") + ) { container.start(); assertTrue("container started following an authenticated pull", container.isRunning()); @@ -108,23 +119,23 @@ public void testThatAuthLocatorIsUsedForDockerfileBuild() throws IOException { public void testThatAuthLocatorIsUsedForDockerComposePull() throws IOException { // Prepare a simple temporary Docker Compose manifest which requires our custom private image Path tempFile = getLocalTempFile(".docker-compose.yml"); - @Language("yaml") String composeFileContent = + @Language("yaml") + String composeFileContent = "version: '2.0'\n" + - "services:\n" + - " privateservice:\n" + - " command: /bin/sh -c 'sleep 60'\n" + - " image: " + testImageName.asCanonicalNameString(); + "services:\n" + + " privateservice:\n" + + " command: /bin/sh -c 'sleep 60'\n" + + " image: " + + testImageName.asCanonicalNameString(); Files.write(tempFile, composeFileContent.getBytes()); // Start the docker compose project, which will require an authenticated pull try (final DockerComposeContainer compose = new DockerComposeContainer<>(tempFile.toFile())) { compose.start(); - assertTrue("container started following an authenticated pull", - compose - .getContainerByServiceName("privateservice_1") - .map(ContainerState::isRunning) - .orElse(false) + assertTrue( + "container started following an authenticated pull", + compose.getContainerByServiceName("privateservice_1").map(ContainerState::isRunning).orElse(false) ); } } diff --git a/core/src/test/java/org/testcontainers/utility/ClasspathScannerTest.java b/core/src/test/java/org/testcontainers/utility/ClasspathScannerTest.java index bdd71b0db9f..4ed86e3a8fa 100644 --- a/core/src/test/java/org/testcontainers/utility/ClasspathScannerTest.java +++ b/core/src/test/java/org/testcontainers/utility/ClasspathScannerTest.java @@ -5,11 +5,11 @@ import java.io.IOException; import java.net.URL; +import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.stream.Collectors; -import static java.util.Arrays.asList; -import static java.util.stream.Collectors.toList; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -18,9 +18,13 @@ public class ClasspathScannerTest { private static URL FILE_A; + private static URL FILE_B; + private static URL JAR_A; + private static URL JAR_B; + private static URL FILE_C; @BeforeClass @@ -35,7 +39,7 @@ public static void setUp() throws Exception { @Test public void realClassLoaderLookupOccurs() { // look for a resource that we know exists only once - final List foundURLs = ClasspathScanner.scanFor("expectedClasspathFile.txt").collect(toList()); + final List foundURLs = ClasspathScanner.scanFor("expectedClasspathFile.txt").collect(Collectors.toList()); assertEquals("Exactly one resource was found", 1, foundURLs.size()); } @@ -43,41 +47,27 @@ public void realClassLoaderLookupOccurs() { @Test public void multipleResultsOnOneClassLoaderAreFound() throws IOException { final ClassLoader firstMockClassLoader = mock(ClassLoader.class); - when(firstMockClassLoader.getResources(eq("someName"))).thenReturn( - Collections.enumeration( - asList( - FILE_A, - FILE_B - ) - ) - ); + when(firstMockClassLoader.getResources(eq("someName"))) + .thenReturn(Collections.enumeration(Arrays.asList(FILE_A, FILE_B))); - final List foundURLs = ClasspathScanner.scanFor("someName", firstMockClassLoader).collect(toList()); - assertEquals( - "The expected URLs are found", - asList(FILE_A, FILE_B), - foundURLs - ); + final List foundURLs = ClasspathScanner + .scanFor("someName", firstMockClassLoader) + .collect(Collectors.toList()); + assertEquals("The expected URLs are found", Arrays.asList(FILE_A, FILE_B), foundURLs); } @Test public void orderIsAlphabeticalForDeterminism() throws IOException { final ClassLoader firstMockClassLoader = mock(ClassLoader.class); - when(firstMockClassLoader.getResources(eq("someName"))).thenReturn( - Collections.enumeration( - asList( - FILE_B, - JAR_A, - JAR_B, - FILE_A - ) - ) - ); + when(firstMockClassLoader.getResources(eq("someName"))) + .thenReturn(Collections.enumeration(Arrays.asList(FILE_B, JAR_A, JAR_B, FILE_A))); - final List foundURLs = ClasspathScanner.scanFor("someName", firstMockClassLoader).collect(toList()); + final List foundURLs = ClasspathScanner + .scanFor("someName", firstMockClassLoader) + .collect(Collectors.toList()); assertEquals( "The expected URLs are found in the expected order", - asList(FILE_A, FILE_B, JAR_A, JAR_B), + Arrays.asList(FILE_A, FILE_B, JAR_A, JAR_B), foundURLs ); } @@ -85,30 +75,23 @@ public void orderIsAlphabeticalForDeterminism() throws IOException { @Test public void multipleClassLoadersAreQueried() throws IOException { final ClassLoader firstMockClassLoader = mock(ClassLoader.class); - when(firstMockClassLoader.getResources(eq("someName"))).thenReturn( - Collections.enumeration( - asList( - FILE_A, - FILE_B - ) - ) - ); + when(firstMockClassLoader.getResources(eq("someName"))) + .thenReturn(Collections.enumeration(Arrays.asList(FILE_A, FILE_B))); final ClassLoader secondMockClassLoader = mock(ClassLoader.class); - when(secondMockClassLoader.getResources(eq("someName"))).thenReturn( - Collections.enumeration( - asList( - FILE_B, // duplicate - FILE_C + when(secondMockClassLoader.getResources(eq("someName"))) + .thenReturn( + Collections.enumeration( + Arrays.asList( + FILE_B, // duplicate + FILE_C + ) ) - ) - ); + ); - final List foundURLs = ClasspathScanner.scanFor("someName", firstMockClassLoader, secondMockClassLoader).collect(toList()); + final List foundURLs = ClasspathScanner + .scanFor("someName", firstMockClassLoader, secondMockClassLoader) + .collect(Collectors.toList()); - assertEquals( - "The expected URLs are found", - asList(FILE_A, FILE_B, FILE_C), - foundURLs - ); + assertEquals("The expected URLs are found", Arrays.asList(FILE_A, FILE_B, FILE_C), foundURLs); } } diff --git a/core/src/test/java/org/testcontainers/utility/ComparableVersionTest.java b/core/src/test/java/org/testcontainers/utility/ComparableVersionTest.java index d5a2c63a5b8..fb3c7e05c0b 100644 --- a/core/src/test/java/org/testcontainers/utility/ComparableVersionTest.java +++ b/core/src/test/java/org/testcontainers/utility/ComparableVersionTest.java @@ -1,18 +1,19 @@ package org.testcontainers.utility; -import java.util.Arrays; - import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; +import java.util.Arrays; + import static org.junit.Assert.assertArrayEquals; @RunWith(Parameterized.class) public class ComparableVersionTest { private final int[] expected; + private final String given; public ComparableVersionTest(final String given, final int[] expected) { @@ -27,14 +28,15 @@ public void shouldParseVersions() { @Parameters(name = "Parsed version: {0}={1}") public static Iterable data() { - return Arrays.asList(new Object[][] { - {"1.2.3", new int[] {1, 2, 3}}, - {"", new int[0]}, - {"1", new int[] {1}}, - {"1.2.3.4.5.6.7", new int[] {1, 2, 3, 4, 5, 6, 7}}, - {"1.2-dev", new int[] {1, 2}}, - {"18.06.0-dev", new int[] {18, 6}}, - }); + return Arrays.asList( + new Object[][] { + { "1.2.3", new int[] { 1, 2, 3 } }, + { "", new int[0] }, + { "1", new int[] { 1 } }, + { "1.2.3.4.5.6.7", new int[] { 1, 2, 3, 4, 5, 6, 7 } }, + { "1.2-dev", new int[] { 1, 2 } }, + { "18.06.0-dev", new int[] { 18, 6 } }, + } + ); } - } diff --git a/core/src/test/java/org/testcontainers/utility/DefaultImageNameSubstitutorTest.java b/core/src/test/java/org/testcontainers/utility/DefaultImageNameSubstitutorTest.java index a6f7813c3d3..0978003f815 100644 --- a/core/src/test/java/org/testcontainers/utility/DefaultImageNameSubstitutorTest.java +++ b/core/src/test/java/org/testcontainers/utility/DefaultImageNameSubstitutorTest.java @@ -12,7 +12,9 @@ public class DefaultImageNameSubstitutorTest { public static final DockerImageName ORIGINAL_IMAGE = DockerImageName.parse("foo"); + public static final DockerImageName SUBSTITUTE_IMAGE = DockerImageName.parse("bar"); + private ConfigurationFileImageNameSubstitutor underTest; @Rule diff --git a/core/src/test/java/org/testcontainers/utility/DirectoryTarResourceTest.java b/core/src/test/java/org/testcontainers/utility/DirectoryTarResourceTest.java index f439c55c3ea..cbd35a62b52 100644 --- a/core/src/test/java/org/testcontainers/utility/DirectoryTarResourceTest.java +++ b/core/src/test/java/org/testcontainers/utility/DirectoryTarResourceTest.java @@ -11,8 +11,8 @@ import java.io.File; import java.util.Arrays; -import static org.hamcrest.CoreMatchers.allOf; -import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.containsString; import static org.rnorth.visibleassertions.VisibleAssertions.assertThat; import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; @@ -24,41 +24,51 @@ public void simpleRecursiveFileTest() { File directory = new File("src"); GenericContainer container = new GenericContainer( - new ImageFromDockerfile() - .withDockerfileFromBuilder(builder -> - builder.from("alpine:3.14") - .copy("/tmp/foo", "/foo") - .cmd("cat /foo/test/resources/test-recursive-file.txt") - .build() - ).withFileFromFile("/tmp/foo", directory)) - .withStartupCheckStrategy(new OneShotStartupCheckStrategy()); + new ImageFromDockerfile() + .withDockerfileFromBuilder(builder -> { + builder + .from("alpine:3.14") + .copy("/tmp/foo", "/foo") + .cmd("cat /foo/test/resources/test-recursive-file.txt") + .build(); + }) + .withFileFromFile("/tmp/foo", directory) + ) + .withStartupCheckStrategy(new OneShotStartupCheckStrategy()); container.start(); final String results = container.getLogs(); - assertTrue("The container has a file that was copied in via a recursive copy", results.contains("Used for DirectoryTarResourceTest")); + assertTrue( + "The container has a file that was copied in via a recursive copy", + results.contains("Used for DirectoryTarResourceTest") + ); } @Test public void simpleRecursiveFileWithPermissionTest() { GenericContainer container = new GenericContainer( - new ImageFromDockerfile() - .withDockerfileFromBuilder(builder -> - builder.from("alpine:3.14") - .copy("/tmp/foo", "/foo") - .cmd("ls", "-al", "/") - .build() - ).withFileFromFile("/tmp/foo", new File("/mappable-resource/test-resource.txt"), - 0754)) - .withStartupCheckStrategy(new OneShotStartupCheckStrategy()); + new ImageFromDockerfile() + .withDockerfileFromBuilder(builder -> { + builder + .from("alpine:3.14") // + .copy("/tmp/foo", "/foo") + .cmd("ls", "-al", "/") + .build(); + }) + .withFileFromFile("/tmp/foo", new File("/mappable-resource/test-resource.txt"), 0754) + ) + .withStartupCheckStrategy(new OneShotStartupCheckStrategy()); container.start(); String listing = container.getLogs(); - assertThat("Listing shows that file is copied with mode requested.", - Arrays.asList(listing.split("\\n")), - exactlyNItems(1, allOf(containsString("-rwxr-xr--"), containsString("foo")))); + assertThat( + "Listing shows that file is copied with mode requested.", + Arrays.asList(listing.split("\\n")), + exactlyNItems(1, allOf(containsString("-rwxr-xr--"), containsString("foo"))) + ); } @Test @@ -66,21 +76,27 @@ public void simpleRecursiveClasspathResourceTest() { // This test combines the copying of classpath resources from JAR files with the recursive TAR approach, to allow JARed classpath resources to be copied in to an image GenericContainer container = new GenericContainer( - new ImageFromDockerfile() - .withDockerfileFromBuilder(builder -> - builder.from("alpine:3.14") - .copy("/tmp/foo", "/foo") - .cmd("ls -lRt /foo") - .build() - ).withFileFromClasspath("/tmp/foo", "/recursive/dir")) // here we use /org/junit as a directory that really should exist on the classpath - .withStartupCheckStrategy(new OneShotStartupCheckStrategy()); + new ImageFromDockerfile() + .withDockerfileFromBuilder(builder -> { + builder + .from("alpine:3.14") // + .copy("/tmp/foo", "/foo") + .cmd("ls -lRt /foo") + .build(); + }) + .withFileFromClasspath("/tmp/foo", "/recursive/dir") + ) // here we use /org/junit as a directory that really should exist on the classpath + .withStartupCheckStrategy(new OneShotStartupCheckStrategy()); container.start(); final String results = container.getLogs(); // ExternalResource.class is known to exist in a subdirectory of /org/junit so should be successfully copied in - assertTrue("The container has a file that was copied in via a recursive copy from a JAR resource", results.contains("content.txt")); + assertTrue( + "The container has a file that was copied in via a recursive copy from a JAR resource", + results.contains("content.txt") + ); } public static Matcher> exactlyNItems(final int n, Matcher elementMatcher) { @@ -91,7 +107,6 @@ protected boolean matchesSafely(Iterable collection, Description mism boolean isPastFirst = false; for (Object item : collection) { - if (elementMatcher.matches(item)) { count++; } diff --git a/core/src/test/java/org/testcontainers/utility/DockerImageNameCompatibilityTest.java b/core/src/test/java/org/testcontainers/utility/DockerImageNameCompatibilityTest.java index 22d67de5785..f2ca26b8947 100644 --- a/core/src/test/java/org/testcontainers/utility/DockerImageNameCompatibilityTest.java +++ b/core/src/test/java/org/testcontainers/utility/DockerImageNameCompatibilityTest.java @@ -1,13 +1,12 @@ package org.testcontainers.utility; -import static org.hamcrest.core.StringContains.containsString; -import static org.rnorth.visibleassertions.VisibleAssertions.assertFalse; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; - import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; +import static org.hamcrest.Matchers.containsString; +import static org.rnorth.visibleassertions.VisibleAssertions.assertFalse; +import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; public class DockerImageNameCompatibilityTest { @@ -20,6 +19,7 @@ public void testPlainImage() { assertFalse("image name foo != bar", subject.isCompatibleWith(DockerImageName.parse("bar"))); } + @Test public void testNoTagTreatedAsWildcard() { final DockerImageName subject = DockerImageName.parse("foo:4.5.6"); @@ -59,7 +59,10 @@ public void testImageWithClaimedCompatibilityAndVersion() { public void testImageWithClaimedCompatibilityForFullPath() { DockerImageName subject = DockerImageName.parse("foo").asCompatibleSubstituteFor("registry/repo/bar"); - assertTrue("foo(registry/repo/bar) ~= registry/repo/bar", subject.isCompatibleWith(DockerImageName.parse("registry/repo/bar"))); + assertTrue( + "foo(registry/repo/bar) ~= registry/repo/bar", + subject.isCompatibleWith(DockerImageName.parse("registry/repo/bar")) + ); assertFalse("foo(registry/repo/bar) != repo/bar", subject.isCompatibleWith(DockerImageName.parse("repo/bar"))); assertFalse("foo(registry/repo/bar) != bar", subject.isCompatibleWith(DockerImageName.parse("bar"))); } diff --git a/core/src/test/java/org/testcontainers/utility/DockerImageNameTest.java b/core/src/test/java/org/testcontainers/utility/DockerImageNameTest.java index e06407fbf56..98fd21064ed 100644 --- a/core/src/test/java/org/testcontainers/utility/DockerImageNameTest.java +++ b/core/src/test/java/org/testcontainers/utility/DockerImageNameTest.java @@ -16,7 +16,7 @@ public static class ValidNames { @Parameterized.Parameters(name = "{0}") public static String[] getNames() { - return new String[]{ + return new String[] { "myname:latest", "repo/my-name:1.0", "registry.foo.com:1234/my-name:1.0", @@ -27,7 +27,8 @@ public static String[] getNames() { "1.2.3.4/my-name:1.0", "1.2.3.4:1234/my-name:1.0", "1.2.3.4/repo-here/my-name:1.0", - "1.2.3.4:1234/repo-here/my-name:1.0"}; + "1.2.3.4:1234/repo-here/my-name:1.0", + }; } @Parameterized.Parameter @@ -44,11 +45,13 @@ public static class InvalidNames { @Parameterized.Parameters(name = "{0}") public static String[] getNames() { - return new String[]{":latest", + return new String[] { + ":latest", "/myname:latest", "/myname@sha256:latest", "/myname@sha256:gggggggggggggggggggggggggggggggg", - "repo:notaport/myname:latest"}; + "repo:notaport/myname:latest", + }; } @Parameterized.Parameter @@ -65,39 +68,43 @@ public static class Parsing { @Parameterized.Parameters(name = "{0}{1}{2}{3}{4}") public static String[][] getNames() { - return new String[][]{ - {"", "", "myname", ":", null}, - {"", "", "myname", ":", "latest"}, - {"", "", "repo/myname", ":", null}, - {"", "", "repo/myname", ":", "latest"}, - {"registry.foo.com:1234", "/", "my-name", ":", null}, - {"registry.foo.com:1234", "/", "my-name", ":", "1.0"}, - {"registry.foo.com", "/", "my-name", ":", "1.0"}, - {"registry.foo.com:1234", "/", "repo_here/my-name", ":", null}, - {"registry.foo.com:1234", "/", "repo_here/my-name", ":", "1.0"}, - {"1.2.3.4:1234", "/", "repo_here/my-name", ":", null}, - {"1.2.3.4:1234", "/", "repo_here/my-name", ":", "1.0"}, - {"1.2.3.4:1234", "/", "my-name", ":", null}, - {"1.2.3.4:1234", "/", "my-name", ":", "1.0"}, - {"", "", "myname", "@", "sha256:1234abcd1234abcd1234abcd1234abcd"}, - {"", "", "repo/myname", "@", "sha256:1234abcd1234abcd1234abcd1234abcd"}, - {"registry.foo.com:1234", "/", "repo-here/my-name", "@", "sha256:1234abcd1234abcd1234abcd1234abcd"}, - {"registry.foo.com:1234", "/", "my-name", "@", "sha256:1234abcd1234abcd1234abcd1234abcd"}, - {"1.2.3.4", "/", "my-name", "@", "sha256:1234abcd1234abcd1234abcd1234abcd"}, - {"1.2.3.4:1234", "/", "my-name", "@", "sha256:1234abcd1234abcd1234abcd1234abcd"}, - {"1.2.3.4", "/", "my-name", "@", "sha256:1234abcd1234abcd1234abcd1234abcd"}, - {"1.2.3.4:1234", "/", "my-name", "@", "sha256:1234abcd1234abcd1234abcd1234abcd"} + return new String[][] { + { "", "", "myname", ":", null }, + { "", "", "myname", ":", "latest" }, + { "", "", "repo/myname", ":", null }, + { "", "", "repo/myname", ":", "latest" }, + { "registry.foo.com:1234", "/", "my-name", ":", null }, + { "registry.foo.com:1234", "/", "my-name", ":", "1.0" }, + { "registry.foo.com", "/", "my-name", ":", "1.0" }, + { "registry.foo.com:1234", "/", "repo_here/my-name", ":", null }, + { "registry.foo.com:1234", "/", "repo_here/my-name", ":", "1.0" }, + { "1.2.3.4:1234", "/", "repo_here/my-name", ":", null }, + { "1.2.3.4:1234", "/", "repo_here/my-name", ":", "1.0" }, + { "1.2.3.4:1234", "/", "my-name", ":", null }, + { "1.2.3.4:1234", "/", "my-name", ":", "1.0" }, + { "", "", "myname", "@", "sha256:1234abcd1234abcd1234abcd1234abcd" }, + { "", "", "repo/myname", "@", "sha256:1234abcd1234abcd1234abcd1234abcd" }, + { "registry.foo.com:1234", "/", "repo-here/my-name", "@", "sha256:1234abcd1234abcd1234abcd1234abcd" }, + { "registry.foo.com:1234", "/", "my-name", "@", "sha256:1234abcd1234abcd1234abcd1234abcd" }, + { "1.2.3.4", "/", "my-name", "@", "sha256:1234abcd1234abcd1234abcd1234abcd" }, + { "1.2.3.4:1234", "/", "my-name", "@", "sha256:1234abcd1234abcd1234abcd1234abcd" }, + { "1.2.3.4", "/", "my-name", "@", "sha256:1234abcd1234abcd1234abcd1234abcd" }, + { "1.2.3.4:1234", "/", "my-name", "@", "sha256:1234abcd1234abcd1234abcd1234abcd" }, }; } @Parameterized.Parameter(0) public String registry; + @Parameterized.Parameter(1) public String registrySeparator; + @Parameterized.Parameter(2) public String repo; + @Parameterized.Parameter(3) public String versionSeparator; + @Parameterized.Parameter(4) public String version; @@ -120,21 +127,45 @@ public void testParsing() { final DockerImageName imageName = DockerImageName.parse(combined); assertEquals(combined + " has registry address: " + registry, registry, imageName.getRegistry()); - assertEquals(combined + " has unversioned part: " + unversionedPart, unversionedPart, imageName.getUnversionedPart()); + assertEquals( + combined + " has unversioned part: " + unversionedPart, + unversionedPart, + imageName.getUnversionedPart() + ); if (version != null) { assertEquals(combined + " has version part: " + version, version, imageName.getVersionPart()); } else { - assertEquals(combined + " has automatic 'latest' version specified", "latest", imageName.getVersionPart()); + assertEquals( + combined + " has automatic 'latest' version specified", + "latest", + imageName.getVersionPart() + ); } - assertEquals(combined + " has canonical name: " + canonicalName, canonicalName, imageName.asCanonicalNameString()); + assertEquals( + combined + " has canonical name: " + canonicalName, + canonicalName, + imageName.asCanonicalNameString() + ); if (version != null) { VisibleAssertions.context("Using two-arg constructor:", 2); final DockerImageName imageNameFromSecondaryConstructor = new DockerImageName(unversionedPart, version); - assertEquals(combined + " has registry address: " + registry, registry, imageNameFromSecondaryConstructor.getRegistry()); - assertEquals(combined + " has unversioned part: " + unversionedPart, unversionedPart, imageNameFromSecondaryConstructor.getUnversionedPart()); - assertEquals(combined + " has version part: " + version, version, imageNameFromSecondaryConstructor.getVersionPart()); + assertEquals( + combined + " has registry address: " + registry, + registry, + imageNameFromSecondaryConstructor.getRegistry() + ); + assertEquals( + combined + " has unversioned part: " + unversionedPart, + unversionedPart, + imageNameFromSecondaryConstructor.getUnversionedPart() + ); + assertEquals( + combined + " has version part: " + version, + version, + imageNameFromSecondaryConstructor.getVersionPart() + ); } } } diff --git a/core/src/test/java/org/testcontainers/utility/DockerStatusTest.java b/core/src/test/java/org/testcontainers/utility/DockerStatusTest.java index 1ef8e136a32..36196aaf4c1 100644 --- a/core/src/test/java/org/testcontainers/utility/DockerStatusTest.java +++ b/core/src/test/java/org/testcontainers/utility/DockerStatusTest.java @@ -8,7 +8,7 @@ import java.time.Duration; import java.time.Instant; -import java.time.ZoneId; +import java.time.ZoneId; import java.time.format.DateTimeFormatter; import static org.junit.Assert.assertFalse; @@ -18,8 +18,9 @@ /** * */ - @RunWith(Parameterized.class) +@RunWith(Parameterized.class) public class DockerStatusTest { + private final DateTimeFormatter dateTimeFormatter; private static final Instant now = Instant.now(); @@ -55,8 +56,8 @@ public DockerStatusTest(DateTimeFormatter dateTimeFormatter) { @Parameterized.Parameters public static Object[][] parameters() { return new Object[][] { - { DateTimeFormatter.ISO_INSTANT}, - { DateTimeFormatter.ISO_OFFSET_DATE_TIME.withZone(ZoneId.of("America/New_York")) } + { DateTimeFormatter.ISO_INSTANT }, + { DateTimeFormatter.ISO_OFFSET_DATE_TIME.withZone(ZoneId.of("America/New_York")) }, }; } @@ -88,9 +89,12 @@ private String buildTimestamp(Instant instant) { // ContainerState is a non-static inner class, with private member variables, in a different package. // It's simpler to mock it that to try to create one. - private static InspectContainerResponse.ContainerState buildState(boolean running, boolean paused, - String startedAt, String finishedAt) { - + private static InspectContainerResponse.ContainerState buildState( + boolean running, + boolean paused, + String startedAt, + String finishedAt + ) { InspectContainerResponse.ContainerState state = Mockito.mock(InspectContainerResponse.ContainerState.class); when(state.getRunning()).thenReturn(running); when(state.getPaused()).thenReturn(paused); diff --git a/core/src/test/java/org/testcontainers/utility/FakeImageSubstitutor.java b/core/src/test/java/org/testcontainers/utility/FakeImageSubstitutor.java index f74a6c942b3..50681ddc729 100644 --- a/core/src/test/java/org/testcontainers/utility/FakeImageSubstitutor.java +++ b/core/src/test/java/org/testcontainers/utility/FakeImageSubstitutor.java @@ -1,6 +1,7 @@ package org.testcontainers.utility; public class FakeImageSubstitutor extends ImageNameSubstitutor { + @Override public DockerImageName apply(final DockerImageName original) { return DockerImageName.parse("transformed-" + original.asCanonicalNameString()); diff --git a/core/src/test/java/org/testcontainers/utility/FilterRegistryTest.java b/core/src/test/java/org/testcontainers/utility/FilterRegistryTest.java index 49bc870e8d0..30367975d1e 100644 --- a/core/src/test/java/org/testcontainers/utility/FilterRegistryTest.java +++ b/core/src/test/java/org/testcontainers/utility/FilterRegistryTest.java @@ -9,22 +9,27 @@ import java.io.InputStream; import java.io.OutputStream; import java.util.AbstractMap.SimpleEntry; +import java.util.Arrays; import java.util.List; import java.util.Map.Entry; -import static java.util.Arrays.asList; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; public class FilterRegistryTest { - private static final List> FILTERS = asList( - new SimpleEntry<>("key1!", "value2?"), new SimpleEntry<>("key2#", "value2%") + private static final List> FILTERS = Arrays.asList( + new SimpleEntry<>("key1!", "value2?"), + new SimpleEntry<>("key2#", "value2%") ); + private static final String URL_ENCODED_FILTERS = "key1%21=value2%3F&key2%23=value2%25"; + private static final byte[] ACKNOWLEDGEMENT = FilterRegistry.ACKNOWLEDGMENT.getBytes(); + private static final byte[] NO_ACKNOWLEDGEMENT = "".getBytes(); + private static final String NEW_LINE = "\n"; @Test @@ -66,5 +71,4 @@ private static InputStream anyInputStream() { private static OutputStream anyOutputStream() { return new ByteArrayOutputStream(); } - } diff --git a/core/src/test/java/org/testcontainers/utility/ImageNameSubstitutorTest.java b/core/src/test/java/org/testcontainers/utility/ImageNameSubstitutorTest.java index 97ee0213f21..f7e319a5948 100644 --- a/core/src/test/java/org/testcontainers/utility/ImageNameSubstitutorTest.java +++ b/core/src/test/java/org/testcontainers/utility/ImageNameSubstitutorTest.java @@ -13,7 +13,9 @@ public class ImageNameSubstitutorTest { @Rule public MockTestcontainersConfigurationRule config = new MockTestcontainersConfigurationRule(); + private ImageNameSubstitutor originalInstance; + private ImageNameSubstitutor originalDefaultImplementation; @Before @@ -27,10 +29,7 @@ public void setUp() throws Exception { .doReturn(DockerImageName.parse("substituted-image")) .when(ImageNameSubstitutor.defaultImplementation) .apply(eq(DockerImageName.parse("original"))); - Mockito - .doReturn("default implementation") - .when(ImageNameSubstitutor.defaultImplementation) - .getDescription(); + Mockito.doReturn("default implementation").when(ImageNameSubstitutor.defaultImplementation).getDescription(); } @After @@ -58,10 +57,7 @@ public void simpleConfigurationTest() { @Test public void testWorksWithoutConfiguredImplementation() { - Mockito - .doReturn(null) - .when(TestcontainersConfiguration.getInstance()) - .getImageSubstitutorClassName(); + Mockito.doReturn(null).when(TestcontainersConfiguration.getInstance()).getImageSubstitutorClassName(); final ImageNameSubstitutor imageNameSubstitutor = ImageNameSubstitutor.instance(); diff --git a/core/src/test/java/org/testcontainers/utility/LazyFutureTest.java b/core/src/test/java/org/testcontainers/utility/LazyFutureTest.java index 81805106806..99443221805 100644 --- a/core/src/test/java/org/testcontainers/utility/LazyFutureTest.java +++ b/core/src/test/java/org/testcontainers/utility/LazyFutureTest.java @@ -8,9 +8,9 @@ import java.util.List; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; import java.util.stream.IntStream; -import static java.util.stream.Collectors.toList; import static org.rnorth.visibleassertions.VisibleAssertions.*; public class LazyFutureTest { @@ -64,9 +64,14 @@ protected Integer resolve() { } }; - Future> task = new ForkJoinPool(numOfThreads).submit(() -> { - return IntStream.rangeClosed(1, numOfThreads).parallel().mapToObj(i -> Futures.getUnchecked(lazyFuture)).collect(toList()); - }); + Future> task = new ForkJoinPool(numOfThreads) + .submit(() -> { + return IntStream + .rangeClosed(1, numOfThreads) + .parallel() + .mapToObj(i -> Futures.getUnchecked(lazyFuture)) + .collect(Collectors.toList()); + }); while (latch.getCount() > 0) { latch.countDown(); @@ -74,5 +79,4 @@ protected Integer resolve() { assertEquals("All threads receives the same result", Collections.nCopies(numOfThreads, 1), task.get()); } - -} \ No newline at end of file +} diff --git a/core/src/test/java/org/testcontainers/utility/MountableFileTest.java b/core/src/test/java/org/testcontainers/utility/MountableFileTest.java index c7dcaad2908..d3ff085c5de 100644 --- a/core/src/test/java/org/testcontainers/utility/MountableFileTest.java +++ b/core/src/test/java/org/testcontainers/utility/MountableFileTest.java @@ -20,7 +20,9 @@ public class MountableFileTest { private static final int TEST_FILE_MODE = 0532; + private static final int BASE_FILE_MODE = 0100000; + private static final int BASE_DIR_MODE = 0040000; @Test @@ -67,7 +69,10 @@ public void forHostPathWithSpaces() throws Exception { performChecks(mountableFile); assertTrue("The resolved path contains the original space", mountableFile.getResolvedPath().contains(" ")); - assertFalse("The resolved path does not contain an escaped space", mountableFile.getResolvedPath().contains("\\ ")); + assertFalse( + "The resolved path does not contain an escaped space", + mountableFile.getResolvedPath().contains("\\ ") + ); } @Test @@ -78,13 +83,18 @@ public void forHostPathWithPlus() throws Exception { performChecks(mountableFile); assertTrue("The resolved path contains the original space", mountableFile.getResolvedPath().contains("+")); - assertFalse("The resolved path does not contain an escaped space", mountableFile.getResolvedPath().contains(" ")); + assertFalse( + "The resolved path does not contain an escaped space", + mountableFile.getResolvedPath().contains(" ") + ); } @Test public void forClasspathResourceWithPermission() throws Exception { - final MountableFile mountableFile = MountableFile.forClasspathResource("mappable-resource/test-resource.txt", - TEST_FILE_MODE); + final MountableFile mountableFile = MountableFile.forClasspathResource( + "mappable-resource/test-resource.txt", + TEST_FILE_MODE + ); performChecks(mountableFile); assertEquals("Valid file mode.", BASE_FILE_MODE | TEST_FILE_MODE, mountableFile.getFileMode()); @@ -110,7 +120,8 @@ public void forHostDirPathWithPermission() throws Exception { public void noTrailingSlashesInTarEntryNames() throws Exception { final MountableFile mountableFile = MountableFile.forClasspathResource("mappable-resource/test-resource.txt"); - @Cleanup final TarArchiveInputStream tais = intoTarArchive((taos) -> { + @Cleanup + final TarArchiveInputStream tais = intoTarArchive(taos -> { mountableFile.transferTo(taos, "/some/path.txt"); mountableFile.transferTo(taos, "/path.txt"); mountableFile.transferTo(taos, "path.txt"); @@ -123,8 +134,10 @@ public void noTrailingSlashesInTarEntryNames() throws Exception { } private TarArchiveInputStream intoTarArchive(Consumer consumer) throws IOException { - @Cleanup final ByteArrayOutputStream baos = new ByteArrayOutputStream(); - @Cleanup final TarArchiveOutputStream taos = new TarArchiveOutputStream(baos); + @Cleanup + final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + @Cleanup + final TarArchiveOutputStream taos = new TarArchiveOutputStream(baos); consumer.accept(taos); taos.close(); @@ -151,7 +164,9 @@ private Path createTempDir() throws IOException { private void performChecks(final MountableFile mountableFile) { final String mountablePath = mountableFile.getResolvedPath(); assertTrue("The filesystem path '" + mountablePath + "' can be found", new File(mountablePath).exists()); - assertFalse("The filesystem path '" + mountablePath + "' does not contain any URL escaping", mountablePath.contains("%20")); + assertFalse( + "The filesystem path '" + mountablePath + "' does not contain any URL escaping", + mountablePath.contains("%20") + ); } - } diff --git a/core/src/test/java/org/testcontainers/utility/PrefixingImageNameSubstitutorTest.java b/core/src/test/java/org/testcontainers/utility/PrefixingImageNameSubstitutorTest.java index d2c33989deb..211f8e00536 100644 --- a/core/src/test/java/org/testcontainers/utility/PrefixingImageNameSubstitutorTest.java +++ b/core/src/test/java/org/testcontainers/utility/PrefixingImageNameSubstitutorTest.java @@ -8,11 +8,11 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.testcontainers.utility.PrefixingImageNameSubstitutor.PREFIX_PROPERTY_KEY; public class PrefixingImageNameSubstitutorTest { private TestcontainersConfiguration mockConfiguration; + private PrefixingImageNameSubstitutor underTest; @Before @@ -23,7 +23,8 @@ public void setUp() { @Test public void testHappyPath() { - when(mockConfiguration.getEnvVarOrProperty(eq(PREFIX_PROPERTY_KEY), any())).thenReturn("someregistry.com/our-mirror/"); + when(mockConfiguration.getEnvVarOrProperty(eq(PrefixingImageNameSubstitutor.PREFIX_PROPERTY_KEY), any())) + .thenReturn("someregistry.com/our-mirror/"); final DockerImageName result = underTest.apply(DockerImageName.parse("some/image:tag")); @@ -36,33 +37,28 @@ public void testHappyPath() { @Test public void hubIoRegistryIsNotChanged() { - when(mockConfiguration.getEnvVarOrProperty(eq(PREFIX_PROPERTY_KEY), any())).thenReturn("someregistry.com/our-mirror/"); + when(mockConfiguration.getEnvVarOrProperty(eq(PrefixingImageNameSubstitutor.PREFIX_PROPERTY_KEY), any())) + .thenReturn("someregistry.com/our-mirror/"); final DockerImageName result = underTest.apply(DockerImageName.parse("docker.io/some/image:tag")); - assertEquals( - "The prefix is applied", - "docker.io/some/image:tag", - result.asCanonicalNameString() - ); + assertEquals("The prefix is applied", "docker.io/some/image:tag", result.asCanonicalNameString()); } @Test public void hubComRegistryIsNotChanged() { - when(mockConfiguration.getEnvVarOrProperty(eq(PREFIX_PROPERTY_KEY), any())).thenReturn("someregistry.com/our-mirror/"); + when(mockConfiguration.getEnvVarOrProperty(eq(PrefixingImageNameSubstitutor.PREFIX_PROPERTY_KEY), any())) + .thenReturn("someregistry.com/our-mirror/"); final DockerImageName result = underTest.apply(DockerImageName.parse("registry.hub.docker.com/some/image:tag")); - assertEquals( - "The prefix is applied", - "registry.hub.docker.com/some/image:tag", - result.asCanonicalNameString() - ); + assertEquals("The prefix is applied", "registry.hub.docker.com/some/image:tag", result.asCanonicalNameString()); } @Test public void thirdPartyRegistriesNotAffected() { - when(mockConfiguration.getEnvVarOrProperty(eq(PREFIX_PROPERTY_KEY), any())).thenReturn("someregistry.com/our-mirror/"); + when(mockConfiguration.getEnvVarOrProperty(eq(PrefixingImageNameSubstitutor.PREFIX_PROPERTY_KEY), any())) + .thenReturn("someregistry.com/our-mirror/"); final DockerImageName result = underTest.apply(DockerImageName.parse("gcr.io/something/image:tag")); @@ -75,7 +71,8 @@ public void thirdPartyRegistriesNotAffected() { @Test public void testNoDoublePrefixing() { - when(mockConfiguration.getEnvVarOrProperty(eq(PREFIX_PROPERTY_KEY), any())).thenReturn("someregistry.com/our-mirror/"); + when(mockConfiguration.getEnvVarOrProperty(eq(PrefixingImageNameSubstitutor.PREFIX_PROPERTY_KEY), any())) + .thenReturn("someregistry.com/our-mirror/"); final DockerImageName result = underTest.apply(DockerImageName.parse("someregistry.com/some/image:tag")); @@ -88,7 +85,8 @@ public void testNoDoublePrefixing() { @Test public void testHandlesEmptyValue() { - when(mockConfiguration.getEnvVarOrProperty(eq(PREFIX_PROPERTY_KEY), any())).thenReturn(""); + when(mockConfiguration.getEnvVarOrProperty(eq(PrefixingImageNameSubstitutor.PREFIX_PROPERTY_KEY), any())) + .thenReturn(""); final DockerImageName result = underTest.apply(DockerImageName.parse("some/image:tag")); @@ -101,39 +99,38 @@ public void testHandlesEmptyValue() { @Test public void testHandlesRegistryOnlyWithTrailingSlash() { - when(mockConfiguration.getEnvVarOrProperty(eq(PREFIX_PROPERTY_KEY), any())).thenReturn("someregistry.com/"); + when(mockConfiguration.getEnvVarOrProperty(eq(PrefixingImageNameSubstitutor.PREFIX_PROPERTY_KEY), any())) + .thenReturn("someregistry.com/"); final DockerImageName result = underTest.apply(DockerImageName.parse("some/image:tag")); - assertEquals( - "The prefix is applied", - "someregistry.com/some/image:tag", - result.asCanonicalNameString() - ); + assertEquals("The prefix is applied", "someregistry.com/some/image:tag", result.asCanonicalNameString()); } @Test public void testCombinesLiterallyForRegistryOnlyWithoutTrailingSlash() { - when(mockConfiguration.getEnvVarOrProperty(eq(PREFIX_PROPERTY_KEY), any())).thenReturn("someregistry.com"); + when(mockConfiguration.getEnvVarOrProperty(eq(PrefixingImageNameSubstitutor.PREFIX_PROPERTY_KEY), any())) + .thenReturn("someregistry.com"); final DockerImageName result = underTest.apply(DockerImageName.parse("some/image:tag")); assertEquals( "The prefix is applied", - "someregistry.comsome/image:tag", // treating the prefix literally, for predictability + "someregistry.comsome/image:tag", // treating the prefix literally, for predictability result.asCanonicalNameString() ); } @Test public void testCombinesLiterallyForBothPartsWithoutTrailingSlash() { - when(mockConfiguration.getEnvVarOrProperty(eq(PREFIX_PROPERTY_KEY), any())).thenReturn("someregistry.com/our-mirror"); + when(mockConfiguration.getEnvVarOrProperty(eq(PrefixingImageNameSubstitutor.PREFIX_PROPERTY_KEY), any())) + .thenReturn("someregistry.com/our-mirror"); final DockerImageName result = underTest.apply(DockerImageName.parse("some/image:tag")); assertEquals( "The prefix is applied", - "someregistry.com/our-mirrorsome/image:tag", // treating the prefix literally, for predictability + "someregistry.com/our-mirrorsome/image:tag", // treating the prefix literally, for predictability result.asCanonicalNameString() ); } diff --git a/core/src/test/java/org/testcontainers/utility/RegistryAuthLocatorTest.java b/core/src/test/java/org/testcontainers/utility/RegistryAuthLocatorTest.java index 5db2583958a..4ddfad8454f 100644 --- a/core/src/test/java/org/testcontainers/utility/RegistryAuthLocatorTest.java +++ b/core/src/test/java/org/testcontainers/utility/RegistryAuthLocatorTest.java @@ -14,13 +14,21 @@ import static org.rnorth.visibleassertions.VisibleAssertions.*; public class RegistryAuthLocatorTest { + @Test public void lookupAuthConfigWithoutCredentials() throws URISyntaxException { final RegistryAuthLocator authLocator = createTestAuthLocator("config-empty.json"); - final AuthConfig authConfig = authLocator.lookupAuthConfig(DockerImageName.parse("unauthenticated.registry.org/org/repo"), new AuthConfig()); + final AuthConfig authConfig = authLocator.lookupAuthConfig( + DockerImageName.parse("unauthenticated.registry.org/org/repo"), + new AuthConfig() + ); - assertEquals("Default docker registry URL is set on auth config", "https://index.docker.io/v1/", authConfig.getRegistryAddress()); + assertEquals( + "Default docker registry URL is set on auth config", + "https://index.docker.io/v1/", + authConfig.getRegistryAddress() + ); assertNull("No username is set", authConfig.getUsername()); assertNull("No password is set", authConfig.getPassword()); } @@ -29,9 +37,16 @@ public void lookupAuthConfigWithoutCredentials() throws URISyntaxException { public void lookupAuthConfigWithBasicAuthCredentials() throws URISyntaxException { final RegistryAuthLocator authLocator = createTestAuthLocator("config-basic-auth.json"); - final AuthConfig authConfig = authLocator.lookupAuthConfig(DockerImageName.parse("registry.example.com/org/repo"), new AuthConfig()); + final AuthConfig authConfig = authLocator.lookupAuthConfig( + DockerImageName.parse("registry.example.com/org/repo"), + new AuthConfig() + ); - assertEquals("Default docker registry URL is set on auth config", "https://registry.example.com", authConfig.getRegistryAddress()); + assertEquals( + "Default docker registry URL is set on auth config", + "https://registry.example.com", + authConfig.getRegistryAddress() + ); assertEquals("Username is set", "user", authConfig.getUsername()); assertEquals("Password is set", "pass", authConfig.getPassword()); } @@ -40,9 +55,16 @@ public void lookupAuthConfigWithBasicAuthCredentials() throws URISyntaxException public void lookupAuthConfigWithJsonKeyCredentials() throws URISyntaxException { final RegistryAuthLocator authLocator = createTestAuthLocator("config-with-json-key.json"); - final AuthConfig authConfig = authLocator.lookupAuthConfig(DockerImageName.parse("registry.example.com/org/repo"), new AuthConfig()); + final AuthConfig authConfig = authLocator.lookupAuthConfig( + DockerImageName.parse("registry.example.com/org/repo"), + new AuthConfig() + ); - assertEquals("Default docker registry URL is set on auth config", "https://registry.example.com", authConfig.getRegistryAddress()); + assertEquals( + "Default docker registry URL is set on auth config", + "https://registry.example.com", + authConfig.getRegistryAddress() + ); assertEquals("Username is set", "_json_key", authConfig.getUsername()); assertNotNull("Password is set", authConfig.getPassword()); } @@ -51,7 +73,10 @@ public void lookupAuthConfigWithJsonKeyCredentials() throws URISyntaxException { public void lookupAuthConfigUsingStore() throws URISyntaxException { final RegistryAuthLocator authLocator = createTestAuthLocator("config-with-store.json"); - final AuthConfig authConfig = authLocator.lookupAuthConfig(DockerImageName.parse("registry.example.com/org/repo"), new AuthConfig()); + final AuthConfig authConfig = authLocator.lookupAuthConfig( + DockerImageName.parse("registry.example.com/org/repo"), + new AuthConfig() + ); assertEquals("Correct server URL is obtained from a credential store", "url", authConfig.getRegistryAddress()); assertEquals("Correct username is obtained from a credential store", "username", authConfig.getUsername()); @@ -62,7 +87,10 @@ public void lookupAuthConfigUsingStore() throws URISyntaxException { public void lookupAuthConfigUsingHelper() throws URISyntaxException { final RegistryAuthLocator authLocator = createTestAuthLocator("config-with-helper.json"); - final AuthConfig authConfig = authLocator.lookupAuthConfig(DockerImageName.parse("registry.example.com/org/repo"), new AuthConfig()); + final AuthConfig authConfig = authLocator.lookupAuthConfig( + DockerImageName.parse("registry.example.com/org/repo"), + new AuthConfig() + ); assertEquals("Correct server URL is obtained from a credential store", "url", authConfig.getRegistryAddress()); assertEquals("Correct username is obtained from a credential store", "username", authConfig.getUsername()); @@ -73,16 +101,26 @@ public void lookupAuthConfigUsingHelper() throws URISyntaxException { public void lookupAuthConfigUsingHelperWithToken() throws URISyntaxException { final RegistryAuthLocator authLocator = createTestAuthLocator("config-with-helper-using-token.json"); - final AuthConfig authConfig = authLocator.lookupAuthConfig(DockerImageName.parse("registrytoken.example.com/org/repo"), new AuthConfig()); + final AuthConfig authConfig = authLocator.lookupAuthConfig( + DockerImageName.parse("registrytoken.example.com/org/repo"), + new AuthConfig() + ); - assertEquals("Correct identitytoken is obtained from a credential store", "secret", authConfig.getIdentitytoken()); + assertEquals( + "Correct identitytoken is obtained from a credential store", + "secret", + authConfig.getIdentitytoken() + ); } @Test public void lookupUsingHelperEmptyAuth() throws URISyntaxException { final RegistryAuthLocator authLocator = createTestAuthLocator("config-empty-auth-with-helper.json"); - final AuthConfig authConfig = authLocator.lookupAuthConfig(DockerImageName.parse("registry.example.com/org/repo"), new AuthConfig()); + final AuthConfig authConfig = authLocator.lookupAuthConfig( + DockerImageName.parse("registry.example.com/org/repo"), + new AuthConfig() + ); assertEquals("Correct server URL is obtained from a credential store", "url", authConfig.getRegistryAddress()); assertEquals("Correct username is obtained from a credential store", "username", authConfig.getUsername()); @@ -93,7 +131,10 @@ public void lookupUsingHelperEmptyAuth() throws URISyntaxException { public void lookupNonEmptyAuthWithHelper() throws URISyntaxException { final RegistryAuthLocator authLocator = createTestAuthLocator("config-existing-auth-with-helper.json"); - final AuthConfig authConfig = authLocator.lookupAuthConfig(DockerImageName.parse("registry.example.com/org/repo"), new AuthConfig()); + final AuthConfig authConfig = authLocator.lookupAuthConfig( + DockerImageName.parse("registry.example.com/org/repo"), + new AuthConfig() + ); assertEquals("Correct server URL is obtained from a credential helper", "url", authConfig.getRegistryAddress()); assertEquals("Correct username is obtained from a credential helper", "username", authConfig.getUsername()); @@ -103,7 +144,10 @@ public void lookupNonEmptyAuthWithHelper() throws URISyntaxException { @Test public void lookupAuthConfigWithCredentialsNotFound() throws URISyntaxException { Map notFoundMessagesReference = new HashMap<>(); - final RegistryAuthLocator authLocator = createTestAuthLocator("config-with-store.json", notFoundMessagesReference); + final RegistryAuthLocator authLocator = createTestAuthLocator( + "config-with-store.json", + notFoundMessagesReference + ); DockerImageName dockerImageName = DockerImageName.parse("registry2.example.com/org/repo"); final AuthConfig authConfig = authLocator.lookupAuthConfig(dockerImageName, new AuthConfig()); @@ -117,7 +161,8 @@ public void lookupAuthConfigWithCredentialsNotFound() throws URISyntaxException assertEquals( "Not correct message discovered", "Fake credentials not found on credentials store 'https://not.a.real.registry/url'", - discoveredMessage); + discoveredMessage + ); } @Test @@ -136,7 +181,8 @@ private RegistryAuthLocator createTestAuthLocator(String configName) throws URIS } @NotNull - private RegistryAuthLocator createTestAuthLocator(String configName, Map notFoundMessagesReference) throws URISyntaxException { + private RegistryAuthLocator createTestAuthLocator(String configName, Map notFoundMessagesReference) + throws URISyntaxException { final File configFile = new File(Resources.getResource("auth-config/" + configName).toURI()); String commandPathPrefix = configFile.getParentFile().getAbsolutePath() + "/"; diff --git a/core/src/test/java/org/testcontainers/utility/ResourceReaperTest.java b/core/src/test/java/org/testcontainers/utility/ResourceReaperTest.java index 214b465c297..60f57a28b3e 100644 --- a/core/src/test/java/org/testcontainers/utility/ResourceReaperTest.java +++ b/core/src/test/java/org/testcontainers/utility/ResourceReaperTest.java @@ -42,25 +42,27 @@ public void shouldCleanupWithJVM() { private void assertCleanup(Map labels) { DockerClient client = DockerClientFactory.instance().client(); - ConditionFactory awaitFactory = Awaitility.await().atMost(Duration.ofMinutes(1)).pollInterval(Duration.ofSeconds(1)); - - List labelValues = labels.entrySet().stream() + ConditionFactory awaitFactory = Awaitility + .await() + .atMost(Duration.ofMinutes(1)) + .pollInterval(Duration.ofSeconds(1)); + + List labelValues = labels + .entrySet() + .stream() .map(it -> it.getKey() + "=" + it.getValue()) .collect(Collectors.toList()); awaitFactory.untilAsserted(() -> { - assertThat(client.listContainersCmd().withFilter("label", labelValues).withShowAll(true).exec()) - .isEmpty(); + assertThat(client.listContainersCmd().withFilter("label", labelValues).withShowAll(true).exec()).isEmpty(); }); awaitFactory.untilAsserted(() -> { - assertThat(client.listNetworksCmd().withFilter("label", labelValues).exec()) - .isEmpty(); + assertThat(client.listNetworksCmd().withFilter("label", labelValues).exec()).isEmpty(); }); awaitFactory.untilAsserted(() -> { - assertThat(client.listVolumesCmd().withFilter("label", labelValues).exec().getVolumes()) - .isEmpty(); + assertThat(client.listVolumesCmd().withFilter("label", labelValues).exec().getVolumes()).isEmpty(); }); } @@ -81,7 +83,8 @@ private Map runProcess(Consumer processExecutor ProcessResult result = processExecutor.execute(); assertEquals(0, result.getExitValue()); - String labelsJson = Stream.of(result.outputUTF8().split("\n")) + String labelsJson = Stream + .of(result.outputUTF8().split("\n")) .filter(it -> it.startsWith(SimpleUsage.LABELS_MARKER)) .map(it -> it.substring(SimpleUsage.LABELS_MARKER.length())) .findFirst() @@ -97,7 +100,9 @@ public static class SimpleUsage { @SneakyThrows @SuppressWarnings("deprecation") public static void main(String[] args) { - System.out.println(LABELS_MARKER + new ObjectMapper().writeValueAsString(ResourceReaper.instance().getLabels())); + System.out.println( + LABELS_MARKER + new ObjectMapper().writeValueAsString(ResourceReaper.instance().getLabels()) + ); GenericContainer container = new GenericContainer<>("testcontainers/helloworld:1.1.0") .withNetwork(org.testcontainers.containers.Network.newNetwork()) diff --git a/core/src/test/java/org/testcontainers/utility/TestcontainersConfigurationTest.java b/core/src/test/java/org/testcontainers/utility/TestcontainersConfigurationTest.java index 7ca656e1eaf..56cbdb98a56 100644 --- a/core/src/test/java/org/testcontainers/utility/TestcontainersConfigurationTest.java +++ b/core/src/test/java/org/testcontainers/utility/TestcontainersConfigurationTest.java @@ -15,7 +15,9 @@ public class TestcontainersConfigurationTest { private Properties userProperties; + private Properties classpathProperties; + private Map environment; @Before @@ -117,21 +119,33 @@ public void shouldReadChecksFromEnvironment() { public void shouldReadDockerSettingsFromEnvironmentWithoutTestcontainersPrefix() { userProperties.remove("docker.foo"); environment.put("DOCKER_FOO", "some value"); - assertEquals("reads unprefixed env vars for docker. settings", "some value", newConfig().getEnvVarOrUserProperty("docker.foo", "default")); + assertEquals( + "reads unprefixed env vars for docker. settings", + "some value", + newConfig().getEnvVarOrUserProperty("docker.foo", "default") + ); } @Test public void shouldNotReadDockerSettingsFromEnvironmentWithTestcontainersPrefix() { userProperties.remove("docker.foo"); environment.put("TESTCONTAINERS_DOCKER_FOO", "some value"); - assertEquals("reads unprefixed env vars for docker. settings", "default", newConfig().getEnvVarOrUserProperty("docker.foo", "default")); + assertEquals( + "reads unprefixed env vars for docker. settings", + "default", + newConfig().getEnvVarOrUserProperty("docker.foo", "default") + ); } @Test public void shouldReadDockerSettingsFromUserProperties() { environment.remove("DOCKER_FOO"); userProperties.put("docker.foo", "some value"); - assertEquals("reads unprefixed user properties for docker. settings", "some value", newConfig().getEnvVarOrUserProperty("docker.foo", "default")); + assertEquals( + "reads unprefixed user properties for docker. settings", + "some value", + newConfig().getEnvVarOrUserProperty("docker.foo", "default") + ); } @Test @@ -139,41 +153,69 @@ public void shouldNotReadDockerClientStrategyFromClasspathProperties() { String currentValue = newConfig().getDockerClientStrategyClassName(); classpathProperties.setProperty("docker.client.strategy", UUID.randomUUID().toString()); - assertEquals("Docker client strategy is not affected by classpath properties", currentValue, newConfig().getDockerClientStrategyClassName()); + assertEquals( + "Docker client strategy is not affected by classpath properties", + currentValue, + newConfig().getDockerClientStrategyClassName() + ); } @Test public void shouldReadDockerClientStrategyFromUserProperties() { userProperties.setProperty("docker.client.strategy", "foo"); - assertEquals("Docker client strategy is changed by user property", "foo", newConfig().getDockerClientStrategyClassName()); + assertEquals( + "Docker client strategy is changed by user property", + "foo", + newConfig().getDockerClientStrategyClassName() + ); } @Test public void shouldReadDockerClientStrategyFromEnvironment() { userProperties.remove("docker.client.strategy"); environment.put("TESTCONTAINERS_DOCKER_CLIENT_STRATEGY", "foo"); - assertEquals("Docker client strategy is changed by env var", "foo", newConfig().getDockerClientStrategyClassName()); + assertEquals( + "Docker client strategy is changed by env var", + "foo", + newConfig().getDockerClientStrategyClassName() + ); } @Test public void shouldNotUseImplicitDockerClientStrategyWhenDockerHostAndStrategyAreBothSet() { userProperties.put("docker.client.strategy", "foo"); userProperties.put("docker.host", "tcp://1.2.3.4:5678"); - assertEquals("Docker client strategy is can be explicitly set", "foo", newConfig().getDockerClientStrategyClassName()); + assertEquals( + "Docker client strategy is can be explicitly set", + "foo", + newConfig().getDockerClientStrategyClassName() + ); userProperties.remove("docker.client.strategy"); environment.put("TESTCONTAINERS_DOCKER_CLIENT_STRATEGY", "bar"); userProperties.put("docker.client.strategy", "foo"); - assertEquals("Docker client strategy is can be explicitly set", "bar", newConfig().getDockerClientStrategyClassName()); + assertEquals( + "Docker client strategy is can be explicitly set", + "bar", + newConfig().getDockerClientStrategyClassName() + ); environment.put("TESTCONTAINERS_DOCKER_CLIENT_STRATEGY", "bar"); userProperties.remove("docker.client.strategy"); - assertEquals("Docker client strategy is can be explicitly set", "bar", newConfig().getDockerClientStrategyClassName()); + assertEquals( + "Docker client strategy is can be explicitly set", + "bar", + newConfig().getDockerClientStrategyClassName() + ); environment.remove("TESTCONTAINERS_DOCKER_CLIENT_STRATEGY"); userProperties.put("docker.client.strategy", "foo"); - assertEquals("Docker client strategy is can be explicitly set", "foo", newConfig().getDockerClientStrategyClassName()); + assertEquals( + "Docker client strategy is can be explicitly set", + "foo", + newConfig().getDockerClientStrategyClassName() + ); } @Test @@ -191,6 +233,7 @@ public void shouldReadReuseFromUserProperties() { userProperties.setProperty("testcontainers.reuse.enable", "true"); assertTrue("reuse enabled via user property", newConfig().environmentSupportsReuse()); } + @Test public void shouldReadReuseFromEnvironment() { assertFalse("no reuse by default", newConfig().environmentSupportsReuse()); @@ -203,7 +246,11 @@ public void shouldReadReuseFromEnvironment() { @Test public void shouldTrimImageNames() { userProperties.setProperty("ryuk.container.image", " testcontainers/ryuk:0.3.2 "); - assertEquals("trailing whitespace was not removed from image name property", "testcontainers/ryuk:0.3.2",newConfig().getRyukImage()); + assertEquals( + "trailing whitespace was not removed from image name property", + "testcontainers/ryuk:0.3.2", + newConfig().getRyukImage() + ); } private TestcontainersConfiguration newConfig() { diff --git a/docs/examples/junit4/generic/src/test/java/generic/CmdModifierTest.java b/docs/examples/junit4/generic/src/test/java/generic/CmdModifierTest.java index 5306426070f..218ed90e5e0 100644 --- a/docs/examples/junit4/generic/src/test/java/generic/CmdModifierTest.java +++ b/docs/examples/junit4/generic/src/test/java/generic/CmdModifierTest.java @@ -19,21 +19,26 @@ public class CmdModifierTest { // hostname { @Rule public GenericContainer theCache = new GenericContainer<>(DockerImageName.parse("redis:3.0.2")) - .withCreateContainerCmdModifier(cmd -> cmd.withHostName("the-cache")); + .withCreateContainerCmdModifier(cmd -> cmd.withHostName("the-cache")); + // } + // spotless:off // memory { private long memoryInBytes = 32 * 1024 * 1024; + private long memorySwapInBytes = 64 * 1024 * 1024; @Rule public GenericContainer memoryLimitedRedis = new GenericContainer<>(DockerImageName.parse("redis:3.0.2")) - .withCreateContainerCmdModifier(cmd -> cmd.getHostConfig() + .withCreateContainerCmdModifier(cmd -> { + cmd.getHostConfig() .withMemory(memoryInBytes) - .withMemorySwap(memorySwapInBytes) - ); - // } + .withMemorySwap(memorySwapInBytes); + }); + // } + // spotless:on @Test public void testHostnameModified() throws IOException, InterruptedException { diff --git a/docs/examples/junit4/generic/src/test/java/generic/CommandsTest.java b/docs/examples/junit4/generic/src/test/java/generic/CommandsTest.java index 23b265c26fa..80292465521 100644 --- a/docs/examples/junit4/generic/src/test/java/generic/CommandsTest.java +++ b/docs/examples/junit4/generic/src/test/java/generic/CommandsTest.java @@ -8,11 +8,12 @@ import static org.junit.Assert.assertTrue; public class CommandsTest { + @Rule // startupCommand { public GenericContainer redisWithCustomPort = new GenericContainer(DockerImageName.parse("redis:5.0")) .withCommand("redis-server --port 7777") - // } + // } .withExposedPorts(7777); @Test diff --git a/docs/examples/junit4/generic/src/test/java/generic/ContainerCreationTest.java b/docs/examples/junit4/generic/src/test/java/generic/ContainerCreationTest.java index 83b45f0435d..bdd82968191 100644 --- a/docs/examples/junit4/generic/src/test/java/generic/ContainerCreationTest.java +++ b/docs/examples/junit4/generic/src/test/java/generic/ContainerCreationTest.java @@ -9,31 +9,33 @@ public class ContainerCreationTest { + // spotless:off // simple { public static final DockerImageName REDIS_IMAGE = DockerImageName.parse("redis:3.0.2"); - // } - // simple { @ClassRule - public static GenericContainer redis = - new GenericContainer<>(REDIS_IMAGE) - .withExposedPorts(6379); + public static GenericContainer redis = new GenericContainer<>(REDIS_IMAGE) + .withExposedPorts(6379); + // } + // spotless:on public static final DockerImageName ALPINE_IMAGE = DockerImageName.parse("alpine:3.14"); + // spotless:off // withOptions { // Set up a plain OS container and customize environment, // command and exposed ports. This just listens on port 80 // and always returns '42' @ClassRule - public static GenericContainer alpine = - new GenericContainer<>(ALPINE_IMAGE) - .withExposedPorts(80) - .withEnv("MAGIC_NUMBER", "42") - .withCommand("/bin/sh", "-c", - "while true; do echo \"$MAGIC_NUMBER\" | nc -l -p 80; done"); + public static GenericContainer alpine = new GenericContainer<>(ALPINE_IMAGE) + .withExposedPorts(80) + .withEnv("MAGIC_NUMBER", "42") + .withCommand("/bin/sh", "-c", + "while true; do echo \"$MAGIC_NUMBER\" | nc -l -p 80; done"); + // } + // spotless:on @Test public void testStartup() { diff --git a/docs/examples/junit4/generic/src/test/java/generic/ContainerLabelTest.java b/docs/examples/junit4/generic/src/test/java/generic/ContainerLabelTest.java index 08d57e31057..64618ad25a9 100644 --- a/docs/examples/junit4/generic/src/test/java/generic/ContainerLabelTest.java +++ b/docs/examples/junit4/generic/src/test/java/generic/ContainerLabelTest.java @@ -7,12 +7,12 @@ import java.util.Map; public class ContainerLabelTest { + // single_label { public GenericContainer containerWithLabel = new GenericContainer(DockerImageName.parse("alpine:3.14")) .withLabel("key", "value"); // } - // multiple_labels { private Map mapOfLabels = new HashMap<>(); // populate map, e.g. mapOfLabels.put("key1", "value1"); diff --git a/docs/examples/junit4/generic/src/test/java/generic/HostPortExposedTest.java b/docs/examples/junit4/generic/src/test/java/generic/HostPortExposedTest.java index 1d15c2553b8..22bf0332a9d 100644 --- a/docs/examples/junit4/generic/src/test/java/generic/HostPortExposedTest.java +++ b/docs/examples/junit4/generic/src/test/java/generic/HostPortExposedTest.java @@ -18,19 +18,23 @@ public class HostPortExposedTest { private static HttpServer server; + private static int localServerPort; @BeforeClass public static void setUp() throws Exception { server = HttpServer.create(new InetSocketAddress(0), 0); - server.createContext("/", exchange -> { - byte[] content = "Hello World!".getBytes(); - exchange.sendResponseHeaders(200, content.length); - try (OutputStream responseBody = exchange.getResponseBody()) { - responseBody.write(content); - responseBody.flush(); + server.createContext( + "/", + exchange -> { + byte[] content = "Hello World!".getBytes(); + exchange.sendResponseHeaders(200, content.length); + try (OutputStream responseBody = exchange.getResponseBody()) { + responseBody.write(content); + responseBody.flush(); + } } - }); + ); server.start(); localServerPort = server.getAddress().getPort(); @@ -46,14 +50,12 @@ public static void tearDown() throws Exception { } @Rule - public BrowserWebDriverContainer browser = new BrowserWebDriverContainer() - .withCapabilities(new ChromeOptions()); + public BrowserWebDriverContainer browser = new BrowserWebDriverContainer().withCapabilities(new ChromeOptions()); @Test public void testContainerRunningAgainstExposedHostPort() { // useHostExposedPort { - final String rootUrl = - String.format("http://host.testcontainers.internal:%d/", localServerPort); + final String rootUrl = String.format("http://host.testcontainers.internal:%d/", localServerPort); final RemoteWebDriver webDriver = browser.getWebDriver(); webDriver.get(rootUrl); diff --git a/docs/examples/junit4/generic/src/test/java/generic/ImageNameSubstitutionTest.java b/docs/examples/junit4/generic/src/test/java/generic/ImageNameSubstitutionTest.java index 65da67fd401..afbe38fe835 100644 --- a/docs/examples/junit4/generic/src/test/java/generic/ImageNameSubstitutionTest.java +++ b/docs/examples/junit4/generic/src/test/java/generic/ImageNameSubstitutionTest.java @@ -1,6 +1,5 @@ package generic; - import generic.support.TestSpecificImageNameSubstitutor; import org.junit.Test; import org.testcontainers.containers.MySQLContainer; @@ -11,14 +10,15 @@ public class ImageNameSubstitutionTest { @Test public void simpleExample() { try ( + // spotless:off // directDockerHubReference { // Referring directly to an image on Docker Hub (mysql:8.0.24) final MySQLContainer mysql = new MySQLContainer<>( DockerImageName.parse("mysql:8.0.24") ) - // start the container and use it for testing // } + // spotless:on ) { mysql.start(); } @@ -32,15 +32,16 @@ public void simpleExample() { @Test public void substitutedExample() { try ( + // spotless:off // hardcodedMirror { // Referring directly to an image on a private registry - image name will vary final MySQLContainer mysql = new MySQLContainer<>( DockerImageName.parse("registry.mycompany.com/mirror/mysql:8.0.24") - .asCompatibleSubstituteFor("mysql") + .asCompatibleSubstituteFor("mysql") ) - // start the container and use it for testing // } + // spotless:on ) { mysql.start(); } diff --git a/docs/examples/junit4/generic/src/test/java/generic/MultiplePortsExposedTest.java b/docs/examples/junit4/generic/src/test/java/generic/MultiplePortsExposedTest.java index de2b56c6a63..49e62cff217 100644 --- a/docs/examples/junit4/generic/src/test/java/generic/MultiplePortsExposedTest.java +++ b/docs/examples/junit4/generic/src/test/java/generic/MultiplePortsExposedTest.java @@ -1,23 +1,25 @@ package generic; -import static org.slf4j.LoggerFactory.getLogger; - import org.junit.Rule; import org.junit.Test; import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.output.Slf4jLogConsumer; import org.testcontainers.utility.DockerImageName; public class MultiplePortsExposedTest { - private static final Logger log = getLogger(MultiplePortsExposedTest.class); + private static final Logger log = LoggerFactory.getLogger(MultiplePortsExposedTest.class); @Rule // rule { - public GenericContainer container = new GenericContainer<>(DockerImageName.parse("testcontainers/helloworld:1.1.0")) + public GenericContainer container = new GenericContainer<>( + DockerImageName.parse("testcontainers/helloworld:1.1.0") + ) .withExposedPorts(8080, 8081) .withLogConsumer(new Slf4jLogConsumer(log)); + // } @Test @@ -38,7 +40,6 @@ public void getHostOnly() { @Test public void getHostAndMappedPort() { - String address = - container.getHost() + ":" + container.getMappedPort(8080); + String address = container.getHost() + ":" + container.getMappedPort(8080); } } diff --git a/docs/examples/junit4/generic/src/test/java/generic/WaitStrategiesTest.java b/docs/examples/junit4/generic/src/test/java/generic/WaitStrategiesTest.java index d45b1e28c47..bdc0d3ced8a 100644 --- a/docs/examples/junit4/generic/src/test/java/generic/WaitStrategiesTest.java +++ b/docs/examples/junit4/generic/src/test/java/generic/WaitStrategiesTest.java @@ -14,8 +14,9 @@ public class WaitStrategiesTest { @Rule // waitForNetworkListening { - public GenericContainer nginx = new GenericContainer(DockerImageName.parse("nginx:1.9.4")) + public GenericContainer nginx = new GenericContainer(DockerImageName.parse("nginx:1.9.4")) // .withExposedPorts(80); + // } @Rule @@ -23,44 +24,48 @@ public class WaitStrategiesTest { public GenericContainer nginxWithHttpWait = new GenericContainer(DockerImageName.parse("nginx:1.9.4")) .withExposedPorts(80) .waitingFor(Wait.forHttp("/")); + // } @Rule // logMessageWait { public GenericContainer containerWithLogWait = new GenericContainer(DockerImageName.parse("redis:5.0.3")) .withExposedPorts(6379) - .waitingFor( - Wait.forLogMessage(".*Ready to accept connections.*\\n", 1) - ); + .waitingFor(Wait.forLogMessage(".*Ready to accept connections.*\\n", 1)); + // } private static final HttpWaitStrategy MULTI_CODE_HTTP_WAIT = + // spotless:off // waitForHttpWithMultipleStatusCodes { Wait.forHttp("/") .forStatusCode(200) - .forStatusCode(301) + .forStatusCode(301); // } - ; + // spotless:on private static final HttpWaitStrategy PREDICATE_HTTP_WAIT = + // spotless:off // waitForHttpWithStatusCodePredicate { Wait.forHttp("/all") - .forStatusCodeMatching(it -> it >= 200 && it < 300 || it == 401) + .forStatusCodeMatching(it -> it >= 200 && it < 300 || it == 401); // } - ; + // spotless:on private static final HttpWaitStrategy TLS_HTTP_WAIT = + // spotless:off // waitForHttpWithTls { Wait.forHttp("/all") - .usingTls() + .usingTls(); // } - ; + // spotless:on private static final WaitStrategy HEALTHCHECK_WAIT = + // spotless:off // healthcheckWait { - Wait.forHealthcheck() + Wait.forHealthcheck(); // } - ; + // spotless:on @Test public void testContainersAllStarted() { diff --git a/docs/examples/junit4/generic/src/test/java/org/testcontainers/containers/startupcheck/StartupCheckStrategyTest.java b/docs/examples/junit4/generic/src/test/java/org/testcontainers/containers/startupcheck/StartupCheckStrategyTest.java index b60225010a1..82e3d83ab24 100644 --- a/docs/examples/junit4/generic/src/test/java/org/testcontainers/containers/startupcheck/StartupCheckStrategyTest.java +++ b/docs/examples/junit4/generic/src/test/java/org/testcontainers/containers/startupcheck/StartupCheckStrategyTest.java @@ -6,6 +6,7 @@ import org.junit.runner.RunWith; import org.junit.runners.Suite; import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.output.OutputFrame; import org.testcontainers.containers.output.WaitingConsumer; import org.testcontainers.utility.DockerImageName; @@ -14,34 +15,39 @@ import java.util.concurrent.TimeoutException; import static org.assertj.core.api.Assertions.assertThat; -import static org.testcontainers.containers.output.OutputFrame.OutputType.STDOUT; -import static org.testcontainers.containers.startupcheck.StartupCheckStrategyTest.IndefiniteOneShotStrategyTest; -import static org.testcontainers.containers.startupcheck.StartupCheckStrategyTest.MinimumDurationStrategyTest; -import static org.testcontainers.containers.startupcheck.StartupCheckStrategyTest.OneShotStrategyTest; @RunWith(Suite.class) -@Suite.SuiteClasses({OneShotStrategyTest.class, IndefiniteOneShotStrategyTest.class, MinimumDurationStrategyTest.class}) +@Suite.SuiteClasses( + { + StartupCheckStrategyTest.OneShotStrategyTest.class, + StartupCheckStrategyTest.IndefiniteOneShotStrategyTest.class, + StartupCheckStrategyTest.MinimumDurationStrategyTest.class, + } +) public class StartupCheckStrategyTest { private static final String HELLO_TESTCONTAINERS = "Hello Testcontainers!"; private static void waitForHello(GenericContainer container) throws TimeoutException { WaitingConsumer consumer = new WaitingConsumer(); - container.followOutput(consumer, STDOUT); + container.followOutput(consumer, OutputFrame.OutputType.STDOUT); - consumer.waitUntil(frame -> - frame.getUtf8String().contains(HELLO_TESTCONTAINERS), 30, TimeUnit.SECONDS); + consumer.waitUntil(frame -> frame.getUtf8String().contains(HELLO_TESTCONTAINERS), 30, TimeUnit.SECONDS); } public static class OneShotStrategyTest { + @Rule + // spotless:off // withOneShotStrategy { public GenericContainer bboxWithOneShot = new GenericContainer<>(DockerImageName.parse("busybox:1.31.1")) .withCommand(String.format("echo %s", HELLO_TESTCONTAINERS)) .withStartupCheckStrategy( new OneShotStartupCheckStrategy().withTimeout(Duration.ofSeconds(3)) ); + // } + // spotless:on @SneakyThrows @Test @@ -53,14 +59,20 @@ public void testCommandIsExecuted() { } public static class IndefiniteOneShotStrategyTest { + @Rule + // spotless:off // withIndefiniteOneShotStrategy { - public GenericContainer bboxWithIndefiniteOneShot = new GenericContainer<>(DockerImageName.parse("busybox:1.31.1")) + public GenericContainer bboxWithIndefiniteOneShot = new GenericContainer<>( + DockerImageName.parse("busybox:1.31.1") + ) .withCommand("sh", "-c", String.format("sleep 5 && echo \"%s\"", HELLO_TESTCONTAINERS)) .withStartupCheckStrategy( new IndefiniteWaitOneShotStartupCheckStrategy() ); + // } + // spotless:on @SneakyThrows @Test @@ -72,14 +84,20 @@ public void testCommandIsExecuted() { } public static class MinimumDurationStrategyTest { + @Rule + // spotless:off // withMinimumDurationStrategy { - public GenericContainer bboxWithMinimumDuration = new GenericContainer<>(DockerImageName.parse("busybox:1.31.1")) + public GenericContainer bboxWithMinimumDuration = new GenericContainer<>( + DockerImageName.parse("busybox:1.31.1") + ) .withCommand("sh", "-c", String.format("sleep 5 && echo \"%s\"", HELLO_TESTCONTAINERS)) .withStartupCheckStrategy( new MinimumDurationRunningStartupCheckStrategy(Duration.ofSeconds(1)) ); + // } + // spotless:on @SneakyThrows @Test diff --git a/docs/examples/junit4/redis/src/test/java/quickstart/RedisBackedCacheIntTest.java b/docs/examples/junit4/redis/src/test/java/quickstart/RedisBackedCacheIntTest.java index cbb49b5fdb3..1d3d178975f 100644 --- a/docs/examples/junit4/redis/src/test/java/quickstart/RedisBackedCacheIntTest.java +++ b/docs/examples/junit4/redis/src/test/java/quickstart/RedisBackedCacheIntTest.java @@ -1,6 +1,5 @@ package quickstart; - import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -16,9 +15,9 @@ public class RedisBackedCacheIntTest { // rule { @Rule public GenericContainer redis = new GenericContainer(DockerImageName.parse("redis:5.0.3-alpine")) - .withExposedPorts(6379); - // } + .withExposedPorts(6379); + // } @Before public void setUp() { diff --git a/docs/examples/junit4/redis/src/test/java/quickstart/RedisBackedCacheIntTestStep0.java b/docs/examples/junit4/redis/src/test/java/quickstart/RedisBackedCacheIntTestStep0.java index 61a86ac3f8b..f837b3af0d5 100644 --- a/docs/examples/junit4/redis/src/test/java/quickstart/RedisBackedCacheIntTestStep0.java +++ b/docs/examples/junit4/redis/src/test/java/quickstart/RedisBackedCacheIntTestStep0.java @@ -1,6 +1,5 @@ package quickstart; - import org.junit.Before; import org.junit.Ignore; import org.junit.Test; @@ -9,6 +8,7 @@ @Ignore("This test class is deliberately invalid, as it relies on a non-existent local Redis") public class RedisBackedCacheIntTestStep0 { + private RedisBackedCache underTest; @Before diff --git a/docs/examples/junit5/redis/src/test/java/quickstart/RedisBackedCacheIntTest.java b/docs/examples/junit5/redis/src/test/java/quickstart/RedisBackedCacheIntTest.java index 9a3855ba23c..59d92210549 100644 --- a/docs/examples/junit5/redis/src/test/java/quickstart/RedisBackedCacheIntTest.java +++ b/docs/examples/junit5/redis/src/test/java/quickstart/RedisBackedCacheIntTest.java @@ -1,6 +1,5 @@ package quickstart; - import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.testcontainers.containers.GenericContainer; @@ -19,9 +18,9 @@ public class RedisBackedCacheIntTest { // container { @Container public GenericContainer redis = new GenericContainer(DockerImageName.parse("redis:5.0.3-alpine")) - .withExposedPorts(6379); - // } + .withExposedPorts(6379); + // } @BeforeEach public void setUp() { diff --git a/docs/examples/junit5/redis/src/test/java/quickstart/RedisBackedCacheIntTestStep0.java b/docs/examples/junit5/redis/src/test/java/quickstart/RedisBackedCacheIntTestStep0.java index c81a185a188..d9f889a8943 100644 --- a/docs/examples/junit5/redis/src/test/java/quickstart/RedisBackedCacheIntTestStep0.java +++ b/docs/examples/junit5/redis/src/test/java/quickstart/RedisBackedCacheIntTestStep0.java @@ -1,6 +1,5 @@ package quickstart; - import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; @@ -9,6 +8,7 @@ @Disabled("This test class is deliberately invalid, as it relies on a non-existent local Redis") public class RedisBackedCacheIntTestStep0 { + private RedisBackedCache underTest; @BeforeEach diff --git a/docs/examples/spock/redis/src/test/groovy/quickstart/RedisBackedCacheIntTest.groovy b/docs/examples/spock/redis/src/test/groovy/quickstart/RedisBackedCacheIntTest.groovy index 068c8079a7d..5cc04dc6b9d 100644 --- a/docs/examples/spock/redis/src/test/groovy/quickstart/RedisBackedCacheIntTest.groovy +++ b/docs/examples/spock/redis/src/test/groovy/quickstart/RedisBackedCacheIntTest.groovy @@ -7,30 +7,30 @@ import spock.lang.Specification @org.testcontainers.spock.Testcontainers class RedisBackedCacheIntTest extends Specification { - private RedisBackedCache underTest + private RedisBackedCache underTest - // init { - GenericContainer redis = new GenericContainer<>("redis:5.0.3-alpine") - .withExposedPorts(6379) - // } + // init { + GenericContainer redis = new GenericContainer<>("redis:5.0.3-alpine") + .withExposedPorts(6379) + // } - void setup() { - String address = redis.host - Integer port = redis.firstMappedPort + void setup() { + String address = redis.host + Integer port = redis.firstMappedPort - // Now we have an address and port for Redis, no matter where it is running - underTest = new RedisBackedCache(address, port) - } + // Now we have an address and port for Redis, no matter where it is running + underTest = new RedisBackedCache(address, port) + } - void testSimplePutAndGet() { - setup: - underTest.put("test", "example") + void testSimplePutAndGet() { + setup: + underTest.put("test", "example") - when: - String retrieved = underTest.get("test") + when: + String retrieved = underTest.get("test") - then: - retrieved == "example" - } + then: + retrieved == "example" + } } // } diff --git a/docs/examples/spock/redis/src/test/groovy/quickstart/RedisBackedCacheIntTestStep0.groovy b/docs/examples/spock/redis/src/test/groovy/quickstart/RedisBackedCacheIntTestStep0.groovy index 52897c30097..d28942420da 100644 --- a/docs/examples/spock/redis/src/test/groovy/quickstart/RedisBackedCacheIntTestStep0.groovy +++ b/docs/examples/spock/redis/src/test/groovy/quickstart/RedisBackedCacheIntTestStep0.groovy @@ -5,21 +5,21 @@ import spock.lang.Specification @Ignore("This test class is deliberately invalid, as it relies on a non-existent local Redis") class RedisBackedCacheIntTestStep0 extends Specification { - private RedisBackedCache underTest + private RedisBackedCache underTest - void setup() { - // Assume that we have Redis running locally? - underTest = new RedisBackedCache("localhost", 6379) - } + void setup() { + // Assume that we have Redis running locally? + underTest = new RedisBackedCache("localhost", 6379) + } - void testSimplePutAndGet() { - setup: - underTest.put("test", "example") + void testSimplePutAndGet() { + setup: + underTest.put("test", "example") - when: - String retrieved = underTest.get("test") + when: + String retrieved = underTest.get("test") - then: - retrieved == "example" - } + then: + retrieved == "example" + } } diff --git a/docs/modules/hivemq.md b/docs/modules/hivemq.md index 1e09bac4fc0..66f9670648c 100644 --- a/docs/modules/hivemq.md +++ b/docs/modules/hivemq.md @@ -31,7 +31,7 @@ Using the Community Edition: Using the Enterprise Edition: -[Enterprise Edition HiveMQ image](../../modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoHiveMQContainerIT.java) inside_block:eeVersion +[Enterprise Edition HiveMQ image](../../modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoHiveMQContainerIT.java) inside_block:hiveEEVersion Using a specifc version is possible by using the tag: @@ -138,7 +138,7 @@ The following example shows how to start a HiveMQ container with the extension c The following test then proceeds to enable and then disable the extension: -[Enable/Disable extension at runtime](../../modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoDisableExtensionsIT.java) inside_block:runtimeEnable +[Enable/Disable extension at runtime](../../modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoDisableExtensionsIT.java) inside_block:hiveRuntimeEnable ## Enable/Disable an extension loaded from a folder diff --git a/modules/azure/src/main/java/org/testcontainers/containers/CosmosDBEmulatorContainer.java b/modules/azure/src/main/java/org/testcontainers/containers/CosmosDBEmulatorContainer.java index 9328d881666..57af95ef106 100644 --- a/modules/azure/src/main/java/org/testcontainers/containers/CosmosDBEmulatorContainer.java +++ b/modules/azure/src/main/java/org/testcontainers/containers/CosmosDBEmulatorContainer.java @@ -10,8 +10,9 @@ */ public class CosmosDBEmulatorContainer extends GenericContainer { - private static final DockerImageName DEFAULT_IMAGE_NAME = - DockerImageName.parse("mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator"); + private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse( + "mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator" + ); private static final int PORT = 8081; diff --git a/modules/azure/src/main/java/org/testcontainers/containers/KeyStoreBuilder.java b/modules/azure/src/main/java/org/testcontainers/containers/KeyStoreBuilder.java index 0179e8d832a..f7b6af370ac 100644 --- a/modules/azure/src/main/java/org/testcontainers/containers/KeyStoreBuilder.java +++ b/modules/azure/src/main/java/org/testcontainers/containers/KeyStoreBuilder.java @@ -5,10 +5,6 @@ import okhttp3.Request; import okhttp3.Response; -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLSocketFactory; -import javax.net.ssl.TrustManager; -import javax.net.ssl.X509TrustManager; import java.io.InputStream; import java.security.KeyStore; import java.security.SecureRandom; @@ -17,6 +13,11 @@ import java.security.cert.X509Certificate; import java.util.Objects; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; + final class KeyStoreBuilder { static KeyStore buildByDownloadingCertificate(String endpoint, String keyStorePassword) { @@ -38,20 +39,18 @@ static KeyStore buildByDownloadingCertificate(String endpoint, String keyStorePa private static TrustManager[] buildTrustAllManagers() { return new TrustManager[] { - new X509TrustManager() { - @Override - public void checkClientTrusted(X509Certificate[] chain, String authType) { - } + new X509TrustManager() { + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) {} - @Override - public void checkServerTrusted(X509Certificate[] chain, String authType) { - } + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) {} - @Override - public X509Certificate[] getAcceptedIssuers() { - return new X509Certificate[]{}; - } + @Override + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[] {}; } + }, }; } @@ -60,16 +59,13 @@ private static OkHttpClient buildTrustAllClient(TrustManager[] trustManagers) th sslContext.init(null, trustManagers, new SecureRandom()); SSLSocketFactory socketFactory = sslContext.getSocketFactory(); return new OkHttpClient.Builder() - .sslSocketFactory(socketFactory, (X509TrustManager) trustManagers[0]) - .hostnameVerifier((s, sslSession) -> true) - .build(); + .sslSocketFactory(socketFactory, (X509TrustManager) trustManagers[0]) + .hostnameVerifier((s, sslSession) -> true) + .build(); } private static Request buildRequest(String endpoint) { - return new Request.Builder() - .get() - .url(endpoint + "/_explorer/emulator.pem") - .build(); + return new Request.Builder().get().url(endpoint + "/_explorer/emulator.pem").build(); } private static KeyStore buildKeyStore(InputStream certificateStream, String keyStorePassword) throws Exception { @@ -85,8 +81,7 @@ private static void closeResponseSilently(Response response) { if (Objects.nonNull(response)) { response.close(); } - } catch (Exception ignored) { - } + } catch (Exception ignored) {} } private static void closeClientSilently(OkHttpClient client) { @@ -99,7 +94,6 @@ private static void closeClientSilently(OkHttpClient client) { cache.close(); } } - } catch (Exception ignored) { - } + } catch (Exception ignored) {} } } diff --git a/modules/azure/src/test/java/org/testcontainers/containers/CosmosDBEmulatorContainerTest.java b/modules/azure/src/test/java/org/testcontainers/containers/CosmosDBEmulatorContainerTest.java index dd9dc6b718f..37422b30af7 100644 --- a/modules/azure/src/test/java/org/testcontainers/containers/CosmosDBEmulatorContainerTest.java +++ b/modules/azure/src/test/java/org/testcontainers/containers/CosmosDBEmulatorContainerTest.java @@ -37,8 +37,9 @@ public static void restoreOriginalSystemProperties() { @Rule // emulatorContainer { public CosmosDBEmulatorContainer emulator = new CosmosDBEmulatorContainer( - DockerImageName.parse("mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator:latest") + DockerImageName.parse("mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator:latest") ); + // } @Test @@ -55,18 +56,19 @@ public void testWithCosmosClient() throws Exception { // } // buildClient { CosmosAsyncClient client = new CosmosClientBuilder() - .gatewayMode() - .endpointDiscoveryEnabled(false) - .endpoint(emulator.getEmulatorEndpoint()) - .key(emulator.getEmulatorKey()) - .buildAsyncClient(); + .gatewayMode() + .endpointDiscoveryEnabled(false) + .endpoint(emulator.getEmulatorEndpoint()) + .key(emulator.getEmulatorKey()) + .buildAsyncClient(); // } // testWithClientAgainstEmulatorContainer { - CosmosDatabaseResponse databaseResponse = - client.createDatabaseIfNotExists("Azure").block(); + CosmosDatabaseResponse databaseResponse = client.createDatabaseIfNotExists("Azure").block(); Assertions.assertThat(databaseResponse.getStatusCode()).isEqualTo(201); - CosmosContainerResponse containerResponse = - client.getDatabase("Azure").createContainerIfNotExists("ServiceContainer", "/name").block(); + CosmosContainerResponse containerResponse = client + .getDatabase("Azure") + .createContainerIfNotExists("ServiceContainer", "/name") + .block(); Assertions.assertThat(containerResponse.getStatusCode()).isEqualTo(201); // } } diff --git a/modules/cassandra/src/main/java/org/testcontainers/containers/CassandraContainer.java b/modules/cassandra/src/main/java/org/testcontainers/containers/CassandraContainer.java index e3ae084cb8a..3c78743fb62 100644 --- a/modules/cassandra/src/main/java/org/testcontainers/containers/CassandraContainer.java +++ b/modules/cassandra/src/main/java/org/testcontainers/containers/CassandraContainer.java @@ -10,12 +10,13 @@ import org.testcontainers.utility.DockerImageName; import org.testcontainers.utility.MountableFile; -import javax.script.ScriptException; import java.io.IOException; import java.net.URL; import java.nio.charset.StandardCharsets; import java.util.Optional; +import javax.script.ScriptException; + /** * Cassandra container * @@ -26,18 +27,24 @@ public class CassandraContainer> extends GenericContainer { private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("cassandra"); + private static final String DEFAULT_TAG = "3.11.2"; @Deprecated public static final String IMAGE = DEFAULT_IMAGE_NAME.getUnversionedPart(); public static final Integer CQL_PORT = 9042; + private static final String CONTAINER_CONFIG_LOCATION = "/etc/cassandra"; + private static final String USERNAME = "cassandra"; + private static final String PASSWORD = "cassandra"; private String configLocation; + private String initScriptPath; + private boolean enableJmxReporting; /** @@ -54,17 +61,13 @@ public CassandraContainer(String dockerImageName) { public CassandraContainer(DockerImageName dockerImageName) { super(dockerImageName); - dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); addExposedPort(CQL_PORT); this.enableJmxReporting = false; withEnv("CASSANDRA_SNITCH", "GossipingPropertyFileSnitch"); - withEnv( - "JVM_OPTS", - "-Dcassandra.skip_wait_for_gossip_to_settle=0 -Dcassandra.initial_token=0" - ); + withEnv("JVM_OPTS", "-Dcassandra.skip_wait_for_gossip_to_settle=0 -Dcassandra.initial_token=0"); withEnv("HEAP_NEWSIZE", "128M"); withEnv("MAX_HEAP_SIZE", "1024M"); } @@ -88,7 +91,9 @@ private void runInitScriptIfRequired() { URL resource = Thread.currentThread().getContextClassLoader().getResource(initScriptPath); if (resource == null) { logger().warn("Could not load classpath init script: {}", initScriptPath); - throw new ScriptLoadException("Could not load classpath init script: " + initScriptPath + ". Resource not found."); + throw new ScriptLoadException( + "Could not load classpath init script: " + initScriptPath + ". Resource not found." + ); } String cql = IOUtils.toString(resource, StandardCharsets.UTF_8); DatabaseDelegate databaseDelegate = getDatabaseDelegate(); @@ -98,7 +103,10 @@ private void runInitScriptIfRequired() { throw new ScriptLoadException("Could not load classpath init script: " + initScriptPath, e); } catch (ScriptException e) { logger().error("Error while executing init script: {}", initScriptPath, e); - throw new ScriptUtils.UncategorizedScriptException("Error while executing init script: " + initScriptPath, e); + throw new ScriptUtils.UncategorizedScriptException( + "Error while executing init script: " + initScriptPath, + e + ); } } } @@ -112,9 +120,10 @@ private void runInitScriptIfRequired() { * @param resourceLocation relative classpath to resource */ protected void optionallyMapResourceParameterAsVolume(String pathNameInContainer, String resourceLocation) { - Optional.ofNullable(resourceLocation) - .map(MountableFile::forClasspathResource) - .ifPresent(mountableFile -> withCopyFileToContainer(mountableFile, pathNameInContainer)); + Optional + .ofNullable(resourceLocation) + .map(MountableFile::forClasspathResource) + .ifPresent(mountableFile -> withCopyFileToContainer(mountableFile, pathNameInContainer)); } /** @@ -184,7 +193,8 @@ public Cluster getCluster() { } public static Cluster getCluster(ContainerState containerState, boolean enableJmxReporting) { - final Cluster.Builder builder = Cluster.builder() + final Cluster.Builder builder = Cluster + .builder() .addContactPoint(containerState.getHost()) .withPort(containerState.getMappedPort(CQL_PORT)); if (!enableJmxReporting) { diff --git a/modules/cassandra/src/main/java/org/testcontainers/containers/delegate/CassandraDatabaseDelegate.java b/modules/cassandra/src/main/java/org/testcontainers/containers/delegate/CassandraDatabaseDelegate.java index e55dd31b0ff..8bc72e7f154 100644 --- a/modules/cassandra/src/main/java/org/testcontainers/containers/delegate/CassandraDatabaseDelegate.java +++ b/modules/cassandra/src/main/java/org/testcontainers/containers/delegate/CassandraDatabaseDelegate.java @@ -25,8 +25,7 @@ public class CassandraDatabaseDelegate extends AbstractDatabaseDelegate @Override protected Session createNewConnection() { try { - return CassandraContainer.getCluster(container) - .newSession(); + return CassandraContainer.getCluster(container).newSession(); } catch (DriverException e) { log.error("Could not obtain cassandra connection"); throw new ConnectionCreationException("Could not obtain cassandra connection", e); @@ -34,7 +33,13 @@ protected Session createNewConnection() { } @Override - public void execute(String statement, String scriptPath, int lineNumber, boolean continueOnError, boolean ignoreFailedDrops) { + public void execute( + String statement, + String scriptPath, + int lineNumber, + boolean continueOnError, + boolean ignoreFailedDrops + ) { try { ResultSet result = getConnection().execute(statement); if (result.wasApplied()) { diff --git a/modules/cassandra/src/main/java/org/testcontainers/containers/wait/CassandraQueryWaitStrategy.java b/modules/cassandra/src/main/java/org/testcontainers/containers/wait/CassandraQueryWaitStrategy.java index cf7f3769985..aa2466cf7c4 100644 --- a/modules/cassandra/src/main/java/org/testcontainers/containers/wait/CassandraQueryWaitStrategy.java +++ b/modules/cassandra/src/main/java/org/testcontainers/containers/wait/CassandraQueryWaitStrategy.java @@ -18,20 +18,26 @@ public class CassandraQueryWaitStrategy extends AbstractWaitStrategy { private static final String SELECT_VERSION_QUERY = "SELECT release_version FROM system.local"; + private static final String TIMEOUT_ERROR = "Timed out waiting for Cassandra to be accessible for query execution"; @Override protected void waitUntilReady() { // execute select version query until success or timeout try { - retryUntilSuccess((int) startupTimeout.getSeconds(), TimeUnit.SECONDS, () -> { - getRateLimiter().doWhenReady(() -> { - try (DatabaseDelegate databaseDelegate = getDatabaseDelegate()) { - databaseDelegate.execute(SELECT_VERSION_QUERY, "", 1, false, false); - } - }); - return true; - }); + retryUntilSuccess( + (int) startupTimeout.getSeconds(), + TimeUnit.SECONDS, + () -> { + getRateLimiter() + .doWhenReady(() -> { + try (DatabaseDelegate databaseDelegate = getDatabaseDelegate()) { + databaseDelegate.execute(SELECT_VERSION_QUERY, "", 1, false, false); + } + }); + return true; + } + ); } catch (TimeoutException e) { throw new ContainerLaunchException(TIMEOUT_ERROR); } diff --git a/modules/cassandra/src/test/java/org/testcontainers/containers/CassandraContainerTest.java b/modules/cassandra/src/test/java/org/testcontainers/containers/CassandraContainerTest.java index 76c8c8438d7..45cbba0abc7 100644 --- a/modules/cassandra/src/test/java/org/testcontainers/containers/CassandraContainerTest.java +++ b/modules/cassandra/src/test/java/org/testcontainers/containers/CassandraContainerTest.java @@ -36,7 +36,11 @@ public void testSimple() { @Test public void testSpecificVersion() { String cassandraVersion = "3.0.15"; - try (CassandraContainer cassandraContainer = new CassandraContainer<>(CASSANDRA_IMAGE.withTag(cassandraVersion))) { + try ( + CassandraContainer cassandraContainer = new CassandraContainer<>( + CASSANDRA_IMAGE.withTag(cassandraVersion) + ) + ) { cassandraContainer.start(); ResultSet resultSet = performQuery(cassandraContainer, "SELECT release_version FROM system.local"); assertTrue("Query was not applied", resultSet.wasApplied()); @@ -53,7 +57,11 @@ public void testConfigurationOverride() { cassandraContainer.start(); ResultSet resultSet = performQuery(cassandraContainer, "SELECT cluster_name FROM system.local"); assertTrue("Query was not applied", resultSet.wasApplied()); - assertEquals("Cassandra configuration is not overridden", TEST_CLUSTER_NAME_IN_CONF, resultSet.one().getString(0)); + assertEquals( + "Cassandra configuration is not overridden", + TEST_CLUSTER_NAME_IN_CONF, + resultSet.one().getString(0) + ); } } @@ -81,7 +89,9 @@ public void testInitScript() { @Test public void testInitScriptWithLegacyCassandra() { try ( - CassandraContainer cassandraContainer = new CassandraContainer<>(DockerImageName.parse("cassandra:2.2.11")) + CassandraContainer cassandraContainer = new CassandraContainer<>( + DockerImageName.parse("cassandra:2.2.11") + ) .withInitScript("initial.cql") ) { cassandraContainer.start(); @@ -107,7 +117,10 @@ public void testCassandraQueryWaitStrategy() { public void testCassandraGetCluster() { try (CassandraContainer cassandraContainer = new CassandraContainer<>()) { cassandraContainer.start(); - ResultSet resultSet = performQuery(cassandraContainer.getCluster(), "SELECT release_version FROM system.local"); + ResultSet resultSet = performQuery( + cassandraContainer.getCluster(), + "SELECT release_version FROM system.local" + ); assertTrue("Query was not applied", resultSet.wasApplied()); assertNotNull("Result set has no release_version", resultSet.one().getString(0)); } @@ -122,7 +135,8 @@ private void testInitScript(CassandraContainer cassandraContainer) { } private ResultSet performQuery(CassandraContainer cassandraContainer, String cql) { - Cluster explicitCluster = Cluster.builder() + Cluster explicitCluster = Cluster + .builder() .addContactPoint(cassandraContainer.getHost()) .withPort(cassandraContainer.getMappedPort(CassandraContainer.CQL_PORT)) .build(); diff --git a/modules/clickhouse/src/main/java/org/testcontainers/containers/ClickHouseContainer.java b/modules/clickhouse/src/main/java/org/testcontainers/containers/ClickHouseContainer.java index 9b5f130a323..4528c48e935 100644 --- a/modules/clickhouse/src/main/java/org/testcontainers/containers/ClickHouseContainer.java +++ b/modules/clickhouse/src/main/java/org/testcontainers/containers/ClickHouseContainer.java @@ -8,6 +8,7 @@ import java.util.Set; public class ClickHouseContainer extends JdbcDatabaseContainer { + public static final String NAME = "clickhouse"; private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("yandex/clickhouse-server"); @@ -19,14 +20,19 @@ public class ClickHouseContainer extends JdbcDatabaseContainer { public static final String DEFAULT_TAG = "18.10.3"; public static final Integer HTTP_PORT = 8123; + public static final Integer NATIVE_PORT = 9000; private static final String DRIVER_CLASS_NAME = "ru.yandex.clickhouse.ClickHouseDriver"; + private static final String JDBC_URL_PREFIX = "jdbc:" + NAME + "://"; + private static final String TEST_QUERY = "SELECT 1"; private String databaseName = "default"; + private String username = "default"; + private String password = ""; /** @@ -43,7 +49,6 @@ public ClickHouseContainer(String dockerImageName) { public ClickHouseContainer(final DockerImageName dockerImageName) { super(dockerImageName); - dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); withExposedPorts(HTTP_PORT, NATIVE_PORT); diff --git a/modules/clickhouse/src/main/java/org/testcontainers/containers/ClickHouseProvider.java b/modules/clickhouse/src/main/java/org/testcontainers/containers/ClickHouseProvider.java index cd2453b8349..80fb71bd5da 100644 --- a/modules/clickhouse/src/main/java/org/testcontainers/containers/ClickHouseProvider.java +++ b/modules/clickhouse/src/main/java/org/testcontainers/containers/ClickHouseProvider.java @@ -3,6 +3,7 @@ import org.testcontainers.utility.DockerImageName; public class ClickHouseProvider extends JdbcDatabaseContainerProvider { + @Override public boolean supports(String databaseType) { return databaseType.equals(ClickHouseContainer.NAME); diff --git a/modules/clickhouse/src/test/java/org/testcontainers/jdbc/clickhouse/ClickhouseJDBCDriverTest.java b/modules/clickhouse/src/test/java/org/testcontainers/jdbc/clickhouse/ClickhouseJDBCDriverTest.java index f3946c8a317..056aace7772 100644 --- a/modules/clickhouse/src/test/java/org/testcontainers/jdbc/clickhouse/ClickhouseJDBCDriverTest.java +++ b/modules/clickhouse/src/test/java/org/testcontainers/jdbc/clickhouse/ClickhouseJDBCDriverTest.java @@ -4,18 +4,18 @@ import org.junit.runners.Parameterized; import org.testcontainers.jdbc.AbstractJDBCDriverTest; +import java.util.Arrays; import java.util.EnumSet; -import static java.util.Arrays.asList; - @RunWith(Parameterized.class) public class ClickhouseJDBCDriverTest extends AbstractJDBCDriverTest { @Parameterized.Parameters(name = "{index} - {0}") public static Iterable data() { - return asList( - new Object[][]{ - {"jdbc:tc:clickhouse://hostname/databasename", EnumSet.of(Options.PmdKnownBroken)}, - }); + return Arrays.asList( + new Object[][] { // + { "jdbc:tc:clickhouse://hostname/databasename", EnumSet.of(Options.PmdKnownBroken) }, + } + ); } } diff --git a/modules/clickhouse/src/test/java/org/testcontainers/junit/clickhouse/SimpleClickhouseTest.java b/modules/clickhouse/src/test/java/org/testcontainers/junit/clickhouse/SimpleClickhouseTest.java index 8f3879673a0..d2375891f8c 100644 --- a/modules/clickhouse/src/test/java/org/testcontainers/junit/clickhouse/SimpleClickhouseTest.java +++ b/modules/clickhouse/src/test/java/org/testcontainers/junit/clickhouse/SimpleClickhouseTest.java @@ -1,6 +1,7 @@ package org.testcontainers.junit.clickhouse; import org.junit.Test; +import org.testcontainers.ClickhouseTestImages; import org.testcontainers.containers.ClickHouseContainer; import org.testcontainers.db.AbstractContainerDatabaseTest; @@ -8,13 +9,12 @@ import java.sql.SQLException; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.testcontainers.ClickhouseTestImages.CLICKHOUSE_IMAGE; public class SimpleClickhouseTest extends AbstractContainerDatabaseTest { @Test public void testSimple() throws SQLException { - try (ClickHouseContainer clickhouse = new ClickHouseContainer(CLICKHOUSE_IMAGE)) { + try (ClickHouseContainer clickhouse = new ClickHouseContainer(ClickhouseTestImages.CLICKHOUSE_IMAGE)) { clickhouse.start(); ResultSet resultSet = performQuery(clickhouse, "SELECT 1"); diff --git a/modules/cockroachdb/src/main/java/org/testcontainers/containers/CockroachContainer.java b/modules/cockroachdb/src/main/java/org/testcontainers/containers/CockroachContainer.java index f32e6bce385..dfec3988f15 100644 --- a/modules/cockroachdb/src/main/java/org/testcontainers/containers/CockroachContainer.java +++ b/modules/cockroachdb/src/main/java/org/testcontainers/containers/CockroachContainer.java @@ -8,6 +8,7 @@ public class CockroachContainer extends JdbcDatabaseContainer { private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("cockroachdb/cockroach"); + private static final String DEFAULT_TAG = "v19.2.11"; public static final String NAME = "cockroach"; @@ -19,13 +20,19 @@ public class CockroachContainer extends JdbcDatabaseContainer data() { - return asList( - new Object[][]{ - {"jdbc:tc:cockroach://hostname/databasename", EnumSet.noneOf(Options.class)}, - }); + return Arrays.asList( + new Object[][] { // + { "jdbc:tc:cockroach://hostname/databasename", EnumSet.noneOf(Options.class) }, + } + ); } } diff --git a/modules/cockroachdb/src/test/java/org/testcontainers/junit/cockroachdb/SimpleCockroachDBTest.java b/modules/cockroachdb/src/test/java/org/testcontainers/junit/cockroachdb/SimpleCockroachDBTest.java index 3121ad82100..87e82fb30e3 100644 --- a/modules/cockroachdb/src/test/java/org/testcontainers/junit/cockroachdb/SimpleCockroachDBTest.java +++ b/modules/cockroachdb/src/test/java/org/testcontainers/junit/cockroachdb/SimpleCockroachDBTest.java @@ -1,6 +1,7 @@ package org.testcontainers.junit.cockroachdb; import org.junit.Test; +import org.testcontainers.CockroachDBTestImages; import org.testcontainers.containers.CockroachContainer; import org.testcontainers.db.AbstractContainerDatabaseTest; @@ -9,13 +10,11 @@ import java.util.logging.Level; import java.util.logging.LogManager; -import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.Matchers.containsString; import static org.junit.Assert.assertThat; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.testcontainers.CockroachDBTestImages.COCKROACHDB_IMAGE; public class SimpleCockroachDBTest extends AbstractContainerDatabaseTest { - static { // Postgres JDBC driver uses JUL; disable it to avoid annoying, irrelevant, stderr logs during connection testing LogManager.getLogManager().getLogger("").setLevel(Level.OFF); @@ -23,7 +22,7 @@ public class SimpleCockroachDBTest extends AbstractContainerDatabaseTest { @Test public void testSimple() throws SQLException { - try (CockroachContainer cockroach = new CockroachContainer(COCKROACHDB_IMAGE)) { + try (CockroachContainer cockroach = new CockroachContainer(CockroachDBTestImages.COCKROACHDB_IMAGE)) { cockroach.start(); ResultSet resultSet = performQuery(cockroach, "SELECT 1"); @@ -35,8 +34,10 @@ public void testSimple() throws SQLException { @Test public void testExplicitInitScript() throws SQLException { - try (CockroachContainer cockroach = new CockroachContainer(COCKROACHDB_IMAGE) - .withInitScript("somepath/init_postgresql.sql")) { // CockroachDB is expected to be compatible with Postgres + try ( + CockroachContainer cockroach = new CockroachContainer(CockroachDBTestImages.COCKROACHDB_IMAGE) + .withInitScript("somepath/init_postgresql.sql") + ) { // CockroachDB is expected to be compatible with Postgres cockroach.start(); ResultSet resultSet = performQuery(cockroach, "SELECT foo FROM bar"); @@ -48,7 +49,7 @@ public void testExplicitInitScript() throws SQLException { @Test public void testWithAdditionalUrlParamInJdbcUrl() { - CockroachContainer cockroach = new CockroachContainer(COCKROACHDB_IMAGE) + CockroachContainer cockroach = new CockroachContainer(CockroachDBTestImages.COCKROACHDB_IMAGE) .withUrlParam("sslmode", "disable") .withUrlParam("application_name", "cockroach"); diff --git a/modules/couchbase/src/main/java/org/testcontainers/couchbase/BucketDefinition.java b/modules/couchbase/src/main/java/org/testcontainers/couchbase/BucketDefinition.java index 3778833182e..ad666b4ffef 100644 --- a/modules/couchbase/src/main/java/org/testcontainers/couchbase/BucketDefinition.java +++ b/modules/couchbase/src/main/java/org/testcontainers/couchbase/BucketDefinition.java @@ -22,8 +22,11 @@ public class BucketDefinition { private final String name; + private boolean flushEnabled = false; + private boolean queryPrimaryIndex = true; + private int quota = 100; public BucketDefinition(final String name) { @@ -49,7 +52,7 @@ public BucketDefinition withFlushEnabled(final boolean flushEnabled) { */ public BucketDefinition withQuota(final int quota) { if (quota < 100) { - throw new IllegalArgumentException("Bucket quota cannot be less than 100MB!"); + throw new IllegalArgumentException("Bucket quota cannot be less than 100MB!"); } this.quota = quota; return this; @@ -81,5 +84,4 @@ public boolean hasPrimaryIndex() { public int getQuota() { return quota; } - } diff --git a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java index c2becc68fc3..4f190a5413c 100644 --- a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java +++ b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.testcontainers.couchbase; import com.fasterxml.jackson.databind.JsonNode; @@ -139,7 +140,6 @@ public CouchbaseContainer(final String dockerImageName) { */ public CouchbaseContainer(final DockerImageName dockerImageName) { super(dockerImageName); - dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); } @@ -182,8 +182,14 @@ public CouchbaseContainer withServiceQuota(final CouchbaseService service, final throw new IllegalArgumentException("The provided service (" + service + ") has no quota to configure"); } if (quotaMb < service.getMinimumQuotaMb()) { - throw new IllegalArgumentException("The custom quota (" + quotaMb + ") must not be smaller than the " + - "minimum quota for the service (" + service.getMinimumQuotaMb() + ")"); + throw new IllegalArgumentException( + "The custom quota (" + + quotaMb + + ") must not be smaller than the " + + "minimum quota for the service (" + + service.getMinimumQuotaMb() + + ")" + ); } this.customServiceQuotas.put(service, quotaMb); return this; @@ -255,54 +261,59 @@ protected void configure() { WaitAllStrategy waitStrategy = new WaitAllStrategy(); // Makes sure that all nodes in the cluster are healthy. - waitStrategy = waitStrategy.withStrategy( - new HttpWaitStrategy() - .forPath("/pools/default") - .forPort(MGMT_PORT) - .withBasicCredentials(username, password) - .forStatusCode(200) - .forResponsePredicate(response -> { - try { - return Optional.of(MAPPER.readTree(response)) - .map(n -> n.at("/nodes/0/status")) - .map(JsonNode::asText) - .map("healthy"::equals) - .orElse(false); - } catch (IOException e) { - logger().error("Unable to parse response: {}", response, e); - return false; - } - }) - ); - - if (enabledServices.contains(CouchbaseService.QUERY)) { - waitStrategy = waitStrategy.withStrategy( + waitStrategy = + waitStrategy.withStrategy( new HttpWaitStrategy() - .forPath("/admin/ping") - .forPort(QUERY_PORT) + .forPath("/pools/default") + .forPort(MGMT_PORT) .withBasicCredentials(username, password) .forStatusCode(200) + .forResponsePredicate(response -> { + try { + return Optional + .of(MAPPER.readTree(response)) + .map(n -> n.at("/nodes/0/status")) + .map(JsonNode::asText) + .map("healthy"::equals) + .orElse(false); + } catch (IOException e) { + logger().error("Unable to parse response: {}", response, e); + return false; + } + }) ); + + if (enabledServices.contains(CouchbaseService.QUERY)) { + waitStrategy = + waitStrategy.withStrategy( + new HttpWaitStrategy() + .forPath("/admin/ping") + .forPort(QUERY_PORT) + .withBasicCredentials(username, password) + .forStatusCode(200) + ); } if (enabledServices.contains(CouchbaseService.ANALYTICS)) { - waitStrategy = waitStrategy.withStrategy( - new HttpWaitStrategy() - .forPath("/admin/ping") - .forPort(ANALYTICS_PORT) - .withBasicCredentials(username, password) - .forStatusCode(200) - ); + waitStrategy = + waitStrategy.withStrategy( + new HttpWaitStrategy() + .forPath("/admin/ping") + .forPort(ANALYTICS_PORT) + .withBasicCredentials(username, password) + .forStatusCode(200) + ); } if (enabledServices.contains(CouchbaseService.EVENTING)) { - waitStrategy = waitStrategy.withStrategy( - new HttpWaitStrategy() - .forPath("/api/v1/config") - .forPort(EVENTING_PORT) - .withBasicCredentials(username, password) - .forStatusCode(200) - ); + waitStrategy = + waitStrategy.withStrategy( + new HttpWaitStrategy() + .forPath("/api/v1/config") + .forPort(EVENTING_PORT) + .withBasicCredentials(username, password) + .forStatusCode(200) + ); } waitingFor(waitStrategy); @@ -329,25 +340,23 @@ protected void containerIsStarting(final InspectContainerResponse containerInfo) protected void containerIsStarted(InspectContainerResponse containerInfo) { timePhase("createBuckets", this::createBuckets); - logger().info("Couchbase container is ready! UI available at http://{}:{}", getHost(), getMappedPort(MGMT_PORT)); + logger() + .info("Couchbase container is ready! UI available at http://{}:{}", getHost(), getMappedPort(MGMT_PORT)); } /** * Before we can start configuring the host, we need to wait until the cluster manager is listening. */ private void waitUntilNodeIsOnline() { - new HttpWaitStrategy() - .forPort(MGMT_PORT) - .forPath("/pools") - .forStatusCode(200) - .waitUntilReady(this); + new HttpWaitStrategy().forPort(MGMT_PORT).forPath("/pools").forStatusCode(200).waitUntilReady(this); } /** * Fetches edition (enterprise or community) of started container. */ private void initializeIsEnterprise() { - @Cleanup Response response = doHttpRequest(MGMT_PORT, "/pools", "GET", null, true); + @Cleanup + Response response = doHttpRequest(MGMT_PORT, "/pools", "GET", null, true); try { isEnterprise = MAPPER.readTree(response.body().string()).get("isEnterprise").asBoolean(); @@ -374,9 +383,13 @@ private void initializeIsEnterprise() { private void renameNode() { logger().debug("Renaming Couchbase Node from localhost to {}", getHost()); - @Cleanup Response response = doHttpRequest(MGMT_PORT, "/node/controller/rename", "POST", new FormBody.Builder() - .add("hostname", getInternalIpAddress()) - .build(), false + @Cleanup + Response response = doHttpRequest( + MGMT_PORT, + "/node/controller/rename", + "POST", + new FormBody.Builder().add("hostname", getInternalIpAddress()).build(), + false ); checkSuccessfulResponse(response, "Could not rename couchbase node"); @@ -393,9 +406,13 @@ private void initializeServices() { .map(CouchbaseService::getIdentifier) .collect(Collectors.joining(",")); - @Cleanup Response response = doHttpRequest(MGMT_PORT, "/node/controller/setupServices", "POST", new FormBody.Builder() - .add("services", services) - .build(), false + @Cleanup + Response response = doHttpRequest( + MGMT_PORT, + "/node/controller/setupServices", + "POST", + new FormBody.Builder().add("services", services).build(), + false ); checkSuccessfulResponse(response, "Could not enable couchbase services"); @@ -423,9 +440,8 @@ private void setMemoryQuotas() { } } - @Cleanup Response response = doHttpRequest( - MGMT_PORT, "/pools/default", "POST", quotaBuilder.build(), false - ); + @Cleanup + Response response = doHttpRequest(MGMT_PORT, "/pools/default", "POST", quotaBuilder.build(), false); checkSuccessfulResponse(response, "Could not configure service memory quotas"); } @@ -438,11 +454,18 @@ private void setMemoryQuotas() { private void configureAdminUser() { logger().debug("Configuring couchbase admin user with username: \"{}\"", username); - @Cleanup Response response = doHttpRequest(MGMT_PORT, "/settings/web", "POST", new FormBody.Builder() - .add("username", username) - .add("password", password) - .add("port", Integer.toString(MGMT_PORT)) - .build(), false); + @Cleanup + Response response = doHttpRequest( + MGMT_PORT, + "/settings/web", + "POST", + new FormBody.Builder() + .add("username", username) + .add("password", password) + .add("port", Integer.toString(MGMT_PORT)) + .build(), + false + ); checkSuccessfulResponse(response, "Could not configure couchbase admin user"); } @@ -489,7 +512,8 @@ private void configureExternalPorts() { builder.add("eventingSSL", Integer.toString(getMappedPort(EVENTING_SSL_PORT))); } - @Cleanup Response response = doHttpRequest( + @Cleanup + Response response = doHttpRequest( MGMT_PORT, "/node/controller/setupAlternateAddresses/external", "PUT", @@ -506,9 +530,13 @@ private void configureExternalPorts() { private void configureIndexer() { logger().debug("Configuring the indexer service"); - @Cleanup Response response = doHttpRequest(MGMT_PORT, "/settings/indexes", "POST", new FormBody.Builder() - .add("storageMode", isEnterprise ? "memory_optimized" : "forestdb") - .build(), true + @Cleanup + Response response = doHttpRequest( + MGMT_PORT, + "/settings/indexes", + "POST", + new FormBody.Builder().add("storageMode", isEnterprise ? "memory_optimized" : "forestdb").build(), + true ); checkSuccessfulResponse(response, "Could not configure the indexing service"); @@ -523,22 +551,32 @@ private void createBuckets() { for (BucketDefinition bucket : buckets) { logger().debug("Creating bucket \"{}\"", bucket.getName()); - @Cleanup Response response = doHttpRequest(MGMT_PORT, "/pools/default/buckets", "POST", new FormBody.Builder() - .add("name", bucket.getName()) - .add("ramQuotaMB", Integer.toString(bucket.getQuota())) - .add("flushEnabled", bucket.hasFlushEnabled() ? "1" : "0") - .build(), true); + @Cleanup + Response response = doHttpRequest( + MGMT_PORT, + "/pools/default/buckets", + "POST", + new FormBody.Builder() + .add("name", bucket.getName()) + .add("ramQuotaMB", Integer.toString(bucket.getQuota())) + .add("flushEnabled", bucket.hasFlushEnabled() ? "1" : "0") + .build(), + true + ); checkSuccessfulResponse(response, "Could not create bucket " + bucket.getName()); - timePhase("createBucket:" + bucket.getName() + ":waitForAllServicesEnabled", () -> - new HttpWaitStrategy() - .forPath("/pools/default/b/" + bucket.getName()) - .forPort(MGMT_PORT) - .withBasicCredentials(username, password) - .forStatusCode(200) - .forResponsePredicate(new AllServicesEnabledPredicate()) - .waitUntilReady(this) + timePhase( + "createBucket:" + bucket.getName() + ":waitForAllServicesEnabled", + () -> { + new HttpWaitStrategy() + .forPath("/pools/default/b/" + bucket.getName()) + .forPort(MGMT_PORT) + .withBasicCredentials(username, password) + .forStatusCode(200) + .forResponsePredicate(new AllServicesEnabledPredicate()) + .waitUntilReady(this); + } ); if (enabledServices.contains(CouchbaseService.QUERY)) { @@ -546,29 +584,62 @@ private void createBuckets() { // knows about the bucket in its metadata configuration. timePhase( "createBucket:" + bucket.getName() + ":queryKeyspacePresent", - () -> Unreliables.retryUntilTrue(1, TimeUnit.MINUTES, () -> { - @Cleanup Response queryResponse = doHttpRequest(QUERY_PORT, "/query/service", "POST", new FormBody.Builder() - .add("statement", "SELECT COUNT(*) > 0 as present FROM system:keyspaces WHERE name = \"" + bucket.getName() + "\"") - .build(), true); - - String body = queryResponse.body() != null ? queryResponse.body().string() : null; - checkSuccessfulResponse(queryResponse, "Could not poll query service state for bucket: " + bucket.getName()); - - return Optional.of(MAPPER.readTree(body)) - .map(n -> n.at("/results/0/present")) - .map(JsonNode::asBoolean) - .orElse(false); - })); + () -> { + Unreliables.retryUntilTrue( + 1, + TimeUnit.MINUTES, + () -> { + @Cleanup + Response queryResponse = doHttpRequest( + QUERY_PORT, + "/query/service", + "POST", + new FormBody.Builder() + .add( + "statement", + "SELECT COUNT(*) > 0 as present FROM system:keyspaces WHERE name = \"" + + bucket.getName() + + "\"" + ) + .build(), + true + ); + + String body = queryResponse.body() != null ? queryResponse.body().string() : null; + checkSuccessfulResponse( + queryResponse, + "Could not poll query service state for bucket: " + bucket.getName() + ); + + return Optional + .of(MAPPER.readTree(body)) + .map(n -> n.at("/results/0/present")) + .map(JsonNode::asBoolean) + .orElse(false); + } + ); + } + ); } if (bucket.hasPrimaryIndex()) { if (enabledServices.contains(CouchbaseService.QUERY)) { - @Cleanup Response queryResponse = doHttpRequest(QUERY_PORT, "/query/service", "POST", new FormBody.Builder() - .add("statement", "CREATE PRIMARY INDEX on `" + bucket.getName() + "`") - .build(), true); + @Cleanup + Response queryResponse = doHttpRequest( + QUERY_PORT, + "/query/service", + "POST", + new FormBody.Builder() + .add("statement", "CREATE PRIMARY INDEX on `" + bucket.getName() + "`") + .build(), + true + ); try { - checkSuccessfulResponse(queryResponse, "Could not create primary index for bucket " + bucket.getName()); + checkSuccessfulResponse( + queryResponse, + "Could not create primary index for bucket " + bucket.getName() + ); } catch (IllegalStateException ex) { // potentially ignore the error, the index will be eventually built. if (!ex.getMessage().contains("Index creation will be retried in background")) { @@ -578,21 +649,48 @@ private void createBuckets() { timePhase( "createBucket:" + bucket.getName() + ":primaryIndexOnline", - () -> Unreliables.retryUntilTrue(1, TimeUnit.MINUTES, () -> { - @Cleanup Response stateResponse = doHttpRequest(QUERY_PORT, "/query/service", "POST", new FormBody.Builder() - .add("statement", "SELECT count(*) > 0 AS online FROM system:indexes where keyspace_id = \"" + bucket.getName() + "\" and is_primary = true and state = \"online\"") - .build(), true); - - String body = stateResponse.body() != null ? stateResponse.body().string() : null; - checkSuccessfulResponse(stateResponse, "Could not poll primary index state for bucket: " + bucket.getName()); - - return Optional.of(MAPPER.readTree(body)) - .map(n -> n.at("/results/0/online")) - .map(JsonNode::asBoolean) - .orElse(false); - })); + () -> { + Unreliables.retryUntilTrue( + 1, + TimeUnit.MINUTES, + () -> { + @Cleanup + Response stateResponse = doHttpRequest( + QUERY_PORT, + "/query/service", + "POST", + new FormBody.Builder() + .add( + "statement", + "SELECT count(*) > 0 AS online FROM system:indexes where keyspace_id = \"" + + bucket.getName() + + "\" and is_primary = true and state = \"online\"" + ) + .build(), + true + ); + + String body = stateResponse.body() != null ? stateResponse.body().string() : null; + checkSuccessfulResponse( + stateResponse, + "Could not poll primary index state for bucket: " + bucket.getName() + ); + + return Optional + .of(MAPPER.readTree(body)) + .map(n -> n.at("/results/0/online")) + .map(JsonNode::asBoolean) + .orElse(false); + } + ); + } + ); } else { - logger().info("Primary index creation for bucket {} ignored, since QUERY service is not present.", bucket.getName()); + logger() + .info( + "Primary index creation for bucket {} ignored, since QUERY service is not present.", + bucket.getName() + ); } } } @@ -602,7 +700,11 @@ private void createBuckets() { * Helper method to extract the internal IP address based on the network configuration. */ private String getInternalIpAddress() { - return getContainerInfo().getNetworkSettings().getNetworks().values().stream() + return getContainerInfo() + .getNetworkSettings() + .getNetworks() + .values() + .stream() .findFirst() .map(ContainerNetwork::getIpAddress) .orElseThrow(() -> new IllegalStateException("No network available to extract the internal IP from!")); @@ -648,8 +750,13 @@ private void checkNotRunning() { * @param auth if authentication with the admin user and password should be used. * @return the response of the request. */ - private Response doHttpRequest(final int port, final String path, final String method, final RequestBody body, - final boolean auth) { + private Response doHttpRequest( + final int port, + final String path, + final String method, + final RequestBody body, + final boolean auth + ) { try { Request.Builder requestBuilder = new Request.Builder() .url("http://" + getHost() + ":" + getMappedPort(port) + path); diff --git a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseService.java b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseService.java index ffb0c9f7f1f..d9a9d2ffe3a 100644 --- a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseService.java +++ b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseService.java @@ -20,7 +20,6 @@ * Describes the different services that should be exposed on the container. */ public enum CouchbaseService { - /** * Key-Value service. */ @@ -88,5 +87,4 @@ int getMinimumQuotaMb() { boolean hasQuota() { return minimumQuotaMb > 0; } - } diff --git a/modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java b/modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java index c91f3f12d7c..1bdbe2ab1db 100644 --- a/modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java +++ b/modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java @@ -34,10 +34,13 @@ public class CouchbaseContainerTest { - private static final DockerImageName COUCHBASE_IMAGE_ENTERPRISE = - DockerImageName.parse("couchbase/server:enterprise-7.0.3"); - private static final DockerImageName COUCHBASE_IMAGE_COMMUNITY = - DockerImageName.parse("couchbase/server:community-7.0.2"); + private static final DockerImageName COUCHBASE_IMAGE_ENTERPRISE = DockerImageName.parse( + "couchbase/server:enterprise-7.0.3" + ); + + private static final DockerImageName COUCHBASE_IMAGE_COMMUNITY = DockerImageName.parse( + "couchbase/server:community-7.0.2" + ); @Test public void testBasicContainerUsageForEnterpriseContainer() { @@ -51,18 +54,21 @@ public void testBasicContainerUsageForEnterpriseContainer() { .withBucket(bucketDefinition) // } ) { - setUpClient(container, cluster -> { - Bucket bucket = cluster.bucket(bucketDefinition.getName()); - bucket.waitUntilReady(Duration.ofSeconds(10L)); + setUpClient( + container, + cluster -> { + Bucket bucket = cluster.bucket(bucketDefinition.getName()); + bucket.waitUntilReady(Duration.ofSeconds(10L)); - Collection collection = bucket.defaultCollection(); + Collection collection = bucket.defaultCollection(); - collection.upsert("foo", JsonObject.create().put("key", "value")); + collection.upsert("foo", JsonObject.create().put("key", "value")); - JsonObject fooObject = collection.get("foo").contentAsObject(); + JsonObject fooObject = collection.get("foo").contentAsObject(); - assertEquals("value", fooObject.getString("key")); - }); + assertEquals("value", fooObject.getString("key")); + } + ); } } @@ -74,42 +80,47 @@ public void testBasicContainerUsageForCommunityContainer() { CouchbaseContainer container = new CouchbaseContainer(COUCHBASE_IMAGE_COMMUNITY) .withBucket(bucketDefinition) ) { - setUpClient(container, cluster -> { - Bucket bucket = cluster.bucket(bucketDefinition.getName()); - bucket.waitUntilReady(Duration.ofSeconds(10L)); + setUpClient( + container, + cluster -> { + Bucket bucket = cluster.bucket(bucketDefinition.getName()); + bucket.waitUntilReady(Duration.ofSeconds(10L)); - Collection collection = bucket.defaultCollection(); + Collection collection = bucket.defaultCollection(); - collection.upsert("foo", JsonObject.create().put("key", "value")); + collection.upsert("foo", JsonObject.create().put("key", "value")); - JsonObject fooObject = collection.get("foo").contentAsObject(); + JsonObject fooObject = collection.get("foo").contentAsObject(); - assertEquals("value", fooObject.getString("key")); - }); + assertEquals("value", fooObject.getString("key")); + } + ); } } @Test public void testBucketIsFlushableIfEnabled() { - BucketDefinition bucketDefinition = new BucketDefinition("mybucket") - .withFlushEnabled(true); + BucketDefinition bucketDefinition = new BucketDefinition("mybucket").withFlushEnabled(true); try ( CouchbaseContainer container = new CouchbaseContainer(COUCHBASE_IMAGE_ENTERPRISE) .withBucket(bucketDefinition) ) { - setUpClient(container, cluster -> { - Bucket bucket = cluster.bucket(bucketDefinition.getName()); - bucket.waitUntilReady(Duration.ofSeconds(10L)); + setUpClient( + container, + cluster -> { + Bucket bucket = cluster.bucket(bucketDefinition.getName()); + bucket.waitUntilReady(Duration.ofSeconds(10L)); - Collection collection = bucket.defaultCollection(); + Collection collection = bucket.defaultCollection(); - collection.upsert("foo", JsonObject.create().put("key", "value")); + collection.upsert("foo", JsonObject.create().put("key", "value")); - cluster.buckets().flushBucket(bucketDefinition.getName()); + cluster.buckets().flushBucket(bucketDefinition.getName()); - await().untilAsserted(() -> assertFalse(collection.exists("foo").exists())); - }); + await().untilAsserted(() -> assertFalse(collection.exists("foo").exists())); + } + ); } } @@ -123,7 +134,12 @@ public void testFailureIfCommunityUsedWithAnalytics() { CouchbaseContainer container = new CouchbaseContainer(COUCHBASE_IMAGE_COMMUNITY) .withEnabledServices(CouchbaseService.KV, CouchbaseService.ANALYTICS) ) { - assertThrows(ContainerLaunchException.class, () -> setUpClient(container, cluster -> {})); + assertThrows( + ContainerLaunchException.class, + () -> { + setUpClient(container, cluster -> {}); + } + ); } } @@ -137,7 +153,12 @@ public void testFailureIfCommunityUsedWithEventing() { CouchbaseContainer container = new CouchbaseContainer(COUCHBASE_IMAGE_COMMUNITY) .withEnabledServices(CouchbaseService.KV, CouchbaseService.EVENTING) ) { - assertThrows(ContainerLaunchException.class, () -> setUpClient(container, cluster -> {})); + assertThrows( + ContainerLaunchException.class, + () -> { + setUpClient(container, cluster -> {}); + } + ); } } diff --git a/modules/database-commons/src/main/java/org/testcontainers/delegate/AbstractDatabaseDelegate.java b/modules/database-commons/src/main/java/org/testcontainers/delegate/AbstractDatabaseDelegate.java index 14572dec421..196eb485b5f 100644 --- a/modules/database-commons/src/main/java/org/testcontainers/delegate/AbstractDatabaseDelegate.java +++ b/modules/database-commons/src/main/java/org/testcontainers/delegate/AbstractDatabaseDelegate.java @@ -27,7 +27,12 @@ protected CONNECTION getConnection() { } @Override - public void execute(Collection statements, String scriptPath, boolean continueOnError, boolean ignoreFailedDrops) { + public void execute( + Collection statements, + String scriptPath, + boolean continueOnError, + boolean ignoreFailedDrops + ) { int lineNumber = 0; for (String statement : statements) { lineNumber++; diff --git a/modules/database-commons/src/main/java/org/testcontainers/delegate/DatabaseDelegate.java b/modules/database-commons/src/main/java/org/testcontainers/delegate/DatabaseDelegate.java index 55886d88143..eda8f197008 100644 --- a/modules/database-commons/src/main/java/org/testcontainers/delegate/DatabaseDelegate.java +++ b/modules/database-commons/src/main/java/org/testcontainers/delegate/DatabaseDelegate.java @@ -10,11 +10,16 @@ * @author Eugeny Karpov */ public interface DatabaseDelegate extends AutoCloseable { - /** * Execute statement by the implementation of the delegate */ - void execute(String statement, String scriptPath, int lineNumber, boolean continueOnError, boolean ignoreFailedDrops); + void execute( + String statement, + String scriptPath, + int lineNumber, + boolean continueOnError, + boolean ignoreFailedDrops + ); /** * Execute collection of statements diff --git a/modules/database-commons/src/main/java/org/testcontainers/ext/ScriptUtils.java b/modules/database-commons/src/main/java/org/testcontainers/ext/ScriptUtils.java index 1b94b84f368..eca4989c70e 100644 --- a/modules/database-commons/src/main/java/org/testcontainers/ext/ScriptUtils.java +++ b/modules/database-commons/src/main/java/org/testcontainers/ext/ScriptUtils.java @@ -22,13 +22,14 @@ import org.slf4j.LoggerFactory; import org.testcontainers.delegate.DatabaseDelegate; -import javax.script.ScriptException; import java.io.IOException; import java.net.URL; import java.nio.charset.StandardCharsets; import java.util.LinkedList; import java.util.List; +import javax.script.ScriptException; + /** * This is a modified version of the Spring-JDBC ScriptUtils class, adapted to reduce * dependencies and slightly alter the API. @@ -48,278 +49,346 @@ */ public abstract class ScriptUtils { - private static final Logger LOGGER = LoggerFactory.getLogger(ScriptUtils.class); - - /** - * Default statement separator within SQL scripts. - */ - public static final String DEFAULT_STATEMENT_SEPARATOR = ";"; + private static final Logger LOGGER = LoggerFactory.getLogger(ScriptUtils.class); - /** - * Fallback statement separator within SQL scripts. - *

Used if neither a custom defined separator nor the - * {@link #DEFAULT_STATEMENT_SEPARATOR} is present in a given script. - */ - public static final String FALLBACK_STATEMENT_SEPARATOR = "\n"; + /** + * Default statement separator within SQL scripts. + */ + public static final String DEFAULT_STATEMENT_SEPARATOR = ";"; - /** - * Default prefix for line comments within SQL scripts. - */ - public static final String DEFAULT_COMMENT_PREFIX = "--"; + /** + * Fallback statement separator within SQL scripts. + *

Used if neither a custom defined separator nor the + * {@link #DEFAULT_STATEMENT_SEPARATOR} is present in a given script. + */ + public static final String FALLBACK_STATEMENT_SEPARATOR = "\n"; - /** - * Default start delimiter for block comments within SQL scripts. - */ - public static final String DEFAULT_BLOCK_COMMENT_START_DELIMITER = "/*"; + /** + * Default prefix for line comments within SQL scripts. + */ + public static final String DEFAULT_COMMENT_PREFIX = "--"; - /** - * Default end delimiter for block comments within SQL scripts. - */ - public static final String DEFAULT_BLOCK_COMMENT_END_DELIMITER = "*/"; + /** + * Default start delimiter for block comments within SQL scripts. + */ + public static final String DEFAULT_BLOCK_COMMENT_START_DELIMITER = "/*"; + /** + * Default end delimiter for block comments within SQL scripts. + */ + public static final String DEFAULT_BLOCK_COMMENT_END_DELIMITER = "*/"; - /** - * Prevent instantiation of this utility class. - */ - private ScriptUtils() { - /* no-op */ - } + /** + * Prevent instantiation of this utility class. + */ + private ScriptUtils() { + /* no-op */ + } /** - * Split an SQL script into separate statements delimited by the provided - * separator string. Each individual statement will be added to the provided - * {@code List}. - *

Within the script, the provided {@code commentPrefix} will be honored: - * any text beginning with the comment prefix and extending to the end of the - * line will be omitted from the output. Similarly, the provided - * {@code blockCommentStartDelimiter} and {@code blockCommentEndDelimiter} - * delimiters will be honored: any text enclosed in a block comment will be - * omitted from the output. In addition, multiple adjacent whitespace characters - * will be collapsed into a single space. - * @param resource the resource from which the script was read - * @param script the SQL script; never {@code null} or empty - * @param separator text separating each statement — typically a ';' or - * newline character; never {@code null} - * @param commentPrefix the prefix that identifies SQL line comments — - * typically "--"; never {@code null} or empty - * @param blockCommentStartDelimiter the start block comment delimiter; - * never {@code null} or empty - * @param blockCommentEndDelimiter the end block comment delimiter; - * never {@code null} or empty - * @param statements the list that will contain the individual statements - */ - public static void splitSqlScript(String resource, String script, String separator, String commentPrefix, - String blockCommentStartDelimiter, String blockCommentEndDelimiter, List statements) { - - checkArgument(StringUtils.isNotEmpty(script), "script must not be null or empty"); - checkArgument(separator != null, "separator must not be null"); - checkArgument(StringUtils.isNotEmpty(commentPrefix), "commentPrefix must not be null or empty"); - checkArgument(StringUtils.isNotEmpty(blockCommentStartDelimiter), "blockCommentStartDelimiter must not be null or empty"); - checkArgument(StringUtils.isNotEmpty(blockCommentEndDelimiter), "blockCommentEndDelimiter must not be null or empty"); - - StringBuilder sb = new StringBuilder(); - boolean inEscape = false; - boolean inLineComment = false; - boolean inBlockComment = false; - Character currentLiteralDelimiter = null; - - int compoundStatementDepth = 0; - final String lowerCaseScriptContent = script.toLowerCase(); - char[] content = script.toCharArray(); - for (int i = 0; i < script.length(); i++) { - char c = content[i]; - if (inEscape) { - inEscape = false; - sb.append(c); - continue; - } - // MySQL style escapes - if (c == '\\') { - inEscape = true; - sb.append(c); - continue; - } - // Determine whether we're entering/leaving a string literal - if (!inBlockComment && !inLineComment && (c == '\'' || c == '"' || c == '`')) { - if (currentLiteralDelimiter == null) { // ignore delimiters within an existing string literal - currentLiteralDelimiter = c; - } else if (currentLiteralDelimiter == c) { // find end of string literal - currentLiteralDelimiter = null; - } - } - final boolean inLiteral = currentLiteralDelimiter != null; - - if (!inLiteral && containsSubstringAtOffset(lowerCaseScriptContent, commentPrefix, i)) { - inLineComment = true; - } - if (inLineComment && c == '\n') { - inLineComment = false; - } - if (!inLiteral && containsSubstringAtOffset(lowerCaseScriptContent, blockCommentStartDelimiter, i)) { - inBlockComment = true; - } - if (!inLiteral && inBlockComment && containsSubstringAtOffset(lowerCaseScriptContent, blockCommentEndDelimiter, i)) { - inBlockComment = false; - } - final boolean inComment = inLineComment || inBlockComment; - - if (!inLiteral && !inComment && containsKeywordsAtOffset(lowerCaseScriptContent, "BEGIN", i, separator, commentPrefix, blockCommentStartDelimiter)) { - compoundStatementDepth++; - } - if (!inLiteral && !inComment && containsKeywordsAtOffset(lowerCaseScriptContent, "END", i, separator, commentPrefix, blockCommentStartDelimiter)) { - compoundStatementDepth--; - } - final boolean inCompoundStatement = compoundStatementDepth != 0; - - if (!inLiteral && !inCompoundStatement) { - if (script.startsWith(separator, i)) { - // we've reached the end of the current statement - sb = flushStringBuilder(sb, statements); - i += separator.length() - 1; - continue; - } - else if (script.startsWith(commentPrefix, i)) { - // skip over any content from the start of the comment to the EOL - int indexOfNextNewline = script.indexOf("\n", i); - if (indexOfNextNewline > i) { - i = indexOfNextNewline; - continue; - } - else { - // if there's no EOL, we must be at the end - // of the script, so stop here. - break; - } - } - else if (script.startsWith(blockCommentStartDelimiter, i)) { - // skip over any block comments - int indexOfCommentEnd = script.indexOf(blockCommentEndDelimiter, i); - if (indexOfCommentEnd > i) { - i = indexOfCommentEnd + blockCommentEndDelimiter.length() - 1; + * Split an SQL script into separate statements delimited by the provided + * separator string. Each individual statement will be added to the provided + * {@code List}. + *

Within the script, the provided {@code commentPrefix} will be honored: + * any text beginning with the comment prefix and extending to the end of the + * line will be omitted from the output. Similarly, the provided + * {@code blockCommentStartDelimiter} and {@code blockCommentEndDelimiter} + * delimiters will be honored: any text enclosed in a block comment will be + * omitted from the output. In addition, multiple adjacent whitespace characters + * will be collapsed into a single space. + * @param resource the resource from which the script was read + * @param script the SQL script; never {@code null} or empty + * @param separator text separating each statement — typically a ';' or + * newline character; never {@code null} + * @param commentPrefix the prefix that identifies SQL line comments — + * typically "--"; never {@code null} or empty + * @param blockCommentStartDelimiter the start block comment delimiter; + * never {@code null} or empty + * @param blockCommentEndDelimiter the end block comment delimiter; + * never {@code null} or empty + * @param statements the list that will contain the individual statements + */ + public static void splitSqlScript( + String resource, + String script, + String separator, + String commentPrefix, + String blockCommentStartDelimiter, + String blockCommentEndDelimiter, + List statements + ) { + checkArgument(StringUtils.isNotEmpty(script), "script must not be null or empty"); + checkArgument(separator != null, "separator must not be null"); + checkArgument(StringUtils.isNotEmpty(commentPrefix), "commentPrefix must not be null or empty"); + checkArgument( + StringUtils.isNotEmpty(blockCommentStartDelimiter), + "blockCommentStartDelimiter must not be null or empty" + ); + checkArgument( + StringUtils.isNotEmpty(blockCommentEndDelimiter), + "blockCommentEndDelimiter must not be null or empty" + ); + + StringBuilder sb = new StringBuilder(); + boolean inEscape = false; + boolean inLineComment = false; + boolean inBlockComment = false; + Character currentLiteralDelimiter = null; + + int compoundStatementDepth = 0; + final String lowerCaseScriptContent = script.toLowerCase(); + char[] content = script.toCharArray(); + for (int i = 0; i < script.length(); i++) { + char c = content[i]; + if (inEscape) { + inEscape = false; + sb.append(c); + continue; + } + // MySQL style escapes + if (c == '\\') { + inEscape = true; + sb.append(c); + continue; + } + // Determine whether we're entering/leaving a string literal + if (!inBlockComment && !inLineComment && (c == '\'' || c == '"' || c == '`')) { + if (currentLiteralDelimiter == null) { // ignore delimiters within an existing string literal + currentLiteralDelimiter = c; + } else if (currentLiteralDelimiter == c) { // find end of string literal + currentLiteralDelimiter = null; + } + } + final boolean inLiteral = currentLiteralDelimiter != null; + + if (!inLiteral && containsSubstringAtOffset(lowerCaseScriptContent, commentPrefix, i)) { + inLineComment = true; + } + if (inLineComment && c == '\n') { + inLineComment = false; + } + if (!inLiteral && containsSubstringAtOffset(lowerCaseScriptContent, blockCommentStartDelimiter, i)) { + inBlockComment = true; + } + if ( + !inLiteral && + inBlockComment && + containsSubstringAtOffset(lowerCaseScriptContent, blockCommentEndDelimiter, i) + ) { + inBlockComment = false; + } + final boolean inComment = inLineComment || inBlockComment; + + if ( + !inLiteral && + !inComment && + containsKeywordsAtOffset( + lowerCaseScriptContent, + "BEGIN", + i, + separator, + commentPrefix, + blockCommentStartDelimiter + ) + ) { + compoundStatementDepth++; + } + if ( + !inLiteral && + !inComment && + containsKeywordsAtOffset( + lowerCaseScriptContent, + "END", + i, + separator, + commentPrefix, + blockCommentStartDelimiter + ) + ) { + compoundStatementDepth--; + } + final boolean inCompoundStatement = compoundStatementDepth != 0; + + if (!inLiteral && !inCompoundStatement) { + if (script.startsWith(separator, i)) { + // we've reached the end of the current statement + sb = flushStringBuilder(sb, statements); + i += separator.length() - 1; + continue; + } else if (script.startsWith(commentPrefix, i)) { + // skip over any content from the start of the comment to the EOL + int indexOfNextNewline = script.indexOf("\n", i); + if (indexOfNextNewline > i) { + i = indexOfNextNewline; + continue; + } else { + // if there's no EOL, we must be at the end + // of the script, so stop here. + break; + } + } else if (script.startsWith(blockCommentStartDelimiter, i)) { + // skip over any block comments + int indexOfCommentEnd = script.indexOf(blockCommentEndDelimiter, i); + if (indexOfCommentEnd > i) { + i = indexOfCommentEnd + blockCommentEndDelimiter.length() - 1; inBlockComment = false; - continue; - } - else { - throw new ScriptParseException(String.format("Missing block comment end delimiter [%s].", - blockCommentEndDelimiter), resource); - } - } - else if (c == ' ' || c == '\n' || c == '\t' || c == '\r') { - // avoid multiple adjacent whitespace characters - if (sb.length() > 0 && sb.charAt(sb.length() - 1) != ' ') { - c = ' '; - } - else { - continue; - } - } - } - sb.append(c); - } - flushStringBuilder(sb, statements); - } - - private static StringBuilder flushStringBuilder(StringBuilder sb, List statements) { - if (sb.length() == 0) { - return sb; - } - - final String s = sb.toString().trim(); - if (StringUtils.isNotEmpty(s)) { - statements.add(s); - } - - return new StringBuilder(); - } - - private static boolean isSeperator(char c, String separator, String commentPrefix, - String blockCommentStartDelimiter) { - return c == ' ' || c == '\r' || c == '\n' || c == '\t' || - c == separator.charAt(0) || c == separator.charAt(separator.length() - 1) || - c == commentPrefix.charAt(0) || c == blockCommentStartDelimiter.charAt(0) || - c == blockCommentStartDelimiter.charAt(blockCommentStartDelimiter.length() - 1); + continue; + } else { + throw new ScriptParseException( + String.format("Missing block comment end delimiter [%s].", blockCommentEndDelimiter), + resource + ); + } + } else if (c == ' ' || c == '\n' || c == '\t' || c == '\r') { + // avoid multiple adjacent whitespace characters + if (sb.length() > 0 && sb.charAt(sb.length() - 1) != ' ') { + c = ' '; + } else { + continue; + } + } + } + sb.append(c); + } + flushStringBuilder(sb, statements); + } + + private static StringBuilder flushStringBuilder(StringBuilder sb, List statements) { + if (sb.length() == 0) { + return sb; + } + + final String s = sb.toString().trim(); + if (StringUtils.isNotEmpty(s)) { + statements.add(s); + } + + return new StringBuilder(); } - private static boolean containsSubstringAtOffset(String lowercaseString, String substring, int offset) { - String lowercaseSubstring = substring.toLowerCase(); + private static boolean isSeperator( + char c, + String separator, + String commentPrefix, + String blockCommentStartDelimiter + ) { + return ( + c == ' ' || + c == '\r' || + c == '\n' || + c == '\t' || + c == separator.charAt(0) || + c == separator.charAt(separator.length() - 1) || + c == commentPrefix.charAt(0) || + c == blockCommentStartDelimiter.charAt(0) || + c == blockCommentStartDelimiter.charAt(blockCommentStartDelimiter.length() - 1) + ); + } - return lowercaseString.startsWith(lowercaseSubstring, offset); - } + private static boolean containsSubstringAtOffset(String lowercaseString, String substring, int offset) { + String lowercaseSubstring = substring.toLowerCase(); - private static boolean containsKeywordsAtOffset(String lowercaseString, String keywords, int offset, - String separator, String commentPrefix, - String blockCommentStartDelimiter) { + return lowercaseString.startsWith(lowercaseSubstring, offset); + } + + private static boolean containsKeywordsAtOffset( + String lowercaseString, + String keywords, + int offset, + String separator, + String commentPrefix, + String blockCommentStartDelimiter + ) { String lowercaseKeywords = keywords.toLowerCase(); - boolean backSeperated = (offset == 0) || isSeperator(lowercaseString.charAt(offset - 1), - separator, commentPrefix, blockCommentStartDelimiter); - boolean frontSeperated = (offset >= (lowercaseString.length() - keywords.length())) || - isSeperator(lowercaseString.charAt(offset + keywords.length()), - separator, commentPrefix, blockCommentStartDelimiter); + boolean backSeperated = + (offset == 0) || + isSeperator(lowercaseString.charAt(offset - 1), separator, commentPrefix, blockCommentStartDelimiter); + boolean frontSeperated = + (offset >= (lowercaseString.length() - keywords.length())) || + isSeperator( + lowercaseString.charAt(offset + keywords.length()), + separator, + commentPrefix, + blockCommentStartDelimiter + ); return backSeperated && frontSeperated && lowercaseString.startsWith(lowercaseKeywords, offset); } - private static void checkArgument(boolean expression, String errorMessage) { - if (!expression) { - throw new IllegalArgumentException(errorMessage); - } - } - - /** - * Does the provided SQL script contain the specified delimiter? - * @param script the SQL script - * @param delim String delimiting each statement - typically a ';' character - */ - public static boolean containsSqlScriptDelimiters(String script, String delim) { - boolean inLiteral = false; - char[] content = script.toCharArray(); - for (int i = 0; i < script.length(); i++) { - if (content[i] == '\'') { - inLiteral = !inLiteral; - } - if (!inLiteral && script.startsWith(delim, i)) { - return true; - } - } - return false; - } - - /** - * Load script from classpath and apply it to the given database - * - * @param databaseDelegate database delegate for script execution - * @param initScriptPath the resource to load the init script from - */ - public static void runInitScript(DatabaseDelegate databaseDelegate, String initScriptPath) { - try { - URL resource = Thread.currentThread().getContextClassLoader().getResource(initScriptPath); - if (resource == null) { - resource = ScriptUtils.class.getClassLoader().getResource(initScriptPath); - if (resource == null) { - LOGGER.warn("Could not load classpath init script: {}", initScriptPath); - throw new ScriptLoadException("Could not load classpath init script: " + initScriptPath + ". Resource not found."); - } - } - String scripts = IOUtils.toString(resource, StandardCharsets.UTF_8); - executeDatabaseScript(databaseDelegate, initScriptPath, scripts); - } catch (IOException e) { - LOGGER.warn("Could not load classpath init script: {}", initScriptPath); - throw new ScriptLoadException("Could not load classpath init script: " + initScriptPath, e); - } catch (ScriptException e) { - LOGGER.error("Error while executing init script: {}", initScriptPath, e); - throw new UncategorizedScriptException("Error while executing init script: " + initScriptPath, e); - } - } - - public static void executeDatabaseScript(DatabaseDelegate databaseDelegate, String scriptPath, String script) throws ScriptException { - executeDatabaseScript(databaseDelegate, scriptPath, script, false, false, DEFAULT_COMMENT_PREFIX, DEFAULT_STATEMENT_SEPARATOR, DEFAULT_BLOCK_COMMENT_START_DELIMITER, DEFAULT_BLOCK_COMMENT_END_DELIMITER); + private static void checkArgument(boolean expression, String errorMessage) { + if (!expression) { + throw new IllegalArgumentException(errorMessage); + } + } + + /** + * Does the provided SQL script contain the specified delimiter? + * @param script the SQL script + * @param delim String delimiting each statement - typically a ';' character + */ + public static boolean containsSqlScriptDelimiters(String script, String delim) { + boolean inLiteral = false; + char[] content = script.toCharArray(); + for (int i = 0; i < script.length(); i++) { + if (content[i] == '\'') { + inLiteral = !inLiteral; + } + if (!inLiteral && script.startsWith(delim, i)) { + return true; + } + } + return false; + } + + /** + * Load script from classpath and apply it to the given database + * + * @param databaseDelegate database delegate for script execution + * @param initScriptPath the resource to load the init script from + */ + public static void runInitScript(DatabaseDelegate databaseDelegate, String initScriptPath) { + try { + URL resource = Thread.currentThread().getContextClassLoader().getResource(initScriptPath); + if (resource == null) { + resource = ScriptUtils.class.getClassLoader().getResource(initScriptPath); + if (resource == null) { + LOGGER.warn("Could not load classpath init script: {}", initScriptPath); + throw new ScriptLoadException( + "Could not load classpath init script: " + initScriptPath + ". Resource not found." + ); + } + } + String scripts = IOUtils.toString(resource, StandardCharsets.UTF_8); + executeDatabaseScript(databaseDelegate, initScriptPath, scripts); + } catch (IOException e) { + LOGGER.warn("Could not load classpath init script: {}", initScriptPath); + throw new ScriptLoadException("Could not load classpath init script: " + initScriptPath, e); + } catch (ScriptException e) { + LOGGER.error("Error while executing init script: {}", initScriptPath, e); + throw new UncategorizedScriptException("Error while executing init script: " + initScriptPath, e); + } + } + + public static void executeDatabaseScript(DatabaseDelegate databaseDelegate, String scriptPath, String script) + throws ScriptException { + executeDatabaseScript( + databaseDelegate, + scriptPath, + script, + false, + false, + DEFAULT_COMMENT_PREFIX, + DEFAULT_STATEMENT_SEPARATOR, + DEFAULT_BLOCK_COMMENT_START_DELIMITER, + DEFAULT_BLOCK_COMMENT_END_DELIMITER + ); } /** - * Execute the given database script. - *

Statement separators and comments will be removed before executing - * individual statements within the supplied script. + * Execute the given database script. + *

Statement separators and comments will be removed before executing + * individual statements within the supplied script. *

Do not use this method to execute DDL if you expect rollback. * @param databaseDelegate database delegate for script execution * @param scriptPath the resource (potentially associated with a specific encoding) @@ -328,85 +397,104 @@ public static void executeDatabaseScript(DatabaseDelegate databaseDelegate, Stri *@param continueOnError whether or not to continue without throwing an exception * in the event of an error * @param ignoreFailedDrops whether or not to continue in the event of specifically -* an error on a {@code DROP} statement + * an error on a {@code DROP} statement * @param commentPrefix the prefix that identifies comments in the SQL script — -* typically "--" + * typically "--" * @param separator the script statement separator; defaults to -* {@value #DEFAULT_STATEMENT_SEPARATOR} if not specified and falls back to -* {@value #FALLBACK_STATEMENT_SEPARATOR} as a last resort + * {@value #DEFAULT_STATEMENT_SEPARATOR} if not specified and falls back to + * {@value #FALLBACK_STATEMENT_SEPARATOR} as a last resort * @param blockCommentStartDelimiter the start block comment delimiter; never -* {@code null} or empty + * {@code null} or empty * @param blockCommentEndDelimiter the end block comment delimiter; never -* {@code null} or empty @throws ScriptException if an error occurred while executing the SQL script - */ - public static void executeDatabaseScript(DatabaseDelegate databaseDelegate, String scriptPath, String script, boolean continueOnError, - boolean ignoreFailedDrops, String commentPrefix, String separator, String blockCommentStartDelimiter, - String blockCommentEndDelimiter) throws ScriptException { - - try { - if (LOGGER.isInfoEnabled()) { - LOGGER.info("Executing database script from " + scriptPath); - } - - long startTime = System.currentTimeMillis(); - List statements = new LinkedList<>(); - - if (separator == null) { - separator = DEFAULT_STATEMENT_SEPARATOR; - } - if (!containsSqlScriptDelimiters(script, separator)) { - separator = FALLBACK_STATEMENT_SEPARATOR; - } - - splitSqlScript(scriptPath, script, separator, commentPrefix, blockCommentStartDelimiter, - blockCommentEndDelimiter, statements); - - try (DatabaseDelegate closeableDelegate = databaseDelegate) { - closeableDelegate.execute(statements, scriptPath, continueOnError, ignoreFailedDrops); - } - - long elapsedTime = System.currentTimeMillis() - startTime; - if (LOGGER.isInfoEnabled()) { - LOGGER.info("Executed database script from " + scriptPath + " in " + elapsedTime + " ms."); - } - } - catch (Exception ex) { - if (ex instanceof ScriptException) { - throw (ScriptException) ex; - } - - throw new UncategorizedScriptException( - "Failed to execute database script from resource [" + script + "]", ex); - } - } - - public static class ScriptLoadException extends RuntimeException { - public ScriptLoadException(String message) { - super(message); - } - - public ScriptLoadException(String message, Throwable cause) { - super(message, cause); - } - } - - public static class ScriptParseException extends RuntimeException { - public ScriptParseException(String format, String scriptPath) { - super(String.format(format, scriptPath)); - } - } - - public static class ScriptStatementFailedException extends RuntimeException { - public ScriptStatementFailedException(String statement, int lineNumber, String scriptPath) { - this(statement, lineNumber, scriptPath, null); - } - - public ScriptStatementFailedException(String statement, int lineNumber, String scriptPath, Exception ex) { - super(String.format("Script execution failed (%s:%d): %s", scriptPath, lineNumber, statement), ex); - } - } + * {@code null} or empty @throws ScriptException if an error occurred while executing the SQL script + */ + public static void executeDatabaseScript( + DatabaseDelegate databaseDelegate, + String scriptPath, + String script, + boolean continueOnError, + boolean ignoreFailedDrops, + String commentPrefix, + String separator, + String blockCommentStartDelimiter, + String blockCommentEndDelimiter + ) throws ScriptException { + try { + if (LOGGER.isInfoEnabled()) { + LOGGER.info("Executing database script from " + scriptPath); + } + + long startTime = System.currentTimeMillis(); + List statements = new LinkedList<>(); + + if (separator == null) { + separator = DEFAULT_STATEMENT_SEPARATOR; + } + if (!containsSqlScriptDelimiters(script, separator)) { + separator = FALLBACK_STATEMENT_SEPARATOR; + } + + splitSqlScript( + scriptPath, + script, + separator, + commentPrefix, + blockCommentStartDelimiter, + blockCommentEndDelimiter, + statements + ); + + try (DatabaseDelegate closeableDelegate = databaseDelegate) { + closeableDelegate.execute(statements, scriptPath, continueOnError, ignoreFailedDrops); + } + + long elapsedTime = System.currentTimeMillis() - startTime; + if (LOGGER.isInfoEnabled()) { + LOGGER.info("Executed database script from " + scriptPath + " in " + elapsedTime + " ms."); + } + } catch (Exception ex) { + if (ex instanceof ScriptException) { + throw (ScriptException) ex; + } + + throw new UncategorizedScriptException( + "Failed to execute database script from resource [" + script + "]", + ex + ); + } + } + + public static class ScriptLoadException extends RuntimeException { + + public ScriptLoadException(String message) { + super(message); + } + + public ScriptLoadException(String message, Throwable cause) { + super(message, cause); + } + } + + public static class ScriptParseException extends RuntimeException { + + public ScriptParseException(String format, String scriptPath) { + super(String.format(format, scriptPath)); + } + } + + public static class ScriptStatementFailedException extends RuntimeException { + + public ScriptStatementFailedException(String statement, int lineNumber, String scriptPath) { + this(statement, lineNumber, scriptPath, null); + } + + public ScriptStatementFailedException(String statement, int lineNumber, String scriptPath, Exception ex) { + super(String.format("Script execution failed (%s:%d): %s", scriptPath, lineNumber, statement), ex); + } + } public static class UncategorizedScriptException extends RuntimeException { + public UncategorizedScriptException(String s, Exception ex) { super(s, ex); } diff --git a/modules/database-commons/src/test/java/org/testcontainers/ext/ScriptSplittingTest.java b/modules/database-commons/src/test/java/org/testcontainers/ext/ScriptSplittingTest.java index 202a3b995db..bc9476cc83d 100644 --- a/modules/database-commons/src/test/java/org/testcontainers/ext/ScriptSplittingTest.java +++ b/modules/database-commons/src/test/java/org/testcontainers/ext/ScriptSplittingTest.java @@ -4,11 +4,10 @@ import org.junit.Test; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; -import static java.util.Arrays.asList; import static org.junit.Assert.fail; -import static org.testcontainers.ext.ScriptUtils.*; public class ScriptSplittingTest { @@ -16,18 +15,15 @@ public class ScriptSplittingTest { public void testStringDemarcation() { String script = "SELECT 'foo `bar`'; SELECT 'foo -- `bar`'; SELECT 'foo /* `bar`';"; - List expected = asList( - "SELECT 'foo `bar`'", - "SELECT 'foo -- `bar`'", - "SELECT 'foo /* `bar`'" - ); + List expected = Arrays.asList("SELECT 'foo `bar`'", "SELECT 'foo -- `bar`'", "SELECT 'foo /* `bar`'"); splitAndCompare(script, expected); } @Test public void testIssue1547Case1() { - String script = "create database if not exists ttt;\n" + + String script = + "create database if not exists ttt;\n" + "\n" + "use ttt;\n" + "\n" + @@ -43,7 +39,7 @@ public void testIssue1547Case1() { " id bigint auto_increment primary key\n" + ") comment 'bbb';"; - List expected = asList( + List expected = Arrays.asList( "create database if not exists ttt", "use ttt", "create table aaa ( id bigint auto_increment primary key, end_time datetime null COMMENT 'end_time', data_status varchar(16) not null ) comment 'aaa'", @@ -55,14 +51,15 @@ public void testIssue1547Case1() { @Test public void testIssue1547Case2() { - String script = "CREATE TABLE bar (\n" + + String script = + "CREATE TABLE bar (\n" + " end_time VARCHAR(255)\n" + ");\n" + "CREATE TABLE bar (\n" + " end_time VARCHAR(255)\n" + ");"; - List expected = asList( + List expected = Arrays.asList( "CREATE TABLE bar ( end_time VARCHAR(255) )", "CREATE TABLE bar ( end_time VARCHAR(255) )" ); @@ -74,25 +71,17 @@ public void testIssue1547Case2() { public void testUnusualSemicolonPlacement() { String script = "SELECT 1;;;;;SELECT 2;\n;SELECT 3\n; SELECT 4;\n SELECT 5"; - List expected = asList( - "SELECT 1", - "SELECT 2", - "SELECT 3", - "SELECT 4", - "SELECT 5" - ); + List expected = Arrays.asList("SELECT 1", "SELECT 2", "SELECT 3", "SELECT 4", "SELECT 5"); splitAndCompare(script, expected); } @Test public void testCommentedSemicolon() { - String script = "CREATE TABLE bar (\n" + - " foo VARCHAR(255)\n" + - "); \nDROP PROCEDURE IF EXISTS -- ;\n" + - " count_foo"; + String script = + "CREATE TABLE bar (\n" + " foo VARCHAR(255)\n" + "); \nDROP PROCEDURE IF EXISTS -- ;\n" + " count_foo"; - List expected = asList( + List expected = Arrays.asList( "CREATE TABLE bar ( foo VARCHAR(255) )", "DROP PROCEDURE IF EXISTS count_foo" ); @@ -102,11 +91,12 @@ public void testCommentedSemicolon() { @Test public void testStringEscaping() { - String script = "SELECT \"a /* string literal containing comment characters like -- here\";\n" + + String script = + "SELECT \"a /* string literal containing comment characters like -- here\";\n" + "SELECT \"a 'quoting' \\\"scenario ` involving BEGIN keyword\\\" here\";\n" + "SELECT * from `bar`;"; - List expected = asList( + List expected = Arrays.asList( "SELECT \"a /* string literal containing comment characters like -- here\"", "SELECT \"a 'quoting' \\\"scenario ` involving BEGIN keyword\\\" here\"", "SELECT * from `bar`" @@ -119,18 +109,17 @@ public void testStringEscaping() { public void testBlockCommentExclusion() { String script = "INSERT INTO bar (foo) /* ; */ VALUES ('hello world');"; - List expected = asList( - "INSERT INTO bar (foo) VALUES ('hello world')" - ); + List expected = Arrays.asList("INSERT INTO bar (foo) VALUES ('hello world')"); splitAndCompare(script, expected); } @Test public void testBeginEndKeywordCorrectDetection() { - String script = "INSERT INTO something_end (begin_with_the_token, another_field) /*end*/ VALUES /* end */ (' begin ', `end`)-- begin\n;"; + String script = + "INSERT INTO something_end (begin_with_the_token, another_field) /*end*/ VALUES /* end */ (' begin ', `end`)-- begin\n;"; - List expected = asList( + List expected = Arrays.asList( "INSERT INTO something_end (begin_with_the_token, another_field) VALUES (' begin ', `end`)" ); @@ -139,16 +128,17 @@ public void testBeginEndKeywordCorrectDetection() { @Test public void testCommentInStrings() { - String script = "CREATE TABLE bar (foo VARCHAR(255));\n" + + String script = + "CREATE TABLE bar (foo VARCHAR(255));\n" + "\n" + "/* Insert Values */\n" + "INSERT INTO bar (foo) values ('--1');\n" + "INSERT INTO bar (foo) values ('--2');\n" + "INSERT INTO bar (foo) values ('/* something */');\n" + - "/* INSERT INTO bar (foo) values (' */'); -- '*/;\n" + // purposefully broken, to see if it breaks our splitting + "/* INSERT INTO bar (foo) values (' */'); -- '*/;\n" + // purposefully broken, to see if it breaks our splitting "INSERT INTO bar (foo) values ('foo');"; - List expected = asList( + List expected = Arrays.asList( "CREATE TABLE bar (foo VARCHAR(255))", "INSERT INTO bar (foo) values ('--1')", "INSERT INTO bar (foo) values ('--2')", @@ -162,7 +152,8 @@ public void testCommentInStrings() { @Test public void testMultipleBeginEndDetection() { - String script = "CREATE TABLE bar (foo VARCHAR(255));\n" + + String script = + "CREATE TABLE bar (foo VARCHAR(255));\n" + "\n" + "CREATE TABLE gender (gender VARCHAR(255));\n" + "CREATE TABLE ending (ending VARCHAR(255));\n" + @@ -187,24 +178,16 @@ public void testMultipleBeginEndDetection() { "\n" + "CREATE TABLE foo (bar VARCHAR(255));"; - List expected = asList( + List expected = Arrays.asList( "CREATE TABLE bar (foo VARCHAR(255))", "CREATE TABLE gender (gender VARCHAR(255))", "CREATE TABLE ending (ending VARCHAR(255))", "CREATE TABLE end2 (end2 VARCHAR(255))", "CREATE TABLE end_2 (end2 VARCHAR(255))", - "BEGIN\n" + - " INSERT INTO ending values ('ending');\n" + - "END", - "BEGIN\n" + - " INSERT INTO ending values ('ending');\n" + - "END", - "BEGIN--Hello\n" + - " INSERT INTO ending values ('ending');\n" + - "END", - "BEGIN\n" + - " INSERT INTO ending values ('ending');\n" + - "END", + "BEGIN\n" + " INSERT INTO ending values ('ending');\n" + "END", + "BEGIN\n" + " INSERT INTO ending values ('ending');\n" + "END", + "BEGIN--Hello\n" + " INSERT INTO ending values ('ending');\n" + "END", + "BEGIN\n" + " INSERT INTO ending values ('ending');\n" + "END", "CREATE TABLE foo (bar VARCHAR(255))" ); @@ -213,7 +196,8 @@ public void testMultipleBeginEndDetection() { @Test public void testProcedureBlock() { - String script = "CREATE PROCEDURE count_foo()\n" + + String script = + "CREATE PROCEDURE count_foo()\n" + " BEGIN\n" + "\n" + " BEGIN\n" + @@ -239,31 +223,31 @@ public void testProcedureBlock() { "\n" + " END /*; */;"; - List expected = asList( + List expected = Arrays.asList( "CREATE PROCEDURE count_foo() BEGIN\n" + - "\n" + - " BEGIN\n" + - " SELECT *\n" + - " FROM bar;\n" + - " SELECT 1\n" + - " FROM dual;\n" + - " END;\n" + - "\n" + - " BEGIN\n" + - " select * from bar;\n" + - " END;\n" + - "\n" + - " -- we can do comments\n" + - "\n" + - " /* including block\n" + - " comments\n" + - " */\n" + - "\n" + - " /* what if BEGIN appears inside a comment? */\n" + - "\n" + - " select \"or what if BEGIN appears inside a literal?\";\n" + - "\n" + - " END" + "\n" + + " BEGIN\n" + + " SELECT *\n" + + " FROM bar;\n" + + " SELECT 1\n" + + " FROM dual;\n" + + " END;\n" + + "\n" + + " BEGIN\n" + + " select * from bar;\n" + + " END;\n" + + "\n" + + " -- we can do comments\n" + + "\n" + + " /* including block\n" + + " comments\n" + + " */\n" + + "\n" + + " /* what if BEGIN appears inside a comment? */\n" + + "\n" + + " select \"or what if BEGIN appears inside a literal?\";\n" + + "\n" + + " END" ); splitAndCompare(script, expected); @@ -283,12 +267,13 @@ public void testUnclosedBlockComment() { @Test public void testIssue1452Case() { - String script = "create table test (text VARCHAR(255));\n" + + String script = + "create table test (text VARCHAR(255));\n" + "\n" + "/* some comment */\n" + "insert into `test` (`text`) values ('a b');"; - List expected = asList( + List expected = Arrays.asList( "create table test (text VARCHAR(255))", "insert into `test` (`text`) values ('a b')" ); @@ -303,7 +288,15 @@ private void splitAndCompare(String script, List expected) { private List doSplit(String script) { final List statements = new ArrayList<>(); - ScriptUtils.splitSqlScript("ignored", script, DEFAULT_STATEMENT_SEPARATOR, DEFAULT_COMMENT_PREFIX, DEFAULT_BLOCK_COMMENT_START_DELIMITER, DEFAULT_BLOCK_COMMENT_END_DELIMITER, statements); + ScriptUtils.splitSqlScript( + "ignored", + script, + ScriptUtils.DEFAULT_STATEMENT_SEPARATOR, + ScriptUtils.DEFAULT_COMMENT_PREFIX, + ScriptUtils.DEFAULT_BLOCK_COMMENT_START_DELIMITER, + ScriptUtils.DEFAULT_BLOCK_COMMENT_END_DELIMITER, + statements + ); return statements; } } diff --git a/modules/db2/src/main/java/org/testcontainers/containers/Db2Container.java b/modules/db2/src/main/java/org/testcontainers/containers/Db2Container.java index c1a252936ea..29e2ce8aa07 100644 --- a/modules/db2/src/main/java/org/testcontainers/containers/Db2Container.java +++ b/modules/db2/src/main/java/org/testcontainers/containers/Db2Container.java @@ -20,10 +20,13 @@ public class Db2Container extends JdbcDatabaseContainer { @Deprecated public static final String DEFAULT_TAG = "11.5.0.0a"; + public static final int DB2_PORT = 50000; private String databaseName = "test"; + private String username = "db2inst1"; + private String password = "foobar1234"; /** @@ -40,11 +43,11 @@ public Db2Container(String dockerImageName) { public Db2Container(final DockerImageName dockerImageName) { super(dockerImageName); - dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); withPrivilegedMode(true); - this.waitStrategy = new LogMessageWaitStrategy() + this.waitStrategy = + new LogMessageWaitStrategy() .withRegEx(".*Setup has completed\\..*") .withStartupTimeout(Duration.of(10, ChronoUnit.MINUTES)); @@ -69,10 +72,12 @@ protected void configure() { addEnv("DB2INST1_PASSWORD", password); // These settings help the DB2 container start faster - if (!getEnvMap().containsKey("AUTOCONFIG")) + if (!getEnvMap().containsKey("AUTOCONFIG")) { addEnv("AUTOCONFIG", "false"); - if (!getEnvMap().containsKey("ARCHIVE_LOGS")) + } + if (!getEnvMap().containsKey("ARCHIVE_LOGS")) { addEnv("ARCHIVE_LOGS", "false"); + } } /** @@ -92,8 +97,7 @@ public String getDriverClassName() { @Override public String getJdbcUrl() { String additionalUrlParams = constructUrlParameters(":", ";", ";"); - return "jdbc:db2://" + getHost() + ":" + getMappedPort(DB2_PORT) + - "/" + databaseName + additionalUrlParams; + return "jdbc:db2://" + getHost() + ":" + getMappedPort(DB2_PORT) + "/" + databaseName + additionalUrlParams; } @Override diff --git a/modules/db2/src/main/java/org/testcontainers/containers/Db2ContainerProvider.java b/modules/db2/src/main/java/org/testcontainers/containers/Db2ContainerProvider.java index 618b97c3ca6..42ac21f271b 100644 --- a/modules/db2/src/main/java/org/testcontainers/containers/Db2ContainerProvider.java +++ b/modules/db2/src/main/java/org/testcontainers/containers/Db2ContainerProvider.java @@ -3,6 +3,7 @@ import org.testcontainers.utility.DockerImageName; public class Db2ContainerProvider extends JdbcDatabaseContainerProvider { + @Override public boolean supports(String databaseType) { return databaseType.equals(Db2Container.NAME); diff --git a/modules/db2/src/test/java/org/testcontainers/jdbc/db2/DB2JDBCDriverTest.java b/modules/db2/src/test/java/org/testcontainers/jdbc/db2/DB2JDBCDriverTest.java index 2c87a4a1a65..4a03f824a28 100644 --- a/modules/db2/src/test/java/org/testcontainers/jdbc/db2/DB2JDBCDriverTest.java +++ b/modules/db2/src/test/java/org/testcontainers/jdbc/db2/DB2JDBCDriverTest.java @@ -4,18 +4,18 @@ import org.junit.runners.Parameterized; import org.testcontainers.jdbc.AbstractJDBCDriverTest; +import java.util.Arrays; import java.util.EnumSet; -import static java.util.Arrays.asList; - @RunWith(Parameterized.class) public class DB2JDBCDriverTest extends AbstractJDBCDriverTest { @Parameterized.Parameters(name = "{index} - {0}") public static Iterable data() { - return asList( - new Object[][]{ - {"jdbc:tc:db2://hostname/databasename", EnumSet.noneOf(Options.class)} - }); + return Arrays.asList( + new Object[][] { // + { "jdbc:tc:db2://hostname/databasename", EnumSet.noneOf(Options.class) }, + } + ); } } diff --git a/modules/db2/src/test/java/org/testcontainers/junit/db2/SimpleDb2Test.java b/modules/db2/src/test/java/org/testcontainers/junit/db2/SimpleDb2Test.java index b7a1d5478e8..8f3489eae95 100644 --- a/modules/db2/src/test/java/org/testcontainers/junit/db2/SimpleDb2Test.java +++ b/modules/db2/src/test/java/org/testcontainers/junit/db2/SimpleDb2Test.java @@ -1,24 +1,22 @@ package org.testcontainers.junit.db2; import org.junit.Test; +import org.testcontainers.Db2TestImages; import org.testcontainers.containers.Db2Container; import org.testcontainers.db.AbstractContainerDatabaseTest; import java.sql.ResultSet; import java.sql.SQLException; -import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.Matchers.containsString; import static org.junit.Assert.assertThat; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.testcontainers.Db2TestImages.DB2_IMAGE; public class SimpleDb2Test extends AbstractContainerDatabaseTest { @Test public void testSimple() throws SQLException { - try (Db2Container db2 = new Db2Container(DB2_IMAGE) - .acceptLicense()) { - + try (Db2Container db2 = new Db2Container(Db2TestImages.DB2_IMAGE).acceptLicense()) { db2.start(); ResultSet resultSet = performQuery(db2, "SELECT 1 FROM SYSIBM.SYSDUMMY1"); @@ -30,10 +28,11 @@ public void testSimple() throws SQLException { @Test public void testWithAdditionalUrlParamInJdbcUrl() { - try (Db2Container db2 = new Db2Container(DB2_IMAGE) - .withUrlParam("sslConnection", "false") - .acceptLicense()) { - + try ( + Db2Container db2 = new Db2Container(Db2TestImages.DB2_IMAGE) + .withUrlParam("sslConnection", "false") + .acceptLicense() + ) { db2.start(); String jdbcUrl = db2.getJdbcUrl(); diff --git a/modules/dynalite/src/main/java/org/testcontainers/dynamodb/DynaliteContainer.java b/modules/dynalite/src/main/java/org/testcontainers/dynamodb/DynaliteContainer.java index 8900964a8bb..401fa8782b1 100644 --- a/modules/dynalite/src/main/java/org/testcontainers/dynamodb/DynaliteContainer.java +++ b/modules/dynalite/src/main/java/org/testcontainers/dynamodb/DynaliteContainer.java @@ -15,7 +15,9 @@ public class DynaliteContainer extends GenericContainer { private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("quay.io/testcontainers/dynalite"); + private static final String DEFAULT_TAG = "v1.2.1-1"; + private static final int MAPPED_PORT = 4567; /** @@ -32,13 +34,11 @@ public DynaliteContainer(String dockerImageName) { public DynaliteContainer(final DockerImageName dockerImageName) { super(dockerImageName); - dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); withExposedPorts(MAPPED_PORT); } - /** * Gets a preconfigured {@link AmazonDynamoDB} client object for connecting to this * container. @@ -46,10 +46,11 @@ public DynaliteContainer(final DockerImageName dockerImageName) { * @return preconfigured client */ public AmazonDynamoDB getClient() { - return AmazonDynamoDBClientBuilder.standard() - .withEndpointConfiguration(getEndpointConfiguration()) - .withCredentials(getCredentials()) - .build(); + return AmazonDynamoDBClientBuilder + .standard() + .withEndpointConfiguration(getEndpointConfiguration()) + .withCredentials(getCredentials()) + .build(); } /** @@ -59,9 +60,10 @@ public AmazonDynamoDB getClient() { * @return endpoint configuration */ public AwsClientBuilder.EndpointConfiguration getEndpointConfiguration() { - return new AwsClientBuilder.EndpointConfiguration("http://" + - this.getHost() + ":" + - this.getMappedPort(MAPPED_PORT), null); + return new AwsClientBuilder.EndpointConfiguration( + "http://" + this.getHost() + ":" + this.getMappedPort(MAPPED_PORT), + null + ); } /** @@ -72,6 +74,4 @@ public AwsClientBuilder.EndpointConfiguration getEndpointConfiguration() { public AWSCredentialsProvider getCredentials() { return new AWSStaticCredentialsProvider(new BasicAWSCredentials("dummy", "dummy")); } - - } diff --git a/modules/dynalite/src/test/java/org/testcontainers/dynamodb/DynaliteContainerTest.java b/modules/dynalite/src/test/java/org/testcontainers/dynamodb/DynaliteContainerTest.java index 0b440939139..ced5931de31 100644 --- a/modules/dynalite/src/test/java/org/testcontainers/dynamodb/DynaliteContainerTest.java +++ b/modules/dynalite/src/test/java/org/testcontainers/dynamodb/DynaliteContainerTest.java @@ -18,17 +18,20 @@ public class DynaliteContainerTest { - private static final DockerImageName DYNALITE_IMAGE = DockerImageName.parse("quay.io/testcontainers/dynalite:v1.2.1-1"); + private static final DockerImageName DYNALITE_IMAGE = DockerImageName.parse( + "quay.io/testcontainers/dynalite:v1.2.1-1" + ); @Rule public DynaliteContainer dynamoDB = new DynaliteContainer(DYNALITE_IMAGE); @Test public void simpleTestWithManualClientCreation() { - final AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard() - .withEndpointConfiguration(dynamoDB.getEndpointConfiguration()) - .withCredentials(dynamoDB.getCredentials()) - .build(); + final AmazonDynamoDB client = AmazonDynamoDBClientBuilder + .standard() + .withEndpointConfiguration(dynamoDB.getEndpointConfiguration()) + .withCredentials(dynamoDB.getCredentials()) + .build(); runTest(client); } @@ -42,13 +45,10 @@ public void simpleTestWithProvidedClient() { private void runTest(AmazonDynamoDB client) { CreateTableRequest request = new CreateTableRequest() - .withAttributeDefinitions(new AttributeDefinition( - "Name", ScalarAttributeType.S)) - .withKeySchema(new KeySchemaElement("Name", KeyType.HASH)) - .withProvisionedThroughput(new ProvisionedThroughput( - 10L, 10L)) - .withTableName("foo"); - + .withAttributeDefinitions(new AttributeDefinition("Name", ScalarAttributeType.S)) + .withKeySchema(new KeySchemaElement("Name", KeyType.HASH)) + .withProvisionedThroughput(new ProvisionedThroughput(10L, 10L)) + .withTableName("foo"); client.createTable(request); @@ -56,6 +56,10 @@ private void runTest(AmazonDynamoDB client) { assertNotNull("the description is not null", tableDescription); assertEquals("the table has the right name", "foo", tableDescription.getTableName()); - assertEquals("the name has the right primary key", "Name", tableDescription.getKeySchema().get(0).getAttributeName()); + assertEquals( + "the name has the right primary key", + "Name", + tableDescription.getKeySchema().get(0).getAttributeName() + ); } } diff --git a/modules/elasticsearch/src/main/java/org/testcontainers/elasticsearch/ElasticsearchContainer.java b/modules/elasticsearch/src/main/java/org/testcontainers/elasticsearch/ElasticsearchContainer.java index ac0b367f403..5a64a5e67a2 100644 --- a/modules/elasticsearch/src/main/java/org/testcontainers/elasticsearch/ElasticsearchContainer.java +++ b/modules/elasticsearch/src/main/java/org/testcontainers/elasticsearch/ElasticsearchContainer.java @@ -11,8 +11,6 @@ import org.testcontainers.utility.ComparableVersion; import org.testcontainers.utility.DockerImageName; -import javax.net.ssl.SSLContext; -import javax.net.ssl.TrustManagerFactory; import java.io.ByteArrayInputStream; import java.net.InetSocketAddress; import java.security.KeyStore; @@ -20,6 +18,9 @@ import java.security.cert.CertificateFactory; import java.util.Optional; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManagerFactory; + /** * Represents an elasticsearch docker instance which exposes by default port 9200 and 9300 (transport.tcp.port) * The docker image is by default fetched from docker.elastic.co/elasticsearch/elasticsearch @@ -47,8 +48,13 @@ public class ElasticsearchContainer extends GenericContainer caCertAsBytes = Optional.empty(); + private String certPath = "/usr/share/elasticsearch/config/certs/http_ca.crt"; /** @@ -83,7 +92,6 @@ public ElasticsearchContainer(String dockerImageName) { */ public ElasticsearchContainer(final DockerImageName dockerImageName) { super(dockerImageName); - dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME, DEFAULT_OSS_IMAGE_NAME); this.isOss = dockerImageName.isCompatibleWith(DEFAULT_OSS_IMAGE_NAME); @@ -91,7 +99,8 @@ public ElasticsearchContainer(final DockerImageName dockerImageName) { withNetworkAliases("elasticsearch-" + Base58.randomString(6)); withEnv("discovery.type", "single-node"); addExposedPorts(ELASTICSEARCH_DEFAULT_PORT, ELASTICSEARCH_DEFAULT_TCP_PORT); - this.isAtLeastMajorVersion8 = new ComparableVersion(dockerImageName.getVersionPart()).isGreaterThanOrEqualTo("8.0.0"); + this.isAtLeastMajorVersion8 = + new ComparableVersion(dockerImageName.getVersionPart()).isGreaterThanOrEqualTo("8.0.0"); // regex that // matches 8.0 JSON logging with no whitespace between message field and content // matches 7.x JSON logging with whitespace between message field and content @@ -159,8 +168,9 @@ public SSLContext createSslContextFromCa() { */ public ElasticsearchContainer withPassword(String password) { if (isOss) { - throw new IllegalArgumentException("You can not activate security on Elastic OSS Image. " + - "Please switch to the default distribution"); + throw new IllegalArgumentException( + "You can not activate security on Elastic OSS Image. " + "Please switch to the default distribution" + ); } withEnv("ELASTIC_PASSWORD", password); if (!isAtLeastMajorVersion8) { diff --git a/modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java b/modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java index 5b6b5ce3194..4427163ca56 100644 --- a/modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java +++ b/modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java @@ -25,12 +25,13 @@ import org.testcontainers.utility.DockerImageName; import org.testcontainers.utility.MountableFile; -import javax.net.ssl.SSLHandshakeException; import java.io.IOException; -import static org.hamcrest.CoreMatchers.containsString; -import static org.hamcrest.CoreMatchers.is; +import javax.net.ssl.SSLHandshakeException; + import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; import static org.rnorth.visibleassertions.VisibleAssertions.assertThrows; public class ElasticsearchContainerTest { @@ -39,10 +40,10 @@ public class ElasticsearchContainerTest { * Elasticsearch version which should be used for the Tests */ private static final String ELASTICSEARCH_VERSION = "7.9.2"; - private static final DockerImageName ELASTICSEARCH_IMAGE = - DockerImageName - .parse("docker.elastic.co/elasticsearch/elasticsearch") - .withTag(ELASTICSEARCH_VERSION); + + private static final DockerImageName ELASTICSEARCH_IMAGE = DockerImageName + .parse("docker.elastic.co/elasticsearch/elasticsearch") + .withTag(ELASTICSEARCH_VERSION); /** * Elasticsearch default username, when secured @@ -55,6 +56,7 @@ public class ElasticsearchContainerTest { private static final String ELASTICSEARCH_PASSWORD = "changeme"; private RestClient client = null; + private RestClient anonymousClient = null; @After @@ -74,8 +76,8 @@ public void stopRestClient() throws IOException { @Deprecated // We will remove this test in the future public void elasticsearchDeprecatedCtorTest() throws IOException { // Create the elasticsearch container. - try (ElasticsearchContainer container = new ElasticsearchContainer() - .withEnv("foo", "bar") // dummy env for compiler checking correct generics usage + try ( + ElasticsearchContainer container = new ElasticsearchContainer().withEnv("foo", "bar") // dummy env for compiler checking correct generics usage ) { // Start the container. This step might take some time... container.start(); @@ -96,8 +98,8 @@ public void elasticsearchDeprecatedCtorTest() throws IOException { @Test public void elasticsearchDefaultTest() throws IOException { // Create the elasticsearch container. - try (ElasticsearchContainer container = new ElasticsearchContainer(ELASTICSEARCH_IMAGE) - .withEnv("foo", "bar") // dummy env for compiler checking correct generics usage + try ( + ElasticsearchContainer container = new ElasticsearchContainer(ELASTICSEARCH_IMAGE).withEnv("foo", "bar") // dummy env for compiler checking correct generics usage ) { // Start the container. This step might take some time... container.start(); @@ -117,14 +119,18 @@ public void elasticsearchDefaultTest() throws IOException { @Test public void elasticsearchSecuredTest() throws IOException { - try (ElasticsearchContainer container = new ElasticsearchContainer(ELASTICSEARCH_IMAGE) - .withPassword(ELASTICSEARCH_PASSWORD)) { + try ( + ElasticsearchContainer container = new ElasticsearchContainer(ELASTICSEARCH_IMAGE) + .withPassword(ELASTICSEARCH_PASSWORD) + ) { container.start(); // The cluster should be secured so it must fail when we try to access / without credentials - assertThrows("We should not be able to access / URI with an anonymous client.", + assertThrows( + "We should not be able to access / URI with an anonymous client.", ResponseException.class, - () -> getAnonymousClient(container).performRequest(new Request("GET", "/"))); + () -> getAnonymousClient(container).performRequest(new Request("GET", "/")) + ); // But it should work when we try to access / with the proper login and password Response response = getClient(container).performRequest(new Request("GET", "/")); @@ -146,22 +152,24 @@ public void elasticsearchVersion() throws IOException { @Test public void elasticsearchOssImage() throws IOException { - try (ElasticsearchContainer container = - // ossContainer { - new ElasticsearchContainer( - DockerImageName - .parse("docker.elastic.co/elasticsearch/elasticsearch-oss") - .withTag(ELASTICSEARCH_VERSION) - ) - // } + try ( + // ossContainer { + ElasticsearchContainer container = new ElasticsearchContainer( + DockerImageName + .parse("docker.elastic.co/elasticsearch/elasticsearch-oss") + .withTag(ELASTICSEARCH_VERSION) + ) + // } ) { container.start(); Response response = getClient(container).performRequest(new Request("GET", "/")); assertThat(response.getStatusLine().getStatusCode(), is(200)); // The OSS image does not have any feature under Elastic License - assertThrows("We should not have /_xpack endpoint with an OSS License", + assertThrows( + "We should not have /_xpack endpoint with an OSS License", ResponseException.class, - () -> getClient(container).performRequest(new Request("GET", "/_xpack/"))); + () -> getClient(container).performRequest(new Request("GET", "/_xpack/")) + ); } } @@ -175,12 +183,18 @@ public void restClientClusterHealth() throws IOException { // Do whatever you want with the rest client ... final CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); - credentialsProvider.setCredentials(AuthScope.ANY, - new UsernamePasswordCredentials(ELASTICSEARCH_USERNAME, ELASTICSEARCH_PASSWORD)); - - client = RestClient.builder(HttpHost.create(container.getHttpHostAddress())) - .setHttpClientConfigCallback(httpClientBuilder -> httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider)) - .build(); + credentialsProvider.setCredentials( + AuthScope.ANY, + new UsernamePasswordCredentials(ELASTICSEARCH_USERNAME, ELASTICSEARCH_PASSWORD) + ); + + client = + RestClient + .builder(HttpHost.create(container.getHttpHostAddress())) + .setHttpClientConfigCallback(httpClientBuilder -> { + return httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider); + }) + .build(); Response response = client.performRequest(new Request("GET", "/_cluster/health")); // }} @@ -195,20 +209,28 @@ public void restClientClusterHealth() throws IOException { public void restClientSecuredClusterHealth() throws IOException { // httpClientSecuredContainer { // Create the elasticsearch container. - try (ElasticsearchContainer container = new ElasticsearchContainer(ELASTICSEARCH_IMAGE) - // With a password - .withPassword(ELASTICSEARCH_PASSWORD)) { + try ( + ElasticsearchContainer container = new ElasticsearchContainer(ELASTICSEARCH_IMAGE) + // With a password + .withPassword(ELASTICSEARCH_PASSWORD) + ) { // Start the container. This step might take some time... container.start(); // Create the secured client. final CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); - credentialsProvider.setCredentials(AuthScope.ANY, - new UsernamePasswordCredentials(ELASTICSEARCH_USERNAME, ELASTICSEARCH_PASSWORD)); - - client = RestClient.builder(HttpHost.create(container.getHttpHostAddress())) - .setHttpClientConfigCallback(httpClientBuilder -> httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider)) - .build(); + credentialsProvider.setCredentials( + AuthScope.ANY, + new UsernamePasswordCredentials(ELASTICSEARCH_USERNAME, ELASTICSEARCH_PASSWORD) + ); + + client = + RestClient + .builder(HttpHost.create(container.getHttpHostAddress())) + .setHttpClientConfigCallback(httpClientBuilder -> { + return httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider); + }) + .build(); Response response = client.performRequest(new Request("GET", "/_cluster/health")); // }} @@ -224,7 +246,7 @@ public void restClientSecuredClusterHealth() throws IOException { public void transportClientClusterHealth() { // transportClientContainer { // Create the elasticsearch container. - try (ElasticsearchContainer container = new ElasticsearchContainer(ELASTICSEARCH_IMAGE)){ + try (ElasticsearchContainer container = new ElasticsearchContainer(ELASTICSEARCH_IMAGE)) { // Start the container. This step might take some time... container.start(); @@ -232,8 +254,10 @@ public void transportClientClusterHealth() { TransportAddress transportAddress = new TransportAddress(container.getTcpHost()); String expectedClusterName = "docker-cluster"; Settings settings = Settings.builder().put("cluster.name", expectedClusterName).build(); - try (TransportClient transportClient = new PreBuiltTransportClient(settings) - .addTransportAddress(transportAddress)) { + try ( + TransportClient transportClient = new PreBuiltTransportClient(settings) + .addTransportAddress(transportAddress) + ) { ClusterHealthResponse healths = transportClient.admin().cluster().prepareHealth().get(); String clusterName = healths.getClusterName(); // }}} @@ -247,19 +271,27 @@ public void transportClientClusterHealth() { @Test public void incompatibleSettingsTest() { // The OSS image can not use security feature - assertThrows("We should not be able to activate security with an OSS License", + assertThrows( + "We should not be able to activate security with an OSS License", IllegalArgumentException.class, - () -> new ElasticsearchContainer( - DockerImageName - .parse("docker.elastic.co/elasticsearch/elasticsearch-oss") - .withTag(ELASTICSEARCH_VERSION)) - .withPassword("foo") + () -> { + new ElasticsearchContainer( + DockerImageName + .parse("docker.elastic.co/elasticsearch/elasticsearch-oss") + .withTag(ELASTICSEARCH_VERSION) + ) + .withPassword("foo"); + } ); } @Test public void testElasticsearch8SecureByDefault() throws Exception { - try (ElasticsearchContainer container = new ElasticsearchContainer("docker.elastic.co/elasticsearch/elasticsearch:8.1.2")) { + try ( + ElasticsearchContainer container = new ElasticsearchContainer( + "docker.elastic.co/elasticsearch/elasticsearch:8.1.2" + ) + ) { // Start the container. This step might take some time... container.start(); @@ -273,29 +305,40 @@ public void testElasticsearch8SecureByDefault() throws Exception { public void testElasticsearch8SecureByDefaultCustomCaCertFails() throws Exception { final MountableFile mountableFile = MountableFile.forClasspathResource("http_ca.crt"); String caPath = "/tmp/http_ca.crt"; - try (ElasticsearchContainer container = new ElasticsearchContainer("docker.elastic.co/elasticsearch/elasticsearch:8.1.2") - .withCopyToContainer(mountableFile, caPath) - .withCertPath(caPath)) { - + try ( + ElasticsearchContainer container = new ElasticsearchContainer( + "docker.elastic.co/elasticsearch/elasticsearch:8.1.2" + ) + .withCopyToContainer(mountableFile, caPath) + .withCertPath(caPath) + ) { container.start(); // this is expected, as a different cert is used for creating the SSL context - assertThrows("PKIX path validation failed: java.security.cert.CertPathValidatorException: Path does not chain with any of the trust anchors", SSLHandshakeException.class, () -> getClusterHealth(container)); + assertThrows( + "PKIX path validation failed: java.security.cert.CertPathValidatorException: Path does not chain with any of the trust anchors", + SSLHandshakeException.class, + () -> getClusterHealth(container) + ); } } @Test public void testElasticsearch8SecureByDefaultHttpWaitStrategy() throws Exception { - final HttpWaitStrategy httpsWaitStrategy = Wait.forHttps("/") + final HttpWaitStrategy httpsWaitStrategy = Wait + .forHttps("/") .forPort(9200) .forStatusCode(200) .withBasicCredentials(ELASTICSEARCH_USERNAME, ELASTICSEARCH_PASSWORD) // trusting self-signed certificate .allowInsecure(); - try (ElasticsearchContainer container = new ElasticsearchContainer("docker.elastic.co/elasticsearch/elasticsearch:8.1.2") - .waitingFor(httpsWaitStrategy)) { - + try ( + ElasticsearchContainer container = new ElasticsearchContainer( + "docker.elastic.co/elasticsearch/elasticsearch:8.1.2" + ) + .waitingFor(httpsWaitStrategy) + ) { // Start the container. This step might take some time... container.start(); @@ -311,7 +354,9 @@ public void testElasticsearch8SecureByDefaultFailsSilentlyOnLatestImages() throw // even though the version might be older than version 8 // this tags an old 7.x version as :latest tagImage("docker.elastic.co/elasticsearch/elasticsearch:7.9.2", "elasticsearch-tc-older-release", "latest"); - DockerImageName image = DockerImageName.parse("elasticsearch-tc-older-release:latest").asCompatibleSubstituteFor("docker.elastic.co/elasticsearch/elasticsearch"); + DockerImageName image = DockerImageName + .parse("elasticsearch-tc-older-release:latest") + .asCompatibleSubstituteFor("docker.elastic.co/elasticsearch/elasticsearch"); try (ElasticsearchContainer container = new ElasticsearchContainer(image)) { container.start(); @@ -324,27 +369,31 @@ public void testElasticsearch8SecureByDefaultFailsSilentlyOnLatestImages() throw private void tagImage(String sourceImage, String targetImage, String targetTag) throws InterruptedException { DockerClient dockerClient = DockerClientFactory.instance().client(); - dockerClient.tagImageCmd( - new RemoteDockerImage(DockerImageName.parse(sourceImage)).get(), - targetImage, - targetTag - ) + dockerClient + .tagImageCmd(new RemoteDockerImage(DockerImageName.parse(sourceImage)).get(), targetImage, targetTag) .exec(); } private Response getClusterHealth(ElasticsearchContainer container) throws IOException { // Create the secured client. final CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); - credentialsProvider.setCredentials(AuthScope.ANY, - new UsernamePasswordCredentials(ELASTICSEARCH_USERNAME, ElasticsearchContainer.ELASTICSEARCH_DEFAULT_PASSWORD)); + credentialsProvider.setCredentials( + AuthScope.ANY, + new UsernamePasswordCredentials( + ELASTICSEARCH_USERNAME, + ElasticsearchContainer.ELASTICSEARCH_DEFAULT_PASSWORD + ) + ); - client = RestClient.builder(HttpHost.create("https://" + container.getHttpHostAddress())) - .setHttpClientConfigCallback(httpClientBuilder -> { - httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider); - httpClientBuilder.setSSLContext(container.createSslContextFromCa()); - return httpClientBuilder; - }) - .build(); + client = + RestClient + .builder(HttpHost.create("https://" + container.getHttpHostAddress())) + .setHttpClientConfigCallback(httpClientBuilder -> { + httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider); + httpClientBuilder.setSSLContext(container.createSslContextFromCa()); + return httpClientBuilder; + }) + .build(); return client.performRequest(new Request("GET", "/_cluster/health")); } @@ -352,12 +401,18 @@ private Response getClusterHealth(ElasticsearchContainer container) throws IOExc private RestClient getClient(ElasticsearchContainer container) { if (client == null) { final CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); - credentialsProvider.setCredentials(AuthScope.ANY, - new UsernamePasswordCredentials(ELASTICSEARCH_USERNAME, ELASTICSEARCH_PASSWORD)); - - client = RestClient.builder(HttpHost.create(container.getHttpHostAddress())) - .setHttpClientConfigCallback(httpClientBuilder -> httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider)) - .build(); + credentialsProvider.setCredentials( + AuthScope.ANY, + new UsernamePasswordCredentials(ELASTICSEARCH_USERNAME, ELASTICSEARCH_PASSWORD) + ); + + client = + RestClient + .builder(HttpHost.create(container.getHttpHostAddress())) + .setHttpClientConfigCallback(httpClientBuilder -> { + return httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider); + }) + .build(); } return client; diff --git a/modules/gcloud/src/main/java/org/testcontainers/containers/BigtableEmulatorContainer.java b/modules/gcloud/src/main/java/org/testcontainers/containers/BigtableEmulatorContainer.java index 4b81e3964ce..84515fc7244 100644 --- a/modules/gcloud/src/main/java/org/testcontainers/containers/BigtableEmulatorContainer.java +++ b/modules/gcloud/src/main/java/org/testcontainers/containers/BigtableEmulatorContainer.java @@ -13,19 +13,20 @@ */ public class BigtableEmulatorContainer extends GenericContainer { - private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("gcr.io/google.com/cloudsdktool/cloud-sdk"); + private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse( + "gcr.io/google.com/cloudsdktool/cloud-sdk" + ); private static final String CMD = "gcloud beta emulators bigtable start --host-port 0.0.0.0:9000"; + private static final int PORT = 9000; public BigtableEmulatorContainer(final DockerImageName dockerImageName) { super(dockerImageName); - dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); withExposedPorts(PORT); - setWaitStrategy(new LogMessageWaitStrategy() - .withRegEx("(?s).*running.*$")); + setWaitStrategy(new LogMessageWaitStrategy().withRegEx("(?s).*running.*$")); withCommand("/bin/sh", "-c", CMD); } diff --git a/modules/gcloud/src/main/java/org/testcontainers/containers/DatastoreEmulatorContainer.java b/modules/gcloud/src/main/java/org/testcontainers/containers/DatastoreEmulatorContainer.java index 58e5c023a0e..b0716829875 100644 --- a/modules/gcloud/src/main/java/org/testcontainers/containers/DatastoreEmulatorContainer.java +++ b/modules/gcloud/src/main/java/org/testcontainers/containers/DatastoreEmulatorContainer.java @@ -12,14 +12,17 @@ */ public class DatastoreEmulatorContainer extends GenericContainer { - private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("gcr.io/google.com/cloudsdktool/cloud-sdk"); + private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse( + "gcr.io/google.com/cloudsdktool/cloud-sdk" + ); + + private static final String CMD = + "gcloud beta emulators datastore start --project test-project --host-port 0.0.0.0:8081"; - private static final String CMD = "gcloud beta emulators datastore start --project test-project --host-port 0.0.0.0:8081"; private static final int HTTP_PORT = 8081; public DatastoreEmulatorContainer(final DockerImageName dockerImageName) { super(dockerImageName); - dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); withExposedPorts(HTTP_PORT); diff --git a/modules/gcloud/src/main/java/org/testcontainers/containers/FirestoreEmulatorContainer.java b/modules/gcloud/src/main/java/org/testcontainers/containers/FirestoreEmulatorContainer.java index c36082dfeb6..a04c1e5831f 100644 --- a/modules/gcloud/src/main/java/org/testcontainers/containers/FirestoreEmulatorContainer.java +++ b/modules/gcloud/src/main/java/org/testcontainers/containers/FirestoreEmulatorContainer.java @@ -12,19 +12,20 @@ */ public class FirestoreEmulatorContainer extends GenericContainer { - private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("gcr.io/google.com/cloudsdktool/cloud-sdk"); + private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse( + "gcr.io/google.com/cloudsdktool/cloud-sdk" + ); private static final String CMD = "gcloud beta emulators firestore start --host-port 0.0.0.0:8080"; + private static final int PORT = 8080; public FirestoreEmulatorContainer(final DockerImageName dockerImageName) { super(dockerImageName); - dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); withExposedPorts(PORT); - setWaitStrategy(new LogMessageWaitStrategy() - .withRegEx("(?s).*running.*$")); + setWaitStrategy(new LogMessageWaitStrategy().withRegEx("(?s).*running.*$")); withCommand("/bin/sh", "-c", CMD); } diff --git a/modules/gcloud/src/main/java/org/testcontainers/containers/PubSubEmulatorContainer.java b/modules/gcloud/src/main/java/org/testcontainers/containers/PubSubEmulatorContainer.java index 231f7626044..8d9f76e44ae 100644 --- a/modules/gcloud/src/main/java/org/testcontainers/containers/PubSubEmulatorContainer.java +++ b/modules/gcloud/src/main/java/org/testcontainers/containers/PubSubEmulatorContainer.java @@ -12,19 +12,20 @@ */ public class PubSubEmulatorContainer extends GenericContainer { - private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("gcr.io/google.com/cloudsdktool/cloud-sdk"); + private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse( + "gcr.io/google.com/cloudsdktool/cloud-sdk" + ); private static final String CMD = "gcloud beta emulators pubsub start --host-port 0.0.0.0:8085"; + private static final int PORT = 8085; public PubSubEmulatorContainer(final DockerImageName dockerImageName) { super(dockerImageName); - dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); withExposedPorts(8085); - setWaitStrategy(new LogMessageWaitStrategy() - .withRegEx("(?s).*started.*$")); + setWaitStrategy(new LogMessageWaitStrategy().withRegEx("(?s).*started.*$")); withCommand("/bin/sh", "-c", CMD); } diff --git a/modules/gcloud/src/main/java/org/testcontainers/containers/SpannerEmulatorContainer.java b/modules/gcloud/src/main/java/org/testcontainers/containers/SpannerEmulatorContainer.java index 0ae8e89dc06..dc45b004c6c 100644 --- a/modules/gcloud/src/main/java/org/testcontainers/containers/SpannerEmulatorContainer.java +++ b/modules/gcloud/src/main/java/org/testcontainers/containers/SpannerEmulatorContainer.java @@ -10,19 +10,20 @@ */ public class SpannerEmulatorContainer extends GenericContainer { - private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("gcr.io/cloud-spanner-emulator/emulator"); + private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse( + "gcr.io/cloud-spanner-emulator/emulator" + ); private static final int GRPC_PORT = 9010; + private static final int HTTP_PORT = 9020; public SpannerEmulatorContainer(final DockerImageName dockerImageName) { super(dockerImageName); - dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); withExposedPorts(GRPC_PORT, HTTP_PORT); - setWaitStrategy(new LogMessageWaitStrategy() - .withRegEx(".*Cloud Spanner emulator running\\..*")); + setWaitStrategy(new LogMessageWaitStrategy().withRegEx(".*Cloud Spanner emulator running\\..*")); } /** diff --git a/modules/gcloud/src/test/java/org/testcontainers/containers/BigtableEmulatorContainerTest.java b/modules/gcloud/src/test/java/org/testcontainers/containers/BigtableEmulatorContainerTest.java index 4ba59a75488..3e22231220b 100644 --- a/modules/gcloud/src/test/java/org/testcontainers/containers/BigtableEmulatorContainerTest.java +++ b/modules/gcloud/src/test/java/org/testcontainers/containers/BigtableEmulatorContainerTest.java @@ -1,22 +1,13 @@ package org.testcontainers.containers; -import static org.assertj.core.api.Assertions.assertThat; - -import com.google.api.core.ApiFuture; -import com.google.api.gax.batching.Batcher; import com.google.api.gax.core.CredentialsProvider; import com.google.api.gax.core.NoCredentialsProvider; import com.google.api.gax.grpc.GrpcTransportChannel; import com.google.api.gax.rpc.FixedTransportChannelProvider; import com.google.api.gax.rpc.TransportChannelProvider; -import com.google.cloud.bigtable.admin.v2.BigtableInstanceAdminClient; import com.google.cloud.bigtable.admin.v2.BigtableTableAdminClient; -import com.google.cloud.bigtable.admin.v2.models.CreateInstanceRequest; import com.google.cloud.bigtable.admin.v2.models.CreateTableRequest; -import com.google.cloud.bigtable.admin.v2.models.Instance; import com.google.cloud.bigtable.admin.v2.models.Table; -import com.google.cloud.bigtable.admin.v2.stub.BigtableInstanceAdminStub; -import com.google.cloud.bigtable.admin.v2.stub.BigtableInstanceAdminStubSettings; import com.google.cloud.bigtable.admin.v2.stub.BigtableTableAdminStubSettings; import com.google.cloud.bigtable.admin.v2.stub.EnhancedBigtableTableAdminStub; import com.google.cloud.bigtable.data.v2.BigtableDataClient; @@ -24,20 +15,22 @@ import com.google.cloud.bigtable.data.v2.models.Row; import com.google.cloud.bigtable.data.v2.models.RowCell; import com.google.cloud.bigtable.data.v2.models.RowMutation; -import com.google.cloud.bigtable.data.v2.models.RowMutationEntry; import io.grpc.ManagedChannel; import io.grpc.ManagedChannelBuilder; -import java.io.IOException; -import java.util.List; -import java.util.UUID; -import java.util.concurrent.ExecutionException; import org.junit.Rule; import org.junit.Test; import org.testcontainers.utility.DockerImageName; +import java.io.IOException; +import java.util.List; +import java.util.concurrent.ExecutionException; + +import static org.assertj.core.api.Assertions.assertThat; + public class BigtableEmulatorContainerTest { public static final String PROJECT_ID = "test-project"; + public static final String INSTANCE_ID = "test-instance"; @Rule @@ -45,28 +38,31 @@ public class BigtableEmulatorContainerTest { public BigtableEmulatorContainer emulator = new BigtableEmulatorContainer( DockerImageName.parse("gcr.io/google.com/cloudsdktool/cloud-sdk:367.0.0-emulators") ); + // } @Test // testWithEmulatorContainer { public void testSimple() throws IOException, InterruptedException, ExecutionException { - ManagedChannel channel = ManagedChannelBuilder.forTarget(emulator.getEmulatorEndpoint()) - .usePlaintext().build(); + ManagedChannel channel = ManagedChannelBuilder.forTarget(emulator.getEmulatorEndpoint()).usePlaintext().build(); - TransportChannelProvider channelProvider = FixedTransportChannelProvider.create(GrpcTransportChannel.create(channel)); + TransportChannelProvider channelProvider = FixedTransportChannelProvider.create( + GrpcTransportChannel.create(channel) + ); NoCredentialsProvider credentialsProvider = NoCredentialsProvider.create(); try { - createTable(channelProvider, credentialsProvider, "test-table" ); + createTable(channelProvider, credentialsProvider, "test-table"); - BigtableDataClient client = BigtableDataClient.create(BigtableDataSettings - .newBuilderForEmulator(emulator.getHost(), emulator.getEmulatorPort()) - .setProjectId(PROJECT_ID) - .setInstanceId(INSTANCE_ID) - .build()); + BigtableDataClient client = BigtableDataClient.create( + BigtableDataSettings + .newBuilderForEmulator(emulator.getHost(), emulator.getEmulatorPort()) + .setProjectId(PROJECT_ID) + .setInstanceId(INSTANCE_ID) + .build() + ); - client.mutateRow(RowMutation.create("test-table", "1") - .setCell("name", "firstName", "Ray")); + client.mutateRow(RowMutation.create("test-table", "1").setCell("name", "firstName", "Ray")); Row row = client.readRow("test-table", "1"); List cells = row.getCells("name", "firstName"); @@ -77,15 +73,22 @@ public void testSimple() throws IOException, InterruptedException, ExecutionExce channel.shutdown(); } } + // } // createTable { - private void createTable(TransportChannelProvider channelProvider, CredentialsProvider credentialsProvider, String tableName) throws IOException { - EnhancedBigtableTableAdminStub stub = EnhancedBigtableTableAdminStub - .createEnhanced(BigtableTableAdminStubSettings.newBuilder() + private void createTable( + TransportChannelProvider channelProvider, + CredentialsProvider credentialsProvider, + String tableName + ) throws IOException { + EnhancedBigtableTableAdminStub stub = EnhancedBigtableTableAdminStub.createEnhanced( + BigtableTableAdminStubSettings + .newBuilder() .setTransportChannelProvider(channelProvider) .setCredentialsProvider(credentialsProvider) - .build()); + .build() + ); try (BigtableTableAdminClient client = BigtableTableAdminClient.create(PROJECT_ID, INSTANCE_ID, stub)) { Table table = client.createTable(CreateTableRequest.of(tableName).addFamily("name")); diff --git a/modules/gcloud/src/test/java/org/testcontainers/containers/DatastoreEmulatorContainerTest.java b/modules/gcloud/src/test/java/org/testcontainers/containers/DatastoreEmulatorContainerTest.java index ba998e402ce..a7fe340791d 100644 --- a/modules/gcloud/src/test/java/org/testcontainers/containers/DatastoreEmulatorContainerTest.java +++ b/modules/gcloud/src/test/java/org/testcontainers/containers/DatastoreEmulatorContainerTest.java @@ -1,7 +1,5 @@ package org.testcontainers.containers; -import static org.assertj.core.api.Assertions.assertThat; - import com.google.cloud.NoCredentials; import com.google.cloud.ServiceOptions; import com.google.cloud.datastore.Datastore; @@ -12,6 +10,8 @@ import org.junit.Test; import org.testcontainers.utility.DockerImageName; +import static org.assertj.core.api.Assertions.assertThat; + public class DatastoreEmulatorContainerTest { @Rule @@ -19,17 +19,19 @@ public class DatastoreEmulatorContainerTest { public DatastoreEmulatorContainer emulator = new DatastoreEmulatorContainer( DockerImageName.parse("gcr.io/google.com/cloudsdktool/cloud-sdk:367.0.0-emulators") ); + // } // startingDatastoreEmulatorContainer { @Test public void testSimple() { - DatastoreOptions options = DatastoreOptions.newBuilder() - .setHost(emulator.getEmulatorEndpoint()) - .setCredentials(NoCredentials.getInstance()) - .setRetrySettings(ServiceOptions.getNoRetrySettings()) - .setProjectId("test-project") - .build(); + DatastoreOptions options = DatastoreOptions + .newBuilder() + .setHost(emulator.getEmulatorEndpoint()) + .setCredentials(NoCredentials.getInstance()) + .setRetrySettings(ServiceOptions.getNoRetrySettings()) + .setProjectId("test-project") + .build(); Datastore datastore = options.getService(); Key key = datastore.newKeyFactory().setKind("Task").newKey("sample"); diff --git a/modules/gcloud/src/test/java/org/testcontainers/containers/FirestoreEmulatorContainerTest.java b/modules/gcloud/src/test/java/org/testcontainers/containers/FirestoreEmulatorContainerTest.java index 04939ecc30e..c87f8e5176a 100644 --- a/modules/gcloud/src/test/java/org/testcontainers/containers/FirestoreEmulatorContainerTest.java +++ b/modules/gcloud/src/test/java/org/testcontainers/containers/FirestoreEmulatorContainerTest.java @@ -1,7 +1,5 @@ package org.testcontainers.containers; -import static org.assertj.core.api.Assertions.assertThat; - import com.google.api.core.ApiFuture; import com.google.cloud.NoCredentials; import com.google.cloud.firestore.CollectionReference; @@ -10,13 +8,16 @@ import com.google.cloud.firestore.FirestoreOptions; import com.google.cloud.firestore.QuerySnapshot; import com.google.cloud.firestore.WriteResult; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.ExecutionException; import org.junit.Rule; import org.junit.Test; import org.testcontainers.utility.DockerImageName; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ExecutionException; + +import static org.assertj.core.api.Assertions.assertThat; + public class FirestoreEmulatorContainerTest { @Rule @@ -24,16 +25,19 @@ public class FirestoreEmulatorContainerTest { public FirestoreEmulatorContainer emulator = new FirestoreEmulatorContainer( DockerImageName.parse("gcr.io/google.com/cloudsdktool/cloud-sdk:367.0.0-emulators") ); + // } // testWithEmulatorContainer { @Test public void testSimple() throws ExecutionException, InterruptedException { - FirestoreOptions options = FirestoreOptions.getDefaultInstance().toBuilder() - .setHost(emulator.getEmulatorEndpoint()) - .setCredentials(NoCredentials.getInstance()) - .setProjectId("test-project") - .build(); + FirestoreOptions options = FirestoreOptions + .getDefaultInstance() + .toBuilder() + .setHost(emulator.getEmulatorEndpoint()) + .setCredentials(NoCredentials.getInstance()) + .setProjectId("test-project") + .build(); Firestore firestore = options.getService(); CollectionReference users = firestore.collection("users"); diff --git a/modules/gcloud/src/test/java/org/testcontainers/containers/PubSubEmulatorContainerTest.java b/modules/gcloud/src/test/java/org/testcontainers/containers/PubSubEmulatorContainerTest.java index 61d56a279d7..aeb92bc4a69 100644 --- a/modules/gcloud/src/test/java/org/testcontainers/containers/PubSubEmulatorContainerTest.java +++ b/modules/gcloud/src/test/java/org/testcontainers/containers/PubSubEmulatorContainerTest.java @@ -1,7 +1,5 @@ package org.testcontainers.containers; -import static org.assertj.core.api.Assertions.assertThat; - import com.google.api.gax.core.NoCredentialsProvider; import com.google.api.gax.grpc.GrpcTransportChannel; import com.google.api.gax.rpc.FixedTransportChannelProvider; @@ -24,11 +22,14 @@ import com.google.pubsub.v1.TopicName; import io.grpc.ManagedChannel; import io.grpc.ManagedChannelBuilder; -import java.io.IOException; import org.junit.Rule; import org.junit.Test; import org.testcontainers.utility.DockerImageName; +import java.io.IOException; + +import static org.assertj.core.api.Assertions.assertThat; + public class PubSubEmulatorContainerTest { public static final String PROJECT_ID = "my-project-id"; @@ -38,8 +39,8 @@ public class PubSubEmulatorContainerTest { public PubSubEmulatorContainer emulator = new PubSubEmulatorContainer( DockerImageName.parse("gcr.io/google.com/cloudsdktool/cloud-sdk:367.0.0-emulators") ); - // } + // } @Test // testWithEmulatorContainer { @@ -47,8 +48,9 @@ public void testSimple() throws IOException { String hostport = emulator.getEmulatorEndpoint(); ManagedChannel channel = ManagedChannelBuilder.forTarget(hostport).usePlaintext().build(); try { - TransportChannelProvider channelProvider = - FixedTransportChannelProvider.create(GrpcTransportChannel.create(channel)); + TransportChannelProvider channelProvider = FixedTransportChannelProvider.create( + GrpcTransportChannel.create(channel) + ); NoCredentialsProvider credentialsProvider = NoCredentialsProvider.create(); String topicId = "my-topic-id"; @@ -57,56 +59,77 @@ public void testSimple() throws IOException { String subscriptionId = "my-subscription-id"; createSubscription(subscriptionId, topicId, channelProvider, credentialsProvider); - Publisher publisher = Publisher.newBuilder(TopicName.of(PROJECT_ID, topicId)) - .setChannelProvider(channelProvider) - .setCredentialsProvider(credentialsProvider) - .build(); + Publisher publisher = Publisher + .newBuilder(TopicName.of(PROJECT_ID, topicId)) + .setChannelProvider(channelProvider) + .setCredentialsProvider(credentialsProvider) + .build(); PubsubMessage message = PubsubMessage.newBuilder().setData(ByteString.copyFromUtf8("test message")).build(); publisher.publish(message); - SubscriberStubSettings subscriberStubSettings = - SubscriberStubSettings.newBuilder() - .setTransportChannelProvider(channelProvider) - .setCredentialsProvider(credentialsProvider) - .build(); + SubscriberStubSettings subscriberStubSettings = SubscriberStubSettings + .newBuilder() + .setTransportChannelProvider(channelProvider) + .setCredentialsProvider(credentialsProvider) + .build(); try (SubscriberStub subscriber = GrpcSubscriberStub.create(subscriberStubSettings)) { - PullRequest pullRequest = PullRequest.newBuilder() - .setMaxMessages(1) - .setSubscription(ProjectSubscriptionName.format(PROJECT_ID, subscriptionId)) - .build(); + PullRequest pullRequest = PullRequest + .newBuilder() + .setMaxMessages(1) + .setSubscription(ProjectSubscriptionName.format(PROJECT_ID, subscriptionId)) + .build(); PullResponse pullResponse = subscriber.pullCallable().call(pullRequest); assertThat(pullResponse.getReceivedMessagesList()).hasSize(1); - assertThat(pullResponse.getReceivedMessages(0).getMessage().getData().toStringUtf8()).isEqualTo("test message"); + assertThat(pullResponse.getReceivedMessages(0).getMessage().getData().toStringUtf8()) + .isEqualTo("test message"); } } finally { channel.shutdown(); } } + // } // createTopic { - private void createTopic(String topicId, TransportChannelProvider channelProvider, NoCredentialsProvider credentialsProvider) throws IOException { - TopicAdminSettings topicAdminSettings = TopicAdminSettings.newBuilder() - .setTransportChannelProvider(channelProvider) - .setCredentialsProvider(credentialsProvider) - .build(); + private void createTopic( + String topicId, + TransportChannelProvider channelProvider, + NoCredentialsProvider credentialsProvider + ) throws IOException { + TopicAdminSettings topicAdminSettings = TopicAdminSettings + .newBuilder() + .setTransportChannelProvider(channelProvider) + .setCredentialsProvider(credentialsProvider) + .build(); try (TopicAdminClient topicAdminClient = TopicAdminClient.create(topicAdminSettings)) { TopicName topicName = TopicName.of(PROJECT_ID, topicId); topicAdminClient.createTopic(topicName); } } + // } // createSubscription { - private void createSubscription(String subscriptionId, String topicId, TransportChannelProvider channelProvider, NoCredentialsProvider credentialsProvider) throws IOException { - SubscriptionAdminSettings subscriptionAdminSettings = SubscriptionAdminSettings.newBuilder() - .setTransportChannelProvider(channelProvider) - .setCredentialsProvider(credentialsProvider) - .build(); + private void createSubscription( + String subscriptionId, + String topicId, + TransportChannelProvider channelProvider, + NoCredentialsProvider credentialsProvider + ) throws IOException { + SubscriptionAdminSettings subscriptionAdminSettings = SubscriptionAdminSettings + .newBuilder() + .setTransportChannelProvider(channelProvider) + .setCredentialsProvider(credentialsProvider) + .build(); SubscriptionAdminClient subscriptionAdminClient = SubscriptionAdminClient.create(subscriptionAdminSettings); SubscriptionName subscriptionName = SubscriptionName.of(PROJECT_ID, subscriptionId); - subscriptionAdminClient.createSubscription(subscriptionName, TopicName.of(PROJECT_ID, topicId), PushConfig.getDefaultInstance(), 10); + subscriptionAdminClient.createSubscription( + subscriptionName, + TopicName.of(PROJECT_ID, topicId), + PushConfig.getDefaultInstance(), + 10 + ); } // } diff --git a/modules/gcloud/src/test/java/org/testcontainers/containers/SpannerEmulatorContainerTest.java b/modules/gcloud/src/test/java/org/testcontainers/containers/SpannerEmulatorContainerTest.java index 64b81546499..b246e7e1f0d 100644 --- a/modules/gcloud/src/test/java/org/testcontainers/containers/SpannerEmulatorContainerTest.java +++ b/modules/gcloud/src/test/java/org/testcontainers/containers/SpannerEmulatorContainerTest.java @@ -1,7 +1,5 @@ package org.testcontainers.containers; -import static org.assertj.core.api.Assertions.assertThat; - import com.google.cloud.NoCredentials; import com.google.cloud.spanner.Database; import com.google.cloud.spanner.DatabaseAdminClient; @@ -16,12 +14,15 @@ import com.google.cloud.spanner.Spanner; import com.google.cloud.spanner.SpannerOptions; import com.google.cloud.spanner.Statement; -import java.util.Arrays; -import java.util.concurrent.ExecutionException; import org.junit.Rule; import org.junit.Test; import org.testcontainers.utility.DockerImageName; +import java.util.Arrays; +import java.util.concurrent.ExecutionException; + +import static org.assertj.core.api.Assertions.assertThat; + public class SpannerEmulatorContainerTest { @Rule @@ -29,20 +30,24 @@ public class SpannerEmulatorContainerTest { public SpannerEmulatorContainer emulator = new SpannerEmulatorContainer( DockerImageName.parse("gcr.io/cloud-spanner-emulator/emulator:1.4.0") ); + // } private static final String PROJECT_NAME = "test-project"; + private static final String INSTANCE_NAME = "test-instance"; + private static final String DATABASE_NAME = "test-database"; // testWithEmulatorContainer { @Test public void testSimple() throws ExecutionException, InterruptedException { - SpannerOptions options = SpannerOptions.newBuilder() - .setEmulatorHost(emulator.getEmulatorGrpcEndpoint()) - .setCredentials(NoCredentials.getInstance()) - .setProjectId(PROJECT_NAME) - .build(); + SpannerOptions options = SpannerOptions + .newBuilder() + .setEmulatorHost(emulator.getEmulatorGrpcEndpoint()) + .setCredentials(NoCredentials.getInstance()) + .setProjectId(PROJECT_NAME) + .build(); Spanner spanner = options.getService(); @@ -52,28 +57,38 @@ public void testSimple() throws ExecutionException, InterruptedException { DatabaseId databaseId = DatabaseId.of(instanceId, DATABASE_NAME); DatabaseClient dbClient = spanner.getDatabaseClient(databaseId); - dbClient.readWriteTransaction() - .run(tx -> { - String sql1 = "Delete from TestTable where 1=1"; - tx.executeUpdate(Statement.of(sql1)); - String sql = "INSERT INTO TestTable (Key, Value) VALUES (1, 'Java'), (2, 'Go')"; - tx.executeUpdate(Statement.of(sql)); - return null; - }); - - ResultSet resultSet = dbClient.readOnlyTransaction() - .executeQuery(Statement.of("select * from TestTable order by Key")); + dbClient + .readWriteTransaction() + .run(tx -> { + String sql1 = "Delete from TestTable where 1=1"; + tx.executeUpdate(Statement.of(sql1)); + String sql = "INSERT INTO TestTable (Key, Value) VALUES (1, 'Java'), (2, 'Go')"; + tx.executeUpdate(Statement.of(sql)); + return null; + }); + + ResultSet resultSet = dbClient + .readOnlyTransaction() + .executeQuery(Statement.of("select * from TestTable order by Key")); resultSet.next(); assertThat(resultSet.getLong(0)).isEqualTo(1); assertThat(resultSet.getString(1)).isEqualTo("Java"); } + // } // createDatabase { private void createDatabase(Spanner spanner) throws InterruptedException, ExecutionException { DatabaseAdminClient dbAdminClient = spanner.getDatabaseAdminClient(); - Database database = dbAdminClient.createDatabase(INSTANCE_NAME, DATABASE_NAME, Arrays.asList("CREATE TABLE TestTable (Key INT64, Value STRING(MAX)) PRIMARY KEY (Key)")).get(); + Database database = dbAdminClient + .createDatabase( + INSTANCE_NAME, + DATABASE_NAME, + Arrays.asList("CREATE TABLE TestTable (Key INT64, Value STRING(MAX)) PRIMARY KEY (Key)") + ) + .get(); } + // } // createInstance { @@ -81,7 +96,16 @@ private InstanceId createInstance(Spanner spanner) throws InterruptedException, InstanceConfigId instanceConfig = InstanceConfigId.of(PROJECT_NAME, "emulator-config"); InstanceId instanceId = InstanceId.of(PROJECT_NAME, INSTANCE_NAME); InstanceAdminClient insAdminClient = spanner.getInstanceAdminClient(); - Instance instance = insAdminClient.createInstance(InstanceInfo.newBuilder(instanceId).setNodeCount(1).setDisplayName("Test instance").setInstanceConfigId(instanceConfig).build()).get(); + Instance instance = insAdminClient + .createInstance( + InstanceInfo + .newBuilder(instanceId) + .setNodeCount(1) + .setDisplayName("Test instance") + .setInstanceConfigId(instanceConfig) + .build() + ) + .get(); return instanceId; } // } diff --git a/modules/hivemq/src/main/java/org/testcontainers/hivemq/HiveMQContainer.java b/modules/hivemq/src/main/java/org/testcontainers/hivemq/HiveMQContainer.java index 5bb8935c39e..242cc7bdb43 100644 --- a/modules/hivemq/src/main/java/org/testcontainers/hivemq/HiveMQContainer.java +++ b/modules/hivemq/src/main/java/org/testcontainers/hivemq/HiveMQContainer.java @@ -34,27 +34,38 @@ public class HiveMQContainer extends GenericContainer { private static final Logger LOGGER = LoggerFactory.getLogger(HiveMQContainer.class); private static final DockerImageName DEFAULT_HIVEMQ_EE_IMAGE_NAME = DockerImageName.parse("hivemq/hivemq4"); + private static final DockerImageName DEFAULT_HIVEMQ_CE_IMAGE_NAME = DockerImageName.parse("hivemq/hivemq-ce"); private static final int DEBUGGING_PORT = 9000; + private static final int MQTT_PORT = 1883; + private static final int CONTROL_CENTER_PORT = 8080; + @SuppressWarnings("OctalInteger") private static final int MODE = 0777; - private static final @NotNull Pattern EXTENSION_ID_PATTERN = Pattern.compile("(.+?)"); - private final @NotNull ConcurrentHashMap containerOutputLatches = new ConcurrentHashMap<>(); + @NotNull + private static final Pattern EXTENSION_ID_PATTERN = Pattern.compile("(.+?)"); + + @NotNull + private final ConcurrentHashMap containerOutputLatches = new ConcurrentHashMap<>(); + private boolean controlCenterEnabled = false; + private boolean debugging = false; - private final @NotNull Set prepackagedExtensionsToRemove = new HashSet<>(); + @NotNull + private final Set prepackagedExtensionsToRemove = new HashSet<>(); + private boolean removeAllPrepackagedExtensions = false; - private final @NotNull WaitAllStrategy waitStrategy = new WaitAllStrategy(); + @NotNull + private final WaitAllStrategy waitStrategy = new WaitAllStrategy(); public HiveMQContainer(final @NotNull DockerImageName dockerImageName) { super(dockerImageName); - dockerImageName.assertCompatibleWith(DEFAULT_HIVEMQ_CE_IMAGE_NAME, DEFAULT_HIVEMQ_EE_IMAGE_NAME); addExposedPort(MQTT_PORT); @@ -96,13 +107,14 @@ protected void configure() { withCreateContainerCmdModifier(it -> it.withEntrypoint("/bin/sh")); if (removeAllPrepackagedExtensions || !prepackagedExtensionsToRemove.isEmpty()) { - if (removeAllPrepackagedExtensions) { removeCommand = "rm -rf /opt/hivemq/extensions/** &&"; } else { - removeCommand = prepackagedExtensionsToRemove.stream() - .map(extensionId -> "rm -rf /opt/hivemq/extensions/" + extensionId + "&&") - .collect(Collectors.joining()); + removeCommand = + prepackagedExtensionsToRemove + .stream() + .map(extensionId -> "rm -rf /opt/hivemq/extensions/" + extensionId + "&&") + .collect(Collectors.joining()); } } else { removeCommand = ""; @@ -110,16 +122,20 @@ protected void configure() { setCommand( "-c", removeCommand + - "cp -r '/opt/hivemq/temp-extensions/'* /opt/hivemq/extensions/ " + - "; rm -rf /opt/hivemq/temp-extensions/** " + - "&& chmod -R 777 /opt/hivemq/extensions " + - "&& /opt/docker-entrypoint.sh /opt/hivemq/bin/run.sh" + "cp -r '/opt/hivemq/temp-extensions/'* /opt/hivemq/extensions/ " + + "; rm -rf /opt/hivemq/temp-extensions/** " + + "&& chmod -R 777 /opt/hivemq/extensions " + + "&& /opt/docker-entrypoint.sh /opt/hivemq/bin/run.sh" ); } protected void containerIsStarted(final @NotNull InspectContainerResponse containerInfo) { if (controlCenterEnabled) { - LOGGER.info("The HiveMQ Control Center is reachable under: http://{}:{}", getHost(), getMappedPort(CONTROL_CENTER_PORT)); + LOGGER.info( + "The HiveMQ Control Center is reachable under: http://{}:{}", + getHost(), + getMappedPort(CONTROL_CENTER_PORT) + ); } } @@ -159,7 +175,10 @@ protected void containerIsStarted(final @NotNull InspectContainerResponse contai public @NotNull HiveMQContainer withDebugging() { debugging = true; addExposedPorts(DEBUGGING_PORT); - withEnv("JAVA_OPTS", "-agentlib:jdwp=transport=dt_socket,address=0.0.0.0:" + DEBUGGING_PORT + ",server=y,suspend=y"); + withEnv( + "JAVA_OPTS", + "-agentlib:jdwp=transport=dt_socket,address=0.0.0.0:" + DEBUGGING_PORT + ",server=y,suspend=y" + ); return self(); } @@ -213,10 +232,16 @@ protected void containerIsStarted(final @NotNull InspectContainerResponse contai public @NotNull HiveMQContainer withExtension(final @NotNull MountableFile mountableExtension) { final File extensionDir = new File(mountableExtension.getResolvedPath()); if (!extensionDir.exists()) { - throw new ContainerLaunchException("Extension '" + mountableExtension.getFilesystemPath() + "' could not be mounted. It does not exist."); + throw new ContainerLaunchException( + "Extension '" + mountableExtension.getFilesystemPath() + "' could not be mounted. It does not exist." + ); } if (!extensionDir.isDirectory()) { - throw new ContainerLaunchException("Extension '" + mountableExtension.getFilesystemPath() + "' could not be mounted. It is not a directory."); + throw new ContainerLaunchException( + "Extension '" + + mountableExtension.getFilesystemPath() + + "' could not be mounted. It is not a directory." + ); } try { final String extensionDirName = getExtensionDirectoryName(extensionDir); @@ -277,10 +302,14 @@ protected void containerIsStarted(final @NotNull InspectContainerResponse contai public @NotNull HiveMQContainer withLicense(final @NotNull MountableFile mountableLicense) { final File licenseFile = new File(mountableLicense.getResolvedPath()); if (!licenseFile.exists()) { - throw new ContainerLaunchException("License file '" + mountableLicense.getFilesystemPath() + "' does not exist."); + throw new ContainerLaunchException( + "License file '" + mountableLicense.getFilesystemPath() + "' does not exist." + ); } if (!licenseFile.getName().endsWith(".lic") && !licenseFile.getName().endsWith(".elic")) { - throw new ContainerLaunchException("License file '" + mountableLicense.getFilesystemPath() + "' does not end wit '.lic' or '.elic'."); + throw new ContainerLaunchException( + "License file '" + mountableLicense.getFilesystemPath() + "' does not end wit '.lic' or '.elic'." + ); } final String containerPath = "/opt/hivemq/license/" + licenseFile.getName(); withCopyFileToContainer(cloneWithFileMode(mountableLicense), containerPath); @@ -299,7 +328,9 @@ protected void containerIsStarted(final @NotNull InspectContainerResponse contai public @NotNull HiveMQContainer withHiveMQConfig(final @NotNull MountableFile mountableConfig) { final File config = new File(mountableConfig.getResolvedPath()); if (!config.exists()) { - throw new ContainerLaunchException("HiveMQ config file '" + mountableConfig.getFilesystemPath() + "' does not exist."); + throw new ContainerLaunchException( + "HiveMQ config file '" + mountableConfig.getFilesystemPath() + "' does not exist." + ); } final String containerPath = "/opt/hivemq/conf/config.xml"; withCopyFileToContainer(cloneWithFileMode(mountableConfig), containerPath); @@ -320,8 +351,8 @@ protected void containerIsStarted(final @NotNull InspectContainerResponse contai */ public @NotNull HiveMQContainer withFileInExtensionHomeFolder( final @NotNull MountableFile file, - final @NotNull String extensionId) { - + final @NotNull String extensionId + ) { return withFileInExtensionHomeFolder(file, extensionId, ""); } @@ -340,9 +371,12 @@ protected void containerIsStarted(final @NotNull InspectContainerResponse contai public @NotNull HiveMQContainer withFileInExtensionHomeFolder( final @NotNull MountableFile file, final @NotNull String extensionId, - final @NotNull String pathInExtensionHome) { - - return withFileInHomeFolder(file, "/temp-extensions/" + extensionId + PathUtil.prepareAppendPath(pathInExtensionHome)); + final @NotNull String pathInExtensionHome + ) { + return withFileInHomeFolder( + file, + "/temp-extensions/" + extensionId + PathUtil.prepareAppendPath(pathInExtensionHome) + ); } /** @@ -356,8 +390,8 @@ protected void containerIsStarted(final @NotNull InspectContainerResponse contai */ public @NotNull HiveMQContainer withFileInHomeFolder( final @NotNull MountableFile mountableFile, - final @NotNull String pathInHomeFolder) { - + final @NotNull String pathInHomeFolder + ) { final File file = new File(mountableFile.getResolvedPath()); if (pathInHomeFolder.trim().isEmpty()) { @@ -388,11 +422,12 @@ protected void containerIsStarted(final @NotNull InspectContainerResponse contai public void disableExtension( final @NotNull String extensionName, final @NotNull String extensionDirectory, - final @NotNull Duration timeout) throws TimeoutException { - + final @NotNull Duration timeout + ) throws TimeoutException { final String regEX = "(.*)Extension \"" + extensionName + "\" version (.*) stopped successfully(.*)"; try { - final String containerPath = "/opt/hivemq/extensions" + PathUtil.prepareInnerPath(extensionDirectory) + "DISABLED"; + final String containerPath = + "/opt/hivemq/extensions" + PathUtil.prepareInnerPath(extensionDirectory) + "DISABLED"; final CountDownLatch latch = new CountDownLatch(1); containerOutputLatches.put(regEX, latch); @@ -402,9 +437,13 @@ public void disableExtension( final boolean await = latch.await(timeout.getSeconds(), TimeUnit.SECONDS); if (!await) { - throw new TimeoutException("Extension disabling timed out after '" + timeout.getSeconds() + "' seconds. " + + throw new TimeoutException( + "Extension disabling timed out after '" + + timeout.getSeconds() + + "' seconds. " + "Maybe you are using a HiveMQ Community Edition image, " + - "which does not support disabling of extensions"); + "which does not support disabling of extensions" + ); } } catch (final InterruptedException | IOException e) { throw new RuntimeException(e); @@ -424,9 +463,8 @@ public void disableExtension( * @param extensionDirectory the name of the extension's directory * @throws TimeoutException if the extension was not disabled within 60 seconds */ - public void disableExtension( - final @NotNull String extensionName, - final @NotNull String extensionDirectory) throws TimeoutException { + public void disableExtension(final @NotNull String extensionName, final @NotNull String extensionDirectory) + throws TimeoutException { disableExtension(extensionName, extensionDirectory, Duration.ofSeconds(60)); } @@ -441,9 +479,8 @@ public void disableExtension( * @param timeout the timeout * @throws TimeoutException if the extension was not disabled within the configured timeout */ - public void disableExtension( - final @NotNull HiveMQExtension hiveMQExtension, - final @NotNull Duration timeout) throws TimeoutException { + public void disableExtension(final @NotNull HiveMQExtension hiveMQExtension, final @NotNull Duration timeout) + throws TimeoutException { disableExtension(hiveMQExtension.getName(), hiveMQExtension.getId(), timeout); } @@ -476,11 +513,12 @@ public void disableExtension(final @NotNull HiveMQExtension hiveMQExtension) thr public void enableExtension( final @NotNull String extensionName, final @NotNull String extensionDirectory, - final @NotNull Duration timeout) throws TimeoutException { - + final @NotNull Duration timeout + ) throws TimeoutException { final String regEX = "(.*)Extension \"" + extensionName + "\" version (.*) started successfully(.*)"; try { - final String containerPath = "/opt/hivemq/extensions" + PathUtil.prepareInnerPath(extensionDirectory) + "DISABLED"; + final String containerPath = + "/opt/hivemq/extensions" + PathUtil.prepareInnerPath(extensionDirectory) + "DISABLED"; final CountDownLatch latch = new CountDownLatch(1); containerOutputLatches.put(regEX, latch); @@ -490,9 +528,13 @@ public void enableExtension( final boolean await = latch.await(timeout.getSeconds(), TimeUnit.SECONDS); if (!await) { - throw new TimeoutException("Extension enabling timed out after '" + timeout.getSeconds() + "' seconds. " + + throw new TimeoutException( + "Extension enabling timed out after '" + + timeout.getSeconds() + + "' seconds. " + "Maybe you are using a HiveMQ Community Edition image, " + - "which does not support disabling of extensions"); + "which does not support disabling of extensions" + ); } } catch (final InterruptedException | IOException e) { throw new RuntimeException(e); @@ -512,9 +554,8 @@ public void enableExtension( * @param extensionDirectory the name of the extension's directory * @throws TimeoutException if the extension was not enabled within 60 seconds */ - public void enableExtension( - final @NotNull String extensionName, - final @NotNull String extensionDirectory) throws TimeoutException { + public void enableExtension(final @NotNull String extensionName, final @NotNull String extensionDirectory) + throws TimeoutException { enableExtension(extensionName, extensionDirectory, Duration.ofSeconds(60)); } @@ -529,9 +570,8 @@ public void enableExtension( * @param timeout the timeout * @throws TimeoutException if the extension was not enabled within the configured timeout */ - public void enableExtension( - final @NotNull HiveMQExtension hiveMQExtension, - final @NotNull Duration timeout) throws TimeoutException { + public void enableExtension(final @NotNull HiveMQExtension hiveMQExtension, final @NotNull Duration timeout) + throws TimeoutException { enableExtension(hiveMQExtension.getName(), hiveMQExtension.getId(), timeout); } diff --git a/modules/hivemq/src/main/java/org/testcontainers/hivemq/HiveMQExtension.java b/modules/hivemq/src/main/java/org/testcontainers/hivemq/HiveMQExtension.java index 934dfd0c29c..91e984835d6 100644 --- a/modules/hivemq/src/main/java/org/testcontainers/hivemq/HiveMQExtension.java +++ b/modules/hivemq/src/main/java/org/testcontainers/hivemq/HiveMQExtension.java @@ -1,7 +1,5 @@ package org.testcontainers.hivemq; -import javassist.ClassPool; -import javassist.NotFoundException; import lombok.Getter; import org.apache.commons.io.FileUtils; import org.jboss.shrinkwrap.api.ExtensionLoader; @@ -23,28 +21,35 @@ import java.util.LinkedList; import java.util.List; import java.util.Set; +import javassist.ClassPool; +import javassist.NotFoundException; public class HiveMQExtension { private static final String VALID_EXTENSION_XML = "" + // - " %s" + // - " %s" + // - " %s" + // - " %s" + // - " %s" + // - ""; + " %s" + // + " %s" + // + " %s" + // + " %s" + // + " %s" + // + ""; + private static final String EXTENSION_MAIN_CLASS_NAME = "com.hivemq.extension.sdk.api.ExtensionMain"; + private static final Logger LOGGER = LoggerFactory.getLogger(HiveMQExtension.class); @Getter - private final @NotNull String id; + @NotNull + private final String id; @Getter - private final @NotNull String name; + @NotNull + private final String name; @Getter - private final @NotNull String version; + @NotNull + private final String version; @Getter private final int priority; @@ -56,9 +61,11 @@ public class HiveMQExtension { private final boolean disabledOnStartup; @Getter - private final @NotNull Class mainClass; + @NotNull + private final Class mainClass; - private final @NotNull List> additionalClasses; + @NotNull + private final List> additionalClasses; private HiveMQExtension( final @NotNull String id, @@ -68,8 +75,8 @@ private HiveMQExtension( final int startPriority, final boolean disabledOnStartup, final @NotNull Class mainClass, - final @NotNull List> additionalClasses) { - + final @NotNull List> additionalClasses + ) { this.id = id; this.name = name; this.version = version; @@ -80,27 +87,31 @@ private HiveMQExtension( this.additionalClasses = additionalClasses; } - @NotNull File createExtension(final @NotNull HiveMQExtension hiveMQExtension) - throws Exception { - + @NotNull + File createExtension(final @NotNull HiveMQExtension hiveMQExtension) throws Exception { final File tempDir = Files.createTempDirectory("").toFile(); final File extensionDir = new File(tempDir, hiveMQExtension.getId()); - FileUtils.writeStringToFile(new File(extensionDir, "hivemq-extension.xml"), + FileUtils.writeStringToFile( + new File(extensionDir, "hivemq-extension.xml"), String.format( VALID_EXTENSION_XML, hiveMQExtension.getId(), hiveMQExtension.getName(), hiveMQExtension.getVersion(), hiveMQExtension.getPriority(), - hiveMQExtension.getStartPriority()), - Charset.defaultCharset()); + hiveMQExtension.getStartPriority() + ), + Charset.defaultCharset() + ); if (hiveMQExtension.isDisabledOnStartup()) { final File disabled = new File(extensionDir, "DISABLED"); final boolean newFile = disabled.createNewFile(); if (!newFile) { - throw new ContainerLaunchException("Could not create DISABLED file '" + disabled.getAbsolutePath() + "' on host machine."); + throw new ContainerLaunchException( + "Could not create DISABLED file '" + disabled.getAbsolutePath() + "' on host machine." + ); } } @@ -129,11 +140,15 @@ private HiveMQExtension( private void putSubclassesIntoJar( final @NotNull String extensionId, final @Nullable Class clazz, - final @NotNull JavaArchive javaArchive) throws NotFoundException { - + final @NotNull JavaArchive javaArchive + ) throws NotFoundException { if (clazz != null) { - final Set subClassNames = - ClassPool.getDefault().get(clazz.getName()).getClassFile().getConstPool().getClassNames(); + final Set subClassNames = ClassPool + .getDefault() + .get(clazz.getName()) + .getClassFile() + .getConstPool() + .getClassNames(); for (final String subClassName : subClassNames) { final String className = subClassName.replaceAll("/", "."); @@ -156,14 +171,27 @@ private void putSubclassesIntoJar( } public static final class Builder { - private @Nullable String id; - private @Nullable String name; - private @Nullable String version; + + @Nullable + private String id; + + @Nullable + private String name; + + @Nullable + private String version; + private int priority = 0; + private int startPriority = 0; + private boolean disabledOnStartup = false; - private @Nullable Class mainClass; - private final @NotNull LinkedList> additionalClasses = new LinkedList<>(); + + @Nullable + private Class mainClass; + + @NotNull + private final LinkedList> additionalClasses = new LinkedList<>(); /** * Builds the {@link HiveMQExtension} with the provided values or default values. @@ -274,12 +302,16 @@ public static final class Builder { try { final Class extensionMain = Class.forName(EXTENSION_MAIN_CLASS_NAME); if (!extensionMain.isAssignableFrom(mainClass)) { - throw new IllegalArgumentException("The provided class does not implement '" + EXTENSION_MAIN_CLASS_NAME + "'"); + throw new IllegalArgumentException( + "The provided class does not implement '" + EXTENSION_MAIN_CLASS_NAME + "'" + ); } this.mainClass = mainClass; return this; } catch (final ClassNotFoundException e) { - throw new IllegalStateException("The class '" + EXTENSION_MAIN_CLASS_NAME + "' was not found in the classpath."); + throw new IllegalStateException( + "The class '" + EXTENSION_MAIN_CLASS_NAME + "' was not found in the classpath." + ); } } diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithControlCenterIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithControlCenterIT.java index f88d60b22ed..a3e8a20216d 100644 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithControlCenterIT.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithControlCenterIT.java @@ -17,18 +17,18 @@ public class ContainerWithControlCenterIT { @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) public void test() throws Exception { - - try (final HiveMQContainer hivemq = - new HiveMQContainer(DockerImageName.parse("hivemq/hivemq4").withTag("4.7.4")) - .withControlCenter()) { - + try ( + final HiveMQContainer hivemq = new HiveMQContainer(DockerImageName.parse("hivemq/hivemq4").withTag("4.7.4")) + .withControlCenter() + ) { hivemq.start(); try (final CloseableHttpClient httpClient = HttpClientBuilder.create().build()) { - final HttpUriRequest request = new HttpGet("http://" + hivemq.getHost() + ":" + hivemq.getMappedPort(CONTROL_CENTER_PORT)); + final HttpUriRequest request = new HttpGet( + "http://" + hivemq.getHost() + ":" + hivemq.getMappedPort(CONTROL_CENTER_PORT) + ); httpClient.execute(request); } } - } } diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithCustomConfigIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithCustomConfigIT.java index 57d3b75bc24..fe119854c80 100755 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithCustomConfigIT.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithCustomConfigIT.java @@ -18,13 +18,14 @@ public class ContainerWithCustomConfigIT { @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) void test() throws Exception { - - try (final HiveMQContainer hivemq = new HiveMQContainer(DockerImageName.parse("hivemq/hivemq4").withTag("4.7.4")) - .withHiveMQConfig(MountableFile.forClasspathResource("/config.xml"))) { - + try ( + final HiveMQContainer hivemq = new HiveMQContainer(DockerImageName.parse("hivemq/hivemq4").withTag("4.7.4")) + .withHiveMQConfig(MountableFile.forClasspathResource("/config.xml")) + ) { hivemq.start(); - final Mqtt5BlockingClient publisher = Mqtt5Client.builder() + final Mqtt5BlockingClient publisher = Mqtt5Client + .builder() .identifier("publisher") .serverPort(hivemq.getMqttPort()) .serverHost(hivemq.getHost()) @@ -32,13 +33,13 @@ void test() throws Exception { publisher.connect(); - assertThrows(MqttSessionExpiredException.class, () -> { - // this should fail since only QoS 0 is allowed by the configuration - publisher.publishWith() - .topic("test/topic") - .qos(MqttQos.EXACTLY_ONCE) - .send(); - }); + assertThrows( + MqttSessionExpiredException.class, + () -> { + // this should fail since only QoS 0 is allowed by the configuration + publisher.publishWith().topic("test/topic").qos(MqttQos.EXACTLY_ONCE).send(); + } + ); } } } diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithExtensionFromDirectoryIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithExtensionFromDirectoryIT.java index 4b89e418b05..c1e6e72cc32 100755 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithExtensionFromDirectoryIT.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithExtensionFromDirectoryIT.java @@ -15,13 +15,14 @@ public class ContainerWithExtensionFromDirectoryIT { @Timeout(value = 3, unit = TimeUnit.MINUTES) void test() throws Exception { try ( - final HiveMQContainer hivemq = - new HiveMQContainer(DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3")) - .withExtension(MountableFile.forClasspathResource("/modifier-extension")) - .waitForExtension("Modifier Extension") - .withHiveMQConfig(MountableFile.forClasspathResource("/inMemoryConfig.xml")) - .withLogLevel(Level.DEBUG)) { - + final HiveMQContainer hivemq = new HiveMQContainer( + DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3") + ) + .withExtension(MountableFile.forClasspathResource("/modifier-extension")) + .waitForExtension("Modifier Extension") + .withHiveMQConfig(MountableFile.forClasspathResource("/inMemoryConfig.xml")) + .withLogLevel(Level.DEBUG) + ) { hivemq.start(); TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost()); } @@ -30,12 +31,14 @@ void test() throws Exception { @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) void test_wrongDirectoryName() throws Exception { - try (final HiveMQContainer hivemq = - new HiveMQContainer(DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3")) + try ( + final HiveMQContainer hivemq = new HiveMQContainer( + DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3") + ) .withExtension(MountableFile.forClasspathResource("/modifier-extension-wrong-name")) .waitForExtension("Modifier Extension") - .withLogLevel(Level.DEBUG)) { - + .withLogLevel(Level.DEBUG) + ) { hivemq.start(); TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost()); } diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithExtensionIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithExtensionIT.java index 8979d2c17b0..01a67e0dcfb 100755 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithExtensionIT.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithExtensionIT.java @@ -14,18 +14,22 @@ public class ContainerWithExtensionIT { @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) void test() throws Exception { - final HiveMQExtension hiveMQExtension = HiveMQExtension.builder() + final HiveMQExtension hiveMQExtension = HiveMQExtension + .builder() .id("extension-1") .name("my-extension") .version("1.0") - .mainClass(MyExtension.class).build(); - - try (final HiveMQContainer hivemq = - new HiveMQContainer(DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3")) - .withHiveMQConfig(MountableFile.forClasspathResource("/inMemoryConfig.xml")) - .waitForExtension(hiveMQExtension) - .withExtension(hiveMQExtension)) { - + .mainClass(MyExtension.class) + .build(); + + try ( + final HiveMQContainer hivemq = new HiveMQContainer( + DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3") + ) + .withHiveMQConfig(MountableFile.forClasspathResource("/inMemoryConfig.xml")) + .waitForExtension(hiveMQExtension) + .withExtension(hiveMQExtension) + ) { hivemq.start(); TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost()); hivemq.stop(); diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithExtensionSubclassIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithExtensionSubclassIT.java index 53714123aff..1f3d9de4aeb 100755 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithExtensionSubclassIT.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithExtensionSubclassIT.java @@ -15,19 +15,23 @@ public class ContainerWithExtensionSubclassIT { @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) void test() throws Exception { - final HiveMQExtension hiveMQExtension = HiveMQExtension.builder() + final HiveMQExtension hiveMQExtension = HiveMQExtension + .builder() .id("extension-1") .name("my-extension") .version("1.0") - .mainClass(MyExtensionWithSubclasses.class).build(); - - try (final HiveMQContainer hivemq = - new HiveMQContainer(DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3")) - .waitForExtension(hiveMQExtension) - .withExtension(hiveMQExtension) - .withHiveMQConfig(MountableFile.forClasspathResource("/inMemoryConfig.xml")) - .withLogLevel(Level.DEBUG)) { + .mainClass(MyExtensionWithSubclasses.class) + .build(); + try ( + final HiveMQContainer hivemq = new HiveMQContainer( + DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3") + ) + .waitForExtension(hiveMQExtension) + .withExtension(hiveMQExtension) + .withHiveMQConfig(MountableFile.forClasspathResource("/inMemoryConfig.xml")) + .withLogLevel(Level.DEBUG) + ) { hivemq.start(); TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost()); } diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithFileInExtensionHomeIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithFileInExtensionHomeIT.java index 559737c8868..a5b5238ce25 100755 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithFileInExtensionHomeIT.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithFileInExtensionHomeIT.java @@ -22,26 +22,30 @@ public class ContainerWithFileInExtensionHomeIT { - @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) void test() throws Exception { - final HiveMQExtension hiveMQExtension = HiveMQExtension.builder() + final HiveMQExtension hiveMQExtension = HiveMQExtension + .builder() .id("extension-1") .name("my-extension") .version("1.0") - .mainClass(FileCheckerExtension.class).build(); - - try (final HiveMQContainer hivemq = - new HiveMQContainer(DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3")) - .withHiveMQConfig(MountableFile.forClasspathResource("/inMemoryConfig.xml")) - .withExtension(hiveMQExtension) - .waitForExtension(hiveMQExtension) - .withFileInExtensionHomeFolder( - MountableFile.forClasspathResource("/additionalFile.txt"), - "extension-1", - "/additionalFiles/my-file.txt")) { - + .mainClass(FileCheckerExtension.class) + .build(); + + try ( + final HiveMQContainer hivemq = new HiveMQContainer( + DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3") + ) + .withHiveMQConfig(MountableFile.forClasspathResource("/inMemoryConfig.xml")) + .withExtension(hiveMQExtension) + .waitForExtension(hiveMQExtension) + .withFileInExtensionHomeFolder( + MountableFile.forClasspathResource("/additionalFile.txt"), + "extension-1", + "/additionalFiles/my-file.txt" + ) + ) { hivemq.start(); TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost()); } @@ -50,28 +54,33 @@ void test() throws Exception { public static class FileCheckerExtension implements ExtensionMain { @Override - public void extensionStart(final @NotNull ExtensionStartInput extensionStartInput, final @NotNull ExtensionStartOutput extensionStartOutput) { - + public void extensionStart( + final @NotNull ExtensionStartInput extensionStartInput, + final @NotNull ExtensionStartOutput extensionStartOutput + ) { final PublishInboundInterceptor publishInboundInterceptor = (publishInboundInput, publishInboundOutput) -> { - final File extensionHomeFolder = extensionStartInput.getExtensionInformation().getExtensionHomeFolder(); final File additionalFile = new File(extensionHomeFolder, "additionalFiles/my-file.txt"); if (additionalFile.exists()) { - publishInboundOutput.getPublishPacket().setPayload(ByteBuffer.wrap("modified".getBytes(StandardCharsets.UTF_8))); + publishInboundOutput + .getPublishPacket() + .setPayload(ByteBuffer.wrap("modified".getBytes(StandardCharsets.UTF_8))); } }; - final ClientInitializer clientInitializer = (initializerInput, clientContext) -> clientContext.addPublishInboundInterceptor(publishInboundInterceptor); + final ClientInitializer clientInitializer = (initializerInput, clientContext) -> { + clientContext.addPublishInboundInterceptor(publishInboundInterceptor); + }; Services.initializerRegistry().setClientInitializer(clientInitializer); } @Override - public void extensionStop(final @NotNull ExtensionStopInput extensionStopInput, final @NotNull ExtensionStopOutput extensionStopOutput) { - - } + public void extensionStop( + final @NotNull ExtensionStopInput extensionStopInput, + final @NotNull ExtensionStopOutput extensionStopOutput + ) {} } - } diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithFileInHomeIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithFileInHomeIT.java index efdf6ad298d..2fd387f394c 100755 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithFileInHomeIT.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithFileInHomeIT.java @@ -25,21 +25,26 @@ public class ContainerWithFileInHomeIT { @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) void test() throws Exception { - final HiveMQExtension hiveMQExtension = HiveMQExtension.builder() + final HiveMQExtension hiveMQExtension = HiveMQExtension + .builder() .id("extension-1") .name("my-extension") .version("1.0") - .mainClass(FileCheckerExtension.class).build(); - - try (final HiveMQContainer hivemq = - new HiveMQContainer(DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3")) - .withHiveMQConfig(MountableFile.forClasspathResource("/inMemoryConfig.xml")) - .withExtension(hiveMQExtension) - .waitForExtension(hiveMQExtension) - .withFileInHomeFolder(MountableFile.forClasspathResource("/additionalFile.txt"), - "/additionalFiles/my-file.txt")) { - - + .mainClass(FileCheckerExtension.class) + .build(); + + try ( + final HiveMQContainer hivemq = new HiveMQContainer( + DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3") + ) + .withHiveMQConfig(MountableFile.forClasspathResource("/inMemoryConfig.xml")) + .withExtension(hiveMQExtension) + .waitForExtension(hiveMQExtension) + .withFileInHomeFolder( + MountableFile.forClasspathResource("/additionalFile.txt"), + "/additionalFiles/my-file.txt" + ) + ) { hivemq.start(); TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost()); } @@ -48,28 +53,33 @@ void test() throws Exception { public static class FileCheckerExtension implements ExtensionMain { @Override - public void extensionStart(@NotNull ExtensionStartInput extensionStartInput, @NotNull ExtensionStartOutput extensionStartOutput) { - + public void extensionStart( + @NotNull ExtensionStartInput extensionStartInput, + @NotNull ExtensionStartOutput extensionStartOutput + ) { final PublishInboundInterceptor publishInboundInterceptor = (publishInboundInput, publishInboundOutput) -> { - final File homeFolder = extensionStartInput.getServerInformation().getHomeFolder(); final File additionalFile = new File(homeFolder, "additionalFiles/my-file.txt"); if (additionalFile.exists()) { - publishInboundOutput.getPublishPacket().setPayload(ByteBuffer.wrap("modified".getBytes(StandardCharsets.UTF_8))); + publishInboundOutput + .getPublishPacket() + .setPayload(ByteBuffer.wrap("modified".getBytes(StandardCharsets.UTF_8))); } }; - final ClientInitializer clientInitializer = (initializerInput, clientContext) -> clientContext.addPublishInboundInterceptor(publishInboundInterceptor); + final ClientInitializer clientInitializer = (initializerInput, clientContext) -> { + clientContext.addPublishInboundInterceptor(publishInboundInterceptor); + }; Services.initializerRegistry().setClientInitializer(clientInitializer); } @Override - public void extensionStop(@NotNull ExtensionStopInput extensionStopInput, @NotNull ExtensionStopOutput extensionStopOutput) { - - } + public void extensionStop( + @NotNull ExtensionStopInput extensionStopInput, + @NotNull ExtensionStopOutput extensionStopOutput + ) {} } - } diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithLicenseIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithLicenseIT.java index ba2dc472cdb..9d82188c35c 100755 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithLicenseIT.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithLicenseIT.java @@ -25,21 +25,24 @@ public class ContainerWithLicenseIT { @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) void test() throws Exception { - final HiveMQExtension hiveMQExtension = HiveMQExtension.builder() + final HiveMQExtension hiveMQExtension = HiveMQExtension + .builder() .id("extension-1") .name("my-extension") .version("1.0") - .mainClass(LicenceCheckerExtension.class).build(); - - try (final HiveMQContainer hivemq = - new HiveMQContainer(DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3")) - .withHiveMQConfig(MountableFile.forClasspathResource("/inMemoryConfig.xml")) - .withExtension(hiveMQExtension) - .waitForExtension(hiveMQExtension) - .withLicense(MountableFile.forClasspathResource("/myLicense.lic")) - .withLicense(MountableFile.forClasspathResource("/myExtensionLicense.elic"))) { - - + .mainClass(LicenceCheckerExtension.class) + .build(); + + try ( + final HiveMQContainer hivemq = new HiveMQContainer( + DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3") + ) + .withHiveMQConfig(MountableFile.forClasspathResource("/inMemoryConfig.xml")) + .withExtension(hiveMQExtension) + .waitForExtension(hiveMQExtension) + .withLicense(MountableFile.forClasspathResource("/myLicense.lic")) + .withLicense(MountableFile.forClasspathResource("/myExtensionLicense.elic")) + ) { hivemq.start(); TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost()); } @@ -49,16 +52,19 @@ void test() throws Exception { public static class LicenceCheckerExtension implements ExtensionMain { @Override - public void extensionStart(@NotNull ExtensionStartInput extensionStartInput, @NotNull ExtensionStartOutput extensionStartOutput) { - + public void extensionStart( + @NotNull ExtensionStartInput extensionStartInput, + @NotNull ExtensionStartOutput extensionStartOutput + ) { final PublishInboundInterceptor publishInboundInterceptor = (publishInboundInput, publishInboundOutput) -> { - final File homeFolder = extensionStartInput.getServerInformation().getHomeFolder(); final File myLicence = new File(homeFolder, "license/myLicense.lic"); final File myExtensionLicence = new File(homeFolder, "license/myExtensionLicense.elic"); if (myLicence.exists() && myExtensionLicence.exists()) { - publishInboundOutput.getPublishPacket().setPayload(ByteBuffer.wrap("modified".getBytes(StandardCharsets.UTF_8))); + publishInboundOutput + .getPublishPacket() + .setPayload(ByteBuffer.wrap("modified".getBytes(StandardCharsets.UTF_8))); } }; @@ -70,9 +76,9 @@ public void extensionStart(@NotNull ExtensionStartInput extensionStartInput, @No } @Override - public void extensionStop(@NotNull ExtensionStopInput extensionStopInput, @NotNull ExtensionStopOutput extensionStopOutput) { - - } + public void extensionStop( + @NotNull ExtensionStopInput extensionStopInput, + @NotNull ExtensionStopOutput extensionStopOutput + ) {} } - } diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithoutPlatformExtensionsIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithoutPlatformExtensionsIT.java index eaec72cb038..75d308e3f90 100644 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithoutPlatformExtensionsIT.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithoutPlatformExtensionsIT.java @@ -29,25 +29,28 @@ public class ContainerWithoutPlatformExtensionsIT { - private final @NotNull HiveMQExtension hiveMQExtension = HiveMQExtension.builder() + @NotNull + private final HiveMQExtension hiveMQExtension = HiveMQExtension + .builder() .name("MyExtension") .id("my-extension") .version("1.0.0") - .mainClass(CheckerExtension.class).build(); - + .mainClass(CheckerExtension.class) + .build(); @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) public void removeAllPlatformExtensions() throws InterruptedException { - - try (final HiveMQContainer hivemq = new HiveMQContainer(DockerImageName.parse("hivemq/hivemq4").withTag("4.7.4")) - .withExtension(hiveMQExtension) - .waitForExtension(hiveMQExtension) - .withoutPrepackagedExtensions()) { - + try ( + final HiveMQContainer hivemq = new HiveMQContainer(DockerImageName.parse("hivemq/hivemq4").withTag("4.7.4")) + .withExtension(hiveMQExtension) + .waitForExtension(hiveMQExtension) + .withoutPrepackagedExtensions() + ) { hivemq.start(); - final Mqtt5BlockingClient client = MqttClient.builder() + final Mqtt5BlockingClient client = MqttClient + .builder() .serverPort(hivemq.getMqttPort()) .serverHost(hivemq.getHost()) .useMqttVersion5() @@ -73,15 +76,16 @@ public void removeAllPlatformExtensions() throws InterruptedException { @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) public void removeKafkaExtension() throws InterruptedException { - - try (final HiveMQContainer hivemq = new HiveMQContainer(DockerImageName.parse("hivemq/hivemq4").withTag("4.7.4")) - .withExtension(hiveMQExtension) - .waitForExtension(hiveMQExtension) - .withoutPrepackagedExtensions("hivemq-kafka-extension")) { - + try ( + final HiveMQContainer hivemq = new HiveMQContainer(DockerImageName.parse("hivemq/hivemq4").withTag("4.7.4")) + .withExtension(hiveMQExtension) + .waitForExtension(hiveMQExtension) + .withoutPrepackagedExtensions("hivemq-kafka-extension") + ) { hivemq.start(); - final Mqtt5BlockingClient client = MqttClient.builder() + final Mqtt5BlockingClient client = MqttClient + .builder() .serverPort(hivemq.getMqttPort()) .serverHost(hivemq.getHost()) .useMqttVersion5() @@ -107,31 +111,32 @@ public static class CheckerExtension implements ExtensionMain { @Override public void extensionStart( final @NotNull ExtensionStartInput extensionStartInput, - final @NotNull ExtensionStartOutput extensionStartOutput) { - - final String extensionFolders = Arrays.stream(extensionStartInput.getServerInformation().getExtensionsFolder().listFiles()) + final @NotNull ExtensionStartOutput extensionStartOutput + ) { + final String extensionFolders = Arrays + .stream(extensionStartInput.getServerInformation().getExtensionsFolder().listFiles()) .filter(File::isDirectory) .map(File::getName) .collect(Collectors.joining("\n")); final byte[] bytes = extensionFolders.getBytes(StandardCharsets.UTF_8); - Services.publishService().publish(Builders.publish() - .topic("extensions") - .retain(true) - .payload(ByteBuffer.wrap(bytes)) - .build()); - - Services.securityRegistry().setAuthenticatorProvider(authenticatorProviderInput -> - (SimpleAuthenticator) (simpleAuthInput, simpleAuthOutput) -> - simpleAuthOutput.authenticateSuccessfully()); - + Services + .publishService() + .publish(Builders.publish().topic("extensions").retain(true).payload(ByteBuffer.wrap(bytes)).build()); + + Services + .securityRegistry() + .setAuthenticatorProvider(authenticatorProviderInput -> { + return (SimpleAuthenticator) (simpleAuthInput, simpleAuthOutput) -> { + simpleAuthOutput.authenticateSuccessfully(); + }; + }); } @Override public void extensionStop( final @NotNull ExtensionStopInput extensionStopInput, - final @NotNull ExtensionStopOutput extensionStopOutput) { - - } + final @NotNull ExtensionStopOutput extensionStopOutput + ) {} } } diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/CreateFileInCopiedDirectoryIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/CreateFileInCopiedDirectoryIT.java index 58b70c3d1d9..191ea41f303 100755 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/CreateFileInCopiedDirectoryIT.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/CreateFileInCopiedDirectoryIT.java @@ -37,19 +37,23 @@ public class CreateFileInCopiedDirectoryIT { @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) void test() throws Exception { - final HiveMQExtension extension = HiveMQExtension.builder() + final HiveMQExtension extension = HiveMQExtension + .builder() .id("extension-1") .name("my-extension") .version("1.0") - .mainClass(FileCreatorExtension.class).build(); - - try (final HiveMQContainer hivemq = - new HiveMQContainer(DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3")) - .withHiveMQConfig(MountableFile.forClasspathResource("/inMemoryConfig.xml")) - .withExtension(extension) - .waitForExtension(extension) - .withFileInHomeFolder(createDirectory(), "directory")) { - + .mainClass(FileCreatorExtension.class) + .build(); + + try ( + final HiveMQContainer hivemq = new HiveMQContainer( + DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3") + ) + .withHiveMQConfig(MountableFile.forClasspathResource("/inMemoryConfig.xml")) + .withExtension(extension) + .waitForExtension(extension) + .withFileInHomeFolder(createDirectory(), "directory") + ) { hivemq.start(); TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost()); } @@ -58,10 +62,11 @@ void test() throws Exception { public static class FileCreatorExtension implements ExtensionMain { @Override - public void extensionStart(@NotNull ExtensionStartInput extensionStartInput, @NotNull ExtensionStartOutput extensionStartOutput) { - + public void extensionStart( + @NotNull ExtensionStartInput extensionStartInput, + @NotNull ExtensionStartOutput extensionStartOutput + ) { final PublishInboundInterceptor publishInboundInterceptor = (publishInboundInput, publishInboundOutput) -> { - final File homeFolder = extensionStartInput.getServerInformation().getHomeFolder(); final File dir = new File(homeFolder, "directory"); @@ -71,22 +76,26 @@ public void extensionStart(@NotNull ExtensionStartInput extensionStartInput, @No try { if (dirFile.createNewFile() && subDirFile.createNewFile()) { - publishInboundOutput.getPublishPacket().setPayload(ByteBuffer.wrap("modified".getBytes(StandardCharsets.UTF_8))); + publishInboundOutput + .getPublishPacket() + .setPayload(ByteBuffer.wrap("modified".getBytes(StandardCharsets.UTF_8))); } } catch (IOException e) { e.printStackTrace(); } }; - final ClientInitializer clientInitializer = (initializerInput, clientContext) -> clientContext.addPublishInboundInterceptor(publishInboundInterceptor); + final ClientInitializer clientInitializer = (initializerInput, clientContext) -> { + clientContext.addPublishInboundInterceptor(publishInboundInterceptor); + }; Services.initializerRegistry().setClientInitializer(clientInitializer); } @Override - public void extensionStop(@NotNull ExtensionStopInput extensionStopInput, @NotNull ExtensionStopOutput extensionStopOutput) { - - } + public void extensionStop( + @NotNull ExtensionStopInput extensionStopInput, + @NotNull ExtensionStopOutput extensionStopOutput + ) {} } - } diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/CreateFileInExtensionDirectoryIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/CreateFileInExtensionDirectoryIT.java index c41137f58c3..44f5f4f632d 100755 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/CreateFileInExtensionDirectoryIT.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/CreateFileInExtensionDirectoryIT.java @@ -26,18 +26,22 @@ public class CreateFileInExtensionDirectoryIT { @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) void test() throws Exception { - final HiveMQExtension hiveMQExtension = HiveMQExtension.builder() + final HiveMQExtension hiveMQExtension = HiveMQExtension + .builder() .id("extension-1") .name("my-extension") .version("1.0") - .mainClass(FileCreatorExtension.class).build(); - - try (final HiveMQContainer hivemq = - new HiveMQContainer(DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3")) - .withHiveMQConfig(MountableFile.forClasspathResource("/inMemoryConfig.xml")) - .waitForExtension(hiveMQExtension) - .withExtension(hiveMQExtension)) { - + .mainClass(FileCreatorExtension.class) + .build(); + + try ( + final HiveMQContainer hivemq = new HiveMQContainer( + DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3") + ) + .withHiveMQConfig(MountableFile.forClasspathResource("/inMemoryConfig.xml")) + .waitForExtension(hiveMQExtension) + .withExtension(hiveMQExtension) + ) { hivemq.start(); TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost()); } @@ -46,31 +50,36 @@ void test() throws Exception { public static class FileCreatorExtension implements ExtensionMain { @Override - public void extensionStart(@NotNull ExtensionStartInput extensionStartInput, @NotNull ExtensionStartOutput extensionStartOutput) { - + public void extensionStart( + @NotNull ExtensionStartInput extensionStartInput, + @NotNull ExtensionStartOutput extensionStartOutput + ) { final PublishInboundInterceptor publishInboundInterceptor = (publishInboundInput, publishInboundOutput) -> { - final File extensionHomeFolder = extensionStartInput.getExtensionInformation().getExtensionHomeFolder(); final File file = new File(extensionHomeFolder, "myfile.txt"); try { if (file.createNewFile()) { - publishInboundOutput.getPublishPacket().setPayload(ByteBuffer.wrap("modified".getBytes(StandardCharsets.UTF_8))); + publishInboundOutput + .getPublishPacket() + .setPayload(ByteBuffer.wrap("modified".getBytes(StandardCharsets.UTF_8))); } } catch (IOException e) { e.printStackTrace(); } }; - final ClientInitializer clientInitializer = (initializerInput, clientContext) -> clientContext.addPublishInboundInterceptor(publishInboundInterceptor); + final ClientInitializer clientInitializer = (initializerInput, clientContext) -> { + clientContext.addPublishInboundInterceptor(publishInboundInterceptor); + }; Services.initializerRegistry().setClientInitializer(clientInitializer); } @Override - public void extensionStop(@NotNull ExtensionStopInput extensionStopInput, @NotNull ExtensionStopOutput extensionStopOutput) { - - } + public void extensionStop( + @NotNull ExtensionStopInput extensionStopInput, + @NotNull ExtensionStopOutput extensionStopOutput + ) {} } - } diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/DisableEnableExtensionFromDirectoryIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/DisableEnableExtensionFromDirectoryIT.java index f6b1b9b5cd7..9588e3404e6 100644 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/DisableEnableExtensionFromDirectoryIT.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/DisableEnableExtensionFromDirectoryIT.java @@ -17,21 +17,22 @@ public class DisableEnableExtensionFromDirectoryIT { @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) void test() throws Exception { - try (final HiveMQContainer hivemq = - new HiveMQContainer(DockerImageName.parse("hivemq/hivemq4").withTag("4.7.4")) - .withExtension(MountableFile.forClasspathResource("/modifier-extension")) - .waitForExtension("Modifier Extension") - .withLogLevel(Level.DEBUG)) { - + try ( + final HiveMQContainer hivemq = new HiveMQContainer(DockerImageName.parse("hivemq/hivemq4").withTag("4.7.4")) + .withExtension(MountableFile.forClasspathResource("/modifier-extension")) + .waitForExtension("Modifier Extension") + .withLogLevel(Level.DEBUG) + ) { hivemq.start(); - TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost()); hivemq.disableExtension("Modifier Extension", "modifier-extension"); - assertThrows(ExecutionException.class, () -> TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost())); + assertThrows( + ExecutionException.class, + () -> TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost()) + ); hivemq.enableExtension("Modifier Extension", "modifier-extension"); TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost()); } } - } diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/DisableEnableExtensionIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/DisableEnableExtensionIT.java index 5b3c6ab107f..e50664f240e 100644 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/DisableEnableExtensionIT.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/DisableEnableExtensionIT.java @@ -15,32 +15,39 @@ public class DisableEnableExtensionIT { - private final @NotNull HiveMQExtension hiveMQExtension = HiveMQExtension.builder() + @NotNull + private final HiveMQExtension hiveMQExtension = HiveMQExtension + .builder() .id("extension-1") .name("my-extension") .version("1.0") .disabledOnStartup(true) - .mainClass(MyExtension.class).build(); + .mainClass(MyExtension.class) + .build(); @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) void test() throws Exception { - try (final HiveMQContainer hivemq = - new HiveMQContainer(DockerImageName.parse("hivemq/hivemq4").withTag("4.7.4")) - .withExtension(hiveMQExtension) - .withLogLevel(Level.DEBUG)) { - - + try ( + final HiveMQContainer hivemq = new HiveMQContainer(DockerImageName.parse("hivemq/hivemq4").withTag("4.7.4")) + .withExtension(hiveMQExtension) + .withLogLevel(Level.DEBUG) + ) { hivemq.start(); - assertThrows(ExecutionException.class, () -> TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost())); + assertThrows( + ExecutionException.class, + () -> TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost()) + ); hivemq.enableExtension(hiveMQExtension); TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost()); hivemq.disableExtension(hiveMQExtension); - assertThrows(ExecutionException.class, () -> TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost())); + assertThrows( + ExecutionException.class, + () -> TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost()) + ); hivemq.enableExtension(hiveMQExtension); TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost()); } } - } diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/HiveMQExtensionTest.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/HiveMQExtensionTest.java index d614a59bd8c..08e1df797a1 100644 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/HiveMQExtensionTest.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/HiveMQExtensionTest.java @@ -8,6 +8,9 @@ class HiveMQExtensionTest { @Test void builder_classDoesNotImplementExtensionMain_exception() { - assertThrows(IllegalArgumentException.class, () -> HiveMQExtension.builder().mainClass(HiveMQExtensionTest.class)); + assertThrows( + IllegalArgumentException.class, + () -> HiveMQExtension.builder().mainClass(HiveMQExtensionTest.class) + ); } } diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/HiveMQTestContainerCore.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/HiveMQTestContainerCore.java index 40820e33ded..d4dd2be9621 100644 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/HiveMQTestContainerCore.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/HiveMQTestContainerCore.java @@ -15,7 +15,8 @@ class HiveMQTestContainerCore { - final @NotNull HiveMQContainer container = new HiveMQContainer(DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3")); + @NotNull + final HiveMQContainer container = new HiveMQContainer(DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3")); @TempDir File tempDir; @@ -69,14 +70,23 @@ void withFileInHomeFolder_pathEmpty_Exception() { @Test void withFileInExtensionHomeFolder_withPath_fileDoesNotExist_Exception() { final MountableFile mountableFile = MountableFile.forHostPath("/this/does/not/exist"); - assertThrows(ContainerLaunchException.class, () -> container.withFileInExtensionHomeFolder(mountableFile, "my-extension", "some/path")); + assertThrows( + ContainerLaunchException.class, + () -> container.withFileInExtensionHomeFolder(mountableFile, "my-extension", "some/path") + ); } @Test void withFileInExtensionHomeFolder_fileDoesNotExist_Exception() { final MountableFile mountableFile = MountableFile.forHostPath("/this/does/not/exist"); - assertThrows(ContainerLaunchException.class, () -> { - final HiveMQContainer hiveMQContainer = container.withFileInExtensionHomeFolder(mountableFile, "my-extension"); - }); + assertThrows( + ContainerLaunchException.class, + () -> { + final HiveMQContainer hiveMQContainer = container.withFileInExtensionHomeFolder( + mountableFile, + "my-extension" + ); + } + ); } } diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoDisableExtensionsIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoDisableExtensionsIT.java index e9d9463653e..97a1365ac91 100644 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoDisableExtensionsIT.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoDisableExtensionsIT.java @@ -14,43 +14,54 @@ public class DemoDisableExtensionsIT { // noExtensions { @Container - final HiveMQContainer hivemqNoExtensions = new HiveMQContainer(DockerImageName.parse("hivemq/hivemq4").withTag("4.7.4")) + final HiveMQContainer hivemqNoExtensions = new HiveMQContainer( + DockerImageName.parse("hivemq/hivemq4").withTag("4.7.4") + ) .withoutPrepackagedExtensions(); + // } // noKafkaExtension { @Container - final HiveMQContainer hivemqNoKafkaExtension = new HiveMQContainer(DockerImageName.parse("hivemq/hivemq4").withTag("4.7.4")) + final HiveMQContainer hivemqNoKafkaExtension = new HiveMQContainer( + DockerImageName.parse("hivemq/hivemq4").withTag("4.7.4") + ) .withoutPrepackagedExtensions("hivemq-kafka-extension"); + // } // startDisabled { - private final HiveMQExtension hiveMQExtension = HiveMQExtension.builder() + private final HiveMQExtension hiveMQExtension = HiveMQExtension + .builder() .id("extension-1") .name("my-extension") .version("1.0") .disabledOnStartup(true) - .mainClass(MyExtension.class).build(); + .mainClass(MyExtension.class) + .build(); @Container final HiveMQContainer hivemq = new HiveMQContainer(DockerImageName.parse("hivemq/hivemq4").withTag("4.7.4")) .withExtension(hiveMQExtension); - // } + // } // startFromFilesystem { @Container - final HiveMQContainer hivemqExtensionFromFilesystem = new HiveMQContainer(DockerImageName.parse("hivemq/hivemq4").withTag("4.7.4")) + final HiveMQContainer hivemqExtensionFromFilesystem = new HiveMQContainer( + DockerImageName.parse("hivemq/hivemq4").withTag("4.7.4") + ) .withExtension(MountableFile.forHostPath("src/test/resources/modifier-extension")); - // } + // } - // runtimeEnable { + // hiveRuntimeEnable { @Test void test_disable_enable_extension() throws Exception { hivemq.enableExtension(hiveMQExtension); hivemq.disableExtension(hiveMQExtension); } + // } // runtimeEnableFilesystem { diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoExtensionTestsIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoExtensionTestsIT.java index 3a8ccc1bebe..e5385369d3a 100644 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoExtensionTestsIT.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoExtensionTestsIT.java @@ -19,33 +19,39 @@ public class DemoExtensionTestsIT { // waitStrategy { @Container - final HiveMQContainer hivemqWithWaitStrategy = - new HiveMQContainer(DockerImageName.parse("hivemq/hivemq4").withTag("4.7.4")) - .withExtension(MountableFile.forClasspathResource("/modifier-extension")) - .waitForExtension("Modifier Extension"); + final HiveMQContainer hivemqWithWaitStrategy = new HiveMQContainer( + DockerImageName.parse("hivemq/hivemq4").withTag("4.7.4") + ) + .withExtension(MountableFile.forClasspathResource("/modifier-extension")) + .waitForExtension("Modifier Extension"); + // } // extensionClasspath { - final HiveMQExtension hiveMQEClasspathxtension = HiveMQExtension.builder() + final HiveMQExtension hiveMQEClasspathxtension = HiveMQExtension + .builder() .id("extension-1") .name("my-extension") .version("1.0") - .mainClass(MyExtensionWithSubclasses.class).build(); + .mainClass(MyExtensionWithSubclasses.class) + .build(); @Container - final HiveMQContainer hivemqWithClasspathExtension = - new HiveMQContainer(DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3")) - .waitForExtension(hiveMQEClasspathxtension) - .withExtension(hiveMQEClasspathxtension) - .withHiveMQConfig(MountableFile.forClasspathResource("/inMemoryConfig.xml")); + final HiveMQContainer hivemqWithClasspathExtension = new HiveMQContainer( + DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3") + ) + .waitForExtension(hiveMQEClasspathxtension) + .withExtension(hiveMQEClasspathxtension) + .withHiveMQConfig(MountableFile.forClasspathResource("/inMemoryConfig.xml")); + // } @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) public void test() throws Exception { - // mqtt5client { - final Mqtt5BlockingClient client = Mqtt5Client.builder() + final Mqtt5BlockingClient client = Mqtt5Client + .builder() .serverPort(hivemqWithClasspathExtension.getMqttPort()) .serverHost(hivemqWithClasspathExtension.getHost()) .buildBlocking(); diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoFilesIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoFilesIT.java index 7ec777d02b8..d0bf6fc6b9c 100644 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoFilesIT.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoFilesIT.java @@ -18,24 +18,36 @@ public class DemoFilesIT { // hivemqHome { - final HiveMQContainer hivemqFileInHome = new HiveMQContainer(DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3")) + final HiveMQContainer hivemqFileInHome = new HiveMQContainer( + DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3") + ) .withFileInHomeFolder( MountableFile.forHostPath("src/test/resources/additionalFile.txt"), - "/path/in/home/folder"); + "/path/in/home/folder" + ); + // } // extensionHome { @Container - final HiveMQContainer hivemqFileInExtensionHome = new HiveMQContainer(DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3")) - .withExtension(HiveMQExtension.builder() - .id("extension-1") - .name("my-extension") - .version("1.0") - .mainClass(MyExtension.class).build()) + final HiveMQContainer hivemqFileInExtensionHome = new HiveMQContainer( + DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3") + ) + .withExtension( + HiveMQExtension + .builder() + .id("extension-1") + .name("my-extension") + .version("1.0") + .mainClass(MyExtension.class) + .build() + ) .withFileInExtensionHomeFolder( MountableFile.forHostPath("src/test/resources/additionalFile.txt"), "extension-1", - "/path/in/extension/home"); + "/path/in/extension/home" + ); + // } // withLicenses { @@ -43,14 +55,15 @@ public class DemoFilesIT { final HiveMQContainer hivemq = new HiveMQContainer(DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3")) .withLicense(MountableFile.forHostPath("src/test/resources/myLicense.lic")) .withLicense(MountableFile.forHostPath("src/test/resources/myExtensionLicense.elic")); + // } @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) public void test() throws Exception { - // mqtt5client { - final Mqtt5BlockingClient client = Mqtt5Client.builder() + final Mqtt5BlockingClient client = Mqtt5Client + .builder() .serverPort(hivemq.getMqttPort()) .serverHost(hivemq.getHost()) .buildBlocking(); diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoHiveMQContainerIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoHiveMQContainerIT.java index 51e3a43d9e5..be39fe6d7f2 100644 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoHiveMQContainerIT.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/docs/DemoHiveMQContainerIT.java @@ -18,38 +18,41 @@ public class DemoHiveMQContainerIT { // ceVersion { @Container - final HiveMQContainer hivemqCe = - new HiveMQContainer(DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3")) - .withLogLevel(Level.DEBUG); + final HiveMQContainer hivemqCe = new HiveMQContainer(DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3")) + .withLogLevel(Level.DEBUG); + // } - // eeVersion { + // hiveEEVersion { @Container - final HiveMQContainer hivemqEe = - new HiveMQContainer(DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3")) - .withLogLevel(Level.DEBUG); + final HiveMQContainer hivemqEe = new HiveMQContainer(DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3")) + .withLogLevel(Level.DEBUG); + // } // eeVersionWithControlCenter { @Container - final HiveMQContainer hivemqEeWithControLCenter = - new HiveMQContainer(DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3")) - .withLogLevel(Level.DEBUG) - .withHiveMQConfig(MountableFile.forClasspathResource("/inMemoryConfig.xml")) - .withControlCenter(); + final HiveMQContainer hivemqEeWithControlCenter = new HiveMQContainer( + DockerImageName.parse("hivemq/hivemq-ce").withTag("2021.3") + ) + .withLogLevel(Level.DEBUG) + .withHiveMQConfig(MountableFile.forClasspathResource("/inMemoryConfig.xml")) + .withControlCenter(); + // } // specificVersion { @Container final HiveMQContainer hivemqSpecificVersion = new HiveMQContainer(DockerImageName.parse("hivemq/hivemq-ce:2021.3")); + // } @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) public void test() throws Exception { - // mqtt5client { - final Mqtt5BlockingClient client = Mqtt5Client.builder() + final Mqtt5BlockingClient client = Mqtt5Client + .builder() .serverPort(hivemqCe.getMqttPort()) .serverHost(hivemqCe.getHost()) .buildBlocking(); diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/util/MyExtension.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/util/MyExtension.java index e707859be77..4c3991122d7 100755 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/util/MyExtension.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/util/MyExtension.java @@ -1,7 +1,6 @@ package org.testcontainers.hivemq.util; import com.hivemq.extension.sdk.api.ExtensionMain; -import org.jetbrains.annotations.NotNull; import com.hivemq.extension.sdk.api.interceptor.publish.PublishInboundInterceptor; import com.hivemq.extension.sdk.api.parameter.ExtensionStartInput; import com.hivemq.extension.sdk.api.parameter.ExtensionStartOutput; @@ -9,6 +8,7 @@ import com.hivemq.extension.sdk.api.parameter.ExtensionStopOutput; import com.hivemq.extension.sdk.api.services.Services; import com.hivemq.extension.sdk.api.services.intializer.ClientInitializer; +import org.jetbrains.annotations.NotNull; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; @@ -17,10 +17,14 @@ public class MyExtension implements ExtensionMain { @Override - public void extensionStart(final @NotNull ExtensionStartInput extensionStartInput, final @NotNull ExtensionStartOutput extensionStartOutput) { - + public void extensionStart( + final @NotNull ExtensionStartInput extensionStartInput, + final @NotNull ExtensionStartOutput extensionStartOutput + ) { final PublishInboundInterceptor publishInboundInterceptor = (publishInboundInput, publishInboundOutput) -> { - publishInboundOutput.getPublishPacket().setPayload(ByteBuffer.wrap("modified".getBytes(StandardCharsets.UTF_8))); + publishInboundOutput + .getPublishPacket() + .setPayload(ByteBuffer.wrap("modified".getBytes(StandardCharsets.UTF_8))); }; final ClientInitializer clientInitializer = (initializerInput, clientContext) -> { @@ -31,7 +35,8 @@ public void extensionStart(final @NotNull ExtensionStartInput extensionStartInpu } @Override - public void extensionStop(final @NotNull ExtensionStopInput extensionStopInput, final @NotNull ExtensionStopOutput extensionStopOutput) { - - } + public void extensionStop( + final @NotNull ExtensionStopInput extensionStopInput, + final @NotNull ExtensionStopOutput extensionStopOutput + ) {} } diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/util/MyExtensionWithSubclasses.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/util/MyExtensionWithSubclasses.java index abc24585358..9e4565b15c9 100755 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/util/MyExtensionWithSubclasses.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/util/MyExtensionWithSubclasses.java @@ -1,26 +1,31 @@ package org.testcontainers.hivemq.util; import com.hivemq.extension.sdk.api.ExtensionMain; -import org.jetbrains.annotations.NotNull; import com.hivemq.extension.sdk.api.parameter.ExtensionStartInput; import com.hivemq.extension.sdk.api.parameter.ExtensionStartOutput; import com.hivemq.extension.sdk.api.parameter.ExtensionStopInput; import com.hivemq.extension.sdk.api.parameter.ExtensionStopOutput; import com.hivemq.extension.sdk.api.services.Services; import com.hivemq.extension.sdk.api.services.intializer.ClientInitializer; +import org.jetbrains.annotations.NotNull; public class MyExtensionWithSubclasses implements ExtensionMain { @Override - public void extensionStart(final @NotNull ExtensionStartInput extensionStartInput, final @NotNull ExtensionStartOutput extensionStartOutput) { - - final ClientInitializer clientInitializer = (initializerInput, clientContext) -> clientContext.addPublishInboundInterceptor(new PublishModifier()); + public void extensionStart( + final @NotNull ExtensionStartInput extensionStartInput, + final @NotNull ExtensionStartOutput extensionStartOutput + ) { + final ClientInitializer clientInitializer = (initializerInput, clientContext) -> { + clientContext.addPublishInboundInterceptor(new PublishModifier()); + }; Services.initializerRegistry().setClientInitializer(clientInitializer); } @Override - public void extensionStop(final @NotNull ExtensionStopInput extensionStopInput, final @NotNull ExtensionStopOutput extensionStopOutput) { - - } + public void extensionStop( + final @NotNull ExtensionStopInput extensionStopInput, + final @NotNull ExtensionStopOutput extensionStopOutput + ) {} } diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/util/PublishModifier.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/util/PublishModifier.java index d33f29a9c10..ec5b63ee1e6 100755 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/util/PublishModifier.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/util/PublishModifier.java @@ -1,16 +1,22 @@ package org.testcontainers.hivemq.util; -import org.jetbrains.annotations.NotNull; import com.hivemq.extension.sdk.api.interceptor.publish.PublishInboundInterceptor; import com.hivemq.extension.sdk.api.interceptor.publish.parameter.PublishInboundInput; import com.hivemq.extension.sdk.api.interceptor.publish.parameter.PublishInboundOutput; +import org.jetbrains.annotations.NotNull; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; public class PublishModifier implements PublishInboundInterceptor { + @Override - public void onInboundPublish(final @NotNull PublishInboundInput publishInboundInput, final @NotNull PublishInboundOutput publishInboundOutput) { - publishInboundOutput.getPublishPacket().setPayload(ByteBuffer.wrap("modified".getBytes(StandardCharsets.UTF_8))); + public void onInboundPublish( + final @NotNull PublishInboundInput publishInboundInput, + final @NotNull PublishInboundOutput publishInboundOutput + ) { + publishInboundOutput + .getPublishPacket() + .setPayload(ByteBuffer.wrap("modified".getBytes(StandardCharsets.UTF_8))); } } diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/util/TestPublishModifiedUtil.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/util/TestPublishModifiedUtil.java index f2f1cfd692f..963570c07c4 100755 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/util/TestPublishModifiedUtil.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/util/TestPublishModifiedUtil.java @@ -14,27 +14,38 @@ public class TestPublishModifiedUtil { public static void testPublishModified(final int mqttPort, final @NotNull String host) throws Exception { final CompletableFuture publishReceived = new CompletableFuture<>(); - final Mqtt5BlockingClient publisher = Mqtt5Client.builder() + final Mqtt5BlockingClient publisher = Mqtt5Client + .builder() .serverPort(mqttPort) .serverHost(host) .identifier("publisher") .buildBlocking(); publisher.connect(); - final Mqtt5BlockingClient subscriber = Mqtt5Client.builder() + final Mqtt5BlockingClient subscriber = Mqtt5Client + .builder() .serverPort(mqttPort) .serverHost(host) .identifier("subscriber") .buildBlocking(); subscriber.connect(); subscriber.subscribeWith().topicFilter("test/topic").send(); - subscriber.toAsync().publishes(MqttGlobalPublishFilter.ALL, publish -> { - if (Arrays.equals(publish.getPayloadAsBytes(), "modified".getBytes(StandardCharsets.UTF_8))) { - publishReceived.complete(null); - } else { - publishReceived.completeExceptionally(new IllegalArgumentException("unexpected payload: " + new String(publish.getPayloadAsBytes()))); - } - }); + subscriber + .toAsync() + .publishes( + MqttGlobalPublishFilter.ALL, + publish -> { + if (Arrays.equals(publish.getPayloadAsBytes(), "modified".getBytes(StandardCharsets.UTF_8))) { + publishReceived.complete(null); + } else { + publishReceived.completeExceptionally( + new IllegalArgumentException( + "unexpected payload: " + new String(publish.getPayloadAsBytes()) + ) + ); + } + } + ); publisher.publishWith().topic("test/topic").payload("unmodified".getBytes(StandardCharsets.UTF_8)).send(); @@ -45,6 +56,4 @@ public static void testPublishModified(final int mqttPort, final @NotNull String subscriber.disconnect(); } } - - } diff --git a/modules/influxdb/src/main/java/org/testcontainers/containers/InfluxDBContainer.java b/modules/influxdb/src/main/java/org/testcontainers/containers/InfluxDBContainer.java index 7ebfbff1a86..40eea721b57 100644 --- a/modules/influxdb/src/main/java/org/testcontainers/containers/InfluxDBContainer.java +++ b/modules/influxdb/src/main/java/org/testcontainers/containers/InfluxDBContainer.java @@ -17,17 +17,22 @@ public class InfluxDBContainer> extends Gen public static final Integer INFLUXDB_PORT = 8086; private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("influxdb"); + private static final String DEFAULT_TAG = "1.4.3"; @Deprecated public static final String VERSION = DEFAULT_TAG; private boolean authEnabled = true; + private String admin = "admin"; + private String adminPassword = "password"; private String database; + private String username = "any"; + private String password = "any"; /** @@ -48,12 +53,12 @@ public InfluxDBContainer(final String version) { public InfluxDBContainer(final DockerImageName dockerImageName) { super(dockerImageName); - dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); - waitStrategy = new WaitAllStrategy() - .withStrategy(Wait.forHttp("/ping").withBasicCredentials(username, password).forStatusCode(204)) - .withStrategy(Wait.forListeningPort()); + waitStrategy = + new WaitAllStrategy() + .withStrategy(Wait.forHttp("/ping").withBasicCredentials(username, password).forStatusCode(204)) + .withStrategy(Wait.forListeningPort()); addExposedPort(INFLUXDB_PORT); } @@ -144,7 +149,6 @@ public SELF withPassword(final String password) { return self(); } - /** * @return a url to influxDb */ diff --git a/modules/influxdb/src/test/java/org/testcontainers/containers/InfluxDBContainerTest.java b/modules/influxdb/src/test/java/org/testcontainers/containers/InfluxDBContainerTest.java index 7acf030b5e4..c7fc71a3a19 100644 --- a/modules/influxdb/src/test/java/org/testcontainers/containers/InfluxDBContainerTest.java +++ b/modules/influxdb/src/test/java/org/testcontainers/containers/InfluxDBContainerTest.java @@ -4,14 +4,16 @@ import org.junit.ClassRule; import org.junit.Test; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; import static org.junit.Assert.assertThat; public class InfluxDBContainerTest { @ClassRule - public static InfluxDBContainer influxDBContainer = new InfluxDBContainer<>(InfluxDBTestImages.INFLUXDB_TEST_IMAGE); + public static InfluxDBContainer influxDBContainer = new InfluxDBContainer<>( + InfluxDBTestImages.INFLUXDB_TEST_IMAGE + ); @Test public void getUrl() { diff --git a/modules/influxdb/src/test/java/org/testcontainers/containers/InfluxDBContainerWithUserTest.java b/modules/influxdb/src/test/java/org/testcontainers/containers/InfluxDBContainerWithUserTest.java index b045ca297c1..a7ae347e7cc 100644 --- a/modules/influxdb/src/test/java/org/testcontainers/containers/InfluxDBContainerWithUserTest.java +++ b/modules/influxdb/src/test/java/org/testcontainers/containers/InfluxDBContainerWithUserTest.java @@ -9,17 +9,20 @@ import java.util.concurrent.TimeUnit; -import static org.hamcrest.CoreMatchers.hasItem; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.notNullValue; -import static org.hamcrest.CoreMatchers.nullValue; +import static org.hamcrest.Matchers.hasItem; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.Matchers.nullValue; import static org.junit.Assert.assertThat; public class InfluxDBContainerWithUserTest { private static final String TEST_VERSION = InfluxDBTestImages.INFLUXDB_TEST_IMAGE.getVersionPart(); + private static final String DATABASE = "test"; + private static final String USER = "test-user"; + private static final String PASSWORD = "test-password"; @Rule @@ -52,7 +55,8 @@ public void checkVersion() { public void queryForWriteAndRead() { InfluxDB influxDB = influxDBContainer.getNewInfluxDB(); - Point point = Point.measurement("cpu") + Point point = Point + .measurement("cpu") .time(System.currentTimeMillis(), TimeUnit.MILLISECONDS) .addField("idle", 90L) .addField("user", 9L) @@ -67,6 +71,5 @@ public void queryForWriteAndRead() { assertThat(actual.getError(), nullValue()); assertThat(actual.getResults(), notNullValue()); assertThat(actual.getResults().size(), is(1)); - } } diff --git a/modules/jdbc-test/src/main/java/org/testcontainers/db/AbstractContainerDatabaseTest.java b/modules/jdbc-test/src/main/java/org/testcontainers/db/AbstractContainerDatabaseTest.java index 6a5ca5094d6..6d3080c7ee3 100644 --- a/modules/jdbc-test/src/main/java/org/testcontainers/db/AbstractContainerDatabaseTest.java +++ b/modules/jdbc-test/src/main/java/org/testcontainers/db/AbstractContainerDatabaseTest.java @@ -4,11 +4,12 @@ import com.zaxxer.hikari.HikariDataSource; import org.testcontainers.containers.JdbcDatabaseContainer; -import javax.sql.DataSource; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; +import javax.sql.DataSource; + public abstract class AbstractContainerDatabaseTest { protected ResultSet performQuery(JdbcDatabaseContainer container, String sql) throws SQLException { diff --git a/modules/jdbc-test/src/main/java/org/testcontainers/jdbc/AbstractJDBCDriverTest.java b/modules/jdbc-test/src/main/java/org/testcontainers/jdbc/AbstractJDBCDriverTest.java index 4e5eb03bb49..5566db6244e 100644 --- a/modules/jdbc-test/src/main/java/org/testcontainers/jdbc/AbstractJDBCDriverTest.java +++ b/modules/jdbc-test/src/main/java/org/testcontainers/jdbc/AbstractJDBCDriverTest.java @@ -25,22 +25,19 @@ protected enum Options { CharacterSet, CustomIniFile, JDBCParams, - PmdKnownBroken + PmdKnownBroken, } @Parameter public String jdbcUrl; + @Parameter(1) public EnumSet options; public static void sampleInitFunction(Connection connection) throws SQLException { - connection.createStatement().execute("CREATE TABLE bar (\n" + - " foo VARCHAR(255)\n" + - ");"); + connection.createStatement().execute("CREATE TABLE bar (\n" + " foo VARCHAR(255)\n" + ");"); connection.createStatement().execute("INSERT INTO bar (foo) VALUES ('hello world');"); - connection.createStatement().execute("CREATE TABLE my_counter (\n" + - " n INT\n" + - ");"); + connection.createStatement().execute("CREATE TABLE my_counter (\n" + " n INT\n" + ");"); } @AfterClass @@ -79,67 +76,99 @@ private void performSimpleTest(HikariDataSource dataSource) throws SQLException query = "SELECT 1 FROM SYSIBM.SYSDUMMY1"; } - boolean result = new QueryRunner(dataSource, options.contains(Options.PmdKnownBroken)).query(query, rs -> { - rs.next(); - int resultSetInt = rs.getInt(1); - assertEquals("A basic SELECT query succeeds", 1, resultSetInt); - return true; - }); + boolean result = new QueryRunner(dataSource, options.contains(Options.PmdKnownBroken)) + .query( + query, + rs -> { + rs.next(); + int resultSetInt = rs.getInt(1); + assertEquals("A basic SELECT query succeeds", 1, resultSetInt); + return true; + } + ); assertTrue("The database returned a record as expected", result); } private void performTestForScriptedSchema(HikariDataSource dataSource) throws SQLException { - boolean result = new QueryRunner(dataSource).query("SELECT foo FROM bar WHERE foo LIKE '%world'", rs -> { - rs.next(); - String resultSetString = rs.getString(1); - assertEquals("A basic SELECT query succeeds where the schema has been applied from a script", "hello world", resultSetString); - return true; - }); + boolean result = new QueryRunner(dataSource) + .query( + "SELECT foo FROM bar WHERE foo LIKE '%world'", + rs -> { + rs.next(); + String resultSetString = rs.getString(1); + assertEquals( + "A basic SELECT query succeeds where the schema has been applied from a script", + "hello world", + resultSetString + ); + return true; + } + ); assertTrue("The database returned a record as expected", result); } private void performTestForJDBCParamUsage(HikariDataSource dataSource) throws SQLException { - boolean result = new QueryRunner(dataSource).query("select CURRENT_USER", rs -> { - rs.next(); - String resultUser = rs.getString(1); - // Not all databases (eg. Postgres) return @% at the end of user name. We just need to make sure the user name matches. - if (resultUser.endsWith("@%")) { - resultUser = resultUser.substring(0, resultUser.length() - 2); - } - assertEquals("User from query param is created.", "someuser", resultUser); - return true; - }); + boolean result = new QueryRunner(dataSource) + .query( + "select CURRENT_USER", + rs -> { + rs.next(); + String resultUser = rs.getString(1); + // Not all databases (eg. Postgres) return @% at the end of user name. We just need to make sure the user name matches. + if (resultUser.endsWith("@%")) { + resultUser = resultUser.substring(0, resultUser.length() - 2); + } + assertEquals("User from query param is created.", "someuser", resultUser); + return true; + } + ); assertTrue("The database returned a record as expected", result); String databaseQuery = "SELECT DATABASE()"; // Postgres does not have Database() as a function String databaseType = ConnectionUrl.newInstance(jdbcUrl).getDatabaseType(); - if (databaseType.equalsIgnoreCase("postgresql") || + if ( + databaseType.equalsIgnoreCase("postgresql") || databaseType.equalsIgnoreCase("postgis") || - databaseType.equalsIgnoreCase("timescaledb")) { + databaseType.equalsIgnoreCase("timescaledb") + ) { databaseQuery = "SELECT CURRENT_DATABASE()"; } - result = new QueryRunner(dataSource).query(databaseQuery, rs -> { - rs.next(); - String resultDB = rs.getString(1); - assertEquals("Database name from URL String is used.", "databasename", resultDB); - return true; - }); + result = + new QueryRunner(dataSource) + .query( + databaseQuery, + rs -> { + rs.next(); + String resultDB = rs.getString(1); + assertEquals("Database name from URL String is used.", "databasename", resultDB); + return true; + } + ); assertTrue("The database returned a record as expected", result); } - private void performTestForCharacterEncodingForInitialScriptConnection(HikariDataSource dataSource) throws SQLException { - boolean result = new QueryRunner(dataSource).query("SELECT foo FROM bar WHERE foo LIKE '%мир'", rs -> { - rs.next(); - String resultSetString = rs.getString(1); - assertEquals("A SELECT query succeed and the correct charset has been applied for the init script", "привет мир", resultSetString); - return true; - }); + private void performTestForCharacterEncodingForInitialScriptConnection(HikariDataSource dataSource) + throws SQLException { + boolean result = new QueryRunner(dataSource) + .query( + "SELECT foo FROM bar WHERE foo LIKE '%мир'", + rs -> { + rs.next(); + String resultSetString = rs.getString(1); + assertEquals( + "A SELECT query succeed and the correct charset has been applied for the init script", + "привет мир", + resultSetString + ); + return true; + } + ); assertTrue("The database returned a record as expected", result); } @@ -160,12 +189,19 @@ private void performSimpleTestWithCharacterSet(String jdbcUrl) throws SQLExcepti private HikariDataSource verifyCharacterSet(String jdbcUrl) throws SQLException { HikariDataSource dataSource = getDataSource(jdbcUrl, 1); - boolean result = new QueryRunner(dataSource).query("SHOW VARIABLES LIKE 'character\\_set\\_connection'", rs -> { - rs.next(); - String resultSetString = rs.getString(2); - assertTrue("Passing query parameters to set DB connection encoding is successful", resultSetString.startsWith("utf8")); - return true; - }); + boolean result = new QueryRunner(dataSource) + .query( + "SHOW VARIABLES LIKE 'character\\_set\\_connection'", + rs -> { + rs.next(); + String resultSetString = rs.getString(2); + assertTrue( + "Passing query parameters to set DB connection encoding is successful", + resultSetString.startsWith("utf8") + ); + return true; + } + ); assertTrue("The database returned a record as expected", result); return dataSource; diff --git a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java index 5e5342f1ee1..84d242a29c7 100644 --- a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java +++ b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java @@ -20,23 +20,29 @@ import java.util.Map; import java.util.Properties; import java.util.concurrent.Future; - -import static java.util.stream.Collectors.joining; +import java.util.stream.Collectors; /** * Base class for containers that expose a JDBC connection * * @author richardnorth */ -public abstract class JdbcDatabaseContainer> extends GenericContainer implements LinkableContainer { +public abstract class JdbcDatabaseContainer> + extends GenericContainer + implements LinkableContainer { private static final Object DRIVER_LOAD_MUTEX = new Object(); + private Driver driver; + private String initScriptPath; + protected Map parameters = new HashMap<>(); + protected Map urlParameters = new HashMap<>(); private int startupTimeoutSeconds = 120; + private int connectTimeoutSeconds = 120; private static final String QUERY_PARAM_SEPARATOR = "&"; @@ -135,7 +141,12 @@ public SELF withInitScript(String initScriptPath) { @SneakyThrows(InterruptedException.class) @Override protected void waitUntilContainerStarted() { - logger().info("Waiting for database connection to become available at {} using query '{}'", getJdbcUrl(), getTestQueryString()); + logger() + .info( + "Waiting for database connection to become available at {} using query '{}'", + getJdbcUrl(), + getTestQueryString() + ); // Repeatedly try and open a connection to the DB and execute a test query long start = System.currentTimeMillis(); @@ -144,8 +155,7 @@ protected void waitUntilContainerStarted() { if (!isRunning()) { Thread.sleep(100L); } else { - try (Connection connection = createConnection(""); - Statement statement = connection.createStatement()) { + try (Connection connection = createConnection(""); Statement statement = connection.createStatement()) { boolean testQuerySucceeded = statement.execute(this.getTestQueryString()); if (testQuerySucceeded) { logger().info("Container is started (JDBC URL: {})", this.getJdbcUrl()); @@ -163,8 +173,10 @@ protected void waitUntilContainerStarted() { } throw new IllegalStateException( - String.format("Container is started, but cannot be accessed by (JDBC URL: %s), please check container logs", - this.getJdbcUrl()) + String.format( + "Container is started, but cannot be accessed by (JDBC URL: %s), please check container logs", + this.getJdbcUrl() + ) ); } @@ -179,7 +191,6 @@ protected void containerIsStarted(InspectContainerResponse containerInfo) { * @return a JDBC Driver */ public Driver getJdbcDriverInstance() throws NoDriverFoundException { - synchronized (DRIVER_LOAD_MUTEX) { if (driver == null) { try { @@ -215,7 +226,13 @@ public Connection createConnection(String queryString) throws SQLException, NoDr // give up if we hit the time limit or the container stops running for some reason while (System.currentTimeMillis() < start + (1000 * connectTimeoutSeconds) && isRunning()) { try { - logger().debug("Trying to create JDBC connection using {} to {} with properties: {}", driver.getClass().getName(), url, info); + logger() + .debug( + "Trying to create JDBC connection using {} to {} with properties: {}", + driver.getClass().getName(), + url, + info + ); return jdbcDriverInstance.connect(url, info); } catch (SQLException e) { @@ -261,15 +278,18 @@ protected String constructUrlParameters(String startCharacter, String delimiter) protected String constructUrlParameters(String startCharacter, String delimiter, String endCharacter) { String urlParameters = ""; if (!this.urlParameters.isEmpty()) { - String additionalParameters = this.urlParameters.entrySet().stream() - .map(Object::toString) - .collect(joining(delimiter)); + String additionalParameters = + this.urlParameters.entrySet().stream().map(Object::toString).collect(Collectors.joining(delimiter)); urlParameters = startCharacter + additionalParameters + endCharacter; } return urlParameters; } - protected void optionallyMapResourceParameterAsVolume(@NotNull String paramName, @NotNull String pathNameInContainer, @NotNull String defaultResource) { + protected void optionallyMapResourceParameterAsVolume( + @NotNull String paramName, + @NotNull String pathNameInContainer, + @NotNull String defaultResource + ) { String resourceName = parameters.getOrDefault(paramName, defaultResource); if (resourceName != null) { @@ -319,6 +339,7 @@ protected DatabaseDelegate getDatabaseDelegate() { } public static class NoDriverFoundException extends RuntimeException { + public NoDriverFoundException(String message, Throwable e) { super(message, e); } diff --git a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainerProvider.java b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainerProvider.java index f8694306384..3cd74dc98d4 100644 --- a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainerProvider.java +++ b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainerProvider.java @@ -1,8 +1,7 @@ package org.testcontainers.containers; -import org.testcontainers.jdbc.ConnectionUrl; - import lombok.extern.slf4j.Slf4j; +import org.testcontainers.jdbc.ConnectionUrl; import java.util.Objects; @@ -26,9 +25,12 @@ public abstract class JdbcDatabaseContainerProvider { * @return Instance of {@link JdbcDatabaseContainer} */ public JdbcDatabaseContainer newInstance() { - log.warn("No explicit version tag was provided in JDBC URL and this class ({}) does not " + + log.warn( + "No explicit version tag was provided in JDBC URL and this class ({}) does not " + "override newInstance() to set a default tag. `latest` will be used but results may " + - "be unreliable!", this.getClass().getCanonicalName()); + "be unreliable!", + this.getClass().getCanonicalName() + ); return this.newInstance("latest"); } @@ -55,7 +57,11 @@ public JdbcDatabaseContainer newInstance(ConnectionUrl url) { return result; } - protected JdbcDatabaseContainer newInstanceFromConnectionUrl(ConnectionUrl connectionUrl, final String userParamName, final String pwdParamName) { + protected JdbcDatabaseContainer newInstanceFromConnectionUrl( + ConnectionUrl connectionUrl, + final String userParamName, + final String pwdParamName + ) { Objects.requireNonNull(connectionUrl, "Connection URL cannot be null"); final String databaseName = connectionUrl.getDatabaseName().orElse("test"); diff --git a/modules/jdbc/src/main/java/org/testcontainers/jdbc/ConnectionDelegate.java b/modules/jdbc/src/main/java/org/testcontainers/jdbc/ConnectionDelegate.java index e7f59b2df4f..47d3cadc823 100644 --- a/modules/jdbc/src/main/java/org/testcontainers/jdbc/ConnectionDelegate.java +++ b/modules/jdbc/src/main/java/org/testcontainers/jdbc/ConnectionDelegate.java @@ -1,13 +1,13 @@ package org.testcontainers.jdbc; import lombok.RequiredArgsConstructor; -import lombok.Value; import lombok.experimental.Delegate; import java.sql.Connection; @RequiredArgsConstructor class ConnectionDelegate implements Connection { + @Delegate private final Connection delegate; } diff --git a/modules/jdbc/src/main/java/org/testcontainers/jdbc/ConnectionUrl.java b/modules/jdbc/src/main/java/org/testcontainers/jdbc/ConnectionUrl.java index 3354ed45c39..b4aef740da0 100644 --- a/modules/jdbc/src/main/java/org/testcontainers/jdbc/ConnectionUrl.java +++ b/modules/jdbc/src/main/java/org/testcontainers/jdbc/ConnectionUrl.java @@ -1,5 +1,10 @@ package org.testcontainers.jdbc; +import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import org.testcontainers.UnstableAPI; + import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -10,13 +15,6 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import lombok.AllArgsConstructor; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import org.testcontainers.UnstableAPI; - -import static java.util.stream.Collectors.toMap; - /** * This is an Immutable class holding JDBC Connection Url and its parsed components, used by {@link ContainerDatabaseDriver}. *

@@ -60,6 +58,7 @@ public class ConnectionUrl { private Map containerParameters; private Map queryParameters; + private Map tmpfsOptions = new HashMap<>(); public static ConnectionUrl newInstance(final String url) { @@ -94,7 +93,9 @@ private void parseUrl() { //Try for Oracle pattern urlMatcher = Patterns.ORACLE_URL_MATCHING_PATTERN.matcher(this.getUrl()); if (!urlMatcher.matches()) { - throw new IllegalArgumentException("JDBC URL matches jdbc:tc: prefix but the database or tag name could not be identified"); + throw new IllegalArgumentException( + "JDBC URL matches jdbc:tc: prefix but the database or tag name could not be identified" + ); } } databaseType = urlMatcher.group("databaseType"); @@ -113,9 +114,10 @@ private void parseUrl() { databaseName = Optional.of(dbInstanceMatcher.group("databaseName")); } - queryParameters = Collections.unmodifiableMap( - parseQueryParameters( - Optional.ofNullable(urlMatcher.group("queryParameters")).orElse(""))); + queryParameters = + Collections.unmodifiableMap( + parseQueryParameters(Optional.ofNullable(urlMatcher.group("queryParameters")).orElse("")) + ); String query = queryParameters .entrySet() @@ -133,7 +135,6 @@ private void parseUrl() { tmpfsOptions = parseTmpfsOptions(containerParameters); - initScriptPath = Optional.ofNullable(containerParameters.get("TC_INITSCRIPT")); reusable = Boolean.parseBoolean(containerParameters.get("TC_REUSABLE")); @@ -145,7 +146,6 @@ private void parseUrl() { Matcher daemonMatcher = Patterns.DAEMON_MATCHING_PATTERN.matcher(this.getUrl()); inDaemonMode = daemonMatcher.matches() && Boolean.parseBoolean(daemonMatcher.group(2)); - } private Map parseTmpfsOptions(Map containerParameters) { @@ -155,10 +155,9 @@ private Map parseTmpfsOptions(Map containerParam String tmpfsOptions = containerParameters.get("TC_TMPFS"); - return Stream.of(tmpfsOptions.split(",")) - .collect(toMap( - string -> string.split(":")[0], - string -> string.split(":")[1])); + return Stream + .of(tmpfsOptions.split(",")) + .collect(Collectors.toMap(string -> string.split(":")[0], string -> string.split(":")[1])); } /** @@ -167,7 +166,6 @@ private Map parseTmpfsOptions(Map containerParam * @return {@link Map} */ private Map parseContainerParameters() { - Map results = new HashMap<>(); Matcher matcher = Patterns.TC_PARAM_MATCHING_PATTERN.matcher(this.getUrl()); @@ -186,14 +184,14 @@ private Map parseContainerParameters() { * @return {@link Map} */ private Map parseQueryParameters(final String queryString) { - Map results = new HashMap<>(); Matcher matcher = Patterns.QUERY_PARAM_MATCHING_PATTERN.matcher(queryString); while (matcher.find()) { String key = matcher.group(1); String value = matcher.group(2); - if (!key.matches(Patterns.TC_PARAM_NAME_PATTERN)) + if (!key.matches(Patterns.TC_PARAM_NAME_PATTERN)) { results.put(key, value); + } } return results; @@ -211,36 +209,36 @@ public Map getTmpfsOptions() { public interface Patterns { Pattern URL_MATCHING_PATTERN = Pattern.compile( "jdbc:tc:" + - "(?[a-z0-9]+)" + - "(:(?[^:]+))?" + - "://" + - "(?[^?]+)" + - "(?\\?.*)?" + "(?[a-z0-9]+)" + + "(:(?[^:]+))?" + + "://" + + "(?[^?]+)" + + "(?\\?.*)?" ); Pattern ORACLE_URL_MATCHING_PATTERN = Pattern.compile( "jdbc:tc:" + - "(?[a-z]+)" + - "(:(?(?!thin).+))?:thin:(//)?" + - "(" + - "(?[^:" + - "?^/]+)/(?[^?^/]+)" + - ")?" + - "@" + - "(?[^?]+)" + - "(?\\?.*)?" + "(?[a-z]+)" + + "(:(?(?!thin).+))?:thin:(//)?" + + "(" + + "(?[^:" + + "?^/]+)/(?[^?^/]+)" + + ")?" + + "@" + + "(?[^?]+)" + + "(?\\?.*)?" ); //Matches to part of string - hostname:port/databasename Pattern DB_INSTANCE_MATCHING_PATTERN = Pattern.compile( "(?[^:]+)" + - "(:(?[0-9]+))?" + - "(" + - "(?[:/])" + - "|" + - ";databaseName=" + - ")" + - "(?[^\\\\?]+)" + "(:(?[0-9]+))?" + + "(" + + "(?[:/])" + + "|" + + ";databaseName=" + + ")" + + "(?[^\\\\?]+)" ); Pattern DAEMON_MATCHING_PATTERN = Pattern.compile(".*([?&]?)TC_DAEMON=([^?&]+).*"); @@ -251,24 +249,27 @@ public interface Patterns { @Deprecated Pattern INITSCRIPT_MATCHING_PATTERN = Pattern.compile(".*([?&]?)TC_INITSCRIPT=([^?&]+).*"); - Pattern INITFUNCTION_MATCHING_PATTERN = Pattern.compile(".*([?&]?)TC_INITFUNCTION=" + + Pattern INITFUNCTION_MATCHING_PATTERN = Pattern.compile( + ".*([?&]?)TC_INITFUNCTION=" + "((\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*\\.)*\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*)" + "::" + "(\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*)" + - ".*"); + ".*" + ); String TC_PARAM_NAME_PATTERN = "(TC_[A-Z_]+)"; Pattern TC_PARAM_MATCHING_PATTERN = Pattern.compile(TC_PARAM_NAME_PATTERN + "=([^?&]+)"); Pattern QUERY_PARAM_MATCHING_PATTERN = Pattern.compile("([^?&=]+)=([^?&]*)"); - } @Getter @AllArgsConstructor public class InitFunctionDef { + private String className; + private String methodName; } } diff --git a/modules/jdbc/src/main/java/org/testcontainers/jdbc/ConnectionWrapper.java b/modules/jdbc/src/main/java/org/testcontainers/jdbc/ConnectionWrapper.java index 3e1f67c5b86..4f245590f78 100644 --- a/modules/jdbc/src/main/java/org/testcontainers/jdbc/ConnectionWrapper.java +++ b/modules/jdbc/src/main/java/org/testcontainers/jdbc/ConnectionWrapper.java @@ -17,7 +17,7 @@ public void close() throws SQLException { super.close(); try { closeCallback.run(); - } catch(Exception e) { + } catch (Exception e) { e.printStackTrace(); } } diff --git a/modules/jdbc/src/main/java/org/testcontainers/jdbc/ContainerDatabaseDriver.java b/modules/jdbc/src/main/java/org/testcontainers/jdbc/ContainerDatabaseDriver.java index 4b3a7ba98f7..23ed9d78b25 100644 --- a/modules/jdbc/src/main/java/org/testcontainers/jdbc/ContainerDatabaseDriver.java +++ b/modules/jdbc/src/main/java/org/testcontainers/jdbc/ContainerDatabaseDriver.java @@ -7,7 +7,6 @@ import org.testcontainers.delegate.DatabaseDelegate; import org.testcontainers.ext.ScriptUtils; -import javax.script.ScriptException; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -17,6 +16,8 @@ import java.util.*; import java.util.logging.Logger; +import javax.script.ScriptException; + /** * Test Containers JDBC proxy driver. This driver will handle JDBC URLs of the form: *

@@ -38,9 +39,13 @@ public class ContainerDatabaseDriver implements Driver { private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(ContainerDatabaseDriver.class); private Driver delegate; + private static final Map> containerConnections = new HashMap<>(); + private static final Map jdbcUrlContainerCache = new HashMap<>(); + private static final Set initializedContainers = new HashSet<>(); + private static final String FILE_PATH_PREFIX = "file:"; static { @@ -62,7 +67,6 @@ public boolean acceptsURL(String url) throws SQLException { @Override public synchronized Connection connect(String url, final Properties info) throws SQLException { - /* The driver should return "null" if it realizes it is the wrong kind of driver to connect to the given URL. */ @@ -73,7 +77,6 @@ public synchronized Connection connect(String url, final Properties info) throws ConnectionUrl connectionUrl = ConnectionUrl.newInstance(url); synchronized (jdbcUrlContainerCache) { - String queryString = connectionUrl.getQueryString().orElse(""); /* If we already have a running container for this exact connection string, we want to connect @@ -81,7 +84,6 @@ public synchronized Connection connect(String url, final Properties info) throws */ JdbcDatabaseContainer container = jdbcUrlContainerCache.get(connectionUrl.getUrl()); if (container == null) { - LOGGER.debug("Container not found in cache, creating new instance"); Map parameters = connectionUrl.getContainerParameters(); @@ -89,7 +91,9 @@ public synchronized Connection connect(String url, final Properties info) throws /* Find a matching container type using ServiceLoader. */ - ServiceLoader databaseContainers = ServiceLoader.load(JdbcDatabaseContainerProvider.class); + ServiceLoader databaseContainers = ServiceLoader.load( + JdbcDatabaseContainerProvider.class + ); for (JdbcDatabaseContainerProvider candidateContainerType : databaseContainers) { if (candidateContainerType.supports(connectionUrl.getDatabaseType())) { container = candidateContainerType.newInstance(connectionUrl); @@ -98,7 +102,9 @@ public synchronized Connection connect(String url, final Properties info) throws } } if (container == null) { - throw new UnsupportedOperationException("Database name " + connectionUrl.getDatabaseType() + " not supported"); + throw new UnsupportedOperationException( + "Database name " + connectionUrl.getDatabaseType() + " not supported" + ); } /* @@ -138,7 +144,6 @@ public synchronized Connection connect(String url, final Properties info) throws } } - /** * Wrap the connection, setting up a callback to be called when the connection is closed. *

@@ -149,25 +154,34 @@ public synchronized Connection connect(String url, final Properties info) throws * @param connectionUrl {@link ConnectionUrl} instance representing JDBC Url for this connection * @return the connection, wrapped */ - private Connection wrapConnection(final Connection connection, final JdbcDatabaseContainer container, final ConnectionUrl connectionUrl) { - + private Connection wrapConnection( + final Connection connection, + final JdbcDatabaseContainer container, + final ConnectionUrl connectionUrl + ) { final boolean isDaemon = connectionUrl.isInDaemonMode() || connectionUrl.isReusable(); - Set connections = containerConnections.computeIfAbsent(container.getContainerId(), k -> new HashSet<>()); + Set connections = containerConnections.computeIfAbsent( + container.getContainerId(), + k -> new HashSet<>() + ); connections.add(connection); final Set finalConnections = connections; - return new ConnectionWrapper(connection, () -> { - finalConnections.remove(connection); - if (!isDaemon && finalConnections.isEmpty()) { - synchronized (jdbcUrlContainerCache) { - container.stop(); - jdbcUrlContainerCache.remove(connectionUrl.getUrl()); + return new ConnectionWrapper( + connection, + () -> { + finalConnections.remove(connection); + if (!isDaemon && finalConnections.isEmpty()) { + synchronized (jdbcUrlContainerCache) { + container.stop(); + jdbcUrlContainerCache.remove(connectionUrl.getUrl()); + } } } - }); + ); } /** @@ -177,7 +191,8 @@ private Connection wrapConnection(final Connection connection, final JdbcDatabas * @param databaseDelegate database delegate to apply init scripts to the database * @throws SQLException on script or DB error */ - private void runInitScriptIfRequired(final ConnectionUrl connectionUrl, DatabaseDelegate databaseDelegate) throws SQLException { + private void runInitScriptIfRequired(final ConnectionUrl connectionUrl, DatabaseDelegate databaseDelegate) + throws SQLException { if (connectionUrl.getInitScriptPath().isPresent()) { String initScriptPath = connectionUrl.getInitScriptPath().get(); try { @@ -191,7 +206,9 @@ private void runInitScriptIfRequired(final ConnectionUrl connectionUrl, Database } if (resource == null) { LOGGER.warn("Could not load classpath init script: {}", initScriptPath); - throw new SQLException("Could not load classpath init script: " + initScriptPath + ". Resource not found."); + throw new SQLException( + "Could not load classpath init script: " + initScriptPath + ". Resource not found." + ); } String sql = IOUtils.toString(resource, StandardCharsets.UTF_8); @@ -213,7 +230,8 @@ private void runInitScriptIfRequired(final ConnectionUrl connectionUrl, Database * @param connection JDBC connection to apply init functions to. * @throws SQLException on script or DB error */ - private void runInitFunctionIfRequired(final ConnectionUrl connectionUrl, Connection connection) throws SQLException { + private void runInitFunctionIfRequired(final ConnectionUrl connectionUrl, Connection connection) + throws SQLException { if (connectionUrl.getInitFunction().isPresent()) { String className = connectionUrl.getInitFunction().get().getClassName(); String methodName = connectionUrl.getInitFunction().get().getMethodName(); @@ -223,7 +241,9 @@ private void runInitFunctionIfRequired(final ConnectionUrl connectionUrl, Connec Method method = initFunctionClazz.getMethod(methodName, Connection.class); method.invoke(null, connection); - } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + } catch ( + ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e + ) { LOGGER.error("Error while executing init function: {}::{}", className, methodName, e); throw new SQLException("Error while executing init function: " + className + "::" + methodName, e); } @@ -270,7 +290,6 @@ public static void killContainers() { containerConnections.clear(); initializedContainers.clear(); } - } /** diff --git a/modules/jdbc/src/main/java/org/testcontainers/jdbc/JdbcDatabaseDelegate.java b/modules/jdbc/src/main/java/org/testcontainers/jdbc/JdbcDatabaseDelegate.java index 6173a8f6a37..7ed2f7ffde1 100644 --- a/modules/jdbc/src/main/java/org/testcontainers/jdbc/JdbcDatabaseDelegate.java +++ b/modules/jdbc/src/main/java/org/testcontainers/jdbc/JdbcDatabaseDelegate.java @@ -18,6 +18,7 @@ public class JdbcDatabaseDelegate extends AbstractDatabaseDelegate { private JdbcDatabaseContainer container; + private String queryString; public JdbcDatabaseDelegate(JdbcDatabaseContainer container, String queryString) { @@ -35,16 +36,27 @@ protected Statement createNewConnection() { } } - @Override - public void execute(String statement, String scriptPath, int lineNumber, boolean continueOnError, boolean ignoreFailedDrops) { + public void execute( + String statement, + String scriptPath, + int lineNumber, + boolean continueOnError, + boolean ignoreFailedDrops + ) { try { boolean rowsAffected = getConnection().execute(statement); log.debug("{} returned as updateCount for SQL: {}", rowsAffected, statement); } catch (SQLException ex) { boolean dropStatement = statement.trim().toLowerCase().startsWith("drop"); if (continueOnError || (dropStatement && ignoreFailedDrops)) { - log.debug("Failed to execute SQL script statement at line {} of resource {}: {}", lineNumber, scriptPath, statement, ex); + log.debug( + "Failed to execute SQL script statement at line {} of resource {}: {}", + lineNumber, + scriptPath, + statement, + ex + ); } else { throw new ScriptUtils.ScriptStatementFailedException(statement, lineNumber, scriptPath, ex); } diff --git a/modules/jdbc/src/main/java/org/testcontainers/jdbc/ext/ScriptUtils.java b/modules/jdbc/src/main/java/org/testcontainers/jdbc/ext/ScriptUtils.java index 76b7a044c9d..c6a3c0fa3ed 100644 --- a/modules/jdbc/src/main/java/org/testcontainers/jdbc/ext/ScriptUtils.java +++ b/modules/jdbc/src/main/java/org/testcontainers/jdbc/ext/ScriptUtils.java @@ -18,10 +18,11 @@ import org.testcontainers.jdbc.ContainerLessJdbcDelegate; -import javax.script.ScriptException; import java.sql.Connection; import java.util.List; +import javax.script.ScriptException; + /** * Wrapper for database-agnostic ScriptUtils * @@ -68,9 +69,24 @@ private ScriptUtils() { * @see org.testcontainers.ext.ScriptUtils * @deprecated Needed only to keep binary compatibility for this internal API. Consider using database-agnostic ScriptUtils */ - public static void splitSqlScript(String resource, String script, String separator, String commentPrefix, - String blockCommentStartDelimiter, String blockCommentEndDelimiter, List statements) { - org.testcontainers.ext.ScriptUtils.splitSqlScript(resource, script, separator, commentPrefix, blockCommentStartDelimiter, blockCommentEndDelimiter, statements); + public static void splitSqlScript( + String resource, + String script, + String separator, + String commentPrefix, + String blockCommentStartDelimiter, + String blockCommentEndDelimiter, + List statements + ) { + org.testcontainers.ext.ScriptUtils.splitSqlScript( + resource, + script, + separator, + commentPrefix, + blockCommentStartDelimiter, + blockCommentEndDelimiter, + statements + ); } /** @@ -85,18 +101,40 @@ public static boolean containsSqlScriptDelimiters(String script, String delim) { * @see org.testcontainers.ext.ScriptUtils * @deprecated Needed only to keep binary compatibility for this internal API. Consider using database-agnostic ScriptUtils */ - public static void executeSqlScript(Connection connection, String scriptPath, String script) throws ScriptException { - org.testcontainers.ext.ScriptUtils.executeDatabaseScript(new ContainerLessJdbcDelegate(connection), scriptPath, script); + public static void executeSqlScript(Connection connection, String scriptPath, String script) + throws ScriptException { + org.testcontainers.ext.ScriptUtils.executeDatabaseScript( + new ContainerLessJdbcDelegate(connection), + scriptPath, + script + ); } /** * @see org.testcontainers.ext.ScriptUtils * @deprecated Needed only to keep binary compatibility for this internal API. Consider using database-agnostic ScriptUtils */ - public static void executeSqlScript(Connection connection, String scriptPath, String script, boolean continueOnError, - boolean ignoreFailedDrops, String commentPrefix, String separator, String blockCommentStartDelimiter, - String blockCommentEndDelimiter) throws ScriptException { - org.testcontainers.ext.ScriptUtils.executeDatabaseScript(new ContainerLessJdbcDelegate(connection), scriptPath, - script, continueOnError, ignoreFailedDrops, commentPrefix, separator, blockCommentStartDelimiter, blockCommentEndDelimiter); + public static void executeSqlScript( + Connection connection, + String scriptPath, + String script, + boolean continueOnError, + boolean ignoreFailedDrops, + String commentPrefix, + String separator, + String blockCommentStartDelimiter, + String blockCommentEndDelimiter + ) throws ScriptException { + org.testcontainers.ext.ScriptUtils.executeDatabaseScript( + new ContainerLessJdbcDelegate(connection), + scriptPath, + script, + continueOnError, + ignoreFailedDrops, + commentPrefix, + separator, + blockCommentStartDelimiter, + blockCommentEndDelimiter + ); } -} \ No newline at end of file +} diff --git a/modules/jdbc/src/test/java/org/testcontainers/containers/JdbcDatabaseContainerTest.java b/modules/jdbc/src/test/java/org/testcontainers/containers/JdbcDatabaseContainerTest.java index 9f0cf514558..1dc6646bd17 100644 --- a/modules/jdbc/src/test/java/org/testcontainers/containers/JdbcDatabaseContainerTest.java +++ b/modules/jdbc/src/test/java/org/testcontainers/containers/JdbcDatabaseContainerTest.java @@ -1,11 +1,12 @@ package org.testcontainers.containers; -import java.sql.Connection; -import java.sql.SQLException; import lombok.NonNull; import org.junit.Test; import org.slf4j.Logger; +import java.sql.Connection; +import java.sql.SQLException; + import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.mockito.Mockito.mock; diff --git a/modules/jdbc/src/test/java/org/testcontainers/jdbc/ConnectionUrlDriversTests.java b/modules/jdbc/src/test/java/org/testcontainers/jdbc/ConnectionUrlDriversTests.java index 12ca5c28c3d..9b88e6b503d 100644 --- a/modules/jdbc/src/test/java/org/testcontainers/jdbc/ConnectionUrlDriversTests.java +++ b/modules/jdbc/src/test/java/org/testcontainers/jdbc/ConnectionUrlDriversTests.java @@ -5,9 +5,9 @@ import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameter; +import java.util.Arrays; import java.util.Optional; -import static java.util.Arrays.asList; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; /** @@ -20,35 +20,106 @@ public class ConnectionUrlDriversTests { @Parameter public String jdbcUrl; + @Parameter(1) public String databaseType; + @Parameter(2) public Optional tag; + @Parameter(3) public String dbHostString; + @Parameter(4) public String databaseName; @Parameterized.Parameters(name = "{index} - {0}") public static Iterable data() { - return asList( - new Object[][]{ - {"jdbc:tc:mysql:5.7.34://hostname/test", "mysql", Optional.of("5.7.34"), "hostname/test", "test"}, - {"jdbc:tc:mysql://hostname/test", "mysql", Optional.empty(), "hostname/test", "test"}, - {"jdbc:tc:postgresql:1.2.3://hostname/test", "postgresql", Optional.of("1.2.3"), "hostname/test", "test"}, - {"jdbc:tc:postgresql://hostname/test", "postgresql", Optional.empty(), "hostname/test", "test"}, - {"jdbc:tc:sqlserver:1.2.3://localhost;instance=SQLEXPRESS:1433;databaseName=test", "sqlserver", Optional.of("1.2.3"), "localhost;instance=SQLEXPRESS:1433;databaseName=test", "test"}, - {"jdbc:tc:sqlserver://localhost;instance=SQLEXPRESS:1433;databaseName=test", "sqlserver", Optional.empty(), "localhost;instance=SQLEXPRESS:1433;databaseName=test", "test"}, - {"jdbc:tc:mariadb:1.2.3://localhost:3306/test", "mariadb", Optional.of("1.2.3"), "localhost:3306/test", "test"}, - {"jdbc:tc:mariadb://localhost:3306/test", "mariadb", Optional.empty(), "localhost:3306/test", "test"}, - {"jdbc:tc:oracle:1.2.3:thin://@localhost:1521/test", "oracle", Optional.of("1.2.3"), "localhost:1521/test", "test"}, - {"jdbc:tc:oracle:1.2.3:thin:@localhost:1521/test", "oracle", Optional.of("1.2.3"), "localhost:1521/test", "test"}, - {"jdbc:tc:oracle:thin:@localhost:1521/test", "oracle", Optional.empty(), "localhost:1521/test", "test"}, - {"jdbc:tc:oracle:1.2.3:thin:@localhost:1521:test", "oracle", Optional.of("1.2.3"), "localhost:1521:test", "test"}, - {"jdbc:tc:oracle:1.2.3:thin://@localhost:1521:test", "oracle", Optional.of("1.2.3"), "localhost:1521:test", "test"}, - {"jdbc:tc:oracle:1.2.3-anything:thin://@localhost:1521:test", "oracle", Optional.of("1.2.3-anything"), "localhost:1521:test", "test"}, - {"jdbc:tc:oracle:thin:@localhost:1521:test", "oracle", Optional.empty(), "localhost:1521:test", "test"} - }); + return Arrays.asList( + new Object[][] { + { "jdbc:tc:mysql:5.7.34://hostname/test", "mysql", Optional.of("5.7.34"), "hostname/test", "test" }, + { "jdbc:tc:mysql://hostname/test", "mysql", Optional.empty(), "hostname/test", "test" }, + { + "jdbc:tc:postgresql:1.2.3://hostname/test", + "postgresql", + Optional.of("1.2.3"), + "hostname/test", + "test", + }, + { "jdbc:tc:postgresql://hostname/test", "postgresql", Optional.empty(), "hostname/test", "test" }, + { + "jdbc:tc:sqlserver:1.2.3://localhost;instance=SQLEXPRESS:1433;databaseName=test", + "sqlserver", + Optional.of("1.2.3"), + "localhost;instance=SQLEXPRESS:1433;databaseName=test", + "test", + }, + { + "jdbc:tc:sqlserver://localhost;instance=SQLEXPRESS:1433;databaseName=test", + "sqlserver", + Optional.empty(), + "localhost;instance=SQLEXPRESS:1433;databaseName=test", + "test", + }, + { + "jdbc:tc:mariadb:1.2.3://localhost:3306/test", + "mariadb", + Optional.of("1.2.3"), + "localhost:3306/test", + "test", + }, + { "jdbc:tc:mariadb://localhost:3306/test", "mariadb", Optional.empty(), "localhost:3306/test", "test" }, + { + "jdbc:tc:oracle:1.2.3:thin://@localhost:1521/test", + "oracle", + Optional.of("1.2.3"), + "localhost:1521/test", + "test", + }, + { + "jdbc:tc:oracle:1.2.3:thin:@localhost:1521/test", + "oracle", + Optional.of("1.2.3"), + "localhost:1521/test", + "test", + }, + { + "jdbc:tc:oracle:thin:@localhost:1521/test", + "oracle", + Optional.empty(), + "localhost:1521/test", + "test", + }, + { + "jdbc:tc:oracle:1.2.3:thin:@localhost:1521:test", + "oracle", + Optional.of("1.2.3"), + "localhost:1521:test", + "test", + }, + { + "jdbc:tc:oracle:1.2.3:thin://@localhost:1521:test", + "oracle", + Optional.of("1.2.3"), + "localhost:1521:test", + "test", + }, + { + "jdbc:tc:oracle:1.2.3-anything:thin://@localhost:1521:test", + "oracle", + Optional.of("1.2.3-anything"), + "localhost:1521:test", + "test", + }, + { + "jdbc:tc:oracle:thin:@localhost:1521:test", + "oracle", + Optional.empty(), + "localhost:1521:test", + "test", + }, + } + ); } @Test diff --git a/modules/jdbc/src/test/java/org/testcontainers/jdbc/ConnectionUrlTest.java b/modules/jdbc/src/test/java/org/testcontainers/jdbc/ConnectionUrlTest.java index 4fefd5d1e13..6888f881ec4 100644 --- a/modules/jdbc/src/test/java/org/testcontainers/jdbc/ConnectionUrlTest.java +++ b/modules/jdbc/src/test/java/org/testcontainers/jdbc/ConnectionUrlTest.java @@ -13,7 +13,6 @@ public class ConnectionUrlTest { @Rule public ExpectedException thrown = ExpectedException.none(); - @Test public void testConnectionUrl1() { String urlString = "jdbc:tc:mysql:5.7.34://somehostname:3306/databasename?a=b&c=d"; @@ -31,7 +30,6 @@ public void testConnectionUrl1() { assertEquals("Parameter c is captured", "d", url.getQueryParameters().get("c")); } - @Test public void testConnectionUrl2() { String urlString = "jdbc:tc:mysql://somehostname/databasename"; @@ -46,7 +44,6 @@ public void testConnectionUrl2() { assertEquals("Database Name value is as expected", "databasename", url.getDatabaseName().get()); assertTrue("Connection Parameters set is empty", url.getQueryParameters().isEmpty()); - } @Test @@ -62,40 +59,55 @@ public void testTmpfsOption() { assertTrue("Connection Parameters set is empty", url.getQueryParameters().isEmpty()); assertFalse("Container Parameters set is not empty", url.getContainerParameters().isEmpty()); - assertEquals("Container Parameter TC_TMPFS is true", "key:value,key1:value1", url.getContainerParameters().get("TC_TMPFS")); + assertEquals( + "Container Parameter TC_TMPFS is true", + "key:value,key1:value1", + url.getContainerParameters().get("TC_TMPFS") + ); assertTrue("tmpfs option key exists", url.getTmpfsOptions().containsKey("key")); - assertEquals("tmpfs option key has correct value", "value" , url.getTmpfsOptions().get("key")); + assertEquals("tmpfs option key has correct value", "value", url.getTmpfsOptions().get("key")); assertTrue("tmpfs option key1 exists", url.getTmpfsOptions().containsKey("key1")); - assertEquals("tmpfs option key1 has correct value", "value1" , url.getTmpfsOptions().get("key1")); + assertEquals("tmpfs option key1 has correct value", "value1", url.getTmpfsOptions().get("key1")); } - @Test public void testInitScriptPathCapture() { - String urlString = "jdbc:tc:mysql:5.7.34://somehostname:3306/databasename?a=b&c=d&TC_INITSCRIPT=somepath/init_mysql.sql"; + String urlString = + "jdbc:tc:mysql:5.7.34://somehostname:3306/databasename?a=b&c=d&TC_INITSCRIPT=somepath/init_mysql.sql"; ConnectionUrl url = ConnectionUrl.newInstance(urlString); assertEquals("Database Type value is as expected", "somepath/init_mysql.sql", url.getInitScriptPath().get()); assertEquals("Query String value is as expected", "?a=b&c=d", url.getQueryString().get()); - assertEquals("INIT SCRIPT Path exists in Container Parameters", "somepath/init_mysql.sql", url.getContainerParameters().get("TC_INITSCRIPT")); + assertEquals( + "INIT SCRIPT Path exists in Container Parameters", + "somepath/init_mysql.sql", + url.getContainerParameters().get("TC_INITSCRIPT") + ); //Parameter sets are unmodifiable thrown.expect(UnsupportedOperationException.class); url.getContainerParameters().remove("TC_INITSCRIPT"); url.getQueryParameters().remove("a"); - } @Test public void testInitFunctionCapture() { - String urlString = "jdbc:tc:mysql:5.7.34://somehostname:3306/databasename?a=b&c=d&TC_INITFUNCTION=org.testcontainers.jdbc.JDBCDriverTest::sampleInitFunction"; + String urlString = + "jdbc:tc:mysql:5.7.34://somehostname:3306/databasename?a=b&c=d&TC_INITFUNCTION=org.testcontainers.jdbc.JDBCDriverTest::sampleInitFunction"; ConnectionUrl url = ConnectionUrl.newInstance(urlString); assertTrue("Init Function parameter exists", url.getInitFunction().isPresent()); - assertEquals("Init function class is as expected", "org.testcontainers.jdbc.JDBCDriverTest", url.getInitFunction().get().getClassName()); - assertEquals("Init function class is as expected", "sampleInitFunction", url.getInitFunction().get().getMethodName()); - + assertEquals( + "Init function class is as expected", + "org.testcontainers.jdbc.JDBCDriverTest", + url.getInitFunction().get().getClassName() + ); + assertEquals( + "Init function class is as expected", + "sampleInitFunction", + url.getInitFunction().get().getMethodName() + ); } @Test @@ -104,6 +116,5 @@ public void testDaemonCapture() { ConnectionUrl url = ConnectionUrl.newInstance(urlString); assertTrue("Daemon flag is set to true.", url.isInDaemonMode()); - } } diff --git a/modules/jdbc/src/test/java/org/testcontainers/jdbc/ContainerDatabaseDriverTest.java b/modules/jdbc/src/test/java/org/testcontainers/jdbc/ContainerDatabaseDriverTest.java index d33e399db27..5b4d223cd36 100644 --- a/modules/jdbc/src/test/java/org/testcontainers/jdbc/ContainerDatabaseDriverTest.java +++ b/modules/jdbc/src/test/java/org/testcontainers/jdbc/ContainerDatabaseDriverTest.java @@ -13,23 +13,22 @@ public class ContainerDatabaseDriverTest { - private static final String PLAIN_POSTGRESQL_JDBC_URL = "jdbc:postgresql://localhost:5432/test"; - - @Rule - public ExpectedException thrown = ExpectedException.none(); - - @Test - public void shouldNotTryToConnectToNonMatchingJdbcUrlDirectly() throws SQLException { - ContainerDatabaseDriver driver = new ContainerDatabaseDriver(); - Connection connection = driver.connect(PLAIN_POSTGRESQL_JDBC_URL, new Properties()); - Assert.assertNull(connection); - } - - @Test - public void shouldNotTryToConnectToNonMatchingJdbcUrlViaDriverManager() throws SQLException { - thrown.expect(SQLException.class); - thrown.expectMessage(CoreMatchers.startsWith("No suitable driver found for ")); - DriverManager.getConnection(PLAIN_POSTGRESQL_JDBC_URL); - } - + private static final String PLAIN_POSTGRESQL_JDBC_URL = "jdbc:postgresql://localhost:5432/test"; + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Test + public void shouldNotTryToConnectToNonMatchingJdbcUrlDirectly() throws SQLException { + ContainerDatabaseDriver driver = new ContainerDatabaseDriver(); + Connection connection = driver.connect(PLAIN_POSTGRESQL_JDBC_URL, new Properties()); + Assert.assertNull(connection); + } + + @Test + public void shouldNotTryToConnectToNonMatchingJdbcUrlViaDriverManager() throws SQLException { + thrown.expect(SQLException.class); + thrown.expectMessage(CoreMatchers.startsWith("No suitable driver found for ")); + DriverManager.getConnection(PLAIN_POSTGRESQL_JDBC_URL); + } } diff --git a/modules/jdbc/src/test/java/org/testcontainers/jdbc/MissingJdbcDriverTest.java b/modules/jdbc/src/test/java/org/testcontainers/jdbc/MissingJdbcDriverTest.java index 46138d91347..68afe8692e8 100644 --- a/modules/jdbc/src/test/java/org/testcontainers/jdbc/MissingJdbcDriverTest.java +++ b/modules/jdbc/src/test/java/org/testcontainers/jdbc/MissingJdbcDriverTest.java @@ -17,7 +17,6 @@ public class MissingJdbcDriverTest { @Test public void shouldFailFastIfNoDriverFound() { - final MissingDriverContainer container = new MissingDriverContainer(); try { @@ -37,6 +36,7 @@ public void shouldFailFastIfNoDriverFound() { * Container class for the purposes of testing, with a known non-existent driver */ static class MissingDriverContainer extends JdbcDatabaseContainer { + private final AtomicInteger connectionAttempts = new AtomicInteger(); MissingDriverContainer() { diff --git a/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/FilesystemFriendlyNameGenerator.java b/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/FilesystemFriendlyNameGenerator.java index ef8560b3f19..a8612fc1516 100644 --- a/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/FilesystemFriendlyNameGenerator.java +++ b/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/FilesystemFriendlyNameGenerator.java @@ -1,22 +1,22 @@ package org.testcontainers.junit.jupiter; import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.platform.commons.util.StringUtils; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; - -import static java.nio.charset.StandardCharsets.UTF_8; -import static org.junit.platform.commons.util.StringUtils.isBlank; +import java.nio.charset.StandardCharsets; class FilesystemFriendlyNameGenerator { + private static final String UNKNOWN_NAME = "unknown"; static String filesystemFriendlyNameOf(ExtensionContext context) { String contextId = context.getUniqueId(); try { - return (isBlank(contextId)) + return (StringUtils.isBlank(contextId)) ? UNKNOWN_NAME - : URLEncoder.encode(contextId, UTF_8.toString()); + : URLEncoder.encode(contextId, StandardCharsets.UTF_8.toString()); } catch (UnsupportedEncodingException e) { return UNKNOWN_NAME; } diff --git a/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/Testcontainers.java b/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/Testcontainers.java index db8273563a7..730339f0fd5 100644 --- a/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/Testcontainers.java +++ b/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/Testcontainers.java @@ -59,7 +59,6 @@ @ExtendWith(TestcontainersExtension.class) @Inherited public @interface Testcontainers { - /** * Whether tests should be disabled (rather than failing) when Docker is not available. */ diff --git a/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersExtension.java b/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersExtension.java index 990940be14d..47ed56d9ac5 100644 --- a/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersExtension.java +++ b/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersExtension.java @@ -29,33 +29,38 @@ import java.util.Optional; import java.util.Set; import java.util.function.Predicate; +import java.util.stream.Collectors; import java.util.stream.Stream; -import static java.util.stream.Collectors.toList; -import static org.testcontainers.junit.jupiter.FilesystemFriendlyNameGenerator.filesystemFriendlyNameOf; - -class TestcontainersExtension implements BeforeEachCallback, BeforeAllCallback, AfterEachCallback, AfterAllCallback, ExecutionCondition { +class TestcontainersExtension + implements BeforeEachCallback, BeforeAllCallback, AfterEachCallback, AfterAllCallback, ExecutionCondition { private static final Namespace NAMESPACE = Namespace.create(TestcontainersExtension.class); private static final String SHARED_LIFECYCLE_AWARE_CONTAINERS = "sharedLifecycleAwareContainers"; + private static final String LOCAL_LIFECYCLE_AWARE_CONTAINERS = "localLifecycleAwareContainers"; @Override public void beforeAll(ExtensionContext context) { - Class testClass = context.getTestClass() - .orElseThrow(() -> new ExtensionConfigurationException("TestcontainersExtension is only supported for classes.")); + Class testClass = context + .getTestClass() + .orElseThrow(() -> { + return new ExtensionConfigurationException("TestcontainersExtension is only supported for classes."); + }); Store store = context.getStore(NAMESPACE); List sharedContainersStoreAdapters = findSharedContainers(testClass); - sharedContainersStoreAdapters.forEach(adapter -> store.getOrComputeIfAbsent(adapter.getKey(), k -> adapter.start())); + sharedContainersStoreAdapters.forEach(adapter -> { + store.getOrComputeIfAbsent(adapter.getKey(), k -> adapter.start()); + }); List lifecycleAwareContainers = sharedContainersStoreAdapters .stream() .filter(this::isTestLifecycleAware) .map(lifecycleAwareAdapter -> (TestLifecycleAware) lifecycleAwareAdapter.container) - .collect(toList()); + .collect(Collectors.toList()); store.put(SHARED_LIFECYCLE_AWARE_CONTAINERS, lifecycleAwareContainers); signalBeforeTestToContainers(lifecycleAwareContainers, testDescriptionFrom(context)); @@ -70,12 +75,13 @@ public void afterAll(ExtensionContext context) { public void beforeEach(final ExtensionContext context) { Store store = context.getStore(NAMESPACE); - List lifecycleAwareContainers = collectParentTestInstances(context).parallelStream() + List lifecycleAwareContainers = collectParentTestInstances(context) + .parallelStream() .flatMap(this::findRestartContainers) .peek(adapter -> store.getOrComputeIfAbsent(adapter.getKey(), k -> adapter.start())) .filter(this::isTestLifecycleAware) .map(lifecycleAwareAdapter -> (TestLifecycleAware) lifecycleAwareAdapter.container) - .collect(toList()); + .collect(Collectors.toList()); store.put(LOCAL_LIFECYCLE_AWARE_CONTAINERS, lifecycleAwareContainers); signalBeforeTestToContainers(lifecycleAwareContainers, testDescriptionFrom(context)); @@ -86,13 +92,17 @@ public void afterEach(ExtensionContext context) { signalAfterTestToContainersFor(LOCAL_LIFECYCLE_AWARE_CONTAINERS, context); } - private void signalBeforeTestToContainers(List lifecycleAwareContainers, TestDescription testDescription) { + private void signalBeforeTestToContainers( + List lifecycleAwareContainers, + TestDescription testDescription + ) { lifecycleAwareContainers.forEach(container -> container.beforeTest(testDescription)); } private void signalAfterTestToContainersFor(String storeKey, ExtensionContext context) { - List lifecycleAwareContainers = - (List) context.getStore(NAMESPACE).get(storeKey); + List lifecycleAwareContainers = (List) context + .getStore(NAMESPACE) + .get(storeKey); if (lifecycleAwareContainers != null) { TestDescription description = testDescriptionFrom(context); Optional throwable = context.getExecutionException(); @@ -103,7 +113,7 @@ private void signalAfterTestToContainersFor(String storeKey, ExtensionContext co private TestDescription testDescriptionFrom(ExtensionContext context) { return new TestcontainersTestDescription( context.getUniqueId(), - filesystemFriendlyNameOf(context) + FilesystemFriendlyNameGenerator.filesystemFriendlyNameOf(context) ); } @@ -113,14 +123,18 @@ private boolean isTestLifecycleAware(StoreAdapter adapter) { @Override public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context) { - return findTestcontainers(context).map(this::evaluate) + return findTestcontainers(context) + .map(this::evaluate) .orElseThrow(() -> new ExtensionConfigurationException("@Testcontainers not found")); } private Optional findTestcontainers(ExtensionContext context) { Optional current = Optional.of(context); while (current.isPresent()) { - Optional testcontainers = AnnotationUtils.findAnnotation(current.get().getRequiredTestClass(), Testcontainers.class); + Optional testcontainers = AnnotationUtils.findAnnotation( + current.get().getRequiredTestClass(), + Testcontainers.class + ); if (testcontainers.isPresent()) { return testcontainers; } @@ -155,13 +169,11 @@ private Set collectParentTestInstances(final ExtensionContext context) { } private List findSharedContainers(Class testClass) { - return ReflectionUtils.findFields( - testClass, - isSharedContainer(), - ReflectionUtils.HierarchyTraversalMode.TOP_DOWN) + return ReflectionUtils + .findFields(testClass, isSharedContainer(), ReflectionUtils.HierarchyTraversalMode.TOP_DOWN) .stream() .map(f -> getContainerInstance(null, f)) - .collect(toList()); + .collect(Collectors.toList()); } private Predicate isSharedContainer() { @@ -169,10 +181,8 @@ private Predicate isSharedContainer() { } private Stream findRestartContainers(Object testInstance) { - return ReflectionUtils.findFields( - testInstance.getClass(), - isRestartContainer(), - ReflectionUtils.HierarchyTraversalMode.TOP_DOWN) + return ReflectionUtils + .findFields(testInstance.getClass(), isRestartContainer(), ReflectionUtils.HierarchyTraversalMode.TOP_DOWN) .stream() .map(f -> getContainerInstance(testInstance, f)); } @@ -188,7 +198,9 @@ private static Predicate isContainer() { boolean isStartable = Startable.class.isAssignableFrom(field.getType()); if (!isStartable) { - throw new ExtensionConfigurationException(String.format("FieldName: %s does not implement Startable", field.getName())); + throw new ExtensionConfigurationException( + String.format("FieldName: %s does not implement Startable", field.getName()) + ); } return true; } @@ -199,7 +211,10 @@ private static Predicate isContainer() { private static StoreAdapter getContainerInstance(final Object testInstance, final Field field) { try { field.setAccessible(true); - Startable containerInstance = Preconditions.notNull((Startable) field.get(testInstance), "Container " + field.getName() + " needs to be initialized"); + Startable containerInstance = Preconditions.notNull( + (Startable) field.get(testInstance), + "Container " + field.getName() + " needs to be initialized" + ); return new StoreAdapter(field.getDeclaringClass(), field.getName(), containerInstance); } catch (IllegalAccessException e) { throw new ExtensionConfigurationException("Can not access container defined in field " + field.getName()); diff --git a/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersTestDescription.java b/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersTestDescription.java index 568d228e08c..3cf466a5708 100644 --- a/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersTestDescription.java +++ b/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersTestDescription.java @@ -5,6 +5,8 @@ @Value class TestcontainersTestDescription implements TestDescription { + String testId; + String filesystemFriendlyName; } diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/ComposeContainerTests.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/ComposeContainerTests.java index 11b9027b392..f49aa8732f5 100644 --- a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/ComposeContainerTests.java +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/ComposeContainerTests.java @@ -18,7 +18,8 @@ class ComposeContainerTests { @Container private DockerComposeContainer composeContainer = new DockerComposeContainer( - new File("src/test/resources/docker-compose.yml")) + new File("src/test/resources/docker-compose.yml") + ) .withExposedService("whoami_1", 80, Wait.forHttp("/")); private String host; diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/FilesystemFriendlyNameGeneratorTest.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/FilesystemFriendlyNameGeneratorTest.java index 96617b0e063..9050df6f257 100644 --- a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/FilesystemFriendlyNameGeneratorTest.java +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/FilesystemFriendlyNameGeneratorTest.java @@ -10,7 +10,6 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; -import static org.testcontainers.junit.jupiter.FilesystemFriendlyNameGenerator.filesystemFriendlyNameOf; class FilesystemFriendlyNameGeneratorTest { @@ -18,10 +17,9 @@ class FilesystemFriendlyNameGeneratorTest { @MethodSource("provideDisplayNamesAndFilesystemFriendlyNames") void should_generate_filesystem_friendly_name(String displayName, String expectedName) { ExtensionContext context = mock(ExtensionContext.class); - doReturn(displayName) - .when(context).getUniqueId(); + doReturn(displayName).when(context).getUniqueId(); - String filesystemFriendlyName = filesystemFriendlyNameOf(context); + String filesystemFriendlyName = FilesystemFriendlyNameGenerator.filesystemFriendlyNameOf(context); assertThat(filesystemFriendlyName).isEqualTo(expectedName); } diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/MixedLifecycleTests.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/MixedLifecycleTests.java index 791c887cc19..6c44d978b69 100644 --- a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/MixedLifecycleTests.java +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/MixedLifecycleTests.java @@ -1,19 +1,11 @@ package org.testcontainers.junit.jupiter; -import org.apache.http.HttpResponse; -import org.apache.http.client.HttpClient; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.client.HttpClientBuilder; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.testcontainers.containers.DockerComposeContainer; import org.testcontainers.containers.MySQLContainer; import org.testcontainers.containers.PostgreSQLContainer; -import org.testcontainers.containers.wait.strategy.Wait; import static org.junit.jupiter.api.Assertions.assertTrue; - // testClass { @Testcontainers class MixedLifecycleTests { diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/PostgresContainerTests.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/PostgresContainerTests.java index 4cbc331e64b..186d4095b0d 100644 --- a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/PostgresContainerTests.java +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/PostgresContainerTests.java @@ -9,16 +9,17 @@ import java.sql.Statement; import static org.junit.Assert.assertEquals; -import static org.testcontainers.junit.jupiter.JUnitJupiterTestImages.POSTGRES_IMAGE; @Testcontainers class PostgresContainerTests { @Container - private static final PostgreSQLContainer POSTGRE_SQL_CONTAINER = new PostgreSQLContainer<>(POSTGRES_IMAGE) - .withDatabaseName("foo") - .withUsername("foo") - .withPassword("secret"); + private static final PostgreSQLContainer POSTGRE_SQL_CONTAINER = new PostgreSQLContainer<>( + JUnitJupiterTestImages.POSTGRES_IMAGE + ) + .withDatabaseName("foo") + .withUsername("foo") + .withPassword("secret"); @Test void waits_until_postgres_accepts_jdbc_connections() throws Exception { @@ -37,5 +38,4 @@ void waits_until_postgres_accepts_jdbc_connections() throws Exception { assertEquals(1, resultSetInt); } } - } diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareContainerMock.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareContainerMock.java index 38751c8789a..a8217d60840 100644 --- a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareContainerMock.java +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareContainerMock.java @@ -11,9 +11,11 @@ public class TestLifecycleAwareContainerMock implements Startable, TestLifecycleAware { static final String BEFORE_TEST = "beforeTest"; + static final String AFTER_TEST = "afterTest"; private final List lifecycleMethodCalls = new ArrayList<>(); + private final List lifecycleFilesystemFriendlyNames = new ArrayList<>(); private Throwable capturedThrowable; @@ -43,12 +45,8 @@ public List getLifecycleFilesystemFriendlyNames() { } @Override - public void start() { - - } + public void start() {} @Override - public void stop() { - - } + public void stop() {} } diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareExceptionCapturingTest.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareExceptionCapturingTest.java index 85d837a7880..09b816be56e 100644 --- a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareExceptionCapturingTest.java +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareExceptionCapturingTest.java @@ -14,6 +14,7 @@ @Testcontainers @TestMethodOrder(OrderAnnotation.class) class TestLifecycleAwareExceptionCapturingTest { + @Container private final TestLifecycleAwareContainerMock testContainer = new TestLifecycleAwareContainerMock(); diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareMethodTest.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareMethodTest.java index cecdff490ef..a77242e1d3a 100644 --- a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareMethodTest.java +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareMethodTest.java @@ -10,14 +10,13 @@ import org.junit.jupiter.api.extension.ExtensionContext; import static org.assertj.core.api.Assertions.assertThat; -import static org.testcontainers.junit.jupiter.TestLifecycleAwareContainerMock.AFTER_TEST; -import static org.testcontainers.junit.jupiter.TestLifecycleAwareContainerMock.BEFORE_TEST; // The order of @ExtendsWith and @Testcontainers is crucial for the tests -@ExtendWith({TestLifecycleAwareMethodTest.SharedContainerAfterAllTestExtension.class}) +@ExtendWith({ TestLifecycleAwareMethodTest.SharedContainerAfterAllTestExtension.class }) @Testcontainers @TestMethodOrder(MethodOrderer.OrderAnnotation.class) class TestLifecycleAwareMethodTest { + @Container private final TestLifecycleAwareContainerMock testContainer = new TestLifecycleAwareContainerMock(); @@ -28,8 +27,8 @@ class TestLifecycleAwareMethodTest { @BeforeAll static void beforeAll() { - assertThat(SHARED_CONTAINER.getLifecycleMethodCalls()).containsExactly(BEFORE_TEST); - + assertThat(SHARED_CONTAINER.getLifecycleMethodCalls()) + .containsExactly(TestLifecycleAwareContainerMock.BEFORE_TEST); } @Test @@ -43,7 +42,7 @@ void should_prepare_before_and_after_test() { @Order(2) void should_call_beforeTest_first_afterTest_later_with_filesystem_friendly_name() { assertThat(startedTestContainer.getLifecycleMethodCalls()) - .containsExactly(BEFORE_TEST, AFTER_TEST); + .containsExactly(TestLifecycleAwareContainerMock.BEFORE_TEST, TestLifecycleAwareContainerMock.AFTER_TEST); } @Test @@ -63,11 +62,15 @@ void static_container_should_have_a_filesystem_friendly_name_after_container_has } static class SharedContainerAfterAllTestExtension implements AfterAllCallback { + // Unfortunately it's not possible to write a @Test that is run after all tests @Override public void afterAll(ExtensionContext context) { assertThat(SHARED_CONTAINER.getLifecycleMethodCalls()) - .containsExactly(BEFORE_TEST, AFTER_TEST); + .containsExactly( + TestLifecycleAwareContainerMock.BEFORE_TEST, + TestLifecycleAwareContainerMock.AFTER_TEST + ); } } } diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersExtensionTests.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersExtensionTests.java index f3aea9bb850..526038853c9 100644 --- a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersExtensionTests.java +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersExtensionTests.java @@ -46,14 +46,10 @@ private ExtensionContext extensionContext(Class clazz) { } @Testcontainers(disabledWithoutDocker = true) - static final class DisabledWithoutDocker { - - } + static final class DisabledWithoutDocker {} @Testcontainers - static final class EnabledWithoutDocker { - - } + static final class EnabledWithoutDocker {} static final class TestTestcontainersExtension extends TestcontainersExtension { @@ -66,7 +62,5 @@ private TestTestcontainersExtension(boolean dockerAvailable) { boolean isDockerAvailable() { return dockerAvailable; } - } - } diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersNestedRestartedContainerTests.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersNestedRestartedContainerTests.java index 6e02e31b774..c41b184defa 100644 --- a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersNestedRestartedContainerTests.java +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersNestedRestartedContainerTests.java @@ -6,15 +6,15 @@ import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.testcontainers.junit.jupiter.JUnitJupiterTestImages.HTTPD_IMAGE; // testClass { @Testcontainers class TestcontainersNestedRestartedContainerTests { @Container - private final GenericContainer topLevelContainer = new GenericContainer<>(HTTPD_IMAGE) + private final GenericContainer topLevelContainer = new GenericContainer<>(JUnitJupiterTestImages.HTTPD_IMAGE) .withExposedPorts(80); + // }} private static String topLevelContainerId; @@ -25,16 +25,16 @@ class TestcontainersNestedRestartedContainerTests { @Test void top_level_container_should_be_running() { assertTrue(topLevelContainer.isRunning()); -// }} + // }} topLevelContainerId = topLevelContainer.getContainerId(); -// testClass {{ + // testClass {{ } @Nested class NestedTestCase { @Container - private final GenericContainer nestedContainer = new GenericContainer<>(HTTPD_IMAGE) + private final GenericContainer nestedContainer = new GenericContainer<>(JUnitJupiterTestImages.HTTPD_IMAGE) .withExposedPorts(80); @Test @@ -43,13 +43,13 @@ void both_containers_should_be_running() { assertTrue(topLevelContainer.isRunning()); // nested containers are only available inside their nested class assertTrue(nestedContainer.isRunning()); -// }}} + // }}} if (nestedContainerId == null) { nestedContainerId = nestedContainer.getContainerId(); } else { assertNotEquals(nestedContainerId, nestedContainer.getContainerId()); } -// testClass {{ + // testClass {{ } // } @@ -74,7 +74,7 @@ void ids_should_not_change() { assertNotEquals(nestedContainerId, nestedContainer.getContainerId()); } } -// testClass {{{ + // testClass {{{ } } // } diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersNestedSharedContainerTests.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersNestedSharedContainerTests.java index cf59e0338a2..c00e66c2d9d 100644 --- a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersNestedSharedContainerTests.java +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersNestedSharedContainerTests.java @@ -6,13 +6,14 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.testcontainers.junit.jupiter.JUnitJupiterTestImages.HTTPD_IMAGE; @Testcontainers class TestcontainersNestedSharedContainerTests { @Container - private static final GenericContainer TOP_LEVEL_CONTAINER = new GenericContainer<>(HTTPD_IMAGE) + private static final GenericContainer TOP_LEVEL_CONTAINER = new GenericContainer<>( + JUnitJupiterTestImages.HTTPD_IMAGE + ) .withExposedPorts(80); private static String topLevelContainerId; diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersRestartBetweenTests.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersRestartBetweenTests.java index eeb63b72380..46c6045984f 100644 --- a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersRestartBetweenTests.java +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersRestartBetweenTests.java @@ -4,14 +4,13 @@ import org.testcontainers.containers.GenericContainer; import static org.junit.jupiter.api.Assertions.assertNotEquals; -import static org.testcontainers.junit.jupiter.JUnitJupiterTestImages.HTTPD_IMAGE; @Testcontainers class TestcontainersRestartBetweenTests { @Container - private GenericContainer genericContainer = new GenericContainer<>(HTTPD_IMAGE) - .withExposedPorts(80); + private GenericContainer genericContainer = new GenericContainer<>(JUnitJupiterTestImages.HTTPD_IMAGE) + .withExposedPorts(80); private static String lastContainerId; @@ -19,7 +18,7 @@ class TestcontainersRestartBetweenTests { void first_test() { if (lastContainerId == null) { lastContainerId = genericContainer.getContainerId(); - } else { + } else { assertNotEquals(lastContainerId, genericContainer.getContainerId()); } } @@ -28,9 +27,8 @@ void first_test() { void second_test() { if (lastContainerId == null) { lastContainerId = genericContainer.getContainerId(); - } else { + } else { assertNotEquals(lastContainerId, genericContainer.getContainerId()); } } - } diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersSharedContainerTests.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersSharedContainerTests.java index 60094994f69..b23b2fe0e53 100644 --- a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersSharedContainerTests.java +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersSharedContainerTests.java @@ -6,13 +6,14 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.testcontainers.junit.jupiter.JUnitJupiterTestImages.HTTPD_IMAGE; @Testcontainers class TestcontainersSharedContainerTests { @Container - private static final GenericContainer GENERIC_CONTAINER = new GenericContainer<>(HTTPD_IMAGE) + private static final GenericContainer GENERIC_CONTAINER = new GenericContainer<>( + JUnitJupiterTestImages.HTTPD_IMAGE + ) .withExposedPorts(80); private static String lastContainerId; @@ -39,5 +40,4 @@ void second_test() { assertEquals(lastContainerId, GENERIC_CONTAINER.getContainerId()); } } - } diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/WrongAnnotationUsageTests.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/WrongAnnotationUsageTests.java index bec99894307..9fe9893d9ee 100644 --- a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/WrongAnnotationUsageTests.java +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/WrongAnnotationUsageTests.java @@ -1,10 +1,8 @@ package org.testcontainers.junit.jupiter; - import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; - @Disabled @Testcontainers class WrongAnnotationUsageTests { @@ -16,5 +14,4 @@ class WrongAnnotationUsageTests { void extension_throws_exception() { assert true; } - } diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/inheritance/AbstractTestBase.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/inheritance/AbstractTestBase.java index 3766c3f7a60..70614d3dbc7 100644 --- a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/inheritance/AbstractTestBase.java +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/inheritance/AbstractTestBase.java @@ -11,5 +11,4 @@ abstract class AbstractTestBase { @Container RedisContainer redisPerTest = new RedisContainer(); - } diff --git a/modules/k3s/src/main/java/org/testcontainers/k3s/K3sContainer.java b/modules/k3s/src/main/java/org/testcontainers/k3s/K3sContainer.java index 7f21ca7dbb8..c9dad93a06e 100644 --- a/modules/k3s/src/main/java/org/testcontainers/k3s/K3sContainer.java +++ b/modules/k3s/src/main/java/org/testcontainers/k3s/K3sContainer.java @@ -6,7 +6,6 @@ import com.fasterxml.jackson.databind.node.TextNode; import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; import com.github.dockerjava.api.command.InspectContainerResponse; -import com.github.dockerjava.api.model.DockerObjectAccessor; import lombok.SneakyThrows; import org.apache.commons.io.IOUtils; import org.testcontainers.containers.BindMode; @@ -42,17 +41,14 @@ public K3sContainer(DockerImageName dockerImageName) { tmpFsMapping.put("/var/run", ""); setTmpFsMapping(tmpFsMapping); - setCommand( - "server", - "--no-deploy=traefik", - "--tls-san=" + this.getHost() - ); + setCommand("server", "--no-deploy=traefik", "--tls-san=" + this.getHost()); setWaitStrategy(new LogMessageWaitStrategy().withRegEx(".*Node controller sync successful.*")); } @Override protected void containerIsStarted(InspectContainerResponse containerInfo) { - String rawKubeConfig = copyFileFromContainer("/etc/rancher/k3s/k3s.yaml", + String rawKubeConfig = copyFileFromContainer( + "/etc/rancher/k3s/k3s.yaml", is -> IOUtils.toString(is, StandardCharsets.UTF_8) ); String serverUrl = "https://" + this.getHost() + ":" + this.getMappedPort(KUBE_SECURE_PORT); @@ -100,7 +96,6 @@ private String kubeConfigWithServerUrl(String kubeConfigYaml, String serverUrl) kubeConfigObjectNode.set("current-context", new TextNode("default")); - return objectMapper.writerWithDefaultPrettyPrinter() - .writeValueAsString(kubeConfigObjectNode); + return objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(kubeConfigObjectNode); } } diff --git a/modules/k3s/src/test/java/org/testcontainers/k3s/Fabric8K3sContainerTest.java b/modules/k3s/src/test/java/org/testcontainers/k3s/Fabric8K3sContainerTest.java index 8b31c299634..6f9550eab2a 100644 --- a/modules/k3s/src/test/java/org/testcontainers/k3s/Fabric8K3sContainerTest.java +++ b/modules/k3s/src/test/java/org/testcontainers/k3s/Fabric8K3sContainerTest.java @@ -10,19 +10,17 @@ import io.fabric8.kubernetes.api.model.ProbeBuilder; import io.fabric8.kubernetes.client.Config; import io.fabric8.kubernetes.client.DefaultKubernetesClient; -import io.fabric8.kubernetes.client.dsl.PodResource; import io.fabric8.kubernetes.client.dsl.Readiable; import lombok.extern.slf4j.Slf4j; -import org.assertj.core.api.Assertions; -import org.assertj.core.api.Condition; import org.junit.Test; import org.testcontainers.containers.output.Slf4jLogConsumer; import org.testcontainers.utility.DockerImageName; import java.util.List; -import java.util.Map; import java.util.concurrent.TimeUnit; +import static org.assertj.core.api.Assertions.assertThat; + @Slf4j public class Fabric8K3sContainerTest { @@ -49,14 +47,14 @@ public void shouldStartAndHaveListableNode() { List nodes = client.nodes().list().getItems(); // } - Assertions.assertThat(nodes).hasSize(1); + assertThat(nodes).hasSize(1); // verify that we can start a pod Pod helloworld = dummyStartablePod(); client.pods().create(helloworld); client.pods().inNamespace("default").withName("helloworld").waitUntilReady(30, TimeUnit.SECONDS); - Assertions.assertThat(client.pods().inNamespace("default").withName("helloworld")) + assertThat(client.pods().inNamespace("default").withName("helloworld")) .extracting(Readiable::isReady) .isEqualTo(true); } diff --git a/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java b/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java index 8b1128efef0..02342351b83 100644 --- a/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java +++ b/modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java @@ -11,6 +11,7 @@ public class KafkaContainer extends GenericContainer { private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("confluentinc/cp-kafka"); + private static final String DEFAULT_TAG = "5.4.3"; public static final int KAFKA_PORT = 9093; @@ -39,7 +40,6 @@ public KafkaContainer(String confluentPlatformVersion) { public KafkaContainer(final DockerImageName dockerImageName) { super(dockerImageName); - dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); withExposedPorts(KAFKA_PORT); @@ -77,12 +77,7 @@ public String getBootstrapServers() { protected void configure() { withEnv( "KAFKA_ADVERTISED_LISTENERS", - String.format( - "BROKER://%s:9092", - getNetwork() != null - ? getNetworkAliases().get(0) - : "localhost" - ) + String.format("BROKER://%s:9092", getNetwork() != null ? getNetworkAliases().get(0) : "localhost") ); String command = "#!/bin/bash\n"; @@ -111,9 +106,12 @@ protected void containerIsStarted(InspectContainerResponse containerInfo) { ExecResult result = execInContainer( "kafka-configs", "--alter", - "--bootstrap-server", brokerAdvertisedListener, - "--entity-type", "brokers", - "--entity-name", getEnvMap().get("KAFKA_BROKER_ID"), + "--bootstrap-server", + brokerAdvertisedListener, + "--entity-type", + "brokers", + "--entity-name", + getEnvMap().get("KAFKA_BROKER_ID"), "--add-config", "advertised.listeners=[" + String.join(",", getBootstrapServers(), brokerAdvertisedListener) + "]" ); diff --git a/modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java b/modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java index f0ef0db2067..380df00e9e9 100644 --- a/modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java +++ b/modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java @@ -20,17 +20,20 @@ import java.time.Duration; import java.util.Collection; +import java.util.Collections; import java.util.UUID; import java.util.concurrent.TimeUnit; -import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.tuple; public class KafkaContainerTest { private static final DockerImageName KAFKA_TEST_IMAGE = DockerImageName.parse("confluentinc/cp-kafka:6.2.1"); - private static final DockerImageName ZOOKEEPER_TEST_IMAGE = DockerImageName.parse("confluentinc/cp-zookeeper:4.0.0"); + + private static final DockerImageName ZOOKEEPER_TEST_IMAGE = DockerImageName.parse( + "confluentinc/cp-zookeeper:4.0.0" + ); @Test public void testUsage() throws Exception { @@ -40,7 +43,6 @@ public void testUsage() throws Exception { } } - @Test public void testUsageWithSpecificImage() throws Exception { try ( @@ -50,18 +52,16 @@ public void testUsageWithSpecificImage() throws Exception { ) { kafka.start(); testKafkaFunctionality( - // getBootstrapServers { - kafka.getBootstrapServers() - // } + // getBootstrapServers { + kafka.getBootstrapServers() + // } ); } } @Test public void testUsageWithVersion() throws Exception { - try ( - KafkaContainer kafka = new KafkaContainer("6.2.1") - ) { + try (KafkaContainer kafka = new KafkaContainer("6.2.1")) { kafka.start(); testKafkaFunctionality(kafka.getBootstrapServers()); } @@ -71,7 +71,6 @@ public void testUsageWithVersion() throws Exception { public void testExternalZookeeperWithExternalNetwork() throws Exception { try ( Network network = Network.newNetwork(); - // withExternalZookeeper { KafkaContainer kafka = new KafkaContainer(KAFKA_TEST_IMAGE) .withNetwork(network) @@ -82,11 +81,10 @@ public void testExternalZookeeperWithExternalNetwork() throws Exception { .withNetwork(network) .withNetworkAliases("zookeeper") .withEnv("ZOOKEEPER_CLIENT_PORT", "2181"); - // withKafkaNetwork { GenericContainer application = new GenericContainer<>(DockerImageName.parse("alpine")) .withNetwork(network) - // } + // } .withNetworkAliases("dummy") .withCommand("sleep 10000") ) { @@ -100,9 +98,7 @@ public void testExternalZookeeperWithExternalNetwork() throws Exception { @Test public void testConfluentPlatformVersion5() throws Exception { - try ( - KafkaContainer kafka = new KafkaContainer(DockerImageName.parse("confluentinc/cp-kafka:5.4.3")) - ) { + try (KafkaContainer kafka = new KafkaContainer(DockerImageName.parse("confluentinc/cp-kafka:5.4.3"))) { kafka.start(); testKafkaFunctionality(kafka.getBootstrapServers()); } @@ -132,24 +128,27 @@ protected void testKafkaFunctionality(String bootstrapServers) throws Exception protected void testKafkaFunctionality(String bootstrapServers, int partitions, int rf) throws Exception { try ( - AdminClient adminClient = AdminClient.create(ImmutableMap.of( - AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers - )); - + AdminClient adminClient = AdminClient.create( + ImmutableMap.of(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers) + ); KafkaProducer producer = new KafkaProducer<>( ImmutableMap.of( - ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers, - ProducerConfig.CLIENT_ID_CONFIG, UUID.randomUUID().toString() + ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, + bootstrapServers, + ProducerConfig.CLIENT_ID_CONFIG, + UUID.randomUUID().toString() ), new StringSerializer(), new StringSerializer() ); - KafkaConsumer consumer = new KafkaConsumer<>( ImmutableMap.of( - ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers, - ConsumerConfig.GROUP_ID_CONFIG, "tc-" + UUID.randomUUID(), - ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest" + ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, + bootstrapServers, + ConsumerConfig.GROUP_ID_CONFIG, + "tc-" + UUID.randomUUID(), + ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, + "earliest" ), new StringDeserializer(), new StringDeserializer() @@ -157,30 +156,33 @@ protected void testKafkaFunctionality(String bootstrapServers, int partitions, i ) { String topicName = "messages-" + UUID.randomUUID(); - Collection topics = singletonList(new NewTopic(topicName, partitions, (short) rf)); + Collection topics = Collections.singletonList(new NewTopic(topicName, partitions, (short) rf)); adminClient.createTopics(topics).all().get(30, TimeUnit.SECONDS); - consumer.subscribe(singletonList(topicName)); + consumer.subscribe(Collections.singletonList(topicName)); producer.send(new ProducerRecord<>(topicName, "testcontainers", "rulezzz")).get(); - Unreliables.retryUntilTrue(10, TimeUnit.SECONDS, () -> { - ConsumerRecords records = consumer.poll(Duration.ofMillis(100)); + Unreliables.retryUntilTrue( + 10, + TimeUnit.SECONDS, + () -> { + ConsumerRecords records = consumer.poll(Duration.ofMillis(100)); - if (records.isEmpty()) { - return false; - } + if (records.isEmpty()) { + return false; + } - assertThat(records) - .hasSize(1) - .extracting(ConsumerRecord::topic, ConsumerRecord::key, ConsumerRecord::value) - .containsExactly(tuple(topicName, "testcontainers", "rulezzz")); + assertThat(records) + .hasSize(1) + .extracting(ConsumerRecord::topic, ConsumerRecord::key, ConsumerRecord::value) + .containsExactly(tuple(topicName, "testcontainers", "rulezzz")); - return true; - }); + return true; + } + ); consumer.unsubscribe(); } } - } diff --git a/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java b/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java index dd03a4206e0..8bd6f14d3fd 100644 --- a/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java +++ b/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java @@ -36,10 +36,13 @@ public class LocalStackContainer extends GenericContainer { static final int PORT = 4566; + private static final String HOSTNAME_EXTERNAL_ENV_VAR = "HOSTNAME_EXTERNAL"; + private final List services = new ArrayList<>(); private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("localstack/localstack"); + private static final String DEFAULT_TAG = "0.11.2"; @Deprecated @@ -88,7 +91,6 @@ public LocalStackContainer(final DockerImageName dockerImageName) { */ public LocalStackContainer(final DockerImageName dockerImageName, boolean useLegacyMode) { super(dockerImageName); - dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); this.legacyMode = useLegacyMode; @@ -109,7 +111,9 @@ private static boolean shouldRunInLegacyMode(String version) { } log.warn("Version {} is not a semantic version, LocalStack will run in legacy mode.", version); - log.warn("Consider using \"LocalStackContainer(DockerImageName dockerImageName, boolean legacyMode)\" constructor if you want to disable legacy mode."); + log.warn( + "Consider using \"LocalStackContainer(DockerImageName dockerImageName, boolean legacyMode)\" constructor if you want to disable legacy mode." + ); return true; } @@ -126,22 +130,25 @@ protected void configure() { // do nothing hostnameExternalReason = "explicitly as environment variable"; } else if (getNetwork() != null && getNetworkAliases() != null && getNetworkAliases().size() >= 1) { - withEnv(HOSTNAME_EXTERNAL_ENV_VAR, getNetworkAliases().get(getNetworkAliases().size() - 1)); // use the last network alias set + withEnv(HOSTNAME_EXTERNAL_ENV_VAR, getNetworkAliases().get(getNetworkAliases().size() - 1)); // use the last network alias set hostnameExternalReason = "to match last network alias on container with non-default network"; } else { withEnv(HOSTNAME_EXTERNAL_ENV_VAR, getHost()); hostnameExternalReason = "to match host-routable address for container"; } - logger().info("{} environment variable set to {} ({})", HOSTNAME_EXTERNAL_ENV_VAR, getEnvMap().get(HOSTNAME_EXTERNAL_ENV_VAR), hostnameExternalReason); + logger() + .info( + "{} environment variable set to {} ({})", + HOSTNAME_EXTERNAL_ENV_VAR, + getEnvMap().get(HOSTNAME_EXTERNAL_ENV_VAR), + hostnameExternalReason + ); exposePorts(); } private void exposePorts() { - services.stream() - .map(this::getServicePort) - .distinct() - .forEach(this::addExposedPort); + services.stream().map(this::getServicePort).distinct().forEach(this::addExposedPort); } public LocalStackContainer withServices(Service... services) { @@ -218,10 +225,7 @@ public URI getEndpointOverride(EnabledService service) { String ipAddress = address; // resolve IP address and use that as the endpoint so that path-style access is automatically used for S3 ipAddress = InetAddress.getByName(address).getHostAddress(); - return new URI("http://" + - ipAddress + - ":" + - getMappedPort(getServicePort(service))); + return new URI("http://" + ipAddress + ":" + getMappedPort(getServicePort(service))); } catch (UnknownHostException | URISyntaxException e) { throw new IllegalStateException("Cannot obtain endpoint URL", e); } @@ -332,7 +336,7 @@ public enum Service implements EnabledService { DYNAMODB("dynamodb", 4569), DYNAMODB_STREAMS("dynamodbstreams", 4570), // TODO: Clarify usage for ELASTICSEARCH and ELASTICSEARCH_SERVICE -// ELASTICSEARCH("es", 4571), + // ELASTICSEARCH("es", 4571), S3("s3", 4572), FIREHOSE("firehose", 4573), LAMBDA("lambda", 4574), diff --git a/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LegacyModeTest.java b/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LegacyModeTest.java index b34f43f79ed..a757c11e928 100644 --- a/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LegacyModeTest.java +++ b/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LegacyModeTest.java @@ -16,48 +16,51 @@ import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; import static org.rnorth.visibleassertions.VisibleAssertions.assertNotEquals; import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; -import static org.testcontainers.containers.localstack.LocalStackContainer.Service.S3; -import static org.testcontainers.containers.localstack.LocalStackContainer.Service.SQS; -import static org.testcontainers.containers.localstack.LocalstackTestImages.LOCALSTACK_0_10_IMAGE; -import static org.testcontainers.containers.localstack.LocalstackTestImages.LOCALSTACK_0_11_IMAGE; -import static org.testcontainers.containers.localstack.LocalstackTestImages.LOCALSTACK_0_12_IMAGE; -import static org.testcontainers.containers.localstack.LocalstackTestImages.LOCALSTACK_0_7_IMAGE; -import static org.testcontainers.containers.localstack.LocalstackTestImages.LOCALSTACK_IMAGE; @RunWith(Enclosed.class) public class LegacyModeTest { - private static DockerImageName LOCALSTACK_CUSTOM_TAG = LOCALSTACK_IMAGE.withTag("custom"); + + private static DockerImageName LOCALSTACK_CUSTOM_TAG = LocalstackTestImages.LOCALSTACK_IMAGE.withTag("custom"); @RunWith(Parameterized.class) @AllArgsConstructor public static class Off { + private final String description; + private final LocalStackContainer localstack; @Parameterized.Parameters(name = "{0}") public static Iterable constructors() { - return Arrays.asList(new Object[][]{ - {"0.12", new LocalStackContainer(LOCALSTACK_0_12_IMAGE)}, - {"0.11", new LocalStackContainer(LOCALSTACK_0_11_IMAGE)}, - {"0.7 with legacy = off", new LocalStackContainer(LOCALSTACK_0_7_IMAGE, false)} - }); + return Arrays.asList( + new Object[][] { + { "0.12", new LocalStackContainer(LocalstackTestImages.LOCALSTACK_0_12_IMAGE) }, + { "0.11", new LocalStackContainer(LocalstackTestImages.LOCALSTACK_0_11_IMAGE) }, + { + "0.7 with legacy = off", + new LocalStackContainer(LocalstackTestImages.LOCALSTACK_0_7_IMAGE, false), + }, + } + ); } @Test public void samePortIsExposedForAllServices() { - localstack.withServices(S3, SQS); + localstack.withServices(LocalStackContainer.Service.S3, LocalStackContainer.Service.SQS); localstack.start(); try { assertTrue("A single port is exposed", localstack.getExposedPorts().size() == 1); assertEquals( "Endpoint overrides are different", - localstack.getEndpointOverride(S3).toString(), - localstack.getEndpointOverride(SQS).toString()); + localstack.getEndpointOverride(LocalStackContainer.Service.S3).toString(), + localstack.getEndpointOverride(LocalStackContainer.Service.SQS).toString() + ); assertEquals( "Endpoint configuration have different endpoints", - localstack.getEndpointConfiguration(S3).getServiceEndpoint(), - localstack.getEndpointConfiguration(SQS).getServiceEndpoint()); + localstack.getEndpointConfiguration(LocalStackContainer.Service.S3).getServiceEndpoint(), + localstack.getEndpointConfiguration(LocalStackContainer.Service.SQS).getServiceEndpoint() + ); } finally { localstack.stop(); } @@ -67,7 +70,9 @@ public void samePortIsExposedForAllServices() { @RunWith(Parameterized.class) @AllArgsConstructor public static class On { + private final String description; + private final LocalStackContainer localstack; @BeforeClass @@ -75,7 +80,7 @@ public static void createCustomTag() { DockerClient dockerClient = DockerClientFactory.instance().client(); dockerClient .tagImageCmd( - new RemoteDockerImage(LOCALSTACK_0_12_IMAGE).get(), + new RemoteDockerImage(LocalstackTestImages.LOCALSTACK_0_12_IMAGE).get(), LOCALSTACK_CUSTOM_TAG.getRepository(), LOCALSTACK_CUSTOM_TAG.getVersionPart() ) @@ -84,28 +89,35 @@ public static void createCustomTag() { @Parameterized.Parameters(name = "{0}") public static Iterable constructors() { - return Arrays.asList(new Object[][]{ - {"0.10", new LocalStackContainer(LOCALSTACK_0_10_IMAGE)}, - {"custom", new LocalStackContainer(LOCALSTACK_CUSTOM_TAG)}, - {"0.11 with legacy = on", new LocalStackContainer(LOCALSTACK_0_11_IMAGE, true)} - }); + return Arrays.asList( + new Object[][] { + { "0.10", new LocalStackContainer(LocalstackTestImages.LOCALSTACK_0_10_IMAGE) }, + { "custom", new LocalStackContainer(LOCALSTACK_CUSTOM_TAG) }, + { + "0.11 with legacy = on", + new LocalStackContainer(LocalstackTestImages.LOCALSTACK_0_11_IMAGE, true), + }, + } + ); } @Test public void differentPortsAreExposed() { - localstack.withServices(S3, SQS); + localstack.withServices(LocalStackContainer.Service.S3, LocalStackContainer.Service.SQS); localstack.start(); try { assertTrue("Multiple ports are exposed", localstack.getExposedPorts().size() > 1); assertNotEquals( "Endpoint overrides are different", - localstack.getEndpointOverride(S3).toString(), - localstack.getEndpointOverride(SQS).toString()); + localstack.getEndpointOverride(LocalStackContainer.Service.S3).toString(), + localstack.getEndpointOverride(LocalStackContainer.Service.SQS).toString() + ); assertNotEquals( "Endpoint configuration have different endpoints", - localstack.getEndpointConfiguration(S3).getServiceEndpoint(), - localstack.getEndpointConfiguration(SQS).getServiceEndpoint()); + localstack.getEndpointConfiguration(LocalStackContainer.Service.S3).getServiceEndpoint(), + localstack.getEndpointConfiguration(LocalStackContainer.Service.SQS).getServiceEndpoint() + ); } finally { localstack.stop(); } diff --git a/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java b/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java index 6b32ba2c102..cf4e1a813a4 100644 --- a/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java +++ b/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java @@ -1,17 +1,5 @@ package org.testcontainers.containers.localstack; - -import static org.hamcrest.CoreMatchers.containsString; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.rnorth.visibleassertions.VisibleAssertions.assertThat; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; -import static org.testcontainers.containers.localstack.LocalStackContainer.PORT; -import static org.testcontainers.containers.localstack.LocalStackContainer.Service.CLOUDWATCHLOGS; -import static org.testcontainers.containers.localstack.LocalStackContainer.Service.KMS; -import static org.testcontainers.containers.localstack.LocalStackContainer.Service.S3; -import static org.testcontainers.containers.localstack.LocalStackContainer.Service.SQS; -import static org.testcontainers.containers.localstack.LocalstackTestImages.LOCALSTACK_IMAGE; - import com.amazonaws.services.kms.AWSKMS; import com.amazonaws.services.kms.AWSKMSClientBuilder; import com.amazonaws.services.kms.model.CreateKeyRequest; @@ -29,10 +17,6 @@ import com.amazonaws.services.sqs.AmazonSQS; import com.amazonaws.services.sqs.AmazonSQSClientBuilder; import com.amazonaws.services.sqs.model.CreateQueueResult; -import java.io.IOException; -import java.nio.charset.Charset; -import java.util.List; -import java.util.Optional; import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.IOUtils; import org.junit.Assert; @@ -44,12 +28,22 @@ import org.testcontainers.containers.Container; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.Network; -import org.testcontainers.utility.DockerImageName; +import org.testcontainers.containers.localstack.LocalStackContainer.Service; import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.services.s3.S3Client; +import java.io.IOException; +import java.nio.charset.Charset; +import java.util.List; +import java.util.Optional; + +import static org.hamcrest.Matchers.containsString; +import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.rnorth.visibleassertions.VisibleAssertions.assertThat; +import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; + /** * Tests for Localstack Container, used both in bridge network (exposed to host) and docker network modes. *

@@ -65,15 +59,22 @@ public static class WithoutNetwork { // without_network { @ClassRule - public static LocalStackContainer localstack = new LocalStackContainer(LOCALSTACK_IMAGE) - .withServices(S3, SQS, CLOUDWATCHLOGS, KMS, LocalStackContainer.EnabledService.named("events")); + public static LocalStackContainer localstack = new LocalStackContainer(LocalstackTestImages.LOCALSTACK_IMAGE) + .withServices( + Service.S3, + Service.SQS, + Service.CLOUDWATCHLOGS, + Service.KMS, + LocalStackContainer.EnabledService.named("events") + ); + // } @Test public void s3TestOverBridgeNetwork() throws IOException { AmazonS3 s3 = AmazonS3ClientBuilder .standard() - .withEndpointConfiguration(localstack.getEndpointConfiguration(S3)) + .withEndpointConfiguration(localstack.getEndpointConfiguration(Service.S3)) .withCredentials(localstack.getDefaultCredentialsProvider()) .build(); @@ -82,7 +83,10 @@ public void s3TestOverBridgeNetwork() throws IOException { s3.putObject(bucketName, "bar", "baz"); final List buckets = s3.listBuckets(); - final Optional maybeBucket = buckets.stream().filter(b -> b.getName().equals(bucketName)).findFirst(); + final Optional maybeBucket = buckets + .stream() + .filter(b -> b.getName().equals(bucketName)) + .findFirst(); assertTrue("The created bucket is present", maybeBucket.isPresent()); final Bucket bucket = maybeBucket.get(); @@ -100,32 +104,49 @@ public void s3TestOverBridgeNetwork() throws IOException { public void s3TestUsingAwsSdkV2() { S3Client s3 = S3Client .builder() - .endpointOverride(localstack.getEndpointOverride(LocalStackContainer.Service.S3)) - .credentialsProvider(StaticCredentialsProvider.create(AwsBasicCredentials.create( - localstack.getAccessKey(), localstack.getSecretKey() - ))) + .endpointOverride(localstack.getEndpointOverride(Service.S3)) + .credentialsProvider( + StaticCredentialsProvider.create( + AwsBasicCredentials.create(localstack.getAccessKey(), localstack.getSecretKey()) + ) + ) .region(Region.of(localstack.getRegion())) .build(); final String bucketName = "foov2"; s3.createBucket(b -> b.bucket(bucketName)); - assertTrue("New bucket was created", s3.listBuckets().buckets().stream().anyMatch(b -> b.name().equals(bucketName))); + assertTrue( + "New bucket was created", + s3.listBuckets().buckets().stream().anyMatch(b -> b.name().equals(bucketName)) + ); } @Test public void sqsTestOverBridgeNetwork() { - AmazonSQS sqs = AmazonSQSClientBuilder.standard() - .withEndpointConfiguration(localstack.getEndpointConfiguration(SQS)) + AmazonSQS sqs = AmazonSQSClientBuilder + .standard() + .withEndpointConfiguration(localstack.getEndpointConfiguration(Service.SQS)) .withCredentials(localstack.getDefaultCredentialsProvider()) .build(); CreateQueueResult queueResult = sqs.createQueue("baz"); String fooQueueUrl = queueResult.getQueueUrl(); - assertThat("Created queue has external hostname URL", fooQueueUrl, - containsString("http://" + DockerClientFactory.instance().dockerHostIpAddress() + ":" + localstack.getMappedPort(PORT))); + assertThat( + "Created queue has external hostname URL", + fooQueueUrl, + containsString( + "http://" + + DockerClientFactory.instance().dockerHostIpAddress() + + ":" + + localstack.getMappedPort(LocalStackContainer.PORT) + ) + ); sqs.sendMessage(fooQueueUrl, "test"); - final long messageCount = sqs.receiveMessage(fooQueueUrl).getMessages().stream() + final long messageCount = sqs + .receiveMessage(fooQueueUrl) + .getMessages() + .stream() .filter(message -> message.getBody().equals("test")) .count(); assertEquals("the sent message can be received", 1L, messageCount); @@ -133,9 +154,11 @@ public void sqsTestOverBridgeNetwork() { @Test public void cloudWatchLogsTestOverBridgeNetwork() { - AWSLogs logs = AWSLogsClientBuilder.standard() - .withEndpointConfiguration(localstack.getEndpointConfiguration(CLOUDWATCHLOGS)) - .withCredentials(localstack.getDefaultCredentialsProvider()).build(); + AWSLogs logs = AWSLogsClientBuilder + .standard() + .withEndpointConfiguration(localstack.getEndpointConfiguration(Service.CLOUDWATCHLOGS)) + .withCredentials(localstack.getDefaultCredentialsProvider()) + .build(); logs.createLogGroup(new CreateLogGroupRequest("foo")); @@ -146,8 +169,9 @@ public void cloudWatchLogsTestOverBridgeNetwork() { @Test public void kmsKeyCreationTest() { - AWSKMS awskms = AWSKMSClientBuilder.standard() - .withEndpointConfiguration(localstack.getEndpointConfiguration(KMS)) + AWSKMS awskms = AWSKMSClientBuilder + .standard() + .withEndpointConfiguration(localstack.getEndpointConfiguration(Service.KMS)) .withCredentials(localstack.getDefaultCredentialsProvider()) .build(); @@ -156,7 +180,11 @@ public void kmsKeyCreationTest() { CreateKeyRequest req = new CreateKeyRequest().withDescription(desc).withTags(createdByTag); CreateKeyResult key = awskms.createKey(req); - assertEquals("AWS KMS Customer Managed Key should be created ", key.getKeyMetadata().getDescription(), desc); + assertEquals( + "AWS KMS Customer Managed Key should be created ", + key.getKeyMetadata().getDescription(), + desc + ); } @Test @@ -164,35 +192,42 @@ public void samePortIsExposedForAllServices() { assertTrue("A single port is exposed", localstack.getExposedPorts().size() == 1); assertEquals( "Endpoint overrides are different", - localstack.getEndpointOverride(S3).toString(), - localstack.getEndpointOverride(SQS).toString()); + localstack.getEndpointOverride(Service.S3).toString(), + localstack.getEndpointOverride(Service.SQS).toString() + ); assertEquals( "Endpoint configuration have different endpoints", - localstack.getEndpointConfiguration(S3).getServiceEndpoint(), - localstack.getEndpointConfiguration(SQS).getServiceEndpoint()); + localstack.getEndpointConfiguration(Service.S3).getServiceEndpoint(), + localstack.getEndpointConfiguration(Service.SQS).getServiceEndpoint() + ); } } public static class WithNetwork { + // with_network { private static Network network = Network.newNetwork(); @ClassRule - public static LocalStackContainer localstackInDockerNetwork = new LocalStackContainer(LOCALSTACK_IMAGE) + public static LocalStackContainer localstackInDockerNetwork = new LocalStackContainer( + LocalstackTestImages.LOCALSTACK_IMAGE + ) .withNetwork(network) - .withNetworkAliases("notthis", "localstack") // the last alias is used for HOSTNAME_EXTERNAL - .withServices(S3, SQS, CLOUDWATCHLOGS); + .withNetworkAliases("notthis", "localstack") // the last alias is used for HOSTNAME_EXTERNAL + .withServices(Service.S3, Service.SQS, Service.CLOUDWATCHLOGS); + // } @ClassRule - public static GenericContainer awsCliInDockerNetwork = new GenericContainer<>(LocalstackTestImages.AWS_CLI_IMAGE) + public static GenericContainer awsCliInDockerNetwork = new GenericContainer<>( + LocalstackTestImages.AWS_CLI_IMAGE + ) .withNetwork(network) .withCreateContainerCmdModifier(cmd -> cmd.withEntrypoint("top")) .withEnv("AWS_ACCESS_KEY_ID", "accesskey") .withEnv("AWS_SECRET_ACCESS_KEY", "secretkey") .withEnv("AWS_REGION", "eu-west-1"); - @Test public void s3TestOverDockerNetwork() throws Exception { runAwsCliAgainstDockerNetworkContainer("s3api create-bucket --bucket foo"); @@ -202,15 +237,30 @@ public void s3TestOverDockerNetwork() throws Exception { @Test public void sqsTestOverDockerNetwork() throws Exception { - final String queueCreationResponse = runAwsCliAgainstDockerNetworkContainer("sqs create-queue --queue-name baz"); + final String queueCreationResponse = runAwsCliAgainstDockerNetworkContainer( + "sqs create-queue --queue-name baz" + ); - assertThat("Created queue has external hostname URL", queueCreationResponse, - containsString("http://localstack:" + PORT)); + assertThat( + "Created queue has external hostname URL", + queueCreationResponse, + containsString("http://localstack:" + LocalStackContainer.PORT) + ); runAwsCliAgainstDockerNetworkContainer( - String.format("sqs send-message --endpoint http://localstack:%d --queue-url http://localstack:%d/queue/baz --message-body test", PORT, PORT)); + String.format( + "sqs send-message --endpoint http://localstack:%d --queue-url http://localstack:%d/queue/baz --message-body test", + LocalStackContainer.PORT, + LocalStackContainer.PORT + ) + ); final String message = runAwsCliAgainstDockerNetworkContainer( - String.format("sqs receive-message --endpoint http://localstack:%d --queue-url http://localstack:%d/queue/baz", PORT, PORT)); + String.format( + "sqs receive-message --endpoint http://localstack:%d --queue-url http://localstack:%d/queue/baz", + LocalStackContainer.PORT, + LocalStackContainer.PORT + ) + ); assertTrue("the sent message can be received", message.contains("\"Body\": \"test\"")); } @@ -221,7 +271,13 @@ public void cloudWatchLogsTestOverDockerNetwork() throws Exception { } private String runAwsCliAgainstDockerNetworkContainer(String command) throws Exception { - final String[] commandParts = String.format("/usr/bin/aws --region eu-west-1 %s --endpoint-url http://localstack:%d --no-verify-ssl", command, PORT).split(" "); + final String[] commandParts = String + .format( + "/usr/bin/aws --region eu-west-1 %s --endpoint-url http://localstack:%d --no-verify-ssl", + command, + LocalStackContainer.PORT + ) + .split(" "); final Container.ExecResult execResult = awsCliInDockerNetwork.execInContainer(commandParts); Assert.assertEquals(0, execResult.getExitCode()); diff --git a/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainer.java b/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainer.java index 2e28e5d0480..b0944a6d3c0 100644 --- a/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainer.java +++ b/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainer.java @@ -27,10 +27,15 @@ public class MariaDBContainer> extends JdbcD static final String DEFAULT_PASSWORD = "test"; static final Integer MARIADB_PORT = 3306; + private String databaseName = "test"; + private String username = DEFAULT_USER; + private String password = DEFAULT_PASSWORD; + private static final String MARIADB_ROOT_USER = "root"; + private static final String MY_CNF_CONFIG_OVERRIDE_PARAM_NAME = "TC_MY_CNF"; /** @@ -47,7 +52,6 @@ public MariaDBContainer(String dockerImageName) { public MariaDBContainer(final DockerImageName dockerImageName) { super(dockerImageName); - dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); addExposedPort(MARIADB_PORT); @@ -60,7 +64,11 @@ public Set getLivenessCheckPortNumbers() { @Override protected void configure() { - optionallyMapResourceParameterAsVolume(MY_CNF_CONFIG_OVERRIDE_PARAM_NAME, "/etc/mysql/conf.d", "mariadb-default-conf"); + optionallyMapResourceParameterAsVolume( + MY_CNF_CONFIG_OVERRIDE_PARAM_NAME, + "/etc/mysql/conf.d", + "mariadb-default-conf" + ); addEnv("MYSQL_DATABASE", databaseName); addEnv("MYSQL_USER", username); @@ -83,13 +91,14 @@ public String getDriverClassName() { @Override public String getJdbcUrl() { String additionalUrlParams = constructUrlParameters("?", "&"); - return "jdbc:mariadb://" + getHost() + ":" + getMappedPort(MARIADB_PORT) + - "/" + databaseName + additionalUrlParams; + return ( + "jdbc:mariadb://" + getHost() + ":" + getMappedPort(MARIADB_PORT) + "/" + databaseName + additionalUrlParams + ); } @Override public String getDatabaseName() { - return databaseName; + return databaseName; } @Override diff --git a/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainerProvider.java b/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainerProvider.java index 4330465a9c3..6697255ca79 100644 --- a/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainerProvider.java +++ b/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBContainerProvider.java @@ -29,7 +29,6 @@ public JdbcDatabaseContainer newInstance(String tag) { @Override public JdbcDatabaseContainer newInstance(ConnectionUrl connectionUrl) { - return newInstanceFromConnectionUrl(connectionUrl, USER_PARAM, PASSWORD_PARAM); + return newInstanceFromConnectionUrl(connectionUrl, USER_PARAM, PASSWORD_PARAM); } - } diff --git a/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBR2DBCDatabaseContainer.java b/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBR2DBCDatabaseContainer.java index e37e9b0b6bf..99041e83e28 100644 --- a/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBR2DBCDatabaseContainer.java +++ b/modules/mariadb/src/main/java/org/testcontainers/containers/MariaDBR2DBCDatabaseContainer.java @@ -13,7 +13,8 @@ public class MariaDBR2DBCDatabaseContainer implements R2DBCDatabaseContainer { private final MariaDBContainer container; public static ConnectionFactoryOptions getOptions(MariaDBContainer container) { - ConnectionFactoryOptions options = ConnectionFactoryOptions.builder() + ConnectionFactoryOptions options = ConnectionFactoryOptions + .builder() .option(ConnectionFactoryOptions.DRIVER, MariaDBR2DBCDatabaseContainerProvider.DRIVER) .build(); @@ -22,7 +23,8 @@ public static ConnectionFactoryOptions getOptions(MariaDBContainer container) @Override public ConnectionFactoryOptions configure(ConnectionFactoryOptions options) { - return options.mutate() + return options + .mutate() .option(ConnectionFactoryOptions.HOST, container.getHost()) .option(ConnectionFactoryOptions.PORT, container.getMappedPort(MariaDBContainer.MARIADB_PORT)) .option(ConnectionFactoryOptions.DATABASE, container.getDatabaseName()) diff --git a/modules/mariadb/src/test/java/org/testcontainers/containers/MariaDBR2DBCDatabaseContainerTest.java b/modules/mariadb/src/test/java/org/testcontainers/containers/MariaDBR2DBCDatabaseContainerTest.java index b6fb4dd659e..f2339b0e5f9 100644 --- a/modules/mariadb/src/test/java/org/testcontainers/containers/MariaDBR2DBCDatabaseContainerTest.java +++ b/modules/mariadb/src/test/java/org/testcontainers/containers/MariaDBR2DBCDatabaseContainerTest.java @@ -20,5 +20,4 @@ protected String createR2DBCUrl() { protected MariaDBContainer createContainer() { return new MariaDBContainer<>(DockerImageName.parse("mariadb:10.3.6")); } - } diff --git a/modules/mariadb/src/test/java/org/testcontainers/jdbc/mariadb/MariaDBJDBCDriverTest.java b/modules/mariadb/src/test/java/org/testcontainers/jdbc/mariadb/MariaDBJDBCDriverTest.java index ef1dfef2703..ffb26f19734 100644 --- a/modules/mariadb/src/test/java/org/testcontainers/jdbc/mariadb/MariaDBJDBCDriverTest.java +++ b/modules/mariadb/src/test/java/org/testcontainers/jdbc/mariadb/MariaDBJDBCDriverTest.java @@ -4,26 +4,47 @@ import org.junit.runners.Parameterized; import org.testcontainers.jdbc.AbstractJDBCDriverTest; +import java.util.Arrays; import java.util.EnumSet; -import static java.util.Arrays.asList; - @RunWith(Parameterized.class) public class MariaDBJDBCDriverTest extends AbstractJDBCDriverTest { @Parameterized.Parameters(name = "{index} - {0}") public static Iterable data() { - return asList( - new Object[][]{ - {"jdbc:tc:mariadb://hostname/databasename", EnumSet.noneOf(Options.class)}, - {"jdbc:tc:mariadb://hostname/databasename?user=someuser&TC_INITSCRIPT=somepath/init_mariadb.sql", EnumSet.of(Options.ScriptedSchema, Options.JDBCParams)}, - {"jdbc:tc:mariadb:10.2.14://hostname/databasename", EnumSet.noneOf(Options.class)}, - {"jdbc:tc:mariadb:10.2.14://hostname/databasename?TC_INITSCRIPT=somepath/init_unicode_mariadb.sql&useUnicode=yes&characterEncoding=utf8", EnumSet.of(Options.CharacterSet)}, - {"jdbc:tc:mariadb:10.2.14://hostname/databasename?user=someuser&TC_INITSCRIPT=somepath/init_mariadb.sql", EnumSet.of(Options.ScriptedSchema, Options.JDBCParams)}, - {"jdbc:tc:mariadb:10.2.14://hostname/databasename?user=someuser&TC_INITFUNCTION=org.testcontainers.jdbc.AbstractJDBCDriverTest::sampleInitFunction", EnumSet.of(Options.ScriptedSchema, Options.JDBCParams)}, - {"jdbc:tc:mariadb:10.2.14://hostname/databasename?user=someuser&password=somepwd&TC_INITSCRIPT=somepath/init_mariadb.sql", EnumSet.of(Options.ScriptedSchema, Options.JDBCParams)}, - {"jdbc:tc:mariadb:10.2.14://hostname/databasename?user=someuser&password=somepwd&TC_INITFUNCTION=org.testcontainers.jdbc.AbstractJDBCDriverTest::sampleInitFunction", EnumSet.of(Options.ScriptedSchema, Options.JDBCParams)}, - {"jdbc:tc:mariadb:10.2.14://hostname/databasename?TC_MY_CNF=somepath/mariadb_conf_override", EnumSet.of(Options.CustomIniFile)}, - }); + return Arrays.asList( + new Object[][] { + { "jdbc:tc:mariadb://hostname/databasename", EnumSet.noneOf(Options.class) }, + { + "jdbc:tc:mariadb://hostname/databasename?user=someuser&TC_INITSCRIPT=somepath/init_mariadb.sql", + EnumSet.of(Options.ScriptedSchema, Options.JDBCParams), + }, + { "jdbc:tc:mariadb:10.2.14://hostname/databasename", EnumSet.noneOf(Options.class) }, + { + "jdbc:tc:mariadb:10.2.14://hostname/databasename?TC_INITSCRIPT=somepath/init_unicode_mariadb.sql&useUnicode=yes&characterEncoding=utf8", + EnumSet.of(Options.CharacterSet), + }, + { + "jdbc:tc:mariadb:10.2.14://hostname/databasename?user=someuser&TC_INITSCRIPT=somepath/init_mariadb.sql", + EnumSet.of(Options.ScriptedSchema, Options.JDBCParams), + }, + { + "jdbc:tc:mariadb:10.2.14://hostname/databasename?user=someuser&TC_INITFUNCTION=org.testcontainers.jdbc.AbstractJDBCDriverTest::sampleInitFunction", + EnumSet.of(Options.ScriptedSchema, Options.JDBCParams), + }, + { + "jdbc:tc:mariadb:10.2.14://hostname/databasename?user=someuser&password=somepwd&TC_INITSCRIPT=somepath/init_mariadb.sql", + EnumSet.of(Options.ScriptedSchema, Options.JDBCParams), + }, + { + "jdbc:tc:mariadb:10.2.14://hostname/databasename?user=someuser&password=somepwd&TC_INITFUNCTION=org.testcontainers.jdbc.AbstractJDBCDriverTest::sampleInitFunction", + EnumSet.of(Options.ScriptedSchema, Options.JDBCParams), + }, + { + "jdbc:tc:mariadb:10.2.14://hostname/databasename?TC_MY_CNF=somepath/mariadb_conf_override", + EnumSet.of(Options.CustomIniFile), + }, + } + ); } } diff --git a/modules/mariadb/src/test/java/org/testcontainers/junit/mariadb/SimpleMariaDBTest.java b/modules/mariadb/src/test/java/org/testcontainers/junit/mariadb/SimpleMariaDBTest.java index 2dcdbafd9e3..21a48b6e73c 100644 --- a/modules/mariadb/src/test/java/org/testcontainers/junit/mariadb/SimpleMariaDBTest.java +++ b/modules/mariadb/src/test/java/org/testcontainers/junit/mariadb/SimpleMariaDBTest.java @@ -2,26 +2,24 @@ import org.apache.commons.lang3.SystemUtils; import org.junit.Test; +import org.testcontainers.MariaDBTestImages; import org.testcontainers.containers.MariaDBContainer; import org.testcontainers.db.AbstractContainerDatabaseTest; import java.sql.ResultSet; import java.sql.SQLException; -import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.Matchers.containsString; import static org.junit.Assert.assertThat; import static org.junit.Assume.assumeFalse; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; -import static org.testcontainers.MariaDBTestImages.MARIADB_IMAGE; - public class SimpleMariaDBTest extends AbstractContainerDatabaseTest { @Test public void testSimple() throws SQLException { - try (MariaDBContainer mariadb = new MariaDBContainer<>(MARIADB_IMAGE)) { - + try (MariaDBContainer mariadb = new MariaDBContainer<>(MariaDBTestImages.MARIADB_IMAGE)) { mariadb.start(); ResultSet resultSet = performQuery(mariadb, "SELECT 1"); @@ -33,14 +31,20 @@ public void testSimple() throws SQLException { @Test public void testSpecificVersion() throws SQLException { - try (MariaDBContainer mariadbOldVersion = new MariaDBContainer<>(MARIADB_IMAGE.withTag("5.5.51"))) { - + try ( + MariaDBContainer mariadbOldVersion = new MariaDBContainer<>( + MariaDBTestImages.MARIADB_IMAGE.withTag("5.5.51") + ) + ) { mariadbOldVersion.start(); ResultSet resultSet = performQuery(mariadbOldVersion, "SELECT VERSION()"); String resultSetString = resultSet.getString(1); - assertTrue("The database version can be set using a container rule parameter", resultSetString.startsWith("5.5.51")); + assertTrue( + "The database version can be set using a container rule parameter", + resultSetString.startsWith("5.5.51") + ); } } @@ -48,8 +52,12 @@ public void testSpecificVersion() throws SQLException { public void testMariaDBWithCustomIniFile() throws SQLException { assumeFalse(SystemUtils.IS_OS_WINDOWS); - try (MariaDBContainer mariadbCustomConfig = new MariaDBContainer<>(MARIADB_IMAGE.withTag("10.1.16")) - .withConfigurationOverride("somepath/mariadb_conf_override")) { + try ( + MariaDBContainer mariadbCustomConfig = new MariaDBContainer<>( + MariaDBTestImages.MARIADB_IMAGE.withTag("10.1.16") + ) + .withConfigurationOverride("somepath/mariadb_conf_override") + ) { mariadbCustomConfig.start(); ResultSet resultSet = performQuery(mariadbCustomConfig, "SELECT @@GLOBAL.innodb_file_format"); @@ -61,9 +69,10 @@ public void testMariaDBWithCustomIniFile() throws SQLException { @Test public void testMariaDBWithCommandOverride() throws SQLException { - - try (MariaDBContainer mariadbCustomConfig = new MariaDBContainer<>(MARIADB_IMAGE) - .withCommand("mysqld --auto_increment_increment=10")) { + try ( + MariaDBContainer mariadbCustomConfig = new MariaDBContainer<>(MariaDBTestImages.MARIADB_IMAGE) + .withCommand("mysqld --auto_increment_increment=10") + ) { mariadbCustomConfig.start(); ResultSet resultSet = performQuery(mariadbCustomConfig, "show variables like 'auto_increment_increment'"); String result = resultSet.getString("Value"); @@ -74,7 +83,7 @@ public void testMariaDBWithCommandOverride() throws SQLException { @Test public void testWithAdditionalUrlParamInJdbcUrl() { - MariaDBContainer mariaDBContainer = new MariaDBContainer<>(MARIADB_IMAGE) + MariaDBContainer mariaDBContainer = new MariaDBContainer<>(MariaDBTestImages.MARIADB_IMAGE) .withUrlParam("connectTimeout", "40000") .withUrlParam("rewriteBatchedStatements", "true"); diff --git a/modules/mockserver/src/main/java/org/testcontainers/containers/MockServerContainer.java b/modules/mockserver/src/main/java/org/testcontainers/containers/MockServerContainer.java index 6ab1c541325..8b39d5d3763 100644 --- a/modules/mockserver/src/main/java/org/testcontainers/containers/MockServerContainer.java +++ b/modules/mockserver/src/main/java/org/testcontainers/containers/MockServerContainer.java @@ -8,6 +8,7 @@ public class MockServerContainer extends GenericContainer { private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("jamesdbloom/mockserver"); + private static final String DEFAULT_TAG = "mockserver-5.5.4"; @Deprecated @@ -33,7 +34,6 @@ public MockServerContainer(String version) { public MockServerContainer(DockerImageName dockerImageName) { super(dockerImageName); - dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME, DockerImageName.parse("mockserver/mockserver")); waitingFor(Wait.forHttp("/mockserver/status").withMethod("PUT").forStatusCode(200)); diff --git a/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerRuleTest.java b/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerRuleTest.java index d0c290ad46a..3f34d000603 100644 --- a/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerRuleTest.java +++ b/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerRuleTest.java @@ -5,22 +5,26 @@ import org.mockserver.client.MockServerClient; import org.testcontainers.utility.DockerImageName; -import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.Matchers.containsString; import static org.mockserver.model.HttpRequest.request; import static org.mockserver.model.HttpResponse.response; import static org.rnorth.visibleassertions.VisibleAssertions.assertThat; public class MockServerContainerRuleTest { - public static final DockerImageName MOCKSERVER_IMAGE = DockerImageName.parse("jamesdbloom/mockserver:mockserver-5.5.4"); + public static final DockerImageName MOCKSERVER_IMAGE = DockerImageName.parse( + "jamesdbloom/mockserver:mockserver-5.5.4" + ); // creatingProxy { @Rule public MockServerContainer mockServer = new MockServerContainer(MOCKSERVER_IMAGE); + // } @Test public void shouldReturnExpectation() throws Exception { + // spotless:off // testSimpleExpectation { new MockServerClient(mockServer.getHost(), mockServer.getServerPort()) .when(request() @@ -31,8 +35,10 @@ public void shouldReturnExpectation() throws Exception { // ...a GET request to '/person?name=peter' returns "Peter the person!" // } + // spotless:on - assertThat("Expectation returns expected response body", + assertThat( + "Expectation returns expected response body", SimpleHttpClient.responseFromMockserver(mockServer, "/person?name=peter"), containsString("Peter the person") ); diff --git a/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerTest.java b/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerTest.java index 61a5c33c630..d038b111824 100644 --- a/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerTest.java +++ b/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerTest.java @@ -4,7 +4,7 @@ import org.mockserver.client.MockServerClient; import org.testcontainers.utility.DockerImageName; -import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.Matchers.equalTo; import static org.mockserver.model.HttpRequest.request; import static org.mockserver.model.HttpResponse.response; import static org.rnorth.visibleassertions.VisibleAssertions.assertThat; @@ -12,12 +12,18 @@ public class MockServerContainerTest { - public static final DockerImageName MOCKSERVER_IMAGE = DockerImageName.parse("jamesdbloom/mockserver:mockserver-5.5.4"); + public static final DockerImageName MOCKSERVER_IMAGE = DockerImageName.parse( + "jamesdbloom/mockserver:mockserver-5.5.4" + ); @Test public void shouldCallActualMockserverVersion() throws Exception { String actualVersion = MockServerClient.class.getPackage().getImplementationVersion(); - try (MockServerContainer mockServer = new MockServerContainer(MOCKSERVER_IMAGE.withTag("mockserver-" + actualVersion))) { + try ( + MockServerContainer mockServer = new MockServerContainer( + MOCKSERVER_IMAGE.withTag("mockserver-" + actualVersion) + ) + ) { mockServer.start(); String expectedBody = "Hello World!"; @@ -26,11 +32,10 @@ public void shouldCallActualMockserverVersion() throws Exception { assertTrue("Mockserver running", client.isRunning()); - client - .when(request().withPath("/hello")) - .respond(response().withBody(expectedBody)); + client.when(request().withPath("/hello")).respond(response().withBody(expectedBody)); - assertThat("MockServer returns correct result", + assertThat( + "MockServer returns correct result", SimpleHttpClient.responseFromMockserver(mockServer, "/hello"), equalTo(expectedBody) ); diff --git a/modules/mockserver/src/test/java/org/testcontainers/containers/SimpleHttpClient.java b/modules/mockserver/src/test/java/org/testcontainers/containers/SimpleHttpClient.java index cd5cb25cabb..da60ca9396a 100644 --- a/modules/mockserver/src/test/java/org/testcontainers/containers/SimpleHttpClient.java +++ b/modules/mockserver/src/test/java/org/testcontainers/containers/SimpleHttpClient.java @@ -9,9 +9,11 @@ import java.net.URLConnection; public class SimpleHttpClient { + public static String responseFromMockserver(MockServerContainer mockServer, String path) throws IOException { URLConnection urlConnection = new URL(mockServer.getEndpoint() + path).openConnection(); - @Cleanup BufferedReader reader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream())); + @Cleanup + BufferedReader reader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream())); return reader.readLine(); } } 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 87db80846a8..81bb59fd69d 100644 --- a/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDBContainer.java +++ b/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDBContainer.java @@ -18,10 +18,15 @@ public class MongoDBContainer extends GenericContainer { private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("mongo"); + private static final String DEFAULT_TAG = "4.0.10"; + 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_DATABASE_NAME_DEFAULT = "test"; /** @@ -38,14 +43,11 @@ public MongoDBContainer(@NonNull final String dockerImageName) { public MongoDBContainer(final DockerImageName dockerImageName) { super(dockerImageName); - dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); withExposedPorts(MONGODB_INTERNAL_PORT); withCommand("--replSet", "docker-rs"); - waitingFor( - Wait.forLogMessage("(?i).*waiting for connections.*", 1) - ); + waitingFor(Wait.forLogMessage("(?i).*waiting for connections.*", 1)); } /** @@ -54,11 +56,7 @@ public MongoDBContainer(final DockerImageName dockerImageName) { * @return a connection url pointing to a mongodb instance */ public String getConnectionString() { - return String.format( - "mongodb://%s:%d", - getHost(), - getMappedPort(MONGODB_INTERNAL_PORT) - ); + return String.format("mongodb://%s:%d", getHost(), getMappedPort(MONGODB_INTERNAL_PORT)); } /** @@ -89,7 +87,7 @@ protected void containerIsStarted(InspectContainerResponse containerInfo) { } private String[] buildMongoEvalCommand(final String command) { - return new String[]{"mongo", "--eval", command}; + return new String[] { "mongo", "--eval", command }; } private void checkMongoNodeExitCode(final Container.ExecResult execResult) { @@ -103,21 +101,19 @@ private void checkMongoNodeExitCode(final Container.ExecResult execResult) { private String buildMongoWaitCommand() { return String.format( "var attempt = 0; " + - "while" + - "(%s) " + - "{ " + - "if (attempt > %d) {quit(1);} " + - "print('%s ' + attempt); sleep(100); attempt++; " + - " }", + "while" + + "(%s) " + + "{ " + + "if (attempt > %d) {quit(1);} " + + "print('%s ' + attempt); sleep(100); 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 - ) { + private void checkMongoNodeExitCodeAfterWaiting(final Container.ExecResult execResultWaitForMaster) { 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", @@ -128,12 +124,10 @@ private void checkMongoNodeExitCodeAfterWaiting( } } - @SneakyThrows(value = {IOException.class, InterruptedException.class}) + @SneakyThrows(value = { IOException.class, InterruptedException.class }) private void initReplicaSet() { log.debug("Initializing a single node node replica set..."); - final ExecResult execResultInitRs = execInContainer( - buildMongoEvalCommand("rs.initiate();") - ); + final ExecResult execResultInitRs = execInContainer(buildMongoEvalCommand("rs.initiate();")); log.debug(execResultInitRs.getStdout()); checkMongoNodeExitCode(execResultInitRs); @@ -141,15 +135,14 @@ private void initReplicaSet() { "Awaiting for a single node replica set initialization up to {} attempts", AWAIT_INIT_REPLICA_SET_ATTEMPTS ); - final ExecResult execResultWaitForMaster = execInContainer( - buildMongoEvalCommand(buildMongoWaitCommand()) - ); + final ExecResult 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 index 532a86208c1..6c98978f20a 100644 --- a/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBContainerTest.java +++ b/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBContainerTest.java @@ -13,12 +13,11 @@ import org.junit.Test; import org.testcontainers.utility.DockerImageName; -import static org.hamcrest.CoreMatchers.endsWith; +import static org.hamcrest.Matchers.endsWith; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThat; - public class MongoDBContainerTest { /** @@ -31,7 +30,6 @@ public void shouldExecuteTransactions() { final MongoDBContainer mongoDBContainer = new MongoDBContainer(DockerImageName.parse("mongo:4.0.10")) // } ) { - // startingMongoDBContainer { mongoDBContainer.start(); // } @@ -41,15 +39,25 @@ public void shouldExecuteTransactions() { final String connectionString = mongoDBContainer.getConnectionString(); final MongoClient mongoSyncClientBase = MongoClients.create(connectionString); 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)); - mongoSyncClientBase.getDatabase("mydb3").getCollection("baz") - .withWriteConcern(WriteConcern.MAJORITY).insertOne(new Document("def", 0)); + 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)); + mongoSyncClientBase + .getDatabase("mydb3") + .getCollection("baz") + .withWriteConcern(WriteConcern.MAJORITY) + .insertOne(new Document("def", 0)); final ClientSession clientSession = mongoSyncClient.startSession(); - final TransactionOptions txnOptions = TransactionOptions.builder() + final TransactionOptions txnOptions = TransactionOptions + .builder() .readPreference(ReadPreference.primary()) .readConcern(ReadConcern.LOCAL) .writeConcern(WriteConcern.MAJORITY) @@ -58,10 +66,8 @@ public void shouldExecuteTransactions() { final String trxResult = "Inserted into collections in different databases"; TransactionBody txnBody = () -> { - final MongoCollection coll1 = - mongoSyncClient.getDatabase("mydb1").getCollection("foo"); - final MongoCollection 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)); @@ -82,18 +88,14 @@ public void shouldExecuteTransactions() { @Test public void supportsMongoDB_4_4() { - try ( - final MongoDBContainer mongoDBContainer = new MongoDBContainer(DockerImageName.parse("mongo:4.4")) - ) { + try (final MongoDBContainer mongoDBContainer = new MongoDBContainer(DockerImageName.parse("mongo:4.4"))) { mongoDBContainer.start(); } } @Test public void shouldTestDatabaseName() { - try ( - final MongoDBContainer mongoDBContainer = new MongoDBContainer(DockerImageName.parse("mongo:4.0.10")) - ) { + try (final MongoDBContainer mongoDBContainer = new MongoDBContainer(DockerImageName.parse("mongo:4.0.10"))) { mongoDBContainer.start(); final String databaseName = "my-db"; assertThat(mongoDBContainer.getReplicaSetUrl(databaseName), endsWith(databaseName)); diff --git a/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLR2DBCDatabaseContainer.java b/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLR2DBCDatabaseContainer.java index 59c3ed0de68..6a58b3488ff 100644 --- a/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLR2DBCDatabaseContainer.java +++ b/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLR2DBCDatabaseContainer.java @@ -13,7 +13,8 @@ public class MSSQLR2DBCDatabaseContainer implements R2DBCDatabaseContainer { private final MSSQLServerContainer container; public static ConnectionFactoryOptions getOptions(MSSQLServerContainer container) { - ConnectionFactoryOptions options = ConnectionFactoryOptions.builder() + ConnectionFactoryOptions options = ConnectionFactoryOptions + .builder() .option(ConnectionFactoryOptions.DRIVER, MSSQLR2DBCDatabaseContainerProvider.DRIVER) .build(); @@ -22,7 +23,8 @@ public static ConnectionFactoryOptions getOptions(MSSQLServerContainer contai @Override public ConnectionFactoryOptions configure(ConnectionFactoryOptions options) { - return options.mutate() + return options + .mutate() .option(ConnectionFactoryOptions.HOST, container.getHost()) .option(ConnectionFactoryOptions.PORT, container.getMappedPort(MSSQLServerContainer.MS_SQL_SERVER_PORT)) // TODO enable if/when MSSQLServerContainer adds support for customizing the DB name diff --git a/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainer.java b/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainer.java index fe6270d04c7..532316fb967 100644 --- a/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainer.java +++ b/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainer.java @@ -14,6 +14,7 @@ public class MSSQLServerContainer> extends JdbcDatabaseContainer { private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("mcr.microsoft.com/mssql/server"); + @Deprecated public static final String DEFAULT_TAG = "2017-CU12"; @@ -30,13 +31,14 @@ public class MSSQLServerContainer> exten private String password = DEFAULT_PASSWORD; private static final int DEFAULT_STARTUP_TIMEOUT_SECONDS = 240; + private static final int DEFAULT_CONNECT_TIMEOUT_SECONDS = 240; - private static final Pattern[] PASSWORD_CATEGORY_VALIDATION_PATTERNS = new Pattern[]{ + private static final Pattern[] PASSWORD_CATEGORY_VALIDATION_PATTERNS = new Pattern[] { Pattern.compile("[A-Z]+"), Pattern.compile("[a-z]+"), Pattern.compile("[0-9]+"), - Pattern.compile("[^a-zA-Z0-9]+", Pattern.CASE_INSENSITIVE) + Pattern.compile("[^a-zA-Z0-9]+", Pattern.CASE_INSENSITIVE), }; /** @@ -53,7 +55,6 @@ public MSSQLServerContainer(final String dockerImageName) { public MSSQLServerContainer(final DockerImageName dockerImageName) { super(dockerImageName); - dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); withStartupTimeoutSeconds(DEFAULT_STARTUP_TIMEOUT_SECONDS); @@ -131,7 +132,6 @@ public SELF withPassword(final String password) { } private void checkPasswordStrength(String password) { - if (password == null) { throw new IllegalArgumentException("Null password is not allowed"); } @@ -144,18 +144,19 @@ private void checkPasswordStrength(String password) { throw new IllegalArgumentException("Password can be up to 128 characters long"); } - long satisfiedCategories = Stream.of(PASSWORD_CATEGORY_VALIDATION_PATTERNS) + long satisfiedCategories = Stream + .of(PASSWORD_CATEGORY_VALIDATION_PATTERNS) .filter(p -> p.matcher(password).find()) .count(); if (satisfiedCategories < 3) { throw new IllegalArgumentException( "Password must contain characters from three of the following four categories:\n" + - " - Latin uppercase letters (A through Z)\n" + - " - Latin lowercase letters (a through z)\n" + - " - Base 10 digits (0 through 9)\n" + - " - Non-alphanumeric characters such as: exclamation point (!), dollar sign ($), number sign (#), " + - "or percent (%)." + " - Latin uppercase letters (A through Z)\n" + + " - Latin lowercase letters (a through z)\n" + + " - Base 10 digits (0 through 9)\n" + + " - Non-alphanumeric characters such as: exclamation point (!), dollar sign ($), number sign (#), " + + "or percent (%)." ); } } diff --git a/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainerProvider.java b/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainerProvider.java index c3495c8a3a6..27e8ab75225 100644 --- a/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainerProvider.java +++ b/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainerProvider.java @@ -6,6 +6,7 @@ * Factory for MS SQL Server containers. */ public class MSSQLServerContainerProvider extends JdbcDatabaseContainerProvider { + @Override public boolean supports(String databaseType) { return databaseType.equals(MSSQLServerContainer.NAME); diff --git a/modules/mssqlserver/src/test/java/org/testcontainers/jdbc/mssqlserver/MSSQLServerJDBCDriverTest.java b/modules/mssqlserver/src/test/java/org/testcontainers/jdbc/mssqlserver/MSSQLServerJDBCDriverTest.java index 00e22199b46..bdb623136fb 100644 --- a/modules/mssqlserver/src/test/java/org/testcontainers/jdbc/mssqlserver/MSSQLServerJDBCDriverTest.java +++ b/modules/mssqlserver/src/test/java/org/testcontainers/jdbc/mssqlserver/MSSQLServerJDBCDriverTest.java @@ -4,18 +4,21 @@ import org.junit.runners.Parameterized; import org.testcontainers.jdbc.AbstractJDBCDriverTest; +import java.util.Arrays; import java.util.EnumSet; -import static java.util.Arrays.asList; - @RunWith(Parameterized.class) public class MSSQLServerJDBCDriverTest extends AbstractJDBCDriverTest { @Parameterized.Parameters(name = "{index} - {0}") public static Iterable data() { - return asList( - new Object[][]{ - {"jdbc:tc:sqlserver:2017-CU12://hostname:hostport;databaseName=databasename", EnumSet.noneOf(Options.class)}, - }); + return Arrays.asList( + new Object[][] { + { + "jdbc:tc:sqlserver:2017-CU12://hostname:hostport;databaseName=databasename", + EnumSet.noneOf(Options.class), + }, + } + ); } } diff --git a/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/CustomPasswordMSSQLServerTest.java b/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/CustomPasswordMSSQLServerTest.java index 6ebd3624577..49bae13d9e3 100644 --- a/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/CustomPasswordMSSQLServerTest.java +++ b/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/CustomPasswordMSSQLServerTest.java @@ -22,11 +22,15 @@ public class CustomPasswordMSSQLServerTest { private static String UPPER_CASE_LETTERS = "ABCDE"; + private static String LOWER_CASE_LETTERS = "abcde"; + private static String NUMBERS = "12345"; + private static String SPECIAL_CHARS = "_(!)_"; private String password; + private Boolean valid; public CustomPasswordMSSQLServerTest(String password, Boolean valid) { @@ -36,45 +40,41 @@ public CustomPasswordMSSQLServerTest(String password, Boolean valid) { @Parameterized.Parameters public static Collection data() { - return Arrays.asList(new Object[][]{ - new Object[]{null, false}, - // too short - {"abc123", false}, - - // too long - {RandomStringUtils.randomAlphabetic(129), false}, - - // only 2 categories - {UPPER_CASE_LETTERS + NUMBERS, false}, - {UPPER_CASE_LETTERS + SPECIAL_CHARS, false}, - {LOWER_CASE_LETTERS + NUMBERS, false}, - {LOWER_CASE_LETTERS + SPECIAL_CHARS, false}, - {NUMBERS + SPECIAL_CHARS, false}, - - // 3 categories - {UPPER_CASE_LETTERS + LOWER_CASE_LETTERS + NUMBERS, true}, - {UPPER_CASE_LETTERS + LOWER_CASE_LETTERS + SPECIAL_CHARS, true}, - {UPPER_CASE_LETTERS + NUMBERS + SPECIAL_CHARS, true}, - {LOWER_CASE_LETTERS + NUMBERS + SPECIAL_CHARS, true}, - - // 4 categories - {UPPER_CASE_LETTERS + LOWER_CASE_LETTERS + NUMBERS + SPECIAL_CHARS, true}, - - - }); + return Arrays.asList( + new Object[][] { + new Object[] { null, false }, + // too short + { "abc123", false }, + // too long + { RandomStringUtils.randomAlphabetic(129), false }, + // only 2 categories + { UPPER_CASE_LETTERS + NUMBERS, false }, + { UPPER_CASE_LETTERS + SPECIAL_CHARS, false }, + { LOWER_CASE_LETTERS + NUMBERS, false }, + { LOWER_CASE_LETTERS + SPECIAL_CHARS, false }, + { NUMBERS + SPECIAL_CHARS, false }, + // 3 categories + { UPPER_CASE_LETTERS + LOWER_CASE_LETTERS + NUMBERS, true }, + { UPPER_CASE_LETTERS + LOWER_CASE_LETTERS + SPECIAL_CHARS, true }, + { UPPER_CASE_LETTERS + NUMBERS + SPECIAL_CHARS, true }, + { LOWER_CASE_LETTERS + NUMBERS + SPECIAL_CHARS, true }, + // 4 categories + { UPPER_CASE_LETTERS + LOWER_CASE_LETTERS + NUMBERS + SPECIAL_CHARS, true }, + } + ); } @Test public void runPasswordTests() { try { new MSSQLServerContainer<>(MSSQLServerTestImages.MSSQL_SERVER_IMAGE).withPassword(this.password); - if (!valid) + if (!valid) { fail("Password " + this.password + " is not valid. Expected exception"); + } } catch (IllegalArgumentException e) { - if (valid) + if (valid) { fail("Password " + this.password + " should have been validated"); + } } } - - } diff --git a/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/CustomizableMSSQLServerTest.java b/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/CustomizableMSSQLServerTest.java index d3c387e7b4a..21ea5380801 100644 --- a/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/CustomizableMSSQLServerTest.java +++ b/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/CustomizableMSSQLServerTest.java @@ -16,9 +16,12 @@ public class CustomizableMSSQLServerTest extends AbstractContainerDatabaseTest { @Test public void testSqlServerConnection() throws SQLException { - try (MSSQLServerContainer mssqlServerContainer = new MSSQLServerContainer<>(DockerImageName.parse("mcr.microsoft.com/mssql/server:2017-CU12")) - .withPassword(STRONG_PASSWORD)) { - + try ( + MSSQLServerContainer mssqlServerContainer = new MSSQLServerContainer<>( + DockerImageName.parse("mcr.microsoft.com/mssql/server:2017-CU12") + ) + .withPassword(STRONG_PASSWORD) + ) { mssqlServerContainer.start(); ResultSet resultSet = performQuery(mssqlServerContainer, mssqlServerContainer.getTestQueryString()); diff --git a/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/SimpleMSSQLServerTest.java b/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/SimpleMSSQLServerTest.java index 1530760b673..36d215c8757 100644 --- a/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/SimpleMSSQLServerTest.java +++ b/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/SimpleMSSQLServerTest.java @@ -1,24 +1,27 @@ package org.testcontainers.junit.mssqlserver; import org.junit.Test; +import org.testcontainers.MSSQLServerTestImages; import org.testcontainers.containers.MSSQLServerContainer; import org.testcontainers.db.AbstractContainerDatabaseTest; -import javax.sql.DataSource; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; -import static org.hamcrest.CoreMatchers.containsString; +import javax.sql.DataSource; + import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.testcontainers.MSSQLServerTestImages.MSSQL_SERVER_IMAGE; public class SimpleMSSQLServerTest extends AbstractContainerDatabaseTest { @Test public void testSimple() throws SQLException { - try (MSSQLServerContainer mssqlServer = new MSSQLServerContainer<>(MSSQL_SERVER_IMAGE)) { + try ( + MSSQLServerContainer mssqlServer = new MSSQLServerContainer<>(MSSQLServerTestImages.MSSQL_SERVER_IMAGE) + ) { mssqlServer.start(); ResultSet resultSet = performQuery(mssqlServer, "SELECT 1"); @@ -29,10 +32,11 @@ public void testSimple() throws SQLException { @Test public void testWithAdditionalUrlParamInJdbcUrl() { - try (MSSQLServerContainer mssqlServer = new MSSQLServerContainer<>(MSSQL_SERVER_IMAGE) - .withUrlParam("integratedSecurity", "false") - .withUrlParam("applicationName", "MyApp")) { - + try ( + MSSQLServerContainer mssqlServer = new MSSQLServerContainer<>(MSSQLServerTestImages.MSSQL_SERVER_IMAGE) + .withUrlParam("integratedSecurity", "false") + .withUrlParam("applicationName", "MyApp") + ) { mssqlServer.start(); String jdbcUrl = mssqlServer.getJdbcUrl(); @@ -40,10 +44,11 @@ public void testWithAdditionalUrlParamInJdbcUrl() { } } - @Test public void testSetupDatabase() throws SQLException { - try (MSSQLServerContainer mssqlServer = new MSSQLServerContainer<>(MSSQL_SERVER_IMAGE)) { + try ( + MSSQLServerContainer mssqlServer = new MSSQLServerContainer<>(MSSQLServerTestImages.MSSQL_SERVER_IMAGE) + ) { mssqlServer.start(); DataSource ds = getDataSource(mssqlServer); Statement statement = ds.getConnection().createStatement(); diff --git a/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java b/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java index 20be08c2aec..14206018954 100644 --- a/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java +++ b/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java @@ -26,10 +26,15 @@ public class MySQLContainer> extends JdbcDatab static final String DEFAULT_PASSWORD = "test"; private static final String MY_CNF_CONFIG_OVERRIDE_PARAM_NAME = "TC_MY_CNF"; + public static final Integer MYSQL_PORT = 3306; + private String databaseName = "test"; + private String username = DEFAULT_USER; + private String password = DEFAULT_PASSWORD; + private static final String MYSQL_ROOT_USER = "root"; /** @@ -46,13 +51,11 @@ public MySQLContainer(String dockerImageName) { public MySQLContainer(final DockerImageName dockerImageName) { super(dockerImageName); - dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); addExposedPort(MYSQL_PORT); } - @NotNull @Override protected Set getLivenessCheckPorts() { @@ -61,8 +64,11 @@ protected Set getLivenessCheckPorts() { @Override protected void configure() { - optionallyMapResourceParameterAsVolume(MY_CNF_CONFIG_OVERRIDE_PARAM_NAME, "/etc/mysql/conf.d", - "mysql-default-conf"); + optionallyMapResourceParameterAsVolume( + MY_CNF_CONFIG_OVERRIDE_PARAM_NAME, + "/etc/mysql/conf.d", + "mysql-default-conf" + ); addEnv("MYSQL_DATABASE", databaseName); if (!MYSQL_ROOT_USER.equalsIgnoreCase(username)) { @@ -92,8 +98,7 @@ public String getDriverClassName() { @Override public String getJdbcUrl() { String additionalUrlParams = constructUrlParameters("?", "&"); - return "jdbc:mysql://" + getHost() + ":" + getMappedPort(MYSQL_PORT) + - "/" + databaseName + additionalUrlParams; + return "jdbc:mysql://" + getHost() + ":" + getMappedPort(MYSQL_PORT) + "/" + databaseName + additionalUrlParams; } @Override diff --git a/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainerProvider.java b/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainerProvider.java index 18d126635b1..621001d7b57 100644 --- a/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainerProvider.java +++ b/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainerProvider.java @@ -35,5 +35,4 @@ public JdbcDatabaseContainer newInstance(String tag) { public JdbcDatabaseContainer newInstance(ConnectionUrl connectionUrl) { return newInstanceFromConnectionUrl(connectionUrl, USER_PARAM, PASSWORD_PARAM); } - } diff --git a/modules/mysql/src/main/java/org/testcontainers/containers/MySQLR2DBCDatabaseContainer.java b/modules/mysql/src/main/java/org/testcontainers/containers/MySQLR2DBCDatabaseContainer.java index 274224352a8..4bbb542fec8 100644 --- a/modules/mysql/src/main/java/org/testcontainers/containers/MySQLR2DBCDatabaseContainer.java +++ b/modules/mysql/src/main/java/org/testcontainers/containers/MySQLR2DBCDatabaseContainer.java @@ -13,7 +13,8 @@ public class MySQLR2DBCDatabaseContainer implements R2DBCDatabaseContainer { private final MySQLContainer container; public static ConnectionFactoryOptions getOptions(MySQLContainer container) { - ConnectionFactoryOptions options = ConnectionFactoryOptions.builder() + ConnectionFactoryOptions options = ConnectionFactoryOptions + .builder() .option(ConnectionFactoryOptions.DRIVER, MySQLR2DBCDatabaseContainerProvider.DRIVER) .build(); @@ -22,7 +23,8 @@ public static ConnectionFactoryOptions getOptions(MySQLContainer container) { @Override public ConnectionFactoryOptions configure(ConnectionFactoryOptions options) { - return options.mutate() + return options + .mutate() .option(ConnectionFactoryOptions.HOST, container.getHost()) .option(ConnectionFactoryOptions.PORT, container.getMappedPort(MySQLContainer.MYSQL_PORT)) .option(ConnectionFactoryOptions.DATABASE, container.getDatabaseName()) diff --git a/modules/mysql/src/test/java/org/testcontainers/MySQLTestImages.java b/modules/mysql/src/test/java/org/testcontainers/MySQLTestImages.java index 833dc1448cf..81de83be0b9 100644 --- a/modules/mysql/src/test/java/org/testcontainers/MySQLTestImages.java +++ b/modules/mysql/src/test/java/org/testcontainers/MySQLTestImages.java @@ -3,7 +3,10 @@ import org.testcontainers.utility.DockerImageName; public class MySQLTestImages { + public static final DockerImageName MYSQL_56_IMAGE = DockerImageName.parse("mysql:5.6.51"); + public static final DockerImageName MYSQL_57_IMAGE = DockerImageName.parse("mysql:5.7.34"); + public static final DockerImageName MYSQL_80_IMAGE = DockerImageName.parse("mysql:8.0.24"); } diff --git a/modules/mysql/src/test/java/org/testcontainers/containers/MySQLR2DBCDatabaseContainerTest.java b/modules/mysql/src/test/java/org/testcontainers/containers/MySQLR2DBCDatabaseContainerTest.java index 334fabb0a49..42a852ad1f1 100644 --- a/modules/mysql/src/test/java/org/testcontainers/containers/MySQLR2DBCDatabaseContainerTest.java +++ b/modules/mysql/src/test/java/org/testcontainers/containers/MySQLR2DBCDatabaseContainerTest.java @@ -20,5 +20,4 @@ protected String createR2DBCUrl() { protected MySQLContainer createContainer() { return new MySQLContainer<>(MySQLTestImages.MYSQL_80_IMAGE); } - } diff --git a/modules/mysql/src/test/java/org/testcontainers/containers/MySQLRootAccountTest.java b/modules/mysql/src/test/java/org/testcontainers/containers/MySQLRootAccountTest.java index 86b13a0d1bf..0320eee9255 100644 --- a/modules/mysql/src/test/java/org/testcontainers/containers/MySQLRootAccountTest.java +++ b/modules/mysql/src/test/java/org/testcontainers/containers/MySQLRootAccountTest.java @@ -18,13 +18,10 @@ public class MySQLRootAccountTest { @Parameterized.Parameters(name = "{0}") public static DockerImageName[] params() { - return new DockerImageName[]{ - MySQLTestImages.MYSQL_80_IMAGE, - MySQLTestImages.MYSQL_57_IMAGE - }; + return new DockerImageName[] { MySQLTestImages.MYSQL_80_IMAGE, MySQLTestImages.MYSQL_57_IMAGE }; } - @Parameterized.Parameter() + @Parameterized.Parameter public DockerImageName image; @Test diff --git a/modules/mysql/src/test/java/org/testcontainers/jdbc/mysql/JDBCDriverWithPoolTest.java b/modules/mysql/src/test/java/org/testcontainers/jdbc/mysql/JDBCDriverWithPoolTest.java index 3b9f9842095..a30f3f59888 100644 --- a/modules/mysql/src/test/java/org/testcontainers/jdbc/mysql/JDBCDriverWithPoolTest.java +++ b/modules/mysql/src/test/java/org/testcontainers/jdbc/mysql/JDBCDriverWithPoolTest.java @@ -11,15 +11,16 @@ import org.testcontainers.jdbc.ContainerDatabaseDriver; import org.vibur.dbcp.ViburDBCPDataSource; -import javax.sql.DataSource; import java.sql.Connection; import java.sql.SQLException; +import java.util.Arrays; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.function.Supplier; -import static java.util.Arrays.asList; +import javax.sql.DataSource; + import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; /** @@ -31,12 +32,14 @@ @RunWith(Parameterized.class) public class JDBCDriverWithPoolTest { - public static final String URL = "jdbc:tc:mysql:5.7.34://hostname/databasename?TC_INITFUNCTION=org.testcontainers.jdbc.mysql.JDBCDriverWithPoolTest::sampleInitFunction"; + public static final String URL = + "jdbc:tc:mysql:5.7.34://hostname/databasename?TC_INITFUNCTION=org.testcontainers.jdbc.mysql.JDBCDriverWithPoolTest::sampleInitFunction"; + private final DataSource dataSource; @Parameterized.Parameters public static Iterable> dataSourceSuppliers() { - return asList( + return Arrays.asList( JDBCDriverWithPoolTest::getTomcatDataSourceWithDriverClassName, JDBCDriverWithPoolTest::getTomcatDataSource, JDBCDriverWithPoolTest::getHikariDataSourceWithDriverClassName, @@ -54,13 +57,12 @@ public JDBCDriverWithPoolTest(Supplier dataSourceSupplier) { @Test public void testMySQLWithConnectionPoolUsingSameContainer() throws SQLException, InterruptedException { - // Populate the database with some data in multiple threads, so that multiple connections from the pool will be used for (int i = 0; i < 100; i++) { executorService.submit(() -> { try { - new QueryRunner(dataSource).insert("INSERT INTO my_counter (n) VALUES (5)", - (ResultSetHandler) rs -> true); + new QueryRunner(dataSource) + .insert("INSERT INTO my_counter (n) VALUES (5)", (ResultSetHandler) rs -> true); } catch (SQLException e) { e.printStackTrace(); } @@ -72,22 +74,28 @@ public void testMySQLWithConnectionPoolUsingSameContainer() throws SQLException, executorService.awaitTermination(5, TimeUnit.MINUTES); // compare to expected results - int count = new QueryRunner(dataSource).query("SELECT COUNT(1) FROM my_counter", rs -> { - rs.next(); - return rs.getInt(1); - }); + int count = new QueryRunner(dataSource) + .query( + "SELECT COUNT(1) FROM my_counter", + rs -> { + rs.next(); + return rs.getInt(1); + } + ); assertEquals("Reuse of a datasource points to the same DB container", 100, count); - - int sum = new QueryRunner(dataSource).query("SELECT SUM(n) FROM my_counter", rs -> { - rs.next(); - return rs.getInt(1); - }); + int sum = new QueryRunner(dataSource) + .query( + "SELECT SUM(n) FROM my_counter", + rs -> { + rs.next(); + return rs.getInt(1); + } + ); // 100 records * 5 = 500 expected assertEquals("Reuse of a datasource points to the same DB container", 500, sum); } - private static DataSource getTomcatDataSourceWithDriverClassName() { PoolProperties poolProperties = new PoolProperties(); poolProperties.setUrl(URL + ";TEST=TOMCAT_WITH_CLASSNAME"); // append a dummy URL element to ensure different DB per test @@ -134,7 +142,7 @@ private static DataSource getViburDataSourceWithDriverClassName() { ViburDBCPDataSource ds = new ViburDBCPDataSource(); ds.setJdbcUrl(URL + ";TEST=VIBUR_WITH_CLASSNAME"); - ds.setUsername("any"); // Recent versions of Vibur require a username, even though it will not be used + ds.setUsername("any"); // Recent versions of Vibur require a username, even though it will not be used ds.setPassword(""); ds.setPoolInitialSize(3); ds.setPoolMaxSize(10); @@ -149,7 +157,7 @@ private static DataSource getViburDataSourceWithDriverClassName() { private static DataSource getViburDataSource() { ViburDBCPDataSource ds = new ViburDBCPDataSource(); ds.setJdbcUrl(URL + ";TEST=VIBUR"); - ds.setUsername("any"); // Recent versions of Vibur require a username, even though it will not be used + ds.setUsername("any"); // Recent versions of Vibur require a username, even though it will not be used ds.setPassword(""); ds.setPoolInitialSize(3); ds.setPoolMaxSize(10); @@ -162,12 +170,8 @@ private static DataSource getViburDataSource() { @SuppressWarnings("SqlNoDataSourceInspection") public static void sampleInitFunction(Connection connection) throws SQLException { - connection.createStatement().execute("CREATE TABLE bar (\n" + - " foo VARCHAR(255)\n" + - ");"); + connection.createStatement().execute("CREATE TABLE bar (\n" + " foo VARCHAR(255)\n" + ");"); connection.createStatement().execute("INSERT INTO bar (foo) VALUES ('hello world');"); - connection.createStatement().execute("CREATE TABLE my_counter (\n" + - " n INT\n" + - ");"); + connection.createStatement().execute("CREATE TABLE my_counter (\n" + " n INT\n" + ");"); } } diff --git a/modules/mysql/src/test/java/org/testcontainers/jdbc/mysql/MySQLJDBCDriverTest.java b/modules/mysql/src/test/java/org/testcontainers/jdbc/mysql/MySQLJDBCDriverTest.java index 302621879eb..752fbca8e07 100644 --- a/modules/mysql/src/test/java/org/testcontainers/jdbc/mysql/MySQLJDBCDriverTest.java +++ b/modules/mysql/src/test/java/org/testcontainers/jdbc/mysql/MySQLJDBCDriverTest.java @@ -4,27 +4,48 @@ import org.junit.runners.Parameterized; import org.testcontainers.jdbc.AbstractJDBCDriverTest; +import java.util.Arrays; import java.util.EnumSet; -import static java.util.Arrays.asList; - @RunWith(Parameterized.class) public class MySQLJDBCDriverTest extends AbstractJDBCDriverTest { @Parameterized.Parameters(name = "{index} - {0}") public static Iterable data() { - return asList( - new Object[][]{ - {"jdbc:tc:mysql://hostname/databasename", EnumSet.noneOf(Options.class)}, - {"jdbc:tc:mysql://hostname/databasename?user=someuser&TC_INITSCRIPT=somepath/init_mysql.sql", EnumSet.of(Options.ScriptedSchema, Options.JDBCParams)}, - {"jdbc:tc:mysql:5.7.34://hostname/databasename?user=someuser&TC_INITFUNCTION=org.testcontainers.jdbc.AbstractJDBCDriverTest::sampleInitFunction", EnumSet.of(Options.ScriptedSchema, Options.JDBCParams)}, - {"jdbc:tc:mysql:5.7.34://hostname/databasename?user=someuser&password=somepwd&TC_INITSCRIPT=somepath/init_mysql.sql", EnumSet.of(Options.ScriptedSchema, Options.JDBCParams)}, - {"jdbc:tc:mysql:5.7.34://hostname/databasename?user=someuser&password=somepwd&TC_INITSCRIPT=file:sql/init_mysql.sql", EnumSet.of(Options.ScriptedSchema, Options.JDBCParams)}, - {"jdbc:tc:mysql:5.7.34://hostname/databasename?user=someuser&password=somepwd&TC_INITFUNCTION=org.testcontainers.jdbc.AbstractJDBCDriverTest::sampleInitFunction", EnumSet.of(Options.ScriptedSchema, Options.JDBCParams)}, - {"jdbc:tc:mysql:5.7.34://hostname/databasename?TC_INITSCRIPT=somepath/init_unicode_mysql.sql&useUnicode=yes&characterEncoding=utf8", EnumSet.of(Options.CharacterSet)}, - {"jdbc:tc:mysql:5.7.34://hostname/databasename", EnumSet.noneOf(Options.class)}, - {"jdbc:tc:mysql:5.7.34://hostname/databasename?useSSL=false", EnumSet.noneOf(Options.class)}, - {"jdbc:tc:mysql:5.6.51://hostname/databasename?TC_MY_CNF=somepath/mysql_conf_override", EnumSet.of(Options.CustomIniFile)}, - }); + return Arrays.asList( + new Object[][] { + { "jdbc:tc:mysql://hostname/databasename", EnumSet.noneOf(Options.class) }, + { + "jdbc:tc:mysql://hostname/databasename?user=someuser&TC_INITSCRIPT=somepath/init_mysql.sql", + EnumSet.of(Options.ScriptedSchema, Options.JDBCParams), + }, + { + "jdbc:tc:mysql:5.7.34://hostname/databasename?user=someuser&TC_INITFUNCTION=org.testcontainers.jdbc.AbstractJDBCDriverTest::sampleInitFunction", + EnumSet.of(Options.ScriptedSchema, Options.JDBCParams), + }, + { + "jdbc:tc:mysql:5.7.34://hostname/databasename?user=someuser&password=somepwd&TC_INITSCRIPT=somepath/init_mysql.sql", + EnumSet.of(Options.ScriptedSchema, Options.JDBCParams), + }, + { + "jdbc:tc:mysql:5.7.34://hostname/databasename?user=someuser&password=somepwd&TC_INITSCRIPT=file:sql/init_mysql.sql", + EnumSet.of(Options.ScriptedSchema, Options.JDBCParams), + }, + { + "jdbc:tc:mysql:5.7.34://hostname/databasename?user=someuser&password=somepwd&TC_INITFUNCTION=org.testcontainers.jdbc.AbstractJDBCDriverTest::sampleInitFunction", + EnumSet.of(Options.ScriptedSchema, Options.JDBCParams), + }, + { + "jdbc:tc:mysql:5.7.34://hostname/databasename?TC_INITSCRIPT=somepath/init_unicode_mysql.sql&useUnicode=yes&characterEncoding=utf8", + EnumSet.of(Options.CharacterSet), + }, + { "jdbc:tc:mysql:5.7.34://hostname/databasename", EnumSet.noneOf(Options.class) }, + { "jdbc:tc:mysql:5.7.34://hostname/databasename?useSSL=false", EnumSet.noneOf(Options.class) }, + { + "jdbc:tc:mysql:5.6.51://hostname/databasename?TC_MY_CNF=somepath/mysql_conf_override", + EnumSet.of(Options.CustomIniFile), + }, + } + ); } } diff --git a/modules/mysql/src/test/java/org/testcontainers/junit/mysql/CustomizableMysqlTest.java b/modules/mysql/src/test/java/org/testcontainers/junit/mysql/CustomizableMysqlTest.java index 0b72ce083d2..20b49d19201 100644 --- a/modules/mysql/src/test/java/org/testcontainers/junit/mysql/CustomizableMysqlTest.java +++ b/modules/mysql/src/test/java/org/testcontainers/junit/mysql/CustomizableMysqlTest.java @@ -11,19 +11,23 @@ import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; public class CustomizableMysqlTest extends AbstractContainerDatabaseTest { + private static final String DB_NAME = "foo"; + private static final String USER = "bar"; + private static final String PWD = "baz"; @Test public void testSimple() throws SQLException { // Add MYSQL_ROOT_HOST environment so that we can root login from anywhere for testing purposes - try (MySQLContainer mysql = new MySQLContainer<>(MySQLTestImages.MYSQL_57_IMAGE) - .withDatabaseName(DB_NAME) - .withUsername(USER) - .withPassword(PWD) - .withEnv("MYSQL_ROOT_HOST", "%")) { - + try ( + MySQLContainer mysql = new MySQLContainer<>(MySQLTestImages.MYSQL_57_IMAGE) + .withDatabaseName(DB_NAME) + .withUsername(USER) + .withPassword(PWD) + .withEnv("MYSQL_ROOT_HOST", "%") + ) { mysql.start(); ResultSet resultSet = performQuery(mysql, "SELECT 1"); diff --git a/modules/mysql/src/test/java/org/testcontainers/junit/mysql/MultiVersionMySQLTest.java b/modules/mysql/src/test/java/org/testcontainers/junit/mysql/MultiVersionMySQLTest.java index 32dee3cd056..735af251236 100644 --- a/modules/mysql/src/test/java/org/testcontainers/junit/mysql/MultiVersionMySQLTest.java +++ b/modules/mysql/src/test/java/org/testcontainers/junit/mysql/MultiVersionMySQLTest.java @@ -3,6 +3,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; +import org.testcontainers.MySQLTestImages; import org.testcontainers.containers.MySQLContainer; import org.testcontainers.db.AbstractContainerDatabaseTest; import org.testcontainers.utility.DockerImageName; @@ -11,23 +12,20 @@ import java.sql.SQLException; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.testcontainers.MySQLTestImages.MYSQL_56_IMAGE; -import static org.testcontainers.MySQLTestImages.MYSQL_57_IMAGE; -import static org.testcontainers.MySQLTestImages.MYSQL_80_IMAGE; @RunWith(Parameterized.class) public class MultiVersionMySQLTest extends AbstractContainerDatabaseTest { @Parameterized.Parameters(name = "{0}") public static DockerImageName[] params() { - return new DockerImageName[]{ - MYSQL_56_IMAGE, - MYSQL_57_IMAGE, - MYSQL_80_IMAGE + return new DockerImageName[] { + MySQLTestImages.MYSQL_56_IMAGE, + MySQLTestImages.MYSQL_57_IMAGE, + MySQLTestImages.MYSQL_80_IMAGE, }; } - @Parameterized.Parameter() + @Parameterized.Parameter public DockerImageName dockerImageName; @Test @@ -37,7 +35,11 @@ public void versionCheckTest() throws SQLException { final ResultSet resultSet = performQuery(mysql, "SELECT VERSION()"); final String resultSetString = resultSet.getString(1); - assertEquals("The database version can be set using a container rule parameter", dockerImageName.getVersionPart(), resultSetString); + assertEquals( + "The database version can be set using a container rule parameter", + dockerImageName.getVersionPart(), + resultSetString + ); } } } diff --git a/modules/mysql/src/test/java/org/testcontainers/junit/mysql/SimpleMySQLTest.java b/modules/mysql/src/test/java/org/testcontainers/junit/mysql/SimpleMySQLTest.java index 90973bc4b56..6441724c443 100644 --- a/modules/mysql/src/test/java/org/testcontainers/junit/mysql/SimpleMySQLTest.java +++ b/modules/mysql/src/test/java/org/testcontainers/junit/mysql/SimpleMySQLTest.java @@ -4,6 +4,7 @@ import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.testcontainers.MySQLTestImages; import org.testcontainers.containers.ContainerLaunchException; import org.testcontainers.containers.MySQLContainer; import org.testcontainers.containers.output.Slf4jLogConsumer; @@ -18,15 +19,12 @@ import java.time.ZonedDateTime; import java.time.temporal.ChronoUnit; -import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.Matchers.containsString; import static org.junit.Assert.assertThat; import static org.junit.Assume.assumeFalse; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; import static org.rnorth.visibleassertions.VisibleAssertions.fail; -import static org.testcontainers.MySQLTestImages.MYSQL_56_IMAGE; -import static org.testcontainers.MySQLTestImages.MYSQL_57_IMAGE; - public class SimpleMySQLTest extends AbstractContainerDatabaseTest { @@ -51,10 +49,11 @@ public class SimpleMySQLTest extends AbstractContainerDatabaseTest { @Test public void testSimple() throws SQLException { - try (MySQLContainer mysql = new MySQLContainer<>(MYSQL_57_IMAGE) - .withConfigurationOverride("somepath/mysql_conf_override") - .withLogConsumer(new Slf4jLogConsumer(logger))) { - + try ( + MySQLContainer mysql = new MySQLContainer<>(MySQLTestImages.MYSQL_57_IMAGE) + .withConfigurationOverride("somepath/mysql_conf_override") + .withLogConsumer(new Slf4jLogConsumer(logger)) + ) { mysql.start(); ResultSet resultSet = performQuery(mysql, "SELECT 1"); @@ -66,16 +65,20 @@ public void testSimple() throws SQLException { @Test public void testSpecificVersion() throws SQLException { - try (MySQLContainer mysqlOldVersion = new MySQLContainer<>(MYSQL_56_IMAGE) - .withConfigurationOverride("somepath/mysql_conf_override") - .withLogConsumer(new Slf4jLogConsumer(logger))) { - + try ( + MySQLContainer mysqlOldVersion = new MySQLContainer<>(MySQLTestImages.MYSQL_56_IMAGE) + .withConfigurationOverride("somepath/mysql_conf_override") + .withLogConsumer(new Slf4jLogConsumer(logger)) + ) { mysqlOldVersion.start(); ResultSet resultSet = performQuery(mysqlOldVersion, "SELECT VERSION()"); String resultSetString = resultSet.getString(1); - assertTrue("The database version can be set using a container rule parameter", resultSetString.startsWith("5.6")); + assertTrue( + "The database version can be set using a container rule parameter", + resultSetString.startsWith("5.6") + ); } } @@ -83,9 +86,10 @@ public void testSpecificVersion() throws SQLException { public void testMySQLWithCustomIniFile() throws SQLException { assumeFalse(SystemUtils.IS_OS_WINDOWS); - try (MySQLContainer mysqlCustomConfig = new MySQLContainer<>(MYSQL_56_IMAGE) - .withConfigurationOverride("somepath/mysql_conf_override")) { - + try ( + MySQLContainer mysqlCustomConfig = new MySQLContainer<>(MySQLTestImages.MYSQL_56_IMAGE) + .withConfigurationOverride("somepath/mysql_conf_override") + ) { mysqlCustomConfig.start(); ResultSet resultSet = performQuery(mysqlCustomConfig, "SELECT @@GLOBAL.innodb_file_format"); @@ -97,9 +101,10 @@ public void testMySQLWithCustomIniFile() throws SQLException { @Test public void testCommandOverride() throws SQLException { - try (MySQLContainer mysqlCustomConfig = new MySQLContainer<>(MYSQL_57_IMAGE) - .withCommand("mysqld --auto_increment_increment=42")) { - + try ( + MySQLContainer mysqlCustomConfig = new MySQLContainer<>(MySQLTestImages.MYSQL_57_IMAGE) + .withCommand("mysqld --auto_increment_increment=42") + ) { mysqlCustomConfig.start(); ResultSet resultSet = performQuery(mysqlCustomConfig, "show variables like 'auto_increment_increment'"); @@ -111,9 +116,11 @@ public void testCommandOverride() throws SQLException { @Test public void testExplicitInitScript() throws SQLException { - try (MySQLContainer container = new MySQLContainer<>(MYSQL_57_IMAGE) - .withInitScript("somepath/init_mysql.sql") - .withLogConsumer(new Slf4jLogConsumer(logger))) { + try ( + MySQLContainer container = new MySQLContainer<>(MySQLTestImages.MYSQL_57_IMAGE) + .withInitScript("somepath/init_mysql.sql") + .withLogConsumer(new Slf4jLogConsumer(logger)) + ) { container.start(); ResultSet resultSet = performQuery(container, "SELECT foo FROM bar"); @@ -125,11 +132,13 @@ public void testExplicitInitScript() throws SQLException { @Test(expected = ContainerLaunchException.class) public void testEmptyPasswordWithNonRootUser() { - try (MySQLContainer container = new MySQLContainer<>(MYSQL_56_IMAGE) - .withDatabaseName("TEST") - .withUsername("test") - .withPassword("") - .withEnv("MYSQL_ROOT_HOST", "%")){ + try ( + MySQLContainer container = new MySQLContainer<>(MySQLTestImages.MYSQL_56_IMAGE) + .withDatabaseName("TEST") + .withUsername("test") + .withPassword("") + .withEnv("MYSQL_ROOT_HOST", "%") + ) { container.start(); fail("ContainerLaunchException expected to be thrown"); } @@ -138,12 +147,13 @@ public void testEmptyPasswordWithNonRootUser() { @Test public void testEmptyPasswordWithRootUser() throws SQLException { // Add MYSQL_ROOT_HOST environment so that we can root login from anywhere for testing purposes - try (MySQLContainer mysql = new MySQLContainer<>(MYSQL_56_IMAGE) - .withDatabaseName("foo") - .withUsername("root") - .withPassword("") - .withEnv("MYSQL_ROOT_HOST", "%")) { - + try ( + MySQLContainer mysql = new MySQLContainer<>(MySQLTestImages.MYSQL_56_IMAGE) + .withDatabaseName("foo") + .withUsername("root") + .withPassword("") + .withEnv("MYSQL_ROOT_HOST", "%") + ) { mysql.start(); ResultSet resultSet = performQuery(mysql, "SELECT 1"); @@ -155,13 +165,13 @@ public void testEmptyPasswordWithRootUser() throws SQLException { @Test public void testWithAdditionalUrlParamTimeZone() throws SQLException { - MySQLContainer mysql = new MySQLContainer<>(MYSQL_57_IMAGE) + MySQLContainer mysql = new MySQLContainer<>(MySQLTestImages.MYSQL_57_IMAGE) .withUrlParam("serverTimezone", "Europe/Zurich") .withEnv("TZ", "Europe/Zurich") .withLogConsumer(new Slf4jLogConsumer(logger)); mysql.start(); - try(Connection connection = mysql.createConnection("")) { + try (Connection connection = mysql.createConnection("")) { Statement statement = connection.createStatement(); statement.execute("SELECT NOW();"); try (ResultSet resultSet = statement.getResultSet()) { @@ -169,13 +179,18 @@ public void testWithAdditionalUrlParamTimeZone() throws SQLException { // checking that the time_zone MySQL is Europe/Zurich LocalDateTime localDateTime = resultSet.getObject(1, LocalDateTime.class); - ZonedDateTime actualDateTime = localDateTime.atZone(ZoneId.of("Europe/Zurich")) + ZonedDateTime actualDateTime = localDateTime + .atZone(ZoneId.of("Europe/Zurich")) .truncatedTo(ChronoUnit.MINUTES); - ZonedDateTime expectedDateTime = ZonedDateTime.now(ZoneId.of("Europe/Zurich")) + ZonedDateTime expectedDateTime = ZonedDateTime + .now(ZoneId.of("Europe/Zurich")) .truncatedTo(ChronoUnit.MINUTES); - String message = String.format("MySQL time zone is not Europe/Zurich. MySQL date:%s, current date:%s", - actualDateTime, expectedDateTime); + String message = String.format( + "MySQL time zone is not Europe/Zurich. MySQL date:%s, current date:%s", + actualDateTime, + expectedDateTime + ); assertTrue(message, actualDateTime.equals(expectedDateTime)); } } finally { @@ -185,19 +200,20 @@ public void testWithAdditionalUrlParamTimeZone() throws SQLException { @Test public void testWithAdditionalUrlParamMultiQueries() throws SQLException { - MySQLContainer mysql = new MySQLContainer<>(MYSQL_57_IMAGE) + MySQLContainer mysql = new MySQLContainer<>(MySQLTestImages.MYSQL_57_IMAGE) .withUrlParam("allowMultiQueries", "true") .withLogConsumer(new Slf4jLogConsumer(logger)); mysql.start(); - try(Connection connection = mysql.createConnection("")) { + try (Connection connection = mysql.createConnection("")) { Statement statement = connection.createStatement(); - String multiQuery = "DROP TABLE IF EXISTS bar; " + + String multiQuery = + "DROP TABLE IF EXISTS bar; " + "CREATE TABLE bar (foo VARCHAR(20)); " + "INSERT INTO bar (foo) VALUES ('hello world');"; statement.execute(multiQuery); statement.execute("SELECT foo FROM bar;"); - try(ResultSet resultSet = statement.getResultSet()) { + try (ResultSet resultSet = statement.getResultSet()) { resultSet.next(); String firstColumnValue = resultSet.getString(1); assertEquals("Value from bar should equal real value", "hello world", firstColumnValue); @@ -209,7 +225,7 @@ public void testWithAdditionalUrlParamMultiQueries() throws SQLException { @Test public void testWithAdditionalUrlParamInJdbcUrl() { - MySQLContainer mysql = new MySQLContainer<>(MYSQL_57_IMAGE) + MySQLContainer mysql = new MySQLContainer<>(MySQLTestImages.MYSQL_57_IMAGE) .withUrlParam("allowMultiQueries", "true") .withUrlParam("rewriteBatchedStatements", "true") .withLogConsumer(new Slf4jLogConsumer(logger)); diff --git a/modules/neo4j/src/custom-neo4j-plugin/java/ac/simons/neo4j/demos/plugins/HelloWorld.java b/modules/neo4j/src/custom-neo4j-plugin/java/ac/simons/neo4j/demos/plugins/HelloWorld.java index cbfcdc03a55..8948c98d53c 100644 --- a/modules/neo4j/src/custom-neo4j-plugin/java/ac/simons/neo4j/demos/plugins/HelloWorld.java +++ b/modules/neo4j/src/custom-neo4j-plugin/java/ac/simons/neo4j/demos/plugins/HelloWorld.java @@ -9,7 +9,6 @@ public class HelloWorld { @UserFunction("ac.simons.helloWorld") @Description("Simple Hello World") public String helloWorld(@Name("name") String name) { - return "Hello, " + name; } } diff --git a/modules/neo4j/src/main/java/org/testcontainers/containers/Neo4jContainer.java b/modules/neo4j/src/main/java/org/testcontainers/containers/Neo4jContainer.java index 6f0cf10ded7..94b4263fe8c 100644 --- a/modules/neo4j/src/main/java/org/testcontainers/containers/Neo4jContainer.java +++ b/modules/neo4j/src/main/java/org/testcontainers/containers/Neo4jContainer.java @@ -1,15 +1,5 @@ package org.testcontainers.containers; -import static java.net.HttpURLConnection.HTTP_OK; -import static java.util.stream.Collectors.toSet; - -import java.time.Duration; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; import org.testcontainers.containers.wait.strategy.HttpWaitStrategy; import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy; import org.testcontainers.containers.wait.strategy.WaitAllStrategy; @@ -19,6 +9,15 @@ import org.testcontainers.utility.LicenseAcceptance; import org.testcontainers.utility.MountableFile; +import java.net.HttpURLConnection; +import java.time.Duration; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + /** * Testcontainer for Neo4j. * @@ -36,6 +35,7 @@ public class Neo4jContainer> extends GenericContaine * The default tag (version) to use. */ private static final String DEFAULT_TAG = "4.4"; + private static final String ENTERPRISE_TAG = DEFAULT_TAG + "-enterprise"; /** @@ -91,9 +91,7 @@ public Neo4jContainer(String dockerImageName) { */ public Neo4jContainer(final DockerImageName dockerImageName) { super(dockerImageName); - - this.standardImage = dockerImageName.getUnversionedPart() - .equals(DEFAULT_IMAGE_NAME.getUnversionedPart()); + this.standardImage = dockerImageName.getUnversionedPart().equals(DEFAULT_IMAGE_NAME.getUnversionedPart()); dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); @@ -101,39 +99,37 @@ public Neo4jContainer(final DockerImageName dockerImageName) { .withRegEx(String.format(".*Bolt enabled on .*:%d\\.\n", DEFAULT_BOLT_PORT)); WaitStrategy waitForHttp = new HttpWaitStrategy() .forPort(DEFAULT_HTTP_PORT) - .forStatusCodeMatching(response -> response == HTTP_OK); + .forStatusCodeMatching(response -> response == HttpURLConnection.HTTP_OK); - this.waitStrategy = new WaitAllStrategy() - .withStrategy(waitForBolt) - .withStrategy(waitForHttp) - .withStartupTimeout(Duration.ofMinutes(2)); + this.waitStrategy = + new WaitAllStrategy() + .withStrategy(waitForBolt) + .withStrategy(waitForHttp) + .withStartupTimeout(Duration.ofMinutes(2)); addExposedPorts(DEFAULT_BOLT_PORT, DEFAULT_HTTP_PORT, DEFAULT_HTTPS_PORT); } @Override public Set getLivenessCheckPortNumbers() { - - return Stream.of(DEFAULT_BOLT_PORT, DEFAULT_HTTP_PORT, DEFAULT_HTTPS_PORT) + return Stream + .of(DEFAULT_BOLT_PORT, DEFAULT_HTTP_PORT, DEFAULT_HTTPS_PORT) .map(this::getMappedPort) - .collect(toSet()); + .collect(Collectors.toSet()); } @Override protected void configure() { - boolean emptyAdminPassword = this.adminPassword == null || this.adminPassword.isEmpty(); String neo4jAuth = emptyAdminPassword ? "none" : String.format(AUTH_FORMAT, this.adminPassword); addEnv("NEO4J_AUTH", neo4jAuth); if (!this.labsPlugins.isEmpty()) { - String enabledPlugins = this.labsPlugins.stream() - .map(pluginName -> "\"" + pluginName + "\"") - .collect(Collectors.joining(",")); + String enabledPlugins = + this.labsPlugins.stream().map(pluginName -> "\"" + pluginName + "\"").collect(Collectors.joining(",")); addEnv("NEO4JLABS_PLUGINS", "[" + enabledPlugins + "]"); } - } /** @@ -169,8 +165,8 @@ public String getHttpsUrl() { public S withEnterpriseEdition() { if (!standardImage) { throw new IllegalStateException( - String.format("Cannot use enterprise version with alternative image %s.", - getDockerImageName())); + String.format("Cannot use enterprise version with alternative image %s.", getDockerImageName()) + ); } setDockerImageName(DEFAULT_IMAGE_NAME.withTag(ENTERPRISE_TAG).asCanonicalNameString()); @@ -189,7 +185,6 @@ public S withEnterpriseEdition() { * @return This container. */ public S withAdminPassword(final String adminPassword) { - this.adminPassword = adminPassword; return self(); } @@ -226,7 +221,8 @@ public S withoutAuthentication() { public S withDatabase(MountableFile graphDb) { if (!isNeo4jDatabaseVersionSupportingDbCopy()) { throw new IllegalArgumentException( - "Copying database folder is not supported for Neo4j instances with version 4.0 or higher."); + "Copying database folder is not supported for Neo4j instances with version 4.0 or higher." + ); } return withCopyFileToContainer(graphDb, "/data/databases/graph.db"); } @@ -254,7 +250,6 @@ public S withPlugins(MountableFile plugins) { * @return This container. */ public S withNeo4jConfig(String key, String value) { - addEnv(formatConfigurationKey(key), value); return self(); } @@ -273,7 +268,8 @@ public String getAdminPassword() { * @return This container. */ public S withLabsPlugins(Neo4jLabsPlugin... neo4jLabsPlugins) { - List pluginNames = Arrays.stream(neo4jLabsPlugins) + List pluginNames = Arrays + .stream(neo4jLabsPlugins) .map(plugin -> plugin.pluginName) .collect(Collectors.toList()); @@ -295,9 +291,7 @@ public S withLabsPlugins(String... neo4jLabsPlugins) { private static String formatConfigurationKey(String plainConfigKey) { final String prefix = "NEO4J_"; - return String.format("%s%s", prefix, plainConfigKey - .replaceAll("_", "__") - .replaceAll("\\.", "_")); + return String.format("%s%s", prefix, plainConfigKey.replaceAll("_", "__").replaceAll("\\.", "_")); } private boolean isNeo4jDatabaseVersionSupportingDbCopy() { @@ -311,9 +305,12 @@ private boolean isNeo4jDatabaseVersionSupportingDbCopy() { return true; } if (!usedComparableVersion.isSemanticVersion()) { - logger().warn("Version {} is not a semantic version. The function \"withDatabase\" will fail.", usedImageVersion); + logger() + .warn( + "Version {} is not a semantic version. The function \"withDatabase\" will fail.", + usedImageVersion + ); logger().warn("Copying databases is only supported for Neo4j versions 3.5.x"); - } return false; diff --git a/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerJUnitIntegrationTest.java b/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerJUnitIntegrationTest.java index 6832dfff0c7..3a2df1f7d0c 100644 --- a/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerJUnitIntegrationTest.java +++ b/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerJUnitIntegrationTest.java @@ -24,12 +24,11 @@ public class Neo4jContainerJUnitIntegrationTest { @Test public void shouldStart() { - boolean actual = neo4jContainer.isRunning(); assertThat(actual).isTrue(); - try (Driver driver = GraphDatabase - .driver(neo4jContainer.getBoltUrl(), AuthTokens.basic("neo4j", "password")); + try ( + Driver driver = GraphDatabase.driver(neo4jContainer.getBoltUrl(), AuthTokens.basic("neo4j", "password")); Session session = driver.session() ) { long one = session.run("RETURN 1", Collections.emptyMap()).next().get(0).asLong(); diff --git a/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerTest.java b/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerTest.java index 0d3f04ef570..20cd38dc70c 100644 --- a/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerTest.java +++ b/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerTest.java @@ -29,17 +29,16 @@ public class Neo4jContainerTest { @Test public void shouldDisableAuthentication() { - try ( + // spotless:off // withoutAuthentication { Neo4jContainer neo4jContainer = new Neo4jContainer<>("neo4j:4.4") .withoutAuthentication() // } + // spotless:on ) { neo4jContainer.start(); - try (Driver driver = getDriver(neo4jContainer); - Session session = driver.session() - ) { + try (Driver driver = getDriver(neo4jContainer); Session session = driver.session()) { long one = session.run("RETURN 1", Collections.emptyMap()).next().get(0).asLong(); assertThat(one).isEqualTo(1L); } @@ -55,10 +54,7 @@ public void shouldCopyDatabase() { // } ) { neo4jContainer.start(); - try ( - Driver driver = getDriver(neo4jContainer); - Session session = driver.session() - ) { + try (Driver driver = getDriver(neo4jContainer); Session session = driver.session()) { Result result = session.run("MATCH (t:Thing) RETURN t"); assertThat(result.list().stream().map(r -> r.get("t").get("name").asString())) .containsExactlyInAnyOrder("Thing", "Thing 2", "Thing 3", "A box"); @@ -68,25 +64,29 @@ public void shouldCopyDatabase() { @Test public void shouldFailOnCopyDatabaseForDefaultNeo4j4Image() { - Assertions.assertThatIllegalArgumentException() - .isThrownBy(() -> new Neo4jContainer<>() - .withDatabase(MountableFile.forClasspathResource("/test-graph.db"))) + Assertions + .assertThatIllegalArgumentException() + .isThrownBy(() -> new Neo4jContainer<>().withDatabase(MountableFile.forClasspathResource("/test-graph.db"))) .withMessage("Copying database folder is not supported for Neo4j instances with version 4.0 or higher."); } @Test public void shouldFailOnCopyDatabaseForCustomNeo4j4Image() { - Assertions.assertThatIllegalArgumentException() - .isThrownBy(() -> new Neo4jContainer<>("neo4j:4.4.1") - .withDatabase(MountableFile.forClasspathResource("/test-graph.db"))) + Assertions + .assertThatIllegalArgumentException() + .isThrownBy(() -> { + new Neo4jContainer<>("neo4j:4.4.1").withDatabase(MountableFile.forClasspathResource("/test-graph.db")); + }) .withMessage("Copying database folder is not supported for Neo4j instances with version 4.0 or higher."); } @Test public void shouldFailOnCopyDatabaseForCustomNonSemverNeo4j4Image() { - Assertions.assertThatIllegalArgumentException() - .isThrownBy(() -> new Neo4jContainer<>("neo4j:latest") - .withDatabase(MountableFile.forClasspathResource("/test-graph.db"))) + Assertions + .assertThatIllegalArgumentException() + .isThrownBy(() -> { + new Neo4jContainer<>("neo4j:latest").withDatabase(MountableFile.forClasspathResource("/test-graph.db")); + }) .withMessage("Copying database folder is not supported for Neo4j instances with version 4.0 or higher."); } @@ -99,10 +99,7 @@ public void shouldCopyPlugins() { // } ) { neo4jContainer.start(); - try ( - Driver driver = getDriver(neo4jContainer); - Session session = driver.session() - ) { + try (Driver driver = getDriver(neo4jContainer); Session session = driver.session()) { assertThatCustomPluginWasCopied(session); } } @@ -117,10 +114,7 @@ public void shouldCopyPlugin() { // } ) { neo4jContainer.start(); - try ( - Driver driver = getDriver(neo4jContainer); - Session session = driver.session() - ) { + try (Driver driver = getDriver(neo4jContainer); Session session = driver.session()) { assertThatCustomPluginWasCopied(session); } } @@ -152,17 +146,16 @@ public void shouldRunEnterprise() { // enterpriseEdition { Neo4jContainer neo4jContainer = new Neo4jContainer<>("neo4j:4.4") .withEnterpriseEdition() - // } + // } .withAdminPassword("Picard123") ) { neo4jContainer.start(); - try ( - Driver driver = getDriver(neo4jContainer); - Session session = driver.session() - ) { + try (Driver driver = getDriver(neo4jContainer); Session session = driver.session()) { String edition = session .run("CALL dbms.components() YIELD edition RETURN edition", Collections.emptyMap()) - .next().get(0).asString(); + .next() + .get(0) + .asString(); assertThat(edition).isEqualTo("enterprise"); } } @@ -178,31 +171,28 @@ public void shouldAddConfigToEnvironment() { assertThat(neo4jContainer.getEnvMap()) .containsEntry("NEO4J_dbms_security_procedures_unrestricted", "apoc.*,algo.*"); - assertThat(neo4jContainer.getEnvMap()) - .containsEntry("NEO4J_dbms_tx__log_rotation_size", "42M"); + assertThat(neo4jContainer.getEnvMap()).containsEntry("NEO4J_dbms_tx__log_rotation_size", "42M"); } @Test public void shouldConfigureSingleLabsPlugin() { - try (Neo4jContainer neo4jContainer = new Neo4jContainer<>("neo4j:4.4") - .withLabsPlugins(Neo4jLabsPlugin.APOC)) { - - + try ( + Neo4jContainer neo4jContainer = new Neo4jContainer<>("neo4j:4.4").withLabsPlugins(Neo4jLabsPlugin.APOC) + ) { // needs to get called explicitly for setup neo4jContainer.configure(); - assertThat(neo4jContainer.getEnvMap()) - .containsEntry("NEO4JLABS_PLUGINS", "[\"apoc\"]"); + assertThat(neo4jContainer.getEnvMap()).containsEntry("NEO4JLABS_PLUGINS", "[\"apoc\"]"); } } @Test public void shouldConfigureMultipleLabsPlugins() { - try( - // configureLabsPlugins { - Neo4jContainer neo4jContainer = new Neo4jContainer<>("neo4j:4.4") - .withLabsPlugins(Neo4jLabsPlugin.APOC, Neo4jLabsPlugin.BLOOM); - // } + try ( + // configureLabsPlugins { + Neo4jContainer neo4jContainer = new Neo4jContainer<>("neo4j:4.4") + .withLabsPlugins(Neo4jLabsPlugin.APOC, Neo4jLabsPlugin.BLOOM); + // } ) { // needs to get called explicitly for setup neo4jContainer.configure(); @@ -214,23 +204,19 @@ public void shouldConfigureMultipleLabsPlugins() { @Test public void shouldConfigureSingleLabsPluginWithString() { - try (Neo4jContainer neo4jContainer = new Neo4jContainer<>("neo4j:4.4") - .withLabsPlugins("myApoc")) { - + try (Neo4jContainer neo4jContainer = new Neo4jContainer<>("neo4j:4.4").withLabsPlugins("myApoc")) { // needs to get called explicitly for setup neo4jContainer.configure(); - assertThat(neo4jContainer.getEnvMap()) - .containsEntry("NEO4JLABS_PLUGINS", "[\"myApoc\"]"); + assertThat(neo4jContainer.getEnvMap()).containsEntry("NEO4JLABS_PLUGINS", "[\"myApoc\"]"); } } @Test public void shouldConfigureMultipleLabsPluginsWithString() { - - try (Neo4jContainer neo4jContainer = new Neo4jContainer<>("neo4j:4.4") - .withLabsPlugins("myApoc", "myBloom")) { - + try ( + Neo4jContainer neo4jContainer = new Neo4jContainer<>("neo4j:4.4").withLabsPlugins("myApoc", "myBloom") + ) { // needs to get called explicitly for setup neo4jContainer.configure(); @@ -240,7 +226,6 @@ public void shouldConfigureMultipleLabsPluginsWithString() { } private static Driver getDriver(Neo4jContainer container) { - AuthToken authToken = AuthTokens.none(); if (container.getAdminPassword() != null) { authToken = AuthTokens.basic("neo4j", container.getAdminPassword()); diff --git a/modules/nginx/src/main/java/org/testcontainers/containers/NginxContainer.java b/modules/nginx/src/main/java/org/testcontainers/containers/NginxContainer.java index 296d82b7997..3e7a2062fd0 100644 --- a/modules/nginx/src/main/java/org/testcontainers/containers/NginxContainer.java +++ b/modules/nginx/src/main/java/org/testcontainers/containers/NginxContainer.java @@ -12,10 +12,14 @@ /** * @author richardnorth */ -public class NginxContainer> extends GenericContainer implements LinkableContainer { +public class NginxContainer> + extends GenericContainer + implements LinkableContainer { private static final int NGINX_DEFAULT_PORT = 80; + private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("nginx"); + private static final String DEFAULT_TAG = "1.9.4"; /** @@ -32,7 +36,6 @@ public NginxContainer(String dockerImageName) { public NginxContainer(final DockerImageName dockerImageName) { super(dockerImageName); - dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); addExposedPort(NGINX_DEFAULT_PORT); diff --git a/modules/nginx/src/test/java/org/testcontainers/junit/SimpleNginxTest.java b/modules/nginx/src/test/java/org/testcontainers/junit/SimpleNginxTest.java index e322804bf75..c1ba3d30684 100644 --- a/modules/nginx/src/test/java/org/testcontainers/junit/SimpleNginxTest.java +++ b/modules/nginx/src/test/java/org/testcontainers/junit/SimpleNginxTest.java @@ -12,7 +12,7 @@ import java.net.URL; import java.net.URLConnection; -import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.Matchers.containsString; import static org.rnorth.visibleassertions.VisibleAssertions.*; /** @@ -29,9 +29,10 @@ public class SimpleNginxTest { public NginxContainer nginx = new NginxContainer<>(NGINX_IMAGE) .withCustomContent(tmpDirectory) .waitingFor(new HttpWaitStrategy()); + // } - @SuppressWarnings({"Duplicates", "ResultOfMethodCallIgnored"}) + @SuppressWarnings({ "Duplicates", "ResultOfMethodCallIgnored" }) @BeforeClass public static void setupContent() throws Exception { // addCustomContent { @@ -43,7 +44,8 @@ public static void setupContent() throws Exception { // And "hello world" HTTP file File indexFile = new File(contentFolder, "index.html"); indexFile.deleteOnExit(); - @Cleanup PrintStream printStream = new PrintStream(new FileOutputStream(indexFile)); + @Cleanup + PrintStream printStream = new PrintStream(new FileOutputStream(indexFile)); printStream.println("Hello World!"); // } } @@ -53,7 +55,8 @@ public void testSimple() throws Exception { // getFromNginxServer { URL baseUrl = nginx.getBaseUrl("http", 80); - assertThat("An HTTP GET from the Nginx server returns the index.html from the custom content directory", + assertThat( + "An HTTP GET from the Nginx server returns the index.html from the custom content directory", responseFromNginx(baseUrl), containsString("Hello World!") ); @@ -62,7 +65,8 @@ public void testSimple() throws Exception { private static String responseFromNginx(URL baseUrl) throws IOException { URLConnection urlConnection = baseUrl.openConnection(); - @Cleanup BufferedReader reader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream())); + @Cleanup + BufferedReader reader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream())); return reader.readLine(); } } diff --git a/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java b/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java index 689646f0006..bd5d9aeb7ca 100644 --- a/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java +++ b/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java @@ -1,50 +1,59 @@ package org.testcontainers.containers; -import com.google.common.collect.Sets; import org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.NotNull; import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy; import org.testcontainers.utility.DockerImageName; import java.time.Duration; +import java.time.temporal.ChronoUnit; import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.Set; import java.util.concurrent.Future; -import static java.time.temporal.ChronoUnit.SECONDS; -import static java.util.Collections.singleton; - public class OracleContainer extends JdbcDatabaseContainer { public static final String NAME = "oracle"; + private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("gvenzl/oracle-xe"); static final String DEFAULT_TAG = "18.4.0-slim"; + static final String IMAGE = DEFAULT_IMAGE_NAME.getUnversionedPart(); private static final int ORACLE_PORT = 1521; + private static final int APEX_HTTP_PORT = 8080; private static final int DEFAULT_STARTUP_TIMEOUT_SECONDS = 240; + private static final int DEFAULT_CONNECT_TIMEOUT_SECONDS = 120; // Container defaults static final String DEFAULT_DATABASE_NAME = "xepdb1"; + static final String DEFAULT_SID = "xe"; + static final String DEFAULT_SYSTEM_USER = "system"; + static final String DEFAULT_SYS_USER = "sys"; // Test container defaults static final String APP_USER = "test"; + static final String APP_USER_PASSWORD = "test"; // Restricted user and database names private static final List ORACLE_SYSTEM_USERS = Arrays.asList(DEFAULT_SYSTEM_USER, DEFAULT_SYS_USER); private String databaseName = DEFAULT_DATABASE_NAME; + private String username = APP_USER; + private String password = APP_USER_PASSWORD; + private boolean usingSid = false; /** @@ -71,10 +80,11 @@ public OracleContainer(Future dockerImageName) { } private void preconfigure() { - this.waitStrategy = new LogMessageWaitStrategy() - .withRegEx(".*DATABASE IS READY TO USE!.*\\s") - .withTimes(1) - .withStartupTimeout(Duration.of(DEFAULT_STARTUP_TIMEOUT_SECONDS, SECONDS)); + this.waitStrategy = + new LogMessageWaitStrategy() + .withRegEx(".*DATABASE IS READY TO USE!.*\\s") + .withTimes(1) + .withStartupTimeout(Duration.of(DEFAULT_STARTUP_TIMEOUT_SECONDS, ChronoUnit.SECONDS)); withConnectTimeoutSeconds(DEFAULT_CONNECT_TIMEOUT_SECONDS); addExposedPorts(ORACLE_PORT, APEX_HTTP_PORT); @@ -88,7 +98,7 @@ protected void waitUntilContainerStarted() { @NotNull @Override public Set getLivenessCheckPortNumbers() { - return singleton(getMappedPort(ORACLE_PORT)); + return Collections.singleton(getMappedPort(ORACLE_PORT)); } @Override @@ -98,9 +108,9 @@ public String getDriverClassName() { @Override public String getJdbcUrl() { - return isUsingSid() ? - "jdbc:oracle:thin:" + "@" + getHost() + ":" + getOraclePort() + ":" + getSid() : - "jdbc:oracle:thin:" + "@" + getHost() + ":" + getOraclePort() + "/" + getDatabaseName(); + return isUsingSid() + ? "jdbc:oracle:thin:" + "@" + getHost() + ":" + getOraclePort() + ":" + getSid() + : "jdbc:oracle:thin:" + "@" + getHost() + ":" + getOraclePort() + "/" + getDatabaseName(); } @Override @@ -192,7 +202,7 @@ protected void configure() { withEnv("ORACLE_PASSWORD", password); // Only set ORACLE_DATABASE if different than the default. - if(databaseName != DEFAULT_DATABASE_NAME) { + if (databaseName != DEFAULT_DATABASE_NAME) { withEnv("ORACLE_DATABASE", databaseName); } diff --git a/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainerProvider.java b/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainerProvider.java index a298ae23359..f92f0ec40be 100644 --- a/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainerProvider.java +++ b/modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainerProvider.java @@ -6,6 +6,7 @@ * Factory for Oracle containers. */ public class OracleContainerProvider extends JdbcDatabaseContainerProvider { + @Override public boolean supports(String databaseType) { return databaseType.equals(OracleContainer.NAME); diff --git a/modules/oracle-xe/src/test/java/org/testcontainers/containers/jdbc/OracleJDBCDriverTest.java b/modules/oracle-xe/src/test/java/org/testcontainers/containers/jdbc/OracleJDBCDriverTest.java index 79dc35deb64..003941f4dfc 100644 --- a/modules/oracle-xe/src/test/java/org/testcontainers/containers/jdbc/OracleJDBCDriverTest.java +++ b/modules/oracle-xe/src/test/java/org/testcontainers/containers/jdbc/OracleJDBCDriverTest.java @@ -4,7 +4,6 @@ import com.zaxxer.hikari.HikariDataSource; import org.apache.commons.dbutils.QueryRunner; import org.apache.commons.dbutils.ResultSetHandler; -import org.junit.Ignore; import org.junit.Test; import java.sql.ResultSet; @@ -19,18 +18,21 @@ public void testOracleWithNoSpecifiedVersion() throws SQLException { performSimpleTest("jdbc:tc:oracle://hostname/databasename"); } - private void performSimpleTest(String jdbcUrl) throws SQLException { HikariDataSource dataSource = getDataSource(jdbcUrl, 1); - new QueryRunner(dataSource).query("SELECT 1 FROM dual", new ResultSetHandler() { - @Override - public Object handle(ResultSet rs) throws SQLException { - rs.next(); - int resultSetInt = rs.getInt(1); - assertEquals("A basic SELECT query succeeds", 1, resultSetInt); - return true; - } - }); + new QueryRunner(dataSource) + .query( + "SELECT 1 FROM dual", + new ResultSetHandler() { + @Override + public Object handle(ResultSet rs) throws SQLException { + rs.next(); + int resultSetInt = rs.getInt(1); + assertEquals("A basic SELECT query succeeds", 1, resultSetInt); + return true; + } + } + ); dataSource.close(); } diff --git a/modules/oracle-xe/src/test/java/org/testcontainers/junit/oracle/SimpleOracleTest.java b/modules/oracle-xe/src/test/java/org/testcontainers/junit/oracle/SimpleOracleTest.java index 2dd82c437a4..2c514062415 100644 --- a/modules/oracle-xe/src/test/java/org/testcontainers/junit/oracle/SimpleOracleTest.java +++ b/modules/oracle-xe/src/test/java/org/testcontainers/junit/oracle/SimpleOracleTest.java @@ -1,8 +1,5 @@ package org.testcontainers.junit.oracle; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; - import org.junit.Test; import org.testcontainers.containers.OracleContainer; import org.testcontainers.db.AbstractContainerDatabaseTest; @@ -11,16 +8,22 @@ import java.sql.ResultSet; import java.sql.SQLException; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + public class SimpleOracleTest extends AbstractContainerDatabaseTest { - public static final DockerImageName ORACLE_DOCKER_IMAGE_NAME = DockerImageName.parse("gvenzl/oracle-xe:18.4.0-slim"); + public static final DockerImageName ORACLE_DOCKER_IMAGE_NAME = DockerImageName.parse( + "gvenzl/oracle-xe:18.4.0-slim" + ); - private void runTest(OracleContainer container, String databaseName, String username, String password) throws SQLException { + private void runTest(OracleContainer container, String databaseName, String username, String password) + throws SQLException { //Test config was honored assertEquals(databaseName, container.getDatabaseName()); assertEquals(username, container.getUsername()); assertEquals(password, container.getPassword()); - + //Test we can get a connection container.start(); ResultSet resultSet = performQuery(container, "SELECT 1 FROM dual"); @@ -30,9 +33,7 @@ private void runTest(OracleContainer container, String databaseName, String user @Test public void testDefaultSettings() throws SQLException { - try ( - OracleContainer oracle = new OracleContainer(ORACLE_DOCKER_IMAGE_NAME); - ) { + try (OracleContainer oracle = new OracleContainer(ORACLE_DOCKER_IMAGE_NAME);) { runTest(oracle, "xepdb1", "test", "test"); // Match against the last '/' @@ -43,10 +44,7 @@ public void testDefaultSettings() throws SQLException { @Test public void testPluggableDatabase() throws SQLException { - try ( - OracleContainer oracle = new OracleContainer(ORACLE_DOCKER_IMAGE_NAME) - .withDatabaseName("testDB") - ) { + try (OracleContainer oracle = new OracleContainer(ORACLE_DOCKER_IMAGE_NAME).withDatabaseName("testDB")) { runTest(oracle, "testDB", "test", "test"); } } @@ -76,10 +74,7 @@ public void testCustomUser() throws SQLException { @Test public void testSID() throws SQLException { - try ( - OracleContainer oracle = new OracleContainer(ORACLE_DOCKER_IMAGE_NAME) - .usingSid(); - ) { + try (OracleContainer oracle = new OracleContainer(ORACLE_DOCKER_IMAGE_NAME).usingSid();) { runTest(oracle, "xepdb1", "system", "test"); // Match against the last ':' diff --git a/modules/orientdb/src/main/java/org/testcontainers/containers/OrientDBContainer.java b/modules/orientdb/src/main/java/org/testcontainers/containers/OrientDBContainer.java index a68f55b60ef..2f606475dc0 100644 --- a/modules/orientdb/src/main/java/org/testcontainers/containers/OrientDBContainer.java +++ b/modules/orientdb/src/main/java/org/testcontainers/containers/OrientDBContainer.java @@ -21,25 +21,33 @@ * @author robfrank */ public class OrientDBContainer extends GenericContainer { + private static final Logger LOGGER = LoggerFactory.getLogger(OrientDBContainer.class); private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("orientdb"); + private static final String DEFAULT_TAG = "3.0.24-tp3"; private static final String DEFAULT_USERNAME = "admin"; + private static final String DEFAULT_PASSWORD = "admin"; + private static final String DEFAULT_SERVER_PASSWORD = "root"; private static final String DEFAULT_DATABASE_NAME = "testcontainers"; private static final int DEFAULT_BINARY_PORT = 2424; + private static final int DEFAULT_HTTP_PORT = 2480; private String databaseName; + private String serverPassword; + private Optional scriptPath = Optional.empty(); private OrientDB orientDB; + private ODatabaseSession session; /** @@ -56,13 +64,12 @@ public OrientDBContainer(@NonNull String dockerImageName) { public OrientDBContainer(final DockerImageName dockerImageName) { super(dockerImageName); - dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); serverPassword = DEFAULT_SERVER_PASSWORD; databaseName = DEFAULT_DATABASE_NAME; - waitStrategy = new LogMessageWaitStrategy().withRegEx(".*Gremlin started correctly.*"); + waitStrategy = new LogMessageWaitStrategy().withRegEx(".*Gremlin started correctly.*"); addExposedPorts(DEFAULT_BINARY_PORT, DEFAULT_HTTP_PORT); } @@ -133,7 +140,9 @@ private void loadScript(String path, ODatabaseSession session) { if (resource == null) { LOGGER.warn("Could not load classpath init script: {}", scriptPath); - throw new RuntimeException("Could not load classpath init script: " + scriptPath + ". Resource not found."); + throw new RuntimeException( + "Could not load classpath init script: " + scriptPath + ". Resource not found." + ); } String script = IOUtils.toString(resource, StandardCharsets.UTF_8); @@ -147,5 +156,4 @@ private void loadScript(String path, ODatabaseSession session) { throw new RuntimeException("Error while executing init script: " + scriptPath, e); } } - } diff --git a/modules/orientdb/src/test/java/org/testcontainers/containers/OrientDBContainerTest.java b/modules/orientdb/src/test/java/org/testcontainers/containers/OrientDBContainerTest.java index 60d7b172436..675218637ce 100644 --- a/modules/orientdb/src/test/java/org/testcontainers/containers/OrientDBContainerTest.java +++ b/modules/orientdb/src/test/java/org/testcontainers/containers/OrientDBContainerTest.java @@ -42,7 +42,6 @@ public void shouldInitializeWithCommands() { @Test public void shouldQueryWithGremlin() { - try (OrientDBContainer container = new OrientDBContainer(ORIENTDB_IMAGE)) { container.start(); @@ -52,17 +51,17 @@ public void shouldQueryWithGremlin() { session.command("INSERT INTO Person set name='john'"); session.command("INSERT INTO Person set name='jane'"); - assertThat(session.execute("gremlin", - "g.V().hasLabel('Person')").stream()).hasSize(2); + assertThat(session.execute("gremlin", "g.V().hasLabel('Person')").stream()).hasSize(2); } } @Test public void shouldInitializeDatabaseFromScript() { - try (OrientDBContainer container = new OrientDBContainer(ORIENTDB_IMAGE) - .withScriptPath("initscript.osql") - .withDatabaseName("persons")) { - + try ( + OrientDBContainer container = new OrientDBContainer(ORIENTDB_IMAGE) + .withScriptPath("initscript.osql") + .withDatabaseName("persons") + ) { container.start(); assertThat(container.getDbUrl()) diff --git a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgisContainerProvider.java b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgisContainerProvider.java index e41a349365c..0c30ebbb246 100644 --- a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgisContainerProvider.java +++ b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgisContainerProvider.java @@ -9,11 +9,16 @@ public class PostgisContainerProvider extends JdbcDatabaseContainerProvider { private static final String NAME = "postgis"; - private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("postgis/postgis").asCompatibleSubstituteFor("postgres"); + + private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName + .parse("postgis/postgis") + .asCompatibleSubstituteFor("postgres"); + private static final String DEFAULT_TAG = "12-3.0"; + public static final String USER_PARAM = "user"; - public static final String PASSWORD_PARAM = "password"; + public static final String PASSWORD_PARAM = "password"; @Override public boolean supports(String databaseType) { diff --git a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java index 741a8c4139a..3b36b61ea77 100644 --- a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java +++ b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java @@ -5,19 +5,21 @@ import org.testcontainers.utility.DockerImageName; import java.time.Duration; +import java.time.temporal.ChronoUnit; +import java.util.Collections; import java.util.Set; -import static java.time.temporal.ChronoUnit.SECONDS; -import static java.util.Collections.singleton; - /** * @author richardnorth */ public class PostgreSQLContainer> extends JdbcDatabaseContainer { + public static final String NAME = "postgresql"; + public static final String IMAGE = "postgres"; public static final String DEFAULT_TAG = "9.6.12"; + private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("postgres"); public static final Integer POSTGRESQL_PORT = 5432; @@ -27,7 +29,9 @@ public class PostgreSQLContainer> extends static final String DEFAULT_PASSWORD = "test"; private String databaseName = "test"; + private String username = "test"; + private String password = "test"; private static final String FSYNC_OFF_OPTION = "fsync=off"; @@ -46,13 +50,13 @@ public PostgreSQLContainer(final String dockerImageName) { public PostgreSQLContainer(final DockerImageName dockerImageName) { super(dockerImageName); - dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); - this.waitStrategy = new LogMessageWaitStrategy() + this.waitStrategy = + new LogMessageWaitStrategy() .withRegEx(".*database system is ready to accept connections.*\\s") .withTimes(2) - .withStartupTimeout(Duration.of(60, SECONDS)); + .withStartupTimeout(Duration.of(60, ChronoUnit.SECONDS)); this.setCommand("postgres", "-c", FSYNC_OFF_OPTION); addExposedPort(POSTGRESQL_PORT); @@ -61,7 +65,7 @@ public PostgreSQLContainer(final DockerImageName dockerImageName) { @NotNull @Override protected Set getLivenessCheckPorts() { - return singleton(getMappedPort(POSTGRESQL_PORT)); + return Collections.singleton(getMappedPort(POSTGRESQL_PORT)); } @Override @@ -81,8 +85,15 @@ public String getDriverClassName() { @Override public String getJdbcUrl() { String additionalUrlParams = constructUrlParameters("?", "&"); - return "jdbc:postgresql://" + getHost() + ":" + getMappedPort(POSTGRESQL_PORT) - + "/" + databaseName + additionalUrlParams; + return ( + "jdbc:postgresql://" + + getHost() + + ":" + + getMappedPort(POSTGRESQL_PORT) + + "/" + + databaseName + + additionalUrlParams + ); } @Override diff --git a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainerProvider.java b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainerProvider.java index 5e7580c75eb..de98885510c 100644 --- a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainerProvider.java +++ b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainerProvider.java @@ -9,6 +9,7 @@ public class PostgreSQLContainerProvider extends JdbcDatabaseContainerProvider { public static final String USER_PARAM = "user"; + public static final String PASSWORD_PARAM = "password"; @Override @@ -30,5 +31,4 @@ public JdbcDatabaseContainer newInstance(String tag) { public JdbcDatabaseContainer newInstance(ConnectionUrl connectionUrl) { return newInstanceFromConnectionUrl(connectionUrl, USER_PARAM, PASSWORD_PARAM); } - } diff --git a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLR2DBCDatabaseContainer.java b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLR2DBCDatabaseContainer.java index 88b5497f5d3..d2b933694c7 100644 --- a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLR2DBCDatabaseContainer.java +++ b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLR2DBCDatabaseContainer.java @@ -13,7 +13,8 @@ public final class PostgreSQLR2DBCDatabaseContainer implements R2DBCDatabaseCont private final PostgreSQLContainer container; public static ConnectionFactoryOptions getOptions(PostgreSQLContainer container) { - ConnectionFactoryOptions options = ConnectionFactoryOptions.builder() + ConnectionFactoryOptions options = ConnectionFactoryOptions + .builder() .option(ConnectionFactoryOptions.DRIVER, PostgreSQLR2DBCDatabaseContainerProvider.DRIVER) .build(); @@ -22,7 +23,8 @@ public static ConnectionFactoryOptions getOptions(PostgreSQLContainer contain @Override public ConnectionFactoryOptions configure(ConnectionFactoryOptions options) { - return options.mutate() + return options + .mutate() .option(ConnectionFactoryOptions.HOST, container.getHost()) .option(ConnectionFactoryOptions.PORT, container.getMappedPort(PostgreSQLContainer.POSTGRESQL_PORT)) .option(ConnectionFactoryOptions.DATABASE, container.getDatabaseName()) diff --git a/modules/postgresql/src/main/java/org/testcontainers/containers/TimescaleDBContainerProvider.java b/modules/postgresql/src/main/java/org/testcontainers/containers/TimescaleDBContainerProvider.java index c9b81fccfe4..86fcca2c039 100644 --- a/modules/postgresql/src/main/java/org/testcontainers/containers/TimescaleDBContainerProvider.java +++ b/modules/postgresql/src/main/java/org/testcontainers/containers/TimescaleDBContainerProvider.java @@ -11,11 +11,16 @@ public class TimescaleDBContainerProvider extends JdbcDatabaseContainerProvider { private static final String NAME = "timescaledb"; + private static final String DEFAULT_TAG = "2.1.0-pg11"; - private static final DockerImageName DEFAULT_IMAGE = DockerImageName.parse("timescale/timescaledb").asCompatibleSubstituteFor("postgres"); + + private static final DockerImageName DEFAULT_IMAGE = DockerImageName + .parse("timescale/timescaledb") + .asCompatibleSubstituteFor("postgres"); + public static final String USER_PARAM = "user"; - public static final String PASSWORD_PARAM = "password"; + public static final String PASSWORD_PARAM = "password"; @Override public boolean supports(String databaseType) { diff --git a/modules/postgresql/src/test/java/org/testcontainers/containers/PostgreSQLConnectionURLTest.java b/modules/postgresql/src/test/java/org/testcontainers/containers/PostgreSQLConnectionURLTest.java index c18f19a109e..3be7685ea27 100644 --- a/modules/postgresql/src/test/java/org/testcontainers/containers/PostgreSQLConnectionURLTest.java +++ b/modules/postgresql/src/test/java/org/testcontainers/containers/PostgreSQLConnectionURLTest.java @@ -16,7 +16,10 @@ public void shouldCorrectlyAppendQueryString() { String connectionUrl = postgres.constructUrlForConnection("?stringtype=unspecified&stringtype=unspecified"); String queryString = connectionUrl.substring(connectionUrl.indexOf('?')); - assertTrue("Query String contains expected params", queryString.contains("?stringtype=unspecified&stringtype=unspecified")); + assertTrue( + "Query String contains expected params", + queryString.contains("?stringtype=unspecified&stringtype=unspecified") + ); assertEquals("Query String starts with '?'", 0, queryString.indexOf('?')); assertFalse("Query String does not contain extra '?'", queryString.substring(1).contains("?")); } @@ -27,7 +30,10 @@ public void shouldCorrectlyAppendQueryStringWhenNoBaseParams() { String connectionUrl = postgres.constructUrlForConnection("?stringtype=unspecified&stringtype=unspecified"); String queryString = connectionUrl.substring(connectionUrl.indexOf('?')); - assertTrue("Query String contains expected params", queryString.contains("?stringtype=unspecified&stringtype=unspecified")); + assertTrue( + "Query String contains expected params", + queryString.contains("?stringtype=unspecified&stringtype=unspecified") + ); assertEquals("Query String starts with '?'", 0, queryString.indexOf('?')); assertFalse("Query String does not contain extra '?'", queryString.substring(1).contains("?")); } @@ -42,11 +48,15 @@ public void shouldReturnOriginalURLWhenEmptyQueryString() { @Test public void shouldRejectInvalidQueryString() { - assertThrows("Fails when invalid query string provided", IllegalArgumentException.class, - () -> new NoParamsUrlPostgreSQLContainer().constructUrlForConnection("stringtype=unspecified")); + assertThrows( + "Fails when invalid query string provided", + IllegalArgumentException.class, + () -> new NoParamsUrlPostgreSQLContainer().constructUrlForConnection("stringtype=unspecified") + ); } static class FixedJdbcUrlPostgreSQLContainer extends PostgreSQLContainer { + public FixedJdbcUrlPostgreSQLContainer() { super(PostgreSQLTestImages.POSTGRES_TEST_IMAGE); } @@ -63,6 +73,7 @@ public Integer getMappedPort(int originalPort) { } static class NoParamsUrlPostgreSQLContainer extends PostgreSQLContainer { + public NoParamsUrlPostgreSQLContainer() { super(PostgreSQLTestImages.POSTGRES_TEST_IMAGE); } diff --git a/modules/postgresql/src/test/java/org/testcontainers/containers/PostgreSQLR2DBCDatabaseContainerTest.java b/modules/postgresql/src/test/java/org/testcontainers/containers/PostgreSQLR2DBCDatabaseContainerTest.java index 46e439aaf66..fb503bfd659 100644 --- a/modules/postgresql/src/test/java/org/testcontainers/containers/PostgreSQLR2DBCDatabaseContainerTest.java +++ b/modules/postgresql/src/test/java/org/testcontainers/containers/PostgreSQLR2DBCDatabaseContainerTest.java @@ -13,11 +13,13 @@ protected PostgreSQLContainer createContainer() { @Override protected ConnectionFactoryOptions getOptions(PostgreSQLContainer container) { + // spotless:off // get_options { ConnectionFactoryOptions options = PostgreSQLR2DBCDatabaseContainer.getOptions( container ); // } + // spotless:on return options; } diff --git a/modules/postgresql/src/test/java/org/testcontainers/containers/TimescaleDBContainerTest.java b/modules/postgresql/src/test/java/org/testcontainers/containers/TimescaleDBContainerTest.java index f3c73a4111b..74d60b2d32d 100644 --- a/modules/postgresql/src/test/java/org/testcontainers/containers/TimescaleDBContainerTest.java +++ b/modules/postgresql/src/test/java/org/testcontainers/containers/TimescaleDBContainerTest.java @@ -24,10 +24,17 @@ public void testSimple() throws SQLException { @Test public void testCommandOverride() throws SQLException { - try (GenericContainer postgres = new TimescaleDBContainerProvider().newInstance().withCommand("postgres -c max_connections=42")) { + try ( + GenericContainer postgres = new TimescaleDBContainerProvider() + .newInstance() + .withCommand("postgres -c max_connections=42") + ) { postgres.start(); - ResultSet resultSet = performQuery((JdbcDatabaseContainer) postgres, "SELECT current_setting('max_connections')"); + ResultSet resultSet = performQuery( + (JdbcDatabaseContainer) postgres, + "SELECT current_setting('max_connections')" + ); String result = resultSet.getString(1); assertEquals("max_connections should be overriden", "42", result); } @@ -35,10 +42,18 @@ public void testCommandOverride() throws SQLException { @Test public void testUnsetCommand() throws SQLException { - try (GenericContainer postgres = new TimescaleDBContainerProvider().newInstance().withCommand("postgres -c max_connections=42").withCommand()) { + try ( + GenericContainer postgres = new TimescaleDBContainerProvider() + .newInstance() + .withCommand("postgres -c max_connections=42") + .withCommand() + ) { postgres.start(); - ResultSet resultSet = performQuery((JdbcDatabaseContainer) postgres, "SELECT current_setting('max_connections')"); + ResultSet resultSet = performQuery( + (JdbcDatabaseContainer) postgres, + "SELECT current_setting('max_connections')" + ); String result = resultSet.getString(1); assertNotEquals("max_connections should not be overriden", "42", result); } @@ -46,7 +61,11 @@ public void testUnsetCommand() throws SQLException { @Test public void testExplicitInitScript() throws SQLException { - try (JdbcDatabaseContainer postgres = new TimescaleDBContainerProvider().newInstance().withInitScript("somepath/init_timescaledb.sql")) { + try ( + JdbcDatabaseContainer postgres = new TimescaleDBContainerProvider() + .newInstance() + .withInitScript("somepath/init_timescaledb.sql") + ) { postgres.start(); ResultSet resultSet = performQuery(postgres, "SELECT foo FROM bar"); diff --git a/modules/postgresql/src/test/java/org/testcontainers/jdbc/DatabaseDriverShutdownTest.java b/modules/postgresql/src/test/java/org/testcontainers/jdbc/DatabaseDriverShutdownTest.java index 9e3e0b03413..5acbd875c23 100644 --- a/modules/postgresql/src/test/java/org/testcontainers/jdbc/DatabaseDriverShutdownTest.java +++ b/modules/postgresql/src/test/java/org/testcontainers/jdbc/DatabaseDriverShutdownTest.java @@ -19,6 +19,7 @@ * TODO: Move to the jdbc module and either (a) implement a barebones {@link org.testcontainers.containers.JdbcDatabaseContainerProvider} for testing, or (b) refactor into a unit test. */ public class DatabaseDriverShutdownTest { + @AfterClass public static void testCleanup() { ContainerDatabaseDriver.killContainers(); diff --git a/modules/postgresql/src/test/java/org/testcontainers/jdbc/DatabaseDriverTmpfsTest.java b/modules/postgresql/src/test/java/org/testcontainers/jdbc/DatabaseDriverTmpfsTest.java index a2b1fd5da8b..4c8d6afeef0 100644 --- a/modules/postgresql/src/test/java/org/testcontainers/jdbc/DatabaseDriverTmpfsTest.java +++ b/modules/postgresql/src/test/java/org/testcontainers/jdbc/DatabaseDriverTmpfsTest.java @@ -22,7 +22,6 @@ public class DatabaseDriverTmpfsTest { public void testDatabaseHasTmpFsViaConnectionString() throws Exception { final String jdbcUrl = "jdbc:tc:postgresql:9.6.8://hostname/databasename?TC_TMPFS=/testtmpfs:rw"; try (Connection ignored = DriverManager.getConnection(jdbcUrl)) { - JdbcDatabaseContainer container = ContainerDatabaseDriver.getContainer(jdbcUrl); // check file doesn't exist String path = "/testtmpfs/test.file"; diff --git a/modules/postgresql/src/test/java/org/testcontainers/jdbc/postgis/PostgisJDBCDriverTest.java b/modules/postgresql/src/test/java/org/testcontainers/jdbc/postgis/PostgisJDBCDriverTest.java index b1d01441016..abc0a3b76bc 100644 --- a/modules/postgresql/src/test/java/org/testcontainers/jdbc/postgis/PostgisJDBCDriverTest.java +++ b/modules/postgresql/src/test/java/org/testcontainers/jdbc/postgis/PostgisJDBCDriverTest.java @@ -4,19 +4,25 @@ import org.junit.runners.Parameterized; import org.testcontainers.jdbc.AbstractJDBCDriverTest; +import java.util.Arrays; import java.util.EnumSet; -import static java.util.Arrays.asList; - @RunWith(Parameterized.class) public class PostgisJDBCDriverTest extends AbstractJDBCDriverTest { @Parameterized.Parameters(name = "{index} - {0}") public static Iterable data() { - return asList( - new Object[][]{ - {"jdbc:tc:postgis://hostname/databasename?user=someuser&password=somepwd", EnumSet.of(Options.JDBCParams)}, - {"jdbc:tc:postgis:9.6-2.5://hostname/databasename?user=someuser&password=somepwd", EnumSet.of(Options.JDBCParams)}, - }); + return Arrays.asList( + new Object[][] { + { + "jdbc:tc:postgis://hostname/databasename?user=someuser&password=somepwd", + EnumSet.of(Options.JDBCParams), + }, + { + "jdbc:tc:postgis:9.6-2.5://hostname/databasename?user=someuser&password=somepwd", + EnumSet.of(Options.JDBCParams), + }, + } + ); } } diff --git a/modules/postgresql/src/test/java/org/testcontainers/jdbc/postgresql/PostgreSQLJDBCDriverTest.java b/modules/postgresql/src/test/java/org/testcontainers/jdbc/postgresql/PostgreSQLJDBCDriverTest.java index c63eb9ffb9d..d42f17f3ec2 100644 --- a/modules/postgresql/src/test/java/org/testcontainers/jdbc/postgresql/PostgreSQLJDBCDriverTest.java +++ b/modules/postgresql/src/test/java/org/testcontainers/jdbc/postgresql/PostgreSQLJDBCDriverTest.java @@ -4,18 +4,21 @@ import org.junit.runners.Parameterized; import org.testcontainers.jdbc.AbstractJDBCDriverTest; +import java.util.Arrays; import java.util.EnumSet; -import static java.util.Arrays.asList; - @RunWith(Parameterized.class) public class PostgreSQLJDBCDriverTest extends AbstractJDBCDriverTest { @Parameterized.Parameters(name = "{index} - {0}") public static Iterable data() { - return asList( - new Object[][]{ - {"jdbc:tc:postgresql:9.6.8://hostname/databasename?user=someuser&password=somepwd", EnumSet.of(Options.JDBCParams)}, - }); + return Arrays.asList( + new Object[][] { + { + "jdbc:tc:postgresql:9.6.8://hostname/databasename?user=someuser&password=somepwd", + EnumSet.of(Options.JDBCParams), + }, + } + ); } } diff --git a/modules/postgresql/src/test/java/org/testcontainers/jdbc/timescaledb/TimescaleDBJDBCDriverTest.java b/modules/postgresql/src/test/java/org/testcontainers/jdbc/timescaledb/TimescaleDBJDBCDriverTest.java index 8a6eaa3d04b..2a50b46c901 100644 --- a/modules/postgresql/src/test/java/org/testcontainers/jdbc/timescaledb/TimescaleDBJDBCDriverTest.java +++ b/modules/postgresql/src/test/java/org/testcontainers/jdbc/timescaledb/TimescaleDBJDBCDriverTest.java @@ -4,19 +4,25 @@ import org.junit.runners.Parameterized; import org.testcontainers.jdbc.AbstractJDBCDriverTest; +import java.util.Arrays; import java.util.EnumSet; -import static java.util.Arrays.asList; - @RunWith(Parameterized.class) public class TimescaleDBJDBCDriverTest extends AbstractJDBCDriverTest { @Parameterized.Parameters(name = "{index} - {0}") public static Iterable data() { - return asList( - new Object[][]{ - {"jdbc:tc:timescaledb://hostname/databasename?user=someuser&password=somepwd", EnumSet.of(Options.JDBCParams)}, - {"jdbc:tc:timescaledb:2.1.0-pg13://hostname/databasename?user=someuser&password=somepwd", EnumSet.of(Options.JDBCParams)}, - }); + return Arrays.asList( + new Object[][] { + { + "jdbc:tc:timescaledb://hostname/databasename?user=someuser&password=somepwd", + EnumSet.of(Options.JDBCParams), + }, + { + "jdbc:tc:timescaledb:2.1.0-pg13://hostname/databasename?user=someuser&password=somepwd", + EnumSet.of(Options.JDBCParams), + }, + } + ); } } diff --git a/modules/postgresql/src/test/java/org/testcontainers/junit/postgresql/CustomizablePostgreSQLTest.java b/modules/postgresql/src/test/java/org/testcontainers/junit/postgresql/CustomizablePostgreSQLTest.java index 8fd523434dd..1b256c0fd6b 100644 --- a/modules/postgresql/src/test/java/org/testcontainers/junit/postgresql/CustomizablePostgreSQLTest.java +++ b/modules/postgresql/src/test/java/org/testcontainers/junit/postgresql/CustomizablePostgreSQLTest.java @@ -14,17 +14,21 @@ * @author richardnorth */ public class CustomizablePostgreSQLTest extends AbstractContainerDatabaseTest { + private static final String DB_NAME = "foo"; + private static final String USER = "bar"; + private static final String PWD = "baz"; @Test public void testSimple() throws SQLException { - try (PostgreSQLContainer postgres = new PostgreSQLContainer<>(PostgreSQLTestImages.POSTGRES_TEST_IMAGE) + try ( + PostgreSQLContainer postgres = new PostgreSQLContainer<>(PostgreSQLTestImages.POSTGRES_TEST_IMAGE) .withDatabaseName(DB_NAME) .withUsername(USER) - .withPassword(PWD)) { - + .withPassword(PWD) + ) { postgres.start(); ResultSet resultSet = performQuery(postgres, "SELECT 1"); diff --git a/modules/postgresql/src/test/java/org/testcontainers/junit/postgresql/SimplePostgreSQLTest.java b/modules/postgresql/src/test/java/org/testcontainers/junit/postgresql/SimplePostgreSQLTest.java index 95e1fdf10fd..2a0dd351ad6 100644 --- a/modules/postgresql/src/test/java/org/testcontainers/junit/postgresql/SimplePostgreSQLTest.java +++ b/modules/postgresql/src/test/java/org/testcontainers/junit/postgresql/SimplePostgreSQLTest.java @@ -1,6 +1,7 @@ package org.testcontainers.junit.postgresql; import org.junit.Test; +import org.testcontainers.PostgreSQLTestImages; import org.testcontainers.containers.PostgreSQLContainer; import org.testcontainers.db.AbstractContainerDatabaseTest; @@ -9,14 +10,12 @@ import java.util.logging.Level; import java.util.logging.LogManager; -import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.Matchers.containsString; import static org.junit.Assert.assertThat; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; import static org.rnorth.visibleassertions.VisibleAssertions.assertNotEquals; -import static org.testcontainers.PostgreSQLTestImages.POSTGRES_TEST_IMAGE; public class SimplePostgreSQLTest extends AbstractContainerDatabaseTest { - static { // Postgres JDBC driver uses JUL; disable it to avoid annoying, irrelevant, stderr logs during connection testing LogManager.getLogManager().getLogger("").setLevel(Level.OFF); @@ -24,7 +23,7 @@ public class SimplePostgreSQLTest extends AbstractContainerDatabaseTest { @Test public void testSimple() throws SQLException { - try (PostgreSQLContainer postgres = new PostgreSQLContainer<>(POSTGRES_TEST_IMAGE)) { + try (PostgreSQLContainer postgres = new PostgreSQLContainer<>(PostgreSQLTestImages.POSTGRES_TEST_IMAGE)) { postgres.start(); ResultSet resultSet = performQuery(postgres, "SELECT 1"); @@ -35,7 +34,10 @@ public void testSimple() throws SQLException { @Test public void testCommandOverride() throws SQLException { - try (PostgreSQLContainer postgres = new PostgreSQLContainer<>(POSTGRES_TEST_IMAGE).withCommand("postgres -c max_connections=42")) { + try ( + PostgreSQLContainer postgres = new PostgreSQLContainer<>(PostgreSQLTestImages.POSTGRES_TEST_IMAGE) + .withCommand("postgres -c max_connections=42") + ) { postgres.start(); ResultSet resultSet = performQuery(postgres, "SELECT current_setting('max_connections')"); @@ -46,7 +48,11 @@ public void testCommandOverride() throws SQLException { @Test public void testUnsetCommand() throws SQLException { - try (PostgreSQLContainer postgres = new PostgreSQLContainer<>(POSTGRES_TEST_IMAGE).withCommand("postgres -c max_connections=42").withCommand()) { + try ( + PostgreSQLContainer postgres = new PostgreSQLContainer<>(PostgreSQLTestImages.POSTGRES_TEST_IMAGE) + .withCommand("postgres -c max_connections=42") + .withCommand() + ) { postgres.start(); ResultSet resultSet = performQuery(postgres, "SELECT current_setting('max_connections')"); @@ -57,7 +63,10 @@ public void testUnsetCommand() throws SQLException { @Test public void testExplicitInitScript() throws SQLException { - try (PostgreSQLContainer postgres = new PostgreSQLContainer<>(POSTGRES_TEST_IMAGE).withInitScript("somepath/init_postgresql.sql")) { + try ( + PostgreSQLContainer postgres = new PostgreSQLContainer<>(PostgreSQLTestImages.POSTGRES_TEST_IMAGE) + .withInitScript("somepath/init_postgresql.sql") + ) { postgres.start(); ResultSet resultSet = performQuery(postgres, "SELECT foo FROM bar"); @@ -69,9 +78,10 @@ public void testExplicitInitScript() throws SQLException { @Test public void testWithAdditionalUrlParamInJdbcUrl() { - try (PostgreSQLContainer postgres = new PostgreSQLContainer<>(POSTGRES_TEST_IMAGE) - .withUrlParam("charSet", "UNICODE")) { - + try ( + PostgreSQLContainer postgres = new PostgreSQLContainer<>(PostgreSQLTestImages.POSTGRES_TEST_IMAGE) + .withUrlParam("charSet", "UNICODE") + ) { postgres.start(); String jdbcUrl = postgres.getJdbcUrl(); assertThat(jdbcUrl, containsString("?")); diff --git a/modules/presto/src/main/java/org/testcontainers/containers/PrestoContainer.java b/modules/presto/src/main/java/org/testcontainers/containers/PrestoContainer.java index 30d77c1a469..e73f6cc0f6b 100644 --- a/modules/presto/src/main/java/org/testcontainers/containers/PrestoContainer.java +++ b/modules/presto/src/main/java/org/testcontainers/containers/PrestoContainer.java @@ -1,5 +1,6 @@ package org.testcontainers.containers; +import com.google.common.base.Strings; import org.jetbrains.annotations.NotNull; import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy; import org.testcontainers.utility.DockerImageName; @@ -7,26 +8,28 @@ import java.sql.Connection; import java.sql.SQLException; import java.time.Duration; +import java.time.temporal.ChronoUnit; import java.util.HashSet; import java.util.Set; -import static com.google.common.base.Strings.nullToEmpty; -import static java.lang.String.format; -import static java.time.temporal.ChronoUnit.SECONDS; - /** * @deprecated Use {@code TrinoContainer} instead. */ @Deprecated public class PrestoContainer> extends JdbcDatabaseContainer { + public static final String NAME = "presto"; + private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("ghcr.io/trinodb/presto"); + public static final String IMAGE = "ghcr.io/trinodb/presto"; + public static final String DEFAULT_TAG = "344"; public static final Integer PRESTO_PORT = 8080; private String username = "test"; + private String catalog = null; /** @@ -43,12 +46,12 @@ public PrestoContainer(final String dockerImageName) { public PrestoContainer(final DockerImageName dockerImageName) { super(dockerImageName); - dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); - this.waitStrategy = new LogMessageWaitStrategy() - .withRegEx(".*======== SERVER STARTED ========.*") - .withStartupTimeout(Duration.of(60, SECONDS)); + this.waitStrategy = + new LogMessageWaitStrategy() + .withRegEx(".*======== SERVER STARTED ========.*") + .withStartupTimeout(Duration.of(60, ChronoUnit.SECONDS)); addExposedPort(PRESTO_PORT); } @@ -66,7 +69,12 @@ public String getDriverClassName() { @Override public String getJdbcUrl() { - return format("jdbc:presto://%s:%s/%s", getHost(), getMappedPort(PRESTO_PORT), nullToEmpty(catalog)); + return String.format( + "jdbc:presto://%s:%s/%s", + getHost(), + getMappedPort(PRESTO_PORT), + Strings.nullToEmpty(catalog) + ); } @Override diff --git a/modules/presto/src/main/java/org/testcontainers/containers/PrestoContainerProvider.java b/modules/presto/src/main/java/org/testcontainers/containers/PrestoContainerProvider.java index 8408aab4517..f37efb9438e 100644 --- a/modules/presto/src/main/java/org/testcontainers/containers/PrestoContainerProvider.java +++ b/modules/presto/src/main/java/org/testcontainers/containers/PrestoContainerProvider.java @@ -9,6 +9,7 @@ public class PrestoContainerProvider extends JdbcDatabaseContainerProvider { public static final String USER_PARAM = "user"; + public static final String PASSWORD_PARAM = "password"; @Override @@ -30,5 +31,4 @@ public JdbcDatabaseContainer newInstance(String tag) { public JdbcDatabaseContainer newInstance(ConnectionUrl connectionUrl) { return newInstanceFromConnectionUrl(connectionUrl, USER_PARAM, PASSWORD_PARAM); } - } diff --git a/modules/presto/src/test/java/org/testcontainers/containers/PrestoContainerTest.java b/modules/presto/src/test/java/org/testcontainers/containers/PrestoContainerTest.java index d5a4d9587a6..a54108ea1a2 100644 --- a/modules/presto/src/test/java/org/testcontainers/containers/PrestoContainerTest.java +++ b/modules/presto/src/test/java/org/testcontainers/containers/PrestoContainerTest.java @@ -3,7 +3,6 @@ import org.junit.Assert; import org.junit.Test; import org.testcontainers.PrestoTestImages; -import org.testcontainers.utility.DockerImageName; import java.sql.Connection; import java.sql.DriverManager; @@ -14,8 +13,6 @@ import java.util.Arrays; import java.util.List; -import static java.lang.Integer.parseInt; -import static java.lang.String.format; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -29,9 +26,11 @@ public class PrestoContainerTest { public void testSimple() throws Exception { try (PrestoContainer prestoSql = new PrestoContainer<>(PrestoTestImages.PRESTO_TEST_IMAGE)) { prestoSql.start(); - try (Connection connection = prestoSql.createConnection(); - Statement statement = connection.createStatement(); - ResultSet resultSet = statement.executeQuery("SELECT DISTINCT node_version FROM system.runtime.nodes")) { + try ( + Connection connection = prestoSql.createConnection(); + Statement statement = connection.createStatement(); + ResultSet resultSet = statement.executeQuery("SELECT DISTINCT node_version FROM system.runtime.nodes") + ) { assertTrue("No result", resultSet.next()); assertEquals("Presto version", PrestoContainer.DEFAULT_TAG, resultSet.getString("node_version")); } @@ -40,13 +39,21 @@ public void testSimple() throws Exception { @Test public void testSpecificVersion() throws Exception { - try (PrestoContainer prestoSql = new PrestoContainer<>(PrestoTestImages.PRESTO_PREVIOUS_VERSION_TEST_IMAGE)) { + try ( + PrestoContainer prestoSql = new PrestoContainer<>(PrestoTestImages.PRESTO_PREVIOUS_VERSION_TEST_IMAGE) + ) { prestoSql.start(); - try (Connection connection = prestoSql.createConnection(); - Statement statement = connection.createStatement(); - ResultSet resultSet = statement.executeQuery("SELECT DISTINCT node_version FROM system.runtime.nodes")) { + try ( + Connection connection = prestoSql.createConnection(); + Statement statement = connection.createStatement(); + ResultSet resultSet = statement.executeQuery("SELECT DISTINCT node_version FROM system.runtime.nodes") + ) { assertTrue("No result", resultSet.next()); - assertEquals("Presto version", PrestoTestImages.PRESTO_PREVIOUS_VERSION_TEST_IMAGE.getVersionPart(), resultSet.getString("node_version")); + assertEquals( + "Presto version", + PrestoTestImages.PRESTO_PREVIOUS_VERSION_TEST_IMAGE.getVersionPart(), + resultSet.getString("node_version") + ); } } } @@ -55,18 +62,26 @@ public void testSpecificVersion() throws Exception { public void testQueryMemoryAndTpch() throws SQLException { try (PrestoContainer prestoSql = new PrestoContainer<>(PrestoTestImages.PRESTO_TEST_IMAGE)) { prestoSql.start(); - try (Connection connection = prestoSql.createConnection(); - Statement statement = connection.createStatement()) { + try ( + Connection connection = prestoSql.createConnection(); + Statement statement = connection.createStatement() + ) { // Prepare data - statement.execute("CREATE TABLE memory.default.table_with_array AS SELECT 1 id, ARRAY[1, 42, 2, 42, 4, 42] my_array"); + statement.execute( + "CREATE TABLE memory.default.table_with_array AS SELECT 1 id, ARRAY[1, 42, 2, 42, 4, 42] my_array" + ); // Query Presto using newly created table and a builtin connector - try (ResultSet resultSet = statement.executeQuery("" + - "SELECT nationkey, element " + - "FROM tpch.tiny.nation " + - "JOIN memory.default.table_with_array twa ON nationkey = twa.id " + - "LEFT JOIN UNNEST(my_array) a(element) ON true " + - "ORDER BY element OFFSET 1 FETCH NEXT 3 ROWS WITH TIES ")) { + try ( + ResultSet resultSet = statement.executeQuery( + "" + + "SELECT nationkey, element " + + "FROM tpch.tiny.nation " + + "JOIN memory.default.table_with_array twa ON nationkey = twa.id " + + "LEFT JOIN UNNEST(my_array) a(element) ON true " + + "ORDER BY element OFFSET 1 FETCH NEXT 3 ROWS WITH TIES " + ) + ) { List actualElements = new ArrayList<>(); while (resultSet.next()) { actualElements.add(resultSet.getInt("element")); @@ -82,9 +97,11 @@ public void testInitScript() throws Exception { try (PrestoContainer prestoSql = new PrestoContainer<>(PrestoTestImages.PRESTO_TEST_IMAGE)) { prestoSql.withInitScript("initial.sql"); prestoSql.start(); - try (Connection connection = prestoSql.createConnection(); - Statement statement = connection.createStatement(); - ResultSet resultSet = statement.executeQuery("SELECT a FROM memory.default.test_table")) { + try ( + Connection connection = prestoSql.createConnection(); + Statement statement = connection.createStatement(); + ResultSet resultSet = statement.executeQuery("SELECT a FROM memory.default.test_table") + ) { assertTrue("No result", resultSet.next()); assertEquals("Value", 12345678909324L, resultSet.getObject("a")); assertFalse("Too many result", resultSet.next()); @@ -94,19 +111,38 @@ public void testInitScript() throws Exception { @Test public void testTcJdbcUri() throws Exception { - try (Connection connection = DriverManager.getConnection(format("jdbc:tc:presto:%s://hostname/", PrestoContainer.DEFAULT_TAG))) { + try ( + Connection connection = DriverManager.getConnection( + String.format("jdbc:tc:presto:%s://hostname/", PrestoContainer.DEFAULT_TAG) + ) + ) { // Verify metadata with tc: JDBC connection URI - assertEquals(connection.getMetaData().getDatabaseMajorVersion(), parseInt(PrestoContainer.DEFAULT_TAG)); + assertEquals( + connection.getMetaData().getDatabaseMajorVersion(), + Integer.parseInt(PrestoContainer.DEFAULT_TAG) + ); // Verify transactions with tc: JDBC connection URI assertTrue("Is autocommit", connection.getAutoCommit()); connection.setAutoCommit(false); assertFalse("Is autocommit", connection.getAutoCommit()); - assertEquals("Transaction isolation", Connection.TRANSACTION_READ_UNCOMMITTED, connection.getTransactionIsolation()); + assertEquals( + "Transaction isolation", + Connection.TRANSACTION_READ_UNCOMMITTED, + connection.getTransactionIsolation() + ); try (Statement statement = connection.createStatement()) { - assertEquals("Update result", 0, statement.executeUpdate("CREATE TABLE memory.default.test_tc(a bigint)")); - try (ResultSet resultSet = statement.executeQuery("SELECT sum(cast(node_version AS bigint)) AS v FROM system.runtime.nodes")) { + assertEquals( + "Update result", + 0, + statement.executeUpdate("CREATE TABLE memory.default.test_tc(a bigint)") + ); + try ( + ResultSet resultSet = statement.executeQuery( + "SELECT sum(cast(node_version AS bigint)) AS v FROM system.runtime.nodes" + ) + ) { assertTrue(resultSet.next()); assertEquals(PrestoContainer.DEFAULT_TAG, resultSet.getString("v")); assertFalse(resultSet.next()); @@ -117,7 +153,11 @@ public void testTcJdbcUri() throws Exception { } connection.setAutoCommit(true); assertTrue("Is autocommit", connection.getAutoCommit()); - assertEquals("Transaction isolation should be retained", Connection.TRANSACTION_READ_UNCOMMITTED, connection.getTransactionIsolation()); + assertEquals( + "Transaction isolation should be retained", + Connection.TRANSACTION_READ_UNCOMMITTED, + connection.getTransactionIsolation() + ); } } } diff --git a/modules/presto/src/test/java/org/testcontainers/jdbc/presto/PrestoJDBCDriverTest.java b/modules/presto/src/test/java/org/testcontainers/jdbc/presto/PrestoJDBCDriverTest.java index d1a64c92aaa..9c5dd712104 100644 --- a/modules/presto/src/test/java/org/testcontainers/jdbc/presto/PrestoJDBCDriverTest.java +++ b/modules/presto/src/test/java/org/testcontainers/jdbc/presto/PrestoJDBCDriverTest.java @@ -4,18 +4,18 @@ import org.junit.runners.Parameterized; import org.testcontainers.jdbc.AbstractJDBCDriverTest; +import java.util.Arrays; import java.util.EnumSet; -import static java.util.Arrays.asList; - @RunWith(Parameterized.class) public class PrestoJDBCDriverTest extends AbstractJDBCDriverTest { @Parameterized.Parameters(name = "{index} - {0}") public static Iterable data() { - return asList( - new Object[][]{ - {"jdbc:tc:presto:344://hostname/", EnumSet.of(Options.PmdKnownBroken)}, - }); + return Arrays.asList( + new Object[][] { // + { "jdbc:tc:presto:344://hostname/", EnumSet.of(Options.PmdKnownBroken) }, + } + ); } } diff --git a/modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java b/modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java index b3c836b56a5..9b14e90d736 100644 --- a/modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java +++ b/modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java @@ -10,10 +10,13 @@ public class PulsarContainer extends GenericContainer { public static final int BROKER_PORT = 6650; + public static final int BROKER_HTTP_PORT = 8080; + public static final String METRICS_ENDPOINT = "/metrics"; private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("apachepulsar/pulsar"); + @Deprecated private static final String DEFAULT_TAG = "2.2.0"; @@ -37,7 +40,6 @@ public PulsarContainer(String pulsarVersion) { public PulsarContainer(final DockerImageName dockerImageName) { super(dockerImageName); - dockerImageName.assertCompatibleWith(DockerImageName.parse("apachepulsar/pulsar")); withExposedPorts(BROKER_PORT, BROKER_HTTP_PORT); diff --git a/modules/pulsar/src/test/java/org/testcontainers/containers/PulsarContainerTest.java b/modules/pulsar/src/test/java/org/testcontainers/containers/PulsarContainerTest.java index f2354d98ef7..6d739dfbf98 100644 --- a/modules/pulsar/src/test/java/org/testcontainers/containers/PulsarContainerTest.java +++ b/modules/pulsar/src/test/java/org/testcontainers/containers/PulsarContainerTest.java @@ -18,6 +18,7 @@ public class PulsarContainerTest { public static final String TEST_TOPIC = "test_topic"; + private static final DockerImageName PULSAR_IMAGE = DockerImageName.parse("apachepulsar/pulsar:2.2.0"); @Test @@ -33,9 +34,7 @@ public void shouldNotEnableFunctionsWorkerByDefault() throws Exception { try (PulsarContainer pulsar = new PulsarContainer("2.5.1")) { pulsar.start(); - PulsarAdmin pulsarAdmin = PulsarAdmin.builder() - .serviceHttpUrl(pulsar.getHttpServiceUrl()) - .build(); + PulsarAdmin pulsarAdmin = PulsarAdmin.builder().serviceHttpUrl(pulsar.getHttpServiceUrl()).build(); assertThatThrownBy(() -> pulsarAdmin.functions().getFunctions("public", "default")) .isInstanceOf(PulsarAdminException.class); @@ -47,36 +46,23 @@ public void shouldWaitForFunctionsWorkerStarted() throws Exception { try (PulsarContainer pulsar = new PulsarContainer("2.5.1").withFunctionsWorker()) { pulsar.start(); - PulsarAdmin pulsarAdmin = PulsarAdmin.builder() - .serviceHttpUrl(pulsar.getHttpServiceUrl()) - .build(); + PulsarAdmin pulsarAdmin = PulsarAdmin.builder().serviceHttpUrl(pulsar.getHttpServiceUrl()).build(); assertThat(pulsarAdmin.functions().getFunctions("public", "default")).hasSize(0); } } protected void testPulsarFunctionality(String pulsarBrokerUrl) throws Exception { - try ( - PulsarClient client = PulsarClient.builder() - .serviceUrl(pulsarBrokerUrl) - .build(); - Consumer consumer = client.newConsumer() - .topic(TEST_TOPIC) - .subscriptionName("test-subs") - .subscribe(); - Producer producer = client.newProducer() - .topic(TEST_TOPIC) - .create() + PulsarClient client = PulsarClient.builder().serviceUrl(pulsarBrokerUrl).build(); + Consumer consumer = client.newConsumer().topic(TEST_TOPIC).subscriptionName("test-subs").subscribe(); + Producer producer = client.newProducer().topic(TEST_TOPIC).create() ) { - producer.send("test containers".getBytes()); CompletableFuture future = consumer.receiveAsync(); Message message = future.get(5, TimeUnit.SECONDS); - assertThat(new String(message.getData())) - .isEqualTo("test containers"); + assertThat(new String(message.getData())).isEqualTo("test containers"); } } - } diff --git a/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/CancellableSubscription.java b/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/CancellableSubscription.java index e64d19a9f49..3f4fd7e4b1e 100644 --- a/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/CancellableSubscription.java +++ b/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/CancellableSubscription.java @@ -9,8 +9,7 @@ class CancellableSubscription implements Subscription { private final AtomicBoolean cancelled = new AtomicBoolean(); @Override - public void request(long n) { - } + public void request(long n) {} @Override public void cancel() { diff --git a/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/ConnectionPublisher.java b/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/ConnectionPublisher.java index 489d545429d..d1b1c0e43f3 100644 --- a/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/ConnectionPublisher.java +++ b/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/ConnectionPublisher.java @@ -56,8 +56,8 @@ synchronized void transitionTo(SubscriptionState newState) { } abstract class SubscriptionState implements Subscription { - void enter() { - } + + void enter() {} } class WaitRequestSubscriptionState extends SubscriptionState { @@ -68,8 +68,7 @@ public void request(long n) { } @Override - public void cancel() { - } + public void cancel() {} } class WaitFutureCompletionSubscriptionState extends SubscriptionState { @@ -93,8 +92,7 @@ void enter() { } @Override - public void request(long n) { - } + public void request(long n) {} @Override public void cancel() { diff --git a/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/EmptySubscription.java b/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/EmptySubscription.java index 7828d121c6f..5cd2ffbd894 100644 --- a/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/EmptySubscription.java +++ b/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/EmptySubscription.java @@ -6,12 +6,8 @@ enum EmptySubscription implements Subscription { INSTANCE; @Override - public void request(long n) { - - } + public void request(long n) {} @Override - public void cancel() { - - } + public void cancel() {} } diff --git a/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/Hidden.java b/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/Hidden.java index f642a60d67c..1eecb22a2c7 100644 --- a/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/Hidden.java +++ b/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/Hidden.java @@ -52,7 +52,8 @@ private ConnectionFactoryOptions removeProxying(ConnectionFactoryOptions options // when protocol does NOT contain COLON, the length becomes 1 String protocolDelegate = protocols.length == 2 ? protocols[1] : ""; - return ConnectionFactoryOptions.builder() + return ConnectionFactoryOptions + .builder() .from(options) .option(ConnectionFactoryOptions.DRIVER, driverDelegate) .option(ConnectionFactoryOptions.PROTOCOL, protocolDelegate) @@ -68,6 +69,5 @@ public boolean supports(ConnectionFactoryOptions options) { public String getDriver() { return DRIVER; } - } } diff --git a/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/R2DBCDatabaseContainer.java b/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/R2DBCDatabaseContainer.java index a303cbafff8..8bf0401fe69 100644 --- a/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/R2DBCDatabaseContainer.java +++ b/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/R2DBCDatabaseContainer.java @@ -4,6 +4,5 @@ import org.testcontainers.lifecycle.Startable; public interface R2DBCDatabaseContainer extends Startable { - ConnectionFactoryOptions configure(ConnectionFactoryOptions options); } diff --git a/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/R2DBCDatabaseContainerProvider.java b/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/R2DBCDatabaseContainerProvider.java index 287407dc15e..9d3efc17ac7 100644 --- a/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/R2DBCDatabaseContainerProvider.java +++ b/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/R2DBCDatabaseContainerProvider.java @@ -9,7 +9,6 @@ import javax.annotation.Nullable; public interface R2DBCDatabaseContainerProvider { - Option REUSABLE_OPTION = Option.valueOf("TC_REUSABLE"); Option IMAGE_TAG_OPTION = Option.valueOf("TC_IMAGE_TAG"); diff --git a/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/TestcontainersR2DBCConnectionFactory.java b/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/TestcontainersR2DBCConnectionFactory.java index 691775a94f8..bada076eec9 100644 --- a/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/TestcontainersR2DBCConnectionFactory.java +++ b/modules/r2dbc/src/main/java/org/testcontainers/r2dbc/TestcontainersR2DBCConnectionFactory.java @@ -36,34 +36,36 @@ class TestcontainersR2DBCConnectionFactory implements ConnectionFactory, Closeab TestcontainersR2DBCConnectionFactory(ConnectionFactoryOptions options) { this.options = options; - containerProvider = StreamSupport.stream(ServiceLoader.load(R2DBCDatabaseContainerProvider.class).spliterator(), false) - .filter(it -> it.supports(options)) - .findAny() - .orElseThrow(() -> new IllegalArgumentException("Missing provider for " + options)); + containerProvider = + StreamSupport + .stream(ServiceLoader.load(R2DBCDatabaseContainerProvider.class).spliterator(), false) + .filter(it -> it.supports(options)) + .findAny() + .orElseThrow(() -> new IllegalArgumentException("Missing provider for " + options)); } @Override public Publisher create() { - return new ConnectionPublisher( - () -> { - if (future == null) { - synchronized (this) { - if (future == null) { - future = CompletableFuture.supplyAsync(() -> { - R2DBCDatabaseContainer container = containerProvider.createContainer(options); - container.start(); - return container; - }, EXECUTOR); - } + return new ConnectionPublisher(() -> { + if (future == null) { + synchronized (this) { + if (future == null) { + future = + CompletableFuture.supplyAsync( + () -> { + R2DBCDatabaseContainer container = containerProvider.createContainer(options); + container.start(); + return container; + }, + EXECUTOR + ); } } - return future.thenApply(it -> { - return ConnectionFactories.find( - it.configure(options) - ); - }); } - ); + return future.thenApply(it -> { + return ConnectionFactories.find(it.configure(options)); + }); + }); } @Override @@ -99,5 +101,4 @@ public Publisher close() { } }; } - } diff --git a/modules/r2dbc/src/test/java/org/testcontainers/r2dbc/TestcontainersR2DBCConnectionFactoryTest.java b/modules/r2dbc/src/test/java/org/testcontainers/r2dbc/TestcontainersR2DBCConnectionFactoryTest.java index 6fad93f0e2a..3772e6d49a6 100644 --- a/modules/r2dbc/src/test/java/org/testcontainers/r2dbc/TestcontainersR2DBCConnectionFactoryTest.java +++ b/modules/r2dbc/src/test/java/org/testcontainers/r2dbc/TestcontainersR2DBCConnectionFactoryTest.java @@ -48,16 +48,15 @@ public void reusesUntilConnectionFactoryIsClosed() { assertThat(updated).isEqualTo(1); - Flux select = Flux - .usingWhen( - Flux.defer(connectionFactory::create), - connection -> { - return Flux - .from(connection.createStatement("SELECT COUNT(*) FROM test").execute()) - .flatMap(it -> it.map((row, meta) -> (Long) row.get(0))); - }, - Connection::close - ); + Flux select = Flux.usingWhen( + Flux.defer(connectionFactory::create), + connection -> { + return Flux + .from(connection.createStatement("SELECT COUNT(*) FROM test").execute()) + .flatMap(it -> it.map((row, meta) -> (Long) row.get(0))); + }, + Connection::close + ); Long rows = select.blockFirst(); diff --git a/modules/r2dbc/src/testFixtures/java/org/testcontainers/r2dbc/AbstractR2DBCDatabaseContainerTest.java b/modules/r2dbc/src/testFixtures/java/org/testcontainers/r2dbc/AbstractR2DBCDatabaseContainerTest.java index c21c7078410..63603379e40 100644 --- a/modules/r2dbc/src/testFixtures/java/org/testcontainers/r2dbc/AbstractR2DBCDatabaseContainerTest.java +++ b/modules/r2dbc/src/testFixtures/java/org/testcontainers/r2dbc/AbstractR2DBCDatabaseContainerTest.java @@ -60,9 +60,7 @@ protected void runTestQuery(ConnectionFactory connectionFactory) { .flatMap(it -> it.map((row, meta) -> (Number) row.get(0))) .blockFirst(); - assertThat(result) - .isNotNull() - .returns(expected, Number::intValue); + assertThat(result).isNotNull().returns(expected, Number::intValue); } finally { if (connectionFactory instanceof Closeable) { Mono.from(((Closeable) connectionFactory).close()).block(); diff --git a/modules/rabbitmq/src/main/java/org/testcontainers/containers/RabbitMQContainer.java b/modules/rabbitmq/src/main/java/org/testcontainers/containers/RabbitMQContainer.java index f5c63a267a9..c13dfdf1a24 100644 --- a/modules/rabbitmq/src/main/java/org/testcontainers/containers/RabbitMQContainer.java +++ b/modules/rabbitmq/src/main/java/org/testcontainers/containers/RabbitMQContainer.java @@ -11,13 +11,11 @@ import java.io.IOException; import java.time.Duration; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Set; -import static java.lang.String.join; -import static java.util.Arrays.asList; - /** * Testcontainer for RabbitMQ. * @@ -29,15 +27,21 @@ public class RabbitMQContainer extends GenericContainer { * The image defaults to the official RabbitmQ image: RabbitMQ. */ private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("rabbitmq"); + private static final String DEFAULT_TAG = "3.7.25-management-alpine"; private static final int DEFAULT_AMQP_PORT = 5672; + private static final int DEFAULT_AMQPS_PORT = 5671; + private static final int DEFAULT_HTTPS_PORT = 15671; + private static final int DEFAULT_HTTP_PORT = 15672; private String adminPassword = "guest"; + private String adminUsername = "guest"; + private final List> values = new ArrayList<>(); /** @@ -60,14 +64,12 @@ public RabbitMQContainer(String dockerImageName) { public RabbitMQContainer(final DockerImageName dockerImageName) { super(dockerImageName); - dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); addExposedPorts(DEFAULT_AMQP_PORT, DEFAULT_AMQPS_PORT, DEFAULT_HTTP_PORT, DEFAULT_HTTPS_PORT); - this.waitStrategy = Wait. - forLogMessage(".*Server startup complete.*", 1) - .withStartupTimeout(Duration.ofSeconds(60)); + this.waitStrategy = + Wait.forLogMessage(".*Server startup complete.*", 1).withStartupTimeout(Duration.ofSeconds(60)); } @Override @@ -79,7 +81,6 @@ protected void configure() { @Override protected void containerIsStarted(InspectContainerResponse containerInfo) { - values.forEach(command -> { try { ExecResult execResult = execInContainer(command.toArray(new String[0])); @@ -159,257 +160,394 @@ public RabbitMQContainer withAdminPassword(final String adminPassword) { } public enum SslVerification { - VERIFY_NONE("verify_none"), VERIFY_PEER("verify_peer"); + VERIFY_NONE("verify_none"), + VERIFY_PEER("verify_peer"); SslVerification(String value) { this.value = value; } - private final String value; + private final String value; } public RabbitMQContainer withSSL( - final MountableFile keyFile, - final MountableFile certFile, - final MountableFile caFile, - final SslVerification verify, - boolean failIfNoCert, - int verificationDepth) { - + final MountableFile keyFile, + final MountableFile certFile, + final MountableFile caFile, + final SslVerification verify, + boolean failIfNoCert, + int verificationDepth + ) { return withSSL(keyFile, certFile, caFile, verify, failIfNoCert) - .withEnv("RABBITMQ_SSL_DEPTH", String.valueOf(verificationDepth)); + .withEnv("RABBITMQ_SSL_DEPTH", String.valueOf(verificationDepth)); } public RabbitMQContainer withSSL( - final MountableFile keyFile, - final MountableFile certFile, - final MountableFile caFile, - final SslVerification verify, - boolean failIfNoCert) { - + final MountableFile keyFile, + final MountableFile certFile, + final MountableFile caFile, + final SslVerification verify, + boolean failIfNoCert + ) { return withSSL(keyFile, certFile, caFile, verify) - .withEnv("RABBITMQ_SSL_FAIL_IF_NO_PEER_CERT", String.valueOf(failIfNoCert)); + .withEnv("RABBITMQ_SSL_FAIL_IF_NO_PEER_CERT", String.valueOf(failIfNoCert)); } public RabbitMQContainer withSSL( - final MountableFile keyFile, - final MountableFile certFile, - final MountableFile caFile, - final SslVerification verify) { - + final MountableFile keyFile, + final MountableFile certFile, + final MountableFile caFile, + final SslVerification verify + ) { return withEnv("RABBITMQ_SSL_CACERTFILE", "/etc/rabbitmq/ca_cert.pem") - .withEnv("RABBITMQ_SSL_CERTFILE", "/etc/rabbitmq/rabbitmq_cert.pem") - .withEnv("RABBITMQ_SSL_KEYFILE", "/etc/rabbitmq/rabbitmq_key.pem") - .withEnv("RABBITMQ_SSL_VERIFY", verify.value) - .withCopyFileToContainer(certFile, "/etc/rabbitmq/rabbitmq_cert.pem") - .withCopyFileToContainer(caFile, "/etc/rabbitmq/ca_cert.pem") - .withCopyFileToContainer(keyFile, "/etc/rabbitmq/rabbitmq_key.pem"); + .withEnv("RABBITMQ_SSL_CERTFILE", "/etc/rabbitmq/rabbitmq_cert.pem") + .withEnv("RABBITMQ_SSL_KEYFILE", "/etc/rabbitmq/rabbitmq_key.pem") + .withEnv("RABBITMQ_SSL_VERIFY", verify.value) + .withCopyFileToContainer(certFile, "/etc/rabbitmq/rabbitmq_cert.pem") + .withCopyFileToContainer(caFile, "/etc/rabbitmq/ca_cert.pem") + .withCopyFileToContainer(keyFile, "/etc/rabbitmq/rabbitmq_key.pem"); } public RabbitMQContainer withPluginsEnabled(String... pluginNames) { - List command = new ArrayList<>(asList("rabbitmq-plugins", "enable")); - command.addAll(asList(pluginNames)); + List command = new ArrayList<>(Arrays.asList("rabbitmq-plugins", "enable")); + command.addAll(Arrays.asList(pluginNames)); values.add(command); return self(); } public RabbitMQContainer withBinding(String source, String destination) { - values.add(asList("rabbitmqadmin", "declare", "binding", - "source=" + source, - "destination=" + destination)); + values.add( + Arrays.asList("rabbitmqadmin", "declare", "binding", "source=" + source, "destination=" + destination) + ); return self(); } public RabbitMQContainer withBinding(String vhost, String source, String destination) { - values.add(asList("rabbitmqadmin", "--vhost=" + vhost, "declare", "binding", - "source=" + source, - "destination=" + destination)); + values.add( + Arrays.asList( + "rabbitmqadmin", + "--vhost=" + vhost, + "declare", + "binding", + "source=" + source, + "destination=" + destination + ) + ); return self(); } - public RabbitMQContainer withBinding(String source, String destination, Map arguments, String routingKey, String destinationType) { - values.add(asList("rabbitmqadmin", "declare", "binding", - "source=" + source, - "destination=" + destination, - "routing_key=" + routingKey, - "destination_type=" + destinationType, - "arguments=" + toJson(arguments))); + public RabbitMQContainer withBinding( + String source, + String destination, + Map arguments, + String routingKey, + String destinationType + ) { + values.add( + Arrays.asList( + "rabbitmqadmin", + "declare", + "binding", + "source=" + source, + "destination=" + destination, + "routing_key=" + routingKey, + "destination_type=" + destinationType, + "arguments=" + toJson(arguments) + ) + ); return self(); } - public RabbitMQContainer withBinding(String vhost, String source, String destination, Map arguments, String routingKey, String destinationType) { - values.add(asList("rabbitmqadmin", "--vhost=" + vhost, "declare", "binding", + public RabbitMQContainer withBinding( + String vhost, + String source, + String destination, + Map arguments, + String routingKey, + String destinationType + ) { + values.add( + Arrays.asList( + "rabbitmqadmin", + "--vhost=" + vhost, + "declare", + "binding", "source=" + source, "destination=" + destination, "routing_key=" + routingKey, "destination_type=" + destinationType, - "arguments=" + toJson(arguments))); + "arguments=" + toJson(arguments) + ) + ); return self(); } public RabbitMQContainer withParameter(String component, String name, String value) { - values.add(asList("rabbitmqadmin", "declare", "parameter", + values.add( + Arrays.asList( + "rabbitmqadmin", + "declare", + "parameter", "component=" + component, "name=" + name, - "value=" + value)); + "value=" + value + ) + ); return self(); } public RabbitMQContainer withPermission(String vhost, String user, String configure, String write, String read) { - values.add(asList("rabbitmqadmin", "declare", "permission", + values.add( + Arrays.asList( + "rabbitmqadmin", + "declare", + "permission", "vhost=" + vhost, "user=" + user, "configure=" + configure, "write=" + write, - "read=" + read)); + "read=" + read + ) + ); return self(); } public RabbitMQContainer withUser(String name, String password) { - values.add(asList("rabbitmqadmin", "declare", "user", - "name=" + name, - "password=" + password, - "tags=")); + values.add(Arrays.asList("rabbitmqadmin", "declare", "user", "name=" + name, "password=" + password, "tags=")); return self(); } public RabbitMQContainer withUser(String name, String password, Set tags) { - values.add(asList("rabbitmqadmin", "declare", "user", + values.add( + Arrays.asList( + "rabbitmqadmin", + "declare", + "user", "name=" + name, "password=" + password, - "tags=" + join(",", tags))); + "tags=" + String.join(",", tags) + ) + ); return self(); } public RabbitMQContainer withPolicy(String name, String pattern, Map definition) { - values.add(asList("rabbitmqadmin", "declare", "policy", + values.add( + Arrays.asList( + "rabbitmqadmin", + "declare", + "policy", "name=" + name, "pattern=" + pattern, - "definition=" + toJson(definition))); + "definition=" + toJson(definition) + ) + ); return self(); } public RabbitMQContainer withPolicy(String vhost, String name, String pattern, Map definition) { - values.add(asList("rabbitmqadmin", "declare", "policy", + values.add( + Arrays.asList( + "rabbitmqadmin", + "declare", + "policy", "--vhost=" + vhost, "name=" + name, "pattern=" + pattern, - "definition=" + toJson(definition))); + "definition=" + toJson(definition) + ) + ); return self(); } - public RabbitMQContainer withPolicy(String name, String pattern, Map definition, int priority, String applyTo) { - values.add(asList("rabbitmqadmin", "declare", "policy", + public RabbitMQContainer withPolicy( + String name, + String pattern, + Map definition, + int priority, + String applyTo + ) { + values.add( + Arrays.asList( + "rabbitmqadmin", + "declare", + "policy", "name=" + name, "pattern=" + pattern, "priority=" + priority, "apply-to=" + applyTo, - "definition=" + toJson(definition))); + "definition=" + toJson(definition) + ) + ); return self(); } public RabbitMQContainer withOperatorPolicy(String name, String pattern, Map definition) { - values.add(new ArrayList<>(asList("rabbitmqadmin", "declare", "operator_policy", - "name=" + name, - "pattern=" + pattern, - "definition=" + toJson(definition)))); + values.add( + new ArrayList<>( + Arrays.asList( + "rabbitmqadmin", + "declare", + "operator_policy", + "name=" + name, + "pattern=" + pattern, + "definition=" + toJson(definition) + ) + ) + ); return self(); } - public RabbitMQContainer withOperatorPolicy(String name, String pattern, Map definition, int priority, String applyTo) { - values.add(asList("rabbitmqadmin", "declare", "operator_policy", + public RabbitMQContainer withOperatorPolicy( + String name, + String pattern, + Map definition, + int priority, + String applyTo + ) { + values.add( + Arrays.asList( + "rabbitmqadmin", + "declare", + "operator_policy", "name=" + name, "pattern=" + pattern, "priority=" + priority, "apply-to=" + applyTo, - "definition=" + toJson(definition))); + "definition=" + toJson(definition) + ) + ); return self(); } public RabbitMQContainer withVhost(String name) { - values.add(asList("rabbitmqadmin", "declare", "vhost", - "name=" + name)); + values.add(Arrays.asList("rabbitmqadmin", "declare", "vhost", "name=" + name)); return self(); } public RabbitMQContainer withVhost(String name, boolean tracing) { - values.add(asList("rabbitmqadmin", "declare", "vhost", - "name=" + name, - "tracing=" + tracing)); + values.add(Arrays.asList("rabbitmqadmin", "declare", "vhost", "name=" + name, "tracing=" + tracing)); return self(); } public RabbitMQContainer withVhostLimit(String vhost, String name, int value) { - values.add(asList("rabbitmqadmin", "declare", "vhost_limit", - "vhost=" + vhost, - "name=" + name, - "value=" + value)); + values.add( + Arrays.asList("rabbitmqadmin", "declare", "vhost_limit", "vhost=" + vhost, "name=" + name, "value=" + value) + ); return self(); } public RabbitMQContainer withQueue(String name) { - values.add(asList("rabbitmqadmin", "declare", "queue", - "name=" + name)); + values.add(Arrays.asList("rabbitmqadmin", "declare", "queue", "name=" + name)); return self(); } public RabbitMQContainer withQueue(String vhost, String name) { - values.add(asList("rabbitmqadmin", "--vhost=" + vhost, "declare", "queue", - "name=" + name)); + values.add(Arrays.asList("rabbitmqadmin", "--vhost=" + vhost, "declare", "queue", "name=" + name)); return self(); } - public RabbitMQContainer withQueue(String name, boolean autoDelete, boolean durable, Map arguments) { - values.add(asList("rabbitmqadmin", "declare", "queue", + public RabbitMQContainer withQueue( + String name, + boolean autoDelete, + boolean durable, + Map arguments + ) { + values.add( + Arrays.asList( + "rabbitmqadmin", + "declare", + "queue", "name=" + name, "auto_delete=" + autoDelete, "durable=" + durable, - "arguments=" + toJson(arguments))); + "arguments=" + toJson(arguments) + ) + ); return self(); } - public RabbitMQContainer withQueue(String vhost, String name, boolean autoDelete, boolean durable, Map arguments) { - values.add(asList("rabbitmqadmin", "--vhost=" + vhost, "declare", "queue", - "name=" + name, - "auto_delete=" + autoDelete, - "durable=" + durable, - "arguments=" + toJson(arguments))); + public RabbitMQContainer withQueue( + String vhost, + String name, + boolean autoDelete, + boolean durable, + Map arguments + ) { + values.add( + Arrays.asList( + "rabbitmqadmin", + "--vhost=" + vhost, + "declare", + "queue", + "name=" + name, + "auto_delete=" + autoDelete, + "durable=" + durable, + "arguments=" + toJson(arguments) + ) + ); return self(); } public RabbitMQContainer withExchange(String name, String type) { - values.add(asList("rabbitmqadmin", "declare", "exchange", - "name=" + name, - "type=" + type)); + values.add(Arrays.asList("rabbitmqadmin", "declare", "exchange", "name=" + name, "type=" + type)); return self(); } public RabbitMQContainer withExchange(String vhost, String name, String type) { - values.add(asList("rabbitmqadmin", "--vhost=" + vhost, "declare", "exchange", - "name=" + name, - "type=" + type)); + values.add( + Arrays.asList("rabbitmqadmin", "--vhost=" + vhost, "declare", "exchange", "name=" + name, "type=" + type) + ); return self(); } - public RabbitMQContainer withExchange(String name, String type, boolean autoDelete, boolean internal, boolean durable, Map arguments) { - values.add(asList("rabbitmqadmin", "declare", "exchange", - "name=" + name, - "type=" + type, - "auto_delete=" + autoDelete, - "internal=" + internal, - "durable=" + durable, - "arguments=" + toJson(arguments))); + public RabbitMQContainer withExchange( + String name, + String type, + boolean autoDelete, + boolean internal, + boolean durable, + Map arguments + ) { + values.add( + Arrays.asList( + "rabbitmqadmin", + "declare", + "exchange", + "name=" + name, + "type=" + type, + "auto_delete=" + autoDelete, + "internal=" + internal, + "durable=" + durable, + "arguments=" + toJson(arguments) + ) + ); return self(); } - public RabbitMQContainer withExchange(String vhost, String name, String type, boolean autoDelete, boolean internal, boolean durable, Map arguments) { - values.add(asList("rabbitmqadmin", "--vhost=" + vhost, "declare", "exchange", + public RabbitMQContainer withExchange( + String vhost, + String name, + String type, + boolean autoDelete, + boolean internal, + boolean durable, + Map arguments + ) { + values.add( + Arrays.asList( + "rabbitmqadmin", + "--vhost=" + vhost, + "declare", + "exchange", "name=" + name, "type=" + type, "auto_delete=" + autoDelete, "internal=" + internal, "durable=" + durable, - "arguments=" + toJson(arguments))); + "arguments=" + toJson(arguments) + ) + ); return self(); } @@ -420,7 +558,6 @@ public RabbitMQContainer withExchange(String vhost, String name, String type, bo * @return This container. */ public RabbitMQContainer withRabbitMQConfig(MountableFile rabbitMQConf) { - return withRabbitMQConfigSysctl(rabbitMQConf); } diff --git a/modules/rabbitmq/src/test/java/org/testcontainers/containers/RabbitMQContainerTest.java b/modules/rabbitmq/src/test/java/org/testcontainers/containers/RabbitMQContainerTest.java index d82051a94db..6bc935b4e6c 100644 --- a/modules/rabbitmq/src/test/java/org/testcontainers/containers/RabbitMQContainerTest.java +++ b/modules/rabbitmq/src/test/java/org/testcontainers/containers/RabbitMQContainerTest.java @@ -6,11 +6,9 @@ import com.rabbitmq.client.Connection; import com.rabbitmq.client.ConnectionFactory; import org.junit.Test; +import org.testcontainers.containers.RabbitMQContainer.SslVerification; import org.testcontainers.utility.MountableFile; -import javax.net.ssl.KeyManagerFactory; -import javax.net.ssl.SSLContext; -import javax.net.ssl.TrustManagerFactory; import java.io.File; import java.io.FileInputStream; import java.io.IOException; @@ -22,10 +20,12 @@ import java.security.cert.CertificateException; import java.util.Collections; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManagerFactory; + import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatCode; -import static org.testcontainers.containers.RabbitMQContainer.SslVerification.VERIFY_PEER; -import static org.testcontainers.utility.MountableFile.forClasspathResource; /** * @author Martin Greber @@ -33,46 +33,55 @@ public class RabbitMQContainerTest { public static final int DEFAULT_AMQPS_PORT = 5671; + public static final int DEFAULT_AMQP_PORT = 5672; + public static final int DEFAULT_HTTPS_PORT = 15671; + public static final int DEFAULT_HTTP_PORT = 15672; @Test - public void shouldCreateRabbitMQContainer() - { + public void shouldCreateRabbitMQContainer() { try (RabbitMQContainer container = new RabbitMQContainer(RabbitMQTestImages.RABBITMQ_IMAGE)) { - assertThat(container.getAdminPassword()).isEqualTo("guest"); assertThat(container.getAdminUsername()).isEqualTo("guest"); container.start(); - assertThat(container.getAmqpsUrl()).isEqualTo( - String.format("amqps://%s:%d", container.getHost(), container.getMappedPort(DEFAULT_AMQPS_PORT))); - assertThat(container.getAmqpUrl()).isEqualTo( - String.format("amqp://%s:%d", container.getHost(), container.getMappedPort(DEFAULT_AMQP_PORT))); - assertThat(container.getHttpsUrl()).isEqualTo( - String.format("https://%s:%d", container.getHost(), container.getMappedPort(DEFAULT_HTTPS_PORT))); - assertThat(container.getHttpUrl()).isEqualTo( - String.format("http://%s:%d", container.getHost(), container.getMappedPort(DEFAULT_HTTP_PORT))); + assertThat(container.getAmqpsUrl()) + .isEqualTo( + String.format("amqps://%s:%d", container.getHost(), container.getMappedPort(DEFAULT_AMQPS_PORT)) + ); + assertThat(container.getAmqpUrl()) + .isEqualTo( + String.format("amqp://%s:%d", container.getHost(), container.getMappedPort(DEFAULT_AMQP_PORT)) + ); + assertThat(container.getHttpsUrl()) + .isEqualTo( + String.format("https://%s:%d", container.getHost(), container.getMappedPort(DEFAULT_HTTPS_PORT)) + ); + assertThat(container.getHttpUrl()) + .isEqualTo( + String.format("http://%s:%d", container.getHost(), container.getMappedPort(DEFAULT_HTTP_PORT)) + ); assertThat(container.getHttpsPort()).isEqualTo(container.getMappedPort(DEFAULT_HTTPS_PORT)); assertThat(container.getHttpPort()).isEqualTo(container.getMappedPort(DEFAULT_HTTP_PORT)); assertThat(container.getAmqpsPort()).isEqualTo(container.getMappedPort(DEFAULT_AMQPS_PORT)); assertThat(container.getAmqpPort()).isEqualTo(container.getMappedPort(DEFAULT_AMQP_PORT)); - assertThat(container.getLivenessCheckPortNumbers()).containsExactlyInAnyOrder( - container.getMappedPort(DEFAULT_AMQP_PORT), - container.getMappedPort(DEFAULT_AMQPS_PORT), - container.getMappedPort(DEFAULT_HTTP_PORT), - container.getMappedPort(DEFAULT_HTTPS_PORT) - ); + assertThat(container.getLivenessCheckPortNumbers()) + .containsExactlyInAnyOrder( + container.getMappedPort(DEFAULT_AMQP_PORT), + container.getMappedPort(DEFAULT_AMQPS_PORT), + container.getMappedPort(DEFAULT_HTTP_PORT), + container.getMappedPort(DEFAULT_HTTPS_PORT) + ); } } @Test - public void shouldCreateRabbitMQContainerWithExchange() throws IOException, InterruptedException - { + public void shouldCreateRabbitMQContainerWithExchange() throws IOException, InterruptedException { try (RabbitMQContainer container = new RabbitMQContainer(RabbitMQTestImages.RABBITMQ_IMAGE)) { container.withExchange("test-exchange", "direct"); @@ -84,11 +93,18 @@ public void shouldCreateRabbitMQContainerWithExchange() throws IOException, Inte } @Test - public void shouldCreateRabbitMQContainerWithExchangeInVhost() throws IOException, InterruptedException - { + public void shouldCreateRabbitMQContainerWithExchangeInVhost() throws IOException, InterruptedException { try (RabbitMQContainer container = new RabbitMQContainer(RabbitMQTestImages.RABBITMQ_IMAGE)) { container.withVhost("test-vhost"); - container.withExchange("test-vhost", "test-exchange", "direct", false, false, false, Collections.emptyMap()); + container.withExchange( + "test-vhost", + "test-exchange", + "direct", + false, + false, + false, + Collections.emptyMap() + ); container.start(); @@ -98,11 +114,10 @@ public void shouldCreateRabbitMQContainerWithExchangeInVhost() throws IOExceptio } @Test - public void shouldCreateRabbitMQContainerWithQueues() throws IOException, InterruptedException - { + public void shouldCreateRabbitMQContainerWithQueues() throws IOException, InterruptedException { try (RabbitMQContainer container = new RabbitMQContainer(RabbitMQTestImages.RABBITMQ_IMAGE)) { - - container.withQueue("queue-one") + container + .withQueue("queue-one") .withQueue("queue-two", false, true, ImmutableMap.of("x-message-ttl", 1000)); container.start(); @@ -124,7 +139,6 @@ public void shouldMountConfigurationFile() { } } - @Test public void shouldMountConfigurationFileErlang() { try (RabbitMQContainer container = new RabbitMQContainer(RabbitMQTestImages.RABBITMQ_IMAGE)) { @@ -135,7 +149,6 @@ public void shouldMountConfigurationFileErlang() { } } - @Test public void shouldMountConfigurationFileSysctl() { try (RabbitMQContainer container = new RabbitMQContainer(RabbitMQTestImages.RABBITMQ_IMAGE)) { @@ -147,132 +160,143 @@ public void shouldMountConfigurationFileSysctl() { } @Test - public void shouldStartTheWholeEnchilada() throws IOException, InterruptedException - { + public void shouldStartTheWholeEnchilada() throws IOException, InterruptedException { try (RabbitMQContainer container = new RabbitMQContainer(RabbitMQTestImages.RABBITMQ_IMAGE)) { container - .withVhost("vhost1") - .withVhostLimit("vhost1", "max-connections", 1) - .withVhost("vhost2", true) - .withExchange("direct-exchange", "direct") - .withExchange("topic-exchange", "topic") - .withExchange("vhost1", "topic-exchange-2", "topic", false, false, true, Collections.emptyMap()) - .withExchange("vhost2", "topic-exchange-3", "topic") - .withExchange("topic-exchange-4", "topic", false, false, true, Collections.emptyMap()) - .withQueue("queue1") - .withQueue("queue2", true, false, ImmutableMap.of("x-message-ttl", 1000)) - .withQueue("vhost1", "queue3", true, false, ImmutableMap.of("x-message-ttl", 1000)) - .withQueue("vhost2", "queue4") - .withBinding("direct-exchange", "queue1") - .withBinding("vhost1", "topic-exchange-2", "queue3") - .withBinding("vhost2", "topic-exchange-3", "queue4", Collections.emptyMap(), "ss7", "queue") - .withUser("user1", "password1") - .withUser("user2", "password2", ImmutableSet.of("administrator")) - .withPermission("vhost1", "user1", ".*", ".*", ".*") - .withPolicy("max length policy", "^dog", ImmutableMap.of("max-length", 1), 1, "queues") - .withPolicy("alternate exchange policy", "^direct-exchange", ImmutableMap.of("alternate-exchange", "amq.direct")) - .withPolicy("vhost2", "ha-all", ".*", ImmutableMap.of("ha-mode", "all", "ha-sync-mode", "automatic")) - .withOperatorPolicy("operator policy 1", "^queue1", ImmutableMap.of("message-ttl", 1000), 1, "queues") - .withPluginsEnabled("rabbitmq_shovel", "rabbitmq_random_exchange"); + .withVhost("vhost1") + .withVhostLimit("vhost1", "max-connections", 1) + .withVhost("vhost2", true) + .withExchange("direct-exchange", "direct") + .withExchange("topic-exchange", "topic") + .withExchange("vhost1", "topic-exchange-2", "topic", false, false, true, Collections.emptyMap()) + .withExchange("vhost2", "topic-exchange-3", "topic") + .withExchange("topic-exchange-4", "topic", false, false, true, Collections.emptyMap()) + .withQueue("queue1") + .withQueue("queue2", true, false, ImmutableMap.of("x-message-ttl", 1000)) + .withQueue("vhost1", "queue3", true, false, ImmutableMap.of("x-message-ttl", 1000)) + .withQueue("vhost2", "queue4") + .withBinding("direct-exchange", "queue1") + .withBinding("vhost1", "topic-exchange-2", "queue3") + .withBinding("vhost2", "topic-exchange-3", "queue4", Collections.emptyMap(), "ss7", "queue") + .withUser("user1", "password1") + .withUser("user2", "password2", ImmutableSet.of("administrator")) + .withPermission("vhost1", "user1", ".*", ".*", ".*") + .withPolicy("max length policy", "^dog", ImmutableMap.of("max-length", 1), 1, "queues") + .withPolicy( + "alternate exchange policy", + "^direct-exchange", + ImmutableMap.of("alternate-exchange", "amq.direct") + ) + .withPolicy("vhost2", "ha-all", ".*", ImmutableMap.of("ha-mode", "all", "ha-sync-mode", "automatic")) + .withOperatorPolicy("operator policy 1", "^queue1", ImmutableMap.of("message-ttl", 1000), 1, "queues") + .withPluginsEnabled("rabbitmq_shovel", "rabbitmq_random_exchange"); container.start(); - assertThat(container.execInContainer("rabbitmqadmin", "list", "queues") - .getStdout()) + assertThat(container.execInContainer("rabbitmqadmin", "list", "queues").getStdout()) .contains("queue1", "queue2", "queue3", "queue4"); - assertThat(container.execInContainer("rabbitmqadmin", "list", "exchanges") - .getStdout()) - .contains("direct-exchange", "topic-exchange", "topic-exchange-2", "topic-exchange-3", "topic-exchange-4"); + assertThat(container.execInContainer("rabbitmqadmin", "list", "exchanges").getStdout()) + .contains( + "direct-exchange", + "topic-exchange", + "topic-exchange-2", + "topic-exchange-3", + "topic-exchange-4" + ); - assertThat(container.execInContainer("rabbitmqadmin", "list", "bindings") - .getStdout()) + assertThat(container.execInContainer("rabbitmqadmin", "list", "bindings").getStdout()) .contains("direct-exchange", "topic-exchange-2", "topic-exchange-3"); - assertThat(container.execInContainer("rabbitmqadmin", "list", "users") - .getStdout()) + assertThat(container.execInContainer("rabbitmqadmin", "list", "users").getStdout()) .contains("user1", "user2"); - assertThat(container.execInContainer("rabbitmqadmin", "list", "policies") - .getStdout()) + assertThat(container.execInContainer("rabbitmqadmin", "list", "policies").getStdout()) .contains("max length policy", "alternate exchange policy"); - assertThat(container.execInContainer("rabbitmqadmin", "list", "policies", "--vhost=vhost2") - .getStdout()) - .contains("ha-all", "ha-sync-mode"); + assertThat(container.execInContainer("rabbitmqadmin", "list", "policies", "--vhost=vhost2").getStdout()) + .contains("ha-all", "ha-sync-mode"); - assertThat(container.execInContainer("rabbitmqadmin", "list", "operator_policies") - .getStdout()) + assertThat(container.execInContainer("rabbitmqadmin", "list", "operator_policies").getStdout()) .contains("operator policy 1"); - assertThat(container.execInContainer("rabbitmq-plugins", "is_enabled", "rabbitmq_shovel", "--quiet") - .getStdout()) + assertThat( + container.execInContainer("rabbitmq-plugins", "is_enabled", "rabbitmq_shovel", "--quiet").getStdout() + ) .contains("rabbitmq_shovel is enabled"); - assertThat(container.execInContainer("rabbitmq-plugins", "is_enabled", "rabbitmq_random_exchange", "--quiet") - .getStdout()) + assertThat( + container + .execInContainer("rabbitmq-plugins", "is_enabled", "rabbitmq_random_exchange", "--quiet") + .getStdout() + ) .contains("rabbitmq_random_exchange is enabled"); } } @Test - public void shouldThrowExceptionForDodgyJson() - { + public void shouldThrowExceptionForDodgyJson() { try (RabbitMQContainer container = new RabbitMQContainer(RabbitMQTestImages.RABBITMQ_IMAGE)) { - - assertThatCode(() -> - container.withQueue( - "queue2", - true, - false, - ImmutableMap.of("x-message-ttl", container)) - ).hasMessageStartingWith("Failed to convert arguments into json"); - + assertThatCode(() -> container.withQueue("queue2", true, false, ImmutableMap.of("x-message-ttl", container)) + ) + .hasMessageStartingWith("Failed to convert arguments into json"); } } @Test - public void shouldWorkWithSSL() - { + public void shouldWorkWithSSL() { try (RabbitMQContainer container = new RabbitMQContainer(RabbitMQTestImages.RABBITMQ_IMAGE)) { container.withSSL( - forClasspathResource("/certs/server_key.pem", 0644), - forClasspathResource("/certs/server_certificate.pem", 0644), - forClasspathResource("/certs/ca_certificate.pem", 0644), - VERIFY_PEER, + MountableFile.forClasspathResource("/certs/server_key.pem", 0644), + MountableFile.forClasspathResource("/certs/server_certificate.pem", 0644), + MountableFile.forClasspathResource("/certs/ca_certificate.pem", 0644), + SslVerification.VERIFY_PEER, true ); container.start(); assertThatCode(() -> { - ConnectionFactory connectionFactory = new ConnectionFactory(); - connectionFactory.useSslProtocol(createSslContext( - "certs/client_key.p12", "password", - "certs/truststore.jks", "password")); - connectionFactory.enableHostnameVerification(); - connectionFactory.setUri(container.getAmqpsUrl()); - connectionFactory.setPassword(container.getAdminPassword()); - Connection connection = connectionFactory.newConnection(); - Channel channel = connection.openChannel().orElseThrow(() -> new RuntimeException("Failed to Open channel")); - channel.close(); - connection.close(); - }).doesNotThrowAnyException(); + ConnectionFactory connectionFactory = new ConnectionFactory(); + connectionFactory.useSslProtocol( + createSslContext("certs/client_key.p12", "password", "certs/truststore.jks", "password") + ); + connectionFactory.enableHostnameVerification(); + connectionFactory.setUri(container.getAmqpsUrl()); + connectionFactory.setPassword(container.getAdminPassword()); + Connection connection = connectionFactory.newConnection(); + Channel channel = connection + .openChannel() + .orElseThrow(() -> new RuntimeException("Failed to Open channel")); + channel.close(); + connection.close(); + }) + .doesNotThrowAnyException(); } } - private SSLContext createSslContext(String keystoreFile, String keystorePassword, String truststoreFile, String truststorePassword) - throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, UnrecoverableKeyException, KeyManagementException - { + private SSLContext createSslContext( + String keystoreFile, + String keystorePassword, + String truststoreFile, + String truststorePassword + ) + throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, UnrecoverableKeyException, KeyManagementException { ClassLoader classLoader = getClass().getClassLoader(); KeyStore ks = KeyStore.getInstance("PKCS12"); - ks.load(new FileInputStream(new File(classLoader.getResource(keystoreFile).getFile())), keystorePassword.toCharArray()); + ks.load( + new FileInputStream(new File(classLoader.getResource(keystoreFile).getFile())), + keystorePassword.toCharArray() + ); KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); kmf.init(ks, "password".toCharArray()); KeyStore trustStore = KeyStore.getInstance("PKCS12"); - trustStore.load(new FileInputStream(new File(classLoader.getResource(truststoreFile).getFile())), truststorePassword.toCharArray()); + trustStore.load( + new FileInputStream(new File(classLoader.getResource(truststoreFile).getFile())), + truststorePassword.toCharArray() + ); TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509"); tmf.init(trustStore); diff --git a/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java b/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java index fc3778385cd..08d7ab053c6 100644 --- a/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java +++ b/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java @@ -1,22 +1,10 @@ package org.testcontainers.containers; -import static java.time.temporal.ChronoUnit.SECONDS; - import com.github.dockerjava.api.command.InspectContainerResponse; import com.github.dockerjava.api.model.AccessMode; import com.github.dockerjava.api.model.Bind; import com.github.dockerjava.api.model.Volume; import com.google.common.collect.ImmutableSet; -import java.io.File; -import java.io.IOException; -import java.net.MalformedURLException; -import java.net.URL; -import java.nio.file.Files; -import java.time.Duration; -import java.util.Optional; -import java.util.Set; -import java.util.concurrent.TimeUnit; - import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.SystemUtils; import org.jetbrains.annotations.NotNull; @@ -41,40 +29,67 @@ import org.testcontainers.utility.ComparableVersion; import org.testcontainers.utility.DockerImageName; +import java.io.File; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.nio.file.Files; +import java.time.Duration; +import java.time.temporal.ChronoUnit; +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.TimeUnit; + /** * A chrome/firefox/custom container based on SeleniumHQ's standalone container sets. *

* The container should expose Selenium remote control protocol and VNC. */ -public class BrowserWebDriverContainer> extends GenericContainer implements LinkableContainer, TestLifecycleAware { +public class BrowserWebDriverContainer> + extends GenericContainer + implements LinkableContainer, TestLifecycleAware { private static final DockerImageName CHROME_IMAGE = DockerImageName.parse("selenium/standalone-chrome"); + private static final DockerImageName FIREFOX_IMAGE = DockerImageName.parse("selenium/standalone-firefox"); + private static final DockerImageName CHROME_DEBUG_IMAGE = DockerImageName.parse("selenium/standalone-chrome-debug"); - private static final DockerImageName FIREFOX_DEBUG_IMAGE = DockerImageName.parse("selenium/standalone-firefox-debug"); + + private static final DockerImageName FIREFOX_DEBUG_IMAGE = DockerImageName.parse( + "selenium/standalone-firefox-debug" + ); + private static final DockerImageName[] COMPATIBLE_IMAGES = new DockerImageName[] { CHROME_IMAGE, FIREFOX_IMAGE, CHROME_DEBUG_IMAGE, - FIREFOX_DEBUG_IMAGE + FIREFOX_DEBUG_IMAGE, }; private static final String DEFAULT_PASSWORD = "secret"; + private static final int SELENIUM_PORT = 4444; + private static final int VNC_PORT = 5900; private static final String NO_PROXY_KEY = "no_proxy"; + private static final String TC_TEMP_DIR_PREFIX = "tc"; @Nullable private Capabilities capabilities; + private DockerImageName customImageName = null; @Nullable private RemoteWebDriver driver; + private VncRecordingMode recordingMode = VncRecordingMode.RECORD_FAILING; + private VncRecordingFormat recordingFormat; + private RecordingFileFactory recordingFileFactory; + private File vncRecordingDirectory; private VncRecordingContainer vncRecordingContainer = null; @@ -84,13 +99,16 @@ public class BrowserWebDriverContainer getLivenessCheckPorts() { @Override protected void configure() { - String seleniumVersion = SeleniumUtils.determineClasspathSeleniumVersion(); if (recordingMode != VncRecordingMode.SKIP) { - if (vncRecordingDirectory == null) { try { vncRecordingDirectory = Files.createTempDirectory(TC_TEMP_DIR_PREFIX).toFile(); @@ -178,7 +196,8 @@ protected void configure() { withNetwork(Network.SHARED); } - vncRecordingContainer = new VncRecordingContainer(this) + vncRecordingContainer = + new VncRecordingContainer(this) .withVncPassword(DEFAULT_PASSWORD) .withVncPort(VNC_PORT) .withVideoFormat(recordingFormat); @@ -188,7 +207,10 @@ protected void configure() { customImageName.assertCompatibleWith(COMPATIBLE_IMAGES); super.setDockerImageName(customImageName.asCanonicalNameString()); } else { - DockerImageName standardImageForCapabilities = getStandardImageForCapabilities(capabilities, seleniumVersion); + DockerImageName standardImageForCapabilities = getStandardImageForCapabilities( + capabilities, + seleniumVersion + ); super.setDockerImageName(standardImageForCapabilities.asCanonicalNameString()); } @@ -246,7 +268,9 @@ private static DockerImageName getStandardImageForCapabilities(Capabilities capa case BrowserType.FIREFOX: return (supportsVncWithoutDebugImage ? FIREFOX_IMAGE : FIREFOX_DEBUG_IMAGE).withTag(seleniumVersion); default: - throw new UnsupportedOperationException("Browser name must be 'chrome' or 'firefox'; provided '" + browserName + "' is not supported"); + throw new UnsupportedOperationException( + "Browser name must be 'chrome' or 'firefox'; provided '" + browserName + "' is not supported" + ); } } @@ -254,7 +278,7 @@ public URL getSeleniumAddress() { try { return new URL("http", getHost(), getMappedPort(SELENIUM_PORT), "/wd/hub"); } catch (MalformedURLException e) { - e.printStackTrace();// TODO + e.printStackTrace(); // TODO return null; } } @@ -292,15 +316,27 @@ protected void containerIsStarted(InspectContainerResponse containerInfo) { public synchronized RemoteWebDriver getWebDriver() { if (driver == null) { if (capabilities == null) { - logger().warn("No capabilities provided - this will cause an exception in future versions. Falling back to ChromeOptions"); + logger() + .warn( + "No capabilities provided - this will cause an exception in future versions. Falling back to ChromeOptions" + ); capabilities = new ChromeOptions(); } - driver = Unreliables.retryUntilSuccess(30, TimeUnit.SECONDS, () -> { - return Timeouts.getWithTimeout(10, TimeUnit.SECONDS, () -> { - return new RemoteWebDriver(getSeleniumAddress(), capabilities); - }); - }); + driver = + Unreliables.retryUntilSuccess( + 30, + TimeUnit.SECONDS, + () -> { + return Timeouts.getWithTimeout( + 10, + TimeUnit.SECONDS, + () -> { + return new RemoteWebDriver(getSeleniumAddress(), capabilities); + } + ); + } + ); } return driver; } @@ -348,7 +384,12 @@ private void retainRecordingIfNeeded(String prefix, boolean succeeded) { } if (shouldRecord) { - File recordingFile = recordingFileFactory.recordingFileForTest(vncRecordingDirectory, prefix, succeeded, vncRecordingContainer.getVideoFormat()); + File recordingFile = recordingFileFactory.recordingFileForTest( + vncRecordingDirectory, + prefix, + succeeded, + vncRecordingContainer.getVideoFormat() + ); LOGGER.info("Screen recordings for test {} will be stored at: {}", prefix, recordingFile); vncRecordingContainer.saveRecordingToFile(recordingFile); @@ -375,7 +416,11 @@ public SELF withRecordingMode(VncRecordingMode recordingMode, File vncRecordingD return withRecordingMode(recordingMode, vncRecordingDirectory, null); } - public SELF withRecordingMode(VncRecordingMode recordingMode, File vncRecordingDirectory, VncRecordingFormat recordingFormat) { + public SELF withRecordingMode( + VncRecordingMode recordingMode, + File vncRecordingDirectory, + VncRecordingFormat recordingFormat + ) { this.recordingMode = recordingMode; this.vncRecordingDirectory = vncRecordingDirectory; this.recordingFormat = recordingFormat; @@ -388,6 +433,8 @@ public SELF withRecordingFileFactory(RecordingFileFactory recordingFileFactory) } public enum VncRecordingMode { - SKIP, RECORD_ALL, RECORD_FAILING + SKIP, + RECORD_ALL, + RECORD_FAILING, } } diff --git a/modules/selenium/src/main/java/org/testcontainers/containers/DefaultRecordingFileFactory.java b/modules/selenium/src/main/java/org/testcontainers/containers/DefaultRecordingFileFactory.java index 62a1abd38db..3815f5de3e1 100644 --- a/modules/selenium/src/main/java/org/testcontainers/containers/DefaultRecordingFileFactory.java +++ b/modules/selenium/src/main/java/org/testcontainers/containers/DefaultRecordingFileFactory.java @@ -4,25 +4,36 @@ import java.text.SimpleDateFormat; import java.util.Date; -import static org.testcontainers.containers.VncRecordingContainer.VncRecordingFormat; - public class DefaultRecordingFileFactory implements RecordingFileFactory { private static final SimpleDateFormat filenameDateFormat = new SimpleDateFormat("YYYYMMdd-HHmmss"); + private static final String PASSED = "PASSED"; + private static final String FAILED = "FAILED"; - private static final String FILENAME_FORMAT = "%s-%s-%s.%s"; + private static final String FILENAME_FORMAT = "%s-%s-%s.%s"; @Override public File recordingFileForTest(File vncRecordingDirectory, String prefix, boolean succeeded) { - return recordingFileForTest(vncRecordingDirectory, prefix, succeeded, VncRecordingContainer.DEFAULT_RECORDING_FORMAT); + return recordingFileForTest( + vncRecordingDirectory, + prefix, + succeeded, + VncRecordingContainer.DEFAULT_RECORDING_FORMAT + ); } @Override - public File recordingFileForTest(File vncRecordingDirectory, String prefix, boolean succeeded, VncRecordingFormat recordingFormat) { + public File recordingFileForTest( + File vncRecordingDirectory, + String prefix, + boolean succeeded, + VncRecordingContainer.VncRecordingFormat recordingFormat + ) { final String resultMarker = succeeded ? PASSED : FAILED; - final String fileName = String.format(FILENAME_FORMAT, + final String fileName = String.format( + FILENAME_FORMAT, resultMarker, prefix, filenameDateFormat.format(new Date()), diff --git a/modules/selenium/src/main/java/org/testcontainers/containers/RecordingFileFactory.java b/modules/selenium/src/main/java/org/testcontainers/containers/RecordingFileFactory.java index aff607cf23d..31946dac59e 100644 --- a/modules/selenium/src/main/java/org/testcontainers/containers/RecordingFileFactory.java +++ b/modules/selenium/src/main/java/org/testcontainers/containers/RecordingFileFactory.java @@ -6,16 +6,23 @@ import java.io.File; public interface RecordingFileFactory { - @Deprecated default File recordingFileForTest(File vncRecordingDirectory, Description description, boolean succeeded) { - return recordingFileForTest(vncRecordingDirectory, description.getTestClass().getSimpleName() + "-" + description.getMethodName(), succeeded); + return recordingFileForTest( + vncRecordingDirectory, + description.getTestClass().getSimpleName() + "-" + description.getMethodName(), + succeeded + ); } - default File recordingFileForTest(File vncRecordingDirectory, String prefix, boolean succeeded, VncRecordingFormat recordingFormat) { + default File recordingFileForTest( + File vncRecordingDirectory, + String prefix, + boolean succeeded, + VncRecordingFormat recordingFormat + ) { return recordingFileForTest(vncRecordingDirectory, prefix, succeeded); } File recordingFileForTest(File vncRecordingDirectory, String prefix, boolean succeeded); - } diff --git a/modules/selenium/src/main/java/org/testcontainers/containers/SeleniumUtils.java b/modules/selenium/src/main/java/org/testcontainers/containers/SeleniumUtils.java index 131f5a953ea..4fc0765cc86 100644 --- a/modules/selenium/src/main/java/org/testcontainers/containers/SeleniumUtils.java +++ b/modules/selenium/src/main/java/org/testcontainers/containers/SeleniumUtils.java @@ -45,19 +45,24 @@ public static String determineClasspathSeleniumVersion() { } } } - } catch (Exception e) { LOGGER.debug("Failed to determine Selenium-Version from selenium-api JAR Manifest", e); } if (seleniumVersions.size() == 0) { - LOGGER.warn("Failed to determine Selenium version from classpath - will use default version of {}", DEFAULT_SELENIUM_VERSION); + LOGGER.warn( + "Failed to determine Selenium version from classpath - will use default version of {}", + DEFAULT_SELENIUM_VERSION + ); return DEFAULT_SELENIUM_VERSION; } String foundVersion = seleniumVersions.iterator().next(); if (seleniumVersions.size() > 1) { - LOGGER.warn("Multiple versions of Selenium API found on classpath - will select {}, but this may not be reliable", foundVersion); + LOGGER.warn( + "Multiple versions of Selenium API found on classpath - will select {}, but this may not be reliable", + foundVersion + ); } return foundVersion; @@ -76,7 +81,7 @@ public static String getSeleniumVersionFromManifest(Manifest manifest) { } // Compatibility Selenium > 3.X - if(seleniumVersion == null) { + if (seleniumVersion == null) { Attributes seleniumInfo = manifest.getAttributes("Selenium"); if (seleniumInfo != null) { seleniumVersion = seleniumInfo.getValue("Selenium-Version"); diff --git a/modules/selenium/src/test/java/org/testcontainers/containers/DefaultRecordingFileFactoryTest.java b/modules/selenium/src/test/java/org/testcontainers/containers/DefaultRecordingFileFactoryTest.java index 9abea56c5f2..0d8daaad219 100644 --- a/modules/selenium/src/test/java/org/testcontainers/containers/DefaultRecordingFileFactoryTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/containers/DefaultRecordingFileFactoryTest.java @@ -8,19 +8,15 @@ import java.io.File; import java.nio.file.Files; +import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; -import static java.lang.Boolean.FALSE; -import static java.lang.Boolean.TRUE; -import static java.lang.String.format; -import static java.time.LocalDateTime.now; -import static org.hamcrest.core.IsCollectionContaining.hasItem; +import static org.hamcrest.Matchers.hasItem; import static org.junit.Assert.assertThat; -import static org.junit.runner.Description.createTestDescription; @RunWith(Parameterized.class) @Value @@ -29,30 +25,47 @@ public class DefaultRecordingFileFactoryTest { private static final DateTimeFormatter DATETIME_FORMATTER = DateTimeFormatter.ofPattern("YYYYMMdd-HHmmss"); private final DefaultRecordingFileFactory factory = new DefaultRecordingFileFactory(); + private final String methodName; + private final String prefix; + private final boolean success; @Parameterized.Parameters public static Collection data() { Collection args = new ArrayList<>(); - args.add(new Object[]{"testMethod1", "FAILED", FALSE}); - args.add(new Object[]{"testMethod2", "PASSED", TRUE}); + args.add(new Object[] { "testMethod1", "FAILED", Boolean.FALSE }); + args.add(new Object[] { "testMethod2", "PASSED", Boolean.TRUE }); return args; } @Test public void recordingFileThatShouldDescribeTheTestResultAtThePresentTime() throws Exception { File vncRecordingDirectory = Files.createTempDirectory("recording").toFile(); - Description description = createTestDescription(getClass().getCanonicalName(), methodName, Test.class); + Description description = Description.createTestDescription( + getClass().getCanonicalName(), + methodName, + Test.class + ); File recordingFile = factory.recordingFileForTest(vncRecordingDirectory, description, success); - String expectedFilePrefix = format("%s-%s-%s", prefix, getClass().getSimpleName(), methodName); + String expectedFilePrefix = String.format("%s-%s-%s", prefix, getClass().getSimpleName(), methodName); List expectedPossibleFileNames = Arrays.asList( - new File(vncRecordingDirectory, format("%s-%s.flv", expectedFilePrefix, now().format(DATETIME_FORMATTER))), - new File(vncRecordingDirectory, format("%s-%s.flv", expectedFilePrefix, now().minusSeconds(1L).format(DATETIME_FORMATTER))) + new File( + vncRecordingDirectory, + String.format("%s-%s.flv", expectedFilePrefix, LocalDateTime.now().format(DATETIME_FORMATTER)) + ), + new File( + vncRecordingDirectory, + String.format( + "%s-%s.flv", + expectedFilePrefix, + LocalDateTime.now().minusSeconds(1L).format(DATETIME_FORMATTER) + ) + ) ); assertThat(expectedPossibleFileNames, hasItem(recordingFile)); diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/BaseWebDriverContainerTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/BaseWebDriverContainerTest.java index db369824442..d0865a06b09 100644 --- a/modules/selenium/src/test/java/org/testcontainers/junit/BaseWebDriverContainerTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/junit/BaseWebDriverContainerTest.java @@ -1,10 +1,5 @@ package org.testcontainers.junit; -import static java.lang.String.format; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; - -import java.util.concurrent.TimeUnit; import org.jetbrains.annotations.NotNull; import org.junit.ClassRule; import org.openqa.selenium.By; @@ -16,6 +11,11 @@ import org.testcontainers.containers.wait.strategy.HttpWaitStrategy; import org.testcontainers.utility.DockerImageName; +import java.util.concurrent.TimeUnit; + +import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; + /** * */ @@ -25,7 +25,9 @@ public class BaseWebDriverContainerTest { public static Network NETWORK = Network.newNetwork(); @ClassRule - public static GenericContainer HELLO_WORLD = new GenericContainer<>(DockerImageName.parse("testcontainers/helloworld:1.1.0")) + public static GenericContainer HELLO_WORLD = new GenericContainer<>( + DockerImageName.parse("testcontainers/helloworld:1.1.0") + ) .withNetwork(NETWORK) .withNetworkAliases("helloworld") .withExposedPorts(8080, 8081) @@ -39,17 +41,13 @@ protected static void doSimpleExplore(BrowserWebDriverContainer rule) { driver.get("http://helloworld:8080"); WebElement title = driver.findElement(By.tagName("h1")); - assertEquals("the index page contains the title 'Hello world'", - "Hello world", - title.getText().trim() - ); + assertEquals("the index page contains the title 'Hello world'", "Hello world", title.getText().trim()); } protected void assertBrowserNameIs(BrowserWebDriverContainer rule, String expectedName) { RemoteWebDriver driver = setupDriverFromRule(rule); String actual = driver.getCapabilities().getBrowserName(); - assertTrue(format("actual browser name is %s", actual), - actual.equals(expectedName)); + assertTrue(String.format("actual browser name is %s", actual), actual.equals(expectedName)); } @NotNull diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/BrowserWebDriverContainerTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/BrowserWebDriverContainerTest.java index a53fa532f99..60d113dc290 100644 --- a/modules/selenium/src/test/java/org/testcontainers/junit/BrowserWebDriverContainerTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/junit/BrowserWebDriverContainerTest.java @@ -9,10 +9,10 @@ import org.openqa.selenium.firefox.FirefoxOptions; import org.testcontainers.containers.BrowserWebDriverContainer; +import java.util.Collections; import java.util.List; +import java.util.stream.Collectors; -import static java.util.Collections.emptyList; -import static java.util.stream.Collectors.toList; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; public class BrowserWebDriverContainerTest { @@ -40,7 +40,6 @@ public void provideDefaultNoProxyEnvironmentIfNotSet() { try ( BrowserWebDriverContainer chromeWithoutNoProxySet = new BrowserWebDriverContainer() .withCapabilities(new ChromeOptions()) - ) { chromeWithoutNoProxySet.start(); @@ -49,7 +48,6 @@ public void provideDefaultNoProxyEnvironmentIfNotSet() { } } - @Test public void createContainerWithShmVolume() { Assume.assumeFalse("SHM isn't mounted on Windows", SystemUtils.IS_OS_WINDOWS); @@ -76,19 +74,19 @@ public void createContainerWithoutShmVolume() { ) { webDriverContainer.start(); - assertEquals("Shared memory size is configured", - 512 * FileUtils.ONE_MB, - webDriverContainer.getShmSize()); + assertEquals("Shared memory size is configured", 512 * FileUtils.ONE_MB, webDriverContainer.getShmSize()); - assertEquals("No shm mounts present", emptyList(), shmVolumes(webDriverContainer)); + assertEquals("No shm mounts present", Collections.emptyList(), shmVolumes(webDriverContainer)); } } private List shmVolumes(final BrowserWebDriverContainer container) { - return container.getContainerInfo().getMounts() + return container + .getContainerInfo() + .getMounts() .stream() // destination path is always /dev/shm .filter(m -> m.getDestination().getPath().equals("/dev/shm")) - .collect(toList()); + .collect(Collectors.toList()); } } diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/ChromeRecordingWebDriverContainerTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/ChromeRecordingWebDriverContainerTest.java index fdf56aa0134..b2b7d8d6b1c 100644 --- a/modules/selenium/src/test/java/org/testcontainers/junit/ChromeRecordingWebDriverContainerTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/junit/ChromeRecordingWebDriverContainerTest.java @@ -1,6 +1,5 @@ package org.testcontainers.junit; - import com.google.common.io.PatternFilenameFilter; import org.junit.Rule; import org.junit.Test; @@ -9,6 +8,7 @@ import org.junit.runner.RunWith; import org.openqa.selenium.chrome.ChromeOptions; import org.testcontainers.containers.BrowserWebDriverContainer; +import org.testcontainers.containers.BrowserWebDriverContainer.VncRecordingMode; import org.testcontainers.containers.DefaultRecordingFileFactory; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.VncRecordingContainer; @@ -21,14 +21,12 @@ import java.io.File; import java.io.IOException; import java.time.Duration; +import java.time.temporal.ChronoUnit; import java.util.Optional; import java.util.concurrent.TimeUnit; -import static java.time.temporal.ChronoUnit.SECONDS; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; -import static org.testcontainers.containers.BrowserWebDriverContainer.VncRecordingMode.RECORD_ALL; -import static org.testcontainers.containers.BrowserWebDriverContainer.VncRecordingMode.RECORD_FAILING; @RunWith(Enclosed.class) public class ChromeRecordingWebDriverContainerTest extends BaseWebDriverContainerTest { @@ -52,7 +50,7 @@ public void recordingTestThatShouldBeRecordedAndRetainedInFlvFormatAsDefault() t // To do this, simply add extra parameters to the rule constructor, so video will default to FLV format: BrowserWebDriverContainer chrome = new BrowserWebDriverContainer<>() .withCapabilities(new ChromeOptions()) - .withRecordingMode(RECORD_ALL, target) + .withRecordingMode(VncRecordingMode.RECORD_ALL, target) // } .withRecordingFileFactory(new DefaultRecordingFileFactory()) .withNetwork(NETWORK) @@ -62,22 +60,26 @@ public void recordingTestThatShouldBeRecordedAndRetainedInFlvFormatAsDefault() t } } - private File[] runSimpleExploreInContainer(BrowserWebDriverContainer container, String fileNamePattern) throws InterruptedException { + private File[] runSimpleExploreInContainer(BrowserWebDriverContainer container, String fileNamePattern) + throws InterruptedException { container.start(); TimeUnit.MILLISECONDS.sleep(MINIMUM_VIDEO_DURATION_MILLISECONDS); doSimpleExplore(container); - container.afterTest(new TestDescription() { - @Override - public String getTestId() { - return getFilesystemFriendlyName(); - } - - @Override - public String getFilesystemFriendlyName() { - return "ChromeThatRecordsAllTests-recordingTestThatShouldBeRecordedAndRetained"; - } - }, Optional.empty()); + container.afterTest( + new TestDescription() { + @Override + public String getTestId() { + return getFilesystemFriendlyName(); + } + + @Override + public String getFilesystemFriendlyName() { + return "ChromeThatRecordsAllTests-recordingTestThatShouldBeRecordedAndRetained"; + } + }, + Optional.empty() + ); return vncRecordingDirectory.getRoot().listFiles(new PatternFilenameFilter(fileNamePattern)); } @@ -90,7 +92,7 @@ public void recordingTestShouldHaveFlvExtension() throws InterruptedException { // Set (explicitly) FLV format for recorded video: BrowserWebDriverContainer chrome = new BrowserWebDriverContainer<>() .withCapabilities(new ChromeOptions()) - .withRecordingMode(RECORD_ALL, target, VncRecordingFormat.FLV) + .withRecordingMode(VncRecordingMode.RECORD_ALL, target, VncRecordingFormat.FLV) // } .withRecordingFileFactory(new DefaultRecordingFileFactory()) .withNetwork(NETWORK) @@ -108,7 +110,7 @@ public void recordingTestShouldHaveMp4Extension() throws InterruptedException { // Set MP4 format for recorded video: BrowserWebDriverContainer chrome = new BrowserWebDriverContainer<>() .withCapabilities(new ChromeOptions()) - .withRecordingMode(RECORD_ALL, target, VncRecordingFormat.MP4) + .withRecordingMode(VncRecordingMode.RECORD_ALL, target, VncRecordingFormat.MP4) // } .withRecordingFileFactory(new DefaultRecordingFileFactory()) .withNetwork(NETWORK) @@ -124,7 +126,7 @@ public void recordingTestThatShouldHaveCorrectDuration() throws IOException, Int try ( BrowserWebDriverContainer chrome = new BrowserWebDriverContainer<>() .withCapabilities(new ChromeOptions()) - .withRecordingMode(RECORD_ALL, vncRecordingDirectory.getRoot()) + .withRecordingMode(VncRecordingMode.RECORD_ALL, vncRecordingDirectory.getRoot()) .withRecordingFileFactory(new DefaultRecordingFileFactory()) .withNetwork(NETWORK) ) { @@ -132,19 +134,28 @@ public void recordingTestThatShouldHaveCorrectDuration() throws IOException, Int mountableFile = MountableFile.forHostPath(recordedFiles[0].getCanonicalPath()); } - try (GenericContainer container = new GenericContainer<>(DockerImageName.parse("testcontainers/vnc-recorder:1.2.0"))) { + try ( + GenericContainer container = new GenericContainer<>( + DockerImageName.parse("testcontainers/vnc-recorder:1.2.0") + ) + ) { String recordFileContainerPath = "/tmp/chromeTestRecord.flv"; - container.withCopyFileToContainer(mountableFile, recordFileContainerPath) - .withCreateContainerCmdModifier(createContainerCmd -> createContainerCmd.withEntrypoint("ffmpeg")) - .withCommand("-i", recordFileContainerPath, "-f", "null", "-") - .waitingFor(new LogMessageWaitStrategy() - .withRegEx(".*Duration.*") - .withStartupTimeout(Duration.of(60, SECONDS))) - .start(); + container + .withCopyFileToContainer(mountableFile, recordFileContainerPath) + .withCreateContainerCmdModifier(createContainerCmd -> createContainerCmd.withEntrypoint("ffmpeg")) + .withCommand("-i", recordFileContainerPath, "-f", "null", "-") + .waitingFor( + new LogMessageWaitStrategy() + .withRegEx(".*Duration.*") + .withStartupTimeout(Duration.of(60, ChronoUnit.SECONDS)) + ) + .start(); String ffmpegOutput = container.getLogs(); - assertTrue("Duration is incorrect in:\n " + ffmpegOutput, - ffmpegOutput.contains("Duration: 00:") && !(ffmpegOutput.contains("Duration: 00:00:00.00"))); + assertTrue( + "Duration is incorrect in:\n " + ffmpegOutput, + ffmpegOutput.contains("Duration: 00:") && !(ffmpegOutput.contains("Duration: 00:00:00.00")) + ); } } } @@ -180,7 +191,7 @@ public void recordingTestThatShouldBeRecordedAndRetained() throws InterruptedExc // or if you only want videos for test failures: BrowserWebDriverContainer chrome = new BrowserWebDriverContainer<>() .withCapabilities(new ChromeOptions()) - .withRecordingMode(RECORD_FAILING, target) + .withRecordingMode(VncRecordingMode.RECORD_FAILING, target) // } .withRecordingFileFactory(new DefaultRecordingFileFactory()) .withNetwork(NETWORK) @@ -189,25 +200,26 @@ public void recordingTestThatShouldBeRecordedAndRetained() throws InterruptedExc TimeUnit.MILLISECONDS.sleep(MINIMUM_VIDEO_DURATION_MILLISECONDS); doSimpleExplore(chrome); - chrome.afterTest(new TestDescription() { - @Override - public String getTestId() { - return getFilesystemFriendlyName(); - } - - @Override - public String getFilesystemFriendlyName() { - return "ChromeThatRecordsFailingTests-recordingTestThatShouldBeRecordedAndRetained"; - } - }, Optional.of(new RuntimeException("Force writing of video file."))); + chrome.afterTest( + new TestDescription() { + @Override + public String getTestId() { + return getFilesystemFriendlyName(); + } + + @Override + public String getFilesystemFriendlyName() { + return "ChromeThatRecordsFailingTests-recordingTestThatShouldBeRecordedAndRetained"; + } + }, + Optional.of(new RuntimeException("Force writing of video file.")) + ); String[] files = vncRecordingDirectory.getRoot().list(new PatternFilenameFilter("FAILED-.*\\.flv")); assertEquals("Recorded file not found", 1, files.length); } - } - private static class CustomRecordingFileFactory extends DefaultRecordingFileFactory { - } + private static class CustomRecordingFileFactory extends DefaultRecordingFileFactory {} } } diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/ChromeWebDriverContainerTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/ChromeWebDriverContainerTest.java index bea1909a145..eb9872b06e9 100644 --- a/modules/selenium/src/test/java/org/testcontainers/junit/ChromeWebDriverContainerTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/junit/ChromeWebDriverContainerTest.java @@ -15,7 +15,7 @@ public class ChromeWebDriverContainerTest extends BaseWebDriverContainerTest { @Rule public BrowserWebDriverContainer chrome = new BrowserWebDriverContainer<>() .withCapabilities(new ChromeOptions()) - // } + // } .withNetwork(NETWORK); @Before diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/ContainerWithoutCapabilitiesTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/ContainerWithoutCapabilitiesTest.java index b1214d18b60..d20f8c3aad8 100644 --- a/modules/selenium/src/test/java/org/testcontainers/junit/ContainerWithoutCapabilitiesTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/junit/ContainerWithoutCapabilitiesTest.java @@ -4,11 +4,10 @@ import org.junit.Test; import org.testcontainers.containers.BrowserWebDriverContainer; -public class ContainerWithoutCapabilitiesTest extends BaseWebDriverContainerTest{ +public class ContainerWithoutCapabilitiesTest extends BaseWebDriverContainerTest { @Rule - public BrowserWebDriverContainer chrome = new BrowserWebDriverContainer<>() - .withNetwork(NETWORK); + public BrowserWebDriverContainer chrome = new BrowserWebDriverContainer<>().withNetwork(NETWORK); @Test public void chromeIsStartedIfNoCapabilitiesProvided() { @@ -16,7 +15,7 @@ public void chromeIsStartedIfNoCapabilitiesProvided() { } @Test - public void simpleExploreTestWhenNoCapabilitiesProvided(){ + public void simpleExploreTestWhenNoCapabilitiesProvided() { doSimpleExplore(chrome); } } diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/CustomWaitTimeoutWebDriverContainerTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/CustomWaitTimeoutWebDriverContainerTest.java index 8448dc40d53..362be6550e4 100644 --- a/modules/selenium/src/test/java/org/testcontainers/junit/CustomWaitTimeoutWebDriverContainerTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/junit/CustomWaitTimeoutWebDriverContainerTest.java @@ -6,8 +6,7 @@ import org.testcontainers.containers.BrowserWebDriverContainer; import java.time.Duration; - -import static java.time.temporal.ChronoUnit.SECONDS; +import java.time.temporal.ChronoUnit; /** * @@ -17,7 +16,7 @@ public class CustomWaitTimeoutWebDriverContainerTest extends BaseWebDriverContai @Rule public BrowserWebDriverContainer chromeWithCustomTimeout = new BrowserWebDriverContainer<>() .withCapabilities(new ChromeOptions()) - .withStartupTimeout(Duration.of(30, SECONDS)) + .withStartupTimeout(Duration.of(30, ChronoUnit.SECONDS)) .withNetwork(NETWORK); @Test diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/FirefoxWebDriverContainerTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/FirefoxWebDriverContainerTest.java index 1cffec16bc6..e56b33a1704 100644 --- a/modules/selenium/src/test/java/org/testcontainers/junit/FirefoxWebDriverContainerTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/junit/FirefoxWebDriverContainerTest.java @@ -14,8 +14,8 @@ public class FirefoxWebDriverContainerTest extends BaseWebDriverContainerTest { // junitRule { @Rule public BrowserWebDriverContainer firefox = new BrowserWebDriverContainer<>() - .withCapabilities(new FirefoxOptions()) - // } + .withCapabilities(new FirefoxOptions()) + // } .withNetwork(NETWORK); @Before diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/LocalServerWebDriverContainerTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/LocalServerWebDriverContainerTest.java index 3c071984dfb..2d2feb6aaff 100644 --- a/modules/selenium/src/test/java/org/testcontainers/junit/LocalServerWebDriverContainerTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/junit/LocalServerWebDriverContainerTest.java @@ -22,11 +22,11 @@ public class LocalServerWebDriverContainerTest { public BrowserWebDriverContainer chrome = new BrowserWebDriverContainer<>() .withAccessToHost(true) .withCapabilities(new ChromeOptions()); + private int localPort; @Before public void setupLocalServer() throws Exception { - // Set up a local Jetty HTTP server Server server = new Server(); server.addConnector(new SocketConnector()); @@ -53,6 +53,10 @@ public void testConnection() { String headingText = driver.findElement(By.cssSelector("h1")).getText().trim(); - assertEquals("The hardcoded success message was found on a page fetched from a local server", "It worked", headingText); + assertEquals( + "The hardcoded success message was found on a page fetched from a local server", + "It worked", + headingText + ); } } diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/SeleniumStartTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/SeleniumStartTest.java index a236a9696c1..04a31662099 100644 --- a/modules/selenium/src/test/java/org/testcontainers/junit/SeleniumStartTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/junit/SeleniumStartTest.java @@ -15,19 +15,19 @@ public class SeleniumStartTest { @Parameterized.Parameters(name = "tag: {0}") public static String[] data() { - return new String[]{"4.0.0", "3.4.0", "2.53.0", "2.45.0"}; + return new String[] { "4.0.0", "3.4.0", "2.53.0", "2.45.0" }; } - @Parameterized.Parameter() + @Parameterized.Parameter public String tag; @Test public void testAdditionalStartupString() { - final DockerImageName imageName = DockerImageName - .parse("selenium/standalone-chrome") - .withTag(tag); - try (BrowserWebDriverContainer chrome = new BrowserWebDriverContainer<>(imageName) - .withCapabilities(new ChromeOptions())) { + final DockerImageName imageName = DockerImageName.parse("selenium/standalone-chrome").withTag(tag); + try ( + BrowserWebDriverContainer chrome = new BrowserWebDriverContainer<>(imageName) + .withCapabilities(new ChromeOptions()) + ) { chrome.start(); } } diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/SeleniumUtilsTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/SeleniumUtilsTest.java index d706d0fc390..4cbfca77d21 100644 --- a/modules/selenium/src/test/java/org/testcontainers/junit/SeleniumUtilsTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/junit/SeleniumUtilsTest.java @@ -8,7 +8,6 @@ import static org.rnorth.visibleassertions.VisibleAssertions.*; - /** * Created by Julien LAMY */ @@ -35,5 +34,4 @@ private void checkSeleniumVersionDetected(String urlManifest, String expectedVer String seleniumVersion = SeleniumUtils.getSeleniumVersionFromManifest(manifest); assertEquals("Check if Selenium Version detected is the correct one.", expectedVersion, seleniumVersion); } - } diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/SpecificImageNameWebDriverContainerTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/SpecificImageNameWebDriverContainerTest.java index 9a15c6b466d..e3db8ad5887 100644 --- a/modules/selenium/src/test/java/org/testcontainers/junit/SpecificImageNameWebDriverContainerTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/junit/SpecificImageNameWebDriverContainerTest.java @@ -8,8 +8,9 @@ public class SpecificImageNameWebDriverContainerTest extends BaseWebDriverContainerTest { - private static final DockerImageName FIREFOX_IMAGE = DockerImageName - .parse("selenium/standalone-firefox:2.53.1-beryllium"); + private static final DockerImageName FIREFOX_IMAGE = DockerImageName.parse( + "selenium/standalone-firefox:2.53.1-beryllium" + ); @Rule public BrowserWebDriverContainer firefox = new BrowserWebDriverContainer<>(FIREFOX_IMAGE) diff --git a/modules/solr/src/main/java/org/testcontainers/containers/SolrClientUtils.java b/modules/solr/src/main/java/org/testcontainers/containers/SolrClientUtils.java index 4492ca0befe..f831cdd82e9 100644 --- a/modules/solr/src/main/java/org/testcontainers/containers/SolrClientUtils.java +++ b/modules/solr/src/main/java/org/testcontainers/containers/SolrClientUtils.java @@ -1,5 +1,13 @@ package org.testcontainers.containers; +import okhttp3.HttpUrl; +import okhttp3.MediaType; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; +import org.apache.commons.io.IOUtils; + import java.io.ByteArrayOutputStream; import java.io.IOException; import java.net.URISyntaxException; @@ -11,15 +19,6 @@ import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; -import org.apache.commons.io.IOUtils; - -import okhttp3.HttpUrl; -import okhttp3.MediaType; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.RequestBody; -import okhttp3.Response; - /** * Utils class which can create collections and configurations. * @@ -38,7 +37,13 @@ public class SolrClientUtils { * @param solrConfig the url under which the solrconfig.xml can be found * @param solrSchema the url under which the schema.xml can be found or null if the default schema should be used */ - public static void uploadConfiguration(String hostname, int port, String configurationName, URL solrConfig, URL solrSchema) throws URISyntaxException, IOException { + public static void uploadConfiguration( + String hostname, + int port, + String configurationName, + URL solrConfig, + URL solrSchema + ) throws URISyntaxException, IOException { Map parameters = new HashMap<>(); parameters.put("action", "UPLOAD"); parameters.put("name", configurationName); @@ -46,7 +51,6 @@ public static void uploadConfiguration(String hostname, int port, String configu byte[] configurationZipFile = generateConfigZipFile(solrConfig, solrSchema); executePost(url, configurationZipFile); - } /** @@ -58,7 +62,8 @@ public static void uploadConfiguration(String hostname, int port, String configu * @param configurationName the name of the configuration which should used to create the collection * or null if the default configuration should be used */ - public static void createCollection(String hostname, int port, String collectionName, String configurationName) throws URISyntaxException, IOException { + public static void createCollection(String hostname, int port, String collectionName, String configurationName) + throws URISyntaxException, IOException { Map parameters = new HashMap<>(); parameters.put("action", "CREATE"); parameters.put("name", collectionName); @@ -73,16 +78,11 @@ public static void createCollection(String hostname, int port, String collection } private static void executePost(HttpUrl url, byte[] data) throws IOException { + RequestBody requestBody = data == null + ? RequestBody.create(MediaType.parse("text/plain"), "") + : RequestBody.create(MediaType.parse("application/octet-stream"), data); + Request request = new Request.Builder().url(url).post(requestBody).build(); - RequestBody requestBody = data == null ? - RequestBody.create(MediaType.parse("text/plain"), "") : - RequestBody.create(MediaType.parse("application/octet-stream"), data); - ; - - Request request = new Request.Builder() - .url(url) - .post(requestBody) - .build(); Response response = httpClient.newCall(request).execute(); if (!response.isSuccessful()) { String responseBody = ""; @@ -97,7 +97,12 @@ private static void executePost(HttpUrl url, byte[] data) throws IOException { } } - private static HttpUrl generateSolrURL(String hostname, int port, List pathSegments, Map parameters) throws URISyntaxException { + private static HttpUrl generateSolrURL( + String hostname, + int port, + List pathSegments, + Map parameters + ) throws URISyntaxException { HttpUrl.Builder builder = new HttpUrl.Builder(); builder.scheme("http"); builder.host(hostname); diff --git a/modules/solr/src/main/java/org/testcontainers/containers/SolrClientUtilsException.java b/modules/solr/src/main/java/org/testcontainers/containers/SolrClientUtilsException.java index 5ba0865502c..a3b2cfd77c6 100644 --- a/modules/solr/src/main/java/org/testcontainers/containers/SolrClientUtilsException.java +++ b/modules/solr/src/main/java/org/testcontainers/containers/SolrClientUtilsException.java @@ -1,6 +1,7 @@ package org.testcontainers.containers; public class SolrClientUtilsException extends RuntimeException { + public SolrClientUtilsException(int statusCode, String msg) { super("Http Call Status: " + statusCode + "\n" + msg); } diff --git a/modules/solr/src/main/java/org/testcontainers/containers/SolrContainer.java b/modules/solr/src/main/java/org/testcontainers/containers/SolrContainer.java index e583c1f54ee..5161bce2738 100644 --- a/modules/solr/src/main/java/org/testcontainers/containers/SolrContainer.java +++ b/modules/solr/src/main/java/org/testcontainers/containers/SolrContainer.java @@ -8,11 +8,10 @@ import java.net.URL; import java.time.Duration; +import java.time.temporal.ChronoUnit; import java.util.HashSet; import java.util.Set; -import static java.time.temporal.ChronoUnit.SECONDS; - /** * SolrContainer allows a solr container to be launched and controlled. */ @@ -27,6 +26,7 @@ public class SolrContainer extends GenericContainer { public static final String DEFAULT_TAG = "8.3.0"; public static final Integer ZOOKEEPER_PORT = 9983; + public static final Integer SOLR_PORT = 8983; private SolrContainerConfiguration configuration; @@ -48,12 +48,12 @@ public SolrContainer(final String dockerImageName) { public SolrContainer(final DockerImageName dockerImageName) { super(dockerImageName); - dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); - this.waitStrategy = new LogMessageWaitStrategy() - .withRegEx(".*o\\.e\\.j\\.s\\.Server Started.*") - .withStartupTimeout(Duration.of(60, SECONDS)); + this.waitStrategy = + new LogMessageWaitStrategy() + .withRegEx(".*o\\.e\\.j\\.s\\.Server Started.*") + .withStartupTimeout(Duration.of(60, ChronoUnit.SECONDS)); this.configuration = new SolrContainerConfiguration(); } @@ -129,7 +129,9 @@ protected void containerIsStarted(InspectContainerResponse containerInfo) { if (!configuration.isZookeeper()) { ExecResult result = execInContainer("solr", "create_core", "-c", configuration.getCollectionName()); if (result.getExitCode() != 0) { - throw new IllegalStateException("Unable to create solr core:\nStdout: " + result.getStdout() + "\nStderr:" + result.getStderr()); + throw new IllegalStateException( + "Unable to create solr core:\nStdout: " + result.getStdout() + "\nStderr:" + result.getStderr() + ); } return; } @@ -140,14 +142,15 @@ protected void containerIsStarted(InspectContainerResponse containerInfo) { getSolrPort(), configuration.getConfigurationName(), configuration.getSolrConfiguration(), - configuration.getSolrSchema()); + configuration.getSolrSchema() + ); } SolrClientUtils.createCollection( getHost(), getSolrPort(), configuration.getCollectionName(), - configuration.getConfigurationName()); + configuration.getConfigurationName() + ); } } - diff --git a/modules/solr/src/main/java/org/testcontainers/containers/SolrContainerConfiguration.java b/modules/solr/src/main/java/org/testcontainers/containers/SolrContainerConfiguration.java index 1f35ce2a810..698e956c8fe 100644 --- a/modules/solr/src/main/java/org/testcontainers/containers/SolrContainerConfiguration.java +++ b/modules/solr/src/main/java/org/testcontainers/containers/SolrContainerConfiguration.java @@ -1,9 +1,9 @@ package org.testcontainers.containers; -import java.net.URL; - import lombok.Data; +import java.net.URL; + /** * @author Simon Schneider */ @@ -11,8 +11,12 @@ public class SolrContainerConfiguration { private boolean zookeeper = true; + private String collectionName = "dummy"; + private String configurationName; + private URL solrConfiguration; + private URL solrSchema; } diff --git a/modules/solr/src/test/java/org/testcontainers/containers/SolrContainerTest.java b/modules/solr/src/test/java/org/testcontainers/containers/SolrContainerTest.java index 9f82e2c3af9..9b561e5cda9 100644 --- a/modules/solr/src/test/java/org/testcontainers/containers/SolrContainerTest.java +++ b/modules/solr/src/test/java/org/testcontainers/containers/SolrContainerTest.java @@ -10,9 +10,9 @@ import java.io.IOException; -import static org.hamcrest.CoreMatchers.containsString; -import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; /** * @author Simon Schneider @@ -20,6 +20,7 @@ public class SolrContainerTest { private static final DockerImageName SOLR_IMAGE = DockerImageName.parse("solr:8.3.0"); + private SolrClient client = null; @After @@ -60,7 +61,10 @@ public void solrCloudPingTest() throws IOException, SolrServerException { container.start(); // Do whatever you want with the client ... - SolrClient client = new Http2SolrClient.Builder("http://" + container.getHost() + ":" + container.getSolrPort() + "/solr").build(); + SolrClient client = new Http2SolrClient.Builder( + "http://" + container.getHost() + ":" + container.getSolrPort() + "/solr" + ) + .build(); SolrPingResponse response = client.ping("dummy"); // Stop the container. @@ -70,9 +74,10 @@ public void solrCloudPingTest() throws IOException, SolrServerException { private SolrClient getClient(SolrContainer container) { if (client == null) { - client = new Http2SolrClient.Builder("http://" + container.getHost() + ":" + container.getSolrPort() + "/solr").build(); + client = + new Http2SolrClient.Builder("http://" + container.getHost() + ":" + container.getSolrPort() + "/solr") + .build(); } return client; } - } diff --git a/modules/spock/src/main/groovy/org/testcontainers/spock/SpockTestDescription.groovy b/modules/spock/src/main/groovy/org/testcontainers/spock/SpockTestDescription.groovy index 85b58724000..6c71ac421c6 100644 --- a/modules/spock/src/main/groovy/org/testcontainers/spock/SpockTestDescription.groovy +++ b/modules/spock/src/main/groovy/org/testcontainers/spock/SpockTestDescription.groovy @@ -12,25 +12,25 @@ import org.testcontainers.lifecycle.TestDescription @PackageScope class SpockTestDescription implements TestDescription { - String specName - String featureName + String specName + String featureName - static SpockTestDescription fromTestDescription(IMethodInvocation invocation) { - return new SpockTestDescription([ - specName: invocation.spec.name, - featureName: invocation.feature.name - ]) - } + static SpockTestDescription fromTestDescription(IMethodInvocation invocation) { + return new SpockTestDescription([ + specName: invocation.spec.name, + featureName: invocation.feature.name + ]) + } - @Override - String getTestId() { - return getFilesystemFriendlyName() - } + @Override + String getTestId() { + return getFilesystemFriendlyName() + } - @Override - String getFilesystemFriendlyName() { - return [specName, featureName].collect { - URLEncoder.encode(it, 'UTF-8') - }.join('-') - } + @Override + String getFilesystemFriendlyName() { + return [specName, featureName].collect { + URLEncoder.encode(it, 'UTF-8') + }.join('-') + } } diff --git a/modules/spock/src/main/groovy/org/testcontainers/spock/Testcontainers.groovy b/modules/spock/src/main/groovy/org/testcontainers/spock/Testcontainers.groovy index 675ed68c4a6..2e70b6d589f 100644 --- a/modules/spock/src/main/groovy/org/testcontainers/spock/Testcontainers.groovy +++ b/modules/spock/src/main/groovy/org/testcontainers/spock/Testcontainers.groovy @@ -13,5 +13,4 @@ import java.lang.annotation.Target @Target([ElementType.TYPE, ElementType.METHOD]) @ExtensionAnnotation(TestcontainersExtension) @interface Testcontainers { - } diff --git a/modules/spock/src/main/groovy/org/testcontainers/spock/TestcontainersExtension.groovy b/modules/spock/src/main/groovy/org/testcontainers/spock/TestcontainersExtension.groovy index 1f66fd8f596..40392210f3a 100644 --- a/modules/spock/src/main/groovy/org/testcontainers/spock/TestcontainersExtension.groovy +++ b/modules/spock/src/main/groovy/org/testcontainers/spock/TestcontainersExtension.groovy @@ -7,26 +7,24 @@ import org.spockframework.runtime.model.SpecInfo class TestcontainersExtension extends AbstractAnnotationDrivenExtension { - @Override - void visitSpecAnnotation(Testcontainers annotation, SpecInfo spec) { - def listener = new ErrorListener() - def interceptor = new TestcontainersMethodInterceptor(spec, listener) - spec.addSetupSpecInterceptor(interceptor) - spec.addCleanupSpecInterceptor(interceptor) - spec.addSetupInterceptor(interceptor) - spec.addCleanupInterceptor(interceptor) - - spec.addListener(listener) - - } - - private class ErrorListener extends AbstractRunListener { - List errors = [] - - @Override - void error(ErrorInfo error) { - errors.add(error) - } - } - + @Override + void visitSpecAnnotation(Testcontainers annotation, SpecInfo spec) { + def listener = new ErrorListener() + def interceptor = new TestcontainersMethodInterceptor(spec, listener) + spec.addSetupSpecInterceptor(interceptor) + spec.addCleanupSpecInterceptor(interceptor) + spec.addSetupInterceptor(interceptor) + spec.addCleanupInterceptor(interceptor) + + spec.addListener(listener) + } + + private class ErrorListener extends AbstractRunListener { + List errors = [] + + @Override + void error(ErrorInfo error) { + errors.add(error) + } + } } diff --git a/modules/spock/src/main/groovy/org/testcontainers/spock/TestcontainersMethodInterceptor.groovy b/modules/spock/src/main/groovy/org/testcontainers/spock/TestcontainersMethodInterceptor.groovy index 16be002aeb1..2399a0a6e32 100644 --- a/modules/spock/src/main/groovy/org/testcontainers/spock/TestcontainersMethodInterceptor.groovy +++ b/modules/spock/src/main/groovy/org/testcontainers/spock/TestcontainersMethodInterceptor.groovy @@ -11,116 +11,116 @@ import org.testcontainers.spock.TestcontainersExtension.ErrorListener class TestcontainersMethodInterceptor extends AbstractMethodInterceptor { - private final SpecInfo spec - private final ErrorListener errorListener - - TestcontainersMethodInterceptor(SpecInfo spec, ErrorListener errorListener) { - this.spec = spec - this.errorListener = errorListener - } - - @Override - void interceptSetupSpecMethod(IMethodInvocation invocation) throws Throwable { - def containers = findAllContainers(true) - startContainers(containers, invocation) - - def compose = findAllComposeContainers(true) - startComposeContainers(compose, invocation) - - invocation.proceed() - } - - @Override - void interceptCleanupSpecMethod(IMethodInvocation invocation) throws Throwable { - def containers = findAllContainers(true) - stopContainers(containers, invocation) - - def compose = findAllComposeContainers(true) - stopComposeContainers(compose, invocation) - - invocation.proceed() - } - - @Override - void interceptSetupMethod(IMethodInvocation invocation) throws Throwable { - def containers = findAllContainers(false) - startContainers(containers, invocation) - - def compose = findAllComposeContainers(false) - startComposeContainers(compose, invocation) - - invocation.proceed() - } - - - @Override - void interceptCleanupMethod(IMethodInvocation invocation) throws Throwable { - def containers = findAllContainers(false) - stopContainers(containers, invocation) - - def compose = findAllComposeContainers(false) - stopComposeContainers(compose, invocation) - - invocation.proceed() - } - - private List findAllContainers(boolean shared) { - spec.allFields.findAll { FieldInfo f -> - GenericContainer.isAssignableFrom(f.type) && f.shared == shared - } - } - - private List findAllComposeContainers(boolean shared) { - spec.allFields.findAll { FieldInfo f -> - DockerComposeContainer.isAssignableFrom(f.type) && f.shared == shared - } - } - - private static void startContainers(List containers, IMethodInvocation invocation) { - containers.each { FieldInfo f -> - GenericContainer container = readContainerFromField(f, invocation) - if(!container.isRunning()){ - container.start() - } - - if (container instanceof TestLifecycleAware) { - def testDescription = SpockTestDescription.fromTestDescription(invocation) - (container as TestLifecycleAware).beforeTest(testDescription) - } - } - } - - private void stopContainers(List containers, IMethodInvocation invocation) { - containers.each { FieldInfo f -> - GenericContainer container = readContainerFromField(f, invocation) - - if (container instanceof TestLifecycleAware) { - // we assume first error is the one we want - def maybeException = Optional.ofNullable(errorListener.errors[0]?.exception) - def testDescription = SpockTestDescription.fromTestDescription(invocation) - (container as TestLifecycleAware).afterTest(testDescription, maybeException) - } - - container.stop() - } - } - - private static void startComposeContainers(List compose, IMethodInvocation invocation) { - compose.each { FieldInfo f -> - DockerComposeContainer c = f.readValue(invocation.instance) as DockerComposeContainer - c.start() - } - } - - private static void stopComposeContainers(List compose, IMethodInvocation invocation) { - compose.each { FieldInfo f -> - DockerComposeContainer c = f.readValue(invocation.instance) as DockerComposeContainer - c.stop() - } - } - - - private static GenericContainer readContainerFromField(FieldInfo f, IMethodInvocation invocation) { - f.readValue(invocation.instance) as GenericContainer - } + private final SpecInfo spec + private final ErrorListener errorListener + + TestcontainersMethodInterceptor(SpecInfo spec, ErrorListener errorListener) { + this.spec = spec + this.errorListener = errorListener + } + + @Override + void interceptSetupSpecMethod(IMethodInvocation invocation) throws Throwable { + def containers = findAllContainers(true) + startContainers(containers, invocation) + + def compose = findAllComposeContainers(true) + startComposeContainers(compose, invocation) + + invocation.proceed() + } + + @Override + void interceptCleanupSpecMethod(IMethodInvocation invocation) throws Throwable { + def containers = findAllContainers(true) + stopContainers(containers, invocation) + + def compose = findAllComposeContainers(true) + stopComposeContainers(compose, invocation) + + invocation.proceed() + } + + @Override + void interceptSetupMethod(IMethodInvocation invocation) throws Throwable { + def containers = findAllContainers(false) + startContainers(containers, invocation) + + def compose = findAllComposeContainers(false) + startComposeContainers(compose, invocation) + + invocation.proceed() + } + + + @Override + void interceptCleanupMethod(IMethodInvocation invocation) throws Throwable { + def containers = findAllContainers(false) + stopContainers(containers, invocation) + + def compose = findAllComposeContainers(false) + stopComposeContainers(compose, invocation) + + invocation.proceed() + } + + private List findAllContainers(boolean shared) { + spec.allFields.findAll { FieldInfo f -> + GenericContainer.isAssignableFrom(f.type) && f.shared == shared + } + } + + private List findAllComposeContainers(boolean shared) { + spec.allFields.findAll { FieldInfo f -> + DockerComposeContainer.isAssignableFrom(f.type) && f.shared == shared + } + } + + private static void startContainers(List containers, IMethodInvocation invocation) { + containers.each { FieldInfo f -> + GenericContainer container = readContainerFromField(f, invocation) + if(!container.isRunning()){ + container.start() + } + + if (container instanceof TestLifecycleAware) { + def testDescription = SpockTestDescription.fromTestDescription(invocation) + (container as TestLifecycleAware).beforeTest(testDescription) + } + } + } + + private void stopContainers(List containers, IMethodInvocation invocation) { + containers.each { FieldInfo f -> + GenericContainer container = readContainerFromField(f, invocation) + + if (container instanceof TestLifecycleAware) { + // we assume first error is the one we want + def maybeException = Optional.ofNullable(errorListener.errors[0]?.exception) + def testDescription = SpockTestDescription.fromTestDescription(invocation) + (container as TestLifecycleAware).afterTest(testDescription, maybeException) + } + + container.stop() + } + } + + private static void startComposeContainers(List compose, IMethodInvocation invocation) { + compose.each { FieldInfo f -> + DockerComposeContainer c = f.readValue(invocation.instance) as DockerComposeContainer + c.start() + } + } + + private static void stopComposeContainers(List compose, IMethodInvocation invocation) { + compose.each { FieldInfo f -> + DockerComposeContainer c = f.readValue(invocation.instance) as DockerComposeContainer + c.stop() + } + } + + + private static GenericContainer readContainerFromField(FieldInfo f, IMethodInvocation invocation) { + f.readValue(invocation.instance) as GenericContainer + } } diff --git a/modules/spock/src/test/groovy/org/testcontainers/spock/ComposeContainerIT.groovy b/modules/spock/src/test/groovy/org/testcontainers/spock/ComposeContainerIT.groovy index b952dd1c5dc..7294d534275 100644 --- a/modules/spock/src/test/groovy/org/testcontainers/spock/ComposeContainerIT.groovy +++ b/modules/spock/src/test/groovy/org/testcontainers/spock/ComposeContainerIT.groovy @@ -9,27 +9,27 @@ import spock.lang.Specification @Testcontainers class ComposeContainerIT extends Specification { - DockerComposeContainer composeContainer = new DockerComposeContainer( - new File("src/test/resources/docker-compose.yml")) - .withExposedService("whoami_1", 80, Wait.forHttp("/")) + DockerComposeContainer composeContainer = new DockerComposeContainer( + new File("src/test/resources/docker-compose.yml")) + .withExposedService("whoami_1", 80, Wait.forHttp("/")) - String host + String host - int port + int port - def setup() { - host = composeContainer.getServiceHost("whoami_1", 80) - port = composeContainer.getServicePort("whoami_1", 80) - } + def setup() { + host = composeContainer.getServiceHost("whoami_1", 80) + port = composeContainer.getServicePort("whoami_1", 80) + } - def "running compose defined container is accessible on configured port"() { - given: "a http client" - def client = HttpClientBuilder.create().build() + def "running compose defined container is accessible on configured port"() { + given: "a http client" + def client = HttpClientBuilder.create().build() - when: "accessing web server" - def response = client.execute(new HttpGet("http://$host:$port")) + when: "accessing web server" + def response = client.execute(new HttpGet("http://$host:$port")) - then: "docker container is running and returns http status code 200" - response.statusLine.statusCode == 200 - } + then: "docker container is running and returns http status code 200" + response.statusLine.statusCode == 200 + } } diff --git a/modules/spock/src/test/groovy/org/testcontainers/spock/MySqlContainerIT.groovy b/modules/spock/src/test/groovy/org/testcontainers/spock/MySqlContainerIT.groovy index 639b46f5886..2f44408fed6 100644 --- a/modules/spock/src/test/groovy/org/testcontainers/spock/MySqlContainerIT.groovy +++ b/modules/spock/src/test/groovy/org/testcontainers/spock/MySqlContainerIT.groovy @@ -14,13 +14,11 @@ import spock.lang.Specification @Testcontainers class MySqlContainerIT extends Specification { - @Shared - MySQLContainer mySQLContainer = new MySQLContainer(SpockTestImages.MYSQL_IMAGE) - - def "dummy test"() { - expect: - mySQLContainer.isRunning() - } - + @Shared + MySQLContainer mySQLContainer = new MySQLContainer(SpockTestImages.MYSQL_IMAGE) + def "dummy test"() { + expect: + mySQLContainer.isRunning() + } } diff --git a/modules/spock/src/test/groovy/org/testcontainers/spock/PostgresContainerIT.groovy b/modules/spock/src/test/groovy/org/testcontainers/spock/PostgresContainerIT.groovy index a886d552732..19edca4b459 100644 --- a/modules/spock/src/test/groovy/org/testcontainers/spock/PostgresContainerIT.groovy +++ b/modules/spock/src/test/groovy/org/testcontainers/spock/PostgresContainerIT.groovy @@ -13,34 +13,34 @@ import java.sql.Statement @Testcontainers class PostgresContainerIT extends Specification { - @Shared - PostgreSQLContainer postgreSQLContainer = new PostgreSQLContainer(SpockTestImages.POSTGRES_TEST_IMAGE) - .withDatabaseName("foo") - .withUsername("foo") - .withPassword("secret") - - def "waits until postgres accepts jdbc connections"() { - - given: "a jdbc connection" - HikariConfig hikariConfig = new HikariConfig() - hikariConfig.setJdbcUrl(postgreSQLContainer.jdbcUrl) - hikariConfig.setUsername("foo") - hikariConfig.setPassword("secret") - HikariDataSource ds = new HikariDataSource(hikariConfig) - - when: "querying the database" - Statement statement = ds.getConnection().createStatement() - statement.execute("SELECT 1") - ResultSet resultSet = statement.getResultSet() - resultSet.next() - - then: "result is returned" - int resultSetInt = resultSet.getInt(1) - resultSetInt == 1 - - cleanup: - ds.close() - } + @Shared + PostgreSQLContainer postgreSQLContainer = new PostgreSQLContainer(SpockTestImages.POSTGRES_TEST_IMAGE) + .withDatabaseName("foo") + .withUsername("foo") + .withPassword("secret") + + def "waits until postgres accepts jdbc connections"() { + + given: "a jdbc connection" + HikariConfig hikariConfig = new HikariConfig() + hikariConfig.setJdbcUrl(postgreSQLContainer.jdbcUrl) + hikariConfig.setUsername("foo") + hikariConfig.setPassword("secret") + HikariDataSource ds = new HikariDataSource(hikariConfig) + + when: "querying the database" + Statement statement = ds.getConnection().createStatement() + statement.execute("SELECT 1") + ResultSet resultSet = statement.getResultSet() + resultSet.next() + + then: "result is returned" + int resultSetInt = resultSet.getInt(1) + resultSetInt == 1 + + cleanup: + ds.close() + } } // } diff --git a/modules/spock/src/test/groovy/org/testcontainers/spock/SharedComposeContainerIT.groovy b/modules/spock/src/test/groovy/org/testcontainers/spock/SharedComposeContainerIT.groovy index dd2ce9f5b3c..f728212278f 100644 --- a/modules/spock/src/test/groovy/org/testcontainers/spock/SharedComposeContainerIT.groovy +++ b/modules/spock/src/test/groovy/org/testcontainers/spock/SharedComposeContainerIT.groovy @@ -10,28 +10,28 @@ import spock.lang.Specification @Testcontainers class SharedComposeContainerIT extends Specification { - @Shared - DockerComposeContainer composeContainer = new DockerComposeContainer( - new File("src/test/resources/docker-compose.yml")) - .withExposedService("whoami_1", 80, Wait.forHttp("/")) + @Shared + DockerComposeContainer composeContainer = new DockerComposeContainer( + new File("src/test/resources/docker-compose.yml")) + .withExposedService("whoami_1", 80, Wait.forHttp("/")) - String host + String host - int port + int port - def setup() { - host = composeContainer.getServiceHost("whoami_1", 80) - port = composeContainer.getServicePort("whoami_1", 80) - } + def setup() { + host = composeContainer.getServiceHost("whoami_1", 80) + port = composeContainer.getServicePort("whoami_1", 80) + } - def "running compose defined container is accessible on configured port"() { - given: "a http client" - def client = HttpClientBuilder.create().build() + def "running compose defined container is accessible on configured port"() { + given: "a http client" + def client = HttpClientBuilder.create().build() - when: "accessing web server" - def response = client.execute(new HttpGet("http://$host:$port")) + when: "accessing web server" + def response = client.execute(new HttpGet("http://$host:$port")) - then: "docker container is running and returns http status code 200" - response.statusLine.statusCode == 200 - } + then: "docker container is running and returns http status code 200" + response.statusLine.statusCode == 200 + } } diff --git a/modules/spock/src/test/groovy/org/testcontainers/spock/SpockTestImages.groovy b/modules/spock/src/test/groovy/org/testcontainers/spock/SpockTestImages.groovy index b9ef64fe475..41432e5e514 100644 --- a/modules/spock/src/test/groovy/org/testcontainers/spock/SpockTestImages.groovy +++ b/modules/spock/src/test/groovy/org/testcontainers/spock/SpockTestImages.groovy @@ -3,8 +3,8 @@ package org.testcontainers.spock import org.testcontainers.utility.DockerImageName interface SpockTestImages { - DockerImageName MYSQL_IMAGE = DockerImageName.parse("mysql:5.7.34") - DockerImageName POSTGRES_TEST_IMAGE = DockerImageName.parse("postgres:9.6.12") - DockerImageName HTTPD_IMAGE = DockerImageName.parse("httpd:2.4-alpine") - DockerImageName TINY_IMAGE = DockerImageName.parse("alpine:3.14") + DockerImageName MYSQL_IMAGE = DockerImageName.parse("mysql:5.7.34") + DockerImageName POSTGRES_TEST_IMAGE = DockerImageName.parse("postgres:9.6.12") + DockerImageName HTTPD_IMAGE = DockerImageName.parse("httpd:2.4-alpine") + DockerImageName TINY_IMAGE = DockerImageName.parse("alpine:3.14") } diff --git a/modules/spock/src/test/groovy/org/testcontainers/spock/TestHierarchyIT.groovy b/modules/spock/src/test/groovy/org/testcontainers/spock/TestHierarchyIT.groovy index c28643910a4..567998281a7 100644 --- a/modules/spock/src/test/groovy/org/testcontainers/spock/TestHierarchyIT.groovy +++ b/modules/spock/src/test/groovy/org/testcontainers/spock/TestHierarchyIT.groovy @@ -9,17 +9,15 @@ import spock.lang.Shared */ class TestHierarchyIT extends MySqlContainerIT { - @Shared - PostgreSQLContainer postgreSQLContainer = new PostgreSQLContainer(SpockTestImages.POSTGRES_TEST_IMAGE) - .withDatabaseName("foo") - .withUsername("foo") - .withPassword("secret") - - def "both containers are running"() { - expect: - postgreSQLContainer.isRunning() - mySQLContainer.isRunning() - - } + @Shared + PostgreSQLContainer postgreSQLContainer = new PostgreSQLContainer(SpockTestImages.POSTGRES_TEST_IMAGE) + .withDatabaseName("foo") + .withUsername("foo") + .withPassword("secret") + def "both containers are running"() { + expect: + postgreSQLContainer.isRunning() + mySQLContainer.isRunning() + } } diff --git a/modules/spock/src/test/groovy/org/testcontainers/spock/TestLifecycleAwareContainerMock.java b/modules/spock/src/test/groovy/org/testcontainers/spock/TestLifecycleAwareContainerMock.java index eb628fd2f33..7502d097008 100644 --- a/modules/spock/src/test/groovy/org/testcontainers/spock/TestLifecycleAwareContainerMock.java +++ b/modules/spock/src/test/groovy/org/testcontainers/spock/TestLifecycleAwareContainerMock.java @@ -8,20 +8,22 @@ import java.util.List; import java.util.Optional; -import static org.testcontainers.spock.SpockTestImages.TINY_IMAGE; - -public class TestLifecycleAwareContainerMock extends GenericContainer implements TestLifecycleAware { +public class TestLifecycleAwareContainerMock + extends GenericContainer + implements TestLifecycleAware { static final String BEFORE_TEST = "beforeTest"; + static final String AFTER_TEST = "afterTest"; final List lifecycleMethodCalls = new ArrayList<>(); + final List lifecycleFilesystemFriendlyNames = new ArrayList<>(); Throwable capturedThrowable; public TestLifecycleAwareContainerMock() { - super(TINY_IMAGE); + super(SpockTestImages.TINY_IMAGE); } @Override diff --git a/modules/spock/src/test/groovy/org/testcontainers/spock/TestLifecycleAwareIT.groovy b/modules/spock/src/test/groovy/org/testcontainers/spock/TestLifecycleAwareIT.groovy index 18f7517ea3f..fae834ea422 100644 --- a/modules/spock/src/test/groovy/org/testcontainers/spock/TestLifecycleAwareIT.groovy +++ b/modules/spock/src/test/groovy/org/testcontainers/spock/TestLifecycleAwareIT.groovy @@ -7,12 +7,12 @@ import spock.util.EmbeddedSpecRunner class TestLifecycleAwareIT extends Specification { - @Unroll("When failing test is #fails, afterTest receives '#filesystemFriendlyNames' and throwable starting with message '#errorMessageStartsWith'") - def "lifecycle awareness"() { - given: + @Unroll("When failing test is #fails, afterTest receives '#filesystemFriendlyNames' and throwable starting with message '#errorMessageStartsWith'") + def "lifecycle awareness"() { + given: - @Language("groovy") - String myTest = """ + @Language("groovy") + String myTest = """ import org.testcontainers.spock.Testcontainers import org.testcontainers.containers.GenericContainer import spock.lang.Specification @@ -28,28 +28,27 @@ class TestLifecycleAwareIT extends Specification { } } """ - and: - def container = new TestLifecycleAwareContainerMock() - System.properties["org.testcontainers.container"] = container - System.properties["org.testcontainers.shouldFail"] = fails - - when: "executing the test" - def runner = new EmbeddedSpecRunner(throwFailure: false) - runner.run(myTest) - - then: "mock container received lifecycle calls as expected" - container.lifecycleMethodCalls == ["beforeTest", "afterTest"] - container.lifecycleFilesystemFriendlyNames.join(",") == filesystemFriendlyNames - if (errorMessageStartsWith) { - assert container.capturedThrowable.message.startsWith(errorMessageStartsWith) - } else { - assert container.capturedThrowable == null - } - - where: - fails | filesystemFriendlyNames | errorMessageStartsWith - false | 'TestLifecycleAwareIT-perform+test' | null - true | 'TestLifecycleAwareIT-perform+test' | "Condition not satisfied:" - } - + and: + def container = new TestLifecycleAwareContainerMock() + System.properties["org.testcontainers.container"] = container + System.properties["org.testcontainers.shouldFail"] = fails + + when: "executing the test" + def runner = new EmbeddedSpecRunner(throwFailure: false) + runner.run(myTest) + + then: "mock container received lifecycle calls as expected" + container.lifecycleMethodCalls == ["beforeTest", "afterTest"] + container.lifecycleFilesystemFriendlyNames.join(",") == filesystemFriendlyNames + if (errorMessageStartsWith) { + assert container.capturedThrowable.message.startsWith(errorMessageStartsWith) + } else { + assert container.capturedThrowable == null + } + + where: + fails | filesystemFriendlyNames | errorMessageStartsWith + false | 'TestLifecycleAwareIT-perform+test' | null + true | 'TestLifecycleAwareIT-perform+test' | "Condition not satisfied:" + } } diff --git a/modules/spock/src/test/groovy/org/testcontainers/spock/TestcontainersRestartBetweenTestsIT.groovy b/modules/spock/src/test/groovy/org/testcontainers/spock/TestcontainersRestartBetweenTestsIT.groovy index 81ed0eea724..ebbb1de2474 100644 --- a/modules/spock/src/test/groovy/org/testcontainers/spock/TestcontainersRestartBetweenTestsIT.groovy +++ b/modules/spock/src/test/groovy/org/testcontainers/spock/TestcontainersRestartBetweenTestsIT.groovy @@ -9,23 +9,22 @@ import spock.lang.Stepwise @Testcontainers class TestcontainersRestartBetweenTestsIT extends Specification { - GenericContainer genericContainer = new GenericContainer(SpockTestImages.HTTPD_IMAGE) - .withExposedPorts(80) + GenericContainer genericContainer = new GenericContainer(SpockTestImages.HTTPD_IMAGE) + .withExposedPorts(80) - @Shared - String lastContainerId + @Shared + String lastContainerId - def "retrieving first id"() { - when: - lastContainerId = genericContainer.containerId + def "retrieving first id"() { + when: + lastContainerId = genericContainer.containerId - then: - true - } - - def "containers is recreated between tests"() { - expect: - genericContainer.containerId != lastContainerId - } + then: + true + } + def "containers is recreated between tests"() { + expect: + genericContainer.containerId != lastContainerId + } } diff --git a/modules/spock/src/test/groovy/org/testcontainers/spock/TestcontainersSharedContainerIT.groovy b/modules/spock/src/test/groovy/org/testcontainers/spock/TestcontainersSharedContainerIT.groovy index 23b761fcc38..dd2f8443772 100644 --- a/modules/spock/src/test/groovy/org/testcontainers/spock/TestcontainersSharedContainerIT.groovy +++ b/modules/spock/src/test/groovy/org/testcontainers/spock/TestcontainersSharedContainerIT.groovy @@ -13,35 +13,34 @@ import spock.lang.Stepwise @Testcontainers class TestcontainersSharedContainerIT extends Specification { - @Shared - GenericContainer genericContainer = new GenericContainer(SpockTestImages.HTTPD_IMAGE) - .withExposedPorts(80) - - @Shared - String lastContainerId - - def "starts accessible docker container"() { - given: "a http client" - def client = HttpClientBuilder.create().build() - lastContainerId = genericContainer.containerId - - when: "accessing web server" - CloseableHttpResponse response = performHttpRequest(client) - - then: "docker container is running and returns http status code 200" - response.statusLine.statusCode == 200 - } - - def "containers keeps on running between features"() { - expect: - genericContainer.containerId == lastContainerId - } - - private CloseableHttpResponse performHttpRequest(CloseableHttpClient client) { - String ip = genericContainer.host - String port = genericContainer.getMappedPort(80) - def response = client.execute(new HttpGet("http://$ip:$port")) - response - } - + @Shared + GenericContainer genericContainer = new GenericContainer(SpockTestImages.HTTPD_IMAGE) + .withExposedPorts(80) + + @Shared + String lastContainerId + + def "starts accessible docker container"() { + given: "a http client" + def client = HttpClientBuilder.create().build() + lastContainerId = genericContainer.containerId + + when: "accessing web server" + CloseableHttpResponse response = performHttpRequest(client) + + then: "docker container is running and returns http status code 200" + response.statusLine.statusCode == 200 + } + + def "containers keeps on running between features"() { + expect: + genericContainer.containerId == lastContainerId + } + + private CloseableHttpResponse performHttpRequest(CloseableHttpClient client) { + String ip = genericContainer.host + String port = genericContainer.getMappedPort(80) + def response = client.execute(new HttpGet("http://$ip:$port")) + response + } } diff --git a/modules/toxiproxy/src/main/java/org/testcontainers/containers/ToxiproxyContainer.java b/modules/toxiproxy/src/main/java/org/testcontainers/containers/ToxiproxyContainer.java index e22cc1fdd55..ed8413a2a31 100644 --- a/modules/toxiproxy/src/main/java/org/testcontainers/containers/ToxiproxyContainer.java +++ b/modules/toxiproxy/src/main/java/org/testcontainers/containers/ToxiproxyContainer.java @@ -22,13 +22,19 @@ public class ToxiproxyContainer extends GenericContainer { private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("shopify/toxiproxy"); + private static final String DEFAULT_TAG = "2.1.0"; + private static final int TOXIPROXY_CONTROL_PORT = 8474; + private static final int FIRST_PROXIED_PORT = 8666; + private static final int LAST_PROXIED_PORT = 8666 + 31; private ToxiproxyClient client; + private final Map proxies = new HashMap<>(); + private final AtomicInteger nextPort = new AtomicInteger(FIRST_PROXIED_PORT); /** @@ -45,7 +51,6 @@ public ToxiproxyContainer(String dockerImageName) { public ToxiproxyContainer(final DockerImageName dockerImageName) { super(dockerImageName); - dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); addExposedPorts(TOXIPROXY_CONTROL_PORT); @@ -99,42 +104,55 @@ public ContainerProxy getProxy(GenericContainer container, int port) { public ContainerProxy getProxy(String hostname, int port) { String upstream = hostname + ":" + port; - return proxies.computeIfAbsent(upstream, __ -> { - try { - final int toxiPort = nextPort.getAndIncrement(); - if (toxiPort > LAST_PROXIED_PORT) { - throw new IllegalStateException("Maximum number of proxies exceeded"); + return proxies.computeIfAbsent( + upstream, + __ -> { + try { + final int toxiPort = nextPort.getAndIncrement(); + if (toxiPort > LAST_PROXIED_PORT) { + throw new IllegalStateException("Maximum number of proxies exceeded"); + } + + final Proxy proxy = client.createProxy(upstream, "0.0.0.0:" + toxiPort, upstream); + final int mappedPort = getMappedPort(toxiPort); + return new ContainerProxy(proxy, getHost(), mappedPort, toxiPort); + } catch (IOException e) { + throw new RuntimeException("Proxy could not be created", e); } - - final Proxy proxy = client.createProxy(upstream, "0.0.0.0:" + toxiPort, upstream); - final int mappedPort = getMappedPort(toxiPort); - return new ContainerProxy(proxy, getHost(), mappedPort, toxiPort); - } catch (IOException e) { - throw new RuntimeException("Proxy could not be created", e); } - }); + ); } @RequiredArgsConstructor(access = AccessLevel.PROTECTED) public static class ContainerProxy { + private static final String CUT_CONNECTION_DOWNSTREAM = "CUT_CONNECTION_DOWNSTREAM"; + private static final String CUT_CONNECTION_UPSTREAM = "CUT_CONNECTION_UPSTREAM"; + private final Proxy toxi; + /** * The IP address that this proxy container may be reached on from the host machine. */ - @Getter private final String containerIpAddress; + @Getter + private final String containerIpAddress; + /** * The mapped port of this proxy. This is a port of the host machine. It can be used to * access the Toxiproxy container from the host machine. */ - @Getter private final int proxyPort; + @Getter + private final int proxyPort; + /** * The original (exposed) port of this proxy. This is a port of the Toxiproxy Docker * container. It can be used to access this container from a different Docker container * on the same network. */ - @Getter private final int originalProxyPort; + @Getter + private final int originalProxyPort; + private boolean isCurrentlyCut; public String getName() { diff --git a/modules/toxiproxy/src/test/java/org/testcontainers/containers/ToxiproxyTest.java b/modules/toxiproxy/src/test/java/org/testcontainers/containers/ToxiproxyTest.java index 3177fb2e8e8..c5982bc45c1 100644 --- a/modules/toxiproxy/src/test/java/org/testcontainers/containers/ToxiproxyTest.java +++ b/modules/toxiproxy/src/test/java/org/testcontainers/containers/ToxiproxyTest.java @@ -10,7 +10,6 @@ import java.io.IOException; import java.time.Duration; -import static java.lang.String.format; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; import static org.rnorth.visibleassertions.VisibleAssertions.assertThrows; import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; @@ -19,6 +18,7 @@ public class ToxiproxyTest { private static final Duration JEDIS_TIMEOUT = Duration.ofSeconds(10); + // spotless:off // creatingProxy { // An alias that can be used to resolve the Toxiproxy container by name in the network it is connected to. // It can be used as a hostname of the Toxiproxy container by other containers in the same network. @@ -29,6 +29,7 @@ public class ToxiproxyTest { public Network network = Network.newNetwork(); private static final DockerImageName REDIS_IMAGE = DockerImageName.parse("redis:5.0.4"); + // The target container - this could be anything @Rule public GenericContainer redis = new GenericContainer<>(REDIS_IMAGE) @@ -36,12 +37,15 @@ public class ToxiproxyTest { .withNetwork(network); private static final DockerImageName TOXIPROXY_IMAGE = DockerImageName.parse("shopify/toxiproxy:2.1.0"); + // Toxiproxy container, which will be used as a TCP proxy @Rule public ToxiproxyContainer toxiproxy = new ToxiproxyContainer(TOXIPROXY_IMAGE) .withNetwork(network) .withNetworkAliases(TOXIPROXY_NETWORK_ALIAS); + // } + // spotless:on @Test public void testDirect() { @@ -68,12 +72,14 @@ public void testLatencyViaProxy() throws IOException { checkCallWithLatency(jedis, "without interference", 0, 250); + // spotless:off // addingLatency { proxy.toxics() .latency("latency", ToxicDirection.DOWNSTREAM, 1_100) .setJitter(100); // from now on the connection latency should be from 1000-1200 ms. // } + // spotless:on checkCallWithLatency(jedis, "with interference", 1_000, 1_500); } @@ -84,30 +90,42 @@ public void testConnectionCut() { final Jedis jedis = createJedis(proxy.getContainerIpAddress(), proxy.getProxyPort()); jedis.set("somekey", "somevalue"); - assertEquals("access to the container works OK before cutting the connection", "somevalue", jedis.get("somekey")); + assertEquals( + "access to the container works OK before cutting the connection", + "somevalue", + jedis.get("somekey") + ); // disableProxy { proxy.setConnectionCut(true); // for example, expect failure when the connection is cut - assertThrows("calls fail when the connection is cut", - JedisConnectionException.class, () -> { + assertThrows( + "calls fail when the connection is cut", + JedisConnectionException.class, + () -> { jedis.get("somekey"); - }); + } + ); proxy.setConnectionCut(false); // and with the connection re-established, expect success - assertEquals("access to the container works OK after re-establishing the connection", "somevalue", jedis.get("somekey")); + assertEquals( + "access to the container works OK after re-establishing the connection", + "somevalue", + jedis.get("somekey") + ); // } } @Test public void testMultipleProxiesCanBeCreated() { - try (GenericContainer secondRedis = new GenericContainer<>(REDIS_IMAGE) - .withExposedPorts(6379) - .withNetwork(network)) { - + try ( + GenericContainer secondRedis = new GenericContainer<>(REDIS_IMAGE) + .withExposedPorts(6379) + .withNetwork(network) + ) { secondRedis.start(); final ToxiproxyContainer.ContainerProxy firstProxy = toxiproxy.getProxy(redis, 6379); @@ -121,10 +139,13 @@ public void testMultipleProxiesCanBeCreated() { firstProxy.setConnectionCut(true); - assertThrows("calls fail when the connection is cut, for only the relevant proxy", - JedisConnectionException.class, () -> { + assertThrows( + "calls fail when the connection is cut, for only the relevant proxy", + JedisConnectionException.class, + () -> { firstJedis.get("somekey"); - }); + } + ); assertEquals("access via a different proxy is OK", "somevalue", secondJedis.get("somekey")); } @@ -142,11 +163,19 @@ public void testOriginalAndMappedPorts() { final ToxiproxyContainer.ContainerProxy proxy1 = toxiproxy.getProxy("hostname1", 8080); assertEquals("original port is correct", 8667, proxy1.getOriginalProxyPort()); - assertEquals("mapped port is correct", toxiproxy.getMappedPort(proxy1.getOriginalProxyPort()), proxy1.getProxyPort()); + assertEquals( + "mapped port is correct", + toxiproxy.getMappedPort(proxy1.getOriginalProxyPort()), + proxy1.getProxyPort() + ); final ToxiproxyContainer.ContainerProxy proxy2 = toxiproxy.getProxy("hostname2", 9090); assertEquals("original port is correct", 8668, proxy2.getOriginalProxyPort()); - assertEquals("mapped port is correct", toxiproxy.getMappedPort(proxy2.getOriginalProxyPort()), proxy2.getProxyPort()); + assertEquals( + "mapped port is correct", + toxiproxy.getMappedPort(proxy2.getOriginalProxyPort()), + proxy2.getProxyPort() + ); } @Test @@ -163,15 +192,26 @@ public void testControlPort() { assertEquals("control port is mapped from port 8474", toxiproxy.getMappedPort(8474), controlPort); } - private void checkCallWithLatency(Jedis jedis, final String description, int expectedMinLatency, long expectedMaxLatency) { + private void checkCallWithLatency( + Jedis jedis, + final String description, + int expectedMinLatency, + long expectedMaxLatency + ) { final long start = System.currentTimeMillis(); String s = jedis.get("somekey"); final long end = System.currentTimeMillis(); final long duration = end - start; - assertEquals(format("access to the container %s works OK", description), "somevalue", s); - assertTrue(format("%s there is at least %dms latency", description, expectedMinLatency), duration >= expectedMinLatency); - assertTrue(format("%s there is no more than %dms latency", description, expectedMaxLatency), duration < expectedMaxLatency); + assertEquals(String.format("access to the container %s works OK", description), "somevalue", s); + assertTrue( + String.format("%s there is at least %dms latency", description, expectedMinLatency), + duration >= expectedMinLatency + ); + assertTrue( + String.format("%s there is no more than %dms latency", description, expectedMaxLatency), + duration < expectedMaxLatency + ); } private static Jedis createJedis(String host, int port) { diff --git a/modules/trino/src/main/java/org/testcontainers/containers/TrinoContainer.java b/modules/trino/src/main/java/org/testcontainers/containers/TrinoContainer.java index 825340459f3..9ed18e96d28 100644 --- a/modules/trino/src/main/java/org/testcontainers/containers/TrinoContainer.java +++ b/modules/trino/src/main/java/org/testcontainers/containers/TrinoContainer.java @@ -1,5 +1,6 @@ package org.testcontainers.containers; +import com.google.common.base.Strings; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.VisibleForTesting; import org.testcontainers.utility.DockerImageName; @@ -9,19 +10,21 @@ import java.util.HashSet; import java.util.Set; -import static com.google.common.base.Strings.nullToEmpty; -import static java.lang.String.format; - public class TrinoContainer extends JdbcDatabaseContainer { + static final String NAME = "trino"; + private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("trinodb/trino"); + static final String IMAGE = "trinodb/trino"; + @VisibleForTesting static final String DEFAULT_TAG = "352"; private static final int TRINO_PORT = 8080; private String username = "test"; + private String catalog = null; public TrinoContainer(final String dockerImageName) { @@ -30,7 +33,6 @@ public TrinoContainer(final String dockerImageName) { public TrinoContainer(final DockerImageName dockerImageName) { super(dockerImageName); - dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); addExposedPort(TRINO_PORT); } @@ -48,7 +50,12 @@ public String getDriverClassName() { @Override public String getJdbcUrl() { - return format("jdbc:trino://%s:%s/%s", getHost(), getMappedPort(TRINO_PORT), nullToEmpty(catalog)); + return String.format( + "jdbc:trino://%s:%s/%s", + getHost(), + getMappedPort(TRINO_PORT), + Strings.nullToEmpty(catalog) + ); } @Override diff --git a/modules/trino/src/main/java/org/testcontainers/containers/TrinoContainerProvider.java b/modules/trino/src/main/java/org/testcontainers/containers/TrinoContainerProvider.java index 233fc1b9b07..30d877c9e4a 100644 --- a/modules/trino/src/main/java/org/testcontainers/containers/TrinoContainerProvider.java +++ b/modules/trino/src/main/java/org/testcontainers/containers/TrinoContainerProvider.java @@ -6,6 +6,7 @@ * Factory for Trino containers. */ public class TrinoContainerProvider extends JdbcDatabaseContainerProvider { + @Override public boolean supports(String databaseType) { return databaseType.equals(TrinoContainer.NAME); diff --git a/modules/trino/src/test/java/org/testcontainers/containers/TrinoContainerTest.java b/modules/trino/src/test/java/org/testcontainers/containers/TrinoContainerTest.java index d7f8014ca9a..97bbdf6dedd 100644 --- a/modules/trino/src/test/java/org/testcontainers/containers/TrinoContainerTest.java +++ b/modules/trino/src/test/java/org/testcontainers/containers/TrinoContainerTest.java @@ -1,20 +1,12 @@ package org.testcontainers.containers; -import org.junit.Assert; import org.junit.Test; import org.testcontainers.TrinoTestImages; import java.sql.Connection; -import java.sql.DriverManager; import java.sql.ResultSet; -import java.sql.SQLException; import java.sql.Statement; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import static java.lang.Integer.parseInt; -import static java.lang.String.format; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -25,9 +17,11 @@ public class TrinoContainerTest { public void testSimple() throws Exception { try (TrinoContainer trino = new TrinoContainer(TrinoTestImages.TRINO_TEST_IMAGE)) { trino.start(); - try (Connection connection = trino.createConnection(); - Statement statement = connection.createStatement(); - ResultSet resultSet = statement.executeQuery("SELECT DISTINCT node_version FROM system.runtime.nodes")) { + try ( + Connection connection = trino.createConnection(); + Statement statement = connection.createStatement(); + ResultSet resultSet = statement.executeQuery("SELECT DISTINCT node_version FROM system.runtime.nodes") + ) { assertTrue("No result", resultSet.next()); assertEquals("Trino version", TrinoContainer.DEFAULT_TAG, resultSet.getString("node_version")); } @@ -38,11 +32,17 @@ public void testSimple() throws Exception { public void testSpecificVersion() throws Exception { try (TrinoContainer trino = new TrinoContainer(TrinoTestImages.TRINO_PREVIOUS_VERSION_TEST_IMAGE)) { trino.start(); - try (Connection connection = trino.createConnection(); - Statement statement = connection.createStatement(); - ResultSet resultSet = statement.executeQuery("SELECT DISTINCT node_version FROM system.runtime.nodes")) { + try ( + Connection connection = trino.createConnection(); + Statement statement = connection.createStatement(); + ResultSet resultSet = statement.executeQuery("SELECT DISTINCT node_version FROM system.runtime.nodes") + ) { assertTrue("No result", resultSet.next()); - assertEquals("Trino version", TrinoTestImages.TRINO_PREVIOUS_VERSION_TEST_IMAGE.getVersionPart(), resultSet.getString("node_version")); + assertEquals( + "Trino version", + TrinoTestImages.TRINO_PREVIOUS_VERSION_TEST_IMAGE.getVersionPart(), + resultSet.getString("node_version") + ); } } } @@ -52,9 +52,11 @@ public void testInitScript() throws Exception { try (TrinoContainer trino = new TrinoContainer(TrinoTestImages.TRINO_TEST_IMAGE)) { trino.withInitScript("initial.sql"); trino.start(); - try (Connection connection = trino.createConnection(); - Statement statement = connection.createStatement(); - ResultSet resultSet = statement.executeQuery("SELECT a FROM memory.default.test_table")) { + try ( + Connection connection = trino.createConnection(); + Statement statement = connection.createStatement(); + ResultSet resultSet = statement.executeQuery("SELECT a FROM memory.default.test_table") + ) { assertTrue("No result", resultSet.next()); assertEquals("Value", 12345678909324L, resultSet.getObject("a")); assertFalse("Too many result", resultSet.next()); diff --git a/modules/trino/src/test/java/org/testcontainers/jdbc/trino/TrinoJDBCDriverTest.java b/modules/trino/src/test/java/org/testcontainers/jdbc/trino/TrinoJDBCDriverTest.java index 8f06aef7278..9ec98c2e6d7 100644 --- a/modules/trino/src/test/java/org/testcontainers/jdbc/trino/TrinoJDBCDriverTest.java +++ b/modules/trino/src/test/java/org/testcontainers/jdbc/trino/TrinoJDBCDriverTest.java @@ -4,18 +4,18 @@ import org.junit.runners.Parameterized; import org.testcontainers.jdbc.AbstractJDBCDriverTest; +import java.util.Arrays; import java.util.EnumSet; -import static java.util.Arrays.asList; - @RunWith(Parameterized.class) public class TrinoJDBCDriverTest extends AbstractJDBCDriverTest { @Parameterized.Parameters(name = "{index} - {0}") public static Iterable data() { - return asList( - new Object[][]{ - {"jdbc:tc:trino:352://hostname/", EnumSet.of(Options.PmdKnownBroken)}, - }); + return Arrays.asList( + new Object[][] { // + { "jdbc:tc:trino:352://hostname/", EnumSet.of(Options.PmdKnownBroken) }, + } + ); } } diff --git a/modules/vault/src/main/java/org/testcontainers/vault/VaultContainer.java b/modules/vault/src/main/java/org/testcontainers/vault/VaultContainer.java index 37e78a14f95..31bb1f31d9f 100644 --- a/modules/vault/src/main/java/org/testcontainers/vault/VaultContainer.java +++ b/modules/vault/src/main/java/org/testcontainers/vault/VaultContainer.java @@ -1,6 +1,7 @@ package org.testcontainers.vault; import com.github.dockerjava.api.command.InspectContainerResponse; +import com.github.dockerjava.api.model.Capability; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.wait.strategy.Wait; import org.testcontainers.utility.DockerImageName; @@ -14,9 +15,6 @@ import java.util.function.Consumer; import java.util.stream.Collectors; -import static com.github.dockerjava.api.model.Capability.IPC_LOCK; - - /** * GenericContainer subclass for Vault specific configuration and features. The main feature is the * withSecretInVault method, where users can specify which secrets to be pre-loaded into Vault for @@ -27,11 +25,13 @@ public class VaultContainer> extends GenericContainer { private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("vault"); + private static final String DEFAULT_TAG = "1.1.3"; private static final int VAULT_PORT = 8200; private Map> secretsMap = new HashMap<>(); + private List initCommands = new ArrayList<>(); private int port = VAULT_PORT; @@ -50,13 +50,12 @@ public VaultContainer(String dockerImageName) { public VaultContainer(final DockerImageName dockerImageName) { super(dockerImageName); - dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); // Use the vault healthcheck endpoint to check for readiness, per https://www.vaultproject.io/api/system/health.html setWaitStrategy(Wait.forHttp("/v1/sys/health").forStatusCode(200)); - withCreateContainerCmdModifier(cmd -> cmd.withCapAdd(IPC_LOCK)); + withCreateContainerCmdModifier(cmd -> cmd.withCapAdd(Capability.IPC_LOCK)); withEnv("VAULT_ADDR", "http://0.0.0.0:" + port); withExposedPorts(port); } @@ -72,7 +71,12 @@ private void addSecrets() { try { this.execInContainer(buildExecCommand(secretsMap)).getStdout().contains("Success"); } catch (IOException | InterruptedException e) { - logger().error("Failed to add these secrets {} into Vault via exec command. Exception message: {}", secretsMap, e.getMessage()); + logger() + .error( + "Failed to add these secrets {} into Vault via exec command. Exception message: {}", + secretsMap, + e.getMessage() + ); } } } @@ -83,22 +87,34 @@ private String[] buildExecCommand(Map> map) { stringBuilder.append(" && vault kv put " + path); secrets.forEach(item -> stringBuilder.append(" " + item)); }); - return new String[]{"/bin/sh", "-c", stringBuilder.toString().substring(4)}; + return new String[] { "/bin/sh", "-c", stringBuilder.toString().substring(4) }; } private void runInitCommands() { if (!initCommands.isEmpty()) { - String commands = initCommands.stream() - .map(command -> "vault " + command) - .collect(Collectors.joining(" && ")); + String commands = initCommands + .stream() + .map(command -> "vault " + command) + .collect(Collectors.joining(" && ")); try { - ExecResult execResult = this.execInContainer(new String[]{"/bin/sh", "-c", commands}); + ExecResult execResult = this.execInContainer(new String[] { "/bin/sh", "-c", commands }); if (execResult.getExitCode() != 0) { - logger().error("Failed to execute these init commands {}. Exit code {}. Stdout {}. Stderr {}", - initCommands, execResult.getExitCode(), execResult.getStdout(), execResult.getStderr()); + logger() + .error( + "Failed to execute these init commands {}. Exit code {}. Stdout {}. Stderr {}", + initCommands, + execResult.getExitCode(), + execResult.getStdout(), + execResult.getStderr() + ); } } catch (IOException | InterruptedException e) { - logger().error("Failed to execute these init commands {}. Exception message: {}", initCommands, e.getMessage()); + logger() + .error( + "Failed to execute these init commands {}. Exception message: {}", + initCommands, + e.getMessage() + ); } } } @@ -166,7 +182,7 @@ public SELF withSecretInVault(String path, String firstSecret, String... remaini /** * Run initialization commands using the vault cli. - * + * * Useful for enableing more secret engines like: *

      *     .withInitCommand("secrets enable pki")
diff --git a/modules/vault/src/main/java/org/testcontainers/vault/VaultLogLevel.java b/modules/vault/src/main/java/org/testcontainers/vault/VaultLogLevel.java
index 58b22f975ff..e5f0db784a8 100644
--- a/modules/vault/src/main/java/org/testcontainers/vault/VaultLogLevel.java
+++ b/modules/vault/src/main/java/org/testcontainers/vault/VaultLogLevel.java
@@ -4,7 +4,11 @@
  * Vault preset of logging levels.
  */
 public enum VaultLogLevel {
-    Trace("trace"), Debug("debug"), Info("info"), Warn("warn"), Error("err");
+    Trace("trace"),
+    Debug("debug"),
+    Info("info"),
+    Warn("warn"),
+    Error("err");
 
     public final String config;
 
diff --git a/modules/vault/src/test/java/org/testcontainers/vault/VaultClientTest.java b/modules/vault/src/test/java/org/testcontainers/vault/VaultClientTest.java
index 4a1a800da17..cd1f1a8acea 100644
--- a/modules/vault/src/test/java/org/testcontainers/vault/VaultClientTest.java
+++ b/modules/vault/src/test/java/org/testcontainers/vault/VaultClientTest.java
@@ -10,7 +10,6 @@
 import java.util.Map;
 
 import static org.assertj.core.api.Assertions.assertThat;
-import static org.testcontainers.vault.VaultTestImages.VAULT_IMAGE;
 
 public class VaultClientTest {
 
@@ -19,10 +18,9 @@ public class VaultClientTest {
     @Test
     public void writeAndReadMultipleValues() throws VaultException {
         try (
-            VaultContainer vaultContainer = new VaultContainer<>(VAULT_IMAGE)
-                    .withVaultToken(VAULT_TOKEN)
+            VaultContainer vaultContainer = new VaultContainer<>(VaultTestImages.VAULT_IMAGE)
+                .withVaultToken(VAULT_TOKEN)
         ) {
-
             vaultContainer.start();
 
             final VaultConfig config = new VaultConfig()
@@ -37,22 +35,14 @@ public void writeAndReadMultipleValues() throws VaultException {
             secrets.put("other_value", "another world");
 
             // Write operation
-            final LogicalResponse writeResponse = vault.logical()
-                .write("secret/hello", secrets);
+            final LogicalResponse writeResponse = vault.logical().write("secret/hello", secrets);
 
             assertThat(writeResponse.getRestResponse().getStatus()).isEqualTo(200);
 
             // Read operation
-            final Map value = vault.logical()
-                .read("secret/hello")
-                .getData();
-
-
-            assertThat(value)
-                .containsEntry("value", "world")
-                .containsEntry("other_value", "another world");
+            final Map value = vault.logical().read("secret/hello").getData();
 
+            assertThat(value).containsEntry("value", "world").containsEntry("other_value", "another world");
         }
-
     }
 }
diff --git a/modules/vault/src/test/java/org/testcontainers/vault/VaultContainerTest.java b/modules/vault/src/test/java/org/testcontainers/vault/VaultContainerTest.java
index 9dee4c21416..22310bf7a23 100644
--- a/modules/vault/src/test/java/org/testcontainers/vault/VaultContainerTest.java
+++ b/modules/vault/src/test/java/org/testcontainers/vault/VaultContainerTest.java
@@ -7,7 +7,7 @@
 import java.io.IOException;
 
 import static io.restassured.RestAssured.given;
-import static org.hamcrest.CoreMatchers.*;
+import static org.hamcrest.Matchers.*;
 import static org.junit.Assert.assertThat;
 
 /**
@@ -22,24 +22,38 @@ public class VaultContainerTest {
     public static VaultContainer vaultContainer = new VaultContainer<>(VaultTestImages.VAULT_IMAGE)
         .withVaultToken(VAULT_TOKEN)
         .withSecretInVault("secret/testing1", "top_secret=password123")
-        .withSecretInVault("secret/testing2",
+        .withSecretInVault(
+            "secret/testing2",
             "secret_one=password1",
             "secret_two=password2",
             "secret_three=password3",
             "secret_three=password3",
-            "secret_four=password4")
+            "secret_four=password4"
+        )
         .withInitCommand("secrets enable transit", "write -f transit/keys/my-key");
 
     @Test
     public void readFirstSecretPathWithCli() throws IOException, InterruptedException {
-        GenericContainer.ExecResult result = vaultContainer.execInContainer("vault", "kv", "get", "-format=json", "secret/testing1");
+        GenericContainer.ExecResult result = vaultContainer.execInContainer(
+            "vault",
+            "kv",
+            "get",
+            "-format=json",
+            "secret/testing1"
+        );
         final String output = result.getStdout().replaceAll("\\r?\\n", "");
         assertThat(output, containsString("password123"));
     }
 
     @Test
     public void readSecondSecretPathWithCli() throws IOException, InterruptedException {
-        GenericContainer.ExecResult result = vaultContainer.execInContainer("vault", "kv", "get", "-format=json", "secret/testing2");
+        GenericContainer.ExecResult result = vaultContainer.execInContainer(
+            "vault",
+            "kv",
+            "get",
+            "-format=json",
+            "secret/testing2"
+        );
         final String output = result.getStdout().replaceAll("\\r?\\n", "");
         System.out.println("output = " + output);
         assertThat(output, containsString("password1"));
@@ -50,40 +64,44 @@ public void readSecondSecretPathWithCli() throws IOException, InterruptedExcepti
 
     @Test
     public void readFirstSecretPathOverHttpApi() throws InterruptedException {
-        given().
-            header("X-Vault-Token", VAULT_TOKEN).
-            when().
-            get("http://" + getHostAndPort() + "/v1/secret/data/testing1").
-            then().
-            assertThat().body("data.data.top_secret", equalTo("password123"));
+        given()
+            .header("X-Vault-Token", VAULT_TOKEN)
+            .when()
+            .get("http://" + getHostAndPort() + "/v1/secret/data/testing1")
+            .then()
+            .assertThat()
+            .body("data.data.top_secret", equalTo("password123"));
     }
 
     @Test
     public void readSecondSecretPathOverHttpApi() throws InterruptedException {
-        given().
-            header("X-Vault-Token", VAULT_TOKEN).
-            when().
-            get("http://" + getHostAndPort() + "/v1/secret/data/testing2").
-            then().
-            assertThat().body("data.data.secret_one", containsString("password1")).
-            assertThat().body("data.data.secret_two", containsString("password2")).
-            assertThat().body("data.data.secret_three", hasItem("password3")).
-            assertThat().body("data.data.secret_four", containsString("password4"));
+        given()
+            .header("X-Vault-Token", VAULT_TOKEN)
+            .when()
+            .get("http://" + getHostAndPort() + "/v1/secret/data/testing2")
+            .then()
+            .assertThat()
+            .body("data.data.secret_one", containsString("password1"))
+            .assertThat()
+            .body("data.data.secret_two", containsString("password2"))
+            .assertThat()
+            .body("data.data.secret_three", hasItem("password3"))
+            .assertThat()
+            .body("data.data.secret_four", containsString("password4"));
     }
 
     @Test
     public void readTransitKeyOverHttpApi() throws InterruptedException {
-        given().
-            header("X-Vault-Token", VAULT_TOKEN).
-            when().
-            get("http://" + getHostAndPort() + "/v1/transit/keys/my-key").
-            then().
-            assertThat().body("data.name", equalTo("my-key"));
+        given()
+            .header("X-Vault-Token", VAULT_TOKEN)
+            .when()
+            .get("http://" + getHostAndPort() + "/v1/transit/keys/my-key")
+            .then()
+            .assertThat()
+            .body("data.name", equalTo("my-key"));
     }
 
     private String getHostAndPort() {
         return vaultContainer.getHost() + ":" + vaultContainer.getMappedPort(8200);
     }
-
-
 }
diff --git a/test-support/src/main/java/org/testcontainers/testsupport/Flaky.java b/test-support/src/main/java/org/testcontainers/testsupport/Flaky.java
index cc5f6f947fa..83e18bfa161 100644
--- a/test-support/src/main/java/org/testcontainers/testsupport/Flaky.java
+++ b/test-support/src/main/java/org/testcontainers/testsupport/Flaky.java
@@ -1,19 +1,17 @@
 package org.testcontainers.testsupport;
 
+import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
 
-import static java.lang.annotation.ElementType.METHOD;
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
-
 /**
  * Annotation for test methods that should be retried in the event of failure. See {@link FlakyTestJUnit4RetryRule} for
  * more details.
  */
-@Retention(RUNTIME)
-@Target({METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.METHOD })
 public @interface Flaky {
-
     /**
      * @return a URL for a GitHub issue where this flaky test can be discussed, and where actions to resolve it can be
      * coordinated.
diff --git a/test-support/src/main/java/org/testcontainers/testsupport/FlakyTestJUnit4RetryRule.java b/test-support/src/main/java/org/testcontainers/testsupport/FlakyTestJUnit4RetryRule.java
index aa1a3ed3c5f..759e23ebbca 100644
--- a/test-support/src/main/java/org/testcontainers/testsupport/FlakyTestJUnit4RetryRule.java
+++ b/test-support/src/main/java/org/testcontainers/testsupport/FlakyTestJUnit4RetryRule.java
@@ -31,7 +31,6 @@ public class FlakyTestJUnit4RetryRule implements TestRule {
 
     @Override
     public Statement apply(Statement base, Description description) {
-
         final Flaky annotation = description.getAnnotation(Flaky.class);
 
         if (annotation == null) {
@@ -53,11 +52,13 @@ public Statement apply(Statement base, Description description) {
         try {
             reviewDate = LocalDate.parse(annotation.reviewDate());
         } catch (DateTimeParseException e) {
-            throw new IllegalArgumentException("@Flaky reviewDate could not be parsed. Please provide a date in yyyy-mm-dd format");
+            throw new IllegalArgumentException(
+                "@Flaky reviewDate could not be parsed. Please provide a date in yyyy-mm-dd format"
+            );
         }
 
         // the annotation should only have an effect before the review date, to encourage review and resolution
-        if ( LocalDate.now().isBefore(reviewDate) ) {
+        if (LocalDate.now().isBefore(reviewDate)) {
             return new RetryingStatement(base, description, maxTries);
         } else {
             return base;
@@ -65,8 +66,11 @@ public Statement apply(Statement base, Description description) {
     }
 
     private static class RetryingStatement extends Statement {
+
         private final Statement base;
+
         private final Description description;
+
         private final int maxTries;
 
         RetryingStatement(Statement base, Description description, int maxTries) {
@@ -77,7 +81,6 @@ private static class RetryingStatement extends Statement {
 
         @Override
         public void evaluate() {
-
             int attempts = 0;
             final List causes = new ArrayList<>();
 
@@ -93,7 +96,8 @@ public void evaluate() {
 
             throw new IllegalStateException(
                 "@Flaky-annotated test failed despite retries.",
-                new MultipleFailureException(causes));
+                new MultipleFailureException(causes)
+            );
         }
     }
 }
diff --git a/test-support/src/test/java/org/testcontainers/testsupport/FlakyRuleTest.java b/test-support/src/test/java/org/testcontainers/testsupport/FlakyRuleTest.java
index 07b0ffe1fa8..14a3d4c018c 100644
--- a/test-support/src/test/java/org/testcontainers/testsupport/FlakyRuleTest.java
+++ b/test-support/src/test/java/org/testcontainers/testsupport/FlakyRuleTest.java
@@ -12,11 +12,15 @@
 public class FlakyRuleTest {
 
     private static final String VALID_URL = "http://some.url/here";
+
     private static final String INVALID_URL = "";
 
     private static final String VALID_DATE_IN_FAR_FUTURE = "2063-04-05";
+
     private static final String VALID_DATE_IN_PAST = "1991-08-16";
+
     private static final String INVALID_DATE = "91-01-45";
+
     private static final int DEFAULT_TRIES = 3;
 
     private FlakyTestJUnit4RetryRule rule = new FlakyTestJUnit4RetryRule();
@@ -28,9 +32,7 @@ public void testIgnoresMethodWithoutAnnotation() throws Throwable {
         try {
             rule.apply(statement, description).evaluate();
             fail("Should not reach here");
-        } catch (Exception ignored) {
-
-        }
+        } catch (Exception ignored) {}
         assertEquals("The statement should only be invoked once, even if it throws", 1, statement.invocationCount);
     }
 
@@ -42,9 +44,7 @@ public void testRetriesMethodWithAnnotationUntilFailure() throws Throwable {
         try {
             rule.apply(statement, description).evaluate();
             fail("Should not reach here");
-        } catch (Exception ignored) {
-
-        }
+        } catch (Exception ignored) {}
         assertEquals("The statement should be invoked three times", 3, statement.invocationCount);
     }
 
@@ -56,9 +56,7 @@ public void testCustomRetryCount() throws Throwable {
         try {
             rule.apply(statement, description).evaluate();
             fail("Should not reach here");
-        } catch (Exception ignored) {
-
-        }
+        } catch (Exception ignored) {}
         assertEquals("The statement should be invoked ten times", 10, statement.invocationCount);
     }
 
@@ -70,7 +68,11 @@ public void testRetriesMethodWithAnnotationUntilSuccess() throws Throwable {
 
         rule.apply(statement, description).evaluate();
 
-        assertEquals("The statement should be invoked three times, and succeed the third time", 3, statement.invocationCount);
+        assertEquals(
+            "The statement should be invoked three times, and succeed the third time",
+            3,
+            statement.invocationCount
+        );
     }
 
     @Test
@@ -91,9 +93,7 @@ public void testTreatsExpiredAnnotationAsNoAnnotation() throws Throwable {
         try {
             rule.apply(statement, description).evaluate();
             fail("Should not reach here");
-        } catch (Exception ignored) {
-
-        }
+        } catch (Exception ignored) {}
         assertEquals("The statement should only be invoked once, even if it throws", 1, statement.invocationCount);
     }
 
@@ -104,9 +104,7 @@ public void testThrowsOnInvalidDateFormat() throws Throwable {
         try {
             rule.apply(statement, description).evaluate();
             fail("Should not reach here");
-        } catch (IllegalArgumentException ignored) {
-
-        }
+        } catch (IllegalArgumentException ignored) {}
         assertEquals("The statement should not be invoked if the annotation is invalid", 0, statement.invocationCount);
     }
 
@@ -117,14 +115,16 @@ public void testThrowsOnInvalidGitHubUrl() throws Throwable {
         try {
             rule.apply(statement, description).evaluate();
             fail("Should not reach here");
-        } catch (IllegalArgumentException ignored) {
-
-        }
+        } catch (IllegalArgumentException ignored) {}
         assertEquals("The statement should not be invoked if the annotation is invalid", 0, statement.invocationCount);
     }
 
     private Description newDescriptionWithAnnotation(String reviewDate, String gitHubUrl) {
-        return Description.createTestDescription("SomeTestClass", "someMethod", newAnnotation(reviewDate, gitHubUrl, DEFAULT_TRIES));
+        return Description.createTestDescription(
+            "SomeTestClass",
+            "someMethod",
+            newAnnotation(reviewDate, gitHubUrl, DEFAULT_TRIES)
+        );
     }
 
     private Description newDescriptionWithoutAnnotation() {
@@ -132,15 +132,27 @@ private Description newDescriptionWithoutAnnotation() {
     }
 
     private Description newDescriptionWithAnnotation() {
-        return Description.createTestDescription("SomeTestClass", "someMethod", newAnnotation(VALID_DATE_IN_FAR_FUTURE, VALID_URL, DEFAULT_TRIES));
+        return Description.createTestDescription(
+            "SomeTestClass",
+            "someMethod",
+            newAnnotation(VALID_DATE_IN_FAR_FUTURE, VALID_URL, DEFAULT_TRIES)
+        );
     }
 
     private Description newDescriptionWithAnnotationAndCustomTries(int maxTries) {
-        return Description.createTestDescription("SomeTestClass", "someMethod", newAnnotation(VALID_DATE_IN_FAR_FUTURE, VALID_URL, maxTries));
+        return Description.createTestDescription(
+            "SomeTestClass",
+            "someMethod",
+            newAnnotation(VALID_DATE_IN_FAR_FUTURE, VALID_URL, maxTries)
+        );
     }
 
     private Description newDescriptionWithExpiredAnnotation() {
-        return Description.createTestDescription("SomeTestClass", "someMethod", newAnnotation(VALID_DATE_IN_PAST, VALID_URL, DEFAULT_TRIES));
+        return Description.createTestDescription(
+            "SomeTestClass",
+            "someMethod",
+            newAnnotation(VALID_DATE_IN_PAST, VALID_URL, DEFAULT_TRIES)
+        );
     }
 
     private Flaky newAnnotation(final String reviewDate, String gitHubUrl, int tries) {
@@ -174,7 +186,9 @@ private DummyStatement newStatement(int timesToThrow) {
     }
 
     private static class DummyStatement extends Statement {
+
         int shouldThrowTimes = -1;
+
         int invocationCount = 0;
 
         @Override

From 3a82068bf2b20ff7dfce801fc5ca063698c3e0c9 Mon Sep 17 00:00:00 2001
From: Sergei Egorov 
Date: Thu, 26 May 2022 09:39:03 -0400
Subject: [PATCH 0160/3231] Update mkdocs.yml

---
 mkdocs.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/mkdocs.yml b/mkdocs.yml
index 16b2106bb3f..f6daad68155 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -102,4 +102,4 @@ nav:
           - contributing_docs.md
           - bounty.md
 extra:
-    latest_version: 1.16.3
+    latest_version: 1.17.2

From 5d3349ffd6a376f89b92e530b9b7048c826e16b2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?=
 
Date: Thu, 26 May 2022 10:13:11 -0500
Subject: [PATCH 0161/3231] Add try-with-resources to HttpWaitStrategyTest
 (#5397)

Co-authored-by: Sergei Egorov 
---
 .../strategy/AbstractWaitStrategyTest.java    |  8 +-
 .../wait/strategy/HttpWaitStrategyTest.java   | 85 ++++++++++++-------
 2 files changed, 58 insertions(+), 35 deletions(-)

diff --git a/core/src/test/java/org/testcontainers/junit/wait/strategy/AbstractWaitStrategyTest.java b/core/src/test/java/org/testcontainers/junit/wait/strategy/AbstractWaitStrategyTest.java
index de16eb0e582..530638f0f70 100644
--- a/core/src/test/java/org/testcontainers/junit/wait/strategy/AbstractWaitStrategyTest.java
+++ b/core/src/test/java/org/testcontainers/junit/wait/strategy/AbstractWaitStrategyTest.java
@@ -84,7 +84,9 @@ protected GenericContainer startContainerWithCommand(
      * @param shellCommand the shell command to execute
      */
     protected void waitUntilReadyAndSucceed(String shellCommand) {
-        waitUntilReadyAndSucceed(startContainerWithCommand(shellCommand));
+        try (GenericContainer container = startContainerWithCommand(shellCommand)) {
+            waitUntilReadyAndSucceed(container);
+        }
     }
 
     /**
@@ -94,7 +96,9 @@ protected void waitUntilReadyAndSucceed(String shellCommand) {
      * @param shellCommand the shell command to execute
      */
     protected void waitUntilReadyAndTimeout(String shellCommand) {
-        waitUntilReadyAndTimeout(startContainerWithCommand(shellCommand));
+        try (GenericContainer container = startContainerWithCommand(shellCommand)) {
+            waitUntilReadyAndTimeout(container);
+        }
     }
 
     /**
diff --git a/core/src/test/java/org/testcontainers/junit/wait/strategy/HttpWaitStrategyTest.java b/core/src/test/java/org/testcontainers/junit/wait/strategy/HttpWaitStrategyTest.java
index 7014749115e..972bf2ab66a 100644
--- a/core/src/test/java/org/testcontainers/junit/wait/strategy/HttpWaitStrategyTest.java
+++ b/core/src/test/java/org/testcontainers/junit/wait/strategy/HttpWaitStrategyTest.java
@@ -11,8 +11,8 @@
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.function.Predicate;
 
+import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.containsString;
-import static org.junit.Assert.assertThat;
 
 /**
  * Tests for {@link HttpWaitStrategy}.
@@ -45,16 +45,19 @@ public void testWaitUntilReadyWithSuccess() {
     public void testWaitUntilReadyWithSuccessWithCustomHeaders() {
         HashMap headers = new HashMap<>();
         headers.put("baz", "boo");
-        GenericContainer container = startContainerWithCommand(
-            createShellCommand("200 OK", GOOD_RESPONSE_BODY),
-            createHttpWaitStrategy(ready).withHeader("foo", "bar").withHeaders(headers)
-        );
-        waitUntilReadyAndSucceed(container);
+        try (
+            GenericContainer container = startContainerWithCommand(
+                createShellCommand("200 OK", GOOD_RESPONSE_BODY),
+                createHttpWaitStrategy(ready).withHeader("foo", "bar").withHeaders(headers)
+            )
+        ) {
+            waitUntilReadyAndSucceed(container);
 
-        String logs = container.getLogs();
+            String logs = container.getLogs();
 
-        assertThat(logs, containsString("foo: bar"));
-        assertThat(logs, containsString("baz: boo"));
+            assertThat(logs, containsString("foo: bar"));
+            assertThat(logs, containsString("baz: boo"));
+        }
     }
 
     /**
@@ -63,12 +66,14 @@ public void testWaitUntilReadyWithSuccessWithCustomHeaders() {
      */
     @Test
     public void testWaitUntilReadyWithTlsAndAllowUnsecure() {
-        waitUntilReadyAndSucceed(
-            startContainerWithCommand(
+        try (
+            GenericContainer container = startContainerWithCommand(
                 createHttpsShellCommand("200 OK", GOOD_RESPONSE_BODY, 8080),
                 createHttpWaitStrategy(ready).usingTls().allowInsecure()
             )
-        );
+        ) {
+            waitUntilReadyAndSucceed(container);
+        }
     }
 
     /**
@@ -77,12 +82,14 @@ public void testWaitUntilReadyWithTlsAndAllowUnsecure() {
      */
     @Test
     public void testWaitUntilReadyWithUnauthorizedWithLambda() {
-        waitUntilReadyAndSucceed(
-            startContainerWithCommand(
+        try (
+            GenericContainer container = startContainerWithCommand(
                 createShellCommand("401 UNAUTHORIZED", GOOD_RESPONSE_BODY),
                 createHttpWaitStrategy(ready).forStatusCodeMatching(it -> it >= 200 && it < 300 || it == 401)
             )
-        );
+        ) {
+            waitUntilReadyAndSucceed(container);
+        }
     }
 
     /**
@@ -91,12 +98,14 @@ public void testWaitUntilReadyWithUnauthorizedWithLambda() {
      */
     @Test
     public void testWaitUntilReadyWithManyStatusCodes() {
-        waitUntilReadyAndSucceed(
-            startContainerWithCommand(
+        try (
+            GenericContainer container = startContainerWithCommand(
                 createShellCommand("401 UNAUTHORIZED", GOOD_RESPONSE_BODY),
                 createHttpWaitStrategy(ready).forStatusCode(300).forStatusCode(401).forStatusCode(500)
             )
-        );
+        ) {
+            waitUntilReadyAndSucceed(container);
+        }
     }
 
     /**
@@ -106,15 +115,17 @@ public void testWaitUntilReadyWithManyStatusCodes() {
      */
     @Test
     public void testWaitUntilReadyWithManyStatusCodesAndLambda() {
-        waitUntilReadyAndSucceed(
-            startContainerWithCommand(
+        try (
+            GenericContainer container = startContainerWithCommand(
                 createShellCommand("401 UNAUTHORIZED", GOOD_RESPONSE_BODY),
                 createHttpWaitStrategy(ready)
                     .forStatusCode(300)
                     .forStatusCode(500)
                     .forStatusCodeMatching(it -> it == 401)
             )
-        );
+        ) {
+            waitUntilReadyAndSucceed(container);
+        }
     }
 
     /**
@@ -124,12 +135,14 @@ public void testWaitUntilReadyWithManyStatusCodesAndLambda() {
      */
     @Test
     public void testWaitUntilReadyWithTimeoutAndWithManyStatusCodesAndLambda() {
-        waitUntilReadyAndTimeout(
-            startContainerWithCommand(
+        try (
+            GenericContainer container = startContainerWithCommand(
                 createShellCommand("401 UNAUTHORIZED", GOOD_RESPONSE_BODY),
                 createHttpWaitStrategy(ready).forStatusCode(300).forStatusCodeMatching(it -> it == 500)
             )
-        );
+        ) {
+            waitUntilReadyAndTimeout(container);
+        }
     }
 
     /**
@@ -141,12 +154,14 @@ public void testWaitUntilReadyWithTimeoutAndWithManyStatusCodesAndLambda() {
      */
     @Test
     public void testWaitUntilReadyWithTimeoutAndWithLambdaShouldNotMatchOk() {
-        waitUntilReadyAndTimeout(
-            startContainerWithCommand(
+        try (
+            GenericContainer container = startContainerWithCommand(
                 createShellCommand("200 OK", GOOD_RESPONSE_BODY),
                 createHttpWaitStrategy(ready).forStatusCodeMatching(it -> it >= 300)
             )
-        );
+        ) {
+            waitUntilReadyAndTimeout(container);
+        }
     }
 
     /**
@@ -172,26 +187,30 @@ public void testWaitUntilReadyWithTimeoutAndBadResponseBody() {
      */
     @Test
     public void testWaitUntilReadyWithSpecificPort() {
-        waitUntilReadyAndSucceed(
-            startContainerWithCommand(
+        try (
+            GenericContainer container = startContainerWithCommand(
                 createShellCommand("200 OK", GOOD_RESPONSE_BODY, 9090),
                 createHttpWaitStrategy(ready).forPort(9090),
                 7070,
                 8080,
                 9090
             )
-        );
+        ) {
+            waitUntilReadyAndSucceed(container);
+        }
     }
 
     @Test
     public void testWaitUntilReadyWithTimoutCausedByReadTimeout() {
-        waitUntilReadyAndTimeout(
-            startContainerWithCommand(
+        try (
+            GenericContainer container = startContainerWithCommand(
                 createShellCommand("0 Connection Refused", GOOD_RESPONSE_BODY, 9090),
                 createHttpWaitStrategy(ready).forPort(9090).withReadTimeout(Duration.ofMillis(1)),
                 9090
             )
-        );
+        ) {
+            waitUntilReadyAndTimeout(container);
+        }
     }
 
     /**

From aeab7a55c4564e7199efe531705f4c9268839f43 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 1 Jun 2022 22:00:40 +0000
Subject: [PATCH 0162/3231] Bump tomcat-jdbc from 10.0.20 to 10.0.21 in
 /modules/jdbc-test

Bumps tomcat-jdbc from 10.0.20 to 10.0.21.

---
updated-dependencies:
- dependency-name: org.apache.tomcat:tomcat-jdbc
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] 
---
 modules/jdbc-test/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/jdbc-test/build.gradle b/modules/jdbc-test/build.gradle
index d77465c5616..5674f235c7f 100644
--- a/modules/jdbc-test/build.gradle
+++ b/modules/jdbc-test/build.gradle
@@ -14,7 +14,7 @@ dependencies {
         exclude(group: "net.java.dev.jna", module: "jna")
     }
 
-    api 'org.apache.tomcat:tomcat-jdbc:10.0.20'
+    api 'org.apache.tomcat:tomcat-jdbc:10.0.21'
     api 'org.vibur:vibur-dbcp:25.0'
     api 'mysql:mysql-connector-java:8.0.29'
 }

From af64f653ecced7237ee1035504ebaa8d07b430de Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 1 Jun 2022 22:00:42 +0000
Subject: [PATCH 0163/3231] Bump neo4j from 3.5.32 to 3.5.33 in /modules/neo4j

Bumps [neo4j](https://github.com/neo4j/neo4j) from 3.5.32 to 3.5.33.
- [Release notes](https://github.com/neo4j/neo4j/releases)
- [Commits](https://github.com/neo4j/neo4j/compare/3.5.32...3.5.33)

---
updated-dependencies:
- dependency-name: org.neo4j:neo4j
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] 
---
 modules/neo4j/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/neo4j/build.gradle b/modules/neo4j/build.gradle
index efba3dd0db5..da996db865f 100644
--- a/modules/neo4j/build.gradle
+++ b/modules/neo4j/build.gradle
@@ -29,7 +29,7 @@ task customNeo4jPluginJar(type: Jar) {
 processTestResources.dependsOn customNeo4jPluginJar
 
 dependencies {
-    customNeo4jPluginCompileOnly "org.neo4j:neo4j:3.5.32"
+    customNeo4jPluginCompileOnly "org.neo4j:neo4j:3.5.33"
 
     api project(":testcontainers")
 

From 443f9d250456be62802b7d8c11bdf8f77e37dac9 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 1 Jun 2022 22:00:43 +0000
Subject: [PATCH 0164/3231] Bump assertj-core from 3.22.0 to 3.23.1 in
 /modules/jdbc

Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.22.0 to 3.23.1.
- [Release notes](https://github.com/assertj/assertj-core/releases)
- [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.22.0...assertj-core-3.23.1)

---
updated-dependencies:
- dependency-name: org.assertj:assertj-core
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] 
---
 modules/jdbc/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/jdbc/build.gradle b/modules/jdbc/build.gradle
index 84b927ac4a9..54023b6bed4 100644
--- a/modules/jdbc/build.gradle
+++ b/modules/jdbc/build.gradle
@@ -11,5 +11,5 @@ dependencies {
     testImplementation 'com.zaxxer:HikariCP-java6:2.3.13'
     testImplementation 'com.googlecode.junit-toolbox:junit-toolbox:2.4'
     testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2'
-    testImplementation 'org.assertj:assertj-core:3.22.0'
+    testImplementation 'org.assertj:assertj-core:3.23.1'
 }

From a615e261b90cc2d1f1e177f593b529865d5fcac8 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 1 Jun 2022 22:00:43 +0000
Subject: [PATCH 0165/3231] Bump mockito-core from 4.3.1 to 4.6.0 in /core

Bumps [mockito-core](https://github.com/mockito/mockito) from 4.3.1 to 4.6.0.
- [Release notes](https://github.com/mockito/mockito/releases)
- [Commits](https://github.com/mockito/mockito/compare/v4.3.1...v4.6.0)

---
updated-dependencies:
- dependency-name: org.mockito:mockito-core
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] 
---
 core/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/core/build.gradle b/core/build.gradle
index efb08991f8d..3b148503c51 100644
--- a/core/build.gradle
+++ b/core/build.gradle
@@ -95,7 +95,7 @@ dependencies {
     testImplementation 'com.rabbitmq:amqp-client:5.14.2'
     testImplementation 'org.mongodb:mongo-java-driver:3.12.10'
 
-    testImplementation ('org.mockito:mockito-core:4.3.1') {
+    testImplementation ('org.mockito:mockito-core:4.6.0') {
         exclude(module: 'hamcrest-core')
     }
     // Synthetic JAR used for MountableFileTest and DirectoryTarResourceTest

From 84f5d0cebb278b058d3b00bc3e2f3f6008ab154d Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 1 Jun 2022 22:00:43 +0000
Subject: [PATCH 0166/3231] Bump postgresql from 42.3.4 to 42.3.6 in
 /modules/postgresql

Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.3.4 to 42.3.6.
- [Release notes](https://github.com/pgjdbc/pgjdbc/releases)
- [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md)
- [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.3.4...REL42.3.6)

---
updated-dependencies:
- dependency-name: org.postgresql:postgresql
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] 
---
 modules/postgresql/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/postgresql/build.gradle b/modules/postgresql/build.gradle
index f2c50c30754..d849ad408df 100644
--- a/modules/postgresql/build.gradle
+++ b/modules/postgresql/build.gradle
@@ -11,7 +11,7 @@ dependencies {
 
     testImplementation project(':jdbc-test')
     testImplementation project(':test-support')
-    testImplementation 'org.postgresql:postgresql:42.3.4'
+    testImplementation 'org.postgresql:postgresql:42.3.6'
 
     testImplementation testFixtures(project(':r2dbc'))
     testImplementation 'io.r2dbc:r2dbc-postgresql:0.8.11.RELEASE'

From c36cdcad20c666f83b42de1ef9b7e0bb2950d5b7 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 1 Jun 2022 22:00:43 +0000
Subject: [PATCH 0167/3231] Bump mariadb-java-client from 3.0.3 to 3.0.5 in
 /modules/mariadb

Bumps [mariadb-java-client](https://github.com/mariadb-corporation/mariadb-connector-j) from 3.0.3 to 3.0.5.
- [Release notes](https://github.com/mariadb-corporation/mariadb-connector-j/releases)
- [Changelog](https://github.com/mariadb-corporation/mariadb-connector-j/blob/master/CHANGELOG.md)
- [Commits](https://github.com/mariadb-corporation/mariadb-connector-j/compare/3.0.3...3.0.5)

---
updated-dependencies:
- dependency-name: org.mariadb.jdbc:mariadb-java-client
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] 
---
 modules/mariadb/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/mariadb/build.gradle b/modules/mariadb/build.gradle
index 8d65ed00deb..fd534915ba1 100644
--- a/modules/mariadb/build.gradle
+++ b/modules/mariadb/build.gradle
@@ -10,7 +10,7 @@ dependencies {
     compileOnly 'org.mariadb:r2dbc-mariadb:1.0.3'
 
     testImplementation project(':jdbc-test')
-    testImplementation 'org.mariadb.jdbc:mariadb-java-client:3.0.3'
+    testImplementation 'org.mariadb.jdbc:mariadb-java-client:3.0.5'
 
     testImplementation testFixtures(project(':r2dbc'))
     testImplementation 'org.mariadb:r2dbc-mariadb:1.0.2'

From fab88d0d60f90fa9e08f2f6c2f083b3323dbc145 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 1 Jun 2022 22:00:44 +0000
Subject: [PATCH 0168/3231] Bump assertj-core from 3.22.0 to 3.23.1 in
 /modules/neo4j

Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.22.0 to 3.23.1.
- [Release notes](https://github.com/assertj/assertj-core/releases)
- [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.22.0...assertj-core-3.23.1)

---
updated-dependencies:
- dependency-name: org.assertj:assertj-core
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] 
---
 modules/neo4j/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/neo4j/build.gradle b/modules/neo4j/build.gradle
index efba3dd0db5..555d55e3a72 100644
--- a/modules/neo4j/build.gradle
+++ b/modules/neo4j/build.gradle
@@ -34,5 +34,5 @@ dependencies {
     api project(":testcontainers")
 
     testImplementation 'org.neo4j.driver:neo4j-java-driver:4.4.5'
-    testImplementation 'org.assertj:assertj-core:3.22.0'
+    testImplementation 'org.assertj:assertj-core:3.23.1'
 }

From af16f640661406c78c2a591f6dcc80ba981203d6 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 1 Jun 2022 22:00:44 +0000
Subject: [PATCH 0169/3231] Bump assertj-core from 3.22.0 to 3.23.1 in
 /modules/kafka

Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.22.0 to 3.23.1.
- [Release notes](https://github.com/assertj/assertj-core/releases)
- [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.22.0...assertj-core-3.23.1)

---
updated-dependencies:
- dependency-name: org.assertj:assertj-core
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] 
---
 modules/kafka/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/kafka/build.gradle b/modules/kafka/build.gradle
index b9157d9d539..a1022ae1720 100644
--- a/modules/kafka/build.gradle
+++ b/modules/kafka/build.gradle
@@ -13,6 +13,6 @@ dependencies {
     api project(':testcontainers')
 
     testImplementation 'org.apache.kafka:kafka-clients:3.0.0'
-    testImplementation 'org.assertj:assertj-core:3.22.0'
+    testImplementation 'org.assertj:assertj-core:3.23.1'
     testImplementation 'com.google.guava:guava:23.0'
 }

From 670f523cee9bdcc1bdf258a83905071ea0b5a417 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 1 Jun 2022 22:00:44 +0000
Subject: [PATCH 0170/3231] Bump assertj-core from 3.22.0 to 3.23.1 in
 /modules/pulsar

Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.22.0 to 3.23.1.
- [Release notes](https://github.com/assertj/assertj-core/releases)
- [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.22.0...assertj-core-3.23.1)

---
updated-dependencies:
- dependency-name: org.assertj:assertj-core
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] 
---
 modules/pulsar/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/pulsar/build.gradle b/modules/pulsar/build.gradle
index cf11a36b411..69c63cc3033 100644
--- a/modules/pulsar/build.gradle
+++ b/modules/pulsar/build.gradle
@@ -4,6 +4,6 @@ dependencies {
     api project(':testcontainers')
 
     testImplementation group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.7.4'
-    testImplementation group: 'org.assertj', name: 'assertj-core', version: '3.22.0'
+    testImplementation group: 'org.assertj', name: 'assertj-core', version: '3.23.1'
     testImplementation group: 'org.apache.pulsar', name: 'pulsar-client-admin', version: '2.7.4'
 }

From 43feb287518abb1d777cb10a46d839f03fd9e10b Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 1 Jun 2022 22:00:45 +0000
Subject: [PATCH 0171/3231] Bump tomcat-jdbc from 10.0.20 to 10.0.21 in
 /modules/jdbc

Bumps tomcat-jdbc from 10.0.20 to 10.0.21.

---
updated-dependencies:
- dependency-name: org.apache.tomcat:tomcat-jdbc
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] 
---
 modules/jdbc/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/jdbc/build.gradle b/modules/jdbc/build.gradle
index 84b927ac4a9..3c9865b5246 100644
--- a/modules/jdbc/build.gradle
+++ b/modules/jdbc/build.gradle
@@ -7,7 +7,7 @@ dependencies {
     compileOnly 'org.jetbrains:annotations:23.0.0'
     testImplementation 'commons-dbutils:commons-dbutils:1.7'
     testImplementation 'org.vibur:vibur-dbcp:25.0'
-    testImplementation 'org.apache.tomcat:tomcat-jdbc:10.0.20'
+    testImplementation 'org.apache.tomcat:tomcat-jdbc:10.0.21'
     testImplementation 'com.zaxxer:HikariCP-java6:2.3.13'
     testImplementation 'com.googlecode.junit-toolbox:junit-toolbox:2.4'
     testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2'

From e267d78335346416aaf065d04b9c5a39757d78e9 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 1 Jun 2022 22:00:46 +0000
Subject: [PATCH 0172/3231] Bump aws-java-sdk-s3 from 1.12.191 to 1.12.231 in
 /modules/localstack

Bumps [aws-java-sdk-s3](https://github.com/aws/aws-sdk-java) from 1.12.191 to 1.12.231.
- [Release notes](https://github.com/aws/aws-sdk-java/releases)
- [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md)
- [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.191...1.12.231)

---
updated-dependencies:
- dependency-name: com.amazonaws:aws-java-sdk-s3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] 
---
 modules/localstack/build.gradle | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle
index 496b4e23983..8a12cd75327 100644
--- a/modules/localstack/build.gradle
+++ b/modules/localstack/build.gradle
@@ -3,8 +3,8 @@ description = "Testcontainers :: Localstack"
 dependencies {
     api project(':testcontainers')
 
-    compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.191'
-    testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.169'
+    compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.231'
+    testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.231'
     testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.210'
     testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.169'
     testImplementation 'software.amazon.awssdk:s3:2.17.181'

From a67d40a1e1fa8447b021ad6e98350fe4edc898b1 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 1 Jun 2022 22:00:47 +0000
Subject: [PATCH 0173/3231] Bump aws-java-sdk-dynamodb in /modules/dynalite

Bumps [aws-java-sdk-dynamodb](https://github.com/aws/aws-sdk-java) from 1.12.210 to 1.12.232.
- [Release notes](https://github.com/aws/aws-sdk-java/releases)
- [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md)
- [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.210...1.12.232)

---
updated-dependencies:
- dependency-name: com.amazonaws:aws-java-sdk-dynamodb
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] 
---
 modules/dynalite/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/dynalite/build.gradle b/modules/dynalite/build.gradle
index 6dabdec119b..09cb51e10a4 100644
--- a/modules/dynalite/build.gradle
+++ b/modules/dynalite/build.gradle
@@ -3,7 +3,7 @@ description = "Testcontainers :: Dynalite"
 dependencies {
     api project(':testcontainers')
 
-    compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.12.210'
+    compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.12.232'
     testImplementation 'com.amazonaws:aws-java-sdk-dynamodb:1.12.210'
 
     testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2'

From 7fb15fbac3912e999aedd63431461142c61a0fb1 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 1 Jun 2022 22:00:48 +0000
Subject: [PATCH 0174/3231] Bump assertj-core from 3.22.0 to 3.23.1 in
 /modules/junit-jupiter

Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.22.0 to 3.23.1.
- [Release notes](https://github.com/assertj/assertj-core/releases)
- [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.22.0...assertj-core-3.23.1)

---
updated-dependencies:
- dependency-name: org.assertj:assertj-core
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] 
---
 modules/junit-jupiter/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle
index 6685eddf15a..7efd4fe3afa 100644
--- a/modules/junit-jupiter/build.gradle
+++ b/modules/junit-jupiter/build.gradle
@@ -12,7 +12,7 @@ dependencies {
     testImplementation ('org.mockito:mockito-core:4.4.0') {
         exclude(module: 'hamcrest-core')
     }
-    testImplementation 'org.assertj:assertj-core:3.22.0'
+    testImplementation 'org.assertj:assertj-core:3.23.1'
     testImplementation 'org.junit.jupiter:junit-jupiter-params:5.8.2'
 
     testRuntimeOnly 'org.postgresql:postgresql:42.3.4'

From 6f33590747b80490d0f7c873fa366e559e9e9ffc Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 1 Jun 2022 22:00:49 +0000
Subject: [PATCH 0175/3231] Bump assertj-core from 3.22.0 to 3.23.1 in
 /modules/vault

Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.22.0 to 3.23.1.
- [Release notes](https://github.com/assertj/assertj-core/releases)
- [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.22.0...assertj-core-3.23.1)

---
updated-dependencies:
- dependency-name: org.assertj:assertj-core
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] 
---
 modules/vault/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/vault/build.gradle b/modules/vault/build.gradle
index 80b416ce235..f6e94f2139d 100644
--- a/modules/vault/build.gradle
+++ b/modules/vault/build.gradle
@@ -5,6 +5,6 @@ dependencies {
 
     testImplementation 'com.bettercloud:vault-java-driver:5.1.0'
     testImplementation 'io.rest-assured:rest-assured:5.0.1'
-    testImplementation 'org.assertj:assertj-core:3.22.0'
+    testImplementation 'org.assertj:assertj-core:3.23.1'
 
 }

From 0f2f843d5b205a8dbda4be9ca5840722bf9777f8 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 1 Jun 2022 22:00:49 +0000
Subject: [PATCH 0176/3231] Bump common-custom-user-data-gradle-plugin in
 /examples

Bumps common-custom-user-data-gradle-plugin from 1.6.2 to 1.7.

---
updated-dependencies:
- dependency-name: com.gradle:common-custom-user-data-gradle-plugin
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] 
---
 examples/settings.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/examples/settings.gradle b/examples/settings.gradle
index ceb39498854..2046a7d282e 100644
--- a/examples/settings.gradle
+++ b/examples/settings.gradle
@@ -7,7 +7,7 @@ buildscript {
     dependencies {
         classpath "gradle.plugin.ch.myniva.gradle:s3-build-cache:0.10.0"
         classpath "com.gradle.enterprise:com.gradle.enterprise.gradle.plugin:3.10"
-        classpath "com.gradle:common-custom-user-data-gradle-plugin:1.6.2"
+        classpath "com.gradle:common-custom-user-data-gradle-plugin:1.7"
     }
 }
 

From 761e70a06761036246647dd0a1313d7f2ff9d1bc Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 1 Jun 2022 22:00:52 +0000
Subject: [PATCH 0177/3231] Bump assertj-core from 3.22.0 to 3.23.1 in /core

Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.22.0 to 3.23.1.
- [Release notes](https://github.com/assertj/assertj-core/releases)
- [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.22.0...assertj-core-3.23.1)

---
updated-dependencies:
- dependency-name: org.assertj:assertj-core
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] 
---
 core/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/core/build.gradle b/core/build.gradle
index efb08991f8d..3b870a59e5c 100644
--- a/core/build.gradle
+++ b/core/build.gradle
@@ -102,7 +102,7 @@ dependencies {
     testImplementation files('testlib/repo/fakejar/fakejar/0/fakejar-0.jar')
 
     testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2'
-    testImplementation 'org.assertj:assertj-core:3.22.0'
+    testImplementation 'org.assertj:assertj-core:3.23.1'
     testImplementation project(':test-support')
 
     jarFileTestCompileOnly "org.projectlombok:lombok:${lombok.version}"

From b8466947c2451bebc2760397293e57334f101697 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 1 Jun 2022 22:00:52 +0000
Subject: [PATCH 0178/3231] Bump postgresql from 42.3.1 to 42.3.6 in
 /modules/spock

Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.3.1 to 42.3.6.
- [Release notes](https://github.com/pgjdbc/pgjdbc/releases)
- [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md)
- [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.3.1...REL42.3.6)

---
updated-dependencies:
- dependency-name: org.postgresql:postgresql
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] 
---
 modules/spock/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle
index 396c0071042..e609733d398 100644
--- a/modules/spock/build.gradle
+++ b/modules/spock/build.gradle
@@ -15,7 +15,7 @@ dependencies {
     testImplementation 'com.zaxxer:HikariCP:4.0.3'
     testImplementation 'org.apache.httpcomponents:httpclient:4.5.13'
 
-    testRuntimeOnly 'org.postgresql:postgresql:42.3.1'
+    testRuntimeOnly 'org.postgresql:postgresql:42.3.6'
     testRuntimeOnly 'mysql:mysql-connector-java:8.0.29'
 
     testCompileClasspath 'org.jetbrains:annotations:23.0.0'

From a76a9ca56f20070a0bf96b73dc155eec6e07c817 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 1 Jun 2022 22:00:53 +0000
Subject: [PATCH 0179/3231] Bump jedis from 4.2.2 to 4.2.3 in /core

Bumps [jedis](https://github.com/redis/jedis) from 4.2.2 to 4.2.3.
- [Release notes](https://github.com/redis/jedis/releases)
- [Commits](https://github.com/redis/jedis/compare/v4.2.2...v4.2.3)

---
updated-dependencies:
- dependency-name: redis.clients:jedis
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] 
---
 core/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/core/build.gradle b/core/build.gradle
index efb08991f8d..833831f0aed 100644
--- a/core/build.gradle
+++ b/core/build.gradle
@@ -91,7 +91,7 @@ dependencies {
     shaded 'org.zeroturnaround:zt-exec:1.12'
 
     testImplementation 'org.apache.httpcomponents:httpclient:4.5.9'
-    testImplementation 'redis.clients:jedis:4.2.2'
+    testImplementation 'redis.clients:jedis:4.2.3'
     testImplementation 'com.rabbitmq:amqp-client:5.14.2'
     testImplementation 'org.mongodb:mongo-java-driver:3.12.10'
 

From e97b0c2073f1ec59ccf89890442ed50047fbf7f2 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 1 Jun 2022 22:00:56 +0000
Subject: [PATCH 0180/3231] Bump postgresql from 42.3.4 to 42.3.6 in
 /modules/junit-jupiter

Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.3.4 to 42.3.6.
- [Release notes](https://github.com/pgjdbc/pgjdbc/releases)
- [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md)
- [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.3.4...REL42.3.6)

---
updated-dependencies:
- dependency-name: org.postgresql:postgresql
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] 
---
 modules/junit-jupiter/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle
index 6685eddf15a..0b1a21a3970 100644
--- a/modules/junit-jupiter/build.gradle
+++ b/modules/junit-jupiter/build.gradle
@@ -15,7 +15,7 @@ dependencies {
     testImplementation 'org.assertj:assertj-core:3.22.0'
     testImplementation 'org.junit.jupiter:junit-jupiter-params:5.8.2'
 
-    testRuntimeOnly 'org.postgresql:postgresql:42.3.4'
+    testRuntimeOnly 'org.postgresql:postgresql:42.3.6'
     testRuntimeOnly 'mysql:mysql-connector-java:8.0.27'
     testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.2'
 }

From 553405f0c0e28ade15637578922f0bd6f7936ed0 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 1 Jun 2022 22:00:57 +0000
Subject: [PATCH 0181/3231] Bump transport from 7.17.3 to 7.17.4 in
 /modules/elasticsearch

Bumps [transport](https://github.com/elastic/elasticsearch) from 7.17.3 to 7.17.4.
- [Release notes](https://github.com/elastic/elasticsearch/releases)
- [Changelog](https://github.com/elastic/elasticsearch/blob/master/CHANGELOG.md)
- [Commits](https://github.com/elastic/elasticsearch/compare/v7.17.3...v7.17.4)

---
updated-dependencies:
- dependency-name: org.elasticsearch.client:transport
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] 
---
 modules/elasticsearch/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/elasticsearch/build.gradle b/modules/elasticsearch/build.gradle
index 3d8f7bff12d..4bff8106f0b 100644
--- a/modules/elasticsearch/build.gradle
+++ b/modules/elasticsearch/build.gradle
@@ -3,6 +3,6 @@ description = "TestContainers :: elasticsearch"
 dependencies {
     api project(':testcontainers')
     testImplementation "org.elasticsearch.client:elasticsearch-rest-client:7.16.2"
-    testImplementation "org.elasticsearch.client:transport:7.17.3"
+    testImplementation "org.elasticsearch.client:transport:7.17.4"
     testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2'
 }

From dd539ed0409658cd278ee26f4c795e472bbd0c67 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 1 Jun 2022 22:00:57 +0000
Subject: [PATCH 0182/3231] Bump assertj-core from 3.22.0 to 3.23.1 in
 /modules/database-commons

Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.22.0 to 3.23.1.
- [Release notes](https://github.com/assertj/assertj-core/releases)
- [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.22.0...assertj-core-3.23.1)

---
updated-dependencies:
- dependency-name: org.assertj:assertj-core
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] 
---
 modules/database-commons/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/database-commons/build.gradle b/modules/database-commons/build.gradle
index 7a201d772fb..33a6d1e9826 100644
--- a/modules/database-commons/build.gradle
+++ b/modules/database-commons/build.gradle
@@ -3,5 +3,5 @@ description = "Testcontainers :: Database-Commons"
 dependencies {
     api project(':testcontainers')
 
-    testImplementation 'org.assertj:assertj-core:3.22.0'
+    testImplementation 'org.assertj:assertj-core:3.23.1'
 }

From 4f6fea6c5feaf1c5c90ecbec8b823aa09fec708b Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 1 Jun 2022 22:00:57 +0000
Subject: [PATCH 0183/3231] Bump aws-java-sdk-logs from 1.12.169 to 1.12.232 in
 /modules/localstack

Bumps [aws-java-sdk-logs](https://github.com/aws/aws-sdk-java) from 1.12.169 to 1.12.232.
- [Release notes](https://github.com/aws/aws-sdk-java/releases)
- [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md)
- [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.169...1.12.232)

---
updated-dependencies:
- dependency-name: com.amazonaws:aws-java-sdk-logs
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] 
---
 modules/localstack/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle
index 496b4e23983..6917199b470 100644
--- a/modules/localstack/build.gradle
+++ b/modules/localstack/build.gradle
@@ -6,7 +6,7 @@ dependencies {
     compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.191'
     testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.169'
     testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.210'
-    testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.169'
+    testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.232'
     testImplementation 'software.amazon.awssdk:s3:2.17.181'
     testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2'
 }

From 62d34bd7899fadbc9c4ba2874d5cdc2e2f0ef7f5 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 1 Jun 2022 22:01:01 +0000
Subject: [PATCH 0184/3231] Bump postgresql from 42.3.3 to 42.3.6 in /examples

Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.3.3 to 42.3.6.
- [Release notes](https://github.com/pgjdbc/pgjdbc/releases)
- [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md)
- [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.3.3...REL42.3.6)

---
updated-dependencies:
- dependency-name: org.postgresql:postgresql
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] 
---
 examples/linked-container/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/examples/linked-container/build.gradle b/examples/linked-container/build.gradle
index c61ce36e473..05c5b241b51 100644
--- a/examples/linked-container/build.gradle
+++ b/examples/linked-container/build.gradle
@@ -9,7 +9,7 @@ dependencies {
     compileOnly 'org.slf4j:slf4j-api:1.7.32'
     implementation 'com.squareup.okhttp3:okhttp:4.9.3'
     implementation 'org.json:json:20211205'
-    testImplementation 'org.postgresql:postgresql:42.3.3'
+    testImplementation 'org.postgresql:postgresql:42.3.6'
     testImplementation 'ch.qos.logback:logback-classic:1.2.11'
     testImplementation 'org.testcontainers:postgresql'
 }

From ce9b09a1fdbc28509d53485371089339416ed44c Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 1 Jun 2022 22:01:25 +0000
Subject: [PATCH 0185/3231] Bump peter-evans/create-pull-request from 4.0.2 to
 4.0.3

Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 4.0.2 to 4.0.3.
- [Release notes](https://github.com/peter-evans/create-pull-request/releases)
- [Commits](https://github.com/peter-evans/create-pull-request/compare/bd72e1b7922d417764d27d30768117ad7da78a0e...f094b77505fb89581e68a1163fbd2fffece39da1)

---
updated-dependencies:
- dependency-name: peter-evans/create-pull-request
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] 
---
 .github/workflows/update-docs-version.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/update-docs-version.yml b/.github/workflows/update-docs-version.yml
index b5d672c092d..b4d4d250346 100644
--- a/.github/workflows/update-docs-version.yml
+++ b/.github/workflows/update-docs-version.yml
@@ -17,7 +17,7 @@ jobs:
           sed -i "s/latest_version: .*/latest_version: ${GITHUB_REF##*/}/g" mkdocs.yml
           git diff
       - name: Create Pull Request
-        uses: peter-evans/create-pull-request@bd72e1b7922d417764d27d30768117ad7da78a0e # v3.10.1
+        uses: peter-evans/create-pull-request@f094b77505fb89581e68a1163fbd2fffece39da1 # v3.10.1
         with:
           title: Update docs version to ${GITHUB_REF##*/}
           body: |

From 76381504e6c5cf4083ec1e42a1ffd4f241820793 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 1 Jun 2022 22:01:30 +0000
Subject: [PATCH 0186/3231] Bump actions/cache from 3.0.2 to 3.0.3

Bumps [actions/cache](https://github.com/actions/cache) from 3.0.2 to 3.0.3.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/v3.0.2...v3.0.3)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] 
---
 .github/workflows/ci-examples.yml | 4 ++--
 .github/workflows/ci.yml          | 4 ++--
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/.github/workflows/ci-examples.yml b/.github/workflows/ci-examples.yml
index 90b493df481..9a7b314c67f 100644
--- a/.github/workflows/ci-examples.yml
+++ b/.github/workflows/ci-examples.yml
@@ -22,7 +22,7 @@ jobs:
           java-version: '8.0.302'
           distribution: temurin
       - name: Cache Gradle Home files
-        uses: actions/cache@v3.0.2
+        uses: actions/cache@v3.0.3
         continue-on-error: true
         with:
           path: ~/.gradle/caches
@@ -50,7 +50,7 @@ jobs:
           java-version: '8.0.302'
           distribution: temurin
       - name: Cache Gradle Home files
-        uses: actions/cache@v3.0.2
+        uses: actions/cache@v3.0.3
         continue-on-error: true
         with:
           path: ~/.gradle/caches
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 0c0e5eaf601..56bb4e0799d 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -30,7 +30,7 @@ jobs:
           java-version: '8.0.302'
           distribution: temurin
       - name: Cache Gradle Home files
-        uses: actions/cache@v3.0.2
+        uses: actions/cache@v3.0.3
         with:
           path: ~/.gradle/caches
           key: ${{ runner.os }}-gradle-home-testmatrix-${{ hashFiles('**/*.gradle') }}
@@ -56,7 +56,7 @@ jobs:
           java-version: '8.0.302'
           distribution: temurin
       - name: Cache Gradle Home files
-        uses: actions/cache@v3.0.2
+        uses: actions/cache@v3.0.3
         continue-on-error: true
         with:
           path: ~/.gradle/caches

From c3e8979012144fa559629f11ad8bad9d742839d6 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 1 Jun 2022 22:01:35 +0000
Subject: [PATCH 0187/3231] Bump release-drafter/release-drafter from 5.19.0 to
 5.20.0

Bumps [release-drafter/release-drafter](https://github.com/release-drafter/release-drafter) from 5.19.0 to 5.20.0.
- [Release notes](https://github.com/release-drafter/release-drafter/releases)
- [Commits](https://github.com/release-drafter/release-drafter/compare/e9ee02fbac03d922bac6212003695e8358dd88b0...ac463ffd9cc4c6ad5682af93dc3e3591c4657ee3)

---
updated-dependencies:
- dependency-name: release-drafter/release-drafter
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] 
---
 .github/workflows/release-drafter.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml
index 839fb43df4d..132b5ffd0a5 100644
--- a/.github/workflows/release-drafter.yml
+++ b/.github/workflows/release-drafter.yml
@@ -12,6 +12,6 @@ jobs:
     if: github.repository == 'testcontainers/testcontainers-java'
     runs-on: ubuntu-latest
     steps:
-      - uses: release-drafter/release-drafter@e9ee02fbac03d922bac6212003695e8358dd88b0 # v5.19.0
+      - uses: release-drafter/release-drafter@ac463ffd9cc4c6ad5682af93dc3e3591c4657ee3 # v5.19.0
         env:
           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

From b8c4ddf2a2c2eb1b56b6b566db9e29ff3e88991b Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 1 Jun 2022 22:05:19 +0000
Subject: [PATCH 0188/3231] Bump assertj-core from 3.22.0 to 3.23.1 in
 /modules/orientdb

Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.22.0 to 3.23.1.
- [Release notes](https://github.com/assertj/assertj-core/releases)
- [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.22.0...assertj-core-3.23.1)

---
updated-dependencies:
- dependency-name: org.assertj:assertj-core
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] 
---
 modules/orientdb/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/orientdb/build.gradle b/modules/orientdb/build.gradle
index 820e96e114f..05bdb380c28 100644
--- a/modules/orientdb/build.gradle
+++ b/modules/orientdb/build.gradle
@@ -5,7 +5,7 @@ dependencies {
 
     api "com.orientechnologies:orientdb-client:3.0.24"
 
-    testImplementation 'org.assertj:assertj-core:3.22.0'
+    testImplementation 'org.assertj:assertj-core:3.23.1'
     testImplementation 'org.apache.tinkerpop:gremlin-driver:3.3.4'
     testImplementation "com.orientechnologies:orientdb-gremlin:3.0.18"
 }

From 7e93a7e78e710589e14ef8da91a1b5d392274471 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 1 Jun 2022 22:05:22 +0000
Subject: [PATCH 0189/3231] Bump assertj-core from 3.22.0 to 3.23.1 in
 /modules/rabbitmq

Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.22.0 to 3.23.1.
- [Release notes](https://github.com/assertj/assertj-core/releases)
- [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.22.0...assertj-core-3.23.1)

---
updated-dependencies:
- dependency-name: org.assertj:assertj-core
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] 
---
 modules/rabbitmq/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/rabbitmq/build.gradle b/modules/rabbitmq/build.gradle
index 967b9eb4ed9..6c2953909bb 100644
--- a/modules/rabbitmq/build.gradle
+++ b/modules/rabbitmq/build.gradle
@@ -3,6 +3,6 @@ description = "TestContainers :: RabbitMQ"
 dependencies {
     api project(":testcontainers")
     testImplementation 'com.rabbitmq:amqp-client:5.14.2'
-    testImplementation 'org.assertj:assertj-core:3.22.0'
+    testImplementation 'org.assertj:assertj-core:3.23.1'
     compileOnly 'org.jetbrains:annotations:23.0.0'
 }

From 46d75a20b081044fc341e7db3b4278d36450a67a Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 1 Jun 2022 22:05:25 +0000
Subject: [PATCH 0190/3231] Bump assertj-core from 3.22.0 to 3.23.1 in
 /modules/k3s

Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.22.0 to 3.23.1.
- [Release notes](https://github.com/assertj/assertj-core/releases)
- [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.22.0...assertj-core-3.23.1)

---
updated-dependencies:
- dependency-name: org.assertj:assertj-core
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] 
---
 modules/k3s/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/k3s/build.gradle b/modules/k3s/build.gradle
index 0ab368d60c2..1c7e6138b7f 100644
--- a/modules/k3s/build.gradle
+++ b/modules/k3s/build.gradle
@@ -10,5 +10,5 @@ dependencies {
 
     testImplementation 'io.fabric8:kubernetes-client:5.12.2'
     testImplementation 'io.kubernetes:client-java:15.0.0'
-    testImplementation 'org.assertj:assertj-core:3.22.0'
+    testImplementation 'org.assertj:assertj-core:3.23.1'
 }

From 0c7730eb1d1e4e034a872b9bb52dced44e65ece7 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 1 Jun 2022 22:05:25 +0000
Subject: [PATCH 0191/3231] Bump trino-jdbc from 379 to 382 in /modules/trino

Bumps trino-jdbc from 379 to 382.

---
updated-dependencies:
- dependency-name: io.trino:trino-jdbc
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] 
---
 modules/trino/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/trino/build.gradle b/modules/trino/build.gradle
index d0b56af633f..8305e8e0629 100644
--- a/modules/trino/build.gradle
+++ b/modules/trino/build.gradle
@@ -4,6 +4,6 @@ dependencies {
     api project(':jdbc')
 
     testImplementation project(':jdbc-test')
-    testImplementation 'io.trino:trino-jdbc:379'
+    testImplementation 'io.trino:trino-jdbc:382'
     compileOnly 'org.jetbrains:annotations:23.0.0'
 }

From c0b0612ace8545533258f2295358fb750d65d6af Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 1 Jun 2022 22:05:26 +0000
Subject: [PATCH 0192/3231] Bump google-cloud-pubsub from 1.116.4 to 1.119.0 in
 /modules/gcloud

Bumps [google-cloud-pubsub](https://github.com/googleapis/java-pubsub) from 1.116.4 to 1.119.0.
- [Release notes](https://github.com/googleapis/java-pubsub/releases)
- [Changelog](https://github.com/googleapis/java-pubsub/blob/main/CHANGELOG.md)
- [Commits](https://github.com/googleapis/java-pubsub/compare/v1.116.4...v1.119.0)

---
updated-dependencies:
- dependency-name: com.google.cloud:google-cloud-pubsub
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] 
---
 modules/gcloud/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle
index 031e68a7f76..c493020c61f 100644
--- a/modules/gcloud/build.gradle
+++ b/modules/gcloud/build.gradle
@@ -5,7 +5,7 @@ dependencies {
 
     testImplementation 'com.google.cloud:google-cloud-datastore:2.4.0'
     testImplementation 'com.google.cloud:google-cloud-firestore:3.0.21'
-    testImplementation 'com.google.cloud:google-cloud-pubsub:1.116.4'
+    testImplementation 'com.google.cloud:google-cloud-pubsub:1.119.0'
     testImplementation 'com.google.cloud:google-cloud-spanner:6.20.0'
     testImplementation 'com.google.cloud:google-cloud-bigtable:2.6.2'
     testImplementation 'org.assertj:assertj-core:3.22.0'

From c6beb9219642e0b054802e9cc221462dd3218751 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 1 Jun 2022 22:05:27 +0000
Subject: [PATCH 0193/3231] Bump postgresql from 42.3.4 to 42.3.6 in
 /modules/cockroachdb

Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.3.4 to 42.3.6.
- [Release notes](https://github.com/pgjdbc/pgjdbc/releases)
- [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md)
- [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.3.4...REL42.3.6)

---
updated-dependencies:
- dependency-name: org.postgresql:postgresql
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] 
---
 modules/cockroachdb/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/cockroachdb/build.gradle b/modules/cockroachdb/build.gradle
index ad1007a4744..490b2d7fb11 100644
--- a/modules/cockroachdb/build.gradle
+++ b/modules/cockroachdb/build.gradle
@@ -4,5 +4,5 @@ dependencies {
     api project(':jdbc')
 
     testImplementation project(':jdbc-test')
-    testImplementation 'org.postgresql:postgresql:42.3.4'
+    testImplementation 'org.postgresql:postgresql:42.3.6'
 }

From 2e6ac22bcbf163329d171c5b4f57b3ecbb2e2ed8 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 1 Jun 2022 22:05:31 +0000
Subject: [PATCH 0194/3231] Bump azure-cosmos from 4.29.1 to 4.30.0 in
 /modules/azure

Bumps [azure-cosmos](https://github.com/Azure/azure-sdk-for-java) from 4.29.1 to 4.30.0.
- [Release notes](https://github.com/Azure/azure-sdk-for-java/releases)
- [Commits](https://github.com/Azure/azure-sdk-for-java/compare/azure-cosmos_4.29.1...azure-cosmos_4.30.0)

---
updated-dependencies:
- dependency-name: com.azure:azure-cosmos
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] 
---
 modules/azure/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/azure/build.gradle b/modules/azure/build.gradle
index 8ab7612c18b..f26028b8ede 100644
--- a/modules/azure/build.gradle
+++ b/modules/azure/build.gradle
@@ -6,5 +6,5 @@ dependencies {
     shaded 'com.squareup.okhttp3:okhttp:3.14.9'
 
     testImplementation 'org.assertj:assertj-core:3.22.0'
-    testImplementation 'com.azure:azure-cosmos:4.29.1'
+    testImplementation 'com.azure:azure-cosmos:4.30.0'
 }

From 110fe8e4f9e813949779a3d8473655f216b44375 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 1 Jun 2022 22:05:44 +0000
Subject: [PATCH 0195/3231] Bump google-cloud-bigtable from 2.6.2 to 2.8.0 in
 /modules/gcloud

Bumps [google-cloud-bigtable](https://github.com/googleapis/java-bigtable) from 2.6.2 to 2.8.0.
- [Release notes](https://github.com/googleapis/java-bigtable/releases)
- [Changelog](https://github.com/googleapis/java-bigtable/blob/main/CHANGELOG.md)
- [Commits](https://github.com/googleapis/java-bigtable/compare/v2.6.2...v2.8.0)

---
updated-dependencies:
- dependency-name: com.google.cloud:google-cloud-bigtable
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] 
---
 modules/gcloud/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle
index 031e68a7f76..cdb09edd51f 100644
--- a/modules/gcloud/build.gradle
+++ b/modules/gcloud/build.gradle
@@ -7,6 +7,6 @@ dependencies {
     testImplementation 'com.google.cloud:google-cloud-firestore:3.0.21'
     testImplementation 'com.google.cloud:google-cloud-pubsub:1.116.4'
     testImplementation 'com.google.cloud:google-cloud-spanner:6.20.0'
-    testImplementation 'com.google.cloud:google-cloud-bigtable:2.6.2'
+    testImplementation 'com.google.cloud:google-cloud-bigtable:2.8.0'
     testImplementation 'org.assertj:assertj-core:3.22.0'
 }

From 4a66388f7f10e587280b135673c87958758db9fb Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 1 Jun 2022 22:05:45 +0000
Subject: [PATCH 0196/3231] Bump reactor-core from 3.4.17 to 3.4.18 in
 /modules/r2dbc

Bumps [reactor-core](https://github.com/reactor/reactor-core) from 3.4.17 to 3.4.18.
- [Release notes](https://github.com/reactor/reactor-core/releases)
- [Commits](https://github.com/reactor/reactor-core/compare/v3.4.17...v3.4.18)

---
updated-dependencies:
- dependency-name: io.projectreactor:reactor-core
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] 
---
 modules/r2dbc/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/r2dbc/build.gradle b/modules/r2dbc/build.gradle
index 2a4e3958af8..11e22e2bc9f 100644
--- a/modules/r2dbc/build.gradle
+++ b/modules/r2dbc/build.gradle
@@ -15,6 +15,6 @@ dependencies {
     testImplementation 'io.r2dbc:r2dbc-postgresql:0.8.12.RELEASE'
     testImplementation project(':postgresql')
 
-    testFixturesImplementation 'io.projectreactor:reactor-core:3.4.17'
+    testFixturesImplementation 'io.projectreactor:reactor-core:3.4.18'
     testFixturesImplementation 'org.assertj:assertj-core:3.14.0'
 }

From eeba70a2266b7f20ce94a6566fffdb99f3950ee3 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 1 Jun 2022 22:06:07 +0000
Subject: [PATCH 0197/3231] Bump hivemq-extension-sdk from 4.8.0 to 4.8.1 in
 /modules/hivemq

Bumps [hivemq-extension-sdk](https://github.com/hivemq/hivemq-extension-sdk) from 4.8.0 to 4.8.1.
- [Release notes](https://github.com/hivemq/hivemq-extension-sdk/releases)
- [Commits](https://github.com/hivemq/hivemq-extension-sdk/compare/4.8.0...4.8.1)

---
updated-dependencies:
- dependency-name: com.hivemq:hivemq-extension-sdk
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] 
---
 modules/hivemq/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/hivemq/build.gradle b/modules/hivemq/build.gradle
index bfdfecb1761..972e07d1f8a 100644
--- a/modules/hivemq/build.gradle
+++ b/modules/hivemq/build.gradle
@@ -13,7 +13,7 @@ dependencies {
 
     testImplementation("org.junit.jupiter:junit-jupiter-api:5.8.2")
     testImplementation(project(":junit-jupiter"))
-    testImplementation("com.hivemq:hivemq-extension-sdk:4.8.0")
+    testImplementation("com.hivemq:hivemq-extension-sdk:4.8.1")
     testImplementation("com.hivemq:hivemq-mqtt-client:1.3.0")
     testImplementation("org.apache.httpcomponents:httpclient:4.5.13")
     testImplementation("ch.qos.logback:logback-classic:1.2.10")

From 1bbdbb1deccfe70ef1c64de566963c7334ba8bc9 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 1 Jun 2022 22:07:49 +0000
Subject: [PATCH 0198/3231] Bump common-custom-user-data-gradle-plugin from
 1.6.5 to 1.7

Bumps common-custom-user-data-gradle-plugin from 1.6.5 to 1.7.

---
updated-dependencies:
- dependency-name: com.gradle:common-custom-user-data-gradle-plugin
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] 
---
 settings.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/settings.gradle b/settings.gradle
index 6b80183bc34..cf15e5ec31a 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -6,7 +6,7 @@ buildscript {
     }
     dependencies {
         classpath "com.gradle.enterprise:com.gradle.enterprise.gradle.plugin:3.10"
-        classpath "com.gradle:common-custom-user-data-gradle-plugin:1.6.5"
+        classpath "com.gradle:common-custom-user-data-gradle-plugin:1.7"
     }
 }
 

From 69856364f1a06803c12719b45e6e693e2258404a Mon Sep 17 00:00:00 2001
From: adrianpozueco 
Date: Thu, 2 Jun 2022 20:35:29 +0200
Subject: [PATCH 0199/3231] [Localstack] Get region from env variables or
 default (#3556)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Co-authored-by: Pozueco Adrian 
Co-authored-by: Eddú Meléndez Gonzales 
---
 .../localstack/LocalStackContainer.java       |  4 ++-
 .../localstack/LocalstackContainerTest.java   | 26 +++++++++++++++++++
 2 files changed, 29 insertions(+), 1 deletion(-)

diff --git a/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java b/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java
index 8bd6f14d3fd..d37c80d06fa 100644
--- a/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java
+++ b/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java
@@ -45,6 +45,8 @@ public class LocalStackContainer extends GenericContainer {
 
     private static final String DEFAULT_TAG = "0.11.2";
 
+    private static final String DEFAULT_REGION = "us-east-1";
+
     @Deprecated
     public static final String VERSION = DEFAULT_TAG;
 
@@ -311,7 +313,7 @@ public String getSecretKey() {
      * @return a default region
      */
     public String getRegion() {
-        return "us-east-1";
+        return this.getEnvMap().getOrDefault("DEFAULT_REGION", DEFAULT_REGION);
     }
 
     public interface EnabledService {
diff --git a/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java b/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java
index cf4e1a813a4..3b85c69ca34 100644
--- a/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java
+++ b/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java
@@ -1,5 +1,6 @@
 package org.testcontainers.containers.localstack;
 
+import com.amazonaws.client.builder.AwsClientBuilder;
 import com.amazonaws.services.kms.AWSKMS;
 import com.amazonaws.services.kms.AWSKMSClientBuilder;
 import com.amazonaws.services.kms.model.CreateKeyRequest;
@@ -286,4 +287,29 @@ private String runAwsCliAgainstDockerNetworkContainer(String command) throws Exc
             return logs;
         }
     }
+
+    public static class WithRegion {
+
+        // with_region {
+        private static String region = "eu-west-1";
+
+        @ClassRule
+        public static LocalStackContainer localstack = new LocalStackContainer(LocalstackTestImages.LOCALSTACK_IMAGE)
+            .withEnv("DEFAULT_REGION", region)
+            .withServices(Service.S3);
+
+        // }
+
+        @Test
+        public void s3EndpointHasProperRegion() {
+            final AwsClientBuilder.EndpointConfiguration endpointConfiguration = localstack.getEndpointConfiguration(
+                Service.S3
+            );
+            assertEquals(
+                "The endpoint configuration has right region",
+                region,
+                endpointConfiguration.getSigningRegion()
+            );
+        }
+    }
 }

From 74e87885ecd4737c392eb4c67ec41e7294977089 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?=
 
Date: Thu, 2 Jun 2022 14:05:48 -0500
Subject: [PATCH 0200/3231] Cleanup japicmp baselines (#5463)

Co-authored-by: Sergei Egorov 
---
 core/build.gradle          | 4 ----
 gradle/japicmp.gradle      | 2 +-
 modules/kafka/build.gradle | 9 ---------
 3 files changed, 1 insertion(+), 14 deletions(-)

diff --git a/core/build.gradle b/core/build.gradle
index efb08991f8d..7aac72e50ed 100644
--- a/core/build.gradle
+++ b/core/build.gradle
@@ -60,10 +60,6 @@ configurations.all {
 }
 
 dependencies {
-    baseline 'org.testcontainers:testcontainers:1.17.1', {
-        exclude group: "*", module: "*"
-    }
-
     api 'junit:junit:4.13.2'
     api 'org.slf4j:slf4j-api:1.7.35'
     compileOnly 'org.jetbrains:annotations:23.0.0'
diff --git a/gradle/japicmp.gradle b/gradle/japicmp.gradle
index e74cdccd3af..fe3f8881b1c 100644
--- a/gradle/japicmp.gradle
+++ b/gradle/japicmp.gradle
@@ -3,7 +3,7 @@ configurations {
 }
 
 dependencies {
-    baseline "org.testcontainers:${project.name}:1.16.0", {
+    baseline "org.testcontainers:${project.name}:1.17.2", {
         exclude group: "*", module: "*"
     }
 }
diff --git a/modules/kafka/build.gradle b/modules/kafka/build.gradle
index b9157d9d539..6d1cf82e5c8 100644
--- a/modules/kafka/build.gradle
+++ b/modules/kafka/build.gradle
@@ -1,14 +1,5 @@
 description = "Testcontainers :: Kafka"
 
-tasks.japicmp {
-    // TODO clean after 1.17.0 is released
-    methodExcludes = [
-        // Overrides removed, not a breaking change per se
-        "org.testcontainers.containers.KafkaContainer#containerIsStarting(com.github.dockerjava.api.command.InspectContainerResponse,boolean)",
-        "org.testcontainers.containers.KafkaContainer#doStart()",
-    ]
-}
-
 dependencies {
     api project(':testcontainers')
 

From 7ea69bdd4e911c98003487b5ee444b47aa52432f Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 2 Jun 2022 21:47:36 +0000
Subject: [PATCH 0201/3231] Bump neo4j-java-driver from 4.4.5 to 4.4.6 in
 /examples

Bumps [neo4j-java-driver](https://github.com/neo4j/neo4j-java-driver) from 4.4.5 to 4.4.6.
- [Release notes](https://github.com/neo4j/neo4j-java-driver/releases)
- [Commits](https://github.com/neo4j/neo4j-java-driver/compare/4.4.5...4.4.6)

---
updated-dependencies:
- dependency-name: org.neo4j.driver:neo4j-java-driver
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] 
---
 examples/neo4j-container/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/examples/neo4j-container/build.gradle b/examples/neo4j-container/build.gradle
index 652937d8b33..745d3206953 100644
--- a/examples/neo4j-container/build.gradle
+++ b/examples/neo4j-container/build.gradle
@@ -8,7 +8,7 @@ repositories {
 
 dependencies {
     testImplementation 'org.assertj:assertj-core:3.22.0'
-    testImplementation 'org.neo4j.driver:neo4j-java-driver:4.4.5'
+    testImplementation 'org.neo4j.driver:neo4j-java-driver:4.4.6'
     testImplementation 'org.testcontainers:neo4j'
     testImplementation 'org.testcontainers:junit-jupiter'
 }

From 3e4d1213715d08886e5abeabf6df4a1b93eff883 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 2 Jun 2022 21:47:54 +0000
Subject: [PATCH 0202/3231] Bump mongo-java-driver from 3.12.10 to 3.12.11 in
 /core

Bumps [mongo-java-driver](https://github.com/mongodb/mongo-java-driver) from 3.12.10 to 3.12.11.
- [Release notes](https://github.com/mongodb/mongo-java-driver/releases)
- [Commits](https://github.com/mongodb/mongo-java-driver/compare/r3.12.10...r3.12.11)

---
updated-dependencies:
- dependency-name: org.mongodb:mongo-java-driver
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] 
---
 core/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/core/build.gradle b/core/build.gradle
index f6b336e9600..310b4f43ef0 100644
--- a/core/build.gradle
+++ b/core/build.gradle
@@ -89,7 +89,7 @@ dependencies {
     testImplementation 'org.apache.httpcomponents:httpclient:4.5.9'
     testImplementation 'redis.clients:jedis:4.2.3'
     testImplementation 'com.rabbitmq:amqp-client:5.14.2'
-    testImplementation 'org.mongodb:mongo-java-driver:3.12.10'
+    testImplementation 'org.mongodb:mongo-java-driver:3.12.11'
 
     testImplementation ('org.mockito:mockito-core:4.6.0') {
         exclude(module: 'hamcrest-core')

From deef741188cc4d659dfae0849cb2e0ea948b4c24 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 2 Jun 2022 21:47:55 +0000
Subject: [PATCH 0203/3231] Bump mysql-connector-java in /modules/junit-jupiter

Bumps [mysql-connector-java](https://github.com/mysql/mysql-connector-j) from 8.0.27 to 8.0.29.
- [Release notes](https://github.com/mysql/mysql-connector-j/releases)
- [Changelog](https://github.com/mysql/mysql-connector-j/blob/release/8.0/CHANGES)
- [Commits](https://github.com/mysql/mysql-connector-j/compare/8.0.27...8.0.29)

---
updated-dependencies:
- dependency-name: mysql:mysql-connector-java
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] 
---
 modules/junit-jupiter/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle
index 5c42d57fcbb..386fc023902 100644
--- a/modules/junit-jupiter/build.gradle
+++ b/modules/junit-jupiter/build.gradle
@@ -16,7 +16,7 @@ dependencies {
     testImplementation 'org.junit.jupiter:junit-jupiter-params:5.8.2'
 
     testRuntimeOnly 'org.postgresql:postgresql:42.3.6'
-    testRuntimeOnly 'mysql:mysql-connector-java:8.0.27'
+    testRuntimeOnly 'mysql:mysql-connector-java:8.0.29'
     testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.2'
 }
 

From c22e1e7dbe84614b79cea59d970c882b97a4abe9 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 2 Jun 2022 21:48:00 +0000
Subject: [PATCH 0204/3231] Bump json from 20211205 to 20220320 in /examples

Bumps [json](https://github.com/douglascrockford/JSON-java) from 20211205 to 20220320.
- [Release notes](https://github.com/douglascrockford/JSON-java/releases)
- [Changelog](https://github.com/stleary/JSON-java/blob/master/docs/RELEASES.md)
- [Commits](https://github.com/douglascrockford/JSON-java/commits)

---
updated-dependencies:
- dependency-name: org.json:json
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] 
---
 examples/linked-container/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/examples/linked-container/build.gradle b/examples/linked-container/build.gradle
index 05c5b241b51..a801e4f8588 100644
--- a/examples/linked-container/build.gradle
+++ b/examples/linked-container/build.gradle
@@ -8,7 +8,7 @@ repositories {
 dependencies {
     compileOnly 'org.slf4j:slf4j-api:1.7.32'
     implementation 'com.squareup.okhttp3:okhttp:4.9.3'
-    implementation 'org.json:json:20211205'
+    implementation 'org.json:json:20220320'
     testImplementation 'org.postgresql:postgresql:42.3.6'
     testImplementation 'ch.qos.logback:logback-classic:1.2.11'
     testImplementation 'org.testcontainers:postgresql'

From a8ae7c898c297b81d6d872865a823ef8b02ef106 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 2 Jun 2022 21:48:02 +0000
Subject: [PATCH 0205/3231] Bump client-java from 15.0.0 to 15.0.1 in
 /modules/k3s

Bumps [client-java](https://github.com/kubernetes-client/java) from 15.0.0 to 15.0.1.
- [Release notes](https://github.com/kubernetes-client/java/releases)
- [Changelog](https://github.com/kubernetes-client/java/blob/master/CHANGELOG.md)
- [Commits](https://github.com/kubernetes-client/java/compare/v15.0.0...v15.0.1)

---
updated-dependencies:
- dependency-name: io.kubernetes:client-java
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] 
---
 modules/k3s/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/k3s/build.gradle b/modules/k3s/build.gradle
index 1c7e6138b7f..bf51cbfefa9 100644
--- a/modules/k3s/build.gradle
+++ b/modules/k3s/build.gradle
@@ -9,6 +9,6 @@ dependencies {
     shaded 'com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.8.8'
 
     testImplementation 'io.fabric8:kubernetes-client:5.12.2'
-    testImplementation 'io.kubernetes:client-java:15.0.0'
+    testImplementation 'io.kubernetes:client-java:15.0.1'
     testImplementation 'org.assertj:assertj-core:3.23.1'
 }

From bbb31506cb890ef75c48461ac107c73615885e99 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 2 Jun 2022 21:48:03 +0000
Subject: [PATCH 0206/3231] Bump slf4j-api from 1.7.35 to 1.7.36 in /core

Bumps [slf4j-api](https://github.com/qos-ch/slf4j) from 1.7.35 to 1.7.36.
- [Release notes](https://github.com/qos-ch/slf4j/releases)
- [Commits](https://github.com/qos-ch/slf4j/compare/v_1.7.35...v_1.7.36)

---
updated-dependencies:
- dependency-name: org.slf4j:slf4j-api
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] 
---
 core/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/core/build.gradle b/core/build.gradle
index f6b336e9600..393272aabf3 100644
--- a/core/build.gradle
+++ b/core/build.gradle
@@ -61,7 +61,7 @@ configurations.all {
 
 dependencies {
     api 'junit:junit:4.13.2'
-    api 'org.slf4j:slf4j-api:1.7.35'
+    api 'org.slf4j:slf4j-api:1.7.36'
     compileOnly 'org.jetbrains:annotations:23.0.0'
     testCompileClasspath 'org.jetbrains:annotations:23.0.0'
     api 'org.apache.commons:commons-compress:1.21'

From e7b77ba2a640672e2893920351f374010c241df6 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 2 Jun 2022 21:48:06 +0000
Subject: [PATCH 0207/3231] Bump logback-classic from 1.2.10 to 1.2.11 in
 /modules/hivemq

Bumps logback-classic from 1.2.10 to 1.2.11.

---
updated-dependencies:
- dependency-name: ch.qos.logback:logback-classic
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] 
---
 modules/hivemq/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/hivemq/build.gradle b/modules/hivemq/build.gradle
index 972e07d1f8a..7add9af67ea 100644
--- a/modules/hivemq/build.gradle
+++ b/modules/hivemq/build.gradle
@@ -16,7 +16,7 @@ dependencies {
     testImplementation("com.hivemq:hivemq-extension-sdk:4.8.1")
     testImplementation("com.hivemq:hivemq-mqtt-client:1.3.0")
     testImplementation("org.apache.httpcomponents:httpclient:4.5.13")
-    testImplementation("ch.qos.logback:logback-classic:1.2.10")
+    testImplementation("ch.qos.logback:logback-classic:1.2.11")
     testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2")
 }
 

From 0891ac2410871133c57b2931ac89bec97673224f Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 2 Jun 2022 21:48:09 +0000
Subject: [PATCH 0208/3231] Bump kafka-clients from 3.0.0 to 3.2.0 in
 /modules/kafka

Bumps kafka-clients from 3.0.0 to 3.2.0.

---
updated-dependencies:
- dependency-name: org.apache.kafka:kafka-clients
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] 
---
 modules/kafka/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/kafka/build.gradle b/modules/kafka/build.gradle
index cc081326932..79264b8207f 100644
--- a/modules/kafka/build.gradle
+++ b/modules/kafka/build.gradle
@@ -3,7 +3,7 @@ description = "Testcontainers :: Kafka"
 dependencies {
     api project(':testcontainers')
 
-    testImplementation 'org.apache.kafka:kafka-clients:3.0.0'
+    testImplementation 'org.apache.kafka:kafka-clients:3.2.0'
     testImplementation 'org.assertj:assertj-core:3.23.1'
     testImplementation 'com.google.guava:guava:23.0'
 }

From 231a01b52e7e97409f0ea2fdfa94e92c6725e624 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 2 Jun 2022 21:48:16 +0000
Subject: [PATCH 0209/3231] Bump elasticsearch-rest-client in
 /modules/elasticsearch

Bumps [elasticsearch-rest-client](https://github.com/elastic/elasticsearch) from 7.16.2 to 8.2.2.
- [Release notes](https://github.com/elastic/elasticsearch/releases)
- [Changelog](https://github.com/elastic/elasticsearch/blob/master/CHANGELOG.md)
- [Commits](https://github.com/elastic/elasticsearch/compare/v7.16.2...v8.2.2)

---
updated-dependencies:
- dependency-name: org.elasticsearch.client:elasticsearch-rest-client
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] 
---
 modules/elasticsearch/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/elasticsearch/build.gradle b/modules/elasticsearch/build.gradle
index 4bff8106f0b..6d2b5b8a667 100644
--- a/modules/elasticsearch/build.gradle
+++ b/modules/elasticsearch/build.gradle
@@ -2,7 +2,7 @@ description = "TestContainers :: elasticsearch"
 
 dependencies {
     api project(':testcontainers')
-    testImplementation "org.elasticsearch.client:elasticsearch-rest-client:7.16.2"
+    testImplementation "org.elasticsearch.client:elasticsearch-rest-client:8.2.2"
     testImplementation "org.elasticsearch.client:transport:7.17.4"
     testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2'
 }

From 0cab47b7b3fa3b8fb41b619a1ea4ea8c2c771bfe Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 2 Jun 2022 21:48:18 +0000
Subject: [PATCH 0210/3231] Bump jedis from 4.2.2 to 4.2.3 in
 /modules/junit-jupiter

Bumps [jedis](https://github.com/redis/jedis) from 4.2.2 to 4.2.3.
- [Release notes](https://github.com/redis/jedis/releases)
- [Commits](https://github.com/redis/jedis/compare/v4.2.2...v4.2.3)

---
updated-dependencies:
- dependency-name: redis.clients:jedis
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] 
---
 modules/junit-jupiter/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle
index 5c42d57fcbb..371eb6ea4a8 100644
--- a/modules/junit-jupiter/build.gradle
+++ b/modules/junit-jupiter/build.gradle
@@ -7,7 +7,7 @@ dependencies {
     testImplementation project(':mysql')
     testImplementation project(':postgresql')
     testImplementation 'com.zaxxer:HikariCP:4.0.3'
-    testImplementation 'redis.clients:jedis:4.2.2'
+    testImplementation 'redis.clients:jedis:4.2.3'
     testImplementation 'org.apache.httpcomponents:httpclient:4.5.13'
     testImplementation ('org.mockito:mockito-core:4.4.0') {
         exclude(module: 'hamcrest-core')

From 74dd5f49055fbb5160426548ff6784336491caa7 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 2 Jun 2022 21:48:23 +0000
Subject: [PATCH 0211/3231] Bump s3 from 2.17.181 to 2.17.204 in
 /modules/localstack

Bumps s3 from 2.17.181 to 2.17.204.

---
updated-dependencies:
- dependency-name: software.amazon.awssdk:s3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] 
---
 modules/localstack/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle
index f512b3d5fab..0b8aa654dd5 100644
--- a/modules/localstack/build.gradle
+++ b/modules/localstack/build.gradle
@@ -7,6 +7,6 @@ dependencies {
     testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.231'
     testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.210'
     testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.232'
-    testImplementation 'software.amazon.awssdk:s3:2.17.181'
+    testImplementation 'software.amazon.awssdk:s3:2.17.204'
     testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2'
 }

From 9ae45502f706067a7cb1a8b637c6769cf6235adc Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 2 Jun 2022 21:48:23 +0000
Subject: [PATCH 0212/3231] Bump com.gradle.enterprise.gradle.plugin from 3.10
 to 3.10.1

Bumps com.gradle.enterprise.gradle.plugin from 3.10 to 3.10.1.

---
updated-dependencies:
- dependency-name: com.gradle.enterprise:com.gradle.enterprise.gradle.plugin
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] 
---
 settings.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/settings.gradle b/settings.gradle
index cf15e5ec31a..16b3fcdf98a 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -5,7 +5,7 @@ buildscript {
         }
     }
     dependencies {
-        classpath "com.gradle.enterprise:com.gradle.enterprise.gradle.plugin:3.10"
+        classpath "com.gradle.enterprise:com.gradle.enterprise.gradle.plugin:3.10.1"
         classpath "com.gradle:common-custom-user-data-gradle-plugin:1.7"
     }
 }

From 97d436d90b463e5f6182b63e945ec2452f52ea03 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 2 Jun 2022 21:48:27 +0000
Subject: [PATCH 0213/3231] Bump jedis from 4.2.2 to 4.2.3 in /examples

Bumps [jedis](https://github.com/redis/jedis) from 4.2.2 to 4.2.3.
- [Release notes](https://github.com/redis/jedis/releases)
- [Commits](https://github.com/redis/jedis/compare/v4.2.2...v4.2.3)

---
updated-dependencies:
- dependency-name: redis.clients:jedis
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] 
---
 examples/redis-backed-cache-testng/build.gradle | 2 +-
 examples/redis-backed-cache/build.gradle        | 2 +-
 examples/singleton-container/build.gradle       | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/examples/redis-backed-cache-testng/build.gradle b/examples/redis-backed-cache-testng/build.gradle
index 4faead13f3a..ba883520719 100644
--- a/examples/redis-backed-cache-testng/build.gradle
+++ b/examples/redis-backed-cache-testng/build.gradle
@@ -8,7 +8,7 @@ repositories {
 
 dependencies {
     compileOnly 'org.slf4j:slf4j-api:1.7.32'
-    implementation 'redis.clients:jedis:4.2.2'
+    implementation 'redis.clients:jedis:4.2.3'
     implementation 'com.google.code.gson:gson:2.8.9'
     implementation 'com.google.guava:guava:23.0'
     testImplementation 'org.testcontainers:testcontainers'
diff --git a/examples/redis-backed-cache/build.gradle b/examples/redis-backed-cache/build.gradle
index b0d64f0524b..87c54f81a8d 100644
--- a/examples/redis-backed-cache/build.gradle
+++ b/examples/redis-backed-cache/build.gradle
@@ -8,7 +8,7 @@ repositories {
 
 dependencies {
     compileOnly 'org.slf4j:slf4j-api:1.7.32'
-    implementation 'redis.clients:jedis:4.2.2'
+    implementation 'redis.clients:jedis:4.2.3'
     implementation 'com.google.code.gson:gson:2.8.9'
     implementation 'com.google.guava:guava:23.0'
     testImplementation 'org.testcontainers:testcontainers'
diff --git a/examples/singleton-container/build.gradle b/examples/singleton-container/build.gradle
index 2b7c3b7dda4..0195c1704f8 100644
--- a/examples/singleton-container/build.gradle
+++ b/examples/singleton-container/build.gradle
@@ -8,7 +8,7 @@ repositories {
 
 dependencies {
 
-    implementation 'redis.clients:jedis:4.2.2'
+    implementation 'redis.clients:jedis:4.2.3'
     implementation 'com.google.code.gson:gson:2.8.9'
     implementation 'com.google.guava:guava:23.0'
     compileOnly 'org.slf4j:slf4j-api:1.7.32'

From f4281805934055ac969c6b9aedca4fd5620362f3 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 2 Jun 2022 21:48:28 +0000
Subject: [PATCH 0214/3231] Bump assertj-core from 3.22.0 to 3.23.1 in
 /modules/r2dbc

Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.22.0 to 3.23.1.
- [Release notes](https://github.com/assertj/assertj-core/releases)
- [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.22.0...assertj-core-3.23.1)

---
updated-dependencies:
- dependency-name: org.assertj:assertj-core
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] 
---
 modules/r2dbc/build.gradle | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/modules/r2dbc/build.gradle b/modules/r2dbc/build.gradle
index 11e22e2bc9f..51199c9a53c 100644
--- a/modules/r2dbc/build.gradle
+++ b/modules/r2dbc/build.gradle
@@ -11,10 +11,10 @@ dependencies {
     api project(':testcontainers')
     api 'io.r2dbc:r2dbc-spi:0.8.1.RELEASE'
 
-    testImplementation 'org.assertj:assertj-core:3.22.0'
+    testImplementation 'org.assertj:assertj-core:3.23.1'
     testImplementation 'io.r2dbc:r2dbc-postgresql:0.8.12.RELEASE'
     testImplementation project(':postgresql')
 
     testFixturesImplementation 'io.projectreactor:reactor-core:3.4.18'
-    testFixturesImplementation 'org.assertj:assertj-core:3.14.0'
+    testFixturesImplementation 'org.assertj:assertj-core:3.23.1'
 }

From 1fe3c1f17ef7f02bbb032fb3e1bc01ce92198b4c Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 2 Jun 2022 21:48:29 +0000
Subject: [PATCH 0215/3231] Bump google-cloud-datastore from 2.4.0 to 2.7.0 in
 /modules/gcloud

Bumps [google-cloud-datastore](https://github.com/googleapis/java-datastore) from 2.4.0 to 2.7.0.
- [Release notes](https://github.com/googleapis/java-datastore/releases)
- [Changelog](https://github.com/googleapis/java-datastore/blob/main/CHANGELOG.md)
- [Commits](https://github.com/googleapis/java-datastore/compare/v2.4.0...v2.7.0)

---
updated-dependencies:
- dependency-name: com.google.cloud:google-cloud-datastore
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] 
---
 modules/gcloud/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle
index 32854badc04..cc674a584b2 100644
--- a/modules/gcloud/build.gradle
+++ b/modules/gcloud/build.gradle
@@ -3,7 +3,7 @@ description = "Testcontainers :: GCloud"
 dependencies {
     api project(':testcontainers')
 
-    testImplementation 'com.google.cloud:google-cloud-datastore:2.4.0'
+    testImplementation 'com.google.cloud:google-cloud-datastore:2.7.0'
     testImplementation 'com.google.cloud:google-cloud-firestore:3.0.21'
     testImplementation 'com.google.cloud:google-cloud-pubsub:1.119.0'
     testImplementation 'com.google.cloud:google-cloud-spanner:6.20.0'

From 133233647c587993bc5214940240161a159914f8 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 2 Jun 2022 21:48:36 +0000
Subject: [PATCH 0216/3231] Bump rest-assured from 5.0.1 to 5.1.0 in
 /modules/vault

Bumps [rest-assured](https://github.com/rest-assured/rest-assured) from 5.0.1 to 5.1.0.
- [Release notes](https://github.com/rest-assured/rest-assured/releases)
- [Changelog](https://github.com/rest-assured/rest-assured/blob/master/changelog.txt)
- [Commits](https://github.com/rest-assured/rest-assured/compare/rest-assured-5.0.1...rest-assured-5.1.0)

---
updated-dependencies:
- dependency-name: io.rest-assured:rest-assured
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] 
---
 modules/vault/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/vault/build.gradle b/modules/vault/build.gradle
index f6e94f2139d..1ffc366987c 100644
--- a/modules/vault/build.gradle
+++ b/modules/vault/build.gradle
@@ -4,7 +4,7 @@ dependencies {
     api project(':testcontainers')
 
     testImplementation 'com.bettercloud:vault-java-driver:5.1.0'
-    testImplementation 'io.rest-assured:rest-assured:5.0.1'
+    testImplementation 'io.rest-assured:rest-assured:5.1.0'
     testImplementation 'org.assertj:assertj-core:3.23.1'
 
 }

From a8cbbdce72ba627410935824034ba82117a3bf5c Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 2 Jun 2022 21:48:39 +0000
Subject: [PATCH 0217/3231] Bump aws-java-sdk-sqs from 1.12.210 to 1.12.233 in
 /modules/localstack

Bumps [aws-java-sdk-sqs](https://github.com/aws/aws-sdk-java) from 1.12.210 to 1.12.233.
- [Release notes](https://github.com/aws/aws-sdk-java/releases)
- [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md)
- [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.210...1.12.233)

---
updated-dependencies:
- dependency-name: com.amazonaws:aws-java-sdk-sqs
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] 
---
 modules/localstack/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle
index f512b3d5fab..5fc67214e69 100644
--- a/modules/localstack/build.gradle
+++ b/modules/localstack/build.gradle
@@ -5,7 +5,7 @@ dependencies {
 
     compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.231'
     testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.231'
-    testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.210'
+    testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.233'
     testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.232'
     testImplementation 'software.amazon.awssdk:s3:2.17.181'
     testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2'

From 0b15e6ecd3dbbb96bb9d9f4c2ccc12ba1b4dd5eb Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 2 Jun 2022 21:48:40 +0000
Subject: [PATCH 0218/3231] Bump google-cloud-spanner from 6.20.0 to 6.25.5 in
 /modules/gcloud

Bumps [google-cloud-spanner](https://github.com/googleapis/java-spanner) from 6.20.0 to 6.25.5.
- [Release notes](https://github.com/googleapis/java-spanner/releases)
- [Changelog](https://github.com/googleapis/java-spanner/blob/main/CHANGELOG.md)
- [Commits](https://github.com/googleapis/java-spanner/compare/v6.20.0...v6.25.5)

---
updated-dependencies:
- dependency-name: com.google.cloud:google-cloud-spanner
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] 
---
 modules/gcloud/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle
index 32854badc04..29df32c0b33 100644
--- a/modules/gcloud/build.gradle
+++ b/modules/gcloud/build.gradle
@@ -6,7 +6,7 @@ dependencies {
     testImplementation 'com.google.cloud:google-cloud-datastore:2.4.0'
     testImplementation 'com.google.cloud:google-cloud-firestore:3.0.21'
     testImplementation 'com.google.cloud:google-cloud-pubsub:1.119.0'
-    testImplementation 'com.google.cloud:google-cloud-spanner:6.20.0'
+    testImplementation 'com.google.cloud:google-cloud-spanner:6.25.5'
     testImplementation 'com.google.cloud:google-cloud-bigtable:2.8.0'
     testImplementation 'org.assertj:assertj-core:3.22.0'
 }

From b544343eeb97ffa2e6ca4c4e4c53e0af4910e9fb Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 2 Jun 2022 21:48:41 +0000
Subject: [PATCH 0219/3231] Bump assertj-core from 3.22.0 to 3.23.1 in
 /modules/gcloud

Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.22.0 to 3.23.1.
- [Release notes](https://github.com/assertj/assertj-core/releases)
- [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.22.0...assertj-core-3.23.1)

---
updated-dependencies:
- dependency-name: org.assertj:assertj-core
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] 
---
 modules/gcloud/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle
index 32854badc04..9b3bcd403de 100644
--- a/modules/gcloud/build.gradle
+++ b/modules/gcloud/build.gradle
@@ -8,5 +8,5 @@ dependencies {
     testImplementation 'com.google.cloud:google-cloud-pubsub:1.119.0'
     testImplementation 'com.google.cloud:google-cloud-spanner:6.20.0'
     testImplementation 'com.google.cloud:google-cloud-bigtable:2.8.0'
-    testImplementation 'org.assertj:assertj-core:3.22.0'
+    testImplementation 'org.assertj:assertj-core:3.23.1'
 }

From 4ad81aebc92107af531b970cc3db04eda00d27c7 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 2 Jun 2022 21:48:56 +0000
Subject: [PATCH 0220/3231] Bump mockito-core from 4.4.0 to 4.6.1 in
 /modules/junit-jupiter

Bumps [mockito-core](https://github.com/mockito/mockito) from 4.4.0 to 4.6.1.
- [Release notes](https://github.com/mockito/mockito/releases)
- [Commits](https://github.com/mockito/mockito/compare/v4.4.0...v4.6.1)

---
updated-dependencies:
- dependency-name: org.mockito:mockito-core
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] 
---
 modules/junit-jupiter/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle
index 5c42d57fcbb..27d62be6b6d 100644
--- a/modules/junit-jupiter/build.gradle
+++ b/modules/junit-jupiter/build.gradle
@@ -9,7 +9,7 @@ dependencies {
     testImplementation 'com.zaxxer:HikariCP:4.0.3'
     testImplementation 'redis.clients:jedis:4.2.2'
     testImplementation 'org.apache.httpcomponents:httpclient:4.5.13'
-    testImplementation ('org.mockito:mockito-core:4.4.0') {
+    testImplementation ('org.mockito:mockito-core:4.6.1') {
         exclude(module: 'hamcrest-core')
     }
     testImplementation 'org.assertj:assertj-core:3.23.1'

From 979d74a785daa96fc380a346c64491d1710503ea Mon Sep 17 00:00:00 2001
From: Maxim Bartkov <84171296+MaxBartkov@users.noreply.github.com>
Date: Sat, 11 Jun 2022 00:29:07 +0300
Subject: [PATCH 0221/3231] Added the Bucket4j library to "Who is using
 Testcontainers?" (#5480)

---
 docs/index.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/docs/index.md b/docs/index.md
index b2c11d6d8e8..0ad72bcb400 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -201,6 +201,7 @@ A huge thank you to our sponsors:
 * [Togglz](https://www.togglz.org/) - Feature Flags for the Java platform
 * [Byzer](https://www.byzer.org/home) - Integration tests for Data and AI platforms are based on multiple versions of Byzer, Ray and Apache Spark.
 * [Apache SeaTunnel](https://github.com/apache/incubator-seatunnel) - Integration testing with different datasource.
+* [Bucket4j](https://github.com/bucket4j/bucket4j) - Java rate-limiting library based on the token-bucket algorithm.
 
 ## License
 

From e7cdfa31af08074aa9dbb3f3d21a622144995b9d Mon Sep 17 00:00:00 2001
From: anatoly0karyakin 
Date: Mon, 13 Jun 2022 14:02:09 +0300
Subject: [PATCH 0222/3231] Don't ignore sql test query exception, add it as
 cause exception (#5484)

---
 .../org/testcontainers/containers/JdbcDatabaseContainer.java | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java
index 84d242a29c7..0b80eaac406 100644
--- a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java
+++ b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java
@@ -151,6 +151,7 @@ protected void waitUntilContainerStarted() {
         // Repeatedly try and open a connection to the DB and execute a test query
         long start = System.currentTimeMillis();
 
+        Exception lastConnectionException = null;
         while (System.currentTimeMillis() < start + (1000 * startupTimeoutSeconds)) {
             if (!isRunning()) {
                 Thread.sleep(100L);
@@ -165,6 +166,7 @@ protected void waitUntilContainerStarted() {
                     // we explicitly want this exception to fail fast without retries
                     throw e;
                 } catch (Exception e) {
+                    lastConnectionException = e;
                     // ignore so that we can try again
                     logger().debug("Failure when trying test query", e);
                     Thread.sleep(100L);
@@ -176,7 +178,8 @@ protected void waitUntilContainerStarted() {
             String.format(
                 "Container is started, but cannot be accessed by (JDBC URL: %s), please check container logs",
                 this.getJdbcUrl()
-            )
+            ),
+            lastConnectionException
         );
     }
 

From 7f9293b089bf4a2e27d28f12e91ef9b22963c473 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?=
 
Date: Mon, 13 Jun 2022 11:32:19 -0500
Subject: [PATCH 0223/3231] [LocalStack] Deprecate methods using aws sdk v1
 (#5489)

Currently, `LocalStackContainer` depends on aws sdk v1. We have received
feedback about moving to aws sdk v2. However, we think that the
class already provides the enough information to be used such as
`getEndpointOverride()`, `getRegion()`, `getAccessKey()` and `getSecretKey`.
So, we are deprecating `getDefaultCredentialsProvider()` and `getEndpointConfiguration`
which are going to be removed in the feature and `LocalStack` module will
not depend on any aws sdk.
---
 .../containers/localstack/LocalStackContainer.java            | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java b/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java
index d37c80d06fa..f3f905b84f6 100644
--- a/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java
+++ b/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java
@@ -193,7 +193,9 @@ public LocalStackContainer withServices(EnabledService... services) {
      *
      * @param service the service that is to be accessed
      * @return an {@link AwsClientBuilder.EndpointConfiguration}
+     * @deprecated {@link LocalStackContainer} will not depend on aws sdk.
      */
+    @Deprecated
     public AwsClientBuilder.EndpointConfiguration getEndpointConfiguration(Service service) {
         return new AwsClientBuilder.EndpointConfiguration(getEndpointOverride(service).toString(), getRegion());
     }
@@ -257,7 +259,9 @@ private int getServicePort(EnabledService service) {
              .build()
      
* @return an {@link AWSCredentialsProvider} + * @deprecated {@link LocalStackContainer} will not depend on aws sdk. */ + @Deprecated public AWSCredentialsProvider getDefaultCredentialsProvider() { return new AWSStaticCredentialsProvider(new BasicAWSCredentials(getAccessKey(), getSecretKey())); } From 2cd6b560bcb67087b12e172bbde6bf5902f853f3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Jun 2022 02:32:59 +0000 Subject: [PATCH 0224/3231] Bump gson from 2.8.9 to 2.9.0 in /examples Bumps [gson](https://github.com/google/gson) from 2.8.9 to 2.9.0. - [Release notes](https://github.com/google/gson/releases) - [Changelog](https://github.com/google/gson/blob/master/CHANGELOG.md) - [Commits](https://github.com/google/gson/compare/gson-parent-2.8.9...gson-parent-2.9.0) --- updated-dependencies: - dependency-name: com.google.code.gson:gson dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- examples/disque-job-queue/build.gradle | 2 +- examples/redis-backed-cache-testng/build.gradle | 2 +- examples/redis-backed-cache/build.gradle | 2 +- examples/singleton-container/build.gradle | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/disque-job-queue/build.gradle b/examples/disque-job-queue/build.gradle index bfb89f9cef2..1dc3497ec42 100644 --- a/examples/disque-job-queue/build.gradle +++ b/examples/disque-job-queue/build.gradle @@ -10,7 +10,7 @@ dependencies { compileOnly "org.projectlombok:lombok:1.18.24" annotationProcessor "org.projectlombok:lombok:1.18.24" implementation 'biz.paluch.redis:spinach:0.3' - implementation 'com.google.code.gson:gson:2.8.9' + implementation 'com.google.code.gson:gson:2.9.0' implementation 'com.google.guava:guava:23.0' testImplementation 'ch.qos.logback:logback-classic:1.2.11' testImplementation 'org.mockito:mockito-all:1.10.19' diff --git a/examples/redis-backed-cache-testng/build.gradle b/examples/redis-backed-cache-testng/build.gradle index ba883520719..56ba5934bdb 100644 --- a/examples/redis-backed-cache-testng/build.gradle +++ b/examples/redis-backed-cache-testng/build.gradle @@ -9,7 +9,7 @@ repositories { dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.32' implementation 'redis.clients:jedis:4.2.3' - implementation 'com.google.code.gson:gson:2.8.9' + implementation 'com.google.code.gson:gson:2.9.0' implementation 'com.google.guava:guava:23.0' testImplementation 'org.testcontainers:testcontainers' testImplementation 'ch.qos.logback:logback-classic:1.2.11' diff --git a/examples/redis-backed-cache/build.gradle b/examples/redis-backed-cache/build.gradle index 87c54f81a8d..c563cc658d8 100644 --- a/examples/redis-backed-cache/build.gradle +++ b/examples/redis-backed-cache/build.gradle @@ -9,7 +9,7 @@ repositories { dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.32' implementation 'redis.clients:jedis:4.2.3' - implementation 'com.google.code.gson:gson:2.8.9' + implementation 'com.google.code.gson:gson:2.9.0' implementation 'com.google.guava:guava:23.0' testImplementation 'org.testcontainers:testcontainers' testImplementation 'junit:junit:4.13.2' diff --git a/examples/singleton-container/build.gradle b/examples/singleton-container/build.gradle index 0195c1704f8..416f29de095 100644 --- a/examples/singleton-container/build.gradle +++ b/examples/singleton-container/build.gradle @@ -9,7 +9,7 @@ repositories { dependencies { implementation 'redis.clients:jedis:4.2.3' - implementation 'com.google.code.gson:gson:2.8.9' + implementation 'com.google.code.gson:gson:2.9.0' implementation 'com.google.guava:guava:23.0' compileOnly 'org.slf4j:slf4j-api:1.7.32' From 2fec86109e9bedb6f18d5627a6e7074b019e02a7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Jun 2022 02:33:00 +0000 Subject: [PATCH 0225/3231] Bump slf4j-api from 1.7.32 to 1.7.36 in /examples Bumps [slf4j-api](https://github.com/qos-ch/slf4j) from 1.7.32 to 1.7.36. - [Release notes](https://github.com/qos-ch/slf4j/releases) - [Commits](https://github.com/qos-ch/slf4j/compare/v_1.7.32...v_1.7.36) --- updated-dependencies: - dependency-name: org.slf4j:slf4j-api dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/linked-container/build.gradle | 2 +- examples/redis-backed-cache-testng/build.gradle | 2 +- examples/redis-backed-cache/build.gradle | 2 +- examples/singleton-container/build.gradle | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/linked-container/build.gradle b/examples/linked-container/build.gradle index a801e4f8588..f25127f75eb 100644 --- a/examples/linked-container/build.gradle +++ b/examples/linked-container/build.gradle @@ -6,7 +6,7 @@ repositories { mavenCentral() } dependencies { - compileOnly 'org.slf4j:slf4j-api:1.7.32' + compileOnly 'org.slf4j:slf4j-api:1.7.36' implementation 'com.squareup.okhttp3:okhttp:4.9.3' implementation 'org.json:json:20220320' testImplementation 'org.postgresql:postgresql:42.3.6' diff --git a/examples/redis-backed-cache-testng/build.gradle b/examples/redis-backed-cache-testng/build.gradle index ba883520719..d7e174d55c1 100644 --- a/examples/redis-backed-cache-testng/build.gradle +++ b/examples/redis-backed-cache-testng/build.gradle @@ -7,7 +7,7 @@ repositories { } dependencies { - compileOnly 'org.slf4j:slf4j-api:1.7.32' + compileOnly 'org.slf4j:slf4j-api:1.7.36' implementation 'redis.clients:jedis:4.2.3' implementation 'com.google.code.gson:gson:2.8.9' implementation 'com.google.guava:guava:23.0' diff --git a/examples/redis-backed-cache/build.gradle b/examples/redis-backed-cache/build.gradle index 87c54f81a8d..11bec04a48b 100644 --- a/examples/redis-backed-cache/build.gradle +++ b/examples/redis-backed-cache/build.gradle @@ -7,7 +7,7 @@ repositories { } dependencies { - compileOnly 'org.slf4j:slf4j-api:1.7.32' + compileOnly 'org.slf4j:slf4j-api:1.7.36' implementation 'redis.clients:jedis:4.2.3' implementation 'com.google.code.gson:gson:2.8.9' implementation 'com.google.guava:guava:23.0' diff --git a/examples/singleton-container/build.gradle b/examples/singleton-container/build.gradle index 0195c1704f8..61a63f6fab5 100644 --- a/examples/singleton-container/build.gradle +++ b/examples/singleton-container/build.gradle @@ -11,7 +11,7 @@ dependencies { implementation 'redis.clients:jedis:4.2.3' implementation 'com.google.code.gson:gson:2.8.9' implementation 'com.google.guava:guava:23.0' - compileOnly 'org.slf4j:slf4j-api:1.7.32' + compileOnly 'org.slf4j:slf4j-api:1.7.36' testImplementation 'ch.qos.logback:logback-classic:1.2.11' testImplementation 'org.testcontainers:testcontainers' From a35a4e13eead2a10e13bd9a62264256eb13508c8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Jun 2022 02:33:28 +0000 Subject: [PATCH 0226/3231] Bump google-cloud-firestore from 3.0.21 to 3.2.0 in /modules/gcloud Bumps [google-cloud-firestore](https://github.com/googleapis/java-firestore) from 3.0.21 to 3.2.0. - [Release notes](https://github.com/googleapis/java-firestore/releases) - [Changelog](https://github.com/googleapis/java-firestore/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/java-firestore/compare/v3.0.21...v3.2.0) --- updated-dependencies: - dependency-name: com.google.cloud:google-cloud-firestore dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/gcloud/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle index f1060b8ec83..cdea13f5fac 100644 --- a/modules/gcloud/build.gradle +++ b/modules/gcloud/build.gradle @@ -4,7 +4,7 @@ dependencies { api project(':testcontainers') testImplementation 'com.google.cloud:google-cloud-datastore:2.7.0' - testImplementation 'com.google.cloud:google-cloud-firestore:3.0.21' + testImplementation 'com.google.cloud:google-cloud-firestore:3.2.0' testImplementation 'com.google.cloud:google-cloud-pubsub:1.119.0' testImplementation 'com.google.cloud:google-cloud-spanner:6.25.5' testImplementation 'com.google.cloud:google-cloud-bigtable:2.8.0' From 7831ef2bceb076d584c69ffd29eb5f2f3bb62e39 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Jun 2022 02:33:28 +0000 Subject: [PATCH 0227/3231] Bump assertj-core from 3.22.0 to 3.23.1 in /modules/azure Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.22.0 to 3.23.1. - [Release notes](https://github.com/assertj/assertj-core/releases) - [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.22.0...assertj-core-3.23.1) --- updated-dependencies: - dependency-name: org.assertj:assertj-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/azure/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/azure/build.gradle b/modules/azure/build.gradle index f26028b8ede..6656445af09 100644 --- a/modules/azure/build.gradle +++ b/modules/azure/build.gradle @@ -5,6 +5,6 @@ dependencies { // TODO use JDK's HTTP client and/or Apache HttpClient5 shaded 'com.squareup.okhttp3:okhttp:3.14.9' - testImplementation 'org.assertj:assertj-core:3.22.0' + testImplementation 'org.assertj:assertj-core:3.23.1' testImplementation 'com.azure:azure-cosmos:4.30.0' } From 731da03265983689c80d9c6faee96c77e910d2bb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Jun 2022 02:33:48 +0000 Subject: [PATCH 0228/3231] Bump common-custom-user-data-gradle-plugin in /examples Bumps common-custom-user-data-gradle-plugin from 1.7 to 1.7.2. --- updated-dependencies: - dependency-name: com.gradle:common-custom-user-data-gradle-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/settings.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/settings.gradle b/examples/settings.gradle index 2046a7d282e..b9fb73f86c1 100644 --- a/examples/settings.gradle +++ b/examples/settings.gradle @@ -7,7 +7,7 @@ buildscript { dependencies { classpath "gradle.plugin.ch.myniva.gradle:s3-build-cache:0.10.0" classpath "com.gradle.enterprise:com.gradle.enterprise.gradle.plugin:3.10" - classpath "com.gradle:common-custom-user-data-gradle-plugin:1.7" + classpath "com.gradle:common-custom-user-data-gradle-plugin:1.7.2" } } From 4083228e0f72e9155a47b9a651c24ac896dd2a08 Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Tue, 14 Jun 2022 18:30:46 +0200 Subject: [PATCH 0229/3231] Add section about MySQL root user password to docs (#5497) --- docs/modules/databases/mysql.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/modules/databases/mysql.md b/docs/modules/databases/mysql.md index 26fc62308ef..6e6265e91ba 100644 --- a/docs/modules/databases/mysql.md +++ b/docs/modules/databases/mysql.md @@ -12,6 +12,11 @@ is a directory on the classpath containing .cnf files, the following URL can be Any .cnf files in this classpath directory will be mapped into the database container's /etc/mysql/conf.d directory, and will be able to override server settings when the container starts. +## MySQL `root` user password + +If no custom password is specified, the container will use the default user password `test` for the `root` user as well. +When you specify a custom password for the database user, +this will also act as the password of the MySQL `root` user automatically. ## Adding this module to your project dependencies From d96137a6614f06c5288de6000d43c7dab726b024 Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Tue, 14 Jun 2022 18:41:02 +0200 Subject: [PATCH 0230/3231] Add MariaDB `root` user password section to docs (#5498) --- docs/modules/databases/mariadb.md | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/docs/modules/databases/mariadb.md b/docs/modules/databases/mariadb.md index 495965bee0a..25367d629bd 100644 --- a/docs/modules/databases/mariadb.md +++ b/docs/modules/databases/mariadb.md @@ -2,22 +2,11 @@ See [Database containers](./index.md) for documentation and usage that is common to all relational database container types. -## Usage example +## MariaDB `root` user password -Running MariaDB as a stand-in for in a test: - -```java -public class SomeTest { - - @Rule - public MariaDBContainer mariaDB = new MariaDBContainer(); - - @Test - public void someTestMethod() { - String url = mariaDB.getJdbcUrl(); - - ... create a connection and run test as normal -``` +If no custom password is specified, the container will use the default user password `test` for the `root` user as well. +When you specify a custom password for the database user, +this will also act as the password of the MariaDB `root` user automatically. ## Adding this module to your project dependencies From b3f1b6676e934e67987fd564ddec701d6e0b6647 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Tue, 14 Jun 2022 13:11:11 -0500 Subject: [PATCH 0231/3231] Add issue forms (#5490) Introducing issue forms for: bug report, enhacements and features. In case of help or question there is a link to the slack channel. Co-authored-by: Kevin Wittek --- .github/ISSUE_TEMPLATE/bug_report.yaml | 96 +++++++++++++++++++++++++ .github/ISSUE_TEMPLATE/config.yml | 5 ++ .github/ISSUE_TEMPLATE/enhancement.yaml | 56 +++++++++++++++ .github/ISSUE_TEMPLATE/feature.yaml | 85 ++++++++++++++++++++++ 4 files changed, 242 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.yaml create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 .github/ISSUE_TEMPLATE/enhancement.yaml create mode 100644 .github/ISSUE_TEMPLATE/feature.yaml diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml new file mode 100644 index 00000000000..80e0b47741d --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -0,0 +1,96 @@ +name: Bug Report +description: File a bug report +title: "[Bug]: " +labels: ["type/bug"] +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to fill out this bug report! + - type: dropdown + id: module + attributes: + label: Module + description: Which Testcontainers module are you using? + options: + - Core + - Azure + - Cassandra + - Clickhouse + - Couchbase + - DB2 + - Dynalite + - Elasticsearch + - GCloud + - HiveMQ + - InfluxDB + - K3S + - Kafka + - LocalStack + - MariaDB + - MockServer + - MongoDB + - MSSQLServer + - MySQL + - Neo4j + - NGINX + - Oracle-XE + - OrientDB + - PostgreSQL + - Presto + - Pulsar + - RabbitMQ + - Selenium + - Solr + - ToxiProxy + - Trino + - Vault + validations: + required: true + - type: input + id: tc-version + attributes: + label: Testcontainers version + description: Which Testcontainers version are you using? + placeholder: ex. 1.17.2 + validations: + required: true + - type: dropdown + id: latest-version + attributes: + label: Using the latest Testcontainers version? + description: If you are not using the latest version, can you update your project and try to reproduce the issue? Is it still happening? + options: + - 'Yes' + - 'No' + validations: + required: true + - type: textarea + id: docker-version + attributes: + label: Docker version + description: Please run `docker version` and copy and paste the output into this field. + render: shell + validations: + required: true + - type: textarea + id: what-happened + attributes: + label: What happened? + description: Provide the context and the expected result. + validations: + required: true + - type: textarea + id: logs + attributes: + label: Relevant log output + description: Please copy and paste any relevant log output. The content will be automatically formatted as code, so no need for backticks. + render: shell + - type: textarea + id: additional-information + attributes: + label: Additional Information + description: | + Any links or references to have more context about the issue. + + Tip: You can attach a minimal sample project to reproduce the issue or provide further log files by clicking into this area to focus it and then dragging files in. diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 00000000000..d27b0d086e9 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: false +contact_links: + - name: Need help or have a question? + url: https://slack.testcontainers.org/ + about: Visit our slack channel. diff --git a/.github/ISSUE_TEMPLATE/enhancement.yaml b/.github/ISSUE_TEMPLATE/enhancement.yaml new file mode 100644 index 00000000000..f06ea99c8ba --- /dev/null +++ b/.github/ISSUE_TEMPLATE/enhancement.yaml @@ -0,0 +1,56 @@ +name: Enhancement +description: Suggest an enhancement +title: "[Enhancement]: " +labels: ["type/enhancement"] +body: + - type: markdown + attributes: + value: | + Please provide the following information + - type: dropdown + id: module + attributes: + label: Module + description: For which Testcontainers module does the enhancement proposal apply? + options: + - Core + - Azure + - Cassandra + - Clickhouse + - Couchbase + - DB2 + - Dynalite + - Elasticsearch + - GCloud + - HiveMQ + - InfluxDB + - K3S + - Kafka + - LocalStack + - MariaDB + - MockServer + - MongoDB + - MSSQLServer + - MySQL + - Neo4j + - NGINX + - Oracle-XE + - OrientDB + - PostgreSQL + - Presto + - Pulsar + - RabbitMQ + - Selenium + - Solr + - ToxiProxy + - Trino + - Vault + validations: + required: true + - type: textarea + id: proposal + attributes: + label: Proposal + description: What should be improved? What are the limitations of the current implications that would be solved by the proposal? + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/feature.yaml b/.github/ISSUE_TEMPLATE/feature.yaml new file mode 100644 index 00000000000..32edb0d25f5 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature.yaml @@ -0,0 +1,85 @@ +name: Feature +description: Suggest a new feature +title: "[Feature]: " +labels: ["type/feature"] +body: + - type: markdown + attributes: + value: | + Please provide the following information + - type: dropdown + id: module + attributes: + label: Module + description: Is this feature related to any of the existing modules? + options: + - Core + - Azure + - Cassandra + - Clickhouse + - Couchbase + - DB2 + - Dynalite + - Elasticsearch + - GCloud + - HiveMQ + - InfluxDB + - K3S + - Kafka + - LocalStack + - MariaDB + - MockServer + - MongoDB + - MSSQLServer + - MySQL + - Neo4j + - NGINX + - Oracle-XE + - OrientDB + - PostgreSQL + - Presto + - Pulsar + - RabbitMQ + - Selenium + - Solr + - ToxiProxy + - Trino + - Vault + - New Module + - type: textarea + id: problem + attributes: + label: Problem + description: Is this feature related to a problem? Please describe it. + validations: + required: true + - type: textarea + id: solution + attributes: + label: Solution + description: What's the proposed solution for this feature? + validations: + required: true + - type: textarea + id: benefit + attributes: + label: Benefit + description: What's the benefit of addng this feature to the project? + validations: + required: true + - type: textarea + id: alternatives + attributes: + label: Benefit + description: Are there other alternatives? Please describe them. + validations: + required: true + - type: dropdown + id: contribute + attributes: + label: Would you like to help contributing this feature? + options: + - 'Yes' + - 'No' + validations: + required: true From c769e64d94a1d756245c53a62ab17670fcfff78c Mon Sep 17 00:00:00 2001 From: Hyeonmin Park Date: Thu, 16 Jun 2022 23:54:24 +0900 Subject: [PATCH 0232/3231] Remove free disk space check log in example (#5502) --- docs/features/configuration.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/features/configuration.md b/docs/features/configuration.md index 1761d1a2a71..cccae7a86df 100644 --- a/docs/features/configuration.md +++ b/docs/features/configuration.md @@ -30,7 +30,6 @@ Before running any containers Testcontainers will perform a set of startup check ``` ℹ︎ Checking the system... ✔ Docker version should be at least 1.6.0 - ✔ Docker environment should have more than 2GB free disk space ✔ File should be mountable ✔ A port exposed by a docker container should be accessible ``` From d57d7e940a02098b0f75c0599d4d4e8614e9c970 Mon Sep 17 00:00:00 2001 From: Cheng Pan Date: Fri, 17 Jun 2022 07:00:50 +0800 Subject: [PATCH 0233/3231] Docs: Add Spark ClickHouse Connector use case (#5270) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Eddú Meléndez Gonzales --- docs/index.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/index.md b/docs/index.md index 0ad72bcb400..3a99c88a5b1 100644 --- a/docs/index.md +++ b/docs/index.md @@ -202,6 +202,7 @@ A huge thank you to our sponsors: * [Byzer](https://www.byzer.org/home) - Integration tests for Data and AI platforms are based on multiple versions of Byzer, Ray and Apache Spark. * [Apache SeaTunnel](https://github.com/apache/incubator-seatunnel) - Integration testing with different datasource. * [Bucket4j](https://github.com/bucket4j/bucket4j) - Java rate-limiting library based on the token-bucket algorithm. +* [Spark ClickHouse Connector](https://github.com/housepower/spark-clickhouse-connector) - Integration tests for Apache Spark with both single node ClickHouse instance and multi-node ClickHouse cluster. ## License From 5dc5c21b3f4ae29fcf05d7c6ba9b287b65cc2517 Mon Sep 17 00:00:00 2001 From: Cristian Briscaru <39033668+cristianbriscaru@users.noreply.github.com> Date: Fri, 17 Jun 2022 00:23:43 +0100 Subject: [PATCH 0234/3231] adds docs cockroach jdbc example (#5100) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Eddú Meléndez Gonzales --- docs/modules/databases/jdbc.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/modules/databases/jdbc.md b/docs/modules/databases/jdbc.md index b2a1588ae22..3c52dd1d8a8 100644 --- a/docs/modules/databases/jdbc.md +++ b/docs/modules/databases/jdbc.md @@ -47,6 +47,10 @@ Insert `tc:` after `jdbc:` as follows. Note that the hostname, port and database `jdbc:tc:trino:352://localhost/memory/default` +#### Using CockroachDB + +`jdbc:tc:cockroach:v21.2.3:///databasename` + ### Using a classpath init script Testcontainers can run an init script after the database container is started, but before your code is given a connection to it. The script must be on the classpath, and is referenced as follows: From 745bfc337071ffec7289758df0fe136c2c307430 Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Mon, 20 Jun 2022 21:45:49 +0200 Subject: [PATCH 0235/3231] Add color for links (#5508) The rebranding of the docs removed any special rendering of links, so they became invisible to users. This change adds an explicit color for links (with the color taken from the TC logo). This also implicitly highlights the currently selected page in the side-navigation panel. --- docs/css/extra.css | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/css/extra.css b/docs/css/extra.css index 4c11e3df748..e97480c5341 100644 --- a/docs/css/extra.css +++ b/docs/css/extra.css @@ -5,6 +5,7 @@ h1, h2, h3, h4, h5, h6 { } [data-md-color-scheme="testcontainers"] { - --md-primary-fg-color: #291A3F; + --md-primary-fg-color: #291A3F; --md-accent-fg-color: #291A3F; + --md-typeset-a-color: #0C94AA; } From 2afe714517640b24a404124dce23fe49bbe910f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Boschi?= Date: Tue, 21 Jun 2022 01:41:15 +0200 Subject: [PATCH 0236/3231] Pulsar: add flag to enable transactions and set configuration (#5479) * Add `withTransactions()` method to enable transactions on Pulsar container * Set default version to latest released (2.10.0) * New docker command that enables the user to easily change configuration parameters * Added new tests to cover new methods * Improved the documentation with simple usage and new methods --- docs/modules/pulsar.md | 46 +++++++ modules/pulsar/build.gradle | 4 +- .../containers/PulsarContainer.java | 57 +++++++-- .../containers/PulsarContainerTest.java | 113 ++++++++++++++++-- 4 files changed, 195 insertions(+), 25 deletions(-) diff --git a/docs/modules/pulsar.md b/docs/modules/pulsar.md index 6d16a248872..f74c315cbdc 100644 --- a/docs/modules/pulsar.md +++ b/docs/modules/pulsar.md @@ -1,5 +1,51 @@ # Apache Pulsar Module +Testcontainers can be used to automatically create [Apache Pulsar](https://pulsar.apache.org) containers without external services. + +It's based on the official Apache Pulsar docker image, it is recommended to read the [official guide](https://pulsar.apache.org/docs/next/getting-started-docker/). + +## Example + +Create a `PulsarContainer` to use it in your tests: + + +[Create a Pulsar container](../../modules/pulsar/src/test/java/org/testcontainers/containers/PulsarContainerTest.java) inside_block:constructorWithVersion + + +Then you can retrieve the broker and the admin url: + + +[Get broker and admin urls](../../modules/pulsar/src/test/java/org/testcontainers/containers/PulsarContainerTest.java) inside_block:coordinates + + +## Options + +### Configuration +If you need to set Pulsar configuration variables you can use the native APIs and set each variable with `PULSAR_PREFIX_` as prefix. + +For example, if you want to enable `brokerDeduplicationEnabled`: + + +[Set configuration variables](../../modules/pulsar/src/test/java/org/testcontainers/containers/PulsarContainerTest.java) inside_block:constructorWithEnv + + +### Pulsar IO + +If you need to test Pulsar IO framework you can enable the Pulsar Functions Worker: + + +[Create a Pulsar container with functions worker](../../modules/pulsar/src/test/java/org/testcontainers/containers/PulsarContainerTest.java) inside_block:constructorWithFunctionsWorker + + +### Pulsar Transactions + +If you need to test Pulsar Transactions you can enable the transactions feature: + + +[Create a Pulsar container with transactions](../../modules/pulsar/src/test/java/org/testcontainers/containers/PulsarContainerTest.java) inside_block:constructorWithTransactions + + + ## Adding this module to your project dependencies Add the following dependency to your `pom.xml`/`build.gradle` file: diff --git a/modules/pulsar/build.gradle b/modules/pulsar/build.gradle index 69c63cc3033..a1b24efba37 100644 --- a/modules/pulsar/build.gradle +++ b/modules/pulsar/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Pulsar" dependencies { api project(':testcontainers') - testImplementation group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.7.4' + testImplementation group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.10.0' testImplementation group: 'org.assertj', name: 'assertj-core', version: '3.23.1' - testImplementation group: 'org.apache.pulsar', name: 'pulsar-client-admin', version: '2.7.4' + testImplementation group: 'org.apache.pulsar', name: 'pulsar-client-admin', version: '2.10.0' } diff --git a/modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java b/modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java index 9b14e90d736..069fccaae65 100644 --- a/modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java +++ b/modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java @@ -2,8 +2,12 @@ import org.testcontainers.containers.wait.strategy.Wait; import org.testcontainers.containers.wait.strategy.WaitAllStrategy; +import org.testcontainers.containers.wait.strategy.WaitStrategy; import org.testcontainers.utility.DockerImageName; +import java.util.ArrayList; +import java.util.List; + /** * This container wraps Apache Pulsar running in standalone mode */ @@ -15,13 +19,21 @@ public class PulsarContainer extends GenericContainer { public static final String METRICS_ENDPOINT = "/metrics"; + /** + * See SystemTopicNames. + */ + private static final String TRANSACTION_TOPIC_ENDPOINT = + "/admin/v2/persistent/pulsar/system/transaction_coordinator_assign/partitions"; + private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("apachepulsar/pulsar"); @Deprecated - private static final String DEFAULT_TAG = "2.2.0"; + private static final String DEFAULT_TAG = "2.10.0"; private boolean functionsWorkerEnabled = false; + private boolean transactionsEnabled = false; + /** * @deprecated use {@link PulsarContainer(DockerImageName)} instead */ @@ -41,24 +53,13 @@ public PulsarContainer(String pulsarVersion) { public PulsarContainer(final DockerImageName dockerImageName) { super(dockerImageName); dockerImageName.assertCompatibleWith(DockerImageName.parse("apachepulsar/pulsar")); - withExposedPorts(BROKER_PORT, BROKER_HTTP_PORT); - withCommand("/pulsar/bin/pulsar", "standalone", "--no-functions-worker", "-nss"); - waitingFor(Wait.forHttp(METRICS_ENDPOINT).forStatusCode(200).forPort(BROKER_HTTP_PORT)); } @Override protected void configure() { super.configure(); - - if (functionsWorkerEnabled) { - withCommand("/pulsar/bin/pulsar", "standalone"); - waitingFor( - new WaitAllStrategy() - .withStrategy(waitStrategy) - .withStrategy(Wait.forLogMessage(".*Function worker service started.*", 1)) - ); - } + setupCommandAndEnv(); } public PulsarContainer withFunctionsWorker() { @@ -66,6 +67,11 @@ public PulsarContainer withFunctionsWorker() { return this; } + public PulsarContainer withTransactions() { + transactionsEnabled = true; + return this; + } + public String getPulsarBrokerUrl() { return String.format("pulsar://%s:%s", getHost(), getMappedPort(BROKER_PORT)); } @@ -73,4 +79,29 @@ public String getPulsarBrokerUrl() { public String getHttpServiceUrl() { return String.format("http://%s:%s", getHost(), getMappedPort(BROKER_HTTP_PORT)); } + + protected void setupCommandAndEnv() { + String standaloneBaseCommand = + "/pulsar/bin/apply-config-from-env.py /pulsar/conf/standalone.conf " + "&& bin/pulsar standalone"; + + if (!functionsWorkerEnabled) { + standaloneBaseCommand += " --no-functions-worker -nss"; + } + + withCommand("/bin/bash", "-c", standaloneBaseCommand); + + List waitStrategies = new ArrayList<>(); + waitStrategies.add(Wait.defaultWaitStrategy()); + waitStrategies.add(Wait.forHttp(METRICS_ENDPOINT).forStatusCode(200).forPort(BROKER_HTTP_PORT)); + if (transactionsEnabled) { + withEnv("PULSAR_PREFIX_transactionCoordinatorEnabled", "true"); + waitStrategies.add(Wait.forHttp(TRANSACTION_TOPIC_ENDPOINT).forStatusCode(200).forPort(BROKER_HTTP_PORT)); + } + if (functionsWorkerEnabled) { + waitStrategies.add(Wait.forLogMessage(".*Function worker service started.*", 1)); + } + final WaitAllStrategy compoundedWaitStrategy = new WaitAllStrategy(); + waitStrategies.forEach(compoundedWaitStrategy::withStrategy); + waitingFor(compoundedWaitStrategy); + } } diff --git a/modules/pulsar/src/test/java/org/testcontainers/containers/PulsarContainerTest.java b/modules/pulsar/src/test/java/org/testcontainers/containers/PulsarContainerTest.java index 6d739dfbf98..91397d00c40 100644 --- a/modules/pulsar/src/test/java/org/testcontainers/containers/PulsarContainerTest.java +++ b/modules/pulsar/src/test/java/org/testcontainers/containers/PulsarContainerTest.java @@ -6,6 +6,9 @@ import org.apache.pulsar.client.api.Message; import org.apache.pulsar.client.api.Producer; import org.apache.pulsar.client.api.PulsarClient; +import org.apache.pulsar.client.api.Schema; +import org.apache.pulsar.client.api.SubscriptionInitialPosition; +import org.apache.pulsar.client.api.transaction.Transaction; import org.junit.Test; import org.testcontainers.utility.DockerImageName; @@ -19,11 +22,33 @@ public class PulsarContainerTest { public static final String TEST_TOPIC = "test_topic"; - private static final DockerImageName PULSAR_IMAGE = DockerImageName.parse("apachepulsar/pulsar:2.2.0"); + private static final DockerImageName PULSAR_IMAGE = DockerImageName.parse("apachepulsar/pulsar:2.10.0"); @Test public void testUsage() throws Exception { - try (PulsarContainer pulsar = new PulsarContainer(PULSAR_IMAGE)) { + try ( + // do not use PULSAR_IMAGE to make the doc looks easier + // constructorWithVersion { + PulsarContainer pulsar = new PulsarContainer(DockerImageName.parse("apachepulsar/pulsar:2.10.0")); + // } + ) { + pulsar.start(); + // coordinates { + final String pulsarBrokerUrl = pulsar.getPulsarBrokerUrl(); + final String httpServiceUrl = pulsar.getHttpServiceUrl(); + // } + testPulsarFunctionality(pulsarBrokerUrl); + } + } + + @Test + public void envVarsUsage() throws Exception { + try ( + // constructorWithEnv { + PulsarContainer pulsar = new PulsarContainer(PULSAR_IMAGE) + .withEnv("PULSAR_PREFIX_brokerDeduplicationEnabled", "true"); + // } + ) { pulsar.start(); testPulsarFunctionality(pulsar.getPulsarBrokerUrl()); } @@ -31,24 +56,69 @@ public void testUsage() throws Exception { @Test public void shouldNotEnableFunctionsWorkerByDefault() throws Exception { - try (PulsarContainer pulsar = new PulsarContainer("2.5.1")) { + try (PulsarContainer pulsar = new PulsarContainer(PULSAR_IMAGE)) { pulsar.start(); - PulsarAdmin pulsarAdmin = PulsarAdmin.builder().serviceHttpUrl(pulsar.getHttpServiceUrl()).build(); - - assertThatThrownBy(() -> pulsarAdmin.functions().getFunctions("public", "default")) - .isInstanceOf(PulsarAdminException.class); + try (PulsarAdmin pulsarAdmin = PulsarAdmin.builder().serviceHttpUrl(pulsar.getHttpServiceUrl()).build()) { + assertThatThrownBy(() -> pulsarAdmin.functions().getFunctions("public", "default")) + .isInstanceOf(PulsarAdminException.class); + } } } @Test public void shouldWaitForFunctionsWorkerStarted() throws Exception { - try (PulsarContainer pulsar = new PulsarContainer("2.5.1").withFunctionsWorker()) { + try ( + // constructorWithFunctionsWorker { + PulsarContainer pulsar = new PulsarContainer(PULSAR_IMAGE).withFunctionsWorker(); + // } + ) { + pulsar.start(); + + try (PulsarAdmin pulsarAdmin = PulsarAdmin.builder().serviceHttpUrl(pulsar.getHttpServiceUrl()).build()) { + assertThat(pulsarAdmin.functions().getFunctions("public", "default")).hasSize(0); + } + } + } + + @Test + public void testTransactions() throws Exception { + try ( + // constructorWithTransactions { + PulsarContainer pulsar = new PulsarContainer(PULSAR_IMAGE).withTransactions(); + // } + ) { pulsar.start(); - PulsarAdmin pulsarAdmin = PulsarAdmin.builder().serviceHttpUrl(pulsar.getHttpServiceUrl()).build(); + try (PulsarAdmin pulsarAdmin = PulsarAdmin.builder().serviceHttpUrl(pulsar.getHttpServiceUrl()).build()) { + assertThat( + pulsarAdmin + .topics() + .getList("pulsar/system") + .contains("persistent://pulsar/system/transaction_coordinator_assign-partition-0") + ) + .isTrue(); + } + testTransactionFunctionality(pulsar.getPulsarBrokerUrl()); + } + } + + @Test + public void testTransactionsAndFunctionsWorker() throws Exception { + try (PulsarContainer pulsar = new PulsarContainer(PULSAR_IMAGE).withTransactions().withFunctionsWorker()) { + pulsar.start(); - assertThat(pulsarAdmin.functions().getFunctions("public", "default")).hasSize(0); + try (PulsarAdmin pulsarAdmin = PulsarAdmin.builder().serviceHttpUrl(pulsar.getHttpServiceUrl()).build();) { + assertThat( + pulsarAdmin + .topics() + .getList("pulsar/system") + .contains("persistent://pulsar/system/transaction_coordinator_assign-partition-0") + ) + .isTrue(); + assertThat(pulsarAdmin.functions().getFunctions("public", "default")).hasSize(0); + } + testTransactionFunctionality(pulsar.getPulsarBrokerUrl()); } } @@ -65,4 +135,27 @@ protected void testPulsarFunctionality(String pulsarBrokerUrl) throws Exception assertThat(new String(message.getData())).isEqualTo("test containers"); } } + + protected void testTransactionFunctionality(String pulsarBrokerUrl) throws Exception { + try ( + PulsarClient client = PulsarClient.builder().serviceUrl(pulsarBrokerUrl).enableTransaction(true).build(); + Consumer consumer = client + .newConsumer(Schema.STRING) + .topic("transaction-topic") + .subscriptionInitialPosition(SubscriptionInitialPosition.Earliest) + .subscriptionName("test-transaction-sub") + .subscribe(); + Producer producer = client + .newProducer(Schema.STRING) + .sendTimeout(0, TimeUnit.SECONDS) + .topic("transaction-topic") + .create() + ) { + final Transaction transaction = client.newTransaction().build().get(); + producer.newMessage(transaction).value("first").send(); + transaction.commit(); + Message message = consumer.receive(); + assertThat(message.getValue()).isEqualTo("first"); + } + } } From a2acf3cc6f828ad81a92eb4cc624ee9ddd29c2e2 Mon Sep 17 00:00:00 2001 From: Daniel Shuy Date: Tue, 21 Jun 2022 07:43:11 +0800 Subject: [PATCH 0237/3231] Feature template: Fix typo in Alternatives section label (#5505) --- .github/ISSUE_TEMPLATE/feature.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/feature.yaml b/.github/ISSUE_TEMPLATE/feature.yaml index 32edb0d25f5..9e79553d357 100644 --- a/.github/ISSUE_TEMPLATE/feature.yaml +++ b/.github/ISSUE_TEMPLATE/feature.yaml @@ -70,7 +70,7 @@ body: - type: textarea id: alternatives attributes: - label: Benefit + label: Alternatives description: Are there other alternatives? Please describe them. validations: required: true From 00faa95834120b482117d7d0363e9b713adb18eb Mon Sep 17 00:00:00 2001 From: Yannick Weber Date: Wed, 22 Jun 2022 10:56:25 +0200 Subject: [PATCH 0238/3231] [HiveMQ] do not delete /opt/hivemq/temp-extensions in startup command (#5509) * do not delete /opt/hivemq/temp-extensions in startup command * command does not get stuck when '/opt/hivemq/temp-extensions' does not exist --- .../main/java/org/testcontainers/hivemq/HiveMQContainer.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/modules/hivemq/src/main/java/org/testcontainers/hivemq/HiveMQContainer.java b/modules/hivemq/src/main/java/org/testcontainers/hivemq/HiveMQContainer.java index 242cc7bdb43..8a2832f65bd 100644 --- a/modules/hivemq/src/main/java/org/testcontainers/hivemq/HiveMQContainer.java +++ b/modules/hivemq/src/main/java/org/testcontainers/hivemq/HiveMQContainer.java @@ -123,8 +123,7 @@ protected void configure() { "-c", removeCommand + "cp -r '/opt/hivemq/temp-extensions/'* /opt/hivemq/extensions/ " + - "; rm -rf /opt/hivemq/temp-extensions/** " + - "&& chmod -R 777 /opt/hivemq/extensions " + + "; chmod -R 777 /opt/hivemq/extensions " + "&& /opt/docker-entrypoint.sh /opt/hivemq/bin/run.sh" ); } From 1f7dc46e2f48d76e6770dce10479a2c63d30bad1 Mon Sep 17 00:00:00 2001 From: Jaromir Hamala Date: Wed, 22 Jun 2022 19:10:26 +0200 Subject: [PATCH 0239/3231] Use sshd container 1.1.0 (#5486) --- .../testcontainers/containers/PortForwardingContainer.java | 7 +++++-- docs/features/configuration.md | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/org/testcontainers/containers/PortForwardingContainer.java b/core/src/main/java/org/testcontainers/containers/PortForwardingContainer.java index 22d364c0762..4a99ccacd91 100644 --- a/core/src/main/java/org/testcontainers/containers/PortForwardingContainer.java +++ b/core/src/main/java/org/testcontainers/containers/PortForwardingContainer.java @@ -31,14 +31,17 @@ public enum PortForwardingContainer { private Connection createSSHSession() { String password = UUID.randomUUID().toString(); container = - new GenericContainer<>(DockerImageName.parse("testcontainers/sshd:1.0.0")) + new GenericContainer<>(DockerImageName.parse("testcontainers/sshd:1.1.0")) .withExposedPorts(22) .withEnv("PASSWORD", password) .withCommand( "sh", "-c", // Disable ipv6 & Make it listen on all interfaces, not just localhost - "echo \"root:$PASSWORD\" | chpasswd && /usr/sbin/sshd -D -o PermitRootLogin=yes -o AddressFamily=inet -o GatewayPorts=yes" + // Enable algorithms supported by our ssh client library + "echo \"root:$PASSWORD\" | chpasswd && /usr/sbin/sshd -D -o PermitRootLogin=yes " + + "-o AddressFamily=inet -o GatewayPorts=yes -o AllowAgentForwarding=yes -o AllowTcpForwarding=yes " + + "-o KexAlgorithms=+diffie-hellman-group1-sha1 -o HostkeyAlgorithms=+ssh-rsa " ); container.start(); diff --git a/docs/features/configuration.md b/docs/features/configuration.md index cccae7a86df..fbb369fe6a6 100644 --- a/docs/features/configuration.md +++ b/docs/features/configuration.md @@ -52,7 +52,7 @@ Some companies disallow the usage of Docker Hub, but you can override `*.image` > **tinyimage.container.image = alpine:3.14** > Used to check whether images can be pulled at startup, and always required (unless [startup checks are disabled](#disabling-the-startup-checks)) -> **sshd.container.image = testcontainers/sshd:1.0.0** +> **sshd.container.image = testcontainers/sshd:1.1.0** > Required if [exposing host ports to containers](./networking.md#exposing-host-ports-to-the-container) > **vncrecorder.container.image = testcontainers/vnc-recorder:1.1.0** From cfc64bd2997000f67c46fa891dc402fd4ddbc5b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Wed, 22 Jun 2022 12:56:20 -0500 Subject: [PATCH 0240/3231] Add clickhouse/clickhouse-server compatibility (#5488) `clickhouse` was initially developed by Yandex and now it is OpenSource. See more [here](https://clickhouse.com/blog/yandex-opensources-click-house/) For backward compatibility, we will be supporting the image provided by Yandex too. --- .../containers/ClickHouseContainer.java | 4 +++- .../testcontainers/ClickhouseTestImages.java | 3 ++- .../clickhouse/SimpleClickhouseTest.java | 20 ++++++++++++++++++- 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/modules/clickhouse/src/main/java/org/testcontainers/containers/ClickHouseContainer.java b/modules/clickhouse/src/main/java/org/testcontainers/containers/ClickHouseContainer.java index 4528c48e935..16bbeef6c81 100644 --- a/modules/clickhouse/src/main/java/org/testcontainers/containers/ClickHouseContainer.java +++ b/modules/clickhouse/src/main/java/org/testcontainers/containers/ClickHouseContainer.java @@ -13,6 +13,8 @@ public class ClickHouseContainer extends JdbcDatabaseContainer { private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("yandex/clickhouse-server"); + private static final DockerImageName CLICKHOUSE_IMAGE_NAME = DockerImageName.parse("clickhouse/clickhouse-server"); + @Deprecated public static final String IMAGE = DEFAULT_IMAGE_NAME.getUnversionedPart(); @@ -49,7 +51,7 @@ public ClickHouseContainer(String dockerImageName) { public ClickHouseContainer(final DockerImageName dockerImageName) { super(dockerImageName); - dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); + dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME, CLICKHOUSE_IMAGE_NAME); withExposedPorts(HTTP_PORT, NATIVE_PORT); waitingFor( diff --git a/modules/clickhouse/src/test/java/org/testcontainers/ClickhouseTestImages.java b/modules/clickhouse/src/test/java/org/testcontainers/ClickhouseTestImages.java index 961df045bd1..a6707705ff8 100644 --- a/modules/clickhouse/src/test/java/org/testcontainers/ClickhouseTestImages.java +++ b/modules/clickhouse/src/test/java/org/testcontainers/ClickhouseTestImages.java @@ -3,5 +3,6 @@ import org.testcontainers.utility.DockerImageName; public interface ClickhouseTestImages { - DockerImageName CLICKHOUSE_IMAGE = DockerImageName.parse("yandex/clickhouse-server:18.10.3"); + DockerImageName YANDEX_CLICKHOUSE_IMAGE = DockerImageName.parse("yandex/clickhouse-server:18.10.3"); + DockerImageName CLICKHOUSE_IMAGE = DockerImageName.parse("clickhouse/clickhouse-server:21.9.2-alpine"); } diff --git a/modules/clickhouse/src/test/java/org/testcontainers/junit/clickhouse/SimpleClickhouseTest.java b/modules/clickhouse/src/test/java/org/testcontainers/junit/clickhouse/SimpleClickhouseTest.java index d2375891f8c..2f6edcc722d 100644 --- a/modules/clickhouse/src/test/java/org/testcontainers/junit/clickhouse/SimpleClickhouseTest.java +++ b/modules/clickhouse/src/test/java/org/testcontainers/junit/clickhouse/SimpleClickhouseTest.java @@ -1,20 +1,38 @@ package org.testcontainers.junit.clickhouse; import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; import org.testcontainers.ClickhouseTestImages; import org.testcontainers.containers.ClickHouseContainer; import org.testcontainers.db.AbstractContainerDatabaseTest; +import org.testcontainers.utility.DockerImageName; import java.sql.ResultSet; import java.sql.SQLException; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +@RunWith(Parameterized.class) public class SimpleClickhouseTest extends AbstractContainerDatabaseTest { + private final DockerImageName imageName; + + public SimpleClickhouseTest(DockerImageName imageName) { + this.imageName = imageName; + } + + @Parameterized.Parameters(name = "{0}") + public static Object[][] data() { + return new Object[][] { // + { ClickhouseTestImages.CLICKHOUSE_IMAGE }, + { ClickhouseTestImages.YANDEX_CLICKHOUSE_IMAGE }, + }; + } + @Test public void testSimple() throws SQLException { - try (ClickHouseContainer clickhouse = new ClickHouseContainer(ClickhouseTestImages.CLICKHOUSE_IMAGE)) { + try (ClickHouseContainer clickhouse = new ClickHouseContainer(this.imageName)) { clickhouse.start(); ResultSet resultSet = performQuery(clickhouse, "SELECT 1"); From a9c2e9e148097f4996dc7a96f85fced1ee857ddc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Wed, 22 Jun 2022 13:30:09 -0500 Subject: [PATCH 0241/3231] [LocalStack] Remove usage of deprecated methods (#5491) Improve documentation and tests avoiding usage of deprecated methods. --- docs/modules/localstack.md | 35 +++------ .../localstack/LocalStackContainer.java | 5 +- .../containers/localstack/LegacyModeTest.java | 38 +++++++--- .../localstack/LocalstackContainerTest.java | 75 ++++++++++++++++--- 4 files changed, 102 insertions(+), 51 deletions(-) diff --git a/docs/modules/localstack.md b/docs/modules/localstack.md index b551536af74..dcc474e484e 100644 --- a/docs/modules/localstack.md +++ b/docs/modules/localstack.md @@ -12,33 +12,18 @@ DockerImageName localstackImage = DockerImageName.parse("localstack/localstack:0 @Rule public LocalStackContainer localstack = new LocalStackContainer(localstackImage) .withServices(S3); - -@Test -public void someTestMethod() { - // AWS SDK v1 - AmazonS3 s3 = AmazonS3ClientBuilder - .standard() - .withEndpointConfiguration(localstack.getEndpointConfiguration(S3)) - .withCredentials(localstack.getDefaultCredentialsProvider()) - .build(); - - s3.createBucket("foo"); - s3.putObject("foo", "bar", "baz"); - - // AWS SDK v2 - S3Client s3 = S3Client - .builder() - .endpointOverride(localstack.getEndpointOverride(LocalStackContainer.Service.S3)) - .credentialsProvider(StaticCredentialsProvider.create(AwsBasicCredentials.create( - localstack.getAccessKey(), localstack.getSecretKey() - ))) - .region(Region.of(localstack.getRegion())) - .build(); - - s3.createBucket(b -> b.bucket("foo")); - s3.putObject(b -> b.bucket("foo").key("bar"), RequestBody.fromBytes("baz".getBytes())); ``` +## Creating a client using AWS SDK + + +[AWS SDK V1](../../modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java) inside_block:with_aws_sdk_v1 + + + +[AWS SDK V2](../../modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java) inside_block:with_aws_sdk_v2 + + Environment variables listed in [Localstack's README](https://github.com/localstack/localstack#configurations) may be used to customize Localstack's configuration. Use the `.withEnv(key, value)` method on `LocalStackContainer` to apply configuration settings. diff --git a/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java b/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java index f3f905b84f6..146cb3200f1 100644 --- a/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java +++ b/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java @@ -27,10 +27,7 @@ /** *

Container for LocalStack, 'A fully functional local AWS cloud stack'.

*

{@link LocalStackContainer#withServices(Service...)} should be used to select which services - * are to be launched. See {@link Service} for available choices. It is advised that - * {@link LocalStackContainer#getEndpointConfiguration(Service)} and - * {@link LocalStackContainer#getDefaultCredentialsProvider()} - * be used to obtain compatible endpoint configuration and credentials, respectively.

+ * are to be launched. See {@link Service} for available choices. */ @Slf4j public class LocalStackContainer extends GenericContainer { diff --git a/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LegacyModeTest.java b/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LegacyModeTest.java index a757c11e928..c075d2805d6 100644 --- a/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LegacyModeTest.java +++ b/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LegacyModeTest.java @@ -1,5 +1,6 @@ package org.testcontainers.containers.localstack; +import com.amazonaws.client.builder.AwsClientBuilder; import com.github.dockerjava.api.DockerClient; import lombok.AllArgsConstructor; import org.junit.BeforeClass; @@ -8,6 +9,7 @@ import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.testcontainers.DockerClientFactory; +import org.testcontainers.containers.localstack.LocalStackContainer.Service; import org.testcontainers.images.RemoteDockerImage; import org.testcontainers.utility.DockerImageName; @@ -46,20 +48,28 @@ public static Iterable constructors() { @Test public void samePortIsExposedForAllServices() { - localstack.withServices(LocalStackContainer.Service.S3, LocalStackContainer.Service.SQS); + localstack.withServices(Service.S3, Service.SQS); localstack.start(); try { assertTrue("A single port is exposed", localstack.getExposedPorts().size() == 1); assertEquals( "Endpoint overrides are different", - localstack.getEndpointOverride(LocalStackContainer.Service.S3).toString(), - localstack.getEndpointOverride(LocalStackContainer.Service.SQS).toString() + localstack.getEndpointOverride(Service.S3).toString(), + localstack.getEndpointOverride(Service.SQS).toString() ); assertEquals( "Endpoint configuration have different endpoints", - localstack.getEndpointConfiguration(LocalStackContainer.Service.S3).getServiceEndpoint(), - localstack.getEndpointConfiguration(LocalStackContainer.Service.SQS).getServiceEndpoint() + new AwsClientBuilder.EndpointConfiguration( + localstack.getEndpointOverride(Service.S3).toString(), + localstack.getRegion() + ) + .getServiceEndpoint(), + new AwsClientBuilder.EndpointConfiguration( + localstack.getEndpointOverride(Service.SQS).toString(), + localstack.getRegion() + ) + .getServiceEndpoint() ); } finally { localstack.stop(); @@ -103,20 +113,28 @@ public static Iterable constructors() { @Test public void differentPortsAreExposed() { - localstack.withServices(LocalStackContainer.Service.S3, LocalStackContainer.Service.SQS); + localstack.withServices(Service.S3, Service.SQS); localstack.start(); try { assertTrue("Multiple ports are exposed", localstack.getExposedPorts().size() > 1); assertNotEquals( "Endpoint overrides are different", - localstack.getEndpointOverride(LocalStackContainer.Service.S3).toString(), - localstack.getEndpointOverride(LocalStackContainer.Service.SQS).toString() + localstack.getEndpointOverride(Service.S3).toString(), + localstack.getEndpointOverride(Service.SQS).toString() ); assertNotEquals( "Endpoint configuration have different endpoints", - localstack.getEndpointConfiguration(LocalStackContainer.Service.S3).getServiceEndpoint(), - localstack.getEndpointConfiguration(LocalStackContainer.Service.SQS).getServiceEndpoint() + new AwsClientBuilder.EndpointConfiguration( + localstack.getEndpointOverride(Service.S3).toString(), + localstack.getRegion() + ) + .getServiceEndpoint(), + new AwsClientBuilder.EndpointConfiguration( + localstack.getEndpointOverride(Service.SQS).toString(), + localstack.getRegion() + ) + .getServiceEndpoint() ); } finally { localstack.stop(); diff --git a/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java b/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java index 3b85c69ca34..950f29866ba 100644 --- a/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java +++ b/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java @@ -1,5 +1,7 @@ package org.testcontainers.containers.localstack; +import com.amazonaws.auth.AWSStaticCredentialsProvider; +import com.amazonaws.auth.BasicAWSCredentials; import com.amazonaws.client.builder.AwsClientBuilder; import com.amazonaws.services.kms.AWSKMS; import com.amazonaws.services.kms.AWSKMSClientBuilder; @@ -73,11 +75,22 @@ public static class WithoutNetwork { @Test public void s3TestOverBridgeNetwork() throws IOException { + // with_aws_sdk_v1 { AmazonS3 s3 = AmazonS3ClientBuilder .standard() - .withEndpointConfiguration(localstack.getEndpointConfiguration(Service.S3)) - .withCredentials(localstack.getDefaultCredentialsProvider()) + .withEndpointConfiguration( + new AwsClientBuilder.EndpointConfiguration( + localstack.getEndpointOverride(Service.S3).toString(), + localstack.getRegion() + ) + ) + .withCredentials( + new AWSStaticCredentialsProvider( + new BasicAWSCredentials(localstack.getAccessKey(), localstack.getSecretKey()) + ) + ) .build(); + // } final String bucketName = "foo"; s3.createBucket(bucketName); @@ -103,6 +116,7 @@ public void s3TestOverBridgeNetwork() throws IOException { @Test public void s3TestUsingAwsSdkV2() { + // with_aws_sdk_v2 { S3Client s3 = S3Client .builder() .endpointOverride(localstack.getEndpointOverride(Service.S3)) @@ -113,6 +127,7 @@ public void s3TestUsingAwsSdkV2() { ) .region(Region.of(localstack.getRegion())) .build(); + // } final String bucketName = "foov2"; s3.createBucket(b -> b.bucket(bucketName)); @@ -126,8 +141,17 @@ public void s3TestUsingAwsSdkV2() { public void sqsTestOverBridgeNetwork() { AmazonSQS sqs = AmazonSQSClientBuilder .standard() - .withEndpointConfiguration(localstack.getEndpointConfiguration(Service.SQS)) - .withCredentials(localstack.getDefaultCredentialsProvider()) + .withEndpointConfiguration( + new AwsClientBuilder.EndpointConfiguration( + localstack.getEndpointOverride(Service.SQS).toString(), + localstack.getRegion() + ) + ) + .withCredentials( + new AWSStaticCredentialsProvider( + new BasicAWSCredentials(localstack.getAccessKey(), localstack.getSecretKey()) + ) + ) .build(); CreateQueueResult queueResult = sqs.createQueue("baz"); @@ -157,8 +181,17 @@ public void sqsTestOverBridgeNetwork() { public void cloudWatchLogsTestOverBridgeNetwork() { AWSLogs logs = AWSLogsClientBuilder .standard() - .withEndpointConfiguration(localstack.getEndpointConfiguration(Service.CLOUDWATCHLOGS)) - .withCredentials(localstack.getDefaultCredentialsProvider()) + .withEndpointConfiguration( + new AwsClientBuilder.EndpointConfiguration( + localstack.getEndpointOverride(Service.CLOUDWATCHLOGS).toString(), + localstack.getRegion() + ) + ) + .withCredentials( + new AWSStaticCredentialsProvider( + new BasicAWSCredentials(localstack.getAccessKey(), localstack.getSecretKey()) + ) + ) .build(); logs.createLogGroup(new CreateLogGroupRequest("foo")); @@ -172,8 +205,17 @@ public void cloudWatchLogsTestOverBridgeNetwork() { public void kmsKeyCreationTest() { AWSKMS awskms = AWSKMSClientBuilder .standard() - .withEndpointConfiguration(localstack.getEndpointConfiguration(Service.KMS)) - .withCredentials(localstack.getDefaultCredentialsProvider()) + .withEndpointConfiguration( + new AwsClientBuilder.EndpointConfiguration( + localstack.getEndpointOverride(Service.KMS).toString(), + localstack.getRegion() + ) + ) + .withCredentials( + new AWSStaticCredentialsProvider( + new BasicAWSCredentials(localstack.getAccessKey(), localstack.getSecretKey()) + ) + ) .build(); String desc = String.format("AWS CMK Description"); @@ -198,8 +240,16 @@ public void samePortIsExposedForAllServices() { ); assertEquals( "Endpoint configuration have different endpoints", - localstack.getEndpointConfiguration(Service.S3).getServiceEndpoint(), - localstack.getEndpointConfiguration(Service.SQS).getServiceEndpoint() + new AwsClientBuilder.EndpointConfiguration( + localstack.getEndpointOverride(Service.S3).toString(), + localstack.getRegion() + ) + .getServiceEndpoint(), + new AwsClientBuilder.EndpointConfiguration( + localstack.getEndpointOverride(Service.SQS).toString(), + localstack.getRegion() + ) + .getServiceEndpoint() ); } } @@ -302,8 +352,9 @@ public static class WithRegion { @Test public void s3EndpointHasProperRegion() { - final AwsClientBuilder.EndpointConfiguration endpointConfiguration = localstack.getEndpointConfiguration( - Service.S3 + final AwsClientBuilder.EndpointConfiguration endpointConfiguration = new AwsClientBuilder.EndpointConfiguration( + localstack.getEndpointOverride(Service.S3).toString(), + localstack.getRegion() ); assertEquals( "The endpoint configuration has right region", From afde5d3cde1657625e890740c9f137344b360cee Mon Sep 17 00:00:00 2001 From: Andrei Nevedomskii Date: Mon, 27 Jun 2022 20:34:49 +0200 Subject: [PATCH 0242/3231] JdbcDatabaseContainer: fix wrong instance call (#5465) createConnection() method calls a wrong driver instance when logging --- .../org/testcontainers/containers/JdbcDatabaseContainer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java index 0b80eaac406..aa397929497 100644 --- a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java +++ b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java @@ -232,7 +232,7 @@ public Connection createConnection(String queryString) throws SQLException, NoDr logger() .debug( "Trying to create JDBC connection using {} to {} with properties: {}", - driver.getClass().getName(), + jdbcDriverInstance.getClass().getName(), url, info ); From c0eb042dabe7b048130f33459d4a542075fb1355 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Mon, 27 Jun 2022 19:52:38 -0500 Subject: [PATCH 0243/3231] Polish examples (#5520) --- .../examples/CucumberTest.java | 2 +- .../{testcontainsers => testcontainers}/examples/Stepdefs.java | 2 +- .../examples/is_search_possible.feature | 0 .../java/org/testcontainers/containers/Neo4jExampleTest.java | 2 +- 4 files changed, 3 insertions(+), 3 deletions(-) rename examples/cucumber/src/test/java/org/{testcontainsers => testcontainers}/examples/CucumberTest.java (84%) rename examples/cucumber/src/test/java/org/{testcontainsers => testcontainers}/examples/Stepdefs.java (98%) rename examples/cucumber/src/test/resources/org/{testcontainsers => testcontainers}/examples/is_search_possible.feature (100%) diff --git a/examples/cucumber/src/test/java/org/testcontainsers/examples/CucumberTest.java b/examples/cucumber/src/test/java/org/testcontainers/examples/CucumberTest.java similarity index 84% rename from examples/cucumber/src/test/java/org/testcontainsers/examples/CucumberTest.java rename to examples/cucumber/src/test/java/org/testcontainers/examples/CucumberTest.java index 0551b4dbf2e..f55919c0fe6 100644 --- a/examples/cucumber/src/test/java/org/testcontainsers/examples/CucumberTest.java +++ b/examples/cucumber/src/test/java/org/testcontainers/examples/CucumberTest.java @@ -1,4 +1,4 @@ -package org.testcontainsers.examples; +package org.testcontainers.examples; import io.cucumber.junit.Cucumber; import io.cucumber.junit.CucumberOptions; diff --git a/examples/cucumber/src/test/java/org/testcontainsers/examples/Stepdefs.java b/examples/cucumber/src/test/java/org/testcontainers/examples/Stepdefs.java similarity index 98% rename from examples/cucumber/src/test/java/org/testcontainsers/examples/Stepdefs.java rename to examples/cucumber/src/test/java/org/testcontainers/examples/Stepdefs.java index ca70173ae92..e1b9c7e81ef 100644 --- a/examples/cucumber/src/test/java/org/testcontainsers/examples/Stepdefs.java +++ b/examples/cucumber/src/test/java/org/testcontainers/examples/Stepdefs.java @@ -1,4 +1,4 @@ -package org.testcontainsers.examples; +package org.testcontainers.examples; import io.cucumber.java.After; import io.cucumber.java.Before; diff --git a/examples/cucumber/src/test/resources/org/testcontainsers/examples/is_search_possible.feature b/examples/cucumber/src/test/resources/org/testcontainers/examples/is_search_possible.feature similarity index 100% rename from examples/cucumber/src/test/resources/org/testcontainsers/examples/is_search_possible.feature rename to examples/cucumber/src/test/resources/org/testcontainers/examples/is_search_possible.feature diff --git a/examples/neo4j-container/src/test/java/org/testcontainers/containers/Neo4jExampleTest.java b/examples/neo4j-container/src/test/java/org/testcontainers/containers/Neo4jExampleTest.java index 7730d4fa8d0..d151e1a3f24 100644 --- a/examples/neo4j-container/src/test/java/org/testcontainers/containers/Neo4jExampleTest.java +++ b/examples/neo4j-container/src/test/java/org/testcontainers/containers/Neo4jExampleTest.java @@ -24,7 +24,7 @@ // junitExample { @Testcontainers -public class Neo4jExampleTest { +class Neo4jExampleTest { @Container private static Neo4jContainer neo4jContainer = From 56d0cf1941ad5faf77d1223766a4a0c495b29f8c Mon Sep 17 00:00:00 2001 From: Dave Maughan Date: Tue, 28 Jun 2022 02:16:44 +0100 Subject: [PATCH 0244/3231] Check admin endpoint instead of metrics for Pulsar WaitStrategy. (#5514) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #5513 Co-authored-by: Eddú Meléndez Gonzales Co-authored-by: Kevin Wittek --- .../testcontainers/containers/PulsarContainer.java | 13 ++++++++++++- .../containers/PulsarContainerTest.java | 11 +++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java b/modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java index 069fccaae65..30c36db01d8 100644 --- a/modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java +++ b/modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java @@ -17,8 +17,14 @@ public class PulsarContainer extends GenericContainer { public static final int BROKER_HTTP_PORT = 8080; + /** + * @deprecated The metrics endpoint is no longer being used for the WaitStrategy. + */ + @Deprecated public static final String METRICS_ENDPOINT = "/metrics"; + private static final String ADMIN_CLUSTERS_ENDPOINT = "/admin/v2/clusters"; + /** * See SystemTopicNames. */ @@ -92,7 +98,12 @@ protected void setupCommandAndEnv() { List waitStrategies = new ArrayList<>(); waitStrategies.add(Wait.defaultWaitStrategy()); - waitStrategies.add(Wait.forHttp(METRICS_ENDPOINT).forStatusCode(200).forPort(BROKER_HTTP_PORT)); + waitStrategies.add( + Wait + .forHttp(ADMIN_CLUSTERS_ENDPOINT) + .forPort(BROKER_HTTP_PORT) + .forResponsePredicate("[\"standalone\"]"::equals) + ); if (transactionsEnabled) { withEnv("PULSAR_PREFIX_transactionCoordinatorEnabled", "true"); waitStrategies.add(Wait.forHttp(TRANSACTION_TOPIC_ENDPOINT).forStatusCode(200).forPort(BROKER_HTTP_PORT)); diff --git a/modules/pulsar/src/test/java/org/testcontainers/containers/PulsarContainerTest.java b/modules/pulsar/src/test/java/org/testcontainers/containers/PulsarContainerTest.java index 91397d00c40..29de6c4c872 100644 --- a/modules/pulsar/src/test/java/org/testcontainers/containers/PulsarContainerTest.java +++ b/modules/pulsar/src/test/java/org/testcontainers/containers/PulsarContainerTest.java @@ -122,6 +122,17 @@ public void testTransactionsAndFunctionsWorker() throws Exception { } } + @Test + public void testClusterFullyInitialized() throws Exception { + try (PulsarContainer pulsar = new PulsarContainer(PULSAR_IMAGE)) { + pulsar.start(); + + try (PulsarAdmin pulsarAdmin = PulsarAdmin.builder().serviceHttpUrl(pulsar.getHttpServiceUrl()).build()) { + assertThat(pulsarAdmin.clusters().getClusters()).hasSize(1).contains("standalone"); + } + } + } + protected void testPulsarFunctionality(String pulsarBrokerUrl) throws Exception { try ( PulsarClient client = PulsarClient.builder().serviceUrl(pulsarBrokerUrl).build(); From 5a2d76fa7f26b5ad1b57c98b2f1cac94ace397cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gonzalo=20Mu=C3=B1oz?= Date: Tue, 28 Jun 2022 06:58:54 +0200 Subject: [PATCH 0245/3231] Switch default image to ghcr.io and update default tag to 2.4.0 (#5173) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Eddú Meléndez --- .../org/testcontainers/containers/ToxiproxyContainer.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/toxiproxy/src/main/java/org/testcontainers/containers/ToxiproxyContainer.java b/modules/toxiproxy/src/main/java/org/testcontainers/containers/ToxiproxyContainer.java index ed8413a2a31..9850d7a0918 100644 --- a/modules/toxiproxy/src/main/java/org/testcontainers/containers/ToxiproxyContainer.java +++ b/modules/toxiproxy/src/main/java/org/testcontainers/containers/ToxiproxyContainer.java @@ -25,6 +25,8 @@ public class ToxiproxyContainer extends GenericContainer { private static final String DEFAULT_TAG = "2.1.0"; + private static final DockerImageName GHCR_IMAGE_NAME = DockerImageName.parse("ghcr.io/shopify/toxiproxy"); + private static final int TOXIPROXY_CONTROL_PORT = 8474; private static final int FIRST_PROXIED_PORT = 8666; @@ -51,7 +53,7 @@ public ToxiproxyContainer(String dockerImageName) { public ToxiproxyContainer(final DockerImageName dockerImageName) { super(dockerImageName); - dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); + dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME, GHCR_IMAGE_NAME); addExposedPorts(TOXIPROXY_CONTROL_PORT); setWaitStrategy(new HttpWaitStrategy().forPath("/version").forPort(TOXIPROXY_CONTROL_PORT)); From e23f378e68938f2a1f459f82d80c9485c7fcf006 Mon Sep 17 00:00:00 2001 From: Andrei Navrotski <6005070+anavrotski@users.noreply.github.com> Date: Tue, 28 Jun 2022 08:32:35 +0300 Subject: [PATCH 0246/3231] [clickhouse] Add new JDBC driver name (#5474) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also, fix the liveness port. Co-authored-by: Sergei Egorov Co-authored-by: Eddú Meléndez --- .../containers/ClickHouseContainer.java | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/modules/clickhouse/src/main/java/org/testcontainers/containers/ClickHouseContainer.java b/modules/clickhouse/src/main/java/org/testcontainers/containers/ClickHouseContainer.java index 16bbeef6c81..4e87c77ee5b 100644 --- a/modules/clickhouse/src/main/java/org/testcontainers/containers/ClickHouseContainer.java +++ b/modules/clickhouse/src/main/java/org/testcontainers/containers/ClickHouseContainer.java @@ -1,13 +1,13 @@ package org.testcontainers.containers; -import com.google.common.collect.Sets; import org.testcontainers.containers.wait.strategy.HttpWaitStrategy; import org.testcontainers.utility.DockerImageName; import java.time.Duration; +import java.util.HashSet; import java.util.Set; -public class ClickHouseContainer extends JdbcDatabaseContainer { +public class ClickHouseContainer extends JdbcDatabaseContainer { public static final String NAME = "clickhouse"; @@ -53,23 +53,27 @@ public ClickHouseContainer(final DockerImageName dockerImageName) { super(dockerImageName); dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME, CLICKHOUSE_IMAGE_NAME); - withExposedPorts(HTTP_PORT, NATIVE_PORT); - waitingFor( + addExposedPorts(HTTP_PORT, NATIVE_PORT); + this.waitStrategy = new HttpWaitStrategy() .forStatusCode(200) - .forResponsePredicate(responseBody -> "Ok.".equals(responseBody)) - .withStartupTimeout(Duration.ofMinutes(1)) - ); + .forResponsePredicate("Ok."::equals) + .withStartupTimeout(Duration.ofMinutes(1)); } @Override public Set getLivenessCheckPortNumbers() { - return Sets.newHashSet(HTTP_PORT); + return new HashSet<>(getMappedPort(HTTP_PORT)); } @Override public String getDriverClassName() { - return DRIVER_CLASS_NAME; + try { + Class.forName(DRIVER_CLASS_NAME); + return DRIVER_CLASS_NAME; + } catch (ClassNotFoundException e) { + return "com.clickhouse.jdbc.ClickHouseDriver"; + } } @Override From 4a14eccad30b0090ea99db7bd1ffa49a94ba37a1 Mon Sep 17 00:00:00 2001 From: Alexander Reelsen Date: Wed, 29 Jun 2022 00:27:00 +0200 Subject: [PATCH 0247/3231] Elasticsearch: Fix startup check for 8.3 and above (#5521) --- .../elasticsearch/ElasticsearchContainer.java | 3 ++- .../ElasticsearchContainerTest.java | 21 ++++++++++++++++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/modules/elasticsearch/src/main/java/org/testcontainers/elasticsearch/ElasticsearchContainer.java b/modules/elasticsearch/src/main/java/org/testcontainers/elasticsearch/ElasticsearchContainer.java index 5a64a5e67a2..f8b9f779519 100644 --- a/modules/elasticsearch/src/main/java/org/testcontainers/elasticsearch/ElasticsearchContainer.java +++ b/modules/elasticsearch/src/main/java/org/testcontainers/elasticsearch/ElasticsearchContainer.java @@ -102,10 +102,11 @@ public ElasticsearchContainer(final DockerImageName dockerImageName) { this.isAtLeastMajorVersion8 = new ComparableVersion(dockerImageName.getVersionPart()).isGreaterThanOrEqualTo("8.0.0"); // regex that + // matches 8.3 JSON logging with started message and some follow up content within the message field // matches 8.0 JSON logging with no whitespace between message field and content // matches 7.x JSON logging with whitespace between message field and content // matches 6.x text logging with node name in brackets and just a 'started' message till the end of the line - String regex = ".*(\"message\":\\s?\"started\".*|] started\n$)"; + String regex = ".*(\"message\":\\s?\"started[\\s?|\"].*|] started\n$)"; setWaitStrategy(new LogMessageWaitStrategy().withRegEx(regex)); if (isAtLeastMajorVersion8) { withPassword(ELASTICSEARCH_DEFAULT_PASSWORD); diff --git a/modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java b/modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java index 4427163ca56..9fcfc45af76 100644 --- a/modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java +++ b/modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java @@ -150,6 +150,20 @@ public void elasticsearchVersion() throws IOException { } } + @Test + public void elasticsearchVersion83() throws IOException { + try ( + ElasticsearchContainer container = new ElasticsearchContainer( + "docker.elastic.co/elasticsearch/elasticsearch:8.3.0" + ) + ) { + container.start(); + Response response = getClient(container).performRequest(new Request("GET", "/")); + assertThat(response.getStatusLine().getStatusCode(), is(200)); + assertThat(EntityUtils.toString(response.getEntity()), containsString("8.3.0")); + } + } + @Test public void elasticsearchOssImage() throws IOException { try ( @@ -406,10 +420,15 @@ private RestClient getClient(ElasticsearchContainer container) { new UsernamePasswordCredentials(ELASTICSEARCH_USERNAME, ELASTICSEARCH_PASSWORD) ); + String protocol = container.caCertAsBytes().isPresent() ? "https://" : "http://"; + client = RestClient - .builder(HttpHost.create(container.getHttpHostAddress())) + .builder(HttpHost.create(protocol + container.getHttpHostAddress())) .setHttpClientConfigCallback(httpClientBuilder -> { + if (container.caCertAsBytes().isPresent()) { + httpClientBuilder.setSSLContext(container.createSslContextFromCa()); + } return httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider); }) .build(); From 3395ceba894b2f58e37524e1df4786f662c4aab5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Wed, 29 Jun 2022 03:46:20 -0500 Subject: [PATCH 0248/3231] Add labeler action (#5516) Triage PRs. --- .github/labeler.yml | 79 +++++++++++++++++++++++++++++++++++ .github/workflows/labeler.yml | 14 +++++++ 2 files changed, 93 insertions(+) create mode 100644 .github/labeler.yml create mode 100644 .github/workflows/labeler.yml diff --git a/.github/labeler.yml b/.github/labeler.yml new file mode 100644 index 00000000000..9293237182e --- /dev/null +++ b/.github/labeler.yml @@ -0,0 +1,79 @@ +"github_actions": + - .github/workflows/* +"gradle-wrapper": + - gradle/wrapper/* + - gradlew + - gradlew.bat +"modules/azure": + - modules/azure/* +"modules/cassandra": + - modules/cassandra/* +"modules/clickhouse": + - modules/clickhouse/* +"modules/cockroachdb": + - modules/cockroachdb/* +"modules/couchbase": + - modules/couchbase/* +"modules/db2": + - modules/db2/* +"modules/dynalite": + - modules/dynalite/* +"modules/elasticsearch": + - modules/elasticsearch/* +"modules/gcloud": + - modules/gcloud/* +"modules/hivemq": + - modules/hivemq/* +"modules/influx": + - modules/influxdb/* +"modules/jdbc": + - modules/jdbc/* +"modules/jupiter": + - modules/junit-jupiter/* +"modules/k3s": + - modules/k3s/* +"modules/kafka": + - modules/kafka/* +"modules/localstack": + - modules/localstack/* +"modules/mariadb": + - modules/mariadb/* +"modules/mockserver": + - modules/mockserver/* +"modules/mongodb": + - modules/mongodb/* +"modules/sql-server": + - modules/mssqlserver/* +"modules/mysql": + - modules/mysql/* +"modules/neo4j": + - modules/neo4j/* +"modules/nginx": + - modules/nginx/* +"modules/oracle": + - modules/oracle-xe/* +"modules/orientdb": + - modules/orientdb/* +"modules/postgres": + - modules/postgresql/* +"modules/presto": + - modules/presto/* +"modules/pulsar": + - modules/pulsar/* +"modules/r2dbc": + - modules/r2dbc/* +"modules/rabbitmq": + - modules/rabbitmq/* +"modules/selenium": + - modules/selenium/* +"modules/solr": + - modules/solr/* +"modules/spock": + - modules/spock/* +"modules/toxiproxy": + - modules/toxiproxy/* +"modules/trino": + - modules/trino/* +"modules/vault": + - modules/vault/* +"type/docs": docs/**/* diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml new file mode 100644 index 00000000000..057208eda32 --- /dev/null +++ b/.github/workflows/labeler.yml @@ -0,0 +1,14 @@ +name: "Pull Request Labeler" +on: +- pull_request_target + +jobs: + triage: + permissions: + contents: read + pull-requests: write + runs-on: ubuntu-latest + steps: + - uses: actions/labeler@v4 + with: + repo-token: "${{ secrets.GITHUB_TOKEN }}" From c562080f1ce487dfedf19e76f7d890e9871ddd03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez?= Date: Wed, 29 Jun 2022 03:51:47 -0500 Subject: [PATCH 0249/3231] Update latest version in mkdocs.yml --- mkdocs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mkdocs.yml b/mkdocs.yml index f6daad68155..21e157969e5 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -102,4 +102,4 @@ nav: - contributing_docs.md - bounty.md extra: - latest_version: 1.17.2 + latest_version: 1.17.3 From fbc5fe4935fd97747a1f7e18dade3132d474045f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez?= Date: Wed, 29 Jun 2022 03:52:58 -0500 Subject: [PATCH 0250/3231] Upgrade testcontainers version to 1.17.3 in japicmp --- gradle/japicmp.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/japicmp.gradle b/gradle/japicmp.gradle index fe3f8881b1c..3c438d340f0 100644 --- a/gradle/japicmp.gradle +++ b/gradle/japicmp.gradle @@ -3,7 +3,7 @@ configurations { } dependencies { - baseline "org.testcontainers:${project.name}:1.17.2", { + baseline "org.testcontainers:${project.name}:1.17.3", { exclude group: "*", module: "*" } } From 1fe0ef23f4851506b56b355a134fb60203554631 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Wed, 29 Jun 2022 11:40:49 -0500 Subject: [PATCH 0251/3231] Improve docs (#5522) Add additional steps for linux and colima --- docs/supported_docker_environment/index.md | 27 ++++++++++++++-------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/docs/supported_docker_environment/index.md b/docs/supported_docker_environment/index.md index 3b82cea2165..aa6c7b1b867 100644 --- a/docs/supported_docker_environment/index.md +++ b/docs/supported_docker_environment/index.md @@ -2,16 +2,25 @@ ## Overview -| Host Operating System / Environment | Minimum recommended docker versions | Known issues / tips | -|-------------------------------------|-----------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| Linux - general | Docker v17.09 | | -| Linux - CircleCI (LXC driver) | Docker v17.09 | The `exec` feature is not compatible with CircleCI. See CircleCI configuration [example](./continuous_integration/circle_ci.md) | -| Linux - within a Docker container | Docker v17.09 | See [Running inside Docker](continuous_integration/dind_patterns.md) for Docker-in-Docker and Docker wormhole patterns | -| Mac OS X - Docker Toolbox | Docker Machine v0.8.0 | | +| Host Operating System / Environment | Minimum recommended docker versions | Known issues / tips | +|-------------------------------------|-----------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| Linux - general | Docker v17.09 | After docker installation, follow [post-installation steps](https://docs.docker.com/engine/install/linux-postinstall/). | +| Linux - CircleCI (LXC driver) | Docker v17.09 | The `exec` feature is not compatible with CircleCI. See CircleCI configuration [example](./continuous_integration/circle_ci.md) | +| Linux - within a Docker container | Docker v17.09 | See [Running inside Docker](continuous_integration/dind_patterns.md) for Docker-in-Docker and Docker wormhole patterns | +| Mac OS X - Docker Toolbox | Docker Machine v0.8.0 | | | Mac OS X - Docker for Mac | v17.09 | *Support is best-efforts at present*. `getTestHostIpAddress()` is [not currently supported](https://github.com/testcontainers/testcontainers-java/issues/166) due to limitations in Docker for Mac. | -| Windows - Docker Toolbox | | *Support is limited at present and this is not currently tested on a regular basis*. | -| Windows - Docker for Windows | | *Support is best-efforts at present.* Only Linux Containers (LCOW) are supported at the moment. See [Windows Support](windows.md) | -| Windows - Windows Subsystem for Linux (WSL) | Docker v17.09 | *Support is best-efforts at present.* Only Linux Containers (LCOW) are supported at the moment. See [Windows Support](windows.md). | +| Windows - Docker Toolbox | | *Support is limited at present and this is not currently tested on a regular basis*. | +| Windows - Docker for Windows | | *Support is best-efforts at present.* Only Linux Containers (LCOW) are supported at the moment. See [Windows Support](windows.md) | +| Windows - Windows Subsystem for Linux (WSL) | Docker v17.09 | *Support is best-efforts at present.* Only Linux Containers (LCOW) are supported at the moment. See [Windows Support](windows.md). | + +## Using Colima? + +In order to run testcontainers against [colima](https://github.com/abiosoft/colima) the env vars bellow should be set + +``` +export TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE=/var/run/docker.sock +export DOCKER_HOST="unix://${HOME}/.colima/docker.sock" +``` ## Docker environment discovery From a2bf452bb5851a9278315b26741984038c66cd1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Fri, 1 Jul 2022 14:48:52 -0500 Subject: [PATCH 0252/3231] Add host os and arch in bug report template (#5525) Co-authored-by: Kevin Wittek --- .github/ISSUE_TEMPLATE/bug_report.yaml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index 80e0b47741d..dc86b416e46 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -65,6 +65,22 @@ body: - 'No' validations: required: true + - type: input + id: host-os + attributes: + label: Host OS + description: Which Operating System are you using? + placeholder: e.g. Linux, Windows + validations: + required: true + - type: input + id: host-arch + attributes: + label: Host Arch + description: Which architecture are you using? + placeholder: e.g. x86, ARM + validations: + required: true - type: textarea id: docker-version attributes: From 0fc238075546f56e22b0fd44ba985c2fcc2f8424 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jul 2022 22:03:51 +0000 Subject: [PATCH 0253/3231] Bump neo4j-java-driver from 4.4.5 to 4.4.6 in /modules/neo4j Bumps [neo4j-java-driver](https://github.com/neo4j/neo4j-java-driver) from 4.4.5 to 4.4.6. - [Release notes](https://github.com/neo4j/neo4j-java-driver/releases) - [Commits](https://github.com/neo4j/neo4j-java-driver/compare/4.4.5...4.4.6) --- updated-dependencies: - dependency-name: org.neo4j.driver:neo4j-java-driver dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/neo4j/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/neo4j/build.gradle b/modules/neo4j/build.gradle index e0a51ea783e..feff7329185 100644 --- a/modules/neo4j/build.gradle +++ b/modules/neo4j/build.gradle @@ -33,6 +33,6 @@ dependencies { api project(":testcontainers") - testImplementation 'org.neo4j.driver:neo4j-java-driver:4.4.5' + testImplementation 'org.neo4j.driver:neo4j-java-driver:4.4.6' testImplementation 'org.assertj:assertj-core:3.23.1' } From 9d63e7fa477e050d2fe1998b6f8824768b667397 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jul 2022 22:03:53 +0000 Subject: [PATCH 0254/3231] Bump neo4j from 3.5.33 to 3.5.34 in /modules/neo4j Bumps [neo4j](https://github.com/neo4j/neo4j) from 3.5.33 to 3.5.34. - [Release notes](https://github.com/neo4j/neo4j/releases) - [Commits](https://github.com/neo4j/neo4j/compare/3.5.33...3.5.34) --- updated-dependencies: - dependency-name: org.neo4j:neo4j dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/neo4j/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/neo4j/build.gradle b/modules/neo4j/build.gradle index e0a51ea783e..779aab2bde8 100644 --- a/modules/neo4j/build.gradle +++ b/modules/neo4j/build.gradle @@ -29,7 +29,7 @@ task customNeo4jPluginJar(type: Jar) { processTestResources.dependsOn customNeo4jPluginJar dependencies { - customNeo4jPluginCompileOnly "org.neo4j:neo4j:3.5.33" + customNeo4jPluginCompileOnly "org.neo4j:neo4j:3.5.34" api project(":testcontainers") From 1748c8ed29ccaf6964614bb97ff0d326675bab31 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jul 2022 22:04:32 +0000 Subject: [PATCH 0255/3231] Bump aws-java-sdk-dynamodb in /modules/dynalite Bumps [aws-java-sdk-dynamodb](https://github.com/aws/aws-sdk-java) from 1.12.232 to 1.12.253. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.232...1.12.253) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-dynamodb dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/dynalite/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/dynalite/build.gradle b/modules/dynalite/build.gradle index 09cb51e10a4..acf379cda79 100644 --- a/modules/dynalite/build.gradle +++ b/modules/dynalite/build.gradle @@ -3,8 +3,8 @@ description = "Testcontainers :: Dynalite" dependencies { api project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.12.232' - testImplementation 'com.amazonaws:aws-java-sdk-dynamodb:1.12.210' + compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.12.253' + testImplementation 'com.amazonaws:aws-java-sdk-dynamodb:1.12.253' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } From 80695194043f8824a28c0aae44659b2f4f5051fc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jul 2022 22:04:34 +0000 Subject: [PATCH 0256/3231] Bump mariadb-java-client from 3.0.5 to 3.0.6 in /modules/mariadb Bumps [mariadb-java-client](https://github.com/mariadb-corporation/mariadb-connector-j) from 3.0.5 to 3.0.6. - [Release notes](https://github.com/mariadb-corporation/mariadb-connector-j/releases) - [Changelog](https://github.com/mariadb-corporation/mariadb-connector-j/blob/master/CHANGELOG.md) - [Commits](https://github.com/mariadb-corporation/mariadb-connector-j/compare/3.0.5...3.0.6) --- updated-dependencies: - dependency-name: org.mariadb.jdbc:mariadb-java-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/mariadb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mariadb/build.gradle b/modules/mariadb/build.gradle index fd534915ba1..ed7f428ba3e 100644 --- a/modules/mariadb/build.gradle +++ b/modules/mariadb/build.gradle @@ -10,7 +10,7 @@ dependencies { compileOnly 'org.mariadb:r2dbc-mariadb:1.0.3' testImplementation project(':jdbc-test') - testImplementation 'org.mariadb.jdbc:mariadb-java-client:3.0.5' + testImplementation 'org.mariadb.jdbc:mariadb-java-client:3.0.6' testImplementation testFixtures(project(':r2dbc')) testImplementation 'org.mariadb:r2dbc-mariadb:1.0.2' From 37afe8e054c764ce2898ce3e7525b56c44fee4a9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jul 2022 22:04:43 +0000 Subject: [PATCH 0257/3231] Bump transport from 7.17.4 to 7.17.5 in /modules/elasticsearch Bumps [transport](https://github.com/elastic/elasticsearch) from 7.17.4 to 7.17.5. - [Release notes](https://github.com/elastic/elasticsearch/releases) - [Changelog](https://github.com/elastic/elasticsearch/blob/master/CHANGELOG.md) - [Commits](https://github.com/elastic/elasticsearch/compare/v7.17.4...v7.17.5) --- updated-dependencies: - dependency-name: org.elasticsearch.client:transport dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/elasticsearch/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/elasticsearch/build.gradle b/modules/elasticsearch/build.gradle index 6d2b5b8a667..29e6591034d 100644 --- a/modules/elasticsearch/build.gradle +++ b/modules/elasticsearch/build.gradle @@ -3,6 +3,6 @@ description = "TestContainers :: elasticsearch" dependencies { api project(':testcontainers') testImplementation "org.elasticsearch.client:elasticsearch-rest-client:8.2.2" - testImplementation "org.elasticsearch.client:transport:7.17.4" + testImplementation "org.elasticsearch.client:transport:7.17.5" testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } From 72846fdf802e5d4a6c6bf11bd8a741049e480253 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jul 2022 22:05:12 +0000 Subject: [PATCH 0258/3231] Bump java-client from 3.3.0 to 3.3.1 in /modules/couchbase Bumps [java-client](https://github.com/couchbase/couchbase-jvm-clients) from 3.3.0 to 3.3.1. - [Release notes](https://github.com/couchbase/couchbase-jvm-clients/releases) - [Commits](https://github.com/couchbase/couchbase-jvm-clients/compare/java-client-3.3.0...java-client-3.3.1) --- updated-dependencies: - dependency-name: com.couchbase.client:java-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/couchbase/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/couchbase/build.gradle b/modules/couchbase/build.gradle index 71844a30d20..df7f4cbc221 100644 --- a/modules/couchbase/build.gradle +++ b/modules/couchbase/build.gradle @@ -5,6 +5,6 @@ dependencies { // TODO use JDK's HTTP client and/or Apache HttpClient5 shaded 'com.squareup.okhttp3:okhttp:3.14.9' - testImplementation 'com.couchbase.client:java-client:3.3.0' + testImplementation 'com.couchbase.client:java-client:3.3.1' testImplementation 'org.awaitility:awaitility:4.2.0' } From 602d11d68f4b8e225e6d5635b85a640c3fcde92a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jul 2022 22:05:32 +0000 Subject: [PATCH 0259/3231] Bump rest-assured from 5.1.0 to 5.1.1 in /modules/vault Bumps [rest-assured](https://github.com/rest-assured/rest-assured) from 5.1.0 to 5.1.1. - [Release notes](https://github.com/rest-assured/rest-assured/releases) - [Changelog](https://github.com/rest-assured/rest-assured/blob/master/changelog.txt) - [Commits](https://github.com/rest-assured/rest-assured/compare/rest-assured-5.1.0...rest-assured-5.1.1) --- updated-dependencies: - dependency-name: io.rest-assured:rest-assured dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/vault/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/vault/build.gradle b/modules/vault/build.gradle index 1ffc366987c..499286ae72a 100644 --- a/modules/vault/build.gradle +++ b/modules/vault/build.gradle @@ -4,7 +4,7 @@ dependencies { api project(':testcontainers') testImplementation 'com.bettercloud:vault-java-driver:5.1.0' - testImplementation 'io.rest-assured:rest-assured:5.1.0' + testImplementation 'io.rest-assured:rest-assured:5.1.1' testImplementation 'org.assertj:assertj-core:3.23.1' } From 49f680ead43656a758d03441254bc42448c89fa8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jul 2022 22:05:36 +0000 Subject: [PATCH 0260/3231] Bump tomcat-jdbc from 10.0.21 to 10.0.22 in /modules/jdbc Bumps tomcat-jdbc from 10.0.21 to 10.0.22. --- updated-dependencies: - dependency-name: org.apache.tomcat:tomcat-jdbc dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/jdbc/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jdbc/build.gradle b/modules/jdbc/build.gradle index 043c2774d6c..012e9f410ec 100644 --- a/modules/jdbc/build.gradle +++ b/modules/jdbc/build.gradle @@ -7,7 +7,7 @@ dependencies { compileOnly 'org.jetbrains:annotations:23.0.0' testImplementation 'commons-dbutils:commons-dbutils:1.7' testImplementation 'org.vibur:vibur-dbcp:25.0' - testImplementation 'org.apache.tomcat:tomcat-jdbc:10.0.21' + testImplementation 'org.apache.tomcat:tomcat-jdbc:10.0.22' testImplementation 'com.zaxxer:HikariCP-java6:2.3.13' testImplementation 'com.googlecode.junit-toolbox:junit-toolbox:2.4' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' From 81e19bf43be0927fb1ac598ad09a645eef59f415 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jul 2022 22:05:38 +0000 Subject: [PATCH 0261/3231] Bump pulsar-client from 2.10.0 to 2.10.1 in /modules/pulsar Bumps [pulsar-client](https://github.com/apache/pulsar) from 2.10.0 to 2.10.1. - [Release notes](https://github.com/apache/pulsar/releases) - [Commits](https://github.com/apache/pulsar/compare/v2.10.0...v2.10.1) --- updated-dependencies: - dependency-name: org.apache.pulsar:pulsar-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/pulsar/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/pulsar/build.gradle b/modules/pulsar/build.gradle index a1b24efba37..31524211f63 100644 --- a/modules/pulsar/build.gradle +++ b/modules/pulsar/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Pulsar" dependencies { api project(':testcontainers') - testImplementation group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.10.0' + testImplementation group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.10.1' testImplementation group: 'org.assertj', name: 'assertj-core', version: '3.23.1' testImplementation group: 'org.apache.pulsar', name: 'pulsar-client-admin', version: '2.10.0' } From c4aa3db14fdbc189de6b368069f9871ab456891f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jul 2022 22:05:39 +0000 Subject: [PATCH 0262/3231] Bump mssql-jdbc in /modules/mssqlserver Bumps [mssql-jdbc](https://github.com/Microsoft/mssql-jdbc) from 11.1.1.jre8-preview to 11.1.2.jre8-preview. - [Release notes](https://github.com/Microsoft/mssql-jdbc/releases) - [Changelog](https://github.com/microsoft/mssql-jdbc/blob/main/CHANGELOG.md) - [Commits](https://github.com/Microsoft/mssql-jdbc/commits) --- updated-dependencies: - dependency-name: com.microsoft.sqlserver:mssql-jdbc dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/mssqlserver/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mssqlserver/build.gradle b/modules/mssqlserver/build.gradle index e7a654e12f1..30b9af2bd77 100644 --- a/modules/mssqlserver/build.gradle +++ b/modules/mssqlserver/build.gradle @@ -10,7 +10,7 @@ dependencies { compileOnly 'io.r2dbc:r2dbc-mssql:0.9.0.RELEASE' testImplementation project(':jdbc-test') - testImplementation 'com.microsoft.sqlserver:mssql-jdbc:11.1.1.jre8-preview' + testImplementation 'com.microsoft.sqlserver:mssql-jdbc:11.1.2.jre8-preview' testImplementation project(':r2dbc') testImplementation 'io.r2dbc:r2dbc-mssql:0.8.7.RELEASE' From b1c4fb664fa8d8b42c0e44b3a30555c3138994ea Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jul 2022 22:05:43 +0000 Subject: [PATCH 0263/3231] Bump pulsar-client-admin from 2.10.0 to 2.10.1 in /modules/pulsar Bumps [pulsar-client-admin](https://github.com/apache/pulsar) from 2.10.0 to 2.10.1. - [Release notes](https://github.com/apache/pulsar/releases) - [Commits](https://github.com/apache/pulsar/compare/v2.10.0...v2.10.1) --- updated-dependencies: - dependency-name: org.apache.pulsar:pulsar-client-admin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/pulsar/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/pulsar/build.gradle b/modules/pulsar/build.gradle index a1b24efba37..e49b46a85ed 100644 --- a/modules/pulsar/build.gradle +++ b/modules/pulsar/build.gradle @@ -5,5 +5,5 @@ dependencies { testImplementation group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.10.0' testImplementation group: 'org.assertj', name: 'assertj-core', version: '3.23.1' - testImplementation group: 'org.apache.pulsar', name: 'pulsar-client-admin', version: '2.10.0' + testImplementation group: 'org.apache.pulsar', name: 'pulsar-client-admin', version: '2.10.1' } From d20ddfbecc1d82ce3bda4f441d18d82fc7070290 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jul 2022 22:05:45 +0000 Subject: [PATCH 0264/3231] Bump amqp-client from 5.14.2 to 5.15.0 in /core Bumps [amqp-client](https://github.com/rabbitmq/rabbitmq-java-client) from 5.14.2 to 5.15.0. - [Release notes](https://github.com/rabbitmq/rabbitmq-java-client/releases) - [Commits](https://github.com/rabbitmq/rabbitmq-java-client/compare/v5.14.2...v5.15.0) --- updated-dependencies: - dependency-name: com.rabbitmq:amqp-client dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index ebfd4f36bcb..b4c6d7ad4a2 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -88,7 +88,7 @@ dependencies { testImplementation 'org.apache.httpcomponents:httpclient:4.5.9' testImplementation 'redis.clients:jedis:4.2.3' - testImplementation 'com.rabbitmq:amqp-client:5.14.2' + testImplementation 'com.rabbitmq:amqp-client:5.15.0' testImplementation 'org.mongodb:mongo-java-driver:3.12.11' testImplementation ('org.mockito:mockito-core:4.6.0') { From f3fdbf6a94c5cf7082c2ea452ab93610044e2af6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jul 2022 22:05:45 +0000 Subject: [PATCH 0265/3231] Bump aws-java-sdk-sqs from 1.12.233 to 1.12.253 in /modules/localstack Bumps [aws-java-sdk-sqs](https://github.com/aws/aws-sdk-java) from 1.12.233 to 1.12.253. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.233...1.12.253) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-sqs dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index b6fb4fd3111..e73bf61c19d 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -5,7 +5,7 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.231' testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.231' - testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.233' + testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.253' testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.232' testImplementation 'software.amazon.awssdk:s3:2.17.204' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' From 17086e24c6fe00f93fbec6f7653df62c78f3cc34 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jul 2022 22:05:46 +0000 Subject: [PATCH 0266/3231] Bump s3 from 2.17.204 to 2.17.224 in /modules/localstack Bumps s3 from 2.17.204 to 2.17.224. --- updated-dependencies: - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index b6fb4fd3111..4a191afd82a 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -7,6 +7,6 @@ dependencies { testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.231' testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.233' testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.232' - testImplementation 'software.amazon.awssdk:s3:2.17.204' + testImplementation 'software.amazon.awssdk:s3:2.17.224' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } From 183500e91eb6cbb79e9e7b29bedea3664556b1af Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jul 2022 22:06:14 +0000 Subject: [PATCH 0267/3231] Bump peter-evans/create-pull-request from 4.0.3 to 4.0.4 Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 4.0.3 to 4.0.4. - [Release notes](https://github.com/peter-evans/create-pull-request/releases) - [Commits](https://github.com/peter-evans/create-pull-request/compare/f094b77505fb89581e68a1163fbd2fffece39da1...923ad837f191474af6b1721408744feb989a4c27) --- updated-dependencies: - dependency-name: peter-evans/create-pull-request dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/update-docs-version.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/update-docs-version.yml b/.github/workflows/update-docs-version.yml index b4d4d250346..ed21bca4670 100644 --- a/.github/workflows/update-docs-version.yml +++ b/.github/workflows/update-docs-version.yml @@ -17,7 +17,7 @@ jobs: sed -i "s/latest_version: .*/latest_version: ${GITHUB_REF##*/}/g" mkdocs.yml git diff - name: Create Pull Request - uses: peter-evans/create-pull-request@f094b77505fb89581e68a1163fbd2fffece39da1 # v3.10.1 + uses: peter-evans/create-pull-request@923ad837f191474af6b1721408744feb989a4c27 # v3.10.1 with: title: Update docs version to ${GITHUB_REF##*/} body: | From d3ea1fca61c21e46f243b744e06ec78d73d9294f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jul 2022 22:06:16 +0000 Subject: [PATCH 0268/3231] Bump gradle-update/update-gradle-wrapper-action from 1.0.17 to 1.0.18 Bumps [gradle-update/update-gradle-wrapper-action](https://github.com/gradle-update/update-gradle-wrapper-action) from 1.0.17 to 1.0.18. - [Release notes](https://github.com/gradle-update/update-gradle-wrapper-action/releases) - [Changelog](https://github.com/gradle-update/update-gradle-wrapper-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/gradle-update/update-gradle-wrapper-action/compare/5b865a8b4d09fdf6afaaa20397ba68455bb48f0d...981c551b17dbcf1940b1b4435afdb79babb7c13a) --- updated-dependencies: - dependency-name: gradle-update/update-gradle-wrapper-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/update-gradle-wrapper.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/update-gradle-wrapper.yml b/.github/workflows/update-gradle-wrapper.yml index e2013d0f1b7..5855e609fce 100644 --- a/.github/workflows/update-gradle-wrapper.yml +++ b/.github/workflows/update-gradle-wrapper.yml @@ -13,7 +13,7 @@ jobs: - uses: actions/checkout@v3 - name: Update Gradle Wrapper - uses: gradle-update/update-gradle-wrapper-action@5b865a8b4d09fdf6afaaa20397ba68455bb48f0d # v1.0.13 + uses: gradle-update/update-gradle-wrapper-action@981c551b17dbcf1940b1b4435afdb79babb7c13a # v1.0.13 with: repo-token: ${{ secrets.GITHUB_TOKEN }} labels: dependencies From 82cf76073948a2e278a28fc8fd1d42fb41b49c9b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jul 2022 22:06:20 +0000 Subject: [PATCH 0269/3231] Bump actions/cache from 3.0.3 to 3.0.4 Bumps [actions/cache](https://github.com/actions/cache) from 3.0.3 to 3.0.4. - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/v3.0.3...v3.0.4) --- updated-dependencies: - dependency-name: actions/cache dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/ci-examples.yml | 4 ++-- .github/workflows/ci.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci-examples.yml b/.github/workflows/ci-examples.yml index 9a7b314c67f..b1466ba8621 100644 --- a/.github/workflows/ci-examples.yml +++ b/.github/workflows/ci-examples.yml @@ -22,7 +22,7 @@ jobs: java-version: '8.0.302' distribution: temurin - name: Cache Gradle Home files - uses: actions/cache@v3.0.3 + uses: actions/cache@v3.0.4 continue-on-error: true with: path: ~/.gradle/caches @@ -50,7 +50,7 @@ jobs: java-version: '8.0.302' distribution: temurin - name: Cache Gradle Home files - uses: actions/cache@v3.0.3 + uses: actions/cache@v3.0.4 continue-on-error: true with: path: ~/.gradle/caches diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 56bb4e0799d..70ee552b798 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,7 +30,7 @@ jobs: java-version: '8.0.302' distribution: temurin - name: Cache Gradle Home files - uses: actions/cache@v3.0.3 + uses: actions/cache@v3.0.4 with: path: ~/.gradle/caches key: ${{ runner.os }}-gradle-home-testmatrix-${{ hashFiles('**/*.gradle') }} @@ -56,7 +56,7 @@ jobs: java-version: '8.0.302' distribution: temurin - name: Cache Gradle Home files - uses: actions/cache@v3.0.3 + uses: actions/cache@v3.0.4 continue-on-error: true with: path: ~/.gradle/caches From 92c087da00e3c67a861601c7c15d490ac2ed3390 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jul 2022 22:07:07 +0000 Subject: [PATCH 0270/3231] Bump cucumber-java from 7.2.3 to 7.4.1 in /examples Bumps [cucumber-java](https://github.com/cucumber/cucumber-jvm) from 7.2.3 to 7.4.1. - [Release notes](https://github.com/cucumber/cucumber-jvm/releases) - [Changelog](https://github.com/cucumber/cucumber-jvm/blob/main/CHANGELOG.md) - [Commits](https://github.com/cucumber/cucumber-jvm/compare/v7.2.3...v7.4.1) --- updated-dependencies: - dependency-name: io.cucumber:cucumber-java dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- examples/cucumber/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cucumber/build.gradle b/examples/cucumber/build.gradle index f23a2d9b7af..3e03e776ac4 100644 --- a/examples/cucumber/build.gradle +++ b/examples/cucumber/build.gradle @@ -10,7 +10,7 @@ dependencies { implementation 'org.seleniumhq.selenium:selenium-remote-driver:3.141.59' implementation 'org.seleniumhq.selenium:selenium-firefox-driver:3.141.59' implementation 'org.seleniumhq.selenium:selenium-chrome-driver:3.141.59' - testImplementation 'io.cucumber:cucumber-java:7.2.3' + testImplementation 'io.cucumber:cucumber-java:7.4.1' testImplementation 'io.cucumber:cucumber-junit:7.3.4' testImplementation 'org.testcontainers:selenium' } From 87e734d9c5af84bd34838e8f2f84cccf5210a078 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jul 2022 22:07:18 +0000 Subject: [PATCH 0271/3231] Bump assertj-core from 3.22.0 to 3.23.1 in /examples Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.22.0 to 3.23.1. - [Release notes](https://github.com/assertj/assertj-core/releases) - [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.22.0...assertj-core-3.23.1) --- updated-dependencies: - dependency-name: org.assertj:assertj-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- examples/kafka-cluster/build.gradle | 2 +- examples/neo4j-container/build.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/kafka-cluster/build.gradle b/examples/kafka-cluster/build.gradle index db89b4335fe..79e91ec5a68 100644 --- a/examples/kafka-cluster/build.gradle +++ b/examples/kafka-cluster/build.gradle @@ -11,7 +11,7 @@ dependencies { testAnnotationProcessor "org.projectlombok:lombok:1.18.24" testImplementation 'org.testcontainers:kafka' testImplementation 'org.apache.kafka:kafka-clients:3.1.0' - testImplementation 'org.assertj:assertj-core:3.22.0' + testImplementation 'org.assertj:assertj-core:3.23.1' testImplementation 'com.google.guava:guava:23.0' testImplementation 'ch.qos.logback:logback-classic:1.2.11' } diff --git a/examples/neo4j-container/build.gradle b/examples/neo4j-container/build.gradle index 745d3206953..e937bd2954b 100644 --- a/examples/neo4j-container/build.gradle +++ b/examples/neo4j-container/build.gradle @@ -7,7 +7,7 @@ repositories { } dependencies { - testImplementation 'org.assertj:assertj-core:3.22.0' + testImplementation 'org.assertj:assertj-core:3.23.1' testImplementation 'org.neo4j.driver:neo4j-java-driver:4.4.6' testImplementation 'org.testcontainers:neo4j' testImplementation 'org.testcontainers:junit-jupiter' From 76a7c3b24c754519ccd622e42d84118a864482d5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jul 2022 22:07:25 +0000 Subject: [PATCH 0272/3231] Bump postgresql from 42.3.6 to 42.4.0 in /examples Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.3.6 to 42.4.0. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.3.6...REL42.4.0) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- examples/linked-container/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/linked-container/build.gradle b/examples/linked-container/build.gradle index f25127f75eb..4bb216e6fb7 100644 --- a/examples/linked-container/build.gradle +++ b/examples/linked-container/build.gradle @@ -9,7 +9,7 @@ dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.36' implementation 'com.squareup.okhttp3:okhttp:4.9.3' implementation 'org.json:json:20220320' - testImplementation 'org.postgresql:postgresql:42.3.6' + testImplementation 'org.postgresql:postgresql:42.4.0' testImplementation 'ch.qos.logback:logback-classic:1.2.11' testImplementation 'org.testcontainers:postgresql' } From cae5491705e415e9369eda6ce61a2224aae1631f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jul 2022 22:07:27 +0000 Subject: [PATCH 0273/3231] Bump com.gradle.enterprise.gradle.plugin in /examples Bumps com.gradle.enterprise.gradle.plugin from 3.10 to 3.10.2. --- updated-dependencies: - dependency-name: com.gradle.enterprise:com.gradle.enterprise.gradle.plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/settings.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/settings.gradle b/examples/settings.gradle index b9fb73f86c1..5a4ae8c0d2e 100644 --- a/examples/settings.gradle +++ b/examples/settings.gradle @@ -6,7 +6,7 @@ buildscript { } dependencies { classpath "gradle.plugin.ch.myniva.gradle:s3-build-cache:0.10.0" - classpath "com.gradle.enterprise:com.gradle.enterprise.gradle.plugin:3.10" + classpath "com.gradle.enterprise:com.gradle.enterprise.gradle.plugin:3.10.2" classpath "com.gradle:common-custom-user-data-gradle-plugin:1.7.2" } } From 010ca107405a11d1e9674c635dfc965830fcfba9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jul 2022 22:09:12 +0000 Subject: [PATCH 0274/3231] Bump reactor-core from 3.4.18 to 3.4.19 in /modules/r2dbc Bumps [reactor-core](https://github.com/reactor/reactor-core) from 3.4.18 to 3.4.19. - [Release notes](https://github.com/reactor/reactor-core/releases) - [Commits](https://github.com/reactor/reactor-core/compare/v3.4.18...v3.4.19) --- updated-dependencies: - dependency-name: io.projectreactor:reactor-core dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/r2dbc/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/r2dbc/build.gradle b/modules/r2dbc/build.gradle index 51199c9a53c..692b5ac9ce5 100644 --- a/modules/r2dbc/build.gradle +++ b/modules/r2dbc/build.gradle @@ -15,6 +15,6 @@ dependencies { testImplementation 'io.r2dbc:r2dbc-postgresql:0.8.12.RELEASE' testImplementation project(':postgresql') - testFixturesImplementation 'io.projectreactor:reactor-core:3.4.18' + testFixturesImplementation 'io.projectreactor:reactor-core:3.4.19' testFixturesImplementation 'org.assertj:assertj-core:3.23.1' } From 7ca244e24d036c64d957905580aee5ae818fff5e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jul 2022 22:09:21 +0000 Subject: [PATCH 0275/3231] Bump hivemq-extension-sdk from 4.8.1 to 4.8.2 in /modules/hivemq Bumps [hivemq-extension-sdk](https://github.com/hivemq/hivemq-extension-sdk) from 4.8.1 to 4.8.2. - [Release notes](https://github.com/hivemq/hivemq-extension-sdk/releases) - [Commits](https://github.com/hivemq/hivemq-extension-sdk/compare/4.8.1...4.8.2) --- updated-dependencies: - dependency-name: com.hivemq:hivemq-extension-sdk dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/hivemq/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/hivemq/build.gradle b/modules/hivemq/build.gradle index 7add9af67ea..aa566381d0c 100644 --- a/modules/hivemq/build.gradle +++ b/modules/hivemq/build.gradle @@ -13,7 +13,7 @@ dependencies { testImplementation("org.junit.jupiter:junit-jupiter-api:5.8.2") testImplementation(project(":junit-jupiter")) - testImplementation("com.hivemq:hivemq-extension-sdk:4.8.1") + testImplementation("com.hivemq:hivemq-extension-sdk:4.8.2") testImplementation("com.hivemq:hivemq-mqtt-client:1.3.0") testImplementation("org.apache.httpcomponents:httpclient:4.5.13") testImplementation("ch.qos.logback:logback-classic:1.2.11") From c130082dee8f2e3de1b48022ffc6675df5a9435e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jul 2022 22:09:26 +0000 Subject: [PATCH 0276/3231] Bump trino-jdbc from 382 to 388 in /modules/trino Bumps trino-jdbc from 382 to 388. --- updated-dependencies: - dependency-name: io.trino:trino-jdbc dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- modules/trino/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/trino/build.gradle b/modules/trino/build.gradle index 8305e8e0629..caf1f322c12 100644 --- a/modules/trino/build.gradle +++ b/modules/trino/build.gradle @@ -4,6 +4,6 @@ dependencies { api project(':jdbc') testImplementation project(':jdbc-test') - testImplementation 'io.trino:trino-jdbc:382' + testImplementation 'io.trino:trino-jdbc:388' compileOnly 'org.jetbrains:annotations:23.0.0' } From 30508fe29ed6aab8160efa17c5f67f3f8ecb9fe2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jul 2022 22:09:36 +0000 Subject: [PATCH 0277/3231] Bump google-cloud-datastore from 2.7.0 to 2.10.0 in /modules/gcloud Bumps [google-cloud-datastore](https://github.com/googleapis/java-datastore) from 2.7.0 to 2.10.0. - [Release notes](https://github.com/googleapis/java-datastore/releases) - [Changelog](https://github.com/googleapis/java-datastore/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/java-datastore/compare/v2.7.0...v2.10.0) --- updated-dependencies: - dependency-name: com.google.cloud:google-cloud-datastore dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/gcloud/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle index cdea13f5fac..ddb634fbd79 100644 --- a/modules/gcloud/build.gradle +++ b/modules/gcloud/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: GCloud" dependencies { api project(':testcontainers') - testImplementation 'com.google.cloud:google-cloud-datastore:2.7.0' + testImplementation 'com.google.cloud:google-cloud-datastore:2.10.0' testImplementation 'com.google.cloud:google-cloud-firestore:3.2.0' testImplementation 'com.google.cloud:google-cloud-pubsub:1.119.0' testImplementation 'com.google.cloud:google-cloud-spanner:6.25.5' From c0cc90ffc90d3d324e767d63ad0f1388cc8435ef Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jul 2022 22:09:37 +0000 Subject: [PATCH 0278/3231] Bump client-java from 15.0.1 to 16.0.0 in /modules/k3s Bumps [client-java](https://github.com/kubernetes-client/java) from 15.0.1 to 16.0.0. - [Release notes](https://github.com/kubernetes-client/java/releases) - [Changelog](https://github.com/kubernetes-client/java/blob/master/CHANGELOG.md) - [Commits](https://github.com/kubernetes-client/java/compare/v15.0.1...v16.0.0) --- updated-dependencies: - dependency-name: io.kubernetes:client-java dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- modules/k3s/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/k3s/build.gradle b/modules/k3s/build.gradle index bf51cbfefa9..2223909f051 100644 --- a/modules/k3s/build.gradle +++ b/modules/k3s/build.gradle @@ -9,6 +9,6 @@ dependencies { shaded 'com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.8.8' testImplementation 'io.fabric8:kubernetes-client:5.12.2' - testImplementation 'io.kubernetes:client-java:15.0.1' + testImplementation 'io.kubernetes:client-java:16.0.0' testImplementation 'org.assertj:assertj-core:3.23.1' } From d6dfc0b544c3cada52c8827d83d4851e782f25b1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jul 2022 22:09:40 +0000 Subject: [PATCH 0279/3231] Bump google-cloud-bigtable from 2.8.0 to 2.9.0 in /modules/gcloud Bumps [google-cloud-bigtable](https://github.com/googleapis/java-bigtable) from 2.8.0 to 2.9.0. - [Release notes](https://github.com/googleapis/java-bigtable/releases) - [Changelog](https://github.com/googleapis/java-bigtable/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/java-bigtable/compare/v2.8.0...v2.9.0) --- updated-dependencies: - dependency-name: com.google.cloud:google-cloud-bigtable dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/gcloud/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle index cdea13f5fac..3debc0391cf 100644 --- a/modules/gcloud/build.gradle +++ b/modules/gcloud/build.gradle @@ -7,6 +7,6 @@ dependencies { testImplementation 'com.google.cloud:google-cloud-firestore:3.2.0' testImplementation 'com.google.cloud:google-cloud-pubsub:1.119.0' testImplementation 'com.google.cloud:google-cloud-spanner:6.25.5' - testImplementation 'com.google.cloud:google-cloud-bigtable:2.8.0' + testImplementation 'com.google.cloud:google-cloud-bigtable:2.9.0' testImplementation 'org.assertj:assertj-core:3.23.1' } From 183fce7a00e383de48e95ff672d8a0372a6c61d2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jul 2022 22:09:45 +0000 Subject: [PATCH 0280/3231] Bump google-cloud-pubsub from 1.119.0 to 1.120.0 in /modules/gcloud Bumps [google-cloud-pubsub](https://github.com/googleapis/java-pubsub) from 1.119.0 to 1.120.0. - [Release notes](https://github.com/googleapis/java-pubsub/releases) - [Changelog](https://github.com/googleapis/java-pubsub/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/java-pubsub/compare/v1.119.0...v1.120.0) --- updated-dependencies: - dependency-name: com.google.cloud:google-cloud-pubsub dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/gcloud/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle index cdea13f5fac..1369d741f1d 100644 --- a/modules/gcloud/build.gradle +++ b/modules/gcloud/build.gradle @@ -5,7 +5,7 @@ dependencies { testImplementation 'com.google.cloud:google-cloud-datastore:2.7.0' testImplementation 'com.google.cloud:google-cloud-firestore:3.2.0' - testImplementation 'com.google.cloud:google-cloud-pubsub:1.119.0' + testImplementation 'com.google.cloud:google-cloud-pubsub:1.120.0' testImplementation 'com.google.cloud:google-cloud-spanner:6.25.5' testImplementation 'com.google.cloud:google-cloud-bigtable:2.8.0' testImplementation 'org.assertj:assertj-core:3.23.1' From 1e7ed80c8be857bb80a1bd3e158221ff97269516 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jul 2022 22:10:03 +0000 Subject: [PATCH 0281/3231] Bump amqp-client from 5.14.2 to 5.15.0 in /modules/rabbitmq Bumps [amqp-client](https://github.com/rabbitmq/rabbitmq-java-client) from 5.14.2 to 5.15.0. - [Release notes](https://github.com/rabbitmq/rabbitmq-java-client/releases) - [Commits](https://github.com/rabbitmq/rabbitmq-java-client/compare/v5.14.2...v5.15.0) --- updated-dependencies: - dependency-name: com.rabbitmq:amqp-client dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/rabbitmq/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/rabbitmq/build.gradle b/modules/rabbitmq/build.gradle index 6c2953909bb..9825a27a499 100644 --- a/modules/rabbitmq/build.gradle +++ b/modules/rabbitmq/build.gradle @@ -2,7 +2,7 @@ description = "TestContainers :: RabbitMQ" dependencies { api project(":testcontainers") - testImplementation 'com.rabbitmq:amqp-client:5.14.2' + testImplementation 'com.rabbitmq:amqp-client:5.15.0' testImplementation 'org.assertj:assertj-core:3.23.1' compileOnly 'org.jetbrains:annotations:23.0.0' } From 889b990fb7dc7ae31a370c1bd28d6d7265c3fa6a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jul 2022 22:10:09 +0000 Subject: [PATCH 0282/3231] Bump mongodb-driver-sync from 4.6.0 to 4.6.1 in /modules/mongodb Bumps [mongodb-driver-sync](https://github.com/mongodb/mongo-java-driver) from 4.6.0 to 4.6.1. - [Release notes](https://github.com/mongodb/mongo-java-driver/releases) - [Commits](https://github.com/mongodb/mongo-java-driver/compare/r4.6.0...r4.6.1) --- updated-dependencies: - dependency-name: org.mongodb:mongodb-driver-sync dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/mongodb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mongodb/build.gradle b/modules/mongodb/build.gradle index d8ef34fbb40..297bcd21f3b 100644 --- a/modules/mongodb/build.gradle +++ b/modules/mongodb/build.gradle @@ -3,5 +3,5 @@ description = "Testcontainers :: MongoDB" dependencies { api project(':testcontainers') - testImplementation("org.mongodb:mongodb-driver-sync:4.6.0") + testImplementation("org.mongodb:mongodb-driver-sync:4.6.1") } From 10e5c4936911629cc805a8b9085e79cfa957cf27 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jul 2022 22:10:14 +0000 Subject: [PATCH 0283/3231] Bump azure-cosmos from 4.30.0 to 4.32.1 in /modules/azure Bumps [azure-cosmos](https://github.com/Azure/azure-sdk-for-java) from 4.30.0 to 4.32.1. - [Release notes](https://github.com/Azure/azure-sdk-for-java/releases) - [Commits](https://github.com/Azure/azure-sdk-for-java/compare/azure-cosmos_4.30.0...azure-cosmos_4.32.1) --- updated-dependencies: - dependency-name: com.azure:azure-cosmos dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/azure/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/azure/build.gradle b/modules/azure/build.gradle index 6656445af09..5f8c6837d79 100644 --- a/modules/azure/build.gradle +++ b/modules/azure/build.gradle @@ -6,5 +6,5 @@ dependencies { shaded 'com.squareup.okhttp3:okhttp:3.14.9' testImplementation 'org.assertj:assertj-core:3.23.1' - testImplementation 'com.azure:azure-cosmos:4.30.0' + testImplementation 'com.azure:azure-cosmos:4.32.1' } From 267df8d0bc5a2a8b16513b0b59b3a8ebe6c22150 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jul 2022 22:10:32 +0000 Subject: [PATCH 0284/3231] Bump postgresql from 42.3.6 to 42.4.0 in /modules/cockroachdb Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.3.6 to 42.4.0. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.3.6...REL42.4.0) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/cockroachdb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/cockroachdb/build.gradle b/modules/cockroachdb/build.gradle index 490b2d7fb11..fb9f53189d9 100644 --- a/modules/cockroachdb/build.gradle +++ b/modules/cockroachdb/build.gradle @@ -4,5 +4,5 @@ dependencies { api project(':jdbc') testImplementation project(':jdbc-test') - testImplementation 'org.postgresql:postgresql:42.3.6' + testImplementation 'org.postgresql:postgresql:42.4.0' } From d5d78d0f471462612ac080284423871d0808ec2a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Jul 2022 22:11:53 +0000 Subject: [PATCH 0285/3231] Bump com.gradle.enterprise.gradle.plugin from 3.10.1 to 3.10.2 Bumps com.gradle.enterprise.gradle.plugin from 3.10.1 to 3.10.2. --- updated-dependencies: - dependency-name: com.gradle.enterprise:com.gradle.enterprise.gradle.plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- settings.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.gradle b/settings.gradle index 16b3fcdf98a..73cd6b977fd 100644 --- a/settings.gradle +++ b/settings.gradle @@ -5,7 +5,7 @@ buildscript { } } dependencies { - classpath "com.gradle.enterprise:com.gradle.enterprise.gradle.plugin:3.10.1" + classpath "com.gradle.enterprise:com.gradle.enterprise.gradle.plugin:3.10.2" classpath "com.gradle:common-custom-user-data-gradle-plugin:1.7" } } From c8daeb689cf4b1f500869a8565ebe4cbcf35390a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Jul 2022 15:22:34 +0000 Subject: [PATCH 0286/3231] Bump postgresql from 42.3.6 to 42.4.0 in /modules/junit-jupiter Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.3.6 to 42.4.0. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.3.6...REL42.4.0) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 9a74389e8a9..73a8897800c 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -15,7 +15,7 @@ dependencies { testImplementation 'org.assertj:assertj-core:3.23.1' testImplementation 'org.junit.jupiter:junit-jupiter-params:5.8.2' - testRuntimeOnly 'org.postgresql:postgresql:42.3.6' + testRuntimeOnly 'org.postgresql:postgresql:42.4.0' testRuntimeOnly 'mysql:mysql-connector-java:8.0.29' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.2' } From ea4f953dc06ea7e0751f0b2ac23230af3eef41f8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Jul 2022 15:22:34 +0000 Subject: [PATCH 0287/3231] Bump postgresql from 42.3.6 to 42.4.0 in /modules/spock Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.3.6 to 42.4.0. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.3.6...REL42.4.0) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/spock/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle index e609733d398..a9e3b7d09b1 100644 --- a/modules/spock/build.gradle +++ b/modules/spock/build.gradle @@ -15,7 +15,7 @@ dependencies { testImplementation 'com.zaxxer:HikariCP:4.0.3' testImplementation 'org.apache.httpcomponents:httpclient:4.5.13' - testRuntimeOnly 'org.postgresql:postgresql:42.3.6' + testRuntimeOnly 'org.postgresql:postgresql:42.4.0' testRuntimeOnly 'mysql:mysql-connector-java:8.0.29' testCompileClasspath 'org.jetbrains:annotations:23.0.0' From 7066793701465e01ecf4734a9fccb7b900995228 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Jul 2022 15:22:35 +0000 Subject: [PATCH 0288/3231] Bump postgresql from 42.3.6 to 42.4.0 in /modules/postgresql Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.3.6 to 42.4.0. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.3.6...REL42.4.0) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/postgresql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/postgresql/build.gradle b/modules/postgresql/build.gradle index f083d4ed949..87705f77d5d 100644 --- a/modules/postgresql/build.gradle +++ b/modules/postgresql/build.gradle @@ -11,7 +11,7 @@ dependencies { testImplementation project(':jdbc-test') testImplementation project(':test-support') - testImplementation 'org.postgresql:postgresql:42.3.6' + testImplementation 'org.postgresql:postgresql:42.4.0' testImplementation testFixtures(project(':r2dbc')) testImplementation 'io.r2dbc:r2dbc-postgresql:0.8.11.RELEASE' From 1dec0ec57754e4ba2d201cd09f979a3d93b8519c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Jul 2022 15:22:39 +0000 Subject: [PATCH 0289/3231] Bump org.jetbrains.kotlin.jvm from 1.6.21 to 1.7.0 in /examples Bumps [org.jetbrains.kotlin.jvm](https://github.com/JetBrains/kotlin) from 1.6.21 to 1.7.0. - [Release notes](https://github.com/JetBrains/kotlin/releases) - [Changelog](https://github.com/JetBrains/kotlin/blob/master/ChangeLog.md) - [Commits](https://github.com/JetBrains/kotlin/compare/v1.6.21...v1.7.0) --- updated-dependencies: - dependency-name: org.jetbrains.kotlin.jvm dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- examples/spring-boot-kotlin-redis/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/spring-boot-kotlin-redis/build.gradle b/examples/spring-boot-kotlin-redis/build.gradle index 4f3a50de21a..c0bd965ab79 100644 --- a/examples/spring-boot-kotlin-redis/build.gradle +++ b/examples/spring-boot-kotlin-redis/build.gradle @@ -1,6 +1,6 @@ plugins { id("org.springframework.boot") version "2.6.4" - id("org.jetbrains.kotlin.jvm") version "1.6.21" + id("org.jetbrains.kotlin.jvm") version "1.7.0" id("org.jetbrains.kotlin.plugin.spring") version "1.6.21" } From 770a9d86a721dc8fda92fe883bd4f97d49d20069 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Jul 2022 15:22:39 +0000 Subject: [PATCH 0290/3231] Bump tomcat-jdbc from 10.0.21 to 10.0.22 in /modules/jdbc-test Bumps tomcat-jdbc from 10.0.21 to 10.0.22. --- updated-dependencies: - dependency-name: org.apache.tomcat:tomcat-jdbc dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/jdbc-test/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jdbc-test/build.gradle b/modules/jdbc-test/build.gradle index 5674f235c7f..3649396773d 100644 --- a/modules/jdbc-test/build.gradle +++ b/modules/jdbc-test/build.gradle @@ -14,7 +14,7 @@ dependencies { exclude(group: "net.java.dev.jna", module: "jna") } - api 'org.apache.tomcat:tomcat-jdbc:10.0.21' + api 'org.apache.tomcat:tomcat-jdbc:10.0.22' api 'org.vibur:vibur-dbcp:25.0' api 'mysql:mysql-connector-java:8.0.29' } From 799f1dd49bbe835b65bc1f543df9a5d459fbee3a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Jul 2022 15:22:39 +0000 Subject: [PATCH 0291/3231] Bump elasticsearch-rest-client in /modules/elasticsearch Bumps [elasticsearch-rest-client](https://github.com/elastic/elasticsearch) from 8.2.2 to 8.3.1. - [Release notes](https://github.com/elastic/elasticsearch/releases) - [Changelog](https://github.com/elastic/elasticsearch/blob/master/CHANGELOG.md) - [Commits](https://github.com/elastic/elasticsearch/compare/v8.2.2...v8.3.1) --- updated-dependencies: - dependency-name: org.elasticsearch.client:elasticsearch-rest-client dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/elasticsearch/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/elasticsearch/build.gradle b/modules/elasticsearch/build.gradle index 29e6591034d..a4a277596fc 100644 --- a/modules/elasticsearch/build.gradle +++ b/modules/elasticsearch/build.gradle @@ -2,7 +2,7 @@ description = "TestContainers :: elasticsearch" dependencies { api project(':testcontainers') - testImplementation "org.elasticsearch.client:elasticsearch-rest-client:8.2.2" + testImplementation "org.elasticsearch.client:elasticsearch-rest-client:8.3.1" testImplementation "org.elasticsearch.client:transport:7.17.5" testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } From f14382d24fb349d370a18c185100f27ba603e80d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Jul 2022 15:22:43 +0000 Subject: [PATCH 0292/3231] Bump mockito-core from 4.6.0 to 4.6.1 in /core Bumps [mockito-core](https://github.com/mockito/mockito) from 4.6.0 to 4.6.1. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v4.6.0...v4.6.1) --- updated-dependencies: - dependency-name: org.mockito:mockito-core dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index b4c6d7ad4a2..d32e2fc43b2 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -91,7 +91,7 @@ dependencies { testImplementation 'com.rabbitmq:amqp-client:5.15.0' testImplementation 'org.mongodb:mongo-java-driver:3.12.11' - testImplementation ('org.mockito:mockito-core:4.6.0') { + testImplementation ('org.mockito:mockito-core:4.6.1') { exclude(module: 'hamcrest-core') } // Synthetic JAR used for MountableFileTest and DirectoryTarResourceTest From 4ce0ab4a855a81a0267d2a64ec3805633b0643ad Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Jul 2022 15:22:46 +0000 Subject: [PATCH 0293/3231] Bump cucumber-junit from 7.3.4 to 7.4.1 in /examples Bumps [cucumber-junit](https://github.com/cucumber/cucumber-jvm) from 7.3.4 to 7.4.1. - [Release notes](https://github.com/cucumber/cucumber-jvm/releases) - [Changelog](https://github.com/cucumber/cucumber-jvm/blob/main/CHANGELOG.md) - [Commits](https://github.com/cucumber/cucumber-jvm/compare/v7.3.4...v7.4.1) --- updated-dependencies: - dependency-name: io.cucumber:cucumber-junit dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- examples/cucumber/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cucumber/build.gradle b/examples/cucumber/build.gradle index 3e03e776ac4..1bf4e1f72cb 100644 --- a/examples/cucumber/build.gradle +++ b/examples/cucumber/build.gradle @@ -11,6 +11,6 @@ dependencies { implementation 'org.seleniumhq.selenium:selenium-firefox-driver:3.141.59' implementation 'org.seleniumhq.selenium:selenium-chrome-driver:3.141.59' testImplementation 'io.cucumber:cucumber-java:7.4.1' - testImplementation 'io.cucumber:cucumber-junit:7.3.4' + testImplementation 'io.cucumber:cucumber-junit:7.4.1' testImplementation 'org.testcontainers:selenium' } From cd03ce9275d6f6a1289c91196bf21f23ba81f6c9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Jul 2022 15:22:48 +0000 Subject: [PATCH 0294/3231] Bump aws-java-sdk-s3 from 1.12.231 to 1.12.253 in /modules/localstack Bumps [aws-java-sdk-s3](https://github.com/aws/aws-sdk-java) from 1.12.231 to 1.12.253. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.231...1.12.253) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-s3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index d6bd9781720..4ff9066b4c4 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Localstack" dependencies { api project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.231' + compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.253' testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.231' testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.253' testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.232' From 3e9202d8411bf077f617403e99b0c6c312e73041 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Jul 2022 15:22:51 +0000 Subject: [PATCH 0295/3231] Bump aws-java-sdk-logs from 1.12.232 to 1.12.253 in /modules/localstack Bumps [aws-java-sdk-logs](https://github.com/aws/aws-sdk-java) from 1.12.232 to 1.12.253. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.232...1.12.253) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-logs dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index d6bd9781720..17e63142882 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -6,7 +6,7 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.231' testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.231' testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.253' - testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.232' + testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.253' testImplementation 'software.amazon.awssdk:s3:2.17.224' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } From 69948b3600aa71f9e1ffd8c7e73acbc4a5dd0f49 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Jul 2022 15:23:01 +0000 Subject: [PATCH 0296/3231] Bump okhttp from 4.9.3 to 4.10.0 in /examples Bumps [okhttp](https://github.com/square/okhttp) from 4.9.3 to 4.10.0. - [Release notes](https://github.com/square/okhttp/releases) - [Changelog](https://github.com/square/okhttp/blob/master/CHANGELOG.md) - [Commits](https://github.com/square/okhttp/compare/parent-4.9.3...parent-4.10.0) --- updated-dependencies: - dependency-name: com.squareup.okhttp3:okhttp dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- examples/linked-container/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/linked-container/build.gradle b/examples/linked-container/build.gradle index 4bb216e6fb7..c6272fc7522 100644 --- a/examples/linked-container/build.gradle +++ b/examples/linked-container/build.gradle @@ -7,7 +7,7 @@ repositories { } dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.36' - implementation 'com.squareup.okhttp3:okhttp:4.9.3' + implementation 'com.squareup.okhttp3:okhttp:4.10.0' implementation 'org.json:json:20220320' testImplementation 'org.postgresql:postgresql:42.4.0' testImplementation 'ch.qos.logback:logback-classic:1.2.11' From 5eed792d4679811cf84c1e58887dac7bb1b13901 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Jul 2022 15:23:03 +0000 Subject: [PATCH 0297/3231] Bump google-cloud-spanner from 6.25.5 to 6.25.7 in /modules/gcloud Bumps [google-cloud-spanner](https://github.com/googleapis/java-spanner) from 6.25.5 to 6.25.7. - [Release notes](https://github.com/googleapis/java-spanner/releases) - [Changelog](https://github.com/googleapis/java-spanner/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/java-spanner/compare/v6.25.5...v6.25.7) --- updated-dependencies: - dependency-name: com.google.cloud:google-cloud-spanner dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/gcloud/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle index 4af736c6cb8..2cabbc4cf30 100644 --- a/modules/gcloud/build.gradle +++ b/modules/gcloud/build.gradle @@ -6,7 +6,7 @@ dependencies { testImplementation 'com.google.cloud:google-cloud-datastore:2.10.0' testImplementation 'com.google.cloud:google-cloud-firestore:3.2.0' testImplementation 'com.google.cloud:google-cloud-pubsub:1.120.0' - testImplementation 'com.google.cloud:google-cloud-spanner:6.25.5' + testImplementation 'com.google.cloud:google-cloud-spanner:6.25.7' testImplementation 'com.google.cloud:google-cloud-bigtable:2.9.0' testImplementation 'org.assertj:assertj-core:3.23.1' } From 2c0a004b5208edc57aed7c65d5241325b08b9cd9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Jul 2022 15:23:15 +0000 Subject: [PATCH 0298/3231] Bump common-custom-user-data-gradle-plugin from 1.7 to 1.7.2 Bumps common-custom-user-data-gradle-plugin from 1.7 to 1.7.2. --- updated-dependencies: - dependency-name: com.gradle:common-custom-user-data-gradle-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- settings.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.gradle b/settings.gradle index 73cd6b977fd..d4605c2ad84 100644 --- a/settings.gradle +++ b/settings.gradle @@ -6,7 +6,7 @@ buildscript { } dependencies { classpath "com.gradle.enterprise:com.gradle.enterprise.gradle.plugin:3.10.2" - classpath "com.gradle:common-custom-user-data-gradle-plugin:1.7" + classpath "com.gradle:common-custom-user-data-gradle-plugin:1.7.2" } } From 0060e2b11ce5602f0b000d3deb9ce74b49c91b97 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Jul 2022 20:43:23 +0000 Subject: [PATCH 0299/3231] Bump org.jetbrains.kotlin.plugin.spring in /examples Bumps [org.jetbrains.kotlin.plugin.spring](https://github.com/JetBrains/kotlin) from 1.6.21 to 1.7.0. - [Release notes](https://github.com/JetBrains/kotlin/releases) - [Changelog](https://github.com/JetBrains/kotlin/blob/master/ChangeLog.md) - [Commits](https://github.com/JetBrains/kotlin/compare/v1.6.21...v1.7.0) --- updated-dependencies: - dependency-name: org.jetbrains.kotlin.plugin.spring dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- examples/spring-boot-kotlin-redis/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/spring-boot-kotlin-redis/build.gradle b/examples/spring-boot-kotlin-redis/build.gradle index c0bd965ab79..90338ff9959 100644 --- a/examples/spring-boot-kotlin-redis/build.gradle +++ b/examples/spring-boot-kotlin-redis/build.gradle @@ -1,7 +1,7 @@ plugins { id("org.springframework.boot") version "2.6.4" id("org.jetbrains.kotlin.jvm") version "1.7.0" - id("org.jetbrains.kotlin.plugin.spring") version "1.6.21" + id("org.jetbrains.kotlin.plugin.spring") version "1.7.0" } apply plugin: 'io.spring.dependency-management' From 46b48e60fc2685ceefe7384e3b95f5d8183cbbd1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Jul 2022 20:43:33 +0000 Subject: [PATCH 0300/3231] Bump kafka-clients from 3.1.0 to 3.2.0 in /examples Bumps kafka-clients from 3.1.0 to 3.2.0. --- updated-dependencies: - dependency-name: org.apache.kafka:kafka-clients dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- examples/kafka-cluster/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/kafka-cluster/build.gradle b/examples/kafka-cluster/build.gradle index 79e91ec5a68..d69af5ceb68 100644 --- a/examples/kafka-cluster/build.gradle +++ b/examples/kafka-cluster/build.gradle @@ -10,7 +10,7 @@ dependencies { testCompileOnly "org.projectlombok:lombok:1.18.24" testAnnotationProcessor "org.projectlombok:lombok:1.18.24" testImplementation 'org.testcontainers:kafka' - testImplementation 'org.apache.kafka:kafka-clients:3.1.0' + testImplementation 'org.apache.kafka:kafka-clients:3.2.0' testImplementation 'org.assertj:assertj-core:3.23.1' testImplementation 'com.google.guava:guava:23.0' testImplementation 'ch.qos.logback:logback-classic:1.2.11' From 364111407464cf4b50bf45f1d8638b1a49ed38e0 Mon Sep 17 00:00:00 2001 From: Naveen <172697+naveensrinivasan@users.noreply.github.com> Date: Thu, 7 Jul 2022 18:47:42 -0500 Subject: [PATCH 0301/3231] chore: Set permissions for GitHub actions (#5523) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Restrict the GitHub token permissions only to the required ones; this way, even if the attackers will succeed in compromising your workflow, they won’t be able to do much. - Included permissions for the action. https://github.com/ossf/scorecard/blob/main/docs/checks.md#token-permissions https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#permissions https://docs.github.com/en/actions/using-jobs/assigning-permissions-to-jobs [Keeping your GitHub Actions and workflows secure Part 1: Preventing pwn requests](https://securitylab.github.com/research/github-actions-preventing-pwn-requests/) Signed-off-by: naveen <172697+naveensrinivasan@users.noreply.github.com> --- .github/workflows/ci-docker-wormhole.yml | 3 +++ .github/workflows/ci-examples.yml | 3 +++ .github/workflows/ci-rootless.yml | 3 +++ .github/workflows/release-drafter.yml | 6 ++++++ .github/workflows/release.yml | 3 +++ .github/workflows/update-docs-version.yml | 6 ++++++ 6 files changed, 24 insertions(+) diff --git a/.github/workflows/ci-docker-wormhole.yml b/.github/workflows/ci-docker-wormhole.yml index 6d0df9a3c21..e27e4d8d611 100644 --- a/.github/workflows/ci-docker-wormhole.yml +++ b/.github/workflows/ci-docker-wormhole.yml @@ -4,6 +4,9 @@ on: pull_request: {} push: { branches: [ master ] } +permissions: + contents: read + jobs: in-docker_test: runs-on: ubuntu-18.04 diff --git a/.github/workflows/ci-examples.yml b/.github/workflows/ci-examples.yml index b1466ba8621..f24fd6f6916 100644 --- a/.github/workflows/ci-examples.yml +++ b/.github/workflows/ci-examples.yml @@ -10,6 +10,9 @@ env: GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }} GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GRADLE_ENTERPRISE_CACHE_PASSWORD }} +permissions: + contents: read + jobs: find_gradle_jobs: runs-on: ubuntu-18.04 diff --git a/.github/workflows/ci-rootless.yml b/.github/workflows/ci-rootless.yml index 45c58c5e2d0..348cf5aaaf7 100644 --- a/.github/workflows/ci-rootless.yml +++ b/.github/workflows/ci-rootless.yml @@ -4,6 +4,9 @@ on: pull_request: {} push: { branches: [ master ] } +permissions: + contents: read + jobs: test: runs-on: ubuntu-18.04 diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml index 132b5ffd0a5..fb0780d781f 100644 --- a/.github/workflows/release-drafter.yml +++ b/.github/workflows/release-drafter.yml @@ -7,8 +7,14 @@ on: pull_request: types: [opened, reopened, synchronize] +permissions: + contents: read + jobs: update_release_draft: + permissions: + contents: write # for release-drafter/release-drafter to create a github release + pull-requests: write # for release-drafter/release-drafter to add label to PR if: github.repository == 'testcontainers/testcontainers-java' runs-on: ubuntu-latest steps: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5c786d440c5..29a0b920922 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -8,6 +8,9 @@ env: AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} +permissions: + contents: read + jobs: release: runs-on: ubuntu-18.04 diff --git a/.github/workflows/update-docs-version.yml b/.github/workflows/update-docs-version.yml index ed21bca4670..b2736c42590 100644 --- a/.github/workflows/update-docs-version.yml +++ b/.github/workflows/update-docs-version.yml @@ -4,8 +4,14 @@ on: release: types: [ published ] +permissions: + contents: read + jobs: build: + permissions: + contents: write # for peter-evans/create-pull-request to create branch + pull-requests: write # for peter-evans/create-pull-request to create a PR if: github.repository == 'testcontainers/testcontainers-java' runs-on: ubuntu-18.04 steps: From 2f3178d04fb19dbdaa9d686380c6894e443b2377 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez?= Date: Thu, 7 Jul 2022 18:49:12 -0500 Subject: [PATCH 0302/3231] Add read permission to ci.yml --- .github/workflows/ci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 70ee552b798..3287499c2fc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,6 +4,9 @@ on: pull_request: {} push: { branches: [ master ] } +permissions: + contents: read + env: AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} From 84d3444600aaeeac6813505fe5bfbf9ffce760ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Thu, 7 Jul 2022 19:44:03 -0500 Subject: [PATCH 0303/3231] Add test-retry plugin (#5586) * Enable the plugin only in CI environment * Every test will retry up to 2 times. 3 runs per test in general * Disable retry if 5 tests (no retries are count) fail --- build.gradle | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/build.gradle b/build.gradle index 1458b3db027..8c956133b03 100644 --- a/build.gradle +++ b/build.gradle @@ -16,6 +16,7 @@ plugins { id 'com.github.johnrengelman.shadow' version '7.0.0' id 'me.champeau.gradle.japicmp' version '0.2.9' apply false id 'com.diffplug.spotless' version '6.3.0' apply false + id 'org.gradle.test-retry' version '1.4.0' } apply from: "$rootDir/gradle/ci-support.gradle" @@ -33,6 +34,7 @@ subprojects { apply from: "$rootDir/gradle/shading.gradle" apply plugin: 'com.diffplug.spotless' apply plugin: 'checkstyle' + apply plugin: 'org.gradle.test-retry' group = "org.testcontainers" @@ -86,6 +88,14 @@ subprojects { exceptionFormat = 'full' events "STARTED", "PASSED", "FAILED", "SKIPPED" } + ext.isCI = System.getenv("CI") != null + if (isCI) { + retry { + maxRetries = 2 + maxFailures = 5 + failOnPassedAfterRetry = false + } + } } tasks.withType(Test).all { From d1b13d716616ae359cb9c9b99bd097493d691da1 Mon Sep 17 00:00:00 2001 From: Josef Cacek Date: Tue, 12 Jul 2022 14:08:29 +0200 Subject: [PATCH 0304/3231] Fix javadoc comment in ToxiproxyContainer#getProxy(GenericContainer, int) (#5591) --- .../java/org/testcontainers/containers/ToxiproxyContainer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/toxiproxy/src/main/java/org/testcontainers/containers/ToxiproxyContainer.java b/modules/toxiproxy/src/main/java/org/testcontainers/containers/ToxiproxyContainer.java index 9850d7a0918..5f496a38873 100644 --- a/modules/toxiproxy/src/main/java/org/testcontainers/containers/ToxiproxyContainer.java +++ b/modules/toxiproxy/src/main/java/org/testcontainers/containers/ToxiproxyContainer.java @@ -79,7 +79,7 @@ public int getControlPort() { /** * Obtain a {@link ContainerProxy} instance for target container that is managed by Testcontainers. The target - * container should be routable from this from this {@link ToxiproxyContainer} instance (e.g. on the same + * container should be routable from this {@link ToxiproxyContainer} instance (e.g. on the same * Docker {@link Network}). * * @param container target container From a76a7f52513426692f8d41cb01369f2aa10a3160 Mon Sep 17 00:00:00 2001 From: Quinn Bast Date: Wed, 13 Jul 2022 17:17:15 -0600 Subject: [PATCH 0305/3231] Add documentation for DependsOn (#5597) --- .../src/test/java/generic/DependsOnTest.java | 25 +++++++++++++++++++ docs/features/startup_and_waits.md | 8 ++++++ 2 files changed, 33 insertions(+) create mode 100644 docs/examples/junit4/generic/src/test/java/generic/DependsOnTest.java diff --git a/docs/examples/junit4/generic/src/test/java/generic/DependsOnTest.java b/docs/examples/junit4/generic/src/test/java/generic/DependsOnTest.java new file mode 100644 index 00000000000..6534a35a00f --- /dev/null +++ b/docs/examples/junit4/generic/src/test/java/generic/DependsOnTest.java @@ -0,0 +1,25 @@ +package generic; + +import org.junit.Rule; +import org.junit.Test; +import org.testcontainers.containers.GenericContainer; + +import static org.junit.Assert.assertTrue; + +public class DependsOnTest { + + @Rule + // dependsOn { + public GenericContainer redis = new GenericContainer<>("redis:3.0.2").withExposedPorts(6379); + + @Rule + public GenericContainer nginx = new GenericContainer<>("nginx:1.9.4").dependsOn(redis).withExposedPorts(80); + + // } + + @Test + public void testContainersAllStarted() { + assertTrue(redis.isRunning()); + assertTrue(nginx.isRunning()); + } +} diff --git a/docs/features/startup_and_waits.md b/docs/features/startup_and_waits.md index 3cbec97af0d..e092919d496 100644 --- a/docs/features/startup_and_waits.md +++ b/docs/features/startup_and_waits.md @@ -123,3 +123,11 @@ If none of these options meet your requirements, you can create your own subclas /startupcheck/StartupCheckStrategy.html) with an appropriate startup check mechanism in `waitUntilStartupSuccessful()`. Or you can leave it as is and just implement the `checkStartupState(DockerClient dockerClient, String containerId)` if you still want to check state periodically. + +## Depending on another container + +Sometimes, a container relies on another container to be ready before it should start itself. An example of this might be a database that needs to be started before your application container can link to it. You can tell a container that it depends on another container by using the `dependsOn` method: + + +[Depending on another container](../examples/junit4/generic/src/test/java/generic/DependsOnTest.java) inside_block:dependsOn + From 7e364891eed1eda2fdc1cdfea7e5facaccc7b025 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 14 Jul 2022 19:31:32 -0500 Subject: [PATCH 0306/3231] Update Gradle Wrapper to 7.5 (#5598) Signed-off-by: gradle-update-robot Co-authored-by: gradle-update-robot --- examples/gradle/wrapper/gradle-wrapper.jar | Bin 59821 -> 60756 bytes .../gradle/wrapper/gradle-wrapper.properties | 4 ++-- examples/gradlew | 6 ++++++ examples/gradlew.bat | 14 ++++++++------ gradle/wrapper/gradle-wrapper.jar | Bin 59821 -> 60756 bytes gradle/wrapper/gradle-wrapper.properties | 4 ++-- gradlew | 6 ++++++ gradlew.bat | 14 ++++++++------ 8 files changed, 32 insertions(+), 16 deletions(-) diff --git a/examples/gradle/wrapper/gradle-wrapper.jar b/examples/gradle/wrapper/gradle-wrapper.jar index 41d9927a4d4fb3f96a785543079b8df6723c946b..249e5832f090a2944b7473328c07c9755baa3196 100644 GIT binary patch delta 10197 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 diff --git a/examples/gradle/wrapper/gradle-wrapper.properties b/examples/gradle/wrapper/gradle-wrapper.properties index 5c51a4acc9d..012d6d90445 100644 --- a/examples/gradle/wrapper/gradle-wrapper.properties +++ b/examples/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=29e49b10984e585d8118b7d0bc452f944e386458df27371b49b4ac1dec4b7fda -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip +distributionSha256Sum=cb87f222c5585bd46838ad4db78463a5c5f3d336e5e2b98dc7c0c586527351c2 +distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/examples/gradlew b/examples/gradlew index 1b6c787337f..a69d9cb6c20 100755 --- a/examples/gradlew +++ b/examples/gradlew @@ -205,6 +205,12 @@ set -- \ org.gradle.wrapper.GradleWrapperMain \ "$@" +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + # Use "xargs" to parse quoted args. # # With -n1 it outputs one arg per line, with the quotes and backslashes removed. diff --git a/examples/gradlew.bat b/examples/gradlew.bat index ac1b06f9382..53a6b238d41 100644 --- a/examples/gradlew.bat +++ b/examples/gradlew.bat @@ -14,7 +14,7 @@ @rem limitations under the License. @rem -@if "%DEBUG%" == "" @echo off +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -25,7 +25,7 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @@ -40,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute +if %ERRORLEVEL% equ 0 goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -75,13 +75,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar :end @rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd +if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 41d9927a4d4fb3f96a785543079b8df6723c946b..249e5832f090a2944b7473328c07c9755baa3196 100644 GIT binary patch delta 10197 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 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index e1e0c8dc42b..ef80eb5051f 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=e6d864e3b5bc05cc62041842b306383fc1fefcec359e70cebb1d470a6094ca82 -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-all.zip +distributionSha256Sum=97a52d145762adc241bad7fd18289bf7f6801e08ece6badf80402fe2b9f250b1 +distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 1b6c787337f..a69d9cb6c20 100755 --- a/gradlew +++ b/gradlew @@ -205,6 +205,12 @@ set -- \ org.gradle.wrapper.GradleWrapperMain \ "$@" +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + # Use "xargs" to parse quoted args. # # With -n1 it outputs one arg per line, with the quotes and backslashes removed. diff --git a/gradlew.bat b/gradlew.bat index ac1b06f9382..53a6b238d41 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -14,7 +14,7 @@ @rem limitations under the License. @rem -@if "%DEBUG%" == "" @echo off +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -25,7 +25,7 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @@ -40,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute +if %ERRORLEVEL% equ 0 goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -75,13 +75,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar :end @rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd +if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal From 55ec5f1c5311f7fbc959ae56a4b1e546915c87fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez?= Date: Tue, 19 Jul 2022 16:50:55 -0500 Subject: [PATCH 0307/3231] Use host instead of containerIpAddress for Kotlin sample --- .../test/kotlin/com/example/redis/AbstractIntegrationTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/spring-boot-kotlin-redis/src/test/kotlin/com/example/redis/AbstractIntegrationTest.kt b/examples/spring-boot-kotlin-redis/src/test/kotlin/com/example/redis/AbstractIntegrationTest.kt index 6350ace9bd9..98f56192087 100644 --- a/examples/spring-boot-kotlin-redis/src/test/kotlin/com/example/redis/AbstractIntegrationTest.kt +++ b/examples/spring-boot-kotlin-redis/src/test/kotlin/com/example/redis/AbstractIntegrationTest.kt @@ -26,7 +26,7 @@ abstract class AbstractIntegrationTest { redisContainer.start() TestPropertyValues.of( - "spring.redis.host=${redisContainer.containerIpAddress}", + "spring.redis.host=${redisContainer.host}", "spring.redis.port=${redisContainer.firstMappedPort}" ).applyTo(configurableApplicationContext.environment) } From 8c8bf2a818da7bbe1ae3dca2171eebef89746a2b Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Wed, 20 Jul 2022 19:11:35 +0200 Subject: [PATCH 0308/3231] Enable JUnitPlatform in Gradle for JUnit5 examples (#4130) --- docs/examples/junit5/redis/build.gradle | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/examples/junit5/redis/build.gradle b/docs/examples/junit5/redis/build.gradle index 348f4788555..16f0d5bc598 100644 --- a/docs/examples/junit5/redis/build.gradle +++ b/docs/examples/junit5/redis/build.gradle @@ -9,3 +9,7 @@ dependencies { testImplementation project(":testcontainers") testImplementation project(":junit-jupiter") } + +test { + useJUnitPlatform() +} From c654018f93f5259897762c2c6a662b542a765fe1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Thu, 21 Jul 2022 03:16:53 -0500 Subject: [PATCH 0309/3231] Update spotless version to 6.8.0 (#5605) --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 8c956133b03..0ded70ad71a 100644 --- a/build.gradle +++ b/build.gradle @@ -15,7 +15,7 @@ plugins { id 'io.franzbecker.gradle-lombok' version '5.0.0' id 'com.github.johnrengelman.shadow' version '7.0.0' id 'me.champeau.gradle.japicmp' version '0.2.9' apply false - id 'com.diffplug.spotless' version '6.3.0' apply false + id 'com.diffplug.spotless' version '6.8.0' apply false id 'org.gradle.test-retry' version '1.4.0' } From d387d8518cf0976618c6580c09f68b53c4008283 Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Thu, 21 Jul 2022 16:51:50 +0200 Subject: [PATCH 0310/3231] Update Oracle-XE docs and use code example from tests (#4385) Co-authored-by: Sergei Egorov --- docs/modules/databases/oraclexe.md | 25 +++---------------- .../junit/oracle/SimpleOracleTest.java | 4 ++- 2 files changed, 7 insertions(+), 22 deletions(-) diff --git a/docs/modules/databases/oraclexe.md b/docs/modules/databases/oraclexe.md index 583d68f3c3d..bfc06d9a106 100644 --- a/docs/modules/databases/oraclexe.md +++ b/docs/modules/databases/oraclexe.md @@ -4,27 +4,10 @@ See [Database containers](./index.md) for documentation and usage that is common ## Usage example -Running Oracle XE as a stand-in for in a test: - -```java -public class SomeTest { - - @Rule - public OracleContainer oracle = new OracleContainer("name_of_your_oracle_xe_image"); - - @Test - public void someTestMethod() { - String url = oracle.getJdbcUrl(); - - ... create a connection and run test as normal -``` - -## Specifying a docker image name via config - -If you do not pass an image name to the `OracleContainer` constructor, a suitable image name should be placed in -configuration instead. -To do this, please place a file on the classpath named `testcontainers.properties`, -containing `oracle.container.image=IMAGE`, where IMAGE is a suitable image name and tag. +You can use `OracleContainer` like any other JDBC container: + +[Container creation](../../../modules/oracle-xe/src/test/java/org/testcontainers/junit/oracle/SimpleOracleTest.java) inside_block:constructor + ## Adding this module to your project dependencies diff --git a/modules/oracle-xe/src/test/java/org/testcontainers/junit/oracle/SimpleOracleTest.java b/modules/oracle-xe/src/test/java/org/testcontainers/junit/oracle/SimpleOracleTest.java index 2c514062415..b48bd67efc9 100644 --- a/modules/oracle-xe/src/test/java/org/testcontainers/junit/oracle/SimpleOracleTest.java +++ b/modules/oracle-xe/src/test/java/org/testcontainers/junit/oracle/SimpleOracleTest.java @@ -52,10 +52,12 @@ public void testPluggableDatabase() throws SQLException { @Test public void testPluggableDatabaseAndCustomUser() throws SQLException { try ( - OracleContainer oracle = new OracleContainer(ORACLE_DOCKER_IMAGE_NAME) + // constructor { + OracleContainer oracle = new OracleContainer("gvenzl/oracle-xe:18.4.0-slim") .withDatabaseName("testDB") .withUsername("testUser") .withPassword("testPassword") + // } ) { runTest(oracle, "testDB", "testUser", "testPassword"); } From 29aa84d19739f3d20c485275180e0619dd4145b1 Mon Sep 17 00:00:00 2001 From: Tobias Stadler Date: Thu, 21 Jul 2022 16:54:40 +0200 Subject: [PATCH 0311/3231] Update alpine image to 3.16 (#4698) --- .../main/java/org/testcontainers/DockerClientFactory.java | 2 +- core/src/test/java/org/testcontainers/TestImages.java | 4 ++-- .../testcontainers/containers/DockerComposeFilesTest.java | 2 +- .../containers/ParsedDockerComposeFileValidationTest.java | 8 ++++---- .../containers/output/ToStringConsumerTest.java | 8 ++++---- .../org/testcontainers/dockerclient/ImagePullTest.java | 2 +- .../java/org/testcontainers/junit/DockerfileTest.java | 6 +++--- .../testcontainers/junit/FixedHostPortContainerTest.java | 2 +- .../junit/ParameterizedDockerfileContainerTest.java | 2 ++ .../testcontainers/utility/DirectoryTarResourceTest.java | 6 +++--- core/src/test/resources/compose-dockerfile/Dockerfile | 2 +- .../resources/compose-scaling-multiple-containers.yml | 2 +- .../test/resources/dockerfile-build-invalid/Dockerfile | 2 +- core/src/test/resources/dockerfile-build-test/Dockerfile | 2 +- .../test/resources/dockerfile-build-test/Dockerfile-alt | 2 +- .../resources/dockerfile-build-test/Dockerfile-buildarg | 2 +- .../resources/dockerfile-build-test/Dockerfile-currentdir | 2 +- .../resources/health-wait-strategy-dockerfile/Dockerfile | 2 +- core/src/test/resources/mappable-dockerfile/Dockerfile | 2 +- .../src/test/java/generic/ContainerCreationTest.java | 2 +- .../generic/src/test/java/generic/ContainerLabelTest.java | 4 ++-- .../junit4/generic/src/test/java/generic/ExecTest.java | 2 +- docs/features/configuration.md | 2 +- docs/features/creating_images.md | 2 +- .../org/testcontainers/spock/SpockTestImages.groovy | 2 +- 25 files changed, 38 insertions(+), 36 deletions(-) diff --git a/core/src/main/java/org/testcontainers/DockerClientFactory.java b/core/src/main/java/org/testcontainers/DockerClientFactory.java index 04c20236763..d4b40193ca2 100644 --- a/core/src/main/java/org/testcontainers/DockerClientFactory.java +++ b/core/src/main/java/org/testcontainers/DockerClientFactory.java @@ -60,7 +60,7 @@ public class DockerClientFactory { public static final Map DEFAULT_LABELS = ImmutableMap.of(TESTCONTAINERS_LABEL, "true"); - private static final DockerImageName TINY_IMAGE = DockerImageName.parse("alpine:3.14"); + private static final DockerImageName TINY_IMAGE = DockerImageName.parse("alpine:3.16"); private static DockerClientFactory instance; diff --git a/core/src/test/java/org/testcontainers/TestImages.java b/core/src/test/java/org/testcontainers/TestImages.java index 3798f42af0d..5308f341100 100644 --- a/core/src/test/java/org/testcontainers/TestImages.java +++ b/core/src/test/java/org/testcontainers/TestImages.java @@ -6,7 +6,7 @@ public interface TestImages { DockerImageName REDIS_IMAGE = DockerImageName.parse("redis:3.0.2"); DockerImageName RABBITMQ_IMAGE = DockerImageName.parse("rabbitmq:3.5.3"); DockerImageName MONGODB_IMAGE = DockerImageName.parse("mongo:3.1.5"); - DockerImageName ALPINE_IMAGE = DockerImageName.parse("alpine:3.14"); + DockerImageName ALPINE_IMAGE = DockerImageName.parse("alpine:3.16"); DockerImageName DOCKER_REGISTRY_IMAGE = DockerImageName.parse("registry:2.7.0"); - DockerImageName TINY_IMAGE = DockerImageName.parse("alpine:3.14"); + DockerImageName TINY_IMAGE = DockerImageName.parse("alpine:3.16"); } diff --git a/core/src/test/java/org/testcontainers/containers/DockerComposeFilesTest.java b/core/src/test/java/org/testcontainers/containers/DockerComposeFilesTest.java index 6e1630f005f..612d249449b 100644 --- a/core/src/test/java/org/testcontainers/containers/DockerComposeFilesTest.java +++ b/core/src/test/java/org/testcontainers/containers/DockerComposeFilesTest.java @@ -28,6 +28,6 @@ public void shouldGetDependencyImagesWhenOverriding() { ); Assertions .assertThat(dockerComposeFiles.getDependencyImages()) - .containsExactlyInAnyOrder("alpine:3.14", "redis:b", "mysql:b", "aservice:latest"); + .containsExactlyInAnyOrder("alpine:3.16", "redis:b", "mysql:b", "aservice:latest"); } } diff --git a/core/src/test/java/org/testcontainers/containers/ParsedDockerComposeFileValidationTest.java b/core/src/test/java/org/testcontainers/containers/ParsedDockerComposeFileValidationTest.java index 61955de98ba..f374018a8ef 100644 --- a/core/src/test/java/org/testcontainers/containers/ParsedDockerComposeFileValidationTest.java +++ b/core/src/test/java/org/testcontainers/containers/ParsedDockerComposeFileValidationTest.java @@ -98,9 +98,9 @@ public void shouldObtainImageFromDockerfileBuild() { .contains( entry("mysql", Sets.newHashSet("mysql")), entry("redis", Sets.newHashSet("redis")), - entry("custom", Sets.newHashSet("alpine:3.14")) + entry("custom", Sets.newHashSet("alpine:3.16")) ) - .as("all defined images are found"); // r/ redis, mysql from compose file, alpine:3.14 from Dockerfile build + .as("all defined images are found"); // r/ redis, mysql from compose file, alpine:3.16 from Dockerfile build } @Test @@ -112,8 +112,8 @@ public void shouldObtainImageFromDockerfileBuildWithContext() { .contains( entry("mysql", Sets.newHashSet("mysql")), entry("redis", Sets.newHashSet("redis")), - entry("custom", Sets.newHashSet("alpine:3.14")) + entry("custom", Sets.newHashSet("alpine:3.16")) ) - .as("all defined images are found"); // redis, mysql from compose file, alpine:3.14 from Dockerfile build + .as("all defined images are found"); // redis, mysql from compose file, alpine:3.16 from Dockerfile build } } diff --git a/core/src/test/java/org/testcontainers/containers/output/ToStringConsumerTest.java b/core/src/test/java/org/testcontainers/containers/output/ToStringConsumerTest.java index 08973f2d601..211b12080a0 100644 --- a/core/src/test/java/org/testcontainers/containers/output/ToStringConsumerTest.java +++ b/core/src/test/java/org/testcontainers/containers/output/ToStringConsumerTest.java @@ -24,7 +24,7 @@ public class ToStringConsumerTest { @Test public void newlines_are_not_added_to_exec_output() throws Exception { - try (GenericContainer container = new GenericContainer<>("alpine:3.14")) { + try (GenericContainer container = new GenericContainer<>("alpine:3.16")) { container.withCommand("sleep", "2m"); container.start(); @@ -35,7 +35,7 @@ public void newlines_are_not_added_to_exec_output() throws Exception { @Test(timeout = 60_000L) public void newlines_are_not_added_to_exec_output_with_tty() throws Exception { - try (GenericContainer container = new GenericContainer<>("alpine:3.14")) { + try (GenericContainer container = new GenericContainer<>("alpine:3.16")) { container.withCreateContainerCmdModifier(cmd -> { cmd.withAttachStdin(true).withStdinOpen(true).withTty(true); }); @@ -49,7 +49,7 @@ public void newlines_are_not_added_to_exec_output_with_tty() throws Exception { @Test public void newlines_are_not_added_to_container_output() { - try (GenericContainer container = new GenericContainer<>("alpine:3.14")) { + try (GenericContainer container = new GenericContainer<>("alpine:3.16")) { container.withCommand("echo", "-n", LARGE_PAYLOAD); container.setStartupCheckStrategy(new OneShotStartupCheckStrategy()); container.start(); @@ -62,7 +62,7 @@ public void newlines_are_not_added_to_container_output() { @Test public void newlines_are_not_added_to_container_output_with_tty() { - try (GenericContainer container = new GenericContainer<>("alpine:3.14")) { + try (GenericContainer container = new GenericContainer<>("alpine:3.16")) { container.withCreateContainerCmdModifier(cmd -> { cmd.withTty(true); }); diff --git a/core/src/test/java/org/testcontainers/dockerclient/ImagePullTest.java b/core/src/test/java/org/testcontainers/dockerclient/ImagePullTest.java index 730208c553f..e1c2a56e8a9 100644 --- a/core/src/test/java/org/testcontainers/dockerclient/ImagePullTest.java +++ b/core/src/test/java/org/testcontainers/dockerclient/ImagePullTest.java @@ -16,7 +16,7 @@ public class ImagePullTest { public static String[] parameters() { return new String[] { "alpine:latest", - "alpine:3.14", + "alpine:3.16", "alpine", // omitting the tag should work and default to latest "alpine@sha256:1775bebec23e1f3ce486989bfc9ff3c4e951690df84aa9f926497d82f2ffca9d", "quay.io/testcontainers/ryuk:latest", diff --git a/core/src/test/java/org/testcontainers/junit/DockerfileTest.java b/core/src/test/java/org/testcontainers/junit/DockerfileTest.java index e121e7db5a4..df7bc417688 100644 --- a/core/src/test/java/org/testcontainers/junit/DockerfileTest.java +++ b/core/src/test/java/org/testcontainers/junit/DockerfileTest.java @@ -40,7 +40,7 @@ protected void configure(BuildImageCmd buildImageCmd) { super.configure(buildImageCmd); List dockerfile = Arrays.asList( - "FROM alpine:3.14", + "FROM alpine:3.16", "RUN echo 'hello from Docker build process'", "CMD yes" ); @@ -60,7 +60,7 @@ public void dockerfileBuilderWorks() { .withFileFromString("folder/someFile.txt", "hello") .withDockerfileFromBuilder(builder -> { builder - .from("alpine:3.14") + .from("alpine:3.16") .workDir("/app") .add("test.txt", "test file.txt") .run("ls", "-la", "/app/test file.txt") @@ -103,7 +103,7 @@ public int getFileMode() { ) .withDockerfileFromBuilder(builder -> { builder - .from("alpine:3.14") // + .from("alpine:3.16") // .copy("someFile.txt", "/someFile.txt") .cmd("stat -c \"%a\" /someFile.txt"); }); diff --git a/core/src/test/java/org/testcontainers/junit/FixedHostPortContainerTest.java b/core/src/test/java/org/testcontainers/junit/FixedHostPortContainerTest.java index a6bb13d160c..08545535e6b 100644 --- a/core/src/test/java/org/testcontainers/junit/FixedHostPortContainerTest.java +++ b/core/src/test/java/org/testcontainers/junit/FixedHostPortContainerTest.java @@ -24,7 +24,7 @@ */ public class FixedHostPortContainerTest { - private static final String TEST_IMAGE = "alpine:3.14"; + private static final String TEST_IMAGE = "alpine:3.16"; /** * Default http server port (just something different from default) diff --git a/core/src/test/java/org/testcontainers/junit/ParameterizedDockerfileContainerTest.java b/core/src/test/java/org/testcontainers/junit/ParameterizedDockerfileContainerTest.java index 62eccc2000c..7c176f14a1a 100644 --- a/core/src/test/java/org/testcontainers/junit/ParameterizedDockerfileContainerTest.java +++ b/core/src/test/java/org/testcontainers/junit/ParameterizedDockerfileContainerTest.java @@ -43,6 +43,8 @@ public static Object[][] data() { { "alpine:3.12", "3.12" }, { "alpine:3.13", "3.13" }, { "alpine:3.14", "3.14" }, + { "alpine:3.15", "3.15" }, + { "alpine:3.16", "3.16" }, }; } diff --git a/core/src/test/java/org/testcontainers/utility/DirectoryTarResourceTest.java b/core/src/test/java/org/testcontainers/utility/DirectoryTarResourceTest.java index cbd35a62b52..46fd6894a02 100644 --- a/core/src/test/java/org/testcontainers/utility/DirectoryTarResourceTest.java +++ b/core/src/test/java/org/testcontainers/utility/DirectoryTarResourceTest.java @@ -27,7 +27,7 @@ public void simpleRecursiveFileTest() { new ImageFromDockerfile() .withDockerfileFromBuilder(builder -> { builder - .from("alpine:3.14") + .from("alpine:3.16") .copy("/tmp/foo", "/foo") .cmd("cat /foo/test/resources/test-recursive-file.txt") .build(); @@ -52,7 +52,7 @@ public void simpleRecursiveFileWithPermissionTest() { new ImageFromDockerfile() .withDockerfileFromBuilder(builder -> { builder - .from("alpine:3.14") // + .from("alpine:3.16") // .copy("/tmp/foo", "/foo") .cmd("ls", "-al", "/") .build(); @@ -79,7 +79,7 @@ public void simpleRecursiveClasspathResourceTest() { new ImageFromDockerfile() .withDockerfileFromBuilder(builder -> { builder - .from("alpine:3.14") // + .from("alpine:3.16") // .copy("/tmp/foo", "/foo") .cmd("ls -lRt /foo") .build(); diff --git a/core/src/test/resources/compose-dockerfile/Dockerfile b/core/src/test/resources/compose-dockerfile/Dockerfile index 7252932b104..566428fa97f 100644 --- a/core/src/test/resources/compose-dockerfile/Dockerfile +++ b/core/src/test/resources/compose-dockerfile/Dockerfile @@ -1,4 +1,4 @@ -FROM alpine:3.14 +FROM alpine:3.16 ADD passthrough.sh /passthrough.sh diff --git a/core/src/test/resources/compose-scaling-multiple-containers.yml b/core/src/test/resources/compose-scaling-multiple-containers.yml index 3ea528d4574..fc498a9fd42 100644 --- a/core/src/test/resources/compose-scaling-multiple-containers.yml +++ b/core/src/test/resources/compose-scaling-multiple-containers.yml @@ -3,5 +3,5 @@ services: redis: image: redis other: - image: alpine:3.14 + image: alpine:3.16 command: sleep 10000 diff --git a/core/src/test/resources/dockerfile-build-invalid/Dockerfile b/core/src/test/resources/dockerfile-build-invalid/Dockerfile index b0198eb3e02..66663986d13 100644 --- a/core/src/test/resources/dockerfile-build-invalid/Dockerfile +++ b/core/src/test/resources/dockerfile-build-invalid/Dockerfile @@ -1 +1 @@ -FROM alpine:3.14 +FROM alpine:3.16 diff --git a/core/src/test/resources/dockerfile-build-test/Dockerfile b/core/src/test/resources/dockerfile-build-test/Dockerfile index 9ae7dba8658..3f341034fef 100644 --- a/core/src/test/resources/dockerfile-build-test/Dockerfile +++ b/core/src/test/resources/dockerfile-build-test/Dockerfile @@ -1,2 +1,2 @@ -FROM alpine:3.14 +FROM alpine:3.16 COPY localfile.txt /test.txt diff --git a/core/src/test/resources/dockerfile-build-test/Dockerfile-alt b/core/src/test/resources/dockerfile-build-test/Dockerfile-alt index 43e5c556fe9..fe36516c397 100644 --- a/core/src/test/resources/dockerfile-build-test/Dockerfile-alt +++ b/core/src/test/resources/dockerfile-build-test/Dockerfile-alt @@ -1,2 +1,2 @@ -FROM alpine:3.14 +FROM alpine:3.16 RUN echo "test4567" > /test.txt diff --git a/core/src/test/resources/dockerfile-build-test/Dockerfile-buildarg b/core/src/test/resources/dockerfile-build-test/Dockerfile-buildarg index b37f4193643..f137d39f0d2 100644 --- a/core/src/test/resources/dockerfile-build-test/Dockerfile-buildarg +++ b/core/src/test/resources/dockerfile-build-test/Dockerfile-buildarg @@ -1,3 +1,3 @@ -FROM alpine:3.14 +FROM alpine:3.16 ARG CUSTOM_ARG RUN echo "${CUSTOM_ARG}" > /test.txt diff --git a/core/src/test/resources/dockerfile-build-test/Dockerfile-currentdir b/core/src/test/resources/dockerfile-build-test/Dockerfile-currentdir index e8456e1bc1f..3a6bfd62f02 100644 --- a/core/src/test/resources/dockerfile-build-test/Dockerfile-currentdir +++ b/core/src/test/resources/dockerfile-build-test/Dockerfile-currentdir @@ -1,2 +1,2 @@ -FROM alpine:3.14 +FROM alpine:3.16 COPY . / diff --git a/core/src/test/resources/health-wait-strategy-dockerfile/Dockerfile b/core/src/test/resources/health-wait-strategy-dockerfile/Dockerfile index c0abb7bb597..ed404c1f0d8 100644 --- a/core/src/test/resources/health-wait-strategy-dockerfile/Dockerfile +++ b/core/src/test/resources/health-wait-strategy-dockerfile/Dockerfile @@ -1,4 +1,4 @@ -FROM alpine:3.14 +FROM alpine:3.16 HEALTHCHECK --interval=1s CMD test -e /testfile diff --git a/core/src/test/resources/mappable-dockerfile/Dockerfile b/core/src/test/resources/mappable-dockerfile/Dockerfile index df186f6579c..5b9f6a02783 100644 --- a/core/src/test/resources/mappable-dockerfile/Dockerfile +++ b/core/src/test/resources/mappable-dockerfile/Dockerfile @@ -1,4 +1,4 @@ -FROM alpine:3.14 +FROM alpine:3.16 ADD folder/someFile.txt /someFile.txt RUN cat /someFile.txt ADD test.txt /test.txt diff --git a/docs/examples/junit4/generic/src/test/java/generic/ContainerCreationTest.java b/docs/examples/junit4/generic/src/test/java/generic/ContainerCreationTest.java index bdd82968191..0f6269968dd 100644 --- a/docs/examples/junit4/generic/src/test/java/generic/ContainerCreationTest.java +++ b/docs/examples/junit4/generic/src/test/java/generic/ContainerCreationTest.java @@ -20,7 +20,7 @@ public class ContainerCreationTest { // } // spotless:on - public static final DockerImageName ALPINE_IMAGE = DockerImageName.parse("alpine:3.14"); + public static final DockerImageName ALPINE_IMAGE = DockerImageName.parse("alpine:3.16"); // spotless:off // withOptions { diff --git a/docs/examples/junit4/generic/src/test/java/generic/ContainerLabelTest.java b/docs/examples/junit4/generic/src/test/java/generic/ContainerLabelTest.java index 64618ad25a9..868b8ec44f7 100644 --- a/docs/examples/junit4/generic/src/test/java/generic/ContainerLabelTest.java +++ b/docs/examples/junit4/generic/src/test/java/generic/ContainerLabelTest.java @@ -9,7 +9,7 @@ public class ContainerLabelTest { // single_label { - public GenericContainer containerWithLabel = new GenericContainer(DockerImageName.parse("alpine:3.14")) + public GenericContainer containerWithLabel = new GenericContainer(DockerImageName.parse("alpine:3.16")) .withLabel("key", "value"); // } @@ -17,7 +17,7 @@ public class ContainerLabelTest { private Map mapOfLabels = new HashMap<>(); // populate map, e.g. mapOfLabels.put("key1", "value1"); - public GenericContainer containerWithMultipleLabels = new GenericContainer(DockerImageName.parse("alpine:3.14")) + public GenericContainer containerWithMultipleLabels = new GenericContainer(DockerImageName.parse("alpine:3.16")) .withLabels(mapOfLabels); // } } diff --git a/docs/examples/junit4/generic/src/test/java/generic/ExecTest.java b/docs/examples/junit4/generic/src/test/java/generic/ExecTest.java index 2530cadb410..7548ae4a613 100644 --- a/docs/examples/junit4/generic/src/test/java/generic/ExecTest.java +++ b/docs/examples/junit4/generic/src/test/java/generic/ExecTest.java @@ -13,7 +13,7 @@ public class ExecTest { @Rule - public GenericContainer container = new GenericContainer<>(DockerImageName.parse("alpine:3.14")) + public GenericContainer container = new GenericContainer<>(DockerImageName.parse("alpine:3.16")) .withCommand("top"); @Test diff --git a/docs/features/configuration.md b/docs/features/configuration.md index fbb369fe6a6..f93d0807d01 100644 --- a/docs/features/configuration.md +++ b/docs/features/configuration.md @@ -49,7 +49,7 @@ Some companies disallow the usage of Docker Hub, but you can override `*.image` > **ryuk.container.image = testcontainers/ryuk:0.3.3** > Performs fail-safe cleanup of containers, and always required (unless [Ryuk is disabled](#disabling-ryuk)) -> **tinyimage.container.image = alpine:3.14** +> **tinyimage.container.image = alpine:3.16** > Used to check whether images can be pulled at startup, and always required (unless [startup checks are disabled](#disabling-the-startup-checks)) > **sshd.container.image = testcontainers/sshd:1.1.0** diff --git a/docs/features/creating_images.md b/docs/features/creating_images.md index 6a06627efa0..e4e6315a405 100644 --- a/docs/features/creating_images.md +++ b/docs/features/creating_images.md @@ -51,7 +51,7 @@ new GenericContainer( new ImageFromDockerfile() .withDockerfileFromBuilder(builder -> builder - .from("alpine:3.14") + .from("alpine:3.16") .run("apk add --update nginx") .cmd("nginx", "-g", "daemon off;") .build())) diff --git a/modules/spock/src/test/groovy/org/testcontainers/spock/SpockTestImages.groovy b/modules/spock/src/test/groovy/org/testcontainers/spock/SpockTestImages.groovy index 41432e5e514..bea0a974c89 100644 --- a/modules/spock/src/test/groovy/org/testcontainers/spock/SpockTestImages.groovy +++ b/modules/spock/src/test/groovy/org/testcontainers/spock/SpockTestImages.groovy @@ -6,5 +6,5 @@ interface SpockTestImages { DockerImageName MYSQL_IMAGE = DockerImageName.parse("mysql:5.7.34") DockerImageName POSTGRES_TEST_IMAGE = DockerImageName.parse("postgres:9.6.12") DockerImageName HTTPD_IMAGE = DockerImageName.parse("httpd:2.4-alpine") - DockerImageName TINY_IMAGE = DockerImageName.parse("alpine:3.14") + DockerImageName TINY_IMAGE = DockerImageName.parse("alpine:3.16") } From 6aab4d92aeaef5d1e6174f52267bf48edc3aaf8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Mon, 25 Jul 2022 03:05:10 -0500 Subject: [PATCH 0312/3231] Add pr label for area/docker-compose (#5610) --- .github/labeler.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/labeler.yml b/.github/labeler.yml index 9293237182e..84e21af744a 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -1,3 +1,6 @@ +"area/docker-compose": + - core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java + - core/src/main/java/org/testcontainers/containers/DockerComposeFiles.java "github_actions": - .github/workflows/* "gradle-wrapper": From d3cb25ec7d66332d124ad1a8fa36771a7090412d Mon Sep 17 00:00:00 2001 From: Erik Pragt <91307175+epragtbeamtree@users.noreply.github.com> Date: Wed, 27 Jul 2022 16:47:04 +1000 Subject: [PATCH 0313/3231] Update CircleCI docs for current Cloud/v2/v3 configuration (#5611) Co-authored-by: Kevin Wittek --- .../continuous_integration/circle_ci.md | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/docs/supported_docker_environment/continuous_integration/circle_ci.md b/docs/supported_docker_environment/continuous_integration/circle_ci.md index 64e7ea131f2..05f0776f6c1 100644 --- a/docs/supported_docker_environment/continuous_integration/circle_ci.md +++ b/docs/supported_docker_environment/continuous_integration/circle_ci.md @@ -1,19 +1,16 @@ -# CircleCI 2.0 +# CircleCI (Cloud, Server v2.x, and Server v3.x) -Your CircleCI configuration should use a dedicated VM for testcontainers to work. You can achieve this by specifying the -executor type in your `.circleci/config.yml` to be `machine` instead of the default `docker` executor ( see [Choosing an Executor Type](https://circleci.com/docs/2.0/executor-types/) for more info ). +Your CircleCI configuration should use a dedicated VM for Testcontainers to work. You can achieve this by specifying the +executor type in your `.circleci/config.yml` to be `machine` instead of the default `docker` executor (see [Choosing an Executor Type](https://circleci.com/docs/2.0/executor-types/) for more info). -Here is a sample CircleCI configuration that does a checkout of a project and runs maven: +Here is a sample CircleCI configuration that does a checkout of a project and runs Maven: ```yml -# Check https://circleci.com/docs/2.0/language-java/ for more details -# -version: 2 -machine: true jobs: build: + # Check https://circleci.com/docs/executor-intro#linux-vm for more details + machine: true steps: - checkout - - run: mvn -B clean install ``` From fd97b769a8b083a8e3a208d119377176fe88cb8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Wed, 27 Jul 2022 19:57:31 -0500 Subject: [PATCH 0314/3231] Add docs for AWS CodeBuild and Tekton (#5614) --- .../continuous_integration/aws_codebuild.md | 18 ++++ .../continuous_integration/tekton.md | 94 +++++++++++++++++++ mkdocs.yml | 2 + 3 files changed, 114 insertions(+) create mode 100644 docs/supported_docker_environment/continuous_integration/aws_codebuild.md create mode 100644 docs/supported_docker_environment/continuous_integration/tekton.md diff --git a/docs/supported_docker_environment/continuous_integration/aws_codebuild.md b/docs/supported_docker_environment/continuous_integration/aws_codebuild.md new file mode 100644 index 00000000000..c28854d9e0e --- /dev/null +++ b/docs/supported_docker_environment/continuous_integration/aws_codebuild.md @@ -0,0 +1,18 @@ +# AWS CodeBuild + +To enable access to Docker in AWS CodeBuild, go to `Privileged` section and check +`Enable this flag if you want to build Docker images or want your builds to get elevated privileges`. + +This is a sample `buildspec.yml` config: + +```yaml +version: 0.2 + +phases: + install: + runtime-versions: + java: corretto17 + build: + commands: + - ./mvnw test +``` diff --git a/docs/supported_docker_environment/continuous_integration/tekton.md b/docs/supported_docker_environment/continuous_integration/tekton.md new file mode 100644 index 00000000000..f8c6fb96adc --- /dev/null +++ b/docs/supported_docker_environment/continuous_integration/tekton.md @@ -0,0 +1,94 @@ +# Tekton + +o enable access to Docker in Tekton, a dind sidecar needs to be added. An example of it can be found +[here](https://github.com/tektoncd/pipeline/blob/main/examples/v1beta1/taskruns/dind-sidecar.yaml) + +This is an example + +```yaml +apiVersion: tekton.dev/v1beta1 +kind: Task +metadata: + name: run-tests + description: Run Tests +spec: + workspaces: + - name: source + steps: + - name: read + image: eclipse-temurin:17.0.3_7-jdk-alpine + workingDir: $(workspaces.source.path) + script: ./mvnw test + volumeMounts: + - mountPath: /var/run/ + name: dind-socket + sidecars: + - image: docker:20.10-dind + name: docker + securityContext: + privileged: true + volumeMounts: + - mountPath: /var/lib/docker + name: dind-storage + - mountPath: /var/run/ + name: dind-socket + volumes: + - name: dind-storage + emptyDir: { } + - name: dind-socket + emptyDir: { } +--- +apiVersion: tekton.dev/v1beta1 +kind: Pipeline +metadata: + name: testcontainers-demo +spec: + description: | + This pipeline clones a git repo, run testcontainers. + params: + - name: repo-url + type: string + description: The git repo URL to clone from. + workspaces: + - name: shared-data + description: | + This workspace contains the cloned repo files, so they can be read by the + next task. + tasks: + - name: fetch-source + taskRef: + name: git-clone + workspaces: + - name: output + workspace: shared-data + params: + - name: url + value: $(params.repo-url) + - name: run-tests + runAfter: ["fetch-source"] + taskRef: + name: run-tests + workspaces: + - name: source + workspace: shared-data +--- +apiVersion: tekton.dev/v1beta1 +kind: PipelineRun +metadata: + name: testcontainers-demo-run +spec: + pipelineRef: + name: testcontainers-demo + workspaces: + - name: shared-data + volumeClaimTemplate: + spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 1Gi + params: + - name: repo-url + value: https://github.com/testcontainers/testcontainers-java-repro.git +``` diff --git a/mkdocs.yml b/mkdocs.yml index 21e157969e5..396986a955b 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -88,11 +88,13 @@ nav: - System Requirements: - supported_docker_environment/index.md - Continuous Integration: + - supported_docker_environment/continuous_integration/aws_codebuild.md - supported_docker_environment/continuous_integration/dind_patterns.md - supported_docker_environment/continuous_integration/circle_ci.md - supported_docker_environment/continuous_integration/drone.md - supported_docker_environment/continuous_integration/gitlab_ci.md - supported_docker_environment/continuous_integration/bitbucket_pipelines.md + - supported_docker_environment/continuous_integration/tekton.md - supported_docker_environment/windows.md - supported_docker_environment/logging_config.md - supported_docker_environment/image_registry_rate_limiting.md From fca585db3920b127dae56600772d889e509a5dca Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Thu, 28 Jul 2022 03:10:56 -0400 Subject: [PATCH 0315/3231] Link to the docs on environment discovery failure (#5615) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Eddú Meléndez --- .../dockerclient/DockerClientProviderStrategy.java | 9 ++++----- docs/_redirects | 1 + 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java b/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java index 0d2aa3d08ed..01fb60e4b5d 100644 --- a/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java +++ b/core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java @@ -246,12 +246,11 @@ public boolean test(DockerClientProviderStrategy dockerClientProviderStrategy) { .findFirst() .orElseThrow(() -> { log.error( - "Could not find a valid Docker environment. Please check configuration. Attempted configurations were:" + "Could not find a valid Docker environment. Please check configuration. Attempted configurations were:\n" + + configurationFailures.stream().map(it -> "\t" + it).collect(Collectors.joining("\n")) + + "As no valid configuration was found, execution cannot continue.\n" + + "See https://www.testcontainers.org/on_failure.html for more details." ); - for (String failureMessage : configurationFailures) { - log.error(" " + failureMessage); - } - log.error("As no valid configuration was found, execution cannot continue"); FAIL_FAST_ALWAYS.set(true); return new IllegalStateException( diff --git a/docs/_redirects b/docs/_redirects index 99fe10d6900..5918fb853f2 100644 --- a/docs/_redirects +++ b/docs/_redirects @@ -12,6 +12,7 @@ /usage/database_containers.html /modules/databases/ /usage/neo4j_container.html /modules/databases/neo4j/ /compatibility.html /supported_docker_environment/ +/on_failure.html /supported_docker_environment/ # No great 1:1 mapping exists for the following, so redirect to somewhere where at least a sensible sidebar will be shown From 1419cf24eea3e145ab86f4fd17356143a5f9e9d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Thu, 28 Jul 2022 10:26:59 -0500 Subject: [PATCH 0316/3231] Improve Pulsar's wait strategy to rely on clusterName (#5613) `PulsarContainer` allow to override configuration using env vars, using `PULSAR_PREFIX_clusterName" will override the default's value "standalone". The endpoint response should adapt accordingly. --- .../testcontainers/containers/PulsarContainer.java | 8 ++++---- .../containers/PulsarContainerTest.java | 11 +++++++++++ 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java b/modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java index 30c36db01d8..10dc35dc4c8 100644 --- a/modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java +++ b/modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java @@ -96,13 +96,13 @@ protected void setupCommandAndEnv() { withCommand("/bin/bash", "-c", standaloneBaseCommand); + final String clusterName = getEnvMap().getOrDefault("PULSAR_PREFIX_clusterName", "standalone"); + final String response = String.format("[\"%s\"]", clusterName); + List waitStrategies = new ArrayList<>(); waitStrategies.add(Wait.defaultWaitStrategy()); waitStrategies.add( - Wait - .forHttp(ADMIN_CLUSTERS_ENDPOINT) - .forPort(BROKER_HTTP_PORT) - .forResponsePredicate("[\"standalone\"]"::equals) + Wait.forHttp(ADMIN_CLUSTERS_ENDPOINT).forPort(BROKER_HTTP_PORT).forResponsePredicate(response::equals) ); if (transactionsEnabled) { withEnv("PULSAR_PREFIX_transactionCoordinatorEnabled", "true"); diff --git a/modules/pulsar/src/test/java/org/testcontainers/containers/PulsarContainerTest.java b/modules/pulsar/src/test/java/org/testcontainers/containers/PulsarContainerTest.java index 29de6c4c872..bf9f3d2201f 100644 --- a/modules/pulsar/src/test/java/org/testcontainers/containers/PulsarContainerTest.java +++ b/modules/pulsar/src/test/java/org/testcontainers/containers/PulsarContainerTest.java @@ -54,6 +54,17 @@ public void envVarsUsage() throws Exception { } } + @Test + public void customClusterName() throws Exception { + try ( + PulsarContainer pulsar = new PulsarContainer(PULSAR_IMAGE) + .withEnv("PULSAR_PREFIX_clusterName", "tc-cluster"); + ) { + pulsar.start(); + testPulsarFunctionality(pulsar.getPulsarBrokerUrl()); + } + } + @Test public void shouldNotEnableFunctionsWorkerByDefault() throws Exception { try (PulsarContainer pulsar = new PulsarContainer(PULSAR_IMAGE)) { From bac4dfea7994c2f4993a98b02d50104825a6953e Mon Sep 17 00:00:00 2001 From: Henri Tremblay Date: Fri, 29 Jul 2022 06:33:04 -0400 Subject: [PATCH 0317/3231] Make outer maximum startup timeout in DockerComposeContainer configurable (#5588) --- .../containers/DockerComposeContainer.java | 16 ++++++++++++++-- ...DockerComposeContainerWithServicesTest.java | 18 ++++++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java index 86a98586c7f..14124d88c28 100644 --- a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java +++ b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java @@ -103,6 +103,8 @@ public class DockerComposeContainer> private final Map waitStrategyMap = new ConcurrentHashMap<>(); + private Duration startupTimeout = Duration.ofMinutes(30); + private final SocatContainer ambassadorContainer = new SocatContainer(); private final Map>> logConsumers = new ConcurrentHashMap<>(); @@ -433,14 +435,14 @@ private String getServiceInstanceName(String serviceName) { /* * can have multiple wait strategies for a single container, e.g. if waiting on several ports * if no wait strategy is defined, the WaitAllStrategy will return immediately. - * The WaitAllStrategy uses an long timeout, because timeouts should be handled by the inner strategies. + * The WaitAllStrategy uses the startup timeout for everything as a global maximum, but we expect timeouts to be handled by the inner strategies. */ private void addWaitStrategy(String serviceInstanceName, @NonNull WaitStrategy waitStrategy) { final WaitAllStrategy waitAllStrategy = waitStrategyMap.computeIfAbsent( serviceInstanceName, __ -> { return new WaitAllStrategy(WaitAllStrategy.Mode.WITH_MAXIMUM_OUTER_TIMEOUT) - .withStartupTimeout(Duration.ofMinutes(30)); + .withStartupTimeout(startupTimeout); } ); waitAllStrategy.withStrategy(waitStrategy); @@ -598,6 +600,16 @@ public SELF withRemoveImages(RemoveImages removeImages) { return self(); } + /** + * Set the maximum startup timeout all the waits set are bounded to. + * + * @return this instance. for chaining + */ + public SELF withStartupTimeout(Duration startupTimeout) { + this.startupTimeout = startupTimeout; + return self(); + } + public Optional getContainerByServiceName(String serviceName) { return Optional.ofNullable(serviceInstanceMap.get(serviceName)); } diff --git a/core/src/test/java/org/testcontainers/containers/DockerComposeContainerWithServicesTest.java b/core/src/test/java/org/testcontainers/containers/DockerComposeContainerWithServicesTest.java index 8f056de98fa..305de496cc9 100644 --- a/core/src/test/java/org/testcontainers/containers/DockerComposeContainerWithServicesTest.java +++ b/core/src/test/java/org/testcontainers/containers/DockerComposeContainerWithServicesTest.java @@ -1,12 +1,15 @@ package org.testcontainers.containers; import org.junit.Test; +import org.testcontainers.containers.wait.strategy.Wait; import java.io.File; +import java.time.Duration; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; +import static org.rnorth.visibleassertions.VisibleAssertions.assertThrows; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; public class DockerComposeContainerWithServicesTest { @@ -87,6 +90,21 @@ public void testScaleInComposeFileIsRespected() { } } + @Test + public void testStartupTimeoutSetsTheHighestTimeout() { + assertThrows("We expect a timeout from the startup timeout", org.rnorth.ducttape.TimeoutException.class, + () -> { + try ( + DockerComposeContainer compose = new DockerComposeContainer<>(SIMPLE_COMPOSE_FILE) + .withServices("redis") + .withStartupTimeout(Duration.ofMillis(1)) + .withExposedService("redis", 80, Wait.forListeningPort().withStartupTimeout(Duration.ofMinutes(1))); + ) { + compose.start(); + } + }); + } + private void verifyStartedContainers(final DockerComposeContainer compose, final String... names) { final List containerNames = compose .listChildContainers() From 0b374b391d4aaa4ccd9c723b41e8bac9a55a5d4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez?= Date: Fri, 29 Jul 2022 16:19:02 -0500 Subject: [PATCH 0318/3231] Fix format --- .../DockerComposeContainerWithServicesTest.java | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/core/src/test/java/org/testcontainers/containers/DockerComposeContainerWithServicesTest.java b/core/src/test/java/org/testcontainers/containers/DockerComposeContainerWithServicesTest.java index 305de496cc9..b7b820a3011 100644 --- a/core/src/test/java/org/testcontainers/containers/DockerComposeContainerWithServicesTest.java +++ b/core/src/test/java/org/testcontainers/containers/DockerComposeContainerWithServicesTest.java @@ -9,8 +9,8 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import static org.rnorth.visibleassertions.VisibleAssertions.assertThrows; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.rnorth.visibleassertions.VisibleAssertions.assertThrows; public class DockerComposeContainerWithServicesTest { @@ -92,17 +92,24 @@ public void testScaleInComposeFileIsRespected() { @Test public void testStartupTimeoutSetsTheHighestTimeout() { - assertThrows("We expect a timeout from the startup timeout", org.rnorth.ducttape.TimeoutException.class, + assertThrows( + "We expect a timeout from the startup timeout", + org.rnorth.ducttape.TimeoutException.class, () -> { try ( DockerComposeContainer compose = new DockerComposeContainer<>(SIMPLE_COMPOSE_FILE) .withServices("redis") .withStartupTimeout(Duration.ofMillis(1)) - .withExposedService("redis", 80, Wait.forListeningPort().withStartupTimeout(Duration.ofMinutes(1))); + .withExposedService( + "redis", + 80, + Wait.forListeningPort().withStartupTimeout(Duration.ofMinutes(1)) + ); ) { compose.start(); } - }); + } + ); } private void verifyStartedContainers(final DockerComposeContainer compose, final String... names) { From edee92250a03f54d3e2908de3589b0b7efd754eb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 22:05:48 +0000 Subject: [PATCH 0319/3231] Bump kafka-clients from 3.2.0 to 3.2.1 in /modules/kafka Bumps kafka-clients from 3.2.0 to 3.2.1. --- updated-dependencies: - dependency-name: org.apache.kafka:kafka-clients dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/kafka/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/kafka/build.gradle b/modules/kafka/build.gradle index 79264b8207f..ebd9c84120c 100644 --- a/modules/kafka/build.gradle +++ b/modules/kafka/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Kafka" dependencies { api project(':testcontainers') - testImplementation 'org.apache.kafka:kafka-clients:3.2.0' + testImplementation 'org.apache.kafka:kafka-clients:3.2.1' testImplementation 'org.assertj:assertj-core:3.23.1' testImplementation 'com.google.guava:guava:23.0' } From 8b2d5d17f503e44b2713267648938942e086b108 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 22:05:51 +0000 Subject: [PATCH 0320/3231] Bump tomcat-jdbc from 10.0.22 to 10.0.23 in /modules/jdbc Bumps tomcat-jdbc from 10.0.22 to 10.0.23. --- updated-dependencies: - dependency-name: org.apache.tomcat:tomcat-jdbc dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/jdbc/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jdbc/build.gradle b/modules/jdbc/build.gradle index 012e9f410ec..3a6681aee13 100644 --- a/modules/jdbc/build.gradle +++ b/modules/jdbc/build.gradle @@ -7,7 +7,7 @@ dependencies { compileOnly 'org.jetbrains:annotations:23.0.0' testImplementation 'commons-dbutils:commons-dbutils:1.7' testImplementation 'org.vibur:vibur-dbcp:25.0' - testImplementation 'org.apache.tomcat:tomcat-jdbc:10.0.22' + testImplementation 'org.apache.tomcat:tomcat-jdbc:10.0.23' testImplementation 'com.zaxxer:HikariCP-java6:2.3.13' testImplementation 'com.googlecode.junit-toolbox:junit-toolbox:2.4' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' From c83590275d4342eb10345e9a9451f3cb54c920a3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 22:06:01 +0000 Subject: [PATCH 0321/3231] Bump neo4j-java-driver from 4.4.6 to 4.4.9 in /modules/neo4j Bumps [neo4j-java-driver](https://github.com/neo4j/neo4j-java-driver) from 4.4.6 to 4.4.9. - [Release notes](https://github.com/neo4j/neo4j-java-driver/releases) - [Commits](https://github.com/neo4j/neo4j-java-driver/compare/4.4.6...4.4.9) --- updated-dependencies: - dependency-name: org.neo4j.driver:neo4j-java-driver dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/neo4j/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/neo4j/build.gradle b/modules/neo4j/build.gradle index 355dbe3f4ee..de42bca2d5d 100644 --- a/modules/neo4j/build.gradle +++ b/modules/neo4j/build.gradle @@ -33,6 +33,6 @@ dependencies { api project(":testcontainers") - testImplementation 'org.neo4j.driver:neo4j-java-driver:4.4.6' + testImplementation 'org.neo4j.driver:neo4j-java-driver:4.4.9' testImplementation 'org.assertj:assertj-core:3.23.1' } From 0d1ebaee2c19c3a2a2bc3538099b94072838ff5d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 22:06:06 +0000 Subject: [PATCH 0322/3231] Bump java-client from 3.3.1 to 3.3.2 in /modules/couchbase Bumps [java-client](https://github.com/couchbase/couchbase-jvm-clients) from 3.3.1 to 3.3.2. - [Release notes](https://github.com/couchbase/couchbase-jvm-clients/releases) - [Commits](https://github.com/couchbase/couchbase-jvm-clients/compare/java-client-3.3.1...java-client-3.3.2) --- updated-dependencies: - dependency-name: com.couchbase.client:java-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/couchbase/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/couchbase/build.gradle b/modules/couchbase/build.gradle index df7f4cbc221..e59e346c29f 100644 --- a/modules/couchbase/build.gradle +++ b/modules/couchbase/build.gradle @@ -5,6 +5,6 @@ dependencies { // TODO use JDK's HTTP client and/or Apache HttpClient5 shaded 'com.squareup.okhttp3:okhttp:3.14.9' - testImplementation 'com.couchbase.client:java-client:3.3.1' + testImplementation 'com.couchbase.client:java-client:3.3.2' testImplementation 'org.awaitility:awaitility:4.2.0' } From 699938b9a3f4675adc07452d368ef504b1fa2dd8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 22:06:07 +0000 Subject: [PATCH 0323/3231] Bump mysql-connector-java from 8.0.29 to 8.0.30 in /modules/jdbc-test Bumps [mysql-connector-java](https://github.com/mysql/mysql-connector-j) from 8.0.29 to 8.0.30. - [Release notes](https://github.com/mysql/mysql-connector-j/releases) - [Changelog](https://github.com/mysql/mysql-connector-j/blob/release/8.0/CHANGES) - [Commits](https://github.com/mysql/mysql-connector-j/compare/8.0.29...8.0.30) --- updated-dependencies: - dependency-name: mysql:mysql-connector-java dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/jdbc-test/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jdbc-test/build.gradle b/modules/jdbc-test/build.gradle index 3649396773d..a3c7dfdeefe 100644 --- a/modules/jdbc-test/build.gradle +++ b/modules/jdbc-test/build.gradle @@ -16,5 +16,5 @@ dependencies { api 'org.apache.tomcat:tomcat-jdbc:10.0.22' api 'org.vibur:vibur-dbcp:25.0' - api 'mysql:mysql-connector-java:8.0.29' + api 'mysql:mysql-connector-java:8.0.30' } From 26386e6ef2f41e0c403e24c2dcf4d87848c06d25 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 22:06:08 +0000 Subject: [PATCH 0324/3231] Bump tomcat-jdbc from 10.0.22 to 10.0.23 in /modules/jdbc-test Bumps tomcat-jdbc from 10.0.22 to 10.0.23. --- updated-dependencies: - dependency-name: org.apache.tomcat:tomcat-jdbc dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/jdbc-test/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jdbc-test/build.gradle b/modules/jdbc-test/build.gradle index 3649396773d..038cc110a1b 100644 --- a/modules/jdbc-test/build.gradle +++ b/modules/jdbc-test/build.gradle @@ -14,7 +14,7 @@ dependencies { exclude(group: "net.java.dev.jna", module: "jna") } - api 'org.apache.tomcat:tomcat-jdbc:10.0.22' + api 'org.apache.tomcat:tomcat-jdbc:10.0.23' api 'org.vibur:vibur-dbcp:25.0' api 'mysql:mysql-connector-java:8.0.29' } From 463a5c5d48b55f882f29b046bb6e5e5ecc5ae0b7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 22:06:12 +0000 Subject: [PATCH 0325/3231] Bump aws-java-sdk-dynamodb in /modules/dynalite Bumps [aws-java-sdk-dynamodb](https://github.com/aws/aws-sdk-java) from 1.12.253 to 1.12.273. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.253...1.12.273) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-dynamodb dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/dynalite/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/dynalite/build.gradle b/modules/dynalite/build.gradle index acf379cda79..5b9ff919da7 100644 --- a/modules/dynalite/build.gradle +++ b/modules/dynalite/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Dynalite" dependencies { api project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.12.253' + compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.12.273' testImplementation 'com.amazonaws:aws-java-sdk-dynamodb:1.12.253' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' From 85cc40b057c9c2e740180ce92dfb980c5430c9de Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 22:06:20 +0000 Subject: [PATCH 0326/3231] Bump mysql-connector-java from 8.0.29 to 8.0.30 in /modules/mysql Bumps [mysql-connector-java](https://github.com/mysql/mysql-connector-j) from 8.0.29 to 8.0.30. - [Release notes](https://github.com/mysql/mysql-connector-j/releases) - [Changelog](https://github.com/mysql/mysql-connector-j/blob/release/8.0/CHANGES) - [Commits](https://github.com/mysql/mysql-connector-j/compare/8.0.29...8.0.30) --- updated-dependencies: - dependency-name: mysql:mysql-connector-java dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/mysql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mysql/build.gradle b/modules/mysql/build.gradle index e23920eaae9..87887384973 100644 --- a/modules/mysql/build.gradle +++ b/modules/mysql/build.gradle @@ -10,7 +10,7 @@ dependencies { compileOnly 'dev.miku:r2dbc-mysql:0.8.2.RELEASE' testImplementation project(':jdbc-test') - testImplementation 'mysql:mysql-connector-java:8.0.29' + testImplementation 'mysql:mysql-connector-java:8.0.30' testImplementation testFixtures(project(':r2dbc')) testImplementation 'dev.miku:r2dbc-mysql:0.8.2.RELEASE' From 33f08921d5996d71f41b353d5e5cc8b28494f00a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 22:07:10 +0000 Subject: [PATCH 0327/3231] Bump mysql-connector-java from 8.0.29 to 8.0.30 in /modules/spock Bumps [mysql-connector-java](https://github.com/mysql/mysql-connector-j) from 8.0.29 to 8.0.30. - [Release notes](https://github.com/mysql/mysql-connector-j/releases) - [Changelog](https://github.com/mysql/mysql-connector-j/blob/release/8.0/CHANGES) - [Commits](https://github.com/mysql/mysql-connector-j/compare/8.0.29...8.0.30) --- updated-dependencies: - dependency-name: mysql:mysql-connector-java dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/spock/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle index a9e3b7d09b1..3eadd33ea2a 100644 --- a/modules/spock/build.gradle +++ b/modules/spock/build.gradle @@ -16,7 +16,7 @@ dependencies { testImplementation 'org.apache.httpcomponents:httpclient:4.5.13' testRuntimeOnly 'org.postgresql:postgresql:42.4.0' - testRuntimeOnly 'mysql:mysql-connector-java:8.0.29' + testRuntimeOnly 'mysql:mysql-connector-java:8.0.30' testCompileClasspath 'org.jetbrains:annotations:23.0.0' } From c43589ffb3ac30e564885df29168234ced5c6273 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 22:07:11 +0000 Subject: [PATCH 0328/3231] Bump elasticsearch-rest-client in /modules/elasticsearch Bumps [elasticsearch-rest-client](https://github.com/elastic/elasticsearch) from 8.3.1 to 8.3.3. - [Release notes](https://github.com/elastic/elasticsearch/releases) - [Changelog](https://github.com/elastic/elasticsearch/blob/main/CHANGELOG.md) - [Commits](https://github.com/elastic/elasticsearch/compare/v8.3.1...v8.3.3) --- updated-dependencies: - dependency-name: org.elasticsearch.client:elasticsearch-rest-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/elasticsearch/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/elasticsearch/build.gradle b/modules/elasticsearch/build.gradle index a4a277596fc..a3a47d11fab 100644 --- a/modules/elasticsearch/build.gradle +++ b/modules/elasticsearch/build.gradle @@ -2,7 +2,7 @@ description = "TestContainers :: elasticsearch" dependencies { api project(':testcontainers') - testImplementation "org.elasticsearch.client:elasticsearch-rest-client:8.3.1" + testImplementation "org.elasticsearch.client:elasticsearch-rest-client:8.3.3" testImplementation "org.elasticsearch.client:transport:7.17.5" testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } From 17526d7085dc1044aad98c801df407d99b5d196d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 22:07:16 +0000 Subject: [PATCH 0329/3231] Bump junit-jupiter-api from 5.8.2 to 5.9.0 in /modules/junit-jupiter Bumps [junit-jupiter-api](https://github.com/junit-team/junit5) from 5.8.2 to 5.9.0. - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.8.2...r5.9.0) --- updated-dependencies: - dependency-name: org.junit.jupiter:junit-jupiter-api dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 73a8897800c..167529f9fea 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -2,7 +2,7 @@ description = "Testcontainers :: JUnit Jupiter Extension" dependencies { api project(':testcontainers') - api 'org.junit.jupiter:junit-jupiter-api:5.8.2' + api 'org.junit.jupiter:junit-jupiter-api:5.9.0' testImplementation project(':mysql') testImplementation project(':postgresql') From cc10355394942be97fc22aac1c882e88386f7ccf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 22:07:21 +0000 Subject: [PATCH 0330/3231] Bump junit-jupiter-params from 5.8.2 to 5.9.0 in /modules/junit-jupiter Bumps [junit-jupiter-params](https://github.com/junit-team/junit5) from 5.8.2 to 5.9.0. - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.8.2...r5.9.0) --- updated-dependencies: - dependency-name: org.junit.jupiter:junit-jupiter-params dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 73a8897800c..a7e23a2f65c 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -13,7 +13,7 @@ dependencies { exclude(module: 'hamcrest-core') } testImplementation 'org.assertj:assertj-core:3.23.1' - testImplementation 'org.junit.jupiter:junit-jupiter-params:5.8.2' + testImplementation 'org.junit.jupiter:junit-jupiter-params:5.9.0' testRuntimeOnly 'org.postgresql:postgresql:42.4.0' testRuntimeOnly 'mysql:mysql-connector-java:8.0.29' From b9d49464dd9e0dc60643d38a11e4eeae478b9a31 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 22:07:30 +0000 Subject: [PATCH 0331/3231] Bump junit-jupiter-engine from 5.8.2 to 5.9.0 in /modules/junit-jupiter Bumps [junit-jupiter-engine](https://github.com/junit-team/junit5) from 5.8.2 to 5.9.0. - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.8.2...r5.9.0) --- updated-dependencies: - dependency-name: org.junit.jupiter:junit-jupiter-engine dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 73a8897800c..3dd6c7dd25f 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -17,7 +17,7 @@ dependencies { testRuntimeOnly 'org.postgresql:postgresql:42.4.0' testRuntimeOnly 'mysql:mysql-connector-java:8.0.29' - testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.2' + testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.9.0' } test { From 8dfe6c5aaea81e6c7794acac22168ef874e9e826 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 22:08:21 +0000 Subject: [PATCH 0332/3231] Bump actions/cache from 3.0.4 to 3.0.5 Bumps [actions/cache](https://github.com/actions/cache) from 3.0.4 to 3.0.5. - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/v3.0.4...v3.0.5) --- updated-dependencies: - dependency-name: actions/cache dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/ci-examples.yml | 4 ++-- .github/workflows/ci.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci-examples.yml b/.github/workflows/ci-examples.yml index f24fd6f6916..9fa1f78e47b 100644 --- a/.github/workflows/ci-examples.yml +++ b/.github/workflows/ci-examples.yml @@ -25,7 +25,7 @@ jobs: java-version: '8.0.302' distribution: temurin - name: Cache Gradle Home files - uses: actions/cache@v3.0.4 + uses: actions/cache@v3.0.5 continue-on-error: true with: path: ~/.gradle/caches @@ -53,7 +53,7 @@ jobs: java-version: '8.0.302' distribution: temurin - name: Cache Gradle Home files - uses: actions/cache@v3.0.4 + uses: actions/cache@v3.0.5 continue-on-error: true with: path: ~/.gradle/caches diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3287499c2fc..55432b3e2c6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,7 +33,7 @@ jobs: java-version: '8.0.302' distribution: temurin - name: Cache Gradle Home files - uses: actions/cache@v3.0.4 + uses: actions/cache@v3.0.5 with: path: ~/.gradle/caches key: ${{ runner.os }}-gradle-home-testmatrix-${{ hashFiles('**/*.gradle') }} @@ -59,7 +59,7 @@ jobs: java-version: '8.0.302' distribution: temurin - name: Cache Gradle Home files - uses: actions/cache@v3.0.4 + uses: actions/cache@v3.0.5 continue-on-error: true with: path: ~/.gradle/caches From b687658cf70d842de21c000b5da409cf270cb1a5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 22:08:29 +0000 Subject: [PATCH 0333/3231] Bump influxdb-java from 2.22 to 2.23 in /modules/influxdb Bumps [influxdb-java](https://github.com/influxdata/influxdb-java) from 2.22 to 2.23. - [Release notes](https://github.com/influxdata/influxdb-java/releases) - [Changelog](https://github.com/influxdata/influxdb-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/influxdata/influxdb-java/compare/influxdb-java-2.22...influxdb-java-2.23) --- updated-dependencies: - dependency-name: org.influxdb:influxdb-java dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/influxdb/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/influxdb/build.gradle b/modules/influxdb/build.gradle index 7a028d4a19d..a7ff4e66729 100644 --- a/modules/influxdb/build.gradle +++ b/modules/influxdb/build.gradle @@ -3,6 +3,6 @@ description = "Testcontainers :: InfluxDB" dependencies { api project(':testcontainers') - compileOnly 'org.influxdb:influxdb-java:2.22' - testImplementation 'org.influxdb:influxdb-java:2.21' + compileOnly 'org.influxdb:influxdb-java:2.23' + testImplementation 'org.influxdb:influxdb-java:2.23' } From 5cb65abe0014e2ef0a1cb7245589e789760e7f49 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 22:08:39 +0000 Subject: [PATCH 0334/3231] Bump s3 from 2.17.224 to 2.17.244 in /modules/localstack Bumps s3 from 2.17.224 to 2.17.244. --- updated-dependencies: - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 56776965f8a..9a29eb0e343 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -7,6 +7,6 @@ dependencies { testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.231' testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.253' testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.253' - testImplementation 'software.amazon.awssdk:s3:2.17.224' + testImplementation 'software.amazon.awssdk:s3:2.17.244' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } From 1cf31e61d347f0e95a13729f094b719c193bf43e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 22:09:23 +0000 Subject: [PATCH 0335/3231] Bump neo4j-java-driver from 4.4.6 to 4.4.9 in /examples Bumps [neo4j-java-driver](https://github.com/neo4j/neo4j-java-driver) from 4.4.6 to 4.4.9. - [Release notes](https://github.com/neo4j/neo4j-java-driver/releases) - [Commits](https://github.com/neo4j/neo4j-java-driver/compare/4.4.6...4.4.9) --- updated-dependencies: - dependency-name: org.neo4j.driver:neo4j-java-driver dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/neo4j-container/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/neo4j-container/build.gradle b/examples/neo4j-container/build.gradle index e937bd2954b..e7e42ff00bd 100644 --- a/examples/neo4j-container/build.gradle +++ b/examples/neo4j-container/build.gradle @@ -8,7 +8,7 @@ repositories { dependencies { testImplementation 'org.assertj:assertj-core:3.23.1' - testImplementation 'org.neo4j.driver:neo4j-java-driver:4.4.6' + testImplementation 'org.neo4j.driver:neo4j-java-driver:4.4.9' testImplementation 'org.testcontainers:neo4j' testImplementation 'org.testcontainers:junit-jupiter' } From 95e917f31b8152fb84a2ebcf1dd5ef2f4e720785 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 22:09:23 +0000 Subject: [PATCH 0336/3231] Bump com.gradle.enterprise.gradle.plugin in /examples Bumps com.gradle.enterprise.gradle.plugin from 3.10.2 to 3.10.3. --- updated-dependencies: - dependency-name: com.gradle.enterprise:com.gradle.enterprise.gradle.plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/settings.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/settings.gradle b/examples/settings.gradle index 5a4ae8c0d2e..cfb204f4b00 100644 --- a/examples/settings.gradle +++ b/examples/settings.gradle @@ -6,7 +6,7 @@ buildscript { } dependencies { classpath "gradle.plugin.ch.myniva.gradle:s3-build-cache:0.10.0" - classpath "com.gradle.enterprise:com.gradle.enterprise.gradle.plugin:3.10.2" + classpath "com.gradle.enterprise:com.gradle.enterprise.gradle.plugin:3.10.3" classpath "com.gradle:common-custom-user-data-gradle-plugin:1.7.2" } } From 6436b1019cec2a27c029ecc28b8b53044c9e8dce Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 22:09:25 +0000 Subject: [PATCH 0337/3231] Bump kafka-clients from 3.2.0 to 3.2.1 in /examples Bumps kafka-clients from 3.2.0 to 3.2.1. --- updated-dependencies: - dependency-name: org.apache.kafka:kafka-clients dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/kafka-cluster/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/kafka-cluster/build.gradle b/examples/kafka-cluster/build.gradle index d69af5ceb68..c3a79d4058f 100644 --- a/examples/kafka-cluster/build.gradle +++ b/examples/kafka-cluster/build.gradle @@ -10,7 +10,7 @@ dependencies { testCompileOnly "org.projectlombok:lombok:1.18.24" testAnnotationProcessor "org.projectlombok:lombok:1.18.24" testImplementation 'org.testcontainers:kafka' - testImplementation 'org.apache.kafka:kafka-clients:3.2.0' + testImplementation 'org.apache.kafka:kafka-clients:3.2.1' testImplementation 'org.assertj:assertj-core:3.23.1' testImplementation 'com.google.guava:guava:23.0' testImplementation 'ch.qos.logback:logback-classic:1.2.11' From 6fcaeefcbf1bdeb3cfb5b7ec07255e3fba39580e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 22:09:29 +0000 Subject: [PATCH 0338/3231] Bump cucumber-junit from 7.4.1 to 7.5.0 in /examples Bumps [cucumber-junit](https://github.com/cucumber/cucumber-jvm) from 7.4.1 to 7.5.0. - [Release notes](https://github.com/cucumber/cucumber-jvm/releases) - [Changelog](https://github.com/cucumber/cucumber-jvm/blob/main/CHANGELOG.md) - [Commits](https://github.com/cucumber/cucumber-jvm/compare/v7.4.1...v7.5.0) --- updated-dependencies: - dependency-name: io.cucumber:cucumber-junit dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- examples/cucumber/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cucumber/build.gradle b/examples/cucumber/build.gradle index 1bf4e1f72cb..5cf03f419fe 100644 --- a/examples/cucumber/build.gradle +++ b/examples/cucumber/build.gradle @@ -11,6 +11,6 @@ dependencies { implementation 'org.seleniumhq.selenium:selenium-firefox-driver:3.141.59' implementation 'org.seleniumhq.selenium:selenium-chrome-driver:3.141.59' testImplementation 'io.cucumber:cucumber-java:7.4.1' - testImplementation 'io.cucumber:cucumber-junit:7.4.1' + testImplementation 'io.cucumber:cucumber-junit:7.5.0' testImplementation 'org.testcontainers:selenium' } From eb766900d2c12168e17e9af2fc9d667e49040207 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 22:09:39 +0000 Subject: [PATCH 0339/3231] Bump gson from 2.9.0 to 2.9.1 in /examples Bumps [gson](https://github.com/google/gson) from 2.9.0 to 2.9.1. - [Release notes](https://github.com/google/gson/releases) - [Changelog](https://github.com/google/gson/blob/master/CHANGELOG.md) - [Commits](https://github.com/google/gson/compare/gson-parent-2.9.0...gson-parent-2.9.1) --- updated-dependencies: - dependency-name: com.google.code.gson:gson dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/disque-job-queue/build.gradle | 2 +- examples/redis-backed-cache-testng/build.gradle | 2 +- examples/redis-backed-cache/build.gradle | 2 +- examples/singleton-container/build.gradle | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/disque-job-queue/build.gradle b/examples/disque-job-queue/build.gradle index 1dc3497ec42..47bf340e2eb 100644 --- a/examples/disque-job-queue/build.gradle +++ b/examples/disque-job-queue/build.gradle @@ -10,7 +10,7 @@ dependencies { compileOnly "org.projectlombok:lombok:1.18.24" annotationProcessor "org.projectlombok:lombok:1.18.24" implementation 'biz.paluch.redis:spinach:0.3' - implementation 'com.google.code.gson:gson:2.9.0' + implementation 'com.google.code.gson:gson:2.9.1' implementation 'com.google.guava:guava:23.0' testImplementation 'ch.qos.logback:logback-classic:1.2.11' testImplementation 'org.mockito:mockito-all:1.10.19' diff --git a/examples/redis-backed-cache-testng/build.gradle b/examples/redis-backed-cache-testng/build.gradle index 0c4de18f3c6..1e09fbc84f0 100644 --- a/examples/redis-backed-cache-testng/build.gradle +++ b/examples/redis-backed-cache-testng/build.gradle @@ -9,7 +9,7 @@ repositories { dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.36' implementation 'redis.clients:jedis:4.2.3' - implementation 'com.google.code.gson:gson:2.9.0' + implementation 'com.google.code.gson:gson:2.9.1' implementation 'com.google.guava:guava:23.0' testImplementation 'org.testcontainers:testcontainers' testImplementation 'ch.qos.logback:logback-classic:1.2.11' diff --git a/examples/redis-backed-cache/build.gradle b/examples/redis-backed-cache/build.gradle index 6e9d7664384..94f445f5ffe 100644 --- a/examples/redis-backed-cache/build.gradle +++ b/examples/redis-backed-cache/build.gradle @@ -9,7 +9,7 @@ repositories { dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.36' implementation 'redis.clients:jedis:4.2.3' - implementation 'com.google.code.gson:gson:2.9.0' + implementation 'com.google.code.gson:gson:2.9.1' implementation 'com.google.guava:guava:23.0' testImplementation 'org.testcontainers:testcontainers' testImplementation 'junit:junit:4.13.2' diff --git a/examples/singleton-container/build.gradle b/examples/singleton-container/build.gradle index 7ece47a44ea..fd603d515c7 100644 --- a/examples/singleton-container/build.gradle +++ b/examples/singleton-container/build.gradle @@ -9,7 +9,7 @@ repositories { dependencies { implementation 'redis.clients:jedis:4.2.3' - implementation 'com.google.code.gson:gson:2.9.0' + implementation 'com.google.code.gson:gson:2.9.1' implementation 'com.google.guava:guava:23.0' compileOnly 'org.slf4j:slf4j-api:1.7.36' From 57159ae6ede175c641e3afc5b7c8cd59ae15c031 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 22:10:19 +0000 Subject: [PATCH 0340/3231] Bump org.jetbrains.kotlin.plugin.spring in /examples Bumps [org.jetbrains.kotlin.plugin.spring](https://github.com/JetBrains/kotlin) from 1.7.0 to 1.7.10. - [Release notes](https://github.com/JetBrains/kotlin/releases) - [Changelog](https://github.com/JetBrains/kotlin/blob/v1.7.10/ChangeLog.md) - [Commits](https://github.com/JetBrains/kotlin/compare/v1.7.0...v1.7.10) --- updated-dependencies: - dependency-name: org.jetbrains.kotlin.plugin.spring dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/spring-boot-kotlin-redis/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/spring-boot-kotlin-redis/build.gradle b/examples/spring-boot-kotlin-redis/build.gradle index 90338ff9959..4a48c40d8bf 100644 --- a/examples/spring-boot-kotlin-redis/build.gradle +++ b/examples/spring-boot-kotlin-redis/build.gradle @@ -1,7 +1,7 @@ plugins { id("org.springframework.boot") version "2.6.4" id("org.jetbrains.kotlin.jvm") version "1.7.0" - id("org.jetbrains.kotlin.plugin.spring") version "1.7.0" + id("org.jetbrains.kotlin.plugin.spring") version "1.7.10" } apply plugin: 'io.spring.dependency-management' From 9b8d2391a3eb9f30f38d1be47eadf8b3081f528d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 22:11:31 +0000 Subject: [PATCH 0341/3231] Bump trino-jdbc from 388 to 391 in /modules/trino Bumps trino-jdbc from 388 to 391. --- updated-dependencies: - dependency-name: io.trino:trino-jdbc dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- modules/trino/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/trino/build.gradle b/modules/trino/build.gradle index caf1f322c12..c25007bb223 100644 --- a/modules/trino/build.gradle +++ b/modules/trino/build.gradle @@ -4,6 +4,6 @@ dependencies { api project(':jdbc') testImplementation project(':jdbc-test') - testImplementation 'io.trino:trino-jdbc:388' + testImplementation 'io.trino:trino-jdbc:391' compileOnly 'org.jetbrains:annotations:23.0.0' } From 89283187e0b3a047a335da9d53399ba7a6c8c5e0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 22:11:50 +0000 Subject: [PATCH 0342/3231] Bump mongodb-driver-sync from 4.6.1 to 4.7.1 in /modules/mongodb Bumps [mongodb-driver-sync](https://github.com/mongodb/mongo-java-driver) from 4.6.1 to 4.7.1. - [Release notes](https://github.com/mongodb/mongo-java-driver/releases) - [Commits](https://github.com/mongodb/mongo-java-driver/compare/r4.6.1...r4.7.1) --- updated-dependencies: - dependency-name: org.mongodb:mongodb-driver-sync dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/mongodb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mongodb/build.gradle b/modules/mongodb/build.gradle index 297bcd21f3b..7f9725bf0e8 100644 --- a/modules/mongodb/build.gradle +++ b/modules/mongodb/build.gradle @@ -3,5 +3,5 @@ description = "Testcontainers :: MongoDB" dependencies { api project(':testcontainers') - testImplementation("org.mongodb:mongodb-driver-sync:4.6.1") + testImplementation("org.mongodb:mongodb-driver-sync:4.7.1") } From bfd82f4536195abe12dde5ee4ca2c617a923603d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 22:11:58 +0000 Subject: [PATCH 0343/3231] Bump reactor-core from 3.4.19 to 3.4.21 in /modules/r2dbc Bumps [reactor-core](https://github.com/reactor/reactor-core) from 3.4.19 to 3.4.21. - [Release notes](https://github.com/reactor/reactor-core/releases) - [Commits](https://github.com/reactor/reactor-core/compare/v3.4.19...v3.4.21) --- updated-dependencies: - dependency-name: io.projectreactor:reactor-core dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/r2dbc/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/r2dbc/build.gradle b/modules/r2dbc/build.gradle index 692b5ac9ce5..f51a13b9296 100644 --- a/modules/r2dbc/build.gradle +++ b/modules/r2dbc/build.gradle @@ -15,6 +15,6 @@ dependencies { testImplementation 'io.r2dbc:r2dbc-postgresql:0.8.12.RELEASE' testImplementation project(':postgresql') - testFixturesImplementation 'io.projectreactor:reactor-core:3.4.19' + testFixturesImplementation 'io.projectreactor:reactor-core:3.4.21' testFixturesImplementation 'org.assertj:assertj-core:3.23.1' } From b303688bdedcd84e8f660993591dadcc9ea5e338 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 22:12:07 +0000 Subject: [PATCH 0344/3231] Bump azure-cosmos from 4.32.1 to 4.33.1 in /modules/azure Bumps [azure-cosmos](https://github.com/Azure/azure-sdk-for-java) from 4.32.1 to 4.33.1. - [Release notes](https://github.com/Azure/azure-sdk-for-java/releases) - [Commits](https://github.com/Azure/azure-sdk-for-java/compare/azure-cosmos_4.32.1...azure-cosmos_4.33.1) --- updated-dependencies: - dependency-name: com.azure:azure-cosmos dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/azure/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/azure/build.gradle b/modules/azure/build.gradle index 5f8c6837d79..c6707401ff6 100644 --- a/modules/azure/build.gradle +++ b/modules/azure/build.gradle @@ -6,5 +6,5 @@ dependencies { shaded 'com.squareup.okhttp3:okhttp:3.14.9' testImplementation 'org.assertj:assertj-core:3.23.1' - testImplementation 'com.azure:azure-cosmos:4.32.1' + testImplementation 'com.azure:azure-cosmos:4.33.1' } From 9aeb0c78803af4ef061d81c3bccbc44ddb83756d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 22:12:15 +0000 Subject: [PATCH 0345/3231] Bump junit-jupiter-engine from 5.8.2 to 5.9.0 in /modules/hivemq Bumps [junit-jupiter-engine](https://github.com/junit-team/junit5) from 5.8.2 to 5.9.0. - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.8.2...r5.9.0) --- updated-dependencies: - dependency-name: org.junit.jupiter:junit-jupiter-engine dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/hivemq/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/hivemq/build.gradle b/modules/hivemq/build.gradle index aa566381d0c..b62121ce9cc 100644 --- a/modules/hivemq/build.gradle +++ b/modules/hivemq/build.gradle @@ -17,7 +17,7 @@ dependencies { testImplementation("com.hivemq:hivemq-mqtt-client:1.3.0") testImplementation("org.apache.httpcomponents:httpclient:4.5.13") testImplementation("ch.qos.logback:logback-classic:1.2.11") - testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2") + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.9.0") } test { From bb1769c468c9dd5ca88abd4a70bca913e60f0f9b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 22:12:21 +0000 Subject: [PATCH 0346/3231] Bump junit-jupiter-api from 5.8.2 to 5.9.0 in /modules/hivemq Bumps [junit-jupiter-api](https://github.com/junit-team/junit5) from 5.8.2 to 5.9.0. - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.8.2...r5.9.0) --- updated-dependencies: - dependency-name: org.junit.jupiter:junit-jupiter-api dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/hivemq/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/hivemq/build.gradle b/modules/hivemq/build.gradle index aa566381d0c..a30a7d30517 100644 --- a/modules/hivemq/build.gradle +++ b/modules/hivemq/build.gradle @@ -11,7 +11,7 @@ dependencies { shaded("org.jboss.shrinkwrap:shrinkwrap-impl-base:1.2.6") shaded("net.lingala.zip4j:zip4j:2.10.0") - testImplementation("org.junit.jupiter:junit-jupiter-api:5.8.2") + testImplementation("org.junit.jupiter:junit-jupiter-api:5.9.0") testImplementation(project(":junit-jupiter")) testImplementation("com.hivemq:hivemq-extension-sdk:4.8.2") testImplementation("com.hivemq:hivemq-mqtt-client:1.3.0") From 909404b9be70970673c6e592aec72041fff9d49a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 22:13:32 +0000 Subject: [PATCH 0347/3231] Bump com.gradle.enterprise.gradle.plugin from 3.10.2 to 3.10.3 Bumps com.gradle.enterprise.gradle.plugin from 3.10.2 to 3.10.3. --- updated-dependencies: - dependency-name: com.gradle.enterprise:com.gradle.enterprise.gradle.plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- settings.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.gradle b/settings.gradle index d4605c2ad84..b56048c1126 100644 --- a/settings.gradle +++ b/settings.gradle @@ -5,7 +5,7 @@ buildscript { } } dependencies { - classpath "com.gradle.enterprise:com.gradle.enterprise.gradle.plugin:3.10.2" + classpath "com.gradle.enterprise:com.gradle.enterprise.gradle.plugin:3.10.3" classpath "com.gradle:common-custom-user-data-gradle-plugin:1.7.2" } } From 456bdcc6a22099e743d98459ce262a0a86b98ab1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 22:14:19 +0000 Subject: [PATCH 0348/3231] Bump google-cloud-datastore from 2.10.0 to 2.10.1 in /modules/gcloud Bumps [google-cloud-datastore](https://github.com/googleapis/java-datastore) from 2.10.0 to 2.10.1. - [Release notes](https://github.com/googleapis/java-datastore/releases) - [Changelog](https://github.com/googleapis/java-datastore/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/java-datastore/compare/v2.10.0...v2.10.1) --- updated-dependencies: - dependency-name: com.google.cloud:google-cloud-datastore dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/gcloud/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle index 2cabbc4cf30..d885dc11023 100644 --- a/modules/gcloud/build.gradle +++ b/modules/gcloud/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: GCloud" dependencies { api project(':testcontainers') - testImplementation 'com.google.cloud:google-cloud-datastore:2.10.0' + testImplementation 'com.google.cloud:google-cloud-datastore:2.10.1' testImplementation 'com.google.cloud:google-cloud-firestore:3.2.0' testImplementation 'com.google.cloud:google-cloud-pubsub:1.120.0' testImplementation 'com.google.cloud:google-cloud-spanner:6.25.7' From 7499443fae265f702fd15a718de25c63fbdd8d07 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 22:14:34 +0000 Subject: [PATCH 0349/3231] Bump google-cloud-pubsub from 1.120.0 to 1.120.6 in /modules/gcloud Bumps [google-cloud-pubsub](https://github.com/googleapis/java-pubsub) from 1.120.0 to 1.120.6. - [Release notes](https://github.com/googleapis/java-pubsub/releases) - [Changelog](https://github.com/googleapis/java-pubsub/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/java-pubsub/compare/v1.120.0...v1.120.6) --- updated-dependencies: - dependency-name: com.google.cloud:google-cloud-pubsub dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/gcloud/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle index 2cabbc4cf30..9c837ff5e0e 100644 --- a/modules/gcloud/build.gradle +++ b/modules/gcloud/build.gradle @@ -5,7 +5,7 @@ dependencies { testImplementation 'com.google.cloud:google-cloud-datastore:2.10.0' testImplementation 'com.google.cloud:google-cloud-firestore:3.2.0' - testImplementation 'com.google.cloud:google-cloud-pubsub:1.120.0' + testImplementation 'com.google.cloud:google-cloud-pubsub:1.120.6' testImplementation 'com.google.cloud:google-cloud-spanner:6.25.7' testImplementation 'com.google.cloud:google-cloud-bigtable:2.9.0' testImplementation 'org.assertj:assertj-core:3.23.1' From 720024f1f9244b766165782ce1c1889ce35e13c6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 2 Aug 2022 10:30:50 +0000 Subject: [PATCH 0350/3231] Bump mysql-connector-java in /modules/junit-jupiter Bumps [mysql-connector-java](https://github.com/mysql/mysql-connector-j) from 8.0.29 to 8.0.30. - [Release notes](https://github.com/mysql/mysql-connector-j/releases) - [Changelog](https://github.com/mysql/mysql-connector-j/blob/release/8.0/CHANGES) - [Commits](https://github.com/mysql/mysql-connector-j/compare/8.0.29...8.0.30) --- updated-dependencies: - dependency-name: mysql:mysql-connector-java dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 5bf5aa4557a..790aa394849 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -16,7 +16,7 @@ dependencies { testImplementation 'org.junit.jupiter:junit-jupiter-params:5.8.2' testRuntimeOnly 'org.postgresql:postgresql:42.4.0' - testRuntimeOnly 'mysql:mysql-connector-java:8.0.29' + testRuntimeOnly 'mysql:mysql-connector-java:8.0.30' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.9.0' } From 521eb8d8a746531415d5043cf0a02e4ad697717f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 2 Aug 2022 10:31:01 +0000 Subject: [PATCH 0351/3231] Bump aws-java-sdk-sqs from 1.12.253 to 1.12.273 in /modules/localstack Bumps [aws-java-sdk-sqs](https://github.com/aws/aws-sdk-java) from 1.12.253 to 1.12.273. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.253...1.12.273) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-sqs dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 9a29eb0e343..d754c676320 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -5,7 +5,7 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.253' testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.231' - testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.253' + testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.273' testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.253' testImplementation 'software.amazon.awssdk:s3:2.17.244' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' From 04842f1559b4f8e073ac430d7b73e9bc496f44a0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 2 Aug 2022 10:31:14 +0000 Subject: [PATCH 0352/3231] Bump org.jetbrains.kotlin.jvm from 1.7.0 to 1.7.10 in /examples Bumps [org.jetbrains.kotlin.jvm](https://github.com/JetBrains/kotlin) from 1.7.0 to 1.7.10. - [Release notes](https://github.com/JetBrains/kotlin/releases) - [Changelog](https://github.com/JetBrains/kotlin/blob/v1.7.10/ChangeLog.md) - [Commits](https://github.com/JetBrains/kotlin/compare/v1.7.0...v1.7.10) --- updated-dependencies: - dependency-name: org.jetbrains.kotlin.jvm dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/spring-boot-kotlin-redis/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/spring-boot-kotlin-redis/build.gradle b/examples/spring-boot-kotlin-redis/build.gradle index 4a48c40d8bf..383491eb32d 100644 --- a/examples/spring-boot-kotlin-redis/build.gradle +++ b/examples/spring-boot-kotlin-redis/build.gradle @@ -1,6 +1,6 @@ plugins { id("org.springframework.boot") version "2.6.4" - id("org.jetbrains.kotlin.jvm") version "1.7.0" + id("org.jetbrains.kotlin.jvm") version "1.7.10" id("org.jetbrains.kotlin.plugin.spring") version "1.7.10" } From 6805a75ce7747fc09bca48cb27f8b83c07146abb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 2 Aug 2022 10:31:20 +0000 Subject: [PATCH 0353/3231] Bump google-cloud-spanner from 6.25.7 to 6.27.0 in /modules/gcloud Bumps [google-cloud-spanner](https://github.com/googleapis/java-spanner) from 6.25.7 to 6.27.0. - [Release notes](https://github.com/googleapis/java-spanner/releases) - [Changelog](https://github.com/googleapis/java-spanner/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/java-spanner/compare/v6.25.7...v6.27.0) --- updated-dependencies: - dependency-name: com.google.cloud:google-cloud-spanner dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/gcloud/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle index 395ef1372bf..2da6136c67b 100644 --- a/modules/gcloud/build.gradle +++ b/modules/gcloud/build.gradle @@ -6,7 +6,7 @@ dependencies { testImplementation 'com.google.cloud:google-cloud-datastore:2.10.1' testImplementation 'com.google.cloud:google-cloud-firestore:3.2.0' testImplementation 'com.google.cloud:google-cloud-pubsub:1.120.6' - testImplementation 'com.google.cloud:google-cloud-spanner:6.25.7' + testImplementation 'com.google.cloud:google-cloud-spanner:6.27.0' testImplementation 'com.google.cloud:google-cloud-bigtable:2.9.0' testImplementation 'org.assertj:assertj-core:3.23.1' } From ada9da62207c4ffa0d04804cc6a04249d412bb2d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 2 Aug 2022 10:41:10 +0000 Subject: [PATCH 0354/3231] Bump cucumber-java from 7.4.1 to 7.5.0 in /examples Bumps [cucumber-java](https://github.com/cucumber/cucumber-jvm) from 7.4.1 to 7.5.0. - [Release notes](https://github.com/cucumber/cucumber-jvm/releases) - [Changelog](https://github.com/cucumber/cucumber-jvm/blob/main/CHANGELOG.md) - [Commits](https://github.com/cucumber/cucumber-jvm/compare/v7.4.1...v7.5.0) --- updated-dependencies: - dependency-name: io.cucumber:cucumber-java dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- examples/cucumber/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cucumber/build.gradle b/examples/cucumber/build.gradle index 5cf03f419fe..ba51bbe0d2c 100644 --- a/examples/cucumber/build.gradle +++ b/examples/cucumber/build.gradle @@ -10,7 +10,7 @@ dependencies { implementation 'org.seleniumhq.selenium:selenium-remote-driver:3.141.59' implementation 'org.seleniumhq.selenium:selenium-firefox-driver:3.141.59' implementation 'org.seleniumhq.selenium:selenium-chrome-driver:3.141.59' - testImplementation 'io.cucumber:cucumber-java:7.4.1' + testImplementation 'io.cucumber:cucumber-java:7.5.0' testImplementation 'io.cucumber:cucumber-junit:7.5.0' testImplementation 'org.testcontainers:selenium' } From 485a743aecc0a65733d0dc2bbbb45727cf11810a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 2 Aug 2022 15:00:47 +0000 Subject: [PATCH 0355/3231] Bump aws-java-sdk-logs from 1.12.253 to 1.12.273 in /modules/localstack Bumps [aws-java-sdk-logs](https://github.com/aws/aws-sdk-java) from 1.12.253 to 1.12.273. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.253...1.12.273) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-logs dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index d754c676320..2c547febfb8 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -6,7 +6,7 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.253' testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.231' testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.273' - testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.253' + testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.273' testImplementation 'software.amazon.awssdk:s3:2.17.244' testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } From 87265750993592ebe617be1ec885ed2e9d0580a8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 2 Aug 2022 15:00:48 +0000 Subject: [PATCH 0356/3231] Bump google-cloud-firestore from 3.2.0 to 3.3.0 in /modules/gcloud Bumps [google-cloud-firestore](https://github.com/googleapis/java-firestore) from 3.2.0 to 3.3.0. - [Release notes](https://github.com/googleapis/java-firestore/releases) - [Changelog](https://github.com/googleapis/java-firestore/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/java-firestore/compare/v3.2.0...v3.3.0) --- updated-dependencies: - dependency-name: com.google.cloud:google-cloud-firestore dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/gcloud/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle index 2da6136c67b..eeca4d49fde 100644 --- a/modules/gcloud/build.gradle +++ b/modules/gcloud/build.gradle @@ -4,7 +4,7 @@ dependencies { api project(':testcontainers') testImplementation 'com.google.cloud:google-cloud-datastore:2.10.1' - testImplementation 'com.google.cloud:google-cloud-firestore:3.2.0' + testImplementation 'com.google.cloud:google-cloud-firestore:3.3.0' testImplementation 'com.google.cloud:google-cloud-pubsub:1.120.6' testImplementation 'com.google.cloud:google-cloud-spanner:6.27.0' testImplementation 'com.google.cloud:google-cloud-bigtable:2.9.0' From 290818fdb13baf7d13cd9364d470ce90852c8f67 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 2 Aug 2022 15:00:51 +0000 Subject: [PATCH 0357/3231] Bump aws-java-sdk-s3 from 1.12.253 to 1.12.273 in /modules/localstack Bumps [aws-java-sdk-s3](https://github.com/aws/aws-sdk-java) from 1.12.253 to 1.12.273. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.253...1.12.273) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-s3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index d754c676320..0ff951ad1b9 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -3,8 +3,8 @@ description = "Testcontainers :: Localstack" dependencies { api project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.253' - testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.231' + compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.273' + testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.273' testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.273' testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.253' testImplementation 'software.amazon.awssdk:s3:2.17.244' From 1518bebe30d028a2d69014f080d4e61221aa9caa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 2 Aug 2022 15:00:52 +0000 Subject: [PATCH 0358/3231] Bump google-cloud-bigtable from 2.9.0 to 2.10.1 in /modules/gcloud Bumps [google-cloud-bigtable](https://github.com/googleapis/java-bigtable) from 2.9.0 to 2.10.1. - [Release notes](https://github.com/googleapis/java-bigtable/releases) - [Changelog](https://github.com/googleapis/java-bigtable/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/java-bigtable/compare/v2.9.0...v2.10.1) --- updated-dependencies: - dependency-name: com.google.cloud:google-cloud-bigtable dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/gcloud/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle index 2da6136c67b..b96e9e5fd94 100644 --- a/modules/gcloud/build.gradle +++ b/modules/gcloud/build.gradle @@ -7,6 +7,6 @@ dependencies { testImplementation 'com.google.cloud:google-cloud-firestore:3.2.0' testImplementation 'com.google.cloud:google-cloud-pubsub:1.120.6' testImplementation 'com.google.cloud:google-cloud-spanner:6.27.0' - testImplementation 'com.google.cloud:google-cloud-bigtable:2.9.0' + testImplementation 'com.google.cloud:google-cloud-bigtable:2.10.1' testImplementation 'org.assertj:assertj-core:3.23.1' } From 7c3c00e02e8d93201b5934cd2a2803f530de404a Mon Sep 17 00:00:00 2001 From: Kishan B Date: Wed, 3 Aug 2022 21:22:33 +0530 Subject: [PATCH 0359/3231] Add syntax highlighting to colima usage doc (#5673) --- docs/supported_docker_environment/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/supported_docker_environment/index.md b/docs/supported_docker_environment/index.md index aa6c7b1b867..debca47e574 100644 --- a/docs/supported_docker_environment/index.md +++ b/docs/supported_docker_environment/index.md @@ -17,7 +17,7 @@ In order to run testcontainers against [colima](https://github.com/abiosoft/colima) the env vars bellow should be set -``` +```bash export TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE=/var/run/docker.sock export DOCKER_HOST="unix://${HOME}/.colima/docker.sock" ``` From c38ba11e9bd018edcd2896e234cc277552ab2c7d Mon Sep 17 00:00:00 2001 From: Erik Merkle Date: Thu, 4 Aug 2022 07:37:49 -0500 Subject: [PATCH 0360/3231] Update Cassandra driver to 4.x (#2830) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also, deprecate Driver 3.x methods and remove some driver coupling. Co-authored-by: Kevin Wittek Co-authored-by: Eddú Meléndez --- docs/modules/databases/cassandra.md | 35 +++--------------- modules/cassandra/build.gradle | 9 ++++- .../containers/CassandraContainer.java | 34 +++++++++++++++++ .../containers/CassandraContainerTest.java | 13 +++---- .../containers/CassandraDriver3Test.java | 37 +++++++++++++++++++ .../containers/CassandraDriver4Test.java | 35 ++++++++++++++++++ 6 files changed, 126 insertions(+), 37 deletions(-) create mode 100644 modules/cassandra/src/test/java/org/testcontainers/containers/CassandraDriver3Test.java create mode 100644 modules/cassandra/src/test/java/org/testcontainers/containers/CassandraDriver4Test.java diff --git a/docs/modules/databases/cassandra.md b/docs/modules/databases/cassandra.md index f7ed3094886..73f93bf203a 100644 --- a/docs/modules/databases/cassandra.md +++ b/docs/modules/databases/cassandra.md @@ -4,35 +4,12 @@ This example connects to the Cassandra Cluster, creates a keyspaces and asserts that is has been created. -=== "JUnit 4 example" - ```java - public class SomeTest { - - @Rule - public CassandraContainer cassandra = new CassandraContainer(); - - - @Test - public void test(){ - Cluster cluster = cassandra.getCluster(); - - try(Session session = cluster.connect()) { - - session.execute("CREATE KEYSPACE IF NOT EXISTS test WITH replication = \n" + - "{'class':'SimpleStrategy','replication_factor':'1'};"); - - List keyspaces = session.getCluster().getMetadata().getKeyspaces(); - List filteredKeyspaces = keyspaces - .stream() - .filter(km -> km.getName().equals("test")) - .collect(Collectors.toList()); - - assertEquals(1, filteredKeyspaces.size()); - } - } - - } - ``` + +[Building CqlSession](../../../modules/cassandra/src/test/java/org/testcontainers/containers/CassandraDriver3Test.java) inside_block:cassandra + + +!!! warning + All methods returning instances of the Cassandra Driver's Cluster object in `CassandraContainer` have been deprecated. Providing these methods unnecessarily couples the Container to the Driver and creates potential breaking changes if the driver is updated. ## Adding this module to your project dependencies diff --git a/modules/cassandra/build.gradle b/modules/cassandra/build.gradle index a5427aaea07..b00740d09fd 100644 --- a/modules/cassandra/build.gradle +++ b/modules/cassandra/build.gradle @@ -1,6 +1,13 @@ description = "TestContainers :: Cassandra" +configurations.all { + resolutionStrategy { + force 'io.dropwizard.metrics:metrics-core:3.2.4' + } +} + dependencies { api project(":database-commons") - api "com.datastax.cassandra:cassandra-driver-core:3.7.1" + api "com.datastax.cassandra:cassandra-driver-core:3.10.0" + testImplementation 'com.datastax.oss:java-driver-core:4.8.0' } diff --git a/modules/cassandra/src/main/java/org/testcontainers/containers/CassandraContainer.java b/modules/cassandra/src/main/java/org/testcontainers/containers/CassandraContainer.java index 3c78743fb62..a12ac03f18d 100644 --- a/modules/cassandra/src/main/java/org/testcontainers/containers/CassandraContainer.java +++ b/modules/cassandra/src/main/java/org/testcontainers/containers/CassandraContainer.java @@ -11,6 +11,7 @@ import org.testcontainers.utility.MountableFile; import java.io.IOException; +import java.net.InetSocketAddress; import java.net.URL; import java.nio.charset.StandardCharsets; import java.util.Optional; @@ -35,6 +36,8 @@ public class CassandraContainer> extends G public static final Integer CQL_PORT = 9042; + private static final String DEFAULT_LOCAL_DATACENTER = "datacenter1"; + private static final String CONTAINER_CONFIG_LOCATION = "/etc/cassandra"; private static final String USERNAME = "cassandra"; @@ -70,6 +73,8 @@ public CassandraContainer(DockerImageName dockerImageName) { withEnv("JVM_OPTS", "-Dcassandra.skip_wait_for_gossip_to_settle=0 -Dcassandra.initial_token=0"); withEnv("HEAP_NEWSIZE", "128M"); withEnv("MAX_HEAP_SIZE", "1024M"); + withEnv("CASSANDRA_ENDPOINT_SNITCH", "GossipingPropertyFileSnitch"); + withEnv("CASSANDRA_DC", DEFAULT_LOCAL_DATACENTER); } @Override @@ -187,11 +192,20 @@ public String getPassword() { * Get configured Cluster * * Can be used to obtain connections to Cassandra in the container + * + * @deprecated For Cassandra driver 3.x, use {@link #getHost()} and {@link #getMappedPort(int)} with + * the driver's {@link Cluster#builder() Cluster.Builder} {@code addContactPoint(String)} and + * {@code withPort(int)} methods to create a Cluster object. For Cassandra driver 4.x, use + * {@link #getContactPoint()} and {@link #getLocalDatacenter()} with the driver's {@code CqlSession.builder()} + * {@code addContactPoint(InetSocketAddress)} and {@code withLocalDatacenter(String)} methods to create + * a Session Object. See https://docs.datastax.com/en/developer/java-driver/ for more on the driver. */ + @Deprecated public Cluster getCluster() { return getCluster(this, enableJmxReporting); } + @Deprecated public static Cluster getCluster(ContainerState containerState, boolean enableJmxReporting) { final Cluster.Builder builder = Cluster .builder() @@ -203,10 +217,30 @@ public static Cluster getCluster(ContainerState containerState, boolean enableJm return builder.build(); } + @Deprecated public static Cluster getCluster(ContainerState containerState) { return getCluster(containerState, false); } + /** + * Retrieve an {@link InetSocketAddress} for connecting to the Cassandra container via the driver. + * + * @return A InetSocketAddrss representation of this Cassandra container's host and port. + */ + public InetSocketAddress getContactPoint() { + return new InetSocketAddress(getHost(), getMappedPort(CQL_PORT)); + } + + /** + * Retrieve the Local Datacenter for connecting to the Cassandra container via the driver. + * + * @return The configured local Datacenter name. + */ + public String getLocalDatacenter() { + return getEnvMap().getOrDefault("CASSANDRA_DC", DEFAULT_LOCAL_DATACENTER); + } + + @Deprecated private DatabaseDelegate getDatabaseDelegate() { return new CassandraDatabaseDelegate(this); } diff --git a/modules/cassandra/src/test/java/org/testcontainers/containers/CassandraContainerTest.java b/modules/cassandra/src/test/java/org/testcontainers/containers/CassandraContainerTest.java index 45cbba0abc7..d1ad581bcc0 100644 --- a/modules/cassandra/src/test/java/org/testcontainers/containers/CassandraContainerTest.java +++ b/modules/cassandra/src/test/java/org/testcontainers/containers/CassandraContainerTest.java @@ -23,11 +23,13 @@ public class CassandraContainerTest { private static final String TEST_CLUSTER_NAME_IN_CONF = "Test Cluster Integration Test"; + private static final String BASIC_QUERY = "SELECT release_version FROM system.local"; + @Test public void testSimple() { try (CassandraContainer cassandraContainer = new CassandraContainer<>(CASSANDRA_IMAGE)) { cassandraContainer.start(); - ResultSet resultSet = performQuery(cassandraContainer, "SELECT release_version FROM system.local"); + ResultSet resultSet = performQuery(cassandraContainer, BASIC_QUERY); assertTrue("Query was not applied", resultSet.wasApplied()); assertNotNull("Result set has no release_version", resultSet.one().getString(0)); } @@ -42,7 +44,7 @@ public void testSpecificVersion() { ) ) { cassandraContainer.start(); - ResultSet resultSet = performQuery(cassandraContainer, "SELECT release_version FROM system.local"); + ResultSet resultSet = performQuery(cassandraContainer, BASIC_QUERY); assertTrue("Query was not applied", resultSet.wasApplied()); assertEquals("Cassandra has wrong version", cassandraVersion, resultSet.one().getString(0)); } @@ -107,7 +109,7 @@ public void testCassandraQueryWaitStrategy() { .waitingFor(new CassandraQueryWaitStrategy()) ) { cassandraContainer.start(); - ResultSet resultSet = performQuery(cassandraContainer, "SELECT release_version FROM system.local"); + ResultSet resultSet = performQuery(cassandraContainer, BASIC_QUERY); assertTrue("Query was not applied", resultSet.wasApplied()); } } @@ -117,10 +119,7 @@ public void testCassandraQueryWaitStrategy() { public void testCassandraGetCluster() { try (CassandraContainer cassandraContainer = new CassandraContainer<>()) { cassandraContainer.start(); - ResultSet resultSet = performQuery( - cassandraContainer.getCluster(), - "SELECT release_version FROM system.local" - ); + ResultSet resultSet = performQuery(cassandraContainer.getCluster(), BASIC_QUERY); assertTrue("Query was not applied", resultSet.wasApplied()); assertNotNull("Result set has no release_version", resultSet.one().getString(0)); } diff --git a/modules/cassandra/src/test/java/org/testcontainers/containers/CassandraDriver3Test.java b/modules/cassandra/src/test/java/org/testcontainers/containers/CassandraDriver3Test.java new file mode 100644 index 00000000000..78f3fd2a664 --- /dev/null +++ b/modules/cassandra/src/test/java/org/testcontainers/containers/CassandraDriver3Test.java @@ -0,0 +1,37 @@ +package org.testcontainers.containers; + +import com.datastax.oss.driver.api.core.CqlIdentifier; +import com.datastax.oss.driver.api.core.CqlSession; +import com.datastax.oss.driver.api.core.metadata.schema.KeyspaceMetadata; +import org.junit.Rule; +import org.junit.Test; + +import static org.junit.Assert.assertNotNull; + +public class CassandraDriver3Test { + + @Rule + public CassandraContainer cassandra = new CassandraContainer<>("cassandra:3.11.2"); + + @Test + public void testCassandraGetContactPoint() { + try ( + // cassandra { + CqlSession session = CqlSession + .builder() + .addContactPoint(this.cassandra.getContactPoint()) + .withLocalDatacenter(this.cassandra.getLocalDatacenter()) + .build() + // } + ) { + session.execute( + "CREATE KEYSPACE IF NOT EXISTS test WITH replication = \n" + + "{'class':'SimpleStrategy','replication_factor':'1'};" + ); + + KeyspaceMetadata keyspace = session.getMetadata().getKeyspaces().get(CqlIdentifier.fromCql("test")); + + assertNotNull("Failed to create test keyspace", keyspace); + } + } +} diff --git a/modules/cassandra/src/test/java/org/testcontainers/containers/CassandraDriver4Test.java b/modules/cassandra/src/test/java/org/testcontainers/containers/CassandraDriver4Test.java new file mode 100644 index 00000000000..3a05bc723bc --- /dev/null +++ b/modules/cassandra/src/test/java/org/testcontainers/containers/CassandraDriver4Test.java @@ -0,0 +1,35 @@ +package org.testcontainers.containers; + +import com.datastax.oss.driver.api.core.CqlIdentifier; +import com.datastax.oss.driver.api.core.CqlSession; +import com.datastax.oss.driver.api.core.metadata.schema.KeyspaceMetadata; +import org.junit.Rule; +import org.junit.Test; + +import static org.junit.Assert.assertNotNull; + +public class CassandraDriver4Test { + + @Rule + public CassandraContainer cassandra = new CassandraContainer<>("cassandra:3.11.2"); + + @Test + public void testCassandraGetContactPoint() { + try ( + CqlSession session = CqlSession + .builder() + .addContactPoint(this.cassandra.getContactPoint()) + .withLocalDatacenter(this.cassandra.getLocalDatacenter()) + .build() + ) { + session.execute( + "CREATE KEYSPACE IF NOT EXISTS test WITH replication = \n" + + "{'class':'SimpleStrategy','replication_factor':'1'};" + ); + + KeyspaceMetadata keyspace = session.getMetadata().getKeyspaces().get(CqlIdentifier.fromCql("test")); + + assertNotNull("Failed to create test keyspace", keyspace); + } + } +} From 4d2b98fca08f13f4b847776aee104e94249f52d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez?= Date: Thu, 4 Aug 2022 22:40:06 -0500 Subject: [PATCH 0361/3231] Add cockroachdb to issue's template --- .github/ISSUE_TEMPLATE/bug_report.yaml | 1 + .github/ISSUE_TEMPLATE/enhancement.yaml | 1 + .github/ISSUE_TEMPLATE/feature.yaml | 1 + 3 files changed, 3 insertions(+) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index dc86b416e46..686086d076c 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -17,6 +17,7 @@ body: - Azure - Cassandra - Clickhouse + - CockroachDB - Couchbase - DB2 - Dynalite diff --git a/.github/ISSUE_TEMPLATE/enhancement.yaml b/.github/ISSUE_TEMPLATE/enhancement.yaml index f06ea99c8ba..9ef8eafaab2 100644 --- a/.github/ISSUE_TEMPLATE/enhancement.yaml +++ b/.github/ISSUE_TEMPLATE/enhancement.yaml @@ -17,6 +17,7 @@ body: - Azure - Cassandra - Clickhouse + - CockroachDB - Couchbase - DB2 - Dynalite diff --git a/.github/ISSUE_TEMPLATE/feature.yaml b/.github/ISSUE_TEMPLATE/feature.yaml index 9e79553d357..1ab2693f210 100644 --- a/.github/ISSUE_TEMPLATE/feature.yaml +++ b/.github/ISSUE_TEMPLATE/feature.yaml @@ -17,6 +17,7 @@ body: - Azure - Cassandra - Clickhouse + - CockroachDB - Couchbase - DB2 - Dynalite From ef50721d86f83c7a1c0769001f004673b12c11c2 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 5 Aug 2022 19:28:53 -0500 Subject: [PATCH 0362/3231] Update Gradle Wrapper to 7.5.1 (#5676) Signed-off-by: gradle-update-robot --- examples/gradle/wrapper/gradle-wrapper.properties | 4 ++-- gradle/wrapper/gradle-wrapper.properties | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/gradle/wrapper/gradle-wrapper.properties b/examples/gradle/wrapper/gradle-wrapper.properties index 012d6d90445..b916c04dbb2 100644 --- a/examples/gradle/wrapper/gradle-wrapper.properties +++ b/examples/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=cb87f222c5585bd46838ad4db78463a5c5f3d336e5e2b98dc7c0c586527351c2 -distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip +distributionSha256Sum=f6b8596b10cce501591e92f229816aa4046424f3b24d771751b06779d58c8ec4 +distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index ef80eb5051f..f7189a776c1 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=97a52d145762adc241bad7fd18289bf7f6801e08ece6badf80402fe2b9f250b1 -distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip +distributionSha256Sum=db9c8211ed63f61f60292c69e80d89196f9eb36665e369e7f00ac4cc841c2219 +distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists From cae12726c04ca80a7049b49245b8d005ac38e95d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Mon, 8 Aug 2022 08:07:13 -0500 Subject: [PATCH 0363/3231] Update ryuk version to 0.3.4 (#5619) --- .../src/main/java/org/testcontainers/utility/RyukContainer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/testcontainers/utility/RyukContainer.java b/core/src/main/java/org/testcontainers/utility/RyukContainer.java index 511e6ccac17..beed2e92c62 100644 --- a/core/src/main/java/org/testcontainers/utility/RyukContainer.java +++ b/core/src/main/java/org/testcontainers/utility/RyukContainer.java @@ -9,7 +9,7 @@ class RyukContainer extends GenericContainer { RyukContainer() { - super("testcontainers/ryuk:0.3.3"); + super("testcontainers/ryuk:0.3.4"); withExposedPorts(8080); withCreateContainerCmdModifier(cmd -> { cmd.withName("testcontainers-ryuk-" + DockerClientFactory.SESSION_ID); From e8d737513cc0c7f16ea757a6f4f2eeea7a5e602e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Mon, 8 Aug 2022 18:06:47 -0500 Subject: [PATCH 0364/3231] Move to assertj (#5679) --- config/checkstyle/checkstyle.xml | 1 - core/build.gradle | 1 - .../java/org/testcontainers/DaemonTest.java | 14 +- .../containers/ContainerStateTest.java | 4 +- ...ockerComposeContainerWithServicesTest.java | 29 +-- .../containers/DockerComposeFilesTest.java | 9 +- .../DockerComposeOverridesTest.java | 5 - .../containers/ExposedHostTest.java | 4 +- .../containers/GenericContainerTest.java | 30 +-- .../containers/NetworkTest.java | 39 ++-- ...ParsedDockerComposeFileValidationTest.java | 30 ++- .../containers/ReusabilityUnitTests.java | 5 +- .../containers/output/ContainerLogsTest.java | 15 +- .../output/ToStringConsumerTest.java | 5 +- .../ExternalPortListeningCheckTest.java | 16 +- ...InternalCommandPortListeningCheckTest.java | 9 +- .../DockerHealthcheckWaitStrategyTest.java | 7 +- .../wait/strategy/WaitAllStrategyTest.java | 51 ++--- ...temPropertyClientProviderStrategyTest.java | 21 +-- .../images/ImagePullPolicyTest.java | 6 +- .../images/builder/DockerfileBuildTest.java | 9 +- .../images/builder/DockerignoreTest.java | 18 +- .../statement/AbstractStatementTest.java | 5 +- .../junit/BaseDockerComposeTest.java | 10 +- .../junit/CopyFileToContainerTest.java | 17 +- .../junit/DependenciesTest.java | 39 ++-- .../DockerComposeContainerScalingTest.java | 4 +- .../junit/DockerComposeContainerTest.java | 35 ++-- .../DockerComposeContainerWithBuildTest.java | 24 ++- .../junit/DockerComposeErrorHandlingTest.java | 16 +- .../junit/DockerComposePassthroughTest.java | 18 +- .../junit/DockerComposeServiceTest.java | 4 +- .../junit/DockerComposeWaitStrategyTest.java | 28 +-- .../junit/DockerNetworkModeTest.java | 11 +- .../junit/DockerfileContainerTest.java | 20 +- .../testcontainers/junit/DockerfileTest.java | 4 - .../junit/FileOperationsTest.java | 19 +- .../junit/GenericContainerRuleTest.java | 134 ++++++-------- .../junit/NonExistentImagePullTest.java | 15 +- .../junit/OutputStreamTest.java | 24 ++- .../junit/OutputStreamWithTTYTest.java | 34 ++-- .../ParameterizedDockerfileContainerTest.java | 4 +- .../junit/WorkingDirectoryTest.java | 8 +- .../strategy/AbstractWaitStrategyTest.java | 11 +- .../strategy/HostPortWaitStrategyTest.java | 6 +- .../utility/AuthenticatedImagePullTest.java | 13 +- .../utility/ClasspathScannerTest.java | 16 +- .../utility/DirectoryTarResourceTest.java | 132 +++++-------- .../DockerImageNameCompatibilityTest.java | 52 ++++-- .../utility/DockerImageNameTest.java | 62 +++---- .../utility/ImageNameSubstitutorTest.java | 18 +- .../utility/LazyFutureTest.java | 22 ++- .../utility/MountableFileTest.java | 37 ++-- .../PrefixingImageNameSubstitutorTest.java | 60 +++--- .../utility/RegistryAuthLocatorTest.java | 114 +++++++----- .../utility/TestEnvironmentTest.java | 18 +- .../TestcontainersConfigurationTest.java | 174 +++++++----------- docs/examples/junit5/redis/build.gradle | 1 + .../quickstart/RedisBackedCacheIntTest.java | 4 +- .../RedisBackedCacheIntTestStep0.java | 4 +- examples/disque-job-queue/build.gradle | 2 +- .../test/java/DisqueBackedMailSenderTest.java | 26 +-- .../test/java/SingleDisqueInstanceTest.java | 31 +--- .../redis-backed-cache-testng/build.gradle | 2 +- .../src/test/java/RedisBackedCacheTest.java | 14 +- examples/redis-backed-cache/build.gradle | 2 +- .../src/test/java/RedisBackedCacheTest.java | 14 +- examples/selenium-container/build.gradle | 2 +- .../src/test/java/SeleniumContainerTest.java | 4 +- examples/settings.gradle | 1 - examples/singleton-container/build.gradle | 2 +- .../com/example/BarConcreteTestClass.java | 7 +- .../com/example/FooConcreteTestClass.java | 9 +- examples/solr-container/build.gradle | 2 +- .../test/java/com/example/SolrQueryTest.java | 12 +- examples/spring-boot/build.gradle | 1 - .../java/com/example/DemoControllerTest.java | 8 +- .../CosmosDBEmulatorContainerTest.java | 7 +- modules/clickhouse/build.gradle | 1 + .../clickhouse/SimpleClickhouseTest.java | 4 +- modules/cockroachdb/build.gradle | 1 + .../cockroachdb/SimpleCockroachDBTest.java | 16 +- .../ext/ScriptSplittingTest.java | 6 +- modules/db2/build.gradle | 1 + .../junit/db2/SimpleDb2Test.java | 8 +- modules/dynalite/build.gradle | 3 +- .../dynamodb/DynaliteContainerTest.java | 15 +- modules/elasticsearch/build.gradle | 2 +- .../ElasticsearchContainerTest.java | 96 +++++----- modules/hivemq/build.gradle | 1 + .../hivemq/ContainerWithCustomConfigIT.java | 10 +- .../ContainerWithoutPlatformExtensionsIT.java | 23 ++- .../hivemq/CreateFileInCopiedDirectoryIT.java | 6 +- ...DisableEnableExtensionFromDirectoryIT.java | 8 +- .../hivemq/DisableEnableExtensionIT.java | 14 +- .../hivemq/HiveMQExtensionTest.java | 8 +- .../hivemq/HiveMQTestContainerCore.java | 43 +++-- .../testcontainers/hivemq/PathUtilTest.java | 16 +- modules/jdbc-test/build.gradle | 5 +- .../jdbc/AbstractJDBCDriverTest.java | 48 +++-- modules/jdbc/build.gradle | 1 - .../jdbc/ConnectionUrlDriversTests.java | 10 +- .../jdbc/ConnectionUrlTest.java | 102 +++++----- .../jdbc/MissingJdbcDriverTest.java | 13 +- .../junit/jupiter/ComposeContainerTests.java | 4 +- .../junit/jupiter/MixedLifecycleTests.java | 6 +- .../jupiter/TestcontainersExtensionTests.java | 11 +- ...ntainersNestedRestartedContainerTests.java | 19 +- ...tcontainersNestedSharedContainerTests.java | 9 +- .../TestcontainersRestartBetweenTests.java | 6 +- .../TestcontainersSharedContainerTests.java | 9 +- .../jupiter/inheritance/InheritedTests.java | 14 +- .../k3s/OfficialClientK3sContainerTest.java | 5 +- modules/localstack/build.gradle | 2 +- .../containers/localstack/LegacyModeTest.java | 60 +++--- .../localstack/LocalstackContainerTest.java | 94 +++++----- .../junit/mariadb/SimpleMariaDBTest.java | 26 ++- modules/mockserver/build.gradle | 2 +- .../MockServerContainerRuleTest.java | 11 +- .../containers/MockServerContainerTest.java | 14 +- .../src/test/resources/logback-test.xml | 16 ++ .../CustomPasswordMSSQLServerTest.java | 2 +- .../CustomizableMSSQLServerTest.java | 4 +- .../mssqlserver/SimpleMSSQLServerTest.java | 10 +- .../jdbc/mysql/JDBCDriverWithPoolTest.java | 6 +- .../junit/mysql/CustomizableMysqlTest.java | 4 +- .../junit/mysql/MultiVersionMySQLTest.java | 10 +- .../junit/mysql/SimpleMySQLTest.java | 36 ++-- .../containers/Neo4jContainerTest.java | 13 +- modules/nginx/build.gradle | 2 +- .../testcontainers/junit/SimpleNginxTest.java | 11 +- .../containers/jdbc/OracleJDBCDriverTest.java | 4 +- .../containers/OrientDBContainerTest.java | 2 +- .../PostgreSQLConnectionURLTest.java | 42 ++--- .../containers/TimescaleDBContainerTest.java | 11 +- .../jdbc/DatabaseDriverShutdownTest.java | 12 +- .../jdbc/DatabaseDriverTmpfsTest.java | 9 +- .../CustomizablePostgreSQLTest.java | 4 +- .../postgresql/SimplePostgreSQLTest.java | 19 +- ...tcontainersR2DBCConnectionFactoryTest.java | 8 +- modules/selenium/build.gradle | 2 +- .../junit/BaseWebDriverContainerTest.java | 9 +- .../junit/BrowserWebDriverContainerTest.java | 18 +- .../LocalServerWebDriverContainerTest.java | 10 +- .../junit/SeleniumUtilsTest.java | 6 +- modules/toxiproxy/build.gradle | 2 +- .../containers/ToxiproxyTest.java | 93 +++++----- 147 files changed, 1265 insertions(+), 1544 deletions(-) create mode 100644 modules/mockserver/src/test/resources/logback-test.xml diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml index ccb8367404c..6e73f4da4aa 100644 --- a/config/checkstyle/checkstyle.xml +++ b/config/checkstyle/checkstyle.xml @@ -39,7 +39,6 @@ org.mockserver.model.HttpRequest.*, org.mockserver.model.HttpResponse.*, org.rnorth.ducttape.unreliables.Unreliables.*, - org.rnorth.visibleassertions.VisibleAssertions.*, " /> diff --git a/core/build.gradle b/core/build.gradle index d32e2fc43b2..9febf2802e9 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -97,7 +97,6 @@ dependencies { // Synthetic JAR used for MountableFileTest and DirectoryTarResourceTest testImplementation files('testlib/repo/fakejar/fakejar/0/fakejar-0.jar') - testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' testImplementation 'org.assertj:assertj-core:3.23.1' testImplementation project(':test-support') diff --git a/core/src/test/java/org/testcontainers/DaemonTest.java b/core/src/test/java/org/testcontainers/DaemonTest.java index c1a1c20d60a..dd35112f781 100644 --- a/core/src/test/java/org/testcontainers/DaemonTest.java +++ b/core/src/test/java/org/testcontainers/DaemonTest.java @@ -1,7 +1,6 @@ package org.testcontainers; import org.junit.Test; -import org.rnorth.visibleassertions.VisibleAssertions; import org.testcontainers.containers.GenericContainer; import java.io.File; @@ -9,7 +8,8 @@ import java.util.Set; import java.util.stream.Collectors; -import static org.junit.Assert.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.fail; /** * This test forks a new JVM, otherwise it's not possible to reliably diff the threads @@ -30,17 +30,13 @@ public static void main(String[] args) { Set nonDaemonThreads = threads.stream().filter(it -> !it.isDaemon()).collect(Collectors.toSet()); - if (nonDaemonThreads.isEmpty()) { - VisibleAssertions.pass("All threads marked as daemon"); - } else { + if (!nonDaemonThreads.isEmpty()) { String nonDaemonThreadNames = nonDaemonThreads .stream() .map(Thread::getName) .collect(Collectors.joining("\n", "\n", "")); - VisibleAssertions.fail( - "Expected all threads to be daemons but the following are not:\n" + nonDaemonThreadNames - ); + fail("Expected all threads to be daemons but the following are not:\n" + nonDaemonThreadNames); } } finally { if (genericContainer != null) { @@ -59,6 +55,6 @@ public void testThatAllThreadsAreDaemons() throws Exception { DaemonTest.class.getCanonicalName() ); - assertEquals(0, processBuilder.inheritIO().start().waitFor()); + assertThat(processBuilder.inheritIO().start().waitFor()).isZero(); } } diff --git a/core/src/test/java/org/testcontainers/containers/ContainerStateTest.java b/core/src/test/java/org/testcontainers/containers/ContainerStateTest.java index 4909e908713..d3cedc11464 100644 --- a/core/src/test/java/org/testcontainers/containers/ContainerStateTest.java +++ b/core/src/test/java/org/testcontainers/containers/ContainerStateTest.java @@ -1,6 +1,5 @@ package org.testcontainers.containers; -import org.assertj.core.api.Assertions; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -8,6 +7,7 @@ import java.util.Collections; import java.util.List; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.doCallRealMethod; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -44,6 +44,6 @@ public void test() { when(containerState.getPortBindings()).thenReturn(Collections.singletonList(testSet)); List result = containerState.getBoundPortNumbers(); - Assertions.assertThat(result).hasSameElementsAs(expectedResult); + assertThat(result).hasSameElementsAs(expectedResult); } } diff --git a/core/src/test/java/org/testcontainers/containers/DockerComposeContainerWithServicesTest.java b/core/src/test/java/org/testcontainers/containers/DockerComposeContainerWithServicesTest.java index b7b820a3011..029e8447a81 100644 --- a/core/src/test/java/org/testcontainers/containers/DockerComposeContainerWithServicesTest.java +++ b/core/src/test/java/org/testcontainers/containers/DockerComposeContainerWithServicesTest.java @@ -1,6 +1,7 @@ package org.testcontainers.containers; import org.junit.Test; +import org.rnorth.ducttape.TimeoutException; import org.testcontainers.containers.wait.strategy.Wait; import java.io.File; @@ -9,8 +10,8 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.rnorth.visibleassertions.VisibleAssertions.assertThrows; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; public class DockerComposeContainerWithServicesTest { @@ -92,10 +93,8 @@ public void testScaleInComposeFileIsRespected() { @Test public void testStartupTimeoutSetsTheHighestTimeout() { - assertThrows( - "We expect a timeout from the startup timeout", - org.rnorth.ducttape.TimeoutException.class, - () -> { + assertThat( + catchThrowable(() -> { try ( DockerComposeContainer compose = new DockerComposeContainer<>(SIMPLE_COMPOSE_FILE) .withServices("redis") @@ -108,8 +107,10 @@ public void testStartupTimeoutSetsTheHighestTimeout() { ) { compose.start(); } - } - ); + }) + ) + .as("We expect a timeout from the startup timeout") + .isInstanceOf(TimeoutException.class); } private void verifyStartedContainers(final DockerComposeContainer compose, final String... names) { @@ -119,16 +120,16 @@ private void verifyStartedContainers(final DockerComposeContainer compose, fi .flatMap(container -> Stream.of(container.getNames())) .collect(Collectors.toList()); - assertEquals( - "number of running services of docker-compose is the same as length of listOfServices", - names.length, - containerNames.size() - ); + assertThat(containerNames) + .as("number of running services of docker-compose is the same as length of listOfServices") + .hasSize(names.length); for (final String expectedName : names) { final long matches = containerNames.stream().filter(foundName -> foundName.endsWith(expectedName)).count(); - assertEquals("container with name starting '" + expectedName + "' should be running", 1L, matches); + assertThat(matches) + .as("container with name starting '" + expectedName + "' should be running") + .isEqualTo(1L); } } } diff --git a/core/src/test/java/org/testcontainers/containers/DockerComposeFilesTest.java b/core/src/test/java/org/testcontainers/containers/DockerComposeFilesTest.java index 612d249449b..41870eb593d 100644 --- a/core/src/test/java/org/testcontainers/containers/DockerComposeFilesTest.java +++ b/core/src/test/java/org/testcontainers/containers/DockerComposeFilesTest.java @@ -1,11 +1,12 @@ package org.testcontainers.containers; import com.google.common.collect.Lists; -import org.assertj.core.api.Assertions; import org.junit.Test; import java.io.File; +import static org.assertj.core.api.Assertions.assertThat; + public class DockerComposeFilesTest { @Test @@ -13,8 +14,7 @@ public void shouldGetDependencyImages() { DockerComposeFiles dockerComposeFiles = new DockerComposeFiles( Lists.newArrayList(new File("src/test/resources/docker-compose-imagename-parsing-v2.yml")) ); - Assertions - .assertThat(dockerComposeFiles.getDependencyImages()) + assertThat(dockerComposeFiles.getDependencyImages()) .containsExactlyInAnyOrder("postgres:latest", "redis:latest", "mysql:latest"); } @@ -26,8 +26,7 @@ public void shouldGetDependencyImagesWhenOverriding() { new File("src/test/resources/docker-compose-imagename-overriding-b.yml") ) ); - Assertions - .assertThat(dockerComposeFiles.getDependencyImages()) + assertThat(dockerComposeFiles.getDependencyImages()) .containsExactlyInAnyOrder("alpine:3.16", "redis:b", "mysql:b", "aservice:latest"); } } diff --git a/core/src/test/java/org/testcontainers/containers/DockerComposeOverridesTest.java b/core/src/test/java/org/testcontainers/containers/DockerComposeOverridesTest.java index 6207ae5ff45..50fedd3cf0f 100644 --- a/core/src/test/java/org/testcontainers/containers/DockerComposeOverridesTest.java +++ b/core/src/test/java/org/testcontainers/containers/DockerComposeOverridesTest.java @@ -16,9 +16,6 @@ import java.util.Arrays; import java.util.concurrent.TimeUnit; -import static org.rnorth.visibleassertions.VisibleAssertions.info; -import static org.rnorth.visibleassertions.VisibleAssertions.pass; - @RunWith(Parameterized.class) public class DockerComposeOverridesTest { @@ -100,11 +97,9 @@ public void test() { while (br.ready()) { String line = br.readLine(); if (line.contains(expectedEnvVar)) { - pass("Mapped environment variable was found"); return true; } } - info("Mapped environment variable was not found yet - process probably not ready"); Uninterruptibles.sleepUninterruptibly(100, TimeUnit.MILLISECONDS); return false; } diff --git a/core/src/test/java/org/testcontainers/containers/ExposedHostTest.java b/core/src/test/java/org/testcontainers/containers/ExposedHostTest.java index 4afddeaa5dd..5f88be90ad8 100644 --- a/core/src/test/java/org/testcontainers/containers/ExposedHostTest.java +++ b/core/src/test/java/org/testcontainers/containers/ExposedHostTest.java @@ -13,7 +13,7 @@ import java.io.OutputStream; import java.net.InetSocketAddress; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; public class ExposedHostTest { @@ -95,7 +95,7 @@ protected void assertResponse(GenericContainer container, int port) { .execInContainer("wget", "-O", "-", "http://host.testcontainers.internal:" + port) .getStdout(); - assertEquals("received response", "Hello World!", response); + assertThat(response).as("received response").isEqualTo("Hello World!"); } finally { container.stop(); } diff --git a/core/src/test/java/org/testcontainers/containers/GenericContainerTest.java b/core/src/test/java/org/testcontainers/containers/GenericContainerTest.java index fdd8ac1b146..1967ad95135 100644 --- a/core/src/test/java/org/testcontainers/containers/GenericContainerTest.java +++ b/core/src/test/java/org/testcontainers/containers/GenericContainerTest.java @@ -6,7 +6,6 @@ import com.github.dockerjava.api.model.ExposedPort; import com.github.dockerjava.api.model.Info; import com.github.dockerjava.api.model.Ports; -import com.google.common.primitives.Ints; import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; import lombok.experimental.FieldDefaults; @@ -30,10 +29,9 @@ import java.util.function.Predicate; import java.util.stream.Collectors; +import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.rnorth.visibleassertions.VisibleAssertions.assertThrows; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import static org.assertj.core.api.Assertions.catchThrowable; public class GenericContainerTest { @@ -119,28 +117,20 @@ public void shouldOnlyPublishExposedPorts() { .map(ExposedPort::getPort) .collect(Collectors.toList()); - assertEquals( - "the exposed ports are all of those EXPOSEd by the image", - Ints.asList(8080, 8081), - exposedPorts - ); + assertThat(exposedPorts).as("the exposed ports are all of those EXPOSEd by the image").contains(8080, 8081); Map hostBindings = inspectedContainer .getHostConfig() .getPortBindings() .getBindings(); - assertEquals("only 1 port is bound on the host (published)", 1, hostBindings.size()); + assertThat(hostBindings).as("only 1 port is bound on the host (published)").hasSize(1); Integer mappedPort = container.getMappedPort(8080); - assertTrue("port 8080 is bound to a different port on the host", mappedPort != 8080); + assertThat(mappedPort != 8080).as("port 8080 is bound to a different port on the host").isTrue(); - assertThrows( - "trying to get a non-bound port mapping fails", - IllegalArgumentException.class, - () -> { - container.getMappedPort(8081); - } - ); + assertThat(catchThrowable(() -> container.getMappedPort(8081))) + .as("trying to get a non-bound port mapping fails") + .isInstanceOf(IllegalArgumentException.class); } } @@ -161,8 +151,8 @@ public void shouldWaitUntilExposedPortIsMapped() { ) { container.start(); - assertEquals("Only withExposedPort should be exposed", 1, container.getExposedPorts().size()); - assertTrue("withExposedPort should be exposed", container.getExposedPorts().contains(8080)); + assertThat(container.getExposedPorts()).as("Only withExposedPort should be exposed").hasSize(1); + assertThat(container.getExposedPorts()).as("withExposedPort should be exposed").contains(8080); } } diff --git a/core/src/test/java/org/testcontainers/containers/NetworkTest.java b/core/src/test/java/org/testcontainers/containers/NetworkTest.java index d783f95e6d1..099bef20ed7 100644 --- a/core/src/test/java/org/testcontainers/containers/NetworkTest.java +++ b/core/src/test/java/org/testcontainers/containers/NetworkTest.java @@ -7,9 +7,7 @@ import org.testcontainers.DockerClientFactory; import org.testcontainers.TestImages; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.rnorth.visibleassertions.VisibleAssertions.assertNotEquals; -import static org.rnorth.visibleassertions.VisibleAssertions.assertNotNull; +import static org.assertj.core.api.Assertions.assertThat; @RunWith(Enclosed.class) public class NetworkTest { @@ -33,7 +31,7 @@ public static class WithRules { @Test public void testNetworkSupport() throws Exception { String response = bar.execInContainer("wget", "-O", "-", "http://foo:8080").getStdout(); - assertEquals("received response", "yay", response); + assertThat(response).as("received response").isEqualTo("yay"); } } @@ -60,7 +58,7 @@ public void testNetworkSupport() throws Exception { bar.start(); String response = bar.execInContainer("wget", "-O", "-", "http://foo:8080").getStdout(); - assertEquals("received response", "yay", response); + assertThat(response).as("received response").isEqualTo("yay"); } // } } @@ -69,11 +67,11 @@ public void testNetworkSupport() throws Exception { public void testBuilder() { try (Network network = Network.builder().driver("macvlan").build()) { String id = network.getId(); - assertEquals( - "Flag is set", - "macvlan", + assertThat( DockerClientFactory.instance().client().inspectNetworkCmd().withNetworkId(id).exec().getDriver() - ); + ) + .as("Flag is set") + .isEqualTo("macvlan"); } } @@ -83,11 +81,11 @@ public void testModifiers() { Network network = Network.builder().createNetworkCmdModifier(cmd -> cmd.withDriver("macvlan")).build() ) { String id = network.getId(); - assertEquals( - "Flag is set", - "macvlan", + assertThat( DockerClientFactory.instance().client().inspectNetworkCmd().withNetworkId(id).exec().getDriver() - ); + ) + .as("Flag is set") + .isEqualTo("macvlan"); } } @@ -95,16 +93,13 @@ public void testModifiers() { public void testReusability() { try (Network network = Network.newNetwork()) { String firstId = network.getId(); - assertNotNull( - "Network exists", - DockerClientFactory.instance().client().inspectNetworkCmd().withNetworkId(firstId).exec() - ); + assertThat(DockerClientFactory.instance().client().inspectNetworkCmd().withNetworkId(firstId).exec()) + .as("Network exists") + .isNotNull(); network.close(); - assertNotEquals( - "New network created", - firstId, + assertThat( DockerClientFactory .instance() .client() @@ -112,7 +107,9 @@ public void testReusability() { .withNetworkId(network.getId()) .exec() .getId() - ); + ) + .as("New network created") + .isNotEqualTo(firstId); } } } diff --git a/core/src/test/java/org/testcontainers/containers/ParsedDockerComposeFileValidationTest.java b/core/src/test/java/org/testcontainers/containers/ParsedDockerComposeFileValidationTest.java index f374018a8ef..8a41ac8d8a4 100644 --- a/core/src/test/java/org/testcontainers/containers/ParsedDockerComposeFileValidationTest.java +++ b/core/src/test/java/org/testcontainers/containers/ParsedDockerComposeFileValidationTest.java @@ -2,12 +2,12 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.Sets; -import org.assertj.core.api.Assertions; import org.junit.Test; import java.io.File; import java.util.Collections; +import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.assertj.core.api.Assertions.entry; @@ -65,55 +65,51 @@ public void shouldIgnoreUnknownStructure() { public void shouldObtainImageNamesV1() { File file = new File("src/test/resources/docker-compose-imagename-parsing-v1.yml"); ParsedDockerComposeFile parsedFile = new ParsedDockerComposeFile(file); - Assertions - .assertThat(parsedFile.getServiceNameToImageNames()) + assertThat(parsedFile.getServiceNameToImageNames()) + .as("all defined images are found") .contains( entry("mysql", Sets.newHashSet("mysql")), entry("redis", Sets.newHashSet("redis")), entry("custom", Sets.newHashSet("postgres")) - ) - .as("all defined images are found"); // redis, mysql from compose file, postgres from Dockerfile build + ); // redis, mysql from compose file, postgres from Dockerfile build } @Test public void shouldObtainImageNamesV2() { File file = new File("src/test/resources/docker-compose-imagename-parsing-v2.yml"); ParsedDockerComposeFile parsedFile = new ParsedDockerComposeFile(file); - Assertions - .assertThat(parsedFile.getServiceNameToImageNames()) + assertThat(parsedFile.getServiceNameToImageNames()) + .as("all defined images are found") .contains( entry("mysql", Sets.newHashSet("mysql")), entry("redis", Sets.newHashSet("redis")), entry("custom", Sets.newHashSet("postgres")) - ) - .as("all defined images are found"); + ); } @Test public void shouldObtainImageFromDockerfileBuild() { File file = new File("src/test/resources/docker-compose-imagename-parsing-dockerfile.yml"); ParsedDockerComposeFile parsedFile = new ParsedDockerComposeFile(file); - Assertions - .assertThat(parsedFile.getServiceNameToImageNames()) + assertThat(parsedFile.getServiceNameToImageNames()) + .as("all defined images are found") .contains( entry("mysql", Sets.newHashSet("mysql")), entry("redis", Sets.newHashSet("redis")), entry("custom", Sets.newHashSet("alpine:3.16")) - ) - .as("all defined images are found"); // r/ redis, mysql from compose file, alpine:3.16 from Dockerfile build + ); // r/ redis, mysql from compose file, alpine:3.16 from Dockerfile build } @Test public void shouldObtainImageFromDockerfileBuildWithContext() { File file = new File("src/test/resources/docker-compose-imagename-parsing-dockerfile-with-context.yml"); ParsedDockerComposeFile parsedFile = new ParsedDockerComposeFile(file); - Assertions - .assertThat(parsedFile.getServiceNameToImageNames()) + assertThat(parsedFile.getServiceNameToImageNames()) + .as("all defined images are found") .contains( entry("mysql", Sets.newHashSet("mysql")), entry("redis", Sets.newHashSet("redis")), entry("custom", Sets.newHashSet("alpine:3.16")) - ) - .as("all defined images are found"); // redis, mysql from compose file, alpine:3.16 from Dockerfile build + ); // redis, mysql from compose file, alpine:3.16 from Dockerfile build } } diff --git a/core/src/test/java/org/testcontainers/containers/ReusabilityUnitTests.java b/core/src/test/java/org/testcontainers/containers/ReusabilityUnitTests.java index 7aafb0149bd..04f97fef514 100644 --- a/core/src/test/java/org/testcontainers/containers/ReusabilityUnitTests.java +++ b/core/src/test/java/org/testcontainers/containers/ReusabilityUnitTests.java @@ -25,7 +25,6 @@ import org.mockito.Answers; import org.mockito.Mockito; import org.mockito.stubbing.Answer; -import org.rnorth.visibleassertions.VisibleAssertions; import org.testcontainers.DockerClientFactory; import org.testcontainers.TestImages; import org.testcontainers.containers.startupcheck.StartupCheckStrategy; @@ -82,9 +81,9 @@ public static Object[][] data() { @Test public void shouldBeReusable() { if (reusable) { - VisibleAssertions.assertTrue("Is reusable", container.canBeReused()); + assertThat(container.canBeReused()).as("Is reusable").isTrue(); } else { - VisibleAssertions.assertFalse("Is not reusable", container.canBeReused()); + assertThat(container.canBeReused()).as("Is not reusable").isFalse(); } } diff --git a/core/src/test/java/org/testcontainers/containers/output/ContainerLogsTest.java b/core/src/test/java/org/testcontainers/containers/output/ContainerLogsTest.java index a860b7802ec..cc3a98dba47 100644 --- a/core/src/test/java/org/testcontainers/containers/output/ContainerLogsTest.java +++ b/core/src/test/java/org/testcontainers/containers/output/ContainerLogsTest.java @@ -6,8 +6,7 @@ import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.startupcheck.OneShotStartupCheckStrategy; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; public class ContainerLogsTest { @@ -18,7 +17,7 @@ public void getLogsReturnsAllLogsToDate() { container.start(); final String logs = container.getLogs(); - assertEquals("stdout and stderr are reflected in the returned logs", "stdout\nstderr", logs); + assertThat(logs).as("stdout and stderr are reflected in the returned logs").isEqualTo("stdout\nstderr"); } } @@ -30,8 +29,8 @@ public void getLogsContainsBothOutputTypes() { // docsGetAllLogs { final String logs = container.getLogs(); // } - assertTrue("stdout is reflected in the returned logs", logs.contains("stdout")); - assertTrue("stderr is reflected in the returned logs", logs.contains("stderr")); + assertThat(logs).as("stdout is reflected in the returned logs").contains("stdout"); + assertThat(logs).as("stderr is reflected in the returned logs").contains("stderr"); } } @@ -43,7 +42,7 @@ public void getLogsReturnsStdOutToDate() { // docsGetStdOut { final String logs = container.getLogs(OutputFrame.OutputType.STDOUT); // } - assertTrue("stdout is reflected in the returned logs", logs.contains("stdout")); + assertThat(logs).as("stdout is reflected in the returned logs").contains("stdout"); } } @@ -55,7 +54,7 @@ public void getLogsReturnsStdErrToDate() { // docsGetStdErr { final String logs = container.getLogs(OutputFrame.OutputType.STDERR); // } - assertTrue("stderr is reflected in the returned logs", logs.contains("stderr")); + assertThat(logs).as("stderr is reflected in the returned logs").contains("stderr"); } } @@ -67,7 +66,7 @@ public void getLogsForLongRunningContainer() throws InterruptedException { Thread.sleep(1000L); final String logs = container.getLogs(OutputFrame.OutputType.STDOUT); - assertTrue("stdout is reflected in the returned logs for a running container", logs.contains("seq=0")); + assertThat(logs).as("stdout is reflected in the returned logs for a running container").contains("seq=0"); } } diff --git a/core/src/test/java/org/testcontainers/containers/output/ToStringConsumerTest.java b/core/src/test/java/org/testcontainers/containers/output/ToStringConsumerTest.java index 211b12080a0..2755f4142d4 100644 --- a/core/src/test/java/org/testcontainers/containers/output/ToStringConsumerTest.java +++ b/core/src/test/java/org/testcontainers/containers/output/ToStringConsumerTest.java @@ -1,7 +1,6 @@ package org.testcontainers.containers.output; import org.apache.commons.lang3.RandomStringUtils; -import org.assertj.core.api.Assertions; import org.junit.Test; import org.testcontainers.containers.Container.ExecResult; import org.testcontainers.containers.GenericContainer; @@ -19,7 +18,7 @@ public class ToStringConsumerTest { builder.append(' ').append(i).append(RandomStringUtils.randomAlphabetic(10000)); } LARGE_PAYLOAD = builder.toString(); - Assertions.assertThat(LARGE_PAYLOAD).doesNotContain("\n"); + assertThat(LARGE_PAYLOAD).doesNotContain("\n"); } @Test @@ -29,7 +28,7 @@ public void newlines_are_not_added_to_exec_output() throws Exception { container.start(); ExecResult build = container.execInContainer("echo", "-n", LARGE_PAYLOAD); - Assertions.assertThat(build.getStdout()).doesNotContain("\n").isEqualTo(LARGE_PAYLOAD); + assertThat(build.getStdout()).doesNotContain("\n").isEqualTo(LARGE_PAYLOAD); } } diff --git a/core/src/test/java/org/testcontainers/containers/wait/internal/ExternalPortListeningCheckTest.java b/core/src/test/java/org/testcontainers/containers/wait/internal/ExternalPortListeningCheckTest.java index c76d2481c7c..b7d9fd5a6fa 100644 --- a/core/src/test/java/org/testcontainers/containers/wait/internal/ExternalPortListeningCheckTest.java +++ b/core/src/test/java/org/testcontainers/containers/wait/internal/ExternalPortListeningCheckTest.java @@ -4,14 +4,14 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; -import org.rnorth.visibleassertions.VisibleAssertions; import org.testcontainers.containers.wait.strategy.WaitStrategyTarget; import java.net.ServerSocket; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import static org.rnorth.visibleassertions.VisibleAssertions.assertThrows; public class ExternalPortListeningCheckTest { @@ -44,7 +44,7 @@ public void singleListening() { final Boolean result = check.call(); - VisibleAssertions.assertTrue("ExternalPortListeningCheck identifies a single listening port", result); + assertThat(result).as("ExternalPortListeningCheck identifies a single listening port").isTrue(); } @Test @@ -56,7 +56,7 @@ public void multipleListening() { final Boolean result = check.call(); - VisibleAssertions.assertTrue("ExternalPortListeningCheck identifies multiple listening port", result); + assertThat(result).as("ExternalPortListeningCheck identifies multiple listening port").isTrue(); } @Test @@ -66,11 +66,9 @@ public void oneNotListening() { ImmutableSet.of(listeningSocket1.getLocalPort(), nonListeningSocket.getLocalPort()) ); - assertThrows( - "ExternalPortListeningCheck detects a non-listening port among many", - IllegalStateException.class, - (Runnable) check::call - ); + assertThat(catchThrowable(check::call)) + .as("ExternalPortListeningCheck detects a non-listening port among many") + .isInstanceOf(IllegalStateException.class); } @After diff --git a/core/src/test/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheckTest.java b/core/src/test/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheckTest.java index 6f2a5d3158c..d93eb90c339 100644 --- a/core/src/test/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheckTest.java +++ b/core/src/test/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheckTest.java @@ -7,13 +7,14 @@ import org.junit.runners.Parameterized; import org.rnorth.ducttape.TimeoutException; import org.rnorth.ducttape.unreliables.Unreliables; -import org.rnorth.visibleassertions.VisibleAssertions; import org.testcontainers.containers.GenericContainer; import org.testcontainers.images.builder.ImageFromDockerfile; import java.util.Arrays; import java.util.concurrent.TimeUnit; +import static org.assertj.core.api.Assertions.fail; + @RunWith(Parameterized.class) public class InternalCommandPortListeningCheckTest { @@ -48,8 +49,6 @@ public void singleListening() { ); Unreliables.retryUntilTrue(5, TimeUnit.SECONDS, check); - - VisibleAssertions.pass("InternalCommandPortListeningCheck identifies a single listening port"); } @Test @@ -61,7 +60,7 @@ public void nonListening() { try { Unreliables.retryUntilTrue(5, TimeUnit.SECONDS, check); - VisibleAssertions.fail("expected to fail"); + fail("expected to fail"); } catch (TimeoutException e) {} } @@ -73,7 +72,5 @@ public void lowAndHighPortListening() { ); Unreliables.retryUntilTrue(5, TimeUnit.SECONDS, check); - - VisibleAssertions.pass("InternalCommandPortListeningCheck identifies a low and a high port"); } } diff --git a/core/src/test/java/org/testcontainers/containers/wait/strategy/DockerHealthcheckWaitStrategyTest.java b/core/src/test/java/org/testcontainers/containers/wait/strategy/DockerHealthcheckWaitStrategyTest.java index 0d102def5a9..0cbf257e9b5 100644 --- a/core/src/test/java/org/testcontainers/containers/wait/strategy/DockerHealthcheckWaitStrategyTest.java +++ b/core/src/test/java/org/testcontainers/containers/wait/strategy/DockerHealthcheckWaitStrategyTest.java @@ -8,7 +8,8 @@ import java.time.Duration; -import static org.rnorth.visibleassertions.VisibleAssertions.assertThrows; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; public class DockerHealthcheckWaitStrategyTest { @@ -37,6 +38,8 @@ public void startsOnceHealthy() { @Test public void containerStartFailsIfContainerIsUnhealthy() { container.withCommand("tail", "-f", "/dev/null"); - assertThrows("Container launch fails when unhealthy", ContainerLaunchException.class, container::start); + assertThat(catchThrowable(container::start)) + .as("Container launch fails when unhealthy") + .isInstanceOf(ContainerLaunchException.class); } } diff --git a/core/src/test/java/org/testcontainers/containers/wait/strategy/WaitAllStrategyTest.java b/core/src/test/java/org/testcontainers/containers/wait/strategy/WaitAllStrategyTest.java index 2b8ba7c33ec..36facd92329 100644 --- a/core/src/test/java/org/testcontainers/containers/wait/strategy/WaitAllStrategyTest.java +++ b/core/src/test/java/org/testcontainers/containers/wait/strategy/WaitAllStrategyTest.java @@ -10,10 +10,11 @@ import java.time.Duration; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.*; -import static org.rnorth.visibleassertions.VisibleAssertions.*; public class WaitAllStrategyTest { @@ -42,11 +43,11 @@ public void parentTimeoutApplies() { DummyStrategy child1 = new DummyStrategy(Duration.ofMillis(10)); child1.withStartupTimeout(Duration.ofMillis(20)); - assertEquals("withStartupTimeout directly sets the timeout", 20L, child1.startupTimeout.toMillis()); + assertThat(child1.startupTimeout.toMillis()).as("withStartupTimeout directly sets the timeout").isEqualTo(20L); new WaitAllStrategy().withStrategy(child1).withStartupTimeout(Duration.ofMillis(30)); - assertEquals("WaitAllStrategy overrides a child's timeout", 30L, child1.startupTimeout.toMillis()); + assertThat(child1.startupTimeout.toMillis()).as("WaitAllStrategy overrides a child's timeout").isEqualTo(30L); } @Test @@ -59,8 +60,12 @@ public void parentTimeoutAppliesToMultipleChildren() { new WaitAllStrategy().withStrategy(child1).withStrategy(child2).withStartupTimeout(outerWait); - assertEquals("WaitAllStrategy overrides a child's timeout (1st)", 6L, child1.startupTimeout.toMillis()); - assertEquals("WaitAllStrategy overrides a child's timeout (2nd)", 6L, child2.startupTimeout.toMillis()); + assertThat(child1.startupTimeout.toMillis()) + .as("WaitAllStrategy overrides a child's timeout (1st)") + .isEqualTo(6L); + assertThat(child2.startupTimeout.toMillis()) + .as("WaitAllStrategy overrides a child's timeout (2nd)") + .isEqualTo(6L); } @Test @@ -73,12 +78,12 @@ public void parentTimeoutAppliesToAdditionalChildren() { new WaitAllStrategy().withStrategy(child1).withStartupTimeout(outerWait).withStrategy(child2); - assertEquals("WaitAllStrategy overrides a child's timeout (1st)", 20L, child1.startupTimeout.toMillis()); - assertEquals( - "WaitAllStrategy overrides a child's timeout (2nd, additional)", - 20L, - child2.startupTimeout.toMillis() - ); + assertThat(child1.startupTimeout.toMillis()) + .as("WaitAllStrategy overrides a child's timeout (1st)") + .isEqualTo(20L); + assertThat(child2.startupTimeout.toMillis()) + .as("WaitAllStrategy overrides a child's timeout (2nd, additional)") + .isEqualTo(20L); } /* @@ -108,13 +113,13 @@ public void withoutOuterTimeoutShouldRelyOnInnerStrategies() { doNothing().when(strategy1).waitUntilReady(eq(container)); doThrow(TimeoutException.class).when(strategy2).waitUntilReady(eq(container)); - assertThrows( - "The outer strategy timeout applies", - TimeoutException.class, - () -> { + assertThat( + catchThrowable(() -> { underTest.waitUntilReady(container); - } - ); + }) + ) + .as("The outer strategy timeout applies") + .isInstanceOf(TimeoutException.class); InOrder inOrder = inOrder(strategy1, strategy2, strategy3); inOrder.verify(strategy1).waitUntilReady(any()); @@ -126,13 +131,13 @@ public void withoutOuterTimeoutShouldRelyOnInnerStrategies() { public void timeoutChangeShouldNotBePossibleWithIndividualTimeoutMode() { final WaitStrategy underTest = new WaitAllStrategy(WaitAllStrategy.Mode.WITH_INDIVIDUAL_TIMEOUTS_ONLY); - assertThrows( - "Cannot change timeout for individual timeouts", - IllegalStateException.class, - () -> { + assertThat( + catchThrowable(() -> { underTest.withStartupTimeout(Duration.ofSeconds(42)); - } - ); + }) + ) + .as("Cannot change timeout for individual timeouts") + .isInstanceOf(IllegalStateException.class); } @Test diff --git a/core/src/test/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategyTest.java b/core/src/test/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategyTest.java index b2ab27aad27..062c3d793a5 100644 --- a/core/src/test/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategyTest.java +++ b/core/src/test/java/org/testcontainers/dockerclient/EnvironmentAndSystemPropertyClientProviderStrategyTest.java @@ -2,7 +2,6 @@ import com.github.dockerjava.core.DefaultDockerClientConfig; import com.github.dockerjava.transport.SSLConfig; -import org.assertj.core.api.Assertions; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -18,9 +17,7 @@ import java.nio.file.Path; import java.util.Properties; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isNull; @@ -69,8 +66,8 @@ public void testWhenConfigAbsent() { EnvironmentAndSystemPropertyClientProviderStrategy strategy = new EnvironmentAndSystemPropertyClientProviderStrategy(); TransportConfig transportConfig = strategy.getTransportConfig(); - assertEquals(defaultDockerHost, transportConfig.getDockerHost()); - assertEquals(defaultSSLConfig, transportConfig.getSslConfig()); + assertThat(transportConfig.getDockerHost()).isEqualTo(defaultDockerHost); + assertThat(transportConfig.getSslConfig()).isEqualTo(defaultSSLConfig); } @Test @@ -95,8 +92,8 @@ public void testWhenDockerHostPresent() { EnvironmentAndSystemPropertyClientProviderStrategy strategy = new EnvironmentAndSystemPropertyClientProviderStrategy(); TransportConfig transportConfig = strategy.getTransportConfig(); - assertEquals("tcp://1.2.3.4:2375", transportConfig.getDockerHost().toString()); - assertEquals(defaultSSLConfig, transportConfig.getSslConfig()); + assertThat(transportConfig.getDockerHost().toString()).isEqualTo("tcp://1.2.3.4:2375"); + assertThat(transportConfig.getSslConfig()).isEqualTo(defaultSSLConfig); } @Test @@ -124,10 +121,10 @@ public void testWhenDockerHostAndSSLConfigPresent() throws IOException { EnvironmentAndSystemPropertyClientProviderStrategy strategy = new EnvironmentAndSystemPropertyClientProviderStrategy(); TransportConfig transportConfig = strategy.getTransportConfig(); - assertEquals("tcp://1.2.3.4:2375", transportConfig.getDockerHost().toString()); + assertThat(transportConfig.getDockerHost().toString()).isEqualTo("tcp://1.2.3.4:2375"); SSLConfig sslConfig = transportConfig.getSslConfig(); - Assertions.assertThat(sslConfig).extracting("dockerCertPath").isEqualTo(tempDirPath); + assertThat(sslConfig).extracting("dockerCertPath").isEqualTo(tempDirPath); } @Test @@ -142,7 +139,7 @@ public void applicableWhenIgnoringUserPropertiesAndConfigured() { System.setProperty("DOCKER_HOST", "tcp://1.2.3.4:2375"); EnvironmentAndSystemPropertyClientProviderStrategy strategy = new EnvironmentAndSystemPropertyClientProviderStrategy(); - assertTrue(strategy.isApplicable()); + assertThat(strategy.isApplicable()).isTrue(); } finally { System.setProperties(oldProperties); } @@ -157,6 +154,6 @@ public void notApplicableWhenIgnoringUserPropertiesAndNotConfigured() { EnvironmentAndSystemPropertyClientProviderStrategy strategy = new EnvironmentAndSystemPropertyClientProviderStrategy(); - assertFalse(strategy.isApplicable()); + assertThat(strategy.isApplicable()).isFalse(); } } diff --git a/core/src/test/java/org/testcontainers/images/ImagePullPolicyTest.java b/core/src/test/java/org/testcontainers/images/ImagePullPolicyTest.java index 0b0ee5f1b70..a885c035e5c 100644 --- a/core/src/test/java/org/testcontainers/images/ImagePullPolicyTest.java +++ b/core/src/test/java/org/testcontainers/images/ImagePullPolicyTest.java @@ -4,7 +4,6 @@ import org.junit.ClassRule; import org.junit.Test; import org.mockito.Mockito; -import org.rnorth.visibleassertions.VisibleAssertions; import org.testcontainers.DockerClientFactory; import org.testcontainers.DockerRegistryContainer; import org.testcontainers.containers.ContainerLaunchException; @@ -12,7 +11,7 @@ import org.testcontainers.containers.startupcheck.OneShotStartupCheckStrategy; import org.testcontainers.utility.DockerImageName; -import static org.junit.Assert.fail; +import static org.assertj.core.api.Assertions.fail; import static org.mockito.ArgumentMatchers.any; public class ImagePullPolicyTest { @@ -113,11 +112,10 @@ private void expectToFailWithNotFoundException(GenericContainer container) { while (throwable.getCause() != null) { throwable = throwable.getCause(); if (throwable.getCause() instanceof NotFoundException) { - VisibleAssertions.pass("Caused by NotFoundException"); return; } } - VisibleAssertions.fail("Caused by NotFoundException"); + fail("Caused by NotFoundException"); } } diff --git a/core/src/test/java/org/testcontainers/images/builder/DockerfileBuildTest.java b/core/src/test/java/org/testcontainers/images/builder/DockerfileBuildTest.java index 29cf4fcf6a7..c45233ea390 100644 --- a/core/src/test/java/org/testcontainers/images/builder/DockerfileBuildTest.java +++ b/core/src/test/java/org/testcontainers/images/builder/DockerfileBuildTest.java @@ -9,7 +9,7 @@ import java.nio.file.Path; import java.nio.file.Paths; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; @RunWith(Parameterized.class) public class DockerfileBuildTest { @@ -71,10 +71,9 @@ public void performTest() { container.start(); final String logs = container.getLogs(); - assertTrue( - "expected file content indicates that dockerfile build steps have been run", - logs.contains(expectedFileContent) - ); + assertThat(logs) + .as("expected file content indicates that dockerfile build steps have been run") + .contains(expectedFileContent); } } } diff --git a/core/src/test/java/org/testcontainers/images/builder/DockerignoreTest.java b/core/src/test/java/org/testcontainers/images/builder/DockerignoreTest.java index 513b1ce1784..8c6d288da99 100644 --- a/core/src/test/java/org/testcontainers/images/builder/DockerignoreTest.java +++ b/core/src/test/java/org/testcontainers/images/builder/DockerignoreTest.java @@ -9,8 +9,8 @@ import java.nio.file.Path; import java.nio.file.Paths; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; -import static org.rnorth.visibleassertions.VisibleAssertions.fail; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.fail; public class DockerignoreTest { @@ -45,14 +45,12 @@ public void testValidDockerignore() throws Exception { container.start(); final String logs = container.getLogs(); - assertTrue( - "Files in the container indicated the .dockerignore was not applied. Output was: " + logs, - logs.contains("should_not_be_ignored.txt") - ); - assertTrue( - "Files in the container indicated the .dockerignore was not applied. Output was: " + logs, - !logs.contains("should_be_ignored.txt") - ); + assertThat(logs) + .as("Files in the container indicated the .dockerignore was not applied. Output was: " + logs) + .contains("should_not_be_ignored.txt"); + assertThat(logs) + .as("Files in the container indicated the .dockerignore was not applied. Output was: " + logs) + .doesNotContain("should_be_ignored.txt"); } } } diff --git a/core/src/test/java/org/testcontainers/images/builder/dockerfile/statement/AbstractStatementTest.java b/core/src/test/java/org/testcontainers/images/builder/dockerfile/statement/AbstractStatementTest.java index 69d9a1b6840..0c47d556137 100644 --- a/core/src/test/java/org/testcontainers/images/builder/dockerfile/statement/AbstractStatementTest.java +++ b/core/src/test/java/org/testcontainers/images/builder/dockerfile/statement/AbstractStatementTest.java @@ -10,8 +10,7 @@ import java.io.InputStream; import java.util.Arrays; -import static org.rnorth.visibleassertions.VisibleAssertions.fail; -import static org.rnorth.visibleassertions.VisibleAssertions.pass; +import static org.assertj.core.api.Assertions.fail; public abstract class AbstractStatementTest { @@ -64,7 +63,5 @@ protected void assertStatement(Statement statement) { fail(failureBuilder.toString()); } - - pass(testName.getMethodName()); } } diff --git a/core/src/test/java/org/testcontainers/junit/BaseDockerComposeTest.java b/core/src/test/java/org/testcontainers/junit/BaseDockerComposeTest.java index dd62ace1fac..c1aee4d7f2b 100644 --- a/core/src/test/java/org/testcontainers/junit/BaseDockerComposeTest.java +++ b/core/src/test/java/org/testcontainers/junit/BaseDockerComposeTest.java @@ -15,9 +15,7 @@ import java.util.List; import java.util.stream.Collectors; -import static org.hamcrest.Matchers.is; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.rnorth.visibleassertions.VisibleAssertions.assertThat; +import static org.assertj.core.api.Assertions.assertThat; /** * Created by rnorth on 21/05/2016. @@ -46,7 +44,7 @@ public void simpleTest() { jedis.incr("test"); jedis.incr("test"); - assertEquals("A redis instance defined in compose can be used in isolation", "3", jedis.get("test")); + assertThat(jedis.get("test")).as("A redis instance defined in compose can be used in isolation").isEqualTo("3"); } @Test @@ -61,7 +59,7 @@ public void secondTest() { jedis.incr("test"); jedis.incr("test"); - assertEquals("Tests use fresh container instances", "3", jedis.get("test")); + assertThat(jedis.get("test")).as("Tests use fresh container instances").isEqualTo("3"); // if these end up using the same container one of the test methods will fail. // However, @Rule creates a separate DockerComposeContainer instance per test, so this just shouldn't happen } @@ -73,7 +71,7 @@ public void captureNetworks() { @After public void verifyNoNetworks() { - assertThat("The networks", findAllNetworks(), is(existingNetworks)); + assertThat(findAllNetworks()).as("The networks").isEqualTo(existingNetworks); } private List findAllNetworks() { diff --git a/core/src/test/java/org/testcontainers/junit/CopyFileToContainerTest.java b/core/src/test/java/org/testcontainers/junit/CopyFileToContainerTest.java index 7041e74912a..b139ece5410 100644 --- a/core/src/test/java/org/testcontainers/junit/CopyFileToContainerTest.java +++ b/core/src/test/java/org/testcontainers/junit/CopyFileToContainerTest.java @@ -10,8 +10,7 @@ import java.io.IOException; import java.util.Map; -import static org.rnorth.visibleassertions.VisibleAssertions.assertFalse; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; public class CopyFileToContainerTest { @@ -28,7 +27,7 @@ public void checkFileCopied() throws IOException, InterruptedException { ) { container.start(); String filesList = container.execInContainer("ls", "/tmp/mappable-resource").getStdout(); - assertTrue("file list contains the file", filesList.contains(fileName)); + assertThat(filesList).as("file list contains the file").contains(fileName); } } @@ -41,7 +40,7 @@ public void shouldUseCopyForReadOnlyClasspathResources() throws Exception { ) { container.start(); String filesList = container.execInContainer("ls", "/tmp/mappable-resource").getStdout(); - assertTrue("file list contains the file", filesList.contains(fileName)); + assertThat(filesList).as("file list contains the file").contains(fileName); } } @@ -55,11 +54,11 @@ public void shouldUseCopyOnlyWithReadOnlyClasspathResources() { .withClasspathResourceMapping(resource, "/readWrite", BindMode.READ_WRITE); Map copyMap = container.getCopyToFileContainerPathMap(); - assertTrue("uses copy for read-only", copyMap.containsValue("/readOnly")); - assertTrue("uses copy for read-only and no Selinux", copyMap.containsValue("/readOnlyNoSelinux")); + assertThat(copyMap).as("uses copy for read-only").containsValue("/readOnly"); + assertThat(copyMap).as("uses copy for read-only and no Selinux").containsValue("/readOnlyNoSelinux"); - assertFalse("uses mount for read-only with Selinux", copyMap.containsValue("/readOnlyShared")); - assertFalse("uses mount for read-write", copyMap.containsValue("/readWrite")); + assertThat(copyMap).as("uses mount for read-only with Selinux").doesNotContainValue("/readOnlyShared"); + assertThat(copyMap).as("uses mount for read-write").doesNotContainValue("/readWrite"); } @Test @@ -72,7 +71,7 @@ public void shouldCreateFoldersStructureWithCopy() throws Exception { ) { container.start(); String filesList = container.execInContainer("ls", "/a/b/c/").getStdout(); - assertTrue("file list contains the file", filesList.contains("file")); + assertThat(filesList).as("file list contains the file").contains("file"); } } } diff --git a/core/src/test/java/org/testcontainers/junit/DependenciesTest.java b/core/src/test/java/org/testcontainers/junit/DependenciesTest.java index 78e55335d9a..fd58dc4335f 100644 --- a/core/src/test/java/org/testcontainers/junit/DependenciesTest.java +++ b/core/src/test/java/org/testcontainers/junit/DependenciesTest.java @@ -2,7 +2,6 @@ import lombok.Getter; import org.junit.Test; -import org.rnorth.visibleassertions.VisibleAssertions; import org.testcontainers.TestImages; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.startupcheck.OneShotStartupCheckStrategy; @@ -17,6 +16,8 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import static org.assertj.core.api.Assertions.assertThat; + public class DependenciesTest { @Test @@ -31,8 +32,8 @@ public void shouldWorkWithSimpleDependency() { container.start(); } - VisibleAssertions.assertEquals("Started once", 1, startable.getStartInvocationCount().intValue()); - VisibleAssertions.assertEquals("Does not trigger .stop()", 0, startable.getStopInvocationCount().intValue()); + assertThat(startable.getStartInvocationCount().intValue()).as("Started once").isEqualTo(1); + assertThat(startable.getStopInvocationCount().intValue()).as("Does not trigger .stop()").isZero(); } @Test @@ -48,8 +49,8 @@ public void shouldWorkWithMutlipleDependencies() { container.start(); } - VisibleAssertions.assertEquals("Startable1 started once", 1, startable1.getStartInvocationCount().intValue()); - VisibleAssertions.assertEquals("Startable2 started once", 1, startable2.getStartInvocationCount().intValue()); + assertThat(startable1.getStartInvocationCount().intValue()).as("Startable1 started once").isEqualTo(1); + assertThat(startable2.getStartInvocationCount().intValue()).as("Startable2 started once").isEqualTo(1); } @Test @@ -70,8 +71,8 @@ public void shouldStartEveryTime() { container.start(); } - VisibleAssertions.assertEquals("Started multiple times", 3, startable.getStartInvocationCount().intValue()); - VisibleAssertions.assertEquals("Does not trigger .stop()", 0, startable.getStopInvocationCount().intValue()); + assertThat(startable.getStartInvocationCount().intValue()).as("Started multiple times").isEqualTo(3); + assertThat(startable.getStopInvocationCount().intValue()).as("Does not trigger .stop()").isZero(); } @Test @@ -92,17 +93,11 @@ public void shouldStartTransitiveDependencies() { container.stop(); } - VisibleAssertions.assertEquals("Root started", 1, startable.getStartInvocationCount().intValue()); - VisibleAssertions.assertEquals( - "Transitive started", - 1, - transitiveStartable.getStartInvocationCount().intValue() - ); - VisibleAssertions.assertEquals( - "Transitive of transitive started", - 1, - transitiveOfTransitiveStartable.getStartInvocationCount().intValue() - ); + assertThat(startable.getStartInvocationCount().intValue()).as("Root started").isEqualTo(1); + assertThat(transitiveStartable.getStartInvocationCount().intValue()).as("Transitive started").isEqualTo(1); + assertThat(transitiveOfTransitiveStartable.getStartInvocationCount().intValue()) + .as("Transitive of transitive started") + .isEqualTo(1); } @Test @@ -122,10 +117,10 @@ public void shouldHandleDiamondDependencies() throws Exception { Startables.deepStart(Stream.of(d)).get(1, TimeUnit.SECONDS); - VisibleAssertions.assertEquals("A started", 1, a.getStartInvocationCount().intValue()); - VisibleAssertions.assertEquals("B started", 1, b.getStartInvocationCount().intValue()); - VisibleAssertions.assertEquals("C started", 1, c.getStartInvocationCount().intValue()); - VisibleAssertions.assertEquals("D started", 1, d.getStartInvocationCount().intValue()); + assertThat(a.getStartInvocationCount().intValue()).as("A started").isEqualTo(1); + assertThat(b.getStartInvocationCount().intValue()).as("B started").isEqualTo(1); + assertThat(c.getStartInvocationCount().intValue()).as("C started").isEqualTo(1); + assertThat(d.getStartInvocationCount().intValue()).as("D started").isEqualTo(1); } @Test diff --git a/core/src/test/java/org/testcontainers/junit/DockerComposeContainerScalingTest.java b/core/src/test/java/org/testcontainers/junit/DockerComposeContainerScalingTest.java index 651696696c7..dc0187f5251 100644 --- a/core/src/test/java/org/testcontainers/junit/DockerComposeContainerScalingTest.java +++ b/core/src/test/java/org/testcontainers/junit/DockerComposeContainerScalingTest.java @@ -7,7 +7,7 @@ import java.io.File; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; /** * Created by rnorth on 08/08/2015. @@ -47,7 +47,7 @@ public void simpleTest() { for (int i = 0; i < 3; i++) { clients[i].incr("somekey"); - assertEquals("Each redis instance is separate", "1", clients[i].get("somekey")); + assertThat(clients[i].get("somekey")).as("Each redis instance is separate").isEqualTo("1"); } } } diff --git a/core/src/test/java/org/testcontainers/junit/DockerComposeContainerTest.java b/core/src/test/java/org/testcontainers/junit/DockerComposeContainerTest.java index c49663a4e35..c610fc584af 100644 --- a/core/src/test/java/org/testcontainers/junit/DockerComposeContainerTest.java +++ b/core/src/test/java/org/testcontainers/junit/DockerComposeContainerTest.java @@ -12,10 +12,7 @@ import java.util.Collections; import java.util.Optional; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.rnorth.visibleassertions.VisibleAssertions.assertFalse; -import static org.rnorth.visibleassertions.VisibleAssertions.assertNotNull; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; /** * Created by rnorth on 08/08/2015. @@ -37,35 +34,31 @@ protected DockerComposeContainer getEnvironment() { @Test public void testGetServicePort() { int serviceWithInstancePort = environment.getServicePort("redis_1", REDIS_PORT); - assertNotNull("Port is set for service with instance number", serviceWithInstancePort); + assertThat(serviceWithInstancePort).as("Port is set for service with instance number").isNotNull(); int serviceWithoutInstancePort = environment.getServicePort("redis", REDIS_PORT); - assertNotNull("Port is set for service with instance number", serviceWithoutInstancePort); - assertEquals("Service ports are the same", serviceWithInstancePort, serviceWithoutInstancePort); + assertThat(serviceWithoutInstancePort).as("Port is set for service with instance number").isNotNull(); + assertThat(serviceWithoutInstancePort).as("Service ports are the same").isEqualTo(serviceWithInstancePort); } @Test public void shouldRetrieveContainerByServiceName() { String existingServiceName = "db_1"; Optional result = environment.getContainerByServiceName(existingServiceName); - assertTrue( - String.format("Container should be found by service name %s", existingServiceName), - result.isPresent() - ); - assertEquals( - "Mapped port for result container was wrong, probably wrong container found", - result.get().getExposedPorts(), - Collections.singletonList(3306) - ); + assertThat(result) + .as(String.format("Container should be found by service name %s", existingServiceName)) + .isPresent(); + assertThat(Collections.singletonList(3306)) + .as("Mapped port for result container was wrong, probably wrong container found") + .isEqualTo(result.get().getExposedPorts()); } @Test public void shouldReturnEmptyResultOnNoneExistingService() { String notExistingServiceName = "db_256"; Optional result = environment.getContainerByServiceName(notExistingServiceName); - assertFalse( - String.format("No container should be found under service name %s", notExistingServiceName), - result.isPresent() - ); + assertThat(result) + .as(String.format("No container should be found under service name %s", notExistingServiceName)) + .isNotPresent(); } @Test @@ -87,6 +80,6 @@ public void shouldCreateContainerWhenFileNotPrefixedWithPath() throws IOExceptio final DockerComposeContainer dockerComposeContainer = new DockerComposeContainer<>( filePathNotStartWithDotSlash ); - assertNotNull("Container could not be created using docker compose file", dockerComposeContainer); + assertThat(dockerComposeContainer).as("Container could not be created using docker compose file").isNotNull(); } } diff --git a/core/src/test/java/org/testcontainers/junit/DockerComposeContainerWithBuildTest.java b/core/src/test/java/org/testcontainers/junit/DockerComposeContainerWithBuildTest.java index e0705a2c73b..ea7e35689d0 100644 --- a/core/src/test/java/org/testcontainers/junit/DockerComposeContainerWithBuildTest.java +++ b/core/src/test/java/org/testcontainers/junit/DockerComposeContainerWithBuildTest.java @@ -13,7 +13,7 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Stream; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; @RunWith(Parameterized.class) public class DockerComposeContainerWithBuildTest { @@ -59,11 +59,13 @@ public void performTest() { builtImageName.set(imageNameForRunningContainer("_customredis_1")); final boolean isBuiltImagePresentWhileRunning = isImagePresent(builtImageName.get()); - assertEquals("the built image is present while running", true, isBuiltImagePresentWhileRunning); + assertThat(isBuiltImagePresentWhileRunning).as("the built image is present while running").isEqualTo(true); pulledImageName.set(imageNameForRunningContainer("_normalredis_1")); final boolean isPulledImagePresentWhileRunning = isImagePresent(pulledImageName.get()); - assertEquals("the pulled image is present while running", true, isPulledImagePresentWhileRunning); + assertThat(isPulledImagePresentWhileRunning) + .as("the pulled image is present while running") + .isEqualTo(true); } Unreliables.retryUntilSuccess( @@ -71,11 +73,9 @@ public void performTest() { TimeUnit.SECONDS, () -> { final boolean isBuiltImagePresentAfterRunning = isImagePresent(builtImageName.get()); - assertEquals( - "the built image is not present after running", - shouldBuiltImageBePresentAfterRunning, - isBuiltImagePresentAfterRunning - ); + assertThat(isBuiltImagePresentAfterRunning) + .as("the built image is not present after running") + .isEqualTo(shouldBuiltImageBePresentAfterRunning); return null; } ); @@ -85,11 +85,9 @@ public void performTest() { TimeUnit.SECONDS, () -> { final boolean isPulledImagePresentAfterRunning = isImagePresent(pulledImageName.get()); - assertEquals( - "the pulled image is present after running", - shouldPulledImageBePresentAfterRunning, - isPulledImagePresentAfterRunning - ); + assertThat(isPulledImagePresentAfterRunning) + .as("the pulled image is present after running") + .isEqualTo(shouldPulledImageBePresentAfterRunning); return null; } ); diff --git a/core/src/test/java/org/testcontainers/junit/DockerComposeErrorHandlingTest.java b/core/src/test/java/org/testcontainers/junit/DockerComposeErrorHandlingTest.java index 51f09bdb095..63383bb7175 100644 --- a/core/src/test/java/org/testcontainers/junit/DockerComposeErrorHandlingTest.java +++ b/core/src/test/java/org/testcontainers/junit/DockerComposeErrorHandlingTest.java @@ -1,24 +1,26 @@ package org.testcontainers.junit; import org.junit.Test; -import org.rnorth.visibleassertions.VisibleAssertions; import org.testcontainers.containers.DockerComposeContainer; import java.io.File; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; + public class DockerComposeErrorHandlingTest { @Test public void simpleTest() { - VisibleAssertions.assertThrows( - "starting with an invalid docker-compose file throws an exception", - IllegalArgumentException.class, - () -> { + assertThat( + catchThrowable(() -> { DockerComposeContainer environment = new DockerComposeContainer( new File("src/test/resources/invalid-compose.yml") ) .withExposedService("something", 123); - } - ); + }) + ) + .as("starting with an invalid docker-compose file throws an exception") + .isInstanceOf(IllegalArgumentException.class); } } diff --git a/core/src/test/java/org/testcontainers/junit/DockerComposePassthroughTest.java b/core/src/test/java/org/testcontainers/junit/DockerComposePassthroughTest.java index c026bcaa741..10a26306248 100644 --- a/core/src/test/java/org/testcontainers/junit/DockerComposePassthroughTest.java +++ b/core/src/test/java/org/testcontainers/junit/DockerComposePassthroughTest.java @@ -13,9 +13,7 @@ import java.util.Arrays; import java.util.Objects; -import static org.hamcrest.Matchers.hasItem; -import static org.rnorth.visibleassertions.VisibleAssertions.assertNotNull; -import static org.rnorth.visibleassertions.VisibleAssertions.assertThat; +import static org.assertj.core.api.Assertions.assertThat; /** * Created by rnorth on 11/06/2016. @@ -41,16 +39,14 @@ public void testContainerInstanceProperties() { final ContainerState container = waitStrategy.getContainer(); //check environment variable was set - assertThat( - "Environment variable set correctly", - Arrays.asList(Objects.requireNonNull(container.getContainerInfo().getConfig().getEnv())), - hasItem("bar=bar") - ); + assertThat(Arrays.asList(Objects.requireNonNull(container.getContainerInfo().getConfig().getEnv()))) + .as("Environment variable set correctly") + .containsOnlyOnce("bar=bar"); //check other container properties - assertNotNull("Container id is not null", container.getContainerId()); - assertNotNull("Port mapped", container.getMappedPort(3000)); - assertThat("Exposed Ports", container.getExposedPorts(), hasItem(3000)); + assertThat(container.getContainerId()).as("Container id is not null").isNotNull(); + assertThat(container.getMappedPort(3000)).as("Port mapped").isNotNull(); + assertThat(container.getExposedPorts()).containsExactly(3000); } /* diff --git a/core/src/test/java/org/testcontainers/junit/DockerComposeServiceTest.java b/core/src/test/java/org/testcontainers/junit/DockerComposeServiceTest.java index 5e6ba4cc30f..ccca85407f3 100644 --- a/core/src/test/java/org/testcontainers/junit/DockerComposeServiceTest.java +++ b/core/src/test/java/org/testcontainers/junit/DockerComposeServiceTest.java @@ -6,7 +6,7 @@ import java.io.File; -import static org.rnorth.visibleassertions.VisibleAssertions.assertNotNull; +import static org.assertj.core.api.Assertions.assertThat; public class DockerComposeServiceTest extends BaseDockerComposeTest { @@ -29,6 +29,6 @@ public void testDbIsNotStarting() { @Test public void testRedisIsStarting() { - assertNotNull("Redis server started", environment.getServicePort("redis_1", REDIS_PORT)); + assertThat(environment.getServicePort("redis_1", REDIS_PORT)).as("Redis server started").isNotNull(); } } diff --git a/core/src/test/java/org/testcontainers/junit/DockerComposeWaitStrategyTest.java b/core/src/test/java/org/testcontainers/junit/DockerComposeWaitStrategyTest.java index a30c716964e..b110e776285 100644 --- a/core/src/test/java/org/testcontainers/junit/DockerComposeWaitStrategyTest.java +++ b/core/src/test/java/org/testcontainers/junit/DockerComposeWaitStrategyTest.java @@ -5,7 +5,6 @@ import org.junit.Test; import org.junit.runner.Description; import org.mockito.Mockito; -import org.rnorth.visibleassertions.VisibleAssertions; import org.testcontainers.containers.DockerComposeContainer; import org.testcontainers.containers.wait.strategy.Wait; import org.testcontainers.containers.wait.strategy.WaitStrategy; @@ -15,6 +14,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.catchThrowable; +import static org.assertj.core.api.Assertions.fail; public class DockerComposeWaitStrategyTest { @@ -38,11 +38,8 @@ public void testWaitOnListeningPort() { try { environment.starting(Description.createTestDescription(Object.class, "name")); - VisibleAssertions.pass("Docker compose should start after waiting for listening port"); } catch (RuntimeException e) { - VisibleAssertions.fail( - "Docker compose should start after waiting for listening port with failed with: " + e - ); + fail("Docker compose should start after waiting for listening port with failed with: " + e); } } @@ -55,11 +52,8 @@ public void testWaitOnMultipleStrategiesPassing() { try { environment.starting(Description.createTestDescription(Object.class, "name")); - VisibleAssertions.pass("Docker compose should start after waiting for listening port"); } catch (RuntimeException e) { - VisibleAssertions.fail( - "Docker compose should start after waiting for listening port with failed with: " + e - ); + fail("Docker compose should start after waiting for listening port with failed with: " + e); } } @@ -70,11 +64,9 @@ public void testWaitingFails() { REDIS_PORT, Wait.forHttp("/test").withStartupTimeout(Duration.ofSeconds(10)) ); - VisibleAssertions.assertThrows( - "waiting on an invalid http path times out", - RuntimeException.class, - () -> environment.starting(Description.createTestDescription(Object.class, "name")) - ); + assertThat(catchThrowable(() -> environment.starting(Description.createTestDescription(Object.class, "name")))) + .as("waiting on an invalid http path times out") + .isInstanceOf(RuntimeException.class); } @Test @@ -91,11 +83,9 @@ public void testWaitOnOneOfMultipleStrategiesFailing() { ) .withTailChildContainers(true); - VisibleAssertions.assertThrows( - "waiting on one failing strategy to time out", - RuntimeException.class, - () -> environment.starting(Description.createTestDescription(Object.class, "name")) - ); + assertThat(catchThrowable(() -> environment.starting(Description.createTestDescription(Object.class, "name")))) + .as("waiting on one failing strategy to time out") + .isInstanceOf(RuntimeException.class); } @Test diff --git a/core/src/test/java/org/testcontainers/junit/DockerNetworkModeTest.java b/core/src/test/java/org/testcontainers/junit/DockerNetworkModeTest.java index 7962b87e157..b048e4716fd 100644 --- a/core/src/test/java/org/testcontainers/junit/DockerNetworkModeTest.java +++ b/core/src/test/java/org/testcontainers/junit/DockerNetworkModeTest.java @@ -7,8 +7,7 @@ import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.startupcheck.OneShotStartupCheckStrategy; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; /** * Simple tests of named network modes - more may be possible, but may not be reproducible @@ -28,8 +27,8 @@ public void testNoNetworkContainer() { container.start(); NetworkSettings networkSettings = container.getContainerInfo().getNetworkSettings(); - assertEquals("only one network is set", 1, networkSettings.getNetworks().size()); - assertTrue("network is 'none'", networkSettings.getNetworks().containsKey("none")); + assertThat(networkSettings.getNetworks()).as("only one network is set").hasSize(1); + assertThat(networkSettings.getNetworks()).as("network is 'none'").containsKey("none"); } } @@ -44,8 +43,8 @@ public void testHostNetworkContainer() { container.start(); NetworkSettings networkSettings = container.getContainerInfo().getNetworkSettings(); - assertEquals("only one network is set", 1, networkSettings.getNetworks().size()); - assertTrue("network is 'host'", networkSettings.getNetworks().containsKey("host")); + assertThat(networkSettings.getNetworks()).as("only one network is set").hasSize(1); + assertThat(networkSettings.getNetworks()).as("network is 'host'").containsKey("host"); } } } diff --git a/core/src/test/java/org/testcontainers/junit/DockerfileContainerTest.java b/core/src/test/java/org/testcontainers/junit/DockerfileContainerTest.java index 715ba3f64a1..9ed143aca9c 100644 --- a/core/src/test/java/org/testcontainers/junit/DockerfileContainerTest.java +++ b/core/src/test/java/org/testcontainers/junit/DockerfileContainerTest.java @@ -11,8 +11,7 @@ import java.io.IOException; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; /** * Simple test case / demonstration of creating a fresh container image from a Dockerfile DSL @@ -40,15 +39,14 @@ public void simpleDslTest() throws IOException { HttpGet get = new HttpGet(address); try (CloseableHttpResponse response = httpClient.execute(get)) { - assertEquals( - "A container built from a dockerfile can run nginx as expected, and returns a good status code", - 200, - response.getStatusLine().getStatusCode() - ); - assertTrue( - "A container built from a dockerfile can run nginx as expected, and returns an expected Server header", - response.getHeaders("Server")[0].getValue().contains("nginx") - ); + assertThat(response.getStatusLine().getStatusCode()) + .as("A container built from a dockerfile can run nginx as expected, and returns a good status code") + .isEqualTo(200); + assertThat(response.getHeaders("Server")[0].getValue()) + .as( + "A container built from a dockerfile can run nginx as expected, and returns an expected Server header" + ) + .contains("nginx"); } } } diff --git a/core/src/test/java/org/testcontainers/junit/DockerfileTest.java b/core/src/test/java/org/testcontainers/junit/DockerfileTest.java index df7bc417688..86292bcf9ac 100644 --- a/core/src/test/java/org/testcontainers/junit/DockerfileTest.java +++ b/core/src/test/java/org/testcontainers/junit/DockerfileTest.java @@ -16,8 +16,6 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import static org.rnorth.visibleassertions.VisibleAssertions.pass; - public class DockerfileTest { private static final Logger LOGGER = LoggerFactory.getLogger(DockerfileTest.class); @@ -130,8 +128,6 @@ protected void verifyImage(ImageFromDockerfile image) { try { container.start(); - - pass("Should start from Dockerfile"); } finally { container.stop(); } diff --git a/core/src/test/java/org/testcontainers/junit/FileOperationsTest.java b/core/src/test/java/org/testcontainers/junit/FileOperationsTest.java index e61721f80c4..d9655443480 100644 --- a/core/src/test/java/org/testcontainers/junit/FileOperationsTest.java +++ b/core/src/test/java/org/testcontainers/junit/FileOperationsTest.java @@ -3,7 +3,6 @@ import com.github.dockerjava.api.exception.NotFoundException; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; -import org.assertj.core.api.Assertions; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; @@ -16,7 +15,7 @@ import java.io.IOException; import static org.assertj.core.api.Assertions.assertThat; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import static org.assertj.core.api.Assertions.assertThatThrownBy; public class FileOperationsTest { @@ -37,7 +36,7 @@ public void copyFileToContainerFileTest() throws Exception { alpineCopyToContainer.copyFileFromContainer("/test.txt", actualFile.getPath()); File expectedFile = new File(mountableFile.getResolvedPath()); - assertTrue("Files aren't same ", FileUtils.contentEquals(expectedFile, actualFile)); + assertThat(FileUtils.contentEquals(expectedFile, actualFile)).as("Files aren't same ").isTrue(); } } @@ -55,7 +54,7 @@ public void copyFileToContainerFolderTest() throws Exception { alpineCopyToContainer.copyFileFromContainer("/home/test_copy_to_container.txt", actualFile.getPath()); File expectedFile = new File(mountableFile.getResolvedPath()); - assertTrue("Files aren't same ", FileUtils.contentEquals(expectedFile, actualFile)); + assertThat(FileUtils.contentEquals(expectedFile, actualFile)).as("Files aren't same ").isTrue(); } } @@ -73,7 +72,7 @@ public void copyFolderToContainerFolderTest() throws Exception { alpineCopyToContainer.copyFileFromContainer("/home/test/test-resource.txt", actualFile.getPath()); File expectedFile = new File(mountableFile.getResolvedPath() + "/test-resource.txt"); - assertTrue("Files aren't same ", FileUtils.contentEquals(expectedFile, actualFile)); + assertThat(FileUtils.contentEquals(expectedFile, actualFile)).as("Files aren't same ").isTrue(); } } @@ -102,23 +101,21 @@ public void shouldCopyFileFromContainerTest() throws IOException { alpineCopyToContainer.copyFileFromContainer("/home/test_copy_to_container.txt", actualFile.getPath()); File expectedFile = new File(mountableFile.getResolvedPath()); - assertTrue("Files aren't same ", FileUtils.contentEquals(expectedFile, actualFile)); + assertThat(FileUtils.contentEquals(expectedFile, actualFile)).as("Files aren't same ").isTrue(); } } @Test public void copyFileOperationsShouldFailWhenNotStartedTest() { try (GenericContainer container = new GenericContainer<>(TestImages.ALPINE_IMAGE).withCommand("top")) { - Assertions - .assertThatThrownBy(() -> { + assertThatThrownBy(() -> { MountableFile mountableFile = MountableFile.forClasspathResource("test_copy_to_container.txt"); container.copyFileToContainer(mountableFile, "/home/test.txt"); }) .isInstanceOf(IllegalStateException.class) .hasMessageContaining("can only be used with created / running container"); - Assertions - .assertThatThrownBy(() -> { + assertThatThrownBy(() -> { container.copyFileFromContainer("/home/test_copy_to_container.txt", IOUtils::toByteArray); }) .isInstanceOf(IllegalStateException.class) @@ -137,7 +134,7 @@ public void shouldCopyFileFromExitedContainerTest() throws IOException { assertThat( container.getDockerClient().waitContainerCmd(container.getContainerId()).start().awaitStatusCode() ) - .isEqualTo(0); + .isZero(); container.copyFileFromContainer("/home/file_in_container.txt", IOUtils::toByteArray); diff --git a/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java b/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java index 8c696585902..4ba491d83fd 100644 --- a/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java +++ b/core/src/test/java/org/testcontainers/junit/GenericContainerRuleTest.java @@ -47,12 +47,8 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -import static org.hamcrest.Matchers.equalTo; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.rnorth.visibleassertions.VisibleAssertions.assertFalse; -import static org.rnorth.visibleassertions.VisibleAssertions.assertThat; -import static org.rnorth.visibleassertions.VisibleAssertions.assertThrows; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; /** * Tests for GenericContainerRules @@ -158,9 +154,9 @@ public static void setupContent() throws FileNotFoundException { @Test public void testIsRunning() { try (GenericContainer container = new GenericContainer<>(TestImages.TINY_IMAGE).withCommand("top")) { - assertFalse("Container is not started and not running", container.isRunning()); + assertThat(container.isRunning()).as("Container is not started and not running").isFalse(); container.start(); - assertTrue("Container is started and running", container.isRunning()); + assertThat(container.isRunning()).as("Container is started and running").isTrue(); } } @@ -175,15 +171,13 @@ public void withTmpFsTest() throws Exception { // check file doesn't exist String path = "/testtmpfs/test.file"; Container.ExecResult execResult = container.execInContainer("ls", path); - assertEquals( - "tmpfs inside container works fine", - execResult.getStderr(), - "ls: /testtmpfs/test.file: No such file or directory\n" - ); + assertThat(execResult.getStderr()) + .as("tmpfs inside container works fine") + .isEqualTo("ls: /testtmpfs/test.file: No such file or directory\n"); // touch && check file does exist container.execInContainer("touch", path); execResult = container.execInContainer("ls", path); - assertEquals("tmpfs inside container works fine", execResult.getStdout(), path + "\n"); + assertThat(execResult.getStdout()).as("tmpfs inside container works fine").isEqualTo(path + "\n"); } } @@ -221,8 +215,7 @@ public void handleDelivery( channel.basicPublish(RABBIQMQ_TEST_EXCHANGE, RABBITMQ_TEST_ROUTING_KEY, null, RABBITMQ_TEST_MESSAGE.getBytes()); // check the message was received - assertTrue( - "The message was received", + assertThat( Unreliables.retryUntilSuccess( 5, TimeUnit.SECONDS, @@ -233,7 +226,9 @@ public void handleDelivery( return true; } ) - ); + ) + .as("The message was received") + .isTrue(); } @Test @@ -246,21 +241,21 @@ public void simpleMongoDbTest() { collection.insertOne(doc); Document doc2 = collection.find(new Document("name", "foo")).first(); - assertEquals("A record can be inserted into and retrieved from MongoDB", 1, doc2.get("value")); + assertThat(doc2.get("value")).as("A record can be inserted into and retrieved from MongoDB").isEqualTo(1); } @Test public void environmentAndCustomCommandTest() throws IOException { String line = getReaderForContainerPort80(alpineEnvVar).readLine(); - assertEquals("An environment variable can be passed into a command", "42", line); + assertThat(line).as("An environment variable can be passed into a command").isEqualTo("42"); } @Test public void environmentFromMapTest() throws IOException { String line = getReaderForContainerPort80(alpineEnvVarFromMap).readLine(); - assertEquals("Environment variables can be passed into a command from a map", "42 and 50", line); + assertThat(line).as("Environment variables can be passed into a command from a map").isEqualTo("42 and 50"); } @Test @@ -273,21 +268,21 @@ public void customLabelTest() { alpineCustomLabel.start(); Map labels = alpineCustomLabel.getCurrentContainerInfo().getConfig().getLabels(); - assertTrue("org.testcontainers label is present", labels.containsKey("org.testcontainers")); - assertTrue("our.custom label is present", labels.containsKey("our.custom")); - assertEquals("our.custom label value is label", labels.get("our.custom"), "label"); + assertThat(labels).as("org.testcontainers label is present").containsKey("org.testcontainers"); + assertThat(labels).as("our.custom label is present").containsKey("our.custom"); + assertThat(labels).as("our.custom label value is label").containsEntry("our.custom", "label"); } } @Test public void exceptionThrownWhenTryingToOverrideTestcontainersLabels() { - assertThrows( - "When trying to overwrite an 'org.testcontainers' label, withLabel() throws an exception", - IllegalArgumentException.class, - () -> { + assertThat( + catchThrowable(() -> { new GenericContainer<>(TestImages.ALPINE_IMAGE).withLabel("org.testcontainers.foo", "false"); - } - ); + }) + ) + .as("When trying to overwrite an 'org.testcontainers' label, withLabel() throws an exception") + .isInstanceOf(IllegalArgumentException.class); } @Test @@ -296,32 +291,24 @@ public void customClasspathResourceMappingTest() throws IOException { // in that case this test will fail. String line = getReaderForContainerPort80(alpineClasspathResource).readLine(); - assertEquals( - "Resource on the classpath can be mapped using calls to withClasspathResourceMapping", - "FOOBAR", - line - ); + assertThat(line) + .as("Resource on the classpath can be mapped using calls to withClasspathResourceMapping") + .isEqualTo("FOOBAR"); } @Test public void customClasspathResourceMappingWithSelinuxTest() throws IOException { String line = getReaderForContainerPort80(alpineClasspathResourceSelinux).readLine(); - assertEquals( - "Resource on the classpath can be mapped using calls to withClasspathResourceMappingSelinux", - "FOOBAR", - line - ); + assertThat(line) + .as("Resource on the classpath can be mapped using calls to withClasspathResourceMappingSelinux") + .isEqualTo("FOOBAR"); } @Test public void exceptionThrownWhenMappedPortNotFound() { - assertThrows( - "When the requested port is not mapped, getMappedPort() throws an exception", - IllegalArgumentException.class, - () -> { - return redis.getMappedPort(666); - } - ); + assertThat(catchThrowable(() -> redis.getMappedPort(666))) + .as("When the requested port is not mapped, getMappedPort() throws an exception") + .isInstanceOf(IllegalArgumentException.class); } protected static void writeStringToFile(File contentFolder, String filename, String string) @@ -342,14 +329,9 @@ public void failFastWhenContainerHaltsImmediately() { .withMinimumRunningDuration(Duration.ofMillis(100)); try { - assertThrows( - "When we start a container that halts immediately, an exception is thrown", - RetryCountExceededException.class, - () -> { - failsImmediately.start(); - return null; - } - ); + assertThat(catchThrowable(failsImmediately::start)) + .as("When we start a container that halts immediately, an exception is thrown") + .isInstanceOf(RetryCountExceededException.class); // Check how long it took, to verify that we ARE bailing out early. // Want to strike a balance here; too short and this test will fail intermittently @@ -357,10 +339,9 @@ public void failFastWhenContainerHaltsImmediately() { // what we're intending to test. int allowedSecondsToFailure = GenericContainer.CONTAINER_RUNNING_TIMEOUT_SEC / 2; long completedTimeMs = System.currentTimeMillis(); - assertTrue( - "container should not take long to start up", - completedTimeMs - startingTimeMs < 1000L * allowedSecondsToFailure - ); + assertThat(completedTimeMs - startingTimeMs < 1000L * allowedSecondsToFailure) + .as("container should not take long to start up") + .isTrue(); } finally { failsImmediately.stop(); } @@ -374,11 +355,10 @@ public void testExecInContainer() throws Exception { Assume.assumeTrue(TestEnvironment.dockerExecutionDriverSupportsExec()); final GenericContainer.ExecResult result = redis.execInContainer("redis-cli", "role"); - assertTrue( - "Output for \"redis-cli role\" command should start with \"master\"", - result.getStdout().startsWith("master") - ); - assertEquals("Stderr for \"redis-cli role\" command should be empty", "", result.getStderr()); + assertThat(result.getStdout()) + .as("Output for \"redis-cli role\" command should start with \"master\"") + .startsWith("master"); + assertThat(result.getStderr()).as("Stderr for \"redis-cli role\" command should be empty").isEmpty(); // We expect to reach this point for modern Docker versions. } @@ -396,7 +376,7 @@ public void extraHostTest() throws IOException { } Matcher matcher = Pattern.compile("^192.168.1.10\\s.*somehost", Pattern.MULTILINE).matcher(hosts.toString()); - assertTrue("The hosts file of container contains extra host", matcher.find()); + assertThat(matcher.find()).as("The hosts file of container contains extra host").isTrue(); } @Test @@ -414,12 +394,10 @@ public void createContainerCmdHookTest() { ) { container.start(); - assertEquals("Name is configured", "/" + randomName, container.getContainerInfo().getName()); - assertEquals( - "Command is configured", - "[redis-server, --port, 6379]", - Arrays.toString(container.getContainerInfo().getConfig().getCmd()) - ); + assertThat(container.getContainerInfo().getName()).as("Name is configured").isEqualTo("/" + randomName); + assertThat(Arrays.toString(container.getContainerInfo().getConfig().getCmd())) + .as("Command is configured") + .isEqualTo("[redis-server, --port, 6379]"); } } @@ -439,18 +417,18 @@ private BufferedReader getReaderForContainerPort80(GenericContainer container) { @Test public void addExposedPortAfterWithExposedPortsTest() { redis.addExposedPort(8987); - assertThat("Both ports should be exposed", redis.getExposedPorts().size(), equalTo(2)); - assertTrue("withExposedPort should be exposed", redis.getExposedPorts().contains(REDIS_PORT)); - assertTrue("addExposedPort should be exposed", redis.getExposedPorts().contains(8987)); + assertThat(redis.getExposedPorts()).as("Both ports should be exposed").hasSize(2); + assertThat(redis.getExposedPorts()).as("withExposedPort should be exposed").contains(REDIS_PORT); + assertThat(redis.getExposedPorts()).as("addExposedPort should be exposed").contains(8987); } @Test public void addingExposedPortTwiceShouldNotFail() { redis.addExposedPort(8987); redis.addExposedPort(8987); - assertThat("Both ports should be exposed", redis.getExposedPorts().size(), equalTo(2)); // 2 ports = de-duplicated port 8897 and original port 6379 - assertTrue("withExposedPort should be exposed", redis.getExposedPorts().contains(REDIS_PORT)); - assertTrue("addExposedPort should be exposed", redis.getExposedPorts().contains(8987)); + assertThat(redis.getExposedPorts()).as("Both ports should be exposed").hasSize(2); // 2 ports = de-duplicated port 8897 and original port 6379 + assertThat(redis.getExposedPorts()).as("withExposedPort should be exposed").contains(REDIS_PORT); + assertThat(redis.getExposedPorts()).as("addExposedPort should be exposed").contains(8987); } @Test @@ -463,7 +441,9 @@ public void sharedMemorySetTest() { containerWithSharedMemory.start(); HostConfig hostConfig = containerWithSharedMemory.getContainerInfo().getHostConfig(); - assertEquals("Shared memory not set on container", hostConfig.getShmSize(), 42L * FileUtils.ONE_MB); + assertThat(hostConfig.getShmSize()) + .as("Shared memory not set on container") + .isEqualTo(42L * FileUtils.ONE_MB); } } } diff --git a/core/src/test/java/org/testcontainers/junit/NonExistentImagePullTest.java b/core/src/test/java/org/testcontainers/junit/NonExistentImagePullTest.java index 33909e2ed27..aff9b29cbe7 100644 --- a/core/src/test/java/org/testcontainers/junit/NonExistentImagePullTest.java +++ b/core/src/test/java/org/testcontainers/junit/NonExistentImagePullTest.java @@ -5,7 +5,8 @@ import org.testcontainers.containers.GenericContainer; import org.testcontainers.utility.DockerImageName; -import static org.rnorth.visibleassertions.VisibleAssertions.assertThrows; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; /** * Created by rnorth on 20/03/2016. @@ -14,12 +15,12 @@ public class NonExistentImagePullTest { @Test(timeout = 60_000L) public void pullingNonExistentImageFailsGracefully() { - assertThrows( - "Pulling a nonexistent container will cause an exception to be thrown", - ContainerFetchException.class, - () -> { + assertThat( + catchThrowable(() -> { new GenericContainer<>(DockerImageName.parse("testcontainers/nonexistent:latest")).getDockerImageName(); - } - ); + }) + ) + .as("Pulling a nonexistent container will cause an exception to be thrown") + .isInstanceOf(ContainerFetchException.class); } } diff --git a/core/src/test/java/org/testcontainers/junit/OutputStreamTest.java b/core/src/test/java/org/testcontainers/junit/OutputStreamTest.java index 104fb78fe48..17b1d247cbb 100644 --- a/core/src/test/java/org/testcontainers/junit/OutputStreamTest.java +++ b/core/src/test/java/org/testcontainers/junit/OutputStreamTest.java @@ -15,9 +15,8 @@ import java.util.concurrent.TimeoutException; import java.util.function.Consumer; -import static org.rnorth.visibleassertions.VisibleAssertions.assertFalse; -import static org.rnorth.visibleassertions.VisibleAssertions.assertThrows; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; /** * Simple test for following container output. @@ -49,10 +48,8 @@ public void testFetchStdoutWithTimeout() { container.followOutput(consumer, OutputFrame.OutputType.STDOUT); - assertThrows( - "a TimeoutException should be thrown", - TimeoutException.class, - () -> { + assertThat( + catchThrowable(() -> { consumer.waitUntil( frame -> { return ( @@ -62,9 +59,10 @@ public void testFetchStdoutWithTimeout() { 2, TimeUnit.SECONDS ); - return true; - } - ); + }) + ) + .as("a TimeoutException should be thrown") + .isInstanceOf(TimeoutException.class); } @Test(timeout = 60_000L) @@ -102,8 +100,8 @@ public void testToStringConsumer() throws TimeoutException { waitingConsumer.waitUntilEnd(30, TimeUnit.SECONDS); String utf8String = toStringConsumer.toUtf8String(); - assertTrue("the expected first value was found", utf8String.contains("seq=1")); - assertTrue("the expected last value was found", utf8String.contains("seq=4")); - assertFalse("a non-expected value was found", utf8String.contains("seq=42")); + assertThat(utf8String).as("the expected first value was found").contains("seq=1"); + assertThat(utf8String).as("the expected last value was found").contains("seq=4"); + assertThat(utf8String).as("a non-expected value was found").doesNotContain("seq=42"); } } diff --git a/core/src/test/java/org/testcontainers/junit/OutputStreamWithTTYTest.java b/core/src/test/java/org/testcontainers/junit/OutputStreamWithTTYTest.java index a87dbef3990..c340f26d3fb 100644 --- a/core/src/test/java/org/testcontainers/junit/OutputStreamWithTTYTest.java +++ b/core/src/test/java/org/testcontainers/junit/OutputStreamWithTTYTest.java @@ -16,9 +16,8 @@ import java.util.concurrent.TimeoutException; import java.util.function.Consumer; -import static org.rnorth.visibleassertions.VisibleAssertions.assertFalse; -import static org.rnorth.visibleassertions.VisibleAssertions.assertThrows; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; @Slf4j public class OutputStreamWithTTYTest { @@ -39,7 +38,9 @@ public void testFetchStdout() throws TimeoutException { container.followOutput(consumer, OutputFrame.OutputType.STDOUT); consumer.waitUntil( - frame -> frame.getType() == OutputFrame.OutputType.STDOUT && frame.getUtf8String().contains("home"), + frame -> { + return frame.getType() == OutputFrame.OutputType.STDOUT && frame.getUtf8String().contains("home"); + }, 4, TimeUnit.SECONDS ); @@ -51,18 +52,21 @@ public void testFetchStdoutWithTimeout() { container.followOutput(consumer, OutputFrame.OutputType.STDOUT); - assertThrows( - "a TimeoutException should be thrown", - TimeoutException.class, - () -> { + assertThat( + catchThrowable(() -> { consumer.waitUntil( - frame -> frame.getType() == OutputFrame.OutputType.STDOUT && frame.getUtf8String().contains("qqq"), + frame -> { + return ( + frame.getType() == OutputFrame.OutputType.STDOUT && frame.getUtf8String().contains("qqq") + ); + }, 1, TimeUnit.SECONDS ); - return true; - } - ); + }) + ) + .as("a TimeoutException should be thrown") + .isInstanceOf(TimeoutException.class); } @Test @@ -100,8 +104,8 @@ public void testToStringConsumer() throws TimeoutException { waitingConsumer.waitUntilEnd(4, TimeUnit.SECONDS); String utf8String = toStringConsumer.toUtf8String(); - assertTrue("the expected first value was found", utf8String.contains("home")); - assertTrue("the expected last value was found", utf8String.contains("media")); - assertFalse("a non-expected value was found", utf8String.contains("qqq")); + assertThat(utf8String).as("the expected first value was found").contains("home"); + assertThat(utf8String).as("the expected last value was found").contains("media"); + assertThat(utf8String).as("a non-expected value was found").doesNotContain("qqq"); } } diff --git a/core/src/test/java/org/testcontainers/junit/ParameterizedDockerfileContainerTest.java b/core/src/test/java/org/testcontainers/junit/ParameterizedDockerfileContainerTest.java index 7c176f14a1a..8437123f97a 100644 --- a/core/src/test/java/org/testcontainers/junit/ParameterizedDockerfileContainerTest.java +++ b/core/src/test/java/org/testcontainers/junit/ParameterizedDockerfileContainerTest.java @@ -7,7 +7,7 @@ import org.testcontainers.containers.GenericContainer; import org.testcontainers.images.builder.ImageFromDockerfile; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; /** * Simple test case / demonstration of creating a fresh container image from a Dockerfile DSL when the test @@ -52,6 +52,6 @@ public static Object[][] data() { public void simpleTest() throws Exception { final String release = container.execInContainer("cat", "/etc/alpine-release").getStdout(); - assertTrue("/etc/alpine-release starts with " + expectedVersion, release.startsWith(expectedVersion)); + assertThat(release).as("/etc/alpine-release starts with " + expectedVersion).startsWith(expectedVersion); } } diff --git a/core/src/test/java/org/testcontainers/junit/WorkingDirectoryTest.java b/core/src/test/java/org/testcontainers/junit/WorkingDirectoryTest.java index 0be4022c171..05f65e1f493 100644 --- a/core/src/test/java/org/testcontainers/junit/WorkingDirectoryTest.java +++ b/core/src/test/java/org/testcontainers/junit/WorkingDirectoryTest.java @@ -6,7 +6,7 @@ import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.startupcheck.OneShotStartupCheckStrategy; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; /** * Created by rnorth on 26/07/2016. @@ -23,8 +23,8 @@ public class WorkingDirectoryTest { public void checkOutput() { String listing = container.getLogs(); - assertTrue("Directory listing contains expected /etc content", listing.contains("hostname")); - assertTrue("Directory listing contains expected /etc content", listing.contains("init.d")); - assertTrue("Directory listing contains expected /etc content", listing.contains("passwd")); + assertThat(listing).as("Directory listing contains expected /etc content").contains("hostname"); + assertThat(listing).as("Directory listing contains expected /etc content").contains("init.d"); + assertThat(listing).as("Directory listing contains expected /etc content").contains("passwd"); } } diff --git a/core/src/test/java/org/testcontainers/junit/wait/strategy/AbstractWaitStrategyTest.java b/core/src/test/java/org/testcontainers/junit/wait/strategy/AbstractWaitStrategyTest.java index 530638f0f70..45809f95627 100644 --- a/core/src/test/java/org/testcontainers/junit/wait/strategy/AbstractWaitStrategyTest.java +++ b/core/src/test/java/org/testcontainers/junit/wait/strategy/AbstractWaitStrategyTest.java @@ -3,7 +3,6 @@ import org.jetbrains.annotations.NotNull; import org.junit.Before; import org.rnorth.ducttape.RetryCountExceededException; -import org.rnorth.visibleassertions.VisibleAssertions; import org.testcontainers.TestImages; import org.testcontainers.containers.ContainerLaunchException; import org.testcontainers.containers.GenericContainer; @@ -12,6 +11,8 @@ import java.time.Duration; import java.util.concurrent.atomic.AtomicBoolean; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; import static org.junit.Assert.assertTrue; /** @@ -109,11 +110,9 @@ protected void waitUntilReadyAndTimeout(String shellCommand) { */ protected void waitUntilReadyAndTimeout(GenericContainer container) { // start() blocks until successful or timeout - VisibleAssertions.assertThrows( - "an exception is thrown when timeout occurs (" + WAIT_TIMEOUT_MILLIS + "ms)", - ContainerLaunchException.class, - container::start - ); + assertThat(catchThrowable(container::start)) + .as("an exception is thrown when timeout occurs (" + WAIT_TIMEOUT_MILLIS + "ms)") + .isInstanceOf(ContainerLaunchException.class); } /** diff --git a/core/src/test/java/org/testcontainers/junit/wait/strategy/HostPortWaitStrategyTest.java b/core/src/test/java/org/testcontainers/junit/wait/strategy/HostPortWaitStrategyTest.java index 4b5f43a4d4b..d5a0ba6929d 100644 --- a/core/src/test/java/org/testcontainers/junit/wait/strategy/HostPortWaitStrategyTest.java +++ b/core/src/test/java/org/testcontainers/junit/wait/strategy/HostPortWaitStrategyTest.java @@ -8,8 +8,6 @@ import java.time.Duration; -import static org.rnorth.visibleassertions.VisibleAssertions.pass; - /** * Test wait strategy with overloaded waitingFor methods. * Other implementations of WaitStrategy are tested through backwards compatible wait strategy tests @@ -24,7 +22,5 @@ public class HostPortWaitStrategyTest { .waitingFor(Wait.forListeningPort().withStartupTimeout(Duration.ofSeconds(10))); @Test - public void testWaiting() { - pass("Container starts after waiting"); - } + public void testWaiting() {} } diff --git a/core/src/test/java/org/testcontainers/utility/AuthenticatedImagePullTest.java b/core/src/test/java/org/testcontainers/utility/AuthenticatedImagePullTest.java index bcf88d7be11..0e00dd87bf7 100644 --- a/core/src/test/java/org/testcontainers/utility/AuthenticatedImagePullTest.java +++ b/core/src/test/java/org/testcontainers/utility/AuthenticatedImagePullTest.java @@ -19,10 +19,10 @@ import java.nio.file.Path; import java.nio.file.Paths; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.Mockito.when; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; /** * This test checks the integration between Testcontainers and an authenticated registry, but uses @@ -91,7 +91,7 @@ public void testThatAuthLocatorIsUsedForContainerCreation() { ) { container.start(); - assertTrue("container started following an authenticated pull", container.isRunning()); + assertThat(container.isRunning()).as("container started following an authenticated pull").isTrue(); } } @@ -111,7 +111,7 @@ public void testThatAuthLocatorIsUsedForDockerfileBuild() throws IOException { ) { container.start(); - assertTrue("container started following an authenticated pull", container.isRunning()); + assertThat(container.isRunning()).as("container started following an authenticated pull").isTrue(); } } @@ -133,10 +133,11 @@ public void testThatAuthLocatorIsUsedForDockerComposePull() throws IOException { try (final DockerComposeContainer compose = new DockerComposeContainer<>(tempFile.toFile())) { compose.start(); - assertTrue( - "container started following an authenticated pull", + assertThat( compose.getContainerByServiceName("privateservice_1").map(ContainerState::isRunning).orElse(false) - ); + ) + .as("container started following an authenticated pull") + .isTrue(); } } diff --git a/core/src/test/java/org/testcontainers/utility/ClasspathScannerTest.java b/core/src/test/java/org/testcontainers/utility/ClasspathScannerTest.java index 4ed86e3a8fa..e775a2cd492 100644 --- a/core/src/test/java/org/testcontainers/utility/ClasspathScannerTest.java +++ b/core/src/test/java/org/testcontainers/utility/ClasspathScannerTest.java @@ -10,10 +10,10 @@ import java.util.List; import java.util.stream.Collectors; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; public class ClasspathScannerTest { @@ -41,7 +41,7 @@ public void realClassLoaderLookupOccurs() { // look for a resource that we know exists only once final List foundURLs = ClasspathScanner.scanFor("expectedClasspathFile.txt").collect(Collectors.toList()); - assertEquals("Exactly one resource was found", 1, foundURLs.size()); + assertThat(foundURLs).as("Exactly one resource was found").hasSize(1); } @Test @@ -53,7 +53,7 @@ public void multipleResultsOnOneClassLoaderAreFound() throws IOException { final List foundURLs = ClasspathScanner .scanFor("someName", firstMockClassLoader) .collect(Collectors.toList()); - assertEquals("The expected URLs are found", Arrays.asList(FILE_A, FILE_B), foundURLs); + assertThat(foundURLs).as("The expected URLs are found").containsExactly(FILE_A, FILE_B); } @Test @@ -65,11 +65,9 @@ public void orderIsAlphabeticalForDeterminism() throws IOException { final List foundURLs = ClasspathScanner .scanFor("someName", firstMockClassLoader) .collect(Collectors.toList()); - assertEquals( - "The expected URLs are found in the expected order", - Arrays.asList(FILE_A, FILE_B, JAR_A, JAR_B), - foundURLs - ); + assertThat(foundURLs) + .as("The expected URLs are found in the expected order") + .containsExactly(FILE_A, FILE_B, JAR_A, JAR_B); } @Test @@ -92,6 +90,6 @@ public void multipleClassLoadersAreQueried() throws IOException { .scanFor("someName", firstMockClassLoader, secondMockClassLoader) .collect(Collectors.toList()); - assertEquals("The expected URLs are found", Arrays.asList(FILE_A, FILE_B, FILE_C), foundURLs); + assertThat(foundURLs).as("The expected URLs are found").containsExactly(FILE_A, FILE_B, FILE_C); } } diff --git a/core/src/test/java/org/testcontainers/utility/DirectoryTarResourceTest.java b/core/src/test/java/org/testcontainers/utility/DirectoryTarResourceTest.java index 46fd6894a02..6215f84520b 100644 --- a/core/src/test/java/org/testcontainers/utility/DirectoryTarResourceTest.java +++ b/core/src/test/java/org/testcontainers/utility/DirectoryTarResourceTest.java @@ -1,20 +1,15 @@ package org.testcontainers.utility; -import org.hamcrest.Description; -import org.hamcrest.Matcher; -import org.hamcrest.core.IsCollectionContaining; +import org.assertj.core.api.Condition; import org.junit.Test; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.startupcheck.OneShotStartupCheckStrategy; import org.testcontainers.images.builder.ImageFromDockerfile; import java.io.File; -import java.util.Arrays; +import java.util.function.Predicate; -import static org.hamcrest.Matchers.allOf; -import static org.hamcrest.Matchers.containsString; -import static org.rnorth.visibleassertions.VisibleAssertions.assertThat; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; public class DirectoryTarResourceTest { @@ -40,88 +35,63 @@ public void simpleRecursiveFileTest() { final String results = container.getLogs(); - assertTrue( - "The container has a file that was copied in via a recursive copy", - results.contains("Used for DirectoryTarResourceTest") - ); + assertThat(results) + .as("The container has a file that was copied in via a recursive copy") + .contains("Used for DirectoryTarResourceTest"); } @Test public void simpleRecursiveFileWithPermissionTest() { - GenericContainer container = new GenericContainer( - new ImageFromDockerfile() - .withDockerfileFromBuilder(builder -> { - builder - .from("alpine:3.16") // - .copy("/tmp/foo", "/foo") - .cmd("ls", "-al", "/") - .build(); - }) - .withFileFromFile("/tmp/foo", new File("/mappable-resource/test-resource.txt"), 0754) - ) - .withStartupCheckStrategy(new OneShotStartupCheckStrategy()); - - container.start(); - String listing = container.getLogs(); - - assertThat( - "Listing shows that file is copied with mode requested.", - Arrays.asList(listing.split("\\n")), - exactlyNItems(1, allOf(containsString("-rwxr-xr--"), containsString("foo"))) - ); + try ( + GenericContainer container = new GenericContainer( + new ImageFromDockerfile() + .withDockerfileFromBuilder(builder -> { + builder + .from("alpine:3.16") // + .copy("/tmp/foo", "/foo") + .cmd("ls", "-al", "/") + .build(); + }) + .withFileFromFile("/tmp/foo", new File("/mappable-resource/test-resource.txt"), 0754) + ) + .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) + ) { + container.start(); + String listing = container.getLogs(); + + Predicate condition = s -> s.contains("-rwxr-xr--") && s.contains("foo"); + assertThat(listing.split("\\n")) + .as("Listing shows that file is copied with mode requested.") + .haveAtLeastOne(new Condition<>(condition, "File not found in listing")); + } } @Test public void simpleRecursiveClasspathResourceTest() { // This test combines the copying of classpath resources from JAR files with the recursive TAR approach, to allow JARed classpath resources to be copied in to an image - GenericContainer container = new GenericContainer( - new ImageFromDockerfile() - .withDockerfileFromBuilder(builder -> { - builder - .from("alpine:3.16") // - .copy("/tmp/foo", "/foo") - .cmd("ls -lRt /foo") - .build(); - }) - .withFileFromClasspath("/tmp/foo", "/recursive/dir") - ) // here we use /org/junit as a directory that really should exist on the classpath - .withStartupCheckStrategy(new OneShotStartupCheckStrategy()); - - container.start(); - - final String results = container.getLogs(); - - // ExternalResource.class is known to exist in a subdirectory of /org/junit so should be successfully copied in - assertTrue( - "The container has a file that was copied in via a recursive copy from a JAR resource", - results.contains("content.txt") - ); - } - - public static Matcher> exactlyNItems(final int n, Matcher elementMatcher) { - return new IsCollectionContaining(elementMatcher) { - @Override - protected boolean matchesSafely(Iterable collection, Description mismatchDescription) { - int count = 0; - boolean isPastFirst = false; - - for (Object item : collection) { - if (elementMatcher.matches(item)) { - count++; - } - if (isPastFirst) { - mismatchDescription.appendText(", "); - } - elementMatcher.describeMismatch(item, mismatchDescription); - isPastFirst = true; - } - - if (count != n) { - mismatchDescription.appendText(". Expected exactly " + n + " but got " + count); - } - return count == n; - } - }; + try ( + GenericContainer container = new GenericContainer( + new ImageFromDockerfile() + .withDockerfileFromBuilder(builder -> { + builder + .from("alpine:3.16") // + .copy("/tmp/foo", "/foo") + .cmd("ls -lRt /foo") + .build(); + }) + .withFileFromClasspath("/tmp/foo", "/recursive/dir") + ) // here we use /org/junit as a directory that really should exist on the classpath + .withStartupCheckStrategy(new OneShotStartupCheckStrategy()) + ) { + container.start(); + + final String results = container.getLogs(); + + // ExternalResource.class is known to exist in a subdirectory of /org/junit so should be successfully copied in + assertThat(results) + .as("The container has a file that was copied in via a recursive copy from a JAR resource") + .contains("content.txt"); + } } } diff --git a/core/src/test/java/org/testcontainers/utility/DockerImageNameCompatibilityTest.java b/core/src/test/java/org/testcontainers/utility/DockerImageNameCompatibilityTest.java index f2ca26b8947..e9aac2ed475 100644 --- a/core/src/test/java/org/testcontainers/utility/DockerImageNameCompatibilityTest.java +++ b/core/src/test/java/org/testcontainers/utility/DockerImageNameCompatibilityTest.java @@ -4,9 +4,8 @@ import org.junit.Test; import org.junit.rules.ExpectedException; +import static org.assertj.core.api.Assertions.assertThat; import static org.hamcrest.Matchers.containsString; -import static org.rnorth.visibleassertions.VisibleAssertions.assertFalse; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; public class DockerImageNameCompatibilityTest { @@ -17,7 +16,7 @@ public class DockerImageNameCompatibilityTest { public void testPlainImage() { DockerImageName subject = DockerImageName.parse("foo"); - assertFalse("image name foo != bar", subject.isCompatibleWith(DockerImageName.parse("bar"))); + assertThat(subject.isCompatibleWith(DockerImageName.parse("bar"))).as("image name foo != bar").isFalse(); } @Test @@ -29,53 +28,66 @@ public void testNoTagTreatedAsWildcard() { The test is effectively making sure that 'no tag' is treated as a wildcard */ - assertFalse("foo:4.5.6 != foo:1.2.3", subject.isCompatibleWith(DockerImageName.parse("foo:1.2.3"))); - assertTrue("foo:4.5.6 ~= foo", subject.isCompatibleWith(DockerImageName.parse("foo"))); + assertThat(subject.isCompatibleWith(DockerImageName.parse("foo:1.2.3"))).as("foo:4.5.6 != foo:1.2.3").isFalse(); + assertThat(subject.isCompatibleWith(DockerImageName.parse("foo"))).as("foo:4.5.6 ~= foo").isTrue(); } @Test public void testImageWithAutomaticCompatibilityForFullPath() { DockerImageName subject = DockerImageName.parse("repo/foo:1.2.3"); - assertTrue("repo/foo:1.2.3 ~= repo/foo", subject.isCompatibleWith(DockerImageName.parse("repo/foo"))); + assertThat(subject.isCompatibleWith(DockerImageName.parse("repo/foo"))) + .as("repo/foo:1.2.3 ~= repo/foo") + .isTrue(); } @Test public void testImageWithClaimedCompatibility() { DockerImageName subject = DockerImageName.parse("foo").asCompatibleSubstituteFor("bar"); - assertTrue("foo(bar) ~= bar", subject.isCompatibleWith(DockerImageName.parse("bar"))); - assertFalse("foo(bar) != fizz", subject.isCompatibleWith(DockerImageName.parse("fizz"))); + assertThat(subject.isCompatibleWith(DockerImageName.parse("bar"))).as("foo(bar) ~= bar").isTrue(); + assertThat(subject.isCompatibleWith(DockerImageName.parse("fizz"))).as("foo(bar) != fizz").isFalse(); } @Test public void testImageWithClaimedCompatibilityAndVersion() { DockerImageName subject = DockerImageName.parse("foo:1.2.3").asCompatibleSubstituteFor("bar"); - assertTrue("foo:1.2.3(bar) ~= bar", subject.isCompatibleWith(DockerImageName.parse("bar"))); + assertThat(subject.isCompatibleWith(DockerImageName.parse("bar"))).as("foo:1.2.3(bar) ~= bar").isTrue(); } @Test public void testImageWithClaimedCompatibilityForFullPath() { DockerImageName subject = DockerImageName.parse("foo").asCompatibleSubstituteFor("registry/repo/bar"); - assertTrue( - "foo(registry/repo/bar) ~= registry/repo/bar", - subject.isCompatibleWith(DockerImageName.parse("registry/repo/bar")) - ); - assertFalse("foo(registry/repo/bar) != repo/bar", subject.isCompatibleWith(DockerImageName.parse("repo/bar"))); - assertFalse("foo(registry/repo/bar) != bar", subject.isCompatibleWith(DockerImageName.parse("bar"))); + assertThat(subject.isCompatibleWith(DockerImageName.parse("registry/repo/bar"))) + .as("foo(registry/repo/bar) ~= registry/repo/bar") + .isTrue(); + assertThat(subject.isCompatibleWith(DockerImageName.parse("repo/bar"))) + .as("foo(registry/repo/bar) != repo/bar") + .isFalse(); + assertThat(subject.isCompatibleWith(DockerImageName.parse("bar"))) + .as("foo(registry/repo/bar) != bar") + .isFalse(); } @Test public void testImageWithClaimedCompatibilityForVersion() { DockerImageName subject = DockerImageName.parse("foo").asCompatibleSubstituteFor("bar:1.2.3"); - assertTrue("foo(bar:1.2.3) ~= bar", subject.isCompatibleWith(DockerImageName.parse("bar"))); - assertTrue("foo(bar:1.2.3) ~= bar:1.2.3", subject.isCompatibleWith(DockerImageName.parse("bar:1.2.3"))); - assertFalse("foo(bar:1.2.3) != bar:0.0.1", subject.isCompatibleWith(DockerImageName.parse("bar:0.0.1"))); - assertFalse("foo(bar:1.2.3) != bar:2.0.0", subject.isCompatibleWith(DockerImageName.parse("bar:2.0.0"))); - assertFalse("foo(bar:1.2.3) != bar:1.2.4", subject.isCompatibleWith(DockerImageName.parse("bar:1.2.4"))); + assertThat(subject.isCompatibleWith(DockerImageName.parse("bar"))).as("foo(bar:1.2.3) ~= bar").isTrue(); + assertThat(subject.isCompatibleWith(DockerImageName.parse("bar:1.2.3"))) + .as("foo(bar:1.2.3) ~= bar:1.2.3") + .isTrue(); + assertThat(subject.isCompatibleWith(DockerImageName.parse("bar:0.0.1"))) + .as("foo(bar:1.2.3) != bar:0.0.1") + .isFalse(); + assertThat(subject.isCompatibleWith(DockerImageName.parse("bar:2.0.0"))) + .as("foo(bar:1.2.3) != bar:2.0.0") + .isFalse(); + assertThat(subject.isCompatibleWith(DockerImageName.parse("bar:1.2.4"))) + .as("foo(bar:1.2.3) != bar:1.2.4") + .isFalse(); } @Test diff --git a/core/src/test/java/org/testcontainers/utility/DockerImageNameTest.java b/core/src/test/java/org/testcontainers/utility/DockerImageNameTest.java index 98fd21064ed..b1a0cbc980f 100644 --- a/core/src/test/java/org/testcontainers/utility/DockerImageNameTest.java +++ b/core/src/test/java/org/testcontainers/utility/DockerImageNameTest.java @@ -4,9 +4,8 @@ import org.junit.experimental.runners.Enclosed; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; -import org.rnorth.visibleassertions.VisibleAssertions; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; @RunWith(Enclosed.class) public class DockerImageNameTest { @@ -122,50 +121,35 @@ public void testParsing() { canonicalName = unversionedPart + ":latest"; } - VisibleAssertions.context("For " + combined); - VisibleAssertions.context("Using single-arg constructor:", 2); - final DockerImageName imageName = DockerImageName.parse(combined); - assertEquals(combined + " has registry address: " + registry, registry, imageName.getRegistry()); - assertEquals( - combined + " has unversioned part: " + unversionedPart, - unversionedPart, - imageName.getUnversionedPart() - ); + assertThat(imageName.getRegistry()).as(combined + " has registry address: " + registry).isEqualTo(registry); + assertThat(imageName.getUnversionedPart()) + .as(combined + " has unversioned part: " + unversionedPart) + .isEqualTo(unversionedPart); if (version != null) { - assertEquals(combined + " has version part: " + version, version, imageName.getVersionPart()); + assertThat(imageName.getVersionPart()) + .as(combined + " has version part: " + version) + .isEqualTo(version); } else { - assertEquals( - combined + " has automatic 'latest' version specified", - "latest", - imageName.getVersionPart() - ); + assertThat(imageName.getVersionPart()) + .as(combined + " has automatic 'latest' version specified") + .isEqualTo("latest"); } - assertEquals( - combined + " has canonical name: " + canonicalName, - canonicalName, - imageName.asCanonicalNameString() - ); + assertThat(imageName.asCanonicalNameString()) + .as(combined + " has canonical name: " + canonicalName) + .isEqualTo(canonicalName); if (version != null) { - VisibleAssertions.context("Using two-arg constructor:", 2); - final DockerImageName imageNameFromSecondaryConstructor = new DockerImageName(unversionedPart, version); - assertEquals( - combined + " has registry address: " + registry, - registry, - imageNameFromSecondaryConstructor.getRegistry() - ); - assertEquals( - combined + " has unversioned part: " + unversionedPart, - unversionedPart, - imageNameFromSecondaryConstructor.getUnversionedPart() - ); - assertEquals( - combined + " has version part: " + version, - version, - imageNameFromSecondaryConstructor.getVersionPart() - ); + assertThat(imageNameFromSecondaryConstructor.getRegistry()) + .as(combined + " has registry address: " + registry) + .isEqualTo(registry); + assertThat(imageNameFromSecondaryConstructor.getUnversionedPart()) + .as(combined + " has unversioned part: " + unversionedPart) + .isEqualTo(unversionedPart); + assertThat(imageNameFromSecondaryConstructor.getVersionPart()) + .as(combined + " has version part: " + version) + .isEqualTo(version); } } } diff --git a/core/src/test/java/org/testcontainers/utility/ImageNameSubstitutorTest.java b/core/src/test/java/org/testcontainers/utility/ImageNameSubstitutorTest.java index f7e319a5948..5c65d9e5841 100644 --- a/core/src/test/java/org/testcontainers/utility/ImageNameSubstitutorTest.java +++ b/core/src/test/java/org/testcontainers/utility/ImageNameSubstitutorTest.java @@ -6,8 +6,8 @@ import org.junit.Test; import org.mockito.Mockito; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.eq; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; public class ImageNameSubstitutorTest { @@ -48,11 +48,9 @@ public void simpleConfigurationTest() { final ImageNameSubstitutor imageNameSubstitutor = ImageNameSubstitutor.instance(); DockerImageName result = imageNameSubstitutor.apply(DockerImageName.parse("original")); - assertEquals( - "the image has been substituted by default then configured implementations", - "transformed-substituted-image:latest", - result.asCanonicalNameString() - ); + assertThat(result.asCanonicalNameString()) + .as("the image has been substituted by default then configured implementations") + .isEqualTo("transformed-substituted-image:latest"); } @Test @@ -62,10 +60,8 @@ public void testWorksWithoutConfiguredImplementation() { final ImageNameSubstitutor imageNameSubstitutor = ImageNameSubstitutor.instance(); DockerImageName result = imageNameSubstitutor.apply(DockerImageName.parse("original")); - assertEquals( - "the image has been substituted by default then configured implementations", - "substituted-image:latest", - result.asCanonicalNameString() - ); + assertThat(result.asCanonicalNameString()) + .as("the image has been substituted by default then configured implementations") + .isEqualTo("substituted-image:latest"); } } diff --git a/core/src/test/java/org/testcontainers/utility/LazyFutureTest.java b/core/src/test/java/org/testcontainers/utility/LazyFutureTest.java index 99443221805..8631bf6de19 100644 --- a/core/src/test/java/org/testcontainers/utility/LazyFutureTest.java +++ b/core/src/test/java/org/testcontainers/utility/LazyFutureTest.java @@ -11,7 +11,8 @@ import java.util.stream.Collectors; import java.util.stream.IntStream; -import static org.rnorth.visibleassertions.VisibleAssertions.*; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; public class LazyFutureTest { @@ -26,16 +27,16 @@ protected Integer resolve() { } }; - assertEquals("No resolve() invocations before get()", 0, counter.get()); - assertEquals("get() call returns proper result", 1, lazyFuture.get()); - assertEquals("resolve() was called only once after single get() call", 1, counter.get()); + assertThat(counter).as("No resolve() invocations before get()").hasValue(0); + assertThat(lazyFuture.get()).as("get() call returns proper result").isEqualTo(1); + assertThat(counter).as("resolve() was called only once after single get() call").hasValue(1); counter.incrementAndGet(); - assertEquals("result of resolve() must be cached", 1, lazyFuture.get()); + assertThat(lazyFuture.get()).as("result of resolve() must be cached").isEqualTo(1); } @Test(timeout = 5_000) - public void timeoutWorks() throws Exception { + public void timeoutWorks() { Future lazyFuture = new LazyFuture() { @Override @SneakyThrows(InterruptedException.class) @@ -45,8 +46,9 @@ protected Void resolve() { } }; - assertThrows("Should timeout", TimeoutException.class, () -> lazyFuture.get(10, TimeUnit.MILLISECONDS)); - pass("timeout works"); + assertThat(catchThrowable(() -> lazyFuture.get(10, TimeUnit.MILLISECONDS))) + .as("Should timeout") + .isInstanceOf(TimeoutException.class); } @Test(timeout = 5_000) @@ -77,6 +79,8 @@ protected Integer resolve() { latch.countDown(); } - assertEquals("All threads receives the same result", Collections.nCopies(numOfThreads, 1), task.get()); + assertThat(task.get()) + .as("All threads receives the same result") + .isEqualTo(Collections.nCopies(numOfThreads, 1)); } } diff --git a/core/src/test/java/org/testcontainers/utility/MountableFileTest.java b/core/src/test/java/org/testcontainers/utility/MountableFileTest.java index d3ff085c5de..fbfb537ef87 100644 --- a/core/src/test/java/org/testcontainers/utility/MountableFileTest.java +++ b/core/src/test/java/org/testcontainers/utility/MountableFileTest.java @@ -15,7 +15,7 @@ import java.nio.file.Path; import java.util.function.Consumer; -import static org.rnorth.visibleassertions.VisibleAssertions.*; +import static org.assertj.core.api.Assertions.assertThat; public class MountableFileTest { @@ -68,11 +68,10 @@ public void forHostPathWithSpaces() throws Exception { performChecks(mountableFile); - assertTrue("The resolved path contains the original space", mountableFile.getResolvedPath().contains(" ")); - assertFalse( - "The resolved path does not contain an escaped space", - mountableFile.getResolvedPath().contains("\\ ") - ); + assertThat(mountableFile.getResolvedPath()).as("The resolved path contains the original space").contains(" "); + assertThat(mountableFile.getResolvedPath()) + .as("The resolved path does not contain an escaped space") + .doesNotContain("\\ "); } @Test @@ -82,11 +81,10 @@ public void forHostPathWithPlus() throws Exception { performChecks(mountableFile); - assertTrue("The resolved path contains the original space", mountableFile.getResolvedPath().contains("+")); - assertFalse( - "The resolved path does not contain an escaped space", - mountableFile.getResolvedPath().contains(" ") - ); + assertThat(mountableFile.getResolvedPath()).as("The resolved path contains the original space").contains("+"); + assertThat(mountableFile.getResolvedPath()) + .as("The resolved path does not contain an escaped space") + .doesNotContain(" "); } @Test @@ -97,7 +95,7 @@ public void forClasspathResourceWithPermission() throws Exception { ); performChecks(mountableFile); - assertEquals("Valid file mode.", BASE_FILE_MODE | TEST_FILE_MODE, mountableFile.getFileMode()); + assertThat(mountableFile.getFileMode()).as("Valid file mode.").isEqualTo(BASE_FILE_MODE | TEST_FILE_MODE); } @Test @@ -105,7 +103,7 @@ public void forHostFilePathWithPermission() throws Exception { final Path file = createTempFile("somepath"); final MountableFile mountableFile = MountableFile.forHostPath(file.toString(), TEST_FILE_MODE); performChecks(mountableFile); - assertEquals("Valid file mode.", BASE_FILE_MODE | TEST_FILE_MODE, mountableFile.getFileMode()); + assertThat(mountableFile.getFileMode()).as("Valid file mode.").isEqualTo(BASE_FILE_MODE | TEST_FILE_MODE); } @Test @@ -113,7 +111,7 @@ public void forHostDirPathWithPermission() throws Exception { final Path dir = createTempDir(); final MountableFile mountableFile = MountableFile.forHostPath(dir.toString(), TEST_FILE_MODE); performChecks(mountableFile); - assertEquals("Valid dir mode.", BASE_DIR_MODE | TEST_FILE_MODE, mountableFile.getFileMode()); + assertThat(mountableFile.getFileMode()).as("Valid dir mode.").isEqualTo(BASE_DIR_MODE | TEST_FILE_MODE); } @Test @@ -129,7 +127,7 @@ public void noTrailingSlashesInTarEntryNames() throws Exception { ArchiveEntry entry; while ((entry = tais.getNextEntry()) != null) { - assertFalse("no entries should have a trailing slash", entry.getName().endsWith("/")); + assertThat(entry.getName()).as("no entries should have a trailing slash").doesNotEndWith("/"); } } @@ -163,10 +161,9 @@ private Path createTempDir() throws IOException { private void performChecks(final MountableFile mountableFile) { final String mountablePath = mountableFile.getResolvedPath(); - assertTrue("The filesystem path '" + mountablePath + "' can be found", new File(mountablePath).exists()); - assertFalse( - "The filesystem path '" + mountablePath + "' does not contain any URL escaping", - mountablePath.contains("%20") - ); + assertThat(new File(mountablePath)).as("The filesystem path '" + mountablePath + "' can be found").exists(); + assertThat(mountablePath) + .as("The filesystem path '" + mountablePath + "' does not contain any URL escaping") + .doesNotContain("%20"); } } diff --git a/core/src/test/java/org/testcontainers/utility/PrefixingImageNameSubstitutorTest.java b/core/src/test/java/org/testcontainers/utility/PrefixingImageNameSubstitutorTest.java index 211f8e00536..04786119e36 100644 --- a/core/src/test/java/org/testcontainers/utility/PrefixingImageNameSubstitutorTest.java +++ b/core/src/test/java/org/testcontainers/utility/PrefixingImageNameSubstitutorTest.java @@ -3,11 +3,11 @@ import org.junit.Before; import org.junit.Test; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; public class PrefixingImageNameSubstitutorTest { @@ -28,11 +28,9 @@ public void testHappyPath() { final DockerImageName result = underTest.apply(DockerImageName.parse("some/image:tag")); - assertEquals( - "The prefix is applied", - "someregistry.com/our-mirror/some/image:tag", - result.asCanonicalNameString() - ); + assertThat(result.asCanonicalNameString()) + .as("The prefix is applied") + .isEqualTo("someregistry.com/our-mirror/some/image:tag"); } @Test @@ -42,7 +40,7 @@ public void hubIoRegistryIsNotChanged() { final DockerImageName result = underTest.apply(DockerImageName.parse("docker.io/some/image:tag")); - assertEquals("The prefix is applied", "docker.io/some/image:tag", result.asCanonicalNameString()); + assertThat(result.asCanonicalNameString()).as("The prefix is applied").isEqualTo("docker.io/some/image:tag"); } @Test @@ -52,7 +50,9 @@ public void hubComRegistryIsNotChanged() { final DockerImageName result = underTest.apply(DockerImageName.parse("registry.hub.docker.com/some/image:tag")); - assertEquals("The prefix is applied", "registry.hub.docker.com/some/image:tag", result.asCanonicalNameString()); + assertThat(result.asCanonicalNameString()) + .as("The prefix is applied") + .isEqualTo("registry.hub.docker.com/some/image:tag"); } @Test @@ -62,11 +62,9 @@ public void thirdPartyRegistriesNotAffected() { final DockerImageName result = underTest.apply(DockerImageName.parse("gcr.io/something/image:tag")); - assertEquals( - "The prefix is not applied if a third party registry is used", - "gcr.io/something/image:tag", - result.asCanonicalNameString() - ); + assertThat(result.asCanonicalNameString()) + .as("The prefix is not applied if a third party registry is used") + .isEqualTo("gcr.io/something/image:tag"); } @Test @@ -76,11 +74,9 @@ public void testNoDoublePrefixing() { final DockerImageName result = underTest.apply(DockerImageName.parse("someregistry.com/some/image:tag")); - assertEquals( - "The prefix is not applied if already present", - "someregistry.com/some/image:tag", - result.asCanonicalNameString() - ); + assertThat(result.asCanonicalNameString()) + .as("The prefix is not applied if already present") + .isEqualTo("someregistry.com/some/image:tag"); } @Test @@ -90,11 +86,9 @@ public void testHandlesEmptyValue() { final DockerImageName result = underTest.apply(DockerImageName.parse("some/image:tag")); - assertEquals( - "The prefix is not applied if the env var is not set", - "some/image:tag", - result.asCanonicalNameString() - ); + assertThat(result.asCanonicalNameString()) + .as("The prefix is not applied if the env var is not set") + .isEqualTo("some/image:tag"); } @Test @@ -104,7 +98,9 @@ public void testHandlesRegistryOnlyWithTrailingSlash() { final DockerImageName result = underTest.apply(DockerImageName.parse("some/image:tag")); - assertEquals("The prefix is applied", "someregistry.com/some/image:tag", result.asCanonicalNameString()); + assertThat(result.asCanonicalNameString()) + .as("The prefix is applied") + .isEqualTo("someregistry.com/some/image:tag"); } @Test @@ -114,11 +110,9 @@ public void testCombinesLiterallyForRegistryOnlyWithoutTrailingSlash() { final DockerImageName result = underTest.apply(DockerImageName.parse("some/image:tag")); - assertEquals( - "The prefix is applied", - "someregistry.comsome/image:tag", // treating the prefix literally, for predictability - result.asCanonicalNameString() - ); + assertThat(result.asCanonicalNameString()) + .as("The prefix is applied") + .isEqualTo("someregistry.comsome/image:tag"); } @Test @@ -128,10 +122,8 @@ public void testCombinesLiterallyForBothPartsWithoutTrailingSlash() { final DockerImageName result = underTest.apply(DockerImageName.parse("some/image:tag")); - assertEquals( - "The prefix is applied", - "someregistry.com/our-mirrorsome/image:tag", // treating the prefix literally, for predictability - result.asCanonicalNameString() - ); + assertThat(result.asCanonicalNameString()) + .as("The prefix is applied") + .isEqualTo("someregistry.com/our-mirrorsome/image:tag"); } } diff --git a/core/src/test/java/org/testcontainers/utility/RegistryAuthLocatorTest.java b/core/src/test/java/org/testcontainers/utility/RegistryAuthLocatorTest.java index 4ddfad8454f..fea7f93ed5a 100644 --- a/core/src/test/java/org/testcontainers/utility/RegistryAuthLocatorTest.java +++ b/core/src/test/java/org/testcontainers/utility/RegistryAuthLocatorTest.java @@ -11,7 +11,7 @@ import java.util.HashMap; import java.util.Map; -import static org.rnorth.visibleassertions.VisibleAssertions.*; +import static org.assertj.core.api.Assertions.assertThat; public class RegistryAuthLocatorTest { @@ -24,13 +24,11 @@ public void lookupAuthConfigWithoutCredentials() throws URISyntaxException { new AuthConfig() ); - assertEquals( - "Default docker registry URL is set on auth config", - "https://index.docker.io/v1/", - authConfig.getRegistryAddress() - ); - assertNull("No username is set", authConfig.getUsername()); - assertNull("No password is set", authConfig.getPassword()); + assertThat(authConfig.getRegistryAddress()) + .as("Default docker registry URL is set on auth config") + .isEqualTo("https://index.docker.io/v1/"); + assertThat(authConfig.getUsername()).as("No username is set").isNull(); + assertThat(authConfig.getPassword()).as("No password is set").isNull(); } @Test @@ -42,13 +40,11 @@ public void lookupAuthConfigWithBasicAuthCredentials() throws URISyntaxException new AuthConfig() ); - assertEquals( - "Default docker registry URL is set on auth config", - "https://registry.example.com", - authConfig.getRegistryAddress() - ); - assertEquals("Username is set", "user", authConfig.getUsername()); - assertEquals("Password is set", "pass", authConfig.getPassword()); + assertThat(authConfig.getRegistryAddress()) + .as("Default docker registry URL is set on auth config") + .isEqualTo("https://registry.example.com"); + assertThat(authConfig.getUsername()).as("Username is set").isEqualTo("user"); + assertThat(authConfig.getPassword()).as("Password is set").isEqualTo("pass"); } @Test @@ -60,13 +56,11 @@ public void lookupAuthConfigWithJsonKeyCredentials() throws URISyntaxException { new AuthConfig() ); - assertEquals( - "Default docker registry URL is set on auth config", - "https://registry.example.com", - authConfig.getRegistryAddress() - ); - assertEquals("Username is set", "_json_key", authConfig.getUsername()); - assertNotNull("Password is set", authConfig.getPassword()); + assertThat(authConfig.getRegistryAddress()) + .as("Default docker registry URL is set on auth config") + .isEqualTo("https://registry.example.com"); + assertThat(authConfig.getUsername()).as("Username is set").isEqualTo("_json_key"); + assertThat(authConfig.getPassword()).as("Password is set").isNotNull(); } @Test @@ -78,9 +72,15 @@ public void lookupAuthConfigUsingStore() throws URISyntaxException { new AuthConfig() ); - assertEquals("Correct server URL is obtained from a credential store", "url", authConfig.getRegistryAddress()); - assertEquals("Correct username is obtained from a credential store", "username", authConfig.getUsername()); - assertEquals("Correct secret is obtained from a credential store", "secret", authConfig.getPassword()); + assertThat(authConfig.getRegistryAddress()) + .as("Correct server URL is obtained from a credential store") + .isEqualTo("url"); + assertThat(authConfig.getUsername()) + .as("Correct username is obtained from a credential store") + .isEqualTo("username"); + assertThat(authConfig.getPassword()) + .as("Correct secret is obtained from a credential store") + .isEqualTo("secret"); } @Test @@ -92,9 +92,15 @@ public void lookupAuthConfigUsingHelper() throws URISyntaxException { new AuthConfig() ); - assertEquals("Correct server URL is obtained from a credential store", "url", authConfig.getRegistryAddress()); - assertEquals("Correct username is obtained from a credential store", "username", authConfig.getUsername()); - assertEquals("Correct secret is obtained from a credential store", "secret", authConfig.getPassword()); + assertThat(authConfig.getRegistryAddress()) + .as("Correct server URL is obtained from a credential store") + .isEqualTo("url"); + assertThat(authConfig.getUsername()) + .as("Correct username is obtained from a credential store") + .isEqualTo("username"); + assertThat(authConfig.getPassword()) + .as("Correct secret is obtained from a credential store") + .isEqualTo("secret"); } @Test @@ -106,11 +112,9 @@ public void lookupAuthConfigUsingHelperWithToken() throws URISyntaxException { new AuthConfig() ); - assertEquals( - "Correct identitytoken is obtained from a credential store", - "secret", - authConfig.getIdentitytoken() - ); + assertThat(authConfig.getIdentitytoken()) + .as("Correct identitytoken is obtained from a credential store") + .isEqualTo("secret"); } @Test @@ -122,9 +126,15 @@ public void lookupUsingHelperEmptyAuth() throws URISyntaxException { new AuthConfig() ); - assertEquals("Correct server URL is obtained from a credential store", "url", authConfig.getRegistryAddress()); - assertEquals("Correct username is obtained from a credential store", "username", authConfig.getUsername()); - assertEquals("Correct secret is obtained from a credential store", "secret", authConfig.getPassword()); + assertThat(authConfig.getRegistryAddress()) + .as("Correct server URL is obtained from a credential store") + .isEqualTo("url"); + assertThat(authConfig.getUsername()) + .as("Correct username is obtained from a credential store") + .isEqualTo("username"); + assertThat(authConfig.getPassword()) + .as("Correct secret is obtained from a credential store") + .isEqualTo("secret"); } @Test @@ -136,9 +146,15 @@ public void lookupNonEmptyAuthWithHelper() throws URISyntaxException { new AuthConfig() ); - assertEquals("Correct server URL is obtained from a credential helper", "url", authConfig.getRegistryAddress()); - assertEquals("Correct username is obtained from a credential helper", "username", authConfig.getUsername()); - assertEquals("Correct password is obtained from a credential helper", "secret", authConfig.getPassword()); + assertThat(authConfig.getRegistryAddress()) + .as("Correct server URL is obtained from a credential helper") + .isEqualTo("url"); + assertThat(authConfig.getUsername()) + .as("Correct username is obtained from a credential helper") + .isEqualTo("username"); + assertThat(authConfig.getPassword()) + .as("Correct password is obtained from a credential helper") + .isEqualTo("secret"); } @Test @@ -152,17 +168,19 @@ public void lookupAuthConfigWithCredentialsNotFound() throws URISyntaxException DockerImageName dockerImageName = DockerImageName.parse("registry2.example.com/org/repo"); final AuthConfig authConfig = authLocator.lookupAuthConfig(dockerImageName, new AuthConfig()); - assertNull("No username should have been obtained from a credential store", authConfig.getUsername()); - assertNull("No secret should have been obtained from a credential store", authConfig.getPassword()); - assertEquals("Should have one 'credentials not found' message discovered", 1, notFoundMessagesReference.size()); + assertThat(authConfig.getUsername()) + .as("No username should have been obtained from a credential store") + .isNull(); + assertThat(authConfig.getPassword()).as("No secret should have been obtained from a credential store").isNull(); + assertThat(notFoundMessagesReference.size()) + .as("Should have one 'credentials not found' message discovered") + .isEqualTo(1); String discoveredMessage = notFoundMessagesReference.values().iterator().next(); - assertEquals( - "Not correct message discovered", - "Fake credentials not found on credentials store 'https://not.a.real.registry/url'", - discoveredMessage - ); + assertThat(discoveredMessage) + .as("Not correct message discovered") + .isEqualTo("Fake credentials not found on credentials store 'https://not.a.real.registry/url'"); } @Test @@ -172,7 +190,7 @@ public void lookupAuthConfigWithCredStoreEmpty() throws URISyntaxException { DockerImageName dockerImageName = DockerImageName.parse("registry2.example.com/org/repo"); final AuthConfig authConfig = authLocator.lookupAuthConfig(dockerImageName, new AuthConfig()); - assertNull("CredStore field will be ignored, because value is blank", authConfig.getAuth()); + assertThat(authConfig.getAuth()).as("CredStore field will be ignored, because value is blank").isNull(); } @NotNull diff --git a/core/src/test/java/org/testcontainers/utility/TestEnvironmentTest.java b/core/src/test/java/org/testcontainers/utility/TestEnvironmentTest.java index de97d100a39..4bd9e1b8edb 100644 --- a/core/src/test/java/org/testcontainers/utility/TestEnvironmentTest.java +++ b/core/src/test/java/org/testcontainers/utility/TestEnvironmentTest.java @@ -2,7 +2,7 @@ import org.junit.Test; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; /** * Created by rnorth on 03/07/2016. @@ -11,21 +11,29 @@ public class TestEnvironmentTest { @Test public void testCompareVersionGreaterThanSameMajor() { - assertTrue("1.22 > 1.20", new ComparableVersion("1.22").compareTo(new ComparableVersion("1.20")) > 0); + assertThat(new ComparableVersion("1.22").compareTo(new ComparableVersion("1.20")) > 0) + .as("1.22 > 1.20") + .isTrue(); } @Test public void testCompareVersionEqual() { - assertTrue("1.20 == 1.20", new ComparableVersion("1.20").compareTo(new ComparableVersion("1.20")) == 0); + assertThat(new ComparableVersion("1.20")) + .as("1.20 == 1.20") + .isEqualByComparingTo(new ComparableVersion("1.20")); } @Test public void testCompareVersionGreaterThan() { - assertTrue("2.10 > 1.20", new ComparableVersion("2.10").compareTo(new ComparableVersion("1.20")) > 0); + assertThat(new ComparableVersion("2.10").compareTo(new ComparableVersion("1.20")) > 0) + .as("2.10 > 1.20") + .isTrue(); } @Test public void testCompareVersionIgnoresExcessLength() { - assertTrue("1.20 == 1.20.3", new ComparableVersion("1.20").compareTo(new ComparableVersion("1.20.3")) == 0); + assertThat(new ComparableVersion("1.20")) + .as("1.20 == 1.20.3") + .isEqualByComparingTo(new ComparableVersion("1.20.3")); } } diff --git a/core/src/test/java/org/testcontainers/utility/TestcontainersConfigurationTest.java b/core/src/test/java/org/testcontainers/utility/TestcontainersConfigurationTest.java index 56cbdb98a56..d51bc89348c 100644 --- a/core/src/test/java/org/testcontainers/utility/TestcontainersConfigurationTest.java +++ b/core/src/test/java/org/testcontainers/utility/TestcontainersConfigurationTest.java @@ -8,9 +8,7 @@ import java.util.Properties; import java.util.UUID; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.rnorth.visibleassertions.VisibleAssertions.assertFalse; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; public class TestcontainersConfigurationTest { @@ -30,122 +28,102 @@ public void setUp() { @Test public void shouldSubstituteImageNamesFromClasspathProperties() { classpathProperties.setProperty("ryuk.container.image", "foo:version"); - assertEquals( - "an image name can be pulled from classpath properties", - DockerImageName.parse("foo:version"), - newConfig().getConfiguredSubstituteImage(DockerImageName.parse("testcontainers/ryuk:any")) - ); + assertThat(newConfig().getConfiguredSubstituteImage(DockerImageName.parse("testcontainers/ryuk:any"))) + .as("an image name can be pulled from classpath properties") + .isEqualTo(DockerImageName.parse("foo:version")); } @Test public void shouldSubstituteImageNamesFromUserProperties() { userProperties.setProperty("ryuk.container.image", "foo:version"); - assertEquals( - "an image name can be pulled from user properties", - DockerImageName.parse("foo:version"), - newConfig().getConfiguredSubstituteImage(DockerImageName.parse("testcontainers/ryuk:any")) - ); + assertThat(newConfig().getConfiguredSubstituteImage(DockerImageName.parse("testcontainers/ryuk:any"))) + .as("an image name can be pulled from user properties") + .isEqualTo(DockerImageName.parse("foo:version")); } @Test public void shouldSubstituteImageNamesFromEnvironmentVariables() { environment.put("TESTCONTAINERS_RYUK_CONTAINER_IMAGE", "foo:version"); - assertEquals( - "an image name can be pulled from an environment variable", - DockerImageName.parse("foo:version"), - newConfig().getConfiguredSubstituteImage(DockerImageName.parse("testcontainers/ryuk:any")) - ); + assertThat(newConfig().getConfiguredSubstituteImage(DockerImageName.parse("testcontainers/ryuk:any"))) + .as("an image name can be pulled from an environment variable") + .isEqualTo(DockerImageName.parse("foo:version")); } @Test public void shouldApplySettingsInOrder() { - assertEquals( - "precedence order for multiple sources of the same value is correct", - "default", - newConfig().getEnvVarOrProperty("key", "default") - ); + assertThat(newConfig().getEnvVarOrProperty("key", "default")) + .as("precedence order for multiple sources of the same value is correct") + .isEqualTo("default"); classpathProperties.setProperty("key", "foo"); - assertEquals( - "precedence order for multiple sources of the same value is correct", - "foo", - newConfig().getEnvVarOrProperty("key", "default") - ); + assertThat(newConfig().getEnvVarOrProperty("key", "default")) + .as("precedence order for multiple sources of the same value is correct") + .isEqualTo("foo"); userProperties.setProperty("key", "bar"); - assertEquals( - "precedence order for multiple sources of the same value is correct", - "bar", - newConfig().getEnvVarOrProperty("key", "default") - ); + assertThat(newConfig().getEnvVarOrProperty("key", "default")) + .as("precedence order for multiple sources of the same value is correct") + .isEqualTo("bar"); environment.put("TESTCONTAINERS_KEY", "baz"); - assertEquals( - "precedence order for multiple sources of the same value is correct", - "baz", - newConfig().getEnvVarOrProperty("key", "default") - ); + assertThat(newConfig().getEnvVarOrProperty("key", "default")) + .as("precedence order for multiple sources of the same value is correct") + .isEqualTo("baz"); } @Test public void shouldNotReadChecksFromClasspathProperties() { - assertFalse("checks enabled by default", newConfig().isDisableChecks()); + assertThat(newConfig().isDisableChecks()).as("checks enabled by default").isFalse(); classpathProperties.setProperty("checks.disable", "true"); - assertFalse("checks are not affected by classpath properties", newConfig().isDisableChecks()); + assertThat(newConfig().isDisableChecks()).as("checks are not affected by classpath properties").isFalse(); } @Test public void shouldReadChecksFromUserProperties() { - assertFalse("checks enabled by default", newConfig().isDisableChecks()); + assertThat(newConfig().isDisableChecks()).as("checks enabled by default").isFalse(); userProperties.setProperty("checks.disable", "true"); - assertTrue("checks disabled via user properties", newConfig().isDisableChecks()); + assertThat(newConfig().isDisableChecks()).as("checks disabled via user properties").isTrue(); } @Test public void shouldReadChecksFromEnvironment() { - assertFalse("checks enabled by default", newConfig().isDisableChecks()); + assertThat(newConfig().isDisableChecks()).as("checks enabled by default").isFalse(); userProperties.remove("checks.disable"); environment.put("TESTCONTAINERS_CHECKS_DISABLE", "true"); - assertTrue("checks disabled via env var", newConfig().isDisableChecks()); + assertThat(newConfig().isDisableChecks()).as("checks disabled via env var").isTrue(); } @Test public void shouldReadDockerSettingsFromEnvironmentWithoutTestcontainersPrefix() { userProperties.remove("docker.foo"); environment.put("DOCKER_FOO", "some value"); - assertEquals( - "reads unprefixed env vars for docker. settings", - "some value", - newConfig().getEnvVarOrUserProperty("docker.foo", "default") - ); + assertThat(newConfig().getEnvVarOrUserProperty("docker.foo", "default")) + .as("reads unprefixed env vars for docker. settings") + .isEqualTo("some value"); } @Test public void shouldNotReadDockerSettingsFromEnvironmentWithTestcontainersPrefix() { userProperties.remove("docker.foo"); environment.put("TESTCONTAINERS_DOCKER_FOO", "some value"); - assertEquals( - "reads unprefixed env vars for docker. settings", - "default", - newConfig().getEnvVarOrUserProperty("docker.foo", "default") - ); + assertThat(newConfig().getEnvVarOrUserProperty("docker.foo", "default")) + .as("reads unprefixed env vars for docker. settings") + .isEqualTo("default"); } @Test public void shouldReadDockerSettingsFromUserProperties() { environment.remove("DOCKER_FOO"); userProperties.put("docker.foo", "some value"); - assertEquals( - "reads unprefixed user properties for docker. settings", - "some value", - newConfig().getEnvVarOrUserProperty("docker.foo", "default") - ); + assertThat(newConfig().getEnvVarOrUserProperty("docker.foo", "default")) + .as("reads unprefixed user properties for docker. settings") + .isEqualTo("some value"); } @Test @@ -153,104 +131,90 @@ public void shouldNotReadDockerClientStrategyFromClasspathProperties() { String currentValue = newConfig().getDockerClientStrategyClassName(); classpathProperties.setProperty("docker.client.strategy", UUID.randomUUID().toString()); - assertEquals( - "Docker client strategy is not affected by classpath properties", - currentValue, - newConfig().getDockerClientStrategyClassName() - ); + assertThat(newConfig().getDockerClientStrategyClassName()) + .as("Docker client strategy is not affected by classpath properties") + .isEqualTo(currentValue); } @Test public void shouldReadDockerClientStrategyFromUserProperties() { userProperties.setProperty("docker.client.strategy", "foo"); - assertEquals( - "Docker client strategy is changed by user property", - "foo", - newConfig().getDockerClientStrategyClassName() - ); + assertThat(newConfig().getDockerClientStrategyClassName()) + .as("Docker client strategy is changed by user property") + .isEqualTo("foo"); } @Test public void shouldReadDockerClientStrategyFromEnvironment() { userProperties.remove("docker.client.strategy"); environment.put("TESTCONTAINERS_DOCKER_CLIENT_STRATEGY", "foo"); - assertEquals( - "Docker client strategy is changed by env var", - "foo", - newConfig().getDockerClientStrategyClassName() - ); + assertThat(newConfig().getDockerClientStrategyClassName()) + .as("Docker client strategy is changed by env var") + .isEqualTo("foo"); } @Test public void shouldNotUseImplicitDockerClientStrategyWhenDockerHostAndStrategyAreBothSet() { userProperties.put("docker.client.strategy", "foo"); userProperties.put("docker.host", "tcp://1.2.3.4:5678"); - assertEquals( - "Docker client strategy is can be explicitly set", - "foo", - newConfig().getDockerClientStrategyClassName() - ); + assertThat(newConfig().getDockerClientStrategyClassName()) + .as("Docker client strategy is can be explicitly set") + .isEqualTo("foo"); userProperties.remove("docker.client.strategy"); environment.put("TESTCONTAINERS_DOCKER_CLIENT_STRATEGY", "bar"); userProperties.put("docker.client.strategy", "foo"); - assertEquals( - "Docker client strategy is can be explicitly set", - "bar", - newConfig().getDockerClientStrategyClassName() - ); + assertThat(newConfig().getDockerClientStrategyClassName()) + .as("Docker client strategy is can be explicitly set") + .isEqualTo("bar"); environment.put("TESTCONTAINERS_DOCKER_CLIENT_STRATEGY", "bar"); userProperties.remove("docker.client.strategy"); - assertEquals( - "Docker client strategy is can be explicitly set", - "bar", - newConfig().getDockerClientStrategyClassName() - ); + assertThat(newConfig().getDockerClientStrategyClassName()) + .as("Docker client strategy is can be explicitly set") + .isEqualTo("bar"); environment.remove("TESTCONTAINERS_DOCKER_CLIENT_STRATEGY"); userProperties.put("docker.client.strategy", "foo"); - assertEquals( - "Docker client strategy is can be explicitly set", - "foo", - newConfig().getDockerClientStrategyClassName() - ); + assertThat(newConfig().getDockerClientStrategyClassName()) + .as("Docker client strategy is can be explicitly set") + .isEqualTo("foo"); } @Test public void shouldNotReadReuseFromClasspathProperties() { - assertFalse("no reuse by default", newConfig().environmentSupportsReuse()); + assertThat(newConfig().environmentSupportsReuse()).as("no reuse by default").isFalse(); classpathProperties.setProperty("testcontainers.reuse.enable", "true"); - assertFalse("reuse is not affected by classpath properties", newConfig().environmentSupportsReuse()); + assertThat(newConfig().environmentSupportsReuse()) + .as("reuse is not affected by classpath properties") + .isFalse(); } @Test public void shouldReadReuseFromUserProperties() { - assertFalse("no reuse by default", newConfig().environmentSupportsReuse()); + assertThat(newConfig().environmentSupportsReuse()).as("no reuse by default").isFalse(); userProperties.setProperty("testcontainers.reuse.enable", "true"); - assertTrue("reuse enabled via user property", newConfig().environmentSupportsReuse()); + assertThat(newConfig().environmentSupportsReuse()).as("reuse enabled via user property").isTrue(); } @Test public void shouldReadReuseFromEnvironment() { - assertFalse("no reuse by default", newConfig().environmentSupportsReuse()); + assertThat(newConfig().environmentSupportsReuse()).as("no reuse by default").isFalse(); userProperties.remove("testcontainers.reuse.enable"); environment.put("TESTCONTAINERS_REUSE_ENABLE", "true"); - assertTrue("reuse enabled via env var", newConfig().environmentSupportsReuse()); + assertThat(newConfig().environmentSupportsReuse()).as("reuse enabled via env var").isTrue(); } @Test public void shouldTrimImageNames() { userProperties.setProperty("ryuk.container.image", " testcontainers/ryuk:0.3.2 "); - assertEquals( - "trailing whitespace was not removed from image name property", - "testcontainers/ryuk:0.3.2", - newConfig().getRyukImage() - ); + assertThat(newConfig().getRyukImage()) + .as("trailing whitespace was not removed from image name property") + .isEqualTo("testcontainers/ryuk:0.3.2"); } private TestcontainersConfiguration newConfig() { diff --git a/docs/examples/junit5/redis/build.gradle b/docs/examples/junit5/redis/build.gradle index 16f0d5bc598..72b943adc23 100644 --- a/docs/examples/junit5/redis/build.gradle +++ b/docs/examples/junit5/redis/build.gradle @@ -8,6 +8,7 @@ dependencies { testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:5.4.2" testImplementation project(":testcontainers") testImplementation project(":junit-jupiter") + testImplementation 'org.assertj:assertj-core:3.23.1' } test { diff --git a/docs/examples/junit5/redis/src/test/java/quickstart/RedisBackedCacheIntTest.java b/docs/examples/junit5/redis/src/test/java/quickstart/RedisBackedCacheIntTest.java index 59d92210549..2c5f4baef67 100644 --- a/docs/examples/junit5/redis/src/test/java/quickstart/RedisBackedCacheIntTest.java +++ b/docs/examples/junit5/redis/src/test/java/quickstart/RedisBackedCacheIntTest.java @@ -7,7 +7,7 @@ import org.testcontainers.junit.jupiter.Testcontainers; import org.testcontainers.utility.DockerImageName; -import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; // class { @Testcontainers @@ -36,7 +36,7 @@ public void testSimplePutAndGet() { underTest.put("test", "example"); String retrieved = underTest.get("test"); - assertEquals("example", retrieved); + assertThat(retrieved).isEqualTo("example"); } } // } diff --git a/docs/examples/junit5/redis/src/test/java/quickstart/RedisBackedCacheIntTestStep0.java b/docs/examples/junit5/redis/src/test/java/quickstart/RedisBackedCacheIntTestStep0.java index d9f889a8943..e1f3c3efbe8 100644 --- a/docs/examples/junit5/redis/src/test/java/quickstart/RedisBackedCacheIntTestStep0.java +++ b/docs/examples/junit5/redis/src/test/java/quickstart/RedisBackedCacheIntTestStep0.java @@ -4,7 +4,7 @@ import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; @Disabled("This test class is deliberately invalid, as it relies on a non-existent local Redis") public class RedisBackedCacheIntTestStep0 { @@ -22,6 +22,6 @@ public void testSimplePutAndGet() { underTest.put("test", "example"); String retrieved = underTest.get("test"); - assertEquals("example", retrieved); + assertThat(retrieved).isEqualTo("example"); } } diff --git a/examples/disque-job-queue/build.gradle b/examples/disque-job-queue/build.gradle index 47bf340e2eb..6c7a453c3e9 100644 --- a/examples/disque-job-queue/build.gradle +++ b/examples/disque-job-queue/build.gradle @@ -15,5 +15,5 @@ dependencies { testImplementation 'ch.qos.logback:logback-classic:1.2.11' testImplementation 'org.mockito:mockito-all:1.10.19' testImplementation 'org.testcontainers:testcontainers' - testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' + testImplementation 'org.assertj:assertj-core:3.23.1' } diff --git a/examples/disque-job-queue/src/test/java/DisqueBackedMailSenderTest.java b/examples/disque-job-queue/src/test/java/DisqueBackedMailSenderTest.java index 4da6201eecb..dfbab3f9e6a 100644 --- a/examples/disque-job-queue/src/test/java/DisqueBackedMailSenderTest.java +++ b/examples/disque-job-queue/src/test/java/DisqueBackedMailSenderTest.java @@ -6,15 +6,13 @@ import org.testcontainers.containers.GenericContainer; import org.testcontainers.utility.DockerImageName; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.rnorth.visibleassertions.VisibleAssertions.context; -import static org.rnorth.visibleassertions.VisibleAssertions.info; /** * Created by rnorth on 03/01/2016. @@ -35,14 +33,11 @@ public class DisqueBackedMailSenderTest { @Before public void setup() { - context(""); disqueClient = new DisqueClient(DisqueURI.create(container.getHost(), container.getMappedPort(7711))); mockMailApiClient = mock(MailApiClient.class); service = new DisqueBackedMailSenderService(disqueClient, mockMailApiClient); - info("Initialized a fresh Disque instance and service instance"); - dummyMessage1 = new MailMessage("Dummy Message 1", "bob@example.com", "Test body"); dummyMessage2 = new MailMessage("Dummy Message 2", "bob@example.com", "Test body"); dummyMessage3 = new MailMessage("Dummy Message 3", "bob@example.com", "Test body"); @@ -69,28 +64,23 @@ public void testRetryOnFailure() throws Exception { service.enqueueMessage(dummyMessage2); service.enqueueMessage(dummyMessage3); - info("Message 1 will fail to send on the first attempt"); when(mockMailApiClient.send(eq(dummyMessage1))).thenThrow(MailApiClient.MailSendException.class).thenReturn(true); when(mockMailApiClient.send(eq(dummyMessage2))).thenReturn(true); when(mockMailApiClient.send(eq(dummyMessage3))).thenReturn(true); MailSenderService.Result result; - context("Simulating sending messages"); - context("First sending attempt", 4); + result = service.doScheduledSend(); - assertEquals("2 messages were 'sent' successfully", 2, result.successfulCount); - assertEquals("1 messages failed", 1, result.failedCount); + assertThat(result.successfulCount).as("2 messages were 'sent' successfully").isEqualTo(2); + assertThat(result.failedCount).as("1 messages failed").isEqualTo(1); - context("Second attempt", 4); result = service.doScheduledSend(); - assertEquals("1 message was 'sent' successfully", 1, result.successfulCount); - assertEquals("0 messages failed", 0, result.failedCount); + assertThat(result.successfulCount).as("1 message was 'sent' successfully").isEqualTo(1); + assertThat(result.failedCount).as("0 messages failed").isZero(); - context("Third attempt", 4); - info("No messages should be due to send this time"); result = service.doScheduledSend(); - assertEquals("0 messages were 'sent' successfully", 0, result.successfulCount); - assertEquals("0 messages failed", 0, result.failedCount); + assertThat(result.successfulCount).as("0 messages were 'sent' successfully").isZero(); + assertThat(result.failedCount).as("0 messages failed").isZero(); verify(mockMailApiClient, times(2)).send(eq(dummyMessage1)); verify(mockMailApiClient).send(eq(dummyMessage2)); diff --git a/examples/disque-job-queue/src/test/java/SingleDisqueInstanceTest.java b/examples/disque-job-queue/src/test/java/SingleDisqueInstanceTest.java index 7ded5d8da43..09830614eb0 100644 --- a/examples/disque-job-queue/src/test/java/SingleDisqueInstanceTest.java +++ b/examples/disque-job-queue/src/test/java/SingleDisqueInstanceTest.java @@ -11,10 +11,7 @@ import java.util.concurrent.TimeUnit; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.rnorth.visibleassertions.VisibleAssertions.assertNotNull; -import static org.rnorth.visibleassertions.VisibleAssertions.context; -import static org.rnorth.visibleassertions.VisibleAssertions.info; +import static org.assertj.core.api.Assertions.assertThat; /** * Created by rnorth on 03/01/2016. @@ -29,54 +26,36 @@ public class SingleDisqueInstanceTest { @Before public void setup() { - context(""); DisqueClient client = new DisqueClient(DisqueURI.create(container.getHost(), container.getMappedPort(7711))); connection = client.connect().sync(); retryAfter1Second = AddJobArgs.builder().retry(1, TimeUnit.SECONDS).build(); - - info("Initialized a fresh Disque instance and client"); } @Test public void testJobStoreAndRetrieve() { - - info("Adding a job to the queue"); connection.addjob("main_queue", "body", 1, TimeUnit.MINUTES); - info("Getting a job from the queue"); Job job = connection.getjob("main_queue"); - assertEquals("The retrieved job is the same as the one that was added", - "body", - job.getBody()); + assertThat(job.getBody()).as("The retrieved job is the same as the one that was added").isEqualTo("body"); - info("Acknowledging the job to mark completion"); connection.ackjob(job.getId()); } @Test public void testFailureToAckJobBeforeTimeout() throws InterruptedException { - - info("Adding a job to the queue"); connection.addjob("main_queue", "body", 1, TimeUnit.MINUTES, retryAfter1Second); - info("Getting a job from the queue"); Job job = connection.getjob("main_queue"); - assertEquals("The retrieved job is the same as the one that was added", - "body", - job.getBody()); + assertThat(job.getBody()).as("The retrieved job is the same as the one that was added").isEqualTo("body"); - info("Simulating a failure to ack the job before the timeout (1s)"); TimeUnit.SECONDS.sleep(2); - info("Attempting to get another job from the queue - the RETRY setting for the job means it should have reappeared"); // The timeout specified here is how long the command will wait for a job to appear Job job2 = connection.getjob(5, TimeUnit.SECONDS, "main_queue"); - assertNotNull("After re-getting the original job is back on the queue", job2); - assertEquals("The retrieved job is the same as the one that was added", - "body", - job2.getBody()); + assertThat(job2).as("After re-getting the original job is back on the queue").isNotNull(); + assertThat(job2.getBody()).as("The retrieved job is the same as the one that was added").isEqualTo("body"); } } diff --git a/examples/redis-backed-cache-testng/build.gradle b/examples/redis-backed-cache-testng/build.gradle index 1e09fbc84f0..9d0fd118f61 100644 --- a/examples/redis-backed-cache-testng/build.gradle +++ b/examples/redis-backed-cache-testng/build.gradle @@ -14,7 +14,7 @@ dependencies { testImplementation 'org.testcontainers:testcontainers' testImplementation 'ch.qos.logback:logback-classic:1.2.11' testImplementation 'org.testng:testng:7.5' - testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' + testImplementation 'org.assertj:assertj-core:3.23.1' } test { diff --git a/examples/redis-backed-cache-testng/src/test/java/RedisBackedCacheTest.java b/examples/redis-backed-cache-testng/src/test/java/RedisBackedCacheTest.java index 927f097aa5f..e02b271f290 100644 --- a/examples/redis-backed-cache-testng/src/test/java/RedisBackedCacheTest.java +++ b/examples/redis-backed-cache-testng/src/test/java/RedisBackedCacheTest.java @@ -10,9 +10,7 @@ import java.util.Optional; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.rnorth.visibleassertions.VisibleAssertions.assertFalse; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; /** * Integration test for Redis-backed cache implementation. @@ -45,18 +43,14 @@ public void testFindingAnInsertedValue() { cache.put("foo", "FOO"); Optional foundObject = cache.get("foo", String.class); - assertTrue("When an object in the cache is retrieved, it can be found", - foundObject.isPresent()); - assertEquals("When we put a String in to the cache and retrieve it, the value is the same", - "FOO", - foundObject.get()); + assertThat(foundObject).as("When an object in the cache is retrieved, it can be found").isPresent(); + assertThat(foundObject).as("When we put a String in to the cache and retrieve it, the value is the same").contains("FOO"); } @Test public void testNotFindingAValueThatWasNotInserted() { Optional foundObject = cache.get("bar", String.class); - assertFalse("When an object that's not in the cache is retrieved, nothing is found", - foundObject.isPresent()); + assertThat(foundObject).as("When an object that's not in the cache is retrieved, nothing is found").isNotPresent(); } } diff --git a/examples/redis-backed-cache/build.gradle b/examples/redis-backed-cache/build.gradle index 94f445f5ffe..c5f365b5502 100644 --- a/examples/redis-backed-cache/build.gradle +++ b/examples/redis-backed-cache/build.gradle @@ -14,5 +14,5 @@ dependencies { testImplementation 'org.testcontainers:testcontainers' testImplementation 'junit:junit:4.13.2' testImplementation 'ch.qos.logback:logback-classic:1.2.11' - testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' + testImplementation 'org.assertj:assertj-core:3.23.1' } diff --git a/examples/redis-backed-cache/src/test/java/RedisBackedCacheTest.java b/examples/redis-backed-cache/src/test/java/RedisBackedCacheTest.java index aa4199684e8..24a21723b15 100644 --- a/examples/redis-backed-cache/src/test/java/RedisBackedCacheTest.java +++ b/examples/redis-backed-cache/src/test/java/RedisBackedCacheTest.java @@ -9,9 +9,7 @@ import java.util.Optional; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.rnorth.visibleassertions.VisibleAssertions.assertFalse; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; /** * Integration test for Redis-backed cache implementation. @@ -35,18 +33,14 @@ public void testFindingAnInsertedValue() { cache.put("foo", "FOO"); Optional foundObject = cache.get("foo", String.class); - assertTrue("When an object in the cache is retrieved, it can be found", - foundObject.isPresent()); - assertEquals("When we put a String in to the cache and retrieve it, the value is the same", - "FOO", - foundObject.get()); + assertThat(foundObject.isPresent()).as("When an object in the cache is retrieved, it can be found").isTrue(); + assertThat(foundObject.get()).as("When we put a String in to the cache and retrieve it, the value is the same").isEqualTo("FOO"); } @Test public void testNotFindingAValueThatWasNotInserted() { Optional foundObject = cache.get("bar", String.class); - assertFalse("When an object that's not in the cache is retrieved, nothing is found", - foundObject.isPresent()); + assertThat(foundObject.isPresent()).as("When an object that's not in the cache is retrieved, nothing is found").isFalse(); } } diff --git a/examples/selenium-container/build.gradle b/examples/selenium-container/build.gradle index 7677e82eefb..dc10798a060 100644 --- a/examples/selenium-container/build.gradle +++ b/examples/selenium-container/build.gradle @@ -15,5 +15,5 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'org.testcontainers:selenium' - testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' + testImplementation 'org.assertj:assertj-core:3.23.1' } diff --git a/examples/selenium-container/src/test/java/SeleniumContainerTest.java b/examples/selenium-container/src/test/java/SeleniumContainerTest.java index c21d6bf4eca..32411b98ed9 100644 --- a/examples/selenium-container/src/test/java/SeleniumContainerTest.java +++ b/examples/selenium-container/src/test/java/SeleniumContainerTest.java @@ -19,7 +19,7 @@ import java.io.File; import java.util.List; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; import static org.testcontainers.containers.BrowserWebDriverContainer.VncRecordingMode.RECORD_ALL; /** @@ -45,7 +45,7 @@ public void simplePlainSeleniumTest() { driver.get("http://host.testcontainers.internal:" + port + "/foo.html"); List hElement = driver.findElementsByTagName("h"); - assertTrue("The h element is found", hElement != null && hElement.size() > 0); + assertThat(hElement).as("The h element is found").isNotEmpty(); } public static class Initializer implements ApplicationContextInitializer { diff --git a/examples/settings.gradle b/examples/settings.gradle index cfb204f4b00..be455a78955 100644 --- a/examples/settings.gradle +++ b/examples/settings.gradle @@ -32,7 +32,6 @@ include 'solr-container' include 'spring-boot' include 'cucumber' include 'spring-boot-kotlin-redis' -include 'spock' ext.isCI = System.getenv("CI") != null diff --git a/examples/singleton-container/build.gradle b/examples/singleton-container/build.gradle index fd603d515c7..89f067d3a19 100644 --- a/examples/singleton-container/build.gradle +++ b/examples/singleton-container/build.gradle @@ -15,5 +15,5 @@ dependencies { testImplementation 'ch.qos.logback:logback-classic:1.2.11' testImplementation 'org.testcontainers:testcontainers' - testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' + testImplementation 'org.assertj:assertj-core:3.23.1' } diff --git a/examples/singleton-container/src/test/java/com/example/BarConcreteTestClass.java b/examples/singleton-container/src/test/java/com/example/BarConcreteTestClass.java index 0f4e6d672d8..fff6e1a4a3a 100644 --- a/examples/singleton-container/src/test/java/com/example/BarConcreteTestClass.java +++ b/examples/singleton-container/src/test/java/com/example/BarConcreteTestClass.java @@ -8,8 +8,7 @@ import java.util.Optional; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; public class BarConcreteTestClass extends AbstractIntegrationTest { @@ -27,8 +26,8 @@ public void testInsertValue() { cache.put("bar", "BAR"); Optional foundObject = cache.get("bar", String.class); - assertTrue("When inserting an object into the cache, it can be retrieved", foundObject.isPresent()); - assertEquals("When accessing the value of a retrieved object, the value must be the same", "BAR", foundObject.get()); + assertThat(foundObject).as("When inserting an object into the cache, it can be retrieved").isPresent(); + assertThat(foundObject).as("When accessing the value of a retrieved object, the value must be the same").contains("BAR"); } } diff --git a/examples/singleton-container/src/test/java/com/example/FooConcreteTestClass.java b/examples/singleton-container/src/test/java/com/example/FooConcreteTestClass.java index 3a9e4e84f04..11ad98a0580 100644 --- a/examples/singleton-container/src/test/java/com/example/FooConcreteTestClass.java +++ b/examples/singleton-container/src/test/java/com/example/FooConcreteTestClass.java @@ -8,15 +8,14 @@ import java.util.Optional; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; public class FooConcreteTestClass extends AbstractIntegrationTest { private Cache cache; @Before - public void setUp() throws Exception { + public void setUp() { Jedis jedis = new Jedis(redis.getHost(), redis.getMappedPort(6379)); cache = new RedisBackedCache(jedis, "foo"); @@ -27,7 +26,7 @@ public void testInsertValue() { cache.put("foo", "FOO"); Optional foundObject = cache.get("foo", String.class); - assertTrue("When inserting an object into the cache, it can be retrieved", foundObject.isPresent()); - assertEquals("When accessing the value of a retrieved object, the value must be the same", "FOO", foundObject.get()); + assertThat(foundObject).as("When inserting an object into the cache, it can be retrieved").isPresent(); + assertThat(foundObject).as("When accessing the value of a retrieved object, the value must be the same").contains("FOO"); } } diff --git a/examples/solr-container/build.gradle b/examples/solr-container/build.gradle index abb7c12e0ed..56fd297cf5e 100644 --- a/examples/solr-container/build.gradle +++ b/examples/solr-container/build.gradle @@ -14,6 +14,6 @@ dependencies { testImplementation 'org.testcontainers:testcontainers' testImplementation 'org.testcontainers:solr' - testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' + testImplementation 'org.assertj:assertj-core:3.23.1' } diff --git a/examples/solr-container/src/test/java/com/example/SolrQueryTest.java b/examples/solr-container/src/test/java/com/example/SolrQueryTest.java index 49f8cf99f33..6443a19b869 100644 --- a/examples/solr-container/src/test/java/com/example/SolrQueryTest.java +++ b/examples/solr-container/src/test/java/com/example/SolrQueryTest.java @@ -16,7 +16,7 @@ import java.util.Map; import static com.example.SolrSearchEngine.COLLECTION_NAME; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; public class SolrQueryTest { @@ -55,8 +55,8 @@ public void testQueryForShoes() { SolrSearchEngine searchEngine = new SolrSearchEngine(solrClient); SearchResult result = searchEngine.search("shoes"); - assertEquals("When searching for shoes we expect one result", 1L, result.getTotalHits()); - assertEquals("The result should have the id 1", "1", result.getResults().get(0).get("id")); + assertThat(result.getTotalHits()).as("When searching for shoes we expect one result").isEqualTo(1L); + assertThat(result.getResults().get(0).get("id")).as("The result should have the id 1").isEqualTo("1"); } @Test @@ -64,8 +64,8 @@ public void testQueryForTShirt() { SolrSearchEngine searchEngine = new SolrSearchEngine(solrClient); SearchResult result = searchEngine.search("t-shirt"); - assertEquals("When searching for t-shirt we expect one result", 1L, result.getTotalHits()); - assertEquals("The result should have the id 2", "2", result.getResults().get(0).get("id")); + assertThat(result.getTotalHits()).as("When searching for t-shirt we expect one result").isEqualTo(1L); + assertThat(result.getResults().get(0).get("id")).as("The result should have the id 2").isEqualTo("2"); } @Test @@ -73,7 +73,7 @@ public void testQueryForAsterisk() { SolrSearchEngine searchEngine = new SolrSearchEngine(solrClient); SearchResult result = searchEngine.search("*"); - assertEquals("When searching for * we expect no results", 0L, result.getTotalHits()); + assertThat(result.getTotalHits()).as("When searching for * we expect no results").isEqualTo(0L); } private static SolrInputField createInputField(String key, String value) { diff --git a/examples/spring-boot/build.gradle b/examples/spring-boot/build.gradle index e3b3376e28f..650b5a60d98 100644 --- a/examples/spring-boot/build.gradle +++ b/examples/spring-boot/build.gradle @@ -17,5 +17,4 @@ dependencies { runtimeOnly 'org.postgresql:postgresql' testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'org.testcontainers:postgresql' - testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' } diff --git a/examples/spring-boot/src/test/java/com/example/DemoControllerTest.java b/examples/spring-boot/src/test/java/com/example/DemoControllerTest.java index 286ce41ffbf..c305ae67811 100644 --- a/examples/spring-boot/src/test/java/com/example/DemoControllerTest.java +++ b/examples/spring-boot/src/test/java/com/example/DemoControllerTest.java @@ -4,8 +4,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.web.client.TestRestTemplate; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.rnorth.visibleassertions.VisibleAssertions.info; +import static org.assertj.core.api.Assertions.assertThat; public class DemoControllerTest extends AbstractIntegrationTest { @@ -19,10 +18,9 @@ public class DemoControllerTest extends AbstractIntegrationTest { public void simpleTest() { String fooResource = "/foo"; - info("putting 'bar' to " + fooResource); restTemplate.put(fooResource, "bar"); - assertEquals("value is set", "bar", restTemplate.getForObject(fooResource, String.class)); + assertThat(restTemplate.getForObject(fooResource, String.class)).as("value is set").isEqualTo("bar"); } @Test @@ -33,7 +31,7 @@ public void simpleJPATest() { DemoEntity result = restTemplate.getForObject("/" + demoEntity.getId(), DemoEntity.class); - assertEquals("value is set", "Some value", result.getValue()); + assertThat(result.getValue()).as("value is set").isEqualTo("Some value"); } } diff --git a/modules/azure/src/test/java/org/testcontainers/containers/CosmosDBEmulatorContainerTest.java b/modules/azure/src/test/java/org/testcontainers/containers/CosmosDBEmulatorContainerTest.java index 37422b30af7..140bd64c39a 100644 --- a/modules/azure/src/test/java/org/testcontainers/containers/CosmosDBEmulatorContainerTest.java +++ b/modules/azure/src/test/java/org/testcontainers/containers/CosmosDBEmulatorContainerTest.java @@ -4,7 +4,6 @@ import com.azure.cosmos.CosmosClientBuilder; import com.azure.cosmos.models.CosmosContainerResponse; import com.azure.cosmos.models.CosmosDatabaseResponse; -import org.assertj.core.api.Assertions; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Rule; @@ -17,6 +16,8 @@ import java.security.KeyStore; import java.util.Properties; +import static org.assertj.core.api.Assertions.assertThat; + public class CosmosDBEmulatorContainerTest { private static Properties originalSystemProperties; @@ -64,12 +65,12 @@ public void testWithCosmosClient() throws Exception { // } // testWithClientAgainstEmulatorContainer { CosmosDatabaseResponse databaseResponse = client.createDatabaseIfNotExists("Azure").block(); - Assertions.assertThat(databaseResponse.getStatusCode()).isEqualTo(201); + assertThat(databaseResponse.getStatusCode()).isEqualTo(201); CosmosContainerResponse containerResponse = client .getDatabase("Azure") .createContainerIfNotExists("ServiceContainer", "/name") .block(); - Assertions.assertThat(containerResponse.getStatusCode()).isEqualTo(201); + assertThat(containerResponse.getStatusCode()).isEqualTo(201); // } } } diff --git a/modules/clickhouse/build.gradle b/modules/clickhouse/build.gradle index 914ee7efbd4..9f7df373b27 100644 --- a/modules/clickhouse/build.gradle +++ b/modules/clickhouse/build.gradle @@ -6,4 +6,5 @@ dependencies { testImplementation project(':jdbc-test') testImplementation 'ru.yandex.clickhouse:clickhouse-jdbc:0.3.2' + testImplementation 'org.assertj:assertj-core:3.23.1' } diff --git a/modules/clickhouse/src/test/java/org/testcontainers/junit/clickhouse/SimpleClickhouseTest.java b/modules/clickhouse/src/test/java/org/testcontainers/junit/clickhouse/SimpleClickhouseTest.java index 2f6edcc722d..8f81c88bfc0 100644 --- a/modules/clickhouse/src/test/java/org/testcontainers/junit/clickhouse/SimpleClickhouseTest.java +++ b/modules/clickhouse/src/test/java/org/testcontainers/junit/clickhouse/SimpleClickhouseTest.java @@ -11,7 +11,7 @@ import java.sql.ResultSet; import java.sql.SQLException; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; @RunWith(Parameterized.class) public class SimpleClickhouseTest extends AbstractContainerDatabaseTest { @@ -38,7 +38,7 @@ public void testSimple() throws SQLException { ResultSet resultSet = performQuery(clickhouse, "SELECT 1"); int resultSetInt = resultSet.getInt(1); - assertEquals("A basic SELECT query succeeds", 1, resultSetInt); + assertThat(resultSetInt).as("A basic SELECT query succeeds").isEqualTo(1); } } } diff --git a/modules/cockroachdb/build.gradle b/modules/cockroachdb/build.gradle index fb9f53189d9..80763e312b5 100644 --- a/modules/cockroachdb/build.gradle +++ b/modules/cockroachdb/build.gradle @@ -5,4 +5,5 @@ dependencies { testImplementation project(':jdbc-test') testImplementation 'org.postgresql:postgresql:42.4.0' + testImplementation 'org.assertj:assertj-core:3.23.1' } diff --git a/modules/cockroachdb/src/test/java/org/testcontainers/junit/cockroachdb/SimpleCockroachDBTest.java b/modules/cockroachdb/src/test/java/org/testcontainers/junit/cockroachdb/SimpleCockroachDBTest.java index 87e82fb30e3..65c79441f32 100644 --- a/modules/cockroachdb/src/test/java/org/testcontainers/junit/cockroachdb/SimpleCockroachDBTest.java +++ b/modules/cockroachdb/src/test/java/org/testcontainers/junit/cockroachdb/SimpleCockroachDBTest.java @@ -10,9 +10,7 @@ import java.util.logging.Level; import java.util.logging.LogManager; -import static org.hamcrest.Matchers.containsString; -import static org.junit.Assert.assertThat; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; public class SimpleCockroachDBTest extends AbstractContainerDatabaseTest { static { @@ -28,7 +26,7 @@ public void testSimple() throws SQLException { ResultSet resultSet = performQuery(cockroach, "SELECT 1"); int resultSetInt = resultSet.getInt(1); - assertEquals("A basic SELECT query succeeds", 1, resultSetInt); + assertThat(resultSetInt).as("A basic SELECT query succeeds").isEqualTo(1); } } @@ -43,7 +41,7 @@ public void testExplicitInitScript() throws SQLException { ResultSet resultSet = performQuery(cockroach, "SELECT foo FROM bar"); String firstColumnValue = resultSet.getString(1); - assertEquals("Value from init script should equal real value", "hello world", firstColumnValue); + assertThat(firstColumnValue).as("Value from init script should equal real value").isEqualTo("hello world"); } } @@ -56,10 +54,10 @@ public void testWithAdditionalUrlParamInJdbcUrl() { try { cockroach.start(); String jdbcUrl = cockroach.getJdbcUrl(); - assertThat(jdbcUrl, containsString("?")); - assertThat(jdbcUrl, containsString("&")); - assertThat(jdbcUrl, containsString("sslmode=disable")); - assertThat(jdbcUrl, containsString("application_name=cockroach")); + assertThat(jdbcUrl).contains("?"); + assertThat(jdbcUrl).contains("&"); + assertThat(jdbcUrl).contains("sslmode=disable"); + assertThat(jdbcUrl).contains("application_name=cockroach"); } finally { cockroach.stop(); } diff --git a/modules/database-commons/src/test/java/org/testcontainers/ext/ScriptSplittingTest.java b/modules/database-commons/src/test/java/org/testcontainers/ext/ScriptSplittingTest.java index bc9476cc83d..99ebb69a1e8 100644 --- a/modules/database-commons/src/test/java/org/testcontainers/ext/ScriptSplittingTest.java +++ b/modules/database-commons/src/test/java/org/testcontainers/ext/ScriptSplittingTest.java @@ -1,13 +1,13 @@ package org.testcontainers.ext; -import org.assertj.core.api.Assertions; import org.junit.Test; import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import static org.junit.Assert.fail; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.fail; public class ScriptSplittingTest { @@ -283,7 +283,7 @@ public void testIssue1452Case() { private void splitAndCompare(String script, List expected) { final List statements = doSplit(script); - Assertions.assertThat(statements).isEqualTo(expected); + assertThat(statements).isEqualTo(expected); } private List doSplit(String script) { diff --git a/modules/db2/build.gradle b/modules/db2/build.gradle index 0c8cd323779..804f51000d7 100644 --- a/modules/db2/build.gradle +++ b/modules/db2/build.gradle @@ -5,4 +5,5 @@ dependencies { testImplementation project(':jdbc-test') testImplementation 'com.ibm.db2:jcc:11.5.7.0' + testImplementation 'org.assertj:assertj-core:3.23.1' } diff --git a/modules/db2/src/test/java/org/testcontainers/junit/db2/SimpleDb2Test.java b/modules/db2/src/test/java/org/testcontainers/junit/db2/SimpleDb2Test.java index 8f3489eae95..2c2c4cb2f09 100644 --- a/modules/db2/src/test/java/org/testcontainers/junit/db2/SimpleDb2Test.java +++ b/modules/db2/src/test/java/org/testcontainers/junit/db2/SimpleDb2Test.java @@ -8,9 +8,7 @@ import java.sql.ResultSet; import java.sql.SQLException; -import static org.hamcrest.Matchers.containsString; -import static org.junit.Assert.assertThat; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; public class SimpleDb2Test extends AbstractContainerDatabaseTest { @@ -22,7 +20,7 @@ public void testSimple() throws SQLException { ResultSet resultSet = performQuery(db2, "SELECT 1 FROM SYSIBM.SYSDUMMY1"); int resultSetInt = resultSet.getInt(1); - assertEquals("A basic SELECT query succeeds", 1, resultSetInt); + assertThat(resultSetInt).as("A basic SELECT query succeeds").isEqualTo(1); } } @@ -36,7 +34,7 @@ public void testWithAdditionalUrlParamInJdbcUrl() { db2.start(); String jdbcUrl = db2.getJdbcUrl(); - assertThat(jdbcUrl, containsString(":sslConnection=false;")); + assertThat(jdbcUrl).contains(":sslConnection=false;"); } } } diff --git a/modules/dynalite/build.gradle b/modules/dynalite/build.gradle index 5b9ff919da7..6abd9cd943a 100644 --- a/modules/dynalite/build.gradle +++ b/modules/dynalite/build.gradle @@ -5,6 +5,5 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.12.273' testImplementation 'com.amazonaws:aws-java-sdk-dynamodb:1.12.253' - - testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' + testImplementation 'org.assertj:assertj-core:3.23.1' } diff --git a/modules/dynalite/src/test/java/org/testcontainers/dynamodb/DynaliteContainerTest.java b/modules/dynalite/src/test/java/org/testcontainers/dynamodb/DynaliteContainerTest.java index ced5931de31..10e02566d16 100644 --- a/modules/dynalite/src/test/java/org/testcontainers/dynamodb/DynaliteContainerTest.java +++ b/modules/dynalite/src/test/java/org/testcontainers/dynamodb/DynaliteContainerTest.java @@ -13,8 +13,7 @@ import org.junit.Test; import org.testcontainers.utility.DockerImageName; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.rnorth.visibleassertions.VisibleAssertions.assertNotNull; +import static org.assertj.core.api.Assertions.assertThat; public class DynaliteContainerTest { @@ -54,12 +53,10 @@ private void runTest(AmazonDynamoDB client) { final TableDescription tableDescription = client.describeTable("foo").getTable(); - assertNotNull("the description is not null", tableDescription); - assertEquals("the table has the right name", "foo", tableDescription.getTableName()); - assertEquals( - "the name has the right primary key", - "Name", - tableDescription.getKeySchema().get(0).getAttributeName() - ); + assertThat(tableDescription).as("the description is not null").isNotNull(); + assertThat(tableDescription.getTableName()).as("the table has the right name").isEqualTo("foo"); + assertThat(tableDescription.getKeySchema().get(0).getAttributeName()) + .as("the name has the right primary key") + .isEqualTo("Name"); } } diff --git a/modules/elasticsearch/build.gradle b/modules/elasticsearch/build.gradle index a3a47d11fab..0e6c5bd7b5d 100644 --- a/modules/elasticsearch/build.gradle +++ b/modules/elasticsearch/build.gradle @@ -4,5 +4,5 @@ dependencies { api project(':testcontainers') testImplementation "org.elasticsearch.client:elasticsearch-rest-client:8.3.3" testImplementation "org.elasticsearch.client:transport:7.17.5" - testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' + testImplementation 'org.assertj:assertj-core:3.23.1' } diff --git a/modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java b/modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java index 9fcfc45af76..1e1e70b5d75 100644 --- a/modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java +++ b/modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java @@ -29,10 +29,8 @@ import javax.net.ssl.SSLHandshakeException; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.is; -import static org.rnorth.visibleassertions.VisibleAssertions.assertThrows; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; public class ElasticsearchContainerTest { @@ -84,14 +82,14 @@ public void elasticsearchDeprecatedCtorTest() throws IOException { // Do whatever you want with the rest client ... Response response = getClient(container).performRequest(new Request("GET", "/")); - assertThat(response.getStatusLine().getStatusCode(), is(200)); - assertThat(EntityUtils.toString(response.getEntity()), containsString(ELASTICSEARCH_VERSION)); + assertThat(response.getStatusLine().getStatusCode()).isEqualTo(200); + assertThat(EntityUtils.toString(response.getEntity())).contains(ELASTICSEARCH_VERSION); // The default image is running with the features under Elastic License response = getClient(container).performRequest(new Request("GET", "/_xpack/")); - assertThat(response.getStatusLine().getStatusCode(), is(200)); + assertThat(response.getStatusLine().getStatusCode()).isEqualTo(200); // For now we test that we have the monitoring feature available - assertThat(EntityUtils.toString(response.getEntity()), containsString("monitoring")); + assertThat(EntityUtils.toString(response.getEntity())).contains("monitoring"); } } @@ -106,14 +104,14 @@ public void elasticsearchDefaultTest() throws IOException { // Do whatever you want with the rest client ... Response response = getClient(container).performRequest(new Request("GET", "/")); - assertThat(response.getStatusLine().getStatusCode(), is(200)); - assertThat(EntityUtils.toString(response.getEntity()), containsString(ELASTICSEARCH_VERSION)); + assertThat(response.getStatusLine().getStatusCode()).isEqualTo(200); + assertThat(EntityUtils.toString(response.getEntity())).contains(ELASTICSEARCH_VERSION); // The default image is running with the features under Elastic License response = getClient(container).performRequest(new Request("GET", "/_xpack/")); - assertThat(response.getStatusLine().getStatusCode(), is(200)); + assertThat(response.getStatusLine().getStatusCode()).isEqualTo(200); // For now we test that we have the monitoring feature available - assertThat(EntityUtils.toString(response.getEntity()), containsString("monitoring")); + assertThat(EntityUtils.toString(response.getEntity())).contains("monitoring"); } } @@ -126,16 +124,14 @@ public void elasticsearchSecuredTest() throws IOException { container.start(); // The cluster should be secured so it must fail when we try to access / without credentials - assertThrows( - "We should not be able to access / URI with an anonymous client.", - ResponseException.class, - () -> getAnonymousClient(container).performRequest(new Request("GET", "/")) - ); + assertThat(catchThrowable(() -> getAnonymousClient(container).performRequest(new Request("GET", "/")))) + .as("We should not be able to access / URI with an anonymous client.") + .isInstanceOf(ResponseException.class); // But it should work when we try to access / with the proper login and password Response response = getClient(container).performRequest(new Request("GET", "/")); - assertThat(response.getStatusLine().getStatusCode(), is(200)); - assertThat(EntityUtils.toString(response.getEntity()), containsString(ELASTICSEARCH_VERSION)); + assertThat(response.getStatusLine().getStatusCode()).isEqualTo(200); + assertThat(EntityUtils.toString(response.getEntity())).contains(ELASTICSEARCH_VERSION); } } @@ -144,9 +140,9 @@ public void elasticsearchVersion() throws IOException { try (ElasticsearchContainer container = new ElasticsearchContainer(ELASTICSEARCH_IMAGE)) { container.start(); Response response = getClient(container).performRequest(new Request("GET", "/")); - assertThat(response.getStatusLine().getStatusCode(), is(200)); + assertThat(response.getStatusLine().getStatusCode()).isEqualTo(200); String responseAsString = EntityUtils.toString(response.getEntity()); - assertThat(responseAsString, containsString(ELASTICSEARCH_VERSION)); + assertThat(responseAsString).contains(ELASTICSEARCH_VERSION); } } @@ -159,8 +155,8 @@ public void elasticsearchVersion83() throws IOException { ) { container.start(); Response response = getClient(container).performRequest(new Request("GET", "/")); - assertThat(response.getStatusLine().getStatusCode(), is(200)); - assertThat(EntityUtils.toString(response.getEntity()), containsString("8.3.0")); + assertThat(response.getStatusLine().getStatusCode()).isEqualTo(200); + assertThat(EntityUtils.toString(response.getEntity())).contains("8.3.0"); } } @@ -177,13 +173,11 @@ public void elasticsearchOssImage() throws IOException { ) { container.start(); Response response = getClient(container).performRequest(new Request("GET", "/")); - assertThat(response.getStatusLine().getStatusCode(), is(200)); + assertThat(response.getStatusLine().getStatusCode()).isEqualTo(200); // The OSS image does not have any feature under Elastic License - assertThrows( - "We should not have /_xpack endpoint with an OSS License", - ResponseException.class, - () -> getClient(container).performRequest(new Request("GET", "/_xpack/")) - ); + assertThat(catchThrowable(() -> getClient(container).performRequest(new Request("GET", "/_xpack/")))) + .as("We should not have /_xpack endpoint with an OSS License") + .isInstanceOf(ResponseException.class); } } @@ -212,8 +206,8 @@ public void restClientClusterHealth() throws IOException { Response response = client.performRequest(new Request("GET", "/_cluster/health")); // }} - assertThat(response.getStatusLine().getStatusCode(), is(200)); - assertThat(EntityUtils.toString(response.getEntity()), containsString("cluster_name")); + assertThat(response.getStatusLine().getStatusCode()).isEqualTo(200); + assertThat(EntityUtils.toString(response.getEntity())).contains("cluster_name"); // httpClientContainer {{ } // } @@ -248,8 +242,8 @@ public void restClientSecuredClusterHealth() throws IOException { Response response = client.performRequest(new Request("GET", "/_cluster/health")); // }} - assertThat(response.getStatusLine().getStatusCode(), is(200)); - assertThat(EntityUtils.toString(response.getEntity()), containsString("cluster_name")); + assertThat(response.getStatusLine().getStatusCode()).isEqualTo(200); + assertThat(EntityUtils.toString(response.getEntity())).contains("cluster_name"); // httpClientSecuredContainer {{ } // } @@ -275,7 +269,7 @@ public void transportClientClusterHealth() { ClusterHealthResponse healths = transportClient.admin().cluster().prepareHealth().get(); String clusterName = healths.getClusterName(); // }}} - assertThat(clusterName, is(expectedClusterName)); + assertThat(clusterName).isEqualTo(expectedClusterName); // transportClientContainer {{{ } } @@ -285,18 +279,18 @@ public void transportClientClusterHealth() { @Test public void incompatibleSettingsTest() { // The OSS image can not use security feature - assertThrows( - "We should not be able to activate security with an OSS License", - IllegalArgumentException.class, - () -> { + assertThat( + catchThrowable(() -> { new ElasticsearchContainer( DockerImageName .parse("docker.elastic.co/elasticsearch/elasticsearch-oss") .withTag(ELASTICSEARCH_VERSION) ) .withPassword("foo"); - } - ); + }) + ) + .as("We should not be able to activate security with an OSS License") + .isInstanceOf(IllegalArgumentException.class); } @Test @@ -310,8 +304,8 @@ public void testElasticsearch8SecureByDefault() throws Exception { container.start(); Response response = getClusterHealth(container); - assertThat(response.getStatusLine().getStatusCode(), is(200)); - assertThat(EntityUtils.toString(response.getEntity()), containsString("cluster_name")); + assertThat(response.getStatusLine().getStatusCode()).isEqualTo(200); + assertThat(EntityUtils.toString(response.getEntity())).contains("cluster_name"); } } @@ -329,11 +323,11 @@ public void testElasticsearch8SecureByDefaultCustomCaCertFails() throws Exceptio container.start(); // this is expected, as a different cert is used for creating the SSL context - assertThrows( - "PKIX path validation failed: java.security.cert.CertPathValidatorException: Path does not chain with any of the trust anchors", - SSLHandshakeException.class, - () -> getClusterHealth(container) - ); + assertThat(catchThrowable(() -> getClusterHealth(container))) + .as( + "PKIX path validation failed: java.security.cert.CertPathValidatorException: Path does not chain with any of the trust anchors" + ) + .isInstanceOf(SSLHandshakeException.class); } } @@ -357,8 +351,8 @@ public void testElasticsearch8SecureByDefaultHttpWaitStrategy() throws Exception container.start(); Response response = getClusterHealth(container); - assertThat(response.getStatusLine().getStatusCode(), is(200)); - assertThat(EntityUtils.toString(response.getEntity()), containsString("cluster_name")); + assertThat(response.getStatusLine().getStatusCode()).isEqualTo(200); + assertThat(EntityUtils.toString(response.getEntity())).contains("cluster_name"); } } @@ -376,8 +370,8 @@ public void testElasticsearch8SecureByDefaultFailsSilentlyOnLatestImages() throw container.start(); Response response = getClient(container).performRequest(new Request("GET", "/_cluster/health")); - assertThat(response.getStatusLine().getStatusCode(), is(200)); - assertThat(EntityUtils.toString(response.getEntity()), containsString("cluster_name")); + assertThat(response.getStatusLine().getStatusCode()).isEqualTo(200); + assertThat(EntityUtils.toString(response.getEntity())).contains("cluster_name"); } } diff --git a/modules/hivemq/build.gradle b/modules/hivemq/build.gradle index 1651517dece..8336cfd8249 100644 --- a/modules/hivemq/build.gradle +++ b/modules/hivemq/build.gradle @@ -17,6 +17,7 @@ dependencies { testImplementation("com.hivemq:hivemq-mqtt-client:1.3.0") testImplementation("org.apache.httpcomponents:httpclient:4.5.13") testImplementation("ch.qos.logback:logback-classic:1.2.11") + testImplementation 'org.assertj:assertj-core:3.23.1' testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.9.0") } diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithCustomConfigIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithCustomConfigIT.java index fe119854c80..3a3f2d13bb7 100755 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithCustomConfigIT.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithCustomConfigIT.java @@ -11,7 +11,7 @@ import java.util.concurrent.TimeUnit; -import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; public class ContainerWithCustomConfigIT { @@ -33,13 +33,11 @@ void test() throws Exception { publisher.connect(); - assertThrows( - MqttSessionExpiredException.class, - () -> { + assertThatExceptionOfType(MqttSessionExpiredException.class) + .isThrownBy(() -> { // this should fail since only QoS 0 is allowed by the configuration publisher.publishWith().topic("test/topic").qos(MqttQos.EXACTLY_ONCE).send(); - } - ); + }); } } } diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithoutPlatformExtensionsIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithoutPlatformExtensionsIT.java index 75d308e3f90..fde18e09285 100644 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithoutPlatformExtensionsIT.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/ContainerWithoutPlatformExtensionsIT.java @@ -24,8 +24,7 @@ import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; public class ContainerWithoutPlatformExtensionsIT { @@ -61,13 +60,13 @@ public void removeAllPlatformExtensions() throws InterruptedException { client.subscribeWith().topicFilter("extensions").send(); final Mqtt5Publish receive = publishes.receive(); - assertTrue(receive.getPayload().isPresent()); + assertThat(receive.getPayload()).isPresent(); final String extensionInfo = new String(receive.getPayloadAsBytes()); - assertFalse(extensionInfo.contains("hivemq-allow-all-extension")); - assertFalse(extensionInfo.contains("hivemq-kafka-extension")); - assertFalse(extensionInfo.contains("hivemq-bridge-extension")); - assertFalse(extensionInfo.contains("hivemq-enterprise-security-extension")); + assertThat(extensionInfo).doesNotContain("hivemq-allow-all-extension"); + assertThat(extensionInfo).doesNotContain("hivemq-kafka-extension"); + assertThat(extensionInfo).doesNotContain("hivemq-bridge-extension"); + assertThat(extensionInfo).doesNotContain("hivemq-enterprise-security-extension"); hivemq.start(); } @@ -96,13 +95,13 @@ public void removeKafkaExtension() throws InterruptedException { client.subscribeWith().topicFilter("extensions").send(); final Mqtt5Publish receive = publishes.receive(); - assertTrue(receive.getPayload().isPresent()); + assertThat(receive.getPayload().isPresent()).isTrue(); final String extensionInfo = new String(receive.getPayloadAsBytes()); - assertTrue(extensionInfo.contains("hivemq-allow-all-extension")); - assertFalse(extensionInfo.contains("hivemq-kafka-extension")); - assertTrue(extensionInfo.contains("hivemq-bridge-extension")); - assertTrue(extensionInfo.contains("hivemq-enterprise-security-extension")); + assertThat(extensionInfo).contains("hivemq-allow-all-extension"); + assertThat(extensionInfo).doesNotContain("hivemq-kafka-extension"); + assertThat(extensionInfo).contains("hivemq-bridge-extension"); + assertThat(extensionInfo).contains("hivemq-enterprise-security-extension"); } } diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/CreateFileInCopiedDirectoryIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/CreateFileInCopiedDirectoryIT.java index 191ea41f303..cd83cb8a10d 100755 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/CreateFileInCopiedDirectoryIT.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/CreateFileInCopiedDirectoryIT.java @@ -22,15 +22,15 @@ import java.nio.file.Files; import java.util.concurrent.TimeUnit; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; public class CreateFileInCopiedDirectoryIT { private @NotNull MountableFile createDirectory() throws IOException { final File directory = new File(Files.createTempDirectory("").toFile(), "directory"); - assertTrue(directory.mkdir()); + assertThat(directory.mkdir()).isTrue(); final File subdirectory = new File(directory, "sub-directory"); - assertTrue(subdirectory.mkdir()); + assertThat(subdirectory.mkdir()).isTrue(); return MountableFile.forHostPath(directory.getPath()); } diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/DisableEnableExtensionFromDirectoryIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/DisableEnableExtensionFromDirectoryIT.java index 9588e3404e6..a2ee9edfbcf 100644 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/DisableEnableExtensionFromDirectoryIT.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/DisableEnableExtensionFromDirectoryIT.java @@ -10,7 +10,7 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; -import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; public class DisableEnableExtensionFromDirectoryIT { @@ -27,10 +27,8 @@ void test() throws Exception { TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost()); hivemq.disableExtension("Modifier Extension", "modifier-extension"); - assertThrows( - ExecutionException.class, - () -> TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost()) - ); + assertThatExceptionOfType(ExecutionException.class) + .isThrownBy(() -> TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost())); hivemq.enableExtension("Modifier Extension", "modifier-extension"); TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost()); } diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/DisableEnableExtensionIT.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/DisableEnableExtensionIT.java index e50664f240e..361af190a57 100644 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/DisableEnableExtensionIT.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/DisableEnableExtensionIT.java @@ -11,7 +11,7 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; -import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; public class DisableEnableExtensionIT { @@ -35,17 +35,13 @@ void test() throws Exception { ) { hivemq.start(); - assertThrows( - ExecutionException.class, - () -> TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost()) - ); + assertThatExceptionOfType(ExecutionException.class) + .isThrownBy(() -> TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost())); hivemq.enableExtension(hiveMQExtension); TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost()); hivemq.disableExtension(hiveMQExtension); - assertThrows( - ExecutionException.class, - () -> TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost()) - ); + assertThatExceptionOfType(ExecutionException.class) + .isThrownBy(() -> TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost())); hivemq.enableExtension(hiveMQExtension); TestPublishModifiedUtil.testPublishModified(hivemq.getMqttPort(), hivemq.getHost()); } diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/HiveMQExtensionTest.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/HiveMQExtensionTest.java index 08e1df797a1..38b8e918925 100644 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/HiveMQExtensionTest.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/HiveMQExtensionTest.java @@ -2,15 +2,13 @@ import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.*; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; class HiveMQExtensionTest { @Test void builder_classDoesNotImplementExtensionMain_exception() { - assertThrows( - IllegalArgumentException.class, - () -> HiveMQExtension.builder().mainClass(HiveMQExtensionTest.class) - ); + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> HiveMQExtension.builder().mainClass(HiveMQExtensionTest.class)); } } diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/HiveMQTestContainerCore.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/HiveMQTestContainerCore.java index d4dd2be9621..32097e52641 100644 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/HiveMQTestContainerCore.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/HiveMQTestContainerCore.java @@ -10,8 +10,8 @@ import java.io.File; import java.io.IOException; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; class HiveMQTestContainerCore { @@ -24,69 +24,72 @@ class HiveMQTestContainerCore { @Test void withExtension_fileDoesNotExist_Exception() { final MountableFile mountableFile = MountableFile.forHostPath("/this/does/not/exist"); - assertThrows(ContainerLaunchException.class, () -> container.withExtension(mountableFile)); + assertThatExceptionOfType(ContainerLaunchException.class) + .isThrownBy(() -> container.withExtension(mountableFile)); } @Test void withExtension_fileNoDirectory_Exception() throws IOException { final File extension = new File(tempDir, "extension"); - assertTrue(extension.createNewFile()); + assertThat(extension.createNewFile()).isTrue(); final MountableFile mountableFile = MountableFile.forHostPath(extension.getAbsolutePath()); - assertThrows(ContainerLaunchException.class, () -> container.withExtension(mountableFile)); + assertThatExceptionOfType(ContainerLaunchException.class) + .isThrownBy(() -> container.withExtension(mountableFile)); } @Test void withLicense_fileDoesNotExist_Exception() { final MountableFile mountableFile = MountableFile.forHostPath("/this/does/not/exist"); - assertThrows(ContainerLaunchException.class, () -> container.withLicense(mountableFile)); + assertThatExceptionOfType(ContainerLaunchException.class) + .isThrownBy(() -> container.withLicense(mountableFile)); } @Test void withExtension_fileEndingWrong_Exception() throws IOException { final File extension = new File(tempDir, "extension.wrong"); - assertTrue(extension.createNewFile()); + assertThat(extension.createNewFile()).isTrue(); final MountableFile mountableFile = MountableFile.forHostPath(extension.getAbsolutePath()); - assertThrows(ContainerLaunchException.class, () -> container.withLicense(mountableFile)); + assertThatExceptionOfType(ContainerLaunchException.class) + .isThrownBy(() -> container.withLicense(mountableFile)); } @Test void withHiveMQConfig_fileDoesNotExist_Exception() { final MountableFile mountableFile = MountableFile.forHostPath("/this/does/not/exist"); - assertThrows(ContainerLaunchException.class, () -> container.withHiveMQConfig(mountableFile)); + assertThatExceptionOfType(ContainerLaunchException.class) + .isThrownBy(() -> container.withHiveMQConfig(mountableFile)); } @Test void withFileInHomeFolder_fileDoesNotExist_Exception() { final MountableFile mountableFile = MountableFile.forHostPath("/this/does/not/exist"); - assertThrows(ContainerLaunchException.class, () -> container.withFileInHomeFolder(mountableFile, "some/path")); + assertThatExceptionOfType(ContainerLaunchException.class) + .isThrownBy(() -> container.withFileInHomeFolder(mountableFile, "some/path")); } @Test void withFileInHomeFolder_pathEmpty_Exception() { final MountableFile mountableFile = MountableFile.forHostPath("/this/does/not/exist"); - assertThrows(ContainerLaunchException.class, () -> container.withFileInHomeFolder(mountableFile, "")); + assertThatExceptionOfType(ContainerLaunchException.class) + .isThrownBy(() -> container.withFileInHomeFolder(mountableFile, "")); } @Test void withFileInExtensionHomeFolder_withPath_fileDoesNotExist_Exception() { final MountableFile mountableFile = MountableFile.forHostPath("/this/does/not/exist"); - assertThrows( - ContainerLaunchException.class, - () -> container.withFileInExtensionHomeFolder(mountableFile, "my-extension", "some/path") - ); + assertThatExceptionOfType(ContainerLaunchException.class) + .isThrownBy(() -> container.withFileInExtensionHomeFolder(mountableFile, "my-extension", "some/path")); } @Test void withFileInExtensionHomeFolder_fileDoesNotExist_Exception() { final MountableFile mountableFile = MountableFile.forHostPath("/this/does/not/exist"); - assertThrows( - ContainerLaunchException.class, - () -> { + assertThatExceptionOfType(ContainerLaunchException.class) + .isThrownBy(() -> { final HiveMQContainer hiveMQContainer = container.withFileInExtensionHomeFolder( mountableFile, "my-extension" ); - } - ); + }); } } diff --git a/modules/hivemq/src/test/java/org/testcontainers/hivemq/PathUtilTest.java b/modules/hivemq/src/test/java/org/testcontainers/hivemq/PathUtilTest.java index f1e060e68d9..d845acb6684 100755 --- a/modules/hivemq/src/test/java/org/testcontainers/hivemq/PathUtilTest.java +++ b/modules/hivemq/src/test/java/org/testcontainers/hivemq/PathUtilTest.java @@ -2,42 +2,42 @@ import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; class PathUtilTest { @Test void prepareInnerPath_emptyString() { - assertEquals("/", PathUtil.prepareInnerPath("")); + assertThat(PathUtil.prepareInnerPath("")).isEqualTo("/"); } @Test void prepareInnerPath_onlyDelimiter() { - assertEquals("/", PathUtil.prepareInnerPath("/")); + assertThat(PathUtil.prepareInnerPath("/")).isEqualTo("/"); } @Test void prepareInnerPath_noDelimiter() { - assertEquals("/path/", PathUtil.prepareInnerPath("path")); + assertThat(PathUtil.prepareInnerPath("path")).isEqualTo("/path/"); } @Test void prepareInnerPath_delimiterAtEnd() { - assertEquals("/path/", PathUtil.prepareInnerPath("path/")); + assertThat(PathUtil.prepareInnerPath("path/")).isEqualTo("/path/"); } @Test void prepareInnerPath_delimiterAtBeginning() { - assertEquals("/path/", PathUtil.prepareInnerPath("/path")); + assertThat(PathUtil.prepareInnerPath("/path")).isEqualTo("/path/"); } @Test void prepareAppendPath_delimiterAtBeginning_noChange() { - assertEquals("/path", PathUtil.prepareAppendPath("/path")); + assertThat(PathUtil.prepareAppendPath("/path")).isEqualTo("/path"); } @Test void prepareAppendPath_delimiterAtBeginning_delimiterAdded() { - assertEquals("/path", PathUtil.prepareAppendPath("path")); + assertThat(PathUtil.prepareAppendPath("path")).isEqualTo("/path"); } } diff --git a/modules/jdbc-test/build.gradle b/modules/jdbc-test/build.gradle index 0d8e2abedf9..f8b55d01bc5 100644 --- a/modules/jdbc-test/build.gradle +++ b/modules/jdbc-test/build.gradle @@ -9,10 +9,7 @@ dependencies { api 'com.googlecode.junit-toolbox:junit-toolbox:2.4' - api('org.rnorth.visible-assertions:visible-assertions:2.1.2') { - // Excluded in favor of jna-platform - exclude(group: "net.java.dev.jna", module: "jna") - } + api 'org.assertj:assertj-core:3.23.1' api 'org.apache.tomcat:tomcat-jdbc:10.0.23' api 'org.vibur:vibur-dbcp:25.0' diff --git a/modules/jdbc-test/src/main/java/org/testcontainers/jdbc/AbstractJDBCDriverTest.java b/modules/jdbc-test/src/main/java/org/testcontainers/jdbc/AbstractJDBCDriverTest.java index 5566db6244e..a152a1ae550 100644 --- a/modules/jdbc-test/src/main/java/org/testcontainers/jdbc/AbstractJDBCDriverTest.java +++ b/modules/jdbc-test/src/main/java/org/testcontainers/jdbc/AbstractJDBCDriverTest.java @@ -14,9 +14,8 @@ import java.sql.Statement; import java.util.EnumSet; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assume.assumeFalse; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; public class AbstractJDBCDriverTest { @@ -82,12 +81,12 @@ private void performSimpleTest(HikariDataSource dataSource) throws SQLException rs -> { rs.next(); int resultSetInt = rs.getInt(1); - assertEquals("A basic SELECT query succeeds", 1, resultSetInt); + assertThat(resultSetInt).as("A basic SELECT query succeeds").isEqualTo(1); return true; } ); - assertTrue("The database returned a record as expected", result); + assertThat(result).as("The database returned a record as expected").isTrue(); } private void performTestForScriptedSchema(HikariDataSource dataSource) throws SQLException { @@ -97,16 +96,14 @@ private void performTestForScriptedSchema(HikariDataSource dataSource) throws SQ rs -> { rs.next(); String resultSetString = rs.getString(1); - assertEquals( - "A basic SELECT query succeeds where the schema has been applied from a script", - "hello world", - resultSetString - ); + assertThat(resultSetString) + .as("A basic SELECT query succeeds where the schema has been applied from a script") + .isEqualTo("hello world"); return true; } ); - assertTrue("The database returned a record as expected", result); + assertThat(result).as("The database returned a record as expected").isTrue(); } private void performTestForJDBCParamUsage(HikariDataSource dataSource) throws SQLException { @@ -120,12 +117,12 @@ private void performTestForJDBCParamUsage(HikariDataSource dataSource) throws SQ if (resultUser.endsWith("@%")) { resultUser = resultUser.substring(0, resultUser.length() - 2); } - assertEquals("User from query param is created.", "someuser", resultUser); + assertThat(resultUser).as("User from query param is created.").isEqualTo("someuser"); return true; } ); - assertTrue("The database returned a record as expected", result); + assertThat(result).as("The database returned a record as expected").isTrue(); String databaseQuery = "SELECT DATABASE()"; // Postgres does not have Database() as a function @@ -145,12 +142,12 @@ private void performTestForJDBCParamUsage(HikariDataSource dataSource) throws SQ rs -> { rs.next(); String resultDB = rs.getString(1); - assertEquals("Database name from URL String is used.", "databasename", resultDB); + assertThat(resultDB).as("Database name from URL String is used.").isEqualTo("databasename"); return true; } ); - assertTrue("The database returned a record as expected", result); + assertThat(result).as("The database returned a record as expected").isTrue(); } private void performTestForCharacterEncodingForInitialScriptConnection(HikariDataSource dataSource) @@ -161,16 +158,14 @@ private void performTestForCharacterEncodingForInitialScriptConnection(HikariDat rs -> { rs.next(); String resultSetString = rs.getString(1); - assertEquals( - "A SELECT query succeed and the correct charset has been applied for the init script", - "привет мир", - resultSetString - ); + assertThat(resultSetString) + .as("A basic SELECT query succeeds where the schema has been applied from a script") + .isEqualTo("привет мир"); return true; } ); - assertTrue("The database returned a record as expected", result); + assertThat(result).as("The database returned a record as expected").isTrue(); } /** @@ -195,15 +190,14 @@ private HikariDataSource verifyCharacterSet(String jdbcUrl) throws SQLException rs -> { rs.next(); String resultSetString = rs.getString(2); - assertTrue( - "Passing query parameters to set DB connection encoding is successful", - resultSetString.startsWith("utf8") - ); + assertThat(resultSetString) + .as("Passing query parameters to set DB connection encoding is successful") + .startsWith("utf8"); return true; } ); - assertTrue("The database returned a record as expected", result); + assertThat(result).as("The database returned a record as expected").isTrue(); return dataSource; } @@ -213,10 +207,10 @@ private void performTestForCustomIniFile(HikariDataSource dataSource) throws SQL statement.execute("SELECT @@GLOBAL.innodb_file_format"); ResultSet resultSet = statement.getResultSet(); - assertTrue("The query returns a result", resultSet.next()); + assertThat(resultSet.next()).as("The query returns a result").isTrue(); String result = resultSet.getString(1); - assertEquals("The InnoDB file format has been set by the ini file content", "Barracuda", result); + assertThat(result).as("The InnoDB file format has been set by the ini file content").isEqualTo("Barracuda"); } private HikariDataSource getDataSource(String jdbcUrl, int poolSize) { diff --git a/modules/jdbc/build.gradle b/modules/jdbc/build.gradle index 3a6681aee13..6047a3c0f3e 100644 --- a/modules/jdbc/build.gradle +++ b/modules/jdbc/build.gradle @@ -10,6 +10,5 @@ dependencies { testImplementation 'org.apache.tomcat:tomcat-jdbc:10.0.23' testImplementation 'com.zaxxer:HikariCP-java6:2.3.13' testImplementation 'com.googlecode.junit-toolbox:junit-toolbox:2.4' - testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' testImplementation 'org.assertj:assertj-core:3.23.1' } diff --git a/modules/jdbc/src/test/java/org/testcontainers/jdbc/ConnectionUrlDriversTests.java b/modules/jdbc/src/test/java/org/testcontainers/jdbc/ConnectionUrlDriversTests.java index 9b88e6b503d..2ac8cf88b5d 100644 --- a/modules/jdbc/src/test/java/org/testcontainers/jdbc/ConnectionUrlDriversTests.java +++ b/modules/jdbc/src/test/java/org/testcontainers/jdbc/ConnectionUrlDriversTests.java @@ -8,7 +8,7 @@ import java.util.Arrays; import java.util.Optional; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; /** * This Test class validates that all supported JDBC URL's can be parsed by ConnectionUrl class. @@ -125,9 +125,9 @@ public static Iterable data() { @Test public void test() { ConnectionUrl url = ConnectionUrl.newInstance(jdbcUrl); - assertEquals("Database Type is as expected", databaseType, url.getDatabaseType()); - assertEquals("Image tag is as expected", tag, url.getImageTag()); - assertEquals("Database Host String is as expected", dbHostString, url.getDbHostString()); - assertEquals("Database Name is as expected", databaseName, url.getDatabaseName().orElse("")); + assertThat(url.getDatabaseType()).as("Database Type is as expected").isEqualTo(databaseType); + assertThat(url.getImageTag()).as("Image tag is as expected").isEqualTo(tag); + assertThat(url.getDbHostString()).as("Database Host String is as expected").isEqualTo(dbHostString); + assertThat(url.getDatabaseName().orElse("")).as("Database Name is as expected").isEqualTo(databaseName); } } diff --git a/modules/jdbc/src/test/java/org/testcontainers/jdbc/ConnectionUrlTest.java b/modules/jdbc/src/test/java/org/testcontainers/jdbc/ConnectionUrlTest.java index 6888f881ec4..fa50dbbe76e 100644 --- a/modules/jdbc/src/test/java/org/testcontainers/jdbc/ConnectionUrlTest.java +++ b/modules/jdbc/src/test/java/org/testcontainers/jdbc/ConnectionUrlTest.java @@ -4,9 +4,7 @@ import org.junit.Test; import org.junit.rules.ExpectedException; -import java.util.Optional; - -import static org.rnorth.visibleassertions.VisibleAssertions.*; +import static org.assertj.core.api.Assertions.assertThat; public class ConnectionUrlTest { @@ -18,16 +16,18 @@ public void testConnectionUrl1() { String urlString = "jdbc:tc:mysql:5.7.34://somehostname:3306/databasename?a=b&c=d"; ConnectionUrl url = ConnectionUrl.newInstance(urlString); - assertEquals("Database Type value is as expected", "mysql", url.getDatabaseType()); - assertEquals("Database Image tag value is as expected", "5.7.34", url.getImageTag().get()); - assertEquals("Database Host String is as expected", "somehostname:3306/databasename", url.getDbHostString()); - assertEquals("Query String value is as expected", "?a=b&c=d", url.getQueryString().get()); - assertEquals("Database Host value is as expected", "somehostname", url.getDatabaseHost().get()); - assertEquals("Database Port value is as expected", 3306, url.getDatabasePort().get()); - assertEquals("Database Name value is as expected", "databasename", url.getDatabaseName().get()); - - assertEquals("Parameter a is captured", "b", url.getQueryParameters().get("a")); - assertEquals("Parameter c is captured", "d", url.getQueryParameters().get("c")); + assertThat(url.getDatabaseType()).as("Database Type value is as expected").isEqualTo("mysql"); + assertThat(url.getImageTag()).as("Database Image tag value is as expected").contains("5.7.34"); + assertThat(url.getDbHostString()) + .as("Database Host String is as expected") + .isEqualTo("somehostname:3306/databasename"); + assertThat(url.getQueryString()).as("Query String value is as expected").contains("?a=b&c=d"); + assertThat(url.getDatabaseHost()).as("Database Host value is as expected").contains("somehostname"); + assertThat(url.getDatabasePort()).as("Database Port value is as expected").contains(3306); + assertThat(url.getDatabaseName()).as("Database Name value is as expected").contains("databasename"); + + assertThat(url.getQueryParameters()).as("Parameter a is captured").containsEntry("a", "b"); + assertThat(url.getQueryParameters()).as("Parameter c is captured").containsEntry("c", "d"); } @Test @@ -35,21 +35,23 @@ public void testConnectionUrl2() { String urlString = "jdbc:tc:mysql://somehostname/databasename"; ConnectionUrl url = ConnectionUrl.newInstance(urlString); - assertEquals("Database Type value is as expected", "mysql", url.getDatabaseType()); - assertFalse("Database Image tag value is as expected", url.getImageTag().isPresent()); - assertEquals("Database Host String is as expected", "somehostname/databasename", url.getDbHostString()); - assertEquals("Query String value is as expected", Optional.empty(), url.getQueryString()); - assertEquals("Database Host value is as expected", "somehostname", url.getDatabaseHost().get()); - assertFalse("Database Port is null as expected", url.getDatabasePort().isPresent()); - assertEquals("Database Name value is as expected", "databasename", url.getDatabaseName().get()); - - assertTrue("Connection Parameters set is empty", url.getQueryParameters().isEmpty()); + assertThat(url.getDatabaseType()).as("Database Type value is as expected").isEqualTo("mysql"); + assertThat(url.getImageTag()).as("Database Image tag value is as expected").isNotPresent(); + assertThat(url.getDbHostString()) + .as("Database Host String is as expected") + .isEqualTo("somehostname/databasename"); + assertThat(url.getQueryString()).as("Query String value is as expected").isEmpty(); + assertThat(url.getDatabaseHost()).as("Database Host value is as expected").contains("somehostname"); + assertThat(url.getDatabasePort()).as("Database Port is null as expected").isNotPresent(); + assertThat(url.getDatabaseName()).as("Database Name value is as expected").contains("databasename"); + + assertThat(url.getQueryParameters().isEmpty()).as("Connection Parameters set is empty").isTrue(); } @Test public void testEmptyQueryParameter() { ConnectionUrl url = ConnectionUrl.newInstance("jdbc:tc:mysql://somehostname/databasename?key="); - assertEquals("'key' property value", "", url.getQueryParameters().get("key")); + assertThat(url.getQueryParameters().get("key")).as("'key' property value").isEqualTo(""); } @Test @@ -57,17 +59,13 @@ public void testTmpfsOption() { String urlString = "jdbc:tc:mysql://somehostname/databasename?TC_TMPFS=key:value,key1:value1"; ConnectionUrl url = ConnectionUrl.newInstance(urlString); - assertTrue("Connection Parameters set is empty", url.getQueryParameters().isEmpty()); - assertFalse("Container Parameters set is not empty", url.getContainerParameters().isEmpty()); - assertEquals( - "Container Parameter TC_TMPFS is true", - "key:value,key1:value1", - url.getContainerParameters().get("TC_TMPFS") - ); - assertTrue("tmpfs option key exists", url.getTmpfsOptions().containsKey("key")); - assertEquals("tmpfs option key has correct value", "value", url.getTmpfsOptions().get("key")); - assertTrue("tmpfs option key1 exists", url.getTmpfsOptions().containsKey("key1")); - assertEquals("tmpfs option key1 has correct value", "value1", url.getTmpfsOptions().get("key1")); + assertThat(url.getQueryParameters()).as("Connection Parameters set is empty").isEmpty(); + assertThat(url.getContainerParameters()).as("Container Parameters set is not empty").isNotEmpty(); + assertThat(url.getContainerParameters()) + .as("Container Parameter TC_TMPFS is true") + .containsEntry("TC_TMPFS", "key:value,key1:value1"); + assertThat(url.getTmpfsOptions()).as("tmpfs option key has correct value").containsEntry("key", "value"); + assertThat(url.getTmpfsOptions()).as("tmpfs option key1 has correct value").containsEntry("key1", "value1"); } @Test @@ -76,13 +74,13 @@ public void testInitScriptPathCapture() { "jdbc:tc:mysql:5.7.34://somehostname:3306/databasename?a=b&c=d&TC_INITSCRIPT=somepath/init_mysql.sql"; ConnectionUrl url = ConnectionUrl.newInstance(urlString); - assertEquals("Database Type value is as expected", "somepath/init_mysql.sql", url.getInitScriptPath().get()); - assertEquals("Query String value is as expected", "?a=b&c=d", url.getQueryString().get()); - assertEquals( - "INIT SCRIPT Path exists in Container Parameters", - "somepath/init_mysql.sql", - url.getContainerParameters().get("TC_INITSCRIPT") - ); + assertThat(url.getInitScriptPath()) + .as("Database Type value is as expected") + .contains("somepath/init_mysql.sql"); + assertThat(url.getQueryString()).as("Query String value is as expected").contains("?a=b&c=d"); + assertThat(url.getContainerParameters()) + .as("INIT SCRIPT Path exists in Container Parameters") + .containsEntry("TC_INITSCRIPT", "somepath/init_mysql.sql"); //Parameter sets are unmodifiable thrown.expect(UnsupportedOperationException.class); @@ -96,18 +94,14 @@ public void testInitFunctionCapture() { "jdbc:tc:mysql:5.7.34://somehostname:3306/databasename?a=b&c=d&TC_INITFUNCTION=org.testcontainers.jdbc.JDBCDriverTest::sampleInitFunction"; ConnectionUrl url = ConnectionUrl.newInstance(urlString); - assertTrue("Init Function parameter exists", url.getInitFunction().isPresent()); - - assertEquals( - "Init function class is as expected", - "org.testcontainers.jdbc.JDBCDriverTest", - url.getInitFunction().get().getClassName() - ); - assertEquals( - "Init function class is as expected", - "sampleInitFunction", - url.getInitFunction().get().getMethodName() - ); + assertThat(url.getInitFunction()).as("Init Function parameter exists").isPresent(); + + assertThat(url.getInitFunction().get().getClassName()) + .as("Init function class is as expected") + .isEqualTo("org.testcontainers.jdbc.JDBCDriverTest"); + assertThat(url.getInitFunction().get().getMethodName()) + .as("Init function class is as expected") + .isEqualTo("sampleInitFunction"); } @Test @@ -115,6 +109,6 @@ public void testDaemonCapture() { String urlString = "jdbc:tc:mysql:5.7.34://somehostname:3306/databasename?a=b&c=d&TC_DAEMON=true"; ConnectionUrl url = ConnectionUrl.newInstance(urlString); - assertTrue("Daemon flag is set to true.", url.isInDaemonMode()); + assertThat(url.isInDaemonMode()).as("Daemon flag is set to true.").isTrue(); } } diff --git a/modules/jdbc/src/test/java/org/testcontainers/jdbc/MissingJdbcDriverTest.java b/modules/jdbc/src/test/java/org/testcontainers/jdbc/MissingJdbcDriverTest.java index 68afe8692e8..379b262aa3a 100644 --- a/modules/jdbc/src/test/java/org/testcontainers/jdbc/MissingJdbcDriverTest.java +++ b/modules/jdbc/src/test/java/org/testcontainers/jdbc/MissingJdbcDriverTest.java @@ -9,9 +9,8 @@ import java.sql.SQLException; import java.util.concurrent.atomic.AtomicInteger; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; -import static org.rnorth.visibleassertions.VisibleAssertions.fail; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.fail; public class MissingJdbcDriverTest { @@ -24,12 +23,16 @@ public void shouldFailFastIfNoDriverFound() { fail("The container is expected to fail to start"); } catch (Exception e) { final Throwable rootCause = Throwables.getRootCause(e); - assertTrue("ClassNotFoundException is the root cause", rootCause instanceof ClassNotFoundException); + assertThat(rootCause) + .as("ClassNotFoundException is the root cause") + .isInstanceOf(ClassNotFoundException.class); } finally { container.stop(); } - assertEquals("only one connection attempt should have been made", 1, container.getConnectionAttempts()); + assertThat(container.getConnectionAttempts()) + .as("only one connection attempt should have been made") + .isEqualTo(1); } /** diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/ComposeContainerTests.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/ComposeContainerTests.java index f49aa8732f5..866b76df301 100644 --- a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/ComposeContainerTests.java +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/ComposeContainerTests.java @@ -11,7 +11,7 @@ import java.io.File; -import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; @Testcontainers class ComposeContainerTests { @@ -38,6 +38,6 @@ void running_compose_defined_container_is_accessible_on_configured_port() throws HttpResponse response = client.execute(new HttpGet("http://" + host + ":" + port)); - assertEquals(200, response.getStatusLine().getStatusCode()); + assertThat(response.getStatusLine().getStatusCode()).isEqualTo(200); } } diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/MixedLifecycleTests.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/MixedLifecycleTests.java index 6c44d978b69..e8ce714ab28 100644 --- a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/MixedLifecycleTests.java +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/MixedLifecycleTests.java @@ -4,7 +4,7 @@ import org.testcontainers.containers.MySQLContainer; import org.testcontainers.containers.PostgreSQLContainer; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; // testClass { @Testcontainers @@ -23,8 +23,8 @@ class MixedLifecycleTests { @Test void test() { - assertTrue(MY_SQL_CONTAINER.isRunning()); - assertTrue(postgresqlContainer.isRunning()); + assertThat(MY_SQL_CONTAINER.isRunning()).isTrue(); + assertThat(postgresqlContainer.isRunning()).isTrue(); } } // } diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersExtensionTests.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersExtensionTests.java index 526038853c9..8852524d451 100644 --- a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersExtensionTests.java +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersExtensionTests.java @@ -4,8 +4,7 @@ import org.junit.jupiter.api.extension.ConditionEvaluationResult; import org.junit.jupiter.api.extension.ExtensionContext; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -15,28 +14,28 @@ public class TestcontainersExtensionTests { void whenDisabledWithoutDockerAndDockerIsAvailableTestsAreEnabled() { ConditionEvaluationResult result = new TestTestcontainersExtension(true) .evaluateExecutionCondition(extensionContext(DisabledWithoutDocker.class)); - assertFalse(result.isDisabled()); + assertThat(result.isDisabled()).isFalse(); } @Test void whenDisabledWithoutDockerAndDockerIsUnavailableTestsAreDisabled() { ConditionEvaluationResult result = new TestTestcontainersExtension(false) .evaluateExecutionCondition(extensionContext(DisabledWithoutDocker.class)); - assertTrue(result.isDisabled()); + assertThat(result.isDisabled()).isTrue(); } @Test void whenEnabledWithoutDockerAndDockerIsAvailableTestsAreEnabled() { ConditionEvaluationResult result = new TestTestcontainersExtension(true) .evaluateExecutionCondition(extensionContext(EnabledWithoutDocker.class)); - assertFalse(result.isDisabled()); + assertThat(result.isDisabled()).isFalse(); } @Test void whenEnabledWithoutDockerAndDockerIsUnavailableTestsAreEnabled() { ConditionEvaluationResult result = new TestTestcontainersExtension(false) .evaluateExecutionCondition(extensionContext(EnabledWithoutDocker.class)); - assertFalse(result.isDisabled()); + assertThat(result.isDisabled()).isFalse(); } private ExtensionContext extensionContext(Class clazz) { diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersNestedRestartedContainerTests.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersNestedRestartedContainerTests.java index c41b184defa..f3d81a0dfcd 100644 --- a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersNestedRestartedContainerTests.java +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersNestedRestartedContainerTests.java @@ -4,8 +4,7 @@ import org.junit.jupiter.api.Test; import org.testcontainers.containers.GenericContainer; -import static org.junit.jupiter.api.Assertions.assertNotEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; // testClass { @Testcontainers @@ -24,7 +23,7 @@ class TestcontainersNestedRestartedContainerTests { // testClass { @Test void top_level_container_should_be_running() { - assertTrue(topLevelContainer.isRunning()); + assertThat(topLevelContainer.isRunning()).isTrue(); // }} topLevelContainerId = topLevelContainer.getContainerId(); // testClass {{ @@ -40,14 +39,14 @@ class NestedTestCase { @Test void both_containers_should_be_running() { // top level container is restarted for nested methods - assertTrue(topLevelContainer.isRunning()); + assertThat(topLevelContainer.isRunning()).isTrue(); // nested containers are only available inside their nested class - assertTrue(nestedContainer.isRunning()); + assertThat(nestedContainer.isRunning()).isTrue(); // }}} if (nestedContainerId == null) { nestedContainerId = nestedContainer.getContainerId(); } else { - assertNotEquals(nestedContainerId, nestedContainer.getContainerId()); + assertThat(nestedContainer.getContainerId()).isNotEqualTo(nestedContainerId); } // testClass {{ } @@ -55,23 +54,23 @@ void both_containers_should_be_running() { // } @Test void containers_should_not_be_the_same() { - assertNotEquals(topLevelContainer.getContainerId(), nestedContainer.getContainerId()); + assertThat(nestedContainer.getContainerId()).isNotEqualTo(topLevelContainer.getContainerId()); if (nestedContainerId == null) { nestedContainerId = nestedContainer.getContainerId(); } else { - assertNotEquals(nestedContainerId, nestedContainer.getContainerId()); + assertThat(nestedContainer.getContainerId()).isNotEqualTo(nestedContainerId); } } @Test void ids_should_not_change() { - assertNotEquals(topLevelContainerId, topLevelContainer.getContainerId()); + assertThat(topLevelContainer.getContainerId()).isNotEqualTo(topLevelContainerId); if (nestedContainerId == null) { nestedContainerId = nestedContainer.getContainerId(); } else { - assertNotEquals(nestedContainerId, nestedContainer.getContainerId()); + assertThat(nestedContainer.getContainerId()).isNotEqualTo(nestedContainerId); } } // testClass {{{ diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersNestedSharedContainerTests.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersNestedSharedContainerTests.java index c00e66c2d9d..61a4df2c6ec 100644 --- a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersNestedSharedContainerTests.java +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersNestedSharedContainerTests.java @@ -4,8 +4,7 @@ import org.junit.jupiter.api.Test; import org.testcontainers.containers.GenericContainer; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; @Testcontainers class TestcontainersNestedSharedContainerTests { @@ -20,7 +19,7 @@ class TestcontainersNestedSharedContainerTests { @Test void top_level_container_should_be_running() { - assertTrue(TOP_LEVEL_CONTAINER.isRunning()); + assertThat(TOP_LEVEL_CONTAINER.isRunning()).isTrue(); topLevelContainerId = TOP_LEVEL_CONTAINER.getContainerId(); } @@ -29,12 +28,12 @@ class NestedTestCase { @Test void top_level_containers_should_be_running() { - assertTrue(TOP_LEVEL_CONTAINER.isRunning()); + assertThat(TOP_LEVEL_CONTAINER.isRunning()).isTrue(); } @Test void ids_should_not_change() { - assertEquals(topLevelContainerId, TOP_LEVEL_CONTAINER.getContainerId()); + assertThat(TOP_LEVEL_CONTAINER.getContainerId()).isEqualTo(topLevelContainerId); } } } diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersRestartBetweenTests.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersRestartBetweenTests.java index 46c6045984f..53e6a0968a0 100644 --- a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersRestartBetweenTests.java +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersRestartBetweenTests.java @@ -3,7 +3,7 @@ import org.junit.jupiter.api.Test; import org.testcontainers.containers.GenericContainer; -import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.assertj.core.api.Assertions.assertThat; @Testcontainers class TestcontainersRestartBetweenTests { @@ -19,7 +19,7 @@ void first_test() { if (lastContainerId == null) { lastContainerId = genericContainer.getContainerId(); } else { - assertNotEquals(lastContainerId, genericContainer.getContainerId()); + assertThat(genericContainer.getContainerId()).isNotEqualTo(lastContainerId); } } @@ -28,7 +28,7 @@ void second_test() { if (lastContainerId == null) { lastContainerId = genericContainer.getContainerId(); } else { - assertNotEquals(lastContainerId, genericContainer.getContainerId()); + assertThat(genericContainer.getContainerId()).isNotEqualTo(lastContainerId); } } } diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersSharedContainerTests.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersSharedContainerTests.java index b23b2fe0e53..9bd23e8d20e 100644 --- a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersSharedContainerTests.java +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestcontainersSharedContainerTests.java @@ -4,8 +4,7 @@ import org.junit.jupiter.api.Test; import org.testcontainers.containers.GenericContainer; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; @Testcontainers class TestcontainersSharedContainerTests { @@ -20,7 +19,7 @@ class TestcontainersSharedContainerTests { @BeforeAll static void doSomethingWithAContainer() { - assertTrue(GENERIC_CONTAINER.isRunning()); + assertThat(GENERIC_CONTAINER.isRunning()).isTrue(); } @Test @@ -28,7 +27,7 @@ void first_test() { if (lastContainerId == null) { lastContainerId = GENERIC_CONTAINER.getContainerId(); } else { - assertEquals(lastContainerId, GENERIC_CONTAINER.getContainerId()); + assertThat(GENERIC_CONTAINER.getContainerId()).isEqualTo(lastContainerId); } } @@ -37,7 +36,7 @@ void second_test() { if (lastContainerId == null) { lastContainerId = GENERIC_CONTAINER.getContainerId(); } else { - assertEquals(lastContainerId, GENERIC_CONTAINER.getContainerId()); + assertThat(GENERIC_CONTAINER.getContainerId()).isEqualTo(lastContainerId); } } } diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/inheritance/InheritedTests.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/inheritance/InheritedTests.java index adeaf58675c..f57fde6b419 100644 --- a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/inheritance/InheritedTests.java +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/inheritance/InheritedTests.java @@ -3,7 +3,7 @@ import org.junit.jupiter.api.Test; import org.testcontainers.junit.jupiter.Container; -import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; class InheritedTests extends AbstractTestBase { @@ -12,15 +12,15 @@ class InheritedTests extends AbstractTestBase { @Test void step1() { - assertEquals(1, redisPerClass.getJedis().incr("key")); - assertEquals(1, redisPerTest.getJedis().incr("key")); - assertEquals(1, myRedis.getJedis().incr("key")); + assertThat(redisPerClass.getJedis().incr("key")).isEqualTo(1); + assertThat(redisPerTest.getJedis().incr("key")).isEqualTo(1); + assertThat(myRedis.getJedis().incr("key")).isEqualTo(1); } @Test void step2() { - assertEquals(2, redisPerClass.getJedis().incr("key")); - assertEquals(1, redisPerTest.getJedis().incr("key")); - assertEquals(1, myRedis.getJedis().incr("key")); + assertThat(redisPerClass.getJedis().incr("key")).isEqualTo(2); + assertThat(redisPerTest.getJedis().incr("key")).isEqualTo(1); + assertThat(myRedis.getJedis().incr("key")).isEqualTo(1); } } diff --git a/modules/k3s/src/test/java/org/testcontainers/k3s/OfficialClientK3sContainerTest.java b/modules/k3s/src/test/java/org/testcontainers/k3s/OfficialClientK3sContainerTest.java index 970e2390457..c2504c27dbe 100644 --- a/modules/k3s/src/test/java/org/testcontainers/k3s/OfficialClientK3sContainerTest.java +++ b/modules/k3s/src/test/java/org/testcontainers/k3s/OfficialClientK3sContainerTest.java @@ -6,7 +6,6 @@ import io.kubernetes.client.openapi.models.V1NodeList; import io.kubernetes.client.util.Config; import lombok.extern.slf4j.Slf4j; -import org.assertj.core.api.Assertions; import org.junit.Test; import org.testcontainers.containers.output.Slf4jLogConsumer; import org.testcontainers.utility.DockerImageName; @@ -14,6 +13,8 @@ import java.io.IOException; import java.io.StringReader; +import static org.assertj.core.api.Assertions.assertThat; + @Slf4j public class OfficialClientK3sContainerTest { @@ -37,7 +38,7 @@ public void shouldStartAndHaveListableNode() throws IOException, ApiException { V1NodeList nodes = api.listNode(null, null, null, null, null, null, null, null, null, null); // } - Assertions.assertThat(nodes.getItems()).hasSize(1); + assertThat(nodes.getItems()).hasSize(1); } } } diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 72149520b6a..8702d2e1a83 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -8,5 +8,5 @@ dependencies { testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.273' testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.273' testImplementation 'software.amazon.awssdk:s3:2.17.244' - testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' + testImplementation 'org.assertj:assertj-core:3.23.1' } diff --git a/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LegacyModeTest.java b/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LegacyModeTest.java index c075d2805d6..a535699438f 100644 --- a/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LegacyModeTest.java +++ b/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LegacyModeTest.java @@ -15,9 +15,7 @@ import java.util.Arrays; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.rnorth.visibleassertions.VisibleAssertions.assertNotEquals; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; @RunWith(Enclosed.class) public class LegacyModeTest { @@ -52,25 +50,25 @@ public void samePortIsExposedForAllServices() { localstack.start(); try { - assertTrue("A single port is exposed", localstack.getExposedPorts().size() == 1); - assertEquals( - "Endpoint overrides are different", - localstack.getEndpointOverride(Service.S3).toString(), - localstack.getEndpointOverride(Service.SQS).toString() - ); - assertEquals( - "Endpoint configuration have different endpoints", - new AwsClientBuilder.EndpointConfiguration( - localstack.getEndpointOverride(Service.S3).toString(), - localstack.getRegion() - ) - .getServiceEndpoint(), + assertThat(localstack.getExposedPorts()).as("A single port is exposed").hasSize(1); + assertThat(localstack.getEndpointOverride(Service.SQS).toString()) + .as("Endpoint overrides are different") + .isEqualTo(localstack.getEndpointOverride(Service.S3).toString()); + assertThat( new AwsClientBuilder.EndpointConfiguration( localstack.getEndpointOverride(Service.SQS).toString(), localstack.getRegion() ) .getServiceEndpoint() - ); + ) + .as("Endpoint configuration have different endpoints") + .isEqualTo( + new AwsClientBuilder.EndpointConfiguration( + localstack.getEndpointOverride(Service.S3).toString(), + localstack.getRegion() + ) + .getServiceEndpoint() + ); } finally { localstack.stop(); } @@ -117,25 +115,25 @@ public void differentPortsAreExposed() { localstack.start(); try { - assertTrue("Multiple ports are exposed", localstack.getExposedPorts().size() > 1); - assertNotEquals( - "Endpoint overrides are different", - localstack.getEndpointOverride(Service.S3).toString(), - localstack.getEndpointOverride(Service.SQS).toString() - ); - assertNotEquals( - "Endpoint configuration have different endpoints", - new AwsClientBuilder.EndpointConfiguration( - localstack.getEndpointOverride(Service.S3).toString(), - localstack.getRegion() - ) - .getServiceEndpoint(), + assertThat(localstack.getExposedPorts()).as("Multiple ports are exposed").hasSizeGreaterThan(1); + assertThat(localstack.getEndpointOverride(Service.SQS).toString()) + .as("Endpoint overrides are different") + .isNotEqualTo(localstack.getEndpointOverride(Service.S3).toString()); + assertThat( new AwsClientBuilder.EndpointConfiguration( localstack.getEndpointOverride(Service.SQS).toString(), localstack.getRegion() ) .getServiceEndpoint() - ); + ) + .as("Endpoint configuration have different endpoints") + .isNotEqualTo( + new AwsClientBuilder.EndpointConfiguration( + localstack.getEndpointOverride(Service.S3).toString(), + localstack.getRegion() + ) + .getServiceEndpoint() + ); } finally { localstack.stop(); } diff --git a/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java b/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java index 950f29866ba..a678d30eb80 100644 --- a/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java +++ b/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java @@ -38,14 +38,11 @@ import software.amazon.awssdk.services.s3.S3Client; import java.io.IOException; -import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.util.List; import java.util.Optional; -import static org.hamcrest.Matchers.containsString; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.rnorth.visibleassertions.VisibleAssertions.assertThat; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; /** * Tests for Localstack Container, used both in bridge network (exposed to host) and docker network modes. @@ -101,17 +98,17 @@ public void s3TestOverBridgeNetwork() throws IOException { .stream() .filter(b -> b.getName().equals(bucketName)) .findFirst(); - assertTrue("The created bucket is present", maybeBucket.isPresent()); + assertThat(maybeBucket).as("The created bucket is present").isPresent(); final Bucket bucket = maybeBucket.get(); - assertEquals("The created bucket has the right name", bucketName, bucket.getName()); + assertThat(bucket.getName()).as("The created bucket has the right name").isEqualTo(bucketName); final ObjectListing objectListing = s3.listObjects(bucketName); - assertEquals("The created bucket has 1 item in it", 1, objectListing.getObjectSummaries().size()); + assertThat(objectListing.getObjectSummaries()).as("The created bucket has 1 item in it").hasSize(1); final S3Object object = s3.getObject(bucketName, "bar"); - final String content = IOUtils.toString(object.getObjectContent(), Charset.forName("UTF-8")); - assertEquals("The object can be retrieved", "baz", content); + final String content = IOUtils.toString(object.getObjectContent(), StandardCharsets.UTF_8); + assertThat(content).as("The object can be retrieved").isEqualTo("baz"); } @Test @@ -131,10 +128,9 @@ public void s3TestUsingAwsSdkV2() { final String bucketName = "foov2"; s3.createBucket(b -> b.bucket(bucketName)); - assertTrue( - "New bucket was created", - s3.listBuckets().buckets().stream().anyMatch(b -> b.name().equals(bucketName)) - ); + assertThat(s3.listBuckets().buckets().stream().anyMatch(b -> b.name().equals(bucketName))) + .as("New bucket was created") + .isTrue(); } @Test @@ -156,16 +152,14 @@ public void sqsTestOverBridgeNetwork() { CreateQueueResult queueResult = sqs.createQueue("baz"); String fooQueueUrl = queueResult.getQueueUrl(); - assertThat( - "Created queue has external hostname URL", - fooQueueUrl, - containsString( + assertThat(fooQueueUrl) + .as("Created queue has external hostname URL") + .contains( "http://" + DockerClientFactory.instance().dockerHostIpAddress() + ":" + localstack.getMappedPort(LocalStackContainer.PORT) - ) - ); + ); sqs.sendMessage(fooQueueUrl, "test"); final long messageCount = sqs @@ -174,7 +168,7 @@ public void sqsTestOverBridgeNetwork() { .stream() .filter(message -> message.getBody().equals("test")) .count(); - assertEquals("the sent message can be received", 1L, messageCount); + assertThat(messageCount).as("the sent message can be received").isEqualTo(1L); } @Test @@ -197,8 +191,8 @@ public void cloudWatchLogsTestOverBridgeNetwork() { logs.createLogGroup(new CreateLogGroupRequest("foo")); List groups = logs.describeLogGroups().getLogGroups(); - assertEquals("One log group should be created", 1, groups.size()); - assertEquals("Name of created log group is [foo]", "foo", groups.get(0).getLogGroupName()); + assertThat(groups).as("One log group should be created").hasSize(1); + assertThat(groups.get(0).getLogGroupName()).as("Name of created log group is [foo]").isEqualTo("foo"); } @Test @@ -223,34 +217,32 @@ public void kmsKeyCreationTest() { CreateKeyRequest req = new CreateKeyRequest().withDescription(desc).withTags(createdByTag); CreateKeyResult key = awskms.createKey(req); - assertEquals( - "AWS KMS Customer Managed Key should be created ", - key.getKeyMetadata().getDescription(), - desc - ); + assertThat(desc) + .as("AWS KMS Customer Managed Key should be created ") + .isEqualTo(key.getKeyMetadata().getDescription()); } @Test public void samePortIsExposedForAllServices() { - assertTrue("A single port is exposed", localstack.getExposedPorts().size() == 1); - assertEquals( - "Endpoint overrides are different", - localstack.getEndpointOverride(Service.S3).toString(), - localstack.getEndpointOverride(Service.SQS).toString() - ); - assertEquals( - "Endpoint configuration have different endpoints", - new AwsClientBuilder.EndpointConfiguration( - localstack.getEndpointOverride(Service.S3).toString(), - localstack.getRegion() - ) - .getServiceEndpoint(), + assertThat(localstack.getExposedPorts()).as("A single port is exposed").hasSize(1); + assertThat(localstack.getEndpointOverride(Service.SQS).toString()) + .as("Endpoint overrides are different") + .isEqualTo(localstack.getEndpointOverride(Service.S3).toString()); + assertThat( new AwsClientBuilder.EndpointConfiguration( localstack.getEndpointOverride(Service.SQS).toString(), localstack.getRegion() ) .getServiceEndpoint() - ); + ) + .as("Endpoint configuration have different endpoints") + .isEqualTo( + new AwsClientBuilder.EndpointConfiguration( + localstack.getEndpointOverride(Service.S3).toString(), + localstack.getRegion() + ) + .getServiceEndpoint() + ); } } @@ -292,11 +284,9 @@ public void sqsTestOverDockerNetwork() throws Exception { "sqs create-queue --queue-name baz" ); - assertThat( - "Created queue has external hostname URL", - queueCreationResponse, - containsString("http://localstack:" + LocalStackContainer.PORT) - ); + assertThat(queueCreationResponse) + .as("Created queue has external hostname URL") + .contains("http://localstack:" + LocalStackContainer.PORT); runAwsCliAgainstDockerNetworkContainer( String.format( @@ -313,7 +303,7 @@ public void sqsTestOverDockerNetwork() throws Exception { ) ); - assertTrue("the sent message can be received", message.contains("\"Body\": \"test\"")); + assertThat(message).as("the sent message can be received").contains("\"Body\": \"test\""); } @Test @@ -356,11 +346,9 @@ public void s3EndpointHasProperRegion() { localstack.getEndpointOverride(Service.S3).toString(), localstack.getRegion() ); - assertEquals( - "The endpoint configuration has right region", - region, - endpointConfiguration.getSigningRegion() - ); + assertThat(endpointConfiguration.getSigningRegion()) + .as("The endpoint configuration has right region") + .isEqualTo(region); } } } diff --git a/modules/mariadb/src/test/java/org/testcontainers/junit/mariadb/SimpleMariaDBTest.java b/modules/mariadb/src/test/java/org/testcontainers/junit/mariadb/SimpleMariaDBTest.java index 21a48b6e73c..e403a09b382 100644 --- a/modules/mariadb/src/test/java/org/testcontainers/junit/mariadb/SimpleMariaDBTest.java +++ b/modules/mariadb/src/test/java/org/testcontainers/junit/mariadb/SimpleMariaDBTest.java @@ -9,11 +9,8 @@ import java.sql.ResultSet; import java.sql.SQLException; -import static org.hamcrest.Matchers.containsString; -import static org.junit.Assert.assertThat; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assume.assumeFalse; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; public class SimpleMariaDBTest extends AbstractContainerDatabaseTest { @@ -25,7 +22,7 @@ public void testSimple() throws SQLException { ResultSet resultSet = performQuery(mariadb, "SELECT 1"); int resultSetInt = resultSet.getInt(1); - assertEquals("A basic SELECT query succeeds", 1, resultSetInt); + assertThat(resultSetInt).as("A basic SELECT query succeeds").isEqualTo(1); } } @@ -41,10 +38,9 @@ public void testSpecificVersion() throws SQLException { ResultSet resultSet = performQuery(mariadbOldVersion, "SELECT VERSION()"); String resultSetString = resultSet.getString(1); - assertTrue( - "The database version can be set using a container rule parameter", - resultSetString.startsWith("5.5.51") - ); + assertThat(resultSetString) + .as("The database version can be set using a container rule parameter") + .startsWith("5.5.51"); } } @@ -63,7 +59,7 @@ public void testMariaDBWithCustomIniFile() throws SQLException { ResultSet resultSet = performQuery(mariadbCustomConfig, "SELECT @@GLOBAL.innodb_file_format"); String result = resultSet.getString(1); - assertEquals("The InnoDB file format has been set by the ini file content", "Barracuda", result); + assertThat(result).as("The InnoDB file format has been set by the ini file content").isEqualTo("Barracuda"); } } @@ -77,7 +73,7 @@ public void testMariaDBWithCommandOverride() throws SQLException { ResultSet resultSet = performQuery(mariadbCustomConfig, "show variables like 'auto_increment_increment'"); String result = resultSet.getString("Value"); - assertEquals("Auto increment increment should be overriden by command line", "10", result); + assertThat(result).as("Auto increment increment should be overriden by command line").isEqualTo("10"); } } @@ -90,10 +86,10 @@ public void testWithAdditionalUrlParamInJdbcUrl() { try { mariaDBContainer.start(); String jdbcUrl = mariaDBContainer.getJdbcUrl(); - assertThat(jdbcUrl, containsString("?")); - assertThat(jdbcUrl, containsString("&")); - assertThat(jdbcUrl, containsString("rewriteBatchedStatements=true")); - assertThat(jdbcUrl, containsString("connectTimeout=40000")); + assertThat(jdbcUrl).contains("?"); + assertThat(jdbcUrl).contains("&"); + assertThat(jdbcUrl).contains("rewriteBatchedStatements=true"); + assertThat(jdbcUrl).contains("connectTimeout=40000"); } finally { mariaDBContainer.stop(); } diff --git a/modules/mockserver/build.gradle b/modules/mockserver/build.gradle index 9ea504ca74a..2156ef045cd 100644 --- a/modules/mockserver/build.gradle +++ b/modules/mockserver/build.gradle @@ -4,5 +4,5 @@ dependencies { api project(':testcontainers') testImplementation 'org.mock-server:mockserver-client-java:5.5.4' - testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' + testImplementation 'org.assertj:assertj-core:3.23.1' } diff --git a/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerRuleTest.java b/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerRuleTest.java index 3f34d000603..84d472f5e75 100644 --- a/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerRuleTest.java +++ b/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerRuleTest.java @@ -5,10 +5,9 @@ import org.mockserver.client.MockServerClient; import org.testcontainers.utility.DockerImageName; -import static org.hamcrest.Matchers.containsString; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockserver.model.HttpRequest.request; import static org.mockserver.model.HttpResponse.response; -import static org.rnorth.visibleassertions.VisibleAssertions.assertThat; public class MockServerContainerRuleTest { @@ -37,10 +36,8 @@ public void shouldReturnExpectation() throws Exception { // } // spotless:on - assertThat( - "Expectation returns expected response body", - SimpleHttpClient.responseFromMockserver(mockServer, "/person?name=peter"), - containsString("Peter the person") - ); + assertThat(SimpleHttpClient.responseFromMockserver(mockServer, "/person?name=peter")) + .as("Expectation returns expected response body") + .contains("Peter the person"); } } diff --git a/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerTest.java b/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerTest.java index d038b111824..6ddce4b6cbf 100644 --- a/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerTest.java +++ b/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerTest.java @@ -4,11 +4,9 @@ import org.mockserver.client.MockServerClient; import org.testcontainers.utility.DockerImageName; -import static org.hamcrest.Matchers.equalTo; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockserver.model.HttpRequest.request; import static org.mockserver.model.HttpResponse.response; -import static org.rnorth.visibleassertions.VisibleAssertions.assertThat; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; public class MockServerContainerTest { @@ -30,15 +28,13 @@ public void shouldCallActualMockserverVersion() throws Exception { MockServerClient client = new MockServerClient(mockServer.getHost(), mockServer.getServerPort()); - assertTrue("Mockserver running", client.isRunning()); + assertThat(client.isRunning()).as("Mockserver running").isTrue(); client.when(request().withPath("/hello")).respond(response().withBody(expectedBody)); - assertThat( - "MockServer returns correct result", - SimpleHttpClient.responseFromMockserver(mockServer, "/hello"), - equalTo(expectedBody) - ); + assertThat(SimpleHttpClient.responseFromMockserver(mockServer, "/hello")) + .as("MockServer returns correct result") + .isEqualTo(expectedBody); } } diff --git a/modules/mockserver/src/test/resources/logback-test.xml b/modules/mockserver/src/test/resources/logback-test.xml new file mode 100644 index 00000000000..83ef7a1a3ef --- /dev/null +++ b/modules/mockserver/src/test/resources/logback-test.xml @@ -0,0 +1,16 @@ + + + + + + %d{HH:mm:ss.SSS} %-5level %logger - %msg%n + + + + + + + + + diff --git a/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/CustomPasswordMSSQLServerTest.java b/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/CustomPasswordMSSQLServerTest.java index 49bae13d9e3..97904c6b7d1 100644 --- a/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/CustomPasswordMSSQLServerTest.java +++ b/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/CustomPasswordMSSQLServerTest.java @@ -10,7 +10,7 @@ import java.util.Arrays; import java.util.Collection; -import static org.rnorth.visibleassertions.VisibleAssertions.fail; +import static org.assertj.core.api.Assertions.fail; /** * Tests if the password passed to the container satisfied the password policy described at diff --git a/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/CustomizableMSSQLServerTest.java b/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/CustomizableMSSQLServerTest.java index 21ea5380801..4cfc34f9e73 100644 --- a/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/CustomizableMSSQLServerTest.java +++ b/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/CustomizableMSSQLServerTest.java @@ -8,7 +8,7 @@ import java.sql.ResultSet; import java.sql.SQLException; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; public class CustomizableMSSQLServerTest extends AbstractContainerDatabaseTest { @@ -26,7 +26,7 @@ public void testSqlServerConnection() throws SQLException { ResultSet resultSet = performQuery(mssqlServerContainer, mssqlServerContainer.getTestQueryString()); int resultSetInt = resultSet.getInt(1); - assertEquals("A basic SELECT query succeeds", 1, resultSetInt); + assertThat(resultSetInt).as("A basic SELECT query succeeds").isEqualTo(1); } } } diff --git a/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/SimpleMSSQLServerTest.java b/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/SimpleMSSQLServerTest.java index 36d215c8757..15edbaa2ca1 100644 --- a/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/SimpleMSSQLServerTest.java +++ b/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/SimpleMSSQLServerTest.java @@ -11,9 +11,7 @@ import javax.sql.DataSource; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.containsString; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; public class SimpleMSSQLServerTest extends AbstractContainerDatabaseTest { @@ -26,7 +24,7 @@ public void testSimple() throws SQLException { ResultSet resultSet = performQuery(mssqlServer, "SELECT 1"); int resultSetInt = resultSet.getInt(1); - assertEquals("A basic SELECT query succeeds", 1, resultSetInt); + assertThat(resultSetInt).as("A basic SELECT query succeeds").isEqualTo(1); } } @@ -40,7 +38,7 @@ public void testWithAdditionalUrlParamInJdbcUrl() { mssqlServer.start(); String jdbcUrl = mssqlServer.getJdbcUrl(); - assertThat(jdbcUrl, containsString(";integratedSecurity=false;applicationName=MyApp")); + assertThat(jdbcUrl).contains(";integratedSecurity=false;applicationName=MyApp"); } } @@ -63,7 +61,7 @@ public void testSetupDatabase() throws SQLException { resultSet.next(); int resultSetInt = resultSet.getInt("ID"); - assertEquals("A basic SELECT query succeeds", 3, resultSetInt); + assertThat(resultSetInt).as("A basic SELECT query succeeds").isEqualTo(3); } } } diff --git a/modules/mysql/src/test/java/org/testcontainers/jdbc/mysql/JDBCDriverWithPoolTest.java b/modules/mysql/src/test/java/org/testcontainers/jdbc/mysql/JDBCDriverWithPoolTest.java index a30f3f59888..254f58807b7 100644 --- a/modules/mysql/src/test/java/org/testcontainers/jdbc/mysql/JDBCDriverWithPoolTest.java +++ b/modules/mysql/src/test/java/org/testcontainers/jdbc/mysql/JDBCDriverWithPoolTest.java @@ -21,7 +21,7 @@ import javax.sql.DataSource; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; /** * This test belongs in the jdbc module, as it is focused on testing the behaviour of {@link org.testcontainers.containers.JdbcDatabaseContainer}. @@ -82,7 +82,7 @@ public void testMySQLWithConnectionPoolUsingSameContainer() throws SQLException, return rs.getInt(1); } ); - assertEquals("Reuse of a datasource points to the same DB container", 100, count); + assertThat(count).as("Reuse of a datasource points to the same DB container").isEqualTo(100); int sum = new QueryRunner(dataSource) .query( @@ -93,7 +93,7 @@ public void testMySQLWithConnectionPoolUsingSameContainer() throws SQLException, } ); // 100 records * 5 = 500 expected - assertEquals("Reuse of a datasource points to the same DB container", 500, sum); + assertThat(sum).as("Reuse of a datasource points to the same DB container").isEqualTo(500); } private static DataSource getTomcatDataSourceWithDriverClassName() { diff --git a/modules/mysql/src/test/java/org/testcontainers/junit/mysql/CustomizableMysqlTest.java b/modules/mysql/src/test/java/org/testcontainers/junit/mysql/CustomizableMysqlTest.java index 20b49d19201..f0a4f5d24ea 100644 --- a/modules/mysql/src/test/java/org/testcontainers/junit/mysql/CustomizableMysqlTest.java +++ b/modules/mysql/src/test/java/org/testcontainers/junit/mysql/CustomizableMysqlTest.java @@ -8,7 +8,7 @@ import java.sql.ResultSet; import java.sql.SQLException; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; public class CustomizableMysqlTest extends AbstractContainerDatabaseTest { @@ -33,7 +33,7 @@ public void testSimple() throws SQLException { ResultSet resultSet = performQuery(mysql, "SELECT 1"); int resultSetInt = resultSet.getInt(1); - assertEquals("A basic SELECT query succeeds", 1, resultSetInt); + assertThat(resultSetInt).as("A basic SELECT query succeeds").isEqualTo(1); } } } diff --git a/modules/mysql/src/test/java/org/testcontainers/junit/mysql/MultiVersionMySQLTest.java b/modules/mysql/src/test/java/org/testcontainers/junit/mysql/MultiVersionMySQLTest.java index 735af251236..8a6c17c538e 100644 --- a/modules/mysql/src/test/java/org/testcontainers/junit/mysql/MultiVersionMySQLTest.java +++ b/modules/mysql/src/test/java/org/testcontainers/junit/mysql/MultiVersionMySQLTest.java @@ -11,7 +11,7 @@ import java.sql.ResultSet; import java.sql.SQLException; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; @RunWith(Parameterized.class) public class MultiVersionMySQLTest extends AbstractContainerDatabaseTest { @@ -35,11 +35,9 @@ public void versionCheckTest() throws SQLException { final ResultSet resultSet = performQuery(mysql, "SELECT VERSION()"); final String resultSetString = resultSet.getString(1); - assertEquals( - "The database version can be set using a container rule parameter", - dockerImageName.getVersionPart(), - resultSetString - ); + assertThat(resultSetString) + .as("The database version can be set using a container rule parameter") + .isEqualTo(dockerImageName.getVersionPart()); } } } diff --git a/modules/mysql/src/test/java/org/testcontainers/junit/mysql/SimpleMySQLTest.java b/modules/mysql/src/test/java/org/testcontainers/junit/mysql/SimpleMySQLTest.java index 6441724c443..146010058ac 100644 --- a/modules/mysql/src/test/java/org/testcontainers/junit/mysql/SimpleMySQLTest.java +++ b/modules/mysql/src/test/java/org/testcontainers/junit/mysql/SimpleMySQLTest.java @@ -19,12 +19,9 @@ import java.time.ZonedDateTime; import java.time.temporal.ChronoUnit; -import static org.hamcrest.Matchers.containsString; -import static org.junit.Assert.assertThat; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.fail; import static org.junit.Assume.assumeFalse; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; -import static org.rnorth.visibleassertions.VisibleAssertions.fail; public class SimpleMySQLTest extends AbstractContainerDatabaseTest { @@ -59,7 +56,7 @@ public void testSimple() throws SQLException { ResultSet resultSet = performQuery(mysql, "SELECT 1"); int resultSetInt = resultSet.getInt(1); - assertEquals("A basic SELECT query succeeds", 1, resultSetInt); + assertThat(resultSetInt).as("A basic SELECT query succeeds").isEqualTo(1); } } @@ -75,10 +72,9 @@ public void testSpecificVersion() throws SQLException { ResultSet resultSet = performQuery(mysqlOldVersion, "SELECT VERSION()"); String resultSetString = resultSet.getString(1); - assertTrue( - "The database version can be set using a container rule parameter", - resultSetString.startsWith("5.6") - ); + assertThat(resultSetString) + .as("The database version can be set using a container rule parameter") + .startsWith("5.6"); } } @@ -95,7 +91,7 @@ public void testMySQLWithCustomIniFile() throws SQLException { ResultSet resultSet = performQuery(mysqlCustomConfig, "SELECT @@GLOBAL.innodb_file_format"); String result = resultSet.getString(1); - assertEquals("The InnoDB file format has been set by the ini file content", "Barracuda", result); + assertThat(result).as("The InnoDB file format has been set by the ini file content").isEqualTo("Barracuda"); } } @@ -110,7 +106,7 @@ public void testCommandOverride() throws SQLException { ResultSet resultSet = performQuery(mysqlCustomConfig, "show variables like 'auto_increment_increment'"); String result = resultSet.getString("Value"); - assertEquals("Auto increment increment should be overriden by command line", "42", result); + assertThat(result).as("Auto increment increment should be overriden by command line").isEqualTo("42"); } } @@ -126,7 +122,7 @@ public void testExplicitInitScript() throws SQLException { ResultSet resultSet = performQuery(container, "SELECT foo FROM bar"); String firstColumnValue = resultSet.getString(1); - assertEquals("Value from init script should equal real value", "hello world", firstColumnValue); + assertThat(firstColumnValue).as("Value from init script should equal real value").isEqualTo("hello world"); } } @@ -159,7 +155,7 @@ public void testEmptyPasswordWithRootUser() throws SQLException { ResultSet resultSet = performQuery(mysql, "SELECT 1"); int resultSetInt = resultSet.getInt(1); - assertEquals("A basic SELECT query succeeds", 1, resultSetInt); + assertThat(resultSetInt).as("A basic SELECT query succeeds").isEqualTo(1); } } @@ -191,7 +187,7 @@ public void testWithAdditionalUrlParamTimeZone() throws SQLException { actualDateTime, expectedDateTime ); - assertTrue(message, actualDateTime.equals(expectedDateTime)); + assertThat(actualDateTime).as(message).isEqualTo(expectedDateTime); } } finally { mysql.stop(); @@ -216,7 +212,7 @@ public void testWithAdditionalUrlParamMultiQueries() throws SQLException { try (ResultSet resultSet = statement.getResultSet()) { resultSet.next(); String firstColumnValue = resultSet.getString(1); - assertEquals("Value from bar should equal real value", "hello world", firstColumnValue); + assertThat(firstColumnValue).as("Value from bar should equal real value").isEqualTo("hello world"); } } finally { mysql.stop(); @@ -233,10 +229,10 @@ public void testWithAdditionalUrlParamInJdbcUrl() { try { mysql.start(); String jdbcUrl = mysql.getJdbcUrl(); - assertThat(jdbcUrl, containsString("?")); - assertThat(jdbcUrl, containsString("&")); - assertThat(jdbcUrl, containsString("rewriteBatchedStatements=true")); - assertThat(jdbcUrl, containsString("allowMultiQueries=true")); + assertThat(jdbcUrl).contains("?"); + assertThat(jdbcUrl).contains("&"); + assertThat(jdbcUrl).contains("rewriteBatchedStatements=true"); + assertThat(jdbcUrl).contains("allowMultiQueries=true"); } finally { mysql.stop(); } diff --git a/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerTest.java b/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerTest.java index 20cd38dc70c..de5420afd88 100644 --- a/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerTest.java +++ b/modules/neo4j/src/test/java/org/testcontainers/containers/Neo4jContainerTest.java @@ -1,6 +1,5 @@ package org.testcontainers.containers; -import org.assertj.core.api.Assertions; import org.junit.Test; import org.neo4j.driver.AuthToken; import org.neo4j.driver.AuthTokens; @@ -13,8 +12,7 @@ import java.util.Collections; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.assertj.core.api.Assertions.*; import static org.assertj.core.api.Assumptions.assumeThat; /** @@ -64,16 +62,14 @@ public void shouldCopyDatabase() { @Test public void shouldFailOnCopyDatabaseForDefaultNeo4j4Image() { - Assertions - .assertThatIllegalArgumentException() + assertThatIllegalArgumentException() .isThrownBy(() -> new Neo4jContainer<>().withDatabase(MountableFile.forClasspathResource("/test-graph.db"))) .withMessage("Copying database folder is not supported for Neo4j instances with version 4.0 or higher."); } @Test public void shouldFailOnCopyDatabaseForCustomNeo4j4Image() { - Assertions - .assertThatIllegalArgumentException() + assertThatIllegalArgumentException() .isThrownBy(() -> { new Neo4jContainer<>("neo4j:4.4.1").withDatabase(MountableFile.forClasspathResource("/test-graph.db")); }) @@ -82,8 +78,7 @@ public void shouldFailOnCopyDatabaseForCustomNeo4j4Image() { @Test public void shouldFailOnCopyDatabaseForCustomNonSemverNeo4j4Image() { - Assertions - .assertThatIllegalArgumentException() + assertThatIllegalArgumentException() .isThrownBy(() -> { new Neo4jContainer<>("neo4j:latest").withDatabase(MountableFile.forClasspathResource("/test-graph.db")); }) diff --git a/modules/nginx/build.gradle b/modules/nginx/build.gradle index 288b45b027a..aee69e253ad 100644 --- a/modules/nginx/build.gradle +++ b/modules/nginx/build.gradle @@ -3,5 +3,5 @@ description = "Testcontainers :: Nginx" dependencies { api project(':testcontainers') compileOnly 'org.jetbrains:annotations:23.0.0' - testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' + testImplementation 'org.assertj:assertj-core:3.23.1' } diff --git a/modules/nginx/src/test/java/org/testcontainers/junit/SimpleNginxTest.java b/modules/nginx/src/test/java/org/testcontainers/junit/SimpleNginxTest.java index c1ba3d30684..24e913cf281 100644 --- a/modules/nginx/src/test/java/org/testcontainers/junit/SimpleNginxTest.java +++ b/modules/nginx/src/test/java/org/testcontainers/junit/SimpleNginxTest.java @@ -12,8 +12,7 @@ import java.net.URL; import java.net.URLConnection; -import static org.hamcrest.Matchers.containsString; -import static org.rnorth.visibleassertions.VisibleAssertions.*; +import static org.assertj.core.api.Assertions.assertThat; /** * @author richardnorth @@ -55,11 +54,9 @@ public void testSimple() throws Exception { // getFromNginxServer { URL baseUrl = nginx.getBaseUrl("http", 80); - assertThat( - "An HTTP GET from the Nginx server returns the index.html from the custom content directory", - responseFromNginx(baseUrl), - containsString("Hello World!") - ); + assertThat(responseFromNginx(baseUrl)) + .as("An HTTP GET from the Nginx server returns the index.html from the custom content directory") + .contains("Hello World!"); // } } diff --git a/modules/oracle-xe/src/test/java/org/testcontainers/containers/jdbc/OracleJDBCDriverTest.java b/modules/oracle-xe/src/test/java/org/testcontainers/containers/jdbc/OracleJDBCDriverTest.java index 003941f4dfc..5e44f0d368c 100644 --- a/modules/oracle-xe/src/test/java/org/testcontainers/containers/jdbc/OracleJDBCDriverTest.java +++ b/modules/oracle-xe/src/test/java/org/testcontainers/containers/jdbc/OracleJDBCDriverTest.java @@ -9,7 +9,7 @@ import java.sql.ResultSet; import java.sql.SQLException; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; public class OracleJDBCDriverTest { @@ -28,7 +28,7 @@ private void performSimpleTest(String jdbcUrl) throws SQLException { public Object handle(ResultSet rs) throws SQLException { rs.next(); int resultSetInt = rs.getInt(1); - assertEquals("A basic SELECT query succeeds", 1, resultSetInt); + assertThat(resultSetInt).as("A basic SELECT query succeeds").isEqualTo(1); return true; } } diff --git a/modules/orientdb/src/test/java/org/testcontainers/containers/OrientDBContainerTest.java b/modules/orientdb/src/test/java/org/testcontainers/containers/OrientDBContainerTest.java index 675218637ce..6cf0e2e8ea9 100644 --- a/modules/orientdb/src/test/java/org/testcontainers/containers/OrientDBContainerTest.java +++ b/modules/orientdb/src/test/java/org/testcontainers/containers/OrientDBContainerTest.java @@ -4,7 +4,7 @@ import org.junit.Test; import org.testcontainers.utility.DockerImageName; -import static org.assertj.core.api.Assertions.*; +import static org.assertj.core.api.Assertions.assertThat; /** * @author robfrank diff --git a/modules/postgresql/src/test/java/org/testcontainers/containers/PostgreSQLConnectionURLTest.java b/modules/postgresql/src/test/java/org/testcontainers/containers/PostgreSQLConnectionURLTest.java index 3be7685ea27..275517250b6 100644 --- a/modules/postgresql/src/test/java/org/testcontainers/containers/PostgreSQLConnectionURLTest.java +++ b/modules/postgresql/src/test/java/org/testcontainers/containers/PostgreSQLConnectionURLTest.java @@ -3,10 +3,8 @@ import org.junit.Test; import org.testcontainers.PostgreSQLTestImages; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.rnorth.visibleassertions.VisibleAssertions.assertFalse; -import static org.rnorth.visibleassertions.VisibleAssertions.assertThrows; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; public class PostgreSQLConnectionURLTest { @@ -16,12 +14,11 @@ public void shouldCorrectlyAppendQueryString() { String connectionUrl = postgres.constructUrlForConnection("?stringtype=unspecified&stringtype=unspecified"); String queryString = connectionUrl.substring(connectionUrl.indexOf('?')); - assertTrue( - "Query String contains expected params", - queryString.contains("?stringtype=unspecified&stringtype=unspecified") - ); - assertEquals("Query String starts with '?'", 0, queryString.indexOf('?')); - assertFalse("Query String does not contain extra '?'", queryString.substring(1).contains("?")); + assertThat(queryString) + .as("Query String contains expected params") + .contains("?stringtype=unspecified&stringtype=unspecified"); + assertThat(queryString.indexOf('?')).as("Query String starts with '?'").isZero(); + assertThat(queryString.substring(1)).as("Query String does not contain extra '?'").doesNotContain("?"); } @Test @@ -30,12 +27,11 @@ public void shouldCorrectlyAppendQueryStringWhenNoBaseParams() { String connectionUrl = postgres.constructUrlForConnection("?stringtype=unspecified&stringtype=unspecified"); String queryString = connectionUrl.substring(connectionUrl.indexOf('?')); - assertTrue( - "Query String contains expected params", - queryString.contains("?stringtype=unspecified&stringtype=unspecified") - ); - assertEquals("Query String starts with '?'", 0, queryString.indexOf('?')); - assertFalse("Query String does not contain extra '?'", queryString.substring(1).contains("?")); + assertThat(queryString) + .as("Query String contains expected params") + .contains("?stringtype=unspecified&stringtype=unspecified"); + assertThat(queryString.indexOf('?')).as("Query String starts with '?'").isZero(); + assertThat(queryString.substring(1)).as("Query String does not contain extra '?'").doesNotContain("?"); } @Test @@ -43,16 +39,18 @@ public void shouldReturnOriginalURLWhenEmptyQueryString() { PostgreSQLContainer postgres = new FixedJdbcUrlPostgreSQLContainer(); String connectionUrl = postgres.constructUrlForConnection(""); - assertTrue("Query String remains unchanged", postgres.getJdbcUrl().equals(connectionUrl)); + assertThat(postgres.getJdbcUrl()).as("Query String remains unchanged").isEqualTo(connectionUrl); } @Test public void shouldRejectInvalidQueryString() { - assertThrows( - "Fails when invalid query string provided", - IllegalArgumentException.class, - () -> new NoParamsUrlPostgreSQLContainer().constructUrlForConnection("stringtype=unspecified") - ); + assertThat( + catchThrowable(() -> { + new NoParamsUrlPostgreSQLContainer().constructUrlForConnection("stringtype=unspecified"); + }) + ) + .as("Fails when invalid query string provided") + .isInstanceOf(IllegalArgumentException.class); } static class FixedJdbcUrlPostgreSQLContainer extends PostgreSQLContainer { diff --git a/modules/postgresql/src/test/java/org/testcontainers/containers/TimescaleDBContainerTest.java b/modules/postgresql/src/test/java/org/testcontainers/containers/TimescaleDBContainerTest.java index 74d60b2d32d..f49c7dc6aa7 100644 --- a/modules/postgresql/src/test/java/org/testcontainers/containers/TimescaleDBContainerTest.java +++ b/modules/postgresql/src/test/java/org/testcontainers/containers/TimescaleDBContainerTest.java @@ -6,8 +6,7 @@ import java.sql.ResultSet; import java.sql.SQLException; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.rnorth.visibleassertions.VisibleAssertions.assertNotEquals; +import static org.assertj.core.api.Assertions.assertThat; public class TimescaleDBContainerTest extends AbstractContainerDatabaseTest { @@ -18,7 +17,7 @@ public void testSimple() throws SQLException { ResultSet resultSet = performQuery(postgres, "SELECT 1"); int resultSetInt = resultSet.getInt(1); - assertEquals("A basic SELECT query succeeds", 1, resultSetInt); + assertThat(resultSetInt).as("A basic SELECT query succeeds").isEqualTo(1); } } @@ -36,7 +35,7 @@ public void testCommandOverride() throws SQLException { "SELECT current_setting('max_connections')" ); String result = resultSet.getString(1); - assertEquals("max_connections should be overriden", "42", result); + assertThat(result).as("max_connections should be overriden").isEqualTo("42"); } } @@ -55,7 +54,7 @@ public void testUnsetCommand() throws SQLException { "SELECT current_setting('max_connections')" ); String result = resultSet.getString(1); - assertNotEquals("max_connections should not be overriden", "42", result); + assertThat(result).as("max_connections should not be overriden").isNotEqualTo("42"); } } @@ -71,7 +70,7 @@ public void testExplicitInitScript() throws SQLException { ResultSet resultSet = performQuery(postgres, "SELECT foo FROM bar"); String firstColumnValue = resultSet.getString(1); - assertEquals("Value from init script should equal real value", "hello world", firstColumnValue); + assertThat(firstColumnValue).as("Value from init script should equal real value").isEqualTo("hello world"); } } } diff --git a/modules/postgresql/src/test/java/org/testcontainers/jdbc/DatabaseDriverShutdownTest.java b/modules/postgresql/src/test/java/org/testcontainers/jdbc/DatabaseDriverShutdownTest.java index 5acbd875c23..ed519ed2e4b 100644 --- a/modules/postgresql/src/test/java/org/testcontainers/jdbc/DatabaseDriverShutdownTest.java +++ b/modules/postgresql/src/test/java/org/testcontainers/jdbc/DatabaseDriverShutdownTest.java @@ -8,9 +8,7 @@ import java.sql.DriverManager; import java.sql.SQLException; -import static org.rnorth.visibleassertions.VisibleAssertions.assertNotNull; -import static org.rnorth.visibleassertions.VisibleAssertions.assertNull; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; /** * This test belongs in the jdbc module, as it is focused on testing the behaviour of {@link org.testcontainers.containers.JdbcDatabaseContainer}. @@ -32,7 +30,7 @@ public void shouldStopContainerWhenAllConnectionsClosed() throws SQLException { getConnectionAndClose(jdbcUrl); JdbcDatabaseContainer container = ContainerDatabaseDriver.getContainer(jdbcUrl); - assertNull("Database container instance is null as expected", container); + assertThat(container).as("Database container instance is null as expected").isNull(); } @Test @@ -42,13 +40,13 @@ public void shouldNotStopDaemonContainerWhenAllConnectionsClosed() throws SQLExc getConnectionAndClose(jdbcUrl); JdbcDatabaseContainer container = ContainerDatabaseDriver.getContainer(jdbcUrl); - assertNotNull("Database container instance is not null as expected", container); - assertTrue("Database container is running as expected", container.isRunning()); + assertThat(container).as("Database container instance is not null as expected").isNotNull(); + assertThat(container.isRunning()).as("Database container is running as expected").isTrue(); } private void getConnectionAndClose(String jdbcUrl) throws SQLException { try (Connection connection = DriverManager.getConnection(jdbcUrl)) { - assertNotNull("Obtained connection as expected", connection); + assertThat(connection).as("Obtained connection as expected").isNotNull(); } } } diff --git a/modules/postgresql/src/test/java/org/testcontainers/jdbc/DatabaseDriverTmpfsTest.java b/modules/postgresql/src/test/java/org/testcontainers/jdbc/DatabaseDriverTmpfsTest.java index 4c8d6afeef0..463981d2d59 100644 --- a/modules/postgresql/src/test/java/org/testcontainers/jdbc/DatabaseDriverTmpfsTest.java +++ b/modules/postgresql/src/test/java/org/testcontainers/jdbc/DatabaseDriverTmpfsTest.java @@ -7,8 +7,7 @@ import java.sql.Connection; import java.sql.DriverManager; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.rnorth.visibleassertions.VisibleAssertions.assertNotEquals; +import static org.assertj.core.api.Assertions.assertThat; /** * This test belongs in the jdbc module, as it is focused on testing the behaviour of {@link org.testcontainers.containers.JdbcDatabaseContainer}. @@ -26,11 +25,13 @@ public void testDatabaseHasTmpFsViaConnectionString() throws Exception { // check file doesn't exist String path = "/testtmpfs/test.file"; Container.ExecResult execResult = container.execInContainer("ls", path); - assertNotEquals("tmpfs inside container doesn't have file that doesn't exist", 0, execResult.getExitCode()); + assertThat(execResult.getExitCode()) + .as("tmpfs inside container doesn't have file that doesn't exist") + .isNotZero(); // touch && check file does exist container.execInContainer("touch", path); execResult = container.execInContainer("ls", path); - assertEquals("tmpfs inside container has file that does exist", 0, execResult.getExitCode()); + assertThat(execResult.getExitCode()).as("tmpfs inside container has file that does exist").isZero(); } } } diff --git a/modules/postgresql/src/test/java/org/testcontainers/junit/postgresql/CustomizablePostgreSQLTest.java b/modules/postgresql/src/test/java/org/testcontainers/junit/postgresql/CustomizablePostgreSQLTest.java index 1b256c0fd6b..6dea67c9e26 100644 --- a/modules/postgresql/src/test/java/org/testcontainers/junit/postgresql/CustomizablePostgreSQLTest.java +++ b/modules/postgresql/src/test/java/org/testcontainers/junit/postgresql/CustomizablePostgreSQLTest.java @@ -8,7 +8,7 @@ import java.sql.ResultSet; import java.sql.SQLException; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; /** * @author richardnorth @@ -34,7 +34,7 @@ public void testSimple() throws SQLException { ResultSet resultSet = performQuery(postgres, "SELECT 1"); int resultSetInt = resultSet.getInt(1); - assertEquals("A basic SELECT query succeeds", 1, resultSetInt); + assertThat(resultSetInt).as("A basic SELECT query succeeds").isEqualTo(1); } } } diff --git a/modules/postgresql/src/test/java/org/testcontainers/junit/postgresql/SimplePostgreSQLTest.java b/modules/postgresql/src/test/java/org/testcontainers/junit/postgresql/SimplePostgreSQLTest.java index 2a0dd351ad6..2a7db242421 100644 --- a/modules/postgresql/src/test/java/org/testcontainers/junit/postgresql/SimplePostgreSQLTest.java +++ b/modules/postgresql/src/test/java/org/testcontainers/junit/postgresql/SimplePostgreSQLTest.java @@ -10,10 +10,7 @@ import java.util.logging.Level; import java.util.logging.LogManager; -import static org.hamcrest.Matchers.containsString; -import static org.junit.Assert.assertThat; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.rnorth.visibleassertions.VisibleAssertions.assertNotEquals; +import static org.assertj.core.api.Assertions.assertThat; public class SimplePostgreSQLTest extends AbstractContainerDatabaseTest { static { @@ -28,7 +25,7 @@ public void testSimple() throws SQLException { ResultSet resultSet = performQuery(postgres, "SELECT 1"); int resultSetInt = resultSet.getInt(1); - assertEquals("A basic SELECT query succeeds", 1, resultSetInt); + assertThat(resultSetInt).as("A basic SELECT query succeeds").isEqualTo(1); } } @@ -42,7 +39,7 @@ public void testCommandOverride() throws SQLException { ResultSet resultSet = performQuery(postgres, "SELECT current_setting('max_connections')"); String result = resultSet.getString(1); - assertEquals("max_connections should be overriden", "42", result); + assertThat(result).as("max_connections should be overriden").isEqualTo("42"); } } @@ -57,7 +54,7 @@ public void testUnsetCommand() throws SQLException { ResultSet resultSet = performQuery(postgres, "SELECT current_setting('max_connections')"); String result = resultSet.getString(1); - assertNotEquals("max_connections should not be overriden", "42", result); + assertThat(result).as("max_connections should not be overriden").isNotEqualTo("42"); } } @@ -72,7 +69,7 @@ public void testExplicitInitScript() throws SQLException { ResultSet resultSet = performQuery(postgres, "SELECT foo FROM bar"); String firstColumnValue = resultSet.getString(1); - assertEquals("Value from init script should equal real value", "hello world", firstColumnValue); + assertThat(firstColumnValue).as("Value from init script should equal real value").isEqualTo("hello world"); } } @@ -84,9 +81,9 @@ public void testWithAdditionalUrlParamInJdbcUrl() { ) { postgres.start(); String jdbcUrl = postgres.getJdbcUrl(); - assertThat(jdbcUrl, containsString("?")); - assertThat(jdbcUrl, containsString("&")); - assertThat(jdbcUrl, containsString("charSet=UNICODE")); + assertThat(jdbcUrl).contains("?"); + assertThat(jdbcUrl).contains("&"); + assertThat(jdbcUrl).contains("charSet=UNICODE"); } } } diff --git a/modules/r2dbc/src/test/java/org/testcontainers/r2dbc/TestcontainersR2DBCConnectionFactoryTest.java b/modules/r2dbc/src/test/java/org/testcontainers/r2dbc/TestcontainersR2DBCConnectionFactoryTest.java index 3772e6d49a6..9ae49b00bd1 100644 --- a/modules/r2dbc/src/test/java/org/testcontainers/r2dbc/TestcontainersR2DBCConnectionFactoryTest.java +++ b/modules/r2dbc/src/test/java/org/testcontainers/r2dbc/TestcontainersR2DBCConnectionFactoryTest.java @@ -6,7 +6,6 @@ import io.r2dbc.spi.ConnectionFactories; import io.r2dbc.spi.ConnectionFactory; import io.r2dbc.spi.Result; -import org.assertj.core.api.Assertions; import org.junit.Test; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -14,14 +13,14 @@ import java.util.UUID; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; public class TestcontainersR2DBCConnectionFactoryTest { @Test public void failsOnUnknownProvider() { String nonExistingProvider = UUID.randomUUID().toString(); - Assertions - .assertThatThrownBy(() -> { + assertThatThrownBy(() -> { ConnectionFactories.get(String.format("r2dbc:tc:%s:///db", nonExistingProvider)); }) .hasMessageContaining("Missing provider") @@ -64,8 +63,7 @@ public void reusesUntilConnectionFactoryIsClosed() { close(connectionFactory); - Assertions - .assertThatThrownBy(select::blockFirst) + assertThatThrownBy(select::blockFirst) .isInstanceOf(PostgresqlException.class) // relation "X" does not exists // https://github.com/postgres/postgres/blob/REL_10_0/src/backend/utils/errcodes.txt#L349 diff --git a/modules/selenium/build.gradle b/modules/selenium/build.gradle index 5f0824f8b12..38fab63dc0a 100644 --- a/modules/selenium/build.gradle +++ b/modules/selenium/build.gradle @@ -10,7 +10,7 @@ dependencies { testImplementation 'org.mortbay.jetty:jetty:6.1.26' testImplementation project(':nginx') - testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' + testImplementation 'org.assertj:assertj-core:3.23.1' compileOnly 'org.jetbrains:annotations:23.0.0' testCompileClasspath 'org.jetbrains:annotations:23.0.0' diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/BaseWebDriverContainerTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/BaseWebDriverContainerTest.java index d0865a06b09..c49e24ba867 100644 --- a/modules/selenium/src/test/java/org/testcontainers/junit/BaseWebDriverContainerTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/junit/BaseWebDriverContainerTest.java @@ -13,8 +13,7 @@ import java.util.concurrent.TimeUnit; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; /** * @@ -41,13 +40,15 @@ protected static void doSimpleExplore(BrowserWebDriverContainer rule) { driver.get("http://helloworld:8080"); WebElement title = driver.findElement(By.tagName("h1")); - assertEquals("the index page contains the title 'Hello world'", "Hello world", title.getText().trim()); + assertThat(title.getText().trim()) + .as("the index page contains the title 'Hello world'") + .isEqualTo("Hello world"); } protected void assertBrowserNameIs(BrowserWebDriverContainer rule, String expectedName) { RemoteWebDriver driver = setupDriverFromRule(rule); String actual = driver.getCapabilities().getBrowserName(); - assertTrue(String.format("actual browser name is %s", actual), actual.equals(expectedName)); + assertThat(actual).as(String.format("actual browser name is %s", actual)).isEqualTo(expectedName); } @NotNull diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/BrowserWebDriverContainerTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/BrowserWebDriverContainerTest.java index 60d113dc290..370d9cffedb 100644 --- a/modules/selenium/src/test/java/org/testcontainers/junit/BrowserWebDriverContainerTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/junit/BrowserWebDriverContainerTest.java @@ -13,7 +13,7 @@ import java.util.List; import java.util.stream.Collectors; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; public class BrowserWebDriverContainerTest { @@ -31,7 +31,7 @@ public void honorPresetNoProxyEnvironment() { chromeWithNoProxySet.start(); Object noProxy = chromeWithNoProxySet.getEnvMap().get(NO_PROXY_KEY); - assertEquals("no_proxy should be preserved by the container rule", NO_PROXY_VALUE, noProxy); + assertThat(noProxy).as("no_proxy should be preserved by the container rule").isEqualTo(NO_PROXY_VALUE); } } @@ -44,7 +44,7 @@ public void provideDefaultNoProxyEnvironmentIfNotSet() { chromeWithoutNoProxySet.start(); Object noProxy = chromeWithoutNoProxySet.getEnvMap().get(NO_PROXY_KEY); - assertEquals("no_proxy should be set to default if not already present", "localhost", noProxy); + assertThat(noProxy).as("no_proxy should be set to default if not already present").isEqualTo("localhost"); } } @@ -59,9 +59,9 @@ public void createContainerWithShmVolume() { final List shmVolumes = shmVolumes(webDriverContainer); - assertEquals("Only one shm mount present", 1, shmVolumes.size()); - assertEquals("Shm mount source is correct", "/dev/shm", shmVolumes.get(0).getSource()); - assertEquals("Shm mount mode is correct", "rw", shmVolumes.get(0).getMode()); + assertThat(shmVolumes).as("Only one shm mount present").hasSize(1); + assertThat(shmVolumes.get(0).getSource()).as("Shm mount source is correct").isEqualTo("/dev/shm"); + assertThat(shmVolumes.get(0).getMode()).as("Shm mount mode is correct").isEqualTo("rw"); } } @@ -74,9 +74,11 @@ public void createContainerWithoutShmVolume() { ) { webDriverContainer.start(); - assertEquals("Shared memory size is configured", 512 * FileUtils.ONE_MB, webDriverContainer.getShmSize()); + assertThat(webDriverContainer.getShmSize()) + .as("Shared memory size is configured") + .isEqualTo(512 * FileUtils.ONE_MB); - assertEquals("No shm mounts present", Collections.emptyList(), shmVolumes(webDriverContainer)); + assertThat(shmVolumes(webDriverContainer)).as("No shm mounts present").isEqualTo(Collections.emptyList()); } } diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/LocalServerWebDriverContainerTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/LocalServerWebDriverContainerTest.java index 2d2feb6aaff..88b40f4fbbe 100644 --- a/modules/selenium/src/test/java/org/testcontainers/junit/LocalServerWebDriverContainerTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/junit/LocalServerWebDriverContainerTest.java @@ -10,7 +10,7 @@ import org.testcontainers.Testcontainers; import org.testcontainers.containers.BrowserWebDriverContainer; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; /** * Test that a browser running in a container can access a web server hosted on the host machine (i.e. the one running @@ -53,10 +53,8 @@ public void testConnection() { String headingText = driver.findElement(By.cssSelector("h1")).getText().trim(); - assertEquals( - "The hardcoded success message was found on a page fetched from a local server", - "It worked", - headingText - ); + assertThat(headingText) + .as("The hardcoded success message was found on a page fetched from a local server") + .isEqualTo("It worked"); } } diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/SeleniumUtilsTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/SeleniumUtilsTest.java index 4cbfca77d21..6208cb3a5bb 100644 --- a/modules/selenium/src/test/java/org/testcontainers/junit/SeleniumUtilsTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/junit/SeleniumUtilsTest.java @@ -6,7 +6,7 @@ import java.io.IOException; import java.util.jar.Manifest; -import static org.rnorth.visibleassertions.VisibleAssertions.*; +import static org.assertj.core.api.Assertions.assertThat; /** * Created by Julien LAMY @@ -32,6 +32,8 @@ private void checkSeleniumVersionDetected(String urlManifest, String expectedVer Manifest manifest = new Manifest(); manifest.read(this.getClass().getClassLoader().getResourceAsStream(urlManifest)); String seleniumVersion = SeleniumUtils.getSeleniumVersionFromManifest(manifest); - assertEquals("Check if Selenium Version detected is the correct one.", expectedVersion, seleniumVersion); + assertThat(seleniumVersion) + .as("Check if Selenium Version detected is the correct one.") + .isEqualTo(expectedVersion); } } diff --git a/modules/toxiproxy/build.gradle b/modules/toxiproxy/build.gradle index a1e5f58551a..afe76308ba6 100644 --- a/modules/toxiproxy/build.gradle +++ b/modules/toxiproxy/build.gradle @@ -5,7 +5,7 @@ dependencies { api 'eu.rekawek.toxiproxy:toxiproxy-java:2.1.7' testImplementation 'redis.clients:jedis:3.0.1' - testImplementation 'org.rnorth.visible-assertions:visible-assertions:2.1.2' + testImplementation 'org.assertj:assertj-core:3.23.1' testImplementation project(':test-support') } diff --git a/modules/toxiproxy/src/test/java/org/testcontainers/containers/ToxiproxyTest.java b/modules/toxiproxy/src/test/java/org/testcontainers/containers/ToxiproxyTest.java index c5982bc45c1..bfe488eaac1 100644 --- a/modules/toxiproxy/src/test/java/org/testcontainers/containers/ToxiproxyTest.java +++ b/modules/toxiproxy/src/test/java/org/testcontainers/containers/ToxiproxyTest.java @@ -10,9 +10,8 @@ import java.io.IOException; import java.time.Duration; -import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; -import static org.rnorth.visibleassertions.VisibleAssertions.assertThrows; -import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; public class ToxiproxyTest { @@ -53,7 +52,7 @@ public void testDirect() { jedis.set("somekey", "somevalue"); final String s = jedis.get("somekey"); - assertEquals("direct access to the container works OK", "somevalue", s); + assertThat(s).as("direct access to the container works OK").isEqualTo("somevalue"); } @Test @@ -90,32 +89,28 @@ public void testConnectionCut() { final Jedis jedis = createJedis(proxy.getContainerIpAddress(), proxy.getProxyPort()); jedis.set("somekey", "somevalue"); - assertEquals( - "access to the container works OK before cutting the connection", - "somevalue", - jedis.get("somekey") - ); + assertThat(jedis.get("somekey")) + .as("access to the container works OK before cutting the connection") + .isEqualTo("somevalue"); // disableProxy { proxy.setConnectionCut(true); // for example, expect failure when the connection is cut - assertThrows( - "calls fail when the connection is cut", - JedisConnectionException.class, - () -> { + assertThat( + catchThrowable(() -> { jedis.get("somekey"); - } - ); + }) + ) + .as("calls fail when the connection is cut") + .isInstanceOf(JedisConnectionException.class); proxy.setConnectionCut(false); // and with the connection re-established, expect success - assertEquals( - "access to the container works OK after re-establishing the connection", - "somevalue", - jedis.get("somekey") - ); + assertThat(jedis.get("somekey")) + .as("access to the container works OK after re-establishing the connection") + .isEqualTo("somevalue"); // } } @@ -139,15 +134,15 @@ public void testMultipleProxiesCanBeCreated() { firstProxy.setConnectionCut(true); - assertThrows( - "calls fail when the connection is cut, for only the relevant proxy", - JedisConnectionException.class, - () -> { + assertThat( + catchThrowable(() -> { firstJedis.get("somekey"); - } - ); + }) + ) + .as("calls fail when the connection is cut, for only the relevant proxy") + .isInstanceOf(JedisConnectionException.class); - assertEquals("access via a different proxy is OK", "somevalue", secondJedis.get("somekey")); + assertThat(secondJedis.get("somekey")).as("access via a different proxy is OK").isEqualTo("somevalue"); } } @@ -158,38 +153,34 @@ public void testOriginalAndMappedPorts() { final String hostViaToxiproxy = TOXIPROXY_NETWORK_ALIAS; final int portViaToxiproxy = proxy.getOriginalProxyPort(); // } - assertEquals("host is correct", TOXIPROXY_NETWORK_ALIAS, hostViaToxiproxy); - assertEquals("original port is correct", 8666, portViaToxiproxy); + assertThat(hostViaToxiproxy).as("host is correct").isEqualTo(TOXIPROXY_NETWORK_ALIAS); + assertThat(portViaToxiproxy).as("original port is correct").isEqualTo(8666); final ToxiproxyContainer.ContainerProxy proxy1 = toxiproxy.getProxy("hostname1", 8080); - assertEquals("original port is correct", 8667, proxy1.getOriginalProxyPort()); - assertEquals( - "mapped port is correct", - toxiproxy.getMappedPort(proxy1.getOriginalProxyPort()), - proxy1.getProxyPort() - ); + assertThat(proxy1.getOriginalProxyPort()).as("original port is correct").isEqualTo(8667); + assertThat(proxy1.getProxyPort()) + .as("mapped port is correct") + .isEqualTo(toxiproxy.getMappedPort(proxy1.getOriginalProxyPort())); final ToxiproxyContainer.ContainerProxy proxy2 = toxiproxy.getProxy("hostname2", 9090); - assertEquals("original port is correct", 8668, proxy2.getOriginalProxyPort()); - assertEquals( - "mapped port is correct", - toxiproxy.getMappedPort(proxy2.getOriginalProxyPort()), - proxy2.getProxyPort() - ); + assertThat(proxy2.getOriginalProxyPort()).as("original port is correct").isEqualTo(8668); + assertThat(proxy2.getProxyPort()) + .as("mapped port is correct") + .isEqualTo(toxiproxy.getMappedPort(proxy2.getOriginalProxyPort())); } @Test public void testProxyName() { final ToxiproxyContainer.ContainerProxy proxy = toxiproxy.getProxy("hostname", 7070); - assertEquals("proxy name is hostname and port", "hostname:7070", proxy.getName()); + assertThat(proxy.getName()).as("proxy name is hostname and port").isEqualTo("hostname:7070"); } @Test public void testControlPort() { final int controlPort = toxiproxy.getControlPort(); - assertEquals("control port is mapped from port 8474", toxiproxy.getMappedPort(8474), controlPort); + assertThat(controlPort).as("control port is mapped from port 8474").isEqualTo(toxiproxy.getMappedPort(8474)); } private void checkCallWithLatency( @@ -203,15 +194,13 @@ private void checkCallWithLatency( final long end = System.currentTimeMillis(); final long duration = end - start; - assertEquals(String.format("access to the container %s works OK", description), "somevalue", s); - assertTrue( - String.format("%s there is at least %dms latency", description, expectedMinLatency), - duration >= expectedMinLatency - ); - assertTrue( - String.format("%s there is no more than %dms latency", description, expectedMaxLatency), - duration < expectedMaxLatency - ); + assertThat(s).as(String.format("access to the container %s works OK", description)).isEqualTo("somevalue"); + assertThat(duration >= expectedMinLatency) + .as(String.format("%s there is at least %dms latency", description, expectedMinLatency)) + .isTrue(); + assertThat(duration < expectedMaxLatency) + .as(String.format("%s there is no more than %dms latency", description, expectedMaxLatency)) + .isTrue(); } private static Jedis createJedis(String host, int port) { From 7c70a8af559935fe6474848cd30241a2731d484f Mon Sep 17 00:00:00 2001 From: Filipe Roque Date: Tue, 9 Aug 2022 09:14:55 +0100 Subject: [PATCH 0365/3231] Removes unused module junit-toolbox (#5678) Co-authored-by: Kevin Wittek --- modules/jdbc/build.gradle | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/jdbc/build.gradle b/modules/jdbc/build.gradle index 6047a3c0f3e..43ff0968715 100644 --- a/modules/jdbc/build.gradle +++ b/modules/jdbc/build.gradle @@ -9,6 +9,8 @@ dependencies { testImplementation 'org.vibur:vibur-dbcp:25.0' testImplementation 'org.apache.tomcat:tomcat-jdbc:10.0.23' testImplementation 'com.zaxxer:HikariCP-java6:2.3.13' - testImplementation 'com.googlecode.junit-toolbox:junit-toolbox:2.4' testImplementation 'org.assertj:assertj-core:3.23.1' + testImplementation ('org.mockito:mockito-core:4.6.1') { + exclude(module: 'hamcrest-core') + } } From 54d7f3931d8856e7a53b8d1b2646aff0faa3dbf2 Mon Sep 17 00:00:00 2001 From: Cheese Date: Mon, 15 Aug 2022 22:01:46 +0800 Subject: [PATCH 0366/3231] Add TiDB module (#5511) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Sergei Egorov Co-authored-by: Kevin Wittek Co-authored-by: Eddú Meléndez Gonzales --- .github/ISSUE_TEMPLATE/bug_report.yaml | 1 + .github/ISSUE_TEMPLATE/enhancement.yaml | 1 + .github/ISSUE_TEMPLATE/feature.yaml | 1 + .github/dependabot.yml | 5 + .github/labeler.yml | 2 + docs/modules/databases/jdbc.md | 4 + docs/modules/databases/tidb.md | 25 ++++ mkdocs.yml | 1 + modules/tidb/build.gradle | 10 ++ modules/tidb/sql/init_mysql.sql | 5 + .../testcontainers/tidb/TiDBContainer.java | 126 ++++++++++++++++++ .../tidb/TiDBContainerProvider.java | 32 +++++ ...s.containers.JdbcDatabaseContainerProvider | 1 + .../org/testcontainers/TiDBTestImages.java | 8 ++ .../jdbc/tidb/TiDBJDBCDriverTest.java | 19 +++ .../junit/tidb/SimpleTiDBTest.java | 54 ++++++++ .../tidb/src/test/resources/logback-test.xml | 16 +++ .../src/test/resources/somepath/init_tidb.sql | 16 +++ 18 files changed, 327 insertions(+) create mode 100644 docs/modules/databases/tidb.md create mode 100644 modules/tidb/build.gradle create mode 100644 modules/tidb/sql/init_mysql.sql create mode 100644 modules/tidb/src/main/java/org/testcontainers/tidb/TiDBContainer.java create mode 100644 modules/tidb/src/main/java/org/testcontainers/tidb/TiDBContainerProvider.java create mode 100644 modules/tidb/src/main/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider create mode 100644 modules/tidb/src/test/java/org/testcontainers/TiDBTestImages.java create mode 100644 modules/tidb/src/test/java/org/testcontainers/jdbc/tidb/TiDBJDBCDriverTest.java create mode 100644 modules/tidb/src/test/java/org/testcontainers/junit/tidb/SimpleTiDBTest.java create mode 100644 modules/tidb/src/test/resources/logback-test.xml create mode 100644 modules/tidb/src/test/resources/somepath/init_tidb.sql diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index 686086d076c..07d944243cd 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -43,6 +43,7 @@ body: - RabbitMQ - Selenium - Solr + - TiDB - ToxiProxy - Trino - Vault diff --git a/.github/ISSUE_TEMPLATE/enhancement.yaml b/.github/ISSUE_TEMPLATE/enhancement.yaml index 9ef8eafaab2..ea190cefc80 100644 --- a/.github/ISSUE_TEMPLATE/enhancement.yaml +++ b/.github/ISSUE_TEMPLATE/enhancement.yaml @@ -43,6 +43,7 @@ body: - RabbitMQ - Selenium - Solr + - TiDB - ToxiProxy - Trino - Vault diff --git a/.github/ISSUE_TEMPLATE/feature.yaml b/.github/ISSUE_TEMPLATE/feature.yaml index 1ab2693f210..09810d3e56f 100644 --- a/.github/ISSUE_TEMPLATE/feature.yaml +++ b/.github/ISSUE_TEMPLATE/feature.yaml @@ -43,6 +43,7 @@ body: - RabbitMQ - Selenium - Solr + - TiDB - ToxiProxy - Trino - Vault diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 3e2d53fed65..2d877f664ab 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -197,6 +197,11 @@ updates: schedule: interval: "monthly" open-pull-requests-limit: 10 + - package-ecosystem: "gradle" + directory: "/modules/tidb" + schedule: + interval: "monthly" + open-pull-requests-limit: 10 - package-ecosystem: "gradle" directory: "/modules/toxiproxy" schedule: diff --git a/.github/labeler.yml b/.github/labeler.yml index 84e21af744a..826e5c16215 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -73,6 +73,8 @@ - modules/solr/* "modules/spock": - modules/spock/* +"modules/tidb": + - modules/tidb/* "modules/toxiproxy": - modules/toxiproxy/* "modules/trino": diff --git a/docs/modules/databases/jdbc.md b/docs/modules/databases/jdbc.md index 3c52dd1d8a8..e3a077333cd 100644 --- a/docs/modules/databases/jdbc.md +++ b/docs/modules/databases/jdbc.md @@ -51,6 +51,10 @@ Insert `tc:` after `jdbc:` as follows. Note that the hostname, port and database `jdbc:tc:cockroach:v21.2.3:///databasename` +#### Using TiDB + +`jdbc:tc:tidb:v6.1.0:///databasename` + ### Using a classpath init script Testcontainers can run an init script after the database container is started, but before your code is given a connection to it. The script must be on the classpath, and is referenced as follows: diff --git a/docs/modules/databases/tidb.md b/docs/modules/databases/tidb.md new file mode 100644 index 00000000000..01c6b4cfcd4 --- /dev/null +++ b/docs/modules/databases/tidb.md @@ -0,0 +1,25 @@ +# TiDB Module + +See [Database containers](./index.md) for documentation and usage that is common to all relational database container types. + +## Adding this module to your project dependencies + +Add the following dependency to your `pom.xml`/`build.gradle` file: + +=== "Gradle" + ```groovy + testImplementation "org.testcontainers:tidb:{{latest_version}}" + ``` + +=== "Maven" + ```xml + + org.testcontainers + tidb + {{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. diff --git a/mkdocs.yml b/mkdocs.yml index 396986a955b..9491ebe6019 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -63,6 +63,7 @@ nav: - modules/databases/orientdb.md - modules/databases/postgres.md - modules/databases/presto.md + - modules/databases/tidb.md - modules/databases/trino.md - modules/azure.md - modules/docker_compose.md diff --git a/modules/tidb/build.gradle b/modules/tidb/build.gradle new file mode 100644 index 00000000000..51f15a6a798 --- /dev/null +++ b/modules/tidb/build.gradle @@ -0,0 +1,10 @@ +description = "Testcontainers :: JDBC :: TiDB" + +dependencies { + api project(':jdbc') + + testImplementation project(':jdbc-test') + testImplementation 'mysql:mysql-connector-java:8.0.29' + + compileOnly 'org.jetbrains:annotations:23.0.0' +} diff --git a/modules/tidb/sql/init_mysql.sql b/modules/tidb/sql/init_mysql.sql new file mode 100644 index 00000000000..2b00ee968b0 --- /dev/null +++ b/modules/tidb/sql/init_mysql.sql @@ -0,0 +1,5 @@ +CREATE TABLE bar ( + foo VARCHAR(255) +); + +INSERT INTO bar (foo) VALUES ('hello world'); \ No newline at end of file diff --git a/modules/tidb/src/main/java/org/testcontainers/tidb/TiDBContainer.java b/modules/tidb/src/main/java/org/testcontainers/tidb/TiDBContainer.java new file mode 100644 index 00000000000..f8030fd2422 --- /dev/null +++ b/modules/tidb/src/main/java/org/testcontainers/tidb/TiDBContainer.java @@ -0,0 +1,126 @@ +package org.testcontainers.tidb; + +import org.jetbrains.annotations.NotNull; +import org.testcontainers.containers.JdbcDatabaseContainer; +import org.testcontainers.containers.wait.strategy.HttpWaitStrategy; +import org.testcontainers.utility.DockerImageName; + +import java.time.Duration; +import java.util.HashSet; +import java.util.Set; + +/** + * Testcontainers implementation for TiDB. + * + * @author Icemap + */ +public class TiDBContainer extends JdbcDatabaseContainer { + + static final String NAME = "tidb"; + + static final String DOCKER_IMAGE_NAME = "pingcap/tidb"; + + private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse(DOCKER_IMAGE_NAME); + + private static final Integer TIDB_PORT = 4000; + + private static final int REST_API_PORT = 10080; + + private String databaseName = "test"; + + private String username = "root"; + + private String password = ""; + + public TiDBContainer(String dockerImageName) { + this(DockerImageName.parse(dockerImageName)); + } + + public TiDBContainer(final DockerImageName dockerImageName) { + super(dockerImageName); + dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); + + addExposedPorts(TIDB_PORT, REST_API_PORT); + + waitingFor( + new HttpWaitStrategy() + .forPath("/status") + .forPort(REST_API_PORT) + .forStatusCode(200) + .withStartupTimeout(Duration.ofMinutes(1)) + ); + } + + @NotNull + @Override + protected Set getLivenessCheckPorts() { + return new HashSet<>(getMappedPort(TIDB_PORT)); + } + + @Override + public String getDriverClassName() { + try { + Class.forName("com.mysql.cj.jdbc.Driver"); + return "com.mysql.cj.jdbc.Driver"; + } catch (ClassNotFoundException e) { + return "com.mysql.jdbc.Driver"; + } + } + + @Override + public String getJdbcUrl() { + String additionalUrlParams = constructUrlParameters("?", "&"); + return "jdbc:mysql://" + getHost() + ":" + getMappedPort(TIDB_PORT) + "/" + databaseName + additionalUrlParams; + } + + @Override + protected String constructUrlForConnection(String queryString) { + String url = super.constructUrlForConnection(queryString); + + if (!url.contains("useSSL=")) { + String separator = url.contains("?") ? "&" : "?"; + url = url + separator + "useSSL=false"; + } + + if (!url.contains("allowPublicKeyRetrieval=")) { + url = url + "&allowPublicKeyRetrieval=true"; + } + + return url; + } + + @Override + public String getDatabaseName() { + return databaseName; + } + + @Override + public String getUsername() { + return username; + } + + @Override + public String getPassword() { + return password; + } + + @Override + public String getTestQueryString() { + return "SELECT 1"; + } + + @Override + public TiDBContainer withDatabaseName(final String databaseName) { + throw new UnsupportedOperationException("The TiDB docker image does not currently support this"); + } + + @Override + public TiDBContainer withUsername(final String username) { + throw new UnsupportedOperationException("The TiDB docker image does not currently support this"); + } + + @Override + public TiDBContainer withPassword(final String password) { + throw new UnsupportedOperationException("The TiDB docker image does not currently support this"); + } +} diff --git a/modules/tidb/src/main/java/org/testcontainers/tidb/TiDBContainerProvider.java b/modules/tidb/src/main/java/org/testcontainers/tidb/TiDBContainerProvider.java new file mode 100644 index 00000000000..ee66771b42c --- /dev/null +++ b/modules/tidb/src/main/java/org/testcontainers/tidb/TiDBContainerProvider.java @@ -0,0 +1,32 @@ +package org.testcontainers.tidb; + +import org.testcontainers.containers.JdbcDatabaseContainer; +import org.testcontainers.containers.JdbcDatabaseContainerProvider; +import org.testcontainers.utility.DockerImageName; + +/** + * Factory for TiDB containers. + */ +public class TiDBContainerProvider extends JdbcDatabaseContainerProvider { + + private static final String DEFAULT_TAG = "v6.1.0"; + + @Override + public boolean supports(String databaseType) { + return databaseType.equals(TiDBContainer.NAME); + } + + @Override + public JdbcDatabaseContainer newInstance() { + return newInstance(DEFAULT_TAG); + } + + @Override + public JdbcDatabaseContainer newInstance(String tag) { + if (tag != null) { + return new TiDBContainer(DockerImageName.parse(TiDBContainer.DOCKER_IMAGE_NAME).withTag(tag)); + } else { + return newInstance(); + } + } +} diff --git a/modules/tidb/src/main/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider b/modules/tidb/src/main/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider new file mode 100644 index 00000000000..a4168b00e74 --- /dev/null +++ b/modules/tidb/src/main/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider @@ -0,0 +1 @@ +org.testcontainers.tidb.TiDBContainerProvider diff --git a/modules/tidb/src/test/java/org/testcontainers/TiDBTestImages.java b/modules/tidb/src/test/java/org/testcontainers/TiDBTestImages.java new file mode 100644 index 00000000000..32fb57911a2 --- /dev/null +++ b/modules/tidb/src/test/java/org/testcontainers/TiDBTestImages.java @@ -0,0 +1,8 @@ +package org.testcontainers; + +import org.testcontainers.utility.DockerImageName; + +public class TiDBTestImages { + + public static final DockerImageName TIDB_IMAGE = DockerImageName.parse("pingcap/tidb:v6.1.0"); +} diff --git a/modules/tidb/src/test/java/org/testcontainers/jdbc/tidb/TiDBJDBCDriverTest.java b/modules/tidb/src/test/java/org/testcontainers/jdbc/tidb/TiDBJDBCDriverTest.java new file mode 100644 index 00000000000..566850fa629 --- /dev/null +++ b/modules/tidb/src/test/java/org/testcontainers/jdbc/tidb/TiDBJDBCDriverTest.java @@ -0,0 +1,19 @@ +package org.testcontainers.jdbc.tidb; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.testcontainers.jdbc.AbstractJDBCDriverTest; + +import java.util.Arrays; +import java.util.EnumSet; + +@RunWith(Parameterized.class) +public class TiDBJDBCDriverTest extends AbstractJDBCDriverTest { + + @Parameterized.Parameters(name = "{index} - {0}") + public static Iterable data() { + return Arrays.asList( + new Object[][] { { "jdbc:tc:tidb://hostname/databasename", EnumSet.noneOf(Options.class) } } + ); + } +} diff --git a/modules/tidb/src/test/java/org/testcontainers/junit/tidb/SimpleTiDBTest.java b/modules/tidb/src/test/java/org/testcontainers/junit/tidb/SimpleTiDBTest.java new file mode 100644 index 00000000000..c4833f14224 --- /dev/null +++ b/modules/tidb/src/test/java/org/testcontainers/junit/tidb/SimpleTiDBTest.java @@ -0,0 +1,54 @@ +package org.testcontainers.junit.tidb; + +import org.hamcrest.Matchers; +import org.junit.Assert; +import org.junit.Test; +import org.testcontainers.TiDBTestImages; +import org.testcontainers.db.AbstractContainerDatabaseTest; +import org.testcontainers.tidb.TiDBContainer; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class SimpleTiDBTest extends AbstractContainerDatabaseTest { + + @Test + public void testSimple() throws SQLException { + try (TiDBContainer tidb = new TiDBContainer(TiDBTestImages.TIDB_IMAGE)) { + tidb.start(); + + ResultSet resultSet = performQuery(tidb, "SELECT 1"); + + int resultSetInt = resultSet.getInt(1); + Assert.assertEquals("A basic SELECT query succeeds", 1, resultSetInt); + } + } + + @Test + public void testExplicitInitScript() throws SQLException { + try ( + TiDBContainer tidb = new TiDBContainer(TiDBTestImages.TIDB_IMAGE).withInitScript("somepath/init_tidb.sql") + ) { // TiDB is expected to be compatible with MySQL + tidb.start(); + + ResultSet resultSet = performQuery(tidb, "SELECT foo FROM bar"); + + String firstColumnValue = resultSet.getString(1); + Assert.assertEquals("Value from init script should equal real value", "hello world", firstColumnValue); + } + } + + @Test + public void testWithAdditionalUrlParamInJdbcUrl() { + TiDBContainer tidb = new TiDBContainer(TiDBTestImages.TIDB_IMAGE).withUrlParam("sslmode", "disable"); + + try { + tidb.start(); + String jdbcUrl = tidb.getJdbcUrl(); + Assert.assertThat(jdbcUrl, Matchers.containsString("?")); + Assert.assertThat(jdbcUrl, Matchers.containsString("sslmode=disable")); + } finally { + tidb.stop(); + } + } +} diff --git a/modules/tidb/src/test/resources/logback-test.xml b/modules/tidb/src/test/resources/logback-test.xml new file mode 100644 index 00000000000..535e406fc13 --- /dev/null +++ b/modules/tidb/src/test/resources/logback-test.xml @@ -0,0 +1,16 @@ + + + + + + %d{HH:mm:ss.SSS} %-5level %logger - %msg%n + + + + + + + + + diff --git a/modules/tidb/src/test/resources/somepath/init_tidb.sql b/modules/tidb/src/test/resources/somepath/init_tidb.sql new file mode 100644 index 00000000000..73bc09151cb --- /dev/null +++ b/modules/tidb/src/test/resources/somepath/init_tidb.sql @@ -0,0 +1,16 @@ +CREATE TABLE bar ( + foo VARCHAR(255) +); + +SELECT "a /* string literal containing comment characters like -- here"; +SELECT "a 'quoting' \"scenario ` involving BEGIN keyword\" here"; +SELECT * from `bar`; + +-- What about a line comment containing imbalanced string delimiters? " + +/* or a block comment + containing imbalanced string delimiters? + ' " + */ + +INSERT INTO bar (foo) /* ; */ VALUES ('hello world'); From 89803a204af6c3d4ba8de80d50ac5b43bcd12ea3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Aug 2022 14:02:17 +0000 Subject: [PATCH 0367/3231] Bump mockito-core from 4.6.1 to 4.7.0 in /core Bumps [mockito-core](https://github.com/mockito/mockito) from 4.6.1 to 4.7.0. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v4.6.1...v4.7.0) --- updated-dependencies: - dependency-name: org.mockito:mockito-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 9febf2802e9..c210ed48510 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -91,7 +91,7 @@ dependencies { testImplementation 'com.rabbitmq:amqp-client:5.15.0' testImplementation 'org.mongodb:mongo-java-driver:3.12.11' - testImplementation ('org.mockito:mockito-core:4.6.1') { + testImplementation ('org.mockito:mockito-core:4.7.0') { exclude(module: 'hamcrest-core') } // Synthetic JAR used for MountableFileTest and DirectoryTarResourceTest From 13cbb5d321216239524731476649acf9c976ea57 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Aug 2022 14:02:18 +0000 Subject: [PATCH 0368/3231] Bump java-client from 3.3.2 to 3.3.3 in /modules/couchbase Bumps [java-client](https://github.com/couchbase/couchbase-jvm-clients) from 3.3.2 to 3.3.3. - [Release notes](https://github.com/couchbase/couchbase-jvm-clients/releases) - [Commits](https://github.com/couchbase/couchbase-jvm-clients/compare/java-client-3.3.2...java-client-3.3.3) --- updated-dependencies: - dependency-name: com.couchbase.client:java-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/couchbase/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/couchbase/build.gradle b/modules/couchbase/build.gradle index e59e346c29f..10970df542e 100644 --- a/modules/couchbase/build.gradle +++ b/modules/couchbase/build.gradle @@ -5,6 +5,6 @@ dependencies { // TODO use JDK's HTTP client and/or Apache HttpClient5 shaded 'com.squareup.okhttp3:okhttp:3.14.9' - testImplementation 'com.couchbase.client:java-client:3.3.2' + testImplementation 'com.couchbase.client:java-client:3.3.3' testImplementation 'org.awaitility:awaitility:4.2.0' } From 7dcb1d1705fff6c01fb21626954301b2b6c3ab76 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Aug 2022 14:02:19 +0000 Subject: [PATCH 0369/3231] Bump com.gradle.enterprise.gradle.plugin from 3.10.3 to 3.11.1 Bumps com.gradle.enterprise.gradle.plugin from 3.10.3 to 3.11.1. --- updated-dependencies: - dependency-name: com.gradle.enterprise:com.gradle.enterprise.gradle.plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- settings.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.gradle b/settings.gradle index b56048c1126..116a4e26506 100644 --- a/settings.gradle +++ b/settings.gradle @@ -5,7 +5,7 @@ buildscript { } } dependencies { - classpath "com.gradle.enterprise:com.gradle.enterprise.gradle.plugin:3.10.3" + classpath "com.gradle.enterprise:com.gradle.enterprise.gradle.plugin:3.11.1" classpath "com.gradle:common-custom-user-data-gradle-plugin:1.7.2" } } From 3b2c90e71e72be89648e0b0a2b658ce1a6116638 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Aug 2022 14:02:19 +0000 Subject: [PATCH 0370/3231] Bump postgresql from 42.4.0 to 42.4.1 in /modules/cockroachdb Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.4.0 to 42.4.1. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.4.0...REL42.4.1) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/cockroachdb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/cockroachdb/build.gradle b/modules/cockroachdb/build.gradle index 80763e312b5..1fc5dac8d8a 100644 --- a/modules/cockroachdb/build.gradle +++ b/modules/cockroachdb/build.gradle @@ -4,6 +4,6 @@ dependencies { api project(':jdbc') testImplementation project(':jdbc-test') - testImplementation 'org.postgresql:postgresql:42.4.0' + testImplementation 'org.postgresql:postgresql:42.4.1' testImplementation 'org.assertj:assertj-core:3.23.1' } From 981d47e0d3e45eb3ab726b08b421cf9cb3b50d91 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Aug 2022 14:02:19 +0000 Subject: [PATCH 0371/3231] Bump java-driver-core from 4.8.0 to 4.14.1 in /modules/cassandra Bumps [java-driver-core](https://github.com/datastax/java-driver) from 4.8.0 to 4.14.1. - [Release notes](https://github.com/datastax/java-driver/releases) - [Commits](https://github.com/datastax/java-driver/compare/4.8.0...4.14.1) --- updated-dependencies: - dependency-name: com.datastax.oss:java-driver-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/cassandra/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/cassandra/build.gradle b/modules/cassandra/build.gradle index b00740d09fd..385001db8f8 100644 --- a/modules/cassandra/build.gradle +++ b/modules/cassandra/build.gradle @@ -9,5 +9,5 @@ configurations.all { dependencies { api project(":database-commons") api "com.datastax.cassandra:cassandra-driver-core:3.10.0" - testImplementation 'com.datastax.oss:java-driver-core:4.8.0' + testImplementation 'com.datastax.oss:java-driver-core:4.14.1' } From caee839e2b97fe7fee57b71e463a6464ff04faea Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Aug 2022 14:02:22 +0000 Subject: [PATCH 0372/3231] Bump azure-cosmos from 4.33.1 to 4.34.0 in /modules/azure Bumps [azure-cosmos](https://github.com/Azure/azure-sdk-for-java) from 4.33.1 to 4.34.0. - [Release notes](https://github.com/Azure/azure-sdk-for-java/releases) - [Commits](https://github.com/Azure/azure-sdk-for-java/compare/azure-cosmos_4.33.1...azure-cosmos_4.34.0) --- updated-dependencies: - dependency-name: com.azure:azure-cosmos dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/azure/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/azure/build.gradle b/modules/azure/build.gradle index c6707401ff6..f8a7193571e 100644 --- a/modules/azure/build.gradle +++ b/modules/azure/build.gradle @@ -6,5 +6,5 @@ dependencies { shaded 'com.squareup.okhttp3:okhttp:3.14.9' testImplementation 'org.assertj:assertj-core:3.23.1' - testImplementation 'com.azure:azure-cosmos:4.33.1' + testImplementation 'com.azure:azure-cosmos:4.34.0' } From 88773a7dfda704643972d8ca625f745b5afab4e4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Aug 2022 14:02:23 +0000 Subject: [PATCH 0373/3231] Bump mariadb-java-client from 3.0.6 to 3.0.7 in /modules/mariadb Bumps [mariadb-java-client](https://github.com/mariadb-corporation/mariadb-connector-j) from 3.0.6 to 3.0.7. - [Release notes](https://github.com/mariadb-corporation/mariadb-connector-j/releases) - [Changelog](https://github.com/mariadb-corporation/mariadb-connector-j/blob/master/CHANGELOG.md) - [Commits](https://github.com/mariadb-corporation/mariadb-connector-j/compare/3.0.6...3.0.7) --- updated-dependencies: - dependency-name: org.mariadb.jdbc:mariadb-java-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/mariadb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mariadb/build.gradle b/modules/mariadb/build.gradle index ed7f428ba3e..1a11f0fbfb6 100644 --- a/modules/mariadb/build.gradle +++ b/modules/mariadb/build.gradle @@ -10,7 +10,7 @@ dependencies { compileOnly 'org.mariadb:r2dbc-mariadb:1.0.3' testImplementation project(':jdbc-test') - testImplementation 'org.mariadb.jdbc:mariadb-java-client:3.0.6' + testImplementation 'org.mariadb.jdbc:mariadb-java-client:3.0.7' testImplementation testFixtures(project(':r2dbc')) testImplementation 'org.mariadb:r2dbc-mariadb:1.0.2' From e0c1b3652258b8e44f27807fd3a5248417dbe306 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Aug 2022 14:02:25 +0000 Subject: [PATCH 0374/3231] Bump postgresql from 42.4.0 to 42.4.1 in /modules/junit-jupiter Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.4.0 to 42.4.1. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.4.0...REL42.4.1) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 57932ec84fd..7e14656f20f 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -15,7 +15,7 @@ dependencies { testImplementation 'org.assertj:assertj-core:3.23.1' testImplementation 'org.junit.jupiter:junit-jupiter-params:5.9.0' - testRuntimeOnly 'org.postgresql:postgresql:42.4.0' + testRuntimeOnly 'org.postgresql:postgresql:42.4.1' testRuntimeOnly 'mysql:mysql-connector-java:8.0.30' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.9.0' } From aa8a67964f2f2b6aea378081eeb7aa41f3a27c10 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Aug 2022 14:02:25 +0000 Subject: [PATCH 0375/3231] Bump mssql-jdbc in /modules/mssqlserver Bumps [mssql-jdbc](https://github.com/Microsoft/mssql-jdbc) from 11.1.2.jre8-preview to 11.2.0.jre8. - [Release notes](https://github.com/Microsoft/mssql-jdbc/releases) - [Changelog](https://github.com/microsoft/mssql-jdbc/blob/main/CHANGELOG.md) - [Commits](https://github.com/Microsoft/mssql-jdbc/commits) --- updated-dependencies: - dependency-name: com.microsoft.sqlserver:mssql-jdbc dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/mssqlserver/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mssqlserver/build.gradle b/modules/mssqlserver/build.gradle index 30b9af2bd77..cc05b6ffea4 100644 --- a/modules/mssqlserver/build.gradle +++ b/modules/mssqlserver/build.gradle @@ -10,7 +10,7 @@ dependencies { compileOnly 'io.r2dbc:r2dbc-mssql:0.9.0.RELEASE' testImplementation project(':jdbc-test') - testImplementation 'com.microsoft.sqlserver:mssql-jdbc:11.1.2.jre8-preview' + testImplementation 'com.microsoft.sqlserver:mssql-jdbc:11.2.0.jre8' testImplementation project(':r2dbc') testImplementation 'io.r2dbc:r2dbc-mssql:0.8.7.RELEASE' From 4495b0c89efb355373faa986772cde94c157f29c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Aug 2022 14:02:26 +0000 Subject: [PATCH 0376/3231] Bump aws-java-sdk-dynamodb in /modules/dynalite Bumps [aws-java-sdk-dynamodb](https://github.com/aws/aws-sdk-java) from 1.12.273 to 1.12.281. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.273...1.12.281) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-dynamodb dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/dynalite/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/dynalite/build.gradle b/modules/dynalite/build.gradle index 6abd9cd943a..81ec1f9deb5 100644 --- a/modules/dynalite/build.gradle +++ b/modules/dynalite/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Dynalite" dependencies { api project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.12.273' - testImplementation 'com.amazonaws:aws-java-sdk-dynamodb:1.12.253' + compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.12.281' + testImplementation 'com.amazonaws:aws-java-sdk-dynamodb:1.12.281' testImplementation 'org.assertj:assertj-core:3.23.1' } From 2c9e843d09868f3ec877fdb5a9e6916ae59e586e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Aug 2022 14:02:27 +0000 Subject: [PATCH 0377/3231] Bump hivemq-extension-sdk from 4.8.2 to 4.8.3 in /modules/hivemq Bumps [hivemq-extension-sdk](https://github.com/hivemq/hivemq-extension-sdk) from 4.8.2 to 4.8.3. - [Release notes](https://github.com/hivemq/hivemq-extension-sdk/releases) - [Commits](https://github.com/hivemq/hivemq-extension-sdk/compare/4.8.2...4.8.3) --- updated-dependencies: - dependency-name: com.hivemq:hivemq-extension-sdk dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/hivemq/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/hivemq/build.gradle b/modules/hivemq/build.gradle index 8336cfd8249..a85288f9fbd 100644 --- a/modules/hivemq/build.gradle +++ b/modules/hivemq/build.gradle @@ -13,7 +13,7 @@ dependencies { testImplementation("org.junit.jupiter:junit-jupiter-api:5.9.0") testImplementation(project(":junit-jupiter")) - testImplementation("com.hivemq:hivemq-extension-sdk:4.8.2") + testImplementation("com.hivemq:hivemq-extension-sdk:4.8.3") testImplementation("com.hivemq:hivemq-mqtt-client:1.3.0") testImplementation("org.apache.httpcomponents:httpclient:4.5.13") testImplementation("ch.qos.logback:logback-classic:1.2.11") From c3586528904a4fcfc1de8cf1666606c248b53b3c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Aug 2022 14:02:28 +0000 Subject: [PATCH 0378/3231] Bump mockito-core from 4.6.1 to 4.7.0 in /modules/junit-jupiter Bumps [mockito-core](https://github.com/mockito/mockito) from 4.6.1 to 4.7.0. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v4.6.1...v4.7.0) --- updated-dependencies: - dependency-name: org.mockito:mockito-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 57932ec84fd..03ae3a9a08c 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -9,7 +9,7 @@ dependencies { testImplementation 'com.zaxxer:HikariCP:4.0.3' testImplementation 'redis.clients:jedis:4.2.3' testImplementation 'org.apache.httpcomponents:httpclient:4.5.13' - testImplementation ('org.mockito:mockito-core:4.6.1') { + testImplementation ('org.mockito:mockito-core:4.7.0') { exclude(module: 'hamcrest-core') } testImplementation 'org.assertj:assertj-core:3.23.1' From 3f3987a1ee46c9f0101a055e2e4d4353fcad2fe6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Aug 2022 14:02:29 +0000 Subject: [PATCH 0379/3231] Bump postgresql from 42.4.0 to 42.4.1 in /modules/postgresql Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.4.0 to 42.4.1. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.4.0...REL42.4.1) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/postgresql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/postgresql/build.gradle b/modules/postgresql/build.gradle index 87705f77d5d..f769bf9b07c 100644 --- a/modules/postgresql/build.gradle +++ b/modules/postgresql/build.gradle @@ -11,7 +11,7 @@ dependencies { testImplementation project(':jdbc-test') testImplementation project(':test-support') - testImplementation 'org.postgresql:postgresql:42.4.0' + testImplementation 'org.postgresql:postgresql:42.4.1' testImplementation testFixtures(project(':r2dbc')) testImplementation 'io.r2dbc:r2dbc-postgresql:0.8.11.RELEASE' From 11ca75a76836b51b649d6464e03d9a2b8dff27be Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Aug 2022 14:02:29 +0000 Subject: [PATCH 0380/3231] Bump trino-jdbc from 391 to 392 in /modules/trino Bumps trino-jdbc from 391 to 392. --- updated-dependencies: - dependency-name: io.trino:trino-jdbc dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- modules/trino/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/trino/build.gradle b/modules/trino/build.gradle index c25007bb223..09b5b8cb16c 100644 --- a/modules/trino/build.gradle +++ b/modules/trino/build.gradle @@ -4,6 +4,6 @@ dependencies { api project(':jdbc') testImplementation project(':jdbc-test') - testImplementation 'io.trino:trino-jdbc:391' + testImplementation 'io.trino:trino-jdbc:392' compileOnly 'org.jetbrains:annotations:23.0.0' } From 7f036604c36b6f68bc938b9ad2695b31025c5a60 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Aug 2022 14:02:29 +0000 Subject: [PATCH 0381/3231] Bump neo4j from 3.5.34 to 3.5.35 in /modules/neo4j Bumps [neo4j](https://github.com/neo4j/neo4j) from 3.5.34 to 3.5.35. - [Release notes](https://github.com/neo4j/neo4j/releases) - [Commits](https://github.com/neo4j/neo4j/compare/3.5.34...3.5.35) --- updated-dependencies: - dependency-name: org.neo4j:neo4j dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/neo4j/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/neo4j/build.gradle b/modules/neo4j/build.gradle index de42bca2d5d..aabb571edf6 100644 --- a/modules/neo4j/build.gradle +++ b/modules/neo4j/build.gradle @@ -29,7 +29,7 @@ task customNeo4jPluginJar(type: Jar) { processTestResources.dependsOn customNeo4jPluginJar dependencies { - customNeo4jPluginCompileOnly "org.neo4j:neo4j:3.5.34" + customNeo4jPluginCompileOnly "org.neo4j:neo4j:3.5.35" api project(":testcontainers") From 7bba5118cfd74b9bee5687e546801e56c7dd8779 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Aug 2022 14:02:30 +0000 Subject: [PATCH 0382/3231] Bump aws-java-sdk-s3 from 1.12.273 to 1.12.281 in /modules/localstack Bumps [aws-java-sdk-s3](https://github.com/aws/aws-sdk-java) from 1.12.273 to 1.12.281. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.273...1.12.281) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-s3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 8702d2e1a83..0b28ee848ae 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Localstack" dependencies { api project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.273' + compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.281' testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.273' testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.273' testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.273' From 3842d784ec323af87bfe958b7ff1422ecbdc2ae6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Aug 2022 14:02:30 +0000 Subject: [PATCH 0383/3231] Bump postgresql from 42.4.0 to 42.4.1 in /modules/spock Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.4.0 to 42.4.1. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.4.0...REL42.4.1) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/spock/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle index 3eadd33ea2a..304e0b98dc4 100644 --- a/modules/spock/build.gradle +++ b/modules/spock/build.gradle @@ -15,7 +15,7 @@ dependencies { testImplementation 'com.zaxxer:HikariCP:4.0.3' testImplementation 'org.apache.httpcomponents:httpclient:4.5.13' - testRuntimeOnly 'org.postgresql:postgresql:42.4.0' + testRuntimeOnly 'org.postgresql:postgresql:42.4.1' testRuntimeOnly 'mysql:mysql-connector-java:8.0.30' testCompileClasspath 'org.jetbrains:annotations:23.0.0' From ae6d57c4c2999527d0c91ceb90d597432754ceb4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Aug 2022 14:02:30 +0000 Subject: [PATCH 0384/3231] Bump reactor-core from 3.4.21 to 3.4.22 in /modules/r2dbc Bumps [reactor-core](https://github.com/reactor/reactor-core) from 3.4.21 to 3.4.22. - [Release notes](https://github.com/reactor/reactor-core/releases) - [Commits](https://github.com/reactor/reactor-core/compare/v3.4.21...v3.4.22) --- updated-dependencies: - dependency-name: io.projectreactor:reactor-core dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/r2dbc/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/r2dbc/build.gradle b/modules/r2dbc/build.gradle index f51a13b9296..f38803104a3 100644 --- a/modules/r2dbc/build.gradle +++ b/modules/r2dbc/build.gradle @@ -15,6 +15,6 @@ dependencies { testImplementation 'io.r2dbc:r2dbc-postgresql:0.8.12.RELEASE' testImplementation project(':postgresql') - testFixturesImplementation 'io.projectreactor:reactor-core:3.4.21' + testFixturesImplementation 'io.projectreactor:reactor-core:3.4.22' testFixturesImplementation 'org.assertj:assertj-core:3.23.1' } From 6d873e0f9534706ee5a007eb6e125055f14c4b20 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Aug 2022 14:02:32 +0000 Subject: [PATCH 0385/3231] Bump mysql-connector-java from 8.0.29 to 8.0.30 in /modules/tidb Bumps [mysql-connector-java](https://github.com/mysql/mysql-connector-j) from 8.0.29 to 8.0.30. - [Release notes](https://github.com/mysql/mysql-connector-j/releases) - [Changelog](https://github.com/mysql/mysql-connector-j/blob/release/8.0/CHANGES) - [Commits](https://github.com/mysql/mysql-connector-j/compare/8.0.29...8.0.30) --- updated-dependencies: - dependency-name: mysql:mysql-connector-java dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/tidb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/tidb/build.gradle b/modules/tidb/build.gradle index 51f15a6a798..36f6acfe806 100644 --- a/modules/tidb/build.gradle +++ b/modules/tidb/build.gradle @@ -4,7 +4,7 @@ dependencies { api project(':jdbc') testImplementation project(':jdbc-test') - testImplementation 'mysql:mysql-connector-java:8.0.29' + testImplementation 'mysql:mysql-connector-java:8.0.30' compileOnly 'org.jetbrains:annotations:23.0.0' } From f4a68fe044779bef456e0b2cb917fc21f5217c4e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Aug 2022 14:02:35 +0000 Subject: [PATCH 0386/3231] Bump aws-java-sdk-logs from 1.12.273 to 1.12.281 in /modules/localstack Bumps [aws-java-sdk-logs](https://github.com/aws/aws-sdk-java) from 1.12.273 to 1.12.281. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.273...1.12.281) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-logs dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 8702d2e1a83..eacdc411a73 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -6,7 +6,7 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.273' testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.273' testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.273' - testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.273' + testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.281' testImplementation 'software.amazon.awssdk:s3:2.17.244' testImplementation 'org.assertj:assertj-core:3.23.1' } From dd5dc8a813faa02c8d3bc5b8cf4a697fdf487307 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Aug 2022 14:02:35 +0000 Subject: [PATCH 0387/3231] Bump google-cloud-spanner from 6.27.0 to 6.28.0 in /modules/gcloud Bumps [google-cloud-spanner](https://github.com/googleapis/java-spanner) from 6.27.0 to 6.28.0. - [Release notes](https://github.com/googleapis/java-spanner/releases) - [Changelog](https://github.com/googleapis/java-spanner/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/java-spanner/compare/v6.27.0...v6.28.0) --- updated-dependencies: - dependency-name: com.google.cloud:google-cloud-spanner dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/gcloud/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle index ee7f563c88e..bac92bebde9 100644 --- a/modules/gcloud/build.gradle +++ b/modules/gcloud/build.gradle @@ -6,7 +6,7 @@ dependencies { testImplementation 'com.google.cloud:google-cloud-datastore:2.10.1' testImplementation 'com.google.cloud:google-cloud-firestore:3.3.0' testImplementation 'com.google.cloud:google-cloud-pubsub:1.120.6' - testImplementation 'com.google.cloud:google-cloud-spanner:6.27.0' + testImplementation 'com.google.cloud:google-cloud-spanner:6.28.0' testImplementation 'com.google.cloud:google-cloud-bigtable:2.10.1' testImplementation 'org.assertj:assertj-core:3.23.1' } From 93051c42ac1afaa24959b189cec1270ab7f55443 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Aug 2022 14:02:35 +0000 Subject: [PATCH 0388/3231] Bump actions/cache from 3.0.5 to 3.0.7 Bumps [actions/cache](https://github.com/actions/cache) from 3.0.5 to 3.0.7. - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/v3.0.5...v3.0.7) --- updated-dependencies: - dependency-name: actions/cache dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/ci-examples.yml | 4 ++-- .github/workflows/ci.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci-examples.yml b/.github/workflows/ci-examples.yml index 9fa1f78e47b..f23fc66e889 100644 --- a/.github/workflows/ci-examples.yml +++ b/.github/workflows/ci-examples.yml @@ -25,7 +25,7 @@ jobs: java-version: '8.0.302' distribution: temurin - name: Cache Gradle Home files - uses: actions/cache@v3.0.5 + uses: actions/cache@v3.0.7 continue-on-error: true with: path: ~/.gradle/caches @@ -53,7 +53,7 @@ jobs: java-version: '8.0.302' distribution: temurin - name: Cache Gradle Home files - uses: actions/cache@v3.0.5 + uses: actions/cache@v3.0.7 continue-on-error: true with: path: ~/.gradle/caches diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 55432b3e2c6..288e9329824 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,7 +33,7 @@ jobs: java-version: '8.0.302' distribution: temurin - name: Cache Gradle Home files - uses: actions/cache@v3.0.5 + uses: actions/cache@v3.0.7 with: path: ~/.gradle/caches key: ${{ runner.os }}-gradle-home-testmatrix-${{ hashFiles('**/*.gradle') }} @@ -59,7 +59,7 @@ jobs: java-version: '8.0.302' distribution: temurin - name: Cache Gradle Home files - uses: actions/cache@v3.0.5 + uses: actions/cache@v3.0.7 continue-on-error: true with: path: ~/.gradle/caches From 63e65926b2b97f7249ffe3f626978948df65327a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Aug 2022 14:02:38 +0000 Subject: [PATCH 0389/3231] Bump cucumber-java from 7.5.0 to 7.6.0 in /examples Bumps [cucumber-java](https://github.com/cucumber/cucumber-jvm) from 7.5.0 to 7.6.0. - [Release notes](https://github.com/cucumber/cucumber-jvm/releases) - [Changelog](https://github.com/cucumber/cucumber-jvm/blob/main/CHANGELOG.md) - [Commits](https://github.com/cucumber/cucumber-jvm/compare/v7.5.0...v7.6.0) --- updated-dependencies: - dependency-name: io.cucumber:cucumber-java dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- examples/cucumber/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cucumber/build.gradle b/examples/cucumber/build.gradle index ba51bbe0d2c..cc6b073413f 100644 --- a/examples/cucumber/build.gradle +++ b/examples/cucumber/build.gradle @@ -10,7 +10,7 @@ dependencies { implementation 'org.seleniumhq.selenium:selenium-remote-driver:3.141.59' implementation 'org.seleniumhq.selenium:selenium-firefox-driver:3.141.59' implementation 'org.seleniumhq.selenium:selenium-chrome-driver:3.141.59' - testImplementation 'io.cucumber:cucumber-java:7.5.0' + testImplementation 'io.cucumber:cucumber-java:7.6.0' testImplementation 'io.cucumber:cucumber-junit:7.5.0' testImplementation 'org.testcontainers:selenium' } From a45bb987ed20e03fb441fc76da119eeccb04d744 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Aug 2022 14:02:39 +0000 Subject: [PATCH 0390/3231] Bump google-cloud-firestore from 3.3.0 to 3.4.0 in /modules/gcloud Bumps [google-cloud-firestore](https://github.com/googleapis/java-firestore) from 3.3.0 to 3.4.0. - [Release notes](https://github.com/googleapis/java-firestore/releases) - [Changelog](https://github.com/googleapis/java-firestore/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/java-firestore/compare/v3.3.0...v3.4.0) --- updated-dependencies: - dependency-name: com.google.cloud:google-cloud-firestore dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/gcloud/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle index ee7f563c88e..f28fb904be8 100644 --- a/modules/gcloud/build.gradle +++ b/modules/gcloud/build.gradle @@ -4,7 +4,7 @@ dependencies { api project(':testcontainers') testImplementation 'com.google.cloud:google-cloud-datastore:2.10.1' - testImplementation 'com.google.cloud:google-cloud-firestore:3.3.0' + testImplementation 'com.google.cloud:google-cloud-firestore:3.4.0' testImplementation 'com.google.cloud:google-cloud-pubsub:1.120.6' testImplementation 'com.google.cloud:google-cloud-spanner:6.27.0' testImplementation 'com.google.cloud:google-cloud-bigtable:2.10.1' From 39e2005700fffeb88ed1d4f9788684e1d2b32044 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Aug 2022 14:02:44 +0000 Subject: [PATCH 0391/3231] Bump com.gradle.enterprise.gradle.plugin in /examples Bumps com.gradle.enterprise.gradle.plugin from 3.10.3 to 3.11.1. --- updated-dependencies: - dependency-name: com.gradle.enterprise:com.gradle.enterprise.gradle.plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- examples/settings.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/settings.gradle b/examples/settings.gradle index be455a78955..1d3956a88cb 100644 --- a/examples/settings.gradle +++ b/examples/settings.gradle @@ -6,7 +6,7 @@ buildscript { } dependencies { classpath "gradle.plugin.ch.myniva.gradle:s3-build-cache:0.10.0" - classpath "com.gradle.enterprise:com.gradle.enterprise.gradle.plugin:3.10.3" + classpath "com.gradle.enterprise:com.gradle.enterprise.gradle.plugin:3.11.1" classpath "com.gradle:common-custom-user-data-gradle-plugin:1.7.2" } } From 625947f89c7e2fcbebbf70462923987deded02b5 Mon Sep 17 00:00:00 2001 From: Julien Date: Mon, 15 Aug 2022 16:31:59 +0200 Subject: [PATCH 0392/3231] Add Consul module (#4683) Co-authored-by: Kevin Wittek --- .github/ISSUE_TEMPLATE/bug_report.yaml | 1 + .github/ISSUE_TEMPLATE/enhancement.yaml | 1 + .github/ISSUE_TEMPLATE/feature.yaml | 1 + .github/dependabot.yml | 5 + .github/labeler.yml | 2 + docs/modules/consul.md | 36 +++++++ mkdocs.yml | 1 + modules/consul/build.gradle | 9 ++ .../consul/ConsulContainer.java | 100 ++++++++++++++++++ .../consul/ConsulContainerTest.java | 78 ++++++++++++++ .../consul/ConsulTestImages.java | 7 ++ .../src/test/resources/logback-test.xml | 16 +++ 12 files changed, 257 insertions(+) create mode 100644 docs/modules/consul.md create mode 100644 modules/consul/build.gradle create mode 100644 modules/consul/src/main/java/org/testcontainers/consul/ConsulContainer.java create mode 100644 modules/consul/src/test/java/org/testcontainers/consul/ConsulContainerTest.java create mode 100644 modules/consul/src/test/java/org/testcontainers/consul/ConsulTestImages.java create mode 100644 modules/consul/src/test/resources/logback-test.xml diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index 07d944243cd..5aca9e624b3 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -18,6 +18,7 @@ body: - Cassandra - Clickhouse - CockroachDB + - Consul - Couchbase - DB2 - Dynalite diff --git a/.github/ISSUE_TEMPLATE/enhancement.yaml b/.github/ISSUE_TEMPLATE/enhancement.yaml index ea190cefc80..ba5b1f88006 100644 --- a/.github/ISSUE_TEMPLATE/enhancement.yaml +++ b/.github/ISSUE_TEMPLATE/enhancement.yaml @@ -18,6 +18,7 @@ body: - Cassandra - Clickhouse - CockroachDB + - Consul - Couchbase - DB2 - Dynalite diff --git a/.github/ISSUE_TEMPLATE/feature.yaml b/.github/ISSUE_TEMPLATE/feature.yaml index 09810d3e56f..981f5613a1e 100644 --- a/.github/ISSUE_TEMPLATE/feature.yaml +++ b/.github/ISSUE_TEMPLATE/feature.yaml @@ -18,6 +18,7 @@ body: - Cassandra - Clickhouse - CockroachDB + - Consul - Couchbase - DB2 - Dynalite diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 2d877f664ab..22437e15825 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -42,6 +42,11 @@ updates: schedule: interval: "monthly" open-pull-requests-limit: 10 + - package-ecosystem: "gradle" + directory: "/modules/consul" + schedule: + interval: "monthly" + open-pull-requests-limit: 10 - package-ecosystem: "gradle" directory: "/modules/couchbase" schedule: diff --git a/.github/labeler.yml b/.github/labeler.yml index 826e5c16215..cf75af99d26 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -15,6 +15,8 @@ - modules/clickhouse/* "modules/cockroachdb": - modules/cockroachdb/* +"modules/consul": + - modules/consul/* "modules/couchbase": - modules/couchbase/* "modules/db2": diff --git a/docs/modules/consul.md b/docs/modules/consul.md new file mode 100644 index 00000000000..1580690cad7 --- /dev/null +++ b/docs/modules/consul.md @@ -0,0 +1,36 @@ +# Hashicorp Consul Module + +Testcontainers module for [Consul](https://github.com/hashicorp/consul). Consul is a tool for managing key value properties. More information on Consul [here](https://www.consul.io/). + +## Usage example + + +[Running Consul in your Junit tests](../../modules/consul/src/test/java/org/testcontainers/consul/ConsulContainerTest.java) + + +## Why Consul in Junit tests? + +With the increasing popularity of Consul and config externalization, applications are now needing to source properties from Consul. +This can prove challenging in the development phase without a running Consul instance readily on hand. This library +aims to solve your apps integration testing with Consul. You can also use it to +test how your application behaves with Consul by writing different test scenarios in Junit. + +## Adding this module to your project dependencies + +Add the following dependency to your `pom.xml`/`build.gradle` file: + +```groovy tab='Gradle' +testImplementation "org.testcontainers:consul:{{latest_version}}" +``` + +```xml tab='Maven' + + org.testcontainers + consul + {{latest_version}} + test + +``` + +See [AUTHORS](https://raw.githubusercontent.com/testcontainers/testcontainers-java/master/modules/consul/AUTHORS) for contributors. + diff --git a/mkdocs.yml b/mkdocs.yml index 9491ebe6019..a836e0a2ff7 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -66,6 +66,7 @@ nav: - modules/databases/tidb.md - modules/databases/trino.md - modules/azure.md + - modules/consul.md - modules/docker_compose.md - modules/elasticsearch.md - modules/gcloud.md diff --git a/modules/consul/build.gradle b/modules/consul/build.gradle new file mode 100644 index 00000000000..0d0d6e7e992 --- /dev/null +++ b/modules/consul/build.gradle @@ -0,0 +1,9 @@ +description = "Testcontainers :: Consul" + +dependencies { + api project(':testcontainers') + + testImplementation 'com.ecwid.consul:consul-api:1.4.5' + testImplementation 'io.rest-assured:rest-assured:4.4.0' + testImplementation 'org.assertj:assertj-core:3.21.0' +} diff --git a/modules/consul/src/main/java/org/testcontainers/consul/ConsulContainer.java b/modules/consul/src/main/java/org/testcontainers/consul/ConsulContainer.java new file mode 100644 index 00000000000..0c9d2607072 --- /dev/null +++ b/modules/consul/src/main/java/org/testcontainers/consul/ConsulContainer.java @@ -0,0 +1,100 @@ +package org.testcontainers.consul; + +import com.github.dockerjava.api.command.InspectContainerResponse; +import com.github.dockerjava.api.model.Capability; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.wait.strategy.Wait; +import org.testcontainers.utility.DockerImageName; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +/** + * Testcontainers implementation for Consul. + */ +public class ConsulContainer extends GenericContainer { + + private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("consul"); + + private static final int CONSUL_HTTP_PORT = 8500; + + private static final int CONSUL_GRPC_PORT = 8502; + + private List initCommands = new ArrayList<>(); + + private String[] startConsulCmd = new String[] { "agent", "-dev", "-client", "0.0.0.0" }; + + public ConsulContainer(String dockerImageName) { + this(DockerImageName.parse(dockerImageName)); + } + + public ConsulContainer(final DockerImageName dockerImageName) { + super(dockerImageName); + dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); + + // Use the status leader endpoint to verify if consul is running. + setWaitStrategy(Wait.forHttp("/v1/status/leader").forPort(CONSUL_HTTP_PORT).forStatusCode(200)); + + withCreateContainerCmdModifier(cmd -> cmd.getHostConfig().withCapAdd(Capability.IPC_LOCK)); + withEnv("CONSUL_ADDR", "http://0.0.0.0:" + CONSUL_HTTP_PORT); + withCommand(startConsulCmd); + withExposedPorts(CONSUL_HTTP_PORT, CONSUL_GRPC_PORT); + } + + @Override + protected void containerIsStarted(InspectContainerResponse containerInfo) { + runConsulCommands(); + } + + private void runConsulCommands() { + if (!initCommands.isEmpty()) { + String commands = initCommands + .stream() + .map(command -> "consul " + command) + .collect(Collectors.joining(" && ")); + try { + ExecResult execResult = this.execInContainer(new String[] { "/bin/sh", "-c", commands }); + if (execResult.getExitCode() != 0) { + logger() + .error( + "Failed to execute these init commands {}. Exit code {}. Stdout {}. Stderr {}", + initCommands, + execResult.getExitCode(), + execResult.getStdout(), + execResult.getStderr() + ); + } + } catch (IOException | InterruptedException e) { + logger() + .error( + "Failed to execute these init commands {}. Exception message: {}", + initCommands, + e.getMessage() + ); + } + } + } + + /** + * Run consul commands using the consul cli. + * + * Useful for enableing more secret engines like: + *
+     *     .withConsulCommand("secrets enable pki")
+     *     .withConsulCommand("secrets enable transit")
+     * 
+ * or register specific K/V like: + *
+     *     .withConsulCommand("kv put config/testing1 value123")
+     * 
+ * @param commands The commands to send to the consul cli + * @return this + */ + public ConsulContainer withConsulCommand(String... commands) { + initCommands.addAll(Arrays.asList(commands)); + return self(); + } +} diff --git a/modules/consul/src/test/java/org/testcontainers/consul/ConsulContainerTest.java b/modules/consul/src/test/java/org/testcontainers/consul/ConsulContainerTest.java new file mode 100644 index 00000000000..e29ff7ffbd8 --- /dev/null +++ b/modules/consul/src/test/java/org/testcontainers/consul/ConsulContainerTest.java @@ -0,0 +1,78 @@ +package org.testcontainers.consul; + +import com.ecwid.consul.v1.ConsulClient; +import com.ecwid.consul.v1.Response; +import com.ecwid.consul.v1.kv.model.GetValue; +import io.restassured.RestAssured; +import org.assertj.core.api.Assertions; +import org.hamcrest.CoreMatchers; +import org.hamcrest.MatcherAssert; +import org.junit.ClassRule; +import org.junit.Test; +import org.testcontainers.containers.GenericContainer; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Base64; +import java.util.HashMap; +import java.util.Map; + +/** + * This test shows the pattern to use the ConsulContainer @ClassRule for a junit test. It also has tests that ensure + * the properties were added correctly by reading from Consul with the CLI and over HTTP. + */ +public class ConsulContainerTest { + + @ClassRule + public static ConsulContainer consulContainer = new ConsulContainer(ConsulTestImages.CONSUL_IMAGE) + .withConsulCommand("kv put config/testing1 value123"); + + @Test + public void readFirstPropertyPathWithCli() throws IOException, InterruptedException { + GenericContainer.ExecResult result = consulContainer.execInContainer("consul", "kv", "get", "config/testing1"); + final String output = result.getStdout().replaceAll("\\r?\\n", ""); + MatcherAssert.assertThat(output, CoreMatchers.containsString("value123")); + } + + @Test + public void readFirstSecretPathOverHttpApi() throws InterruptedException { + RestAssured + .given() + .when() + .get("http://" + getHostAndPort() + "/v1/kv/config/testing1") + .then() + .assertThat() + .body( + "[0].Value", + CoreMatchers.equalTo(Base64.getEncoder().encodeToString("value123".getBytes(StandardCharsets.UTF_8))) + ); + } + + @Test + public void writeAndReadMultipleValuesUsingClient() { + final ConsulClient consulClient = new ConsulClient( + consulContainer.getHost(), + consulContainer.getFirstMappedPort() + ); + + final Map properties = new HashMap<>(); + properties.put("value", "world"); + properties.put("other_value", "another world"); + + // Write operation + properties.forEach((key, value) -> { + Response writeResponse = consulClient.setKVValue(key, value); + Assertions.assertThat(writeResponse.getValue()).isTrue(); + }); + + // Read operation + properties.forEach((key, value) -> { + Response readResponse = consulClient.getKVValue(key); + Assertions.assertThat(readResponse.getValue().getDecodedValue()).isEqualTo(value); + }); + } + + private String getHostAndPort() { + return consulContainer.getHost() + ":" + consulContainer.getMappedPort(8500); + } +} diff --git a/modules/consul/src/test/java/org/testcontainers/consul/ConsulTestImages.java b/modules/consul/src/test/java/org/testcontainers/consul/ConsulTestImages.java new file mode 100644 index 00000000000..de2b9702066 --- /dev/null +++ b/modules/consul/src/test/java/org/testcontainers/consul/ConsulTestImages.java @@ -0,0 +1,7 @@ +package org.testcontainers.consul; + +import org.testcontainers.utility.DockerImageName; + +public interface ConsulTestImages { + DockerImageName CONSUL_IMAGE = DockerImageName.parse("consul:1.10.12"); +} diff --git a/modules/consul/src/test/resources/logback-test.xml b/modules/consul/src/test/resources/logback-test.xml new file mode 100644 index 00000000000..535e406fc13 --- /dev/null +++ b/modules/consul/src/test/resources/logback-test.xml @@ -0,0 +1,16 @@ + + + + + + %d{HH:mm:ss.SSS} %-5level %logger - %msg%n + + + + + + + + + From 84102fe2590c9128f218a3c65ac77e3a606d69bf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Aug 2022 14:32:43 +0000 Subject: [PATCH 0393/3231] Bump assertj-core from 3.21.0 to 3.23.1 in /modules/consul Bumps [assertj-core](https://github.com/assertj/assertj-core) from 3.21.0 to 3.23.1. - [Release notes](https://github.com/assertj/assertj-core/releases) - [Commits](https://github.com/assertj/assertj-core/compare/assertj-core-3.21.0...assertj-core-3.23.1) --- updated-dependencies: - dependency-name: org.assertj:assertj-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/consul/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/consul/build.gradle b/modules/consul/build.gradle index 0d0d6e7e992..49e9b404690 100644 --- a/modules/consul/build.gradle +++ b/modules/consul/build.gradle @@ -5,5 +5,5 @@ dependencies { testImplementation 'com.ecwid.consul:consul-api:1.4.5' testImplementation 'io.rest-assured:rest-assured:4.4.0' - testImplementation 'org.assertj:assertj-core:3.21.0' + testImplementation 'org.assertj:assertj-core:3.23.1' } From df2b1d1aed8b8f612059781ff9c620ef06eb2999 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Aug 2022 01:32:24 +0000 Subject: [PATCH 0394/3231] Bump google-cloud-bigtable from 2.10.1 to 2.10.3 in /modules/gcloud Bumps [google-cloud-bigtable](https://github.com/googleapis/java-bigtable) from 2.10.1 to 2.10.3. - [Release notes](https://github.com/googleapis/java-bigtable/releases) - [Changelog](https://github.com/googleapis/java-bigtable/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/java-bigtable/compare/v2.10.1...v2.10.3) --- updated-dependencies: - dependency-name: com.google.cloud:google-cloud-bigtable dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/gcloud/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle index 60778fff111..ce332830e07 100644 --- a/modules/gcloud/build.gradle +++ b/modules/gcloud/build.gradle @@ -7,6 +7,6 @@ dependencies { testImplementation 'com.google.cloud:google-cloud-firestore:3.4.0' testImplementation 'com.google.cloud:google-cloud-pubsub:1.120.6' testImplementation 'com.google.cloud:google-cloud-spanner:6.28.0' - testImplementation 'com.google.cloud:google-cloud-bigtable:2.10.1' + testImplementation 'com.google.cloud:google-cloud-bigtable:2.10.3' testImplementation 'org.assertj:assertj-core:3.23.1' } From 9b51b77f02d776583cca635e55e114b82b687f57 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Aug 2022 01:32:25 +0000 Subject: [PATCH 0395/3231] Bump common-custom-user-data-gradle-plugin from 1.7.2 to 1.8.0 Bumps common-custom-user-data-gradle-plugin from 1.7.2 to 1.8.0. --- updated-dependencies: - dependency-name: com.gradle:common-custom-user-data-gradle-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- settings.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.gradle b/settings.gradle index 116a4e26506..bd51349bb05 100644 --- a/settings.gradle +++ b/settings.gradle @@ -6,7 +6,7 @@ buildscript { } dependencies { classpath "com.gradle.enterprise:com.gradle.enterprise.gradle.plugin:3.11.1" - classpath "com.gradle:common-custom-user-data-gradle-plugin:1.7.2" + classpath "com.gradle:common-custom-user-data-gradle-plugin:1.8.0" } } From b1cd2beca42b259813fa09884c279b79cf0f9b71 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Aug 2022 01:32:34 +0000 Subject: [PATCH 0396/3231] Bump aws-java-sdk-sqs from 1.12.273 to 1.12.282 in /modules/localstack Bumps [aws-java-sdk-sqs](https://github.com/aws/aws-sdk-java) from 1.12.273 to 1.12.282. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.273...1.12.282) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-sqs dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index ea528389c23..98027ef09b7 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -5,7 +5,7 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.281' testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.273' - testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.273' + testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.282' testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.281' testImplementation 'software.amazon.awssdk:s3:2.17.244' testImplementation 'org.assertj:assertj-core:3.23.1' From c3ba559f13f32b44353efc59cc44d4b7f66faf8f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Aug 2022 01:32:49 +0000 Subject: [PATCH 0397/3231] Bump google-cloud-datastore from 2.10.1 to 2.11.0 in /modules/gcloud Bumps [google-cloud-datastore](https://github.com/googleapis/java-datastore) from 2.10.1 to 2.11.0. - [Release notes](https://github.com/googleapis/java-datastore/releases) - [Changelog](https://github.com/googleapis/java-datastore/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/java-datastore/compare/v2.10.1...v2.11.0) --- updated-dependencies: - dependency-name: com.google.cloud:google-cloud-datastore dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/gcloud/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle index 60778fff111..5202c3c8fbe 100644 --- a/modules/gcloud/build.gradle +++ b/modules/gcloud/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: GCloud" dependencies { api project(':testcontainers') - testImplementation 'com.google.cloud:google-cloud-datastore:2.10.1' + testImplementation 'com.google.cloud:google-cloud-datastore:2.11.0' testImplementation 'com.google.cloud:google-cloud-firestore:3.4.0' testImplementation 'com.google.cloud:google-cloud-pubsub:1.120.6' testImplementation 'com.google.cloud:google-cloud-spanner:6.28.0' From 04c7b0a49c470592cb8fa1f1344273987a87e598 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Aug 2022 01:32:49 +0000 Subject: [PATCH 0398/3231] Bump google-cloud-pubsub from 1.120.6 to 1.120.11 in /modules/gcloud Bumps [google-cloud-pubsub](https://github.com/googleapis/java-pubsub) from 1.120.6 to 1.120.11. - [Release notes](https://github.com/googleapis/java-pubsub/releases) - [Changelog](https://github.com/googleapis/java-pubsub/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/java-pubsub/compare/v1.120.6...v1.120.11) --- updated-dependencies: - dependency-name: com.google.cloud:google-cloud-pubsub dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/gcloud/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle index 60778fff111..db006563c74 100644 --- a/modules/gcloud/build.gradle +++ b/modules/gcloud/build.gradle @@ -5,7 +5,7 @@ dependencies { testImplementation 'com.google.cloud:google-cloud-datastore:2.10.1' testImplementation 'com.google.cloud:google-cloud-firestore:3.4.0' - testImplementation 'com.google.cloud:google-cloud-pubsub:1.120.6' + testImplementation 'com.google.cloud:google-cloud-pubsub:1.120.11' testImplementation 'com.google.cloud:google-cloud-spanner:6.28.0' testImplementation 'com.google.cloud:google-cloud-bigtable:2.10.1' testImplementation 'org.assertj:assertj-core:3.23.1' From 115eca1a326bf7fc4e925461ddfdccc987e3a0c3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Aug 2022 01:32:51 +0000 Subject: [PATCH 0399/3231] Bump postgresql from 42.4.0 to 42.4.1 in /examples Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.4.0 to 42.4.1. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.4.0...REL42.4.1) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/linked-container/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/linked-container/build.gradle b/examples/linked-container/build.gradle index c6272fc7522..ab32d55df91 100644 --- a/examples/linked-container/build.gradle +++ b/examples/linked-container/build.gradle @@ -9,7 +9,7 @@ dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.36' implementation 'com.squareup.okhttp3:okhttp:4.10.0' implementation 'org.json:json:20220320' - testImplementation 'org.postgresql:postgresql:42.4.0' + testImplementation 'org.postgresql:postgresql:42.4.1' testImplementation 'ch.qos.logback:logback-classic:1.2.11' testImplementation 'org.testcontainers:postgresql' } From 66e51c890f9992d4fbfdfb01138252821e97a09f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Aug 2022 01:32:51 +0000 Subject: [PATCH 0400/3231] Bump mockito-core from 4.6.1 to 4.7.0 in /modules/jdbc Bumps [mockito-core](https://github.com/mockito/mockito) from 4.6.1 to 4.7.0. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v4.6.1...v4.7.0) --- updated-dependencies: - dependency-name: org.mockito:mockito-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/jdbc/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jdbc/build.gradle b/modules/jdbc/build.gradle index 43ff0968715..ba0f845b47d 100644 --- a/modules/jdbc/build.gradle +++ b/modules/jdbc/build.gradle @@ -10,7 +10,7 @@ dependencies { testImplementation 'org.apache.tomcat:tomcat-jdbc:10.0.23' testImplementation 'com.zaxxer:HikariCP-java6:2.3.13' testImplementation 'org.assertj:assertj-core:3.23.1' - testImplementation ('org.mockito:mockito-core:4.6.1') { + testImplementation ('org.mockito:mockito-core:4.7.0') { exclude(module: 'hamcrest-core') } } From 5bc0a1c0c5f741ca63e595c69b47a44a165935ac Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Aug 2022 01:32:54 +0000 Subject: [PATCH 0401/3231] Bump s3 from 2.17.244 to 2.17.253 in /modules/localstack Bumps s3 from 2.17.244 to 2.17.253. --- updated-dependencies: - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index ea528389c23..d29dbda0cd2 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -7,6 +7,6 @@ dependencies { testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.273' testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.273' testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.281' - testImplementation 'software.amazon.awssdk:s3:2.17.244' + testImplementation 'software.amazon.awssdk:s3:2.17.253' testImplementation 'org.assertj:assertj-core:3.23.1' } From f92ff763362857e9f0854ed3c7a43673ff0dad5e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Aug 2022 01:33:04 +0000 Subject: [PATCH 0402/3231] Bump rest-assured from 4.4.0 to 5.1.1 in /modules/consul Bumps [rest-assured](https://github.com/rest-assured/rest-assured) from 4.4.0 to 5.1.1. - [Release notes](https://github.com/rest-assured/rest-assured/releases) - [Changelog](https://github.com/rest-assured/rest-assured/blob/master/changelog.txt) - [Commits](https://github.com/rest-assured/rest-assured/compare/rest-assured-4.4.0...rest-assured-5.1.1) --- updated-dependencies: - dependency-name: io.rest-assured:rest-assured dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- modules/consul/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/consul/build.gradle b/modules/consul/build.gradle index 49e9b404690..acb97a47927 100644 --- a/modules/consul/build.gradle +++ b/modules/consul/build.gradle @@ -4,6 +4,6 @@ dependencies { api project(':testcontainers') testImplementation 'com.ecwid.consul:consul-api:1.4.5' - testImplementation 'io.rest-assured:rest-assured:4.4.0' + testImplementation 'io.rest-assured:rest-assured:5.1.1' testImplementation 'org.assertj:assertj-core:3.23.1' } From 159922275dd6ddb6ecac7f6ea7b0a923ab9e509c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Aug 2022 01:33:04 +0000 Subject: [PATCH 0403/3231] Bump common-custom-user-data-gradle-plugin in /examples Bumps common-custom-user-data-gradle-plugin from 1.7.2 to 1.8.0. --- updated-dependencies: - dependency-name: com.gradle:common-custom-user-data-gradle-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- examples/settings.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/settings.gradle b/examples/settings.gradle index 1d3956a88cb..83ba91e8321 100644 --- a/examples/settings.gradle +++ b/examples/settings.gradle @@ -7,7 +7,7 @@ buildscript { dependencies { classpath "gradle.plugin.ch.myniva.gradle:s3-build-cache:0.10.0" classpath "com.gradle.enterprise:com.gradle.enterprise.gradle.plugin:3.11.1" - classpath "com.gradle:common-custom-user-data-gradle-plugin:1.7.2" + classpath "com.gradle:common-custom-user-data-gradle-plugin:1.8.0" } } From ae2e71c82bc33922376f7272b9801d79025bd068 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Aug 2022 01:33:07 +0000 Subject: [PATCH 0404/3231] Bump cucumber-junit from 7.5.0 to 7.6.0 in /examples Bumps [cucumber-junit](https://github.com/cucumber/cucumber-jvm) from 7.5.0 to 7.6.0. - [Release notes](https://github.com/cucumber/cucumber-jvm/releases) - [Changelog](https://github.com/cucumber/cucumber-jvm/blob/main/CHANGELOG.md) - [Commits](https://github.com/cucumber/cucumber-jvm/compare/v7.5.0...v7.6.0) --- updated-dependencies: - dependency-name: io.cucumber:cucumber-junit dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- examples/cucumber/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cucumber/build.gradle b/examples/cucumber/build.gradle index cc6b073413f..ab0331f81f4 100644 --- a/examples/cucumber/build.gradle +++ b/examples/cucumber/build.gradle @@ -11,6 +11,6 @@ dependencies { implementation 'org.seleniumhq.selenium:selenium-firefox-driver:3.141.59' implementation 'org.seleniumhq.selenium:selenium-chrome-driver:3.141.59' testImplementation 'io.cucumber:cucumber-java:7.6.0' - testImplementation 'io.cucumber:cucumber-junit:7.5.0' + testImplementation 'io.cucumber:cucumber-junit:7.6.0' testImplementation 'org.testcontainers:selenium' } From 6a3e649b296bba3c5723a2538b21d4eabb593cae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Tue, 16 Aug 2022 09:25:10 -0500 Subject: [PATCH 0405/3231] Move junit4 to assertj (#5685) Co-authored-by: Kevin Wittek --- config/checkstyle/checkstyle.xml | 4 - .../FrameConsumerResultCallbackTest.java | 27 ++++--- .../DockerClientConfigUtilsTest.java | 14 ++-- .../images/AgeBasedPullPolicyTest.java | 7 +- .../images/ParsedDockerfileTest.java | 74 ++++++++----------- .../images/RemoteDockerImageTest.java | 26 +++---- .../junit/FixedHostPortContainerTest.java | 13 ++-- .../strategy/AbstractWaitStrategyTest.java | 8 +- .../wait/strategy/HttpWaitStrategyTest.java | 7 +- .../utility/ComparableVersionTest.java | 4 +- .../DefaultImageNameSubstitutorTest.java | 7 +- .../DockerImageNameCompatibilityTest.java | 14 +--- .../utility/DockerStatusTest.java | 31 ++++---- .../utility/FilterRegistryTest.java | 10 +-- .../utility/ResourceReaperTest.java | 3 +- .../test/java/generic/CmdModifierTest.java | 6 +- .../src/test/java/generic/CommandsTest.java | 4 +- .../java/generic/ContainerCreationTest.java | 6 +- .../src/test/java/generic/DependsOnTest.java | 6 +- .../src/test/java/generic/ExecTest.java | 6 +- .../java/generic/HostPortExposedTest.java | 4 +- .../test/java/generic/WaitStrategiesTest.java | 8 +- docs/examples/junit4/redis/build.gradle | 1 + .../quickstart/RedisBackedCacheIntTest.java | 4 +- .../RedisBackedCacheIntTestStep0.java | 4 +- examples/linked-container/build.gradle | 1 + .../linkedcontainer/RedmineClientTest.java | 7 +- .../containers/Neo4jExampleTest.java | 2 +- modules/cassandra/build.gradle | 2 + .../containers/CassandraContainerTest.java | 34 ++++----- .../containers/CassandraDriver3Test.java | 4 +- .../containers/CassandraDriver4Test.java | 4 +- .../consul/ConsulContainerTest.java | 25 +++---- modules/couchbase/build.gradle | 1 + .../couchbase/CouchbaseContainerTest.java | 27 +++---- modules/influxdb/build.gradle | 1 + .../containers/InfluxDBContainerTest.java | 14 ++-- .../InfluxDBContainerWithUserTest.java | 26 +++---- .../jdbc/ContainerDatabaseDriverTest.java | 5 +- .../junit/jupiter/PostgresContainerTests.java | 4 +- ...tLifecycleAwareExceptionCapturingTest.java | 7 +- .../localstack/LocalstackContainerTest.java | 3 +- modules/mongodb/build.gradle | 1 + .../containers/MongoDBContainerTest.java | 11 +-- .../junit/oracle/SimpleOracleTest.java | 16 ++-- .../containers/PrestoContainerTest.java | 71 ++++++++---------- .../DefaultRecordingFileFactoryTest.java | 5 +- ...ChromeRecordingWebDriverContainerTest.java | 19 +++-- modules/solr/build.gradle | 2 +- .../containers/SolrContainerTest.java | 12 ++- .../junit/tidb/SimpleTiDBTest.java | 12 +-- .../containers/TrinoContainerTest.java | 26 +++---- .../vault/VaultContainerTest.java | 46 ++++++------ test-support/build.gradle | 1 + .../testsupport/FlakyRuleTest.java | 34 +++++---- 55 files changed, 325 insertions(+), 396 deletions(-) diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml index 6e73f4da4aa..6e51c56c942 100644 --- a/config/checkstyle/checkstyle.xml +++ b/config/checkstyle/checkstyle.xml @@ -29,11 +29,7 @@ org.assertj.core.api.Assertions.*, org.assertj.core.api.Assumptions.*, org.awaitility.Awaitility.*, - org.hamcrest.Matchers.*, - org.hamcrest.MatcherAssert.*, - org.junit.Assert.*, org.junit.Assume.*, - org.junit.jupiter.api.Assertions.*, org.mockito.Mockito.*, org.mockito.ArgumentMatchers.*, org.mockserver.model.HttpRequest.*, diff --git a/core/src/test/java/org/testcontainers/containers/output/FrameConsumerResultCallbackTest.java b/core/src/test/java/org/testcontainers/containers/output/FrameConsumerResultCallbackTest.java index 5282712b733..298e7a981a0 100644 --- a/core/src/test/java/org/testcontainers/containers/output/FrameConsumerResultCallbackTest.java +++ b/core/src/test/java/org/testcontainers/containers/output/FrameConsumerResultCallbackTest.java @@ -10,8 +10,7 @@ import java.util.concurrent.TimeoutException; import java.util.function.Consumer; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; public class FrameConsumerResultCallbackTest { @@ -26,7 +25,7 @@ public void passStderrFrameWithoutColors() { ToStringConsumer consumer = new ToStringConsumer(); callback.addConsumer(OutputFrame.OutputType.STDERR, consumer); callback.onNext(new Frame(StreamType.STDERR, FRAME_PAYLOAD.getBytes())); - assertEquals(LOG_RESULT, consumer.toUtf8String()); + assertThat(consumer.toUtf8String()).isEqualTo(LOG_RESULT); } @Test @@ -35,7 +34,7 @@ public void passStderrFrameWithColors() { ToStringConsumer consumer = new ToStringConsumer().withRemoveAnsiCodes(false); callback.addConsumer(OutputFrame.OutputType.STDERR, consumer); callback.onNext(new Frame(StreamType.STDERR, FRAME_PAYLOAD.getBytes())); - assertEquals(FRAME_PAYLOAD, consumer.toUtf8String()); + assertThat(consumer.toUtf8String()).isEqualTo(FRAME_PAYLOAD); } @Test @@ -44,7 +43,7 @@ public void passStdoutFrameWithoutColors() { ToStringConsumer consumer = new ToStringConsumer(); callback.addConsumer(OutputFrame.OutputType.STDOUT, consumer); callback.onNext(new Frame(StreamType.STDOUT, FRAME_PAYLOAD.getBytes())); - assertEquals(LOG_RESULT, consumer.toUtf8String()); + assertThat(consumer.toUtf8String()).isEqualTo(LOG_RESULT); } @Test @@ -53,7 +52,7 @@ public void passStdoutFrameWithColors() { ToStringConsumer consumer = new ToStringConsumer().withRemoveAnsiCodes(false); callback.addConsumer(OutputFrame.OutputType.STDOUT, consumer); callback.onNext(new Frame(StreamType.STDOUT, FRAME_PAYLOAD.getBytes())); - assertEquals(FRAME_PAYLOAD, consumer.toUtf8String()); + assertThat(consumer.toUtf8String()).isEqualTo(FRAME_PAYLOAD); } @Test @@ -62,7 +61,7 @@ public void basicConsumer() { BasicConsumer consumer = new BasicConsumer(); callback.addConsumer(OutputFrame.OutputType.STDOUT, consumer); callback.onNext(new Frame(StreamType.STDOUT, FRAME_PAYLOAD.getBytes())); - assertEquals(LOG_RESULT, consumer.toString()); + assertThat(consumer.toString()).isEqualTo(LOG_RESULT); } @Test @@ -71,7 +70,7 @@ public void passStdoutNull() { ToStringConsumer consumer = new ToStringConsumer().withRemoveAnsiCodes(false); callback.addConsumer(OutputFrame.OutputType.STDOUT, consumer); callback.onNext(new Frame(StreamType.STDOUT, null)); - assertEquals("", consumer.toUtf8String()); + assertThat(consumer.toUtf8String()).isEqualTo(""); } @Test @@ -81,7 +80,7 @@ public void passStdoutEmptyLine() { ToStringConsumer consumer = new ToStringConsumer().withRemoveAnsiCodes(false); callback.addConsumer(OutputFrame.OutputType.STDOUT, consumer); callback.onNext(new Frame(StreamType.STDOUT, payload.getBytes())); - assertEquals(payload, consumer.toUtf8String()); + assertThat(consumer.toUtf8String()).isEqualTo(payload); } @Test @@ -91,7 +90,7 @@ public void passStdoutSingleLine() { ToStringConsumer consumer = new ToStringConsumer().withRemoveAnsiCodes(false); callback.addConsumer(OutputFrame.OutputType.STDOUT, consumer); callback.onNext(new Frame(StreamType.STDOUT, payload.getBytes())); - assertEquals(payload, consumer.toUtf8String()); + assertThat(consumer.toUtf8String()).isEqualTo(payload); } @Test @@ -101,7 +100,7 @@ public void passStdoutSingleLineWithNewline() { ToStringConsumer consumer = new ToStringConsumer().withRemoveAnsiCodes(false); callback.addConsumer(OutputFrame.OutputType.STDOUT, consumer); callback.onNext(new Frame(StreamType.STDOUT, payload.getBytes())); - assertEquals(payload, consumer.toUtf8String()); + assertThat(consumer.toUtf8String()).isEqualTo(payload); } @Test @@ -130,7 +129,7 @@ public void passRawFrameWithoutColors() throws TimeoutException, IOException { } catch (Exception e) { exception = e; } - assertTrue(exception instanceof TimeoutException); + assertThat(exception instanceof TimeoutException).isTrue(); callback.close(); waitConsumer.waitUntil( frame -> frame.getType() == OutputFrame.OutputType.STDOUT && frame.getUtf8String().equals("Test3"), @@ -180,7 +179,7 @@ public void passRawFrameWithColors() throws TimeoutException, IOException { } catch (Exception e) { exception = e; } - assertTrue(exception instanceof TimeoutException); + assertThat(exception instanceof TimeoutException).isTrue(); callback.close(); waitConsumer.waitUntil( frame -> { @@ -208,7 +207,7 @@ public void reconstructBreakedUnicode() throws IOException { callback.onNext(new Frame(StreamType.RAW, bytes1)); callback.onNext(new Frame(StreamType.RAW, bytes2)); callback.close(); - assertEquals(payload, consumer.toUtf8String()); + assertThat(consumer.toUtf8String()).isEqualTo(payload); } private static class BasicConsumer implements Consumer { diff --git a/core/src/test/java/org/testcontainers/dockerclient/DockerClientConfigUtilsTest.java b/core/src/test/java/org/testcontainers/dockerclient/DockerClientConfigUtilsTest.java index 39e1338bc46..d14ffda42cc 100644 --- a/core/src/test/java/org/testcontainers/dockerclient/DockerClientConfigUtilsTest.java +++ b/core/src/test/java/org/testcontainers/dockerclient/DockerClientConfigUtilsTest.java @@ -7,9 +7,7 @@ import java.net.URI; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; +import static org.assertj.core.api.Assertions.assertThat; public class DockerClientConfigUtilsTest { @@ -23,7 +21,7 @@ public void getDockerHostIpAddressShouldReturnLocalhostWhenUnixSocket() { client, URI.create("unix:///var/run/docker.sock") ); - assertEquals("localhost", actual); + assertThat(actual).isEqualTo("localhost"); } @Test @@ -32,7 +30,7 @@ public void getDockerHostIpAddressShouldReturnDockerHostIpWhenHttpsUri() { client, URI.create("http://12.23.34.45") ); - assertEquals("12.23.34.45", actual); + assertThat(actual).isEqualTo("12.23.34.45"); } @Test @@ -41,7 +39,7 @@ public void getDockerHostIpAddressShouldReturnDockerHostIpWhenTcpUri() { client, URI.create("tcp://12.23.34.45") ); - assertEquals("12.23.34.45", actual); + assertThat(actual).isEqualTo("12.23.34.45"); } @Test @@ -50,11 +48,11 @@ public void getDockerHostIpAddressShouldReturnNullWhenUnsupportedUriScheme() { client, URI.create("gopher://12.23.34.45") ); - assertNull(actual); + assertThat(actual).isNull(); } @Test(timeout = 5_000) public void getDefaultGateway() { - assertNotNull(DockerClientConfigUtils.getDefaultGateway()); + assertThat(DockerClientConfigUtils.getDefaultGateway()).isNotNull(); } } diff --git a/core/src/test/java/org/testcontainers/images/AgeBasedPullPolicyTest.java b/core/src/test/java/org/testcontainers/images/AgeBasedPullPolicyTest.java index 96fdfa4010b..0bdf969688b 100644 --- a/core/src/test/java/org/testcontainers/images/AgeBasedPullPolicyTest.java +++ b/core/src/test/java/org/testcontainers/images/AgeBasedPullPolicyTest.java @@ -8,8 +8,7 @@ import java.time.temporal.ChronoUnit; import java.util.UUID; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; public class AgeBasedPullPolicyTest { @@ -20,9 +19,9 @@ public void shouldPull() { ImageData imageData = ImageData.builder().createdAt(Instant.now().minus(2, ChronoUnit.HOURS)).build(); AgeBasedPullPolicy oneHour = new AgeBasedPullPolicy(Duration.of(1L, ChronoUnit.HOURS)); - assertTrue(oneHour.shouldPullCached(imageName, imageData)); + assertThat(oneHour.shouldPullCached(imageName, imageData)).isTrue(); AgeBasedPullPolicy fiveHours = new AgeBasedPullPolicy(Duration.of(5L, ChronoUnit.HOURS)); - assertFalse(fiveHours.shouldPullCached(imageName, imageData)); + assertThat(fiveHours.shouldPullCached(imageName, imageData)).isFalse(); } } diff --git a/core/src/test/java/org/testcontainers/images/ParsedDockerfileTest.java b/core/src/test/java/org/testcontainers/images/ParsedDockerfileTest.java index cd4509965e6..f8c66860022 100644 --- a/core/src/test/java/org/testcontainers/images/ParsedDockerfileTest.java +++ b/core/src/test/java/org/testcontainers/images/ParsedDockerfileTest.java @@ -6,7 +6,7 @@ import java.nio.file.Paths; import java.util.Arrays; -import static org.junit.Assert.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; public class ParsedDockerfileTest { @@ -15,11 +15,9 @@ public void doesSimpleParsing() { final ParsedDockerfile parsedDockerfile = new ParsedDockerfile( Arrays.asList("FROM someimage", "RUN something") ); - assertEquals( - "extracts a single image name", - Sets.newHashSet("someimage"), - parsedDockerfile.getDependencyImageNames() - ); + assertThat(parsedDockerfile.getDependencyImageNames()) + .as("extracts a single image name") + .isEqualTo(Sets.newHashSet("someimage")); } @Test @@ -27,11 +25,9 @@ public void isCaseInsensitive() { final ParsedDockerfile parsedDockerfile = new ParsedDockerfile( Arrays.asList("from someimage", "RUN something") ); - assertEquals( - "extracts a single image name", - Sets.newHashSet("someimage"), - parsedDockerfile.getDependencyImageNames() - ); + assertThat(parsedDockerfile.getDependencyImageNames()) + .as("extracts a single image name") + .isEqualTo(Sets.newHashSet("someimage")); } @Test @@ -39,11 +35,9 @@ public void handlesTags() { final ParsedDockerfile parsedDockerfile = new ParsedDockerfile( Arrays.asList("FROM someimage:tag", "RUN something") ); - assertEquals( - "retains tags in image names", - Sets.newHashSet("someimage:tag"), - parsedDockerfile.getDependencyImageNames() - ); + assertThat(parsedDockerfile.getDependencyImageNames()) + .as("retains tags in image names") + .isEqualTo(Sets.newHashSet("someimage:tag")); } @Test @@ -51,11 +45,9 @@ public void handlesDigests() { final ParsedDockerfile parsedDockerfile = new ParsedDockerfile( Arrays.asList("FROM someimage@sha256:abc123", "RUN something") ); - assertEquals( - "retains digests in image names", - Sets.newHashSet("someimage@sha256:abc123"), - parsedDockerfile.getDependencyImageNames() - ); + assertThat(parsedDockerfile.getDependencyImageNames()) + .as("retains digests in image names") + .isEqualTo(Sets.newHashSet("someimage@sha256:abc123")); } @Test @@ -63,11 +55,9 @@ public void ignoringCommentedFromLines() { final ParsedDockerfile parsedDockerfile = new ParsedDockerfile( Arrays.asList("FROM someimage", "#FROM somethingelse") ); - assertEquals( - "ignores commented from lines", - Sets.newHashSet("someimage"), - parsedDockerfile.getDependencyImageNames() - ); + assertThat(parsedDockerfile.getDependencyImageNames()) + .as("ignores commented from lines") + .isEqualTo(Sets.newHashSet("someimage")); } @Test @@ -75,11 +65,9 @@ public void ignoringBuildStageNames() { final ParsedDockerfile parsedDockerfile = new ParsedDockerfile( Arrays.asList("FROM someimage --as=base", "RUN something", "FROM nextimage", "RUN something") ); - assertEquals( - "ignores build stage names and allows multiple images to be extracted", - Sets.newHashSet("someimage", "nextimage"), - parsedDockerfile.getDependencyImageNames() - ); + assertThat(parsedDockerfile.getDependencyImageNames()) + .as("ignores build stage names and allows multiple images to be extracted") + .isEqualTo(Sets.newHashSet("someimage", "nextimage")); } @Test @@ -87,7 +75,9 @@ public void ignoringPlatformArgs() { final ParsedDockerfile parsedDockerfile = new ParsedDockerfile( Arrays.asList("FROM --platform=linux/amd64 someimage", "RUN something") ); - assertEquals("ignores platform args", Sets.newHashSet("someimage"), parsedDockerfile.getDependencyImageNames()); + assertThat(parsedDockerfile.getDependencyImageNames()) + .as("ignores platform args") + .isEqualTo(Sets.newHashSet("someimage")); } @Test @@ -95,7 +85,9 @@ public void ignoringExtraPlatformArgs() { final ParsedDockerfile parsedDockerfile = new ParsedDockerfile( Arrays.asList("FROM --platform=linux/amd64 --somethingelse=value someimage", "RUN something") ); - assertEquals("ignores platform args", Sets.newHashSet("someimage"), parsedDockerfile.getDependencyImageNames()); + assertThat(parsedDockerfile.getDependencyImageNames()) + .as("ignores platform args") + .isEqualTo(Sets.newHashSet("someimage")); } @Test @@ -103,20 +95,16 @@ public void handlesGracefullyIfNoFromLine() { final ParsedDockerfile parsedDockerfile = new ParsedDockerfile( Arrays.asList("RUN something", "# is this even a valid Dockerfile?") ); - assertEquals( - "handles invalid Dockerfiles gracefully", - Sets.newHashSet(), - parsedDockerfile.getDependencyImageNames() - ); + assertThat(parsedDockerfile.getDependencyImageNames()) + .as("handles invalid Dockerfiles gracefully") + .isEqualTo(Sets.newHashSet()); } @Test public void handlesGracefullyIfDockerfileNotFound() { final ParsedDockerfile parsedDockerfile = new ParsedDockerfile(Paths.get("nonexistent.Dockerfile")); - assertEquals( - "handles missing Dockerfiles gracefully", - Sets.newHashSet(), - parsedDockerfile.getDependencyImageNames() - ); + assertThat(parsedDockerfile.getDependencyImageNames()) + .as("handles missing Dockerfiles gracefully") + .isEqualTo(Sets.newHashSet()); } } diff --git a/core/src/test/java/org/testcontainers/images/RemoteDockerImageTest.java b/core/src/test/java/org/testcontainers/images/RemoteDockerImageTest.java index 592d46f33d1..9e4ac66fb53 100644 --- a/core/src/test/java/org/testcontainers/images/RemoteDockerImageTest.java +++ b/core/src/test/java/org/testcontainers/images/RemoteDockerImageTest.java @@ -9,9 +9,7 @@ import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicBoolean; -import static org.hamcrest.Matchers.containsString; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertThat; +import static org.assertj.core.api.Assertions.assertThat; public class RemoteDockerImageTest { @@ -19,7 +17,7 @@ public class RemoteDockerImageTest { public void toStringContainsOnlyImageName() { String imageName = Base58.randomString(8).toLowerCase(); RemoteDockerImage remoteDockerImage = new RemoteDockerImage(DockerImageName.parse(imageName)); - assertThat(remoteDockerImage.toString(), containsString("imageName=" + imageName)); + assertThat(remoteDockerImage.toString()).contains("imageName=" + imageName); } @Test @@ -28,7 +26,7 @@ public void toStringWithExceptionContainsOnlyImageNameFuture() { imageNameFuture.completeExceptionally(new RuntimeException("arbitrary")); RemoteDockerImage remoteDockerImage = new RemoteDockerImage(imageNameFuture); - assertThat(remoteDockerImage.toString(), containsString("imageName=java.lang.RuntimeException: arbitrary")); + assertThat(remoteDockerImage.toString()).contains("imageName=java.lang.RuntimeException: arbitrary"); } @Test(timeout = 5000L) @@ -36,17 +34,17 @@ public void toStringDoesntResolveImageNameFuture() { CompletableFuture imageNameFuture = new CompletableFuture<>(); // verify that we've set up the test properly - assertFalse(imageNameFuture.isDone()); + assertThat(imageNameFuture).isNotDone(); RemoteDockerImage remoteDockerImage = new RemoteDockerImage(imageNameFuture); - assertThat(remoteDockerImage.toString(), containsString("imageName=")); + assertThat(remoteDockerImage.toString()).contains("imageName="); // Make sure the act of calling toString doesn't resolve the imageNameFuture - assertFalse(imageNameFuture.isDone()); + assertThat(imageNameFuture).isNotDone(); String imageName = Base58.randomString(8).toLowerCase(); imageNameFuture.complete(imageName); - assertThat(remoteDockerImage.toString(), containsString("imageName=" + imageName)); + assertThat(remoteDockerImage.toString()).contains("imageName=" + imageName); } @Test(timeout = 5000L) @@ -62,17 +60,17 @@ protected String resolve() { }; // verify that we've set up the test properly - assertFalse(imageNameFuture.isDone()); + assertThat(imageNameFuture).isNotDone(); RemoteDockerImage remoteDockerImage = new RemoteDockerImage(imageNameFuture); - assertThat(remoteDockerImage.toString(), containsString("imageName=")); + assertThat(remoteDockerImage.toString()).contains("imageName="); // Make sure the act of calling toString doesn't resolve the imageNameFuture - assertFalse(imageNameFuture.isDone()); - assertFalse(resolved.get()); + assertThat(imageNameFuture).isNotDone(); + assertThat(resolved).isFalse(); // Trigger resolve imageNameFuture.get(); - assertThat(remoteDockerImage.toString(), containsString("imageName=" + imageName)); + assertThat(remoteDockerImage.toString()).contains("imageName=" + imageName); } } diff --git a/core/src/test/java/org/testcontainers/junit/FixedHostPortContainerTest.java b/core/src/test/java/org/testcontainers/junit/FixedHostPortContainerTest.java index 08545535e6b..45feb85b359 100644 --- a/core/src/test/java/org/testcontainers/junit/FixedHostPortContainerTest.java +++ b/core/src/test/java/org/testcontainers/junit/FixedHostPortContainerTest.java @@ -13,8 +13,7 @@ import java.net.Socket; import java.util.concurrent.TimeUnit; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.equalTo; +import static org.assertj.core.api.Assertions.assertThat; /** * Test of {@link FixedHostPortGenericContainer}. Note that this is not an example of typical use (usually, a container @@ -68,14 +67,12 @@ public void testFixedHostPortMapping() throws IOException { ) { echoServer.start(); - assertThat( - "Port mapping does not seem to match given fixed port", - echoServer.getMappedPort(TEST_PORT), - equalTo(unusedHostPort) - ); + assertThat(echoServer.getMappedPort(TEST_PORT)) + .as("Port mapping does not seem to match given fixed port") + .isEqualTo(unusedHostPort); final String content = this.readResponse(echoServer, unusedHostPort); - assertThat("Returned echo from fixed port does not match expected", content, equalTo(TEST_RESPONSE)); + assertThat(content).as("Returned echo from fixed port does not match expected").isEqualTo(TEST_RESPONSE); } } diff --git a/core/src/test/java/org/testcontainers/junit/wait/strategy/AbstractWaitStrategyTest.java b/core/src/test/java/org/testcontainers/junit/wait/strategy/AbstractWaitStrategyTest.java index 45809f95627..23ad3802c1b 100644 --- a/core/src/test/java/org/testcontainers/junit/wait/strategy/AbstractWaitStrategyTest.java +++ b/core/src/test/java/org/testcontainers/junit/wait/strategy/AbstractWaitStrategyTest.java @@ -13,7 +13,6 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.catchThrowable; -import static org.junit.Assert.assertTrue; /** * Common test methods for {@link WaitStrategy} implementations. @@ -124,9 +123,8 @@ protected void waitUntilReadyAndSucceed(GenericContainer container) { // start() blocks until successful or timeout container.start(); - assertTrue( - String.format("Expected container to be ready after timeout of %sms", WAIT_TIMEOUT_MILLIS), - ready.get() - ); + assertThat(ready) + .as(String.format("Expected container to be ready after timeout of %sms", WAIT_TIMEOUT_MILLIS)) + .isTrue(); } } diff --git a/core/src/test/java/org/testcontainers/junit/wait/strategy/HttpWaitStrategyTest.java b/core/src/test/java/org/testcontainers/junit/wait/strategy/HttpWaitStrategyTest.java index 972bf2ab66a..37bbe985bac 100644 --- a/core/src/test/java/org/testcontainers/junit/wait/strategy/HttpWaitStrategyTest.java +++ b/core/src/test/java/org/testcontainers/junit/wait/strategy/HttpWaitStrategyTest.java @@ -11,8 +11,7 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Predicate; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.containsString; +import static org.assertj.core.api.Assertions.assertThat; /** * Tests for {@link HttpWaitStrategy}. @@ -55,8 +54,8 @@ public void testWaitUntilReadyWithSuccessWithCustomHeaders() { String logs = container.getLogs(); - assertThat(logs, containsString("foo: bar")); - assertThat(logs, containsString("baz: boo")); + assertThat(logs).contains("foo: bar"); + assertThat(logs).contains("baz: boo"); } } diff --git a/core/src/test/java/org/testcontainers/utility/ComparableVersionTest.java b/core/src/test/java/org/testcontainers/utility/ComparableVersionTest.java index fb3c7e05c0b..cd95d8a5f3d 100644 --- a/core/src/test/java/org/testcontainers/utility/ComparableVersionTest.java +++ b/core/src/test/java/org/testcontainers/utility/ComparableVersionTest.java @@ -7,7 +7,7 @@ import java.util.Arrays; -import static org.junit.Assert.assertArrayEquals; +import static org.assertj.core.api.Assertions.assertThat; @RunWith(Parameterized.class) public class ComparableVersionTest { @@ -23,7 +23,7 @@ public ComparableVersionTest(final String given, final int[] expected) { @Test public void shouldParseVersions() { - assertArrayEquals(expected, ComparableVersion.parseVersion(given)); + assertThat(ComparableVersion.parseVersion(given)).containsExactly(expected); } @Parameters(name = "Parsed version: {0}={1}") diff --git a/core/src/test/java/org/testcontainers/utility/DefaultImageNameSubstitutorTest.java b/core/src/test/java/org/testcontainers/utility/DefaultImageNameSubstitutorTest.java index 0978003f815..90f7745edd7 100644 --- a/core/src/test/java/org/testcontainers/utility/DefaultImageNameSubstitutorTest.java +++ b/core/src/test/java/org/testcontainers/utility/DefaultImageNameSubstitutorTest.java @@ -5,8 +5,7 @@ import org.junit.Test; import org.mockito.Mockito; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.eq; public class DefaultImageNameSubstitutorTest { @@ -34,7 +33,7 @@ public void testConfigurationLookup() { final DockerImageName substitute = underTest.apply(ORIGINAL_IMAGE); - assertEquals("match is found", SUBSTITUTE_IMAGE, substitute); - assertTrue("compatibility is automatically set", substitute.isCompatibleWith(ORIGINAL_IMAGE)); + assertThat(substitute).as("match is found").isEqualTo(SUBSTITUTE_IMAGE); + assertThat(substitute.isCompatibleWith(ORIGINAL_IMAGE)).as("compatibility is automatically set").isTrue(); } } diff --git a/core/src/test/java/org/testcontainers/utility/DockerImageNameCompatibilityTest.java b/core/src/test/java/org/testcontainers/utility/DockerImageNameCompatibilityTest.java index e9aac2ed475..0bb1b3f75ca 100644 --- a/core/src/test/java/org/testcontainers/utility/DockerImageNameCompatibilityTest.java +++ b/core/src/test/java/org/testcontainers/utility/DockerImageNameCompatibilityTest.java @@ -1,17 +1,12 @@ package org.testcontainers.utility; -import org.junit.Rule; import org.junit.Test; -import org.junit.rules.ExpectedException; import static org.assertj.core.api.Assertions.assertThat; -import static org.hamcrest.Matchers.containsString; +import static org.assertj.core.api.Assertions.assertThatThrownBy; public class DockerImageNameCompatibilityTest { - @Rule - public ExpectedException thrown = ExpectedException.none(); - @Test public void testPlainImage() { DockerImageName subject = DockerImageName.parse("foo"); @@ -98,10 +93,9 @@ public void testAssertMethodAcceptsCompatible() { @Test public void testAssertMethodRejectsIncompatible() { - thrown.expect(IllegalStateException.class); - thrown.expectMessage(containsString("Failed to verify that image 'foo' is a compatible substitute for 'bar'")); - DockerImageName subject = DockerImageName.parse("foo"); - subject.assertCompatibleWith(DockerImageName.parse("bar")); + assertThatThrownBy(() -> subject.assertCompatibleWith(DockerImageName.parse("bar"))) + .isInstanceOf(IllegalStateException.class) + .hasMessageContaining("Failed to verify that image 'foo' is a compatible substitute for 'bar'"); } } diff --git a/core/src/test/java/org/testcontainers/utility/DockerStatusTest.java b/core/src/test/java/org/testcontainers/utility/DockerStatusTest.java index 36196aaf4c1..304bbc4190d 100644 --- a/core/src/test/java/org/testcontainers/utility/DockerStatusTest.java +++ b/core/src/test/java/org/testcontainers/utility/DockerStatusTest.java @@ -11,8 +11,7 @@ import java.time.ZoneId; import java.time.format.DateTimeFormatter; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.when; /** @@ -63,24 +62,24 @@ public static Object[][] parameters() { @Test public void testRunning() throws Exception { - assertTrue(DockerStatus.isContainerRunning(running, minimumDuration, now)); - assertTrue(DockerStatus.isContainerRunning(runningVariant, minimumDuration, now)); - assertFalse(DockerStatus.isContainerRunning(shortRunning, minimumDuration, now)); - assertFalse(DockerStatus.isContainerRunning(created, minimumDuration, now)); - assertFalse(DockerStatus.isContainerRunning(createdVariant, minimumDuration, now)); - assertFalse(DockerStatus.isContainerRunning(exited, minimumDuration, now)); - assertFalse(DockerStatus.isContainerRunning(paused, minimumDuration, now)); + assertThat(DockerStatus.isContainerRunning(running, minimumDuration, now)).isTrue(); + assertThat(DockerStatus.isContainerRunning(runningVariant, minimumDuration, now)).isTrue(); + assertThat(DockerStatus.isContainerRunning(shortRunning, minimumDuration, now)).isFalse(); + assertThat(DockerStatus.isContainerRunning(created, minimumDuration, now)).isFalse(); + assertThat(DockerStatus.isContainerRunning(createdVariant, minimumDuration, now)).isFalse(); + assertThat(DockerStatus.isContainerRunning(exited, minimumDuration, now)).isFalse(); + assertThat(DockerStatus.isContainerRunning(paused, minimumDuration, now)).isFalse(); } @Test public void testStopped() throws Exception { - assertFalse(DockerStatus.isContainerStopped(running)); - assertFalse(DockerStatus.isContainerStopped(runningVariant)); - assertFalse(DockerStatus.isContainerStopped(shortRunning)); - assertFalse(DockerStatus.isContainerStopped(created)); - assertFalse(DockerStatus.isContainerStopped(createdVariant)); - assertTrue(DockerStatus.isContainerStopped(exited)); - assertFalse(DockerStatus.isContainerStopped(paused)); + assertThat(DockerStatus.isContainerStopped(running)).isFalse(); + assertThat(DockerStatus.isContainerStopped(runningVariant)).isFalse(); + assertThat(DockerStatus.isContainerStopped(shortRunning)).isFalse(); + assertThat(DockerStatus.isContainerStopped(created)).isFalse(); + assertThat(DockerStatus.isContainerStopped(createdVariant)).isFalse(); + assertThat(DockerStatus.isContainerStopped(exited)).isTrue(); + assertThat(DockerStatus.isContainerStopped(paused)).isFalse(); } private String buildTimestamp(Instant instant) { diff --git a/core/src/test/java/org/testcontainers/utility/FilterRegistryTest.java b/core/src/test/java/org/testcontainers/utility/FilterRegistryTest.java index 30367975d1e..2741edb5cee 100644 --- a/core/src/test/java/org/testcontainers/utility/FilterRegistryTest.java +++ b/core/src/test/java/org/testcontainers/utility/FilterRegistryTest.java @@ -13,9 +13,7 @@ import java.util.List; import java.util.Map.Entry; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; public class FilterRegistryTest { @@ -38,7 +36,7 @@ public void registerReturnsTrueIfAcknowledgementIsReadFromInputStream() throws I boolean successful = registry.register(FILTERS); - assertTrue(successful); + assertThat(successful).isTrue(); } @Test @@ -47,7 +45,7 @@ public void registerReturnsFalseIfNoAcknowledgementIsReadFromInputStream() throw boolean successful = registry.register(FILTERS); - assertFalse(successful); + assertThat(successful).isFalse(); } @Test @@ -57,7 +55,7 @@ public void registerWritesUrlEncodedFiltersAndNewlineToOutputStream() throws IOE registry.register(FILTERS); - assertEquals(URL_ENCODED_FILTERS + NEW_LINE, new String(outputStream.toByteArray())); + assertThat(new String(outputStream.toByteArray())).isEqualTo(URL_ENCODED_FILTERS + NEW_LINE); } private static InputStream inputStream(byte[] bytes) { diff --git a/core/src/test/java/org/testcontainers/utility/ResourceReaperTest.java b/core/src/test/java/org/testcontainers/utility/ResourceReaperTest.java index 60f57a28b3e..77c5d528ba4 100644 --- a/core/src/test/java/org/testcontainers/utility/ResourceReaperTest.java +++ b/core/src/test/java/org/testcontainers/utility/ResourceReaperTest.java @@ -20,7 +20,6 @@ import java.util.stream.Stream; import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.Assert.assertEquals; public class ResourceReaperTest { @@ -81,7 +80,7 @@ private Map runProcess(Consumer processExecutor processExecutorConsumer.accept(processExecutor); ProcessResult result = processExecutor.execute(); - assertEquals(0, result.getExitValue()); + assertThat(result.getExitValue()).isEqualTo(0); String labelsJson = Stream .of(result.outputUTF8().split("\n")) diff --git a/docs/examples/junit4/generic/src/test/java/generic/CmdModifierTest.java b/docs/examples/junit4/generic/src/test/java/generic/CmdModifierTest.java index 218ed90e5e0..a32f23ca8c2 100644 --- a/docs/examples/junit4/generic/src/test/java/generic/CmdModifierTest.java +++ b/docs/examples/junit4/generic/src/test/java/generic/CmdModifierTest.java @@ -12,7 +12,7 @@ import java.io.IOException; import java.util.Objects; -import static org.junit.Assert.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; public class CmdModifierTest { @@ -43,13 +43,13 @@ public class CmdModifierTest { @Test public void testHostnameModified() throws IOException, InterruptedException { final Container.ExecResult execResult = theCache.execInContainer("hostname"); - assertEquals("the-cache", execResult.getStdout().trim()); + assertThat(execResult.getStdout().trim()).isEqualTo("the-cache"); } @Test public void testMemoryLimitModified() throws IOException, InterruptedException { final Container.ExecResult execResult = memoryLimitedRedis.execInContainer("cat", getMemoryLimitFilePath()); - assertEquals(String.valueOf(memoryInBytes), execResult.getStdout().trim()); + assertThat(execResult.getStdout().trim()).isEqualTo(String.valueOf(memoryInBytes)); } private String getMemoryLimitFilePath() { diff --git a/docs/examples/junit4/generic/src/test/java/generic/CommandsTest.java b/docs/examples/junit4/generic/src/test/java/generic/CommandsTest.java index 80292465521..3cab77770dc 100644 --- a/docs/examples/junit4/generic/src/test/java/generic/CommandsTest.java +++ b/docs/examples/junit4/generic/src/test/java/generic/CommandsTest.java @@ -5,7 +5,7 @@ import org.testcontainers.containers.GenericContainer; import org.testcontainers.utility.DockerImageName; -import static org.junit.Assert.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; public class CommandsTest { @@ -18,6 +18,6 @@ public class CommandsTest { @Test public void testStartupCommandOverrideApplied() { - assertTrue(redisWithCustomPort.isRunning()); // good enough to check that the container started listening + assertThat(redisWithCustomPort.isRunning()).isTrue(); // good enough to check that the container started listening } } diff --git a/docs/examples/junit4/generic/src/test/java/generic/ContainerCreationTest.java b/docs/examples/junit4/generic/src/test/java/generic/ContainerCreationTest.java index 0f6269968dd..d98e09ff036 100644 --- a/docs/examples/junit4/generic/src/test/java/generic/ContainerCreationTest.java +++ b/docs/examples/junit4/generic/src/test/java/generic/ContainerCreationTest.java @@ -5,7 +5,7 @@ import org.testcontainers.containers.GenericContainer; import org.testcontainers.utility.DockerImageName; -import static org.junit.Assert.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; public class ContainerCreationTest { @@ -39,7 +39,7 @@ public class ContainerCreationTest { @Test public void testStartup() { - assertTrue(redis.isRunning()); // good enough to check that the container started listening - assertTrue(alpine.isRunning()); // good enough to check that the container started listening + assertThat(redis.isRunning()).isTrue(); // good enough to check that the container started listening + assertThat(alpine.isRunning()).isTrue(); // good enough to check that the container started listening } } diff --git a/docs/examples/junit4/generic/src/test/java/generic/DependsOnTest.java b/docs/examples/junit4/generic/src/test/java/generic/DependsOnTest.java index 6534a35a00f..7116f375283 100644 --- a/docs/examples/junit4/generic/src/test/java/generic/DependsOnTest.java +++ b/docs/examples/junit4/generic/src/test/java/generic/DependsOnTest.java @@ -4,7 +4,7 @@ import org.junit.Test; import org.testcontainers.containers.GenericContainer; -import static org.junit.Assert.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; public class DependsOnTest { @@ -19,7 +19,7 @@ public class DependsOnTest { @Test public void testContainersAllStarted() { - assertTrue(redis.isRunning()); - assertTrue(nginx.isRunning()); + assertThat(redis.isRunning()).isTrue(); + assertThat(nginx.isRunning()).isTrue(); } } diff --git a/docs/examples/junit4/generic/src/test/java/generic/ExecTest.java b/docs/examples/junit4/generic/src/test/java/generic/ExecTest.java index 7548ae4a613..7ccb0f2cc54 100644 --- a/docs/examples/junit4/generic/src/test/java/generic/ExecTest.java +++ b/docs/examples/junit4/generic/src/test/java/generic/ExecTest.java @@ -8,7 +8,7 @@ import java.io.IOException; -import static org.junit.Assert.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; public class ExecTest { @@ -26,8 +26,8 @@ public void testSimpleExec() throws IOException, InterruptedException { Container.ExecResult lsResult = container.execInContainer("ls", "-al", "/"); String stdout = lsResult.getStdout(); int exitCode = lsResult.getExitCode(); - assertTrue(stdout.contains("somefile.txt")); - assertTrue(exitCode == 0); + assertThat(stdout).contains("somefile.txt"); + assertThat(exitCode).isZero(); // } } } diff --git a/docs/examples/junit4/generic/src/test/java/generic/HostPortExposedTest.java b/docs/examples/junit4/generic/src/test/java/generic/HostPortExposedTest.java index 22bf0332a9d..3e80133411a 100644 --- a/docs/examples/junit4/generic/src/test/java/generic/HostPortExposedTest.java +++ b/docs/examples/junit4/generic/src/test/java/generic/HostPortExposedTest.java @@ -13,7 +13,7 @@ import java.io.OutputStream; import java.net.InetSocketAddress; -import static org.junit.Assert.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; public class HostPortExposedTest { @@ -62,6 +62,6 @@ public void testContainerRunningAgainstExposedHostPort() { // } final String pageSource = webDriver.getPageSource(); - assertTrue(pageSource.contains("Hello World!")); + assertThat(pageSource).contains("Hello World!"); } } diff --git a/docs/examples/junit4/generic/src/test/java/generic/WaitStrategiesTest.java b/docs/examples/junit4/generic/src/test/java/generic/WaitStrategiesTest.java index bdc0d3ced8a..0b2b166d1d9 100644 --- a/docs/examples/junit4/generic/src/test/java/generic/WaitStrategiesTest.java +++ b/docs/examples/junit4/generic/src/test/java/generic/WaitStrategiesTest.java @@ -8,7 +8,7 @@ import org.testcontainers.containers.wait.strategy.WaitStrategy; import org.testcontainers.utility.DockerImageName; -import static org.junit.Assert.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; public class WaitStrategiesTest { @@ -69,8 +69,8 @@ public class WaitStrategiesTest { @Test public void testContainersAllStarted() { - assertTrue(nginx.isRunning()); - assertTrue(nginxWithHttpWait.isRunning()); - assertTrue(containerWithLogWait.isRunning()); + assertThat(nginx.isRunning()).isTrue(); + assertThat(nginxWithHttpWait.isRunning()).isTrue(); + assertThat(containerWithLogWait.isRunning()).isTrue(); } } diff --git a/docs/examples/junit4/redis/build.gradle b/docs/examples/junit4/redis/build.gradle index ea36761b716..7ab7cddc88f 100644 --- a/docs/examples/junit4/redis/build.gradle +++ b/docs/examples/junit4/redis/build.gradle @@ -5,4 +5,5 @@ dependencies { testImplementation "junit:junit:4.13.2" testImplementation project(":testcontainers") + testImplementation 'org.assertj:assertj-core:3.23.1' } diff --git a/docs/examples/junit4/redis/src/test/java/quickstart/RedisBackedCacheIntTest.java b/docs/examples/junit4/redis/src/test/java/quickstart/RedisBackedCacheIntTest.java index 1d3d178975f..0f219397bdc 100644 --- a/docs/examples/junit4/redis/src/test/java/quickstart/RedisBackedCacheIntTest.java +++ b/docs/examples/junit4/redis/src/test/java/quickstart/RedisBackedCacheIntTest.java @@ -6,7 +6,7 @@ import org.testcontainers.containers.GenericContainer; import org.testcontainers.utility.DockerImageName; -import static org.junit.Assert.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; public class RedisBackedCacheIntTest { @@ -33,6 +33,6 @@ public void testSimplePutAndGet() { underTest.put("test", "example"); String retrieved = underTest.get("test"); - assertEquals("example", retrieved); + assertThat(retrieved).isEqualTo("example"); } } diff --git a/docs/examples/junit4/redis/src/test/java/quickstart/RedisBackedCacheIntTestStep0.java b/docs/examples/junit4/redis/src/test/java/quickstart/RedisBackedCacheIntTestStep0.java index f837b3af0d5..e3f52403c48 100644 --- a/docs/examples/junit4/redis/src/test/java/quickstart/RedisBackedCacheIntTestStep0.java +++ b/docs/examples/junit4/redis/src/test/java/quickstart/RedisBackedCacheIntTestStep0.java @@ -4,7 +4,7 @@ import org.junit.Ignore; import org.junit.Test; -import static org.junit.Assert.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; @Ignore("This test class is deliberately invalid, as it relies on a non-existent local Redis") public class RedisBackedCacheIntTestStep0 { @@ -22,6 +22,6 @@ public void testSimplePutAndGet() { underTest.put("test", "example"); String retrieved = underTest.get("test"); - assertEquals("example", retrieved); + assertThat(retrieved).isEqualTo("example"); } } diff --git a/examples/linked-container/build.gradle b/examples/linked-container/build.gradle index ab32d55df91..1865f70f801 100644 --- a/examples/linked-container/build.gradle +++ b/examples/linked-container/build.gradle @@ -12,5 +12,6 @@ dependencies { testImplementation 'org.postgresql:postgresql:42.4.1' testImplementation 'ch.qos.logback:logback-classic:1.2.11' testImplementation 'org.testcontainers:postgresql' + testImplementation 'org.assertj:assertj-core:3.23.1' } diff --git a/examples/linked-container/src/test/java/com/example/linkedcontainer/RedmineClientTest.java b/examples/linked-container/src/test/java/com/example/linkedcontainer/RedmineClientTest.java index b84b2a9e2c9..6e4186698e7 100644 --- a/examples/linked-container/src/test/java/com/example/linkedcontainer/RedmineClientTest.java +++ b/examples/linked-container/src/test/java/com/example/linkedcontainer/RedmineClientTest.java @@ -5,7 +5,7 @@ import org.junit.rules.RuleChain; import org.testcontainers.containers.PostgreSQLContainer; -import static org.junit.Assert.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; /** * Tests for RedmineClient. @@ -33,9 +33,6 @@ public void canGetIssueCount() throws Exception { RedmineClient redmineClient = new RedmineClient( redmineContainer.getRedmineUrl()); - assertEquals( - "The issue count can be retrieved.", - 0, - redmineClient.getIssueCount()); + assertThat(redmineClient.getIssueCount()).as("The issue count can be retrieved.").isZero(); } } diff --git a/examples/neo4j-container/src/test/java/org/testcontainers/containers/Neo4jExampleTest.java b/examples/neo4j-container/src/test/java/org/testcontainers/containers/Neo4jExampleTest.java index d151e1a3f24..bc7aeed10bd 100644 --- a/examples/neo4j-container/src/test/java/org/testcontainers/containers/Neo4jExampleTest.java +++ b/examples/neo4j-container/src/test/java/org/testcontainers/containers/Neo4jExampleTest.java @@ -20,7 +20,7 @@ import org.testcontainers.utility.DockerImageName; import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.Assert.fail; +import static org.assertj.core.api.Assertions.fail; // junitExample { @Testcontainers diff --git a/modules/cassandra/build.gradle b/modules/cassandra/build.gradle index 385001db8f8..ff110637ced 100644 --- a/modules/cassandra/build.gradle +++ b/modules/cassandra/build.gradle @@ -9,5 +9,7 @@ configurations.all { dependencies { api project(":database-commons") api "com.datastax.cassandra:cassandra-driver-core:3.10.0" + testImplementation 'com.datastax.oss:java-driver-core:4.14.1' + testImplementation 'org.assertj:assertj-core:3.23.1' } diff --git a/modules/cassandra/src/test/java/org/testcontainers/containers/CassandraContainerTest.java b/modules/cassandra/src/test/java/org/testcontainers/containers/CassandraContainerTest.java index d1ad581bcc0..d8f21fc3068 100644 --- a/modules/cassandra/src/test/java/org/testcontainers/containers/CassandraContainerTest.java +++ b/modules/cassandra/src/test/java/org/testcontainers/containers/CassandraContainerTest.java @@ -9,9 +9,7 @@ import org.testcontainers.containers.wait.CassandraQueryWaitStrategy; import org.testcontainers.utility.DockerImageName; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; /** * @author Eugeny Karpov @@ -30,8 +28,8 @@ public void testSimple() { try (CassandraContainer cassandraContainer = new CassandraContainer<>(CASSANDRA_IMAGE)) { cassandraContainer.start(); ResultSet resultSet = performQuery(cassandraContainer, BASIC_QUERY); - assertTrue("Query was not applied", resultSet.wasApplied()); - assertNotNull("Result set has no release_version", resultSet.one().getString(0)); + assertThat(resultSet.wasApplied()).as("Query was applied").isTrue(); + assertThat(resultSet.one().getString(0)).as("Result set has release_version").isNotNull(); } } @@ -45,8 +43,8 @@ public void testSpecificVersion() { ) { cassandraContainer.start(); ResultSet resultSet = performQuery(cassandraContainer, BASIC_QUERY); - assertTrue("Query was not applied", resultSet.wasApplied()); - assertEquals("Cassandra has wrong version", cassandraVersion, resultSet.one().getString(0)); + assertThat(resultSet.wasApplied()).as("Query was applied").isTrue(); + assertThat(resultSet.one().getString(0)).as("Cassandra has right version").isEqualTo(cassandraVersion); } } @@ -58,12 +56,10 @@ public void testConfigurationOverride() { ) { cassandraContainer.start(); ResultSet resultSet = performQuery(cassandraContainer, "SELECT cluster_name FROM system.local"); - assertTrue("Query was not applied", resultSet.wasApplied()); - assertEquals( - "Cassandra configuration is not overridden", - TEST_CLUSTER_NAME_IN_CONF, - resultSet.one().getString(0) - ); + assertThat(resultSet.wasApplied()).as("Query was applied").isTrue(); + assertThat(resultSet.one().getString(0)) + .as("Cassandra configuration is overridden") + .isEqualTo(TEST_CLUSTER_NAME_IN_CONF); } } @@ -110,7 +106,7 @@ public void testCassandraQueryWaitStrategy() { ) { cassandraContainer.start(); ResultSet resultSet = performQuery(cassandraContainer, BASIC_QUERY); - assertTrue("Query was not applied", resultSet.wasApplied()); + assertThat(resultSet.wasApplied()).as("Query was applied").isTrue(); } } @@ -120,17 +116,17 @@ public void testCassandraGetCluster() { try (CassandraContainer cassandraContainer = new CassandraContainer<>()) { cassandraContainer.start(); ResultSet resultSet = performQuery(cassandraContainer.getCluster(), BASIC_QUERY); - assertTrue("Query was not applied", resultSet.wasApplied()); - assertNotNull("Result set has no release_version", resultSet.one().getString(0)); + assertThat(resultSet.wasApplied()).as("Query was applied").isTrue(); + assertThat(resultSet.one().getString(0)).as("Result set has release_version").isNotNull(); } } private void testInitScript(CassandraContainer cassandraContainer) { ResultSet resultSet = performQuery(cassandraContainer, "SELECT * FROM keySpaceTest.catalog_category"); - assertTrue("Query was not applied", resultSet.wasApplied()); + assertThat(resultSet.wasApplied()).as("Query was applied").isTrue(); Row row = resultSet.one(); - assertEquals("Inserted row is not in expected state", 1, row.getLong(0)); - assertEquals("Inserted row is not in expected state", "test_category", row.getString(1)); + assertThat(row.getLong(0)).as("Inserted row is in expected state").isEqualTo(1); + assertThat(row.getString(1)).as("Inserted row is in expected state").isEqualTo("test_category"); } private ResultSet performQuery(CassandraContainer cassandraContainer, String cql) { diff --git a/modules/cassandra/src/test/java/org/testcontainers/containers/CassandraDriver3Test.java b/modules/cassandra/src/test/java/org/testcontainers/containers/CassandraDriver3Test.java index 78f3fd2a664..502f2ca8e67 100644 --- a/modules/cassandra/src/test/java/org/testcontainers/containers/CassandraDriver3Test.java +++ b/modules/cassandra/src/test/java/org/testcontainers/containers/CassandraDriver3Test.java @@ -6,7 +6,7 @@ import org.junit.Rule; import org.junit.Test; -import static org.junit.Assert.assertNotNull; +import static org.assertj.core.api.Assertions.assertThat; public class CassandraDriver3Test { @@ -31,7 +31,7 @@ public void testCassandraGetContactPoint() { KeyspaceMetadata keyspace = session.getMetadata().getKeyspaces().get(CqlIdentifier.fromCql("test")); - assertNotNull("Failed to create test keyspace", keyspace); + assertThat(keyspace).as("keyspace created").isNotNull(); } } } diff --git a/modules/cassandra/src/test/java/org/testcontainers/containers/CassandraDriver4Test.java b/modules/cassandra/src/test/java/org/testcontainers/containers/CassandraDriver4Test.java index 3a05bc723bc..a4fb8c87b8e 100644 --- a/modules/cassandra/src/test/java/org/testcontainers/containers/CassandraDriver4Test.java +++ b/modules/cassandra/src/test/java/org/testcontainers/containers/CassandraDriver4Test.java @@ -6,7 +6,7 @@ import org.junit.Rule; import org.junit.Test; -import static org.junit.Assert.assertNotNull; +import static org.assertj.core.api.Assertions.assertThat; public class CassandraDriver4Test { @@ -29,7 +29,7 @@ public void testCassandraGetContactPoint() { KeyspaceMetadata keyspace = session.getMetadata().getKeyspaces().get(CqlIdentifier.fromCql("test")); - assertNotNull("Failed to create test keyspace", keyspace); + assertThat(keyspace).as("test keyspace created").isNotNull(); } } } diff --git a/modules/consul/src/test/java/org/testcontainers/consul/ConsulContainerTest.java b/modules/consul/src/test/java/org/testcontainers/consul/ConsulContainerTest.java index e29ff7ffbd8..c0c022d31bb 100644 --- a/modules/consul/src/test/java/org/testcontainers/consul/ConsulContainerTest.java +++ b/modules/consul/src/test/java/org/testcontainers/consul/ConsulContainerTest.java @@ -4,9 +4,6 @@ import com.ecwid.consul.v1.Response; import com.ecwid.consul.v1.kv.model.GetValue; import io.restassured.RestAssured; -import org.assertj.core.api.Assertions; -import org.hamcrest.CoreMatchers; -import org.hamcrest.MatcherAssert; import org.junit.ClassRule; import org.junit.Test; import org.testcontainers.containers.GenericContainer; @@ -17,6 +14,8 @@ import java.util.HashMap; import java.util.Map; +import static org.assertj.core.api.Assertions.assertThat; + /** * This test shows the pattern to use the ConsulContainer @ClassRule for a junit test. It also has tests that ensure * the properties were added correctly by reading from Consul with the CLI and over HTTP. @@ -31,21 +30,19 @@ public class ConsulContainerTest { public void readFirstPropertyPathWithCli() throws IOException, InterruptedException { GenericContainer.ExecResult result = consulContainer.execInContainer("consul", "kv", "get", "config/testing1"); final String output = result.getStdout().replaceAll("\\r?\\n", ""); - MatcherAssert.assertThat(output, CoreMatchers.containsString("value123")); + assertThat(output).contains("value123"); } @Test - public void readFirstSecretPathOverHttpApi() throws InterruptedException { - RestAssured + public void readFirstSecretPathOverHttpApi() { + io.restassured.response.Response response = RestAssured .given() .when() .get("http://" + getHostAndPort() + "/v1/kv/config/testing1") - .then() - .assertThat() - .body( - "[0].Value", - CoreMatchers.equalTo(Base64.getEncoder().encodeToString("value123".getBytes(StandardCharsets.UTF_8))) - ); + .andReturn(); + + assertThat(response.body().jsonPath().getString("[0].Value")) + .isEqualTo(Base64.getEncoder().encodeToString("value123".getBytes(StandardCharsets.UTF_8))); } @Test @@ -62,13 +59,13 @@ public void writeAndReadMultipleValuesUsingClient() { // Write operation properties.forEach((key, value) -> { Response writeResponse = consulClient.setKVValue(key, value); - Assertions.assertThat(writeResponse.getValue()).isTrue(); + assertThat(writeResponse.getValue()).isTrue(); }); // Read operation properties.forEach((key, value) -> { Response readResponse = consulClient.getKVValue(key); - Assertions.assertThat(readResponse.getValue().getDecodedValue()).isEqualTo(value); + assertThat(readResponse.getValue().getDecodedValue()).isEqualTo(value); }); } diff --git a/modules/couchbase/build.gradle b/modules/couchbase/build.gradle index 10970df542e..6cdacbcf77a 100644 --- a/modules/couchbase/build.gradle +++ b/modules/couchbase/build.gradle @@ -7,4 +7,5 @@ dependencies { testImplementation 'com.couchbase.client:java-client:3.3.3' testImplementation 'org.awaitility:awaitility:4.2.0' + testImplementation 'org.assertj:assertj-core:3.23.1' } diff --git a/modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java b/modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java index 1bdbe2ab1db..691dc1307ef 100644 --- a/modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java +++ b/modules/couchbase/src/test/java/org/testcontainers/couchbase/CouchbaseContainerTest.java @@ -27,10 +27,9 @@ import java.time.Duration; import java.util.function.Consumer; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.awaitility.Awaitility.await; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertThrows; public class CouchbaseContainerTest { @@ -66,7 +65,7 @@ public void testBasicContainerUsageForEnterpriseContainer() { JsonObject fooObject = collection.get("foo").contentAsObject(); - assertEquals("value", fooObject.getString("key")); + assertThat(fooObject.getString("key")).isEqualTo("value"); } ); } @@ -92,7 +91,7 @@ public void testBasicContainerUsageForCommunityContainer() { JsonObject fooObject = collection.get("foo").contentAsObject(); - assertEquals("value", fooObject.getString("key")); + assertThat(fooObject.getString("key")).isEqualTo("value"); } ); } @@ -118,7 +117,7 @@ public void testBucketIsFlushableIfEnabled() { cluster.buckets().flushBucket(bucketDefinition.getName()); - await().untilAsserted(() -> assertFalse(collection.exists("foo").exists())); + await().untilAsserted(() -> assertThat(collection.exists("foo").exists()).isFalse()); } ); } @@ -134,12 +133,10 @@ public void testFailureIfCommunityUsedWithAnalytics() { CouchbaseContainer container = new CouchbaseContainer(COUCHBASE_IMAGE_COMMUNITY) .withEnabledServices(CouchbaseService.KV, CouchbaseService.ANALYTICS) ) { - assertThrows( - ContainerLaunchException.class, - () -> { + assertThatThrownBy(() -> { setUpClient(container, cluster -> {}); - } - ); + }) + .isInstanceOf(ContainerLaunchException.class); } } @@ -153,12 +150,10 @@ public void testFailureIfCommunityUsedWithEventing() { CouchbaseContainer container = new CouchbaseContainer(COUCHBASE_IMAGE_COMMUNITY) .withEnabledServices(CouchbaseService.KV, CouchbaseService.EVENTING) ) { - assertThrows( - ContainerLaunchException.class, - () -> { + assertThatThrownBy(() -> { setUpClient(container, cluster -> {}); - } - ); + }) + .isInstanceOf(ContainerLaunchException.class); } } diff --git a/modules/influxdb/build.gradle b/modules/influxdb/build.gradle index a7ff4e66729..2a75c343653 100644 --- a/modules/influxdb/build.gradle +++ b/modules/influxdb/build.gradle @@ -5,4 +5,5 @@ dependencies { compileOnly 'org.influxdb:influxdb-java:2.23' testImplementation 'org.influxdb:influxdb-java:2.23' + testImplementation 'org.assertj:assertj-core:3.23.1' } diff --git a/modules/influxdb/src/test/java/org/testcontainers/containers/InfluxDBContainerTest.java b/modules/influxdb/src/test/java/org/testcontainers/containers/InfluxDBContainerTest.java index c7fc71a3a19..bcf3d21f25b 100644 --- a/modules/influxdb/src/test/java/org/testcontainers/containers/InfluxDBContainerTest.java +++ b/modules/influxdb/src/test/java/org/testcontainers/containers/InfluxDBContainerTest.java @@ -4,9 +4,7 @@ import org.junit.ClassRule; import org.junit.Test; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.notNullValue; -import static org.junit.Assert.assertThat; +import static org.assertj.core.api.Assertions.assertThat; public class InfluxDBContainerTest { @@ -19,28 +17,28 @@ public class InfluxDBContainerTest { public void getUrl() { String actual = influxDBContainer.getUrl(); - assertThat(actual, notNullValue()); + assertThat(actual).isNotNull(); } @Test public void getNewInfluxDB() { InfluxDB actual = influxDBContainer.getNewInfluxDB(); - assertThat(actual, notNullValue()); - assertThat(actual.ping(), notNullValue()); + assertThat(actual).isNotNull(); + assertThat(actual.ping()).isNotNull(); } @Test public void getLivenessCheckPort() { Integer actual = influxDBContainer.getLivenessCheckPort(); - assertThat(actual, notNullValue()); + assertThat(actual).isNotNull(); } @Test public void isRunning() { boolean actual = influxDBContainer.isRunning(); - assertThat(actual, is(true)); + assertThat(actual).isTrue(); } } diff --git a/modules/influxdb/src/test/java/org/testcontainers/containers/InfluxDBContainerWithUserTest.java b/modules/influxdb/src/test/java/org/testcontainers/containers/InfluxDBContainerWithUserTest.java index a7ae347e7cc..7a34bbb4fd6 100644 --- a/modules/influxdb/src/test/java/org/testcontainers/containers/InfluxDBContainerWithUserTest.java +++ b/modules/influxdb/src/test/java/org/testcontainers/containers/InfluxDBContainerWithUserTest.java @@ -9,11 +9,7 @@ import java.util.concurrent.TimeUnit; -import static org.hamcrest.Matchers.hasItem; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.notNullValue; -import static org.hamcrest.Matchers.nullValue; -import static org.junit.Assert.assertThat; +import static org.assertj.core.api.Assertions.assertThat; public class InfluxDBContainerWithUserTest { @@ -35,20 +31,20 @@ public class InfluxDBContainerWithUserTest { public void describeDatabases() { InfluxDB actual = influxDBContainer.getNewInfluxDB(); - assertThat(actual, notNullValue()); - assertThat(actual.describeDatabases(), hasItem(DATABASE)); + assertThat(actual).isNotNull(); + assertThat(actual.describeDatabases()).contains(DATABASE); } @Test public void checkVersion() { InfluxDB actual = influxDBContainer.getNewInfluxDB(); - assertThat(actual, notNullValue()); + assertThat(actual).isNotNull(); - assertThat(actual.ping(), notNullValue()); - assertThat(actual.ping().getVersion(), is(TEST_VERSION)); + assertThat(actual.ping()).isNotNull(); + assertThat(actual.ping().getVersion()).isEqualTo(TEST_VERSION); - assertThat(actual.version(), is(TEST_VERSION)); + assertThat(actual.version()).isEqualTo(TEST_VERSION); } @Test @@ -67,9 +63,9 @@ public void queryForWriteAndRead() { Query query = new Query("SELECT idle FROM cpu", DATABASE); QueryResult actual = influxDB.query(query); - assertThat(actual, notNullValue()); - assertThat(actual.getError(), nullValue()); - assertThat(actual.getResults(), notNullValue()); - assertThat(actual.getResults().size(), is(1)); + assertThat(actual).isNotNull(); + assertThat(actual.getError()).isNull(); + assertThat(actual.getResults()).isNotNull(); + assertThat(actual.getResults()).hasSize(1); } } diff --git a/modules/jdbc/src/test/java/org/testcontainers/jdbc/ContainerDatabaseDriverTest.java b/modules/jdbc/src/test/java/org/testcontainers/jdbc/ContainerDatabaseDriverTest.java index 5b4d223cd36..4edd5d449ba 100644 --- a/modules/jdbc/src/test/java/org/testcontainers/jdbc/ContainerDatabaseDriverTest.java +++ b/modules/jdbc/src/test/java/org/testcontainers/jdbc/ContainerDatabaseDriverTest.java @@ -1,7 +1,6 @@ package org.testcontainers.jdbc; import org.hamcrest.CoreMatchers; -import org.junit.Assert; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; @@ -11,6 +10,8 @@ import java.sql.SQLException; import java.util.Properties; +import static org.assertj.core.api.Assertions.assertThat; + public class ContainerDatabaseDriverTest { private static final String PLAIN_POSTGRESQL_JDBC_URL = "jdbc:postgresql://localhost:5432/test"; @@ -22,7 +23,7 @@ public class ContainerDatabaseDriverTest { public void shouldNotTryToConnectToNonMatchingJdbcUrlDirectly() throws SQLException { ContainerDatabaseDriver driver = new ContainerDatabaseDriver(); Connection connection = driver.connect(PLAIN_POSTGRESQL_JDBC_URL, new Properties()); - Assert.assertNull(connection); + assertThat(connection).isNull(); } @Test diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/PostgresContainerTests.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/PostgresContainerTests.java index 186d4095b0d..ee6d1f1a841 100644 --- a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/PostgresContainerTests.java +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/PostgresContainerTests.java @@ -8,7 +8,7 @@ import java.sql.ResultSet; import java.sql.Statement; -import static org.junit.Assert.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; @Testcontainers class PostgresContainerTests { @@ -35,7 +35,7 @@ void waits_until_postgres_accepts_jdbc_connections() throws Exception { resultSet.next(); int resultSetInt = resultSet.getInt(1); - assertEquals(1, resultSetInt); + assertThat(resultSetInt).isEqualTo(1); } } } diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareExceptionCapturingTest.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareExceptionCapturingTest.java index 09b816be56e..b64f2f3c328 100644 --- a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareExceptionCapturingTest.java +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareExceptionCapturingTest.java @@ -6,8 +6,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestMethodOrder; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assume.assumeTrue; // The order of @ExtendsWith and @Testcontainers is crucial in order for the tests @@ -32,7 +31,7 @@ void failing_test_should_pass_throwable_to_testContainer() { @Order(2) void should_have_captured_thrownException() { Throwable capturedThrowable = startedTestContainer.getCapturedThrowable(); - assertTrue(capturedThrowable instanceof AssumptionViolatedException); - assertEquals("got: , expected: is ", capturedThrowable.getMessage()); + assertThat(capturedThrowable).isInstanceOf(AssumptionViolatedException.class); + assertThat(capturedThrowable.getMessage()).isEqualTo("got: , expected: is "); } } diff --git a/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java b/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java index a678d30eb80..b879b27f0e6 100644 --- a/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java +++ b/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java @@ -22,7 +22,6 @@ import com.amazonaws.services.sqs.model.CreateQueueResult; import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.IOUtils; -import org.junit.Assert; import org.junit.ClassRule; import org.junit.Test; import org.junit.experimental.runners.Enclosed; @@ -320,7 +319,7 @@ private String runAwsCliAgainstDockerNetworkContainer(String command) throws Exc ) .split(" "); final Container.ExecResult execResult = awsCliInDockerNetwork.execInContainer(commandParts); - Assert.assertEquals(0, execResult.getExitCode()); + assertThat(execResult.getExitCode()).isEqualTo(0); final String logs = execResult.getStdout() + execResult.getStderr(); log.info(logs); diff --git a/modules/mongodb/build.gradle b/modules/mongodb/build.gradle index 7f9725bf0e8..6e4b5208298 100644 --- a/modules/mongodb/build.gradle +++ b/modules/mongodb/build.gradle @@ -4,4 +4,5 @@ dependencies { api project(':testcontainers') testImplementation("org.mongodb:mongodb-driver-sync:4.7.1") + testImplementation 'org.assertj:assertj-core:3.23.1' } 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 6c98978f20a..ff6d0d6ccba 100644 --- a/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBContainerTest.java +++ b/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBContainerTest.java @@ -13,10 +13,7 @@ import org.junit.Test; import org.testcontainers.utility.DockerImageName; -import static org.hamcrest.Matchers.endsWith; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertThat; +import static org.assertj.core.api.Assertions.assertThat; public class MongoDBContainerTest { @@ -35,7 +32,7 @@ public void shouldExecuteTransactions() { // } final String mongoRsUrl = mongoDBContainer.getReplicaSetUrl(); - assertNotNull(mongoRsUrl); + assertThat(mongoRsUrl).isNotNull(); final String connectionString = mongoDBContainer.getConnectionString(); final MongoClient mongoSyncClientBase = MongoClients.create(connectionString); final MongoClient mongoSyncClient = MongoClients.create(mongoRsUrl); @@ -76,7 +73,7 @@ public void shouldExecuteTransactions() { try { final String trxResultActual = clientSession.withTransaction(txnBody, txnOptions); - assertEquals(trxResult, trxResultActual); + assertThat(trxResultActual).isEqualTo(trxResult); } catch (RuntimeException re) { throw new IllegalStateException(re.getMessage(), re); } finally { @@ -98,7 +95,7 @@ public void shouldTestDatabaseName() { try (final MongoDBContainer mongoDBContainer = new MongoDBContainer(DockerImageName.parse("mongo:4.0.10"))) { mongoDBContainer.start(); final String databaseName = "my-db"; - assertThat(mongoDBContainer.getReplicaSetUrl(databaseName), endsWith(databaseName)); + assertThat(mongoDBContainer.getReplicaSetUrl(databaseName)).endsWith(databaseName); } } } diff --git a/modules/oracle-xe/src/test/java/org/testcontainers/junit/oracle/SimpleOracleTest.java b/modules/oracle-xe/src/test/java/org/testcontainers/junit/oracle/SimpleOracleTest.java index b48bd67efc9..b7afba041de 100644 --- a/modules/oracle-xe/src/test/java/org/testcontainers/junit/oracle/SimpleOracleTest.java +++ b/modules/oracle-xe/src/test/java/org/testcontainers/junit/oracle/SimpleOracleTest.java @@ -8,8 +8,8 @@ import java.sql.ResultSet; import java.sql.SQLException; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.fail; public class SimpleOracleTest extends AbstractContainerDatabaseTest { @@ -20,15 +20,15 @@ public class SimpleOracleTest extends AbstractContainerDatabaseTest { private void runTest(OracleContainer container, String databaseName, String username, String password) throws SQLException { //Test config was honored - assertEquals(databaseName, container.getDatabaseName()); - assertEquals(username, container.getUsername()); - assertEquals(password, container.getPassword()); + assertThat(container.getDatabaseName()).isEqualTo(databaseName); + assertThat(container.getUsername()).isEqualTo(username); + assertThat(container.getPassword()).isEqualTo(password); //Test we can get a connection container.start(); ResultSet resultSet = performQuery(container, "SELECT 1 FROM dual"); int resultSetInt = resultSet.getInt(1); - assertEquals("A basic SELECT query succeeds", 1, resultSetInt); + assertThat(resultSetInt).as("A basic SELECT query succeeds").isEqualTo(1); } @Test @@ -38,7 +38,7 @@ public void testDefaultSettings() throws SQLException { // Match against the last '/' String urlSuffix = oracle.getJdbcUrl().split("(\\/)(?!.*\\/)", 2)[1]; - assertEquals("xepdb1", urlSuffix); + assertThat(urlSuffix).isEqualTo("xepdb1"); } } @@ -81,7 +81,7 @@ public void testSID() throws SQLException { // Match against the last ':' String urlSuffix = oracle.getJdbcUrl().split("(\\:)(?!.*\\:)", 2)[1]; - assertEquals("xe", urlSuffix); + assertThat(urlSuffix).isEqualTo("xe"); } } diff --git a/modules/presto/src/test/java/org/testcontainers/containers/PrestoContainerTest.java b/modules/presto/src/test/java/org/testcontainers/containers/PrestoContainerTest.java index a54108ea1a2..721186807aa 100644 --- a/modules/presto/src/test/java/org/testcontainers/containers/PrestoContainerTest.java +++ b/modules/presto/src/test/java/org/testcontainers/containers/PrestoContainerTest.java @@ -1,6 +1,5 @@ package org.testcontainers.containers; -import org.junit.Assert; import org.junit.Test; import org.testcontainers.PrestoTestImages; @@ -13,9 +12,7 @@ import java.util.Arrays; import java.util.List; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; /** * @author findepi @@ -31,8 +28,10 @@ public void testSimple() throws Exception { Statement statement = connection.createStatement(); ResultSet resultSet = statement.executeQuery("SELECT DISTINCT node_version FROM system.runtime.nodes") ) { - assertTrue("No result", resultSet.next()); - assertEquals("Presto version", PrestoContainer.DEFAULT_TAG, resultSet.getString("node_version")); + assertThat(resultSet.next()).as("has result").isTrue(); + assertThat(resultSet.getString("node_version")) + .as("Presto version") + .isEqualTo(PrestoContainer.DEFAULT_TAG); } } } @@ -48,12 +47,10 @@ public void testSpecificVersion() throws Exception { Statement statement = connection.createStatement(); ResultSet resultSet = statement.executeQuery("SELECT DISTINCT node_version FROM system.runtime.nodes") ) { - assertTrue("No result", resultSet.next()); - assertEquals( - "Presto version", - PrestoTestImages.PRESTO_PREVIOUS_VERSION_TEST_IMAGE.getVersionPart(), - resultSet.getString("node_version") - ); + assertThat(resultSet.next()).as("has result").isTrue(); + assertThat(resultSet.getString("node_version")) + .as("Presto version") + .isEqualTo(PrestoTestImages.PRESTO_PREVIOUS_VERSION_TEST_IMAGE.getVersionPart()); } } } @@ -86,7 +83,7 @@ public void testQueryMemoryAndTpch() throws SQLException { while (resultSet.next()) { actualElements.add(resultSet.getInt("element")); } - Assert.assertEquals(Arrays.asList(2, 4, 42, 42, 42), actualElements); + assertThat(actualElements).isEqualTo(Arrays.asList(2, 4, 42, 42, 42)); } } } @@ -102,9 +99,9 @@ public void testInitScript() throws Exception { Statement statement = connection.createStatement(); ResultSet resultSet = statement.executeQuery("SELECT a FROM memory.default.test_table") ) { - assertTrue("No result", resultSet.next()); - assertEquals("Value", 12345678909324L, resultSet.getObject("a")); - assertFalse("Too many result", resultSet.next()); + assertThat(resultSet.next()).as("has result").isTrue(); + assertThat(resultSet.getObject("a")).as("Value").isEqualTo(12345678909324L); + assertThat(resultSet.next()).as("only has one result").isFalse(); } } } @@ -117,47 +114,39 @@ public void testTcJdbcUri() throws Exception { ) ) { // Verify metadata with tc: JDBC connection URI - assertEquals( - connection.getMetaData().getDatabaseMajorVersion(), - Integer.parseInt(PrestoContainer.DEFAULT_TAG) - ); + assertThat(Integer.parseInt(PrestoContainer.DEFAULT_TAG)) + .isEqualTo(connection.getMetaData().getDatabaseMajorVersion()); // Verify transactions with tc: JDBC connection URI - assertTrue("Is autocommit", connection.getAutoCommit()); + assertThat(connection.getAutoCommit()).as("Is autocommit").isTrue(); connection.setAutoCommit(false); - assertFalse("Is autocommit", connection.getAutoCommit()); - assertEquals( - "Transaction isolation", - Connection.TRANSACTION_READ_UNCOMMITTED, - connection.getTransactionIsolation() - ); + assertThat(connection.getAutoCommit()).as("Is autocommit").isFalse(); + assertThat(connection.getTransactionIsolation()) + .as("Transaction isolation") + .isEqualTo(Connection.TRANSACTION_READ_UNCOMMITTED); try (Statement statement = connection.createStatement()) { - assertEquals( - "Update result", - 0, - statement.executeUpdate("CREATE TABLE memory.default.test_tc(a bigint)") - ); + assertThat(statement.executeUpdate("CREATE TABLE memory.default.test_tc(a bigint)")) + .as("Update result") + .isEqualTo(0); try ( ResultSet resultSet = statement.executeQuery( "SELECT sum(cast(node_version AS bigint)) AS v FROM system.runtime.nodes" ) ) { - assertTrue(resultSet.next()); - assertEquals(PrestoContainer.DEFAULT_TAG, resultSet.getString("v")); - assertFalse(resultSet.next()); + assertThat(resultSet.next()).isTrue(); + assertThat(resultSet.getString("v")).isEqualTo(PrestoContainer.DEFAULT_TAG); + assertThat(resultSet.next()).isFalse(); } connection.commit(); } finally { connection.rollback(); } connection.setAutoCommit(true); - assertTrue("Is autocommit", connection.getAutoCommit()); - assertEquals( - "Transaction isolation should be retained", - Connection.TRANSACTION_READ_UNCOMMITTED, - connection.getTransactionIsolation() - ); + assertThat(connection.getAutoCommit()).as("Is autocommit").isTrue(); + assertThat(connection.getTransactionIsolation()) + .as("Transaction isolation should be retained") + .isEqualTo(Connection.TRANSACTION_READ_UNCOMMITTED); } } } diff --git a/modules/selenium/src/test/java/org/testcontainers/containers/DefaultRecordingFileFactoryTest.java b/modules/selenium/src/test/java/org/testcontainers/containers/DefaultRecordingFileFactoryTest.java index 0d8daaad219..aa894810930 100644 --- a/modules/selenium/src/test/java/org/testcontainers/containers/DefaultRecordingFileFactoryTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/containers/DefaultRecordingFileFactoryTest.java @@ -15,8 +15,7 @@ import java.util.Collection; import java.util.List; -import static org.hamcrest.Matchers.hasItem; -import static org.junit.Assert.assertThat; +import static org.assertj.core.api.Assertions.assertThat; @RunWith(Parameterized.class) @Value @@ -68,6 +67,6 @@ public void recordingFileThatShouldDescribeTheTestResultAtThePresentTime() throw ) ); - assertThat(expectedPossibleFileNames, hasItem(recordingFile)); + assertThat(expectedPossibleFileNames).contains(recordingFile); } } diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/ChromeRecordingWebDriverContainerTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/ChromeRecordingWebDriverContainerTest.java index b2b7d8d6b1c..d0bdb796181 100644 --- a/modules/selenium/src/test/java/org/testcontainers/junit/ChromeRecordingWebDriverContainerTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/junit/ChromeRecordingWebDriverContainerTest.java @@ -25,8 +25,7 @@ import java.util.Optional; import java.util.concurrent.TimeUnit; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; @RunWith(Enclosed.class) public class ChromeRecordingWebDriverContainerTest extends BaseWebDriverContainerTest { @@ -56,7 +55,7 @@ public void recordingTestThatShouldBeRecordedAndRetainedInFlvFormatAsDefault() t .withNetwork(NETWORK) ) { File[] files = runSimpleExploreInContainer(chrome, "PASSED-.*\\.flv"); - assertEquals("Recorded file not found", 1, files.length); + assertThat(files).as("Recorded file found").hasSize(1); } } @@ -98,7 +97,7 @@ public void recordingTestShouldHaveFlvExtension() throws InterruptedException { .withNetwork(NETWORK) ) { File[] files = runSimpleExploreInContainer(chrome, "PASSED-.*\\.flv"); - assertEquals("Recorded file not found", 1, files.length); + assertThat(files).as("Recorded file found").hasSize(1); } } @@ -116,7 +115,7 @@ public void recordingTestShouldHaveMp4Extension() throws InterruptedException { .withNetwork(NETWORK) ) { File[] files = runSimpleExploreInContainer(chrome, "PASSED-.*\\.mp4"); - assertEquals("Recorded file not found", 1, files.length); + assertThat(files).as("Recorded file found").hasSize(1); } } @@ -152,10 +151,10 @@ public void recordingTestThatShouldHaveCorrectDuration() throws IOException, Int .start(); String ffmpegOutput = container.getLogs(); - assertTrue( - "Duration is incorrect in:\n " + ffmpegOutput, - ffmpegOutput.contains("Duration: 00:") && !(ffmpegOutput.contains("Duration: 00:00:00.00")) - ); + assertThat(ffmpegOutput) + .as("Duration starts with 00:") + .contains("Duration: 00:") + .doesNotContain("Duration: 00:00:00.00"); } } } @@ -216,7 +215,7 @@ public String getFilesystemFriendlyName() { ); String[] files = vncRecordingDirectory.getRoot().list(new PatternFilenameFilter("FAILED-.*\\.flv")); - assertEquals("Recorded file not found", 1, files.length); + assertThat(files).as("recorded file count").hasSize(1); } } diff --git a/modules/solr/build.gradle b/modules/solr/build.gradle index 5a6326f3844..f8e2a99888d 100644 --- a/modules/solr/build.gradle +++ b/modules/solr/build.gradle @@ -6,5 +6,5 @@ dependencies { shaded 'com.squareup.okhttp3:okhttp:4.9.3' testImplementation 'org.apache.solr:solr-solrj:8.11.1' - + testImplementation 'org.assertj:assertj-core:3.23.1' } diff --git a/modules/solr/src/test/java/org/testcontainers/containers/SolrContainerTest.java b/modules/solr/src/test/java/org/testcontainers/containers/SolrContainerTest.java index 9b561e5cda9..94a02acf579 100644 --- a/modules/solr/src/test/java/org/testcontainers/containers/SolrContainerTest.java +++ b/modules/solr/src/test/java/org/testcontainers/containers/SolrContainerTest.java @@ -10,9 +10,7 @@ import java.io.IOException; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.is; +import static org.assertj.core.api.Assertions.assertThat; /** * @author Simon Schneider @@ -36,8 +34,8 @@ public void solrCloudTest() throws IOException, SolrServerException { try (SolrContainer container = new SolrContainer(SOLR_IMAGE)) { container.start(); SolrPingResponse response = getClient(container).ping("dummy"); - assertThat(response.getStatus(), is(0)); - assertThat(response.jsonStr(), containsString("zkConnected\":true")); + assertThat(response.getStatus()).isZero(); + assertThat(response.jsonStr()).contains("zkConnected\":true"); } } @@ -46,8 +44,8 @@ public void solrStandaloneTest() throws IOException, SolrServerException { try (SolrContainer container = new SolrContainer(SOLR_IMAGE).withZookeeper(false)) { container.start(); SolrPingResponse response = getClient(container).ping("dummy"); - assertThat(response.getStatus(), is(0)); - assertThat(response.jsonStr(), containsString("zkConnected\":null")); + assertThat(response.getStatus()).isZero(); + assertThat(response.jsonStr()).contains("zkConnected\":null"); } } diff --git a/modules/tidb/src/test/java/org/testcontainers/junit/tidb/SimpleTiDBTest.java b/modules/tidb/src/test/java/org/testcontainers/junit/tidb/SimpleTiDBTest.java index c4833f14224..714c9854f39 100644 --- a/modules/tidb/src/test/java/org/testcontainers/junit/tidb/SimpleTiDBTest.java +++ b/modules/tidb/src/test/java/org/testcontainers/junit/tidb/SimpleTiDBTest.java @@ -1,7 +1,5 @@ package org.testcontainers.junit.tidb; -import org.hamcrest.Matchers; -import org.junit.Assert; import org.junit.Test; import org.testcontainers.TiDBTestImages; import org.testcontainers.db.AbstractContainerDatabaseTest; @@ -10,6 +8,8 @@ import java.sql.ResultSet; import java.sql.SQLException; +import static org.assertj.core.api.Assertions.assertThat; + public class SimpleTiDBTest extends AbstractContainerDatabaseTest { @Test @@ -20,7 +20,7 @@ public void testSimple() throws SQLException { ResultSet resultSet = performQuery(tidb, "SELECT 1"); int resultSetInt = resultSet.getInt(1); - Assert.assertEquals("A basic SELECT query succeeds", 1, resultSetInt); + assertThat(resultSetInt).isEqualTo(1); } } @@ -34,7 +34,7 @@ public void testExplicitInitScript() throws SQLException { ResultSet resultSet = performQuery(tidb, "SELECT foo FROM bar"); String firstColumnValue = resultSet.getString(1); - Assert.assertEquals("Value from init script should equal real value", "hello world", firstColumnValue); + assertThat(firstColumnValue).isEqualTo("hello world"); } } @@ -45,8 +45,8 @@ public void testWithAdditionalUrlParamInJdbcUrl() { try { tidb.start(); String jdbcUrl = tidb.getJdbcUrl(); - Assert.assertThat(jdbcUrl, Matchers.containsString("?")); - Assert.assertThat(jdbcUrl, Matchers.containsString("sslmode=disable")); + assertThat(jdbcUrl).contains("?"); + assertThat(jdbcUrl).contains("sslmode=disable"); } finally { tidb.stop(); } diff --git a/modules/trino/src/test/java/org/testcontainers/containers/TrinoContainerTest.java b/modules/trino/src/test/java/org/testcontainers/containers/TrinoContainerTest.java index 97bbdf6dedd..a58944946f0 100644 --- a/modules/trino/src/test/java/org/testcontainers/containers/TrinoContainerTest.java +++ b/modules/trino/src/test/java/org/testcontainers/containers/TrinoContainerTest.java @@ -7,9 +7,7 @@ import java.sql.ResultSet; import java.sql.Statement; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; public class TrinoContainerTest { @@ -22,8 +20,10 @@ public void testSimple() throws Exception { Statement statement = connection.createStatement(); ResultSet resultSet = statement.executeQuery("SELECT DISTINCT node_version FROM system.runtime.nodes") ) { - assertTrue("No result", resultSet.next()); - assertEquals("Trino version", TrinoContainer.DEFAULT_TAG, resultSet.getString("node_version")); + assertThat(resultSet.next()).as("results").isTrue(); + assertThat(resultSet.getString("node_version")) + .as("Trino version") + .isEqualTo(TrinoContainer.DEFAULT_TAG); } } } @@ -37,12 +37,10 @@ public void testSpecificVersion() throws Exception { Statement statement = connection.createStatement(); ResultSet resultSet = statement.executeQuery("SELECT DISTINCT node_version FROM system.runtime.nodes") ) { - assertTrue("No result", resultSet.next()); - assertEquals( - "Trino version", - TrinoTestImages.TRINO_PREVIOUS_VERSION_TEST_IMAGE.getVersionPart(), - resultSet.getString("node_version") - ); + assertThat(resultSet.next()).as("results").isTrue(); + assertThat(resultSet.getString("node_version")) + .as("Trino version") + .isEqualTo(TrinoTestImages.TRINO_PREVIOUS_VERSION_TEST_IMAGE.getVersionPart()); } } } @@ -57,9 +55,9 @@ public void testInitScript() throws Exception { Statement statement = connection.createStatement(); ResultSet resultSet = statement.executeQuery("SELECT a FROM memory.default.test_table") ) { - assertTrue("No result", resultSet.next()); - assertEquals("Value", 12345678909324L, resultSet.getObject("a")); - assertFalse("Too many result", resultSet.next()); + assertThat(resultSet.next()).as("results").isTrue(); + assertThat(resultSet.getObject("a")).as("Value").isEqualTo(12345678909324L); + assertThat(resultSet.next()).as("results").isFalse(); } } } diff --git a/modules/vault/src/test/java/org/testcontainers/vault/VaultContainerTest.java b/modules/vault/src/test/java/org/testcontainers/vault/VaultContainerTest.java index 22310bf7a23..f7a7cd49eaf 100644 --- a/modules/vault/src/test/java/org/testcontainers/vault/VaultContainerTest.java +++ b/modules/vault/src/test/java/org/testcontainers/vault/VaultContainerTest.java @@ -1,5 +1,6 @@ package org.testcontainers.vault; +import io.restassured.response.Response; import org.junit.ClassRule; import org.junit.Test; import org.testcontainers.containers.GenericContainer; @@ -7,8 +8,7 @@ import java.io.IOException; import static io.restassured.RestAssured.given; -import static org.hamcrest.Matchers.*; -import static org.junit.Assert.assertThat; +import static org.assertj.core.api.Assertions.assertThat; /** * This test shows the pattern to use the VaultContainer @ClassRule for a junit test. It also has tests that ensure @@ -42,7 +42,7 @@ public void readFirstSecretPathWithCli() throws IOException, InterruptedExceptio "secret/testing1" ); final String output = result.getStdout().replaceAll("\\r?\\n", ""); - assertThat(output, containsString("password123")); + assertThat(output).contains("password123"); } @Test @@ -56,49 +56,45 @@ public void readSecondSecretPathWithCli() throws IOException, InterruptedExcepti ); final String output = result.getStdout().replaceAll("\\r?\\n", ""); System.out.println("output = " + output); - assertThat(output, containsString("password1")); - assertThat(output, containsString("password2")); - assertThat(output, containsString("password3")); - assertThat(output, containsString("password4")); + assertThat(output).contains("password1"); + assertThat(output).contains("password2"); + assertThat(output).contains("password3"); + assertThat(output).contains("password4"); } @Test public void readFirstSecretPathOverHttpApi() throws InterruptedException { - given() + Response response = given() .header("X-Vault-Token", VAULT_TOKEN) .when() .get("http://" + getHostAndPort() + "/v1/secret/data/testing1") - .then() - .assertThat() - .body("data.data.top_secret", equalTo("password123")); + .thenReturn(); + assertThat(response.body().jsonPath().getString("data.data.top_secret")).isEqualTo("password123"); } @Test public void readSecondSecretPathOverHttpApi() throws InterruptedException { - given() + Response response = given() .header("X-Vault-Token", VAULT_TOKEN) .when() .get("http://" + getHostAndPort() + "/v1/secret/data/testing2") - .then() - .assertThat() - .body("data.data.secret_one", containsString("password1")) - .assertThat() - .body("data.data.secret_two", containsString("password2")) - .assertThat() - .body("data.data.secret_three", hasItem("password3")) - .assertThat() - .body("data.data.secret_four", containsString("password4")); + .andReturn(); + + assertThat(response.body().jsonPath().getString("data.data.secret_one")).contains("password1"); + assertThat(response.body().jsonPath().getString("data.data.secret_two")).contains("password2"); + assertThat(response.body().jsonPath().getList("data.data.secret_three")).contains("password3"); + assertThat(response.body().jsonPath().getString("data.data.secret_four")).contains("password4"); } @Test public void readTransitKeyOverHttpApi() throws InterruptedException { - given() + Response response = given() .header("X-Vault-Token", VAULT_TOKEN) .when() .get("http://" + getHostAndPort() + "/v1/transit/keys/my-key") - .then() - .assertThat() - .body("data.name", equalTo("my-key")); + .thenReturn(); + + assertThat(response.body().jsonPath().getString("data.name")).isEqualTo("my-key"); } private String getHostAndPort() { diff --git a/test-support/build.gradle b/test-support/build.gradle index bd85b585b0f..ecf9a0bc60e 100644 --- a/test-support/build.gradle +++ b/test-support/build.gradle @@ -1,4 +1,5 @@ dependencies { implementation 'junit:junit:4.13.2' implementation 'org.slf4j:slf4j-api:1.7.30' + testImplementation 'org.assertj:assertj-core:3.23.1' } diff --git a/test-support/src/test/java/org/testcontainers/testsupport/FlakyRuleTest.java b/test-support/src/test/java/org/testcontainers/testsupport/FlakyRuleTest.java index 14a3d4c018c..60a311922d0 100644 --- a/test-support/src/test/java/org/testcontainers/testsupport/FlakyRuleTest.java +++ b/test-support/src/test/java/org/testcontainers/testsupport/FlakyRuleTest.java @@ -6,8 +6,8 @@ import java.lang.annotation.Annotation; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.fail; public class FlakyRuleTest { @@ -33,7 +33,9 @@ public void testIgnoresMethodWithoutAnnotation() throws Throwable { rule.apply(statement, description).evaluate(); fail("Should not reach here"); } catch (Exception ignored) {} - assertEquals("The statement should only be invoked once, even if it throws", 1, statement.invocationCount); + assertThat(statement.invocationCount) + .as("The statement should only be invoked once, even if it throws") + .isEqualTo(1); } @Test @@ -45,7 +47,7 @@ public void testRetriesMethodWithAnnotationUntilFailure() throws Throwable { rule.apply(statement, description).evaluate(); fail("Should not reach here"); } catch (Exception ignored) {} - assertEquals("The statement should be invoked three times", 3, statement.invocationCount); + assertThat(statement.invocationCount).as("The statement should be invoked three times").isEqualTo(3); } @Test @@ -57,7 +59,7 @@ public void testCustomRetryCount() throws Throwable { rule.apply(statement, description).evaluate(); fail("Should not reach here"); } catch (Exception ignored) {} - assertEquals("The statement should be invoked ten times", 10, statement.invocationCount); + assertThat(statement.invocationCount).as("The statement should be invoked ten times").isEqualTo(10); } @Test @@ -68,11 +70,9 @@ public void testRetriesMethodWithAnnotationUntilSuccess() throws Throwable { rule.apply(statement, description).evaluate(); - assertEquals( - "The statement should be invoked three times, and succeed the third time", - 3, - statement.invocationCount - ); + assertThat(statement.invocationCount) + .as("The statement should be invoked three times, and succeed the third time") + .isEqualTo(3); } @Test @@ -83,7 +83,7 @@ public void testDoesNotRetryMethodWithAnnotationIfNotThrowing() throws Throwable rule.apply(statement, description).evaluate(); - assertEquals("The statement should be invoked once", 1, statement.invocationCount); + assertThat(statement.invocationCount).as("The statement should be invoked once").isEqualTo(1); } @Test @@ -94,7 +94,9 @@ public void testTreatsExpiredAnnotationAsNoAnnotation() throws Throwable { rule.apply(statement, description).evaluate(); fail("Should not reach here"); } catch (Exception ignored) {} - assertEquals("The statement should only be invoked once, even if it throws", 1, statement.invocationCount); + assertThat(statement.invocationCount) + .as("The statement should only be invoked once, even if it throws") + .isEqualTo(1); } @Test @@ -105,7 +107,9 @@ public void testThrowsOnInvalidDateFormat() throws Throwable { rule.apply(statement, description).evaluate(); fail("Should not reach here"); } catch (IllegalArgumentException ignored) {} - assertEquals("The statement should not be invoked if the annotation is invalid", 0, statement.invocationCount); + assertThat(statement.invocationCount) + .as("The statement should not be invoked if the annotation is invalid") + .isEqualTo(0); } @Test @@ -116,7 +120,9 @@ public void testThrowsOnInvalidGitHubUrl() throws Throwable { rule.apply(statement, description).evaluate(); fail("Should not reach here"); } catch (IllegalArgumentException ignored) {} - assertEquals("The statement should not be invoked if the annotation is invalid", 0, statement.invocationCount); + assertThat(statement.invocationCount) + .as("The statement should not be invoked if the annotation is invalid") + .isEqualTo(0); } private Description newDescriptionWithAnnotation(String reviewDate, String gitHubUrl) { From 9275bc51e20e0e7bf937f48d2850a9865cb41948 Mon Sep 17 00:00:00 2001 From: Matt Hornung Date: Tue, 16 Aug 2022 10:06:59 -0500 Subject: [PATCH 0406/3231] Make JUnit5 TestcontainersExtension public (#5285) This allows more flexible and programmatic usage of the `TestcontainersExtension` Fixes #2045 --- .../testcontainers/junit/jupiter/TestcontainersExtension.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersExtension.java b/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersExtension.java index 47ed56d9ac5..5a11b927ec0 100644 --- a/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersExtension.java +++ b/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersExtension.java @@ -32,7 +32,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -class TestcontainersExtension +public class TestcontainersExtension implements BeforeEachCallback, BeforeAllCallback, AfterEachCallback, AfterAllCallback, ExecutionCondition { private static final Namespace NAMESPACE = Namespace.create(TestcontainersExtension.class); From df5e1eec19a133dd922498b0634fdd712bcdd153 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Tue, 16 Aug 2022 11:02:34 -0500 Subject: [PATCH 0407/3231] Drop usage of `org.junit.platform.commons.util` (#5729) Classes from `org.junit.platform.commons.util` are used for internal use in JUnit Jupiter. This commit introduces replacements. --- .../FilesystemFriendlyNameGenerator.java | 3 +- .../jupiter/TestcontainersExtension.java | 28 +++++++++---------- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/FilesystemFriendlyNameGenerator.java b/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/FilesystemFriendlyNameGenerator.java index a8612fc1516..fe5ddcf32e0 100644 --- a/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/FilesystemFriendlyNameGenerator.java +++ b/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/FilesystemFriendlyNameGenerator.java @@ -1,7 +1,6 @@ package org.testcontainers.junit.jupiter; import org.junit.jupiter.api.extension.ExtensionContext; -import org.junit.platform.commons.util.StringUtils; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; @@ -14,7 +13,7 @@ class FilesystemFriendlyNameGenerator { static String filesystemFriendlyNameOf(ExtensionContext context) { String contextId = context.getUniqueId(); try { - return (StringUtils.isBlank(contextId)) + return (contextId == null || contextId.trim().isEmpty()) ? UNKNOWN_NAME : URLEncoder.encode(contextId, StandardCharsets.UTF_8.toString()); } catch (UnsupportedEncodingException e) { diff --git a/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersExtension.java b/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersExtension.java index 5a11b927ec0..757083dc322 100644 --- a/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersExtension.java +++ b/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersExtension.java @@ -13,9 +13,9 @@ import org.junit.jupiter.api.extension.ExtensionContext.Store; import org.junit.jupiter.api.extension.ExtensionContext.Store.CloseableResource; import org.junit.platform.commons.support.AnnotationSupport; -import org.junit.platform.commons.util.AnnotationUtils; -import org.junit.platform.commons.util.Preconditions; -import org.junit.platform.commons.util.ReflectionUtils; +import org.junit.platform.commons.support.HierarchyTraversalMode; +import org.junit.platform.commons.support.ModifierSupport; +import org.junit.platform.commons.support.ReflectionSupport; import org.testcontainers.DockerClientFactory; import org.testcontainers.lifecycle.Startable; import org.testcontainers.lifecycle.TestDescription; @@ -131,7 +131,7 @@ public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext con private Optional findTestcontainers(ExtensionContext context) { Optional current = Optional.of(context); while (current.isPresent()) { - Optional testcontainers = AnnotationUtils.findAnnotation( + Optional testcontainers = AnnotationSupport.findAnnotation( current.get().getRequiredTestClass(), Testcontainers.class ); @@ -169,26 +169,26 @@ private Set collectParentTestInstances(final ExtensionContext context) { } private List findSharedContainers(Class testClass) { - return ReflectionUtils - .findFields(testClass, isSharedContainer(), ReflectionUtils.HierarchyTraversalMode.TOP_DOWN) + return ReflectionSupport + .findFields(testClass, isSharedContainer(), HierarchyTraversalMode.TOP_DOWN) .stream() .map(f -> getContainerInstance(null, f)) .collect(Collectors.toList()); } private Predicate isSharedContainer() { - return isContainer().and(ReflectionUtils::isStatic); + return isContainer().and(ModifierSupport::isStatic); } private Stream findRestartContainers(Object testInstance) { - return ReflectionUtils - .findFields(testInstance.getClass(), isRestartContainer(), ReflectionUtils.HierarchyTraversalMode.TOP_DOWN) + return ReflectionSupport + .findFields(testInstance.getClass(), isRestartContainer(), HierarchyTraversalMode.TOP_DOWN) .stream() .map(f -> getContainerInstance(testInstance, f)); } private Predicate isRestartContainer() { - return isContainer().and(ReflectionUtils::isNotStatic); + return isContainer().and(ModifierSupport::isNotStatic); } private static Predicate isContainer() { @@ -211,10 +211,10 @@ private static Predicate isContainer() { private static StoreAdapter getContainerInstance(final Object testInstance, final Field field) { try { field.setAccessible(true); - Startable containerInstance = Preconditions.notNull( - (Startable) field.get(testInstance), - "Container " + field.getName() + " needs to be initialized" - ); + Startable containerInstance = (Startable) field.get(testInstance); + if (containerInstance == null) { + throw new ExtensionConfigurationException("Container " + field.getName() + " needs to be initialized"); + } return new StoreAdapter(field.getDeclaringClass(), field.getName(), containerInstance); } catch (IllegalAccessException e) { throw new ExtensionConfigurationException("Can not access container defined in field " + field.getName()); From 18304642dcbbd5db229bc4390d431e9208be88d0 Mon Sep 17 00:00:00 2001 From: Tobias Gesellchen Date: Thu, 18 Aug 2022 15:01:50 +0200 Subject: [PATCH 0408/3231] Enforce UTF-8 as Javadoc encoding (#5738) * Use com.gradle:common-custom-user-data-gradle-plugin:1.8 (instead of 1.8.0) --- build.gradle | 1 + examples/settings.gradle | 2 +- settings.gradle | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 0ded70ad71a..a53bd19298d 100644 --- a/build.gradle +++ b/build.gradle @@ -42,6 +42,7 @@ subprojects { targetCompatibility = 1.8 compileJava.options.encoding = 'UTF-8' compileTestJava.options.encoding = 'UTF-8' + javadoc.options.encoding = 'UTF-8' repositories { mavenCentral() diff --git a/examples/settings.gradle b/examples/settings.gradle index 83ba91e8321..9ed079cd7b8 100644 --- a/examples/settings.gradle +++ b/examples/settings.gradle @@ -7,7 +7,7 @@ buildscript { dependencies { classpath "gradle.plugin.ch.myniva.gradle:s3-build-cache:0.10.0" classpath "com.gradle.enterprise:com.gradle.enterprise.gradle.plugin:3.11.1" - classpath "com.gradle:common-custom-user-data-gradle-plugin:1.8.0" + classpath "com.gradle:common-custom-user-data-gradle-plugin:1.8" } } diff --git a/settings.gradle b/settings.gradle index bd51349bb05..2beb91dea01 100644 --- a/settings.gradle +++ b/settings.gradle @@ -6,7 +6,7 @@ buildscript { } dependencies { classpath "com.gradle.enterprise:com.gradle.enterprise.gradle.plugin:3.11.1" - classpath "com.gradle:common-custom-user-data-gradle-plugin:1.8.0" + classpath "com.gradle:common-custom-user-data-gradle-plugin:1.8" } } From 9fb5db16a6af38257256fd04e452737e0f1ac15a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Thu, 18 Aug 2022 12:09:55 -0500 Subject: [PATCH 0409/3231] Add Pull Request Template (#5735) It provides some useful information about what to take into account when submitting a PR. Co-authored-by: Kevin Wittek --- .github/pull_request_template.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 .github/pull_request_template.md diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 00000000000..643177f08a9 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,19 @@ + From 20fdee2da625aa3c12d1212ea0b370f299901e02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Thu, 18 Aug 2022 18:06:21 -0500 Subject: [PATCH 0410/3231] Add `Transferable.of(String, int)` (#5741) The new method adds similar capability of `Transferable.of(byte[], int)` but for `String`. --- core/build.gradle | 6 ++++++ .../testcontainers/images/builder/Transferable.java | 4 ++++ .../containers/GenericContainerTest.java | 13 +++++++++++++ 3 files changed, 23 insertions(+) diff --git a/core/build.gradle b/core/build.gradle index c210ed48510..aa4c6eabf22 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -113,3 +113,9 @@ tasks.generatePomFileForMavenJavaPublication.finalizedBy( ] } ) + +tasks.japicmp { + methodExcludes = [ + "org.testcontainers.images.builder.Transferable#of(java.lang.String,int)" + ] +} diff --git a/core/src/main/java/org/testcontainers/images/builder/Transferable.java b/core/src/main/java/org/testcontainers/images/builder/Transferable.java index e98dbc2ad11..292a70d6ac7 100644 --- a/core/src/main/java/org/testcontainers/images/builder/Transferable.java +++ b/core/src/main/java/org/testcontainers/images/builder/Transferable.java @@ -16,6 +16,10 @@ static Transferable of(String string) { return of(string.getBytes(StandardCharsets.UTF_8)); } + static Transferable of(String string, int fileMode) { + return of(string.getBytes(StandardCharsets.UTF_8), fileMode); + } + static Transferable of(byte[] bytes) { return of(bytes, DEFAULT_FILE_MODE); } diff --git a/core/src/test/java/org/testcontainers/containers/GenericContainerTest.java b/core/src/test/java/org/testcontainers/containers/GenericContainerTest.java index 1967ad95135..12f5810bc27 100644 --- a/core/src/test/java/org/testcontainers/containers/GenericContainerTest.java +++ b/core/src/test/java/org/testcontainers/containers/GenericContainerTest.java @@ -84,6 +84,19 @@ public void shouldCopyTransferableAsFile() { } } + @Test + public void shouldCopyTransferableAsFileWithFileMode() { + try ( + GenericContainer container = new GenericContainer<>(TestImages.TINY_IMAGE) + .withStartupCheckStrategy(new NoopStartupCheckStrategy()) + .withCopyToContainer(Transferable.of("test", 0777), "/tmp/test") + .waitingFor(new WaitForExitedState(state -> state.getExitCodeLong() > 0)) + .withCommand("sh", "-c", "ls -ll /tmp | grep '\\-rwxrwxrwx\\|test' && exit 100") + ) { + assertThatThrownBy(container::start).hasStackTraceContaining("Container exited with code 100"); + } + } + @Test public void shouldCopyTransferableAfterMountableFile() { try ( From 8b55a3e63ae2a5dfbf4f916d7fc9f6eb008b2f0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Thu, 18 Aug 2022 18:35:59 -0500 Subject: [PATCH 0411/3231] Add Redpanda module (#5740) New module uses `docker.redpanda.com/vectorized/redpanda:v22.2.1` docker image as a base giving the `--mode dev-container` flag added in that version. Co-authored-by: Sergei Egorov --- .github/ISSUE_TEMPLATE/bug_report.yaml | 1 + .github/ISSUE_TEMPLATE/enhancement.yaml | 1 + .github/ISSUE_TEMPLATE/feature.yaml | 1 + .github/dependabot.yml | 5 + .github/labeler.yml | 2 + docs/modules/redpanda.md | 38 +++++ mkdocs.yml | 1 + modules/redpanda/build.gradle | 8 ++ .../redpanda/RedpandaContainer.java | 60 ++++++++ .../redpanda/RedpandaContainerTest.java | 130 ++++++++++++++++++ .../src/test/resources/logback-test.xml | 16 +++ 11 files changed, 263 insertions(+) create mode 100644 docs/modules/redpanda.md create mode 100644 modules/redpanda/build.gradle create mode 100644 modules/redpanda/src/main/java/org/testcontainers/redpanda/RedpandaContainer.java create mode 100644 modules/redpanda/src/test/java/org/testcontainers/redpanda/RedpandaContainerTest.java create mode 100644 modules/redpanda/src/test/resources/logback-test.xml diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index 5aca9e624b3..163c5455b1e 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -42,6 +42,7 @@ body: - Presto - Pulsar - RabbitMQ + - Redpanda - Selenium - Solr - TiDB diff --git a/.github/ISSUE_TEMPLATE/enhancement.yaml b/.github/ISSUE_TEMPLATE/enhancement.yaml index ba5b1f88006..b753332073b 100644 --- a/.github/ISSUE_TEMPLATE/enhancement.yaml +++ b/.github/ISSUE_TEMPLATE/enhancement.yaml @@ -42,6 +42,7 @@ body: - Presto - Pulsar - RabbitMQ + - Redpanda - Selenium - Solr - TiDB diff --git a/.github/ISSUE_TEMPLATE/feature.yaml b/.github/ISSUE_TEMPLATE/feature.yaml index 981f5613a1e..91031adfeee 100644 --- a/.github/ISSUE_TEMPLATE/feature.yaml +++ b/.github/ISSUE_TEMPLATE/feature.yaml @@ -42,6 +42,7 @@ body: - Presto - Pulsar - RabbitMQ + - Redpanda - Selenium - Solr - TiDB diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 22437e15825..4c270ef762c 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -187,6 +187,11 @@ updates: schedule: interval: "monthly" open-pull-requests-limit: 10 + - package-ecosystem: "gradle" + directory: "/modules/redpanda" + schedule: + interval: "monthly" + open-pull-requests-limit: 10 - package-ecosystem: "gradle" directory: "/modules/selenium" schedule: diff --git a/.github/labeler.yml b/.github/labeler.yml index cf75af99d26..492991a9947 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -69,6 +69,8 @@ - modules/r2dbc/* "modules/rabbitmq": - modules/rabbitmq/* +"modules/redpanda": + - modules/redpanda/* "modules/selenium": - modules/selenium/* "modules/solr": diff --git a/docs/modules/redpanda.md b/docs/modules/redpanda.md new file mode 100644 index 00000000000..8749c9c87f5 --- /dev/null +++ b/docs/modules/redpanda.md @@ -0,0 +1,38 @@ +# Redpanda + +Testcontainers can be used to automatically instantiate and manage [Redpanda](https://redpanda.com/) containers. +More precisely Testcontainers uses the official Docker images for [Redpanda](https://hub.docker.com/r/vectorized/redpanda/) + +!!! note + This module uses features provided in `docker.redpanda.com/vectorized/redpanda`. + +## Example + +Create a `Redpanda` to use it in your tests: + +[Creating a Redpanda](../../modules/redpanda/src/test/java/org/testcontainers/redpanda/RedpandaContainerTest.java) inside_block:constructorWithVersion + + +Now your tests or any other process running on your machine can get access to running Redpanda broker by using the following bootstrap server location: + + +[Bootstrap Servers](../../modules/redpanda/src/test/java/org/testcontainers/redpanda/RedpandaContainerTest.java) inside_block:getBootstrapServers + + +## Adding this module to your project dependencies + +Add the following dependency to your `pom.xml`/`build.gradle` file: + +=== "Gradle" +```groovy +testImplementation "org.testcontainers:redpanda:{{latest_version}}" +``` +=== "Maven" +```xml + + org.testcontainers + redpanda + {{latest_version}} + test + +``` diff --git a/mkdocs.yml b/mkdocs.yml index a836e0a2ff7..42011817a5c 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -78,6 +78,7 @@ nav: - modules/nginx.md - modules/pulsar.md - modules/rabbitmq.md + - modules/redpanda.md - modules/solr.md - modules/toxiproxy.md - modules/vault.md diff --git a/modules/redpanda/build.gradle b/modules/redpanda/build.gradle new file mode 100644 index 00000000000..5750e873bf6 --- /dev/null +++ b/modules/redpanda/build.gradle @@ -0,0 +1,8 @@ +description = "Testcontainers :: Redpanda" + +dependencies { + api project(':testcontainers') + + testImplementation 'org.apache.kafka:kafka-clients:3.2.1' + testImplementation 'org.assertj:assertj-core:3.23.1' +} diff --git a/modules/redpanda/src/main/java/org/testcontainers/redpanda/RedpandaContainer.java b/modules/redpanda/src/main/java/org/testcontainers/redpanda/RedpandaContainer.java new file mode 100644 index 00000000000..cbb7db1e7c1 --- /dev/null +++ b/modules/redpanda/src/main/java/org/testcontainers/redpanda/RedpandaContainer.java @@ -0,0 +1,60 @@ +package org.testcontainers.redpanda; + +import com.github.dockerjava.api.command.InspectContainerResponse; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.wait.strategy.Wait; +import org.testcontainers.images.builder.Transferable; +import org.testcontainers.utility.ComparableVersion; +import org.testcontainers.utility.DockerImageName; + +/** + * Testcontainers implementation for Redpanda. + */ +public class RedpandaContainer extends GenericContainer { + + private static final String REDPANDA_FULL_IMAGE_NAME = "docker.redpanda.com/vectorized/redpanda"; + + private static final DockerImageName REDPANDA_IMAGE = DockerImageName.parse(REDPANDA_FULL_IMAGE_NAME); + + private static final int REDPANDA_PORT = 9092; + + private static final String STARTER_SCRIPT = "/testcontainers_start.sh"; + + public RedpandaContainer(String image) { + this(DockerImageName.parse(image)); + } + + public RedpandaContainer(DockerImageName imageName) { + super(imageName); + imageName.assertCompatibleWith(REDPANDA_IMAGE); + + boolean isLessThanBaseVersion = new ComparableVersion(imageName.getVersionPart()).isLessThan("v22.2.1"); + if (REDPANDA_FULL_IMAGE_NAME.equals(imageName.getUnversionedPart()) && isLessThanBaseVersion) { + throw new IllegalArgumentException("Redpanda version must be >= v22.2.1"); + } + + withExposedPorts(REDPANDA_PORT); + withCreateContainerCmdModifier(cmd -> { + cmd.withEntrypoint("sh"); + }); + waitingFor(Wait.forLogMessage(".*Started Kafka API server.*", 1)); + withCommand("-c", "while [ ! -f " + STARTER_SCRIPT + " ]; do sleep 0.1; done; " + STARTER_SCRIPT); + } + + @Override + protected void containerIsStarting(InspectContainerResponse containerInfo) { + super.containerIsStarting(containerInfo); + + String command = "#!/bin/bash\n"; + + command += "/usr/bin/rpk redpanda start --mode dev-container "; + command += "--kafka-addr PLAINTEXT://0.0.0.0:29092,OUTSIDE://0.0.0.0:9092 "; + command += "--advertise-kafka-addr PLAINTEXT://kafka:29092,OUTSIDE://" + getHost() + ":" + getMappedPort(9092); + + copyFileToContainer(Transferable.of(command, 0777), STARTER_SCRIPT); + } + + public String getBootstrapServers() { + return String.format("PLAINTEXT://%s:%s", getHost(), getMappedPort(REDPANDA_PORT)); + } +} diff --git a/modules/redpanda/src/test/java/org/testcontainers/redpanda/RedpandaContainerTest.java b/modules/redpanda/src/test/java/org/testcontainers/redpanda/RedpandaContainerTest.java new file mode 100644 index 00000000000..086bf43364b --- /dev/null +++ b/modules/redpanda/src/test/java/org/testcontainers/redpanda/RedpandaContainerTest.java @@ -0,0 +1,130 @@ +package org.testcontainers.redpanda; + +import com.google.common.collect.ImmutableMap; +import org.apache.kafka.clients.admin.AdminClient; +import org.apache.kafka.clients.admin.AdminClientConfig; +import org.apache.kafka.clients.admin.NewTopic; +import org.apache.kafka.clients.consumer.ConsumerConfig; +import org.apache.kafka.clients.consumer.ConsumerRecord; +import org.apache.kafka.clients.consumer.ConsumerRecords; +import org.apache.kafka.clients.consumer.KafkaConsumer; +import org.apache.kafka.clients.producer.KafkaProducer; +import org.apache.kafka.clients.producer.ProducerConfig; +import org.apache.kafka.clients.producer.ProducerRecord; +import org.apache.kafka.common.serialization.StringDeserializer; +import org.apache.kafka.common.serialization.StringSerializer; +import org.junit.Test; +import org.rnorth.ducttape.unreliables.Unreliables; +import org.testcontainers.utility.DockerImageName; + +import java.time.Duration; +import java.util.Collection; +import java.util.Collections; +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.assertj.core.api.Assertions.tuple; + +public class RedpandaContainerTest { + + private static final String REDPANDA_IMAGE = "docker.redpanda.com/vectorized/redpanda:v22.2.1"; + + private static final DockerImageName REDPANDA_DOCKER_IMAGE = DockerImageName.parse(REDPANDA_IMAGE); + + @Test + public void testUsage() throws Exception { + try (RedpandaContainer container = new RedpandaContainer(REDPANDA_DOCKER_IMAGE)) { + container.start(); + testKafkaFunctionality(container.getBootstrapServers()); + } + } + + @Test + public void testUsageWithStringImage() throws Exception { + try ( + // constructorWithVersion { + RedpandaContainer container = new RedpandaContainer("docker.redpanda.com/vectorized/redpanda:v22.2.1") + // } + ) { + container.start(); + testKafkaFunctionality( + // getBootstrapServers { + container.getBootstrapServers() + // } + ); + } + } + + @Test + public void testNotCompatibleVersion() { + assertThatThrownBy(() -> new RedpandaContainer("docker.redpanda.com/vectorized/redpanda:v21.11.19")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("Redpanda version must be >= v22.2.1"); + } + + private void testKafkaFunctionality(String bootstrapServers) throws Exception { + testKafkaFunctionality(bootstrapServers, 1, 1); + } + + private void testKafkaFunctionality(String bootstrapServers, int partitions, int rf) throws Exception { + try ( + AdminClient adminClient = AdminClient.create( + ImmutableMap.of(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers) + ); + KafkaProducer producer = new KafkaProducer<>( + ImmutableMap.of( + ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, + bootstrapServers, + ProducerConfig.CLIENT_ID_CONFIG, + UUID.randomUUID().toString() + ), + new StringSerializer(), + new StringSerializer() + ); + KafkaConsumer consumer = new KafkaConsumer<>( + ImmutableMap.of( + ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, + bootstrapServers, + ConsumerConfig.GROUP_ID_CONFIG, + "tc-" + UUID.randomUUID(), + ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, + "earliest" + ), + new StringDeserializer(), + new StringDeserializer() + ); + ) { + String topicName = "messages-" + UUID.randomUUID(); + + Collection topics = Collections.singletonList(new NewTopic(topicName, partitions, (short) rf)); + adminClient.createTopics(topics).all().get(30, TimeUnit.SECONDS); + + consumer.subscribe(Collections.singletonList(topicName)); + + producer.send(new ProducerRecord<>(topicName, "testcontainers", "rulezzz")).get(); + + Unreliables.retryUntilTrue( + 10, + TimeUnit.SECONDS, + () -> { + ConsumerRecords records = consumer.poll(Duration.ofMillis(100)); + + if (records.isEmpty()) { + return false; + } + + assertThat(records) + .hasSize(1) + .extracting(ConsumerRecord::topic, ConsumerRecord::key, ConsumerRecord::value) + .containsExactly(tuple(topicName, "testcontainers", "rulezzz")); + + return true; + } + ); + + consumer.unsubscribe(); + } + } +} diff --git a/modules/redpanda/src/test/resources/logback-test.xml b/modules/redpanda/src/test/resources/logback-test.xml new file mode 100644 index 00000000000..83ef7a1a3ef --- /dev/null +++ b/modules/redpanda/src/test/resources/logback-test.xml @@ -0,0 +1,16 @@ + + + + + + %d{HH:mm:ss.SSS} %-5level %logger - %msg%n + + + + + + + + + From 5f486d9c5e3e6a0c8ca7a270bd1c033cf0292c23 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 18 Aug 2022 23:36:27 +0000 Subject: [PATCH 0412/3231] Bump postgresql from 42.4.1 to 42.4.2 in /modules/cockroachdb Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.4.1 to 42.4.2. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.4.1...REL42.4.2) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/cockroachdb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/cockroachdb/build.gradle b/modules/cockroachdb/build.gradle index 1fc5dac8d8a..fbd2415e53c 100644 --- a/modules/cockroachdb/build.gradle +++ b/modules/cockroachdb/build.gradle @@ -4,6 +4,6 @@ dependencies { api project(':jdbc') testImplementation project(':jdbc-test') - testImplementation 'org.postgresql:postgresql:42.4.1' + testImplementation 'org.postgresql:postgresql:42.4.2' testImplementation 'org.assertj:assertj-core:3.23.1' } From e8f7c19278c3d12c84097c868a64bb2e969ca298 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 18 Aug 2022 23:36:29 +0000 Subject: [PATCH 0413/3231] Bump postgresql from 42.4.1 to 42.4.2 in /modules/junit-jupiter Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.4.1 to 42.4.2. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.4.1...REL42.4.2) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 08afd67839d..dd1d0c7ed6e 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -15,7 +15,7 @@ dependencies { testImplementation 'org.assertj:assertj-core:3.23.1' testImplementation 'org.junit.jupiter:junit-jupiter-params:5.9.0' - testRuntimeOnly 'org.postgresql:postgresql:42.4.1' + testRuntimeOnly 'org.postgresql:postgresql:42.4.2' testRuntimeOnly 'mysql:mysql-connector-java:8.0.30' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.9.0' } From ba585921f607a12b770bacbda3d6bc1be7fa0f44 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 18 Aug 2022 23:36:29 +0000 Subject: [PATCH 0414/3231] Bump google-cloud-pubsub from 1.120.11 to 1.120.12 in /modules/gcloud Bumps [google-cloud-pubsub](https://github.com/googleapis/java-pubsub) from 1.120.11 to 1.120.12. - [Release notes](https://github.com/googleapis/java-pubsub/releases) - [Changelog](https://github.com/googleapis/java-pubsub/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/java-pubsub/compare/v1.120.11...v1.120.12) --- updated-dependencies: - dependency-name: com.google.cloud:google-cloud-pubsub dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/gcloud/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle index e7bc4065d82..bb79bfbbcc2 100644 --- a/modules/gcloud/build.gradle +++ b/modules/gcloud/build.gradle @@ -5,7 +5,7 @@ dependencies { testImplementation 'com.google.cloud:google-cloud-datastore:2.11.0' testImplementation 'com.google.cloud:google-cloud-firestore:3.4.0' - testImplementation 'com.google.cloud:google-cloud-pubsub:1.120.11' + testImplementation 'com.google.cloud:google-cloud-pubsub:1.120.12' testImplementation 'com.google.cloud:google-cloud-spanner:6.28.0' testImplementation 'com.google.cloud:google-cloud-bigtable:2.10.3' testImplementation 'org.assertj:assertj-core:3.23.1' From 87b0a80d69b674d727b1cc216e13e3b0819e07aa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 18 Aug 2022 23:36:30 +0000 Subject: [PATCH 0415/3231] Bump aws-java-sdk-dynamodb in /modules/dynalite Bumps [aws-java-sdk-dynamodb](https://github.com/aws/aws-sdk-java) from 1.12.281 to 1.12.285. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.281...1.12.285) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-dynamodb dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/dynalite/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/dynalite/build.gradle b/modules/dynalite/build.gradle index 81ec1f9deb5..8b548d5b91f 100644 --- a/modules/dynalite/build.gradle +++ b/modules/dynalite/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Dynalite" dependencies { api project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.12.281' + compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.12.285' testImplementation 'com.amazonaws:aws-java-sdk-dynamodb:1.12.281' testImplementation 'org.assertj:assertj-core:3.23.1' } From 6c7b68c88dcb01a67f56dc9c7d701deca3b1b718 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 18 Aug 2022 23:36:31 +0000 Subject: [PATCH 0416/3231] Bump trino-jdbc from 392 to 393 in /modules/trino Bumps trino-jdbc from 392 to 393. --- updated-dependencies: - dependency-name: io.trino:trino-jdbc dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- modules/trino/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/trino/build.gradle b/modules/trino/build.gradle index 09b5b8cb16c..e5b7efee873 100644 --- a/modules/trino/build.gradle +++ b/modules/trino/build.gradle @@ -4,6 +4,6 @@ dependencies { api project(':jdbc') testImplementation project(':jdbc-test') - testImplementation 'io.trino:trino-jdbc:392' + testImplementation 'io.trino:trino-jdbc:393' compileOnly 'org.jetbrains:annotations:23.0.0' } From 42ddd6b86bcfd1e5f54b594b22f8b13e2ac86c24 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 18 Aug 2022 23:36:32 +0000 Subject: [PATCH 0417/3231] Bump postgresql from 42.4.1 to 42.4.2 in /modules/postgresql Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.4.1 to 42.4.2. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.4.1...REL42.4.2) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/postgresql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/postgresql/build.gradle b/modules/postgresql/build.gradle index f769bf9b07c..0c5035df549 100644 --- a/modules/postgresql/build.gradle +++ b/modules/postgresql/build.gradle @@ -11,7 +11,7 @@ dependencies { testImplementation project(':jdbc-test') testImplementation project(':test-support') - testImplementation 'org.postgresql:postgresql:42.4.1' + testImplementation 'org.postgresql:postgresql:42.4.2' testImplementation testFixtures(project(':r2dbc')) testImplementation 'io.r2dbc:r2dbc-postgresql:0.8.11.RELEASE' From c4462e6d05390988b871b9bce277b9e14733c179 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 18 Aug 2022 23:36:37 +0000 Subject: [PATCH 0418/3231] Bump s3 from 2.17.253 to 2.17.256 in /modules/localstack Bumps s3 from 2.17.253 to 2.17.256. --- updated-dependencies: - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 76090d2f6dd..23f4d045cba 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -7,6 +7,6 @@ dependencies { testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.273' testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.282' testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.281' - testImplementation 'software.amazon.awssdk:s3:2.17.253' + testImplementation 'software.amazon.awssdk:s3:2.17.256' testImplementation 'org.assertj:assertj-core:3.23.1' } From 23d93d52d6414a02772e3768aa6bd70671e333f4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 18 Aug 2022 23:36:40 +0000 Subject: [PATCH 0419/3231] Bump release-drafter/release-drafter from 5.20.0 to 5.20.1 Bumps [release-drafter/release-drafter](https://github.com/release-drafter/release-drafter) from 5.20.0 to 5.20.1. - [Release notes](https://github.com/release-drafter/release-drafter/releases) - [Commits](https://github.com/release-drafter/release-drafter/compare/ac463ffd9cc4c6ad5682af93dc3e3591c4657ee3...06a49bf28488e030d35ca2ac6dbf7f408a481779) --- updated-dependencies: - dependency-name: release-drafter/release-drafter dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/release-drafter.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml index fb0780d781f..94af563bcda 100644 --- a/.github/workflows/release-drafter.yml +++ b/.github/workflows/release-drafter.yml @@ -18,6 +18,6 @@ jobs: if: github.repository == 'testcontainers/testcontainers-java' runs-on: ubuntu-latest steps: - - uses: release-drafter/release-drafter@ac463ffd9cc4c6ad5682af93dc3e3591c4657ee3 # v5.19.0 + - uses: release-drafter/release-drafter@06a49bf28488e030d35ca2ac6dbf7f408a481779 # v5.19.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From aa1512123425260157c313ccd4e8011a436dbc50 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 18 Aug 2022 23:36:41 +0000 Subject: [PATCH 0420/3231] Bump aws-java-sdk-s3 from 1.12.281 to 1.12.285 in /modules/localstack Bumps [aws-java-sdk-s3](https://github.com/aws/aws-sdk-java) from 1.12.281 to 1.12.285. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.281...1.12.285) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-s3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 76090d2f6dd..65128d624bc 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -3,8 +3,8 @@ description = "Testcontainers :: Localstack" dependencies { api project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.281' - testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.273' + compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.285' + testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.285' testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.282' testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.281' testImplementation 'software.amazon.awssdk:s3:2.17.253' From d100185f041aae612e19c11df7223d2f214f56ef Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 18 Aug 2022 23:36:43 +0000 Subject: [PATCH 0421/3231] Bump peter-evans/create-pull-request from 4.0.4 to 4.1.1 Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 4.0.4 to 4.1.1. - [Release notes](https://github.com/peter-evans/create-pull-request/releases) - [Commits](https://github.com/peter-evans/create-pull-request/compare/923ad837f191474af6b1721408744feb989a4c27...18f90432bedd2afd6a825469ffd38aa24712a91d) --- updated-dependencies: - dependency-name: peter-evans/create-pull-request dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/update-docs-version.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/update-docs-version.yml b/.github/workflows/update-docs-version.yml index b2736c42590..070fa2c55d4 100644 --- a/.github/workflows/update-docs-version.yml +++ b/.github/workflows/update-docs-version.yml @@ -23,7 +23,7 @@ jobs: sed -i "s/latest_version: .*/latest_version: ${GITHUB_REF##*/}/g" mkdocs.yml git diff - name: Create Pull Request - uses: peter-evans/create-pull-request@923ad837f191474af6b1721408744feb989a4c27 # v3.10.1 + uses: peter-evans/create-pull-request@18f90432bedd2afd6a825469ffd38aa24712a91d # v3.10.1 with: title: Update docs version to ${GITHUB_REF##*/} body: | From 1850fa7ca123cc7ee5e375d8867b2d80881b2b66 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 18 Aug 2022 23:36:47 +0000 Subject: [PATCH 0422/3231] Bump postgresql from 42.4.1 to 42.4.2 in /examples Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.4.1 to 42.4.2. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.4.1...REL42.4.2) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/linked-container/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/linked-container/build.gradle b/examples/linked-container/build.gradle index 1865f70f801..5b690597eaf 100644 --- a/examples/linked-container/build.gradle +++ b/examples/linked-container/build.gradle @@ -9,7 +9,7 @@ dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.36' implementation 'com.squareup.okhttp3:okhttp:4.10.0' implementation 'org.json:json:20220320' - testImplementation 'org.postgresql:postgresql:42.4.1' + testImplementation 'org.postgresql:postgresql:42.4.2' testImplementation 'ch.qos.logback:logback-classic:1.2.11' testImplementation 'org.testcontainers:postgresql' testImplementation 'org.assertj:assertj-core:3.23.1' From b82ae5c02cddb455d13c7525785799295b767b4c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 19 Aug 2022 02:15:39 +0000 Subject: [PATCH 0423/3231] Bump aws-java-sdk-logs from 1.12.281 to 1.12.285 in /modules/localstack Bumps [aws-java-sdk-logs](https://github.com/aws/aws-sdk-java) from 1.12.281 to 1.12.285. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.281...1.12.285) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-logs dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 6c19c73b3be..ed6f57fb6d2 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -6,7 +6,7 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.285' testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.285' testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.282' - testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.281' + testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.285' testImplementation 'software.amazon.awssdk:s3:2.17.256' testImplementation 'org.assertj:assertj-core:3.23.1' } From 3bb9afa1d42e968297c285ca46f80b49f0142a9f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 19 Aug 2022 02:15:44 +0000 Subject: [PATCH 0424/3231] Bump postgresql from 42.4.1 to 42.4.2 in /modules/spock Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.4.1 to 42.4.2. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.4.1...REL42.4.2) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/spock/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle index 304e0b98dc4..543824ea8f6 100644 --- a/modules/spock/build.gradle +++ b/modules/spock/build.gradle @@ -15,7 +15,7 @@ dependencies { testImplementation 'com.zaxxer:HikariCP:4.0.3' testImplementation 'org.apache.httpcomponents:httpclient:4.5.13' - testRuntimeOnly 'org.postgresql:postgresql:42.4.1' + testRuntimeOnly 'org.postgresql:postgresql:42.4.2' testRuntimeOnly 'mysql:mysql-connector-java:8.0.30' testCompileClasspath 'org.jetbrains:annotations:23.0.0' From 917f451cd04f313f923682fb19ddcc7255d403ff Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 19 Aug 2022 14:48:41 +0000 Subject: [PATCH 0425/3231] Bump aws-java-sdk-sqs from 1.12.282 to 1.12.285 in /modules/localstack Bumps [aws-java-sdk-sqs](https://github.com/aws/aws-sdk-java) from 1.12.282 to 1.12.285. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.282...1.12.285) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-sqs dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index ed6f57fb6d2..ad43f6f9c08 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -5,7 +5,7 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.285' testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.285' - testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.282' + testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.285' testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.285' testImplementation 'software.amazon.awssdk:s3:2.17.256' testImplementation 'org.assertj:assertj-core:3.23.1' From dcf416cdc31c59131819f73e92e382e0b5c368af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Fri, 19 Aug 2022 11:35:54 -0500 Subject: [PATCH 0426/3231] Fix type/docs label (#5759) Only consider files with `.md` extension. --- .github/labeler.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/labeler.yml b/.github/labeler.yml index 492991a9947..85144b7d7f9 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -85,4 +85,5 @@ - modules/trino/* "modules/vault": - modules/vault/* -"type/docs": docs/**/* +"type/docs": + - docs/**/*.md From 017d937dc7d2d847f2f412ba37c3db226c4623d5 Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Mon, 22 Aug 2022 16:48:59 +0200 Subject: [PATCH 0427/3231] Update GHA runner image to latest Ubuntu (#5761) `ubuntu-latest` means `ubuntu-20.04` at the time of the commit. It should always be a LTS version. --- .github/workflows/ci-docker-wormhole.yml | 2 +- .github/workflows/ci-examples.yml | 4 ++-- .github/workflows/ci-rootless.yml | 2 +- .github/workflows/ci.yml | 4 ++-- .github/workflows/release.yml | 2 +- .github/workflows/update-docs-version.yml | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci-docker-wormhole.yml b/.github/workflows/ci-docker-wormhole.yml index e27e4d8d611..ace645e1bb1 100644 --- a/.github/workflows/ci-docker-wormhole.yml +++ b/.github/workflows/ci-docker-wormhole.yml @@ -9,7 +9,7 @@ permissions: jobs: in-docker_test: - runs-on: ubuntu-18.04 + runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Build with Gradle diff --git a/.github/workflows/ci-examples.yml b/.github/workflows/ci-examples.yml index f23fc66e889..70917663e25 100644 --- a/.github/workflows/ci-examples.yml +++ b/.github/workflows/ci-examples.yml @@ -15,7 +15,7 @@ permissions: jobs: find_gradle_jobs: - runs-on: ubuntu-18.04 + runs-on: ubuntu-latest outputs: matrix: ${{ steps.set-matrix.outputs.matrix }} steps: @@ -45,7 +45,7 @@ jobs: strategy: fail-fast: false matrix: ${{ fromJson(needs.find_gradle_jobs.outputs.matrix) }} - runs-on: ubuntu-18.04 + runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: actions/setup-java@v3 diff --git a/.github/workflows/ci-rootless.yml b/.github/workflows/ci-rootless.yml index 348cf5aaaf7..334fe69e9c4 100644 --- a/.github/workflows/ci-rootless.yml +++ b/.github/workflows/ci-rootless.yml @@ -9,7 +9,7 @@ permissions: jobs: test: - runs-on: ubuntu-18.04 + runs-on: ubuntu-latest strategy: fail-fast: false matrix: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 288e9329824..ee1b64b6429 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,7 +23,7 @@ jobs: - id: thundra_test_initializer uses: thundra-io/thundra-test-init-action@v1 find_gradle_jobs: - runs-on: ubuntu-18.04 + runs-on: ubuntu-latest outputs: matrix: ${{ steps.set-matrix.outputs.matrix }} steps: @@ -51,7 +51,7 @@ jobs: strategy: fail-fast: false matrix: ${{ fromJson(needs.find_gradle_jobs.outputs.matrix) }} - runs-on: ubuntu-18.04 + runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: actions/setup-java@v3 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 29a0b920922..fedef1c4a52 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -13,7 +13,7 @@ permissions: jobs: release: - runs-on: ubuntu-18.04 + runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: actions/setup-java@v3 diff --git a/.github/workflows/update-docs-version.yml b/.github/workflows/update-docs-version.yml index 070fa2c55d4..443ba11788f 100644 --- a/.github/workflows/update-docs-version.yml +++ b/.github/workflows/update-docs-version.yml @@ -13,7 +13,7 @@ jobs: contents: write # for peter-evans/create-pull-request to create branch pull-requests: write # for peter-evans/create-pull-request to create a PR if: github.repository == 'testcontainers/testcontainers-java' - runs-on: ubuntu-18.04 + runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 with: From d568e9d2d865f9ec530839d8957941a764825118 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Tue, 23 Aug 2022 13:18:24 -0500 Subject: [PATCH 0428/3231] Add samples section (#5763) List examples provided in testcontainers by providing an specific section in the docs. Co-authored-by: Kevin Wittek --- docs/examples.md | 16 ++++++++++++++++ mkdocs.yml | 1 + 2 files changed, 17 insertions(+) create mode 100644 docs/examples.md diff --git a/docs/examples.md b/docs/examples.md new file mode 100644 index 00000000000..abd5e410c21 --- /dev/null +++ b/docs/examples.md @@ -0,0 +1,16 @@ +# Examples + +Examples of different use cases provided by Testcontainers can be found below: + +- [Kafka Cluster with multiple brokers](https://github.com/testcontainers/testcontainers-java/tree/master/examples/kafka-cluster) +- [Linked containers](https://github.com/testcontainers/testcontainers-java/tree/master/examples/linked-container) +- [MongoDB](https://github.com/testcontainers/testcontainers-java/tree/master/examples/mongodb-container) +- [Neo4j](https://github.com/testcontainers/testcontainers-java/tree/master/examples/neo4j-container) +- [Redis](https://github.com/testcontainers/testcontainers-java/tree/master/examples/redis-backed-cache) +- [Selenium](https://github.com/testcontainers/testcontainers-java/tree/master/examples/cucumber) +- [Selenium Module with Cucumber](https://github.com/testcontainers/testcontainers-java/tree/master/examples/cucumber) +- [Singleton Container Pattern](https://github.com/testcontainers/testcontainers-java/tree/master/examples/singleton-container) +- [Solr](https://github.com/testcontainers/testcontainers-java/tree/master/examples/solr-container) +- [Spring Boot](https://github.com/testcontainers/testcontainers-java/tree/master/examples/spring-boot) +- [Spring Boot with Kotlin](https://github.com/testcontainers/testcontainers-java/tree/master/examples/spring-boot-kotlin-redis) +- [TestNG](https://github.com/testcontainers/testcontainers-java/tree/master/examples/redis-backed-cache-testng) diff --git a/mkdocs.yml b/mkdocs.yml index 42011817a5c..87b19de4f0a 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -88,6 +88,7 @@ nav: - test_framework_integration/junit_5.md - test_framework_integration/spock.md - test_framework_integration/manual_lifecycle_control.md + - Examples: examples.md - System Requirements: - supported_docker_environment/index.md - Continuous Integration: From 7044cde703809ff1d2330923cd30ceecf6845f47 Mon Sep 17 00:00:00 2001 From: Michael Nitschinger Date: Tue, 23 Aug 2022 23:02:11 +0200 Subject: [PATCH 0429/3231] [couchbase] only expose ports for enabled services (#4595) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This changeset is a minor cleanup, but it makes sense to only actually expose the ports for the services which are enabled and not all of them all the time. Co-authored-by: Eddú Meléndez --- .../couchbase/CouchbaseContainer.java | 45 ++++++++++++------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java index 4f190a5413c..8603a0b0787 100644 --- a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java +++ b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java @@ -241,22 +241,7 @@ public String getConnectionString() { protected void configure() { super.configure(); - addExposedPorts( - MGMT_PORT, - MGMT_SSL_PORT, - VIEW_PORT, - VIEW_SSL_PORT, - QUERY_PORT, - QUERY_SSL_PORT, - SEARCH_PORT, - SEARCH_SSL_PORT, - ANALYTICS_PORT, - ANALYTICS_SSL_PORT, - KV_PORT, - KV_SSL_PORT, - EVENTING_PORT, - EVENTING_SSL_PORT - ); + exposePorts(); WaitAllStrategy waitStrategy = new WaitAllStrategy(); @@ -319,6 +304,34 @@ protected void configure() { waitingFor(waitStrategy); } + /** + * Configures the exposed ports based on the enabled services. + *

+ * Note that the MGMT_PORTs are always enabled since there must always be a cluster + * manager. Also, the View engine ports are implicitly available on the same nodes + * where the KV service is enabled - it is not possible to configure them individually. + */ + private void exposePorts() { + addExposedPorts(MGMT_PORT, MGMT_SSL_PORT); + + if (enabledServices.contains(CouchbaseService.KV)) { + addExposedPorts(KV_PORT, KV_SSL_PORT); + addExposedPorts(VIEW_PORT, VIEW_SSL_PORT); + } + if (enabledServices.contains(CouchbaseService.ANALYTICS)) { + addExposedPorts(ANALYTICS_PORT, ANALYTICS_SSL_PORT); + } + if (enabledServices.contains(CouchbaseService.QUERY)) { + addExposedPorts(QUERY_PORT, QUERY_SSL_PORT); + } + if (enabledServices.contains(CouchbaseService.SEARCH)) { + addExposedPorts(SEARCH_PORT, SEARCH_SSL_PORT); + } + if (enabledServices.contains(CouchbaseService.EVENTING)) { + addExposedPorts(EVENTING_PORT, EVENTING_SSL_PORT); + } + } + @Override protected void containerIsStarting(final InspectContainerResponse containerInfo) { logger().debug("Couchbase container is starting, performing configuration."); From 62fc6a9cd3a1ab9af19d59612977968e50e9192e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Wed, 24 Aug 2022 04:20:13 -0500 Subject: [PATCH 0430/3231] Upgrade spring-boot version to 2.7.3 in examples (#5764) `driver.findElementsByTagName()` is not available anymore in 2.7.3 --- examples/selenium-container/build.gradle | 2 +- .../src/test/java/SeleniumContainerTest.java | 5 +++-- examples/spring-boot-kotlin-redis/build.gradle | 4 ++-- examples/spring-boot/build.gradle | 2 +- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/examples/selenium-container/build.gradle b/examples/selenium-container/build.gradle index dc10798a060..6183b89e1d6 100644 --- a/examples/selenium-container/build.gradle +++ b/examples/selenium-container/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id 'org.springframework.boot' version '2.6.4' + id 'org.springframework.boot' version '2.7.3' } apply plugin: 'io.spring.dependency-management' diff --git a/examples/selenium-container/src/test/java/SeleniumContainerTest.java b/examples/selenium-container/src/test/java/SeleniumContainerTest.java index 32411b98ed9..c28c9bc54cb 100644 --- a/examples/selenium-container/src/test/java/SeleniumContainerTest.java +++ b/examples/selenium-container/src/test/java/SeleniumContainerTest.java @@ -2,12 +2,13 @@ import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import org.openqa.selenium.By; import org.openqa.selenium.WebElement; import org.openqa.selenium.chrome.ChromeOptions; import org.openqa.selenium.remote.RemoteWebDriver; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.web.context.WebServerInitializedEvent; -import org.springframework.boot.web.server.LocalServerPort; +import org.springframework.boot.test.web.server.LocalServerPort; import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.ApplicationListener; import org.springframework.context.ConfigurableApplicationContext; @@ -43,7 +44,7 @@ public void simplePlainSeleniumTest() { RemoteWebDriver driver = chrome.getWebDriver(); driver.get("http://host.testcontainers.internal:" + port + "/foo.html"); - List hElement = driver.findElementsByTagName("h"); + List hElement = driver.findElements(By.tagName("h")); assertThat(hElement).as("The h element is found").isNotEmpty(); } diff --git a/examples/spring-boot-kotlin-redis/build.gradle b/examples/spring-boot-kotlin-redis/build.gradle index 383491eb32d..180d6780c8f 100644 --- a/examples/spring-boot-kotlin-redis/build.gradle +++ b/examples/spring-boot-kotlin-redis/build.gradle @@ -1,5 +1,5 @@ plugins { - id("org.springframework.boot") version "2.6.4" + id("org.springframework.boot") version "2.7.3" id("org.jetbrains.kotlin.jvm") version "1.7.10" id("org.jetbrains.kotlin.plugin.spring") version "1.7.10" } @@ -25,4 +25,4 @@ compileKotlin { jvmTarget = "1.8" freeCompilerArgs = ["-Xjsr305=strict"] } -} \ No newline at end of file +} diff --git a/examples/spring-boot/build.gradle b/examples/spring-boot/build.gradle index 650b5a60d98..52a4a63386a 100644 --- a/examples/spring-boot/build.gradle +++ b/examples/spring-boot/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id 'org.springframework.boot' version '2.6.4' + id 'org.springframework.boot' version '2.7.3' } apply plugin: 'io.spring.dependency-management' From d5ff8c362b36a7a3e1b9ee22b0b1cecce9675f43 Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Wed, 24 Aug 2022 11:55:45 +0200 Subject: [PATCH 0431/3231] Revert "Update GHA runner image to latest Ubuntu (#5761)" (#5766) This reverts commit 017d937dc7d2d847f2f412ba37c3db226c4623d5. Necessary to fix CI for the Azure module. --- .github/workflows/ci-docker-wormhole.yml | 2 +- .github/workflows/ci-examples.yml | 4 ++-- .github/workflows/ci-rootless.yml | 2 +- .github/workflows/ci.yml | 4 ++-- .github/workflows/release.yml | 2 +- .github/workflows/update-docs-version.yml | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci-docker-wormhole.yml b/.github/workflows/ci-docker-wormhole.yml index ace645e1bb1..e27e4d8d611 100644 --- a/.github/workflows/ci-docker-wormhole.yml +++ b/.github/workflows/ci-docker-wormhole.yml @@ -9,7 +9,7 @@ permissions: jobs: in-docker_test: - runs-on: ubuntu-latest + runs-on: ubuntu-18.04 steps: - uses: actions/checkout@v3 - name: Build with Gradle diff --git a/.github/workflows/ci-examples.yml b/.github/workflows/ci-examples.yml index 70917663e25..f23fc66e889 100644 --- a/.github/workflows/ci-examples.yml +++ b/.github/workflows/ci-examples.yml @@ -15,7 +15,7 @@ permissions: jobs: find_gradle_jobs: - runs-on: ubuntu-latest + runs-on: ubuntu-18.04 outputs: matrix: ${{ steps.set-matrix.outputs.matrix }} steps: @@ -45,7 +45,7 @@ jobs: strategy: fail-fast: false matrix: ${{ fromJson(needs.find_gradle_jobs.outputs.matrix) }} - runs-on: ubuntu-latest + runs-on: ubuntu-18.04 steps: - uses: actions/checkout@v3 - uses: actions/setup-java@v3 diff --git a/.github/workflows/ci-rootless.yml b/.github/workflows/ci-rootless.yml index 334fe69e9c4..348cf5aaaf7 100644 --- a/.github/workflows/ci-rootless.yml +++ b/.github/workflows/ci-rootless.yml @@ -9,7 +9,7 @@ permissions: jobs: test: - runs-on: ubuntu-latest + runs-on: ubuntu-18.04 strategy: fail-fast: false matrix: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ee1b64b6429..288e9329824 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,7 +23,7 @@ jobs: - id: thundra_test_initializer uses: thundra-io/thundra-test-init-action@v1 find_gradle_jobs: - runs-on: ubuntu-latest + runs-on: ubuntu-18.04 outputs: matrix: ${{ steps.set-matrix.outputs.matrix }} steps: @@ -51,7 +51,7 @@ jobs: strategy: fail-fast: false matrix: ${{ fromJson(needs.find_gradle_jobs.outputs.matrix) }} - runs-on: ubuntu-latest + runs-on: ubuntu-18.04 steps: - uses: actions/checkout@v3 - uses: actions/setup-java@v3 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index fedef1c4a52..29a0b920922 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -13,7 +13,7 @@ permissions: jobs: release: - runs-on: ubuntu-latest + runs-on: ubuntu-18.04 steps: - uses: actions/checkout@v3 - uses: actions/setup-java@v3 diff --git a/.github/workflows/update-docs-version.yml b/.github/workflows/update-docs-version.yml index 443ba11788f..070fa2c55d4 100644 --- a/.github/workflows/update-docs-version.yml +++ b/.github/workflows/update-docs-version.yml @@ -13,7 +13,7 @@ jobs: contents: write # for peter-evans/create-pull-request to create branch pull-requests: write # for peter-evans/create-pull-request to create a PR if: github.repository == 'testcontainers/testcontainers-java' - runs-on: ubuntu-latest + runs-on: ubuntu-18.04 steps: - uses: actions/checkout@v3 with: From f6399608f53aece84a813386ee13d1c4360e813b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Thu, 25 Aug 2022 01:55:37 -0500 Subject: [PATCH 0432/3231] Add link to GH Discussions to Issue forms (#5767) --- .github/ISSUE_TEMPLATE/config.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index d27b0d086e9..418d5a62fe9 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -3,3 +3,6 @@ contact_links: - name: Need help or have a question? url: https://slack.testcontainers.org/ about: Visit our slack channel. + - name: Have a question or want to drive a Community conversation? + url: https://github.com/testcontainers/testcontainers-java/discussions/ + about: Visit our Discussions page. From 1b961d8518339ddcfbb44081c59466260a5aec42 Mon Sep 17 00:00:00 2001 From: Aidan Do <43259657+REslim30@users.noreply.github.com> Date: Fri, 26 Aug 2022 01:43:26 +0930 Subject: [PATCH 0433/3231] Set default elasticsearch heap size to 2GB (#5684) Co-authored-by: Kevin Wittek --- .../elasticsearch/ElasticsearchContainer.java | 8 +++ .../elasticsearch-default-memory-vm.options | 2 + .../ElasticsearchContainerTest.java | 50 +++++++++++++++++++ .../resources/test-custom-memory-jvm.options | 2 + 4 files changed, 62 insertions(+) create mode 100644 modules/elasticsearch/src/main/resources/elasticsearch-default-memory-vm.options create mode 100644 modules/elasticsearch/src/test/resources/test-custom-memory-jvm.options diff --git a/modules/elasticsearch/src/main/java/org/testcontainers/elasticsearch/ElasticsearchContainer.java b/modules/elasticsearch/src/main/java/org/testcontainers/elasticsearch/ElasticsearchContainer.java index f8b9f779519..edef04e001e 100644 --- a/modules/elasticsearch/src/main/java/org/testcontainers/elasticsearch/ElasticsearchContainer.java +++ b/modules/elasticsearch/src/main/java/org/testcontainers/elasticsearch/ElasticsearchContainer.java @@ -5,6 +5,7 @@ import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; +import org.testcontainers.containers.BindMode; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy; import org.testcontainers.utility.Base58; @@ -98,6 +99,13 @@ public ElasticsearchContainer(final DockerImageName dockerImageName) { logger().info("Starting an elasticsearch container using [{}]", dockerImageName); withNetworkAliases("elasticsearch-" + Base58.randomString(6)); withEnv("discovery.type", "single-node"); + // Sets default memory of elasticsearch instance to 2GB + // Spaces are deliberate to allow user to define additional jvm options as elasticsearch resolves option files lexicographically + withClasspathResourceMapping( + "elasticsearch-default-memory-vm.options", + "/usr/share/elasticsearch/config/jvm.options.d/ elasticsearch-default-memory-vm.options", + BindMode.READ_ONLY + ); addExposedPorts(ELASTICSEARCH_DEFAULT_PORT, ELASTICSEARCH_DEFAULT_TCP_PORT); this.isAtLeastMajorVersion8 = new ComparableVersion(dockerImageName.getVersionPart()).isGreaterThanOrEqualTo("8.0.0"); diff --git a/modules/elasticsearch/src/main/resources/elasticsearch-default-memory-vm.options b/modules/elasticsearch/src/main/resources/elasticsearch-default-memory-vm.options new file mode 100644 index 00000000000..10db7b96157 --- /dev/null +++ b/modules/elasticsearch/src/main/resources/elasticsearch-default-memory-vm.options @@ -0,0 +1,2 @@ +-Xms2147483648 +-Xmx2147483648 diff --git a/modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java b/modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java index 1e1e70b5d75..7ee81c82759 100644 --- a/modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java +++ b/modules/elasticsearch/src/test/java/org/testcontainers/elasticsearch/ElasticsearchContainerTest.java @@ -19,6 +19,7 @@ import org.junit.After; import org.junit.Test; import org.testcontainers.DockerClientFactory; +import org.testcontainers.containers.BindMode; import org.testcontainers.containers.wait.strategy.HttpWaitStrategy; import org.testcontainers.containers.wait.strategy.Wait; import org.testcontainers.images.RemoteDockerImage; @@ -375,6 +376,46 @@ public void testElasticsearch8SecureByDefaultFailsSilentlyOnLatestImages() throw } } + @Test + public void testElasticsearchDefaultMaxHeapSize() throws Exception { + long defaultHeapSize = 2147483648L; + + try (ElasticsearchContainer container = new ElasticsearchContainer(ELASTICSEARCH_IMAGE)) { + container.start(); + assertElasticsearchContainerHasHeapSize(container, defaultHeapSize); + } + } + + @Test + public void testElasticsearchCustomMaxHeapSizeInEnvironmentVariable() throws Exception { + long customHeapSize = 1574961152; + + try ( + ElasticsearchContainer container = new ElasticsearchContainer(ELASTICSEARCH_IMAGE) + .withEnv("ES_JAVA_OPTS", String.format("-Xms%d -Xmx%d", customHeapSize, customHeapSize)) + ) { + container.start(); + assertElasticsearchContainerHasHeapSize(container, customHeapSize); + } + } + + @Test + public void testElasticsearchCustomMaxHeapSizeInJvmOptionsFile() throws Exception { + long customHeapSize = 1574961152; + + try ( + ElasticsearchContainer container = new ElasticsearchContainer(ELASTICSEARCH_IMAGE) + .withClasspathResourceMapping( + "test-custom-memory-jvm.options", + "/usr/share/elasticsearch/config/jvm.options.d/a-user-defined-jvm.options", + BindMode.READ_ONLY + ); + ) { + container.start(); + assertElasticsearchContainerHasHeapSize(container, customHeapSize); + } + } + private void tagImage(String sourceImage, String targetImage, String targetTag) throws InterruptedException { DockerClient dockerClient = DockerClientFactory.instance().client(); dockerClient @@ -438,4 +479,13 @@ private RestClient getAnonymousClient(ElasticsearchContainer container) { return anonymousClient; } + + private void assertElasticsearchContainerHasHeapSize(ElasticsearchContainer container, long heapSizeInBytes) + throws Exception { + Response response = getClient(container).performRequest(new Request("GET", "/_nodes/_all/jvm")); + String responseBody = EntityUtils.toString(response.getEntity()); + assertThat(response.getStatusLine().getStatusCode()).isEqualTo(200); + assertThat(responseBody).contains("\"heap_init_in_bytes\":" + heapSizeInBytes); + assertThat(responseBody).contains("\"heap_max_in_bytes\":" + heapSizeInBytes); + } } diff --git a/modules/elasticsearch/src/test/resources/test-custom-memory-jvm.options b/modules/elasticsearch/src/test/resources/test-custom-memory-jvm.options new file mode 100644 index 00000000000..e3fe586284e --- /dev/null +++ b/modules/elasticsearch/src/test/resources/test-custom-memory-jvm.options @@ -0,0 +1,2 @@ +-Xms1574961152 +-Xmx1574961152 From a18cab6cea2502c8bbb1f4eb7e4b11a6e6d4749d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Fri, 26 Aug 2022 01:07:43 -0600 Subject: [PATCH 0434/3231] Add .sdkmanrc with 8.0.345-tem (#5772) `.sdkmanrc` is used by sdkman to automatically switch to the version declared in the file via env command --- .sdkmanrc | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .sdkmanrc diff --git a/.sdkmanrc b/.sdkmanrc new file mode 100644 index 00000000000..95cd27dd1ad --- /dev/null +++ b/.sdkmanrc @@ -0,0 +1,3 @@ +# Enable auto-env through the sdkman_auto_env config +# Add key=value pairs of SDKs to use below +java=8.0.345-tem From bd22f1e0fbf9a165881d68f6c33e1e4813e2014f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Fri, 26 Aug 2022 09:46:51 -0600 Subject: [PATCH 0435/3231] Add compatibility with MongoDB 6 (#5771) MongoDB 6 removed `mongo` shell and `mongosh` is recommended. See [here](https://www.mongodb.com/docs/manual/release-notes/6.0-compatibility/#legacy-mongo-shell-removed) Closes #5768 --- .../org/testcontainers/containers/MongoDBContainer.java | 8 +++++++- .../testcontainers/containers/MongoDBContainerTest.java | 7 +++++++ 2 files changed, 14 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 81bb59fd69d..cd2294e09ac 100644 --- a/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDBContainer.java +++ b/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDBContainer.java @@ -5,6 +5,7 @@ import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.testcontainers.containers.wait.strategy.Wait; +import org.testcontainers.utility.ComparableVersion; import org.testcontainers.utility.DockerImageName; import java.io.IOException; @@ -29,6 +30,8 @@ public class MongoDBContainer extends GenericContainer { private static final String MONGODB_DATABASE_NAME_DEFAULT = "test"; + private final boolean isAtLeastVersion6; + /** * @deprecated use {@link MongoDBContainer(DockerImageName)} instead */ @@ -45,6 +48,8 @@ public MongoDBContainer(final DockerImageName dockerImageName) { super(dockerImageName); dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); + this.isAtLeastVersion6 = new ComparableVersion(dockerImageName.getVersionPart()).isGreaterThanOrEqualTo("6.0"); + withExposedPorts(MONGODB_INTERNAL_PORT); withCommand("--replSet", "docker-rs"); waitingFor(Wait.forLogMessage("(?i).*waiting for connections.*", 1)); @@ -87,7 +92,8 @@ protected void containerIsStarted(InspectContainerResponse containerInfo) { } private String[] buildMongoEvalCommand(final String command) { - return new String[] { "mongo", "--eval", command }; + String cmd = this.isAtLeastVersion6 ? "mongosh" : "mongo"; + return new String[] { cmd, "--eval", command }; } private void checkMongoNodeExitCode(final Container.ExecResult execResult) { 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 ff6d0d6ccba..acaa6c8e5d8 100644 --- a/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBContainerTest.java +++ b/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBContainerTest.java @@ -98,4 +98,11 @@ public void shouldTestDatabaseName() { assertThat(mongoDBContainer.getReplicaSetUrl(databaseName)).endsWith(databaseName); } } + + @Test + public void supportsMongoDB_6() { + try (final MongoDBContainer mongoDBContainer = new MongoDBContainer("mongo:6.0.1")) { + mongoDBContainer.start(); + } + } } From 84e9a66d31caddc89cd123e6aeaa175d5d946e4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Sat, 27 Aug 2022 02:03:39 -0600 Subject: [PATCH 0436/3231] Exclude `org.testcontainers.shaded.*` package and upgrade deps (#5775) Add general japicmp's package exclusion. Also, shadow and test dependencies below are upgraded: * Upgrade okhttp version to 4.10.0 * Upgrade javassist version to 3.29.0-GA * Upgrade zip4j version to 2.11.1 * Upgrade kubernetes-client version to 6.0.0 --- gradle/japicmp.gradle | 3 +++ modules/azure/build.gradle | 2 +- modules/couchbase/build.gradle | 2 +- modules/hivemq/build.gradle | 4 ++-- modules/k3s/build.gradle | 2 +- .../java/org/testcontainers/k3s/Fabric8K3sContainerTest.java | 4 ++-- modules/mockserver/build.gradle | 2 +- .../containers/MockServerContainerRuleTest.java | 2 +- modules/solr/build.gradle | 2 +- 9 files changed, 13 insertions(+), 10 deletions(-) diff --git a/gradle/japicmp.gradle b/gradle/japicmp.gradle index 3c438d340f0..541736a8321 100644 --- a/gradle/japicmp.gradle +++ b/gradle/japicmp.gradle @@ -25,6 +25,9 @@ tasks.japicmp { onlyBinaryIncompatibleModified = true htmlOutputFile = file("$buildDir/reports/japi.html") + packageExcludes = [ + "org.testcontainers.shaded.*", + ] } // do not run on Windows by default // TODO investigate zip issue on Windows diff --git a/modules/azure/build.gradle b/modules/azure/build.gradle index f8a7193571e..252eb895d37 100644 --- a/modules/azure/build.gradle +++ b/modules/azure/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Azure" dependencies { api project(':testcontainers') // TODO use JDK's HTTP client and/or Apache HttpClient5 - shaded 'com.squareup.okhttp3:okhttp:3.14.9' + shaded 'com.squareup.okhttp3:okhttp:4.10.0' testImplementation 'org.assertj:assertj-core:3.23.1' testImplementation 'com.azure:azure-cosmos:4.34.0' diff --git a/modules/couchbase/build.gradle b/modules/couchbase/build.gradle index 6cdacbcf77a..5950aac7655 100644 --- a/modules/couchbase/build.gradle +++ b/modules/couchbase/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Couchbase" dependencies { api project(':testcontainers') // TODO use JDK's HTTP client and/or Apache HttpClient5 - shaded 'com.squareup.okhttp3:okhttp:3.14.9' + shaded 'com.squareup.okhttp3:okhttp:4.10.0' testImplementation 'com.couchbase.client:java-client:3.3.3' testImplementation 'org.awaitility:awaitility:4.2.0' diff --git a/modules/hivemq/build.gradle b/modules/hivemq/build.gradle index a85288f9fbd..9595b3e66b6 100644 --- a/modules/hivemq/build.gradle +++ b/modules/hivemq/build.gradle @@ -6,10 +6,10 @@ dependencies { shaded("org.apache.commons:commons-lang3:3.12.0") shaded("commons-io:commons-io:2.11.0") - shaded("org.javassist:javassist:3.28.0-GA") + shaded("org.javassist:javassist:3.29.0-GA") shaded("org.jboss.shrinkwrap:shrinkwrap-api:1.2.6") shaded("org.jboss.shrinkwrap:shrinkwrap-impl-base:1.2.6") - shaded("net.lingala.zip4j:zip4j:2.10.0") + shaded("net.lingala.zip4j:zip4j:2.11.1") testImplementation("org.junit.jupiter:junit-jupiter-api:5.9.0") testImplementation(project(":junit-jupiter")) diff --git a/modules/k3s/build.gradle b/modules/k3s/build.gradle index 2223909f051..8c6792c42e0 100644 --- a/modules/k3s/build.gradle +++ b/modules/k3s/build.gradle @@ -8,7 +8,7 @@ dependencies { // Any >2.8 version here is not compatible with jackson-databind 2.8.x. shaded 'com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.8.8' - testImplementation 'io.fabric8:kubernetes-client:5.12.2' + testImplementation 'io.fabric8:kubernetes-client:6.0.0' testImplementation 'io.kubernetes:client-java:16.0.0' testImplementation 'org.assertj:assertj-core:3.23.1' } diff --git a/modules/k3s/src/test/java/org/testcontainers/k3s/Fabric8K3sContainerTest.java b/modules/k3s/src/test/java/org/testcontainers/k3s/Fabric8K3sContainerTest.java index 6f9550eab2a..42b08444555 100644 --- a/modules/k3s/src/test/java/org/testcontainers/k3s/Fabric8K3sContainerTest.java +++ b/modules/k3s/src/test/java/org/testcontainers/k3s/Fabric8K3sContainerTest.java @@ -10,7 +10,7 @@ import io.fabric8.kubernetes.api.model.ProbeBuilder; import io.fabric8.kubernetes.client.Config; import io.fabric8.kubernetes.client.DefaultKubernetesClient; -import io.fabric8.kubernetes.client.dsl.Readiable; +import io.fabric8.kubernetes.client.dsl.Resource; import lombok.extern.slf4j.Slf4j; import org.junit.Test; import org.testcontainers.containers.output.Slf4jLogConsumer; @@ -55,7 +55,7 @@ public void shouldStartAndHaveListableNode() { client.pods().inNamespace("default").withName("helloworld").waitUntilReady(30, TimeUnit.SECONDS); assertThat(client.pods().inNamespace("default").withName("helloworld")) - .extracting(Readiable::isReady) + .extracting(Resource::isReady) .isEqualTo(true); } } diff --git a/modules/mockserver/build.gradle b/modules/mockserver/build.gradle index 2156ef045cd..df46295df21 100644 --- a/modules/mockserver/build.gradle +++ b/modules/mockserver/build.gradle @@ -3,6 +3,6 @@ description = "Testcontainers :: MockServer" dependencies { api project(':testcontainers') - testImplementation 'org.mock-server:mockserver-client-java:5.5.4' + testImplementation 'org.mock-server:mockserver-client-java:5.13.2' testImplementation 'org.assertj:assertj-core:3.23.1' } diff --git a/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerRuleTest.java b/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerRuleTest.java index 84d472f5e75..0e4fa102b2e 100644 --- a/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerRuleTest.java +++ b/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerRuleTest.java @@ -12,7 +12,7 @@ public class MockServerContainerRuleTest { public static final DockerImageName MOCKSERVER_IMAGE = DockerImageName.parse( - "jamesdbloom/mockserver:mockserver-5.5.4" + "jamesdbloom/mockserver:mockserver-5.13.2" ); // creatingProxy { diff --git a/modules/solr/build.gradle b/modules/solr/build.gradle index f8e2a99888d..72ab2997dea 100644 --- a/modules/solr/build.gradle +++ b/modules/solr/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Solr" dependencies { api project(':testcontainers') // TODO use JDK's HTTP client and/or Apache HttpClient5 - shaded 'com.squareup.okhttp3:okhttp:4.9.3' + shaded 'com.squareup.okhttp3:okhttp:4.10.0' testImplementation 'org.apache.solr:solr-solrj:8.11.1' testImplementation 'org.assertj:assertj-core:3.23.1' From 42a7441f59bdaa6315af202dfe7afa7e7b579be0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Mon, 29 Aug 2022 09:05:39 -0600 Subject: [PATCH 0437/3231] Run mongosh or mongo if cmd exists (#5774) Previously, the command execution relied on the image version which is different when building custom images. Instead, `mongosh` or `mongo` is executed if it is present within the PATH. --- .../containers/MongoDBContainer.java | 12 +-- .../containers/MongoDBContainerTest.java | 92 ++++++++++--------- 2 files changed, 53 insertions(+), 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 cd2294e09ac..ae93e4121d3 100644 --- a/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDBContainer.java +++ b/modules/mongodb/src/main/java/org/testcontainers/containers/MongoDBContainer.java @@ -5,7 +5,6 @@ import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.testcontainers.containers.wait.strategy.Wait; -import org.testcontainers.utility.ComparableVersion; import org.testcontainers.utility.DockerImageName; import java.io.IOException; @@ -30,8 +29,6 @@ public class MongoDBContainer extends GenericContainer { private static final String MONGODB_DATABASE_NAME_DEFAULT = "test"; - private final boolean isAtLeastVersion6; - /** * @deprecated use {@link MongoDBContainer(DockerImageName)} instead */ @@ -48,8 +45,6 @@ public MongoDBContainer(final DockerImageName dockerImageName) { super(dockerImageName); dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); - this.isAtLeastVersion6 = new ComparableVersion(dockerImageName.getVersionPart()).isGreaterThanOrEqualTo("6.0"); - withExposedPorts(MONGODB_INTERNAL_PORT); withCommand("--replSet", "docker-rs"); waitingFor(Wait.forLogMessage("(?i).*waiting for connections.*", 1)); @@ -92,8 +87,11 @@ protected void containerIsStarted(InspectContainerResponse containerInfo) { } private String[] buildMongoEvalCommand(final String command) { - String cmd = this.isAtLeastVersion6 ? "mongosh" : "mongo"; - return new String[] { cmd, "--eval", command }; + return new String[] { + "sh", + "-c", + "mongosh mongo --eval \"" + command + "\" || mongo --eval \"" + command + "\"", + }; } private void checkMongoNodeExitCode(final Container.ExecResult execResult) { 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 acaa6c8e5d8..dd59420f937 100644 --- a/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBContainerTest.java +++ b/modules/mongodb/src/test/java/org/testcontainers/containers/MongoDBContainerTest.java @@ -30,56 +30,59 @@ public void shouldExecuteTransactions() { // startingMongoDBContainer { mongoDBContainer.start(); // } + executeTx(mongoDBContainer); + } + } - final String mongoRsUrl = mongoDBContainer.getReplicaSetUrl(); - assertThat(mongoRsUrl).isNotNull(); - final String connectionString = mongoDBContainer.getConnectionString(); - final MongoClient mongoSyncClientBase = MongoClients.create(connectionString); - 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)); - mongoSyncClientBase - .getDatabase("mydb3") - .getCollection("baz") - .withWriteConcern(WriteConcern.MAJORITY) - .insertOne(new Document("def", 0)); + private void executeTx(MongoDBContainer mongoDBContainer) { + final String mongoRsUrl = mongoDBContainer.getReplicaSetUrl(); + assertThat(mongoRsUrl).isNotNull(); + final String connectionString = mongoDBContainer.getConnectionString(); + final MongoClient mongoSyncClientBase = MongoClients.create(connectionString); + 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)); + mongoSyncClientBase + .getDatabase("mydb3") + .getCollection("baz") + .withWriteConcern(WriteConcern.MAJORITY) + .insertOne(new Document("def", 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"; - 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); - assertThat(trxResultActual).isEqualTo(trxResult); - } catch (RuntimeException re) { - throw new IllegalStateException(re.getMessage(), re); - } finally { - clientSession.close(); - mongoSyncClient.close(); - } + try { + final String trxResultActual = clientSession.withTransaction(txnBody, txnOptions); + assertThat(trxResultActual).isEqualTo(trxResult); + } catch (RuntimeException re) { + throw new IllegalStateException(re.getMessage(), re); + } finally { + clientSession.close(); + mongoSyncClient.close(); } } @@ -103,6 +106,7 @@ public void shouldTestDatabaseName() { public void supportsMongoDB_6() { try (final MongoDBContainer mongoDBContainer = new MongoDBContainer("mongo:6.0.1")) { mongoDBContainer.start(); + executeTx(mongoDBContainer); } } } From b57cbce1ac6f35d698a0f663a61ec0096981e431 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Mon, 29 Aug 2022 10:51:21 -0600 Subject: [PATCH 0438/3231] Remove `disque-job-queue` and `mongodb-container` examples (#5782) Examples should provided useful usage of technologies. In this case, `disque-job-queue` seems to provide a not up-to-date image and `mongodb-container` already provides its own module. --- docs/examples.md | 1 - examples/disque-job-queue/build.gradle | 19 ---- .../java/DisqueBackedMailSenderService.java | 73 --------------- .../src/main/java/MailApiClient.java | 11 --- .../src/main/java/MailMessage.java | 12 --- .../src/main/java/MailSenderService.java | 45 ---------- .../test/java/DisqueBackedMailSenderTest.java | 89 ------------------- .../test/java/SingleDisqueInstanceTest.java | 61 ------------- .../src/test/resources/logback-test.xml | 14 --- examples/mongodb-container/build.gradle | 13 --- .../containers/MongoDbContainer.java | 64 ------------- .../containers/MongoDbContainerTest.java | 28 ------ examples/settings.gradle | 2 - 13 files changed, 432 deletions(-) delete mode 100644 examples/disque-job-queue/build.gradle delete mode 100644 examples/disque-job-queue/src/main/java/DisqueBackedMailSenderService.java delete mode 100644 examples/disque-job-queue/src/main/java/MailApiClient.java delete mode 100644 examples/disque-job-queue/src/main/java/MailMessage.java delete mode 100644 examples/disque-job-queue/src/main/java/MailSenderService.java delete mode 100644 examples/disque-job-queue/src/test/java/DisqueBackedMailSenderTest.java delete mode 100644 examples/disque-job-queue/src/test/java/SingleDisqueInstanceTest.java delete mode 100644 examples/disque-job-queue/src/test/resources/logback-test.xml delete mode 100644 examples/mongodb-container/build.gradle delete mode 100644 examples/mongodb-container/src/test/java/org/testcontainers/containers/MongoDbContainer.java delete mode 100644 examples/mongodb-container/src/test/java/org/testcontainers/containers/MongoDbContainerTest.java diff --git a/docs/examples.md b/docs/examples.md index abd5e410c21..2c6036ab2ea 100644 --- a/docs/examples.md +++ b/docs/examples.md @@ -4,7 +4,6 @@ Examples of different use cases provided by Testcontainers can be found below: - [Kafka Cluster with multiple brokers](https://github.com/testcontainers/testcontainers-java/tree/master/examples/kafka-cluster) - [Linked containers](https://github.com/testcontainers/testcontainers-java/tree/master/examples/linked-container) -- [MongoDB](https://github.com/testcontainers/testcontainers-java/tree/master/examples/mongodb-container) - [Neo4j](https://github.com/testcontainers/testcontainers-java/tree/master/examples/neo4j-container) - [Redis](https://github.com/testcontainers/testcontainers-java/tree/master/examples/redis-backed-cache) - [Selenium](https://github.com/testcontainers/testcontainers-java/tree/master/examples/cucumber) diff --git a/examples/disque-job-queue/build.gradle b/examples/disque-job-queue/build.gradle deleted file mode 100644 index 6c7a453c3e9..00000000000 --- a/examples/disque-job-queue/build.gradle +++ /dev/null @@ -1,19 +0,0 @@ -plugins { - id 'java' -} - -repositories { - mavenCentral() -} - -dependencies { - compileOnly "org.projectlombok:lombok:1.18.24" - annotationProcessor "org.projectlombok:lombok:1.18.24" - implementation 'biz.paluch.redis:spinach:0.3' - implementation 'com.google.code.gson:gson:2.9.1' - implementation 'com.google.guava:guava:23.0' - testImplementation 'ch.qos.logback:logback-classic:1.2.11' - testImplementation 'org.mockito:mockito-all:1.10.19' - testImplementation 'org.testcontainers:testcontainers' - testImplementation 'org.assertj:assertj-core:3.23.1' -} diff --git a/examples/disque-job-queue/src/main/java/DisqueBackedMailSenderService.java b/examples/disque-job-queue/src/main/java/DisqueBackedMailSenderService.java deleted file mode 100644 index d2096106500..00000000000 --- a/examples/disque-job-queue/src/main/java/DisqueBackedMailSenderService.java +++ /dev/null @@ -1,73 +0,0 @@ -import biz.paluch.spinach.DisqueClient; -import biz.paluch.spinach.api.Job; -import biz.paluch.spinach.api.sync.DisqueCommands; -import com.google.gson.Gson; - -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.concurrent.TimeUnit; - -/** - * Simple example of how a mail service can be backed by a Disque job queue. - * - * It's assumed that for actual sending (SMTP, HTTP API, etc), this service calls a {@link MailApiClient}. - * However, this can be expected to be quite unreliable, so use of Disque as a backing store helps keep track - * of sent/failed state. - */ -public class DisqueBackedMailSenderService implements MailSenderService { - - private final DisqueCommands disque; - private final Gson gson; - private final MailApiClient mailApiClient; - - public DisqueBackedMailSenderService(DisqueClient client, MailApiClient mailApiClient) { - this.mailApiClient = mailApiClient; - - // Obtain a Disque connection from DisqueClient - disque = client.connect().sync(); - - gson = new Gson(); - } - - @Override - public String enqueueMessage(MailMessage message) { - String messageAsJson = gson.toJson(message); - - // Enqueue the message as JSON, setting a TTL of 1 day (will expire off the queue if not sent by then) - String jobId = disque.addjob("mail", messageAsJson, 1, TimeUnit.DAYS); - - return jobId; - } - - @Override - public Result doScheduledSend() { - - Set succeededJobIds = new HashSet<>(); - Set failedJobIds = new HashSet<>(); - - // Wait up to 100ms for new messages to arrive on the queue - // Retrieve up to 10 messages - // (Ordinarily we'd make these configurable!) - List> jobs = disque.getjobs(100, TimeUnit.MILLISECONDS, 10, "mail"); - - for (Job job : jobs) { - String jsonBody = job.getBody(); - MailMessage message = gson.fromJson(jsonBody, MailMessage.class); - - try { - mailApiClient.send(message); - succeededJobIds.add(job.getId()); - } catch (MailApiClient.MailSendException e) { - failedJobIds.add(job.getId()); - } - } - - // For any failed messages, proactively return to the queue - disque.nack(failedJobIds.toArray(new String[failedJobIds.size()])); - // For any successful jobs, mark as completed - disque.ackjob(succeededJobIds.toArray(new String[succeededJobIds.size()])); - - return new Result(succeededJobIds.size(), failedJobIds.size()); - } -} diff --git a/examples/disque-job-queue/src/main/java/MailApiClient.java b/examples/disque-job-queue/src/main/java/MailApiClient.java deleted file mode 100644 index 61a90112f32..00000000000 --- a/examples/disque-job-queue/src/main/java/MailApiClient.java +++ /dev/null @@ -1,11 +0,0 @@ -/** - * Created by rnorth on 03/01/2016. - */ -public interface MailApiClient { - - boolean send(MailMessage message) throws MailSendException; - - class MailSendException extends RuntimeException { - - } -} diff --git a/examples/disque-job-queue/src/main/java/MailMessage.java b/examples/disque-job-queue/src/main/java/MailMessage.java deleted file mode 100644 index de167199057..00000000000 --- a/examples/disque-job-queue/src/main/java/MailMessage.java +++ /dev/null @@ -1,12 +0,0 @@ -import lombok.AllArgsConstructor; -import lombok.Data; - -/** - * Created by rnorth on 03/01/2016. - */ -@Data @AllArgsConstructor -public class MailMessage { - public final String subject; - public final String recipient; - public final String body; -} diff --git a/examples/disque-job-queue/src/main/java/MailSenderService.java b/examples/disque-job-queue/src/main/java/MailSenderService.java deleted file mode 100644 index 95237bf2eec..00000000000 --- a/examples/disque-job-queue/src/main/java/MailSenderService.java +++ /dev/null @@ -1,45 +0,0 @@ -import lombok.Data; -import lombok.RequiredArgsConstructor; - -import java.util.concurrent.Callable; - -/** - * Created by rnorth on 03/01/2016. - */ -public interface MailSenderService extends Callable { - - /** - * Enqueue a message to be sent on a subsequent {@link DisqueBackedMailSenderService#doScheduledSend()} - * @param message mail message to be sent - * @return an ID for the enqueued message - */ - String enqueueMessage(MailMessage message); - - /** - * Trigger a scheduled send of mail messages. - * - * TODO: In a real implementation this could be called by a {@link java.util.concurrent.ScheduledExecutorService}, - * probably - * - * @return the result of sending queued messages. - */ - MailSenderService.Result doScheduledSend(); - - /** - * call() implementation to allow MailSenderService to be used as a Callable. Simply delegates to - * the {@link DisqueBackedMailSenderService#doScheduledSend()} method. - * - * @return the result of sending queued messages. - * @throws Exception - */ - default MailSenderService.Result call() throws Exception { - return doScheduledSend(); - } - - @Data - @RequiredArgsConstructor - class Result { - public final int successfulCount; - public final int failedCount; - } -} diff --git a/examples/disque-job-queue/src/test/java/DisqueBackedMailSenderTest.java b/examples/disque-job-queue/src/test/java/DisqueBackedMailSenderTest.java deleted file mode 100644 index dfbab3f9e6a..00000000000 --- a/examples/disque-job-queue/src/test/java/DisqueBackedMailSenderTest.java +++ /dev/null @@ -1,89 +0,0 @@ -import biz.paluch.spinach.DisqueClient; -import biz.paluch.spinach.DisqueURI; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.testcontainers.containers.GenericContainer; -import org.testcontainers.utility.DockerImageName; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -/** - * Created by rnorth on 03/01/2016. - */ -public class DisqueBackedMailSenderTest { - - @Rule - public GenericContainer container = new GenericContainer<>(DockerImageName.parse("richnorth/disque:1.0-rc1")) - .withExposedPorts(7711); - private DisqueClient disqueClient; - - private MailSenderService service; - private MailApiClient mockMailApiClient; - - private MailMessage dummyMessage1; - private MailMessage dummyMessage2; - private MailMessage dummyMessage3; - - @Before - public void setup() { - disqueClient = new DisqueClient(DisqueURI.create(container.getHost(), container.getMappedPort(7711))); - mockMailApiClient = mock(MailApiClient.class); - - service = new DisqueBackedMailSenderService(disqueClient, mockMailApiClient); - - dummyMessage1 = new MailMessage("Dummy Message 1", "bob@example.com", "Test body"); - dummyMessage2 = new MailMessage("Dummy Message 2", "bob@example.com", "Test body"); - dummyMessage3 = new MailMessage("Dummy Message 3", "bob@example.com", "Test body"); - } - - @Test - public void testSimpleSuccessfulSending() throws Exception { - service.enqueueMessage(dummyMessage1); - service.enqueueMessage(dummyMessage2); - service.enqueueMessage(dummyMessage3); - - when(mockMailApiClient.send(any(MailMessage.class))).thenReturn(true); - - service.doScheduledSend(); - - verify(mockMailApiClient).send(eq(dummyMessage1)); - verify(mockMailApiClient).send(eq(dummyMessage2)); - verify(mockMailApiClient).send(eq(dummyMessage3)); - } - - @Test - public void testRetryOnFailure() throws Exception { - service.enqueueMessage(dummyMessage1); - service.enqueueMessage(dummyMessage2); - service.enqueueMessage(dummyMessage3); - - when(mockMailApiClient.send(eq(dummyMessage1))).thenThrow(MailApiClient.MailSendException.class).thenReturn(true); - when(mockMailApiClient.send(eq(dummyMessage2))).thenReturn(true); - when(mockMailApiClient.send(eq(dummyMessage3))).thenReturn(true); - - MailSenderService.Result result; - - result = service.doScheduledSend(); - assertThat(result.successfulCount).as("2 messages were 'sent' successfully").isEqualTo(2); - assertThat(result.failedCount).as("1 messages failed").isEqualTo(1); - - result = service.doScheduledSend(); - assertThat(result.successfulCount).as("1 message was 'sent' successfully").isEqualTo(1); - assertThat(result.failedCount).as("0 messages failed").isZero(); - - result = service.doScheduledSend(); - assertThat(result.successfulCount).as("0 messages were 'sent' successfully").isZero(); - assertThat(result.failedCount).as("0 messages failed").isZero(); - - verify(mockMailApiClient, times(2)).send(eq(dummyMessage1)); - verify(mockMailApiClient).send(eq(dummyMessage2)); - verify(mockMailApiClient).send(eq(dummyMessage3)); - } -} diff --git a/examples/disque-job-queue/src/test/java/SingleDisqueInstanceTest.java b/examples/disque-job-queue/src/test/java/SingleDisqueInstanceTest.java deleted file mode 100644 index 09830614eb0..00000000000 --- a/examples/disque-job-queue/src/test/java/SingleDisqueInstanceTest.java +++ /dev/null @@ -1,61 +0,0 @@ -import biz.paluch.spinach.DisqueClient; -import biz.paluch.spinach.DisqueURI; -import biz.paluch.spinach.api.AddJobArgs; -import biz.paluch.spinach.api.Job; -import biz.paluch.spinach.api.sync.DisqueCommands; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.testcontainers.containers.GenericContainer; -import org.testcontainers.utility.DockerImageName; - -import java.util.concurrent.TimeUnit; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Created by rnorth on 03/01/2016. - */ -public class SingleDisqueInstanceTest { - - @Rule - public GenericContainer container = new GenericContainer<>(DockerImageName.parse("richnorth/disque:1.0-rc1")) - .withExposedPorts(7711); - private DisqueCommands connection; - private AddJobArgs retryAfter1Second; - - @Before - public void setup() { - DisqueClient client = new DisqueClient(DisqueURI.create(container.getHost(), container.getMappedPort(7711))); - connection = client.connect().sync(); - retryAfter1Second = AddJobArgs.builder().retry(1, TimeUnit.SECONDS).build(); - } - - @Test - public void testJobStoreAndRetrieve() { - connection.addjob("main_queue", "body", 1, TimeUnit.MINUTES); - - Job job = connection.getjob("main_queue"); - - assertThat(job.getBody()).as("The retrieved job is the same as the one that was added").isEqualTo("body"); - - connection.ackjob(job.getId()); - } - - @Test - public void testFailureToAckJobBeforeTimeout() throws InterruptedException { - connection.addjob("main_queue", "body", 1, TimeUnit.MINUTES, retryAfter1Second); - - Job job = connection.getjob("main_queue"); - - assertThat(job.getBody()).as("The retrieved job is the same as the one that was added").isEqualTo("body"); - - TimeUnit.SECONDS.sleep(2); - - // The timeout specified here is how long the command will wait for a job to appear - Job job2 = connection.getjob(5, TimeUnit.SECONDS, "main_queue"); - - assertThat(job2).as("After re-getting the original job is back on the queue").isNotNull(); - assertThat(job2.getBody()).as("The retrieved job is the same as the one that was added").isEqualTo("body"); - } -} diff --git a/examples/disque-job-queue/src/test/resources/logback-test.xml b/examples/disque-job-queue/src/test/resources/logback-test.xml deleted file mode 100644 index be4ebaed2c8..00000000000 --- a/examples/disque-job-queue/src/test/resources/logback-test.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - %d{HH:mm:ss.SSS} %-5level %logger - %msg%n - - - - - - - diff --git a/examples/mongodb-container/build.gradle b/examples/mongodb-container/build.gradle deleted file mode 100644 index 1ae43055790..00000000000 --- a/examples/mongodb-container/build.gradle +++ /dev/null @@ -1,13 +0,0 @@ -plugins { - id 'java' -} - -repositories { - mavenCentral() -} - -dependencies { - testImplementation 'org.testcontainers:testcontainers' - testImplementation 'junit:junit:4.13.2' - testCompileClasspath 'org.jetbrains:annotations:23.0.0' -} diff --git a/examples/mongodb-container/src/test/java/org/testcontainers/containers/MongoDbContainer.java b/examples/mongodb-container/src/test/java/org/testcontainers/containers/MongoDbContainer.java deleted file mode 100644 index c4c07c1f601..00000000000 --- a/examples/mongodb-container/src/test/java/org/testcontainers/containers/MongoDbContainer.java +++ /dev/null @@ -1,64 +0,0 @@ -package org.testcontainers.containers; - -import org.jetbrains.annotations.NotNull; -import org.testcontainers.utility.DockerImageName; - - -/** - * This {@link Container} is based on the official MongoDb ({@code mongo}) image from - * DockerHub. If you need to use a custom MongoDB - * image, you can provide the full image name as well. - * - * @author Stefan Ludwig - */ -public class MongoDbContainer extends GenericContainer { - - /** - * This is the internal port on which MongoDB is running inside the container. - *

- * You can use this constant in case you want to map an explicit public port to it - * instead of the default random port. This can be done using methods like - * {@link #setPortBindings(java.util.List)}. - */ - public static final int MONGODB_PORT = 27017; - public static final String DEFAULT_IMAGE_AND_TAG = "mongo:4.0"; - - /** - * Creates a new {@link MongoDbContainer} with the {@value DEFAULT_IMAGE_AND_TAG} image. - * @deprecated use {@link MongoDbContainer(DockerImageName)} instead - */ - @Deprecated - public MongoDbContainer() { - this(DEFAULT_IMAGE_AND_TAG); - } - - /** - * Creates a new {@link MongoDbContainer} with the given {@code 'image'}. - * - * @param image the image (e.g. {@value DEFAULT_IMAGE_AND_TAG}) to use - * @deprecated use {@link MongoDbContainer(DockerImageName)} instead - */ - public MongoDbContainer(@NotNull String image) { - this(DockerImageName.parse(image)); - } - - /** - * Creates a new {@link MongoDbContainer} with the specified image. - */ - public MongoDbContainer(final DockerImageName dockerImageName) { - super(dockerImageName); - addExposedPort(MONGODB_PORT); - } - - /** - * Returns the actual public port of the internal MongoDB port ({@value MONGODB_PORT}). - * - * @return the public port of this container - * @see #getMappedPort(int) - */ - @NotNull - public Integer getPort() { - return getMappedPort(MONGODB_PORT); - } - -} diff --git a/examples/mongodb-container/src/test/java/org/testcontainers/containers/MongoDbContainerTest.java b/examples/mongodb-container/src/test/java/org/testcontainers/containers/MongoDbContainerTest.java deleted file mode 100644 index 5581c76e675..00000000000 --- a/examples/mongodb-container/src/test/java/org/testcontainers/containers/MongoDbContainerTest.java +++ /dev/null @@ -1,28 +0,0 @@ -package org.testcontainers.containers; - -import org.junit.Test; -import org.testcontainers.utility.DockerImageName; - -import java.io.IOException; -import java.net.Socket; - - -public class MongoDbContainerTest { - - @Test - public void containerStartsAndPublicPortIsAvailable() { - try (MongoDbContainer container = new MongoDbContainer(DockerImageName.parse("mongo:4.0"))) { - container.start(); - assertThatPortIsAvailable(container); - } - } - - private void assertThatPortIsAvailable(MongoDbContainer container) { - try { - new Socket(container.getHost(), container.getPort()); - } catch (IOException e) { - throw new AssertionError("The expected port " + container.getPort() + " is not available!"); - } - } - -} diff --git a/examples/settings.gradle b/examples/settings.gradle index 9ed079cd7b8..7194134350d 100644 --- a/examples/settings.gradle +++ b/examples/settings.gradle @@ -19,10 +19,8 @@ rootProject.name = 'testcontainers-examples' includeBuild '..' // explicit include to allow Dependabot to autodiscover subprojects -include 'disque-job-queue' include 'kafka-cluster' include 'linked-container' -include 'mongodb-container' include 'neo4j-container' include 'redis-backed-cache' include 'redis-backed-cache-testng' From ffb88b3d80004ad569caf080c00304584352b9db Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Wed, 31 Aug 2022 16:05:28 +0200 Subject: [PATCH 0439/3231] Fix link for Selenium examples (#5786) --- docs/examples.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/examples.md b/docs/examples.md index 2c6036ab2ea..918688d86cb 100644 --- a/docs/examples.md +++ b/docs/examples.md @@ -6,7 +6,7 @@ Examples of different use cases provided by Testcontainers can be found below: - [Linked containers](https://github.com/testcontainers/testcontainers-java/tree/master/examples/linked-container) - [Neo4j](https://github.com/testcontainers/testcontainers-java/tree/master/examples/neo4j-container) - [Redis](https://github.com/testcontainers/testcontainers-java/tree/master/examples/redis-backed-cache) -- [Selenium](https://github.com/testcontainers/testcontainers-java/tree/master/examples/cucumber) +- [Selenium](https://github.com/testcontainers/testcontainers-java/tree/master/examples/selenium-container) - [Selenium Module with Cucumber](https://github.com/testcontainers/testcontainers-java/tree/master/examples/cucumber) - [Singleton Container Pattern](https://github.com/testcontainers/testcontainers-java/tree/master/examples/singleton-container) - [Solr](https://github.com/testcontainers/testcontainers-java/tree/master/examples/solr-container) From fc3d60099165c9f73689ee3269370c5347492513 Mon Sep 17 00:00:00 2001 From: Aidan Do <43259657+REslim30@users.noreply.github.com> Date: Thu, 1 Sep 2022 06:08:31 +0930 Subject: [PATCH 0440/3231] Allow `getContainerByServiceName` to receive serviceName without suffix (#5776) Fixes #5773 --- .../containers/DockerComposeContainer.java | 3 ++- .../junit/DockerComposeContainerTest.java | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java index 14124d88c28..683e9d9fdf5 100644 --- a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java +++ b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java @@ -611,7 +611,8 @@ public SELF withStartupTimeout(Duration startupTimeout) { } public Optional getContainerByServiceName(String serviceName) { - return Optional.ofNullable(serviceInstanceMap.get(serviceName)); + String serviceInstantName = getServiceInstanceName(serviceName); + return Optional.ofNullable(serviceInstanceMap.get(serviceInstantName)); } private void followLogs(String containerId, Consumer consumer) { diff --git a/core/src/test/java/org/testcontainers/junit/DockerComposeContainerTest.java b/core/src/test/java/org/testcontainers/junit/DockerComposeContainerTest.java index c610fc584af..96989af90a4 100644 --- a/core/src/test/java/org/testcontainers/junit/DockerComposeContainerTest.java +++ b/core/src/test/java/org/testcontainers/junit/DockerComposeContainerTest.java @@ -44,6 +44,7 @@ public void testGetServicePort() { public void shouldRetrieveContainerByServiceName() { String existingServiceName = "db_1"; Optional result = environment.getContainerByServiceName(existingServiceName); + assertThat(result) .as(String.format("Container should be found by service name %s", existingServiceName)) .isPresent(); @@ -52,6 +53,19 @@ public void shouldRetrieveContainerByServiceName() { .isEqualTo(result.get().getExposedPorts()); } + @Test + public void shouldRetrieveContainerByServiceNameWithoutNumberedSuffix() { + String existingServiceName = "db"; + Optional result = environment.getContainerByServiceName(existingServiceName); + + assertThat(result) + .as(String.format("Container should be found by service name %s", existingServiceName)) + .isPresent(); + assertThat(result.get().getExposedPorts()) + .as("Mapped port for result container was wrong, perhaps wrong container was found") + .isEqualTo(Collections.singletonList(3306)); + } + @Test public void shouldReturnEmptyResultOnNoneExistingService() { String notExistingServiceName = "db_256"; From 3ef2c5177f412135d5c2410abb6bcf31619b20e8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Sep 2022 22:05:15 +0000 Subject: [PATCH 0441/3231] Bump slf4j-api from 1.7.36 to 2.0.0 in /examples Bumps [slf4j-api](https://github.com/qos-ch/slf4j) from 1.7.36 to 2.0.0. - [Release notes](https://github.com/qos-ch/slf4j/releases) - [Commits](https://github.com/qos-ch/slf4j/compare/v_1.7.36...v_2.0.0) --- updated-dependencies: - dependency-name: org.slf4j:slf4j-api dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- examples/linked-container/build.gradle | 2 +- examples/redis-backed-cache-testng/build.gradle | 2 +- examples/redis-backed-cache/build.gradle | 2 +- examples/singleton-container/build.gradle | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/linked-container/build.gradle b/examples/linked-container/build.gradle index 5b690597eaf..46d038e4643 100644 --- a/examples/linked-container/build.gradle +++ b/examples/linked-container/build.gradle @@ -6,7 +6,7 @@ repositories { mavenCentral() } dependencies { - compileOnly 'org.slf4j:slf4j-api:1.7.36' + compileOnly 'org.slf4j:slf4j-api:2.0.0' implementation 'com.squareup.okhttp3:okhttp:4.10.0' implementation 'org.json:json:20220320' testImplementation 'org.postgresql:postgresql:42.4.2' diff --git a/examples/redis-backed-cache-testng/build.gradle b/examples/redis-backed-cache-testng/build.gradle index 9d0fd118f61..799f3f89925 100644 --- a/examples/redis-backed-cache-testng/build.gradle +++ b/examples/redis-backed-cache-testng/build.gradle @@ -7,7 +7,7 @@ repositories { } dependencies { - compileOnly 'org.slf4j:slf4j-api:1.7.36' + compileOnly 'org.slf4j:slf4j-api:2.0.0' implementation 'redis.clients:jedis:4.2.3' implementation 'com.google.code.gson:gson:2.9.1' implementation 'com.google.guava:guava:23.0' diff --git a/examples/redis-backed-cache/build.gradle b/examples/redis-backed-cache/build.gradle index c5f365b5502..1d840f11871 100644 --- a/examples/redis-backed-cache/build.gradle +++ b/examples/redis-backed-cache/build.gradle @@ -7,7 +7,7 @@ repositories { } dependencies { - compileOnly 'org.slf4j:slf4j-api:1.7.36' + compileOnly 'org.slf4j:slf4j-api:2.0.0' implementation 'redis.clients:jedis:4.2.3' implementation 'com.google.code.gson:gson:2.9.1' implementation 'com.google.guava:guava:23.0' diff --git a/examples/singleton-container/build.gradle b/examples/singleton-container/build.gradle index 89f067d3a19..d540a8386ab 100644 --- a/examples/singleton-container/build.gradle +++ b/examples/singleton-container/build.gradle @@ -11,7 +11,7 @@ dependencies { implementation 'redis.clients:jedis:4.2.3' implementation 'com.google.code.gson:gson:2.9.1' implementation 'com.google.guava:guava:23.0' - compileOnly 'org.slf4j:slf4j-api:1.7.36' + compileOnly 'org.slf4j:slf4j-api:2.0.0' testImplementation 'ch.qos.logback:logback-classic:1.2.11' testImplementation 'org.testcontainers:testcontainers' From b83777389a000df475bd7b033551f20659d2820b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Sep 2022 22:05:22 +0000 Subject: [PATCH 0442/3231] Bump postgresql from 42.4.2 to 42.5.0 in /examples Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.4.2 to 42.5.0. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.4.2...REL42.5.0) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- examples/linked-container/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/linked-container/build.gradle b/examples/linked-container/build.gradle index 5b690597eaf..694261f796a 100644 --- a/examples/linked-container/build.gradle +++ b/examples/linked-container/build.gradle @@ -9,7 +9,7 @@ dependencies { compileOnly 'org.slf4j:slf4j-api:1.7.36' implementation 'com.squareup.okhttp3:okhttp:4.10.0' implementation 'org.json:json:20220320' - testImplementation 'org.postgresql:postgresql:42.4.2' + testImplementation 'org.postgresql:postgresql:42.5.0' testImplementation 'ch.qos.logback:logback-classic:1.2.11' testImplementation 'org.testcontainers:postgresql' testImplementation 'org.assertj:assertj-core:3.23.1' From 3b006da54b3f4133564cc6a71eea152320b8ef88 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Sep 2022 22:05:24 +0000 Subject: [PATCH 0443/3231] Bump aws-java-sdk-s3 from 1.12.285 to 1.12.295 in /modules/localstack Bumps [aws-java-sdk-s3](https://github.com/aws/aws-sdk-java) from 1.12.285 to 1.12.295. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.285...1.12.295) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-s3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index ad43f6f9c08..22966424b31 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Localstack" dependencies { api project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.285' + compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.295' testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.285' testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.285' testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.285' From 605a769acdf745ed13bb9e8b0153b1c82f3b52d5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Sep 2022 22:05:25 +0000 Subject: [PATCH 0444/3231] Bump slf4j-api from 1.7.36 to 2.0.0 in /core Bumps [slf4j-api](https://github.com/qos-ch/slf4j) from 1.7.36 to 2.0.0. - [Release notes](https://github.com/qos-ch/slf4j/releases) - [Commits](https://github.com/qos-ch/slf4j/compare/v_1.7.36...v_2.0.0) --- updated-dependencies: - dependency-name: org.slf4j:slf4j-api dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index aa4c6eabf22..f37a6478b88 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -61,7 +61,7 @@ configurations.all { dependencies { api 'junit:junit:4.13.2' - api 'org.slf4j:slf4j-api:1.7.36' + api 'org.slf4j:slf4j-api:2.0.0' compileOnly 'org.jetbrains:annotations:23.0.0' testCompileClasspath 'org.jetbrains:annotations:23.0.0' api 'org.apache.commons:commons-compress:1.21' From d8e674fb1ee4b351b33d3f598220a7af100c0eb5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Sep 2022 22:05:28 +0000 Subject: [PATCH 0445/3231] Bump snakeyaml from 1.30 to 1.31 in /core Bumps [snakeyaml](https://bitbucket.org/snakeyaml/snakeyaml) from 1.30 to 1.31. - [Commits](https://bitbucket.org/snakeyaml/snakeyaml/branches/compare/snakeyaml-1.31..snakeyaml-1.30) --- updated-dependencies: - dependency-name: org.yaml:snakeyaml dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index aa4c6eabf22..b2f1a71f643 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -80,7 +80,7 @@ dependencies { api 'com.github.docker-java:docker-java-transport-zerodep' - shaded "org.yaml:snakeyaml:1.30" + shaded "org.yaml:snakeyaml:1.31" shaded 'org.glassfish.main.external:trilead-ssh2-repackaged:4.1.2' From b6a066fea2d04dcee16f2da3bd9b63dfcf19a7c1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Sep 2022 22:05:32 +0000 Subject: [PATCH 0446/3231] Bump aws-java-sdk-logs from 1.12.285 to 1.12.295 in /modules/localstack Bumps [aws-java-sdk-logs](https://github.com/aws/aws-sdk-java) from 1.12.285 to 1.12.295. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.285...1.12.295) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-logs dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index ad43f6f9c08..2dc763c860f 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -6,7 +6,7 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.285' testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.285' testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.285' - testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.285' + testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.295' testImplementation 'software.amazon.awssdk:s3:2.17.256' testImplementation 'org.assertj:assertj-core:3.23.1' } From 05da3a213d12e3aad87379264c89de725c94c7f7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Sep 2022 22:05:43 +0000 Subject: [PATCH 0447/3231] Bump aws-java-sdk-dynamodb in /modules/dynalite Bumps [aws-java-sdk-dynamodb](https://github.com/aws/aws-sdk-java) from 1.12.285 to 1.12.295. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.285...1.12.295) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-dynamodb dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/dynalite/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/dynalite/build.gradle b/modules/dynalite/build.gradle index 8b548d5b91f..ac9ff346bea 100644 --- a/modules/dynalite/build.gradle +++ b/modules/dynalite/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Dynalite" dependencies { api project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.12.285' - testImplementation 'com.amazonaws:aws-java-sdk-dynamodb:1.12.281' + compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.12.295' + testImplementation 'com.amazonaws:aws-java-sdk-dynamodb:1.12.295' testImplementation 'org.assertj:assertj-core:3.23.1' } From 19d604067a49d6a78771008b44d86cf28f12309a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Sep 2022 22:05:43 +0000 Subject: [PATCH 0448/3231] Bump postgresql from 42.4.2 to 42.5.0 in /modules/postgresql Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.4.2 to 42.5.0. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.4.2...REL42.5.0) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/postgresql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/postgresql/build.gradle b/modules/postgresql/build.gradle index 0c5035df549..59515ed7549 100644 --- a/modules/postgresql/build.gradle +++ b/modules/postgresql/build.gradle @@ -11,7 +11,7 @@ dependencies { testImplementation project(':jdbc-test') testImplementation project(':test-support') - testImplementation 'org.postgresql:postgresql:42.4.2' + testImplementation 'org.postgresql:postgresql:42.5.0' testImplementation testFixtures(project(':r2dbc')) testImplementation 'io.r2dbc:r2dbc-postgresql:0.8.11.RELEASE' From ea3b3ad40ccc19db00580b6911110c56e7b4065c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Sep 2022 22:05:48 +0000 Subject: [PATCH 0449/3231] Bump r2dbc-postgresql in /modules/postgresql Bumps [r2dbc-postgresql](https://github.com/r2dbc/r2dbc-postgresql) from 0.8.12.RELEASE to 0.8.13.RELEASE. - [Release notes](https://github.com/r2dbc/r2dbc-postgresql/releases) - [Changelog](https://github.com/pgjdbc/r2dbc-postgresql/blob/v0.8.13.RELEASE/CHANGELOG) - [Commits](https://github.com/r2dbc/r2dbc-postgresql/compare/v0.8.12.RELEASE...v0.8.13.RELEASE) --- updated-dependencies: - dependency-name: io.r2dbc:r2dbc-postgresql dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/postgresql/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/postgresql/build.gradle b/modules/postgresql/build.gradle index 0c5035df549..15a39d97766 100644 --- a/modules/postgresql/build.gradle +++ b/modules/postgresql/build.gradle @@ -7,14 +7,14 @@ dependencies { api project(':jdbc') compileOnly project(':r2dbc') - compileOnly 'io.r2dbc:r2dbc-postgresql:0.8.12.RELEASE' + compileOnly 'io.r2dbc:r2dbc-postgresql:0.8.13.RELEASE' testImplementation project(':jdbc-test') testImplementation project(':test-support') testImplementation 'org.postgresql:postgresql:42.4.2' testImplementation testFixtures(project(':r2dbc')) - testImplementation 'io.r2dbc:r2dbc-postgresql:0.8.11.RELEASE' + testImplementation 'io.r2dbc:r2dbc-postgresql:0.8.13.RELEASE' compileOnly 'org.jetbrains:annotations:23.0.0' } From f6151a20abb84ab2c2310c7a81f1c29c908f76bf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Sep 2022 22:05:49 +0000 Subject: [PATCH 0450/3231] Bump postgresql from 42.4.2 to 42.5.0 in /modules/junit-jupiter Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.4.2 to 42.5.0. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.4.2...REL42.5.0) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index dd1d0c7ed6e..5b91bd181fe 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -15,7 +15,7 @@ dependencies { testImplementation 'org.assertj:assertj-core:3.23.1' testImplementation 'org.junit.jupiter:junit-jupiter-params:5.9.0' - testRuntimeOnly 'org.postgresql:postgresql:42.4.2' + testRuntimeOnly 'org.postgresql:postgresql:42.5.0' testRuntimeOnly 'mysql:mysql-connector-java:8.0.30' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.9.0' } From 236cdf97d814a34814315d215079dea77aad0d78 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Sep 2022 22:06:20 +0000 Subject: [PATCH 0451/3231] Bump transport from 7.17.5 to 7.17.6 in /modules/elasticsearch Bumps [transport](https://github.com/elastic/elasticsearch) from 7.17.5 to 7.17.6. - [Release notes](https://github.com/elastic/elasticsearch/releases) - [Changelog](https://github.com/elastic/elasticsearch/blob/main/CHANGELOG.md) - [Commits](https://github.com/elastic/elasticsearch/compare/v7.17.5...v7.17.6) --- updated-dependencies: - dependency-name: org.elasticsearch.client:transport dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/elasticsearch/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/elasticsearch/build.gradle b/modules/elasticsearch/build.gradle index 0e6c5bd7b5d..8d8125eb1e1 100644 --- a/modules/elasticsearch/build.gradle +++ b/modules/elasticsearch/build.gradle @@ -3,6 +3,6 @@ description = "TestContainers :: elasticsearch" dependencies { api project(':testcontainers') testImplementation "org.elasticsearch.client:elasticsearch-rest-client:8.3.3" - testImplementation "org.elasticsearch.client:transport:7.17.5" + testImplementation "org.elasticsearch.client:transport:7.17.6" testImplementation 'org.assertj:assertj-core:3.23.1' } From 4171dd86a8b95b8e991c7415be4d9ce724dec9cc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Sep 2022 22:06:41 +0000 Subject: [PATCH 0452/3231] Bump actions/cache from 3.0.7 to 3.0.8 Bumps [actions/cache](https://github.com/actions/cache) from 3.0.7 to 3.0.8. - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/v3.0.7...v3.0.8) --- updated-dependencies: - dependency-name: actions/cache dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/ci-examples.yml | 4 ++-- .github/workflows/ci.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci-examples.yml b/.github/workflows/ci-examples.yml index f23fc66e889..fe4610368aa 100644 --- a/.github/workflows/ci-examples.yml +++ b/.github/workflows/ci-examples.yml @@ -25,7 +25,7 @@ jobs: java-version: '8.0.302' distribution: temurin - name: Cache Gradle Home files - uses: actions/cache@v3.0.7 + uses: actions/cache@v3.0.8 continue-on-error: true with: path: ~/.gradle/caches @@ -53,7 +53,7 @@ jobs: java-version: '8.0.302' distribution: temurin - name: Cache Gradle Home files - uses: actions/cache@v3.0.7 + uses: actions/cache@v3.0.8 continue-on-error: true with: path: ~/.gradle/caches diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 288e9329824..9d7749134ce 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,7 +33,7 @@ jobs: java-version: '8.0.302' distribution: temurin - name: Cache Gradle Home files - uses: actions/cache@v3.0.7 + uses: actions/cache@v3.0.8 with: path: ~/.gradle/caches key: ${{ runner.os }}-gradle-home-testmatrix-${{ hashFiles('**/*.gradle') }} @@ -59,7 +59,7 @@ jobs: java-version: '8.0.302' distribution: temurin - name: Cache Gradle Home files - uses: actions/cache@v3.0.7 + uses: actions/cache@v3.0.8 continue-on-error: true with: path: ~/.gradle/caches From 6b716358bcdda694d1692d93c0b879e7920451a8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Sep 2022 22:08:37 +0000 Subject: [PATCH 0453/3231] Bump r2dbc-postgresql in /modules/r2dbc Bumps [r2dbc-postgresql](https://github.com/r2dbc/r2dbc-postgresql) from 0.8.12.RELEASE to 0.8.13.RELEASE. - [Release notes](https://github.com/r2dbc/r2dbc-postgresql/releases) - [Changelog](https://github.com/pgjdbc/r2dbc-postgresql/blob/v0.8.13.RELEASE/CHANGELOG) - [Commits](https://github.com/r2dbc/r2dbc-postgresql/compare/v0.8.12.RELEASE...v0.8.13.RELEASE) --- updated-dependencies: - dependency-name: io.r2dbc:r2dbc-postgresql dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/r2dbc/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/r2dbc/build.gradle b/modules/r2dbc/build.gradle index f38803104a3..c55b71ddcb2 100644 --- a/modules/r2dbc/build.gradle +++ b/modules/r2dbc/build.gradle @@ -12,7 +12,7 @@ dependencies { api 'io.r2dbc:r2dbc-spi:0.8.1.RELEASE' testImplementation 'org.assertj:assertj-core:3.23.1' - testImplementation 'io.r2dbc:r2dbc-postgresql:0.8.12.RELEASE' + testImplementation 'io.r2dbc:r2dbc-postgresql:0.8.13.RELEASE' testImplementation project(':postgresql') testFixturesImplementation 'io.projectreactor:reactor-core:3.4.22' From ee8c818c7ba68bf3024a47776351211b824e9e0f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Sep 2022 22:08:38 +0000 Subject: [PATCH 0454/3231] Bump google-cloud-firestore from 3.4.0 to 3.4.2 in /modules/gcloud Bumps [google-cloud-firestore](https://github.com/googleapis/java-firestore) from 3.4.0 to 3.4.2. - [Release notes](https://github.com/googleapis/java-firestore/releases) - [Changelog](https://github.com/googleapis/java-firestore/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/java-firestore/compare/v3.4.0...v3.4.2) --- updated-dependencies: - dependency-name: com.google.cloud:google-cloud-firestore dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/gcloud/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle index bb79bfbbcc2..6b4d90d5de1 100644 --- a/modules/gcloud/build.gradle +++ b/modules/gcloud/build.gradle @@ -4,7 +4,7 @@ dependencies { api project(':testcontainers') testImplementation 'com.google.cloud:google-cloud-datastore:2.11.0' - testImplementation 'com.google.cloud:google-cloud-firestore:3.4.0' + testImplementation 'com.google.cloud:google-cloud-firestore:3.4.2' testImplementation 'com.google.cloud:google-cloud-pubsub:1.120.12' testImplementation 'com.google.cloud:google-cloud-spanner:6.28.0' testImplementation 'com.google.cloud:google-cloud-bigtable:2.10.3' From 150b75de98f44c932d9303522ea9d5a4498f2fd0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Sep 2022 22:08:43 +0000 Subject: [PATCH 0455/3231] Bump google-cloud-spanner from 6.28.0 to 6.29.0 in /modules/gcloud Bumps [google-cloud-spanner](https://github.com/googleapis/java-spanner) from 6.28.0 to 6.29.0. - [Release notes](https://github.com/googleapis/java-spanner/releases) - [Changelog](https://github.com/googleapis/java-spanner/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/java-spanner/compare/v6.28.0...v6.29.0) --- updated-dependencies: - dependency-name: com.google.cloud:google-cloud-spanner dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/gcloud/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle index bb79bfbbcc2..e0c20ca6c16 100644 --- a/modules/gcloud/build.gradle +++ b/modules/gcloud/build.gradle @@ -6,7 +6,7 @@ dependencies { testImplementation 'com.google.cloud:google-cloud-datastore:2.11.0' testImplementation 'com.google.cloud:google-cloud-firestore:3.4.0' testImplementation 'com.google.cloud:google-cloud-pubsub:1.120.12' - testImplementation 'com.google.cloud:google-cloud-spanner:6.28.0' + testImplementation 'com.google.cloud:google-cloud-spanner:6.29.0' testImplementation 'com.google.cloud:google-cloud-bigtable:2.10.3' testImplementation 'org.assertj:assertj-core:3.23.1' } From f6ffb47903edf311a1b1aefcb501bcafb14cfe70 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Sep 2022 22:08:45 +0000 Subject: [PATCH 0456/3231] Bump logback-classic from 1.2.11 to 1.4.0 in /modules/hivemq Bumps [logback-classic](https://github.com/qos-ch/logback) from 1.2.11 to 1.4.0. - [Release notes](https://github.com/qos-ch/logback/releases) - [Commits](https://github.com/qos-ch/logback/compare/v_1.2.11...v_1.4.0) --- updated-dependencies: - dependency-name: ch.qos.logback:logback-classic dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/hivemq/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/hivemq/build.gradle b/modules/hivemq/build.gradle index 9595b3e66b6..477d5ac98a3 100644 --- a/modules/hivemq/build.gradle +++ b/modules/hivemq/build.gradle @@ -16,7 +16,7 @@ dependencies { testImplementation("com.hivemq:hivemq-extension-sdk:4.8.3") testImplementation("com.hivemq:hivemq-mqtt-client:1.3.0") testImplementation("org.apache.httpcomponents:httpclient:4.5.13") - testImplementation("ch.qos.logback:logback-classic:1.2.11") + testImplementation("ch.qos.logback:logback-classic:1.4.0") testImplementation 'org.assertj:assertj-core:3.23.1' testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.9.0") } From d6f318c01c6e28e47e3ad25d858d8827e6276f29 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Sep 2022 22:08:58 +0000 Subject: [PATCH 0457/3231] Bump kubernetes-client from 6.0.0 to 6.1.1 in /modules/k3s Bumps [kubernetes-client](https://github.com/fabric8io/kubernetes-client) from 6.0.0 to 6.1.1. - [Release notes](https://github.com/fabric8io/kubernetes-client/releases) - [Changelog](https://github.com/fabric8io/kubernetes-client/blob/master/CHANGELOG.md) - [Commits](https://github.com/fabric8io/kubernetes-client/compare/v6.0.0...v6.1.1) --- updated-dependencies: - dependency-name: io.fabric8:kubernetes-client dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/k3s/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/k3s/build.gradle b/modules/k3s/build.gradle index 8c6792c42e0..0778419a3e2 100644 --- a/modules/k3s/build.gradle +++ b/modules/k3s/build.gradle @@ -8,7 +8,7 @@ dependencies { // Any >2.8 version here is not compatible with jackson-databind 2.8.x. shaded 'com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.8.8' - testImplementation 'io.fabric8:kubernetes-client:6.0.0' + testImplementation 'io.fabric8:kubernetes-client:6.1.1' testImplementation 'io.kubernetes:client-java:16.0.0' testImplementation 'org.assertj:assertj-core:3.23.1' } From bc76fc9086812d557c0e3d700f097f43f2355fbc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Sep 2022 22:08:59 +0000 Subject: [PATCH 0458/3231] Bump trino-jdbc from 393 to 394 in /modules/trino Bumps trino-jdbc from 393 to 394. --- updated-dependencies: - dependency-name: io.trino:trino-jdbc dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- modules/trino/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/trino/build.gradle b/modules/trino/build.gradle index e5b7efee873..ca2619a8631 100644 --- a/modules/trino/build.gradle +++ b/modules/trino/build.gradle @@ -4,6 +4,6 @@ dependencies { api project(':jdbc') testImplementation project(':jdbc-test') - testImplementation 'io.trino:trino-jdbc:393' + testImplementation 'io.trino:trino-jdbc:394' compileOnly 'org.jetbrains:annotations:23.0.0' } From 9ef2d12be740b590a6f6a11fdfeff4e234a83fb9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Sep 2022 22:09:10 +0000 Subject: [PATCH 0459/3231] Bump postgresql from 42.4.2 to 42.5.0 in /modules/cockroachdb Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.4.2 to 42.5.0. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.4.2...REL42.5.0) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/cockroachdb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/cockroachdb/build.gradle b/modules/cockroachdb/build.gradle index fbd2415e53c..638629ef22a 100644 --- a/modules/cockroachdb/build.gradle +++ b/modules/cockroachdb/build.gradle @@ -4,6 +4,6 @@ dependencies { api project(':jdbc') testImplementation project(':jdbc-test') - testImplementation 'org.postgresql:postgresql:42.4.2' + testImplementation 'org.postgresql:postgresql:42.5.0' testImplementation 'org.assertj:assertj-core:3.23.1' } From 1693da989b29deb265d58c279fc4de89b79ad7ba Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Sep 2022 22:09:19 +0000 Subject: [PATCH 0460/3231] Bump azure-cosmos from 4.34.0 to 4.35.1 in /modules/azure Bumps [azure-cosmos](https://github.com/Azure/azure-sdk-for-java) from 4.34.0 to 4.35.1. - [Release notes](https://github.com/Azure/azure-sdk-for-java/releases) - [Commits](https://github.com/Azure/azure-sdk-for-java/compare/azure-cosmos_4.34.0...azure-cosmos_4.35.1) --- updated-dependencies: - dependency-name: com.azure:azure-cosmos dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/azure/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/azure/build.gradle b/modules/azure/build.gradle index 252eb895d37..8753606874c 100644 --- a/modules/azure/build.gradle +++ b/modules/azure/build.gradle @@ -6,5 +6,5 @@ dependencies { shaded 'com.squareup.okhttp3:okhttp:4.10.0' testImplementation 'org.assertj:assertj-core:3.23.1' - testImplementation 'com.azure:azure-cosmos:4.34.0' + testImplementation 'com.azure:azure-cosmos:4.35.1' } From efa0a8920e6a1d07a76e0eeac0a33127fdaaad7d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 2 Sep 2022 03:13:19 +0000 Subject: [PATCH 0461/3231] Bump aws-java-sdk-sqs from 1.12.285 to 1.12.295 in /modules/localstack Bumps [aws-java-sdk-sqs](https://github.com/aws/aws-sdk-java) from 1.12.285 to 1.12.295. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.285...1.12.295) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-sqs dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 5f743a21160..2f727f9072a 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -5,7 +5,7 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.295' testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.285' - testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.285' + testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.295' testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.295' testImplementation 'software.amazon.awssdk:s3:2.17.256' testImplementation 'org.assertj:assertj-core:3.23.1' From 4e7fc5a32949458478100284a20efa36dc9336a9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 2 Sep 2022 03:13:23 +0000 Subject: [PATCH 0462/3231] Bump s3 from 2.17.256 to 2.17.266 in /modules/localstack Bumps s3 from 2.17.256 to 2.17.266. --- updated-dependencies: - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 5f743a21160..47d483fbc6e 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -7,6 +7,6 @@ dependencies { testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.285' testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.285' testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.295' - testImplementation 'software.amazon.awssdk:s3:2.17.256' + testImplementation 'software.amazon.awssdk:s3:2.17.266' testImplementation 'org.assertj:assertj-core:3.23.1' } From 2f7fe4070ffd22bb396aae1202864ce4ab449da6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 2 Sep 2022 03:13:35 +0000 Subject: [PATCH 0463/3231] Bump elasticsearch-rest-client in /modules/elasticsearch Bumps [elasticsearch-rest-client](https://github.com/elastic/elasticsearch) from 8.3.3 to 8.4.1. - [Release notes](https://github.com/elastic/elasticsearch/releases) - [Changelog](https://github.com/elastic/elasticsearch/blob/main/CHANGELOG.md) - [Commits](https://github.com/elastic/elasticsearch/compare/v8.3.3...v8.4.1) --- updated-dependencies: - dependency-name: org.elasticsearch.client:elasticsearch-rest-client dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/elasticsearch/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/elasticsearch/build.gradle b/modules/elasticsearch/build.gradle index 8d8125eb1e1..c5591d0d26a 100644 --- a/modules/elasticsearch/build.gradle +++ b/modules/elasticsearch/build.gradle @@ -2,7 +2,7 @@ description = "TestContainers :: elasticsearch" dependencies { api project(':testcontainers') - testImplementation "org.elasticsearch.client:elasticsearch-rest-client:8.3.3" + testImplementation "org.elasticsearch.client:elasticsearch-rest-client:8.4.1" testImplementation "org.elasticsearch.client:transport:7.17.6" testImplementation 'org.assertj:assertj-core:3.23.1' } From d047a1ca3cce7b4f8146abe0faf7d70148b017af Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 2 Sep 2022 03:13:38 +0000 Subject: [PATCH 0464/3231] Bump postgresql from 42.4.2 to 42.5.0 in /modules/spock Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.4.2 to 42.5.0. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.4.2...REL42.5.0) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/spock/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle index 543824ea8f6..a437ff5b502 100644 --- a/modules/spock/build.gradle +++ b/modules/spock/build.gradle @@ -15,7 +15,7 @@ dependencies { testImplementation 'com.zaxxer:HikariCP:4.0.3' testImplementation 'org.apache.httpcomponents:httpclient:4.5.13' - testRuntimeOnly 'org.postgresql:postgresql:42.4.2' + testRuntimeOnly 'org.postgresql:postgresql:42.5.0' testRuntimeOnly 'mysql:mysql-connector-java:8.0.30' testCompileClasspath 'org.jetbrains:annotations:23.0.0' From 463c45999235b93a1f07acf0984bb630edfc2230 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 2 Sep 2022 03:13:41 +0000 Subject: [PATCH 0465/3231] Bump google-cloud-bigtable from 2.10.3 to 2.11.1 in /modules/gcloud Bumps [google-cloud-bigtable](https://github.com/googleapis/java-bigtable) from 2.10.3 to 2.11.1. - [Release notes](https://github.com/googleapis/java-bigtable/releases) - [Changelog](https://github.com/googleapis/java-bigtable/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/java-bigtable/compare/v2.10.3...v2.11.1) --- updated-dependencies: - dependency-name: com.google.cloud:google-cloud-bigtable dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/gcloud/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle index 6028ef7e7c2..c83d92e551a 100644 --- a/modules/gcloud/build.gradle +++ b/modules/gcloud/build.gradle @@ -7,6 +7,6 @@ dependencies { testImplementation 'com.google.cloud:google-cloud-firestore:3.4.2' testImplementation 'com.google.cloud:google-cloud-pubsub:1.120.12' testImplementation 'com.google.cloud:google-cloud-spanner:6.29.0' - testImplementation 'com.google.cloud:google-cloud-bigtable:2.10.3' + testImplementation 'com.google.cloud:google-cloud-bigtable:2.11.1' testImplementation 'org.assertj:assertj-core:3.23.1' } From caea94d6324aab22bacb522d615e5dc728ba85b3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 2 Sep 2022 03:13:42 +0000 Subject: [PATCH 0466/3231] Bump google-cloud-pubsub from 1.120.12 to 1.120.13 in /modules/gcloud Bumps [google-cloud-pubsub](https://github.com/googleapis/java-pubsub) from 1.120.12 to 1.120.13. - [Release notes](https://github.com/googleapis/java-pubsub/releases) - [Changelog](https://github.com/googleapis/java-pubsub/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/java-pubsub/compare/v1.120.12...v1.120.13) --- updated-dependencies: - dependency-name: com.google.cloud:google-cloud-pubsub dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/gcloud/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle index 6028ef7e7c2..cc8187414be 100644 --- a/modules/gcloud/build.gradle +++ b/modules/gcloud/build.gradle @@ -5,7 +5,7 @@ dependencies { testImplementation 'com.google.cloud:google-cloud-datastore:2.11.0' testImplementation 'com.google.cloud:google-cloud-firestore:3.4.2' - testImplementation 'com.google.cloud:google-cloud-pubsub:1.120.12' + testImplementation 'com.google.cloud:google-cloud-pubsub:1.120.13' testImplementation 'com.google.cloud:google-cloud-spanner:6.29.0' testImplementation 'com.google.cloud:google-cloud-bigtable:2.10.3' testImplementation 'org.assertj:assertj-core:3.23.1' From ebc22dd2eae53691470e5e4f16b23e3267ce6332 Mon Sep 17 00:00:00 2001 From: Alex <93376818+sashashura@users.noreply.github.com> Date: Fri, 2 Sep 2022 23:39:03 +0200 Subject: [PATCH 0467/3231] Update update-gradle-wrapper.yml (#5821) --- .github/workflows/update-gradle-wrapper.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/update-gradle-wrapper.yml b/.github/workflows/update-gradle-wrapper.yml index 5855e609fce..c2fe34cfe19 100644 --- a/.github/workflows/update-gradle-wrapper.yml +++ b/.github/workflows/update-gradle-wrapper.yml @@ -4,8 +4,13 @@ on: schedule: - cron: "0 0 * * *" +permissions: + contents: read + jobs: update-gradle-wrapper: + permissions: + pull-requests: write # for gradle-update/update-gradle-wrapper-action if: github.repository == 'testcontainers/testcontainers-java' runs-on: ubuntu-latest From a01d571ee52ca935021baf2c3d8904a596b99de6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Fri, 9 Sep 2022 14:08:38 +0200 Subject: [PATCH 0468/3231] docs: fix link to the bounty program (#5831) --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 83afa8a9ea8..93a87222781 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -10,4 +10,4 @@ Testcontainers is [in the GitHub Sponsors program](https://github.com/sponsors/t This repository is supported by our sponsors, meaning that issues are eligible to have a 'bounty' attached to them by sponsors. -Please see [the bounty policy page](https://testcontainers.org/bounty) if you are interested, either as a sponsor or as a contributor. \ No newline at end of file +Please see [the bounty policy page](https://www.testcontainers.org/bounty) if you are interested, either as a sponsor or as a contributor. From 0d368f72d898260fc2197564e78b2869d0400ca3 Mon Sep 17 00:00:00 2001 From: Neslihan Wittek <34006979+neslihanedes@users.noreply.github.com> Date: Sun, 11 Sep 2022 00:00:11 +0200 Subject: [PATCH 0469/3231] Fix typos on index.md (#5832) --- docs/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/index.md b/docs/index.md index 3a99c88a5b1..8cc789e41a1 100644 --- a/docs/index.md +++ b/docs/index.md @@ -92,7 +92,7 @@ Using Gradle 5.0 or higher, you can add the following to the `dependencies` sect !!! warning "Shaded dependencies" Testcontainers depends on other libraries (like docker-java) for it to work. Some of them (JUnit, docker-java-{api,transport} and its transitive dependencies, JNA, visible-assertions and others) are part of our public API. - But there are also "private", implementation detail dependencies (e.g. docker-java-core, Guava, OkHttp, etc etc) that are not exposed to public API but prone to conflicts with test code/application under test code. + But there are also "private", implementation detail dependencies (e.g., docker-java-core, Guava, OkHttp, etc.) that are not exposed to public API but prone to conflicts with test code/application under test code. As such, **these libraries are 'shaded' into the core testcontainers JAR** and relocated under `org.testcontainers.shaded` to prevent class conflicts. ## Sponsors From 6e46662311591312eebd59b9d7f22cb13479018b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Tue, 13 Sep 2022 10:00:21 -0500 Subject: [PATCH 0470/3231] Improve Issue Templates (#5836) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Be explicit about searching for an existing `bug`, `feature`, `enhancement` and avoid duplications. Co-authored-by: Manuel de la Peña --- .github/ISSUE_TEMPLATE/bug_report.yaml | 2 +- .github/ISSUE_TEMPLATE/enhancement.yaml | 2 +- .github/ISSUE_TEMPLATE/feature.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index 163c5455b1e..9945297ea7a 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -6,7 +6,7 @@ body: - type: markdown attributes: value: | - Thanks for taking the time to fill out this bug report! + Thanks for taking the time to fill out this bug report! Before submitting a `bug`, please make sure there is no existing issue for the one you encountered. - type: dropdown id: module attributes: diff --git a/.github/ISSUE_TEMPLATE/enhancement.yaml b/.github/ISSUE_TEMPLATE/enhancement.yaml index b753332073b..6d5edab909f 100644 --- a/.github/ISSUE_TEMPLATE/enhancement.yaml +++ b/.github/ISSUE_TEMPLATE/enhancement.yaml @@ -6,7 +6,7 @@ body: - type: markdown attributes: value: | - Please provide the following information + Before submitting an `enhancement`, please make sure there is no existing enhancement for the one you are requesting. If so, please provide the following information: - type: dropdown id: module attributes: diff --git a/.github/ISSUE_TEMPLATE/feature.yaml b/.github/ISSUE_TEMPLATE/feature.yaml index 91031adfeee..8abeb7d2131 100644 --- a/.github/ISSUE_TEMPLATE/feature.yaml +++ b/.github/ISSUE_TEMPLATE/feature.yaml @@ -6,7 +6,7 @@ body: - type: markdown attributes: value: | - Please provide the following information + Before submitting a `feature`, please make sure there is no existing feature for the one you are requesting. If so, please provide the following information: - type: dropdown id: module attributes: From 1f3a1f764f5c31a2f5715ed502b417475aacdd98 Mon Sep 17 00:00:00 2001 From: Michael Nitschinger Date: Wed, 14 Sep 2022 15:19:25 +0200 Subject: [PATCH 0471/3231] couchbase: allow to configure bucket replicas and default to 0. (#5840) This change allows the user to configure the number of bucket replicas and defaults it to 0 since most of the time the user ever only needs no replicas since it is a single container. Note: technically this is a behavioral change since before the number of replicas was 1 (if not defined) but this causes issues when testing transactions (as seen in #5835). Fixes #5835 --- .../couchbase/BucketDefinition.java | 30 +++++++++++++++++-- .../couchbase/CouchbaseContainer.java | 1 + 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/modules/couchbase/src/main/java/org/testcontainers/couchbase/BucketDefinition.java b/modules/couchbase/src/main/java/org/testcontainers/couchbase/BucketDefinition.java index ad666b4ffef..c698855927c 100644 --- a/modules/couchbase/src/main/java/org/testcontainers/couchbase/BucketDefinition.java +++ b/modules/couchbase/src/main/java/org/testcontainers/couchbase/BucketDefinition.java @@ -29,10 +29,32 @@ public class BucketDefinition { private int quota = 100; + private int numReplicas = 0; + public BucketDefinition(final String name) { this.name = name; } + /** + * Allows to configure the number of replicas on a bucket (defaults to 0). + *

+ * By default the bucket is initialized with 0 replicas since only a single container is launched. Modifying + * this value can still be useful in some test scenarios (i.e. to test failures with the wrong number of replicas + * and durability requirements on operations). + *

+ * Couchbase buckets can have a maximum of three replicas configured. + * + * @param numReplicas the number of replicas to configure. + * @return this {@link BucketDefinition} for chaining purposes. + */ + public BucketDefinition withReplicas(final int numReplicas) { + if (numReplicas < 0 || numReplicas > 3) { + throw new IllegalArgumentException("The number of replicas must be between 0 and 3 (inclusive)"); + } + this.numReplicas = numReplicas; + return this; + } + /** * Enables flush for this bucket (disabled by default). * @@ -45,9 +67,9 @@ public BucketDefinition withFlushEnabled(final boolean flushEnabled) { } /** - * Sets a custom bucket quota (100MB by default). + * Sets a custom bucket quota (100MiB by default). * - * @param quota the quota to set for the bucket. + * @param quota the quota to set for the bucket in mebibytes. * @return this {@link BucketDefinition} for chaining purposes. */ public BucketDefinition withQuota(final int quota) { @@ -84,4 +106,8 @@ public boolean hasPrimaryIndex() { public int getQuota() { return quota; } + + public int getNumReplicas() { + return numReplicas; + } } diff --git a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java index 8603a0b0787..2817b79d71f 100644 --- a/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java +++ b/modules/couchbase/src/main/java/org/testcontainers/couchbase/CouchbaseContainer.java @@ -573,6 +573,7 @@ private void createBuckets() { .add("name", bucket.getName()) .add("ramQuotaMB", Integer.toString(bucket.getQuota())) .add("flushEnabled", bucket.hasFlushEnabled() ? "1" : "0") + .add("replicaNumber", Integer.toString(bucket.getNumReplicas())) .build(), true ); From 9540652fa46f10f4d4e724787aa5fdf6ca3fbea6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Fri, 16 Sep 2022 09:09:48 -0500 Subject: [PATCH 0472/3231] Update localstack images in tests (#5783) `atlassian/pipelines-awscli` is deprecated and it recommends to use `amazon/aws-cli`. --- .../localstack/LocalstackContainerTest.java | 17 +++++++---------- .../localstack/LocalstackTestImages.java | 2 +- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java b/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java index b879b27f0e6..d44e98723b7 100644 --- a/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java +++ b/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackContainerTest.java @@ -26,7 +26,6 @@ import org.junit.Test; import org.junit.experimental.runners.Enclosed; import org.junit.runner.RunWith; -import org.testcontainers.DockerClientFactory; import org.testcontainers.containers.Container; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.Network; @@ -153,12 +152,7 @@ public void sqsTestOverBridgeNetwork() { String fooQueueUrl = queueResult.getQueueUrl(); assertThat(fooQueueUrl) .as("Created queue has external hostname URL") - .contains( - "http://" + - DockerClientFactory.instance().dockerHostIpAddress() + - ":" + - localstack.getMappedPort(LocalStackContainer.PORT) - ); + .contains("http://" + localstack.getHost() + ":" + localstack.getMappedPort(LocalStackContainer.PORT)); sqs.sendMessage(fooQueueUrl, "test"); final long messageCount = sqs @@ -265,14 +259,17 @@ public static class WithNetwork { LocalstackTestImages.AWS_CLI_IMAGE ) .withNetwork(network) - .withCreateContainerCmdModifier(cmd -> cmd.withEntrypoint("top")) + .withCreateContainerCmdModifier(cmd -> cmd.withEntrypoint("tail")) + .withCommand(" -f /dev/null") .withEnv("AWS_ACCESS_KEY_ID", "accesskey") .withEnv("AWS_SECRET_ACCESS_KEY", "secretkey") .withEnv("AWS_REGION", "eu-west-1"); @Test public void s3TestOverDockerNetwork() throws Exception { - runAwsCliAgainstDockerNetworkContainer("s3api create-bucket --bucket foo"); + runAwsCliAgainstDockerNetworkContainer( + "s3api create-bucket --bucket foo --create-bucket-configuration LocationConstraint=eu-west-1" + ); runAwsCliAgainstDockerNetworkContainer("s3api list-buckets"); runAwsCliAgainstDockerNetworkContainer("s3 ls s3://foo"); } @@ -313,7 +310,7 @@ public void cloudWatchLogsTestOverDockerNetwork() throws Exception { private String runAwsCliAgainstDockerNetworkContainer(String command) throws Exception { final String[] commandParts = String .format( - "/usr/bin/aws --region eu-west-1 %s --endpoint-url http://localstack:%d --no-verify-ssl", + "/usr/local/bin/aws --region eu-west-1 %s --endpoint-url http://localstack:%d --no-verify-ssl", command, LocalStackContainer.PORT ) diff --git a/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackTestImages.java b/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackTestImages.java index 491394bea8a..fe39b56521a 100644 --- a/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackTestImages.java +++ b/modules/localstack/src/test/java/org/testcontainers/containers/localstack/LocalstackTestImages.java @@ -8,5 +8,5 @@ public interface LocalstackTestImages { DockerImageName LOCALSTACK_0_10_IMAGE = LOCALSTACK_IMAGE.withTag("0.10.7"); DockerImageName LOCALSTACK_0_11_IMAGE = LOCALSTACK_IMAGE.withTag("0.11.3"); DockerImageName LOCALSTACK_0_12_IMAGE = LOCALSTACK_IMAGE.withTag("0.12.8"); - DockerImageName AWS_CLI_IMAGE = DockerImageName.parse("atlassian/pipelines-awscli:1.16.302"); + DockerImageName AWS_CLI_IMAGE = DockerImageName.parse("amazon/aws-cli:2.7.27"); } From 9e98addab9cfe076ace39a41c8cfdf4351649756 Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Fri, 16 Sep 2022 16:42:12 +0200 Subject: [PATCH 0473/3231] Update slf4j in test-support to 2.0.0 (#5848) --- test-support/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test-support/build.gradle b/test-support/build.gradle index ecf9a0bc60e..2fae2344a89 100644 --- a/test-support/build.gradle +++ b/test-support/build.gradle @@ -1,5 +1,5 @@ dependencies { implementation 'junit:junit:4.13.2' - implementation 'org.slf4j:slf4j-api:1.7.30' + implementation 'org.slf4j:slf4j-api:2.0.0' testImplementation 'org.assertj:assertj-core:3.23.1' } From 22aa85d24e91a2a380da6d96f21ca03947de9c91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Sat, 17 Sep 2022 12:22:45 -0500 Subject: [PATCH 0474/3231] Remove thundra from ci.yml (#5850) Using thundra breaks the gradle cache in `master` branch. --- .github/workflows/ci.yml | 22 ++-------------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9d7749134ce..e1eceab8702 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,14 +14,6 @@ env: GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GRADLE_ENTERPRISE_CACHE_PASSWORD }} jobs: - thundra_test_initializer: - runs-on: ubuntu-latest - outputs: - thundra_agent_testrun_id: ${{ steps.thundra_test_initializer.outputs.thundra_agent_testrun_id }} - steps: - - uses: actions/checkout@v3 - - id: thundra_test_initializer - uses: thundra-io/thundra-test-init-action@v1 find_gradle_jobs: runs-on: ubuntu-18.04 outputs: @@ -47,7 +39,7 @@ jobs: echo $TASKS echo "::set-output name=matrix::{\"gradle_args\":$TASKS}" check: - needs: [find_gradle_jobs, thundra_test_initializer] + needs: [find_gradle_jobs] strategy: fail-fast: false matrix: ${{ fromJson(needs.find_gradle_jobs.outputs.matrix) }} @@ -66,16 +58,6 @@ jobs: key: ${{ runner.os }}-gradle-home-${{matrix.gradle_args}}_check-${{ hashFiles('**/*.gradle') }} - name: Clear existing docker image cache run: docker image prune -af - - name: Thundra Gradle Test Instrumentation - if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }} - uses: thundra-io/thundra-gradle-test-action@v1 - with: - apikey: ${{ secrets.THUNDRA_API_KEY }} - project_id: ${{ secrets.THUNDRA_PROJECT_ID }} - name: Build and test with Gradle (${{matrix.gradle_args}}) run: | - ./gradlew --no-daemon --continue --scan ${{matrix.gradle_args}} \ - $([[ -f $THUNDRA_GRADLE_INIT_SCRIPT_PATH ]] && echo "--init-script $THUNDRA_GRADLE_INIT_SCRIPT_PATH") - env: - THUNDRA_AGENT_TEST_RUN_ID: ${{ needs.thundra_test_initializer.outputs.thundra_agent_testrun_id }} - THUNDRA_AGENT_REPORT_REST_BASEURL: https://collector.thundra.us/v1 + ./gradlew --no-daemon --continue --scan ${{matrix.gradle_args}} From 459d2f6b8915b9c0cbcd89b24825d412a6739838 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Mon, 19 Sep 2022 04:55:58 -0500 Subject: [PATCH 0475/3231] Use `testCompileOnly` instead of `testCompileClasspath` (#5849) `testCompileClasspath` is deprecated. Also, removing `org.jetbrains:annotations` from `selenium` module. --- core/build.gradle | 2 +- modules/selenium/build.gradle | 1 - .../org/testcontainers/junit/BaseWebDriverContainerTest.java | 2 -- modules/spock/build.gradle | 2 +- 4 files changed, 2 insertions(+), 5 deletions(-) diff --git a/core/build.gradle b/core/build.gradle index c9dd720e2a3..4072c25db37 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -63,7 +63,7 @@ dependencies { api 'junit:junit:4.13.2' api 'org.slf4j:slf4j-api:2.0.0' compileOnly 'org.jetbrains:annotations:23.0.0' - testCompileClasspath 'org.jetbrains:annotations:23.0.0' + testCompileOnly 'org.jetbrains:annotations:23.0.0' api 'org.apache.commons:commons-compress:1.21' api ('org.rnorth.duct-tape:duct-tape:1.0.8') { exclude(group: 'org.jetbrains', module: 'annotations') diff --git a/modules/selenium/build.gradle b/modules/selenium/build.gradle index 38fab63dc0a..3e5a80a2dfa 100644 --- a/modules/selenium/build.gradle +++ b/modules/selenium/build.gradle @@ -13,5 +13,4 @@ dependencies { testImplementation 'org.assertj:assertj-core:3.23.1' compileOnly 'org.jetbrains:annotations:23.0.0' - testCompileClasspath 'org.jetbrains:annotations:23.0.0' } diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/BaseWebDriverContainerTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/BaseWebDriverContainerTest.java index c49e24ba867..0ce20ca9800 100644 --- a/modules/selenium/src/test/java/org/testcontainers/junit/BaseWebDriverContainerTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/junit/BaseWebDriverContainerTest.java @@ -1,6 +1,5 @@ package org.testcontainers.junit; -import org.jetbrains.annotations.NotNull; import org.junit.ClassRule; import org.openqa.selenium.By; import org.openqa.selenium.WebElement; @@ -51,7 +50,6 @@ protected void assertBrowserNameIs(BrowserWebDriverContainer rule, String exp assertThat(actual).as(String.format("actual browser name is %s", actual)).isEqualTo(expectedName); } - @NotNull private static RemoteWebDriver setupDriverFromRule(BrowserWebDriverContainer rule) { RemoteWebDriver driver = rule.getWebDriver(); driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS); diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle index a437ff5b502..a13a7049e13 100644 --- a/modules/spock/build.gradle +++ b/modules/spock/build.gradle @@ -18,7 +18,7 @@ dependencies { testRuntimeOnly 'org.postgresql:postgresql:42.5.0' testRuntimeOnly 'mysql:mysql-connector-java:8.0.30' - testCompileClasspath 'org.jetbrains:annotations:23.0.0' + testCompileOnly 'org.jetbrains:annotations:23.0.0' } sourceJar { From de1a77ed837ab5b5a542dbd51d40e08391edb129 Mon Sep 17 00:00:00 2001 From: Neslihan Wittek <34006979+neslihanedes@users.noreply.github.com> Date: Mon, 26 Sep 2022 12:04:51 +0200 Subject: [PATCH 0476/3231] Improve consistency of Testcontainers name in docs (#5866) --- docs/contributing_docs.md | 2 +- docs/index.md | 2 +- docs/modules/databases/orientdb.md | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/contributing_docs.md b/docs/contributing_docs.md index f6f9304a339..2d706dfe407 100644 --- a/docs/contributing_docs.md +++ b/docs/contributing_docs.md @@ -1,6 +1,6 @@ # Contributing to documentation -The testcontainers-java documentation is a static site built with [MkDocs](https://www.mkdocs.org/). +The Testcontainers-java documentation is a static site built with [MkDocs](https://www.mkdocs.org/). We use the [Material for MkDocs](https://squidfunk.github.io/mkdocs-material/) theme, which offers a number of useful extensions to MkDocs. In addition we use a [custom plugin](https://github.com/rnorth/mkdocs-codeinclude-plugin) for inclusion of code snippets. diff --git a/docs/index.md b/docs/index.md index 8cc789e41a1..8eb4ddeb2c4 100644 --- a/docs/index.md +++ b/docs/index.md @@ -93,7 +93,7 @@ Using Gradle 5.0 or higher, you can add the following to the `dependencies` sect Testcontainers depends on other libraries (like docker-java) for it to work. Some of them (JUnit, docker-java-{api,transport} and its transitive dependencies, JNA, visible-assertions and others) are part of our public API. But there are also "private", implementation detail dependencies (e.g., docker-java-core, Guava, OkHttp, etc.) that are not exposed to public API but prone to conflicts with test code/application under test code. - As such, **these libraries are 'shaded' into the core testcontainers JAR** and relocated under `org.testcontainers.shaded` to prevent class conflicts. + As such, **these libraries are 'shaded' into the core Testcontainers JAR** and relocated under `org.testcontainers.shaded` to prevent class conflicts. ## Sponsors diff --git a/docs/modules/databases/orientdb.md b/docs/modules/databases/orientdb.md index 4beffefa6c2..57839cd99f0 100644 --- a/docs/modules/databases/orientdb.md +++ b/docs/modules/databases/orientdb.md @@ -10,7 +10,7 @@ Note that it's based on the [official Docker image](https://hub.docker.com/_/ori ## Usage example -Declare your Testcontainer as a `@ClassRule` or `@Rule` in a JUnit 4 test or as static or member attribute of a JUnit 5 test annotated with `@Container` as you would with other Testcontainers. +Declare your Testcontainers instance as a `@ClassRule` or `@Rule` in a JUnit 4 test or as static or member attribute of a JUnit 5 test annotated with `@Container` as you would with other Testcontainers. You can call `getDbUrl()` OrientDB container and build the `ODatabaseSession` by your own, but a more useful `getSession()` method is provided. On the JVM you would most likely use the [Java driver](https://github.com/). @@ -39,7 +39,7 @@ The following example uses the JUnit 5 extension `@Testcontainers` and demonstra } ``` -You are not limited to Unit tests and can of course use an instance of the OrientDB Testcontainer in vanilla Java code as well. +You are not limited to Unit tests and can of course use an instance of the OrientDB Testcontainers implementation in vanilla Java code as well. ## Adding this module to your project dependencies From 9847d5930bb0faea9d14f606c583919d4e2d2113 Mon Sep 17 00:00:00 2001 From: Aidan Do <43259657+REslim30@users.noreply.github.com> Date: Mon, 26 Sep 2022 21:20:45 +0930 Subject: [PATCH 0477/3231] Fix: ContainerDatabaseDriver does not register Properties object (#5829) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Eddú Meléndez Gonzales --- .../containers/JdbcDatabaseContainer.java | 24 +++++++--- .../jdbc/ContainerDatabaseDriver.java | 2 +- .../MySQLDatabaseContainerDriverTest.java | 44 +++++++++++++++++++ 3 files changed, 64 insertions(+), 6 deletions(-) create mode 100644 modules/mysql/src/test/java/org/testcontainers/jdbc/mysql/MySQLDatabaseContainerDriverTest.java diff --git a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java index aa397929497..88c418e73cb 100644 --- a/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java +++ b/modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java @@ -216,9 +216,23 @@ public Driver getJdbcDriverInstance() throws NoDriverFoundException { * @throws SQLException if there is a repeated failure to create the connection */ public Connection createConnection(String queryString) throws SQLException, NoDriverFoundException { - final Properties info = new Properties(); - info.put("user", this.getUsername()); - info.put("password", this.getPassword()); + return createConnection(queryString, new Properties()); + } + + /** + * Creates a connection to the underlying containerized database instance. + * + * @param queryString query string parameters that should be appended to the JDBC connection URL. + * The '?' character must be included + * @param info additional properties to be passed to the JDBC driver + * @return a Connection + * @throws SQLException if there is a repeated failure to create the connection + */ + public Connection createConnection(String queryString, Properties info) + throws SQLException, NoDriverFoundException { + Properties properties = new Properties(info); + properties.put("user", this.getUsername()); + properties.put("password", this.getPassword()); final String url = constructUrlForConnection(queryString); final Driver jdbcDriverInstance = getJdbcDriverInstance(); @@ -234,10 +248,10 @@ public Connection createConnection(String queryString) throws SQLException, NoDr "Trying to create JDBC connection using {} to {} with properties: {}", jdbcDriverInstance.getClass().getName(), url, - info + properties ); - return jdbcDriverInstance.connect(url, info); + return jdbcDriverInstance.connect(url, properties); } catch (SQLException e) { lastException = e; Thread.sleep(100L); diff --git a/modules/jdbc/src/main/java/org/testcontainers/jdbc/ContainerDatabaseDriver.java b/modules/jdbc/src/main/java/org/testcontainers/jdbc/ContainerDatabaseDriver.java index 23ed9d78b25..a3fed34f24c 100644 --- a/modules/jdbc/src/main/java/org/testcontainers/jdbc/ContainerDatabaseDriver.java +++ b/modules/jdbc/src/main/java/org/testcontainers/jdbc/ContainerDatabaseDriver.java @@ -127,7 +127,7 @@ public synchronized Connection connect(String url, final Properties info) throws /* Create a connection using the delegated driver. The container must be ready to accept connections. */ - Connection connection = container.createConnection(queryString); + Connection connection = container.createConnection(queryString, info); /* If this container has not been initialized, AND diff --git a/modules/mysql/src/test/java/org/testcontainers/jdbc/mysql/MySQLDatabaseContainerDriverTest.java b/modules/mysql/src/test/java/org/testcontainers/jdbc/mysql/MySQLDatabaseContainerDriverTest.java new file mode 100644 index 00000000000..7155e49dd48 --- /dev/null +++ b/modules/mysql/src/test/java/org/testcontainers/jdbc/mysql/MySQLDatabaseContainerDriverTest.java @@ -0,0 +1,44 @@ +package org.testcontainers.jdbc.mysql; + +import org.junit.Test; +import org.testcontainers.jdbc.ContainerDatabaseDriver; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.Properties; + +import static org.assertj.core.api.Assertions.assertThat; + +public class MySQLDatabaseContainerDriverTest { + + @Test + public void shouldRespectBothUrlPropertiesAndParameterProperties() throws SQLException { + ContainerDatabaseDriver driver = new ContainerDatabaseDriver(); + String url = "jdbc:tc:mysql:5.7.22://hostname/databasename?padCharsWithSpace=true"; + Properties properties = new Properties(); + properties.setProperty("maxRows", "1"); + + try (Connection connection = driver.connect(url, properties)) { + try (Statement statement = connection.createStatement()) { + statement.execute("CREATE TABLE arbitrary_table (length_5_string CHAR(5))"); + statement.execute("INSERT INTO arbitrary_table VALUES ('abc')"); + statement.execute("INSERT INTO arbitrary_table VALUES ('123')"); + + // Check that maxRows is set + try (ResultSet resultSet = statement.executeQuery("SELECT * FROM arbitrary_table")) { + resultSet.next(); + assertThat(resultSet.isFirst()).isTrue(); + assertThat(resultSet.isLast()).isTrue(); + } + + // Check that pad with chars is set + try (ResultSet resultSet = statement.executeQuery("SELECT * FROM arbitrary_table")) { + assertThat(resultSet.next()).isTrue(); + assertThat(resultSet.getString(1)).isEqualTo("abc "); + } + } + } + } +} From f54a29a48419b4fb14ef1fc024a656e918d994ae Mon Sep 17 00:00:00 2001 From: Aidan Do <43259657+REslim30@users.noreply.github.com> Date: Wed, 28 Sep 2022 19:40:08 +0930 Subject: [PATCH 0478/3231] `getLivenessCheckPortNumbers()` should return mapped port (#5734) Co-authored-by: Kevin Wittek --- .../org/testcontainers/containers/Db2Container.java | 8 ++++++-- .../org/testcontainers/junit/db2/SimpleDb2Test.java | 6 ++++++ .../containers/MSSQLServerContainer.java | 3 +-- .../junit/mssqlserver/SimpleMSSQLServerTest.java | 7 +++++++ .../org/testcontainers/containers/MySQLContainer.java | 8 ++++++-- .../testcontainers/junit/mysql/SimpleMySQLTest.java | 6 ++++++ .../org/testcontainers/containers/NginxContainer.java | 8 ++++++-- .../java/org/testcontainers/junit/SimpleNginxTest.java | 6 ++++++ .../testcontainers/containers/PostgreSQLContainer.java | 8 ++++++-- .../junit/postgresql/SimplePostgreSQLTest.java | 7 +++++++ .../org/testcontainers/containers/PrestoContainer.java | 8 ++++++-- .../testcontainers/containers/PrestoContainerTest.java | 7 +++++++ .../java/org/testcontainers/tidb/TiDBContainer.java | 8 ++++++-- .../org/testcontainers/junit/tidb/SimpleTiDBTest.java | 10 ++++++++++ .../org/testcontainers/containers/TrinoContainer.java | 8 ++++++-- .../testcontainers/containers/TrinoContainerTest.java | 6 ++++++ 16 files changed, 98 insertions(+), 16 deletions(-) diff --git a/modules/db2/src/main/java/org/testcontainers/containers/Db2Container.java b/modules/db2/src/main/java/org/testcontainers/containers/Db2Container.java index 29e2ce8aa07..333a2e709c3 100644 --- a/modules/db2/src/main/java/org/testcontainers/containers/Db2Container.java +++ b/modules/db2/src/main/java/org/testcontainers/containers/Db2Container.java @@ -6,7 +6,6 @@ import java.time.Duration; import java.time.temporal.ChronoUnit; -import java.util.HashSet; import java.util.Set; public class Db2Container extends JdbcDatabaseContainer { @@ -54,9 +53,14 @@ public Db2Container(final DockerImageName dockerImageName) { addExposedPort(DB2_PORT); } + /** + * @return the ports on which to check if the container is ready + * @deprecated use {@link #getLivenessCheckPortNumbers()} instead + */ @Override + @Deprecated protected Set getLivenessCheckPorts() { - return new HashSet<>(getMappedPort(DB2_PORT)); + return super.getLivenessCheckPorts(); } @Override diff --git a/modules/db2/src/test/java/org/testcontainers/junit/db2/SimpleDb2Test.java b/modules/db2/src/test/java/org/testcontainers/junit/db2/SimpleDb2Test.java index 2c2c4cb2f09..ae35a8ee77c 100644 --- a/modules/db2/src/test/java/org/testcontainers/junit/db2/SimpleDb2Test.java +++ b/modules/db2/src/test/java/org/testcontainers/junit/db2/SimpleDb2Test.java @@ -21,6 +21,7 @@ public void testSimple() throws SQLException { int resultSetInt = resultSet.getInt(1); assertThat(resultSetInt).as("A basic SELECT query succeeds").isEqualTo(1); + assertHasCorrectExposedAndLivenessCheckPorts(db2); } } @@ -37,4 +38,9 @@ public void testWithAdditionalUrlParamInJdbcUrl() { assertThat(jdbcUrl).contains(":sslConnection=false;"); } } + + private void assertHasCorrectExposedAndLivenessCheckPorts(Db2Container db2) { + assertThat(db2.getExposedPorts()).containsExactly(Db2Container.DB2_PORT); + assertThat(db2.getLivenessCheckPortNumbers()).containsExactly(db2.getMappedPort(Db2Container.DB2_PORT)); + } } diff --git a/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainer.java b/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainer.java index 532316fb967..8ab3c1113a2 100644 --- a/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainer.java +++ b/modules/mssqlserver/src/main/java/org/testcontainers/containers/MSSQLServerContainer.java @@ -1,6 +1,5 @@ package org.testcontainers.containers; -import com.google.common.collect.Sets; import org.testcontainers.utility.DockerImageName; import org.testcontainers.utility.LicenseAcceptance; @@ -64,7 +63,7 @@ public MSSQLServerContainer(final DockerImageName dockerImageName) { @Override public Set getLivenessCheckPortNumbers() { - return Sets.newHashSet(MS_SQL_SERVER_PORT); + return super.getLivenessCheckPortNumbers(); } @Override diff --git a/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/SimpleMSSQLServerTest.java b/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/SimpleMSSQLServerTest.java index 15edbaa2ca1..2827544cade 100644 --- a/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/SimpleMSSQLServerTest.java +++ b/modules/mssqlserver/src/test/java/org/testcontainers/junit/mssqlserver/SimpleMSSQLServerTest.java @@ -25,6 +25,7 @@ public void testSimple() throws SQLException { int resultSetInt = resultSet.getInt(1); assertThat(resultSetInt).as("A basic SELECT query succeeds").isEqualTo(1); + assertHasCorrectExposedAndLivenessCheckPorts(mssqlServer); } } @@ -64,4 +65,10 @@ public void testSetupDatabase() throws SQLException { assertThat(resultSetInt).as("A basic SELECT query succeeds").isEqualTo(3); } } + + private void assertHasCorrectExposedAndLivenessCheckPorts(MSSQLServerContainer mssqlServer) { + assertThat(mssqlServer.getExposedPorts()).containsExactly(MSSQLServerContainer.MS_SQL_SERVER_PORT); + assertThat(mssqlServer.getLivenessCheckPortNumbers()) + .containsExactly(mssqlServer.getMappedPort(MSSQLServerContainer.MS_SQL_SERVER_PORT)); + } } diff --git a/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java b/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java index 14206018954..316e709995c 100644 --- a/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java +++ b/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java @@ -3,7 +3,6 @@ import org.jetbrains.annotations.NotNull; import org.testcontainers.utility.DockerImageName; -import java.util.HashSet; import java.util.Set; /** @@ -56,10 +55,15 @@ public MySQLContainer(final DockerImageName dockerImageName) { addExposedPort(MYSQL_PORT); } + /** + * @return the ports on which to check if the container is ready + * @deprecated use {@link #getLivenessCheckPortNumbers()} instead + */ @NotNull @Override + @Deprecated protected Set getLivenessCheckPorts() { - return new HashSet<>(getMappedPort(MYSQL_PORT)); + return super.getLivenessCheckPorts(); } @Override diff --git a/modules/mysql/src/test/java/org/testcontainers/junit/mysql/SimpleMySQLTest.java b/modules/mysql/src/test/java/org/testcontainers/junit/mysql/SimpleMySQLTest.java index 146010058ac..f60712e7123 100644 --- a/modules/mysql/src/test/java/org/testcontainers/junit/mysql/SimpleMySQLTest.java +++ b/modules/mysql/src/test/java/org/testcontainers/junit/mysql/SimpleMySQLTest.java @@ -57,6 +57,7 @@ public void testSimple() throws SQLException { int resultSetInt = resultSet.getInt(1); assertThat(resultSetInt).as("A basic SELECT query succeeds").isEqualTo(1); + assertHasCorrectExposedAndLivenessCheckPorts(mysql); } } @@ -237,4 +238,9 @@ public void testWithAdditionalUrlParamInJdbcUrl() { mysql.stop(); } } + + private void assertHasCorrectExposedAndLivenessCheckPorts(MySQLContainer mysql) { + assertThat(mysql.getExposedPorts()).containsExactly(MySQLContainer.MYSQL_PORT); + assertThat(mysql.getLivenessCheckPortNumbers()).containsExactly(mysql.getMappedPort(MySQLContainer.MYSQL_PORT)); + } } diff --git a/modules/nginx/src/main/java/org/testcontainers/containers/NginxContainer.java b/modules/nginx/src/main/java/org/testcontainers/containers/NginxContainer.java index 3e7a2062fd0..b97992a5684 100644 --- a/modules/nginx/src/main/java/org/testcontainers/containers/NginxContainer.java +++ b/modules/nginx/src/main/java/org/testcontainers/containers/NginxContainer.java @@ -6,7 +6,6 @@ import java.net.MalformedURLException; import java.net.URL; -import java.util.Collections; import java.util.Set; /** @@ -42,10 +41,15 @@ public NginxContainer(final DockerImageName dockerImageName) { setCommand("nginx", "-g", "daemon off;"); } + /** + * @return the ports on which to check if the container is ready + * @deprecated use {@link #getLivenessCheckPortNumbers()} instead + */ @NotNull @Override + @Deprecated protected Set getLivenessCheckPorts() { - return Collections.singleton(getMappedPort(80)); + return super.getLivenessCheckPorts(); } public URL getBaseUrl(String scheme, int port) throws MalformedURLException { diff --git a/modules/nginx/src/test/java/org/testcontainers/junit/SimpleNginxTest.java b/modules/nginx/src/test/java/org/testcontainers/junit/SimpleNginxTest.java index 24e913cf281..abfbb663207 100644 --- a/modules/nginx/src/test/java/org/testcontainers/junit/SimpleNginxTest.java +++ b/modules/nginx/src/test/java/org/testcontainers/junit/SimpleNginxTest.java @@ -58,6 +58,12 @@ public void testSimple() throws Exception { .as("An HTTP GET from the Nginx server returns the index.html from the custom content directory") .contains("Hello World!"); // } + assertHasCorrectExposedAndLivenessCheckPorts(nginx); + } + + private void assertHasCorrectExposedAndLivenessCheckPorts(NginxContainer nginxContainer) throws Exception { + assertThat(nginxContainer.getExposedPorts()).containsExactly(80); + assertThat(nginxContainer.getLivenessCheckPortNumbers()).containsExactly(nginxContainer.getMappedPort(80)); } private static String responseFromNginx(URL baseUrl) throws IOException { diff --git a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java index 3b36b61ea77..d3ff65ee351 100644 --- a/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java +++ b/modules/postgresql/src/main/java/org/testcontainers/containers/PostgreSQLContainer.java @@ -6,7 +6,6 @@ import java.time.Duration; import java.time.temporal.ChronoUnit; -import java.util.Collections; import java.util.Set; /** @@ -62,10 +61,15 @@ public PostgreSQLContainer(final DockerImageName dockerImageName) { addExposedPort(POSTGRESQL_PORT); } + /** + * @return the ports on which to check if the container is ready + * @deprecated use {@link #getLivenessCheckPortNumbers()} instead + */ @NotNull @Override + @Deprecated protected Set getLivenessCheckPorts() { - return Collections.singleton(getMappedPort(POSTGRESQL_PORT)); + return super.getLivenessCheckPorts(); } @Override diff --git a/modules/postgresql/src/test/java/org/testcontainers/junit/postgresql/SimplePostgreSQLTest.java b/modules/postgresql/src/test/java/org/testcontainers/junit/postgresql/SimplePostgreSQLTest.java index 2a7db242421..84beb779fa4 100644 --- a/modules/postgresql/src/test/java/org/testcontainers/junit/postgresql/SimplePostgreSQLTest.java +++ b/modules/postgresql/src/test/java/org/testcontainers/junit/postgresql/SimplePostgreSQLTest.java @@ -26,6 +26,7 @@ public void testSimple() throws SQLException { ResultSet resultSet = performQuery(postgres, "SELECT 1"); int resultSetInt = resultSet.getInt(1); assertThat(resultSetInt).as("A basic SELECT query succeeds").isEqualTo(1); + assertHasCorrectExposedAndLivenessCheckPorts(postgres); } } @@ -86,4 +87,10 @@ public void testWithAdditionalUrlParamInJdbcUrl() { assertThat(jdbcUrl).contains("charSet=UNICODE"); } } + + private void assertHasCorrectExposedAndLivenessCheckPorts(PostgreSQLContainer postgres) { + assertThat(postgres.getExposedPorts()).containsExactly(PostgreSQLContainer.POSTGRESQL_PORT); + assertThat(postgres.getLivenessCheckPortNumbers()) + .containsExactly(postgres.getMappedPort(PostgreSQLContainer.POSTGRESQL_PORT)); + } } diff --git a/modules/presto/src/main/java/org/testcontainers/containers/PrestoContainer.java b/modules/presto/src/main/java/org/testcontainers/containers/PrestoContainer.java index e73f6cc0f6b..5c89682643a 100644 --- a/modules/presto/src/main/java/org/testcontainers/containers/PrestoContainer.java +++ b/modules/presto/src/main/java/org/testcontainers/containers/PrestoContainer.java @@ -9,7 +9,6 @@ import java.sql.SQLException; import java.time.Duration; import java.time.temporal.ChronoUnit; -import java.util.HashSet; import java.util.Set; /** @@ -56,10 +55,15 @@ public PrestoContainer(final DockerImageName dockerImageName) { addExposedPort(PRESTO_PORT); } + /** + * @return the ports on which to check if the container is ready + * @deprecated use {@link #getLivenessCheckPortNumbers()} instead + */ @NotNull @Override + @Deprecated protected Set getLivenessCheckPorts() { - return new HashSet<>(getMappedPort(PRESTO_PORT)); + return super.getLivenessCheckPorts(); } @Override diff --git a/modules/presto/src/test/java/org/testcontainers/containers/PrestoContainerTest.java b/modules/presto/src/test/java/org/testcontainers/containers/PrestoContainerTest.java index 721186807aa..0e86b587c50 100644 --- a/modules/presto/src/test/java/org/testcontainers/containers/PrestoContainerTest.java +++ b/modules/presto/src/test/java/org/testcontainers/containers/PrestoContainerTest.java @@ -32,6 +32,7 @@ public void testSimple() throws Exception { assertThat(resultSet.getString("node_version")) .as("Presto version") .isEqualTo(PrestoContainer.DEFAULT_TAG); + assertHasCorrectExposedAndLivenessCheckPorts(prestoSql); } } } @@ -149,4 +150,10 @@ public void testTcJdbcUri() throws Exception { .isEqualTo(Connection.TRANSACTION_READ_UNCOMMITTED); } } + + private void assertHasCorrectExposedAndLivenessCheckPorts(PrestoContainer prestoSql) { + assertThat(prestoSql.getExposedPorts()).containsExactly(PrestoContainer.PRESTO_PORT); + assertThat(prestoSql.getLivenessCheckPortNumbers()) + .containsExactly(prestoSql.getMappedPort(PrestoContainer.PRESTO_PORT)); + } } diff --git a/modules/tidb/src/main/java/org/testcontainers/tidb/TiDBContainer.java b/modules/tidb/src/main/java/org/testcontainers/tidb/TiDBContainer.java index f8030fd2422..c78590d8393 100644 --- a/modules/tidb/src/main/java/org/testcontainers/tidb/TiDBContainer.java +++ b/modules/tidb/src/main/java/org/testcontainers/tidb/TiDBContainer.java @@ -6,7 +6,6 @@ import org.testcontainers.utility.DockerImageName; import java.time.Duration; -import java.util.HashSet; import java.util.Set; /** @@ -51,10 +50,15 @@ public TiDBContainer(final DockerImageName dockerImageName) { ); } + /** + * @return the ports on which to check if the container is ready + * @deprecated use {@link #getLivenessCheckPortNumbers()} instead + */ @NotNull @Override + @Deprecated protected Set getLivenessCheckPorts() { - return new HashSet<>(getMappedPort(TIDB_PORT)); + return super.getLivenessCheckPorts(); } @Override diff --git a/modules/tidb/src/test/java/org/testcontainers/junit/tidb/SimpleTiDBTest.java b/modules/tidb/src/test/java/org/testcontainers/junit/tidb/SimpleTiDBTest.java index 714c9854f39..6b4429afe75 100644 --- a/modules/tidb/src/test/java/org/testcontainers/junit/tidb/SimpleTiDBTest.java +++ b/modules/tidb/src/test/java/org/testcontainers/junit/tidb/SimpleTiDBTest.java @@ -21,6 +21,7 @@ public void testSimple() throws SQLException { int resultSetInt = resultSet.getInt(1); assertThat(resultSetInt).isEqualTo(1); + assertHasCorrectExposedAndLivenessCheckPorts(tidb); } } @@ -51,4 +52,13 @@ public void testWithAdditionalUrlParamInJdbcUrl() { tidb.stop(); } } + + private void assertHasCorrectExposedAndLivenessCheckPorts(TiDBContainer tidb) { + Integer tidbPort = 4000; + Integer restApiPort = 10080; + + assertThat(tidb.getExposedPorts()).containsExactlyInAnyOrder(tidbPort, restApiPort); + assertThat(tidb.getLivenessCheckPortNumbers()) + .containsExactlyInAnyOrder(tidb.getMappedPort(tidbPort), tidb.getMappedPort(restApiPort)); + } } diff --git a/modules/trino/src/main/java/org/testcontainers/containers/TrinoContainer.java b/modules/trino/src/main/java/org/testcontainers/containers/TrinoContainer.java index 9ed18e96d28..21171fd2c15 100644 --- a/modules/trino/src/main/java/org/testcontainers/containers/TrinoContainer.java +++ b/modules/trino/src/main/java/org/testcontainers/containers/TrinoContainer.java @@ -7,7 +7,6 @@ import java.sql.Connection; import java.sql.SQLException; -import java.util.HashSet; import java.util.Set; public class TrinoContainer extends JdbcDatabaseContainer { @@ -37,10 +36,15 @@ public TrinoContainer(final DockerImageName dockerImageName) { addExposedPort(TRINO_PORT); } + /** + * @return the ports on which to check if the container is ready + * @deprecated use {@link #getLivenessCheckPortNumbers()} instead + */ @NotNull @Override + @Deprecated protected Set getLivenessCheckPorts() { - return new HashSet<>(getMappedPort(TRINO_PORT)); + return super.getLivenessCheckPorts(); } @Override diff --git a/modules/trino/src/test/java/org/testcontainers/containers/TrinoContainerTest.java b/modules/trino/src/test/java/org/testcontainers/containers/TrinoContainerTest.java index a58944946f0..6ef733ca972 100644 --- a/modules/trino/src/test/java/org/testcontainers/containers/TrinoContainerTest.java +++ b/modules/trino/src/test/java/org/testcontainers/containers/TrinoContainerTest.java @@ -24,6 +24,7 @@ public void testSimple() throws Exception { assertThat(resultSet.getString("node_version")) .as("Trino version") .isEqualTo(TrinoContainer.DEFAULT_TAG); + assertContainerHasCorrectExposedAndLivenessCheckPorts(trino); } } } @@ -61,4 +62,9 @@ public void testInitScript() throws Exception { } } } + + private void assertContainerHasCorrectExposedAndLivenessCheckPorts(TrinoContainer trino) { + assertThat(trino.getExposedPorts()).containsExactly(8080); + assertThat(trino.getLivenessCheckPortNumbers()).containsExactly(trino.getMappedPort(8080)); + } } From 405ddb7e39443993f8988c4c5782727f0d0b5cc5 Mon Sep 17 00:00:00 2001 From: Dave Maughan Date: Thu, 29 Sep 2022 09:53:46 +0100 Subject: [PATCH 0479/3231] Allow Pulsar default WaitStrategy to honour startup timeout (#5674) --- .../containers/PulsarContainer.java | 22 ++++++++----------- .../containers/PulsarContainerTest.java | 9 ++++++++ 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java b/modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java index 10dc35dc4c8..950149e3226 100644 --- a/modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java +++ b/modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java @@ -2,12 +2,8 @@ import org.testcontainers.containers.wait.strategy.Wait; import org.testcontainers.containers.wait.strategy.WaitAllStrategy; -import org.testcontainers.containers.wait.strategy.WaitStrategy; import org.testcontainers.utility.DockerImageName; -import java.util.ArrayList; -import java.util.List; - /** * This container wraps Apache Pulsar running in standalone mode */ @@ -36,6 +32,8 @@ public class PulsarContainer extends GenericContainer { @Deprecated private static final String DEFAULT_TAG = "2.10.0"; + private final WaitAllStrategy waitAllStrategy = new WaitAllStrategy(); + private boolean functionsWorkerEnabled = false; private boolean transactionsEnabled = false; @@ -60,6 +58,7 @@ public PulsarContainer(final DockerImageName dockerImageName) { super(dockerImageName); dockerImageName.assertCompatibleWith(DockerImageName.parse("apachepulsar/pulsar")); withExposedPorts(BROKER_PORT, BROKER_HTTP_PORT); + setWaitStrategy(waitAllStrategy); } @Override @@ -98,21 +97,18 @@ protected void setupCommandAndEnv() { final String clusterName = getEnvMap().getOrDefault("PULSAR_PREFIX_clusterName", "standalone"); final String response = String.format("[\"%s\"]", clusterName); - - List waitStrategies = new ArrayList<>(); - waitStrategies.add(Wait.defaultWaitStrategy()); - waitStrategies.add( + waitAllStrategy.withStrategy( Wait.forHttp(ADMIN_CLUSTERS_ENDPOINT).forPort(BROKER_HTTP_PORT).forResponsePredicate(response::equals) ); + if (transactionsEnabled) { withEnv("PULSAR_PREFIX_transactionCoordinatorEnabled", "true"); - waitStrategies.add(Wait.forHttp(TRANSACTION_TOPIC_ENDPOINT).forStatusCode(200).forPort(BROKER_HTTP_PORT)); + waitAllStrategy.withStrategy( + Wait.forHttp(TRANSACTION_TOPIC_ENDPOINT).forStatusCode(200).forPort(BROKER_HTTP_PORT) + ); } if (functionsWorkerEnabled) { - waitStrategies.add(Wait.forLogMessage(".*Function worker service started.*", 1)); + waitAllStrategy.withStrategy(Wait.forLogMessage(".*Function worker service started.*", 1)); } - final WaitAllStrategy compoundedWaitStrategy = new WaitAllStrategy(); - waitStrategies.forEach(compoundedWaitStrategy::withStrategy); - waitingFor(compoundedWaitStrategy); } } diff --git a/modules/pulsar/src/test/java/org/testcontainers/containers/PulsarContainerTest.java b/modules/pulsar/src/test/java/org/testcontainers/containers/PulsarContainerTest.java index bf9f3d2201f..b054c30d0ac 100644 --- a/modules/pulsar/src/test/java/org/testcontainers/containers/PulsarContainerTest.java +++ b/modules/pulsar/src/test/java/org/testcontainers/containers/PulsarContainerTest.java @@ -12,6 +12,7 @@ import org.junit.Test; import org.testcontainers.utility.DockerImageName; +import java.time.Duration; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; @@ -144,6 +145,14 @@ public void testClusterFullyInitialized() throws Exception { } } + @Test + public void testStartupTimeoutIsHonored() { + try (PulsarContainer pulsar = new PulsarContainer(PULSAR_IMAGE).withStartupTimeout(Duration.ZERO)) { + assertThatThrownBy(pulsar::start) + .hasRootCauseMessage("Precondition failed: timeout must be greater than zero"); + } + } + protected void testPulsarFunctionality(String pulsarBrokerUrl) throws Exception { try ( PulsarClient client = PulsarClient.builder().serviceUrl(pulsarBrokerUrl).build(); From 2215e219054ee034583c27f3631154d7ec1b908e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Thu, 29 Sep 2022 09:21:07 -0500 Subject: [PATCH 0480/3231] Add Testcontainers icon for JetBrains IDEs (#5870) --- .gitignore | 3 ++- .idea/icon.png | Bin 0 -> 5740 bytes 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 .idea/icon.png diff --git a/.gitignore b/.gitignore index 95fc6e52000..120181d6e1e 100644 --- a/.gitignore +++ b/.gitignore @@ -9,7 +9,8 @@ dependency-reduced-pom.xml *.iml ## Directory-based project format: -.idea/ +.idea/* +!.idea/icon.png # if you remove the above rule, at least ignore the following: # User-specific stuff: diff --git a/.idea/icon.png b/.idea/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..0f1d8ff443473bd28bed2d1a6a256a9cd76fd7fb GIT binary patch literal 5740 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 DUoGH2 literal 0 HcmV?d00001 From c28fbe029e843b69f9d260ae2d2a0bee01257cc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez?= Date: Thu, 29 Sep 2022 10:16:33 -0500 Subject: [PATCH 0481/3231] Remove thundra badges --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index b1c0014c236..d2fce2e9911 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,5 @@ # Testcontainers -[![Thundra Foresight](https://thundra-assets-prod.s3.us-west-2.amazonaws.com/images/badges/thundra-foresight-badge-enabled.svg)](https://foresight.thundra.live/testRuns/5a49cc30-f063-11eb-9a03-0242ac130003) -[![Thundra Foresight Test Results](https://foresight.service.thundra.us/public/api/v1/badge/test/5a49cc30-f063-11eb-9a03-0242ac130003)](https://foresight.thundra.live/testRuns/5a49cc30-f063-11eb-9a03-0242ac130003) [![Revved up by Gradle Enterprise](https://img.shields.io/badge/Revved%20up%20by-Gradle%20Enterprise-06A0CE?logo=Gradle&labelColor=02303A)](https://ge.testcontainers.org/scans) > Testcontainers is a Java library that supports JUnit tests, providing lightweight, throwaway instances of common databases, Selenium web browsers, or anything else that can run in a Docker container. From b6abcef31196682b48cebb26739d607ca9e39ec5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez?= Date: Thu, 29 Sep 2022 10:18:54 -0500 Subject: [PATCH 0482/3231] Update latest version in mkdocs.yml --- mkdocs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mkdocs.yml b/mkdocs.yml index 87b19de4f0a..ab57aeb11d6 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -108,4 +108,4 @@ nav: - contributing_docs.md - bounty.md extra: - latest_version: 1.17.3 + latest_version: 1.17.4 From 08f75a5217f601439ef426b059908164a02deba4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez?= Date: Thu, 29 Sep 2022 10:21:11 -0500 Subject: [PATCH 0483/3231] Upgrade testcontainers version to 1.17.4 in japicmp --- gradle/japicmp.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/japicmp.gradle b/gradle/japicmp.gradle index 541736a8321..6d769bf9286 100644 --- a/gradle/japicmp.gradle +++ b/gradle/japicmp.gradle @@ -3,7 +3,7 @@ configurations { } dependencies { - baseline "org.testcontainers:${project.name}:1.17.3", { + baseline "org.testcontainers:${project.name}:1.17.4", { exclude group: "*", module: "*" } } From ce003ce6c42b423823501503e77d809508c1b502 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 22:06:18 +0000 Subject: [PATCH 0484/3231] Bump aws-java-sdk-dynamodb in /modules/dynalite Bumps [aws-java-sdk-dynamodb](https://github.com/aws/aws-sdk-java) from 1.12.295 to 1.12.313. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.295...1.12.313) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-dynamodb dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/dynalite/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/dynalite/build.gradle b/modules/dynalite/build.gradle index ac9ff346bea..e9cd2111f82 100644 --- a/modules/dynalite/build.gradle +++ b/modules/dynalite/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Dynalite" dependencies { api project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.12.295' + compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.12.313' testImplementation 'com.amazonaws:aws-java-sdk-dynamodb:1.12.295' testImplementation 'org.assertj:assertj-core:3.23.1' } From d48fef2fab6f82d6707e18fbaaf591e7de606f99 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 22:06:18 +0000 Subject: [PATCH 0485/3231] Bump snakeyaml from 1.31 to 1.33 in /core Bumps [snakeyaml](https://bitbucket.org/snakeyaml/snakeyaml) from 1.31 to 1.33. - [Commits](https://bitbucket.org/snakeyaml/snakeyaml/branches/compare/snakeyaml-1.33..snakeyaml-1.31) --- updated-dependencies: - dependency-name: org.yaml:snakeyaml dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 4072c25db37..08ca6d777c5 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -80,7 +80,7 @@ dependencies { api 'com.github.docker-java:docker-java-transport-zerodep' - shaded "org.yaml:snakeyaml:1.31" + shaded "org.yaml:snakeyaml:1.33" shaded 'org.glassfish.main.external:trilead-ssh2-repackaged:4.1.2' From 052b44a76179a3760b6e2ec2b0a30c17d7e22b4e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 22:06:23 +0000 Subject: [PATCH 0486/3231] Bump java-driver-core from 4.14.1 to 4.15.0 in /modules/cassandra Bumps [java-driver-core](https://github.com/datastax/java-driver) from 4.14.1 to 4.15.0. - [Release notes](https://github.com/datastax/java-driver/releases) - [Commits](https://github.com/datastax/java-driver/compare/4.14.1...4.15.0) --- updated-dependencies: - dependency-name: com.datastax.oss:java-driver-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/cassandra/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/cassandra/build.gradle b/modules/cassandra/build.gradle index ff110637ced..ae115c92a71 100644 --- a/modules/cassandra/build.gradle +++ b/modules/cassandra/build.gradle @@ -10,6 +10,6 @@ dependencies { api project(":database-commons") api "com.datastax.cassandra:cassandra-driver-core:3.10.0" - testImplementation 'com.datastax.oss:java-driver-core:4.14.1' + testImplementation 'com.datastax.oss:java-driver-core:4.15.0' testImplementation 'org.assertj:assertj-core:3.23.1' } From dcf8df081a3077d3300dfdd2f7998e6b6de52e11 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 22:06:27 +0000 Subject: [PATCH 0487/3231] Bump junit-jupiter-params from 5.9.0 to 5.9.1 in /modules/junit-jupiter Bumps [junit-jupiter-params](https://github.com/junit-team/junit5) from 5.9.0 to 5.9.1. - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.9.0...r5.9.1) --- updated-dependencies: - dependency-name: org.junit.jupiter:junit-jupiter-params dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 5b91bd181fe..0ef2ef20ab7 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -13,7 +13,7 @@ dependencies { exclude(module: 'hamcrest-core') } testImplementation 'org.assertj:assertj-core:3.23.1' - testImplementation 'org.junit.jupiter:junit-jupiter-params:5.9.0' + testImplementation 'org.junit.jupiter:junit-jupiter-params:5.9.1' testRuntimeOnly 'org.postgresql:postgresql:42.5.0' testRuntimeOnly 'mysql:mysql-connector-java:8.0.30' From d1220f453d1350488e65868a266f817980128f96 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 22:06:35 +0000 Subject: [PATCH 0488/3231] Bump mockito-core from 4.7.0 to 4.8.0 in /modules/junit-jupiter Bumps [mockito-core](https://github.com/mockito/mockito) from 4.7.0 to 4.8.0. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v4.7.0...v4.8.0) --- updated-dependencies: - dependency-name: org.mockito:mockito-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 5b91bd181fe..085ad9f1893 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -9,7 +9,7 @@ dependencies { testImplementation 'com.zaxxer:HikariCP:4.0.3' testImplementation 'redis.clients:jedis:4.2.3' testImplementation 'org.apache.httpcomponents:httpclient:4.5.13' - testImplementation ('org.mockito:mockito-core:4.7.0') { + testImplementation ('org.mockito:mockito-core:4.8.0') { exclude(module: 'hamcrest-core') } testImplementation 'org.assertj:assertj-core:3.23.1' From 4c6a67dd0a6858d3c6985350f140e007201a7069 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 22:06:37 +0000 Subject: [PATCH 0489/3231] Bump release-drafter/release-drafter from 5.20.1 to 5.21.0 Bumps [release-drafter/release-drafter](https://github.com/release-drafter/release-drafter) from 5.20.1 to 5.21.0. - [Release notes](https://github.com/release-drafter/release-drafter/releases) - [Commits](https://github.com/release-drafter/release-drafter/compare/06a49bf28488e030d35ca2ac6dbf7f408a481779...df69d584deac33d8569990cb6413f82447181076) --- updated-dependencies: - dependency-name: release-drafter/release-drafter dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/release-drafter.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml index 94af563bcda..9315b7c342f 100644 --- a/.github/workflows/release-drafter.yml +++ b/.github/workflows/release-drafter.yml @@ -18,6 +18,6 @@ jobs: if: github.repository == 'testcontainers/testcontainers-java' runs-on: ubuntu-latest steps: - - uses: release-drafter/release-drafter@06a49bf28488e030d35ca2ac6dbf7f408a481779 # v5.19.0 + - uses: release-drafter/release-drafter@df69d584deac33d8569990cb6413f82447181076 # v5.19.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From c8d841668bcbb2f8aaae2574a69ee20e699008c1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 22:06:38 +0000 Subject: [PATCH 0490/3231] Bump mockito-core from 4.7.0 to 4.8.0 in /core Bumps [mockito-core](https://github.com/mockito/mockito) from 4.7.0 to 4.8.0. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v4.7.0...v4.8.0) --- updated-dependencies: - dependency-name: org.mockito:mockito-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 4072c25db37..ed85a732f24 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -91,7 +91,7 @@ dependencies { testImplementation 'com.rabbitmq:amqp-client:5.15.0' testImplementation 'org.mongodb:mongo-java-driver:3.12.11' - testImplementation ('org.mockito:mockito-core:4.7.0') { + testImplementation ('org.mockito:mockito-core:4.8.0') { exclude(module: 'hamcrest-core') } // Synthetic JAR used for MountableFileTest and DirectoryTarResourceTest From 05862eb6321a3bc8bf7ae690106eb5c996cc54df Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 22:06:39 +0000 Subject: [PATCH 0491/3231] Bump aws-java-sdk-s3 from 1.12.295 to 1.12.313 in /modules/localstack Bumps [aws-java-sdk-s3](https://github.com/aws/aws-sdk-java) from 1.12.295 to 1.12.313. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.295...1.12.313) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-s3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 2b38b50f162..58be6dceae6 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -3,8 +3,8 @@ description = "Testcontainers :: Localstack" dependencies { api project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.295' - testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.285' + compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.313' + testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.313' testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.295' testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.295' testImplementation 'software.amazon.awssdk:s3:2.17.266' From 5ecb572ee3c058d6a4c3253ec80832b32149b6ee Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 22:06:40 +0000 Subject: [PATCH 0492/3231] Bump junit-jupiter-engine from 5.9.0 to 5.9.1 in /modules/junit-jupiter Bumps [junit-jupiter-engine](https://github.com/junit-team/junit5) from 5.9.0 to 5.9.1. - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.9.0...r5.9.1) --- updated-dependencies: - dependency-name: org.junit.jupiter:junit-jupiter-engine dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 5b91bd181fe..e7456adc1e3 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -17,7 +17,7 @@ dependencies { testRuntimeOnly 'org.postgresql:postgresql:42.5.0' testRuntimeOnly 'mysql:mysql-connector-java:8.0.30' - testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.9.0' + testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.9.1' } test { From 5f4fbf85c585ea828adb8576740852defa9f858c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 22:06:42 +0000 Subject: [PATCH 0493/3231] Bump peter-evans/create-pull-request from 4.1.1 to 4.1.3 Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 4.1.1 to 4.1.3. - [Release notes](https://github.com/peter-evans/create-pull-request/releases) - [Commits](https://github.com/peter-evans/create-pull-request/compare/18f90432bedd2afd6a825469ffd38aa24712a91d...671dc9c9e0c2d73f07fa45a3eb0220e1622f0c5f) --- updated-dependencies: - dependency-name: peter-evans/create-pull-request dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/update-docs-version.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/update-docs-version.yml b/.github/workflows/update-docs-version.yml index 070fa2c55d4..983f872c4f8 100644 --- a/.github/workflows/update-docs-version.yml +++ b/.github/workflows/update-docs-version.yml @@ -23,7 +23,7 @@ jobs: sed -i "s/latest_version: .*/latest_version: ${GITHUB_REF##*/}/g" mkdocs.yml git diff - name: Create Pull Request - uses: peter-evans/create-pull-request@18f90432bedd2afd6a825469ffd38aa24712a91d # v3.10.1 + uses: peter-evans/create-pull-request@671dc9c9e0c2d73f07fa45a3eb0220e1622f0c5f # v3.10.1 with: title: Update docs version to ${GITHUB_REF##*/} body: | From 912b017d04fc4c4204850dffd5b0d53225cd7063 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 22:06:43 +0000 Subject: [PATCH 0494/3231] Bump amqp-client from 5.15.0 to 5.16.0 in /core Bumps [amqp-client](https://github.com/rabbitmq/rabbitmq-java-client) from 5.15.0 to 5.16.0. - [Release notes](https://github.com/rabbitmq/rabbitmq-java-client/releases) - [Commits](https://github.com/rabbitmq/rabbitmq-java-client/compare/v5.15.0...v5.16.0) --- updated-dependencies: - dependency-name: com.rabbitmq:amqp-client dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 4072c25db37..c9f849a9265 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -88,7 +88,7 @@ dependencies { testImplementation 'org.apache.httpcomponents:httpclient:4.5.9' testImplementation 'redis.clients:jedis:4.2.3' - testImplementation 'com.rabbitmq:amqp-client:5.15.0' + testImplementation 'com.rabbitmq:amqp-client:5.16.0' testImplementation 'org.mongodb:mongo-java-driver:3.12.11' testImplementation ('org.mockito:mockito-core:4.7.0') { From 81619973fc9a66d98a0198d90158506905864efc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 22:06:46 +0000 Subject: [PATCH 0495/3231] Bump junit-jupiter-api from 5.9.0 to 5.9.1 in /modules/junit-jupiter Bumps [junit-jupiter-api](https://github.com/junit-team/junit5) from 5.9.0 to 5.9.1. - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.9.0...r5.9.1) --- updated-dependencies: - dependency-name: org.junit.jupiter:junit-jupiter-api dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 5b91bd181fe..575e367512c 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -2,7 +2,7 @@ description = "Testcontainers :: JUnit Jupiter Extension" dependencies { api project(':testcontainers') - api 'org.junit.jupiter:junit-jupiter-api:5.9.0' + api 'org.junit.jupiter:junit-jupiter-api:5.9.1' testImplementation project(':mysql') testImplementation project(':postgresql') From 785c08e6cba914d8092c8e00edf883e9ed390b76 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 22:06:48 +0000 Subject: [PATCH 0496/3231] Bump java-client from 3.3.3 to 3.3.4 in /modules/couchbase Bumps [java-client](https://github.com/couchbase/couchbase-jvm-clients) from 3.3.3 to 3.3.4. - [Release notes](https://github.com/couchbase/couchbase-jvm-clients/releases) - [Commits](https://github.com/couchbase/couchbase-jvm-clients/compare/java-client-3.3.3...java-client-3.3.4) --- updated-dependencies: - dependency-name: com.couchbase.client:java-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/couchbase/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/couchbase/build.gradle b/modules/couchbase/build.gradle index 5950aac7655..0542a5d68d0 100644 --- a/modules/couchbase/build.gradle +++ b/modules/couchbase/build.gradle @@ -5,7 +5,7 @@ dependencies { // TODO use JDK's HTTP client and/or Apache HttpClient5 shaded 'com.squareup.okhttp3:okhttp:4.10.0' - testImplementation 'com.couchbase.client:java-client:3.3.3' + testImplementation 'com.couchbase.client:java-client:3.3.4' testImplementation 'org.awaitility:awaitility:4.2.0' testImplementation 'org.assertj:assertj-core:3.23.1' } From aea8f74fee1b1da6f2a01f17741f8a7729f941ea Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 22:06:52 +0000 Subject: [PATCH 0497/3231] Bump tomcat-jdbc from 10.0.23 to 10.1.0 in /modules/jdbc Bumps tomcat-jdbc from 10.0.23 to 10.1.0. --- updated-dependencies: - dependency-name: org.apache.tomcat:tomcat-jdbc dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/jdbc/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jdbc/build.gradle b/modules/jdbc/build.gradle index ba0f845b47d..14c4a431d00 100644 --- a/modules/jdbc/build.gradle +++ b/modules/jdbc/build.gradle @@ -7,7 +7,7 @@ dependencies { compileOnly 'org.jetbrains:annotations:23.0.0' testImplementation 'commons-dbutils:commons-dbutils:1.7' testImplementation 'org.vibur:vibur-dbcp:25.0' - testImplementation 'org.apache.tomcat:tomcat-jdbc:10.0.23' + testImplementation 'org.apache.tomcat:tomcat-jdbc:10.1.0' testImplementation 'com.zaxxer:HikariCP-java6:2.3.13' testImplementation 'org.assertj:assertj-core:3.23.1' testImplementation ('org.mockito:mockito-core:4.7.0') { From fa7a2c3536a3006e86245bb3dc82a6d1a39f38df Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 22:06:55 +0000 Subject: [PATCH 0498/3231] Bump aws-java-sdk-logs from 1.12.295 to 1.12.313 in /modules/localstack Bumps [aws-java-sdk-logs](https://github.com/aws/aws-sdk-java) from 1.12.295 to 1.12.313. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.295...1.12.313) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-logs dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 2b38b50f162..8ec3a442bc4 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -6,7 +6,7 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.295' testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.285' testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.295' - testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.295' + testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.313' testImplementation 'software.amazon.awssdk:s3:2.17.266' testImplementation 'org.assertj:assertj-core:3.23.1' } From fcd3f607bd3e2d0c07da6ef83d4d1687da93028d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 22:06:58 +0000 Subject: [PATCH 0499/3231] Bump common-custom-user-data-gradle-plugin in /examples Bumps common-custom-user-data-gradle-plugin from 1.8 to 1.8.1. --- updated-dependencies: - dependency-name: com.gradle:common-custom-user-data-gradle-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/settings.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/settings.gradle b/examples/settings.gradle index 7194134350d..a4e4bddd880 100644 --- a/examples/settings.gradle +++ b/examples/settings.gradle @@ -7,7 +7,7 @@ buildscript { dependencies { classpath "gradle.plugin.ch.myniva.gradle:s3-build-cache:0.10.0" classpath "com.gradle.enterprise:com.gradle.enterprise.gradle.plugin:3.11.1" - classpath "com.gradle:common-custom-user-data-gradle-plugin:1.8" + classpath "com.gradle:common-custom-user-data-gradle-plugin:1.8.1" } } From 92f072f3dea0790aebb5273429e9c7be31d1f10d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 22:07:02 +0000 Subject: [PATCH 0500/3231] Bump mockito-core from 4.7.0 to 4.8.0 in /modules/jdbc Bumps [mockito-core](https://github.com/mockito/mockito) from 4.7.0 to 4.8.0. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v4.7.0...v4.8.0) --- updated-dependencies: - dependency-name: org.mockito:mockito-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/jdbc/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jdbc/build.gradle b/modules/jdbc/build.gradle index ba0f845b47d..bf590c08748 100644 --- a/modules/jdbc/build.gradle +++ b/modules/jdbc/build.gradle @@ -10,7 +10,7 @@ dependencies { testImplementation 'org.apache.tomcat:tomcat-jdbc:10.0.23' testImplementation 'com.zaxxer:HikariCP-java6:2.3.13' testImplementation 'org.assertj:assertj-core:3.23.1' - testImplementation ('org.mockito:mockito-core:4.7.0') { + testImplementation ('org.mockito:mockito-core:4.8.0') { exclude(module: 'hamcrest-core') } } From 502a2abf5808e4d102538146c8415dfcf64d9948 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 22:07:02 +0000 Subject: [PATCH 0501/3231] Bump trino-jdbc from 394 to 398 in /modules/trino Bumps trino-jdbc from 394 to 398. --- updated-dependencies: - dependency-name: io.trino:trino-jdbc dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- modules/trino/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/trino/build.gradle b/modules/trino/build.gradle index ca2619a8631..c534f14bd2a 100644 --- a/modules/trino/build.gradle +++ b/modules/trino/build.gradle @@ -4,6 +4,6 @@ dependencies { api project(':jdbc') testImplementation project(':jdbc-test') - testImplementation 'io.trino:trino-jdbc:394' + testImplementation 'io.trino:trino-jdbc:398' compileOnly 'org.jetbrains:annotations:23.0.0' } From 9f2e2c14c6f7a8cf47e8ac73a0f3b2a40ed62d37 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 22:07:14 +0000 Subject: [PATCH 0502/3231] Bump google-cloud-pubsub from 1.120.13 to 1.120.18 in /modules/gcloud Bumps [google-cloud-pubsub](https://github.com/googleapis/java-pubsub) from 1.120.13 to 1.120.18. - [Release notes](https://github.com/googleapis/java-pubsub/releases) - [Changelog](https://github.com/googleapis/java-pubsub/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/java-pubsub/compare/v1.120.13...v1.120.18) --- updated-dependencies: - dependency-name: com.google.cloud:google-cloud-pubsub dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/gcloud/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle index 044d82cbb21..aa0b151f6f8 100644 --- a/modules/gcloud/build.gradle +++ b/modules/gcloud/build.gradle @@ -5,7 +5,7 @@ dependencies { testImplementation 'com.google.cloud:google-cloud-datastore:2.11.0' testImplementation 'com.google.cloud:google-cloud-firestore:3.4.2' - testImplementation 'com.google.cloud:google-cloud-pubsub:1.120.13' + testImplementation 'com.google.cloud:google-cloud-pubsub:1.120.18' testImplementation 'com.google.cloud:google-cloud-spanner:6.29.0' testImplementation 'com.google.cloud:google-cloud-bigtable:2.11.1' testImplementation 'org.assertj:assertj-core:3.23.1' From 826657c84ff24b3a8a15ee5bd65fd1bd2a179fee Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 22:07:16 +0000 Subject: [PATCH 0503/3231] Bump mssql-jdbc from 11.2.0.jre8 to 11.2.1.jre8 in /modules/mssqlserver Bumps [mssql-jdbc](https://github.com/Microsoft/mssql-jdbc) from 11.2.0.jre8 to 11.2.1.jre8. - [Release notes](https://github.com/Microsoft/mssql-jdbc/releases) - [Changelog](https://github.com/microsoft/mssql-jdbc/blob/main/CHANGELOG.md) - [Commits](https://github.com/Microsoft/mssql-jdbc/commits) --- updated-dependencies: - dependency-name: com.microsoft.sqlserver:mssql-jdbc dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/mssqlserver/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mssqlserver/build.gradle b/modules/mssqlserver/build.gradle index cc05b6ffea4..ef91edc79d1 100644 --- a/modules/mssqlserver/build.gradle +++ b/modules/mssqlserver/build.gradle @@ -10,7 +10,7 @@ dependencies { compileOnly 'io.r2dbc:r2dbc-mssql:0.9.0.RELEASE' testImplementation project(':jdbc-test') - testImplementation 'com.microsoft.sqlserver:mssql-jdbc:11.2.0.jre8' + testImplementation 'com.microsoft.sqlserver:mssql-jdbc:11.2.1.jre8' testImplementation project(':r2dbc') testImplementation 'io.r2dbc:r2dbc-mssql:0.8.7.RELEASE' From 6f8c1a536c2f1f3b4242b75d2c590870ccf005f6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 22:07:20 +0000 Subject: [PATCH 0504/3231] Bump mariadb-java-client from 3.0.7 to 3.0.8 in /modules/mariadb Bumps [mariadb-java-client](https://github.com/mariadb-corporation/mariadb-connector-j) from 3.0.7 to 3.0.8. - [Release notes](https://github.com/mariadb-corporation/mariadb-connector-j/releases) - [Changelog](https://github.com/mariadb-corporation/mariadb-connector-j/blob/master/CHANGELOG.md) - [Commits](https://github.com/mariadb-corporation/mariadb-connector-j/compare/3.0.7...3.0.8) --- updated-dependencies: - dependency-name: org.mariadb.jdbc:mariadb-java-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/mariadb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mariadb/build.gradle b/modules/mariadb/build.gradle index 1a11f0fbfb6..b4b3b3b4415 100644 --- a/modules/mariadb/build.gradle +++ b/modules/mariadb/build.gradle @@ -10,7 +10,7 @@ dependencies { compileOnly 'org.mariadb:r2dbc-mariadb:1.0.3' testImplementation project(':jdbc-test') - testImplementation 'org.mariadb.jdbc:mariadb-java-client:3.0.7' + testImplementation 'org.mariadb.jdbc:mariadb-java-client:3.0.8' testImplementation testFixtures(project(':r2dbc')) testImplementation 'org.mariadb:r2dbc-mariadb:1.0.2' From 50c03510cb4e11ad398c726437e0dee26cc2e3b4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 22:07:23 +0000 Subject: [PATCH 0505/3231] Bump google-cloud-datastore from 2.11.0 to 2.11.4 in /modules/gcloud Bumps [google-cloud-datastore](https://github.com/googleapis/java-datastore) from 2.11.0 to 2.11.4. - [Release notes](https://github.com/googleapis/java-datastore/releases) - [Changelog](https://github.com/googleapis/java-datastore/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/java-datastore/compare/v2.11.0...v2.11.4) --- updated-dependencies: - dependency-name: com.google.cloud:google-cloud-datastore dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/gcloud/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle index 044d82cbb21..2758d755c56 100644 --- a/modules/gcloud/build.gradle +++ b/modules/gcloud/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: GCloud" dependencies { api project(':testcontainers') - testImplementation 'com.google.cloud:google-cloud-datastore:2.11.0' + testImplementation 'com.google.cloud:google-cloud-datastore:2.11.4' testImplementation 'com.google.cloud:google-cloud-firestore:3.4.2' testImplementation 'com.google.cloud:google-cloud-pubsub:1.120.13' testImplementation 'com.google.cloud:google-cloud-spanner:6.29.0' From 00f10e3d1388819a792efb0fc218ca40015ce6df Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 22:07:25 +0000 Subject: [PATCH 0506/3231] Bump azure-cosmos from 4.35.1 to 4.36.0 in /modules/azure Bumps [azure-cosmos](https://github.com/Azure/azure-sdk-for-java) from 4.35.1 to 4.36.0. - [Release notes](https://github.com/Azure/azure-sdk-for-java/releases) - [Commits](https://github.com/Azure/azure-sdk-for-java/compare/azure-cosmos_4.35.1...azure-cosmos_4.36.0) --- updated-dependencies: - dependency-name: com.azure:azure-cosmos dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/azure/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/azure/build.gradle b/modules/azure/build.gradle index 8753606874c..43280db4a88 100644 --- a/modules/azure/build.gradle +++ b/modules/azure/build.gradle @@ -6,5 +6,5 @@ dependencies { shaded 'com.squareup.okhttp3:okhttp:4.10.0' testImplementation 'org.assertj:assertj-core:3.23.1' - testImplementation 'com.azure:azure-cosmos:4.35.1' + testImplementation 'com.azure:azure-cosmos:4.36.0' } From 54493fb3c9fe995682890c05997d8b831335678b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 22:07:31 +0000 Subject: [PATCH 0507/3231] Bump reactor-core from 3.4.22 to 3.4.23 in /modules/r2dbc Bumps [reactor-core](https://github.com/reactor/reactor-core) from 3.4.22 to 3.4.23. - [Release notes](https://github.com/reactor/reactor-core/releases) - [Commits](https://github.com/reactor/reactor-core/compare/v3.4.22...v3.4.23) --- updated-dependencies: - dependency-name: io.projectreactor:reactor-core dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/r2dbc/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/r2dbc/build.gradle b/modules/r2dbc/build.gradle index c55b71ddcb2..9126ff0a347 100644 --- a/modules/r2dbc/build.gradle +++ b/modules/r2dbc/build.gradle @@ -15,6 +15,6 @@ dependencies { testImplementation 'io.r2dbc:r2dbc-postgresql:0.8.13.RELEASE' testImplementation project(':postgresql') - testFixturesImplementation 'io.projectreactor:reactor-core:3.4.22' + testFixturesImplementation 'io.projectreactor:reactor-core:3.4.23' testFixturesImplementation 'org.assertj:assertj-core:3.23.1' } From 569ae4c8112b2c95ef7dab82e6f5db6c763bb51b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 22:07:32 +0000 Subject: [PATCH 0508/3231] Bump amqp-client from 5.15.0 to 5.16.0 in /modules/rabbitmq Bumps [amqp-client](https://github.com/rabbitmq/rabbitmq-java-client) from 5.15.0 to 5.16.0. - [Release notes](https://github.com/rabbitmq/rabbitmq-java-client/releases) - [Commits](https://github.com/rabbitmq/rabbitmq-java-client/compare/v5.15.0...v5.16.0) --- updated-dependencies: - dependency-name: com.rabbitmq:amqp-client dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/rabbitmq/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/rabbitmq/build.gradle b/modules/rabbitmq/build.gradle index 9825a27a499..6cf03d4f984 100644 --- a/modules/rabbitmq/build.gradle +++ b/modules/rabbitmq/build.gradle @@ -2,7 +2,7 @@ description = "TestContainers :: RabbitMQ" dependencies { api project(":testcontainers") - testImplementation 'com.rabbitmq:amqp-client:5.15.0' + testImplementation 'com.rabbitmq:amqp-client:5.16.0' testImplementation 'org.assertj:assertj-core:3.23.1' compileOnly 'org.jetbrains:annotations:23.0.0' } From 73596740f3ece5f93af4f1b3e8a2f9e24595f227 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 22:07:35 +0000 Subject: [PATCH 0509/3231] Bump elasticsearch-rest-client in /modules/elasticsearch Bumps [elasticsearch-rest-client](https://github.com/elastic/elasticsearch) from 8.4.1 to 8.4.2. - [Release notes](https://github.com/elastic/elasticsearch/releases) - [Changelog](https://github.com/elastic/elasticsearch/blob/main/CHANGELOG.md) - [Commits](https://github.com/elastic/elasticsearch/compare/v8.4.1...v8.4.2) --- updated-dependencies: - dependency-name: org.elasticsearch.client:elasticsearch-rest-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/elasticsearch/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/elasticsearch/build.gradle b/modules/elasticsearch/build.gradle index c5591d0d26a..04521b974ea 100644 --- a/modules/elasticsearch/build.gradle +++ b/modules/elasticsearch/build.gradle @@ -2,7 +2,7 @@ description = "TestContainers :: elasticsearch" dependencies { api project(':testcontainers') - testImplementation "org.elasticsearch.client:elasticsearch-rest-client:8.4.1" + testImplementation "org.elasticsearch.client:elasticsearch-rest-client:8.4.2" testImplementation "org.elasticsearch.client:transport:7.17.6" testImplementation 'org.assertj:assertj-core:3.23.1' } From 25af50d16d32e66a4c05299610a23077905828cb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 22:07:35 +0000 Subject: [PATCH 0510/3231] Bump kafka-clients from 3.2.1 to 3.3.0 in /modules/redpanda Bumps kafka-clients from 3.2.1 to 3.3.0. --- updated-dependencies: - dependency-name: org.apache.kafka:kafka-clients dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/redpanda/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/redpanda/build.gradle b/modules/redpanda/build.gradle index 5750e873bf6..4d7221711a3 100644 --- a/modules/redpanda/build.gradle +++ b/modules/redpanda/build.gradle @@ -3,6 +3,6 @@ description = "Testcontainers :: Redpanda" dependencies { api project(':testcontainers') - testImplementation 'org.apache.kafka:kafka-clients:3.2.1' + testImplementation 'org.apache.kafka:kafka-clients:3.3.0' testImplementation 'org.assertj:assertj-core:3.23.1' } From 9af2f2282caf9f57bfdb1e582405889782097c78 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 22:07:39 +0000 Subject: [PATCH 0511/3231] Bump common-custom-user-data-gradle-plugin from 1.8 to 1.8.1 Bumps common-custom-user-data-gradle-plugin from 1.8 to 1.8.1. --- updated-dependencies: - dependency-name: com.gradle:common-custom-user-data-gradle-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- settings.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.gradle b/settings.gradle index 2beb91dea01..f0c1d306671 100644 --- a/settings.gradle +++ b/settings.gradle @@ -6,7 +6,7 @@ buildscript { } dependencies { classpath "com.gradle.enterprise:com.gradle.enterprise.gradle.plugin:3.11.1" - classpath "com.gradle:common-custom-user-data-gradle-plugin:1.8" + classpath "com.gradle:common-custom-user-data-gradle-plugin:1.8.1" } } From ea4053bfa065616be0c2c84276279bd60fd16b77 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 22:07:43 +0000 Subject: [PATCH 0512/3231] Bump rest-assured from 5.1.1 to 5.2.0 in /modules/consul Bumps [rest-assured](https://github.com/rest-assured/rest-assured) from 5.1.1 to 5.2.0. - [Release notes](https://github.com/rest-assured/rest-assured/releases) - [Changelog](https://github.com/rest-assured/rest-assured/blob/master/changelog.txt) - [Commits](https://github.com/rest-assured/rest-assured/compare/rest-assured-5.1.1...rest-assured-5.2.0) --- updated-dependencies: - dependency-name: io.rest-assured:rest-assured dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/consul/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/consul/build.gradle b/modules/consul/build.gradle index acb97a47927..a72aefaab5f 100644 --- a/modules/consul/build.gradle +++ b/modules/consul/build.gradle @@ -4,6 +4,6 @@ dependencies { api project(':testcontainers') testImplementation 'com.ecwid.consul:consul-api:1.4.5' - testImplementation 'io.rest-assured:rest-assured:5.1.1' + testImplementation 'io.rest-assured:rest-assured:5.2.0' testImplementation 'org.assertj:assertj-core:3.23.1' } From 80ced857e13cad960b7c781f3b1dd70cb54332aa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 22:07:45 +0000 Subject: [PATCH 0513/3231] Bump rest-assured from 5.1.1 to 5.2.0 in /modules/vault Bumps [rest-assured](https://github.com/rest-assured/rest-assured) from 5.1.1 to 5.2.0. - [Release notes](https://github.com/rest-assured/rest-assured/releases) - [Changelog](https://github.com/rest-assured/rest-assured/blob/master/changelog.txt) - [Commits](https://github.com/rest-assured/rest-assured/compare/rest-assured-5.1.1...rest-assured-5.2.0) --- updated-dependencies: - dependency-name: io.rest-assured:rest-assured dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/vault/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/vault/build.gradle b/modules/vault/build.gradle index 499286ae72a..c9142936b63 100644 --- a/modules/vault/build.gradle +++ b/modules/vault/build.gradle @@ -4,7 +4,7 @@ dependencies { api project(':testcontainers') testImplementation 'com.bettercloud:vault-java-driver:5.1.0' - testImplementation 'io.rest-assured:rest-assured:5.1.1' + testImplementation 'io.rest-assured:rest-assured:5.2.0' testImplementation 'org.assertj:assertj-core:3.23.1' } From 29b18d150c2c9f98fb15ec5a38a32f3b4b989833 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 22:07:52 +0000 Subject: [PATCH 0514/3231] Bump logback-classic from 1.4.0 to 1.4.1 in /modules/hivemq Bumps [logback-classic](https://github.com/qos-ch/logback) from 1.4.0 to 1.4.1. - [Release notes](https://github.com/qos-ch/logback/releases) - [Commits](https://github.com/qos-ch/logback/compare/v_1.4.0...v_1.4.1) --- updated-dependencies: - dependency-name: ch.qos.logback:logback-classic dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/hivemq/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/hivemq/build.gradle b/modules/hivemq/build.gradle index 477d5ac98a3..0520440828f 100644 --- a/modules/hivemq/build.gradle +++ b/modules/hivemq/build.gradle @@ -16,7 +16,7 @@ dependencies { testImplementation("com.hivemq:hivemq-extension-sdk:4.8.3") testImplementation("com.hivemq:hivemq-mqtt-client:1.3.0") testImplementation("org.apache.httpcomponents:httpclient:4.5.13") - testImplementation("ch.qos.logback:logback-classic:1.4.0") + testImplementation("ch.qos.logback:logback-classic:1.4.1") testImplementation 'org.assertj:assertj-core:3.23.1' testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.9.0") } From e094aceeef5d76b57018b4dd453c686f4883ed0a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 22:07:56 +0000 Subject: [PATCH 0515/3231] Bump json from 20220320 to 20220924 in /examples Bumps [json](https://github.com/douglascrockford/JSON-java) from 20220320 to 20220924. - [Release notes](https://github.com/douglascrockford/JSON-java/releases) - [Changelog](https://github.com/stleary/JSON-java/blob/master/docs/RELEASES.md) - [Commits](https://github.com/douglascrockford/JSON-java/commits) --- updated-dependencies: - dependency-name: org.json:json dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- examples/linked-container/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/linked-container/build.gradle b/examples/linked-container/build.gradle index 5d9fb93c20d..fe230f3a22e 100644 --- a/examples/linked-container/build.gradle +++ b/examples/linked-container/build.gradle @@ -8,7 +8,7 @@ repositories { dependencies { compileOnly 'org.slf4j:slf4j-api:2.0.0' implementation 'com.squareup.okhttp3:okhttp:4.10.0' - implementation 'org.json:json:20220320' + implementation 'org.json:json:20220924' testImplementation 'org.postgresql:postgresql:42.5.0' testImplementation 'ch.qos.logback:logback-classic:1.2.11' testImplementation 'org.testcontainers:postgresql' From 942923183b8946b8c4c7edb8d5a24bfbb5748611 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 22:07:59 +0000 Subject: [PATCH 0516/3231] Bump hivemq-extension-sdk from 4.8.3 to 4.9.0 in /modules/hivemq Bumps [hivemq-extension-sdk](https://github.com/hivemq/hivemq-extension-sdk) from 4.8.3 to 4.9.0. - [Release notes](https://github.com/hivemq/hivemq-extension-sdk/releases) - [Commits](https://github.com/hivemq/hivemq-extension-sdk/compare/4.8.3...4.9.0) --- updated-dependencies: - dependency-name: com.hivemq:hivemq-extension-sdk dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/hivemq/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/hivemq/build.gradle b/modules/hivemq/build.gradle index 477d5ac98a3..b6b9a42c425 100644 --- a/modules/hivemq/build.gradle +++ b/modules/hivemq/build.gradle @@ -13,7 +13,7 @@ dependencies { testImplementation("org.junit.jupiter:junit-jupiter-api:5.9.0") testImplementation(project(":junit-jupiter")) - testImplementation("com.hivemq:hivemq-extension-sdk:4.8.3") + testImplementation("com.hivemq:hivemq-extension-sdk:4.9.0") testImplementation("com.hivemq:hivemq-mqtt-client:1.3.0") testImplementation("org.apache.httpcomponents:httpclient:4.5.13") testImplementation("ch.qos.logback:logback-classic:1.4.0") From 27bb1aef9800f8b33c413133b970c6dbecd4aa1c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 22:08:03 +0000 Subject: [PATCH 0517/3231] Bump junit-jupiter-api from 5.9.0 to 5.9.1 in /modules/hivemq Bumps [junit-jupiter-api](https://github.com/junit-team/junit5) from 5.9.0 to 5.9.1. - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.9.0...r5.9.1) --- updated-dependencies: - dependency-name: org.junit.jupiter:junit-jupiter-api dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/hivemq/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/hivemq/build.gradle b/modules/hivemq/build.gradle index 477d5ac98a3..50b857d7ed6 100644 --- a/modules/hivemq/build.gradle +++ b/modules/hivemq/build.gradle @@ -11,7 +11,7 @@ dependencies { shaded("org.jboss.shrinkwrap:shrinkwrap-impl-base:1.2.6") shaded("net.lingala.zip4j:zip4j:2.11.1") - testImplementation("org.junit.jupiter:junit-jupiter-api:5.9.0") + testImplementation("org.junit.jupiter:junit-jupiter-api:5.9.1") testImplementation(project(":junit-jupiter")) testImplementation("com.hivemq:hivemq-extension-sdk:4.8.3") testImplementation("com.hivemq:hivemq-mqtt-client:1.3.0") From f7bd0350106a97eafb53a5ff722b63d63cc5ce67 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 22:08:06 +0000 Subject: [PATCH 0518/3231] Bump org.springframework.boot from 2.7.3 to 2.7.4 in /examples Bumps [org.springframework.boot](https://github.com/spring-projects/spring-boot) from 2.7.3 to 2.7.4. - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v2.7.3...v2.7.4) --- updated-dependencies: - dependency-name: org.springframework.boot dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/selenium-container/build.gradle | 2 +- examples/spring-boot-kotlin-redis/build.gradle | 2 +- examples/spring-boot/build.gradle | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/selenium-container/build.gradle b/examples/selenium-container/build.gradle index 6183b89e1d6..feb677dc64a 100644 --- a/examples/selenium-container/build.gradle +++ b/examples/selenium-container/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id 'org.springframework.boot' version '2.7.3' + id 'org.springframework.boot' version '2.7.4' } apply plugin: 'io.spring.dependency-management' diff --git a/examples/spring-boot-kotlin-redis/build.gradle b/examples/spring-boot-kotlin-redis/build.gradle index 180d6780c8f..6a98573172c 100644 --- a/examples/spring-boot-kotlin-redis/build.gradle +++ b/examples/spring-boot-kotlin-redis/build.gradle @@ -1,5 +1,5 @@ plugins { - id("org.springframework.boot") version "2.7.3" + id("org.springframework.boot") version "2.7.4" id("org.jetbrains.kotlin.jvm") version "1.7.10" id("org.jetbrains.kotlin.plugin.spring") version "1.7.10" } diff --git a/examples/spring-boot/build.gradle b/examples/spring-boot/build.gradle index 52a4a63386a..56cd62e34e6 100644 --- a/examples/spring-boot/build.gradle +++ b/examples/spring-boot/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id 'org.springframework.boot' version '2.7.3' + id 'org.springframework.boot' version '2.7.4' } apply plugin: 'io.spring.dependency-management' From 5c9d06d226bcb23c00eaa02ff94588dd83d582df Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 22:08:07 +0000 Subject: [PATCH 0519/3231] Bump kafka-clients from 3.2.1 to 3.3.0 in /examples Bumps kafka-clients from 3.2.1 to 3.3.0. --- updated-dependencies: - dependency-name: org.apache.kafka:kafka-clients dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- examples/kafka-cluster/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/kafka-cluster/build.gradle b/examples/kafka-cluster/build.gradle index c3a79d4058f..08d3734a956 100644 --- a/examples/kafka-cluster/build.gradle +++ b/examples/kafka-cluster/build.gradle @@ -10,7 +10,7 @@ dependencies { testCompileOnly "org.projectlombok:lombok:1.18.24" testAnnotationProcessor "org.projectlombok:lombok:1.18.24" testImplementation 'org.testcontainers:kafka' - testImplementation 'org.apache.kafka:kafka-clients:3.2.1' + testImplementation 'org.apache.kafka:kafka-clients:3.3.0' testImplementation 'org.assertj:assertj-core:3.23.1' testImplementation 'com.google.guava:guava:23.0' testImplementation 'ch.qos.logback:logback-classic:1.2.11' From 4ec5af3f90e6bb9da32297067e7519a89b61dcb3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 22:08:08 +0000 Subject: [PATCH 0520/3231] Bump junit-jupiter-engine from 5.9.0 to 5.9.1 in /modules/hivemq Bumps [junit-jupiter-engine](https://github.com/junit-team/junit5) from 5.9.0 to 5.9.1. - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.9.0...r5.9.1) --- updated-dependencies: - dependency-name: org.junit.jupiter:junit-jupiter-engine dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/hivemq/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/hivemq/build.gradle b/modules/hivemq/build.gradle index 477d5ac98a3..511e504f8ef 100644 --- a/modules/hivemq/build.gradle +++ b/modules/hivemq/build.gradle @@ -18,7 +18,7 @@ dependencies { testImplementation("org.apache.httpcomponents:httpclient:4.5.13") testImplementation("ch.qos.logback:logback-classic:1.4.0") testImplementation 'org.assertj:assertj-core:3.23.1' - testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.9.0") + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.9.1") } test { From 21e09603751d887da68dc28b0ea431e1758f2beb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 22:08:16 +0000 Subject: [PATCH 0521/3231] Bump zip4j from 2.11.1 to 2.11.2 in /modules/hivemq Bumps [zip4j](https://github.com/srikanth-lingala/zip4j) from 2.11.1 to 2.11.2. - [Release notes](https://github.com/srikanth-lingala/zip4j/releases) - [Commits](https://github.com/srikanth-lingala/zip4j/compare/v2.11.1...v2.11.2) --- updated-dependencies: - dependency-name: net.lingala.zip4j:zip4j dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/hivemq/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/hivemq/build.gradle b/modules/hivemq/build.gradle index 477d5ac98a3..0c336745f77 100644 --- a/modules/hivemq/build.gradle +++ b/modules/hivemq/build.gradle @@ -9,7 +9,7 @@ dependencies { shaded("org.javassist:javassist:3.29.0-GA") shaded("org.jboss.shrinkwrap:shrinkwrap-api:1.2.6") shaded("org.jboss.shrinkwrap:shrinkwrap-impl-base:1.2.6") - shaded("net.lingala.zip4j:zip4j:2.11.1") + shaded("net.lingala.zip4j:zip4j:2.11.2") testImplementation("org.junit.jupiter:junit-jupiter-api:5.9.0") testImplementation(project(":junit-jupiter")) From ff13685d346a6329268b2b5de0f82b8323117087 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 22:08:17 +0000 Subject: [PATCH 0522/3231] Bump cucumber-junit from 7.6.0 to 7.8.0 in /examples Bumps [cucumber-junit](https://github.com/cucumber/cucumber-jvm) from 7.6.0 to 7.8.0. - [Release notes](https://github.com/cucumber/cucumber-jvm/releases) - [Changelog](https://github.com/cucumber/cucumber-jvm/blob/main/CHANGELOG.md) - [Commits](https://github.com/cucumber/cucumber-jvm/compare/v7.6.0...v7.8.0) --- updated-dependencies: - dependency-name: io.cucumber:cucumber-junit dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- examples/cucumber/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cucumber/build.gradle b/examples/cucumber/build.gradle index ab0331f81f4..9d0da7a5e33 100644 --- a/examples/cucumber/build.gradle +++ b/examples/cucumber/build.gradle @@ -11,6 +11,6 @@ dependencies { implementation 'org.seleniumhq.selenium:selenium-firefox-driver:3.141.59' implementation 'org.seleniumhq.selenium:selenium-chrome-driver:3.141.59' testImplementation 'io.cucumber:cucumber-java:7.6.0' - testImplementation 'io.cucumber:cucumber-junit:7.6.0' + testImplementation 'io.cucumber:cucumber-junit:7.8.0' testImplementation 'org.testcontainers:selenium' } From 6b3c7b4fa132021bc443ca09e1322bcc5a2dbb62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Thu, 29 Sep 2022 18:09:45 -0500 Subject: [PATCH 0523/3231] Rename main branch (#5847) `main` is the new default branch name. --- .github/workflows/ci-docker-wormhole.yml | 2 +- .github/workflows/ci-examples.yml | 2 +- .github/workflows/ci-rootless.yml | 2 +- .github/workflows/ci.yml | 2 +- .github/workflows/release-drafter.yml | 2 +- .github/workflows/update-docs-version.yml | 2 +- RELEASING.md | 2 +- docs/contributing.md | 2 +- docs/examples.md | 22 +++++++++++----------- docs/index.md | 4 ++-- docs/jitpack_dependencies.md | 4 ++-- docs/modules/consul.md | 2 +- docs/modules/databases/jdbc.md | 4 ++-- docs/modules/databases/mssqlserver.md | 4 ++-- docs/modules/gcloud.md | 8 ++++---- docs/modules/vault.md | 4 ++-- docs/modules/webdriver_containers.md | 2 +- 17 files changed, 35 insertions(+), 35 deletions(-) diff --git a/.github/workflows/ci-docker-wormhole.yml b/.github/workflows/ci-docker-wormhole.yml index e27e4d8d611..061a004682e 100644 --- a/.github/workflows/ci-docker-wormhole.yml +++ b/.github/workflows/ci-docker-wormhole.yml @@ -2,7 +2,7 @@ name: CI-Docker-Wormhole on: pull_request: {} - push: { branches: [ master ] } + push: { branches: [ main ] } permissions: contents: read diff --git a/.github/workflows/ci-examples.yml b/.github/workflows/ci-examples.yml index fe4610368aa..2cdc9f8ed69 100644 --- a/.github/workflows/ci-examples.yml +++ b/.github/workflows/ci-examples.yml @@ -2,7 +2,7 @@ name: CI-Examples on: pull_request: {} - push: { branches: [ master ] } + push: { branches: [ main ] } env: AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} diff --git a/.github/workflows/ci-rootless.yml b/.github/workflows/ci-rootless.yml index 348cf5aaaf7..50db1655a4a 100644 --- a/.github/workflows/ci-rootless.yml +++ b/.github/workflows/ci-rootless.yml @@ -2,7 +2,7 @@ name: CI-Docker-Rootless on: pull_request: {} - push: { branches: [ master ] } + push: { branches: [ main ] } permissions: contents: read diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e1eceab8702..80f20fb0c0c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,7 +2,7 @@ name: CI on: pull_request: {} - push: { branches: [ master ] } + push: { branches: [ maing ] } permissions: contents: read diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml index 94af563bcda..46b1e261789 100644 --- a/.github/workflows/release-drafter.yml +++ b/.github/workflows/release-drafter.yml @@ -3,7 +3,7 @@ name: Release Drafter on: push: branches: - - master + - main pull_request: types: [opened, reopened, synchronize] diff --git a/.github/workflows/update-docs-version.yml b/.github/workflows/update-docs-version.yml index 070fa2c55d4..2c54f92efcf 100644 --- a/.github/workflows/update-docs-version.yml +++ b/.github/workflows/update-docs-version.yml @@ -17,7 +17,7 @@ jobs: steps: - uses: actions/checkout@v3 with: - ref: master + ref: main - name: Update latest_version property in mkdocs.yml run: | sed -i "s/latest_version: .*/latest_version: ${GITHUB_REF##*/}/g" mkdocs.yml diff --git a/RELEASING.md b/RELEASING.md index f04839d9d41..8027cb9cc98 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -4,7 +4,7 @@ Testcontainers' release process is semi-automated through GitHub Actions. This d ## Steps -1. Ensure that the master branch is building and that tests are passing. +1. Ensure that the `main` branch is building and that tests are passing. 1. Create a new release on GitHub. **The tag name is used as the version**, so please keep the tag name plain (e.g. 1.2.3). 1. The release triggers a GitHub Action workflow. 1. Log in to [Sonatype](https://oss.sonatype.org/) to check the staging repository. diff --git a/docs/contributing.md b/docs/contributing.md index 01c4fce57b6..1457839a5ec 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -101,7 +101,7 @@ For this, we are using the [gh-combine-prs](https://github.com/rnorth/gh-combine The whole process is as follow: 1. Check that all open Dependabot PRs did succeed their build. If they did not succeed, trigger a rerun if the cause were external factors or else document the reason if obvious. -2. Run the extension from an up-to-date local `master` branch: `gh combine-prs --query "author:app/dependabot"` +2. Run the extension from an up-to-date local `main` branch: `gh combine-prs --query "author:app/dependabot"` 3. Merge conflicts might appear. Just ignore them, we will get those PRs in a future run. 4. Once the build of the combined PR did succeed, temporarily enable merge commits and merge the PR using a merge commit through the GitHub UI. 5. After the merge, disable merge commits again. diff --git a/docs/examples.md b/docs/examples.md index 918688d86cb..14f43d21e23 100644 --- a/docs/examples.md +++ b/docs/examples.md @@ -2,14 +2,14 @@ Examples of different use cases provided by Testcontainers can be found below: -- [Kafka Cluster with multiple brokers](https://github.com/testcontainers/testcontainers-java/tree/master/examples/kafka-cluster) -- [Linked containers](https://github.com/testcontainers/testcontainers-java/tree/master/examples/linked-container) -- [Neo4j](https://github.com/testcontainers/testcontainers-java/tree/master/examples/neo4j-container) -- [Redis](https://github.com/testcontainers/testcontainers-java/tree/master/examples/redis-backed-cache) -- [Selenium](https://github.com/testcontainers/testcontainers-java/tree/master/examples/selenium-container) -- [Selenium Module with Cucumber](https://github.com/testcontainers/testcontainers-java/tree/master/examples/cucumber) -- [Singleton Container Pattern](https://github.com/testcontainers/testcontainers-java/tree/master/examples/singleton-container) -- [Solr](https://github.com/testcontainers/testcontainers-java/tree/master/examples/solr-container) -- [Spring Boot](https://github.com/testcontainers/testcontainers-java/tree/master/examples/spring-boot) -- [Spring Boot with Kotlin](https://github.com/testcontainers/testcontainers-java/tree/master/examples/spring-boot-kotlin-redis) -- [TestNG](https://github.com/testcontainers/testcontainers-java/tree/master/examples/redis-backed-cache-testng) +- [Kafka Cluster with multiple brokers](https://github.com/testcontainers/testcontainers-java/tree/main/examples/kafka-cluster) +- [Linked containers](https://github.com/testcontainers/testcontainers-java/tree/main/examples/linked-container) +- [Neo4j](https://github.com/testcontainers/testcontainers-java/tree/main/examples/neo4j-container) +- [Redis](https://github.com/testcontainers/testcontainers-java/tree/main/examples/redis-backed-cache) +- [Selenium](https://github.com/testcontainers/testcontainers-java/tree/main/examples/selenium-container) +- [Selenium Module with Cucumber](https://github.com/testcontainers/testcontainers-java/tree/main/examples/cucumber) +- [Singleton Container Pattern](https://github.com/testcontainers/testcontainers-java/tree/main/examples/singleton-container) +- [Solr](https://github.com/testcontainers/testcontainers-java/tree/main/examples/solr-container) +- [Spring Boot](https://github.com/testcontainers/testcontainers-java/tree/main/examples/spring-boot) +- [Spring Boot with Kotlin](https://github.com/testcontainers/testcontainers-java/tree/main/examples/spring-boot-kotlin-redis) +- [TestNG](https://github.com/testcontainers/testcontainers-java/tree/main/examples/redis-backed-cache-testng) diff --git a/docs/index.md b/docs/index.md index 8eb4ddeb2c4..383f42c0245 100644 --- a/docs/index.md +++ b/docs/index.md @@ -206,7 +206,7 @@ A huge thank you to our sponsors: ## License -See [LICENSE](https://raw.githubusercontent.com/testcontainers/testcontainers-java/master/LICENSE). +See [LICENSE](https://raw.githubusercontent.com/testcontainers/testcontainers-java/main/LICENSE). ## Attributions @@ -218,4 +218,4 @@ This project was initially inspired by a [gist](https://gist.github.com/mosheesh Copyright (c) 2015-2021 Richard North and other authors. -See [AUTHORS](https://raw.githubusercontent.com/testcontainers/testcontainers-java/master/AUTHORS) for contributors. +See [AUTHORS](https://raw.githubusercontent.com/testcontainers/testcontainers-java/main/AUTHORS) for contributors. diff --git a/docs/jitpack_dependencies.md b/docs/jitpack_dependencies.md index 23a4c7353ad..30687dcd3bc 100644 --- a/docs/jitpack_dependencies.md +++ b/docs/jitpack_dependencies.md @@ -5,14 +5,14 @@ Use the following dependency description instead: === "Gradle" ```groovy - testImplementation "com.github.testcontainers.testcontainers-java:--artifact name--:master-SNAPSHOT" + testImplementation "com.github.testcontainers.testcontainers-java:--artifact name--:main-SNAPSHOT" ``` === "Maven" ```xml com.github.testcontainers.testcontainers-java --artifact name-- - master-SNAPSHOT + main-SNAPSHOT ``` diff --git a/docs/modules/consul.md b/docs/modules/consul.md index 1580690cad7..d0eafd6b1a8 100644 --- a/docs/modules/consul.md +++ b/docs/modules/consul.md @@ -32,5 +32,5 @@ testImplementation "org.testcontainers:consul:{{latest_version}}" ``` -See [AUTHORS](https://raw.githubusercontent.com/testcontainers/testcontainers-java/master/modules/consul/AUTHORS) for contributors. +See [AUTHORS](https://raw.githubusercontent.com/testcontainers/testcontainers-java/main/modules/consul/AUTHORS) for contributors. diff --git a/docs/modules/databases/jdbc.md b/docs/modules/databases/jdbc.md index e3a077333cd..9e976d872ba 100644 --- a/docs/modules/databases/jdbc.md +++ b/docs/modules/databases/jdbc.md @@ -127,5 +127,5 @@ Note that if you use `@Rule`, you will be given an isolated container for each t Examples/Tests: - * [MySQL](https://github.com/testcontainers/testcontainers-java/blob/master/modules/mysql/src/test/java/org/testcontainers/junit/mysql/SimpleMySQLTest.java) - * [PostgreSQL](https://github.com/testcontainers/testcontainers-java/blob/master/modules/postgresql/src/test/java/org/testcontainers/junit/postgresql/SimplePostgreSQLTest.java) + * [MySQL](https://github.com/testcontainers/testcontainers-java/blob/main/modules/mysql/src/test/java/org/testcontainers/junit/mysql/SimpleMySQLTest.java) + * [PostgreSQL](https://github.com/testcontainers/testcontainers-java/blob/main/modules/postgresql/src/test/java/org/testcontainers/junit/postgresql/SimplePostgreSQLTest.java) diff --git a/docs/modules/databases/mssqlserver.md b/docs/modules/databases/mssqlserver.md index 7ddd5350a40..9d0912f2563 100644 --- a/docs/modules/databases/mssqlserver.md +++ b/docs/modules/databases/mssqlserver.md @@ -49,10 +49,10 @@ Add the following dependency to your `pom.xml`/`build.gradle` file: ## License -See [LICENSE](https://raw.githubusercontent.com/testcontainers/testcontainers-java/master/modules/mssqlserver/LICENSE). +See [LICENSE](https://raw.githubusercontent.com/testcontainers/testcontainers-java/main/modules/mssqlserver/LICENSE). ## Copyright Copyright (c) 2017 - 2019 G DATA Software AG and other authors. -See [AUTHORS](https://raw.githubusercontent.com/testcontainers/testcontainers-java/master/modules/mssqlserver/AUTHORS) for contributors. +See [AUTHORS](https://raw.githubusercontent.com/testcontainers/testcontainers-java/main/modules/mssqlserver/AUTHORS) for contributors. diff --git a/docs/modules/gcloud.md b/docs/modules/gcloud.md index 7431ed0de0f..807b81de433 100644 --- a/docs/modules/gcloud.md +++ b/docs/modules/gcloud.md @@ -53,7 +53,7 @@ And test against the Emulator: See more examples: - * [Full sample code](https://github.com/testcontainers/testcontainers-java/tree/master/modules/gcloud/src/test/java/org/testcontainers/containers/DatastoreEmulatorContainerTest.java) + * [Full sample code](https://github.com/testcontainers/testcontainers-java/tree/main/modules/gcloud/src/test/java/org/testcontainers/containers/DatastoreEmulatorContainerTest.java) * [With Spring Boot](https://github.com/saturnism/testcontainers-gcloud-examples/tree/main/springboot/datastore-example/src/test/java/com/example/springboot/datastore) ### Firestore @@ -72,7 +72,7 @@ And test against the Emulator: See more examples: - * [Full sample code](https://github.com/testcontainers/testcontainers-java/tree/master/modules/gcloud/src/test/java/org/testcontainers/containers/FirestoreEmulatorContainerTest.java) + * [Full sample code](https://github.com/testcontainers/testcontainers-java/tree/main/modules/gcloud/src/test/java/org/testcontainers/containers/FirestoreEmulatorContainerTest.java) * [With Spring Boot](https://github.com/saturnism/testcontainers-gcloud-examples/tree/main/springboot/firestore-example/src/test/java/com/example/springboot/firestore/FirestoreIntegrationTests.java) ### Spanner @@ -103,7 +103,7 @@ And test against the Emulator: See more examples: - * [Full sample code](https://github.com/testcontainers/testcontainers-java/tree/master/modules/gcloud/src/test/java/org/testcontainers/containers/SpannerEmulatorContainerTest.java) + * [Full sample code](https://github.com/testcontainers/testcontainers-java/tree/main/modules/gcloud/src/test/java/org/testcontainers/containers/SpannerEmulatorContainerTest.java) * [With Spring Boot](https://github.com/saturnism/testcontainers-gcloud-examples/tree/main/springboot/spanner-example/src/test/java/com/example/springboot/spanner/SpannerIntegrationTests.java) ### Pub/Sub @@ -134,7 +134,7 @@ And test against the Emulator: See more examples: - * [Full sample code](https://github.com/testcontainers/testcontainers-java/tree/master/modules/gcloud/src/test/java/org/testcontainers/containers/PubSubEmulatorContainerTest.java) + * [Full sample code](https://github.com/testcontainers/testcontainers-java/tree/main/modules/gcloud/src/test/java/org/testcontainers/containers/PubSubEmulatorContainerTest.java) * [With Spring Boot](https://github.com/saturnism/testcontainers-gcloud-examples/tree/main/springboot/pubsub-example/src/test/java/com/example/springboot/pubsub/PubSubIntegrationTests.java) ## Adding this module to your project dependencies diff --git a/docs/modules/vault.md b/docs/modules/vault.md index 577ed91a32f..4a232361464 100644 --- a/docs/modules/vault.md +++ b/docs/modules/vault.md @@ -57,11 +57,11 @@ Add the following dependency to your `pom.xml`/`build.gradle` file: ## License -See [LICENSE](https://raw.githubusercontent.com/testcontainers/testcontainers-java/master/modules/vault/LICENSE). +See [LICENSE](https://raw.githubusercontent.com/testcontainers/testcontainers-java/main/modules/vault/LICENSE). ## Copyright Copyright (c) 2017 Capital One Services, LLC and other authors. -See [AUTHORS](https://raw.githubusercontent.com/testcontainers/testcontainers-java/master/modules/vault/AUTHORS) for contributors. +See [AUTHORS](https://raw.githubusercontent.com/testcontainers/testcontainers-java/main/modules/vault/AUTHORS) for contributors. diff --git a/docs/modules/webdriver_containers.md b/docs/modules/webdriver_containers.md index ec1dc2a407f..7ff4d2d6304 100644 --- a/docs/modules/webdriver_containers.md +++ b/docs/modules/webdriver_containers.md @@ -81,7 +81,7 @@ Note the factory must implement `org.testcontainers.containers.RecordingFileFact ## More examples -A few different examples are shown in [ChromeWebDriverContainerTest.java](https://github.com/testcontainers/testcontainers-java/blob/master/modules/selenium/src/test/java/org/testcontainers/junit/ChromeWebDriverContainerTest.java). +A few different examples are shown in [ChromeWebDriverContainerTest.java](https://github.com/testcontainers/testcontainers-java/blob/main/modules/selenium/src/test/java/org/testcontainers/junit/ChromeWebDriverContainerTest.java). ## Adding this module to your project dependencies From 14687a6cd23d31df43e87216f8d558bb9eb4943d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Fri, 30 Sep 2022 09:23:29 -0500 Subject: [PATCH 0524/3231] Reference to GitHub Discussions (#5928) Take advantage of discussions to have previous conversations with contributors before investing time working on PRs or asking general questions. Co-authored-by: Kevin Wittek --- .github/ISSUE_TEMPLATE/bug_report.yaml | 2 +- .github/ISSUE_TEMPLATE/enhancement.yaml | 2 +- .github/ISSUE_TEMPLATE/feature.yaml | 2 +- docs/contributing.md | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index 9945297ea7a..8196451e88f 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -6,7 +6,7 @@ body: - type: markdown attributes: value: | - Thanks for taking the time to fill out this bug report! Before submitting a `bug`, please make sure there is no existing issue for the one you encountered. + Thanks for taking the time to fill out this bug report! Before submitting a `bug`, please make sure there is no existing issue for the one you encountered and it has been discussed with the team via [discussions](https://github.com/testcontainers/testcontainers-java/discussions) or Slack. - type: dropdown id: module attributes: diff --git a/.github/ISSUE_TEMPLATE/enhancement.yaml b/.github/ISSUE_TEMPLATE/enhancement.yaml index 6d5edab909f..9d65e1f12b8 100644 --- a/.github/ISSUE_TEMPLATE/enhancement.yaml +++ b/.github/ISSUE_TEMPLATE/enhancement.yaml @@ -6,7 +6,7 @@ body: - type: markdown attributes: value: | - Before submitting an `enhancement`, please make sure there is no existing enhancement for the one you are requesting. If so, please provide the following information: + Before submitting an `enhancement`, please make sure there is no existing enhancement for the one you are requesting and it has been discussed with the team via [discussions](https://github.com/testcontainers/testcontainers-java/discussions) or Slack. If so, please provide the following information: - type: dropdown id: module attributes: diff --git a/.github/ISSUE_TEMPLATE/feature.yaml b/.github/ISSUE_TEMPLATE/feature.yaml index 8abeb7d2131..581870d976d 100644 --- a/.github/ISSUE_TEMPLATE/feature.yaml +++ b/.github/ISSUE_TEMPLATE/feature.yaml @@ -6,7 +6,7 @@ body: - type: markdown attributes: value: | - Before submitting a `feature`, please make sure there is no existing feature for the one you are requesting. If so, please provide the following information: + Before submitting a `feature`, please make sure there is no existing feature for the one you are requesting and it has been discussed with the team via [discussions](https://github.com/testcontainers/testcontainers-java/discussions) or Slack. If so, please provide the following information: - type: dropdown id: module attributes: diff --git a/docs/contributing.md b/docs/contributing.md index 1457839a5ec..c8eac36e03b 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -2,9 +2,9 @@ * Star the project on [Github](https://github.com/testcontainers/testcontainers-java) and help spread the word :) * Join our [Slack group](http://slack.testcontainers.org) -* [Post an issue](https://github.com/testcontainers/testcontainers-java/issues) if you find any bugs +* [Start a discussion](https://github.com/testcontainers/testcontainers-java/discussions) if you have an idea, find a possible bug or have a general question. * Contribute improvements or fixes using a [Pull Request](https://github.com/testcontainers/testcontainers-java/pulls). If you're going to contribute, thank you! Please just be sure to: - * discuss with the authors on an issue ticket prior to doing anything big. + * discuss with the authors prior to doing anything big. * follow the style, naming and structure conventions of the rest of the project. * make commits atomic and easy to merge. * when updating documentation, please see [our guidance for documentation contributions](contributing_docs.md). From f0dd61404fd570b91e71513317bde6a5a2599dfa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Fri, 30 Sep 2022 11:59:54 -0500 Subject: [PATCH 0525/3231] Add banner when Ryuk is disabled (#5929) This banner will warn the user about unexpected behavior if Ryuk is disabled. Co-authored-by: Kevin Wittek --- .../java/org/testcontainers/utility/ResourceReaper.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java index ad0c949f6e7..1b99cd2abb4 100644 --- a/core/src/main/java/org/testcontainers/utility/ResourceReaper.java +++ b/core/src/main/java/org/testcontainers/utility/ResourceReaper.java @@ -79,6 +79,15 @@ public static synchronized ResourceReaper instance() { //noinspection deprecation instance = new RyukResourceReaper(); } else { + String ryukDisabledMessage = + "\n" + + "********************************************************************************" + + "\n" + + "Ryuk has been disabled. This can cause unexpected behavior in your environment." + + "\n" + + "********************************************************************************"; + LOGGER.warn(ryukDisabledMessage); + instance = new JVMHookResourceReaper(); } } From e2aeb68a45d2a3579e9a5667977789c24463478f Mon Sep 17 00:00:00 2001 From: Hari Ohm Prasath Date: Fri, 30 Sep 2022 10:14:00 -0700 Subject: [PATCH 0526/3231] Test container example for ImmuDB (#5860) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Test container example using ImmuDB, performing basic operations Fixes #5594 Co-authored-by: Eddú Meléndez Gonzales --- docs/examples.md | 23 +++---- examples/immudb/build.gradle | 14 +++++ examples/immudb/src/test/java/ImmuDbTest.java | 63 +++++++++++++++++++ examples/settings.gradle | 1 + 4 files changed, 90 insertions(+), 11 deletions(-) create mode 100644 examples/immudb/build.gradle create mode 100644 examples/immudb/src/test/java/ImmuDbTest.java diff --git a/docs/examples.md b/docs/examples.md index 14f43d21e23..cd85226e4be 100644 --- a/docs/examples.md +++ b/docs/examples.md @@ -2,14 +2,15 @@ Examples of different use cases provided by Testcontainers can be found below: -- [Kafka Cluster with multiple brokers](https://github.com/testcontainers/testcontainers-java/tree/main/examples/kafka-cluster) -- [Linked containers](https://github.com/testcontainers/testcontainers-java/tree/main/examples/linked-container) -- [Neo4j](https://github.com/testcontainers/testcontainers-java/tree/main/examples/neo4j-container) -- [Redis](https://github.com/testcontainers/testcontainers-java/tree/main/examples/redis-backed-cache) -- [Selenium](https://github.com/testcontainers/testcontainers-java/tree/main/examples/selenium-container) -- [Selenium Module with Cucumber](https://github.com/testcontainers/testcontainers-java/tree/main/examples/cucumber) -- [Singleton Container Pattern](https://github.com/testcontainers/testcontainers-java/tree/main/examples/singleton-container) -- [Solr](https://github.com/testcontainers/testcontainers-java/tree/main/examples/solr-container) -- [Spring Boot](https://github.com/testcontainers/testcontainers-java/tree/main/examples/spring-boot) -- [Spring Boot with Kotlin](https://github.com/testcontainers/testcontainers-java/tree/main/examples/spring-boot-kotlin-redis) -- [TestNG](https://github.com/testcontainers/testcontainers-java/tree/main/examples/redis-backed-cache-testng) +- [Kafka Cluster with multiple brokers](https://github.com/testcontainers/testcontainers-java/tree/master/examples/kafka-cluster) +- [Linked containers](https://github.com/testcontainers/testcontainers-java/tree/master/examples/linked-container) +- [Neo4j](https://github.com/testcontainers/testcontainers-java/tree/master/examples/neo4j-container) +- [Redis](https://github.com/testcontainers/testcontainers-java/tree/master/examples/redis-backed-cache) +- [Selenium](https://github.com/testcontainers/testcontainers-java/tree/master/examples/selenium-container) +- [Selenium Module with Cucumber](https://github.com/testcontainers/testcontainers-java/tree/master/examples/cucumber) +- [Singleton Container Pattern](https://github.com/testcontainers/testcontainers-java/tree/master/examples/singleton-container) +- [Solr](https://github.com/testcontainers/testcontainers-java/tree/master/examples/solr-container) +- [Spring Boot](https://github.com/testcontainers/testcontainers-java/tree/master/examples/spring-boot) +- [Spring Boot with Kotlin](https://github.com/testcontainers/testcontainers-java/tree/master/examples/spring-boot-kotlin-redis) +- [TestNG](https://github.com/testcontainers/testcontainers-java/tree/master/examples/redis-backed-cache-testng) +- [ImmuDb](https://github.com/testcontainers/testcontainers-java/tree/master/examples/immudb) diff --git a/examples/immudb/build.gradle b/examples/immudb/build.gradle new file mode 100644 index 00000000000..5ff55a2205a --- /dev/null +++ b/examples/immudb/build.gradle @@ -0,0 +1,14 @@ +plugins { + id 'java' +} + +repositories { + mavenCentral() +} + +dependencies { + compileOnly 'org.slf4j:slf4j-api:2.0.0' + implementation 'io.codenotary:immudb4j:0.9.10.2' + testImplementation 'org.testcontainers:testcontainers' + testImplementation 'org.assertj:assertj-core:3.23.1' +} diff --git a/examples/immudb/src/test/java/ImmuDbTest.java b/examples/immudb/src/test/java/ImmuDbTest.java new file mode 100644 index 00000000000..fd515ddd4aa --- /dev/null +++ b/examples/immudb/src/test/java/ImmuDbTest.java @@ -0,0 +1,63 @@ +import io.codenotary.immudb4j.Entry; +import io.codenotary.immudb4j.ImmuClient; +import io.codenotary.immudb4j.exceptions.CorruptedDataException; +import io.codenotary.immudb4j.exceptions.VerificationException; +import org.junit.Assert; +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Test; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.wait.strategy.Wait; +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Test class for the ImmuDbClient. + */ +public class ImmuDbTest { + + // Default port for the ImmuDb server + private static final int IMMUDB_PORT = 3322; + // Default username for the ImmuDb server + private final String IMMUDB_USER = "immudb"; + // Default password for the ImmuDb server + private final String IMMUDB_PASSWORD = "immudb"; + // Default database name for the ImmuDb server + private final String IMMUDB_DATABASE = "defaultdb"; + + // Test container for the ImmuDb database, with the latest version of the image and exposed port + @ClassRule + public static final GenericContainer immuDbContainer = new GenericContainer<>("codenotary/immudb:1.3") + .withExposedPorts(IMMUDB_PORT).waitingFor( + Wait.forLogMessage(".*Web API server enabled.*", 1) + ); + // ImmuClient used to interact with the DB + private ImmuClient immuClient; + + @Before + public void setUp() { + this.immuClient = ImmuClient.newBuilder() + .withServerUrl(immuDbContainer.getHost()) + .withServerPort(immuDbContainer.getMappedPort(IMMUDB_PORT)) + .build(); + this.immuClient.login(IMMUDB_USER, IMMUDB_PASSWORD); + this.immuClient.useDatabase(IMMUDB_DATABASE); + } + + @Test + public void testGetValue() { + try { + immuClient.set("test1", "test2".getBytes()); + + Entry entry = immuClient.verifiedGet("test1"); + + if (entry != null) { + byte[] value = entry.getValue(); + assertThat(new String(value)).isEqualTo("test2"); + } else { + Assert.fail(); + } + } catch (CorruptedDataException | VerificationException e) { + Assert.fail(); + } + } +} diff --git a/examples/settings.gradle b/examples/settings.gradle index 7194134350d..2a508013928 100644 --- a/examples/settings.gradle +++ b/examples/settings.gradle @@ -30,6 +30,7 @@ include 'solr-container' include 'spring-boot' include 'cucumber' include 'spring-boot-kotlin-redis' +include 'immudb' ext.isCI = System.getenv("CI") != null From a71b5310185ee69634557557761dfa1ec73a0bee Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 30 Sep 2022 17:15:08 +0000 Subject: [PATCH 0527/3231] Bump slf4j-api from 2.0.0 to 2.0.3 in /core Bumps [slf4j-api](https://github.com/qos-ch/slf4j) from 2.0.0 to 2.0.3. - [Release notes](https://github.com/qos-ch/slf4j/releases) - [Commits](https://github.com/qos-ch/slf4j/compare/v_2.0.0...v_2.0.3) --- updated-dependencies: - dependency-name: org.slf4j:slf4j-api dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 4072c25db37..e1e38eaeb18 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -61,7 +61,7 @@ configurations.all { dependencies { api 'junit:junit:4.13.2' - api 'org.slf4j:slf4j-api:2.0.0' + api 'org.slf4j:slf4j-api:2.0.3' compileOnly 'org.jetbrains:annotations:23.0.0' testCompileOnly 'org.jetbrains:annotations:23.0.0' api 'org.apache.commons:commons-compress:1.21' From a3d94ebf032eeaca9ed3bb5547de48fa405bec19 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Oct 2022 01:03:24 +0000 Subject: [PATCH 0528/3231] Bump s3 from 2.17.266 to 2.17.285 in /modules/localstack Bumps s3 from 2.17.266 to 2.17.285. --- updated-dependencies: - dependency-name: software.amazon.awssdk:s3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 9759d193475..5ef16b2ca58 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -7,6 +7,6 @@ dependencies { testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.313' testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.295' testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.313' - testImplementation 'software.amazon.awssdk:s3:2.17.266' + testImplementation 'software.amazon.awssdk:s3:2.17.285' testImplementation 'org.assertj:assertj-core:3.23.1' } From 88f23b5fb5bbd25941617b37639f3c2cf852bb27 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Oct 2022 01:03:27 +0000 Subject: [PATCH 0529/3231] Bump aws-java-sdk-sqs from 1.12.295 to 1.12.314 in /modules/localstack Bumps [aws-java-sdk-sqs](https://github.com/aws/aws-sdk-java) from 1.12.295 to 1.12.314. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.295...1.12.314) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-sqs dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index 9759d193475..2666932fbbc 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -5,7 +5,7 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.313' testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.313' - testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.295' + testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.314' testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.313' testImplementation 'software.amazon.awssdk:s3:2.17.266' testImplementation 'org.assertj:assertj-core:3.23.1' From 3802c2934ded989bb5a433e5d48a5d036a9042f5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Oct 2022 01:03:37 +0000 Subject: [PATCH 0530/3231] Bump google-cloud-firestore from 3.4.2 to 3.5.0 in /modules/gcloud Bumps [google-cloud-firestore](https://github.com/googleapis/java-firestore) from 3.4.2 to 3.5.0. - [Release notes](https://github.com/googleapis/java-firestore/releases) - [Changelog](https://github.com/googleapis/java-firestore/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/java-firestore/compare/v3.4.2...v3.5.0) --- updated-dependencies: - dependency-name: com.google.cloud:google-cloud-firestore dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/gcloud/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle index 30c11325653..b52e6ea0dd0 100644 --- a/modules/gcloud/build.gradle +++ b/modules/gcloud/build.gradle @@ -4,7 +4,7 @@ dependencies { api project(':testcontainers') testImplementation 'com.google.cloud:google-cloud-datastore:2.11.4' - testImplementation 'com.google.cloud:google-cloud-firestore:3.4.2' + testImplementation 'com.google.cloud:google-cloud-firestore:3.5.0' testImplementation 'com.google.cloud:google-cloud-pubsub:1.120.18' testImplementation 'com.google.cloud:google-cloud-spanner:6.29.0' testImplementation 'com.google.cloud:google-cloud-bigtable:2.11.1' From a2b1e15f1b008d100fe8b5761a59153a335627ac Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Oct 2022 01:03:38 +0000 Subject: [PATCH 0531/3231] Bump kafka-clients from 3.2.1 to 3.3.0 in /modules/kafka Bumps kafka-clients from 3.2.1 to 3.3.0. --- updated-dependencies: - dependency-name: org.apache.kafka:kafka-clients dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/kafka/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/kafka/build.gradle b/modules/kafka/build.gradle index ebd9c84120c..70907510afe 100644 --- a/modules/kafka/build.gradle +++ b/modules/kafka/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Kafka" dependencies { api project(':testcontainers') - testImplementation 'org.apache.kafka:kafka-clients:3.2.1' + testImplementation 'org.apache.kafka:kafka-clients:3.3.0' testImplementation 'org.assertj:assertj-core:3.23.1' testImplementation 'com.google.guava:guava:23.0' } From d5fe8813f043eda38a5e257f3066bd3520e91314 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Oct 2022 01:03:39 +0000 Subject: [PATCH 0532/3231] Bump cucumber-java from 7.6.0 to 7.8.0 in /examples Bumps [cucumber-java](https://github.com/cucumber/cucumber-jvm) from 7.6.0 to 7.8.0. - [Release notes](https://github.com/cucumber/cucumber-jvm/releases) - [Changelog](https://github.com/cucumber/cucumber-jvm/blob/main/CHANGELOG.md) - [Commits](https://github.com/cucumber/cucumber-jvm/compare/v7.6.0...v7.8.0) --- updated-dependencies: - dependency-name: io.cucumber:cucumber-java dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- examples/cucumber/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cucumber/build.gradle b/examples/cucumber/build.gradle index 9d0da7a5e33..1b9fe04efd8 100644 --- a/examples/cucumber/build.gradle +++ b/examples/cucumber/build.gradle @@ -10,7 +10,7 @@ dependencies { implementation 'org.seleniumhq.selenium:selenium-remote-driver:3.141.59' implementation 'org.seleniumhq.selenium:selenium-firefox-driver:3.141.59' implementation 'org.seleniumhq.selenium:selenium-chrome-driver:3.141.59' - testImplementation 'io.cucumber:cucumber-java:7.6.0' + testImplementation 'io.cucumber:cucumber-java:7.8.0' testImplementation 'io.cucumber:cucumber-junit:7.8.0' testImplementation 'org.testcontainers:selenium' } From 499f2ce17917e4ac181d08c748fab929014c7912 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Oct 2022 01:03:45 +0000 Subject: [PATCH 0533/3231] Bump google-cloud-spanner from 6.29.0 to 6.31.0 in /modules/gcloud Bumps [google-cloud-spanner](https://github.com/googleapis/java-spanner) from 6.29.0 to 6.31.0. - [Release notes](https://github.com/googleapis/java-spanner/releases) - [Changelog](https://github.com/googleapis/java-spanner/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/java-spanner/compare/v6.29.0...v6.31.0) --- updated-dependencies: - dependency-name: com.google.cloud:google-cloud-spanner dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/gcloud/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle index 30c11325653..8be3aab89a5 100644 --- a/modules/gcloud/build.gradle +++ b/modules/gcloud/build.gradle @@ -6,7 +6,7 @@ dependencies { testImplementation 'com.google.cloud:google-cloud-datastore:2.11.4' testImplementation 'com.google.cloud:google-cloud-firestore:3.4.2' testImplementation 'com.google.cloud:google-cloud-pubsub:1.120.18' - testImplementation 'com.google.cloud:google-cloud-spanner:6.29.0' + testImplementation 'com.google.cloud:google-cloud-spanner:6.31.0' testImplementation 'com.google.cloud:google-cloud-bigtable:2.11.1' testImplementation 'org.assertj:assertj-core:3.23.1' } From 3aa35988d5e83628846e27f3197b568bde58c5ff Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Oct 2022 03:52:29 +0000 Subject: [PATCH 0534/3231] Bump google-cloud-bigtable from 2.11.1 to 2.13.0 in /modules/gcloud Bumps [google-cloud-bigtable](https://github.com/googleapis/java-bigtable) from 2.11.1 to 2.13.0. - [Release notes](https://github.com/googleapis/java-bigtable/releases) - [Changelog](https://github.com/googleapis/java-bigtable/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/java-bigtable/compare/v2.11.1...v2.13.0) --- updated-dependencies: - dependency-name: com.google.cloud:google-cloud-bigtable dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/gcloud/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gcloud/build.gradle b/modules/gcloud/build.gradle index 3258063f5dd..a5a72db0982 100644 --- a/modules/gcloud/build.gradle +++ b/modules/gcloud/build.gradle @@ -7,6 +7,6 @@ dependencies { testImplementation 'com.google.cloud:google-cloud-firestore:3.5.0' testImplementation 'com.google.cloud:google-cloud-pubsub:1.120.18' testImplementation 'com.google.cloud:google-cloud-spanner:6.31.0' - testImplementation 'com.google.cloud:google-cloud-bigtable:2.11.1' + testImplementation 'com.google.cloud:google-cloud-bigtable:2.13.0' testImplementation 'org.assertj:assertj-core:3.23.1' } From bd6abf933eb383b0d82a805ca49091fb6fdd2f24 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Oct 2022 22:00:34 +0000 Subject: [PATCH 0535/3231] Bump aws-java-sdk-s3 from 1.12.313 to 1.12.314 in /modules/localstack Bumps [aws-java-sdk-s3](https://github.com/aws/aws-sdk-java) from 1.12.313 to 1.12.314. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.313...1.12.314) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-s3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index b042ee575da..7d263e3e492 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Localstack" dependencies { api project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.313' + compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.314' testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.313' testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.314' testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.313' From 554ec64d4aea8aa6ca50fa0bc654ced8057ab195 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Oct 2022 22:00:37 +0000 Subject: [PATCH 0536/3231] Bump aws-java-sdk-dynamodb in /modules/dynalite Bumps [aws-java-sdk-dynamodb](https://github.com/aws/aws-sdk-java) from 1.12.313 to 1.12.314. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.313...1.12.314) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-dynamodb dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/dynalite/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/dynalite/build.gradle b/modules/dynalite/build.gradle index e9cd2111f82..18ec4a61ac6 100644 --- a/modules/dynalite/build.gradle +++ b/modules/dynalite/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Dynalite" dependencies { api project(':testcontainers') - compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.12.313' - testImplementation 'com.amazonaws:aws-java-sdk-dynamodb:1.12.295' + compileOnly 'com.amazonaws:aws-java-sdk-dynamodb:1.12.314' + testImplementation 'com.amazonaws:aws-java-sdk-dynamodb:1.12.314' testImplementation 'org.assertj:assertj-core:3.23.1' } From f04648425bbc4a7fcf2d26d933e71e30a7886f18 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Oct 2022 22:00:39 +0000 Subject: [PATCH 0537/3231] Bump aws-java-sdk-logs from 1.12.313 to 1.12.314 in /modules/localstack Bumps [aws-java-sdk-logs](https://github.com/aws/aws-sdk-java) from 1.12.313 to 1.12.314. - [Release notes](https://github.com/aws/aws-sdk-java/releases) - [Changelog](https://github.com/aws/aws-sdk-java/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java/compare/1.12.313...1.12.314) --- updated-dependencies: - dependency-name: com.amazonaws:aws-java-sdk-logs dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/localstack/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/localstack/build.gradle b/modules/localstack/build.gradle index b042ee575da..d632c57cbfb 100644 --- a/modules/localstack/build.gradle +++ b/modules/localstack/build.gradle @@ -6,7 +6,7 @@ dependencies { compileOnly 'com.amazonaws:aws-java-sdk-s3:1.12.313' testImplementation 'com.amazonaws:aws-java-sdk-s3:1.12.313' testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.12.314' - testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.313' + testImplementation 'com.amazonaws:aws-java-sdk-logs:1.12.314' testImplementation 'software.amazon.awssdk:s3:2.17.285' testImplementation 'org.assertj:assertj-core:3.23.1' } From a3889d18f8cd33e40d73bf65b11fd44c8364bdee Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Oct 2022 22:01:00 +0000 Subject: [PATCH 0538/3231] Bump actions/cache from 3.0.8 to 3.0.9 Bumps [actions/cache](https://github.com/actions/cache) from 3.0.8 to 3.0.9. - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/v3.0.8...v3.0.9) --- updated-dependencies: - dependency-name: actions/cache dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/ci-examples.yml | 4 ++-- .github/workflows/ci.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci-examples.yml b/.github/workflows/ci-examples.yml index 2cdc9f8ed69..527c2eccfeb 100644 --- a/.github/workflows/ci-examples.yml +++ b/.github/workflows/ci-examples.yml @@ -25,7 +25,7 @@ jobs: java-version: '8.0.302' distribution: temurin - name: Cache Gradle Home files - uses: actions/cache@v3.0.8 + uses: actions/cache@v3.0.9 continue-on-error: true with: path: ~/.gradle/caches @@ -53,7 +53,7 @@ jobs: java-version: '8.0.302' distribution: temurin - name: Cache Gradle Home files - uses: actions/cache@v3.0.8 + uses: actions/cache@v3.0.9 continue-on-error: true with: path: ~/.gradle/caches diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 80f20fb0c0c..f55a5355c4e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,7 +25,7 @@ jobs: java-version: '8.0.302' distribution: temurin - name: Cache Gradle Home files - uses: actions/cache@v3.0.8 + uses: actions/cache@v3.0.9 with: path: ~/.gradle/caches key: ${{ runner.os }}-gradle-home-testmatrix-${{ hashFiles('**/*.gradle') }} @@ -51,7 +51,7 @@ jobs: java-version: '8.0.302' distribution: temurin - name: Cache Gradle Home files - uses: actions/cache@v3.0.8 + uses: actions/cache@v3.0.9 continue-on-error: true with: path: ~/.gradle/caches From d4c8de272e7a9c358e9066baed82cab097164b42 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Oct 2022 22:01:07 +0000 Subject: [PATCH 0539/3231] Bump slf4j-api from 2.0.0 to 2.0.3 in /examples Bumps [slf4j-api](https://github.com/qos-ch/slf4j) from 2.0.0 to 2.0.3. - [Release notes](https://github.com/qos-ch/slf4j/releases) - [Commits](https://github.com/qos-ch/slf4j/compare/v_2.0.0...v_2.0.3) --- updated-dependencies: - dependency-name: org.slf4j:slf4j-api dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/immudb/build.gradle | 2 +- examples/linked-container/build.gradle | 2 +- examples/redis-backed-cache-testng/build.gradle | 2 +- examples/redis-backed-cache/build.gradle | 2 +- examples/singleton-container/build.gradle | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/immudb/build.gradle b/examples/immudb/build.gradle index 5ff55a2205a..1d1940cb85b 100644 --- a/examples/immudb/build.gradle +++ b/examples/immudb/build.gradle @@ -7,7 +7,7 @@ repositories { } dependencies { - compileOnly 'org.slf4j:slf4j-api:2.0.0' + compileOnly 'org.slf4j:slf4j-api:2.0.3' implementation 'io.codenotary:immudb4j:0.9.10.2' testImplementation 'org.testcontainers:testcontainers' testImplementation 'org.assertj:assertj-core:3.23.1' diff --git a/examples/linked-container/build.gradle b/examples/linked-container/build.gradle index fe230f3a22e..50e38518d39 100644 --- a/examples/linked-container/build.gradle +++ b/examples/linked-container/build.gradle @@ -6,7 +6,7 @@ repositories { mavenCentral() } dependencies { - compileOnly 'org.slf4j:slf4j-api:2.0.0' + compileOnly 'org.slf4j:slf4j-api:2.0.3' implementation 'com.squareup.okhttp3:okhttp:4.10.0' implementation 'org.json:json:20220924' testImplementation 'org.postgresql:postgresql:42.5.0' diff --git a/examples/redis-backed-cache-testng/build.gradle b/examples/redis-backed-cache-testng/build.gradle index 799f3f89925..c19dd3a012f 100644 --- a/examples/redis-backed-cache-testng/build.gradle +++ b/examples/redis-backed-cache-testng/build.gradle @@ -7,7 +7,7 @@ repositories { } dependencies { - compileOnly 'org.slf4j:slf4j-api:2.0.0' + compileOnly 'org.slf4j:slf4j-api:2.0.3' implementation 'redis.clients:jedis:4.2.3' implementation 'com.google.code.gson:gson:2.9.1' implementation 'com.google.guava:guava:23.0' diff --git a/examples/redis-backed-cache/build.gradle b/examples/redis-backed-cache/build.gradle index 1d840f11871..82197ddc9b5 100644 --- a/examples/redis-backed-cache/build.gradle +++ b/examples/redis-backed-cache/build.gradle @@ -7,7 +7,7 @@ repositories { } dependencies { - compileOnly 'org.slf4j:slf4j-api:2.0.0' + compileOnly 'org.slf4j:slf4j-api:2.0.3' implementation 'redis.clients:jedis:4.2.3' implementation 'com.google.code.gson:gson:2.9.1' implementation 'com.google.guava:guava:23.0' diff --git a/examples/singleton-container/build.gradle b/examples/singleton-container/build.gradle index d540a8386ab..9c56d53faa3 100644 --- a/examples/singleton-container/build.gradle +++ b/examples/singleton-container/build.gradle @@ -11,7 +11,7 @@ dependencies { implementation 'redis.clients:jedis:4.2.3' implementation 'com.google.code.gson:gson:2.9.1' implementation 'com.google.guava:guava:23.0' - compileOnly 'org.slf4j:slf4j-api:2.0.0' + compileOnly 'org.slf4j:slf4j-api:2.0.3' testImplementation 'ch.qos.logback:logback-classic:1.2.11' testImplementation 'org.testcontainers:testcontainers' From 38e6cc18d77d4cc7b58da1842508142539955795 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Oct 2022 22:04:24 +0000 Subject: [PATCH 0540/3231] Bump azure-cosmos from 4.36.0 to 4.37.0 in /modules/azure Bumps [azure-cosmos](https://github.com/Azure/azure-sdk-for-java) from 4.36.0 to 4.37.0. - [Release notes](https://github.com/Azure/azure-sdk-for-java/releases) - [Commits](https://github.com/Azure/azure-sdk-for-java/compare/azure-cosmos_4.36.0...azure-cosmos_4.37.0) --- updated-dependencies: - dependency-name: com.azure:azure-cosmos dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/azure/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/azure/build.gradle b/modules/azure/build.gradle index 43280db4a88..78c6500d856 100644 --- a/modules/azure/build.gradle +++ b/modules/azure/build.gradle @@ -6,5 +6,5 @@ dependencies { shaded 'com.squareup.okhttp3:okhttp:4.10.0' testImplementation 'org.assertj:assertj-core:3.23.1' - testImplementation 'com.azure:azure-cosmos:4.36.0' + testImplementation 'com.azure:azure-cosmos:4.37.0' } From 2798d3403b9099b9bc4253f71ed2f4840a966e75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Mon, 3 Oct 2022 09:23:22 -0500 Subject: [PATCH 0541/3231] Bump kotlin plugins version to 1.7.20 (#5945) --- examples/spring-boot-kotlin-redis/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/spring-boot-kotlin-redis/build.gradle b/examples/spring-boot-kotlin-redis/build.gradle index 6a98573172c..c0ec19f203c 100644 --- a/examples/spring-boot-kotlin-redis/build.gradle +++ b/examples/spring-boot-kotlin-redis/build.gradle @@ -1,7 +1,7 @@ plugins { id("org.springframework.boot") version "2.7.4" - id("org.jetbrains.kotlin.jvm") version "1.7.10" - id("org.jetbrains.kotlin.plugin.spring") version "1.7.10" + id("org.jetbrains.kotlin.jvm") version "1.7.20" + id("org.jetbrains.kotlin.plugin.spring") version "1.7.20" } apply plugin: 'io.spring.dependency-management' From 318bfc6833482c39f663cdbb0cceaecf46b87202 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Oct 2022 14:31:09 +0000 Subject: [PATCH 0542/3231] Bump javassist from 3.29.0-GA to 3.29.2-GA in /modules/hivemq Bumps [javassist](https://github.com/jboss-javassist/javassist) from 3.29.0-GA to 3.29.2-GA. - [Release notes](https://github.com/jboss-javassist/javassist/releases) - [Changelog](https://github.com/jboss-javassist/javassist/blob/master/Changes.md) - [Commits](https://github.com/jboss-javassist/javassist/commits) --- updated-dependencies: - dependency-name: org.javassist:javassist dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/hivemq/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/hivemq/build.gradle b/modules/hivemq/build.gradle index a12b3866e4d..1da5ce69645 100644 --- a/modules/hivemq/build.gradle +++ b/modules/hivemq/build.gradle @@ -6,7 +6,7 @@ dependencies { shaded("org.apache.commons:commons-lang3:3.12.0") shaded("commons-io:commons-io:2.11.0") - shaded("org.javassist:javassist:3.29.0-GA") + shaded("org.javassist:javassist:3.29.2-GA") shaded("org.jboss.shrinkwrap:shrinkwrap-api:1.2.6") shaded("org.jboss.shrinkwrap:shrinkwrap-impl-base:1.2.6") shaded("net.lingala.zip4j:zip4j:2.11.2") From 03de1ce271fab9f4499097a9ed81cdee5e575450 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez?= Date: Mon, 3 Oct 2022 12:44:00 -0500 Subject: [PATCH 0543/3231] Fix example docs links --- docs/examples.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/examples.md b/docs/examples.md index cd85226e4be..e7578c8b375 100644 --- a/docs/examples.md +++ b/docs/examples.md @@ -2,15 +2,15 @@ Examples of different use cases provided by Testcontainers can be found below: -- [Kafka Cluster with multiple brokers](https://github.com/testcontainers/testcontainers-java/tree/master/examples/kafka-cluster) -- [Linked containers](https://github.com/testcontainers/testcontainers-java/tree/master/examples/linked-container) -- [Neo4j](https://github.com/testcontainers/testcontainers-java/tree/master/examples/neo4j-container) -- [Redis](https://github.com/testcontainers/testcontainers-java/tree/master/examples/redis-backed-cache) -- [Selenium](https://github.com/testcontainers/testcontainers-java/tree/master/examples/selenium-container) -- [Selenium Module with Cucumber](https://github.com/testcontainers/testcontainers-java/tree/master/examples/cucumber) -- [Singleton Container Pattern](https://github.com/testcontainers/testcontainers-java/tree/master/examples/singleton-container) -- [Solr](https://github.com/testcontainers/testcontainers-java/tree/master/examples/solr-container) -- [Spring Boot](https://github.com/testcontainers/testcontainers-java/tree/master/examples/spring-boot) -- [Spring Boot with Kotlin](https://github.com/testcontainers/testcontainers-java/tree/master/examples/spring-boot-kotlin-redis) -- [TestNG](https://github.com/testcontainers/testcontainers-java/tree/master/examples/redis-backed-cache-testng) -- [ImmuDb](https://github.com/testcontainers/testcontainers-java/tree/master/examples/immudb) +- [Kafka Cluster with multiple brokers](https://github.com/testcontainers/testcontainers-java/tree/main/examples/kafka-cluster) +- [Linked containers](https://github.com/testcontainers/testcontainers-java/tree/main/examples/linked-container) +- [Neo4j](https://github.com/testcontainers/testcontainers-java/tree/main/examples/neo4j-container) +- [Redis](https://github.com/testcontainers/testcontainers-java/tree/main/examples/redis-backed-cache) +- [Selenium](https://github.com/testcontainers/testcontainers-java/tree/main/examples/selenium-container) +- [Selenium Module with Cucumber](https://github.com/testcontainers/testcontainers-java/tree/main/examples/cucumber) +- [Singleton Container Pattern](https://github.com/testcontainers/testcontainers-java/tree/main/examples/singleton-container) +- [Solr](https://github.com/testcontainers/testcontainers-java/tree/main/examples/solr-container) +- [Spring Boot](https://github.com/testcontainers/testcontainers-java/tree/main/examples/spring-boot) +- [Spring Boot with Kotlin](https://github.com/testcontainers/testcontainers-java/tree/main/examples/spring-boot-kotlin-redis) +- [TestNG](https://github.com/testcontainers/testcontainers-java/tree/main/examples/redis-backed-cache-testng) +- [ImmuDb](https://github.com/testcontainers/testcontainers-java/tree/main/examples/immudb) From a8ec3e6da1c236b25bfba8d071c27ebbe57e938a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez?= Date: Mon, 3 Oct 2022 12:47:25 -0500 Subject: [PATCH 0544/3231] Fix main branch name in ci.yml --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f55a5355c4e..b406f256ae2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,7 +2,7 @@ name: CI on: pull_request: {} - push: { branches: [ maing ] } + push: { branches: [ main ] } permissions: contents: read From c2a741d3b77531fe196025b559d0ad2fb655993e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Mon, 3 Oct 2022 13:35:08 -0500 Subject: [PATCH 0545/3231] Bump logback-classic versions (#5948) `ch.qos.logback:logback-classic` 1.3.x is compatible with slf4j 2.x. Currently, logs are not displayed. --- build.gradle | 2 +- docs/examples/spock/redis/build.gradle | 2 +- examples/kafka-cluster/build.gradle | 2 +- examples/linked-container/build.gradle | 2 +- examples/redis-backed-cache-testng/build.gradle | 2 +- examples/redis-backed-cache/build.gradle | 2 +- examples/singleton-container/build.gradle | 2 +- modules/hivemq/build.gradle | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/build.gradle b/build.gradle index a53bd19298d..9e6aa6020e4 100644 --- a/build.gradle +++ b/build.gradle @@ -125,7 +125,7 @@ subprojects { } dependencies { - testImplementation 'ch.qos.logback:logback-classic:1.2.3' + testImplementation 'ch.qos.logback:logback-classic:1.3.3' } spotless { diff --git a/docs/examples/spock/redis/build.gradle b/docs/examples/spock/redis/build.gradle index 1bff637a7fb..6699788d026 100644 --- a/docs/examples/spock/redis/build.gradle +++ b/docs/examples/spock/redis/build.gradle @@ -7,5 +7,5 @@ dependencies { api "io.lettuce:lettuce-core:5.2.0.RELEASE" testImplementation 'org.spockframework:spock-core:1.2-groovy-2.5' testImplementation project(":spock") - testImplementation 'ch.qos.logback:logback-classic:1.2.3' + testImplementation 'ch.qos.logback:logback-classic:1.3.3' } diff --git a/examples/kafka-cluster/build.gradle b/examples/kafka-cluster/build.gradle index 08d3734a956..5212bc33fa9 100644 --- a/examples/kafka-cluster/build.gradle +++ b/examples/kafka-cluster/build.gradle @@ -13,5 +13,5 @@ dependencies { testImplementation 'org.apache.kafka:kafka-clients:3.3.0' testImplementation 'org.assertj:assertj-core:3.23.1' testImplementation 'com.google.guava:guava:23.0' - testImplementation 'ch.qos.logback:logback-classic:1.2.11' + testImplementation 'ch.qos.logback:logback-classic:1.3.3' } diff --git a/examples/linked-container/build.gradle b/examples/linked-container/build.gradle index 50e38518d39..297bcb5143f 100644 --- a/examples/linked-container/build.gradle +++ b/examples/linked-container/build.gradle @@ -10,7 +10,7 @@ dependencies { implementation 'com.squareup.okhttp3:okhttp:4.10.0' implementation 'org.json:json:20220924' testImplementation 'org.postgresql:postgresql:42.5.0' - testImplementation 'ch.qos.logback:logback-classic:1.2.11' + testImplementation 'ch.qos.logback:logback-classic:1.3.3' testImplementation 'org.testcontainers:postgresql' testImplementation 'org.assertj:assertj-core:3.23.1' } diff --git a/examples/redis-backed-cache-testng/build.gradle b/examples/redis-backed-cache-testng/build.gradle index c19dd3a012f..8e3394723ba 100644 --- a/examples/redis-backed-cache-testng/build.gradle +++ b/examples/redis-backed-cache-testng/build.gradle @@ -12,7 +12,7 @@ dependencies { implementation 'com.google.code.gson:gson:2.9.1' implementation 'com.google.guava:guava:23.0' testImplementation 'org.testcontainers:testcontainers' - testImplementation 'ch.qos.logback:logback-classic:1.2.11' + testImplementation 'ch.qos.logback:logback-classic:1.3.3' testImplementation 'org.testng:testng:7.5' testImplementation 'org.assertj:assertj-core:3.23.1' } diff --git a/examples/redis-backed-cache/build.gradle b/examples/redis-backed-cache/build.gradle index 82197ddc9b5..ed794088790 100644 --- a/examples/redis-backed-cache/build.gradle +++ b/examples/redis-backed-cache/build.gradle @@ -13,6 +13,6 @@ dependencies { implementation 'com.google.guava:guava:23.0' testImplementation 'org.testcontainers:testcontainers' testImplementation 'junit:junit:4.13.2' - testImplementation 'ch.qos.logback:logback-classic:1.2.11' + testImplementation 'ch.qos.logback:logback-classic:1.3.3' testImplementation 'org.assertj:assertj-core:3.23.1' } diff --git a/examples/singleton-container/build.gradle b/examples/singleton-container/build.gradle index 9c56d53faa3..3ef81b866d4 100644 --- a/examples/singleton-container/build.gradle +++ b/examples/singleton-container/build.gradle @@ -13,7 +13,7 @@ dependencies { implementation 'com.google.guava:guava:23.0' compileOnly 'org.slf4j:slf4j-api:2.0.3' - testImplementation 'ch.qos.logback:logback-classic:1.2.11' + testImplementation 'ch.qos.logback:logback-classic:1.3.3' testImplementation 'org.testcontainers:testcontainers' testImplementation 'org.assertj:assertj-core:3.23.1' } diff --git a/modules/hivemq/build.gradle b/modules/hivemq/build.gradle index a12b3866e4d..3d6b653f6d1 100644 --- a/modules/hivemq/build.gradle +++ b/modules/hivemq/build.gradle @@ -16,7 +16,7 @@ dependencies { testImplementation("com.hivemq:hivemq-extension-sdk:4.9.0") testImplementation("com.hivemq:hivemq-mqtt-client:1.3.0") testImplementation("org.apache.httpcomponents:httpclient:4.5.13") - testImplementation("ch.qos.logback:logback-classic:1.4.1") + testImplementation("ch.qos.logback:logback-classic:1.4.3") testImplementation 'org.assertj:assertj-core:3.23.1' testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.9.1") } From abf87abcae037e4ddf124c888c21e2a8a65180a8 Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Tue, 4 Oct 2022 16:23:50 +0200 Subject: [PATCH 0546/3231] Rollback back to slf4j-api 1.7.36 (#5951) Reverts #5794, #5942, #5875, #5848, #5794, #5791 --- core/build.gradle | 2 +- examples/immudb/build.gradle | 2 +- examples/linked-container/build.gradle | 2 +- examples/redis-backed-cache-testng/build.gradle | 2 +- examples/redis-backed-cache/build.gradle | 2 +- examples/singleton-container/build.gradle | 2 +- test-support/build.gradle | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/core/build.gradle b/core/build.gradle index c20ebc1d9b0..2967b35d4ff 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -61,7 +61,7 @@ configurations.all { dependencies { api 'junit:junit:4.13.2' - api 'org.slf4j:slf4j-api:2.0.3' + api 'org.slf4j:slf4j-api:1.7.36' compileOnly 'org.jetbrains:annotations:23.0.0' testCompileOnly 'org.jetbrains:annotations:23.0.0' api 'org.apache.commons:commons-compress:1.21' diff --git a/examples/immudb/build.gradle b/examples/immudb/build.gradle index 1d1940cb85b..b2e6fcdba43 100644 --- a/examples/immudb/build.gradle +++ b/examples/immudb/build.gradle @@ -7,7 +7,7 @@ repositories { } dependencies { - compileOnly 'org.slf4j:slf4j-api:2.0.3' + compileOnly 'org.slf4j:slf4j-api:1.7.36' implementation 'io.codenotary:immudb4j:0.9.10.2' testImplementation 'org.testcontainers:testcontainers' testImplementation 'org.assertj:assertj-core:3.23.1' diff --git a/examples/linked-container/build.gradle b/examples/linked-container/build.gradle index 297bcb5143f..cf3a3331cc3 100644 --- a/examples/linked-container/build.gradle +++ b/examples/linked-container/build.gradle @@ -6,7 +6,7 @@ repositories { mavenCentral() } dependencies { - compileOnly 'org.slf4j:slf4j-api:2.0.3' + compileOnly 'org.slf4j:slf4j-api:1.7.36' implementation 'com.squareup.okhttp3:okhttp:4.10.0' implementation 'org.json:json:20220924' testImplementation 'org.postgresql:postgresql:42.5.0' diff --git a/examples/redis-backed-cache-testng/build.gradle b/examples/redis-backed-cache-testng/build.gradle index 8e3394723ba..2e4bda4fa74 100644 --- a/examples/redis-backed-cache-testng/build.gradle +++ b/examples/redis-backed-cache-testng/build.gradle @@ -7,7 +7,7 @@ repositories { } dependencies { - compileOnly 'org.slf4j:slf4j-api:2.0.3' + compileOnly 'org.slf4j:slf4j-api:1.7.36' implementation 'redis.clients:jedis:4.2.3' implementation 'com.google.code.gson:gson:2.9.1' implementation 'com.google.guava:guava:23.0' diff --git a/examples/redis-backed-cache/build.gradle b/examples/redis-backed-cache/build.gradle index ed794088790..eb684158731 100644 --- a/examples/redis-backed-cache/build.gradle +++ b/examples/redis-backed-cache/build.gradle @@ -7,7 +7,7 @@ repositories { } dependencies { - compileOnly 'org.slf4j:slf4j-api:2.0.3' + compileOnly 'org.slf4j:slf4j-api:1.7.36' implementation 'redis.clients:jedis:4.2.3' implementation 'com.google.code.gson:gson:2.9.1' implementation 'com.google.guava:guava:23.0' diff --git a/examples/singleton-container/build.gradle b/examples/singleton-container/build.gradle index 3ef81b866d4..449fd9ae8e6 100644 --- a/examples/singleton-container/build.gradle +++ b/examples/singleton-container/build.gradle @@ -11,7 +11,7 @@ dependencies { implementation 'redis.clients:jedis:4.2.3' implementation 'com.google.code.gson:gson:2.9.1' implementation 'com.google.guava:guava:23.0' - compileOnly 'org.slf4j:slf4j-api:2.0.3' + compileOnly 'org.slf4j:slf4j-api:1.7.36' testImplementation 'ch.qos.logback:logback-classic:1.3.3' testImplementation 'org.testcontainers:testcontainers' diff --git a/test-support/build.gradle b/test-support/build.gradle index 2fae2344a89..8247f97dbf4 100644 --- a/test-support/build.gradle +++ b/test-support/build.gradle @@ -1,5 +1,5 @@ dependencies { implementation 'junit:junit:4.13.2' - implementation 'org.slf4j:slf4j-api:2.0.0' + implementation 'org.slf4j:slf4j-api:1.7.36' testImplementation 'org.assertj:assertj-core:3.23.1' } From 320a6203227f93ed138ea905105fa422346c80fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Tue, 4 Oct 2022 11:14:25 -0500 Subject: [PATCH 0547/3231] Ignore dependencies in specific modules (#5946) Some dependencies require Java 11, 17 or have a breaking change such as r2dbc. --- .github/dependabot.yml | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 4c270ef762c..87ce5c86463 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -11,6 +11,9 @@ updates: schedule: interval: "monthly" open-pull-requests-limit: 10 + ignore: + - dependency-name: "org.slf4j:slf4j-api" + update-types: [ "version-update:semver-major" ] - package-ecosystem: "gradle" directory: "/" allow: @@ -32,6 +35,9 @@ updates: schedule: interval: "monthly" open-pull-requests-limit: 10 + ignore: + - dependency-name: "io.dropwizard.metrics:metrics-core" + update-types: [ "version-update:semver-major" ] - package-ecosystem: "gradle" directory: "/modules/clickhouse" schedule: @@ -97,6 +103,9 @@ updates: schedule: interval: "monthly" open-pull-requests-limit: 10 + ignore: + - dependency-name: "org.apache.tomcat:tomcat-jdbc" + update-types: [ "version-update:semver-minor" ] - package-ecosystem: "gradle" directory: "/modules/junit-jupiter" schedule: @@ -107,6 +116,9 @@ updates: schedule: interval: "monthly" open-pull-requests-limit: 10 + ignore: + - dependency-name: "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml" + update-types: [ "version-update:semver-minor" ] - package-ecosystem: "gradle" directory: "/modules/kafka" schedule: @@ -122,6 +134,9 @@ updates: schedule: interval: "monthly" open-pull-requests-limit: 10 + ignore: + - dependency-name: "org.mariadb:r2dbc-mariadb" + update-types: [ "version-update:semver-minor" ] - package-ecosystem: "gradle" directory: "/modules/mockserver" schedule: @@ -147,6 +162,9 @@ updates: schedule: interval: "monthly" open-pull-requests-limit: 10 + ignore: + - dependency-name: "org.neo4j.driver:neo4j-java-driver" + update-types: [ "version-update:semver-major" ] - package-ecosystem: "gradle" directory: "/modules/nginx" schedule: @@ -182,6 +200,9 @@ updates: schedule: interval: "monthly" open-pull-requests-limit: 10 + ignore: + - dependency-name: "io.r2dbc:r2dbc-spi" + versions: ["0.8.x"] - package-ecosystem: "gradle" directory: "/modules/rabbitmq" schedule: @@ -202,6 +223,9 @@ updates: schedule: interval: "monthly" open-pull-requests-limit: 10 + ignore: + - dependency-name: "org.apache.solr:solr-solrj" + update-types: [ "version-update:semver-major" ] - package-ecosystem: "gradle" directory: "/modules/spock" schedule: @@ -234,6 +258,17 @@ updates: schedule: interval: "monthly" open-pull-requests-limit: 10 + ignore: + - dependency-name: "ch.qos.logback:logback-classic" + update-types: [ "version-update:semver-minor" ] + - dependency-name: "org.apache.solr:solr-solrj" + update-types: [ "version-update:semver-major" ] + - dependency-name: "org.neo4j.driver:neo4j-java-driver" + update-types: [ "version-update:semver-major" ] + - dependency-name: "org.testng:testng" + update-types: [ "version-update:semver-minor" ] + - dependency-name: "org.slf4j:slf4j-api" + update-types: [ "version-update:semver-major" ] # GitHub Actions - package-ecosystem: "github-actions" From a6ceeba0b15d66e2f2b901ac56b43fc84e9815ba Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 4 Oct 2022 16:14:55 +0000 Subject: [PATCH 0548/3231] Bump metrics-core from 3.2.4 to 3.2.6 in /modules/cassandra Bumps [metrics-core](https://github.com/dropwizard/metrics) from 3.2.4 to 3.2.6. - [Release notes](https://github.com/dropwizard/metrics/releases) - [Commits](https://github.com/dropwizard/metrics/compare/v3.2.4...v3.2.6) --- updated-dependencies: - dependency-name: io.dropwizard.metrics:metrics-core dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/cassandra/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/cassandra/build.gradle b/modules/cassandra/build.gradle index ae115c92a71..7f9ab53c71b 100644 --- a/modules/cassandra/build.gradle +++ b/modules/cassandra/build.gradle @@ -2,7 +2,7 @@ description = "TestContainers :: Cassandra" configurations.all { resolutionStrategy { - force 'io.dropwizard.metrics:metrics-core:3.2.4' + force 'io.dropwizard.metrics:metrics-core:3.2.6' } } From 461c23d6c65b769f42b5d18077e9b38130dde0bc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 4 Oct 2022 16:14:56 +0000 Subject: [PATCH 0549/3231] Bump mongodb-driver-sync from 4.7.1 to 4.7.2 in /modules/mongodb Bumps [mongodb-driver-sync](https://github.com/mongodb/mongo-java-driver) from 4.7.1 to 4.7.2. - [Release notes](https://github.com/mongodb/mongo-java-driver/releases) - [Commits](https://github.com/mongodb/mongo-java-driver/compare/r4.7.1...r4.7.2) --- updated-dependencies: - dependency-name: org.mongodb:mongodb-driver-sync dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/mongodb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mongodb/build.gradle b/modules/mongodb/build.gradle index 6e4b5208298..cc4a9288373 100644 --- a/modules/mongodb/build.gradle +++ b/modules/mongodb/build.gradle @@ -3,6 +3,6 @@ description = "Testcontainers :: MongoDB" dependencies { api project(':testcontainers') - testImplementation("org.mongodb:mongodb-driver-sync:4.7.1") + testImplementation("org.mongodb:mongodb-driver-sync:4.7.2") testImplementation 'org.assertj:assertj-core:3.23.1' } From c4658989068d3c555fdff9b398c490d300a9a5d3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 4 Oct 2022 16:16:06 +0000 Subject: [PATCH 0550/3231] Bump solr-solrj from 8.11.1 to 8.11.2 in /modules/solr Bumps solr-solrj from 8.11.1 to 8.11.2. --- updated-dependencies: - dependency-name: org.apache.solr:solr-solrj dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/solr/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/solr/build.gradle b/modules/solr/build.gradle index 72ab2997dea..8d053fa4169 100644 --- a/modules/solr/build.gradle +++ b/modules/solr/build.gradle @@ -5,6 +5,6 @@ dependencies { // TODO use JDK's HTTP client and/or Apache HttpClient5 shaded 'com.squareup.okhttp3:okhttp:4.10.0' - testImplementation 'org.apache.solr:solr-solrj:8.11.1' + testImplementation 'org.apache.solr:solr-solrj:8.11.2' testImplementation 'org.assertj:assertj-core:3.23.1' } From c0758a2bf6c1f1ccf7154c9c337fc5aa1cdc9a52 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 4 Oct 2022 16:16:08 +0000 Subject: [PATCH 0551/3231] Bump solr-solrj from 8.11.1 to 8.11.2 in /examples Bumps solr-solrj from 8.11.1 to 8.11.2. --- updated-dependencies: - dependency-name: org.apache.solr:solr-solrj dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- examples/solr-container/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/solr-container/build.gradle b/examples/solr-container/build.gradle index 56fd297cf5e..dd0f7007689 100644 --- a/examples/solr-container/build.gradle +++ b/examples/solr-container/build.gradle @@ -10,7 +10,7 @@ dependencies { compileOnly "org.projectlombok:lombok:1.18.24" annotationProcessor "org.projectlombok:lombok:1.18.24" - implementation 'org.apache.solr:solr-solrj:8.11.1' + implementation 'org.apache.solr:solr-solrj:8.11.2' testImplementation 'org.testcontainers:testcontainers' testImplementation 'org.testcontainers:solr' From c65fc31df0620347e7e4236e8714b18ba52c14d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez?= Date: Tue, 4 Oct 2022 13:43:29 -0500 Subject: [PATCH 0552/3231] Update latest version in mkdocs.yml --- mkdocs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mkdocs.yml b/mkdocs.yml index ab57aeb11d6..3a802d4ec91 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -108,4 +108,4 @@ nav: - contributing_docs.md - bounty.md extra: - latest_version: 1.17.4 + latest_version: 1.17.5 From b1fa4ece54a4d13e67d20a3db123c98230451f09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez?= Date: Tue, 4 Oct 2022 13:44:04 -0500 Subject: [PATCH 0553/3231] Upgrade testcontainers version to 1.17.5 in japicmp --- gradle/japicmp.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/japicmp.gradle b/gradle/japicmp.gradle index 6d769bf9286..ed4223324ab 100644 --- a/gradle/japicmp.gradle +++ b/gradle/japicmp.gradle @@ -3,7 +3,7 @@ configurations { } dependencies { - baseline "org.testcontainers:${project.name}:1.17.4", { + baseline "org.testcontainers:${project.name}:1.17.5", { exclude group: "*", module: "*" } } From f5e79b9aab308acc0211fca4bd0d00ab9b57cbd6 Mon Sep 17 00:00:00 2001 From: Jan-Hendrik Dolling Date: Tue, 4 Oct 2022 22:08:46 +0200 Subject: [PATCH 0554/3231] Remove unnecessary configuration from simple tests (#5973) * Remove mysql configuration from simple test * mysql custom ini file test also runs on Windows Co-authored-by: Kevin Wittek --- .../java/org/testcontainers/junit/mysql/SimpleMySQLTest.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/modules/mysql/src/test/java/org/testcontainers/junit/mysql/SimpleMySQLTest.java b/modules/mysql/src/test/java/org/testcontainers/junit/mysql/SimpleMySQLTest.java index f60712e7123..e39c55b85a9 100644 --- a/modules/mysql/src/test/java/org/testcontainers/junit/mysql/SimpleMySQLTest.java +++ b/modules/mysql/src/test/java/org/testcontainers/junit/mysql/SimpleMySQLTest.java @@ -1,6 +1,5 @@ package org.testcontainers.junit.mysql; -import org.apache.commons.lang3.SystemUtils; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -21,7 +20,6 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.fail; -import static org.junit.Assume.assumeFalse; public class SimpleMySQLTest extends AbstractContainerDatabaseTest { @@ -48,7 +46,6 @@ public class SimpleMySQLTest extends AbstractContainerDatabaseTest { public void testSimple() throws SQLException { try ( MySQLContainer mysql = new MySQLContainer<>(MySQLTestImages.MYSQL_57_IMAGE) - .withConfigurationOverride("somepath/mysql_conf_override") .withLogConsumer(new Slf4jLogConsumer(logger)) ) { mysql.start(); @@ -81,8 +78,6 @@ public void testSpecificVersion() throws SQLException { @Test public void testMySQLWithCustomIniFile() throws SQLException { - assumeFalse(SystemUtils.IS_OS_WINDOWS); - try ( MySQLContainer mysqlCustomConfig = new MySQLContainer<>(MySQLTestImages.MYSQL_56_IMAGE) .withConfigurationOverride("somepath/mysql_conf_override") From 2de82d96b38bb163941b64326f6286e8a4dce02d Mon Sep 17 00:00:00 2001 From: "K. Siva Prasad Reddy" Date: Wed, 5 Oct 2022 05:47:34 +0530 Subject: [PATCH 0555/3231] Update MockServer Tests to use mockserver/mockserver image with 5.14.0 version (#5977) Fixes #5975 --- modules/mockserver/build.gradle | 2 +- .../containers/MockServerContainerRuleTest.java | 6 ++++-- .../testcontainers/containers/MockServerContainerTest.java | 4 ++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/modules/mockserver/build.gradle b/modules/mockserver/build.gradle index df46295df21..85e7aaa181e 100644 --- a/modules/mockserver/build.gradle +++ b/modules/mockserver/build.gradle @@ -3,6 +3,6 @@ description = "Testcontainers :: MockServer" dependencies { api project(':testcontainers') - testImplementation 'org.mock-server:mockserver-client-java:5.13.2' + testImplementation 'org.mock-server:mockserver-client-java:5.14.0' testImplementation 'org.assertj:assertj-core:3.23.1' } diff --git a/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerRuleTest.java b/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerRuleTest.java index 0e4fa102b2e..7fdacd7c580 100644 --- a/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerRuleTest.java +++ b/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerRuleTest.java @@ -12,12 +12,14 @@ public class MockServerContainerRuleTest { public static final DockerImageName MOCKSERVER_IMAGE = DockerImageName.parse( - "jamesdbloom/mockserver:mockserver-5.13.2" + "mockserver/mockserver:mockserver-5.14.0" ); // creatingProxy { @Rule - public MockServerContainer mockServer = new MockServerContainer(MOCKSERVER_IMAGE); + public MockServerContainer mockServer = new MockServerContainer( + MOCKSERVER_IMAGE.withTag("mockserver-" + MockServerClient.class.getPackage().getImplementationVersion()) + ); // } diff --git a/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerTest.java b/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerTest.java index 6ddce4b6cbf..b59f14cc27b 100644 --- a/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerTest.java +++ b/modules/mockserver/src/test/java/org/testcontainers/containers/MockServerContainerTest.java @@ -11,7 +11,7 @@ public class MockServerContainerTest { public static final DockerImageName MOCKSERVER_IMAGE = DockerImageName.parse( - "jamesdbloom/mockserver:mockserver-5.5.4" + "mockserver/mockserver:mockserver-5.14.0" ); @Test @@ -40,7 +40,7 @@ public void shouldCallActualMockserverVersion() throws Exception { @Test public void newVersionStartsWithDefaultWaitStrategy() { - DockerImageName dockerImageName = DockerImageName.parse("mockserver/mockserver:mockserver-5.11.2"); + DockerImageName dockerImageName = DockerImageName.parse("mockserver/mockserver:mockserver-5.14.0"); try (MockServerContainer mockServer = new MockServerContainer(dockerImageName)) { mockServer.start(); } From 478b0492ea1526a2941ef714f9c9c7c4bcbb57a9 Mon Sep 17 00:00:00 2001 From: Etienne Dysli Metref Date: Thu, 6 Oct 2022 01:54:39 +0200 Subject: [PATCH 0556/3231] Update docs about logging configuration (#5976) * Recommend disabling Apache HTTP client wire logging * Recommend enabling debug logging only for org.testcontainers Fixes #4913 Co-authored-by: Kevin Wittek --- docs/supported_docker_environment/logging_config.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/supported_docker_environment/logging_config.md b/docs/supported_docker_environment/logging_config.md index 5c7abcd211c..3e4660e85ae 100644 --- a/docs/supported_docker_environment/logging_config.md +++ b/docs/supported_docker_environment/logging_config.md @@ -18,5 +18,14 @@ should be included in your classpath to show a reasonable level of log output: + ``` + +In order to troubleshoot issues with Testcontainers, increase the logging level of `org.testcontainers` to `DEBUG`: + +```xml + +``` + +Avoid changing the root logger's level to `DEBUG`, because this turns on debug logging for every package whose level isn't explicitly configured here, resulting in a large amount of log data. From 09c42a488b2ee1fb010213976fe9621c17d461c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Thu, 6 Oct 2022 13:13:13 -0500 Subject: [PATCH 0557/3231] Add reference to supported languages (#5979) Co-authored-by: Kevin Wittek --- docs/supported_languages.md | 9 +++++++++ mkdocs.yml | 1 + 2 files changed, 10 insertions(+) create mode 100644 docs/supported_languages.md diff --git a/docs/supported_languages.md b/docs/supported_languages.md new file mode 100644 index 00000000000..4990559f0e7 --- /dev/null +++ b/docs/supported_languages.md @@ -0,0 +1,9 @@ +# Other Supported Languages + +Besides *Testcontainers for Java,* you can find Testcontainers implementations for many other languages. +Please note that not all features that are available in *Testcontainers for Java* might be available in the implementations for other languages yet. + +* [Go](https://golang.testcontainers.org/) +* [.Net](https://github.com/testcontainers/testcontainers-dotnet) +* [Python](https://testcontainers-python.readthedocs.io/) +* [JavaScript/NodeJS](https://github.com/testcontainers/testcontainers-node) diff --git a/mkdocs.yml b/mkdocs.yml index 3a802d4ec91..70421761e02 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -102,6 +102,7 @@ nav: - supported_docker_environment/windows.md - supported_docker_environment/logging_config.md - supported_docker_environment/image_registry_rate_limiting.md + - Other Supported Languages: supported_languages.md - Getting help: getting_help.md - Contributing: - contributing.md From 914769f56a1b5b9c66d4419e6b1ca0e178cef399 Mon Sep 17 00:00:00 2001 From: Priyambada Roul <75459506+roulpriya@users.noreply.github.com> Date: Mon, 10 Oct 2022 23:06:01 +0530 Subject: [PATCH 0558/3231] Do not allow empty env vars (#5983) Fixes #5026 Signed-off-by: Priyambada Roul --- .../utility/TestcontainersConfiguration.java | 5 ++++- .../utility/TestcontainersConfigurationTest.java | 8 ++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java index e647a8d50b3..f3bb8f8319a 100644 --- a/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java +++ b/core/src/main/java/org/testcontainers/utility/TestcontainersConfiguration.java @@ -230,7 +230,10 @@ private String getConfigurable( } if (environment.containsKey(envVarName)) { - return environment.get(envVarName); + String value = environment.get(envVarName); + if (!value.isEmpty()) { + return value; + } } for (final Properties properties : propertiesSources) { diff --git a/core/src/test/java/org/testcontainers/utility/TestcontainersConfigurationTest.java b/core/src/test/java/org/testcontainers/utility/TestcontainersConfigurationTest.java index d51bc89348c..fcc0a654ef5 100644 --- a/core/src/test/java/org/testcontainers/utility/TestcontainersConfigurationTest.java +++ b/core/src/test/java/org/testcontainers/utility/TestcontainersConfigurationTest.java @@ -126,6 +126,14 @@ public void shouldReadDockerSettingsFromUserProperties() { .isEqualTo("some value"); } + @Test + public void shouldNotReadSettingIfCorrespondingEnvironmentVarIsEmptyString() { + environment.put("DOCKER_FOO", ""); + assertThat(newConfig().getEnvVarOrUserProperty("docker.foo", "default")) + .as("reads unprefixed env vars for docker. settings") + .isEqualTo("default"); + } + @Test public void shouldNotReadDockerClientStrategyFromClasspathProperties() { String currentValue = newConfig().getDockerClientStrategyClassName(); From 75ac1d1ff5e14a0306f981288d5736d275970d88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez?= Date: Tue, 11 Oct 2022 20:56:26 -0500 Subject: [PATCH 0559/3231] Upgrade python version to 3.8 --- docker-compose.yml | 2 +- docs/contributing_docs.md | 2 +- runtime.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 1ce5532d0bd..bfb6bdc35fe 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,7 +2,7 @@ version: "3.7" services: docs: - image: python:3.7 + image: python:3.8 command: sh -c "pip install -r requirements.txt && mkdocs serve -a 0.0.0.0:8000" working_dir: /docs volumes: diff --git a/docs/contributing_docs.md b/docs/contributing_docs.md index 2d706dfe407..c303fe6b06c 100644 --- a/docs/contributing_docs.md +++ b/docs/contributing_docs.md @@ -15,7 +15,7 @@ The root of the project contains a `docker-compose.yml` file. Simply run `docker ### Using Python locally -* Ensure that you have Python 3.7.0 or higher. +* Ensure that you have Python 3.8.0 or higher. * Set up a virtualenv and run `pip install -r requirements.txt` in the `testcontainers-java` root directory. * Once Python dependencies have been installed, run `mkdocs serve` to start a local auto-updating MkDocs server. diff --git a/runtime.txt b/runtime.txt index 475ba515c04..cc1923a40b1 100644 --- a/runtime.txt +++ b/runtime.txt @@ -1 +1 @@ -3.7 +3.8 From 2da2675c1b9b13d7fde985c00001105b800a84ce Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Wed, 12 Oct 2022 11:50:26 +0200 Subject: [PATCH 0560/3231] Add paragraph about Docker requirements to docs (#5987) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Eddú Meléndez Gonzales --- docs/supported_docker_environment/index.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/supported_docker_environment/index.md b/docs/supported_docker_environment/index.md index debca47e574..885325545ff 100644 --- a/docs/supported_docker_environment/index.md +++ b/docs/supported_docker_environment/index.md @@ -2,6 +2,15 @@ ## Overview +Testcontainers requires a Docker-API compatible container runtime. +During development, Testcontainers is actively tested against recent versions of Docker on Linux, as well as against Docker Desktop on Mac and Windows. +These Docker environments are automatically detected and used by Testcontainers without any additional configuration being necessary. + +It is possible to configure Testcontainers to work for other Docker setups, such as a remote Docker host or Docker alternatives. +However, these are not actively tested in the main development workflow, so not all Testcontainers features might be available and additional manual configuration might be necessary. +If you have further questions about configuration details for your setup or whether it supports running Testcontainers-based tests, +please contact the Testcontainers team and other users from the Testcontainers community on [Slack](https://slack.testcontainers.org/). + | Host Operating System / Environment | Minimum recommended docker versions | Known issues / tips | |-------------------------------------|-----------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | Linux - general | Docker v17.09 | After docker installation, follow [post-installation steps](https://docs.docker.com/engine/install/linux-postinstall/). | From 96acaf02721c5dbf543ff2b166366a190df57cfa Mon Sep 17 00:00:00 2001 From: Ammar Khaku Date: Fri, 14 Oct 2022 08:59:59 -0700 Subject: [PATCH 0561/3231] Catch and ignore more errors when reflecting into container subclass (#5990) With #2830 we added support for 4.x of the Cassandra driver. It was done in a way to allow a user to use either the 3.x or 4.x driver while excluding the other one. However if using 4.x and excluding 3.x, GenericContainer#canBeReused throws an exception during reflection since the Cluster class returned by CassandraContainer#getCluster is missing. This PR works around that issue by catching and ignoring the thrown NoClassDefFoundError. --- .../java/org/testcontainers/containers/GenericContainer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/testcontainers/containers/GenericContainer.java b/core/src/main/java/org/testcontainers/containers/GenericContainer.java index 7163ea40a93..bc65cb22040 100644 --- a/core/src/main/java/org/testcontainers/containers/GenericContainer.java +++ b/core/src/main/java/org/testcontainers/containers/GenericContainer.java @@ -360,7 +360,7 @@ protected boolean canBeReused() { logger().warn("{} can't be reused because it overrides {}", getClass(), method.getName()); return false; } - } catch (NoSuchMethodException e) { + } catch (NoSuchMethodException | NoClassDefFoundError e) { // ignore } } From 00c5e88562e50f3337d43a3d193669a5fdbac61e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Tue, 18 Oct 2022 08:12:30 -0500 Subject: [PATCH 0562/3231] Remove Transferable#of(java.lang.String,int) for japicmp (#5992) `Transferable#of(java.lang.String,int)` was introduced in version 1.17.4. Current builds will check against 1.17.5 and binary compatibility will be fine. --- core/build.gradle | 6 ------ 1 file changed, 6 deletions(-) diff --git a/core/build.gradle b/core/build.gradle index 2967b35d4ff..364f51b6b70 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -113,9 +113,3 @@ tasks.generatePomFileForMavenJavaPublication.finalizedBy( ] } ) - -tasks.japicmp { - methodExcludes = [ - "org.testcontainers.images.builder.Transferable#of(java.lang.String,int)" - ] -} From 077c84405486e061a31d585b8ffa5c2d3ea0b7c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Wed, 19 Oct 2022 02:27:21 -0500 Subject: [PATCH 0563/3231] Deprecate `setCustomContent` and `withCustomContent` at `NginxContainer` (#5997) In order to make `NginxContainer` friendly with remote environments, we should avoid the use of `addFileSystemBind`. Example has been updated in order to show how to do it. --- .../java/org/testcontainers/containers/NginxContainer.java | 2 ++ .../test/java/org/testcontainers/junit/SimpleNginxTest.java | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/nginx/src/main/java/org/testcontainers/containers/NginxContainer.java b/modules/nginx/src/main/java/org/testcontainers/containers/NginxContainer.java index b97992a5684..1bf951af7c8 100644 --- a/modules/nginx/src/main/java/org/testcontainers/containers/NginxContainer.java +++ b/modules/nginx/src/main/java/org/testcontainers/containers/NginxContainer.java @@ -56,10 +56,12 @@ public URL getBaseUrl(String scheme, int port) throws MalformedURLException { return new URL(scheme + "://" + getHost() + ":" + getMappedPort(port)); } + @Deprecated public void setCustomContent(String htmlContentPath) { addFileSystemBind(htmlContentPath, "/usr/share/nginx/html", BindMode.READ_ONLY); } + @Deprecated public SELF withCustomContent(String htmlContentPath) { this.setCustomContent(htmlContentPath); return self(); diff --git a/modules/nginx/src/test/java/org/testcontainers/junit/SimpleNginxTest.java b/modules/nginx/src/test/java/org/testcontainers/junit/SimpleNginxTest.java index abfbb663207..d338ebf180b 100644 --- a/modules/nginx/src/test/java/org/testcontainers/junit/SimpleNginxTest.java +++ b/modules/nginx/src/test/java/org/testcontainers/junit/SimpleNginxTest.java @@ -7,6 +7,7 @@ import org.testcontainers.containers.NginxContainer; import org.testcontainers.containers.wait.strategy.HttpWaitStrategy; import org.testcontainers.utility.DockerImageName; +import org.testcontainers.utility.MountableFile; import java.io.*; import java.net.URL; @@ -26,7 +27,7 @@ public class SimpleNginxTest { // creatingContainer { @Rule public NginxContainer nginx = new NginxContainer<>(NGINX_IMAGE) - .withCustomContent(tmpDirectory) + .withCopyFileToContainer(MountableFile.forHostPath(tmpDirectory), "/usr/share/nginx/html") .waitingFor(new HttpWaitStrategy()); // } From 0b0e559141c5b83eaeb8ee89b143b5d28cbc3136 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Wed, 19 Oct 2022 02:27:36 -0500 Subject: [PATCH 0564/3231] Update toxiproxy image to ghcr.io/shopify/toxiproxy (#5996) `shopify/toxiproxy` has moved to `ghcr.io/shopify/toxiproxy`. Currently, our tests are using the prior and is display as part of the documentation. In order to keep our tests and docs up-to-date with latest images we are updating the image. Also, we are adding the proper configuration for logging in this module. --- .../testcontainers/containers/ToxiproxyTest.java | 2 +- .../src/test/resources/logback-test.xml | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 modules/toxiproxy/src/test/resources/logback-test.xml diff --git a/modules/toxiproxy/src/test/java/org/testcontainers/containers/ToxiproxyTest.java b/modules/toxiproxy/src/test/java/org/testcontainers/containers/ToxiproxyTest.java index bfe488eaac1..25a0feccc48 100644 --- a/modules/toxiproxy/src/test/java/org/testcontainers/containers/ToxiproxyTest.java +++ b/modules/toxiproxy/src/test/java/org/testcontainers/containers/ToxiproxyTest.java @@ -35,7 +35,7 @@ public class ToxiproxyTest { .withExposedPorts(6379) .withNetwork(network); - private static final DockerImageName TOXIPROXY_IMAGE = DockerImageName.parse("shopify/toxiproxy:2.1.0"); + private static final DockerImageName TOXIPROXY_IMAGE = DockerImageName.parse("ghcr.io/shopify/toxiproxy:2.5.0"); // Toxiproxy container, which will be used as a TCP proxy @Rule diff --git a/modules/toxiproxy/src/test/resources/logback-test.xml b/modules/toxiproxy/src/test/resources/logback-test.xml new file mode 100644 index 00000000000..83ef7a1a3ef --- /dev/null +++ b/modules/toxiproxy/src/test/resources/logback-test.xml @@ -0,0 +1,16 @@ + + + + + + %d{HH:mm:ss.SSS} %-5level %logger - %msg%n + + + + + + + + + From d1864a466b294296bca00368fd904d78c3a78057 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Thu, 20 Oct 2022 07:54:15 -0500 Subject: [PATCH 0565/3231] Fix dependabot.yml ignore section (#5999) --- .github/dependabot.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 87ce5c86463..46c7046300d 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -118,7 +118,7 @@ updates: open-pull-requests-limit: 10 ignore: - dependency-name: "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml" - update-types: [ "version-update:semver-minor" ] + update-types: [ "version-update:semver-minor", "version-update:semver-patch" ] - package-ecosystem: "gradle" directory: "/modules/kafka" schedule: @@ -202,7 +202,7 @@ updates: open-pull-requests-limit: 10 ignore: - dependency-name: "io.r2dbc:r2dbc-spi" - versions: ["0.8.x"] + update-types: [ "version-update:semver-major", "version-update:semver-minor" ] - package-ecosystem: "gradle" directory: "/modules/rabbitmq" schedule: From cf1573e4ffab1f302141d5bda20d23ecaba5b148 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 Oct 2022 12:54:49 +0000 Subject: [PATCH 0566/3231] Bump tomcat-jdbc from 10.0.23 to 10.0.27 in /modules/jdbc-test Bumps tomcat-jdbc from 10.0.23 to 10.0.27. --- updated-dependencies: - dependency-name: org.apache.tomcat:tomcat-jdbc dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/jdbc-test/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jdbc-test/build.gradle b/modules/jdbc-test/build.gradle index f8b55d01bc5..798508ffa22 100644 --- a/modules/jdbc-test/build.gradle +++ b/modules/jdbc-test/build.gradle @@ -11,7 +11,7 @@ dependencies { api 'org.assertj:assertj-core:3.23.1' - api 'org.apache.tomcat:tomcat-jdbc:10.0.23' + api 'org.apache.tomcat:tomcat-jdbc:10.0.27' api 'org.vibur:vibur-dbcp:25.0' api 'mysql:mysql-connector-java:8.0.30' } From 5ee6fd38cf3b4a2940cd399d4a3a662f3d5ec3e1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 Oct 2022 12:54:49 +0000 Subject: [PATCH 0567/3231] Bump mysql-connector-java from 8.0.30 to 8.0.31 in /modules/mysql Bumps mysql-connector-java from 8.0.30 to 8.0.31. --- updated-dependencies: - dependency-name: mysql:mysql-connector-java dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/mysql/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/mysql/build.gradle b/modules/mysql/build.gradle index 87887384973..a18941ab9f7 100644 --- a/modules/mysql/build.gradle +++ b/modules/mysql/build.gradle @@ -10,7 +10,7 @@ dependencies { compileOnly 'dev.miku:r2dbc-mysql:0.8.2.RELEASE' testImplementation project(':jdbc-test') - testImplementation 'mysql:mysql-connector-java:8.0.30' + testImplementation 'mysql:mysql-connector-java:8.0.31' testImplementation testFixtures(project(':r2dbc')) testImplementation 'dev.miku:r2dbc-mysql:0.8.2.RELEASE' From c8ec550ec6278d28cb3e5abcaf376292d2e9920c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 Oct 2022 12:54:49 +0000 Subject: [PATCH 0568/3231] Bump tomcat-jdbc from 10.1.0 to 10.1.1 in /modules/jdbc Bumps tomcat-jdbc from 10.1.0 to 10.1.1. --- updated-dependencies: - dependency-name: org.apache.tomcat:tomcat-jdbc dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/jdbc/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/jdbc/build.gradle b/modules/jdbc/build.gradle index 7a0fbf03fc1..92f72287fa2 100644 --- a/modules/jdbc/build.gradle +++ b/modules/jdbc/build.gradle @@ -7,7 +7,7 @@ dependencies { compileOnly 'org.jetbrains:annotations:23.0.0' testImplementation 'commons-dbutils:commons-dbutils:1.7' testImplementation 'org.vibur:vibur-dbcp:25.0' - testImplementation 'org.apache.tomcat:tomcat-jdbc:10.1.0' + testImplementation 'org.apache.tomcat:tomcat-jdbc:10.1.1' testImplementation 'com.zaxxer:HikariCP-java6:2.3.13' testImplementation 'org.assertj:assertj-core:3.23.1' testImplementation ('org.mockito:mockito-core:4.8.0') { From ceee70954ad8f32252925ff415e6f616b0f3aff3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 Oct 2022 12:54:51 +0000 Subject: [PATCH 0569/3231] Bump reactor-core from 3.4.23 to 3.4.24 in /modules/r2dbc Bumps [reactor-core](https://github.com/reactor/reactor-core) from 3.4.23 to 3.4.24. - [Release notes](https://github.com/reactor/reactor-core/releases) - [Commits](https://github.com/reactor/reactor-core/compare/v3.4.23...v3.4.24) --- updated-dependencies: - dependency-name: io.projectreactor:reactor-core dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/r2dbc/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/r2dbc/build.gradle b/modules/r2dbc/build.gradle index 9126ff0a347..952c9f5b982 100644 --- a/modules/r2dbc/build.gradle +++ b/modules/r2dbc/build.gradle @@ -15,6 +15,6 @@ dependencies { testImplementation 'io.r2dbc:r2dbc-postgresql:0.8.13.RELEASE' testImplementation project(':postgresql') - testFixturesImplementation 'io.projectreactor:reactor-core:3.4.23' + testFixturesImplementation 'io.projectreactor:reactor-core:3.4.24' testFixturesImplementation 'org.assertj:assertj-core:3.23.1' } From d47b1ca51898e01d3002e76ac6fd6a2f2c6a6502 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 Oct 2022 12:54:51 +0000 Subject: [PATCH 0570/3231] Bump mysql-connector-java from 8.0.30 to 8.0.31 in /modules/spock Bumps mysql-connector-java from 8.0.30 to 8.0.31. --- updated-dependencies: - dependency-name: mysql:mysql-connector-java dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/spock/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/spock/build.gradle b/modules/spock/build.gradle index a13a7049e13..e202b07f411 100644 --- a/modules/spock/build.gradle +++ b/modules/spock/build.gradle @@ -16,7 +16,7 @@ dependencies { testImplementation 'org.apache.httpcomponents:httpclient:4.5.13' testRuntimeOnly 'org.postgresql:postgresql:42.5.0' - testRuntimeOnly 'mysql:mysql-connector-java:8.0.30' + testRuntimeOnly 'mysql:mysql-connector-java:8.0.31' testCompileOnly 'org.jetbrains:annotations:23.0.0' } From b260332e1221363aa5b2bd3139460f6b65485ce0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 Oct 2022 12:54:51 +0000 Subject: [PATCH 0571/3231] Bump jedis from 4.2.3 to 4.3.1 in /modules/junit-jupiter Bumps [jedis](https://github.com/redis/jedis) from 4.2.3 to 4.3.1. - [Release notes](https://github.com/redis/jedis/releases) - [Commits](https://github.com/redis/jedis/compare/v4.2.3...v4.3.1) --- updated-dependencies: - dependency-name: redis.clients:jedis dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index e801e54ab83..496c273fbde 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -7,7 +7,7 @@ dependencies { testImplementation project(':mysql') testImplementation project(':postgresql') testImplementation 'com.zaxxer:HikariCP:4.0.3' - testImplementation 'redis.clients:jedis:4.2.3' + testImplementation 'redis.clients:jedis:4.3.1' testImplementation 'org.apache.httpcomponents:httpclient:4.5.13' testImplementation ('org.mockito:mockito-core:4.8.0') { exclude(module: 'hamcrest-core') From 46a4edc16b065f9693a5c754670376e9e5d34467 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 Oct 2022 12:54:52 +0000 Subject: [PATCH 0572/3231] Bump mysql-connector-java in /modules/junit-jupiter Bumps mysql-connector-java from 8.0.30 to 8.0.31. --- updated-dependencies: - dependency-name: mysql:mysql-connector-java dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/junit-jupiter/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index e801e54ab83..2819985921d 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -16,7 +16,7 @@ dependencies { testImplementation 'org.junit.jupiter:junit-jupiter-params:5.9.1' testRuntimeOnly 'org.postgresql:postgresql:42.5.0' - testRuntimeOnly 'mysql:mysql-connector-java:8.0.30' + testRuntimeOnly 'mysql:mysql-connector-java:8.0.31' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.9.1' } From 5e23c5e8e680b6ddbda496d4b102bae08f7092ba Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 Oct 2022 12:54:52 +0000 Subject: [PATCH 0573/3231] Bump pulsar-client-admin from 2.10.1 to 2.10.2 in /modules/pulsar Bumps [pulsar-client-admin](https://github.com/apache/pulsar) from 2.10.1 to 2.10.2. - [Release notes](https://github.com/apache/pulsar/releases) - [Commits](https://github.com/apache/pulsar/compare/v2.10.1...v2.10.2) --- updated-dependencies: - dependency-name: org.apache.pulsar:pulsar-client-admin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/pulsar/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/pulsar/build.gradle b/modules/pulsar/build.gradle index 8bc139a3a93..c15fc32730e 100644 --- a/modules/pulsar/build.gradle +++ b/modules/pulsar/build.gradle @@ -5,5 +5,5 @@ dependencies { testImplementation group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.10.1' testImplementation group: 'org.assertj', name: 'assertj-core', version: '3.23.1' - testImplementation group: 'org.apache.pulsar', name: 'pulsar-client-admin', version: '2.10.1' + testImplementation group: 'org.apache.pulsar', name: 'pulsar-client-admin', version: '2.10.2' } From aa597b8ed501b6dbda20c06fbe0b42c0b5264b89 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 Oct 2022 12:54:54 +0000 Subject: [PATCH 0574/3231] Bump mysql-connector-java from 8.0.30 to 8.0.31 in /modules/tidb Bumps mysql-connector-java from 8.0.30 to 8.0.31. --- updated-dependencies: - dependency-name: mysql:mysql-connector-java dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/tidb/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/tidb/build.gradle b/modules/tidb/build.gradle index 36f6acfe806..92f4aa408d3 100644 --- a/modules/tidb/build.gradle +++ b/modules/tidb/build.gradle @@ -4,7 +4,7 @@ dependencies { api project(':jdbc') testImplementation project(':jdbc-test') - testImplementation 'mysql:mysql-connector-java:8.0.30' + testImplementation 'mysql:mysql-connector-java:8.0.31' compileOnly 'org.jetbrains:annotations:23.0.0' } From 070f670a0f7f9c1c4a61c6c1974e42cea41e15c0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 Oct 2022 12:54:58 +0000 Subject: [PATCH 0575/3231] Bump mysql-connector-java from 8.0.21 to 8.0.31 in /examples Bumps mysql-connector-java from 8.0.21 to 8.0.31. --- updated-dependencies: - dependency-name: mysql:mysql-connector-java dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- docs/examples/junit4/generic/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/examples/junit4/generic/build.gradle b/docs/examples/junit4/generic/build.gradle index da4697a4543..9c3edd6fe27 100644 --- a/docs/examples/junit4/generic/build.gradle +++ b/docs/examples/junit4/generic/build.gradle @@ -6,7 +6,7 @@ dependencies { testImplementation project(":selenium") testImplementation project(":mysql") - testImplementation 'mysql:mysql-connector-java:8.0.21' + testImplementation 'mysql:mysql-connector-java:8.0.31' testImplementation "org.seleniumhq.selenium:selenium-api:3.141.59" testImplementation 'org.assertj:assertj-core:3.15.0' } From 852ed7838b1a2d49bca12b6eaf3fcefd7bb82ad8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 Oct 2022 12:54:59 +0000 Subject: [PATCH 0576/3231] Bump pulsar-client from 2.10.1 to 2.10.2 in /modules/pulsar Bumps [pulsar-client](https://github.com/apache/pulsar) from 2.10.1 to 2.10.2. - [Release notes](https://github.com/apache/pulsar/releases) - [Commits](https://github.com/apache/pulsar/compare/v2.10.1...v2.10.2) --- updated-dependencies: - dependency-name: org.apache.pulsar:pulsar-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/pulsar/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/pulsar/build.gradle b/modules/pulsar/build.gradle index 8bc139a3a93..fd015a9121f 100644 --- a/modules/pulsar/build.gradle +++ b/modules/pulsar/build.gradle @@ -3,7 +3,7 @@ description = "Testcontainers :: Pulsar" dependencies { api project(':testcontainers') - testImplementation group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.10.1' + testImplementation group: 'org.apache.pulsar', name: 'pulsar-client', version: '2.10.2' testImplementation group: 'org.assertj', name: 'assertj-core', version: '3.23.1' testImplementation group: 'org.apache.pulsar', name: 'pulsar-client-admin', version: '2.10.1' } From 5e090c2984fb461fa26d76e14f54f46254dc7b6d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 Oct 2022 12:55:00 +0000 Subject: [PATCH 0577/3231] Bump actions/cache from 3.0.9 to 3.0.11 Bumps [actions/cache](https://github.com/actions/cache) from 3.0.9 to 3.0.11. - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/v3.0.9...v3.0.11) --- updated-dependencies: - dependency-name: actions/cache dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/ci-examples.yml | 4 ++-- .github/workflows/ci.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci-examples.yml b/.github/workflows/ci-examples.yml index 527c2eccfeb..16b6d5249c7 100644 --- a/.github/workflows/ci-examples.yml +++ b/.github/workflows/ci-examples.yml @@ -25,7 +25,7 @@ jobs: java-version: '8.0.302' distribution: temurin - name: Cache Gradle Home files - uses: actions/cache@v3.0.9 + uses: actions/cache@v3.0.11 continue-on-error: true with: path: ~/.gradle/caches @@ -53,7 +53,7 @@ jobs: java-version: '8.0.302' distribution: temurin - name: Cache Gradle Home files - uses: actions/cache@v3.0.9 + uses: actions/cache@v3.0.11 continue-on-error: true with: path: ~/.gradle/caches diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b406f256ae2..25723b20ef0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,7 +25,7 @@ jobs: java-version: '8.0.302' distribution: temurin - name: Cache Gradle Home files - uses: actions/cache@v3.0.9 + uses: actions/cache@v3.0.11 with: path: ~/.gradle/caches key: ${{ runner.os }}-gradle-home-testmatrix-${{ hashFiles('**/*.gradle') }} @@ -51,7 +51,7 @@ jobs: java-version: '8.0.302' distribution: temurin - name: Cache Gradle Home files - uses: actions/cache@v3.0.9 + uses: actions/cache@v3.0.11 continue-on-error: true with: path: ~/.gradle/caches From c745c8f7256ca3852165b631e7919009bb926c5c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 Oct 2022 12:55:03 +0000 Subject: [PATCH 0578/3231] Bump peter-evans/create-pull-request from 4.1.3 to 4.2.0 Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 4.1.3 to 4.2.0. - [Release notes](https://github.com/peter-evans/create-pull-request/releases) - [Commits](https://github.com/peter-evans/create-pull-request/compare/671dc9c9e0c2d73f07fa45a3eb0220e1622f0c5f...b4d51739f96fca8047ad065eccef63442d8e99f7) --- updated-dependencies: - dependency-name: peter-evans/create-pull-request dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/update-docs-version.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/update-docs-version.yml b/.github/workflows/update-docs-version.yml index f40b5f41483..53ac0ae9b9a 100644 --- a/.github/workflows/update-docs-version.yml +++ b/.github/workflows/update-docs-version.yml @@ -23,7 +23,7 @@ jobs: sed -i "s/latest_version: .*/latest_version: ${GITHUB_REF##*/}/g" mkdocs.yml git diff - name: Create Pull Request - uses: peter-evans/create-pull-request@671dc9c9e0c2d73f07fa45a3eb0220e1622f0c5f # v3.10.1 + uses: peter-evans/create-pull-request@b4d51739f96fca8047ad065eccef63442d8e99f7 # v3.10.1 with: title: Update docs version to ${GITHUB_REF##*/} body: | From 7f39a39147eff17b5daed4f29419104cf21321b3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 Oct 2022 13:55:48 +0000 Subject: [PATCH 0579/3231] Bump release-drafter/release-drafter from 5.21.0 to 5.21.1 Bumps [release-drafter/release-drafter](https://github.com/release-drafter/release-drafter) from 5.21.0 to 5.21.1. - [Release notes](https://github.com/release-drafter/release-drafter/releases) - [Commits](https://github.com/release-drafter/release-drafter/compare/df69d584deac33d8569990cb6413f82447181076...6df64e4ba4842c203c604c1f45246c5863410adb) --- updated-dependencies: - dependency-name: release-drafter/release-drafter dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/release-drafter.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml index fca7465637f..df1a05c8441 100644 --- a/.github/workflows/release-drafter.yml +++ b/.github/workflows/release-drafter.yml @@ -18,6 +18,6 @@ jobs: if: github.repository == 'testcontainers/testcontainers-java' runs-on: ubuntu-latest steps: - - uses: release-drafter/release-drafter@df69d584deac33d8569990cb6413f82447181076 # v5.19.0 + - uses: release-drafter/release-drafter@6df64e4ba4842c203c604c1f45246c5863410adb # v5.19.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 64bb21083001d41bb13b1c73fd2df4f44718a529 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 Oct 2022 14:22:42 +0000 Subject: [PATCH 0580/3231] Bump junit-jupiter-params from 5.4.2 to 5.9.1 in /examples Bumps [junit-jupiter-params](https://github.com/junit-team/junit5) from 5.4.2 to 5.9.1. - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.4.2...r5.9.1) --- updated-dependencies: - dependency-name: org.junit.jupiter:junit-jupiter-params dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- docs/examples/junit5/redis/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/examples/junit5/redis/build.gradle b/docs/examples/junit5/redis/build.gradle index 72b943adc23..3701d9699e9 100644 --- a/docs/examples/junit5/redis/build.gradle +++ b/docs/examples/junit5/redis/build.gradle @@ -4,7 +4,7 @@ dependencies { api "io.lettuce:lettuce-core:5.1.1.RELEASE" testImplementation "org.junit.jupiter:junit-jupiter-api:5.4.2" - testImplementation "org.junit.jupiter:junit-jupiter-params:5.4.2" + testImplementation "org.junit.jupiter:junit-jupiter-params:5.9.1" testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:5.4.2" testImplementation project(":testcontainers") testImplementation project(":junit-jupiter") From f9edf87f7a02926c6ceaa1cf2ce05ac30842b195 Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Fri, 21 Oct 2022 18:14:44 +0200 Subject: [PATCH 0581/3231] Remove unused Pipfiles (#6046) --- Pipfile | 15 --- Pipfile.lock | 314 --------------------------------------------------- 2 files changed, 329 deletions(-) delete mode 100644 Pipfile delete mode 100644 Pipfile.lock diff --git a/Pipfile b/Pipfile deleted file mode 100644 index 2236de86daf..00000000000 --- a/Pipfile +++ /dev/null @@ -1,15 +0,0 @@ -[[source]] -name = "pypi" -url = "https://pypi.org/simple" -verify_ssl = true - -[dev-packages] - -[packages] -mkdocs = "*" -mkdocs-codeinclude-plugin = "*" -mkdocs-material = "*" -mkdocs-markdownextradata-plugin = "*" - -[requires] -python_version = "3.7" diff --git a/Pipfile.lock b/Pipfile.lock deleted file mode 100644 index 7dad511e377..00000000000 --- a/Pipfile.lock +++ /dev/null @@ -1,314 +0,0 @@ -{ - "_meta": { - "hash": { - "sha256": "d3830267ea93391a077a2ba5b93aa8218c0a38002694b404b56df11498da3e56" - }, - "pipfile-spec": 6, - "requires": { - "python_version": "3.7" - }, - "sources": [ - { - "name": "pypi", - "url": "https://pypi.org/simple", - "verify_ssl": true - } - ] - }, - "default": { - "click": { - "hashes": [ - "sha256:353f466495adaeb40b6b5f592f9f91cb22372351c84caeb068132442a4518ef3", - "sha256:410e932b050f5eed773c4cda94de75971c89cdb3155a72a0831139a79e5ecb5b" - ], - "markers": "python_version >= '3.6'", - "version": "==8.0.3" - }, - "ghp-import": { - "hashes": [ - "sha256:5f8962b30b20652cdffa9c5a9812f7de6bcb56ec475acac579807719bf242c46", - "sha256:947b3771f11be850c852c64b561c600fdddf794bab363060854c1ee7ad05e071" - ], - "version": "==2.0.2" - }, - "importlib-metadata": { - "hashes": [ - "sha256:53ccfd5c134223e497627b9815d5030edf77d2ed573922f7a0b8f8bb81a1c100", - "sha256:75bdec14c397f528724c1bfd9709d660b33a4d2e77387a3358f20b848bb5e5fb" - ], - "markers": "python_version >= '3.6'", - "version": "==4.8.2" - }, - "jinja2": { - "hashes": [ - "sha256:077ce6014f7b40d03b47d1f1ca4b0fc8328a692bd284016f806ed0eaca390ad8", - "sha256:611bb273cd68f3b993fabdc4064fc858c5b47a973cb5aa7999ec1ba405c87cd7" - ], - "markers": "python_version >= '3.6'", - "version": "==3.0.3" - }, - "markdown": { - "hashes": [ - "sha256:76df8ae32294ec39dcf89340382882dfa12975f87f45c3ed1ecdb1e8cefc7006", - "sha256:9923332318f843411e9932237530df53162e29dc7a4e2b91e35764583c46c9a3" - ], - "markers": "python_version >= '3.6'", - "version": "==3.3.6" - }, - "markupsafe": { - "hashes": [ - "sha256:01a9b8ea66f1658938f65b93a85ebe8bc016e6769611be228d797c9d998dd298", - "sha256:023cb26ec21ece8dc3907c0e8320058b2e0cb3c55cf9564da612bc325bed5e64", - "sha256:0446679737af14f45767963a1a9ef7620189912317d095f2d9ffa183a4d25d2b", - "sha256:04635854b943835a6ea959e948d19dcd311762c5c0c6e1f0e16ee57022669194", - "sha256:0717a7390a68be14b8c793ba258e075c6f4ca819f15edfc2a3a027c823718567", - "sha256:0955295dd5eec6cb6cc2fe1698f4c6d84af2e92de33fbcac4111913cd100a6ff", - "sha256:0d4b31cc67ab36e3392bbf3862cfbadac3db12bdd8b02a2731f509ed5b829724", - "sha256:10f82115e21dc0dfec9ab5c0223652f7197feb168c940f3ef61563fc2d6beb74", - "sha256:168cd0a3642de83558a5153c8bd34f175a9a6e7f6dc6384b9655d2697312a646", - "sha256:1d609f577dc6e1aa17d746f8bd3c31aa4d258f4070d61b2aa5c4166c1539de35", - "sha256:1f2ade76b9903f39aa442b4aadd2177decb66525062db244b35d71d0ee8599b6", - "sha256:20dca64a3ef2d6e4d5d615a3fd418ad3bde77a47ec8a23d984a12b5b4c74491a", - "sha256:2a7d351cbd8cfeb19ca00de495e224dea7e7d919659c2841bbb7f420ad03e2d6", - "sha256:2d7d807855b419fc2ed3e631034685db6079889a1f01d5d9dac950f764da3dad", - "sha256:2ef54abee730b502252bcdf31b10dacb0a416229b72c18b19e24a4509f273d26", - "sha256:36bc903cbb393720fad60fc28c10de6acf10dc6cc883f3e24ee4012371399a38", - "sha256:37205cac2a79194e3750b0af2a5720d95f786a55ce7df90c3af697bfa100eaac", - "sha256:3c112550557578c26af18a1ccc9e090bfe03832ae994343cfdacd287db6a6ae7", - "sha256:3dd007d54ee88b46be476e293f48c85048603f5f516008bee124ddd891398ed6", - "sha256:4296f2b1ce8c86a6aea78613c34bb1a672ea0e3de9c6ba08a960efe0b0a09047", - "sha256:47ab1e7b91c098ab893b828deafa1203de86d0bc6ab587b160f78fe6c4011f75", - "sha256:49e3ceeabbfb9d66c3aef5af3a60cc43b85c33df25ce03d0031a608b0a8b2e3f", - "sha256:4dc8f9fb58f7364b63fd9f85013b780ef83c11857ae79f2feda41e270468dd9b", - "sha256:4efca8f86c54b22348a5467704e3fec767b2db12fc39c6d963168ab1d3fc9135", - "sha256:53edb4da6925ad13c07b6d26c2a852bd81e364f95301c66e930ab2aef5b5ddd8", - "sha256:5855f8438a7d1d458206a2466bf82b0f104a3724bf96a1c781ab731e4201731a", - "sha256:594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a", - "sha256:5b6d930f030f8ed98e3e6c98ffa0652bdb82601e7a016ec2ab5d7ff23baa78d1", - "sha256:5bb28c636d87e840583ee3adeb78172efc47c8b26127267f54a9c0ec251d41a9", - "sha256:60bf42e36abfaf9aff1f50f52644b336d4f0a3fd6d8a60ca0d054ac9f713a864", - "sha256:611d1ad9a4288cf3e3c16014564df047fe08410e628f89805e475368bd304914", - "sha256:6300b8454aa6930a24b9618fbb54b5a68135092bc666f7b06901f897fa5c2fee", - "sha256:63f3268ba69ace99cab4e3e3b5840b03340efed0948ab8f78d2fd87ee5442a4f", - "sha256:6557b31b5e2c9ddf0de32a691f2312a32f77cd7681d8af66c2692efdbef84c18", - "sha256:693ce3f9e70a6cf7d2fb9e6c9d8b204b6b39897a2c4a1aa65728d5ac97dcc1d8", - "sha256:6a7fae0dd14cf60ad5ff42baa2e95727c3d81ded453457771d02b7d2b3f9c0c2", - "sha256:6c4ca60fa24e85fe25b912b01e62cb969d69a23a5d5867682dd3e80b5b02581d", - "sha256:6fcf051089389abe060c9cd7caa212c707e58153afa2c649f00346ce6d260f1b", - "sha256:7d91275b0245b1da4d4cfa07e0faedd5b0812efc15b702576d103293e252af1b", - "sha256:89c687013cb1cd489a0f0ac24febe8c7a666e6e221b783e53ac50ebf68e45d86", - "sha256:8d206346619592c6200148b01a2142798c989edcb9c896f9ac9722a99d4e77e6", - "sha256:905fec760bd2fa1388bb5b489ee8ee5f7291d692638ea5f67982d968366bef9f", - "sha256:97383d78eb34da7e1fa37dd273c20ad4320929af65d156e35a5e2d89566d9dfb", - "sha256:984d76483eb32f1bcb536dc27e4ad56bba4baa70be32fa87152832cdd9db0833", - "sha256:99df47edb6bda1249d3e80fdabb1dab8c08ef3975f69aed437cb69d0a5de1e28", - "sha256:9f02365d4e99430a12647f09b6cc8bab61a6564363f313126f775eb4f6ef798e", - "sha256:a30e67a65b53ea0a5e62fe23682cfe22712e01f453b95233b25502f7c61cb415", - "sha256:ab3ef638ace319fa26553db0624c4699e31a28bb2a835c5faca8f8acf6a5a902", - "sha256:aca6377c0cb8a8253e493c6b451565ac77e98c2951c45f913e0b52facdcff83f", - "sha256:add36cb2dbb8b736611303cd3bfcee00afd96471b09cda130da3581cbdc56a6d", - "sha256:b2f4bf27480f5e5e8ce285a8c8fd176c0b03e93dcc6646477d4630e83440c6a9", - "sha256:b7f2d075102dc8c794cbde1947378051c4e5180d52d276987b8d28a3bd58c17d", - "sha256:baa1a4e8f868845af802979fcdbf0bb11f94f1cb7ced4c4b8a351bb60d108145", - "sha256:be98f628055368795d818ebf93da628541e10b75b41c559fdf36d104c5787066", - "sha256:bf5d821ffabf0ef3533c39c518f3357b171a1651c1ff6827325e4489b0e46c3c", - "sha256:c47adbc92fc1bb2b3274c4b3a43ae0e4573d9fbff4f54cd484555edbf030baf1", - "sha256:cdfba22ea2f0029c9261a4bd07e830a8da012291fbe44dc794e488b6c9bb353a", - "sha256:d6c7ebd4e944c85e2c3421e612a7057a2f48d478d79e61800d81468a8d842207", - "sha256:d7f9850398e85aba693bb640262d3611788b1f29a79f0c93c565694658f4071f", - "sha256:d8446c54dc28c01e5a2dbac5a25f071f6653e6e40f3a8818e8b45d790fe6ef53", - "sha256:deb993cacb280823246a026e3b2d81c493c53de6acfd5e6bfe31ab3402bb37dd", - "sha256:e0f138900af21926a02425cf736db95be9f4af72ba1bb21453432a07f6082134", - "sha256:e9936f0b261d4df76ad22f8fee3ae83b60d7c3e871292cd42f40b81b70afae85", - "sha256:f0567c4dc99f264f49fe27da5f735f414c4e7e7dd850cfd8e69f0862d7c74ea9", - "sha256:f5653a225f31e113b152e56f154ccbe59eeb1c7487b39b9d9f9cdb58e6c79dc5", - "sha256:f826e31d18b516f653fe296d967d700fddad5901ae07c622bb3705955e1faa94", - "sha256:f8ba0e8349a38d3001fae7eadded3f6606f0da5d748ee53cc1dab1d6527b9509", - "sha256:f9081981fe268bd86831e5c75f7de206ef275defcb82bc70740ae6dc507aee51", - "sha256:fa130dd50c57d53368c9d59395cb5526eda596d3ffe36666cd81a44d56e48872" - ], - "markers": "python_version >= '3.6'", - "version": "==2.0.1" - }, - "mergedeep": { - "hashes": [ - "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8", - "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307" - ], - "markers": "python_version >= '3.6'", - "version": "==1.3.4" - }, - "mkdocs": { - "hashes": [ - "sha256:89f5a094764381cda656af4298727c9f53dc3e602983087e1fe96ea1df24f4c1", - "sha256:a1fa8c2d0c1305d7fc2b9d9f607c71778572a8b110fb26642aa00296c9e6d072" - ], - "index": "pypi", - "version": "==1.2.3" - }, - "mkdocs-codeinclude-plugin": { - "hashes": [ - "sha256:5d74bd4c1be8e64d71137c1ef1d27a0f9822d3d9ecb2af658d2af7664568a923", - "sha256:a94be142e27ef4a70d8ecc702c21d9596304e06d382d86b39a10ffe8b0375887" - ], - "index": "pypi", - "version": "==0.2.0" - }, - "mkdocs-markdownextradata-plugin": { - "hashes": [ - "sha256:64d1c966b288d653f51f7531c03204eb988d0d77e56055c9d703d99105259a36" - ], - "index": "pypi", - "version": "==0.0.5" - }, - "mkdocs-material": { - "hashes": [ - "sha256:524debb6ee8ee89cee08886f2a67c3c3875c0ee9579c598d7448cbd2607cd3b7", - "sha256:62ae84082fa9f077c86b7db63e7bedf392005041b451defc850f8d0887a11e91" - ], - "index": "pypi", - "version": "==3.2.0" - }, - "packaging": { - "hashes": [ - "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb", - "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522" - ], - "markers": "python_version >= '3.6'", - "version": "==21.3" - }, - "pygments": { - "hashes": [ - "sha256:b8e67fe6af78f492b3c4b3e2970c0624cbf08beb1e493b2c99b9fa1b67a20380", - "sha256:f398865f7eb6874156579fdf36bc840a03cab64d1cde9e93d68f46a425ec52c6" - ], - "markers": "python_version >= '3.5'", - "version": "==2.10.0" - }, - "pymdown-extensions": { - "hashes": [ - "sha256:74247f2c80f1d9e3c7242abe1c16317da36c6f26c7ad4b8a7f457f0ec20f0365", - "sha256:b03e66f91f33af4a6e7a0e20c740313522995f69a03d86316b1449766c473d0e" - ], - "markers": "python_version >= '3.6'", - "version": "==9.1" - }, - "pyparsing": { - "hashes": [ - "sha256:04ff808a5b90911829c55c4e26f75fa5ca8a2f5f36aa3a51f68e27033341d3e4", - "sha256:d9bdec0013ef1eb5a84ab39a3b3868911598afa494f5faa038647101504e2b81" - ], - "markers": "python_version >= '3.6'", - "version": "==3.0.6" - }, - "python-dateutil": { - "hashes": [ - "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86", - "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==2.8.2" - }, - "pyyaml": { - "hashes": [ - "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293", - "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b", - "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57", - "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b", - "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4", - "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07", - "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba", - "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9", - "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287", - "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513", - "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0", - "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0", - "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92", - "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f", - "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2", - "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc", - "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c", - "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86", - "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4", - "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c", - "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34", - "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b", - "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c", - "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb", - "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737", - "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3", - "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d", - "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53", - "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78", - "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803", - "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a", - "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174", - "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5" - ], - "markers": "python_version >= '3.6'", - "version": "==6.0" - }, - "pyyaml-env-tag": { - "hashes": [ - "sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb", - "sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069" - ], - "markers": "python_version >= '3.6'", - "version": "==0.1" - }, - "six": { - "hashes": [ - "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", - "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.16.0" - }, - "typing-extensions": { - "hashes": [ - "sha256:4ca091dea149f945ec56afb48dae714f21e8692ef22a395223bcd328961b6a0e", - "sha256:7f001e5ac290a0c0401508864c7ec868be4e701886d5b573a9528ed3973d9d3b" - ], - "markers": "python_version < '3.8'", - "version": "==4.0.1" - }, - "watchdog": { - "hashes": [ - "sha256:25fb5240b195d17de949588628fdf93032ebf163524ef08933db0ea1f99bd685", - "sha256:3386b367e950a11b0568062b70cc026c6f645428a698d33d39e013aaeda4cc04", - "sha256:3becdb380d8916c873ad512f1701f8a92ce79ec6978ffde92919fd18d41da7fb", - "sha256:4ae38bf8ba6f39d5b83f78661273216e7db5b00f08be7592062cb1fc8b8ba542", - "sha256:8047da932432aa32c515ec1447ea79ce578d0559362ca3605f8e9568f844e3c6", - "sha256:8f1c00aa35f504197561060ca4c21d3cc079ba29cf6dd2fe61024c70160c990b", - "sha256:922a69fa533cb0c793b483becaaa0845f655151e7256ec73630a1b2e9ebcb660", - "sha256:9693f35162dc6208d10b10ddf0458cc09ad70c30ba689d9206e02cd836ce28a3", - "sha256:a0f1c7edf116a12f7245be06120b1852275f9506a7d90227648b250755a03923", - "sha256:a36e75df6c767cbf46f61a91c70b3ba71811dfa0aca4a324d9407a06a8b7a2e7", - "sha256:aba5c812f8ee8a3ff3be51887ca2d55fb8e268439ed44110d3846e4229eb0e8b", - "sha256:ad6f1796e37db2223d2a3f302f586f74c72c630b48a9872c1e7ae8e92e0ab669", - "sha256:ae67501c95606072aafa865b6ed47343ac6484472a2f95490ba151f6347acfc2", - "sha256:b2fcf9402fde2672545b139694284dc3b665fd1be660d73eca6805197ef776a3", - "sha256:b52b88021b9541a60531142b0a451baca08d28b74a723d0c99b13c8c8d48d604", - "sha256:b7d336912853d7b77f9b2c24eeed6a5065d0a0cc0d3b6a5a45ad6d1d05fb8cd8", - "sha256:bd9ba4f332cf57b2c1f698be0728c020399ef3040577cde2939f2e045b39c1e5", - "sha256:be9be735f827820a06340dff2ddea1fb7234561fa5e6300a62fe7f54d40546a0", - "sha256:cca7741c0fcc765568350cb139e92b7f9f3c9a08c4f32591d18ab0a6ac9e71b6", - "sha256:d0d19fb2441947b58fbf91336638c2b9f4cc98e05e1045404d7a4cb7cddc7a65", - "sha256:e02794ac791662a5eafc6ffeaf9bcc149035a0e48eb0a9d40a8feb4622605a3d", - "sha256:e0f30db709c939cabf64a6dc5babb276e6d823fd84464ab916f9b9ba5623ca15", - "sha256:e92c2d33858c8f560671b448205a268096e17870dcf60a9bb3ac7bfbafb7f5f9" - ], - "markers": "python_version >= '3.6'", - "version": "==2.1.6" - }, - "zipp": { - "hashes": [ - "sha256:71c644c5369f4a6e07636f0aa966270449561fcea2e3d6747b8d23efaa9d7832", - "sha256:9fe5ea21568a0a70e50f273397638d39b03353731e6cbbb3fd8502a33fec40bc" - ], - "markers": "python_version >= '3.6'", - "version": "==3.6.0" - } - }, - "develop": {} -} From 643b815a1adb825b948dc5d97906123d90d4eefd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Wangryn?= Date: Fri, 21 Oct 2022 18:49:33 +0200 Subject: [PATCH 0582/3231] Add QuestDB module (#5995) Introduce Testcontainers implementation for `QuestDB`, `QuestDBContainer`. --- .github/ISSUE_TEMPLATE/bug_report.yaml | 1 + .github/ISSUE_TEMPLATE/enhancement.yaml | 1 + .github/ISSUE_TEMPLATE/feature.yaml | 1 + .github/dependabot.yml | 5 ++ .github/labeler.yml | 2 + docs/modules/databases/questdb.md | 29 ++++++ mkdocs.yml | 1 + modules/questdb/build.gradle | 13 +++ .../containers/QuestDBContainer.java | 88 +++++++++++++++++++ .../containers/QuestDBProvider.java | 14 +++ ...s.containers.JdbcDatabaseContainerProvider | 1 + .../org/testcontainers/QuestDBTestImages.java | 7 ++ .../jdbc/questdb/QuestDBJDBCDriverTest.java | 19 ++++ .../junit/questdb/SimpleQuestDBTest.java | 71 +++++++++++++++ 14 files changed, 253 insertions(+) create mode 100644 docs/modules/databases/questdb.md create mode 100644 modules/questdb/build.gradle create mode 100644 modules/questdb/src/main/java/org/testcontainers/containers/QuestDBContainer.java create mode 100644 modules/questdb/src/main/java/org/testcontainers/containers/QuestDBProvider.java create mode 100644 modules/questdb/src/main/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider create mode 100644 modules/questdb/src/test/java/org/testcontainers/QuestDBTestImages.java create mode 100644 modules/questdb/src/test/java/org/testcontainers/jdbc/questdb/QuestDBJDBCDriverTest.java create mode 100644 modules/questdb/src/test/java/org/testcontainers/junit/questdb/SimpleQuestDBTest.java diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index 8196451e88f..37609ce1424 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -41,6 +41,7 @@ body: - PostgreSQL - Presto - Pulsar + - QuestDB - RabbitMQ - Redpanda - Selenium diff --git a/.github/ISSUE_TEMPLATE/enhancement.yaml b/.github/ISSUE_TEMPLATE/enhancement.yaml index 9d65e1f12b8..2bbca5db3ae 100644 --- a/.github/ISSUE_TEMPLATE/enhancement.yaml +++ b/.github/ISSUE_TEMPLATE/enhancement.yaml @@ -41,6 +41,7 @@ body: - PostgreSQL - Presto - Pulsar + - QuestDB - RabbitMQ - Redpanda - Selenium diff --git a/.github/ISSUE_TEMPLATE/feature.yaml b/.github/ISSUE_TEMPLATE/feature.yaml index 581870d976d..d29d0178c39 100644 --- a/.github/ISSUE_TEMPLATE/feature.yaml +++ b/.github/ISSUE_TEMPLATE/feature.yaml @@ -39,6 +39,7 @@ body: - Oracle-XE - OrientDB - PostgreSQL + - QuestDB - Presto - Pulsar - RabbitMQ diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 46c7046300d..2e3afe8fa8a 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -195,6 +195,11 @@ updates: schedule: interval: "monthly" open-pull-requests-limit: 10 + - package-ecosystem: "gradle" + directory: "/modules/questdb" + schedule: + interval: "monthly" + open-pull-requests-limit: 10 - package-ecosystem: "gradle" directory: "/modules/r2dbc" schedule: diff --git a/.github/labeler.yml b/.github/labeler.yml index 85144b7d7f9..bda614305fa 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -65,6 +65,8 @@ - modules/presto/* "modules/pulsar": - modules/pulsar/* +"modules/questdb": + - modules/questdb/* "modules/r2dbc": - modules/r2dbc/* "modules/rabbitmq": diff --git a/docs/modules/databases/questdb.md b/docs/modules/databases/questdb.md new file mode 100644 index 00000000000..8783dd4b757 --- /dev/null +++ b/docs/modules/databases/questdb.md @@ -0,0 +1,29 @@ +# QuestDB Module + +Testcontainers module for [QuestDB](https://github.com/questdb/questdb). QuestDB is a high-performance, open-source SQL +database for applications in financial services, IoT, machine learning, DevOps and observability. + +See [Database containers](./index.md) for documentation and usage that is common to all relational database container +types. + +## Adding this module to your project dependencies + +Add the following dependency to your `pom.xml`/`build.gradle` file: + +=== "Gradle" + +```groovy +testImplementation "org.testcontainers:questdb:{{latest_version}}" +``` + +=== "Maven" + +```xml + + + org.testcontainers + questdb + {{latest_version}} + test + +``` diff --git a/mkdocs.yml b/mkdocs.yml index 70421761e02..8871267cace 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -63,6 +63,7 @@ nav: - modules/databases/orientdb.md - modules/databases/postgres.md - modules/databases/presto.md + - modules/databases/questdb.md - modules/databases/tidb.md - modules/databases/trino.md - modules/azure.md diff --git a/modules/questdb/build.gradle b/modules/questdb/build.gradle new file mode 100644 index 00000000000..79fc0793879 --- /dev/null +++ b/modules/questdb/build.gradle @@ -0,0 +1,13 @@ +description = "Testcontainers :: QuestDB" + +dependencies { + api project(':testcontainers') + api project(':jdbc') + + testImplementation 'org.postgresql:postgresql:42.5.0' + testImplementation project(':jdbc-test') + testImplementation 'org.assertj:assertj-core:3.23.1' + testImplementation 'org.questdb:questdb:6.4.3-jdk8' + testImplementation 'org.awaitility:awaitility:4.2.0' + testImplementation 'org.apache.httpcomponents:httpclient:4.5.13' +} diff --git a/modules/questdb/src/main/java/org/testcontainers/containers/QuestDBContainer.java b/modules/questdb/src/main/java/org/testcontainers/containers/QuestDBContainer.java new file mode 100644 index 00000000000..dd933dd6c95 --- /dev/null +++ b/modules/questdb/src/main/java/org/testcontainers/containers/QuestDBContainer.java @@ -0,0 +1,88 @@ +package org.testcontainers.containers; + +import lombok.NonNull; +import org.testcontainers.containers.wait.strategy.Wait; +import org.testcontainers.utility.DockerImageName; + +/** + * Testcontainers implementation for QuestDB. + * + * @author vangreen + * @author jerrinot + */ +public class QuestDBContainer extends JdbcDatabaseContainer { + + static final String DATABASE_PROVIDER = "postgresql"; + + private static final String DEFAULT_DATABASE_NAME = "qdb"; + + private static final int DEFAULT_COMMIT_LAG_MS = 1000; + + private static final String DEFAULT_USERNAME = "admin"; + + private static final String DEFAULT_PASSWORD = "quest"; + + private static final Integer POSTGRES_PORT = 8812; + + private static final Integer REST_PORT = 9000; + + private static final Integer ILP_PORT = 9009; + + static final String TEST_QUERY = "SELECT 1"; + + static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("questdb/questdb"); + + public QuestDBContainer(@NonNull String dockerImageName) { + this(DockerImageName.parse(dockerImageName)); + } + + public QuestDBContainer(DockerImageName dockerImageName) { + super(dockerImageName); + dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); + withExposedPorts(POSTGRES_PORT, REST_PORT, ILP_PORT); + addEnv("QDB_CAIRO_COMMIT_LAG", String.valueOf(DEFAULT_COMMIT_LAG_MS)); + waitingFor(Wait.forLogMessage("(?i).*A server-main enjoy.*", 1)); + } + + @Override + public String getDriverClassName() { + return "org.postgresql.Driver"; + } + + @Override + public String getJdbcUrl() { + return String.format("jdbc:postgresql://%s:%d/%s", getHost(), getMappedPort(8812), getDefaultDatabaseName()); + } + + @Override + public String getUsername() { + return DEFAULT_USERNAME; + } + + @Override + public String getPassword() { + return DEFAULT_PASSWORD; + } + + @Override + public String getTestQueryString() { + return TEST_QUERY; + } + + @Override + protected void waitUntilContainerStarted() { + getWaitStrategy().waitUntilReady(this); + } + + public String getDefaultDatabaseName() { + return DEFAULT_DATABASE_NAME; + } + + public String getIlpUrl() { + return getHost() + ":" + getMappedPort(ILP_PORT); + } + + public String getHttpUrl() { + return "http://" + getHost() + ":" + getMappedPort(REST_PORT); + } +} diff --git a/modules/questdb/src/main/java/org/testcontainers/containers/QuestDBProvider.java b/modules/questdb/src/main/java/org/testcontainers/containers/QuestDBProvider.java new file mode 100644 index 00000000000..0d4e9c6eeeb --- /dev/null +++ b/modules/questdb/src/main/java/org/testcontainers/containers/QuestDBProvider.java @@ -0,0 +1,14 @@ +package org.testcontainers.containers; + +public class QuestDBProvider extends JdbcDatabaseContainerProvider { + + @Override + public boolean supports(String databaseType) { + return databaseType.equals(QuestDBContainer.DATABASE_PROVIDER); + } + + @Override + public JdbcDatabaseContainer newInstance(String tag) { + return new QuestDBContainer(QuestDBContainer.DEFAULT_IMAGE_NAME.withTag(tag)); + } +} diff --git a/modules/questdb/src/main/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider b/modules/questdb/src/main/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider new file mode 100644 index 00000000000..922a12a1328 --- /dev/null +++ b/modules/questdb/src/main/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider @@ -0,0 +1 @@ +org.testcontainers.containers.QuestDBProvider diff --git a/modules/questdb/src/test/java/org/testcontainers/QuestDBTestImages.java b/modules/questdb/src/test/java/org/testcontainers/QuestDBTestImages.java new file mode 100644 index 00000000000..555b56f172c --- /dev/null +++ b/modules/questdb/src/test/java/org/testcontainers/QuestDBTestImages.java @@ -0,0 +1,7 @@ +package org.testcontainers; + +import org.testcontainers.utility.DockerImageName; + +public interface QuestDBTestImages { + DockerImageName QUESTDB_IMAGE = DockerImageName.parse("questdb/questdb:6.5.3"); +} diff --git a/modules/questdb/src/test/java/org/testcontainers/jdbc/questdb/QuestDBJDBCDriverTest.java b/modules/questdb/src/test/java/org/testcontainers/jdbc/questdb/QuestDBJDBCDriverTest.java new file mode 100644 index 00000000000..b19c2acc5fd --- /dev/null +++ b/modules/questdb/src/test/java/org/testcontainers/jdbc/questdb/QuestDBJDBCDriverTest.java @@ -0,0 +1,19 @@ +package org.testcontainers.jdbc.questdb; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.testcontainers.jdbc.AbstractJDBCDriverTest; + +import java.util.Arrays; +import java.util.EnumSet; + +@RunWith(Parameterized.class) +public class QuestDBJDBCDriverTest extends AbstractJDBCDriverTest { + + @Parameterized.Parameters(name = "{index} - {0}") + public static Iterable data() { + return Arrays.asList( + new Object[][] { { "jdbc:tc:postgresql://hostname/databasename", EnumSet.of(Options.PmdKnownBroken) } } + ); + } +} diff --git a/modules/questdb/src/test/java/org/testcontainers/junit/questdb/SimpleQuestDBTest.java b/modules/questdb/src/test/java/org/testcontainers/junit/questdb/SimpleQuestDBTest.java new file mode 100644 index 00000000000..c38524018c3 --- /dev/null +++ b/modules/questdb/src/test/java/org/testcontainers/junit/questdb/SimpleQuestDBTest.java @@ -0,0 +1,71 @@ +package org.testcontainers.junit.questdb; + +import io.questdb.client.Sender; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.util.EntityUtils; +import org.junit.Test; +import org.testcontainers.QuestDBTestImages; +import org.testcontainers.containers.QuestDBContainer; +import org.testcontainers.db.AbstractContainerDatabaseTest; + +import java.io.IOException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.sql.ResultSet; +import java.sql.SQLException; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; + +public class SimpleQuestDBTest extends AbstractContainerDatabaseTest { + + private static final String TABLE_NAME = "mytable"; + + @Test + public void testSimple() throws SQLException { + try (QuestDBContainer questDB = new QuestDBContainer(QuestDBTestImages.QUESTDB_IMAGE)) { + questDB.start(); + + ResultSet resultSet = performQuery(questDB, questDB.getTestQueryString()); + + int resultSetInt = resultSet.getInt(1); + assertThat(resultSetInt).as("A basic SELECT query succeeds").isEqualTo(1); + } + } + + @Test + public void testRest() throws IOException { + try (QuestDBContainer questdb = new QuestDBContainer(QuestDBTestImages.QUESTDB_IMAGE)) { + questdb.start(); + populateByInfluxLineProtocol(questdb, 1_000); + try (CloseableHttpClient client = HttpClientBuilder.create().build()) { + String encodedSql = URLEncoder.encode("select * from " + TABLE_NAME, "UTF-8"); + HttpGet httpGet = new HttpGet(questdb.getHttpUrl() + "/exec?query=" + encodedSql); + await() + .untilAsserted(() -> { + try (CloseableHttpResponse response = client.execute(httpGet)) { + assertThat(response.getStatusLine().getStatusCode()).isEqualTo(200); + String json = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8); + assertThat(json.contains("\"count\":1000")).isTrue(); + } + }); + } + } + } + + private static void populateByInfluxLineProtocol(QuestDBContainer questdb, int rowCount) { + try (Sender sender = Sender.builder().address(questdb.getIlpUrl()).build()) { + for (int i = 0; i < rowCount; i++) { + sender + .table(TABLE_NAME) + .symbol("sym", "sym1" + i) + .stringColumn("str", "str1" + i) + .longColumn("long", i) + .atNow(); + } + } + } +} From 323ddc00e0db862e156c7a3e75e02be46c615c49 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 21 Oct 2022 16:50:10 +0000 Subject: [PATCH 0583/3231] Bump client-java from 16.0.0 to 16.0.1 in /modules/k3s Bumps [client-java](https://github.com/kubernetes-client/java) from 16.0.0 to 16.0.1. - [Release notes](https://github.com/kubernetes-client/java/releases) - [Changelog](https://github.com/kubernetes-client/java/blob/master/CHANGELOG.md) - [Commits](https://github.com/kubernetes-client/java/compare/v16.0.0...v16.0.1) --- updated-dependencies: - dependency-name: io.kubernetes:client-java dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/k3s/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/k3s/build.gradle b/modules/k3s/build.gradle index 0778419a3e2..e5fdb9f148b 100644 --- a/modules/k3s/build.gradle +++ b/modules/k3s/build.gradle @@ -9,6 +9,6 @@ dependencies { shaded 'com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.8.8' testImplementation 'io.fabric8:kubernetes-client:6.1.1' - testImplementation 'io.kubernetes:client-java:16.0.0' + testImplementation 'io.kubernetes:client-java:16.0.1' testImplementation 'org.assertj:assertj-core:3.23.1' } From dc341029aeb226891070e7a3a8c0681bbc6ce8c1 Mon Sep 17 00:00:00 2001 From: DanielHabenicht Date: Fri, 21 Oct 2022 23:05:19 +0200 Subject: [PATCH 0584/3231] Fix edit link in documentation (#6055) Previously, the edit links always showed a 404 as the `master` branch is not used anymore. Now, the link is updated to use the current `main` branch. --- mkdocs.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/mkdocs.yml b/mkdocs.yml index 8871267cace..e8f2a1918b0 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -109,5 +109,6 @@ nav: - contributing.md - contributing_docs.md - bounty.md +edit_uri: edit/main/docs/ extra: latest_version: 1.17.5 From fa9b6b97fa76b2e951b8f8df8b559205a06c30e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez?= Date: Fri, 21 Oct 2022 17:53:45 -0500 Subject: [PATCH 0585/3231] Fix typo in tekton docs --- .../continuous_integration/tekton.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/supported_docker_environment/continuous_integration/tekton.md b/docs/supported_docker_environment/continuous_integration/tekton.md index f8c6fb96adc..82c9f1d9a71 100644 --- a/docs/supported_docker_environment/continuous_integration/tekton.md +++ b/docs/supported_docker_environment/continuous_integration/tekton.md @@ -1,6 +1,6 @@ # Tekton -o enable access to Docker in Tekton, a dind sidecar needs to be added. An example of it can be found +To enable access to Docker in Tekton, a dind sidecar needs to be added. An example of it can be found [here](https://github.com/tektoncd/pipeline/blob/main/examples/v1beta1/taskruns/dind-sidecar.yaml) This is an example From 263282b52900746e00b338f40dc9d32b502c2690 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Fri, 21 Oct 2022 18:58:00 -0500 Subject: [PATCH 0586/3231] Add travis minimal configuration (#6056) Fixes #4373 --- .../continuous_integration/travis.md | 14 +++++++++++++- mkdocs.yml | 1 + 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/docs/supported_docker_environment/continuous_integration/travis.md b/docs/supported_docker_environment/continuous_integration/travis.md index 860c07f675d..f4ea31abaea 100644 --- a/docs/supported_docker_environment/continuous_integration/travis.md +++ b/docs/supported_docker_environment/continuous_integration/travis.md @@ -1,3 +1,15 @@ # Travis -> TODO +To run Testcontainers on TravisCI, docker needs to be installed. The configuration below +is the minimal required config. + +```yaml +language: java +jdk: +- openjdk8 + +services: +- docker + +script: ./mvnw verify +``` diff --git a/mkdocs.yml b/mkdocs.yml index e8f2a1918b0..47b000c89fb 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -100,6 +100,7 @@ nav: - supported_docker_environment/continuous_integration/gitlab_ci.md - supported_docker_environment/continuous_integration/bitbucket_pipelines.md - supported_docker_environment/continuous_integration/tekton.md + - supported_docker_environment/continuous_integration/travis.md - supported_docker_environment/windows.md - supported_docker_environment/logging_config.md - supported_docker_environment/image_registry_rate_limiting.md From 77f71b855d7684930fde15d515d76a5840c2f00d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Mon, 24 Oct 2022 10:23:32 -0500 Subject: [PATCH 0587/3231] Add support for flags in DatastoreEmulatorContainer (#5993) Datastore can start with additional flags such as `--consistency 1.0` which is recommended for testing. --- .../DatastoreEmulatorContainer.java | 23 +++++++++++-- .../DatastoreEmulatorContainerTest.java | 32 +++++++++++++++++++ 2 files changed, 53 insertions(+), 2 deletions(-) diff --git a/modules/gcloud/src/main/java/org/testcontainers/containers/DatastoreEmulatorContainer.java b/modules/gcloud/src/main/java/org/testcontainers/containers/DatastoreEmulatorContainer.java index b0716829875..db01001d5ec 100644 --- a/modules/gcloud/src/main/java/org/testcontainers/containers/DatastoreEmulatorContainer.java +++ b/modules/gcloud/src/main/java/org/testcontainers/containers/DatastoreEmulatorContainer.java @@ -21,13 +21,32 @@ public class DatastoreEmulatorContainer extends GenericContainer e.contains("--consistency 1.0")); + assertThat(emulator.execInContainer("ls", "/root/.config/").getStdout()).contains("gcloud"); + } + } + + @Test + public void testWithMultipleFlags() throws IOException, InterruptedException { + try ( + DatastoreEmulatorContainer emulator = new DatastoreEmulatorContainer( + "gcr.io/google.com/cloudsdktool/cloud-sdk:367.0.0-emulators" + ) + .withFlags("--consistency 1.0 --data-dir /root/.config/test-gcloud") + ) { + emulator.start(); + + assertThat(emulator.getContainerInfo().getConfig().getCmd()).anyMatch(e -> e.contains("--consistency 1.0")); + assertThat(emulator.execInContainer("ls", "/root/.config/").getStdout()).contains("test-gcloud"); + } + } } From e97b58b65fcfca4628b996f3b120587346d19739 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Tue, 25 Oct 2022 08:51:58 -0500 Subject: [PATCH 0588/3231] Update set-output command in workflows (#6058) `set-output` command has been deprecated and warning messages are printed within the logs. See more [here](https://github.blog/changelog/2022-10-11-github-actions-deprecating-save-state-and-set-output-commands/) --- .github/workflows/ci-examples.yml | 2 +- .github/workflows/ci.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci-examples.yml b/.github/workflows/ci-examples.yml index 527c2eccfeb..425e9ebd970 100644 --- a/.github/workflows/ci-examples.yml +++ b/.github/workflows/ci-examples.yml @@ -39,7 +39,7 @@ jobs: run: | TASKS=$(./gradlew --no-daemon --parallel -q testMatrix) echo $TASKS - echo "::set-output name=matrix::{\"gradle_args\":$TASKS}" + echo "matrix={\"gradle_args\":$TASKS}" >> $GITHUB_OUTPUT check: needs: find_gradle_jobs strategy: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b406f256ae2..d240ca2ea79 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -37,7 +37,7 @@ jobs: run: | TASKS=$(./gradlew --no-daemon --parallel -q testMatrix) echo $TASKS - echo "::set-output name=matrix::{\"gradle_args\":$TASKS}" + echo "matrix={\"gradle_args\":$TASKS}" >> $GITHUB_OUTPUT check: needs: [find_gradle_jobs] strategy: From 84b1ad4efd55d53c068328fa823c1b02390831b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Tue, 25 Oct 2022 08:52:40 -0500 Subject: [PATCH 0589/3231] Apply spotless and checkstyle to examples (#6061) Currently, spotless and checkstyle has been applied to the core modules. Now, both will execute to core modules and examples. --- build.gradle | 24 +---- examples/build.gradle | 11 +++ examples/cucumber/build.gradle | 1 + .../testcontainers/examples/CucumberTest.java | 7 +- .../org/testcontainers/examples/Stepdefs.java | 36 +++---- examples/immudb/src/test/java/ImmuDbTest.java | 20 ++-- .../kafkacluster/KafkaContainerCluster.java | 93 ++++++++++--------- .../KafkaContainerClusterTest.java | 54 ++++++----- .../linkedcontainer/RedmineClient.java | 5 +- .../linkedcontainer/RedmineClientTest.java | 21 +++-- .../linkedcontainer/RedmineContainer.java | 4 +- .../containers/Neo4jExampleTest.java | 28 +++--- .../main/java/com/mycompany/cache/Cache.java | 3 +- .../com/mycompany/cache/RedisBackedCache.java | 2 + .../src/test/java/RedisBackedCacheTest.java | 13 ++- .../main/java/com/mycompany/cache/Cache.java | 3 +- .../com/mycompany/cache/RedisBackedCache.java | 2 + .../src/test/java/RedisBackedCacheTest.java | 11 ++- .../java/com/example/DemoApplication.java | 1 - .../src/test/java/SeleniumContainerTest.java | 18 ++-- .../main/java/com/example/cache/Cache.java | 3 +- .../com/example/cache/RedisBackedCache.java | 2 + .../com/example/AbstractIntegrationTest.java | 2 +- .../com/example/BarConcreteTestClass.java | 5 +- .../com/example/FooConcreteTestClass.java | 4 +- .../main/java/com/example/SearchEngine.java | 1 - .../main/java/com/example/SearchResult.java | 6 +- .../java/com/example/SolrSearchEngine.java | 14 +-- .../test/java/com/example/SolrQueryTest.java | 48 ++++++---- .../java/com/example/DemoApplication.java | 7 +- .../main/java/com/example/DemoController.java | 1 + .../src/main/java/com/example/DemoEntity.java | 1 - .../main/java/com/example/DemoRepository.java | 4 +- .../main/java/com/example/DemoService.java | 5 +- .../com/example/AbstractIntegrationTest.java | 8 +- .../java/com/example/DemoControllerTest.java | 1 - gradle/spotless.gradle | 23 +++++ 37 files changed, 268 insertions(+), 224 deletions(-) create mode 100644 gradle/spotless.gradle diff --git a/build.gradle b/build.gradle index 9e6aa6020e4..7405154491b 100644 --- a/build.gradle +++ b/build.gradle @@ -32,7 +32,7 @@ subprojects { apply plugin: 'idea' apply plugin: 'io.franzbecker.gradle-lombok' apply from: "$rootDir/gradle/shading.gradle" - apply plugin: 'com.diffplug.spotless' + apply from: "$rootDir/gradle/spotless.gradle" apply plugin: 'checkstyle' apply plugin: 'org.gradle.test-retry' @@ -128,28 +128,6 @@ subprojects { testImplementation 'ch.qos.logback:logback-classic:1.3.3' } - spotless { - java { - toggleOffOn() - removeUnusedImports() - trimTrailingWhitespace() - endWithNewline() - - prettier(['prettier': '2.5.1', 'prettier-plugin-java': '1.6.1']) - .config([ - 'parser' : 'java', - 'tabWidth' : 4, - 'printWidth': 120 - ]) - - importOrder('', 'java', 'javax', '\\#') - } - groovyGradle { - target '**/*.groovy' - greclipse('4.19.0') - } - } - checkstyle { toolVersion = "9.3" configFile = rootProject.file('config/checkstyle/checkstyle.xml') diff --git a/examples/build.gradle b/examples/build.gradle index d340e297d33..b1c31a29c7f 100644 --- a/examples/build.gradle +++ b/examples/build.gradle @@ -1,10 +1,21 @@ // empty build.gradle for dependabot +plugins { + id 'com.diffplug.spotless' version '6.8.0' apply false +} + apply from: "$rootDir/../gradle/ci-support.gradle" subprojects { apply plugin:"java" + apply from: "$rootDir/../gradle/spotless.gradle" + apply plugin: 'checkstyle' repositories { mavenCentral() } + + checkstyle { + toolVersion = "9.3" + configFile = rootProject.file('../config/checkstyle/checkstyle.xml') + } } diff --git a/examples/cucumber/build.gradle b/examples/cucumber/build.gradle index 1b9fe04efd8..49dbd831cf5 100644 --- a/examples/cucumber/build.gradle +++ b/examples/cucumber/build.gradle @@ -13,4 +13,5 @@ dependencies { testImplementation 'io.cucumber:cucumber-java:7.8.0' testImplementation 'io.cucumber:cucumber-junit:7.8.0' testImplementation 'org.testcontainers:selenium' + testImplementation 'org.assertj:assertj-core:3.23.1' } diff --git a/examples/cucumber/src/test/java/org/testcontainers/examples/CucumberTest.java b/examples/cucumber/src/test/java/org/testcontainers/examples/CucumberTest.java index f55919c0fe6..717c42351a8 100644 --- a/examples/cucumber/src/test/java/org/testcontainers/examples/CucumberTest.java +++ b/examples/cucumber/src/test/java/org/testcontainers/examples/CucumberTest.java @@ -5,8 +5,5 @@ import org.junit.runner.RunWith; @RunWith(Cucumber.class) -@CucumberOptions(plugin = {"pretty"}) -public class CucumberTest { - - -} +@CucumberOptions(plugin = { "pretty" }) +public class CucumberTest {} diff --git a/examples/cucumber/src/test/java/org/testcontainers/examples/Stepdefs.java b/examples/cucumber/src/test/java/org/testcontainers/examples/Stepdefs.java index e1b9c7e81ef..1378100f959 100644 --- a/examples/cucumber/src/test/java/org/testcontainers/examples/Stepdefs.java +++ b/examples/cucumber/src/test/java/org/testcontainers/examples/Stepdefs.java @@ -10,22 +10,23 @@ import org.openqa.selenium.chrome.ChromeOptions; import org.openqa.selenium.remote.RemoteWebDriver; import org.testcontainers.containers.BrowserWebDriverContainer; +import org.testcontainers.containers.BrowserWebDriverContainer.VncRecordingMode; import org.testcontainers.lifecycle.TestDescription; import java.io.File; import java.util.List; import java.util.Optional; -import static junit.framework.TestCase.assertEquals; -import static org.testcontainers.containers.BrowserWebDriverContainer.VncRecordingMode.RECORD_ALL; +import static org.assertj.core.api.Assertions.assertThat; public class Stepdefs { private BrowserWebDriverContainer container = new BrowserWebDriverContainer() - .withCapabilities(new ChromeOptions()) - .withRecordingMode(RECORD_ALL, new File("build")); + .withCapabilities(new ChromeOptions()) + .withRecordingMode(VncRecordingMode.RECORD_ALL, new File("build")); private String location; + private String answer; @Before @@ -35,17 +36,20 @@ public void beforeScenario() { @After public void afterScenario(Scenario scenario) { - container.afterTest(new TestDescription() { - @Override - public String getTestId() { - return scenario.getId(); - } + container.afterTest( + new TestDescription() { + @Override + public String getTestId() { + return scenario.getId(); + } - @Override - public String getFilesystemFriendlyName() { - return scenario.getName(); - } - }, Optional.of(scenario).filter(Scenario::isFailed).map(__ -> new RuntimeException())); + @Override + public String getFilesystemFriendlyName() { + return scenario.getName(); + } + }, + Optional.of(scenario).filter(Scenario::isFailed).map(__ -> new RuntimeException()) + ); } @Given("^location is \"([^\"]*)\"$") @@ -63,8 +67,6 @@ public void iAskIsItPossibleToSearchHere() throws Exception { @Then("^I should be told \"([^\"]*)\"$") public void iShouldBeTold(String expected) throws Exception { - assertEquals(expected, answer); + assertThat(answer).isEqualTo(expected); } - - } diff --git a/examples/immudb/src/test/java/ImmuDbTest.java b/examples/immudb/src/test/java/ImmuDbTest.java index fd515ddd4aa..f8c208a1073 100644 --- a/examples/immudb/src/test/java/ImmuDbTest.java +++ b/examples/immudb/src/test/java/ImmuDbTest.java @@ -8,6 +8,7 @@ import org.junit.Test; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.wait.strategy.Wait; + import static org.assertj.core.api.Assertions.assertThat; /** @@ -17,28 +18,33 @@ public class ImmuDbTest { // Default port for the ImmuDb server private static final int IMMUDB_PORT = 3322; + // Default username for the ImmuDb server private final String IMMUDB_USER = "immudb"; + // Default password for the ImmuDb server private final String IMMUDB_PASSWORD = "immudb"; + // Default database name for the ImmuDb server private final String IMMUDB_DATABASE = "defaultdb"; // Test container for the ImmuDb database, with the latest version of the image and exposed port @ClassRule public static final GenericContainer immuDbContainer = new GenericContainer<>("codenotary/immudb:1.3") - .withExposedPorts(IMMUDB_PORT).waitingFor( - Wait.forLogMessage(".*Web API server enabled.*", 1) - ); + .withExposedPorts(IMMUDB_PORT) + .waitingFor(Wait.forLogMessage(".*Web API server enabled.*", 1)); + // ImmuClient used to interact with the DB private ImmuClient immuClient; @Before public void setUp() { - this.immuClient = ImmuClient.newBuilder() - .withServerUrl(immuDbContainer.getHost()) - .withServerPort(immuDbContainer.getMappedPort(IMMUDB_PORT)) - .build(); + this.immuClient = + ImmuClient + .newBuilder() + .withServerUrl(immuDbContainer.getHost()) + .withServerPort(immuDbContainer.getMappedPort(IMMUDB_PORT)) + .build(); this.immuClient.login(IMMUDB_USER, IMMUDB_PASSWORD); this.immuClient.useDatabase(IMMUDB_DATABASE); } diff --git a/examples/kafka-cluster/src/test/java/com/example/kafkacluster/KafkaContainerCluster.java b/examples/kafka-cluster/src/test/java/com/example/kafkacluster/KafkaContainerCluster.java index 398488fe3f9..9ab3f25e0ff 100644 --- a/examples/kafka-cluster/src/test/java/com/example/kafkacluster/KafkaContainerCluster.java +++ b/examples/kafka-cluster/src/test/java/com/example/kafkacluster/KafkaContainerCluster.java @@ -4,10 +4,9 @@ import org.rnorth.ducttape.unreliables.Unreliables; import org.testcontainers.containers.Container; import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.KafkaContainer; import org.testcontainers.containers.Network; import org.testcontainers.lifecycle.Startable; -import org.testcontainers.lifecycle.Startables; -import org.testcontainers.containers.KafkaContainer; import org.testcontainers.utility.DockerImageName; import java.time.Duration; @@ -17,16 +16,17 @@ import java.util.stream.IntStream; import java.util.stream.Stream; -import static java.util.concurrent.TimeUnit.SECONDS; - /** * Provides an easy way to launch a Kafka cluster with multiple brokers. */ public class KafkaContainerCluster implements Startable { private final int brokersNum; + private final Network network; + private final GenericContainer zookeeper; + private final Collection brokers; public KafkaContainerCluster(String confluentPlatformVersion, int brokersNum, int internalTopicsRf) { @@ -34,33 +34,39 @@ public KafkaContainerCluster(String confluentPlatformVersion, int brokersNum, in throw new IllegalArgumentException("brokersNum '" + brokersNum + "' must be greater than 0"); } if (internalTopicsRf < 0 || internalTopicsRf > brokersNum) { - throw new IllegalArgumentException("internalTopicsRf '" + internalTopicsRf + "' must be less than brokersNum and greater than 0"); + throw new IllegalArgumentException( + "internalTopicsRf '" + internalTopicsRf + "' must be less than brokersNum and greater than 0" + ); } this.brokersNum = brokersNum; this.network = Network.newNetwork(); - this.zookeeper = new GenericContainer<>(DockerImageName.parse("confluentinc/cp-zookeeper").withTag(confluentPlatformVersion)) - .withNetwork(network) - .withNetworkAliases("zookeeper") - .withEnv("ZOOKEEPER_CLIENT_PORT", String.valueOf(KafkaContainer.ZOOKEEPER_PORT)); - - this.brokers = IntStream - .range(0, this.brokersNum) - .mapToObj(brokerNum -> { - return new KafkaContainer(DockerImageName.parse("confluentinc/cp-kafka").withTag(confluentPlatformVersion)) - .withNetwork(this.network) - .withNetworkAliases("broker-" + brokerNum) - .dependsOn(this.zookeeper) - .withExternalZookeeper("zookeeper:" + KafkaContainer.ZOOKEEPER_PORT) - .withEnv("KAFKA_BROKER_ID", brokerNum + "") - .withEnv("KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR", internalTopicsRf + "") - .withEnv("KAFKA_OFFSETS_TOPIC_NUM_PARTITIONS", internalTopicsRf + "") - .withEnv("KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR", internalTopicsRf + "") - .withEnv("KAFKA_TRANSACTION_STATE_LOG_MIN_ISR", internalTopicsRf + "") - .withStartupTimeout(Duration.ofMinutes(1)); - }) - .collect(Collectors.toList()); + this.zookeeper = + new GenericContainer<>(DockerImageName.parse("confluentinc/cp-zookeeper").withTag(confluentPlatformVersion)) + .withNetwork(network) + .withNetworkAliases("zookeeper") + .withEnv("ZOOKEEPER_CLIENT_PORT", String.valueOf(KafkaContainer.ZOOKEEPER_PORT)); + + this.brokers = + IntStream + .range(0, this.brokersNum) + .mapToObj(brokerNum -> { + return new KafkaContainer( + DockerImageName.parse("confluentinc/cp-kafka").withTag(confluentPlatformVersion) + ) + .withNetwork(this.network) + .withNetworkAliases("broker-" + brokerNum) + .dependsOn(this.zookeeper) + .withExternalZookeeper("zookeeper:" + KafkaContainer.ZOOKEEPER_PORT) + .withEnv("KAFKA_BROKER_ID", brokerNum + "") + .withEnv("KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR", internalTopicsRf + "") + .withEnv("KAFKA_OFFSETS_TOPIC_NUM_PARTITIONS", internalTopicsRf + "") + .withEnv("KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR", internalTopicsRf + "") + .withEnv("KAFKA_TRANSACTION_STATE_LOG_MIN_ISR", internalTopicsRf + "") + .withStartupTimeout(Duration.ofMinutes(1)); + }) + .collect(Collectors.toList()); } public Collection getBrokers() { @@ -68,16 +74,11 @@ public Collection getBrokers() { } public String getBootstrapServers() { - return brokers.stream() - .map(KafkaContainer::getBootstrapServers) - .collect(Collectors.joining(",")); + return brokers.stream().map(KafkaContainer::getBootstrapServers).collect(Collectors.joining(",")); } private Stream> allContainers() { - return Stream.concat( - this.brokers.stream(), - Stream.of(this.zookeeper) - ); + return Stream.concat(this.brokers.stream(), Stream.of(this.zookeeper)); } @Override @@ -86,15 +87,23 @@ public void start() { // sequential start to avoid resource contention on CI systems with weaker hardware brokers.forEach(GenericContainer::start); - Unreliables.retryUntilTrue(30, TimeUnit.SECONDS, () -> { - Container.ExecResult result = this.zookeeper.execInContainer( - "sh", "-c", - "zookeeper-shell zookeeper:" + KafkaContainer.ZOOKEEPER_PORT + " ls /brokers/ids | tail -n 1" - ); - String brokers = result.getStdout(); - - return brokers != null && brokers.split(",").length == this.brokersNum; - }); + Unreliables.retryUntilTrue( + 30, + TimeUnit.SECONDS, + () -> { + Container.ExecResult result = + this.zookeeper.execInContainer( + "sh", + "-c", + "zookeeper-shell zookeeper:" + + KafkaContainer.ZOOKEEPER_PORT + + " ls /brokers/ids | tail -n 1" + ); + String brokers = result.getStdout(); + + return brokers != null && brokers.split(",").length == this.brokersNum; + } + ); } @Override diff --git a/examples/kafka-cluster/src/test/java/com/example/kafkacluster/KafkaContainerClusterTest.java b/examples/kafka-cluster/src/test/java/com/example/kafkacluster/KafkaContainerClusterTest.java index a431c14bba3..63253f4b309 100644 --- a/examples/kafka-cluster/src/test/java/com/example/kafkacluster/KafkaContainerClusterTest.java +++ b/examples/kafka-cluster/src/test/java/com/example/kafkacluster/KafkaContainerClusterTest.java @@ -29,9 +29,7 @@ public class KafkaContainerClusterTest { @Test public void testKafkaContainerCluster() throws Exception { - try ( - KafkaContainerCluster cluster = new KafkaContainerCluster("6.2.1", 3, 2) - ) { + try (KafkaContainerCluster cluster = new KafkaContainerCluster("6.2.1", 3, 2)) { cluster.start(); String bootstrapServers = cluster.getBootstrapServers(); @@ -43,24 +41,27 @@ public void testKafkaContainerCluster() throws Exception { protected void testKafkaFunctionality(String bootstrapServers, int partitions, int rf) throws Exception { try ( - AdminClient adminClient = AdminClient.create(ImmutableMap.of( - AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers - )); - + AdminClient adminClient = AdminClient.create( + ImmutableMap.of(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers) + ); KafkaProducer producer = new KafkaProducer<>( ImmutableMap.of( - ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers, - ProducerConfig.CLIENT_ID_CONFIG, UUID.randomUUID().toString() + ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, + bootstrapServers, + ProducerConfig.CLIENT_ID_CONFIG, + UUID.randomUUID().toString() ), new StringSerializer(), new StringSerializer() ); - KafkaConsumer consumer = new KafkaConsumer<>( ImmutableMap.of( - ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers, - ConsumerConfig.GROUP_ID_CONFIG, "tc-" + UUID.randomUUID(), - ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest" + ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, + bootstrapServers, + ConsumerConfig.GROUP_ID_CONFIG, + "tc-" + UUID.randomUUID(), + ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, + "earliest" ), new StringDeserializer(), new StringDeserializer() @@ -75,23 +76,26 @@ protected void testKafkaFunctionality(String bootstrapServers, int partitions, i producer.send(new ProducerRecord<>(topicName, "testcontainers", "rulezzz")).get(); - Unreliables.retryUntilTrue(10, TimeUnit.SECONDS, () -> { - ConsumerRecords records = consumer.poll(Duration.ofMillis(100)); + Unreliables.retryUntilTrue( + 10, + TimeUnit.SECONDS, + () -> { + ConsumerRecords records = consumer.poll(Duration.ofMillis(100)); - if (records.isEmpty()) { - return false; - } + if (records.isEmpty()) { + return false; + } - assertThat(records) - .hasSize(1) - .extracting(ConsumerRecord::topic, ConsumerRecord::key, ConsumerRecord::value) - .containsExactly(tuple(topicName, "testcontainers", "rulezzz")); + assertThat(records) + .hasSize(1) + .extracting(ConsumerRecord::topic, ConsumerRecord::key, ConsumerRecord::value) + .containsExactly(tuple(topicName, "testcontainers", "rulezzz")); - return true; - }); + return true; + } + ); consumer.unsubscribe(); } } - } diff --git a/examples/linked-container/src/main/java/com/example/linkedcontainer/RedmineClient.java b/examples/linked-container/src/main/java/com/example/linkedcontainer/RedmineClient.java index be62d932971..c95ec133e2f 100644 --- a/examples/linked-container/src/main/java/com/example/linkedcontainer/RedmineClient.java +++ b/examples/linked-container/src/main/java/com/example/linkedcontainer/RedmineClient.java @@ -13,6 +13,7 @@ public class RedmineClient { private String url; + private OkHttpClient client; public RedmineClient(String url) { @@ -21,9 +22,7 @@ public RedmineClient(String url) { } public int getIssueCount() throws IOException { - Request request = new Request.Builder() - .url(url + "/issues.json") - .build(); + Request request = new Request.Builder().url(url + "/issues.json").build(); Response response = client.newCall(request).execute(); JSONObject jsonObject = new JSONObject(response.body().string()); diff --git a/examples/linked-container/src/test/java/com/example/linkedcontainer/RedmineClientTest.java b/examples/linked-container/src/test/java/com/example/linkedcontainer/RedmineClientTest.java index 6e4186698e7..721c00835c9 100644 --- a/examples/linked-container/src/test/java/com/example/linkedcontainer/RedmineClientTest.java +++ b/examples/linked-container/src/test/java/com/example/linkedcontainer/RedmineClientTest.java @@ -13,25 +13,26 @@ public class RedmineClientTest { private static final String POSTGRES_USERNAME = "redmine"; + private static final String POSTGRES_PASSWORD = "secret"; - private PostgreSQLContainer postgreSQLContainer = new PostgreSQLContainer<>(LinkedContainerTestImages.POSTGRES_TEST_IMAGE) - .withUsername(POSTGRES_USERNAME) - .withPassword(POSTGRES_PASSWORD); + private PostgreSQLContainer postgreSQLContainer = new PostgreSQLContainer<>( + LinkedContainerTestImages.POSTGRES_TEST_IMAGE + ) + .withUsername(POSTGRES_USERNAME) + .withPassword(POSTGRES_PASSWORD); private RedmineContainer redmineContainer = new RedmineContainer(LinkedContainerTestImages.REDMINE_TEST_IMAGE) - .withLinkToContainer(postgreSQLContainer, "postgres") - .withEnv("POSTGRES_ENV_POSTGRES_USER", POSTGRES_USERNAME) - .withEnv("POSTGRES_ENV_POSTGRES_PASSWORD", POSTGRES_PASSWORD); + .withLinkToContainer(postgreSQLContainer, "postgres") + .withEnv("POSTGRES_ENV_POSTGRES_USER", POSTGRES_USERNAME) + .withEnv("POSTGRES_ENV_POSTGRES_PASSWORD", POSTGRES_PASSWORD); @Rule - public RuleChain chain = RuleChain.outerRule(postgreSQLContainer) - .around(redmineContainer); + public RuleChain chain = RuleChain.outerRule(postgreSQLContainer).around(redmineContainer); @Test public void canGetIssueCount() throws Exception { - RedmineClient redmineClient = new RedmineClient( - redmineContainer.getRedmineUrl()); + RedmineClient redmineClient = new RedmineClient(redmineContainer.getRedmineUrl()); assertThat(redmineClient.getIssueCount()).as("The issue count can be retrieved.").isZero(); } diff --git a/examples/linked-container/src/test/java/com/example/linkedcontainer/RedmineContainer.java b/examples/linked-container/src/test/java/com/example/linkedcontainer/RedmineContainer.java index 4303bbee682..0f0bea36c1a 100644 --- a/examples/linked-container/src/test/java/com/example/linkedcontainer/RedmineContainer.java +++ b/examples/linked-container/src/test/java/com/example/linkedcontainer/RedmineContainer.java @@ -28,8 +28,6 @@ public RedmineContainer withLinkToContainer(LinkableContainer otherContainer, St } public String getRedmineUrl() { - return String.format("http://%s:%d", - this.getHost(), - this.getMappedPort(REDMINE_PORT)); + return String.format("http://%s:%d", this.getHost(), this.getMappedPort(REDMINE_PORT)); } } diff --git a/examples/neo4j-container/src/test/java/org/testcontainers/containers/Neo4jExampleTest.java b/examples/neo4j-container/src/test/java/org/testcontainers/containers/Neo4jExampleTest.java index bc7aeed10bd..9ce3a3f078c 100644 --- a/examples/neo4j-container/src/test/java/org/testcontainers/containers/Neo4jExampleTest.java +++ b/examples/neo4j-container/src/test/java/org/testcontainers/containers/Neo4jExampleTest.java @@ -1,5 +1,14 @@ package org.testcontainers.containers; +import org.junit.jupiter.api.Test; +import org.neo4j.driver.AuthTokens; +import org.neo4j.driver.Driver; +import org.neo4j.driver.GraphDatabase; +import org.neo4j.driver.Session; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; +import org.testcontainers.utility.DockerImageName; + import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; @@ -10,15 +19,6 @@ import java.util.Collections; import java.util.stream.Collectors; -import org.junit.jupiter.api.Test; -import org.neo4j.driver.AuthTokens; -import org.neo4j.driver.Driver; -import org.neo4j.driver.GraphDatabase; -import org.neo4j.driver.Session; -import org.testcontainers.junit.jupiter.Container; -import org.testcontainers.junit.jupiter.Testcontainers; -import org.testcontainers.utility.DockerImageName; - import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.fail; @@ -27,19 +27,14 @@ class Neo4jExampleTest { @Container - private static Neo4jContainer neo4jContainer = - new Neo4jContainer<>(DockerImageName.parse("neo4j:4.4")) + private static Neo4jContainer neo4jContainer = new Neo4jContainer<>(DockerImageName.parse("neo4j:4.4")) .withAdminPassword(null); // Disable password @Test void testSomethingUsingBolt() { - // Retrieve the Bolt URL from the container String boltUrl = neo4jContainer.getBoltUrl(); - try ( - Driver driver = GraphDatabase.driver(boltUrl, AuthTokens.none()); - Session session = driver.session() - ) { + try (Driver driver = GraphDatabase.driver(boltUrl, AuthTokens.none()); Session session = driver.session()) { long one = session.run("RETURN 1", Collections.emptyMap()).next().get(0).asLong(); assertThat(one).isEqualTo(1L); } catch (Exception e) { @@ -49,7 +44,6 @@ void testSomethingUsingBolt() { @Test void testSomethingUsingHttp() throws IOException { - // Retrieve the HTTP URL from the container String httpUrl = neo4jContainer.getHttpUrl(); diff --git a/examples/redis-backed-cache-testng/src/main/java/com/mycompany/cache/Cache.java b/examples/redis-backed-cache-testng/src/main/java/com/mycompany/cache/Cache.java index 996c31ecac7..f0c7710b355 100644 --- a/examples/redis-backed-cache-testng/src/main/java/com/mycompany/cache/Cache.java +++ b/examples/redis-backed-cache-testng/src/main/java/com/mycompany/cache/Cache.java @@ -6,7 +6,6 @@ * Cache, for storing data associated with keys. */ public interface Cache { - /** * Store a value object in the cache with no specific expiry time. The object may be evicted by the cache any time, * if necessary. @@ -24,4 +23,4 @@ public interface Cache { * @return the object if it was in the cache, or an empty Optional if not found. */ Optional get(String key, Class expectedClass); -} \ No newline at end of file +} diff --git a/examples/redis-backed-cache-testng/src/main/java/com/mycompany/cache/RedisBackedCache.java b/examples/redis-backed-cache-testng/src/main/java/com/mycompany/cache/RedisBackedCache.java index 2c80818c150..8349de77869 100644 --- a/examples/redis-backed-cache-testng/src/main/java/com/mycompany/cache/RedisBackedCache.java +++ b/examples/redis-backed-cache-testng/src/main/java/com/mycompany/cache/RedisBackedCache.java @@ -11,7 +11,9 @@ public class RedisBackedCache implements Cache { private final Jedis jedis; + private final String cacheName; + private final Gson gson; public RedisBackedCache(Jedis jedis, String cacheName) { diff --git a/examples/redis-backed-cache-testng/src/test/java/RedisBackedCacheTest.java b/examples/redis-backed-cache-testng/src/test/java/RedisBackedCacheTest.java index e02b271f290..45c482c098e 100644 --- a/examples/redis-backed-cache-testng/src/test/java/RedisBackedCacheTest.java +++ b/examples/redis-backed-cache-testng/src/test/java/RedisBackedCacheTest.java @@ -17,13 +17,14 @@ */ public class RedisBackedCacheTest { - private static GenericContainer redis = new GenericContainer<>(DockerImageName.parse("redis:3.0.6")).withExposedPorts(6379); + private static GenericContainer redis = new GenericContainer<>(DockerImageName.parse("redis:3.0.6")) + .withExposedPorts(6379); private Cache cache; @BeforeClass public static void startContainer() { - redis.start(); + redis.start(); } @AfterClass @@ -44,13 +45,17 @@ public void testFindingAnInsertedValue() { Optional foundObject = cache.get("foo", String.class); assertThat(foundObject).as("When an object in the cache is retrieved, it can be found").isPresent(); - assertThat(foundObject).as("When we put a String in to the cache and retrieve it, the value is the same").contains("FOO"); + assertThat(foundObject) + .as("When we put a String in to the cache and retrieve it, the value is the same") + .contains("FOO"); } @Test public void testNotFindingAValueThatWasNotInserted() { Optional foundObject = cache.get("bar", String.class); - assertThat(foundObject).as("When an object that's not in the cache is retrieved, nothing is found").isNotPresent(); + assertThat(foundObject) + .as("When an object that's not in the cache is retrieved, nothing is found") + .isNotPresent(); } } diff --git a/examples/redis-backed-cache/src/main/java/com/mycompany/cache/Cache.java b/examples/redis-backed-cache/src/main/java/com/mycompany/cache/Cache.java index 996c31ecac7..f0c7710b355 100644 --- a/examples/redis-backed-cache/src/main/java/com/mycompany/cache/Cache.java +++ b/examples/redis-backed-cache/src/main/java/com/mycompany/cache/Cache.java @@ -6,7 +6,6 @@ * Cache, for storing data associated with keys. */ public interface Cache { - /** * Store a value object in the cache with no specific expiry time. The object may be evicted by the cache any time, * if necessary. @@ -24,4 +23,4 @@ public interface Cache { * @return the object if it was in the cache, or an empty Optional if not found. */ Optional get(String key, Class expectedClass); -} \ No newline at end of file +} diff --git a/examples/redis-backed-cache/src/main/java/com/mycompany/cache/RedisBackedCache.java b/examples/redis-backed-cache/src/main/java/com/mycompany/cache/RedisBackedCache.java index 2c80818c150..8349de77869 100644 --- a/examples/redis-backed-cache/src/main/java/com/mycompany/cache/RedisBackedCache.java +++ b/examples/redis-backed-cache/src/main/java/com/mycompany/cache/RedisBackedCache.java @@ -11,7 +11,9 @@ public class RedisBackedCache implements Cache { private final Jedis jedis; + private final String cacheName; + private final Gson gson; public RedisBackedCache(Jedis jedis, String cacheName) { diff --git a/examples/redis-backed-cache/src/test/java/RedisBackedCacheTest.java b/examples/redis-backed-cache/src/test/java/RedisBackedCacheTest.java index 24a21723b15..e820646a0da 100644 --- a/examples/redis-backed-cache/src/test/java/RedisBackedCacheTest.java +++ b/examples/redis-backed-cache/src/test/java/RedisBackedCacheTest.java @@ -18,7 +18,8 @@ public class RedisBackedCacheTest { @Rule public GenericContainer redis = new GenericContainer<>(DockerImageName.parse("redis:3.0.6")) - .withExposedPorts(6379); + .withExposedPorts(6379); + private Cache cache; @Before @@ -34,13 +35,17 @@ public void testFindingAnInsertedValue() { Optional foundObject = cache.get("foo", String.class); assertThat(foundObject.isPresent()).as("When an object in the cache is retrieved, it can be found").isTrue(); - assertThat(foundObject.get()).as("When we put a String in to the cache and retrieve it, the value is the same").isEqualTo("FOO"); + assertThat(foundObject.get()) + .as("When we put a String in to the cache and retrieve it, the value is the same") + .isEqualTo("FOO"); } @Test public void testNotFindingAValueThatWasNotInserted() { Optional foundObject = cache.get("bar", String.class); - assertThat(foundObject.isPresent()).as("When an object that's not in the cache is retrieved, nothing is found").isFalse(); + assertThat(foundObject.isPresent()) + .as("When an object that's not in the cache is retrieved, nothing is found") + .isFalse(); } } diff --git a/examples/selenium-container/src/main/java/com/example/DemoApplication.java b/examples/selenium-container/src/main/java/com/example/DemoApplication.java index 909a1ec36e1..6c42baef27d 100644 --- a/examples/selenium-container/src/main/java/com/example/DemoApplication.java +++ b/examples/selenium-container/src/main/java/com/example/DemoApplication.java @@ -9,5 +9,4 @@ public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } - } diff --git a/examples/selenium-container/src/test/java/SeleniumContainerTest.java b/examples/selenium-container/src/test/java/SeleniumContainerTest.java index c28c9bc54cb..52871c4f37e 100644 --- a/examples/selenium-container/src/test/java/SeleniumContainerTest.java +++ b/examples/selenium-container/src/test/java/SeleniumContainerTest.java @@ -7,8 +7,8 @@ import org.openqa.selenium.chrome.ChromeOptions; import org.openqa.selenium.remote.RemoteWebDriver; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.web.context.WebServerInitializedEvent; import org.springframework.boot.test.web.server.LocalServerPort; +import org.springframework.boot.web.context.WebServerInitializedEvent; import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.ApplicationListener; import org.springframework.context.ConfigurableApplicationContext; @@ -16,12 +16,12 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.testcontainers.Testcontainers; import org.testcontainers.containers.BrowserWebDriverContainer; +import org.testcontainers.containers.BrowserWebDriverContainer.VncRecordingMode; import java.io.File; import java.util.List; import static org.assertj.core.api.Assertions.assertThat; -import static org.testcontainers.containers.BrowserWebDriverContainer.VncRecordingMode.RECORD_ALL; /** * Simple example of plain Selenium usage. @@ -36,8 +36,8 @@ public class SeleniumContainerTest { @Rule public BrowserWebDriverContainer chrome = new BrowserWebDriverContainer() - .withCapabilities(new ChromeOptions()) - .withRecordingMode(RECORD_ALL, new File("build")); + .withCapabilities(new ChromeOptions()) + .withRecordingMode(VncRecordingMode.RECORD_ALL, new File("build")); @Test public void simplePlainSeleniumTest() { @@ -50,12 +50,14 @@ public void simplePlainSeleniumTest() { } public static class Initializer implements ApplicationContextInitializer { + @Override public void initialize(ConfigurableApplicationContext applicationContext) { - applicationContext.addApplicationListener((ApplicationListener) event -> { - Testcontainers.exposeHostPorts(event.getWebServer().getPort()); - }); + applicationContext.addApplicationListener( + (ApplicationListener) event -> { + Testcontainers.exposeHostPorts(event.getWebServer().getPort()); + } + ); } } - } diff --git a/examples/singleton-container/src/main/java/com/example/cache/Cache.java b/examples/singleton-container/src/main/java/com/example/cache/Cache.java index 986838d20f9..8a0c5776d80 100644 --- a/examples/singleton-container/src/main/java/com/example/cache/Cache.java +++ b/examples/singleton-container/src/main/java/com/example/cache/Cache.java @@ -3,8 +3,7 @@ import java.util.Optional; public interface Cache { - void put(String key, Object value); Optional get(String key, Class expectedClass); -} \ No newline at end of file +} diff --git a/examples/singleton-container/src/main/java/com/example/cache/RedisBackedCache.java b/examples/singleton-container/src/main/java/com/example/cache/RedisBackedCache.java index 34553dde1f4..430afb2b1f5 100644 --- a/examples/singleton-container/src/main/java/com/example/cache/RedisBackedCache.java +++ b/examples/singleton-container/src/main/java/com/example/cache/RedisBackedCache.java @@ -8,7 +8,9 @@ public class RedisBackedCache implements Cache { private final Jedis jedis; + private final String cacheName; + private final Gson gson; public RedisBackedCache(Jedis jedis, String cacheName) { diff --git a/examples/singleton-container/src/test/java/com/example/AbstractIntegrationTest.java b/examples/singleton-container/src/test/java/com/example/AbstractIntegrationTest.java index d152540fb1c..729e9fb124d 100644 --- a/examples/singleton-container/src/test/java/com/example/AbstractIntegrationTest.java +++ b/examples/singleton-container/src/test/java/com/example/AbstractIntegrationTest.java @@ -6,7 +6,7 @@ public abstract class AbstractIntegrationTest { public static final GenericContainer redis = new GenericContainer<>(DockerImageName.parse("redis:3.0.6")) - .withExposedPorts(6379); + .withExposedPorts(6379); static { redis.start(); diff --git a/examples/singleton-container/src/test/java/com/example/BarConcreteTestClass.java b/examples/singleton-container/src/test/java/com/example/BarConcreteTestClass.java index fff6e1a4a3a..6b9050be277 100644 --- a/examples/singleton-container/src/test/java/com/example/BarConcreteTestClass.java +++ b/examples/singleton-container/src/test/java/com/example/BarConcreteTestClass.java @@ -27,7 +27,8 @@ public void testInsertValue() { Optional foundObject = cache.get("bar", String.class); assertThat(foundObject).as("When inserting an object into the cache, it can be retrieved").isPresent(); - assertThat(foundObject).as("When accessing the value of a retrieved object, the value must be the same").contains("BAR"); + assertThat(foundObject) + .as("When accessing the value of a retrieved object, the value must be the same") + .contains("BAR"); } - } diff --git a/examples/singleton-container/src/test/java/com/example/FooConcreteTestClass.java b/examples/singleton-container/src/test/java/com/example/FooConcreteTestClass.java index 11ad98a0580..ea071202dcd 100644 --- a/examples/singleton-container/src/test/java/com/example/FooConcreteTestClass.java +++ b/examples/singleton-container/src/test/java/com/example/FooConcreteTestClass.java @@ -27,6 +27,8 @@ public void testInsertValue() { Optional foundObject = cache.get("foo", String.class); assertThat(foundObject).as("When inserting an object into the cache, it can be retrieved").isPresent(); - assertThat(foundObject).as("When accessing the value of a retrieved object, the value must be the same").contains("FOO"); + assertThat(foundObject) + .as("When accessing the value of a retrieved object, the value must be the same") + .contains("FOO"); } } diff --git a/examples/solr-container/src/main/java/com/example/SearchEngine.java b/examples/solr-container/src/main/java/com/example/SearchEngine.java index c05bc23aad2..397327fe369 100644 --- a/examples/solr-container/src/main/java/com/example/SearchEngine.java +++ b/examples/solr-container/src/main/java/com/example/SearchEngine.java @@ -1,6 +1,5 @@ package com.example; public interface SearchEngine { - public SearchResult search(String term); } diff --git a/examples/solr-container/src/main/java/com/example/SearchResult.java b/examples/solr-container/src/main/java/com/example/SearchResult.java index 06544f0ba34..89722d1a5a4 100644 --- a/examples/solr-container/src/main/java/com/example/SearchResult.java +++ b/examples/solr-container/src/main/java/com/example/SearchResult.java @@ -1,13 +1,13 @@ package com.example; -import java.util.List; -import java.util.Map; - import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; +import java.util.List; +import java.util.Map; + @Data @Builder @NoArgsConstructor diff --git a/examples/solr-container/src/main/java/com/example/SolrSearchEngine.java b/examples/solr-container/src/main/java/com/example/SolrSearchEngine.java index 8a7f38e455d..3be96f76690 100644 --- a/examples/solr-container/src/main/java/com/example/SolrSearchEngine.java +++ b/examples/solr-container/src/main/java/com/example/SolrSearchEngine.java @@ -1,16 +1,15 @@ package com.example; -import java.util.stream.Collectors; - import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; - import org.apache.solr.client.solrj.SolrClient; import org.apache.solr.client.solrj.SolrQuery; import org.apache.solr.client.solrj.response.QueryResponse; import org.apache.solr.client.solrj.util.ClientUtils; import org.apache.solr.common.SolrDocument; +import java.util.stream.Collectors; + @RequiredArgsConstructor public class SolrSearchEngine implements SearchEngine { @@ -20,7 +19,6 @@ public class SolrSearchEngine implements SearchEngine { @SneakyThrows public SearchResult search(String term) { - SolrQuery query = new SolrQuery(); query.setQuery("title:" + ClientUtils.escapeQueryChars(term)); QueryResponse response = client.query(COLLECTION_NAME, query); @@ -28,12 +26,10 @@ public SearchResult search(String term) { } private SearchResult createResult(QueryResponse response) { - return SearchResult.builder() + return SearchResult + .builder() .totalHits(response.getResults().getNumFound()) - .results(response.getResults() - .stream() - .map(SolrDocument::getFieldValueMap) - .collect(Collectors.toList())) + .results(response.getResults().stream().map(SolrDocument::getFieldValueMap).collect(Collectors.toList())) .build(); } } diff --git a/examples/solr-container/src/test/java/com/example/SolrQueryTest.java b/examples/solr-container/src/test/java/com/example/SolrQueryTest.java index 6443a19b869..e65504c0276 100644 --- a/examples/solr-container/src/test/java/com/example/SolrQueryTest.java +++ b/examples/solr-container/src/test/java/com/example/SolrQueryTest.java @@ -15,7 +15,6 @@ import java.util.HashMap; import java.util.Map; -import static com.example.SolrSearchEngine.COLLECTION_NAME; import static org.assertj.core.api.Assertions.assertThat; public class SolrQueryTest { @@ -23,31 +22,44 @@ public class SolrQueryTest { private static final DockerImageName SOLR_IMAGE = DockerImageName.parse("solr:8.3.0"); public static final SolrContainer solrContainer = new SolrContainer(SOLR_IMAGE) - .withCollection(COLLECTION_NAME); + .withCollection(SolrSearchEngine.COLLECTION_NAME); private static SolrClient solrClient; @BeforeClass public static void setUp() throws IOException, SolrServerException { solrContainer.start(); - solrClient = new Http2SolrClient.Builder("http://" + solrContainer.getHost() + ":" + solrContainer.getSolrPort() + "/solr").build(); + solrClient = + new Http2SolrClient.Builder( + "http://" + solrContainer.getHost() + ":" + solrContainer.getSolrPort() + "/solr" + ) + .build(); // Add Sample Data - solrClient.add(COLLECTION_NAME, Collections.singletonList( - new SolrInputDocument(createMap( - "id", createInputField("id", "1"), - "title", createInputField("title", "old skool - trainers - shoes") - )) - )); - - solrClient.add(COLLECTION_NAME, Collections.singletonList( - new SolrInputDocument(createMap( - "id", createInputField("id", "2"), - "title", createInputField("title", "print t-shirt") - )) - )); - - solrClient.commit(COLLECTION_NAME); + solrClient.add( + SolrSearchEngine.COLLECTION_NAME, + Collections.singletonList( + new SolrInputDocument( + createMap( + "id", + createInputField("id", "1"), + "title", + createInputField("title", "old skool - trainers - shoes") + ) + ) + ) + ); + + solrClient.add( + SolrSearchEngine.COLLECTION_NAME, + Collections.singletonList( + new SolrInputDocument( + createMap("id", createInputField("id", "2"), "title", createInputField("title", "print t-shirt")) + ) + ) + ); + + solrClient.commit(SolrSearchEngine.COLLECTION_NAME); } @Test diff --git a/examples/spring-boot/src/main/java/com/example/DemoApplication.java b/examples/spring-boot/src/main/java/com/example/DemoApplication.java index c968eb42b78..af96419ad6a 100644 --- a/examples/spring-boot/src/main/java/com/example/DemoApplication.java +++ b/examples/spring-boot/src/main/java/com/example/DemoApplication.java @@ -8,8 +8,7 @@ @EnableJpaRepositories public class DemoApplication { - public static void main(String[] args) { - SpringApplication.run(DemoApplication.class, args); - } - + public static void main(String[] args) { + SpringApplication.run(DemoApplication.class, args); + } } diff --git a/examples/spring-boot/src/main/java/com/example/DemoController.java b/examples/spring-boot/src/main/java/com/example/DemoController.java index 79f48711cad..90cea36804e 100644 --- a/examples/spring-boot/src/main/java/com/example/DemoController.java +++ b/examples/spring-boot/src/main/java/com/example/DemoController.java @@ -7,6 +7,7 @@ public class DemoController { private final StringRedisTemplate stringRedisTemplate; + private final DemoService demoService; public DemoController(StringRedisTemplate stringRedisTemplate, DemoService demoService) { diff --git a/examples/spring-boot/src/main/java/com/example/DemoEntity.java b/examples/spring-boot/src/main/java/com/example/DemoEntity.java index 47c0fa94f75..71fceb2c8e8 100644 --- a/examples/spring-boot/src/main/java/com/example/DemoEntity.java +++ b/examples/spring-boot/src/main/java/com/example/DemoEntity.java @@ -7,7 +7,6 @@ import javax.persistence.GeneratedValue; import javax.persistence.Id; - @Data @Entity public class DemoEntity { diff --git a/examples/spring-boot/src/main/java/com/example/DemoRepository.java b/examples/spring-boot/src/main/java/com/example/DemoRepository.java index 89f3775deb0..8ed3216cc18 100644 --- a/examples/spring-boot/src/main/java/com/example/DemoRepository.java +++ b/examples/spring-boot/src/main/java/com/example/DemoRepository.java @@ -1,7 +1,5 @@ package com.example; - import org.springframework.data.jpa.repository.JpaRepository; -public interface DemoRepository extends JpaRepository { -} +public interface DemoRepository extends JpaRepository {} diff --git a/examples/spring-boot/src/main/java/com/example/DemoService.java b/examples/spring-boot/src/main/java/com/example/DemoService.java index 18e8690a99f..78e8c41e907 100644 --- a/examples/spring-boot/src/main/java/com/example/DemoService.java +++ b/examples/spring-boot/src/main/java/com/example/DemoService.java @@ -4,15 +4,14 @@ @Service public class DemoService { + private final DemoRepository demoRepository; public DemoService(DemoRepository demoRepository) { this.demoRepository = demoRepository; } - public DemoEntity getDemoEntity(Long id) { - return demoRepository.findById(id) - .orElseThrow(() ->new RuntimeException("Entity not found")); + return demoRepository.findById(id).orElseThrow(() -> new RuntimeException("Entity not found")); } } diff --git a/examples/spring-boot/src/test/java/com/example/AbstractIntegrationTest.java b/examples/spring-boot/src/test/java/com/example/AbstractIntegrationTest.java index dd1b7b6fb5a..8a1a508c0b3 100644 --- a/examples/spring-boot/src/test/java/com/example/AbstractIntegrationTest.java +++ b/examples/spring-boot/src/test/java/com/example/AbstractIntegrationTest.java @@ -11,9 +11,11 @@ import org.testcontainers.utility.DockerImageName; @RunWith(SpringJUnit4ClassRunner.class) -@SpringBootTest(classes = DemoApplication.class, webEnvironment = WebEnvironment.RANDOM_PORT, properties = { - "spring.datasource.url=jdbc:tc:postgresql:11-alpine:///databasename", -}) +@SpringBootTest( + classes = DemoApplication.class, + webEnvironment = WebEnvironment.RANDOM_PORT, + properties = { "spring.datasource.url=jdbc:tc:postgresql:11-alpine:///databasename" } +) @ActiveProfiles("test") public abstract class AbstractIntegrationTest { diff --git a/examples/spring-boot/src/test/java/com/example/DemoControllerTest.java b/examples/spring-boot/src/test/java/com/example/DemoControllerTest.java index c305ae67811..4282920b37c 100644 --- a/examples/spring-boot/src/test/java/com/example/DemoControllerTest.java +++ b/examples/spring-boot/src/test/java/com/example/DemoControllerTest.java @@ -33,5 +33,4 @@ public void simpleJPATest() { assertThat(result.getValue()).as("value is set").isEqualTo("Some value"); } - } diff --git a/gradle/spotless.gradle b/gradle/spotless.gradle new file mode 100644 index 00000000000..ce71829331e --- /dev/null +++ b/gradle/spotless.gradle @@ -0,0 +1,23 @@ +apply plugin: 'com.diffplug.spotless' + +spotless { + java { + toggleOffOn() + removeUnusedImports() + trimTrailingWhitespace() + endWithNewline() + + prettier(['prettier': '2.5.1', 'prettier-plugin-java': '1.6.1']) + .config([ + 'parser' : 'java', + 'tabWidth' : 4, + 'printWidth': 120 + ]) + + importOrder('', 'java', 'javax', '\\#') + } + groovyGradle { + target '**/*.groovy' + greclipse('4.19.0') + } +} From b9f48abdee62fea8e94ca39ff5f18016646be9b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez=20Gonzales?= Date: Tue, 25 Oct 2022 10:23:05 -0500 Subject: [PATCH 0590/3231] Add zookeeper example (#6059) Demonstrate how to use Apache Zookeeper along with `GenericContainer`. Fixes #1514 --- docs/examples.md | 1 + examples/settings.gradle | 1 + examples/zookeeper/build.gradle | 14 +++++++ .../com/example/ZookeeperContainerTest.java | 41 +++++++++++++++++++ .../src/test/resources/logback-test.xml | 16 ++++++++ 5 files changed, 73 insertions(+) create mode 100644 examples/zookeeper/build.gradle create mode 100644 examples/zookeeper/src/test/java/com/example/ZookeeperContainerTest.java create mode 100644 examples/zookeeper/src/test/resources/logback-test.xml diff --git a/docs/examples.md b/docs/examples.md index e7578c8b375..b7b96ee52bb 100644 --- a/docs/examples.md +++ b/docs/examples.md @@ -14,3 +14,4 @@ Examples of different use cases provided by Testcontainers can be found below: - [Spring Boot with Kotlin](https://github.com/testcontainers/testcontainers-java/tree/main/examples/spring-boot-kotlin-redis) - [TestNG](https://github.com/testcontainers/testcontainers-java/tree/main/examples/redis-backed-cache-testng) - [ImmuDb](https://github.com/testcontainers/testcontainers-java/tree/main/examples/immudb) +- [Zookeeper](https://github.com/testcontainers/testcontainers-java/tree/main/examples/zookeper) diff --git a/examples/settings.gradle b/examples/settings.gradle index d26ecef5a98..35b79454204 100644 --- a/examples/settings.gradle +++ b/examples/settings.gradle @@ -31,6 +31,7 @@ include 'spring-boot' include 'cucumber' include 'spring-boot-kotlin-redis' include 'immudb' +include 'zookeeper' ext.isCI = System.getenv("CI") != null diff --git a/examples/zookeeper/build.gradle b/examples/zookeeper/build.gradle new file mode 100644 index 00000000000..f2c92eb3b1d --- /dev/null +++ b/examples/zookeeper/build.gradle @@ -0,0 +1,14 @@ +plugins { + id 'java' +} + +repositories { + mavenCentral() +} + +dependencies { + testImplementation 'org.apache.curator:curator-framework:5.3.0' + testImplementation 'org.testcontainers:testcontainers' + testImplementation 'org.assertj:assertj-core:3.23.1' + testImplementation 'ch.qos.logback:logback-classic:1.2.11' +} diff --git a/examples/zookeeper/src/test/java/com/example/ZookeeperContainerTest.java b/examples/zookeeper/src/test/java/com/example/ZookeeperContainerTest.java new file mode 100644 index 00000000000..a8aad36d201 --- /dev/null +++ b/examples/zookeeper/src/test/java/com/example/ZookeeperContainerTest.java @@ -0,0 +1,41 @@ +package com.example; + +import org.apache.curator.framework.CuratorFramework; +import org.apache.curator.framework.CuratorFrameworkFactory; +import org.apache.curator.retry.RetryOneTime; +import org.junit.Test; +import org.testcontainers.containers.GenericContainer; + +import java.nio.charset.StandardCharsets; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ZookeeperContainerTest { + + private static final int ZOOKEEPER_PORT = 2181; + + @Test + public void test() throws Exception { + String path = "/messages/zk-tc"; + String content = "Running Zookeeper with Testcontainers"; + try ( + GenericContainer zookeeper = new GenericContainer<>("zookeeper:3.8.0").withExposedPorts(ZOOKEEPER_PORT) + ) { + zookeeper.start(); + + String connectionString = zookeeper.getHost() + ":" + zookeeper.getMappedPort(ZOOKEEPER_PORT); + CuratorFramework curatorFramework = CuratorFrameworkFactory + .builder() + .connectString(connectionString) + .retryPolicy(new RetryOneTime(100)) + .build(); + curatorFramework.start(); + curatorFramework.create().creatingParentsIfNeeded().forPath(path, content.getBytes()); + + byte[] bytes = curatorFramework.getData().forPath(path); + curatorFramework.close(); + + assertThat(new String(bytes, StandardCharsets.UTF_8)).isEqualTo(content); + } + } +} diff --git a/examples/zookeeper/src/test/resources/logback-test.xml b/examples/zookeeper/src/test/resources/logback-test.xml new file mode 100644 index 00000000000..83ef7a1a3ef --- /dev/null +++ b/examples/zookeeper/src/test/resources/logback-test.xml @@ -0,0 +1,16 @@ + + + + + + %d{HH:mm:ss.SSS} %-5level %logger - %msg%n + + + + + + + + + From 0b617b4247f098ad538b58c3f012026c1558d59b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 25 Oct 2022 15:24:44 +0000 Subject: [PATCH 0591/3231] Bump logback-classic from 1.4.3 to 1.4.4 in /modules/hivemq Bumps [logback-classic](https://github.com/qos-ch/logback) from 1.4.3 to 1.4.4. - [Release notes](https://github.com/qos-ch/logback/releases) - [Commits](https://github.com/qos-ch/logback/compare/v_1.4.3...v_1.4.4) --- updated-dependencies: - dependency-name: ch.qos.logback:logback-classic dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- modules/hivemq/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/hivemq/build.gradle b/modules/hivemq/build.gradle index 3d6b653f6d1..796099b3a62 100644 --- a/modules/hivemq/build.gradle +++ b/modules/hivemq/build.gradle @@ -16,7 +16,7 @@ dependencies { testImplementation("com.hivemq:hivemq-extension-sdk:4.9.0") testImplementation("com.hivemq:hivemq-mqtt-client:1.3.0") testImplementation("org.apache.httpcomponents:httpclient:4.5.13") - testImplementation("ch.qos.logback:logback-classic:1.4.3") + testImplementation("ch.qos.logback:logback-classic:1.4.4") testImplementation 'org.assertj:assertj-core:3.23.1' testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.9.1") } From 50ec3f9b1b2111388e9879bb973761f6a2a02330 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 25 Oct 2022 15:25:10 +0000 Subject: [PATCH 0592/3231] Bump logback-classic from 1.3.3 to 1.3.4 in /examples Bumps [logback-classic](https://github.com/qos-ch/logback) from 1.3.3 to 1.3.4. - [Release notes](https://github.com/qos-ch/logback/releases) - [Commits](https://github.com/qos-ch/logback/compare/v_1.3.3...v_1.3.4) --- updated-dependencies: - dependency-name: ch.qos.logback:logback-classic dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- build.gradle | 2 +- docs/examples/spock/redis/build.gradle | 2 +- examples/kafka-cluster/build.gradle | 2 +- examples/linked-container/build.gradle | 2 +- examples/redis-backed-cache-testng/build.gradle | 2 +- examples/redis-backed-cache/build.gradle | 2 +- examples/singleton-container/build.gradle | 2 +- examples/zookeeper/build.gradle | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/build.gradle b/build.gradle index 7405154491b..6c2b6a81c7b 100644 --- a/build.gradle +++ b/build.gradle @@ -125,7 +125,7 @@ subprojects { } dependencies { - testImplementation 'ch.qos.logback:logback-classic:1.3.3' + testImplementation 'ch.qos.logback:logback-classic:1.3.4' } checkstyle { diff --git a/docs/examples/spock/redis/build.gradle b/docs/examples/spock/redis/build.gradle index 6699788d026..390bc06be79 100644 --- a/docs/examples/spock/redis/build.gradle +++ b/docs/examples/spock/redis/build.gradle @@ -7,5 +7,5 @@ dependencies { api "io.lettuce:lettuce-core:5.2.0.RELEASE" testImplementation 'org.spockframework:spock-core:1.2-groovy-2.5' testImplementation project(":spock") - testImplementation 'ch.qos.logback:logback-classic:1.3.3' + testImplementation 'ch.qos.logback:logback-classic:1.3.4' } diff --git a/examples/kafka-cluster/build.gradle b/examples/kafka-cluster/build.gradle index 5212bc33fa9..46c562bac84 100644 --- a/examples/kafka-cluster/build.gradle +++ b/examples/kafka-cluster/build.gradle @@ -13,5 +13,5 @@ dependencies { testImplementation 'org.apache.kafka:kafka-clients:3.3.0' testImplementation 'org.assertj:assertj-core:3.23.1' testImplementation 'com.google.guava:guava:23.0' - testImplementation 'ch.qos.logback:logback-classic:1.3.3' + testImplementation 'ch.qos.logback:logback-classic:1.3.4' } diff --git a/examples/linked-container/build.gradle b/examples/linked-container/build.gradle index cf3a3331cc3..08376fd9d3f 100644 --- a/examples/linked-container/build.gradle +++ b/examples/linked-container/build.gradle @@ -10,7 +10,7 @@ dependencies { implementation 'com.squareup.okhttp3:okhttp:4.10.0' implementation 'org.json:json:20220924' testImplementation 'org.postgresql:postgresql:42.5.0' - testImplementation 'ch.qos.logback:logback-classic:1.3.3' + testImplementation 'ch.qos.logback:logback-classic:1.3.4' testImplementation 'org.testcontainers:postgresql' testImplementation 'org.assertj:assertj-core:3.23.1' } diff --git a/examples/redis-backed-cache-testng/build.gradle b/examples/redis-backed-cache-testng/build.gradle index 2e4bda4fa74..5dba21e1c26 100644 --- a/examples/redis-backed-cache-testng/build.gradle +++ b/examples/redis-backed-cache-testng/build.gradle @@ -12,7 +12,7 @@ dependencies { implementation 'com.google.code.gson:gson:2.9.1' implementation 'com.google.guava:guava:23.0' testImplementation 'org.testcontainers:testcontainers' - testImplementation 'ch.qos.logback:logback-classic:1.3.3' + testImplementation 'ch.qos.logback:logback-classic:1.3.4' testImplementation 'org.testng:testng:7.5' testImplementation 'org.assertj:assertj-core:3.23.1' } diff --git a/examples/redis-backed-cache/build.gradle b/examples/redis-backed-cache/build.gradle index eb684158731..0d19207f4ba 100644 --- a/examples/redis-backed-cache/build.gradle +++ b/examples/redis-backed-cache/build.gradle @@ -13,6 +13,6 @@ dependencies { implementation 'com.google.guava:guava:23.0' testImplementation 'org.testcontainers:testcontainers' testImplementation 'junit:junit:4.13.2' - testImplementation 'ch.qos.logback:logback-classic:1.3.3' + testImplementation 'ch.qos.logback:logback-classic:1.3.4' testImplementation 'org.assertj:assertj-core:3.23.1' } diff --git a/examples/singleton-container/build.gradle b/examples/singleton-container/build.gradle index 449fd9ae8e6..8ebce1bfb77 100644 --- a/examples/singleton-container/build.gradle +++ b/examples/singleton-container/build.gradle @@ -13,7 +13,7 @@ dependencies { implementation 'com.google.guava:guava:23.0' compileOnly 'org.slf4j:slf4j-api:1.7.36' - testImplementation 'ch.qos.logback:logback-classic:1.3.3' + testImplementation 'ch.qos.logback:logback-classic:1.3.4' testImplementation 'org.testcontainers:testcontainers' testImplementation 'org.assertj:assertj-core:3.23.1' } diff --git a/examples/zookeeper/build.gradle b/examples/zookeeper/build.gradle index f2c92eb3b1d..d18be942ff0 100644 --- a/examples/zookeeper/build.gradle +++ b/examples/zookeeper/build.gradle @@ -10,5 +10,5 @@ dependencies { testImplementation 'org.apache.curator:curator-framework:5.3.0' testImplementation 'org.testcontainers:testcontainers' testImplementation 'org.assertj:assertj-core:3.23.1' - testImplementation 'ch.qos.logback:logback-classic:1.2.11' + testImplementation 'ch.qos.logback:logback-classic:1.3.4' } From 46b6cd9bf357a96e01d8e48cf241250c816bb1f3 Mon Sep 17 00:00:00 2001 From: Benjamin Manifold Date: Wed, 26 Oct 2022 06:50:12 -0400 Subject: [PATCH 0593/3231] Update gitlab_ci.md (#6063) Clarity around the port number for unencrypted docker host connections, as most gitlab DinD examples recommend 2736 without explaining why. --- .../continuous_integration/gitlab_ci.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/supported_docker_environment/continuous_integration/gitlab_ci.md b/docs/supported_docker_environment/continuous_integration/gitlab_ci.md index b4571403654..f2226f2d34f 100644 --- a/docs/supported_docker_environment/continuous_integration/gitlab_ci.md +++ b/docs/supported_docker_environment/continuous_integration/gitlab_ci.md @@ -46,7 +46,7 @@ services: command: ["--tls=false"] variables: - # Instruct Testcontainers to use the daemon of DinD. + # Instruct Testcontainers to use the daemon of DinD, use port 2735 for non-tls connections. DOCKER_HOST: "tcp://docker:2375" # Instruct Docker not to start over TLS. DOCKER_TLS_CERTDIR: "" From b0af2f41f98a65f06cb0245a1d7d431db1b7a22b Mon Sep 17 00:00:00 2001 From: Leo Date: Thu, 27 Oct 2022 12:22:53 +0100 Subject: [PATCH 0594/3231] Add language links to docs index (#6067) --- docs/css/extra.css | 39 ++++++++++++++++++++ docs/index.md | 16 +++++++- docs/language-logos/dotnet.svg | 7 ++++ docs/language-logos/go.svg | 10 +++++ docs/language-logos/java.svg | 17 +++++++++ docs/language-logos/javascript.svg | 4 ++ docs/language-logos/python.svg | 8 ++++ docs/language-logos/rust.svg | 57 +++++++++++++++++++++++++++++ docs/logo.png | Bin 84934 -> 67366 bytes 9 files changed, 157 insertions(+), 1 deletion(-) create mode 100644 docs/language-logos/dotnet.svg create mode 100644 docs/language-logos/go.svg create mode 100644 docs/language-logos/java.svg create mode 100644 docs/language-logos/javascript.svg create mode 100644 docs/language-logos/python.svg create mode 100644 docs/language-logos/rust.svg diff --git a/docs/css/extra.css b/docs/css/extra.css index e97480c5341..a89eebc9b24 100644 --- a/docs/css/extra.css +++ b/docs/css/extra.css @@ -9,3 +9,42 @@ h1, h2, h3, h4, h5, h6 { --md-accent-fg-color: #291A3F; --md-typeset-a-color: #0C94AA; } + +.card-grid { + display: grid; + gap: 10px; +} + +@media (min-width: 680px) { + .card-grid { + grid-template-columns: repeat(3, 1fr); + } +} + +body .card-grid-item { + display: flex; + align-items: center; + gap: 20px; + border: 1px solid #C3BEDE; + border-radius: 6px; + padding: 16px; + font-weight: 600; + color: #9991B5; + background: #F2F4FE; +} + +body .card-grid-item:hover, +body .card-grid-item:focus { + color: #9991B5; +} + +.card-grid-item[href] { + color: var(--md-primary-fg-color); + background: transparent; +} + +.card-grid-item[href]:hover, +.card-grid-item[href]:focus { + background: #F2F4FE; + color: var(--md-primary-fg-color); +} \ No newline at end of file diff --git a/docs/index.md b/docs/index.md index 383f42c0245..ed365b2acf3 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,7 +1,21 @@ # Testcontainers -Testcontainers logo +Testcontainers logo + +## Test dependencies as code for your entire stack +Get lightweight and throwaway containers during your tests that let you test against any container image (database, broker, browser, etc..) using one of the several supported languages. + + +

Select your language

+ ## About diff --git a/docs/language-logos/dotnet.svg b/docs/language-logos/dotnet.svg new file mode 100644 index 00000000000..2fb163d4d40 --- /dev/null +++ b/docs/language-logos/dotnet.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/docs/language-logos/go.svg b/docs/language-logos/go.svg new file mode 100644 index 00000000000..bfcca48ceda --- /dev/null +++ b/docs/language-logos/go.svg @@ -0,0 +1,10 @@ + + + + + + + \ No newline at end of file diff --git a/docs/language-logos/java.svg b/docs/language-logos/java.svg new file mode 100644 index 00000000000..986704918f5 --- /dev/null +++ b/docs/language-logos/java.svg @@ -0,0 +1,17 @@ + + + + + + + \ No newline at end of file diff --git a/docs/language-logos/javascript.svg b/docs/language-logos/javascript.svg new file mode 100644 index 00000000000..4c808b1bf68 --- /dev/null +++ b/docs/language-logos/javascript.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/docs/language-logos/python.svg b/docs/language-logos/python.svg new file mode 100644 index 00000000000..d06a313c96b --- /dev/null +++ b/docs/language-logos/python.svg @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/docs/language-logos/rust.svg b/docs/language-logos/rust.svg new file mode 100644 index 00000000000..89039333533 --- /dev/null +++ b/docs/language-logos/rust.svg @@ -0,0 +1,57 @@ + + + \ No newline at end of file diff --git a/docs/logo.png b/docs/logo.png index 3d8f03fa0cf0fb98578e83d9b2ff71de3ead11c0..88961b3e3f0c0520a48e91b06e353a73fce35594 100644 GIT binary patch literal 67366 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