diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/cassandra/CassandraDataAutoConfigurationIntegrationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/cassandra/CassandraDataAutoConfigurationIntegrationTests.java index afc1c3fc59..59c8009121 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/cassandra/CassandraDataAutoConfigurationIntegrationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/cassandra/CassandraDataAutoConfigurationIntegrationTests.java @@ -31,10 +31,10 @@ import org.testcontainers.containers.FixedHostPortGenericContainer; import org.testcontainers.containers.wait.HostPortWaitStrategy; import org.springframework.boot.autoconfigure.AutoConfigurationPackages; -import org.springframework.boot.autoconfigure.DockerTestContainer; import org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration; import org.springframework.boot.autoconfigure.data.cassandra.city.City; import org.springframework.boot.test.util.TestPropertyValues; +import org.springframework.boot.testsupport.testcontainers.DockerTestContainer; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.data.cassandra.config.CassandraSessionFactoryBean; import org.springframework.data.cassandra.config.SchemaAction; diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/redis/RedisRepositoriesAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/redis/RedisRepositoriesAutoConfigurationTests.java index dc02abbbf4..12af831225 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/redis/RedisRepositoriesAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/redis/RedisRepositoriesAutoConfigurationTests.java @@ -19,15 +19,16 @@ package org.springframework.boot.autoconfigure.data.redis; import org.junit.After; import org.junit.ClassRule; import org.junit.Test; -import org.testcontainers.containers.FixedHostPortGenericContainer; +import org.testcontainers.containers.GenericContainer; -import org.springframework.boot.autoconfigure.DockerTestContainer; import org.springframework.boot.autoconfigure.TestAutoConfigurationPackage; import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration; import org.springframework.boot.autoconfigure.data.alt.redis.CityRedisRepository; import org.springframework.boot.autoconfigure.data.empty.EmptyDataPackage; import org.springframework.boot.autoconfigure.data.redis.city.City; import org.springframework.boot.autoconfigure.data.redis.city.CityRepository; +import org.springframework.boot.testsupport.testcontainers.DockerTestContainer; +import org.springframework.boot.testsupport.testcontainers.TestContainers; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.repository.configuration.EnableRedisRepositories; @@ -42,9 +43,8 @@ import static org.assertj.core.api.Assertions.assertThat; public class RedisRepositoriesAutoConfigurationTests { @ClassRule - public static DockerTestContainer> redis = new DockerTestContainer<>( - () -> new FixedHostPortGenericContainer<>("redis:latest") - .withFixedExposedPort(6379, 6379)); + public static DockerTestContainer> redis = new DockerTestContainer<>( + () -> TestContainers.redis()); private AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/session/ReactiveSessionAutoConfigurationRedisTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/session/ReactiveSessionAutoConfigurationRedisTests.java index c850b5d764..1c4b39d009 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/session/ReactiveSessionAutoConfigurationRedisTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/session/ReactiveSessionAutoConfigurationRedisTests.java @@ -22,13 +22,13 @@ import org.testcontainers.containers.FixedHostPortGenericContainer; import org.springframework.beans.DirectFieldAccessor; import org.springframework.boot.autoconfigure.AutoConfigurations; -import org.springframework.boot.autoconfigure.DockerTestContainer; import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; import org.springframework.boot.autoconfigure.data.redis.RedisReactiveAutoConfiguration; import org.springframework.boot.test.context.FilteredClassLoader; import org.springframework.boot.test.context.assertj.AssertableReactiveWebApplicationContext; import org.springframework.boot.test.context.runner.ContextConsumer; import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner; +import org.springframework.boot.testsupport.testcontainers.DockerTestContainer; import org.springframework.session.data.mongo.ReactiveMongoOperationsSessionRepository; import org.springframework.session.data.redis.ReactiveRedisOperationsSessionRepository; import org.springframework.session.data.redis.RedisFlushMode; diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/session/SessionAutoConfigurationRedisTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/session/SessionAutoConfigurationRedisTests.java index 98649edee6..5ef05c6aea 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/session/SessionAutoConfigurationRedisTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/session/SessionAutoConfigurationRedisTests.java @@ -18,17 +18,18 @@ package org.springframework.boot.autoconfigure.session; import org.junit.ClassRule; import org.junit.Test; -import org.testcontainers.containers.FixedHostPortGenericContainer; +import org.testcontainers.containers.GenericContainer; import org.springframework.beans.DirectFieldAccessor; import org.springframework.boot.autoconfigure.AutoConfigurations; -import org.springframework.boot.autoconfigure.DockerTestContainer; import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; import org.springframework.boot.autoconfigure.session.RedisSessionConfiguration.SpringBootRedisHttpSessionConfiguration; import org.springframework.boot.test.context.FilteredClassLoader; import org.springframework.boot.test.context.assertj.AssertableWebApplicationContext; import org.springframework.boot.test.context.runner.ContextConsumer; import org.springframework.boot.test.context.runner.WebApplicationContextRunner; +import org.springframework.boot.testsupport.testcontainers.DockerTestContainer; +import org.springframework.boot.testsupport.testcontainers.TestContainers; import org.springframework.session.data.mongo.MongoOperationsSessionRepository; import org.springframework.session.data.redis.RedisFlushMode; import org.springframework.session.data.redis.RedisOperationsSessionRepository; @@ -47,16 +48,17 @@ public class SessionAutoConfigurationRedisTests extends AbstractSessionAutoConfigurationTests { @ClassRule - public static DockerTestContainer> redis = new DockerTestContainer<>( - () -> new FixedHostPortGenericContainer<>("redis:latest") - .withFixedExposedPort(6379, 6379)); + public static DockerTestContainer> redis = new DockerTestContainer<>( + () -> TestContainers.redis()); protected final WebApplicationContextRunner contextRunner = new WebApplicationContextRunner() .withConfiguration(AutoConfigurations.of(SessionAutoConfiguration.class)); @Test public void defaultConfig() { - this.contextRunner.withPropertyValues("spring.session.store-type=redis") + this.contextRunner + .withPropertyValues("spring.session.store-type=redis", + "spring.redis.port=" + redis.getMappedPort(6379)) .withConfiguration(AutoConfigurations.of(RedisAutoConfiguration.class)) .run(validateSpringSessionUsesRedis("spring:session:event:created:", RedisFlushMode.ON_SAVE, "0 * * * * *")); @@ -69,6 +71,7 @@ public class SessionAutoConfigurationRedisTests JdbcOperationsSessionRepository.class, MongoOperationsSessionRepository.class)) .withConfiguration(AutoConfigurations.of(RedisAutoConfiguration.class)) + .withPropertyValues("spring.redis.port=" + redis.getMappedPort(6379)) .run(validateSpringSessionUsesRedis("spring:session:event:created:", RedisFlushMode.ON_SAVE, "0 * * * * *")); } @@ -80,7 +83,8 @@ public class SessionAutoConfigurationRedisTests .withPropertyValues("spring.session.store-type=redis", "spring.session.redis.namespace=foo", "spring.session.redis.flush-mode=immediate", - "spring.session.redis.cleanup-cron=0 0 12 * * *") + "spring.session.redis.cleanup-cron=0 0 12 * * *", + "spring.redis.port=" + redis.getMappedPort(6379)) .run(validateSpringSessionUsesRedis("foo:event:created:", RedisFlushMode.IMMEDIATE, "0 0 12 * * *")); } diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/DockerTestContainer.java b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/DockerTestContainer.java deleted file mode 100644 index da5b096a52..0000000000 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/DockerTestContainer.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2012-2018 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.test.autoconfigure; - -import java.util.function.Supplier; - -import org.junit.AssumptionViolatedException; -import org.junit.rules.TestRule; -import org.junit.runner.Description; -import org.junit.runners.model.Statement; -import org.testcontainers.DockerClientFactory; -import org.testcontainers.containers.GenericContainer; - -/** - * {@link TestRule} for working with an optional Docker environment. Spins up a - * {@link GenericContainer} if a valid docker environment is found. - * - * @param the type of the container - * @author Madhura Bhave - */ -public class DockerTestContainer> implements TestRule { - - private Supplier containerSupplier; - - public DockerTestContainer(Supplier containerSupplier) { - this.containerSupplier = containerSupplier; - } - - @Override - public Statement apply(Statement base, Description description) { - try { - DockerClientFactory.instance().client(); - return this.containerSupplier.get().apply(base, description); - } - catch (Throwable thrown) { - return new SkipStatement(); - } - } - - private static class SkipStatement extends Statement { - - @Override - public void evaluate() { - throw new AssumptionViolatedException( - "Could not find a valid Docker environment."); - } - - } -} diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/neo4j/DataNeo4jTestIntegrationTests.java b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/neo4j/DataNeo4jTestIntegrationTests.java index 79c17aeaba..5777e27249 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/neo4j/DataNeo4jTestIntegrationTests.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/neo4j/DataNeo4jTestIntegrationTests.java @@ -34,7 +34,7 @@ import org.testcontainers.containers.wait.HostPortWaitStrategy; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.DockerTestContainer; +import org.springframework.boot.testsupport.testcontainers.DockerTestContainer; import org.springframework.context.ApplicationContext; import org.springframework.test.context.junit4.SpringRunner; diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/neo4j/DataNeo4jTestWithIncludeFilterIntegrationTests.java b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/neo4j/DataNeo4jTestWithIncludeFilterIntegrationTests.java index 30a4e82cea..8764c8fe7a 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/neo4j/DataNeo4jTestWithIncludeFilterIntegrationTests.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/neo4j/DataNeo4jTestWithIncludeFilterIntegrationTests.java @@ -22,7 +22,7 @@ import org.junit.runner.RunWith; import org.testcontainers.containers.FixedHostPortGenericContainer; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.DockerTestContainer; +import org.springframework.boot.testsupport.testcontainers.DockerTestContainer; import org.springframework.context.annotation.ComponentScan.Filter; import org.springframework.stereotype.Service; import org.springframework.test.context.junit4.SpringRunner; diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/redis/DataRedisTestIntegrationTests.java b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/redis/DataRedisTestIntegrationTests.java index 0b0bbef90d..548e8093ee 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/redis/DataRedisTestIntegrationTests.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/redis/DataRedisTestIntegrationTests.java @@ -28,7 +28,7 @@ import org.testcontainers.containers.FixedHostPortGenericContainer; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.DockerTestContainer; +import org.springframework.boot.testsupport.testcontainers.DockerTestContainer; import org.springframework.context.ApplicationContext; import org.springframework.data.redis.connection.RedisConnection; import org.springframework.data.redis.core.RedisOperations; diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/redis/DataRedisTestWithIncludeFilterIntegrationTests.java b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/redis/DataRedisTestWithIncludeFilterIntegrationTests.java index 98ea007989..e3e1cc8245 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/redis/DataRedisTestWithIncludeFilterIntegrationTests.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/redis/DataRedisTestWithIncludeFilterIntegrationTests.java @@ -22,7 +22,7 @@ import org.junit.runner.RunWith; import org.testcontainers.containers.FixedHostPortGenericContainer; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.DockerTestContainer; +import org.springframework.boot.testsupport.testcontainers.DockerTestContainer; import org.springframework.context.annotation.ComponentScan.Filter; import org.springframework.stereotype.Service; import org.springframework.test.context.junit4.SpringRunner; diff --git a/spring-boot-project/spring-boot-tools/spring-boot-test-support/pom.xml b/spring-boot-project/spring-boot-tools/spring-boot-test-support/pom.xml index 24bb58dc7a..d8140318c1 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-test-support/pom.xml +++ b/spring-boot-project/spring-boot-tools/spring-boot-test-support/pom.xml @@ -79,6 +79,14 @@ assertj-core + + com.datastax.cassandra + cassandra-driver-core + + + org.neo4j + neo4j-ogm-core + javax.servlet javax.servlet-api @@ -99,6 +107,10 @@ spring-context true + + org.testcontainers + testcontainers + junit diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/DockerTestContainer.java b/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/testcontainers/DockerTestContainer.java similarity index 83% rename from spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/DockerTestContainer.java rename to spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/testcontainers/DockerTestContainer.java index d51de5e61d..f9a2074dff 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/DockerTestContainer.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/testcontainers/DockerTestContainer.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.autoconfigure; +package org.springframework.boot.testsupport.testcontainers; import java.util.function.Supplier; @@ -34,7 +34,9 @@ import org.testcontainers.containers.GenericContainer; */ public class DockerTestContainer> implements TestRule { - private Supplier containerSupplier; + private final Supplier containerSupplier; + + private T container; public DockerTestContainer(Supplier containerSupplier) { this.containerSupplier = containerSupplier; @@ -44,11 +46,16 @@ public class DockerTestContainer> implements TestR public Statement apply(Statement base, Description description) { try { DockerClientFactory.instance().client(); - return this.containerSupplier.get().apply(base, description); } catch (Throwable t) { return new SkipStatement(); } + this.container = this.containerSupplier.get(); + return this.container.apply(base, description); + } + + public int getMappedPort(int originalPort) { + return this.container.getMappedPort(originalPort); } private static class SkipStatement extends Statement { @@ -60,4 +67,5 @@ public class DockerTestContainer> implements TestR } } + } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/testcontainers/TestContainers.java b/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/testcontainers/TestContainers.java new file mode 100644 index 0000000000..efbad3d019 --- /dev/null +++ b/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/testcontainers/TestContainers.java @@ -0,0 +1,119 @@ +/* + * Copyright 2012-2018 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.testsupport.testcontainers; + +import java.util.concurrent.Callable; +import java.util.concurrent.TimeUnit; + +import com.datastax.driver.core.Cluster; +import com.datastax.driver.core.exceptions.NoHostAvailableException; +import org.neo4j.ogm.config.Configuration; +import org.neo4j.ogm.session.SessionFactory; +import org.rnorth.ducttape.TimeoutException; +import org.rnorth.ducttape.unreliables.Unreliables; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.wait.HostPortWaitStrategy; + +/** + * Provides utility methods that allow creation of docker containers for + * tests. + * + * @author Andy Wilkinson + * @author Madhura Bhave + */ +public abstract class TestContainers { + + @SuppressWarnings("resource") + public static GenericContainer redis() { + return new GenericContainer<>("redis:4.0.6").withExposedPorts(6379); + } + + @SuppressWarnings("resource") + public static GenericContainer cassandra() { + return new GenericContainer<>("cassandra:3.11.1").withExposedPorts(9042) + .waitingFor(new CassandraConnectionVerifyingWaitStrategy()); + } + + public static GenericContainer neo4j() { + return new GenericContainer<>("neo4j:3.3.1").withExposedPorts(7687) + .waitingFor(new Neo4jConnectionVerifyingWaitStrategy()) + .withEnv("NEO4J_AUTH", "none"); + } + + private static class CassandraConnectionVerifyingWaitStrategy extends HostPortWaitStrategy { + + @Override + protected void waitUntilReady() { + super.waitUntilReady(); + + try { + Unreliables.retryUntilTrue((int) this.startupTimeout.getSeconds(), + TimeUnit.SECONDS, checkConnection()); + } + catch (TimeoutException ex) { + throw new IllegalStateException(ex); + } + } + + private Callable checkConnection() { + return () -> { + try (Cluster cluster = Cluster.builder().withPort(this.container.getMappedPort(9042)) + .addContactPoint("localhost") + .build()) { + cluster.connect(); + return true; + } + catch (IllegalArgumentException | NoHostAvailableException ex) { + return false; + } + }; + } + + } + + private static class Neo4jConnectionVerifyingWaitStrategy extends HostPortWaitStrategy { + + @Override + protected void waitUntilReady() { + super.waitUntilReady(); + Configuration configuration = new Configuration.Builder() + .uri("bolt://localhost:" + this.container.getMappedPort(7687)).build(); + SessionFactory sessionFactory = new SessionFactory(configuration, + "org.springframework.boot.test.autoconfigure.data.neo4j"); + try { + Unreliables.retryUntilTrue((int) this.startupTimeout.getSeconds(), + TimeUnit.SECONDS, checkConnection(sessionFactory)); + } + catch (TimeoutException e) { + throw new IllegalStateException(); + } + } + + private Callable checkConnection(SessionFactory sessionFactory) { + return () -> { + try { + sessionFactory.openSession().beginTransaction().close(); + return true; + } + catch (Exception ex) { + return false; + } + }; + } + } + +}