Create service connections from Testcontainers-managed containers

Building upon the auto-configuration support for service connections,
this commit adds support for deriving connection details from a
Testcontainers-managed container. Several service-specific
annotations have been introduced. These annotations can be used on a
container field to indicate that it is a source of the details for
a service connection.

See gh-34658

Co-Authored-By: Phillip Webb <pwebb@vmware.com>
Co-Authored-By: Mortitz Halbritter <mkammerer@vmware.com>
pull/34759/head
Andy Wilkinson 2 years ago
parent 8ec266bea4
commit 95f45eab1f

@ -29,11 +29,46 @@ Testcontainers can be used in a Spring Boot test as follows:
include::code:vanilla/MyIntegrationTests[] 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. 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. The above configuration allows Neo4j-related beans in the application to communicate with Neo4j running inside the Testcontainers-managed Docker container.

@ -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() {
// ...
}
}

@ -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<Nothing>("neo4j:4.2")
}
}

@ -60,6 +60,21 @@ dependencies {
optional("org.apache.tomcat.embed:tomcat-embed-core") optional("org.apache.tomcat.embed:tomcat-embed-core")
optional("org.mongodb:mongodb-driver-reactivestreams") optional("org.mongodb:mongodb-driver-reactivestreams")
optional("org.mongodb:mongodb-driver-sync") 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") optional("io.micrometer:micrometer-tracing")
testImplementation(project(":spring-boot-project:spring-boot-actuator")) testImplementation(project(":spring-boot-project:spring-boot-actuator"))
@ -85,6 +100,7 @@ dependencies {
testImplementation("org.eclipse:yasson") testImplementation("org.eclipse:yasson")
testImplementation("org.hibernate.validator:hibernate-validator") testImplementation("org.hibernate.validator:hibernate-validator")
testImplementation("org.hsqldb:hsqldb") testImplementation("org.hsqldb:hsqldb")
testImplementation("org.influxdb:influxdb-java")
testImplementation("org.jooq:jooq") { testImplementation("org.jooq:jooq") {
exclude group: "javax.xml.bind", module: "jaxb-api" exclude group: "javax.xml.bind", module: "jaxb-api"
} }
@ -95,15 +111,11 @@ dependencies {
testImplementation("org.mockito:mockito-junit-jupiter") testImplementation("org.mockito:mockito-junit-jupiter")
testImplementation("org.skyscreamer:jsonassert") testImplementation("org.skyscreamer:jsonassert")
testImplementation("org.springframework:spring-core-test") testImplementation("org.springframework:spring-core-test")
testImplementation("org.springframework.amqp:spring-rabbit")
testImplementation("org.springframework.hateoas:spring-hateoas") testImplementation("org.springframework.hateoas:spring-hateoas")
testImplementation("org.springframework.kafka:spring-kafka")
testImplementation("org.springframework.plugin:spring-plugin-core") 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:junit-jupiter")
testImplementation("org.testcontainers:mongodb")
testImplementation("org.testcontainers:neo4j")
testImplementation("org.testcontainers:testcontainers")
testImplementation("org.thymeleaf:thymeleaf") testImplementation("org.thymeleaf:thymeleaf")
} }

@ -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<RabbitServiceConnection, RabbitConnectionDetails, RabbitMQContainer> {
@Override
protected RabbitConnectionDetails getContainerConnectionDetails(
ContainerConnectionSource<RabbitServiceConnection, RabbitConnectionDetails, RabbitMQContainer> 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<RabbitServiceConnection, RabbitConnectionDetails, RabbitMQContainer> 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<Address> getAddresses() {
URI uri = URI.create(this.container.getAmqpUrl());
return List.of(new Address(uri.getHost(), uri.getPort()));
}
}
}

@ -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 {
}

@ -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;

@ -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<CassandraServiceConnection, CassandraConnectionDetails, CassandraContainer<?>> {
@Override
protected CassandraConnectionDetails getContainerConnectionDetails(
ContainerConnectionSource<CassandraServiceConnection, CassandraConnectionDetails, CassandraContainer<?>> 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<CassandraServiceConnection, CassandraConnectionDetails, CassandraContainer<?>> source) {
super(source);
this.container = source.getContainer();
}
@Override
public List<Node> 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();
}
}
}

@ -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 {
}

@ -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;

@ -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<CouchbaseServiceConnection, CouchbaseConnectionDetails, CouchbaseContainer> {
@Override
protected CouchbaseConnectionDetails getContainerConnectionDetails(
ContainerConnectionSource<CouchbaseServiceConnection, CouchbaseConnectionDetails, CouchbaseContainer> 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<CouchbaseServiceConnection, CouchbaseConnectionDetails, CouchbaseContainer> 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();
}
}
}

@ -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 {
}

@ -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;

@ -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<RedisServiceConnection, RedisConnectionDetails, GenericContainer<?>> {
@Override
public RedisConnectionDetails getContainerConnectionDetails(
ContainerConnectionSource<RedisServiceConnection, RedisConnectionDetails, GenericContainer<?>> 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<RedisServiceConnection, RedisConnectionDetails, GenericContainer<?>> 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;
}
}
}

@ -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 {
}

@ -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<ElasticsearchServiceConnection, ElasticsearchConnectionDetails, GenericContainer<?>> {
private static final int DEFAULT_PORT = 9200;
@Override
protected ElasticsearchConnectionDetails getContainerConnectionDetails(
ContainerConnectionSource<ElasticsearchServiceConnection, ElasticsearchConnectionDetails, GenericContainer<?>> source) {
return new ElasticsearchContainerConnectionDetails(source);
}
/**
* {@link ElasticsearchConnectionDetails} backed by a
* {@link ContainerConnectionSource}.
*/
private static final class ElasticsearchContainerConnectionDetails extends ContainerConnectionDetails
implements ElasticsearchConnectionDetails {
private final List<Node> nodes;
private ElasticsearchContainerConnectionDetails(
ContainerConnectionSource<ElasticsearchServiceConnection, ElasticsearchConnectionDetails, GenericContainer<?>> source) {
super(source);
this.nodes = List.of(new Node(source.getContainer().getHost(),
source.getContainer().getMappedPort(DEFAULT_PORT), Protocol.HTTP, null, null));
}
@Override
public List<Node> getNodes() {
return this.nodes;
}
}
}

@ -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 {
}

@ -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;

@ -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<InfluxDbServiceConnection, InfluxDbConnectionDetails, InfluxDBContainer<?>> {
@Override
protected InfluxDbConnectionDetails getContainerConnectionDetails(
ContainerConnectionSource<InfluxDbServiceConnection, InfluxDbConnectionDetails, InfluxDBContainer<?>> 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<InfluxDbServiceConnection, InfluxDbConnectionDetails, InfluxDBContainer<?>> 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());
}
}
}

@ -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 {
}

@ -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;

@ -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<JdbcServiceConnection, JdbcConnectionDetails, JdbcDatabaseContainer<?>> {
@Override
protected JdbcConnectionDetails getContainerConnectionDetails(
ContainerConnectionSource<JdbcServiceConnection, JdbcConnectionDetails, JdbcDatabaseContainer<?>> 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<JdbcServiceConnection, JdbcConnectionDetails, JdbcDatabaseContainer<?>> 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();
}
}
}

@ -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 {
}

@ -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<KafkaServiceConnection, KafkaConnectionDetails, KafkaContainer> {
@Override
protected KafkaConnectionDetails getContainerConnectionDetails(
ContainerConnectionSource<KafkaServiceConnection, KafkaConnectionDetails, KafkaContainer> 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<KafkaServiceConnection, KafkaConnectionDetails, KafkaContainer> source) {
super(source);
this.container = source.getContainer();
}
@Override
public List<Node> getBootstrapNodes() {
URI uri = URI.create(this.container.getBootstrapServers());
return List.of(new Node(uri.getHost(), uri.getPort()));
}
}
}

@ -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 {
}

@ -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;

@ -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<MongoServiceConnection, MongoConnectionDetails, MongoDBContainer> {
@Override
protected MongoConnectionDetails getContainerConnectionDetails(
ContainerConnectionSource<MongoServiceConnection, MongoConnectionDetails, MongoDBContainer> 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<MongoServiceConnection, MongoConnectionDetails, MongoDBContainer> source) {
super(source);
this.connectionString = new ConnectionString(source.getContainer().getReplicaSetUrl());
}
@Override
public ConnectionString getConnectionString() {
return this.connectionString;
}
}
}

@ -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 {
}

@ -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;

@ -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<Neo4jServiceConnection, Neo4jConnectionDetails, Neo4jContainer<?>> {
@Override
protected Neo4jConnectionDetails getContainerConnectionDetails(
ContainerConnectionSource<Neo4jServiceConnection, Neo4jConnectionDetails, Neo4jContainer<?>> 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<Neo4jServiceConnection, Neo4jConnectionDetails, Neo4jContainer<?>> 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();
}
}
}

@ -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 {
}

@ -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;

@ -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<R2dbcServiceConnection, R2dbcConnectionDetails, MariaDBContainer<?>> {
@Override
public R2dbcConnectionDetails getContainerConnectionDetails(
ContainerConnectionSource<R2dbcServiceConnection, R2dbcConnectionDetails, MariaDBContainer<?>> 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);
}
}
}

@ -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<R2dbcServiceConnection, R2dbcConnectionDetails, MySQLContainer<?>> {
@Override
public R2dbcConnectionDetails getContainerConnectionDetails(
ContainerConnectionSource<R2dbcServiceConnection, R2dbcConnectionDetails, MySQLContainer<?>> 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);
}
}
}

@ -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<R2dbcServiceConnection, R2dbcConnectionDetails, PostgreSQLContainer<?>> {
@Override
public R2dbcConnectionDetails getContainerConnectionDetails(
ContainerConnectionSource<R2dbcServiceConnection, R2dbcConnectionDetails, PostgreSQLContainer<?>> 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);
}
}
}

@ -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 {
}

@ -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<R2dbcServiceConnection, R2dbcConnectionDetails, MSSQLServerContainer<?>> {
@Override
public R2dbcConnectionDetails getContainerConnectionDetails(
ContainerConnectionSource<R2dbcServiceConnection, R2dbcConnectionDetails, MSSQLServerContainer<?>> 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);
}
}
}

@ -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;

@ -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;
}
}

@ -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 <A> the source annotation type. The annotation will be mergable to a
* {@link ServiceConnection @ServiceConnection}.
* @param <D> the connection details type
* @param <C> the generic container type
* @author Moritz Halbritter
* @author Andy Wilkinson
* @author Phillip Webb
* @since 3.1.0
*/
public abstract class ContainerConnectionDetailsFactory<A extends Annotation, D extends ConnectionDetails, C extends GenericContainer<?>>
implements ConnectionDetailsFactory<ContainerConnectionSource<A, D, C>, D> {
@Override
public final D getConnectionDetails(ContainerConnectionSource<A, D, C> 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<A, D, C> 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;
}
}
}

@ -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 <A> the source annotation type. The annotation will mergable to a
* {@link ServiceConnection @ServiceConnection}
* @param <D> the connection details type
* @param <C> the generic container type
* @author Moritz Halbritter
* @author Andy Wilkinson
* @author Phillip Webb
* @since 3.1.0
* @see ContainerConnectionDetailsFactory
*/
public final class ContainerConnectionSource<A extends Annotation, D extends ConnectionDetails, C extends GenericContainer<?>>
implements OriginProvider {
private final Class<D> connectionDetailsType;
private final Field field;
private final A annotation;
private final C container;
private final AnnotatedFieldOrigin origin;
@SuppressWarnings("unchecked")
ContainerConnectionSource(Class<D> connectionDetailsType, Field field,
MergedAnnotation<ServiceConnection> annotation, C container) {
this(connectionDetailsType, field, (A) annotation.getRoot().synthesize(), container);
}
ContainerConnectionSource(Class<D> 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);
}
}

@ -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.
* <p>
* 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<? extends ConnectionDetails> value();
}

@ -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<ContainerConnectionSource<?, ?, ?>> sources;
ServiceConnectionContextCustomizer(List<ContainerConnectionSource<?, ?, ?>> 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 <S> ConnectionDetails getConnectionDetails(S source) {
ConnectionDetailsFactory<S, ConnectionDetails> 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 <T> BeanDefinition createBeanDefinition(T instance) {
return new RootBeanDefinition((Class<T>) instance.getClass(), () -> instance);
}
}

@ -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<ContextConfigurationAttributes> configAttributes) {
List<ContainerConnectionSource<?, ?, ?>> 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<ServiceConnection> annotation) {
Class<? extends ConnectionDetails> 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<? extends ConnectionDetails> getConnectionDetailsType(
MergedAnnotation<ServiceConnection> annotation) {
return (Class<? extends ConnectionDetails>) annotation.getClass(MergedAnnotation.VALUE);
}
private Object getFieldValue(Field field) {
ReflectionUtils.makeAccessible(field);
return ReflectionUtils.getField(field, null);
}
}

@ -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;

@ -4,6 +4,7 @@ org.springframework.boot.test.autoconfigure.OverrideAutoConfigurationContextCust
org.springframework.boot.test.autoconfigure.actuate.observability.ObservabilityContextCustomizerFactory,\ org.springframework.boot.test.autoconfigure.actuate.observability.ObservabilityContextCustomizerFactory,\
org.springframework.boot.test.autoconfigure.filter.TypeExcludeFiltersContextCustomizerFactory,\ org.springframework.boot.test.autoconfigure.filter.TypeExcludeFiltersContextCustomizerFactory,\
org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizerFactory,\ org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizerFactory,\
org.springframework.boot.test.autoconfigure.service.connection.ServiceConnectionContextCustomizerFactory,\
org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory
# Test Execution Listeners # 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.boot.test.autoconfigure.webservices.client.MockWebServiceServerTestExecutionListener
org.springframework.test.context.ApplicationContextFailureProcessor=\ org.springframework.test.context.ApplicationContextFailureProcessor=\
org.springframework.boot.test.autoconfigure.ConditionReportApplicationContextFailureProcessor 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

@ -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<String> messages = new ArrayList<>();
@RabbitListener(queuesToDeclare = @Queue("test"))
void processMessage(String message) {
this.messages.add(message);
}
}
}

@ -26,14 +26,13 @@ import org.testcontainers.junit.jupiter.Testcontainers;
import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.annotation.Autowired; 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.autoconfigure.data.redis.ExampleService;
import org.springframework.boot.test.context.TestConfiguration; import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.boot.testsupport.testcontainers.CassandraContainer; import org.springframework.boot.testsupport.testcontainers.CassandraContainer;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.data.cassandra.core.CassandraTemplate; 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.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType; 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}. * Integration test for {@link DataCassandraTest @DataCassandraTest}.
* *
* @author Artsiom Yudovin * @author Artsiom Yudovin
* @author Moritz Halbritter
* @author Andy Wilkinson
* @author Phillip Webb
*/ */
@DataCassandraTest(properties = { "spring.cassandra.local-datacenter=datacenter1", @DataCassandraTest(properties = { "spring.cassandra.schema-action=create-if-not-exists",
"spring.cassandra.schema-action=create-if-not-exists", "spring.cassandra.connection.connect-timeout=60s", "spring.cassandra.connection.connect-timeout=60s", "spring.cassandra.connection.init-query-timeout=60s",
"spring.cassandra.connection.init-query-timeout=60s", "spring.cassandra.request.timeout=60s" }) "spring.cassandra.request.timeout=60s" })
@Testcontainers(disabledWithoutDocker = true) @Testcontainers(disabledWithoutDocker = true)
class DataCassandraTestIntegrationTests { class DataCassandraTestIntegrationTests {
@Container @Container
@CassandraServiceConnection
static final CassandraContainer cassandra = new CassandraContainer(); static final CassandraContainer cassandra = new CassandraContainer();
@DynamicPropertySource
static void cassandraProperties(DynamicPropertyRegistry registry) {
registry.add("spring.cassandra.contact-points",
() -> cassandra.getHost() + ":" + cassandra.getFirstMappedPort());
}
@Autowired @Autowired
private CassandraTemplate cassandraTemplate; private CassandraTemplate cassandraTemplate;

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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.testcontainers.junit.jupiter.Testcontainers;
import org.springframework.beans.factory.annotation.Autowired; 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.test.context.TestConfiguration;
import org.springframework.boot.testsupport.testcontainers.CassandraContainer; import org.springframework.boot.testsupport.testcontainers.CassandraContainer;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan.Filter; import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.stereotype.Service; 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; import static org.assertj.core.api.Assertions.assertThat;
@ -40,24 +39,21 @@ import static org.assertj.core.api.Assertions.assertThat;
* {@link DataCassandraTest @DataCassandraTest}. * {@link DataCassandraTest @DataCassandraTest}.
* *
* @author Artsiom Yudovin * @author Artsiom Yudovin
* @author Moritz Halbritter
* @author Andy Wilkinson
* @author Phillip Webb
*/ */
@DataCassandraTest(includeFilters = @Filter(Service.class), @DataCassandraTest(includeFilters = @Filter(Service.class),
properties = { "spring.cassandra.local-datacenter=datacenter1", properties = { "spring.cassandra.schema-action=create-if-not-exists",
"spring.cassandra.schema-action=create-if-not-exists",
"spring.cassandra.connection.connect-timeout=60s", "spring.cassandra.connection.init-query-timeout=60s", "spring.cassandra.connection.connect-timeout=60s", "spring.cassandra.connection.init-query-timeout=60s",
"spring.cassandra.request.timeout=60s" }) "spring.cassandra.request.timeout=60s" })
@Testcontainers(disabledWithoutDocker = true) @Testcontainers(disabledWithoutDocker = true)
class DataCassandraTestWithIncludeFilterIntegrationTests { class DataCassandraTestWithIncludeFilterIntegrationTests {
@Container @Container
@CassandraServiceConnection
static final CassandraContainer cassandra = new CassandraContainer(); static final CassandraContainer cassandra = new CassandraContainer();
@DynamicPropertySource
static void cassandraProperties(DynamicPropertyRegistry registry) {
registry.add("spring.cassandra.contact-points",
() -> cassandra.getHost() + ":" + cassandra.getFirstMappedPort());
}
@Autowired @Autowired
private ExampleRepository exampleRepository; private ExampleRepository exampleRepository;

@ -26,11 +26,10 @@ import org.testcontainers.junit.jupiter.Testcontainers;
import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.annotation.Autowired; 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.boot.testsupport.testcontainers.DockerImageNames;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.data.couchbase.core.CouchbaseTemplate; 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.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType; 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}. * Integration test for {@link DataCouchbaseTest @DataCouchbaseTest}.
* *
* @author Eddú Meléndez * @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) @Testcontainers(disabledWithoutDocker = true)
class DataCouchbaseTestIntegrationTests { class DataCouchbaseTestIntegrationTests {
private static final String BUCKET_NAME = "cbbucket"; private static final String BUCKET_NAME = "cbbucket";
@Container @Container
@CouchbaseServiceConnection
static final CouchbaseContainer couchbase = new CouchbaseContainer(DockerImageNames.couchbase()) static final CouchbaseContainer couchbase = new CouchbaseContainer(DockerImageNames.couchbase())
.withStartupAttempts(5) .withStartupAttempts(5)
.withStartupTimeout(Duration.ofMinutes(10)) .withStartupTimeout(Duration.ofMinutes(10))
.withBucket(new BucketDefinition(BUCKET_NAME)); .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 @Autowired
private CouchbaseTemplate couchbaseTemplate; private CouchbaseTemplate couchbaseTemplate;

@ -25,10 +25,9 @@ import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers; import org.testcontainers.junit.jupiter.Testcontainers;
import org.springframework.beans.factory.annotation.Autowired; 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.boot.testsupport.testcontainers.DockerImageNames;
import org.springframework.data.couchbase.core.ReactiveCouchbaseTemplate; 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; import static org.assertj.core.api.Assertions.assertThat;
@ -37,27 +36,23 @@ import static org.assertj.core.api.Assertions.assertThat;
* repositories. * repositories.
* *
* @author Eddú Meléndez * @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) @Testcontainers(disabledWithoutDocker = true)
class DataCouchbaseTestReactiveIntegrationTests { class DataCouchbaseTestReactiveIntegrationTests {
private static final String BUCKET_NAME = "cbbucket"; private static final String BUCKET_NAME = "cbbucket";
@Container @Container
@CouchbaseServiceConnection
static final CouchbaseContainer couchbase = new CouchbaseContainer(DockerImageNames.couchbase()) static final CouchbaseContainer couchbase = new CouchbaseContainer(DockerImageNames.couchbase())
.withStartupAttempts(5) .withStartupAttempts(5)
.withStartupTimeout(Duration.ofMinutes(10)) .withStartupTimeout(Duration.ofMinutes(10))
.withBucket(new BucketDefinition(BUCKET_NAME)); .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 @Autowired
private ReactiveCouchbaseTemplate couchbaseTemplate; private ReactiveCouchbaseTemplate couchbaseTemplate;

@ -25,11 +25,10 @@ import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers; import org.testcontainers.junit.jupiter.Testcontainers;
import org.springframework.beans.factory.annotation.Autowired; 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.boot.testsupport.testcontainers.DockerImageNames;
import org.springframework.context.annotation.ComponentScan.Filter; import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.stereotype.Service; 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; import static org.assertj.core.api.Assertions.assertThat;
@ -38,27 +37,23 @@ import static org.assertj.core.api.Assertions.assertThat;
* {@link DataCouchbaseTest @DataCouchbaseTest}. * {@link DataCouchbaseTest @DataCouchbaseTest}.
* *
* @author Eddú Meléndez * @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) @Testcontainers(disabledWithoutDocker = true)
class DataCouchbaseTestWithIncludeFilterIntegrationTests { class DataCouchbaseTestWithIncludeFilterIntegrationTests {
private static final String BUCKET_NAME = "cbbucket"; private static final String BUCKET_NAME = "cbbucket";
@Container @Container
@CouchbaseServiceConnection
static final CouchbaseContainer couchbase = new CouchbaseContainer(DockerImageNames.couchbase()) static final CouchbaseContainer couchbase = new CouchbaseContainer(DockerImageNames.couchbase())
.withStartupAttempts(5) .withStartupAttempts(5)
.withStartupTimeout(Duration.ofMinutes(10)) .withStartupTimeout(Duration.ofMinutes(10))
.withBucket(new BucketDefinition(BUCKET_NAME)); .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 @Autowired
private ExampleRepository exampleRepository; private ExampleRepository exampleRepository;

@ -26,11 +26,10 @@ import org.testcontainers.junit.jupiter.Testcontainers;
import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.annotation.Autowired; 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.boot.testsupport.testcontainers.DockerImageNames;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.data.elasticsearch.client.elc.ElasticsearchTemplate; 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.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType; 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} * Sample test for {@link DataElasticsearchTest @DataElasticsearchTest}
* *
* @author Eddú Meléndez * @author Eddú Meléndez
* @author Moritz Halbritter
* @author Andy Wilkinson
* @author Phillip Webb
*/ */
@DataElasticsearchTest @DataElasticsearchTest
@Testcontainers(disabledWithoutDocker = true) @Testcontainers(disabledWithoutDocker = true)
class DataElasticsearchTestIntegrationTests { class DataElasticsearchTestIntegrationTests {
@Container @Container
@ElasticsearchServiceConnection
static final ElasticsearchContainer elasticsearch = new ElasticsearchContainer(DockerImageNames.elasticsearch()) static final ElasticsearchContainer elasticsearch = new ElasticsearchContainer(DockerImageNames.elasticsearch())
.withStartupAttempts(5) .withStartupAttempts(5)
.withStartupTimeout(Duration.ofMinutes(10)); .withStartupTimeout(Duration.ofMinutes(10));
@DynamicPropertySource
static void elasticsearchProperties(DynamicPropertyRegistry registry) {
registry.add("spring.elasticsearch.uris", elasticsearch::getHttpHostAddress);
}
@Autowired @Autowired
private ElasticsearchTemplate elasticsearchTemplate; private ElasticsearchTemplate elasticsearchTemplate;

@ -24,10 +24,9 @@ import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers; import org.testcontainers.junit.jupiter.Testcontainers;
import org.springframework.beans.factory.annotation.Autowired; 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.boot.testsupport.testcontainers.DockerImageNames;
import org.springframework.core.env.Environment; 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; import static org.assertj.core.api.Assertions.assertThat;
@ -36,21 +35,20 @@ import static org.assertj.core.api.Assertions.assertThat;
* {@link DataElasticsearchTest @DataElasticsearchTest}. * {@link DataElasticsearchTest @DataElasticsearchTest}.
* *
* @author Eddú Meléndez * @author Eddú Meléndez
* @author Moritz Halbritter
* @author Andy Wilkinson
* @author Phillip Webb
*/ */
@DataElasticsearchTest(properties = "spring.profiles.active=test") @DataElasticsearchTest(properties = "spring.profiles.active=test")
@Testcontainers(disabledWithoutDocker = true) @Testcontainers(disabledWithoutDocker = true)
class DataElasticsearchTestPropertiesIntegrationTests { class DataElasticsearchTestPropertiesIntegrationTests {
@Container @Container
@ElasticsearchServiceConnection
static final ElasticsearchContainer elasticsearch = new ElasticsearchContainer(DockerImageNames.elasticsearch()) static final ElasticsearchContainer elasticsearch = new ElasticsearchContainer(DockerImageNames.elasticsearch())
.withStartupAttempts(5) .withStartupAttempts(5)
.withStartupTimeout(Duration.ofMinutes(10)); .withStartupTimeout(Duration.ofMinutes(10));
@DynamicPropertySource
static void elasticsearchProperties(DynamicPropertyRegistry registry) {
registry.add("spring.elasticsearch.uris", elasticsearch::getHttpHostAddress);
}
@Autowired @Autowired
private Environment environment; private Environment environment;

@ -24,10 +24,9 @@ import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers; import org.testcontainers.junit.jupiter.Testcontainers;
import org.springframework.beans.factory.annotation.Autowired; 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.boot.testsupport.testcontainers.DockerImageNames;
import org.springframework.data.elasticsearch.client.elc.ReactiveElasticsearchTemplate; 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; import static org.assertj.core.api.Assertions.assertThat;
@ -36,21 +35,20 @@ import static org.assertj.core.api.Assertions.assertThat;
* repositories. * repositories.
* *
* @author Eddú Meléndez * @author Eddú Meléndez
* @author Moritz Halbritter
* @author Andy Wilkinson
* @author Phillip Webb
*/ */
@DataElasticsearchTest @DataElasticsearchTest
@Testcontainers(disabledWithoutDocker = true) @Testcontainers(disabledWithoutDocker = true)
class DataElasticsearchTestReactiveIntegrationTests { class DataElasticsearchTestReactiveIntegrationTests {
@Container @Container
@ElasticsearchServiceConnection
static final ElasticsearchContainer elasticsearch = new ElasticsearchContainer(DockerImageNames.elasticsearch()) static final ElasticsearchContainer elasticsearch = new ElasticsearchContainer(DockerImageNames.elasticsearch())
.withStartupAttempts(5) .withStartupAttempts(5)
.withStartupTimeout(Duration.ofMinutes(10)); .withStartupTimeout(Duration.ofMinutes(10));
@DynamicPropertySource
static void elasticsearchProperties(DynamicPropertyRegistry registry) {
registry.add("spring.elasticsearch.uris", elasticsearch::getHttpHostAddress);
}
@Autowired @Autowired
private ReactiveElasticsearchTemplate elasticsearchTemplate; private ReactiveElasticsearchTemplate elasticsearchTemplate;

@ -25,11 +25,10 @@ import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers; import org.testcontainers.junit.jupiter.Testcontainers;
import org.springframework.beans.factory.annotation.Autowired; 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.boot.testsupport.testcontainers.DockerImageNames;
import org.springframework.context.annotation.ComponentScan.Filter; import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.stereotype.Service; 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; import static org.assertj.core.api.Assertions.assertThat;
@ -38,21 +37,20 @@ import static org.assertj.core.api.Assertions.assertThat;
* {@link DataElasticsearchTest @DataElasticsearchTest}. * {@link DataElasticsearchTest @DataElasticsearchTest}.
* *
* @author Eddú Meléndez * @author Eddú Meléndez
* @author Moritz Halbritter
* @author Andy Wilkinson
* @author Phillip Webb
*/ */
@DataElasticsearchTest(includeFilters = @Filter(Service.class)) @DataElasticsearchTest(includeFilters = @Filter(Service.class))
@Testcontainers(disabledWithoutDocker = true) @Testcontainers(disabledWithoutDocker = true)
class DataElasticsearchTestWithIncludeFilterIntegrationTests { class DataElasticsearchTestWithIncludeFilterIntegrationTests {
@Container @Container
@ElasticsearchServiceConnection
static final ElasticsearchContainer elasticsearch = new ElasticsearchContainer(DockerImageNames.elasticsearch()) static final ElasticsearchContainer elasticsearch = new ElasticsearchContainer(DockerImageNames.elasticsearch())
.withStartupAttempts(5) .withStartupAttempts(5)
.withStartupTimeout(Duration.ofMinutes(10)); .withStartupTimeout(Duration.ofMinutes(10));
@DynamicPropertySource
static void elasticsearchProperties(DynamicPropertyRegistry registry) {
registry.add("spring.elasticsearch.uris", elasticsearch::getHttpHostAddress);
}
@Autowired @Autowired
private ExampleRepository exampleRepository; private ExampleRepository exampleRepository;

@ -25,11 +25,10 @@ import org.testcontainers.junit.jupiter.Testcontainers;
import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.annotation.Autowired; 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.boot.testsupport.testcontainers.DockerImageNames;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.data.mongodb.core.MongoTemplate; 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.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType; 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} * Sample test for {@link DataMongoTest @DataMongoTest}
* *
* @author Michael Simons * @author Michael Simons
* @author Moritz Halbritter
* @author Andy Wilkinson
* @author Phillip Webb
*/ */
@DataMongoTest @DataMongoTest
@Testcontainers(disabledWithoutDocker = true) @Testcontainers(disabledWithoutDocker = true)
class DataMongoTestIntegrationTests { class DataMongoTestIntegrationTests {
@Container @Container
@MongoServiceConnection
static final MongoDBContainer mongoDB = new MongoDBContainer(DockerImageNames.mongo()).withStartupAttempts(5) static final MongoDBContainer mongoDB = new MongoDBContainer(DockerImageNames.mongo()).withStartupAttempts(5)
.withStartupTimeout(Duration.ofMinutes(5)); .withStartupTimeout(Duration.ofMinutes(5));
@ -71,9 +74,4 @@ class DataMongoTestIntegrationTests {
.isThrownBy(() -> this.applicationContext.getBean(ExampleService.class)); .isThrownBy(() -> this.applicationContext.getBean(ExampleService.class));
} }
@DynamicPropertySource
static void mongoProperties(DynamicPropertyRegistry registry) {
registry.add("spring.data.mongodb.uri", mongoDB::getReplicaSetUrl);
}
} }

@ -24,10 +24,9 @@ import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers; import org.testcontainers.junit.jupiter.Testcontainers;
import org.springframework.beans.factory.annotation.Autowired; 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.boot.testsupport.testcontainers.DockerImageNames;
import org.springframework.data.mongodb.core.ReactiveMongoTemplate; 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; 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. * Sample tests for {@link DataMongoTest @DataMongoTest} using reactive repositories.
* *
* @author Stephane Nicoll * @author Stephane Nicoll
* @author Moritz Halbritter
* @author Andy Wilkinson
* @author Phillip Webb
*/ */
@DataMongoTest @DataMongoTest
@Testcontainers(disabledWithoutDocker = true) @Testcontainers(disabledWithoutDocker = true)
class DataMongoTestReactiveIntegrationTests { class DataMongoTestReactiveIntegrationTests {
@Container @Container
@MongoServiceConnection
static final MongoDBContainer mongoDB = new MongoDBContainer(DockerImageNames.mongo()).withStartupAttempts(5) static final MongoDBContainer mongoDB = new MongoDBContainer(DockerImageNames.mongo()).withStartupAttempts(5)
.withStartupTimeout(Duration.ofMinutes(5)); .withStartupTimeout(Duration.ofMinutes(5));
@ -50,11 +53,6 @@ class DataMongoTestReactiveIntegrationTests {
@Autowired @Autowired
private ExampleReactiveRepository exampleRepository; private ExampleReactiveRepository exampleRepository;
@DynamicPropertySource
static void mongoProperties(DynamicPropertyRegistry registry) {
registry.add("spring.data.mongodb.uri", mongoDB::getReplicaSetUrl);
}
@Test @Test
void testRepository() { void testRepository() {
ExampleDocument exampleDocument = new ExampleDocument(); ExampleDocument exampleDocument = new ExampleDocument();

@ -24,11 +24,10 @@ import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers; import org.testcontainers.junit.jupiter.Testcontainers;
import org.springframework.beans.factory.annotation.Autowired; 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.boot.testsupport.testcontainers.DockerImageNames;
import org.springframework.context.annotation.ComponentScan.Filter; import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.stereotype.Service; 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; 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}. * Integration test with custom include filter for {@link DataMongoTest @DataMongoTest}.
* *
* @author Michael Simons * @author Michael Simons
* @author Moritz Halbritter
* @author Andy Wilkinson
* @author Phillip Webb
*/ */
@DataMongoTest(includeFilters = @Filter(Service.class)) @DataMongoTest(includeFilters = @Filter(Service.class))
@Testcontainers(disabledWithoutDocker = true) @Testcontainers(disabledWithoutDocker = true)
class DataMongoTestWithIncludeFilterIntegrationTests { class DataMongoTestWithIncludeFilterIntegrationTests {
@Container @Container
@MongoServiceConnection
static final MongoDBContainer mongoDB = new MongoDBContainer(DockerImageNames.mongo()).withStartupAttempts(5) static final MongoDBContainer mongoDB = new MongoDBContainer(DockerImageNames.mongo()).withStartupAttempts(5)
.withStartupTimeout(Duration.ofMinutes(5)); .withStartupTimeout(Duration.ofMinutes(5));
@Autowired @Autowired
private ExampleService service; private ExampleService service;
@DynamicPropertySource
static void mongoProperties(DynamicPropertyRegistry registry) {
registry.add("spring.data.mongodb.uri", mongoDB::getReplicaSetUrl);
}
@Test @Test
void testService() { void testService() {
assertThat(this.service.hasCollection("foobar")).isFalse(); assertThat(this.service.hasCollection("foobar")).isFalse();

@ -25,14 +25,13 @@ import org.testcontainers.junit.jupiter.Testcontainers;
import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired; 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.test.context.TestConfiguration;
import org.springframework.boot.testsupport.testcontainers.DockerImageNames; import org.springframework.boot.testsupport.testcontainers.DockerImageNames;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.data.mongodb.MongoDatabaseFactory; import org.springframework.data.mongodb.MongoDatabaseFactory;
import org.springframework.data.mongodb.MongoTransactionManager; import org.springframework.data.mongodb.MongoTransactionManager;
import org.springframework.data.mongodb.core.MongoTemplate; 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 org.springframework.transaction.annotation.Transactional;
import static org.assertj.core.api.Assertions.assertThat; 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. * Tests for using {@link DataMongoTest @DataMongoTest} with transactions.
* *
* @author Andy Wilkinson * @author Andy Wilkinson
* @author Moritz Halbritter
* @author Phillip Webb
*/ */
@DataMongoTest @DataMongoTest
@Transactional @Transactional
@ -48,6 +49,7 @@ import static org.assertj.core.api.Assertions.assertThat;
class TransactionalDataMongoTestIntegrationTests { class TransactionalDataMongoTestIntegrationTests {
@Container @Container
@MongoServiceConnection
static final MongoDBContainer mongoDB = new MongoDBContainer(DockerImageNames.mongo()).withStartupAttempts(5) static final MongoDBContainer mongoDB = new MongoDBContainer(DockerImageNames.mongo()).withStartupAttempts(5)
.withStartupTimeout(Duration.ofMinutes(5)); .withStartupTimeout(Duration.ofMinutes(5));
@ -62,11 +64,6 @@ class TransactionalDataMongoTestIntegrationTests {
assertThat(exampleDocument.getId()).isNotNull(); assertThat(exampleDocument.getId()).isNotNull();
} }
@DynamicPropertySource
static void mongoProperties(DynamicPropertyRegistry registry) {
registry.add("spring.data.mongodb.uri", mongoDB::getReplicaSetUrl);
}
@TestConfiguration(proxyBeanMethods = false) @TestConfiguration(proxyBeanMethods = false)
static class TransactionManagerConfiguration { static class TransactionManagerConfiguration {

@ -25,11 +25,10 @@ import org.testcontainers.junit.jupiter.Testcontainers;
import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.annotation.Autowired; 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.boot.testsupport.testcontainers.DockerImageNames;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.data.neo4j.core.Neo4jTemplate; 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.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType; 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 Eddú Meléndez
* @author Stephane Nicoll * @author Stephane Nicoll
* @author Michael Simons * @author Michael Simons
* @author Moritz Halbritter
* @author Andy Wilkinson
* @author Phillip Webb
*/ */
@DataNeo4jTest @DataNeo4jTest
@Testcontainers(disabledWithoutDocker = true) @Testcontainers(disabledWithoutDocker = true)
class DataNeo4jTestIntegrationTests { class DataNeo4jTestIntegrationTests {
@Container @Container
static final Neo4jContainer<?> neo4j = new Neo4jContainer<>(DockerImageNames.neo4j()).withoutAuthentication() @Neo4jServiceConnection
.withStartupAttempts(5) static final Neo4jContainer<?> neo4j = new Neo4jContainer<>(DockerImageNames.neo4j()).withStartupAttempts(5)
.withStartupTimeout(Duration.ofMinutes(10)); .withStartupTimeout(Duration.ofMinutes(10));
@DynamicPropertySource
static void neo4jProperties(DynamicPropertyRegistry registry) {
registry.add("spring.neo4j.uri", neo4j::getBoltUrl);
}
@Autowired @Autowired
private Neo4jTemplate neo4jTemplate; private Neo4jTemplate neo4jTemplate;

@ -24,10 +24,9 @@ import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers; import org.testcontainers.junit.jupiter.Testcontainers;
import org.springframework.beans.factory.annotation.Autowired; 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.boot.testsupport.testcontainers.DockerImageNames;
import org.springframework.core.env.Environment; 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; import static org.assertj.core.api.Assertions.assertThat;
@ -36,21 +35,20 @@ import static org.assertj.core.api.Assertions.assertThat;
* {@link DataNeo4jTest @DataNeo4jTest}. * {@link DataNeo4jTest @DataNeo4jTest}.
* *
* @author Artsiom Yudovin * @author Artsiom Yudovin
* @author Moritz Halbritter
* @author Andy Wilkinson
* @author Phillip Webb
*/ */
@Testcontainers(disabledWithoutDocker = true) @Testcontainers(disabledWithoutDocker = true)
@DataNeo4jTest(properties = "spring.profiles.active=test") @DataNeo4jTest(properties = "spring.profiles.active=test")
class DataNeo4jTestPropertiesIntegrationTests { class DataNeo4jTestPropertiesIntegrationTests {
@Container @Container
@Neo4jServiceConnection
static final Neo4jContainer<?> neo4j = new Neo4jContainer<>(DockerImageNames.neo4j()).withoutAuthentication() static final Neo4jContainer<?> neo4j = new Neo4jContainer<>(DockerImageNames.neo4j()).withoutAuthentication()
.withStartupAttempts(5) .withStartupAttempts(5)
.withStartupTimeout(Duration.ofMinutes(10)); .withStartupTimeout(Duration.ofMinutes(10));
@DynamicPropertySource
static void neo4jProperties(DynamicPropertyRegistry registry) {
registry.add("spring.neo4j.uri", neo4j::getBoltUrl);
}
@Autowired @Autowired
private Environment environment; private Environment environment;

@ -28,6 +28,7 @@ import reactor.test.StepVerifier;
import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.annotation.Autowired; 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.test.context.TestConfiguration;
import org.springframework.boot.testsupport.testcontainers.DockerImageNames; import org.springframework.boot.testsupport.testcontainers.DockerImageNames;
import org.springframework.context.ApplicationContext; 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.ReactiveDatabaseSelectionProvider;
import org.springframework.data.neo4j.core.ReactiveNeo4jTemplate; import org.springframework.data.neo4j.core.ReactiveNeo4jTemplate;
import org.springframework.data.neo4j.core.transaction.ReactiveNeo4jTransactionManager; 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.Propagation;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@ -47,7 +46,9 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
* *
* @author Michael J. Simons * @author Michael J. Simons
* @author Scott Frederick * @author Scott Frederick
* @since 2.4.0 * @author Moritz Halbritter
* @author Andy Wilkinson
* @author Phillip Webb
*/ */
@DataNeo4jTest @DataNeo4jTest
@Transactional(propagation = Propagation.NOT_SUPPORTED) @Transactional(propagation = Propagation.NOT_SUPPORTED)
@ -55,15 +56,11 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
class DataNeo4jTestReactiveIntegrationTests { class DataNeo4jTestReactiveIntegrationTests {
@Container @Container
@Neo4jServiceConnection
static final Neo4jContainer<?> neo4j = new Neo4jContainer<>(DockerImageNames.neo4j()).withoutAuthentication() static final Neo4jContainer<?> neo4j = new Neo4jContainer<>(DockerImageNames.neo4j()).withoutAuthentication()
.withStartupAttempts(5) .withStartupAttempts(5)
.withStartupTimeout(Duration.ofMinutes(10)); .withStartupTimeout(Duration.ofMinutes(10));
@DynamicPropertySource
static void neo4jProperties(DynamicPropertyRegistry registry) {
registry.add("spring.neo4j.uri", neo4j::getBoltUrl);
}
@Autowired @Autowired
private ReactiveNeo4jTemplate neo4jTemplate; private ReactiveNeo4jTemplate neo4jTemplate;

@ -24,11 +24,10 @@ import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers; import org.testcontainers.junit.jupiter.Testcontainers;
import org.springframework.beans.factory.annotation.Autowired; 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.boot.testsupport.testcontainers.DockerImageNames;
import org.springframework.context.annotation.ComponentScan.Filter; import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.stereotype.Service; 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; 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 Eddú Meléndez
* @author Michael Simons * @author Michael Simons
* @author Moritz Halbritter
* @author Andy Wilkinson
* @author Phillip Webb
*/ */
@Testcontainers(disabledWithoutDocker = true) @Testcontainers(disabledWithoutDocker = true)
@DataNeo4jTest(includeFilters = @Filter(Service.class)) @DataNeo4jTest(includeFilters = @Filter(Service.class))
class DataNeo4jTestWithIncludeFilterIntegrationTests { class DataNeo4jTestWithIncludeFilterIntegrationTests {
@Container @Container
@Neo4jServiceConnection
static final Neo4jContainer<?> neo4j = new Neo4jContainer<>(DockerImageNames.neo4j()).withoutAuthentication() static final Neo4jContainer<?> neo4j = new Neo4jContainer<>(DockerImageNames.neo4j()).withoutAuthentication()
.withStartupAttempts(5) .withStartupAttempts(5)
.withStartupTimeout(Duration.ofMinutes(10)); .withStartupTimeout(Duration.ofMinutes(10));
@DynamicPropertySource
static void neo4jProperties(DynamicPropertyRegistry registry) {
registry.add("spring.neo4j.uri", neo4j::getBoltUrl);
}
@Autowired @Autowired
private ExampleService service; private ExampleService service;

@ -27,10 +27,7 @@ import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.testsupport.testcontainers.RedisContainer; import org.springframework.boot.testsupport.testcontainers.RedisContainer;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisOperations; 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.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType; 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}. * Integration test for {@link DataRedisTest @DataRedisTest}.
* *
* @author Jayaram Pradhan * @author Jayaram Pradhan
* @author Moritz Halbritter
* @author Andy Wilkinson
* @author Phillip Webb
*/ */
@Testcontainers(disabledWithoutDocker = true) @Testcontainers(disabledWithoutDocker = true)
@DataRedisTest @DataRedisTest
@ -47,6 +47,7 @@ class DataRedisTestIntegrationTests {
private static final Charset CHARSET = StandardCharsets.UTF_8; private static final Charset CHARSET = StandardCharsets.UTF_8;
@Container @Container
@RedisServiceConnection
static RedisContainer redis = new RedisContainer(); static RedisContainer redis = new RedisContainer();
@Autowired @Autowired
@ -58,12 +59,6 @@ class DataRedisTestIntegrationTests {
@Autowired @Autowired
private ApplicationContext applicationContext; 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 @Test
void testRepository() { void testRepository() {
PersonHash personHash = new PersonHash(); PersonHash personHash = new PersonHash();
@ -71,8 +66,10 @@ class DataRedisTestIntegrationTests {
assertThat(personHash.getId()).isNull(); assertThat(personHash.getId()).isNull();
PersonHash savedEntity = this.exampleRepository.save(personHash); PersonHash savedEntity = this.exampleRepository.save(personHash);
assertThat(savedEntity.getId()).isNotNull(); assertThat(savedEntity.getId()).isNotNull();
assertThat(this.operations.execute((RedisConnection connection) -> connection.keyCommands() assertThat(this.operations
.exists(("persons:" + savedEntity.getId()).getBytes(CHARSET)))).isTrue(); .execute((org.springframework.data.redis.connection.RedisConnection connection) -> connection.keyCommands()
.exists(("persons:" + savedEntity.getId()).getBytes(CHARSET))))
.isTrue();
this.exampleRepository.deleteAll(); this.exampleRepository.deleteAll();
} }

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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.beans.factory.annotation.Autowired;
import org.springframework.boot.testsupport.testcontainers.RedisContainer; import org.springframework.boot.testsupport.testcontainers.RedisContainer;
import org.springframework.core.env.Environment; 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; import static org.assertj.core.api.Assertions.assertThat;
@ -33,23 +31,21 @@ import static org.assertj.core.api.Assertions.assertThat;
* {@link DataRedisTest @DataRedisTest}. * {@link DataRedisTest @DataRedisTest}.
* *
* @author Artsiom Yudovin * @author Artsiom Yudovin
* @author Moritz Halbritter
* @author Andy Wilkinson
* @author Phillip Webb
*/ */
@Testcontainers(disabledWithoutDocker = true) @Testcontainers(disabledWithoutDocker = true)
@DataRedisTest(properties = "spring.profiles.active=test") @DataRedisTest(properties = "spring.profiles.active=test")
class DataRedisTestPropertiesIntegrationTests { class DataRedisTestPropertiesIntegrationTests {
@Container @Container
@RedisServiceConnection
static final RedisContainer redis = new RedisContainer(); static final RedisContainer redis = new RedisContainer();
@Autowired @Autowired
private Environment environment; 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 @Test
void environmentWithNewProfile() { void environmentWithNewProfile() {
assertThat(this.environment.getActiveProfiles()).containsExactly("test"); assertThat(this.environment.getActiveProfiles()).containsExactly("test");

@ -28,8 +28,6 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.testsupport.testcontainers.RedisContainer; import org.springframework.boot.testsupport.testcontainers.RedisContainer;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.data.redis.core.ReactiveRedisOperations; 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; 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. * Integration test for {@link DataRedisTest @DataRedisTest} using reactive operations.
* *
* @author Stephane Nicoll * @author Stephane Nicoll
* @author Moritz Halbritter
* @author Andy Wilkinson
* @author Phillip Webb
*/ */
@Testcontainers(disabledWithoutDocker = true) @Testcontainers(disabledWithoutDocker = true)
@DataRedisTest @DataRedisTest
class DataRedisTestReactiveIntegrationTests { class DataRedisTestReactiveIntegrationTests {
@Container @Container
@RedisServiceConnection
static RedisContainer redis = new RedisContainer(); static RedisContainer redis = new RedisContainer();
@Autowired @Autowired
@ -51,12 +53,6 @@ class DataRedisTestReactiveIntegrationTests {
@Autowired @Autowired
private ApplicationContext applicationContext; 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 @Test
void testRepository() { void testRepository() {
String id = UUID.randomUUID().toString(); String id = UUID.randomUUID().toString();

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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.boot.testsupport.testcontainers.RedisContainer;
import org.springframework.context.annotation.ComponentScan.Filter; import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.stereotype.Service; 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; 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}. * Integration test with custom include filter for {@link DataRedisTest @DataRedisTest}.
* *
* @author Jayaram Pradhan * @author Jayaram Pradhan
* @author Moritz Halbritter
* @author Andy Wilkinson
* @author Phillip Webb
*/ */
@Testcontainers(disabledWithoutDocker = true) @Testcontainers(disabledWithoutDocker = true)
@DataRedisTest(includeFilters = @Filter(Service.class)) @DataRedisTest(includeFilters = @Filter(Service.class))
class DataRedisTestWithIncludeFilterIntegrationTests { class DataRedisTestWithIncludeFilterIntegrationTests {
@Container @Container
@RedisServiceConnection
static final RedisContainer redis = new RedisContainer(); static final RedisContainer redis = new RedisContainer();
@Autowired @Autowired
@ -47,12 +49,6 @@ class DataRedisTestWithIncludeFilterIntegrationTests {
@Autowired @Autowired
private ExampleService service; 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 @Test
void testService() { void testService() {
PersonHash personHash = new PersonHash(); PersonHash personHash = new PersonHash();

@ -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 {
}
}

@ -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<String, String> 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<String> messages = new ArrayList<>();
@KafkaListener(topics = "test-topic")
void processMessage(String message) {
this.messages.add(message);
}
}
}

@ -32,12 +32,18 @@ public final class DockerImageNames {
private static final String ELASTICSEARCH_VERSION = "7.17.5"; 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 MONGO_VERSION = "4.0.23";
private static final String NEO4J_VERSION = "4.4.11"; private static final String NEO4J_VERSION = "4.4.11";
private static final String POSTGRESQL_VERSION = "14.0"; 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 REDIS_VERSION = "4.0.14";
private static final String REGISTRY_VERSION = "2.7.1"; 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 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 {@link DockerImageName} suitable for running Mongo.
* @return a docker image name 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 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 {@link DockerImageName} suitable for running Redis.
* @return a docker image name for running redis * @return a docker image name for running redis

@ -21,12 +21,11 @@ import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers; import org.testcontainers.junit.jupiter.Testcontainers;
import org.springframework.beans.factory.annotation.Autowired; 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.context.SpringBootTest;
import org.springframework.boot.testsupport.testcontainers.RedisContainer; import org.springframework.boot.testsupport.testcontainers.RedisContainer;
import org.springframework.cache.Cache; import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager; 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; import static org.assertj.core.api.Assertions.assertThat;
@ -35,6 +34,7 @@ import static org.assertj.core.api.Assertions.assertThat;
class SampleCacheApplicationRedisTests { class SampleCacheApplicationRedisTests {
@Container @Container
@RedisServiceConnection
private static final RedisContainer redis = new RedisContainer(); private static final RedisContainer redis = new RedisContainer();
@Autowired @Autowired
@ -43,11 +43,6 @@ class SampleCacheApplicationRedisTests {
@Autowired @Autowired
private CountryRepository countryRepository; private CountryRepository countryRepository;
@DynamicPropertySource
static void redisProperties(DynamicPropertyRegistry properties) {
properties.add("spring.data.redis.url", () -> "redis://" + redis.getHost() + ":" + redis.getFirstMappedPort());
}
@Test @Test
void validateCache() { void validateCache() {
Cache countries = this.cacheManager.getCache("countries"); Cache countries = this.cacheManager.getCache("countries");

@ -18,4 +18,5 @@ dependencies {
testImplementation("io.projectreactor:reactor-test") testImplementation("io.projectreactor:reactor-test")
testImplementation("org.testcontainers:junit-jupiter") testImplementation("org.testcontainers:junit-jupiter")
testImplementation("org.testcontainers:postgresql") testImplementation("org.testcontainers:postgresql")
testImplementation("org.testcontainers:r2dbc")
} }

@ -24,35 +24,29 @@ import reactor.test.StepVerifier;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.r2dbc.DataR2dbcTest; 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.boot.testsupport.testcontainers.DockerImageNames;
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.assertThat;
/** /**
* Tests for {@link CityRepository}. * Tests for {@link CityRepository}.
*
* @author Moritz Halbritter
* @author Andy Wilkinson
* @author Phillip Webb
*/ */
@Testcontainers(disabledWithoutDocker = true) @Testcontainers(disabledWithoutDocker = true)
@DataR2dbcTest @DataR2dbcTest
class CityRepositoryTests { class CityRepositoryTests {
@Container @Container
@JdbcServiceConnection
@R2dbcServiceConnection
static PostgreSQLContainer<?> postgresql = new PostgreSQLContainer<>(DockerImageNames.postgresql()) static PostgreSQLContainer<?> postgresql = new PostgreSQLContainer<>(DockerImageNames.postgresql())
.withDatabaseName("test_flyway"); .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 @Autowired
private CityRepository repository; private CityRepository repository;
@ -63,9 +57,4 @@ class CityRepositoryTests {
.verifyComplete(); .verifyComplete();
} }
private static String r2dbcUrl() {
return String.format("r2dbc:postgresql://%s:%s/%s", postgresql.getHost(),
postgresql.getMappedPort(PostgreSQLContainer.POSTGRESQL_PORT), postgresql.getDatabaseName());
}
} }

@ -21,4 +21,5 @@ dependencies {
testImplementation("io.projectreactor:reactor-test") testImplementation("io.projectreactor:reactor-test")
testImplementation("org.testcontainers:junit-jupiter") testImplementation("org.testcontainers:junit-jupiter")
testImplementation("org.testcontainers:postgresql") testImplementation("org.testcontainers:postgresql")
testImplementation("org.testcontainers:r2dbc")
} }

@ -24,35 +24,29 @@ import reactor.test.StepVerifier;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.r2dbc.DataR2dbcTest; 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.boot.testsupport.testcontainers.DockerImageNames;
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.assertThat;
/** /**
* Tests for {@link CityRepository}. * Tests for {@link CityRepository}.
*
* @author Moritz Halbritter
* @author Andy Wilkinson
* @author Phillip Webb
*/ */
@Testcontainers(disabledWithoutDocker = true) @Testcontainers(disabledWithoutDocker = true)
@DataR2dbcTest @DataR2dbcTest
class CityRepositoryTests { class CityRepositoryTests {
@Container @Container
@JdbcServiceConnection
@R2dbcServiceConnection
static PostgreSQLContainer<?> postgresql = new PostgreSQLContainer<>(DockerImageNames.postgresql()) static PostgreSQLContainer<?> postgresql = new PostgreSQLContainer<>(DockerImageNames.postgresql())
.withDatabaseName("test_liquibase"); .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 @Autowired
private CityRepository repository; private CityRepository repository;
@ -63,9 +57,4 @@ class CityRepositoryTests {
.verifyComplete(); .verifyComplete();
} }
private static String r2dbcUrl() {
return String.format("r2dbc:postgresql://%s:%s/%s", postgresql.getHost(),
postgresql.getMappedPort(PostgreSQLContainer.POSTGRESQL_PORT), postgresql.getDatabaseName());
}
} }

@ -28,6 +28,7 @@ import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers; import org.testcontainers.junit.jupiter.Testcontainers;
import org.springframework.beans.factory.annotation.Autowired; 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.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate; import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.test.web.server.LocalServerPort; 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.MediaType;
import org.springframework.http.RequestEntity; import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity; 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.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap; import org.springframework.util.MultiValueMap;
@ -51,6 +50,9 @@ import static org.assertj.core.api.Assertions.assertThat;
* Tests for {@link SampleSessionMongoApplication}. * Tests for {@link SampleSessionMongoApplication}.
* *
* @author Angel L. Villalain * @author Angel L. Villalain
* @author Moritz Halbritter
* @author Andy Wilkinson
* @author Phillip Webb
*/ */
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@Testcontainers(disabledWithoutDocker = true) @Testcontainers(disabledWithoutDocker = true)
@ -63,14 +65,10 @@ class SampleSessionMongoApplicationTests {
private int port; private int port;
@Container @Container
@MongoServiceConnection
static MongoDBContainer mongo = new MongoDBContainer(DockerImageNames.mongo()).withStartupAttempts(3) static MongoDBContainer mongo = new MongoDBContainer(DockerImageNames.mongo()).withStartupAttempts(3)
.withStartupTimeout(Duration.ofMinutes(2)); .withStartupTimeout(Duration.ofMinutes(2));
@DynamicPropertySource
static void applicationProperties(DynamicPropertyRegistry registry) {
registry.add("spring.data.mongodb.uri", mongo::getReplicaSetUrl);
}
@Test @Test
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
void sessionsEndpointShouldReturnUserSessions() { void sessionsEndpointShouldReturnUserSessions() {

@ -26,6 +26,7 @@ import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers; import org.testcontainers.junit.jupiter.Testcontainers;
import org.springframework.beans.factory.annotation.Autowired; 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.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate; import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.testsupport.testcontainers.RedisContainer; 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.MediaType;
import org.springframework.http.RequestEntity; import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity; 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.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap; import org.springframework.util.MultiValueMap;
@ -48,23 +47,21 @@ import static org.assertj.core.api.Assertions.assertThat;
* Tests for {@link SampleSessionRedisApplication}. * Tests for {@link SampleSessionRedisApplication}.
* *
* @author Angel L. Villalain * @author Angel L. Villalain
* @author Moritz Halbritter
* @author Andy Wilkinson
* @author Phillip Webb
*/ */
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@Testcontainers(disabledWithoutDocker = true) @Testcontainers(disabledWithoutDocker = true)
class SampleSessionRedisApplicationTests { class SampleSessionRedisApplicationTests {
@Container @Container
@RedisServiceConnection
static RedisContainer redis = new RedisContainer(); static RedisContainer redis = new RedisContainer();
@Autowired @Autowired
private TestRestTemplate restTemplate; 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 @Test
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
void sessionsEndpointShouldReturnUserSessions() { void sessionsEndpointShouldReturnUserSessions() {

@ -26,12 +26,11 @@ import org.testcontainers.junit.jupiter.Testcontainers;
import reactor.util.function.Tuples; import reactor.util.function.Tuples;
import org.springframework.beans.factory.annotation.Autowired; 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.context.SpringBootTest;
import org.springframework.boot.test.web.server.LocalServerPort; import org.springframework.boot.test.web.server.LocalServerPort;
import org.springframework.boot.testsupport.testcontainers.DockerImageNames; import org.springframework.boot.testsupport.testcontainers.DockerImageNames;
import org.springframework.http.HttpStatus; 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 org.springframework.web.reactive.function.client.WebClient;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
@ -41,12 +40,16 @@ import static org.assertj.core.api.Assertions.assertThat;
* *
* @author Vedran Pavic * @author Vedran Pavic
* @author Scott Frederick * @author Scott Frederick
* @author Moritz Halbritter
* @author Andy Wilkinson
* @author Phillip Webb
*/ */
@SpringBootTest(properties = "spring.session.timeout:10", webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @SpringBootTest(properties = "spring.session.timeout:10", webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@Testcontainers(disabledWithoutDocker = true) @Testcontainers(disabledWithoutDocker = true)
class SampleSessionWebFluxMongoApplicationTests { class SampleSessionWebFluxMongoApplicationTests {
@Container @Container
@MongoServiceConnection
private static final MongoDBContainer mongo = new MongoDBContainer(DockerImageNames.mongo()).withStartupAttempts(3) private static final MongoDBContainer mongo = new MongoDBContainer(DockerImageNames.mongo()).withStartupAttempts(3)
.withStartupTimeout(Duration.ofMinutes(2)); .withStartupTimeout(Duration.ofMinutes(2));
@ -56,11 +59,6 @@ class SampleSessionWebFluxMongoApplicationTests {
@Autowired @Autowired
private WebClient.Builder webClientBuilder; private WebClient.Builder webClientBuilder;
@DynamicPropertySource
static void applicationProperties(DynamicPropertyRegistry registry) {
registry.add("spring.data.mongodb.uri", mongo::getReplicaSetUrl);
}
@Test @Test
void userDefinedMappingsSecureByDefault() { void userDefinedMappingsSecureByDefault() {
WebClient client = this.webClientBuilder.baseUrl("http://localhost:" + this.port + "/").build(); WebClient client = this.webClientBuilder.baseUrl("http://localhost:" + this.port + "/").build();

@ -25,12 +25,11 @@ import org.testcontainers.junit.jupiter.Testcontainers;
import reactor.util.function.Tuples; import reactor.util.function.Tuples;
import org.springframework.beans.factory.annotation.Autowired; 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.context.SpringBootTest;
import org.springframework.boot.test.web.server.LocalServerPort; import org.springframework.boot.test.web.server.LocalServerPort;
import org.springframework.boot.testsupport.testcontainers.RedisContainer; import org.springframework.boot.testsupport.testcontainers.RedisContainer;
import org.springframework.http.HttpStatus; 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 org.springframework.web.reactive.function.client.WebClient;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
@ -40,12 +39,16 @@ import static org.assertj.core.api.Assertions.assertThat;
* *
* @author Vedran Pavic * @author Vedran Pavic
* @author Scott Frederick * @author Scott Frederick
* @author Moritz Halbritter
* @author Andy Wilkinson
* @author Phillip Webb
*/ */
@SpringBootTest(properties = "spring.session.timeout:10", webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @SpringBootTest(properties = "spring.session.timeout:10", webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@Testcontainers(disabledWithoutDocker = true) @Testcontainers(disabledWithoutDocker = true)
class SampleSessionWebFluxRedisApplicationTests { class SampleSessionWebFluxRedisApplicationTests {
@Container @Container
@RedisServiceConnection
private static final RedisContainer redis = new RedisContainer(); private static final RedisContainer redis = new RedisContainer();
@LocalServerPort @LocalServerPort
@ -54,12 +57,6 @@ class SampleSessionWebFluxRedisApplicationTests {
@Autowired @Autowired
private WebClient.Builder webClientBuilder; 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 @Test
void userDefinedMappingsSecureByDefault() { void userDefinedMappingsSecureByDefault() {
WebClient client = this.webClientBuilder.baseUrl("http://localhost:" + this.port + "/").build(); WebClient client = this.webClientBuilder.baseUrl("http://localhost:" + this.port + "/").build();

Loading…
Cancel
Save