diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/howto/testing.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/howto/testing.adoc index 882d9c14bc..c9aed299bf 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/howto/testing.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/howto/testing.adoc @@ -29,11 +29,46 @@ Testcontainers can be used in a Spring Boot test as follows: include::code:vanilla/MyIntegrationTests[] This will start up a docker container running Neo4j (if Docker is running locally) before any of the tests are run. -In most cases, you will need to configure the application using details from the running container, such as container IP or port. +In most cases, you will need to configure the application to connect to the service running in the container. -This can be done with a static `@DynamicPropertySource` method that allows adding dynamic property values to the Spring Environment. -include::code:dynamicproperties/MyIntegrationTests[] +[[howto.testing.testcontainers.service-connections]] +==== Service Connections +A service connection is a connection to any remote service. +Spring Boot's auto-configuration can consume the details of a service connection and use them to establish a connection to a remote service. +When doing so, the connection details take precedence over any connection-related configuration properties. + +When using Testcontainers, connection details can be automatically created for a service running in a container by annotating the container field in the test class. + +include::code:MyIntegrationTests[] + +Thanks to `@Neo4jServiceConnection`, the above configuration allows Neo4j-related beans in the application to communicate with Neo4j running inside the Testcontainers-managed Docker container. +This is done by automatically defining a `Neo4jConnectionDetails` bean which is then used by the Neo4j auto-configuration, overriding any connection-related configuration properties. + +The following service connection annotations are provided by `spring-boot-test-autoconfigure`: + +- `@CassandraServiceConnection` +- `@CouchbaseServiceConnection` +- `@ElasticsearchServiceConnection` +- `@InfluxDbServiceConnection` +- `@JdbcServiceConnection` +- `@KafkaServiceConnection` +- `@MongoServiceConnection` +- `@Neo4jServiceConnection` +- `@R2dbcServiceConnection` +- `@RabbitServiceConnection` +- `@RedisServiceConnection` + +As with the earlier `@Neo4jConnectionDetails` example, each can be used on a container field. Doing so will automatically configure the application to connect to the service running in the container. + + + +[[howto.testing.testcontainers.dynamic-properties]] +==== Dynamic Properties +A slightly more verbose but also more flexible alternative to service connections is `@DynamicPropertySource`. +A static `@DynamicPropertySource` method allows adding dynamic property values to the Spring Environment. + +include::code:/MyIntegrationTests[] The above configuration allows Neo4j-related beans in the application to communicate with Neo4j running inside the Testcontainers-managed Docker container. diff --git a/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/howto/testing/testcontainers/serviceconnections/MyIntegrationTests.java b/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/howto/testing/testcontainers/serviceconnections/MyIntegrationTests.java new file mode 100644 index 0000000000..81cb2a6977 --- /dev/null +++ b/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/howto/testing/testcontainers/serviceconnections/MyIntegrationTests.java @@ -0,0 +1,40 @@ +/* + * Copyright 2012-2023 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 + * + * https://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.docs.howto.testing.testcontainers.serviceconnections; + +import org.junit.jupiter.api.Test; +import org.testcontainers.containers.Neo4jContainer; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +import org.springframework.boot.test.autoconfigure.neo4j.Neo4jServiceConnection; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +@Testcontainers +class MyIntegrationTests { + + @Container + @Neo4jServiceConnection + static Neo4jContainer neo4j = new Neo4jContainer<>("neo4j:4.2"); + + @Test + void myTest() { + // ... + } + +} diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/howto/testing/testcontainers/serviceconnections/MyIntegrationTests.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/howto/testing/testcontainers/serviceconnections/MyIntegrationTests.kt new file mode 100644 index 0000000000..779008a451 --- /dev/null +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/howto/testing/testcontainers/serviceconnections/MyIntegrationTests.kt @@ -0,0 +1,45 @@ +/* + * Copyright 2012-2023 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 + * + * https://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.docs.howto.testing.testcontainers.serviceconnection + +import org.junit.jupiter.api.Test +import org.springframework.boot.test.autoconfigure.neo4j.Neo4jServiceConnection; +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.test.context.DynamicPropertyRegistry +import org.springframework.test.context.DynamicPropertySource +import org.testcontainers.containers.Neo4jContainer +import org.testcontainers.junit.jupiter.Container +import org.testcontainers.junit.jupiter.Testcontainers + +@SpringBootTest +@Testcontainers +internal class MyIntegrationTests { + + @Test + fun myTest() { + // ... + } + + companion object { + + @Container + @Neo4jServiceConnection + var neo4j: Neo4jContainer<*> = Neo4jContainer("neo4j:4.2") + + } + +} \ No newline at end of file diff --git a/spring-boot-project/spring-boot-test-autoconfigure/build.gradle b/spring-boot-project/spring-boot-test-autoconfigure/build.gradle index 6053e32431..046638cc2b 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/build.gradle +++ b/spring-boot-project/spring-boot-test-autoconfigure/build.gradle @@ -60,6 +60,21 @@ dependencies { optional("org.apache.tomcat.embed:tomcat-embed-core") optional("org.mongodb:mongodb-driver-reactivestreams") optional("org.mongodb:mongodb-driver-sync") + optional("org.testcontainers:cassandra") + optional("org.testcontainers:couchbase") + optional("org.testcontainers:elasticsearch") + optional("org.testcontainers:influxdb") + optional("org.testcontainers:jdbc") + optional("org.testcontainers:kafka") + optional("org.testcontainers:mariadb") + optional("org.testcontainers:mongodb") + optional("org.testcontainers:mssqlserver") + optional("org.testcontainers:mysql") + optional("org.testcontainers:neo4j") + optional("org.testcontainers:postgresql") + optional("org.testcontainers:rabbitmq") + optional("org.testcontainers:r2dbc") + optional("org.testcontainers:testcontainers") optional("io.micrometer:micrometer-tracing") testImplementation(project(":spring-boot-project:spring-boot-actuator")) @@ -85,6 +100,7 @@ dependencies { testImplementation("org.eclipse:yasson") testImplementation("org.hibernate.validator:hibernate-validator") testImplementation("org.hsqldb:hsqldb") + testImplementation("org.influxdb:influxdb-java") testImplementation("org.jooq:jooq") { exclude group: "javax.xml.bind", module: "jaxb-api" } @@ -95,15 +111,11 @@ dependencies { testImplementation("org.mockito:mockito-junit-jupiter") testImplementation("org.skyscreamer:jsonassert") testImplementation("org.springframework:spring-core-test") + testImplementation("org.springframework.amqp:spring-rabbit") testImplementation("org.springframework.hateoas:spring-hateoas") + testImplementation("org.springframework.kafka:spring-kafka") testImplementation("org.springframework.plugin:spring-plugin-core") - testImplementation("org.testcontainers:cassandra") - testImplementation("org.testcontainers:couchbase") - testImplementation("org.testcontainers:elasticsearch") testImplementation("org.testcontainers:junit-jupiter") - testImplementation("org.testcontainers:mongodb") - testImplementation("org.testcontainers:neo4j") - testImplementation("org.testcontainers:testcontainers") testImplementation("org.thymeleaf:thymeleaf") } diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/amqp/RabbitContainerConnectionDetailsFactory.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/amqp/RabbitContainerConnectionDetailsFactory.java new file mode 100644 index 0000000000..24163378ba --- /dev/null +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/amqp/RabbitContainerConnectionDetailsFactory.java @@ -0,0 +1,83 @@ +/* + * Copyright 2012-2023 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 + * + * https://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.amqp; + +import java.net.URI; +import java.util.List; + +import org.testcontainers.containers.RabbitMQContainer; + +import org.springframework.boot.autoconfigure.amqp.RabbitConnectionDetails; +import org.springframework.boot.test.autoconfigure.service.connection.ContainerConnectionDetailsFactory; +import org.springframework.boot.test.autoconfigure.service.connection.ContainerConnectionSource; + +/** + * {@link ContainerConnectionDetailsFactory} for + * {@link RabbitServiceConnection @RabbitServiceConnection}-annotated + * {@link RabbitMQContainer} fields. + * + * @author Moritz Halbritter + * @author Andy Wilkinson + * @author Phillip Webb + */ +class RabbitContainerConnectionDetailsFactory + extends ContainerConnectionDetailsFactory { + + @Override + protected RabbitConnectionDetails getContainerConnectionDetails( + ContainerConnectionSource source) { + return new RabbitMqContainerConnectionDetails(source); + } + + /** + * {@link RabbitConnectionDetails} backed by a {@link ContainerConnectionSource}. + */ + private static final class RabbitMqContainerConnectionDetails extends ContainerConnectionDetails + implements RabbitConnectionDetails { + + private final RabbitMQContainer container; + + private RabbitMqContainerConnectionDetails( + ContainerConnectionSource source) { + super(source); + this.container = source.getContainer(); + } + + @Override + public String getUsername() { + return this.container.getAdminUsername(); + } + + @Override + public String getPassword() { + return this.container.getAdminPassword(); + } + + @Override + public String getVirtualHost() { + return null; + } + + @Override + public List
getAddresses() { + URI uri = URI.create(this.container.getAmqpUrl()); + return List.of(new Address(uri.getHost(), uri.getPort())); + } + + } + +} diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/amqp/RabbitServiceConnection.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/amqp/RabbitServiceConnection.java new file mode 100644 index 0000000000..5723d8d556 --- /dev/null +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/amqp/RabbitServiceConnection.java @@ -0,0 +1,44 @@ +/* + * Copyright 2012-2023 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 + * + * https://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.amqp; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.boot.autoconfigure.amqp.RabbitConnectionDetails; +import org.springframework.boot.test.autoconfigure.service.connection.ServiceConnection; + +/** + * Annotation that indicates that a field provides a RabbitMQ service connection. + * + * @author Moritz Halbritter + * @author Andy Wilkinson + * @author Phillip Webb + * @since 3.1.0 + * @see RabbitConnectionDetails + * @see ServiceConnection + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.FIELD, ElementType.TYPE }) +@ServiceConnection(RabbitConnectionDetails.class) +public @interface RabbitServiceConnection { + +} diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/amqp/package-info.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/amqp/package-info.java new file mode 100644 index 0000000000..4983b50fa2 --- /dev/null +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/amqp/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2012-2023 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 + * + * https://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. + */ + +/** + * Auto-configuration for using RabbitMQ in tests. + */ +package org.springframework.boot.test.autoconfigure.amqp; diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/cassandra/CassandraContainerConnectionDetailsFactory.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/cassandra/CassandraContainerConnectionDetailsFactory.java new file mode 100644 index 0000000000..28dd41595d --- /dev/null +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/cassandra/CassandraContainerConnectionDetailsFactory.java @@ -0,0 +1,82 @@ +/* + * Copyright 2012-2023 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 + * + * https://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.cassandra; + +import java.util.List; + +import org.testcontainers.containers.CassandraContainer; + +import org.springframework.boot.autoconfigure.cassandra.CassandraConnectionDetails; +import org.springframework.boot.test.autoconfigure.service.connection.ContainerConnectionDetailsFactory; +import org.springframework.boot.test.autoconfigure.service.connection.ContainerConnectionSource; + +/** + * {@link ContainerConnectionDetailsFactory} for + * {@link CassandraServiceConnection @CassandraServiceConnection}-annotated + * {@link CassandraContainer} fields. + * + * @author Moritz Halbritter + * @author Andy Wilkinson + * @author Phillip Webb + */ +class CassandraContainerConnectionDetailsFactory extends + ContainerConnectionDetailsFactory> { + + @Override + protected CassandraConnectionDetails getContainerConnectionDetails( + ContainerConnectionSource> source) { + return new CassandraContainerConnectionDetails(source); + } + + /** + * {@link CassandraConnectionDetails} backed by a {@link ContainerConnectionSource}. + */ + private static final class CassandraContainerConnectionDetails extends ContainerConnectionDetails + implements CassandraConnectionDetails { + + private final CassandraContainer container; + + private CassandraContainerConnectionDetails( + ContainerConnectionSource> source) { + super(source); + this.container = source.getContainer(); + } + + @Override + public List getContactPoints() { + return List.of(new Node(this.container.getContactPoint().getHostString(), + this.container.getContactPoint().getPort())); + } + + @Override + public String getUsername() { + return this.container.getUsername(); + } + + @Override + public String getPassword() { + return this.container.getPassword(); + } + + @Override + public String getLocalDatacenter() { + return this.container.getLocalDatacenter(); + } + + } + +} diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/cassandra/CassandraServiceConnection.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/cassandra/CassandraServiceConnection.java new file mode 100644 index 0000000000..4961ae7623 --- /dev/null +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/cassandra/CassandraServiceConnection.java @@ -0,0 +1,44 @@ +/* + * Copyright 2012-2023 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 + * + * https://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.cassandra; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.boot.autoconfigure.cassandra.CassandraConnectionDetails; +import org.springframework.boot.test.autoconfigure.service.connection.ServiceConnection; + +/** + * Annotation that indicates that a field provides a Cassandra service connection. + * + * @author Moritz Halbritter + * @author Andy Wilkinson + * @author Phillip Webb + * @since 3.1.0 + * @see CassandraConnectionDetails + * @see ServiceConnection + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.FIELD, ElementType.TYPE }) +@ServiceConnection(CassandraConnectionDetails.class) +public @interface CassandraServiceConnection { + +} diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/cassandra/package-info.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/cassandra/package-info.java new file mode 100644 index 0000000000..3aea6ce96b --- /dev/null +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/cassandra/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2012-2023 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 + * + * https://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. + */ + +/** + * Auto-configuration for using Cassandra in tests. + */ +package org.springframework.boot.test.autoconfigure.cassandra; diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/couchbase/CouchbaseContainerConnectionDetailsFactory.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/couchbase/CouchbaseContainerConnectionDetailsFactory.java new file mode 100644 index 0000000000..e068b56d87 --- /dev/null +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/couchbase/CouchbaseContainerConnectionDetailsFactory.java @@ -0,0 +1,74 @@ +/* + * Copyright 2012-2023 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 + * + * https://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.couchbase; + +import org.testcontainers.couchbase.CouchbaseContainer; + +import org.springframework.boot.autoconfigure.couchbase.CouchbaseConnectionDetails; +import org.springframework.boot.test.autoconfigure.service.connection.ContainerConnectionDetailsFactory; +import org.springframework.boot.test.autoconfigure.service.connection.ContainerConnectionSource; + +/** + * {@link ContainerConnectionDetailsFactory} for + * {@link CouchbaseServiceConnection @CouchbaseServiceConnection}-annotated + * {@link CouchbaseContainer} fields. + * + * @author Moritz Halbritter + * @author Andy Wilkinson + * @author Phillip Webb + */ +class CouchbaseContainerConnectionDetailsFactory extends + ContainerConnectionDetailsFactory { + + @Override + protected CouchbaseConnectionDetails getContainerConnectionDetails( + ContainerConnectionSource source) { + return new CouchbaseContainerConnectionDetails(source); + } + + /** + * {@link CouchbaseConnectionDetails} backed by a {@link ContainerConnectionSource}. + */ + private static final class CouchbaseContainerConnectionDetails extends ContainerConnectionDetails + implements CouchbaseConnectionDetails { + + private final CouchbaseContainer container; + + private CouchbaseContainerConnectionDetails( + ContainerConnectionSource source) { + super(source); + this.container = source.getContainer(); + } + + @Override + public String getUsername() { + return this.container.getUsername(); + } + + @Override + public String getPassword() { + return this.container.getPassword(); + } + + @Override + public String getConnectionString() { + return this.container.getConnectionString(); + } + + } + +} diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/couchbase/CouchbaseServiceConnection.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/couchbase/CouchbaseServiceConnection.java new file mode 100644 index 0000000000..53d7cfdbf7 --- /dev/null +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/couchbase/CouchbaseServiceConnection.java @@ -0,0 +1,44 @@ +/* + * Copyright 2012-2023 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 + * + * https://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.couchbase; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.boot.autoconfigure.couchbase.CouchbaseConnectionDetails; +import org.springframework.boot.test.autoconfigure.service.connection.ServiceConnection; + +/** + * Annotation that indicates that a field provides a Couchbase service connection. + * + * @author Moritz Halbritter + * @author Andy Wilkinson + * @author Phillip Webb + * @since 3.1.0 + * @see CouchbaseConnectionDetails + * @see ServiceConnection + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.FIELD, ElementType.TYPE }) +@ServiceConnection(CouchbaseConnectionDetails.class) +public @interface CouchbaseServiceConnection { + +} diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/couchbase/package-info.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/couchbase/package-info.java new file mode 100644 index 0000000000..920989324a --- /dev/null +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/couchbase/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2012-2023 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 + * + * https://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. + */ + +/** + * Auto-configuration for using Couchbase in tests. + */ +package org.springframework.boot.test.autoconfigure.couchbase; diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/data/redis/RedisContainerConnectionDetailsFactory.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/data/redis/RedisContainerConnectionDetailsFactory.java new file mode 100644 index 0000000000..01f2742938 --- /dev/null +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/data/redis/RedisContainerConnectionDetailsFactory.java @@ -0,0 +1,76 @@ +/* + * Copyright 2012-2023 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 + * + * https://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.data.redis; + +import org.testcontainers.containers.GenericContainer; + +import org.springframework.boot.autoconfigure.data.redis.RedisConnectionDetails; +import org.springframework.boot.test.autoconfigure.service.connection.ContainerConnectionDetailsFactory; +import org.springframework.boot.test.autoconfigure.service.connection.ContainerConnectionSource; + +/** + * {@link ContainerConnectionDetailsFactory} for + * {@link RedisServiceConnection @RedisServiceConnection}-annotated + * {@link GenericContainer} fields. + * + * @author Moritz Halbritter + * @author Andy Wilkinson + * @author Phillip Webb + */ +class RedisContainerConnectionDetailsFactory + extends ContainerConnectionDetailsFactory> { + + @Override + public RedisConnectionDetails getContainerConnectionDetails( + ContainerConnectionSource> source) { + return new RedisContainerConnectionDetails(source); + } + + /** + * {@link RedisConnectionDetails} backed by a {@link ContainerConnectionSource}. + */ + private static final class RedisContainerConnectionDetails extends ContainerConnectionDetails + implements RedisConnectionDetails { + + private final Standalone standalone; + + private RedisContainerConnectionDetails( + ContainerConnectionSource> source) { + super(source); + this.standalone = new Standalone() { + + @Override + public String getHost() { + return source.getContainer().getHost(); + } + + @Override + public int getPort() { + return source.getContainer().getFirstMappedPort(); + } + + }; + } + + @Override + public Standalone getStandalone() { + return this.standalone; + } + + } + +} diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/data/redis/RedisServiceConnection.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/data/redis/RedisServiceConnection.java new file mode 100644 index 0000000000..4c93666579 --- /dev/null +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/data/redis/RedisServiceConnection.java @@ -0,0 +1,43 @@ +/* + * Copyright 2012-2023 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 + * + * https://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.data.redis; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.boot.autoconfigure.data.redis.RedisConnectionDetails; +import org.springframework.boot.test.autoconfigure.service.connection.ServiceConnection; + +/** + * Annotation that indicates that a field provides a Redis service connection. + * + * @author Moritz Halbritter + * @author Andy Wilkinson + * @since 3.1.0 + * @see RedisConnectionDetails + * @see ServiceConnection + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.FIELD, ElementType.TYPE }) +@ServiceConnection(RedisConnectionDetails.class) +public @interface RedisServiceConnection { + +} diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/elasticsearch/ElasticsearchContainerConnectionDetailsFactory.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/elasticsearch/ElasticsearchContainerConnectionDetailsFactory.java new file mode 100644 index 0000000000..dba868ae0c --- /dev/null +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/elasticsearch/ElasticsearchContainerConnectionDetailsFactory.java @@ -0,0 +1,71 @@ +/* + * Copyright 2012-2023 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 + * + * https://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.elasticsearch; + +import java.util.List; + +import org.testcontainers.containers.GenericContainer; + +import org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchConnectionDetails; +import org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchConnectionDetails.Node.Protocol; +import org.springframework.boot.test.autoconfigure.service.connection.ContainerConnectionDetailsFactory; +import org.springframework.boot.test.autoconfigure.service.connection.ContainerConnectionSource; + +/** + * {@link ContainerConnectionDetailsFactory} for + * {@link ElasticsearchServiceConnection @ElasticsearchServiceConnection}-annotated + * {@link GenericContainer} fields. + * + * @author Moritz Halbritter + * @author Andy Wilkinson + * @author Phillip Webb + */ +class ElasticsearchContainerConnectionDetailsFactory extends + ContainerConnectionDetailsFactory> { + + private static final int DEFAULT_PORT = 9200; + + @Override + protected ElasticsearchConnectionDetails getContainerConnectionDetails( + ContainerConnectionSource> source) { + return new ElasticsearchContainerConnectionDetails(source); + } + + /** + * {@link ElasticsearchConnectionDetails} backed by a + * {@link ContainerConnectionSource}. + */ + private static final class ElasticsearchContainerConnectionDetails extends ContainerConnectionDetails + implements ElasticsearchConnectionDetails { + + private final List nodes; + + private ElasticsearchContainerConnectionDetails( + ContainerConnectionSource> source) { + super(source); + this.nodes = List.of(new Node(source.getContainer().getHost(), + source.getContainer().getMappedPort(DEFAULT_PORT), Protocol.HTTP, null, null)); + } + + @Override + public List getNodes() { + return this.nodes; + } + + } + +} diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/elasticsearch/ElasticsearchServiceConnection.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/elasticsearch/ElasticsearchServiceConnection.java new file mode 100644 index 0000000000..7cd2df5f4c --- /dev/null +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/elasticsearch/ElasticsearchServiceConnection.java @@ -0,0 +1,44 @@ +/* + * Copyright 2012-2023 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 + * + * https://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.elasticsearch; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchConnectionDetails; +import org.springframework.boot.test.autoconfigure.service.connection.ServiceConnection; + +/** + * Annotation that indicates that a field provides a Elasticsearch service connection. + * + * @author Moritz Halbritter + * @author Andy Wilkinson + * @author Phillip Webb + * @since 3.1.0 + * @see ElasticsearchConnectionDetails + * @see ServiceConnection + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.FIELD, ElementType.TYPE }) +@ServiceConnection(ElasticsearchConnectionDetails.class) +public @interface ElasticsearchServiceConnection { + +} diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/elasticsearch/package-info.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/elasticsearch/package-info.java new file mode 100644 index 0000000000..8762845653 --- /dev/null +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/elasticsearch/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2012-2023 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 + * + * https://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. + */ + +/** + * Auto-configuration for using Elasticsearch in tests. + */ +package org.springframework.boot.test.autoconfigure.elasticsearch; diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/influx/InfluxDbContainerConnectionDetailsFactory.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/influx/InfluxDbContainerConnectionDetailsFactory.java new file mode 100644 index 0000000000..734dcadd32 --- /dev/null +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/influx/InfluxDbContainerConnectionDetailsFactory.java @@ -0,0 +1,76 @@ +/* + * Copyright 2012-2023 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 + * + * https://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.influx; + +import java.net.URI; + +import org.testcontainers.containers.InfluxDBContainer; + +import org.springframework.boot.autoconfigure.influx.InfluxDbConnectionDetails; +import org.springframework.boot.test.autoconfigure.service.connection.ContainerConnectionDetailsFactory; +import org.springframework.boot.test.autoconfigure.service.connection.ContainerConnectionSource; + +/** + * {@link ContainerConnectionDetailsFactory} for + * {@link InfluxDbServiceConnection @InfluxDbServiceConnection}-annotated + * {@link InfluxDBContainer} fields. + * + * @author Moritz Halbritter + * @author Andy Wilkinson + * @author Phillip Webb + */ +class InfluxDbContainerConnectionDetailsFactory extends + ContainerConnectionDetailsFactory> { + + @Override + protected InfluxDbConnectionDetails getContainerConnectionDetails( + ContainerConnectionSource> source) { + return new InfluxDbContainerConnectionDetails(source); + } + + /** + * {@link InfluxDbConnectionDetails} backed by a {@link ContainerConnectionSource}. + */ + private static final class InfluxDbContainerConnectionDetails extends ContainerConnectionDetails + implements InfluxDbConnectionDetails { + + private final InfluxDBContainer container; + + private InfluxDbContainerConnectionDetails( + ContainerConnectionSource> source) { + super(source); + this.container = source.getContainer(); + } + + @Override + public String getUsername() { + return this.container.getUsername(); + } + + @Override + public String getPassword() { + return this.container.getPassword(); + } + + @Override + public URI getUrl() { + return URI.create(this.container.getUrl()); + } + + } + +} diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/influx/InfluxDbServiceConnection.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/influx/InfluxDbServiceConnection.java new file mode 100644 index 0000000000..b6651a9f78 --- /dev/null +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/influx/InfluxDbServiceConnection.java @@ -0,0 +1,44 @@ +/* + * Copyright 2012-2023 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 + * + * https://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.influx; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.boot.autoconfigure.influx.InfluxDbConnectionDetails; +import org.springframework.boot.test.autoconfigure.service.connection.ServiceConnection; + +/** + * Annotation that indicates that a field provides an InfluxDB service connection. + * + * @author Moritz Halbritter + * @author Andy Wilkinson + * @author Phillip Webb + * @since 3.1.0 + * @see InfluxDbConnectionDetails + * @see ServiceConnection + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.FIELD, ElementType.TYPE }) +@ServiceConnection(InfluxDbConnectionDetails.class) +public @interface InfluxDbServiceConnection { + +} diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/influx/package-info.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/influx/package-info.java new file mode 100644 index 0000000000..eace428a79 --- /dev/null +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/influx/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2012-2023 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 + * + * https://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. + */ + +/** + * Auto-configuration for using InfluxDB in tests. + */ +package org.springframework.boot.test.autoconfigure.influx; diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/jdbc/JdbcContainerConnectionDetailsFactory.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/jdbc/JdbcContainerConnectionDetailsFactory.java new file mode 100644 index 0000000000..4c487f0a04 --- /dev/null +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/jdbc/JdbcContainerConnectionDetailsFactory.java @@ -0,0 +1,79 @@ +/* + * Copyright 2012-2023 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 + * + * https://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.jdbc; + +import org.testcontainers.containers.JdbcDatabaseContainer; + +import org.springframework.boot.autoconfigure.jdbc.JdbcConnectionDetails; +import org.springframework.boot.test.autoconfigure.service.connection.ContainerConnectionDetailsFactory; +import org.springframework.boot.test.autoconfigure.service.connection.ContainerConnectionSource; + +/** + * {@link ContainerConnectionDetailsFactory} for + * {@link JdbcServiceConnection @JdbcServiceConnection}-annotated + * {@link JdbcDatabaseContainer} fields. + * + * @author Moritz Halbritter + * @author Andy Wilkinson + * @author Phillip Webb + */ +class JdbcContainerConnectionDetailsFactory extends + ContainerConnectionDetailsFactory> { + + @Override + protected JdbcConnectionDetails getContainerConnectionDetails( + ContainerConnectionSource> source) { + return new JdbcContainerConnectionDetails(source); + } + + /** + * {@link JdbcConnectionDetails} backed by a {@link ContainerConnectionSource}. + */ + private static final class JdbcContainerConnectionDetails extends ContainerConnectionDetails + implements JdbcConnectionDetails { + + private final JdbcDatabaseContainer container; + + private JdbcContainerConnectionDetails( + ContainerConnectionSource> source) { + super(source); + this.container = source.getContainer(); + } + + @Override + public String getUsername() { + return this.container.getUsername(); + } + + @Override + public String getPassword() { + return this.container.getPassword(); + } + + @Override + public String getJdbcUrl() { + return this.container.getJdbcUrl(); + } + + @Override + public String getDriverClassName() { + return this.container.getDriverClassName(); + } + + } + +} diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/jdbc/JdbcServiceConnection.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/jdbc/JdbcServiceConnection.java new file mode 100644 index 0000000000..60ba2fe28c --- /dev/null +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/jdbc/JdbcServiceConnection.java @@ -0,0 +1,43 @@ +/* + * Copyright 2012-2023 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 + * + * https://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.jdbc; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.boot.autoconfigure.jdbc.JdbcConnectionDetails; +import org.springframework.boot.test.autoconfigure.service.connection.ServiceConnection; + +/** + * Annotation that indicates that a field provides a JDBC database service connection. + * + * @author Moritz Halbritter + * @author Andy Wilkinson + * @since 3.1.0 + * @see JdbcConnectionDetails + * @see ServiceConnection + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.FIELD, ElementType.TYPE }) +@ServiceConnection(JdbcConnectionDetails.class) +public @interface JdbcServiceConnection { + +} diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/kafka/KafkaContainerConnectionDetailsFactory.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/kafka/KafkaContainerConnectionDetailsFactory.java new file mode 100644 index 0000000000..19342acbe4 --- /dev/null +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/kafka/KafkaContainerConnectionDetailsFactory.java @@ -0,0 +1,68 @@ +/* + * Copyright 2012-2023 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 + * + * https://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.kafka; + +import java.net.URI; +import java.util.List; + +import org.testcontainers.containers.KafkaContainer; + +import org.springframework.boot.autoconfigure.kafka.KafkaConnectionDetails; +import org.springframework.boot.test.autoconfigure.service.connection.ContainerConnectionDetailsFactory; +import org.springframework.boot.test.autoconfigure.service.connection.ContainerConnectionSource; + +/** + * {@link ContainerConnectionDetailsFactory} for + * {@link KafkaServiceConnection @KafkaServiceConnection}-annotated {@link KafkaContainer} + * fields. + * + * @author Moritz Halbritter + * @author Andy Wilkinson + * @author Phillip Webb + */ +class KafkaContainerConnectionDetailsFactory + extends ContainerConnectionDetailsFactory { + + @Override + protected KafkaConnectionDetails getContainerConnectionDetails( + ContainerConnectionSource source) { + return new KafkaContainerConnectionDetails(source); + } + + /** + * {@link KafkaConnectionDetails} backed by a {@link ContainerConnectionSource}. + */ + private static final class KafkaContainerConnectionDetails extends ContainerConnectionDetails + implements KafkaConnectionDetails { + + private final KafkaContainer container; + + private KafkaContainerConnectionDetails( + ContainerConnectionSource source) { + super(source); + this.container = source.getContainer(); + } + + @Override + public List getBootstrapNodes() { + URI uri = URI.create(this.container.getBootstrapServers()); + return List.of(new Node(uri.getHost(), uri.getPort())); + } + + } + +} diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/kafka/KafkaServiceConnection.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/kafka/KafkaServiceConnection.java new file mode 100644 index 0000000000..19bd96505e --- /dev/null +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/kafka/KafkaServiceConnection.java @@ -0,0 +1,43 @@ +/* + * Copyright 2012-2023 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 + * + * https://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.kafka; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.boot.autoconfigure.kafka.KafkaConnectionDetails; +import org.springframework.boot.test.autoconfigure.service.connection.ServiceConnection; + +/** + * Annotation that indicates that a field provides a Kafka service connection. + * + * @author Moritz Halbritter + * @author Andy Wilkinson + * @since 3.1.0 + * @see KafkaConnectionDetails + * @see ServiceConnection + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.FIELD, ElementType.TYPE }) +@ServiceConnection(KafkaConnectionDetails.class) +public @interface KafkaServiceConnection { + +} diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/kafka/package-info.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/kafka/package-info.java new file mode 100644 index 0000000000..6de454014d --- /dev/null +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/kafka/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2012-2023 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 + * + * https://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. + */ + +/** + * Auto-configuration for using Kafka in tests. + */ +package org.springframework.boot.test.autoconfigure.kafka; diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/mongo/MongoContainerConnectionDetailsFactory.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/mongo/MongoContainerConnectionDetailsFactory.java new file mode 100644 index 0000000000..9e948181c2 --- /dev/null +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/mongo/MongoContainerConnectionDetailsFactory.java @@ -0,0 +1,65 @@ +/* + * Copyright 2012-2023 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 + * + * https://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.mongo; + +import com.mongodb.ConnectionString; +import org.testcontainers.containers.MongoDBContainer; + +import org.springframework.boot.autoconfigure.mongo.MongoConnectionDetails; +import org.springframework.boot.test.autoconfigure.service.connection.ContainerConnectionDetailsFactory; +import org.springframework.boot.test.autoconfigure.service.connection.ContainerConnectionSource; + +/** + * {@link ContainerConnectionDetailsFactory} for + * {@link MongoServiceConnection @MongoServiceConnection}-annotated + * {@link MongoDBContainer} fields. + * + * @author Moritz Halbritter + * @author Andy Wilkinson + * @author Phillip Webb + */ +class MongoContainerConnectionDetailsFactory + extends ContainerConnectionDetailsFactory { + + @Override + protected MongoConnectionDetails getContainerConnectionDetails( + ContainerConnectionSource source) { + return new MongoContainerConnectionDetails(source); + } + + /** + * {@link MongoConnectionDetails} backed by a {@link ContainerConnectionSource}. + */ + private static final class MongoContainerConnectionDetails extends ContainerConnectionDetails + implements MongoConnectionDetails { + + private final ConnectionString connectionString; + + private MongoContainerConnectionDetails( + ContainerConnectionSource source) { + super(source); + this.connectionString = new ConnectionString(source.getContainer().getReplicaSetUrl()); + } + + @Override + public ConnectionString getConnectionString() { + return this.connectionString; + } + + } + +} diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/mongo/MongoServiceConnection.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/mongo/MongoServiceConnection.java new file mode 100644 index 0000000000..45bb65e349 --- /dev/null +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/mongo/MongoServiceConnection.java @@ -0,0 +1,43 @@ +/* + * Copyright 2012-2023 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 + * + * https://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.mongo; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.boot.autoconfigure.mongo.MongoConnectionDetails; +import org.springframework.boot.test.autoconfigure.service.connection.ServiceConnection; + +/** + * Annotation that indicates that a field provides a Mongo service connection. + * + * @author Moritz Halbritter + * @author Andy Wilkinson + * @since 3.1.0 + * @see MongoConnectionDetails + * @see ServiceConnection + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.FIELD, ElementType.TYPE }) +@ServiceConnection(MongoConnectionDetails.class) +public @interface MongoServiceConnection { + +} diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/mongo/package-info.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/mongo/package-info.java new file mode 100644 index 0000000000..bd553633bd --- /dev/null +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/mongo/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2012-2023 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 + * + * https://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. + */ + +/** + * Auto-configuration for using Mongo in tests. + */ +package org.springframework.boot.test.autoconfigure.mongo; diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/neo4j/Neo4jContainerConnectionDetailsFactory.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/neo4j/Neo4jContainerConnectionDetailsFactory.java new file mode 100644 index 0000000000..bc4f36702f --- /dev/null +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/neo4j/Neo4jContainerConnectionDetailsFactory.java @@ -0,0 +1,74 @@ +/* + * Copyright 2012-2023 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 + * + * https://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.neo4j; + +import java.net.URI; + +import org.neo4j.driver.AuthToken; +import org.neo4j.driver.AuthTokens; +import org.testcontainers.containers.Neo4jContainer; + +import org.springframework.boot.autoconfigure.neo4j.Neo4jConnectionDetails; +import org.springframework.boot.test.autoconfigure.service.connection.ContainerConnectionDetailsFactory; +import org.springframework.boot.test.autoconfigure.service.connection.ContainerConnectionSource; + +/** + * {@link ContainerConnectionDetailsFactory} for + * {@link Neo4jServiceConnection @Neo4jServiceConnection}-annotated {@link Neo4jContainer} + * fields. + * + * @author Moritz Halbritter + * @author Andy Wilkinson + * @author Phillip Webb + */ +class Neo4jContainerConnectionDetailsFactory + extends ContainerConnectionDetailsFactory> { + + @Override + protected Neo4jConnectionDetails getContainerConnectionDetails( + ContainerConnectionSource> source) { + return new Neo4jContainerConnectionDetails(source); + } + + /** + * {@link Neo4jConnectionDetails} backed by a {@link ContainerConnectionSource}. + */ + private static final class Neo4jContainerConnectionDetails extends ContainerConnectionDetails + implements Neo4jConnectionDetails { + + private final Neo4jContainer container; + + private Neo4jContainerConnectionDetails( + ContainerConnectionSource> source) { + super(source); + this.container = source.getContainer(); + } + + @Override + public URI getUri() { + return URI.create(this.container.getBoltUrl()); + } + + @Override + public AuthToken getAuthToken() { + String password = this.container.getAdminPassword(); + return (password != null) ? AuthTokens.basic("neo4j", password) : AuthTokens.none(); + } + + } + +} diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/neo4j/Neo4jServiceConnection.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/neo4j/Neo4jServiceConnection.java new file mode 100644 index 0000000000..c7d49c171f --- /dev/null +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/neo4j/Neo4jServiceConnection.java @@ -0,0 +1,43 @@ +/* + * Copyright 2012-2023 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 + * + * https://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.neo4j; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.boot.autoconfigure.neo4j.Neo4jConnectionDetails; +import org.springframework.boot.test.autoconfigure.service.connection.ServiceConnection; + +/** + * Annotation that indicates a field provides a Neo4j service connection. + * + * @author Moritz Halbritter + * @author Andy Wilkinson + * @since 3.1.0 + * @see Neo4jConnectionDetails + * @see ServiceConnection + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.FIELD, ElementType.TYPE }) +@ServiceConnection(Neo4jConnectionDetails.class) +public @interface Neo4jServiceConnection { + +} diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/neo4j/package-info.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/neo4j/package-info.java new file mode 100644 index 0000000000..e166b25616 --- /dev/null +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/neo4j/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2012-2023 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 + * + * https://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. + */ + +/** + * Auto-configuration for using Neo4j in tests. + */ +package org.springframework.boot.test.autoconfigure.neo4j; diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/r2dbc/MariaDbR2dbcContainerConnectionDetailsFactory.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/r2dbc/MariaDbR2dbcContainerConnectionDetailsFactory.java new file mode 100644 index 0000000000..9f16320267 --- /dev/null +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/r2dbc/MariaDbR2dbcContainerConnectionDetailsFactory.java @@ -0,0 +1,64 @@ +/* + * Copyright 2012-2023 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 + * + * https://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.r2dbc; + +import io.r2dbc.spi.ConnectionFactoryOptions; +import org.testcontainers.containers.MariaDBContainer; +import org.testcontainers.containers.MariaDBR2DBCDatabaseContainer; + +import org.springframework.boot.autoconfigure.r2dbc.R2dbcConnectionDetails; +import org.springframework.boot.autoconfigure.service.connection.ConnectionDetailsFactory; +import org.springframework.boot.test.autoconfigure.service.connection.ContainerConnectionDetailsFactory; +import org.springframework.boot.test.autoconfigure.service.connection.ContainerConnectionSource; + +/** + * {@link ConnectionDetailsFactory} for + * {@link R2dbcServiceConnection @R2dbcServiceConnection}- annotated + * {@link MariaDBContainer} fields. + * + * @author Moritz Halbritter + * @author Andy Wilkinson + * @author Phillip Webb + */ +class MariaDbR2dbcContainerConnectionDetailsFactory + extends ContainerConnectionDetailsFactory> { + + @Override + public R2dbcConnectionDetails getContainerConnectionDetails( + ContainerConnectionSource> source) { + return new R2dbcDatabaseContainerConnectionDetails(source.getContainer()); + } + + /** + * {@link R2dbcConnectionDetails} backed by a {@link ContainerConnectionSource}. + */ + private static final class R2dbcDatabaseContainerConnectionDetails implements R2dbcConnectionDetails { + + private final MariaDBContainer container; + + private R2dbcDatabaseContainerConnectionDetails(MariaDBContainer container) { + this.container = container; + } + + @Override + public ConnectionFactoryOptions getConnectionFactoryOptions() { + return MariaDBR2DBCDatabaseContainer.getOptions(this.container); + } + + } + +} diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/r2dbc/MySqlR2dbcContainerConnectionDetailsFactory.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/r2dbc/MySqlR2dbcContainerConnectionDetailsFactory.java new file mode 100644 index 0000000000..f05f70fec3 --- /dev/null +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/r2dbc/MySqlR2dbcContainerConnectionDetailsFactory.java @@ -0,0 +1,64 @@ +/* + * Copyright 2012-2023 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 + * + * https://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.r2dbc; + +import io.r2dbc.spi.ConnectionFactoryOptions; +import org.testcontainers.containers.MySQLContainer; +import org.testcontainers.containers.MySQLR2DBCDatabaseContainer; + +import org.springframework.boot.autoconfigure.r2dbc.R2dbcConnectionDetails; +import org.springframework.boot.autoconfigure.service.connection.ConnectionDetailsFactory; +import org.springframework.boot.test.autoconfigure.service.connection.ContainerConnectionDetailsFactory; +import org.springframework.boot.test.autoconfigure.service.connection.ContainerConnectionSource; + +/** + * {@link ConnectionDetailsFactory} for + * {@link R2dbcServiceConnection @R2dbcServiceConnection}- annotated + * {@link MySQLContainer} fields. + * + * @author Moritz Halbritter + * @author Andy Wilkinson + * @author Phillip Webb + */ +class MySqlR2dbcContainerConnectionDetailsFactory + extends ContainerConnectionDetailsFactory> { + + @Override + public R2dbcConnectionDetails getContainerConnectionDetails( + ContainerConnectionSource> source) { + return new R2dbcDatabaseContainerConnectionDetails(source.getContainer()); + } + + /** + * {@link R2dbcConnectionDetails} backed by a {@link ContainerConnectionSource}. + */ + private static final class R2dbcDatabaseContainerConnectionDetails implements R2dbcConnectionDetails { + + private final MySQLContainer container; + + private R2dbcDatabaseContainerConnectionDetails(MySQLContainer container) { + this.container = container; + } + + @Override + public ConnectionFactoryOptions getConnectionFactoryOptions() { + return MySQLR2DBCDatabaseContainer.getOptions(this.container); + } + + } + +} diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/r2dbc/PostgresR2dbcContainerConnectionDetailsFactory.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/r2dbc/PostgresR2dbcContainerConnectionDetailsFactory.java new file mode 100644 index 0000000000..0273859be4 --- /dev/null +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/r2dbc/PostgresR2dbcContainerConnectionDetailsFactory.java @@ -0,0 +1,63 @@ +/* + * Copyright 2012-2023 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 + * + * https://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.r2dbc; + +import io.r2dbc.spi.ConnectionFactoryOptions; +import org.testcontainers.containers.PostgreSQLContainer; +import org.testcontainers.containers.PostgreSQLR2DBCDatabaseContainer; + +import org.springframework.boot.autoconfigure.r2dbc.R2dbcConnectionDetails; +import org.springframework.boot.autoconfigure.service.connection.ConnectionDetailsFactory; +import org.springframework.boot.test.autoconfigure.service.connection.ContainerConnectionDetailsFactory; +import org.springframework.boot.test.autoconfigure.service.connection.ContainerConnectionSource; + +/** + * {@link ConnectionDetailsFactory} for {@link R2dbcServiceConnection @R2dbcConnection} + * annotated {@link PostgreSQLContainer} fields. + * + * @author Moritz Halbritter + * @author Andy Wilkinson + * @author Phillip Webb + */ +class PostgresR2dbcContainerConnectionDetailsFactory extends + ContainerConnectionDetailsFactory> { + + @Override + public R2dbcConnectionDetails getContainerConnectionDetails( + ContainerConnectionSource> source) { + return new R2dbcDatabaseContainerConnectionDetails(source.getContainer()); + } + + /** + * {@link R2dbcConnectionDetails} backed by a {@link ContainerConnectionSource}. + */ + private static final class R2dbcDatabaseContainerConnectionDetails implements R2dbcConnectionDetails { + + private final PostgreSQLContainer container; + + private R2dbcDatabaseContainerConnectionDetails(PostgreSQLContainer container) { + this.container = container; + } + + @Override + public ConnectionFactoryOptions getConnectionFactoryOptions() { + return PostgreSQLR2DBCDatabaseContainer.getOptions(this.container); + } + + } + +} diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/r2dbc/R2dbcServiceConnection.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/r2dbc/R2dbcServiceConnection.java new file mode 100644 index 0000000000..a85022ce8c --- /dev/null +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/r2dbc/R2dbcServiceConnection.java @@ -0,0 +1,43 @@ +/* + * Copyright 2012-2023 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 + * + * https://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.r2dbc; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.boot.autoconfigure.r2dbc.R2dbcConnectionDetails; +import org.springframework.boot.test.autoconfigure.service.connection.ServiceConnection; + +/** + * Annotation that indicates a field provides a R2DBC database service connection. + * + * @author Moritz Halbritter + * @author Andy Wilkinson + * @since 3.1.0 + * @see ServiceConnection + * @see R2dbcConnectionDetails + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.FIELD, ElementType.TYPE }) +@ServiceConnection(R2dbcConnectionDetails.class) +public @interface R2dbcServiceConnection { + +} diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/r2dbc/SqlServerR2dbcContainerConnectionDetailsFactory.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/r2dbc/SqlServerR2dbcContainerConnectionDetailsFactory.java new file mode 100644 index 0000000000..3acbdf006c --- /dev/null +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/r2dbc/SqlServerR2dbcContainerConnectionDetailsFactory.java @@ -0,0 +1,64 @@ +/* + * Copyright 2012-2023 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 + * + * https://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.r2dbc; + +import io.r2dbc.spi.ConnectionFactoryOptions; +import org.testcontainers.containers.MSSQLR2DBCDatabaseContainer; +import org.testcontainers.containers.MSSQLServerContainer; + +import org.springframework.boot.autoconfigure.r2dbc.R2dbcConnectionDetails; +import org.springframework.boot.autoconfigure.service.connection.ConnectionDetailsFactory; +import org.springframework.boot.test.autoconfigure.service.connection.ContainerConnectionDetailsFactory; +import org.springframework.boot.test.autoconfigure.service.connection.ContainerConnectionSource; + +/** + * {@link ConnectionDetailsFactory} for + * {@link R2dbcServiceConnection @R2dbcServiceConnection}- annotated + * {@link MSSQLServerContainer} fields. + * + * @author Moritz Halbritter + * @author Andy Wilkinson + * @author Phillip Webb + */ +class SqlServerR2dbcContainerConnectionDetailsFactory extends + ContainerConnectionDetailsFactory> { + + @Override + public R2dbcConnectionDetails getContainerConnectionDetails( + ContainerConnectionSource> source) { + return new R2dbcDatabaseContainerConnectionDetails(source.getContainer()); + } + + /** + * {@link R2dbcConnectionDetails} backed by a {@link ContainerConnectionSource}. + */ + private static final class R2dbcDatabaseContainerConnectionDetails implements R2dbcConnectionDetails { + + private final MSSQLServerContainer container; + + private R2dbcDatabaseContainerConnectionDetails(MSSQLServerContainer container) { + this.container = container; + } + + @Override + public ConnectionFactoryOptions getConnectionFactoryOptions() { + return MSSQLR2DBCDatabaseContainer.getOptions(this.container); + } + + } + +} diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/r2dbc/package-info.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/r2dbc/package-info.java new file mode 100644 index 0000000000..80f88f7009 --- /dev/null +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/r2dbc/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2012-2023 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 + * + * https://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. + */ + +/** + * Auto-configuration for using R2DBC in tests. + */ +package org.springframework.boot.test.autoconfigure.r2dbc; diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/service/connection/AnnotatedFieldOrigin.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/service/connection/AnnotatedFieldOrigin.java new file mode 100644 index 0000000000..5df3fb3542 --- /dev/null +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/service/connection/AnnotatedFieldOrigin.java @@ -0,0 +1,68 @@ +/* + * Copyright 2012-2023 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 + * + * https://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.service.connection; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; +import java.util.Objects; + +import org.springframework.boot.origin.Origin; +import org.springframework.util.Assert; + +/** + * {@link Origin} backed by a {@link Field} and an {@link Annotation}. + * + * @author Moritz Halbritter + * @author Andy Wilkinson + * @author Phillip Webb + */ +class AnnotatedFieldOrigin implements Origin { + + private final Field field; + + private final Annotation annotation; + + AnnotatedFieldOrigin(Field field, Annotation annotation) { + Assert.notNull(field, "Field must not be null"); + Assert.notNull(annotation, "Annotation must not be null"); + this.field = field; + this.annotation = annotation; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + AnnotatedFieldOrigin other = (AnnotatedFieldOrigin) obj; + return this.field.equals(other.field) && this.annotation.equals(other.annotation); + } + + @Override + public int hashCode() { + return Objects.hash(this.field, this.annotation); + } + + @Override + public String toString() { + return this.annotation + " " + this.field; + } + +} diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/service/connection/ContainerConnectionDetailsFactory.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/service/connection/ContainerConnectionDetailsFactory.java new file mode 100644 index 0000000000..2739a856eb --- /dev/null +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/service/connection/ContainerConnectionDetailsFactory.java @@ -0,0 +1,93 @@ +/* + * Copyright 2012-2023 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 + * + * https://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.service.connection; + +import java.lang.annotation.Annotation; + +import org.testcontainers.containers.GenericContainer; + +import org.springframework.boot.autoconfigure.service.connection.ConnectionDetails; +import org.springframework.boot.autoconfigure.service.connection.ConnectionDetailsFactory; +import org.springframework.boot.origin.Origin; +import org.springframework.boot.origin.OriginProvider; +import org.springframework.core.ResolvableType; +import org.springframework.util.Assert; + +/** + * Base class for {@link ConnectionDetailsFactory} implementations that provide + * {@link ConnectionDetails} from a {@link ContainerConnectionSource}. + * + * @param the source annotation type. The annotation will be mergable to a + * {@link ServiceConnection @ServiceConnection}. + * @param the connection details type + * @param the generic container type + * @author Moritz Halbritter + * @author Andy Wilkinson + * @author Phillip Webb + * @since 3.1.0 + */ +public abstract class ContainerConnectionDetailsFactory> + implements ConnectionDetailsFactory, D> { + + @Override + public final D getConnectionDetails(ContainerConnectionSource source) { + Class[] generics = resolveGenerics(); + Class annotationType = generics[0]; + Class connectionDetailsType = generics[1]; + Class containerType = generics[2]; + return (!source.accepts(annotationType, connectionDetailsType, containerType)) ? null + : getContainerConnectionDetails(source); + } + + private Class[] resolveGenerics() { + return ResolvableType.forClass(ContainerConnectionDetailsFactory.class, getClass()).resolveGenerics(); + } + + /** + * Get the {@link ConnectionDetails} from the given {@link ContainerConnectionSource} + * {@code source}. May return {@code null} if no connection can be created. Result + * types should consider extending {@link ContainerConnectionDetails}. + * @param source the source + * @return the service connection or {@code null}. + */ + protected abstract D getContainerConnectionDetails(ContainerConnectionSource source); + + /** + * Convenient base class for {@link ConnectionDetails} results that are backed by a + * {@link ContainerConnectionSource}. + */ + protected static class ContainerConnectionDetails implements ConnectionDetails, OriginProvider { + + private final Origin origin; + + /** + * Create a new {@link ContainerConnectionDetails} instance. + * @param source the source {@link ContainerConnectionSource} + */ + protected ContainerConnectionDetails(ContainerConnectionSource source) { + Assert.notNull(source, "Source must not be null"); + this.origin = source.getOrigin(); + } + + @Override + public Origin getOrigin() { + return this.origin; + } + + } + +} diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/service/connection/ContainerConnectionSource.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/service/connection/ContainerConnectionSource.java new file mode 100644 index 0000000000..5c4828275c --- /dev/null +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/service/connection/ContainerConnectionSource.java @@ -0,0 +1,108 @@ +/* + * Copyright 2012-2023 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 + * + * https://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.service.connection; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; + +import org.testcontainers.containers.GenericContainer; + +import org.springframework.boot.autoconfigure.service.connection.ConnectionDetails; +import org.springframework.boot.origin.Origin; +import org.springframework.boot.origin.OriginProvider; +import org.springframework.core.annotation.MergedAnnotation; + +/** + * Passed to {@link ContainerConnectionDetailsFactory} to provide details of the + * {@link ServiceConnection @ServiceConnection} annotation {@link GenericContainer} field + * that provides the service. + * + * @param the source annotation type. The annotation will mergable to a + * {@link ServiceConnection @ServiceConnection} + * @param the connection details type + * @param the generic container type + * @author Moritz Halbritter + * @author Andy Wilkinson + * @author Phillip Webb + * @since 3.1.0 + * @see ContainerConnectionDetailsFactory + */ +public final class ContainerConnectionSource> + implements OriginProvider { + + private final Class connectionDetailsType; + + private final Field field; + + private final A annotation; + + private final C container; + + private final AnnotatedFieldOrigin origin; + + @SuppressWarnings("unchecked") + ContainerConnectionSource(Class connectionDetailsType, Field field, + MergedAnnotation annotation, C container) { + this(connectionDetailsType, field, (A) annotation.getRoot().synthesize(), container); + } + + ContainerConnectionSource(Class connectionDetailsType, Field field, A annotation, C container) { + this.connectionDetailsType = connectionDetailsType; + this.field = field; + this.annotation = annotation; + this.container = container; + this.origin = new AnnotatedFieldOrigin(field, annotation); + } + + boolean accepts(Class annotationType, Class connectionDetailsType, Class containerType) { + return annotationType.isInstance(this.annotation) + && connectionDetailsType.isAssignableFrom(this.connectionDetailsType) + && containerType.isInstance(this.container); + } + + String getBeanName() { + return this.field.getName() + this.connectionDetailsType.getSimpleName() + "ConnectedContainer"; + } + + /** + * Return the source annotation that provided the connection to the container. This + * annotation will be mergable to {@link ServiceConnection @ServiceConnection}. + * @return the source annotation + */ + public A getAnnotation() { + return this.annotation; + } + + /** + * Return the {@link GenericContainer} that implements the service being connected to. + * @return the {@link GenericContainer} providing the service + */ + public C getContainer() { + return this.container; + } + + @Override + public Origin getOrigin() { + return this.origin; + } + + @Override + public String toString() { + return "ServiceConnectedContainer for %s".formatted(this.origin); + } + +} diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/service/connection/ServiceConnection.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/service/connection/ServiceConnection.java new file mode 100644 index 0000000000..190d47661b --- /dev/null +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/service/connection/ServiceConnection.java @@ -0,0 +1,50 @@ +/* + * Copyright 2012-2023 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 + * + * https://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.service.connection; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.boot.autoconfigure.service.connection.ConnectionDetails; +import org.springframework.boot.autoconfigure.service.connection.ConnectionDetailsFactory; + +/** + * Annotation used to indicate that a field provides a service that can be connected to. + * Typically used to meta-annotate a higher-level annotation. + *

+ * When used, a {@link ConnectionDetailsFactory} must be registered in + * {@code spring.factories} to provide {@link ConnectionDetails} for the field value. + * + * @author Moritz Halbritter + * @author Andy Wilkinson + * @author Phillip Webb + * @since 3.1.0 + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.FIELD, ElementType.ANNOTATION_TYPE }) +public @interface ServiceConnection { + + /** + * The type of {@link ConnectionDetails} that can describe how to connect to the + * service. + * @return the connection type + */ + Class value(); + +} diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/service/connection/ServiceConnectionContextCustomizer.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/service/connection/ServiceConnectionContextCustomizer.java new file mode 100644 index 0000000000..243662ca1c --- /dev/null +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/service/connection/ServiceConnectionContextCustomizer.java @@ -0,0 +1,81 @@ +/* + * Copyright 2012-2023 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 + * + * https://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.service.connection; + +import java.util.List; + +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.beans.factory.support.RootBeanDefinition; +import org.springframework.boot.autoconfigure.service.connection.ConnectionDetails; +import org.springframework.boot.autoconfigure.service.connection.ConnectionDetailsFactories; +import org.springframework.boot.autoconfigure.service.connection.ConnectionDetailsFactory; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.test.context.ContextCustomizer; +import org.springframework.test.context.MergedContextConfiguration; +import org.springframework.util.Assert; + +/** + * {@link ContextCustomizer} to support registering {@link ConnectionDetails}. + * + * @author Moritz Halbritter + * @author Andy Wilkinson + * @author Phillip Webb + */ +class ServiceConnectionContextCustomizer implements ContextCustomizer { + + private final ConnectionDetailsFactories factories = new ConnectionDetailsFactories(); + + private final List> sources; + + ServiceConnectionContextCustomizer(List> sources) { + this.sources = sources; + } + + @Override + public void customizeContext(ConfigurableApplicationContext context, MergedContextConfiguration mergedConfig) { + ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); + if (beanFactory instanceof BeanDefinitionRegistry registry) { + registerServiceConnections(registry); + } + } + + private void registerServiceConnections(BeanDefinitionRegistry registry) { + this.sources.forEach((source) -> registerServiceConnection(registry, source)); + } + + private void registerServiceConnection(BeanDefinitionRegistry registry, ContainerConnectionSource source) { + ConnectionDetails connectionDetails = getConnectionDetails(source); + String beanName = source.getBeanName(); + registry.registerBeanDefinition(beanName, createBeanDefinition(connectionDetails)); + } + + private ConnectionDetails getConnectionDetails(S source) { + ConnectionDetailsFactory factory = this.factories.getConnectionDetailsFactory(source); + ConnectionDetails connectionDetails = factory.getConnectionDetails(source); + Assert.state(connectionDetails != null, + () -> "No connection details created by %s".formatted(factory.getClass().getName())); + return connectionDetails; + } + + @SuppressWarnings("unchecked") + private BeanDefinition createBeanDefinition(T instance) { + return new RootBeanDefinition((Class) instance.getClass(), () -> instance); + } + +} diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/service/connection/ServiceConnectionContextCustomizerFactory.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/service/connection/ServiceConnectionContextCustomizerFactory.java new file mode 100644 index 0000000000..485bf3f145 --- /dev/null +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/service/connection/ServiceConnectionContextCustomizerFactory.java @@ -0,0 +1,76 @@ +/* + * Copyright 2012-2023 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 + * + * https://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.service.connection; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.List; + +import org.testcontainers.containers.GenericContainer; + +import org.springframework.boot.autoconfigure.service.connection.ConnectionDetails; +import org.springframework.core.annotation.MergedAnnotation; +import org.springframework.core.annotation.MergedAnnotations; +import org.springframework.test.context.ContextConfigurationAttributes; +import org.springframework.test.context.ContextCustomizer; +import org.springframework.test.context.ContextCustomizerFactory; +import org.springframework.util.Assert; +import org.springframework.util.ReflectionUtils; + +/** + * {@link ContextCustomizerFactory} to support service connections in tests. + * + * @author Moritz Halbritter + * @author Andy Wilkinson + * @author Phillip Webb + */ +class ServiceConnectionContextCustomizerFactory implements ContextCustomizerFactory { + + @Override + public ContextCustomizer createContextCustomizer(Class testClass, + List configAttributes) { + List> sources = new ArrayList<>(); + ReflectionUtils.doWithFields(testClass, (field) -> { + MergedAnnotations annotations = MergedAnnotations.from(field); + annotations.stream(ServiceConnection.class) + .forEach((annotation) -> sources.add(createSource(field, annotation))); + }); + return (sources.isEmpty()) ? null : new ServiceConnectionContextCustomizer(sources); + } + + private ContainerConnectionSource createSource(Field field, + MergedAnnotation annotation) { + Class connectionDetailsType = getConnectionDetailsType(annotation); + Object fieldValue = getFieldValue(field); + Assert.isInstanceOf(GenericContainer.class, fieldValue, + "Field %s must be a %s".formatted(field.getName(), GenericContainer.class.getName())); + GenericContainer container = (GenericContainer) fieldValue; + return new ContainerConnectionSource<>(connectionDetailsType, field, annotation, container); + } + + @SuppressWarnings("unchecked") + private Class getConnectionDetailsType( + MergedAnnotation annotation) { + return (Class) annotation.getClass(MergedAnnotation.VALUE); + } + + private Object getFieldValue(Field field) { + ReflectionUtils.makeAccessible(field); + return ReflectionUtils.getField(field, null); + } + +} diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/service/connection/package-info.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/service/connection/package-info.java new file mode 100644 index 0000000000..b442910e0d --- /dev/null +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/service/connection/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2012-2023 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 + * + * https://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. + */ + +/** + * General support for auto-configuration of service connections in tests. + */ +package org.springframework.boot.test.autoconfigure.service.connection; diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/resources/META-INF/spring.factories b/spring-boot-project/spring-boot-test-autoconfigure/src/main/resources/META-INF/spring.factories index 64fb54f860..8359822535 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/main/resources/META-INF/spring.factories +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/resources/META-INF/spring.factories @@ -4,6 +4,7 @@ org.springframework.boot.test.autoconfigure.OverrideAutoConfigurationContextCust org.springframework.boot.test.autoconfigure.actuate.observability.ObservabilityContextCustomizerFactory,\ org.springframework.boot.test.autoconfigure.filter.TypeExcludeFiltersContextCustomizerFactory,\ org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizerFactory,\ +org.springframework.boot.test.autoconfigure.service.connection.ServiceConnectionContextCustomizerFactory,\ org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory # Test Execution Listeners @@ -15,4 +16,21 @@ org.springframework.boot.test.autoconfigure.web.servlet.WebDriverTestExecutionLi org.springframework.boot.test.autoconfigure.webservices.client.MockWebServiceServerTestExecutionListener org.springframework.test.context.ApplicationContextFailureProcessor=\ -org.springframework.boot.test.autoconfigure.ConditionReportApplicationContextFailureProcessor \ No newline at end of file +org.springframework.boot.test.autoconfigure.ConditionReportApplicationContextFailureProcessor + +# Connection Details Factories +org.springframework.boot.autoconfigure.service.connection.ConnectionDetailsFactory=\ +org.springframework.boot.test.autoconfigure.amqp.RabbitContainerConnectionDetailsFactory,\ +org.springframework.boot.test.autoconfigure.cassandra.CassandraContainerConnectionDetailsFactory,\ +org.springframework.boot.test.autoconfigure.couchbase.CouchbaseContainerConnectionDetailsFactory,\ +org.springframework.boot.test.autoconfigure.data.redis.RedisContainerConnectionDetailsFactory,\ +org.springframework.boot.test.autoconfigure.elasticsearch.ElasticsearchContainerConnectionDetailsFactory,\ +org.springframework.boot.test.autoconfigure.influx.InfluxDbContainerConnectionDetailsFactory,\ +org.springframework.boot.test.autoconfigure.jdbc.JdbcContainerConnectionDetailsFactory,\ +org.springframework.boot.test.autoconfigure.kafka.KafkaContainerConnectionDetailsFactory,\ +org.springframework.boot.test.autoconfigure.mongo.MongoContainerConnectionDetailsFactory,\ +org.springframework.boot.test.autoconfigure.neo4j.Neo4jContainerConnectionDetailsFactory,\ +org.springframework.boot.test.autoconfigure.r2dbc.MariaDbR2dbcContainerConnectionDetailsFactory,\ +org.springframework.boot.test.autoconfigure.r2dbc.MySqlR2dbcContainerConnectionDetailsFactory,\ +org.springframework.boot.test.autoconfigure.r2dbc.PostgresR2dbcContainerConnectionDetailsFactory,\ +org.springframework.boot.test.autoconfigure.r2dbc.SqlServerR2dbcContainerConnectionDetailsFactory diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/amqp/RabbitServiceConnectionTests.java b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/amqp/RabbitServiceConnectionTests.java new file mode 100644 index 0000000000..04f79f7902 --- /dev/null +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/amqp/RabbitServiceConnectionTests.java @@ -0,0 +1,91 @@ +/* + * Copyright 2012-2023 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 + * + * https://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.amqp; + +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; + +import org.awaitility.Awaitility; +import org.junit.jupiter.api.Test; +import org.testcontainers.containers.RabbitMQContainer; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +import org.springframework.amqp.rabbit.annotation.Queue; +import org.springframework.amqp.rabbit.annotation.RabbitListener; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.ImportAutoConfiguration; +import org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration; +import org.springframework.boot.test.autoconfigure.amqp.RabbitServiceConnectionTests.TestConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.testsupport.testcontainers.DockerImageNames; +import org.springframework.context.annotation.Bean; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link RabbitServiceConnection}. + * + * @author Moritz Halbritter + * @author Andy Wilkinson + * @author Phillip Webb + */ +@SpringBootTest(classes = TestConfiguration.class) +@Testcontainers(disabledWithoutDocker = true) +class RabbitServiceConnectionTests { + + @Container + @RabbitServiceConnection + static final RabbitMQContainer rabbit = new RabbitMQContainer(DockerImageNames.rabbit()); + + @Autowired + RabbitTemplate rabbitTemplate; + + @Autowired + TestListener listener; + + @Test + void connectionCanBeMadeToKafkaContainer() { + this.rabbitTemplate.convertAndSend("test", "message"); + Awaitility.waitAtMost(Duration.ofSeconds(30)) + .untilAsserted(() -> assertThat(this.listener.messages).containsExactly("message")); + } + + @ImportAutoConfiguration(RabbitAutoConfiguration.class) + static class TestConfiguration { + + @Bean + TestListener testListener() { + return new TestListener(); + } + + } + + static class TestListener { + + private final List messages = new ArrayList<>(); + + @RabbitListener(queuesToDeclare = @Queue("test")) + void processMessage(String message) { + this.messages.add(message); + } + + } + +} diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/cassandra/DataCassandraTestIntegrationTests.java b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/cassandra/DataCassandraTestIntegrationTests.java index 6dbfc4e185..77a26e33ab 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/cassandra/DataCassandraTestIntegrationTests.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/cassandra/DataCassandraTestIntegrationTests.java @@ -26,14 +26,13 @@ import org.testcontainers.junit.jupiter.Testcontainers; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.cassandra.CassandraServiceConnection; import org.springframework.boot.test.autoconfigure.data.redis.ExampleService; import org.springframework.boot.test.context.TestConfiguration; import org.springframework.boot.testsupport.testcontainers.CassandraContainer; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.data.cassandra.core.CassandraTemplate; -import org.springframework.test.context.DynamicPropertyRegistry; -import org.springframework.test.context.DynamicPropertySource; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; @@ -42,22 +41,20 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType; * Integration test for {@link DataCassandraTest @DataCassandraTest}. * * @author Artsiom Yudovin + * @author Moritz Halbritter + * @author Andy Wilkinson + * @author Phillip Webb */ -@DataCassandraTest(properties = { "spring.cassandra.local-datacenter=datacenter1", - "spring.cassandra.schema-action=create-if-not-exists", "spring.cassandra.connection.connect-timeout=60s", - "spring.cassandra.connection.init-query-timeout=60s", "spring.cassandra.request.timeout=60s" }) +@DataCassandraTest(properties = { "spring.cassandra.schema-action=create-if-not-exists", + "spring.cassandra.connection.connect-timeout=60s", "spring.cassandra.connection.init-query-timeout=60s", + "spring.cassandra.request.timeout=60s" }) @Testcontainers(disabledWithoutDocker = true) class DataCassandraTestIntegrationTests { @Container + @CassandraServiceConnection static final CassandraContainer cassandra = new CassandraContainer(); - @DynamicPropertySource - static void cassandraProperties(DynamicPropertyRegistry registry) { - registry.add("spring.cassandra.contact-points", - () -> cassandra.getHost() + ":" + cassandra.getFirstMappedPort()); - } - @Autowired private CassandraTemplate cassandraTemplate; diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/cassandra/DataCassandraTestWithIncludeFilterIntegrationTests.java b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/cassandra/DataCassandraTestWithIncludeFilterIntegrationTests.java index eaf7de7cab..17226788ee 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/cassandra/DataCassandraTestWithIncludeFilterIntegrationTests.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/cassandra/DataCassandraTestWithIncludeFilterIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2023 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. @@ -25,13 +25,12 @@ import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.cassandra.CassandraServiceConnection; import org.springframework.boot.test.context.TestConfiguration; import org.springframework.boot.testsupport.testcontainers.CassandraContainer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan.Filter; import org.springframework.stereotype.Service; -import org.springframework.test.context.DynamicPropertyRegistry; -import org.springframework.test.context.DynamicPropertySource; import static org.assertj.core.api.Assertions.assertThat; @@ -40,24 +39,21 @@ import static org.assertj.core.api.Assertions.assertThat; * {@link DataCassandraTest @DataCassandraTest}. * * @author Artsiom Yudovin + * @author Moritz Halbritter + * @author Andy Wilkinson + * @author Phillip Webb */ @DataCassandraTest(includeFilters = @Filter(Service.class), - properties = { "spring.cassandra.local-datacenter=datacenter1", - "spring.cassandra.schema-action=create-if-not-exists", + properties = { "spring.cassandra.schema-action=create-if-not-exists", "spring.cassandra.connection.connect-timeout=60s", "spring.cassandra.connection.init-query-timeout=60s", "spring.cassandra.request.timeout=60s" }) @Testcontainers(disabledWithoutDocker = true) class DataCassandraTestWithIncludeFilterIntegrationTests { @Container + @CassandraServiceConnection static final CassandraContainer cassandra = new CassandraContainer(); - @DynamicPropertySource - static void cassandraProperties(DynamicPropertyRegistry registry) { - registry.add("spring.cassandra.contact-points", - () -> cassandra.getHost() + ":" + cassandra.getFirstMappedPort()); - } - @Autowired private ExampleRepository exampleRepository; diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/couchbase/DataCouchbaseTestIntegrationTests.java b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/couchbase/DataCouchbaseTestIntegrationTests.java index 60619c0790..a217e793e4 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/couchbase/DataCouchbaseTestIntegrationTests.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/couchbase/DataCouchbaseTestIntegrationTests.java @@ -26,11 +26,10 @@ import org.testcontainers.junit.jupiter.Testcontainers; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.couchbase.CouchbaseServiceConnection; import org.springframework.boot.testsupport.testcontainers.DockerImageNames; import org.springframework.context.ApplicationContext; import org.springframework.data.couchbase.core.CouchbaseTemplate; -import org.springframework.test.context.DynamicPropertyRegistry; -import org.springframework.test.context.DynamicPropertySource; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; @@ -39,27 +38,24 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType; * Integration test for {@link DataCouchbaseTest @DataCouchbaseTest}. * * @author Eddú Meléndez + * @author Moritz Halbritter + * @author Andy Wilkinson + * @author Phillip Webb */ -@DataCouchbaseTest(properties = "spring.couchbase.env.timeouts.connect=2m") +@DataCouchbaseTest( + properties = { "spring.couchbase.env.timeouts.connect=2m", "spring.data.couchbase.bucket-name=cbbucket" }) @Testcontainers(disabledWithoutDocker = true) class DataCouchbaseTestIntegrationTests { private static final String BUCKET_NAME = "cbbucket"; @Container + @CouchbaseServiceConnection static final CouchbaseContainer couchbase = new CouchbaseContainer(DockerImageNames.couchbase()) .withStartupAttempts(5) .withStartupTimeout(Duration.ofMinutes(10)) .withBucket(new BucketDefinition(BUCKET_NAME)); - @DynamicPropertySource - static void couchbaseProperties(DynamicPropertyRegistry registry) { - registry.add("spring.couchbase.connection-string", couchbase::getConnectionString); - registry.add("spring.couchbase.username", couchbase::getUsername); - registry.add("spring.couchbase.password", couchbase::getPassword); - registry.add("spring.data.couchbase.bucket-name", () -> BUCKET_NAME); - } - @Autowired private CouchbaseTemplate couchbaseTemplate; diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/couchbase/DataCouchbaseTestReactiveIntegrationTests.java b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/couchbase/DataCouchbaseTestReactiveIntegrationTests.java index 27be882a85..239b5cd40b 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/couchbase/DataCouchbaseTestReactiveIntegrationTests.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/couchbase/DataCouchbaseTestReactiveIntegrationTests.java @@ -25,10 +25,9 @@ import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.couchbase.CouchbaseServiceConnection; import org.springframework.boot.testsupport.testcontainers.DockerImageNames; import org.springframework.data.couchbase.core.ReactiveCouchbaseTemplate; -import org.springframework.test.context.DynamicPropertyRegistry; -import org.springframework.test.context.DynamicPropertySource; import static org.assertj.core.api.Assertions.assertThat; @@ -37,27 +36,23 @@ import static org.assertj.core.api.Assertions.assertThat; * repositories. * * @author Eddú Meléndez + * @author Moritz Halbritter + * @author Andy Wilkinson + * @author Phillip Webb */ -@DataCouchbaseTest +@DataCouchbaseTest(properties = "spring.data.couchbase.bucket-name=cbbucket") @Testcontainers(disabledWithoutDocker = true) class DataCouchbaseTestReactiveIntegrationTests { private static final String BUCKET_NAME = "cbbucket"; @Container + @CouchbaseServiceConnection static final CouchbaseContainer couchbase = new CouchbaseContainer(DockerImageNames.couchbase()) .withStartupAttempts(5) .withStartupTimeout(Duration.ofMinutes(10)) .withBucket(new BucketDefinition(BUCKET_NAME)); - @DynamicPropertySource - static void couchbaseProperties(DynamicPropertyRegistry registry) { - registry.add("spring.couchbase.connection-string", couchbase::getConnectionString); - registry.add("spring.couchbase.username", couchbase::getUsername); - registry.add("spring.couchbase.password", couchbase::getPassword); - registry.add("spring.data.couchbase.bucket-name", () -> BUCKET_NAME); - } - @Autowired private ReactiveCouchbaseTemplate couchbaseTemplate; diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/couchbase/DataCouchbaseTestWithIncludeFilterIntegrationTests.java b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/couchbase/DataCouchbaseTestWithIncludeFilterIntegrationTests.java index 1e779b709b..816d8100ae 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/couchbase/DataCouchbaseTestWithIncludeFilterIntegrationTests.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/couchbase/DataCouchbaseTestWithIncludeFilterIntegrationTests.java @@ -25,11 +25,10 @@ import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.couchbase.CouchbaseServiceConnection; import org.springframework.boot.testsupport.testcontainers.DockerImageNames; import org.springframework.context.annotation.ComponentScan.Filter; import org.springframework.stereotype.Service; -import org.springframework.test.context.DynamicPropertyRegistry; -import org.springframework.test.context.DynamicPropertySource; import static org.assertj.core.api.Assertions.assertThat; @@ -38,27 +37,23 @@ import static org.assertj.core.api.Assertions.assertThat; * {@link DataCouchbaseTest @DataCouchbaseTest}. * * @author Eddú Meléndez + * @author Moritz Halbritter + * @author Andy Wilkinson + * @author Phillip Webb */ -@DataCouchbaseTest(includeFilters = @Filter(Service.class)) +@DataCouchbaseTest(includeFilters = @Filter(Service.class), properties = "spring.data.couchbase.bucket-name=cbbucket") @Testcontainers(disabledWithoutDocker = true) class DataCouchbaseTestWithIncludeFilterIntegrationTests { private static final String BUCKET_NAME = "cbbucket"; @Container + @CouchbaseServiceConnection static final CouchbaseContainer couchbase = new CouchbaseContainer(DockerImageNames.couchbase()) .withStartupAttempts(5) .withStartupTimeout(Duration.ofMinutes(10)) .withBucket(new BucketDefinition(BUCKET_NAME)); - @DynamicPropertySource - static void couchbaseProperties(DynamicPropertyRegistry registry) { - registry.add("spring.couchbase.connection-string", couchbase::getConnectionString); - registry.add("spring.couchbase.username", couchbase::getUsername); - registry.add("spring.couchbase.password", couchbase::getPassword); - registry.add("spring.data.couchbase.bucket-name", () -> BUCKET_NAME); - } - @Autowired private ExampleRepository exampleRepository; diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/elasticsearch/DataElasticsearchTestIntegrationTests.java b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/elasticsearch/DataElasticsearchTestIntegrationTests.java index 6249aa0116..992fd0be29 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/elasticsearch/DataElasticsearchTestIntegrationTests.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/elasticsearch/DataElasticsearchTestIntegrationTests.java @@ -26,11 +26,10 @@ import org.testcontainers.junit.jupiter.Testcontainers; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.elasticsearch.ElasticsearchServiceConnection; import org.springframework.boot.testsupport.testcontainers.DockerImageNames; import org.springframework.context.ApplicationContext; import org.springframework.data.elasticsearch.client.elc.ElasticsearchTemplate; -import org.springframework.test.context.DynamicPropertyRegistry; -import org.springframework.test.context.DynamicPropertySource; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; @@ -39,21 +38,20 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType; * Sample test for {@link DataElasticsearchTest @DataElasticsearchTest} * * @author Eddú Meléndez + * @author Moritz Halbritter + * @author Andy Wilkinson + * @author Phillip Webb */ @DataElasticsearchTest @Testcontainers(disabledWithoutDocker = true) class DataElasticsearchTestIntegrationTests { @Container + @ElasticsearchServiceConnection static final ElasticsearchContainer elasticsearch = new ElasticsearchContainer(DockerImageNames.elasticsearch()) .withStartupAttempts(5) .withStartupTimeout(Duration.ofMinutes(10)); - @DynamicPropertySource - static void elasticsearchProperties(DynamicPropertyRegistry registry) { - registry.add("spring.elasticsearch.uris", elasticsearch::getHttpHostAddress); - } - @Autowired private ElasticsearchTemplate elasticsearchTemplate; diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/elasticsearch/DataElasticsearchTestPropertiesIntegrationTests.java b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/elasticsearch/DataElasticsearchTestPropertiesIntegrationTests.java index 9a15e17e98..16cbdadae0 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/elasticsearch/DataElasticsearchTestPropertiesIntegrationTests.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/elasticsearch/DataElasticsearchTestPropertiesIntegrationTests.java @@ -24,10 +24,9 @@ import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.elasticsearch.ElasticsearchServiceConnection; import org.springframework.boot.testsupport.testcontainers.DockerImageNames; import org.springframework.core.env.Environment; -import org.springframework.test.context.DynamicPropertyRegistry; -import org.springframework.test.context.DynamicPropertySource; import static org.assertj.core.api.Assertions.assertThat; @@ -36,21 +35,20 @@ import static org.assertj.core.api.Assertions.assertThat; * {@link DataElasticsearchTest @DataElasticsearchTest}. * * @author Eddú Meléndez + * @author Moritz Halbritter + * @author Andy Wilkinson + * @author Phillip Webb */ @DataElasticsearchTest(properties = "spring.profiles.active=test") @Testcontainers(disabledWithoutDocker = true) class DataElasticsearchTestPropertiesIntegrationTests { @Container + @ElasticsearchServiceConnection static final ElasticsearchContainer elasticsearch = new ElasticsearchContainer(DockerImageNames.elasticsearch()) .withStartupAttempts(5) .withStartupTimeout(Duration.ofMinutes(10)); - @DynamicPropertySource - static void elasticsearchProperties(DynamicPropertyRegistry registry) { - registry.add("spring.elasticsearch.uris", elasticsearch::getHttpHostAddress); - } - @Autowired private Environment environment; diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/elasticsearch/DataElasticsearchTestReactiveIntegrationTests.java b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/elasticsearch/DataElasticsearchTestReactiveIntegrationTests.java index 19d28223bb..3b09058259 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/elasticsearch/DataElasticsearchTestReactiveIntegrationTests.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/elasticsearch/DataElasticsearchTestReactiveIntegrationTests.java @@ -24,10 +24,9 @@ import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.elasticsearch.ElasticsearchServiceConnection; import org.springframework.boot.testsupport.testcontainers.DockerImageNames; import org.springframework.data.elasticsearch.client.elc.ReactiveElasticsearchTemplate; -import org.springframework.test.context.DynamicPropertyRegistry; -import org.springframework.test.context.DynamicPropertySource; import static org.assertj.core.api.Assertions.assertThat; @@ -36,21 +35,20 @@ import static org.assertj.core.api.Assertions.assertThat; * repositories. * * @author Eddú Meléndez + * @author Moritz Halbritter + * @author Andy Wilkinson + * @author Phillip Webb */ @DataElasticsearchTest @Testcontainers(disabledWithoutDocker = true) class DataElasticsearchTestReactiveIntegrationTests { @Container + @ElasticsearchServiceConnection static final ElasticsearchContainer elasticsearch = new ElasticsearchContainer(DockerImageNames.elasticsearch()) .withStartupAttempts(5) .withStartupTimeout(Duration.ofMinutes(10)); - @DynamicPropertySource - static void elasticsearchProperties(DynamicPropertyRegistry registry) { - registry.add("spring.elasticsearch.uris", elasticsearch::getHttpHostAddress); - } - @Autowired private ReactiveElasticsearchTemplate elasticsearchTemplate; diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/elasticsearch/DataElasticsearchTestWithIncludeFilterIntegrationTests.java b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/elasticsearch/DataElasticsearchTestWithIncludeFilterIntegrationTests.java index 18ebfef088..f9f98ed922 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/elasticsearch/DataElasticsearchTestWithIncludeFilterIntegrationTests.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/elasticsearch/DataElasticsearchTestWithIncludeFilterIntegrationTests.java @@ -25,11 +25,10 @@ import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.elasticsearch.ElasticsearchServiceConnection; import org.springframework.boot.testsupport.testcontainers.DockerImageNames; import org.springframework.context.annotation.ComponentScan.Filter; import org.springframework.stereotype.Service; -import org.springframework.test.context.DynamicPropertyRegistry; -import org.springframework.test.context.DynamicPropertySource; import static org.assertj.core.api.Assertions.assertThat; @@ -38,21 +37,20 @@ import static org.assertj.core.api.Assertions.assertThat; * {@link DataElasticsearchTest @DataElasticsearchTest}. * * @author Eddú Meléndez + * @author Moritz Halbritter + * @author Andy Wilkinson + * @author Phillip Webb */ @DataElasticsearchTest(includeFilters = @Filter(Service.class)) @Testcontainers(disabledWithoutDocker = true) class DataElasticsearchTestWithIncludeFilterIntegrationTests { @Container + @ElasticsearchServiceConnection static final ElasticsearchContainer elasticsearch = new ElasticsearchContainer(DockerImageNames.elasticsearch()) .withStartupAttempts(5) .withStartupTimeout(Duration.ofMinutes(10)); - @DynamicPropertySource - static void elasticsearchProperties(DynamicPropertyRegistry registry) { - registry.add("spring.elasticsearch.uris", elasticsearch::getHttpHostAddress); - } - @Autowired private ExampleRepository exampleRepository; diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/mongo/DataMongoTestIntegrationTests.java b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/mongo/DataMongoTestIntegrationTests.java index 132eb9aa33..45ad102224 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/mongo/DataMongoTestIntegrationTests.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/mongo/DataMongoTestIntegrationTests.java @@ -25,11 +25,10 @@ import org.testcontainers.junit.jupiter.Testcontainers; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.mongo.MongoServiceConnection; import org.springframework.boot.testsupport.testcontainers.DockerImageNames; import org.springframework.context.ApplicationContext; import org.springframework.data.mongodb.core.MongoTemplate; -import org.springframework.test.context.DynamicPropertyRegistry; -import org.springframework.test.context.DynamicPropertySource; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; @@ -38,12 +37,16 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType; * Sample test for {@link DataMongoTest @DataMongoTest} * * @author Michael Simons + * @author Moritz Halbritter + * @author Andy Wilkinson + * @author Phillip Webb */ @DataMongoTest @Testcontainers(disabledWithoutDocker = true) class DataMongoTestIntegrationTests { @Container + @MongoServiceConnection static final MongoDBContainer mongoDB = new MongoDBContainer(DockerImageNames.mongo()).withStartupAttempts(5) .withStartupTimeout(Duration.ofMinutes(5)); @@ -71,9 +74,4 @@ class DataMongoTestIntegrationTests { .isThrownBy(() -> this.applicationContext.getBean(ExampleService.class)); } - @DynamicPropertySource - static void mongoProperties(DynamicPropertyRegistry registry) { - registry.add("spring.data.mongodb.uri", mongoDB::getReplicaSetUrl); - } - } diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/mongo/DataMongoTestReactiveIntegrationTests.java b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/mongo/DataMongoTestReactiveIntegrationTests.java index 4259b537f7..8e485d6cf1 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/mongo/DataMongoTestReactiveIntegrationTests.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/mongo/DataMongoTestReactiveIntegrationTests.java @@ -24,10 +24,9 @@ import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.mongo.MongoServiceConnection; import org.springframework.boot.testsupport.testcontainers.DockerImageNames; import org.springframework.data.mongodb.core.ReactiveMongoTemplate; -import org.springframework.test.context.DynamicPropertyRegistry; -import org.springframework.test.context.DynamicPropertySource; import static org.assertj.core.api.Assertions.assertThat; @@ -35,12 +34,16 @@ import static org.assertj.core.api.Assertions.assertThat; * Sample tests for {@link DataMongoTest @DataMongoTest} using reactive repositories. * * @author Stephane Nicoll + * @author Moritz Halbritter + * @author Andy Wilkinson + * @author Phillip Webb */ @DataMongoTest @Testcontainers(disabledWithoutDocker = true) class DataMongoTestReactiveIntegrationTests { @Container + @MongoServiceConnection static final MongoDBContainer mongoDB = new MongoDBContainer(DockerImageNames.mongo()).withStartupAttempts(5) .withStartupTimeout(Duration.ofMinutes(5)); @@ -50,11 +53,6 @@ class DataMongoTestReactiveIntegrationTests { @Autowired private ExampleReactiveRepository exampleRepository; - @DynamicPropertySource - static void mongoProperties(DynamicPropertyRegistry registry) { - registry.add("spring.data.mongodb.uri", mongoDB::getReplicaSetUrl); - } - @Test void testRepository() { ExampleDocument exampleDocument = new ExampleDocument(); diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/mongo/DataMongoTestWithIncludeFilterIntegrationTests.java b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/mongo/DataMongoTestWithIncludeFilterIntegrationTests.java index d2b99dfaf9..42b0ebdb75 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/mongo/DataMongoTestWithIncludeFilterIntegrationTests.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/mongo/DataMongoTestWithIncludeFilterIntegrationTests.java @@ -24,11 +24,10 @@ import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.mongo.MongoServiceConnection; import org.springframework.boot.testsupport.testcontainers.DockerImageNames; import org.springframework.context.annotation.ComponentScan.Filter; import org.springframework.stereotype.Service; -import org.springframework.test.context.DynamicPropertyRegistry; -import org.springframework.test.context.DynamicPropertySource; import static org.assertj.core.api.Assertions.assertThat; @@ -36,23 +35,22 @@ import static org.assertj.core.api.Assertions.assertThat; * Integration test with custom include filter for {@link DataMongoTest @DataMongoTest}. * * @author Michael Simons + * @author Moritz Halbritter + * @author Andy Wilkinson + * @author Phillip Webb */ @DataMongoTest(includeFilters = @Filter(Service.class)) @Testcontainers(disabledWithoutDocker = true) class DataMongoTestWithIncludeFilterIntegrationTests { @Container + @MongoServiceConnection static final MongoDBContainer mongoDB = new MongoDBContainer(DockerImageNames.mongo()).withStartupAttempts(5) .withStartupTimeout(Duration.ofMinutes(5)); @Autowired private ExampleService service; - @DynamicPropertySource - static void mongoProperties(DynamicPropertyRegistry registry) { - registry.add("spring.data.mongodb.uri", mongoDB::getReplicaSetUrl); - } - @Test void testService() { assertThat(this.service.hasCollection("foobar")).isFalse(); diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/mongo/TransactionalDataMongoTestIntegrationTests.java b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/mongo/TransactionalDataMongoTestIntegrationTests.java index d2ede5f559..848f7bd531 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/mongo/TransactionalDataMongoTestIntegrationTests.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/mongo/TransactionalDataMongoTestIntegrationTests.java @@ -25,14 +25,13 @@ import org.testcontainers.junit.jupiter.Testcontainers; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.mongo.MongoServiceConnection; import org.springframework.boot.test.context.TestConfiguration; import org.springframework.boot.testsupport.testcontainers.DockerImageNames; import org.springframework.context.annotation.Bean; import org.springframework.data.mongodb.MongoDatabaseFactory; import org.springframework.data.mongodb.MongoTransactionManager; import org.springframework.data.mongodb.core.MongoTemplate; -import org.springframework.test.context.DynamicPropertyRegistry; -import org.springframework.test.context.DynamicPropertySource; import org.springframework.transaction.annotation.Transactional; import static org.assertj.core.api.Assertions.assertThat; @@ -41,6 +40,8 @@ import static org.assertj.core.api.Assertions.assertThat; * Tests for using {@link DataMongoTest @DataMongoTest} with transactions. * * @author Andy Wilkinson + * @author Moritz Halbritter + * @author Phillip Webb */ @DataMongoTest @Transactional @@ -48,6 +49,7 @@ import static org.assertj.core.api.Assertions.assertThat; class TransactionalDataMongoTestIntegrationTests { @Container + @MongoServiceConnection static final MongoDBContainer mongoDB = new MongoDBContainer(DockerImageNames.mongo()).withStartupAttempts(5) .withStartupTimeout(Duration.ofMinutes(5)); @@ -62,11 +64,6 @@ class TransactionalDataMongoTestIntegrationTests { assertThat(exampleDocument.getId()).isNotNull(); } - @DynamicPropertySource - static void mongoProperties(DynamicPropertyRegistry registry) { - registry.add("spring.data.mongodb.uri", mongoDB::getReplicaSetUrl); - } - @TestConfiguration(proxyBeanMethods = false) static class TransactionManagerConfiguration { 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 72b13abbfd..0a746a094c 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 @@ -25,11 +25,10 @@ import org.testcontainers.junit.jupiter.Testcontainers; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.neo4j.Neo4jServiceConnection; import org.springframework.boot.testsupport.testcontainers.DockerImageNames; import org.springframework.context.ApplicationContext; import org.springframework.data.neo4j.core.Neo4jTemplate; -import org.springframework.test.context.DynamicPropertyRegistry; -import org.springframework.test.context.DynamicPropertySource; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; @@ -40,21 +39,19 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType; * @author Eddú Meléndez * @author Stephane Nicoll * @author Michael Simons + * @author Moritz Halbritter + * @author Andy Wilkinson + * @author Phillip Webb */ @DataNeo4jTest @Testcontainers(disabledWithoutDocker = true) class DataNeo4jTestIntegrationTests { @Container - static final Neo4jContainer neo4j = new Neo4jContainer<>(DockerImageNames.neo4j()).withoutAuthentication() - .withStartupAttempts(5) + @Neo4jServiceConnection + static final Neo4jContainer neo4j = new Neo4jContainer<>(DockerImageNames.neo4j()).withStartupAttempts(5) .withStartupTimeout(Duration.ofMinutes(10)); - @DynamicPropertySource - static void neo4jProperties(DynamicPropertyRegistry registry) { - registry.add("spring.neo4j.uri", neo4j::getBoltUrl); - } - @Autowired private Neo4jTemplate neo4jTemplate; diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/neo4j/DataNeo4jTestPropertiesIntegrationTests.java b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/neo4j/DataNeo4jTestPropertiesIntegrationTests.java index 37413c4434..8e51dab628 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/neo4j/DataNeo4jTestPropertiesIntegrationTests.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/neo4j/DataNeo4jTestPropertiesIntegrationTests.java @@ -24,10 +24,9 @@ import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.neo4j.Neo4jServiceConnection; import org.springframework.boot.testsupport.testcontainers.DockerImageNames; import org.springframework.core.env.Environment; -import org.springframework.test.context.DynamicPropertyRegistry; -import org.springframework.test.context.DynamicPropertySource; import static org.assertj.core.api.Assertions.assertThat; @@ -36,21 +35,20 @@ import static org.assertj.core.api.Assertions.assertThat; * {@link DataNeo4jTest @DataNeo4jTest}. * * @author Artsiom Yudovin + * @author Moritz Halbritter + * @author Andy Wilkinson + * @author Phillip Webb */ @Testcontainers(disabledWithoutDocker = true) @DataNeo4jTest(properties = "spring.profiles.active=test") class DataNeo4jTestPropertiesIntegrationTests { @Container + @Neo4jServiceConnection static final Neo4jContainer neo4j = new Neo4jContainer<>(DockerImageNames.neo4j()).withoutAuthentication() .withStartupAttempts(5) .withStartupTimeout(Duration.ofMinutes(10)); - @DynamicPropertySource - static void neo4jProperties(DynamicPropertyRegistry registry) { - registry.add("spring.neo4j.uri", neo4j::getBoltUrl); - } - @Autowired private Environment environment; diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/neo4j/DataNeo4jTestReactiveIntegrationTests.java b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/neo4j/DataNeo4jTestReactiveIntegrationTests.java index 64ba554c95..56ffd5c44e 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/neo4j/DataNeo4jTestReactiveIntegrationTests.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/neo4j/DataNeo4jTestReactiveIntegrationTests.java @@ -28,6 +28,7 @@ import reactor.test.StepVerifier; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.neo4j.Neo4jServiceConnection; import org.springframework.boot.test.context.TestConfiguration; import org.springframework.boot.testsupport.testcontainers.DockerImageNames; import org.springframework.context.ApplicationContext; @@ -35,8 +36,6 @@ import org.springframework.context.annotation.Bean; import org.springframework.data.neo4j.core.ReactiveDatabaseSelectionProvider; import org.springframework.data.neo4j.core.ReactiveNeo4jTemplate; import org.springframework.data.neo4j.core.transaction.ReactiveNeo4jTransactionManager; -import org.springframework.test.context.DynamicPropertyRegistry; -import org.springframework.test.context.DynamicPropertySource; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; @@ -47,7 +46,9 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType; * * @author Michael J. Simons * @author Scott Frederick - * @since 2.4.0 + * @author Moritz Halbritter + * @author Andy Wilkinson + * @author Phillip Webb */ @DataNeo4jTest @Transactional(propagation = Propagation.NOT_SUPPORTED) @@ -55,15 +56,11 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType; class DataNeo4jTestReactiveIntegrationTests { @Container + @Neo4jServiceConnection static final Neo4jContainer neo4j = new Neo4jContainer<>(DockerImageNames.neo4j()).withoutAuthentication() .withStartupAttempts(5) .withStartupTimeout(Duration.ofMinutes(10)); - @DynamicPropertySource - static void neo4jProperties(DynamicPropertyRegistry registry) { - registry.add("spring.neo4j.uri", neo4j::getBoltUrl); - } - @Autowired private ReactiveNeo4jTemplate neo4jTemplate; 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 85b46d43ae..346537665d 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 @@ -24,11 +24,10 @@ import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.neo4j.Neo4jServiceConnection; import org.springframework.boot.testsupport.testcontainers.DockerImageNames; import org.springframework.context.annotation.ComponentScan.Filter; import org.springframework.stereotype.Service; -import org.springframework.test.context.DynamicPropertyRegistry; -import org.springframework.test.context.DynamicPropertySource; import static org.assertj.core.api.Assertions.assertThat; @@ -37,21 +36,20 @@ import static org.assertj.core.api.Assertions.assertThat; * * @author Eddú Meléndez * @author Michael Simons + * @author Moritz Halbritter + * @author Andy Wilkinson + * @author Phillip Webb */ @Testcontainers(disabledWithoutDocker = true) @DataNeo4jTest(includeFilters = @Filter(Service.class)) class DataNeo4jTestWithIncludeFilterIntegrationTests { @Container + @Neo4jServiceConnection static final Neo4jContainer neo4j = new Neo4jContainer<>(DockerImageNames.neo4j()).withoutAuthentication() .withStartupAttempts(5) .withStartupTimeout(Duration.ofMinutes(10)); - @DynamicPropertySource - static void neo4jProperties(DynamicPropertyRegistry registry) { - registry.add("spring.neo4j.uri", neo4j::getBoltUrl); - } - @Autowired private ExampleService service; 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 23debe499b..d20a861982 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 @@ -27,10 +27,7 @@ import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.testsupport.testcontainers.RedisContainer; import org.springframework.context.ApplicationContext; -import org.springframework.data.redis.connection.RedisConnection; import org.springframework.data.redis.core.RedisOperations; -import org.springframework.test.context.DynamicPropertyRegistry; -import org.springframework.test.context.DynamicPropertySource; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; @@ -39,6 +36,9 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType; * Integration test for {@link DataRedisTest @DataRedisTest}. * * @author Jayaram Pradhan + * @author Moritz Halbritter + * @author Andy Wilkinson + * @author Phillip Webb */ @Testcontainers(disabledWithoutDocker = true) @DataRedisTest @@ -47,6 +47,7 @@ class DataRedisTestIntegrationTests { private static final Charset CHARSET = StandardCharsets.UTF_8; @Container + @RedisServiceConnection static RedisContainer redis = new RedisContainer(); @Autowired @@ -58,12 +59,6 @@ class DataRedisTestIntegrationTests { @Autowired private ApplicationContext applicationContext; - @DynamicPropertySource - static void redisProperties(DynamicPropertyRegistry registry) { - registry.add("spring.data.redis.host", redis::getHost); - registry.add("spring.data.redis.port", redis::getFirstMappedPort); - } - @Test void testRepository() { PersonHash personHash = new PersonHash(); @@ -71,8 +66,10 @@ class DataRedisTestIntegrationTests { assertThat(personHash.getId()).isNull(); PersonHash savedEntity = this.exampleRepository.save(personHash); assertThat(savedEntity.getId()).isNotNull(); - assertThat(this.operations.execute((RedisConnection connection) -> connection.keyCommands() - .exists(("persons:" + savedEntity.getId()).getBytes(CHARSET)))).isTrue(); + assertThat(this.operations + .execute((org.springframework.data.redis.connection.RedisConnection connection) -> connection.keyCommands() + .exists(("persons:" + savedEntity.getId()).getBytes(CHARSET)))) + .isTrue(); this.exampleRepository.deleteAll(); } diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/redis/DataRedisTestPropertiesIntegrationTests.java b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/redis/DataRedisTestPropertiesIntegrationTests.java index 11968e90e5..03f8f81359 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/redis/DataRedisTestPropertiesIntegrationTests.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/redis/DataRedisTestPropertiesIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2023 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. @@ -23,8 +23,6 @@ import org.testcontainers.junit.jupiter.Testcontainers; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.testsupport.testcontainers.RedisContainer; import org.springframework.core.env.Environment; -import org.springframework.test.context.DynamicPropertyRegistry; -import org.springframework.test.context.DynamicPropertySource; import static org.assertj.core.api.Assertions.assertThat; @@ -33,23 +31,21 @@ import static org.assertj.core.api.Assertions.assertThat; * {@link DataRedisTest @DataRedisTest}. * * @author Artsiom Yudovin + * @author Moritz Halbritter + * @author Andy Wilkinson + * @author Phillip Webb */ @Testcontainers(disabledWithoutDocker = true) @DataRedisTest(properties = "spring.profiles.active=test") class DataRedisTestPropertiesIntegrationTests { @Container + @RedisServiceConnection static final RedisContainer redis = new RedisContainer(); @Autowired private Environment environment; - @DynamicPropertySource - static void redisProperties(DynamicPropertyRegistry registry) { - registry.add("spring.data.redis.host", redis::getHost); - registry.add("spring.data.redis.port", redis::getFirstMappedPort); - } - @Test void environmentWithNewProfile() { assertThat(this.environment.getActiveProfiles()).containsExactly("test"); diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/redis/DataRedisTestReactiveIntegrationTests.java b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/redis/DataRedisTestReactiveIntegrationTests.java index 9b80ed135d..13c6668d23 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/redis/DataRedisTestReactiveIntegrationTests.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/data/redis/DataRedisTestReactiveIntegrationTests.java @@ -28,8 +28,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.testsupport.testcontainers.RedisContainer; import org.springframework.context.ApplicationContext; import org.springframework.data.redis.core.ReactiveRedisOperations; -import org.springframework.test.context.DynamicPropertyRegistry; -import org.springframework.test.context.DynamicPropertySource; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; @@ -37,12 +35,16 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType; * Integration test for {@link DataRedisTest @DataRedisTest} using reactive operations. * * @author Stephane Nicoll + * @author Moritz Halbritter + * @author Andy Wilkinson + * @author Phillip Webb */ @Testcontainers(disabledWithoutDocker = true) @DataRedisTest class DataRedisTestReactiveIntegrationTests { @Container + @RedisServiceConnection static RedisContainer redis = new RedisContainer(); @Autowired @@ -51,12 +53,6 @@ class DataRedisTestReactiveIntegrationTests { @Autowired private ApplicationContext applicationContext; - @DynamicPropertySource - static void redisProperties(DynamicPropertyRegistry registry) { - registry.add("spring.data.redis.host", redis::getHost); - registry.add("spring.data.redis.port", redis::getFirstMappedPort); - } - @Test void testRepository() { String id = UUID.randomUUID().toString(); 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 ac64c3ccca..edf78b9dda 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 @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2023 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. @@ -24,8 +24,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.testsupport.testcontainers.RedisContainer; import org.springframework.context.annotation.ComponentScan.Filter; import org.springframework.stereotype.Service; -import org.springframework.test.context.DynamicPropertyRegistry; -import org.springframework.test.context.DynamicPropertySource; import static org.assertj.core.api.Assertions.assertThat; @@ -33,12 +31,16 @@ import static org.assertj.core.api.Assertions.assertThat; * Integration test with custom include filter for {@link DataRedisTest @DataRedisTest}. * * @author Jayaram Pradhan + * @author Moritz Halbritter + * @author Andy Wilkinson + * @author Phillip Webb */ @Testcontainers(disabledWithoutDocker = true) @DataRedisTest(includeFilters = @Filter(Service.class)) class DataRedisTestWithIncludeFilterIntegrationTests { @Container + @RedisServiceConnection static final RedisContainer redis = new RedisContainer(); @Autowired @@ -47,12 +49,6 @@ class DataRedisTestWithIncludeFilterIntegrationTests { @Autowired private ExampleService service; - @DynamicPropertySource - static void redisProperties(DynamicPropertyRegistry registry) { - registry.add("spring.data.redis.host", redis::getHost); - registry.add("spring.data.redis.port", redis::getFirstMappedPort); - } - @Test void testService() { PersonHash personHash = new PersonHash(); diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/influx/InfluxDbServiceConnectionTests.java b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/influx/InfluxDbServiceConnectionTests.java new file mode 100644 index 0000000000..b8a583a729 --- /dev/null +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/influx/InfluxDbServiceConnectionTests.java @@ -0,0 +1,62 @@ +/* + * Copyright 2012-2023 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 + * + * https://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.influx; + +import org.influxdb.InfluxDB; +import org.junit.jupiter.api.Test; +import org.testcontainers.containers.InfluxDBContainer; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.ImportAutoConfiguration; +import org.springframework.boot.autoconfigure.influx.InfluxDbAutoConfiguration; +import org.springframework.boot.test.autoconfigure.influx.InfluxDbServiceConnectionTests.TestConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.testsupport.testcontainers.DockerImageNames; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link InfluxDbServiceConnection}. + * + * @author Moritz Halbritter + * @author Andy Wilkinson + * @author Phillip Webb + */ +@SpringBootTest(classes = TestConfiguration.class) +@Testcontainers(disabledWithoutDocker = true) +class InfluxDbServiceConnectionTests { + + @Container + @InfluxDbServiceConnection + static final InfluxDBContainer influxDbService = new InfluxDBContainer<>(DockerImageNames.influxDb()); + + @Autowired + InfluxDB influxDb; + + @Test + void connectionCanBeMadeToInfluxDbContainer() { + assertThat(this.influxDb.version()).isEqualTo("v" + DockerImageNames.influxDb().getVersionPart()); + } + + @ImportAutoConfiguration(InfluxDbAutoConfiguration.class) + static class TestConfiguration { + + } + +} diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/kafka/KafkaServiceConnectionTests.java b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/kafka/KafkaServiceConnectionTests.java new file mode 100644 index 0000000000..bdab132886 --- /dev/null +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/kafka/KafkaServiceConnectionTests.java @@ -0,0 +1,92 @@ +/* + * Copyright 2012-2023 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 + * + * https://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.kafka; + +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; + +import org.awaitility.Awaitility; +import org.junit.jupiter.api.Test; +import org.testcontainers.containers.KafkaContainer; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.ImportAutoConfiguration; +import org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration; +import org.springframework.boot.test.autoconfigure.kafka.KafkaServiceConnectionTests.TestConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.testsupport.testcontainers.DockerImageNames; +import org.springframework.context.annotation.Bean; +import org.springframework.kafka.annotation.KafkaListener; +import org.springframework.kafka.core.KafkaTemplate; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link KafkaServiceConnection}. + * + * @author Moritz Halbritter + * @author Andy Wilkinson + * @author Phillip Webb + */ +@SpringBootTest(classes = TestConfiguration.class, + properties = { "spring.kafka.consumer.group-id=test-group", + "spring.kafka.consumer.auto-offset-reset=earliest" }) +@Testcontainers(disabledWithoutDocker = true) +class KafkaServiceConnectionTests { + + @Container + @KafkaServiceConnection + static final KafkaContainer kafka = new KafkaContainer(DockerImageNames.kafka()); + + @Autowired + KafkaTemplate kafkaTemplate; + + @Autowired + TestListener listener; + + @Test + void connectionCanBeMadeToKafkaContainer() { + this.kafkaTemplate.send("test-topic", "test-data"); + Awaitility.waitAtMost(Duration.ofSeconds(30)) + .untilAsserted(() -> assertThat(this.listener.messages).containsExactly("test-data")); + } + + @ImportAutoConfiguration(KafkaAutoConfiguration.class) + static class TestConfiguration { + + @Bean + TestListener testListener() { + return new TestListener(); + } + + } + + static class TestListener { + + private final List messages = new ArrayList<>(); + + @KafkaListener(topics = "test-topic") + void processMessage(String message) { + this.messages.add(message); + } + + } + +} diff --git a/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/testcontainers/DockerImageNames.java b/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/testcontainers/DockerImageNames.java index f03e983ad2..2c562ab5a0 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/testcontainers/DockerImageNames.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/testcontainers/DockerImageNames.java @@ -32,12 +32,18 @@ public final class DockerImageNames { private static final String ELASTICSEARCH_VERSION = "7.17.5"; + private static final String KAFKA_VERSION = "5.4.3"; + + private static final String INFLUX_DB_VERSION = "2.6.1"; + private static final String MONGO_VERSION = "4.0.23"; private static final String NEO4J_VERSION = "4.4.11"; private static final String POSTGRESQL_VERSION = "14.0"; + private static final String RABBIT_VERSION = "3.11-alpine"; + private static final String REDIS_VERSION = "4.0.14"; private static final String REGISTRY_VERSION = "2.7.1"; @@ -69,6 +75,22 @@ public final class DockerImageNames { return DockerImageName.parse("docker.elastic.co/elasticsearch/elasticsearch").withTag(ELASTICSEARCH_VERSION); } + /** + * Return a {@link DockerImageName} suitable for running InfluxDB. + * @return a docker image name for running Kafka + */ + public static DockerImageName influxDb() { + return DockerImageName.parse("influxdb").withTag(INFLUX_DB_VERSION); + } + + /** + * Return a {@link DockerImageName} suitable for running Kafka. + * @return a docker image name for running Kafka + */ + public static DockerImageName kafka() { + return DockerImageName.parse("confluentinc/cp-kafka").withTag(KAFKA_VERSION); + } + /** * Return a {@link DockerImageName} suitable for running Mongo. * @return a docker image name for running mongo @@ -93,6 +115,14 @@ public final class DockerImageNames { return DockerImageName.parse("postgres").withTag(POSTGRESQL_VERSION); } + /** + * Return a {@link DockerImageName} suitable for running RabbitMQ. + * @return a docker image name for running redis + */ + public static DockerImageName rabbit() { + return DockerImageName.parse("rabbitmq").withTag(RABBIT_VERSION); + } + /** * Return a {@link DockerImageName} suitable for running Redis. * @return a docker image name for running redis diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-cache/src/redisTest/java/smoketest/cache/SampleCacheApplicationRedisTests.java b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-cache/src/redisTest/java/smoketest/cache/SampleCacheApplicationRedisTests.java index aa9d2dd787..f24dd7338b 100644 --- a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-cache/src/redisTest/java/smoketest/cache/SampleCacheApplicationRedisTests.java +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-cache/src/redisTest/java/smoketest/cache/SampleCacheApplicationRedisTests.java @@ -21,12 +21,11 @@ import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.data.redis.RedisServiceConnection; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.testsupport.testcontainers.RedisContainer; import org.springframework.cache.Cache; import org.springframework.cache.CacheManager; -import org.springframework.test.context.DynamicPropertyRegistry; -import org.springframework.test.context.DynamicPropertySource; import static org.assertj.core.api.Assertions.assertThat; @@ -35,6 +34,7 @@ import static org.assertj.core.api.Assertions.assertThat; class SampleCacheApplicationRedisTests { @Container + @RedisServiceConnection private static final RedisContainer redis = new RedisContainer(); @Autowired @@ -43,11 +43,6 @@ class SampleCacheApplicationRedisTests { @Autowired private CountryRepository countryRepository; - @DynamicPropertySource - static void redisProperties(DynamicPropertyRegistry properties) { - properties.add("spring.data.redis.url", () -> "redis://" + redis.getHost() + ":" + redis.getFirstMappedPort()); - } - @Test void validateCache() { Cache countries = this.cacheManager.getCache("countries"); diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-data-r2dbc-flyway/build.gradle b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-data-r2dbc-flyway/build.gradle index 32ac145609..6c9ee2915c 100644 --- a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-data-r2dbc-flyway/build.gradle +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-data-r2dbc-flyway/build.gradle @@ -18,4 +18,5 @@ dependencies { testImplementation("io.projectreactor:reactor-test") testImplementation("org.testcontainers:junit-jupiter") testImplementation("org.testcontainers:postgresql") + testImplementation("org.testcontainers:r2dbc") } diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-data-r2dbc-flyway/src/test/java/smoketest/data/r2dbc/CityRepositoryTests.java b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-data-r2dbc-flyway/src/test/java/smoketest/data/r2dbc/CityRepositoryTests.java index 9c082c3f3b..843044372f 100644 --- a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-data-r2dbc-flyway/src/test/java/smoketest/data/r2dbc/CityRepositoryTests.java +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-data-r2dbc-flyway/src/test/java/smoketest/data/r2dbc/CityRepositoryTests.java @@ -24,35 +24,29 @@ import reactor.test.StepVerifier; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.data.r2dbc.DataR2dbcTest; +import org.springframework.boot.test.autoconfigure.jdbc.JdbcServiceConnection; +import org.springframework.boot.test.autoconfigure.r2dbc.R2dbcServiceConnection; import org.springframework.boot.testsupport.testcontainers.DockerImageNames; -import org.springframework.test.context.DynamicPropertyRegistry; -import org.springframework.test.context.DynamicPropertySource; import static org.assertj.core.api.Assertions.assertThat; /** * Tests for {@link CityRepository}. + * + * @author Moritz Halbritter + * @author Andy Wilkinson + * @author Phillip Webb */ @Testcontainers(disabledWithoutDocker = true) @DataR2dbcTest class CityRepositoryTests { @Container + @JdbcServiceConnection + @R2dbcServiceConnection static PostgreSQLContainer postgresql = new PostgreSQLContainer<>(DockerImageNames.postgresql()) .withDatabaseName("test_flyway"); - @DynamicPropertySource - static void postgresqlProperties(DynamicPropertyRegistry registry) { - registry.add("spring.r2dbc.url", CityRepositoryTests::r2dbcUrl); - registry.add("spring.r2dbc.username", postgresql::getUsername); - registry.add("spring.r2dbc.password", postgresql::getPassword); - - // configure flyway to use the same database - registry.add("spring.flyway.url", postgresql::getJdbcUrl); - registry.add("spring.flyway.user", postgresql::getUsername); - registry.add("spring.flyway.password", postgresql::getPassword); - } - @Autowired private CityRepository repository; @@ -63,9 +57,4 @@ class CityRepositoryTests { .verifyComplete(); } - private static String r2dbcUrl() { - return String.format("r2dbc:postgresql://%s:%s/%s", postgresql.getHost(), - postgresql.getMappedPort(PostgreSQLContainer.POSTGRESQL_PORT), postgresql.getDatabaseName()); - } - } diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-data-r2dbc-liquibase/build.gradle b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-data-r2dbc-liquibase/build.gradle index 19fb9b574c..90f19ec182 100644 --- a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-data-r2dbc-liquibase/build.gradle +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-data-r2dbc-liquibase/build.gradle @@ -21,4 +21,5 @@ dependencies { testImplementation("io.projectreactor:reactor-test") testImplementation("org.testcontainers:junit-jupiter") testImplementation("org.testcontainers:postgresql") + testImplementation("org.testcontainers:r2dbc") } diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-data-r2dbc-liquibase/src/test/java/smoketest/data/r2dbc/CityRepositoryTests.java b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-data-r2dbc-liquibase/src/test/java/smoketest/data/r2dbc/CityRepositoryTests.java index c0de6abc3f..8a3497f184 100644 --- a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-data-r2dbc-liquibase/src/test/java/smoketest/data/r2dbc/CityRepositoryTests.java +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-data-r2dbc-liquibase/src/test/java/smoketest/data/r2dbc/CityRepositoryTests.java @@ -24,35 +24,29 @@ import reactor.test.StepVerifier; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.data.r2dbc.DataR2dbcTest; +import org.springframework.boot.test.autoconfigure.jdbc.JdbcServiceConnection; +import org.springframework.boot.test.autoconfigure.r2dbc.R2dbcServiceConnection; import org.springframework.boot.testsupport.testcontainers.DockerImageNames; -import org.springframework.test.context.DynamicPropertyRegistry; -import org.springframework.test.context.DynamicPropertySource; import static org.assertj.core.api.Assertions.assertThat; /** * Tests for {@link CityRepository}. + * + * @author Moritz Halbritter + * @author Andy Wilkinson + * @author Phillip Webb */ @Testcontainers(disabledWithoutDocker = true) @DataR2dbcTest class CityRepositoryTests { @Container + @JdbcServiceConnection + @R2dbcServiceConnection static PostgreSQLContainer postgresql = new PostgreSQLContainer<>(DockerImageNames.postgresql()) .withDatabaseName("test_liquibase"); - @DynamicPropertySource - static void postgresqlProperties(DynamicPropertyRegistry registry) { - registry.add("spring.r2dbc.url", CityRepositoryTests::r2dbcUrl); - registry.add("spring.r2dbc.username", postgresql::getUsername); - registry.add("spring.r2dbc.password", postgresql::getPassword); - - // configure liquibase to use the same database - registry.add("spring.liquibase.url", postgresql::getJdbcUrl); - registry.add("spring.liquibase.user", postgresql::getUsername); - registry.add("spring.liquibase.password", postgresql::getPassword); - } - @Autowired private CityRepository repository; @@ -63,9 +57,4 @@ class CityRepositoryTests { .verifyComplete(); } - private static String r2dbcUrl() { - return String.format("r2dbc:postgresql://%s:%s/%s", postgresql.getHost(), - postgresql.getMappedPort(PostgreSQLContainer.POSTGRESQL_PORT), postgresql.getDatabaseName()); - } - } diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-session-mongo/src/test/java/smoketest/session/mongodb/SampleSessionMongoApplicationTests.java b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-session-mongo/src/test/java/smoketest/session/mongodb/SampleSessionMongoApplicationTests.java index a268333c24..6288527538 100644 --- a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-session-mongo/src/test/java/smoketest/session/mongodb/SampleSessionMongoApplicationTests.java +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-session-mongo/src/test/java/smoketest/session/mongodb/SampleSessionMongoApplicationTests.java @@ -28,6 +28,7 @@ import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.mongo.MongoServiceConnection; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.client.TestRestTemplate; import org.springframework.boot.test.web.server.LocalServerPort; @@ -40,8 +41,6 @@ import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.RequestEntity; import org.springframework.http.ResponseEntity; -import org.springframework.test.context.DynamicPropertyRegistry; -import org.springframework.test.context.DynamicPropertySource; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; @@ -51,6 +50,9 @@ import static org.assertj.core.api.Assertions.assertThat; * Tests for {@link SampleSessionMongoApplication}. * * @author Angel L. Villalain + * @author Moritz Halbritter + * @author Andy Wilkinson + * @author Phillip Webb */ @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @Testcontainers(disabledWithoutDocker = true) @@ -63,14 +65,10 @@ class SampleSessionMongoApplicationTests { private int port; @Container + @MongoServiceConnection static MongoDBContainer mongo = new MongoDBContainer(DockerImageNames.mongo()).withStartupAttempts(3) .withStartupTimeout(Duration.ofMinutes(2)); - @DynamicPropertySource - static void applicationProperties(DynamicPropertyRegistry registry) { - registry.add("spring.data.mongodb.uri", mongo::getReplicaSetUrl); - } - @Test @SuppressWarnings("unchecked") void sessionsEndpointShouldReturnUserSessions() { diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-session-redis/src/test/java/smoketest/session/redis/SampleSessionRedisApplicationTests.java b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-session-redis/src/test/java/smoketest/session/redis/SampleSessionRedisApplicationTests.java index f4042ad8fa..26fc117ce1 100644 --- a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-session-redis/src/test/java/smoketest/session/redis/SampleSessionRedisApplicationTests.java +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-session-redis/src/test/java/smoketest/session/redis/SampleSessionRedisApplicationTests.java @@ -26,6 +26,7 @@ import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.data.redis.RedisServiceConnection; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.client.TestRestTemplate; import org.springframework.boot.testsupport.testcontainers.RedisContainer; @@ -37,8 +38,6 @@ import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.RequestEntity; import org.springframework.http.ResponseEntity; -import org.springframework.test.context.DynamicPropertyRegistry; -import org.springframework.test.context.DynamicPropertySource; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; @@ -48,23 +47,21 @@ import static org.assertj.core.api.Assertions.assertThat; * Tests for {@link SampleSessionRedisApplication}. * * @author Angel L. Villalain + * @author Moritz Halbritter + * @author Andy Wilkinson + * @author Phillip Webb */ @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @Testcontainers(disabledWithoutDocker = true) class SampleSessionRedisApplicationTests { @Container + @RedisServiceConnection static RedisContainer redis = new RedisContainer(); @Autowired private TestRestTemplate restTemplate; - @DynamicPropertySource - static void applicationProperties(DynamicPropertyRegistry registry) { - registry.add("spring.data.redis.host", redis::getHost); - registry.add("spring.data.redis.port", redis::getFirstMappedPort); - } - @Test @SuppressWarnings("unchecked") void sessionsEndpointShouldReturnUserSessions() { diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-session-webflux-mongo/src/test/java/smoketest/session/SampleSessionWebFluxMongoApplicationTests.java b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-session-webflux-mongo/src/test/java/smoketest/session/SampleSessionWebFluxMongoApplicationTests.java index c7ccd4cb02..066181c981 100644 --- a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-session-webflux-mongo/src/test/java/smoketest/session/SampleSessionWebFluxMongoApplicationTests.java +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-session-webflux-mongo/src/test/java/smoketest/session/SampleSessionWebFluxMongoApplicationTests.java @@ -26,12 +26,11 @@ import org.testcontainers.junit.jupiter.Testcontainers; import reactor.util.function.Tuples; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.mongo.MongoServiceConnection; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.server.LocalServerPort; import org.springframework.boot.testsupport.testcontainers.DockerImageNames; import org.springframework.http.HttpStatus; -import org.springframework.test.context.DynamicPropertyRegistry; -import org.springframework.test.context.DynamicPropertySource; import org.springframework.web.reactive.function.client.WebClient; import static org.assertj.core.api.Assertions.assertThat; @@ -41,12 +40,16 @@ import static org.assertj.core.api.Assertions.assertThat; * * @author Vedran Pavic * @author Scott Frederick + * @author Moritz Halbritter + * @author Andy Wilkinson + * @author Phillip Webb */ @SpringBootTest(properties = "spring.session.timeout:10", webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @Testcontainers(disabledWithoutDocker = true) class SampleSessionWebFluxMongoApplicationTests { @Container + @MongoServiceConnection private static final MongoDBContainer mongo = new MongoDBContainer(DockerImageNames.mongo()).withStartupAttempts(3) .withStartupTimeout(Duration.ofMinutes(2)); @@ -56,11 +59,6 @@ class SampleSessionWebFluxMongoApplicationTests { @Autowired private WebClient.Builder webClientBuilder; - @DynamicPropertySource - static void applicationProperties(DynamicPropertyRegistry registry) { - registry.add("spring.data.mongodb.uri", mongo::getReplicaSetUrl); - } - @Test void userDefinedMappingsSecureByDefault() { WebClient client = this.webClientBuilder.baseUrl("http://localhost:" + this.port + "/").build(); diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-session-webflux-redis/src/test/java/smoketest/session/SampleSessionWebFluxRedisApplicationTests.java b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-session-webflux-redis/src/test/java/smoketest/session/SampleSessionWebFluxRedisApplicationTests.java index 62aba02ab6..d587cc8299 100644 --- a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-session-webflux-redis/src/test/java/smoketest/session/SampleSessionWebFluxRedisApplicationTests.java +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-session-webflux-redis/src/test/java/smoketest/session/SampleSessionWebFluxRedisApplicationTests.java @@ -25,12 +25,11 @@ import org.testcontainers.junit.jupiter.Testcontainers; import reactor.util.function.Tuples; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.data.redis.RedisServiceConnection; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.server.LocalServerPort; import org.springframework.boot.testsupport.testcontainers.RedisContainer; import org.springframework.http.HttpStatus; -import org.springframework.test.context.DynamicPropertyRegistry; -import org.springframework.test.context.DynamicPropertySource; import org.springframework.web.reactive.function.client.WebClient; import static org.assertj.core.api.Assertions.assertThat; @@ -40,12 +39,16 @@ import static org.assertj.core.api.Assertions.assertThat; * * @author Vedran Pavic * @author Scott Frederick + * @author Moritz Halbritter + * @author Andy Wilkinson + * @author Phillip Webb */ @SpringBootTest(properties = "spring.session.timeout:10", webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @Testcontainers(disabledWithoutDocker = true) class SampleSessionWebFluxRedisApplicationTests { @Container + @RedisServiceConnection private static final RedisContainer redis = new RedisContainer(); @LocalServerPort @@ -54,12 +57,6 @@ class SampleSessionWebFluxRedisApplicationTests { @Autowired private WebClient.Builder webClientBuilder; - @DynamicPropertySource - static void applicationProperties(DynamicPropertyRegistry registry) { - registry.add("spring.data.redis.host", redis::getHost); - registry.add("spring.data.redis.port", redis::getFirstMappedPort); - } - @Test void userDefinedMappingsSecureByDefault() { WebClient client = this.webClientBuilder.baseUrl("http://localhost:" + this.port + "/").build();