Refactor testcontainers service connections
Update restcontainers service connections support so that technology specific `@ServiceConnector` annotations are not longer required. A single `@ServiceConnector` annotation can now be used to create all `ConnectionDetail` beans. Closes gh-35017
This commit is contained in:
@@ -17,14 +17,17 @@
|
||||
package org.springframework.boot.autoconfigure.service.connection;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||
import org.springframework.core.io.support.SpringFactoriesLoader;
|
||||
import org.springframework.core.style.ToStringCreator;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A registry of {@link ConnectionDetailsFactory} instances.
|
||||
@@ -49,30 +52,55 @@ public class ConnectionDetailsFactories {
|
||||
registrations.filter(Objects::nonNull).forEach(this.registrations::add);
|
||||
}
|
||||
|
||||
public <S> ConnectionDetails getConnectionDetails(S source) {
|
||||
return getConnectionDetailsFactory(source).getConnectionDetails(source);
|
||||
/**
|
||||
* Return a {@link Map} of {@link ConnectionDetails} interface type to
|
||||
* {@link ConnectionDetails} instance created from the factories associated with the
|
||||
* given source.
|
||||
* @param <S> the source type
|
||||
* @param source the source
|
||||
* @return a list of {@link ConnectionDetails} instances.
|
||||
*/
|
||||
public <S> Map<Class<?>, ConnectionDetails> getConnectionDetails(S source) {
|
||||
List<Registration<S, ?>> registrations = getRegistrations(source);
|
||||
Map<Class<?>, ConnectionDetails> result = new LinkedHashMap<>();
|
||||
for (Registration<S, ?> registration : registrations) {
|
||||
ConnectionDetails connectionDetails = registration.factory().getConnectionDetails(source);
|
||||
if (connectionDetails != null) {
|
||||
Class<?> connectionDetailsType = registration.connectionDetailsType();
|
||||
ConnectionDetails previous = result.put(connectionDetailsType, connectionDetails);
|
||||
Assert.state(previous == null, () -> "Duplicate connection details supplied for %s"
|
||||
.formatted(connectionDetailsType.getName()));
|
||||
}
|
||||
}
|
||||
return Map.copyOf(result);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <S> ConnectionDetailsFactory<S, ConnectionDetails> getConnectionDetailsFactory(S source) {
|
||||
<S> List<Registration<S, ?>> getRegistrations(S source) {
|
||||
Class<S> sourceType = (Class<S>) source.getClass();
|
||||
List<ConnectionDetailsFactory<S, ConnectionDetails>> result = new ArrayList<>();
|
||||
List<Registration<S, ?>> result = new ArrayList<>();
|
||||
for (Registration<?, ?> candidate : this.registrations) {
|
||||
if (candidate.sourceType().isAssignableFrom(sourceType)) {
|
||||
result.add((ConnectionDetailsFactory<S, ConnectionDetails>) candidate.factory());
|
||||
result.add((Registration<S, ?>) candidate);
|
||||
}
|
||||
}
|
||||
if (result.isEmpty()) {
|
||||
throw new ConnectionDetailsFactoryNotFoundException(source);
|
||||
}
|
||||
AnnotationAwareOrderComparator.sort(result);
|
||||
return (result.size() != 1) ? new CompositeConnectionDetailsFactory<>(result) : result.get(0);
|
||||
result.sort(Comparator.comparing(Registration::factory, AnnotationAwareOrderComparator.INSTANCE));
|
||||
return List.copyOf(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@link ConnectionDetailsFactory} registration.
|
||||
*
|
||||
* @param <S> the source type
|
||||
* @param <D> the connection details type
|
||||
* @param sourceType the source type
|
||||
* @param connectionDetailsType the connection details type
|
||||
* @param factory the factory
|
||||
*/
|
||||
private record Registration<S, D extends ConnectionDetails>(Class<S> sourceType, Class<D> connectionDetailsType,
|
||||
record Registration<S, D extends ConnectionDetails>(Class<S> sourceType, Class<D> connectionDetailsType,
|
||||
ConnectionDetailsFactory<S, D> factory) {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -87,37 +115,4 @@ public class ConnectionDetailsFactories {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Composite {@link ConnectionDetailsFactory} implementation.
|
||||
*
|
||||
* @param <S> the source type
|
||||
*/
|
||||
static class CompositeConnectionDetailsFactory<S> implements ConnectionDetailsFactory<S, ConnectionDetails> {
|
||||
|
||||
private final List<ConnectionDetailsFactory<S, ConnectionDetails>> delegates;
|
||||
|
||||
CompositeConnectionDetailsFactory(List<ConnectionDetailsFactory<S, ConnectionDetails>> delegates) {
|
||||
this.delegates = delegates;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConnectionDetails getConnectionDetails(S source) {
|
||||
return this.delegates.stream()
|
||||
.map((delegate) -> delegate.getConnectionDetails(source))
|
||||
.filter(Objects::nonNull)
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
List<ConnectionDetailsFactory<S, ConnectionDetails>> getDelegates() {
|
||||
return this.delegates;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringCreator(this).append("delegates", this.delegates).toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,15 +16,18 @@
|
||||
|
||||
package org.springframework.boot.autoconfigure.service.connection;
|
||||
|
||||
import org.assertj.core.api.InstanceOfAssertFactories;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.autoconfigure.service.connection.ConnectionDetailsFactories.CompositeConnectionDetailsFactory;
|
||||
import org.springframework.boot.autoconfigure.service.connection.ConnectionDetailsFactories.Registration;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.test.io.support.MockSpringFactoriesLoader;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||
|
||||
/**
|
||||
* Tests for {@link ConnectionDetailsFactories}.
|
||||
@@ -38,43 +41,50 @@ class ConnectionDetailsFactoriesTests {
|
||||
private final MockSpringFactoriesLoader loader = new MockSpringFactoriesLoader();
|
||||
|
||||
@Test
|
||||
void getConnectionDetailsFactoryShouldThrowWhenNoFactoryForSource() {
|
||||
void getConnectionDetailsWhenNoFactoryForSourceThrowsException() {
|
||||
ConnectionDetailsFactories factories = new ConnectionDetailsFactories(this.loader);
|
||||
assertThatExceptionOfType(ConnectionDetailsFactoryNotFoundException.class)
|
||||
.isThrownBy(() -> factories.getConnectionDetailsFactory("source"));
|
||||
.isThrownBy(() -> factories.getConnectionDetails("source"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getConnectionDetailsFactoryShouldReturnSingleFactoryWhenSourceHasOneMatch() {
|
||||
void getConnectionDetailsWhenSourceHasOneMatchReturnsSingleResult() {
|
||||
this.loader.addInstance(ConnectionDetailsFactory.class, new TestConnectionDetailsFactory());
|
||||
ConnectionDetailsFactories factories = new ConnectionDetailsFactories(this.loader);
|
||||
ConnectionDetailsFactory<String, ConnectionDetails> factory = factories.getConnectionDetailsFactory("source");
|
||||
assertThat(factory).isInstanceOf(TestConnectionDetailsFactory.class);
|
||||
Map<Class<?>, ConnectionDetails> connectionDetails = factories.getConnectionDetails("source");
|
||||
assertThat(connectionDetails).hasSize(1);
|
||||
assertThat(connectionDetails.get(TestConnectionDetails.class)).isInstanceOf(TestConnectionDetailsImpl.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
void getConnectionDetailsFactoryShouldReturnCompositeFactoryWhenSourceHasMultipleMatches() {
|
||||
void getConnectionDetailsWhenSourceHasMultipleMatchesReturnsMultipleResults() {
|
||||
this.loader.addInstance(ConnectionDetailsFactory.class, new TestConnectionDetailsFactory(),
|
||||
new OtherConnectionDetailsFactory());
|
||||
ConnectionDetailsFactories factories = new ConnectionDetailsFactories(this.loader);
|
||||
Map<Class<?>, ConnectionDetails> connectionDetails = factories.getConnectionDetails("source");
|
||||
assertThat(connectionDetails).hasSize(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getConnectionDetailsWhenDuplicatesThrowsException() {
|
||||
this.loader.addInstance(ConnectionDetailsFactory.class, new TestConnectionDetailsFactory(),
|
||||
new TestConnectionDetailsFactory());
|
||||
ConnectionDetailsFactories factories = new ConnectionDetailsFactories(this.loader);
|
||||
ConnectionDetailsFactory<String, ConnectionDetails> factory = factories.getConnectionDetailsFactory("source");
|
||||
assertThat(factory).asInstanceOf(InstanceOfAssertFactories.type(CompositeConnectionDetailsFactory.class))
|
||||
.satisfies((composite) -> assertThat(composite.getDelegates()).hasSize(2));
|
||||
assertThatIllegalStateException().isThrownBy(() -> factories.getConnectionDetails("source"))
|
||||
.withMessage("Duplicate connection details supplied for " + TestConnectionDetails.class.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
void compositeFactoryShouldHaveOrderedDelegates() {
|
||||
void getRegistrationsReturnsOrderedDelegates() {
|
||||
TestConnectionDetailsFactory orderOne = new TestConnectionDetailsFactory(1);
|
||||
TestConnectionDetailsFactory orderTwo = new TestConnectionDetailsFactory(2);
|
||||
TestConnectionDetailsFactory orderThree = new TestConnectionDetailsFactory(3);
|
||||
this.loader.addInstance(ConnectionDetailsFactory.class, orderOne, orderThree, orderTwo);
|
||||
ConnectionDetailsFactories factories = new ConnectionDetailsFactories(this.loader);
|
||||
ConnectionDetailsFactory<String, ConnectionDetails> factory = factories.getConnectionDetailsFactory("source");
|
||||
assertThat(factory).asInstanceOf(InstanceOfAssertFactories.type(CompositeConnectionDetailsFactory.class))
|
||||
.satisfies((composite) -> assertThat(composite.getDelegates()).containsExactly(orderOne, orderTwo,
|
||||
orderThree));
|
||||
List<Registration<String, ?>> registrations = factories.getRegistrations("source");
|
||||
assertThat(registrations.get(0).factory()).isEqualTo(orderOne);
|
||||
assertThat(registrations.get(1).factory()).isEqualTo(orderTwo);
|
||||
assertThat(registrations.get(2).factory()).isEqualTo(orderThree);
|
||||
}
|
||||
|
||||
private static final class TestConnectionDetailsFactory
|
||||
@@ -92,7 +102,7 @@ class ConnectionDetailsFactoriesTests {
|
||||
|
||||
@Override
|
||||
public TestConnectionDetails getConnectionDetails(String source) {
|
||||
return new TestConnectionDetails();
|
||||
return new TestConnectionDetailsImpl();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -102,11 +112,30 @@ class ConnectionDetailsFactoriesTests {
|
||||
|
||||
}
|
||||
|
||||
private static final class TestConnectionDetails implements ConnectionDetails {
|
||||
private static final class OtherConnectionDetailsFactory
|
||||
implements ConnectionDetailsFactory<String, OtherConnectionDetails> {
|
||||
|
||||
private TestConnectionDetails() {
|
||||
@Override
|
||||
public OtherConnectionDetails getConnectionDetails(String source) {
|
||||
return new OtherConnectionDetailsImpl();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private interface TestConnectionDetails extends ConnectionDetails {
|
||||
|
||||
}
|
||||
|
||||
private static final class TestConnectionDetailsImpl implements TestConnectionDetails {
|
||||
|
||||
}
|
||||
|
||||
private interface OtherConnectionDetails extends ConnectionDetails {
|
||||
|
||||
}
|
||||
|
||||
private static final class OtherConnectionDetailsImpl implements OtherConnectionDetails {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,12 +18,12 @@ For additional details on Spring Security's testing support, see Spring Security
|
||||
|
||||
|
||||
|
||||
|
||||
[[howto.testing.testcontainers]]
|
||||
=== Use Testcontainers for Integration Testing
|
||||
The https://www.testcontainers.org/[Testcontainers] library provides a way to manage services running inside Docker containers.
|
||||
It integrates with JUnit, allowing you to write a test class that can start up a container before any of the tests run.
|
||||
Testcontainers is especially useful for writing integration tests that talk to a real backend service such as MySQL, MongoDB, Cassandra and others.
|
||||
|
||||
Testcontainers can be used in a Spring Boot test as follows:
|
||||
|
||||
include::code:vanilla/MyIntegrationTests[]
|
||||
@@ -32,6 +32,7 @@ This will start up a docker container running Neo4j (if Docker is running locall
|
||||
In most cases, you will need to configure the application to connect to the service running in the container.
|
||||
|
||||
|
||||
|
||||
[[howto.testing.testcontainers.service-connections]]
|
||||
==== Service Connections
|
||||
A service connection is a connection to any remote service.
|
||||
@@ -42,24 +43,69 @@ When using Testcontainers, connection details can be automatically created for a
|
||||
|
||||
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.
|
||||
Thanks to `@ServiceConnection`, 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`:
|
||||
Service connection annotations are processed by `ContainerConnectionDetailsFactory` classes registered with `spring.factories`.
|
||||
A `ContainerConnectionDetailsFactory` can create a `ConnectionDetails` bean based on a specific `Container` subclass, or the Docker image name.
|
||||
|
||||
- `@CassandraServiceConnection`
|
||||
- `@CouchbaseServiceConnection`
|
||||
- `@ElasticsearchServiceConnection`
|
||||
- `@InfluxDbServiceConnection`
|
||||
- `@JdbcServiceConnection`
|
||||
- `@KafkaServiceConnection`
|
||||
- `@MongoServiceConnection`
|
||||
- `@Neo4jServiceConnection`
|
||||
- `@R2dbcServiceConnection`
|
||||
- `@RabbitServiceConnection`
|
||||
- `@RedisServiceConnection`
|
||||
The following service connection factories are provided in the `spring-boot-testcontainers` jar:
|
||||
|
||||
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.
|
||||
|===
|
||||
| Connection Details | Matched on
|
||||
|
||||
| `CassandraConnectionDetails`
|
||||
| Containers of type `CassandraContainer`
|
||||
|
||||
| `CouchbaseConnectionDetails`
|
||||
| Containers of type `CouchbaseContainer`
|
||||
|
||||
| `ElasticsearchConnectionDetails`
|
||||
| Containers of type `ElasticsearchContainer`
|
||||
|
||||
| `FlywayConnectionDetails`
|
||||
| Containers of type `JdbcDatabaseContainer`
|
||||
|
||||
| `InfluxDbConnectionDetails`
|
||||
| Containers of type `InfluxDBContainer`
|
||||
|
||||
| `JdbcConnectionDetails`
|
||||
| Containers of type `JdbcDatabaseContainer`
|
||||
|
||||
| `KafkaConnectionDetails`
|
||||
| Containers of type `KafkaContainer`
|
||||
|
||||
| `LiquibaseConnectionDetails`
|
||||
| Containers of type `JdbcDatabaseContainer`
|
||||
|
||||
| `MongoConnectionDetails`
|
||||
| Containers of type `MongoDBContainer`
|
||||
|
||||
| `Neo4jConnectionDetails`
|
||||
| Containers of type `Neo4jContainer`
|
||||
|
||||
| `R2dbcConnectionDetails`
|
||||
| Containers of type `MariaDBContainer`, `MSSQLServerContainer`, `MySQLContainer` or `PostgreSQLContainer`
|
||||
|
||||
| `RabbitConnectionDetails`
|
||||
| Containers of type `RabbitMQContainer`
|
||||
|
||||
| `RedisConnectionDetails`
|
||||
| Containers named "redis"
|
||||
|===
|
||||
|
||||
[TIP]
|
||||
====
|
||||
By default all applicable connection details beans will be created for a given `Container`.
|
||||
For example, a `PostgreSQLContainer` will create both `JdbcConnectionDetails` and `R2dbcConnectionDetails`.
|
||||
|
||||
If you want to create only a subset of the applicable types, you can use the `type` attribute of `@ServiceConnection`.
|
||||
====
|
||||
|
||||
By default `Container.getDockerImageName()` is used to obtain the name used to find connection details.
|
||||
If you are using a custom docker image, you can use the `name` attribute of `@ServiceConnection` to override it.
|
||||
|
||||
For example, if you have a `GenericContainer` using a Docker image of `registry.mycompany.com/mirror/myredis`, you'd use `@ServiceConnection(name="redis")` to ensure `RedisConnectionDetails` are created.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -22,14 +22,14 @@ import org.testcontainers.junit.jupiter.Container;
|
||||
import org.testcontainers.junit.jupiter.Testcontainers;
|
||||
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.testcontainers.service.connection.neo4j.Neo4jServiceConnection;
|
||||
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
|
||||
|
||||
@SpringBootTest
|
||||
@Testcontainers
|
||||
class MyIntegrationTests {
|
||||
|
||||
@Container
|
||||
@Neo4jServiceConnection
|
||||
@ServiceConnection
|
||||
static Neo4jContainer<?> neo4j = new Neo4jContainer<>("neo4j:5");
|
||||
|
||||
@Test
|
||||
|
||||
@@ -17,14 +17,13 @@
|
||||
package org.springframework.boot.docs.howto.testing.testcontainers.serviceconnection
|
||||
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.springframework.boot.test.context.SpringBootTest
|
||||
import org.springframework.boot.testcontainers.service.connection.neo4j.Neo4jServiceConnection;
|
||||
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
|
||||
|
||||
import org.springframework.boot.test.context.SpringBootTest
|
||||
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
|
||||
|
||||
@SpringBootTest
|
||||
@Testcontainers
|
||||
internal class MyIntegrationTests {
|
||||
@@ -37,7 +36,7 @@ internal class MyIntegrationTests {
|
||||
companion object {
|
||||
|
||||
@Container
|
||||
@Neo4jServiceConnection
|
||||
@ServiceConnection
|
||||
var neo4j: Neo4jContainer<*> = Neo4jContainer<Nothing>("neo4j:5")
|
||||
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.autoconfigure.data.redis.ExampleService;
|
||||
import org.springframework.boot.test.context.TestConfiguration;
|
||||
import org.springframework.boot.testcontainers.service.connection.cassandra.CassandraServiceConnection;
|
||||
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
|
||||
import org.springframework.boot.testsupport.testcontainers.CassandraContainer;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -52,7 +52,7 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
class DataCassandraTestIntegrationTests {
|
||||
|
||||
@Container
|
||||
@CassandraServiceConnection
|
||||
@ServiceConnection
|
||||
static final CassandraContainer cassandra = new CassandraContainer();
|
||||
|
||||
@Autowired
|
||||
|
||||
@@ -26,7 +26,7 @@ import org.testcontainers.junit.jupiter.Testcontainers;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.TestConfiguration;
|
||||
import org.springframework.boot.testcontainers.service.connection.cassandra.CassandraServiceConnection;
|
||||
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
|
||||
import org.springframework.boot.testsupport.testcontainers.CassandraContainer;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ComponentScan.Filter;
|
||||
@@ -51,7 +51,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
class DataCassandraTestWithIncludeFilterIntegrationTests {
|
||||
|
||||
@Container
|
||||
@CassandraServiceConnection
|
||||
@ServiceConnection
|
||||
static final CassandraContainer cassandra = new CassandraContainer();
|
||||
|
||||
@Autowired
|
||||
|
||||
@@ -26,7 +26,7 @@ import org.testcontainers.junit.jupiter.Testcontainers;
|
||||
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.testcontainers.service.connection.couchbase.CouchbaseServiceConnection;
|
||||
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
|
||||
import org.springframework.boot.testsupport.testcontainers.DockerImageNames;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.data.couchbase.core.CouchbaseTemplate;
|
||||
@@ -50,7 +50,7 @@ class DataCouchbaseTestIntegrationTests {
|
||||
private static final String BUCKET_NAME = "cbbucket";
|
||||
|
||||
@Container
|
||||
@CouchbaseServiceConnection
|
||||
@ServiceConnection
|
||||
static final CouchbaseContainer couchbase = new CouchbaseContainer(DockerImageNames.couchbase())
|
||||
.withStartupAttempts(5)
|
||||
.withStartupTimeout(Duration.ofMinutes(10))
|
||||
|
||||
@@ -25,7 +25,7 @@ import org.testcontainers.junit.jupiter.Container;
|
||||
import org.testcontainers.junit.jupiter.Testcontainers;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.testcontainers.service.connection.couchbase.CouchbaseServiceConnection;
|
||||
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
|
||||
import org.springframework.boot.testsupport.testcontainers.DockerImageNames;
|
||||
import org.springframework.data.couchbase.core.ReactiveCouchbaseTemplate;
|
||||
|
||||
@@ -47,7 +47,7 @@ class DataCouchbaseTestReactiveIntegrationTests {
|
||||
private static final String BUCKET_NAME = "cbbucket";
|
||||
|
||||
@Container
|
||||
@CouchbaseServiceConnection
|
||||
@ServiceConnection
|
||||
static final CouchbaseContainer couchbase = new CouchbaseContainer(DockerImageNames.couchbase())
|
||||
.withStartupAttempts(5)
|
||||
.withStartupTimeout(Duration.ofMinutes(10))
|
||||
|
||||
@@ -25,7 +25,7 @@ import org.testcontainers.junit.jupiter.Container;
|
||||
import org.testcontainers.junit.jupiter.Testcontainers;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.testcontainers.service.connection.couchbase.CouchbaseServiceConnection;
|
||||
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
|
||||
import org.springframework.boot.testsupport.testcontainers.DockerImageNames;
|
||||
import org.springframework.context.annotation.ComponentScan.Filter;
|
||||
import org.springframework.stereotype.Service;
|
||||
@@ -48,7 +48,7 @@ class DataCouchbaseTestWithIncludeFilterIntegrationTests {
|
||||
private static final String BUCKET_NAME = "cbbucket";
|
||||
|
||||
@Container
|
||||
@CouchbaseServiceConnection
|
||||
@ServiceConnection
|
||||
static final CouchbaseContainer couchbase = new CouchbaseContainer(DockerImageNames.couchbase())
|
||||
.withStartupAttempts(5)
|
||||
.withStartupTimeout(Duration.ofMinutes(10))
|
||||
|
||||
@@ -26,7 +26,7 @@ import org.testcontainers.junit.jupiter.Testcontainers;
|
||||
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.testcontainers.service.connection.elasticsearch.ElasticsearchServiceConnection;
|
||||
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
|
||||
import org.springframework.boot.testsupport.testcontainers.DockerImageNames;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.data.elasticsearch.client.elc.ElasticsearchTemplate;
|
||||
@@ -47,7 +47,7 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
class DataElasticsearchTestIntegrationTests {
|
||||
|
||||
@Container
|
||||
@ElasticsearchServiceConnection
|
||||
@ServiceConnection
|
||||
static final ElasticsearchContainer elasticsearch = new ElasticsearchContainer(DockerImageNames.elasticsearch())
|
||||
.withStartupAttempts(5)
|
||||
.withStartupTimeout(Duration.ofMinutes(10));
|
||||
|
||||
@@ -25,7 +25,7 @@ import org.testcontainers.junit.jupiter.Container;
|
||||
import org.testcontainers.junit.jupiter.Testcontainers;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.testcontainers.service.connection.elasticsearch.ElasticsearchServiceConnection;
|
||||
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
|
||||
import org.springframework.boot.testsupport.testcontainers.DockerImageNames;
|
||||
import org.springframework.core.env.Environment;
|
||||
|
||||
@@ -45,7 +45,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
class DataElasticsearchTestPropertiesIntegrationTests {
|
||||
|
||||
@Container
|
||||
@ElasticsearchServiceConnection
|
||||
@ServiceConnection
|
||||
static final ElasticsearchContainer elasticsearch = new ElasticsearchContainer(DockerImageNames.elasticsearch())
|
||||
.withStartupAttempts(5)
|
||||
.withStartupTimeout(Duration.ofMinutes(10));
|
||||
|
||||
@@ -24,7 +24,7 @@ import org.testcontainers.junit.jupiter.Container;
|
||||
import org.testcontainers.junit.jupiter.Testcontainers;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.testcontainers.service.connection.elasticsearch.ElasticsearchServiceConnection;
|
||||
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
|
||||
import org.springframework.boot.testsupport.testcontainers.DockerImageNames;
|
||||
import org.springframework.data.elasticsearch.client.elc.ReactiveElasticsearchTemplate;
|
||||
|
||||
@@ -44,7 +44,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
class DataElasticsearchTestReactiveIntegrationTests {
|
||||
|
||||
@Container
|
||||
@ElasticsearchServiceConnection
|
||||
@ServiceConnection
|
||||
static final ElasticsearchContainer elasticsearch = new ElasticsearchContainer(DockerImageNames.elasticsearch())
|
||||
.withStartupAttempts(5)
|
||||
.withStartupTimeout(Duration.ofMinutes(10));
|
||||
|
||||
@@ -25,7 +25,7 @@ import org.testcontainers.junit.jupiter.Container;
|
||||
import org.testcontainers.junit.jupiter.Testcontainers;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.testcontainers.service.connection.elasticsearch.ElasticsearchServiceConnection;
|
||||
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
|
||||
import org.springframework.boot.testsupport.testcontainers.DockerImageNames;
|
||||
import org.springframework.context.annotation.ComponentScan.Filter;
|
||||
import org.springframework.stereotype.Service;
|
||||
@@ -46,7 +46,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
class DataElasticsearchTestWithIncludeFilterIntegrationTests {
|
||||
|
||||
@Container
|
||||
@ElasticsearchServiceConnection
|
||||
@ServiceConnection
|
||||
static final ElasticsearchContainer elasticsearch = new ElasticsearchContainer(DockerImageNames.elasticsearch())
|
||||
.withStartupAttempts(5)
|
||||
.withStartupTimeout(Duration.ofMinutes(10));
|
||||
|
||||
@@ -25,7 +25,7 @@ import org.testcontainers.junit.jupiter.Testcontainers;
|
||||
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.testcontainers.service.connection.mongo.MongoServiceConnection;
|
||||
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
|
||||
import org.springframework.boot.testsupport.testcontainers.DockerImageNames;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.data.mongodb.core.MongoTemplate;
|
||||
@@ -46,7 +46,7 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
class DataMongoTestIntegrationTests {
|
||||
|
||||
@Container
|
||||
@MongoServiceConnection
|
||||
@ServiceConnection
|
||||
static final MongoDBContainer mongoDB = new MongoDBContainer(DockerImageNames.mongo()).withStartupAttempts(5)
|
||||
.withStartupTimeout(Duration.ofMinutes(5));
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ import org.testcontainers.junit.jupiter.Container;
|
||||
import org.testcontainers.junit.jupiter.Testcontainers;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.testcontainers.service.connection.mongo.MongoServiceConnection;
|
||||
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
|
||||
import org.springframework.boot.testsupport.testcontainers.DockerImageNames;
|
||||
import org.springframework.data.mongodb.core.ReactiveMongoTemplate;
|
||||
|
||||
@@ -43,7 +43,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
class DataMongoTestReactiveIntegrationTests {
|
||||
|
||||
@Container
|
||||
@MongoServiceConnection
|
||||
@ServiceConnection
|
||||
static final MongoDBContainer mongoDB = new MongoDBContainer(DockerImageNames.mongo()).withStartupAttempts(5)
|
||||
.withStartupTimeout(Duration.ofMinutes(5));
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ import org.testcontainers.junit.jupiter.Container;
|
||||
import org.testcontainers.junit.jupiter.Testcontainers;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.testcontainers.service.connection.mongo.MongoServiceConnection;
|
||||
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
|
||||
import org.springframework.boot.testsupport.testcontainers.DockerImageNames;
|
||||
import org.springframework.context.annotation.ComponentScan.Filter;
|
||||
import org.springframework.stereotype.Service;
|
||||
@@ -44,7 +44,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
class DataMongoTestWithIncludeFilterIntegrationTests {
|
||||
|
||||
@Container
|
||||
@MongoServiceConnection
|
||||
@ServiceConnection
|
||||
static final MongoDBContainer mongoDB = new MongoDBContainer(DockerImageNames.mongo()).withStartupAttempts(5)
|
||||
.withStartupTimeout(Duration.ofMinutes(5));
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ import org.testcontainers.junit.jupiter.Testcontainers;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.TestConfiguration;
|
||||
import org.springframework.boot.testcontainers.service.connection.mongo.MongoServiceConnection;
|
||||
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
|
||||
import org.springframework.boot.testsupport.testcontainers.DockerImageNames;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.data.mongodb.MongoDatabaseFactory;
|
||||
@@ -49,7 +49,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
class TransactionalDataMongoTestIntegrationTests {
|
||||
|
||||
@Container
|
||||
@MongoServiceConnection
|
||||
@ServiceConnection
|
||||
static final MongoDBContainer mongoDB = new MongoDBContainer(DockerImageNames.mongo()).withStartupAttempts(5)
|
||||
.withStartupTimeout(Duration.ofMinutes(5));
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ import org.testcontainers.junit.jupiter.Testcontainers;
|
||||
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.testcontainers.service.connection.neo4j.Neo4jServiceConnection;
|
||||
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
|
||||
import org.springframework.boot.testsupport.testcontainers.DockerImageNames;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.data.neo4j.core.Neo4jTemplate;
|
||||
@@ -48,7 +48,7 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
class DataNeo4jTestIntegrationTests {
|
||||
|
||||
@Container
|
||||
@Neo4jServiceConnection
|
||||
@ServiceConnection
|
||||
static final Neo4jContainer<?> neo4j = new Neo4jContainer<>(DockerImageNames.neo4j()).withStartupAttempts(5)
|
||||
.withStartupTimeout(Duration.ofMinutes(10));
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ import org.testcontainers.junit.jupiter.Container;
|
||||
import org.testcontainers.junit.jupiter.Testcontainers;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.testcontainers.service.connection.neo4j.Neo4jServiceConnection;
|
||||
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
|
||||
import org.springframework.boot.testsupport.testcontainers.DockerImageNames;
|
||||
import org.springframework.core.env.Environment;
|
||||
|
||||
@@ -45,7 +45,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
class DataNeo4jTestPropertiesIntegrationTests {
|
||||
|
||||
@Container
|
||||
@Neo4jServiceConnection
|
||||
@ServiceConnection
|
||||
static final Neo4jContainer<?> neo4j = new Neo4jContainer<>(DockerImageNames.neo4j()).withoutAuthentication()
|
||||
.withStartupAttempts(5)
|
||||
.withStartupTimeout(Duration.ofMinutes(10));
|
||||
|
||||
@@ -29,7 +29,7 @@ import reactor.test.StepVerifier;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.TestConfiguration;
|
||||
import org.springframework.boot.testcontainers.service.connection.neo4j.Neo4jServiceConnection;
|
||||
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
|
||||
import org.springframework.boot.testsupport.testcontainers.DockerImageNames;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -56,7 +56,7 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
class DataNeo4jTestReactiveIntegrationTests {
|
||||
|
||||
@Container
|
||||
@Neo4jServiceConnection
|
||||
@ServiceConnection
|
||||
static final Neo4jContainer<?> neo4j = new Neo4jContainer<>(DockerImageNames.neo4j()).withoutAuthentication()
|
||||
.withStartupAttempts(5)
|
||||
.withStartupTimeout(Duration.ofMinutes(10));
|
||||
|
||||
@@ -24,7 +24,7 @@ import org.testcontainers.junit.jupiter.Container;
|
||||
import org.testcontainers.junit.jupiter.Testcontainers;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.testcontainers.service.connection.neo4j.Neo4jServiceConnection;
|
||||
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
|
||||
import org.springframework.boot.testsupport.testcontainers.DockerImageNames;
|
||||
import org.springframework.context.annotation.ComponentScan.Filter;
|
||||
import org.springframework.stereotype.Service;
|
||||
@@ -45,7 +45,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
class DataNeo4jTestWithIncludeFilterIntegrationTests {
|
||||
|
||||
@Container
|
||||
@Neo4jServiceConnection
|
||||
@ServiceConnection
|
||||
static final Neo4jContainer<?> neo4j = new Neo4jContainer<>(DockerImageNames.neo4j()).withoutAuthentication()
|
||||
.withStartupAttempts(5)
|
||||
.withStartupTimeout(Duration.ofMinutes(10));
|
||||
|
||||
@@ -25,7 +25,7 @@ import org.testcontainers.junit.jupiter.Testcontainers;
|
||||
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.testcontainers.service.connection.redis.RedisServiceConnection;
|
||||
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
|
||||
import org.springframework.boot.testsupport.testcontainers.RedisContainer;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.data.redis.core.RedisOperations;
|
||||
@@ -48,7 +48,7 @@ class DataRedisTestIntegrationTests {
|
||||
private static final Charset CHARSET = StandardCharsets.UTF_8;
|
||||
|
||||
@Container
|
||||
@RedisServiceConnection
|
||||
@ServiceConnection
|
||||
static RedisContainer redis = new RedisContainer();
|
||||
|
||||
@Autowired
|
||||
|
||||
@@ -22,7 +22,7 @@ import org.testcontainers.junit.jupiter.Container;
|
||||
import org.testcontainers.junit.jupiter.Testcontainers;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.testcontainers.service.connection.redis.RedisServiceConnection;
|
||||
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
|
||||
import org.springframework.boot.testsupport.testcontainers.RedisContainer;
|
||||
import org.springframework.core.env.Environment;
|
||||
|
||||
@@ -42,7 +42,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
class DataRedisTestPropertiesIntegrationTests {
|
||||
|
||||
@Container
|
||||
@RedisServiceConnection
|
||||
@ServiceConnection
|
||||
static final RedisContainer redis = new RedisContainer();
|
||||
|
||||
@Autowired
|
||||
|
||||
@@ -25,7 +25,7 @@ import reactor.test.StepVerifier;
|
||||
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.testcontainers.service.connection.redis.RedisServiceConnection;
|
||||
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
|
||||
import org.springframework.boot.testsupport.testcontainers.RedisContainer;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.data.redis.core.ReactiveRedisOperations;
|
||||
@@ -45,7 +45,7 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
class DataRedisTestReactiveIntegrationTests {
|
||||
|
||||
@Container
|
||||
@RedisServiceConnection
|
||||
@ServiceConnection
|
||||
static RedisContainer redis = new RedisContainer();
|
||||
|
||||
@Autowired
|
||||
|
||||
@@ -21,7 +21,7 @@ import org.testcontainers.junit.jupiter.Container;
|
||||
import org.testcontainers.junit.jupiter.Testcontainers;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.testcontainers.service.connection.redis.RedisServiceConnection;
|
||||
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
|
||||
import org.springframework.boot.testsupport.testcontainers.RedisContainer;
|
||||
import org.springframework.context.annotation.ComponentScan.Filter;
|
||||
import org.springframework.stereotype.Service;
|
||||
@@ -41,7 +41,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
class DataRedisTestWithIncludeFilterIntegrationTests {
|
||||
|
||||
@Container
|
||||
@RedisServiceConnection
|
||||
@ServiceConnection
|
||||
static final RedisContainer redis = new RedisContainer();
|
||||
|
||||
@Autowired
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
plugins {
|
||||
id "java-library"
|
||||
id "org.springframework.boot.auto-configuration"
|
||||
id "org.springframework.boot.conventions"
|
||||
id "org.springframework.boot.deployed"
|
||||
id "org.springframework.boot.optional-dependencies"
|
||||
@@ -28,6 +29,7 @@ dependencies {
|
||||
optional("org.testcontainers:rabbitmq")
|
||||
optional("org.testcontainers:r2dbc")
|
||||
|
||||
testImplementation(project(":spring-boot-project:spring-boot-tools:spring-boot-test-support"))
|
||||
testImplementation("ch.qos.logback:logback-classic")
|
||||
testImplementation("org.assertj:assertj-core")
|
||||
testImplementation("org.awaitility:awaitility")
|
||||
|
||||
@@ -16,9 +16,9 @@
|
||||
|
||||
package org.springframework.boot.testcontainers.service.connection;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.testcontainers.containers.GenericContainer;
|
||||
import org.testcontainers.containers.Container;
|
||||
|
||||
import org.springframework.boot.autoconfigure.service.connection.ConnectionDetails;
|
||||
import org.springframework.boot.autoconfigure.service.connection.ConnectionDetailsFactory;
|
||||
@@ -26,13 +26,13 @@ import org.springframework.boot.origin.Origin;
|
||||
import org.springframework.boot.origin.OriginProvider;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* 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
|
||||
@@ -40,17 +40,58 @@ import org.springframework.util.Assert;
|
||||
* @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> {
|
||||
public abstract class ContainerConnectionDetailsFactory<D extends ConnectionDetails, C extends Container<?>>
|
||||
implements ConnectionDetailsFactory<ContainerConnectionSource<D, C>, D> {
|
||||
|
||||
/**
|
||||
* Constant passed to the constructor when any connection name is accepted.
|
||||
*/
|
||||
protected static final String ANY_CONNECTION_NAME = null;
|
||||
|
||||
private final String connectionName;
|
||||
|
||||
private final String[] requiredClassNames;
|
||||
|
||||
/**
|
||||
* Create a new {@link ContainerConnectionDetailsFactory} instance that accepts
|
||||
* {@link #ANY_CONNECTION_NAME any connection name}.
|
||||
*/
|
||||
protected ContainerConnectionDetailsFactory() {
|
||||
this(ANY_CONNECTION_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link ContainerConnectionDetailsFactory} instance with the given
|
||||
* connection name restriction.
|
||||
* @param connectionName the required connection name or {@link #ANY_CONNECTION_NAME}
|
||||
* @param requiredClassNames the names of classes that must be present
|
||||
*/
|
||||
protected ContainerConnectionDetailsFactory(String connectionName, String... requiredClassNames) {
|
||||
this.connectionName = connectionName;
|
||||
this.requiredClassNames = requiredClassNames;
|
||||
}
|
||||
|
||||
@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);
|
||||
public final D getConnectionDetails(ContainerConnectionSource<D, C> source) {
|
||||
if (!hasRequiredClasses()) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
Class<?>[] generics = resolveGenerics();
|
||||
Class<?> connectionDetailsType = generics[0];
|
||||
Class<?> containerType = generics[1];
|
||||
if (source.accepts(this.connectionName, connectionDetailsType, containerType)) {
|
||||
return getContainerConnectionDetails(source);
|
||||
}
|
||||
}
|
||||
catch (NoClassDefFoundError ex) {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean hasRequiredClasses() {
|
||||
return ObjectUtils.isEmpty(this.requiredClassNames) || Arrays.stream(this.requiredClassNames)
|
||||
.allMatch((requiredClassName) -> ClassUtils.isPresent(requiredClassName, null));
|
||||
}
|
||||
|
||||
private Class<?>[] resolveGenerics() {
|
||||
@@ -64,7 +105,7 @@ public abstract class ContainerConnectionDetailsFactory<A extends Annotation, D
|
||||
* @param source the source
|
||||
* @return the service connection or {@code null}.
|
||||
*/
|
||||
protected abstract D getContainerConnectionDetails(ContainerConnectionSource<A, D, C> source);
|
||||
protected abstract D getContainerConnectionDetails(ContainerConnectionSource<D, C> source);
|
||||
|
||||
/**
|
||||
* Convenient base class for {@link ConnectionDetails} results that are backed by a
|
||||
@@ -78,7 +119,7 @@ public abstract class ContainerConnectionDetailsFactory<A extends Annotation, D
|
||||
* Create a new {@link ContainerConnectionDetails} instance.
|
||||
* @param source the source {@link ContainerConnectionSource}
|
||||
*/
|
||||
protected ContainerConnectionDetails(ContainerConnectionSource<?, ?, ?> source) {
|
||||
protected ContainerConnectionDetails(ContainerConnectionSource<?, ?> source) {
|
||||
Assert.notNull(source, "Source must not be null");
|
||||
this.origin = source.getOrigin();
|
||||
}
|
||||
|
||||
@@ -16,23 +16,25 @@
|
||||
|
||||
package org.springframework.boot.testcontainers.service.connection;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Set;
|
||||
|
||||
import org.testcontainers.containers.GenericContainer;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.testcontainers.containers.Container;
|
||||
import org.testcontainers.utility.DockerImageName;
|
||||
|
||||
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;
|
||||
import org.springframework.core.log.LogMessage;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Passed to {@link ContainerConnectionDetailsFactory} to provide details of the
|
||||
* {@link ServiceConnection @ServiceConnection} annotation {@link GenericContainer} field
|
||||
* that provides the service.
|
||||
* {@link ServiceConnection @ServiceConnection} annotated {@link Container} 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
|
||||
@@ -41,58 +43,77 @@ import org.springframework.core.annotation.MergedAnnotation;
|
||||
* @since 3.1.0
|
||||
* @see ContainerConnectionDetailsFactory
|
||||
*/
|
||||
public final class ContainerConnectionSource<A extends Annotation, D extends ConnectionDetails, C extends GenericContainer<?>>
|
||||
public final class ContainerConnectionSource<D extends ConnectionDetails, C extends Container<?>>
|
||||
implements OriginProvider {
|
||||
|
||||
private final Class<D> connectionDetailsType;
|
||||
private static final Log logger = LogFactory.getLog(ContainerConnectionSource.class);
|
||||
|
||||
private final Field field;
|
||||
private final String beanNameSuffix;
|
||||
|
||||
private final A annotation;
|
||||
private final Origin origin;
|
||||
|
||||
private final C container;
|
||||
|
||||
private final AnnotatedFieldOrigin origin;
|
||||
private String acceptedConnectionName;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
ContainerConnectionSource(Class<D> connectionDetailsType, Field field,
|
||||
MergedAnnotation<ServiceConnection> annotation, C container) {
|
||||
this(connectionDetailsType, field, (A) annotation.getRoot().synthesize(), container);
|
||||
}
|
||||
private Set<Class<?>> acceptedConnectionDetailsTypes;
|
||||
|
||||
ContainerConnectionSource(Class<D> connectionDetailsType, Field field, A annotation, C container) {
|
||||
this.connectionDetailsType = connectionDetailsType;
|
||||
this.field = field;
|
||||
this.annotation = annotation;
|
||||
ContainerConnectionSource(String beanNameSuffix, Origin origin, C container,
|
||||
MergedAnnotation<ServiceConnection> annotation) {
|
||||
this.beanNameSuffix = beanNameSuffix;
|
||||
this.origin = origin;
|
||||
this.container = container;
|
||||
this.origin = new AnnotatedFieldOrigin(field, annotation);
|
||||
this.acceptedConnectionName = getConnectionName(container, annotation.getString("name"));
|
||||
this.acceptedConnectionDetailsTypes = Set.of(annotation.getClassArray("type"));
|
||||
}
|
||||
|
||||
boolean accepts(Class<?> annotationType, Class<?> connectionDetailsType, Class<?> containerType) {
|
||||
return annotationType.isInstance(this.annotation)
|
||||
&& connectionDetailsType.isAssignableFrom(this.connectionDetailsType)
|
||||
&& containerType.isInstance(this.container);
|
||||
ContainerConnectionSource(String beanNameSuffix, Origin origin, C container, ServiceConnection annotation) {
|
||||
this.beanNameSuffix = beanNameSuffix;
|
||||
this.origin = origin;
|
||||
this.container = container;
|
||||
this.acceptedConnectionName = getConnectionName(container, annotation.name());
|
||||
this.acceptedConnectionDetailsTypes = Set.of(annotation.type());
|
||||
}
|
||||
|
||||
String getBeanName() {
|
||||
return this.field.getName() + this.connectionDetailsType.getSimpleName() + "ConnectedContainer";
|
||||
private static String getConnectionName(Container<?> container, String connectionName) {
|
||||
if (StringUtils.hasLength(connectionName)) {
|
||||
return connectionName;
|
||||
}
|
||||
try {
|
||||
DockerImageName imageName = DockerImageName.parse(container.getDockerImageName());
|
||||
imageName.assertValid();
|
||||
return imageName.getRepository();
|
||||
}
|
||||
catch (IllegalArgumentException ex) {
|
||||
return container.getDockerImageName();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
boolean accepts(String connectionName, Class<?> connectionDetailsType, Class<?> containerType) {
|
||||
if (!containerType.isInstance(this.container)) {
|
||||
logger.trace(LogMessage.of(() -> "%s not accepted as %s is not an instance of %s".formatted(this,
|
||||
this.container.getClass().getName(), containerType.getName())));
|
||||
return false;
|
||||
}
|
||||
if (StringUtils.hasLength(connectionName) && !connectionName.equalsIgnoreCase(this.acceptedConnectionName)) {
|
||||
logger.trace(LogMessage.of(() -> "%s not accepted as connection names '%s' and '%s' do not match"
|
||||
.formatted(this, connectionName, this.acceptedConnectionName)));
|
||||
return false;
|
||||
}
|
||||
if (!this.acceptedConnectionDetailsTypes.isEmpty() && !this.acceptedConnectionDetailsTypes.stream()
|
||||
.anyMatch((candidate) -> candidate.isAssignableFrom(connectionDetailsType))) {
|
||||
logger.trace(LogMessage.of(() -> "%s not accepted as connection details type %s not in %s".formatted(this,
|
||||
connectionDetailsType, this.acceptedConnectionDetailsTypes)));
|
||||
return false;
|
||||
}
|
||||
logger.trace(LogMessage
|
||||
.of(() -> "%s accepted for connection name '%s', connection details type %s, container type %s"
|
||||
.formatted(this, connectionName, connectionDetailsType.getName(), containerType.getName())));
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link GenericContainer} that implements the service being connected to.
|
||||
* @return the {@link GenericContainer} providing the service
|
||||
*/
|
||||
public C getContainer() {
|
||||
return this.container;
|
||||
String getBeanNameSuffix() {
|
||||
return this.beanNameSuffix;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -100,9 +121,17 @@ public final class ContainerConnectionSource<A extends Annotation, D extends Con
|
||||
return this.origin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link Container} that implements the service being connected to.
|
||||
* @return the {@link Container} providing the service
|
||||
*/
|
||||
public C getContainer() {
|
||||
return this.container;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ServiceConnectedContainer for %s".formatted(this.origin);
|
||||
return "@ServiceConnection source for %s".formatted(this.origin);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* 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.testcontainers.service.connection;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.factory.ListableBeanFactory;
|
||||
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.core.log.LogMessage;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Class used to register bean definitions from a list of
|
||||
* {@link ContainerConnectionSource} instances.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class ContainerConnectionSourcesRegistrar {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(ContainerConnectionSourcesRegistrar.class);
|
||||
|
||||
private final ListableBeanFactory beanFactory;
|
||||
|
||||
private final ConnectionDetailsFactories connectionDetailsFactories;
|
||||
|
||||
private final List<ContainerConnectionSource<?, ?>> sources;
|
||||
|
||||
ContainerConnectionSourcesRegistrar(ListableBeanFactory beanFactory,
|
||||
ConnectionDetailsFactories connectionDetailsFactories, List<ContainerConnectionSource<?, ?>> sources) {
|
||||
this.beanFactory = beanFactory;
|
||||
this.connectionDetailsFactories = connectionDetailsFactories;
|
||||
this.sources = sources;
|
||||
}
|
||||
|
||||
void registerBeanDefinitions(BeanDefinitionRegistry registry) {
|
||||
this.sources.forEach((source) -> registerBeanDefinition(registry, source));
|
||||
}
|
||||
|
||||
private void registerBeanDefinition(BeanDefinitionRegistry registry, ContainerConnectionSource<?, ?> source) {
|
||||
getConnectionDetails(source)
|
||||
.forEach((connectionDetailsType, connectionDetails) -> registerBeanDefinition(registry, source,
|
||||
connectionDetailsType, connectionDetails));
|
||||
}
|
||||
|
||||
private <S> Map<Class<?>, ConnectionDetails> getConnectionDetails(S source) {
|
||||
Map<Class<?>, ConnectionDetails> connectionDetails = this.connectionDetailsFactories
|
||||
.getConnectionDetails(source);
|
||||
Assert.state(!connectionDetails.isEmpty(), () -> "No connection details created for %s".formatted(source));
|
||||
return connectionDetails;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> void registerBeanDefinition(BeanDefinitionRegistry registry, ContainerConnectionSource<?, ?> source,
|
||||
Class<?> connectionDetailsType, ConnectionDetails connectionDetails) {
|
||||
String[] existingBeans = this.beanFactory.getBeanNamesForType(connectionDetailsType);
|
||||
if (!ObjectUtils.isEmpty(existingBeans)) {
|
||||
logger.debug(LogMessage.of(() -> "Skipping registration of %s due to existing beans %s".formatted(source,
|
||||
Arrays.asList(existingBeans))));
|
||||
return;
|
||||
}
|
||||
String beanName = getBeanName(source, connectionDetails);
|
||||
Class<T> beanType = (Class<T>) connectionDetails.getClass();
|
||||
Supplier<T> beanSupplier = () -> (T) connectionDetails;
|
||||
logger.debug(LogMessage.of(() -> "Registering '%s' for %s".formatted(beanName, source)));
|
||||
registry.registerBeanDefinition(beanName, new RootBeanDefinition(beanType, beanSupplier));
|
||||
}
|
||||
|
||||
private String getBeanName(ContainerConnectionSource<?, ?> source, ConnectionDetails connectionDetails) {
|
||||
List<String> parts = new ArrayList<>();
|
||||
parts.add(ClassUtils.getShortNameAsProperty(connectionDetails.getClass()));
|
||||
parts.add("for");
|
||||
parts.add(source.getBeanNameSuffix());
|
||||
return StringUtils.uncapitalize(parts.stream().map(StringUtils::capitalize).collect(Collectors.joining()));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,31 +16,26 @@
|
||||
|
||||
package org.springframework.boot.testcontainers.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;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* {@link Origin} backed by a {@link Field} and an {@link Annotation}.
|
||||
* {@link Origin} backed by a {@link Field}.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class AnnotatedFieldOrigin implements Origin {
|
||||
class FieldOrigin implements Origin {
|
||||
|
||||
private final Field field;
|
||||
|
||||
private final Annotation annotation;
|
||||
|
||||
AnnotatedFieldOrigin(Field field, Annotation annotation) {
|
||||
FieldOrigin(Field field) {
|
||||
Assert.notNull(field, "Field must not be null");
|
||||
Assert.notNull(annotation, "Annotation must not be null");
|
||||
this.field = field;
|
||||
this.annotation = annotation;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -51,18 +46,18 @@ class AnnotatedFieldOrigin implements Origin {
|
||||
if (obj == null || getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
AnnotatedFieldOrigin other = (AnnotatedFieldOrigin) obj;
|
||||
return this.field.equals(other.field) && this.annotation.equals(other.annotation);
|
||||
FieldOrigin other = (FieldOrigin) obj;
|
||||
return this.field.equals(other.field);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(this.field, this.annotation);
|
||||
return this.field.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.annotation + " " + this.field;
|
||||
return ClassUtils.getShortName(this.field.getDeclaringClass()) + "." + this.field.getName();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -21,15 +21,14 @@ import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.testcontainers.containers.Container;
|
||||
|
||||
import org.springframework.boot.autoconfigure.service.connection.ConnectionDetails;
|
||||
import org.springframework.boot.autoconfigure.service.connection.ConnectionDetailsFactory;
|
||||
import org.springframework.core.annotation.AliasFor;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* Annotation used to indicate that a field or method is a
|
||||
* {@link ContainerConnectionSource} which provides a service that can be connected to.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
@@ -37,14 +36,35 @@ import org.springframework.boot.autoconfigure.service.connection.ConnectionDetai
|
||||
* @since 3.1.0
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ ElementType.FIELD, ElementType.ANNOTATION_TYPE })
|
||||
@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE })
|
||||
public @interface ServiceConnection {
|
||||
|
||||
/**
|
||||
* The type of {@link ConnectionDetails} that can describe how to connect to the
|
||||
* service.
|
||||
* @return the connection type
|
||||
* The name of the service being connected to. If not specified, the image name will
|
||||
* be used. Container names are used to determine the connection details that should
|
||||
* be created when a technology-specific {@link Container} subclass is not available.
|
||||
* This attribute is an alias for {@link #name()}.
|
||||
* @return the name of the service
|
||||
* @see #name()
|
||||
*/
|
||||
Class<? extends ConnectionDetails> value();
|
||||
@AliasFor("name")
|
||||
String value() default "";
|
||||
|
||||
/**
|
||||
* The name of the service being connected to. If not specified, the image name will
|
||||
* be used. Container names are used to determine the connection details that should
|
||||
* be created when a technology-specific {@link Container} subclass is not available.
|
||||
* @return the name of the service
|
||||
* @see #value()
|
||||
*/
|
||||
@AliasFor("value")
|
||||
String name() default "";
|
||||
|
||||
/**
|
||||
* A restriction to types of {@link ConnectionDetails} that can be created from this
|
||||
* connection. The default value does not restrict the types that can be created.
|
||||
* @return the connection detail types that can be created to establish the connection
|
||||
*/
|
||||
Class<? extends ConnectionDetails>[] type() default {};
|
||||
|
||||
}
|
||||
|
||||
@@ -17,21 +17,17 @@
|
||||
package org.springframework.boot.testcontainers.service.connection;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
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.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}.
|
||||
* Spring Test {@link ContextCustomizer} to support registering {@link ConnectionDetails}.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
@@ -39,46 +35,30 @@ import org.springframework.util.Assert;
|
||||
*/
|
||||
class ServiceConnectionContextCustomizer implements ContextCustomizer {
|
||||
|
||||
private final ConnectionDetailsFactories factories = new ConnectionDetailsFactories();
|
||||
private final List<ContainerConnectionSource<?, ?>> sources;
|
||||
|
||||
private final List<ContainerConnectionSource<?, ?, ?>> sources;
|
||||
private final ConnectionDetailsFactories connectionDetailsFactories;
|
||||
|
||||
ServiceConnectionContextCustomizer(List<ContainerConnectionSource<?, ?, ?>> sources) {
|
||||
ServiceConnectionContextCustomizer(List<ContainerConnectionSource<?, ?>> sources) {
|
||||
this(sources, new ConnectionDetailsFactories());
|
||||
}
|
||||
|
||||
ServiceConnectionContextCustomizer(List<ContainerConnectionSource<?, ?>> sources,
|
||||
ConnectionDetailsFactories connectionDetailsFactories) {
|
||||
this.sources = sources;
|
||||
this.connectionDetailsFactories = connectionDetailsFactories;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void customizeContext(ConfigurableApplicationContext context, MergedContextConfiguration mergedConfig) {
|
||||
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
|
||||
if (beanFactory instanceof BeanDefinitionRegistry registry) {
|
||||
registerServiceConnections(registry);
|
||||
new ContainerConnectionSourcesRegistrar(beanFactory, this.connectionDetailsFactories, this.sources)
|
||||
.registerBeanDefinitions(registry);
|
||||
}
|
||||
}
|
||||
|
||||
private void registerServiceConnections(BeanDefinitionRegistry registry) {
|
||||
this.sources.forEach((source) -> registerServiceConnection(registry, source));
|
||||
}
|
||||
|
||||
private void registerServiceConnection(BeanDefinitionRegistry registry, ContainerConnectionSource<?, ?, ?> source) {
|
||||
ConnectionDetails connectionDetails = getConnectionDetails(source);
|
||||
register(connectionDetails, registry, source.getBeanName());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> void register(ConnectionDetails connectionDetails, BeanDefinitionRegistry registry, String beanName) {
|
||||
Class<T> beanType = (Class<T>) connectionDetails.getClass();
|
||||
Supplier<T> beanSupplier = () -> (T) connectionDetails;
|
||||
BeanDefinition beanDefinition = new RootBeanDefinition(beanType, beanSupplier);
|
||||
registry.registerBeanDefinition(beanName, beanDefinition);
|
||||
}
|
||||
|
||||
private <S> ConnectionDetails getConnectionDetails(S source) {
|
||||
ConnectionDetails connectionDetails = this.factories.getConnectionDetails(source);
|
||||
Assert.state(connectionDetails != null, () -> "No connection details created for %s".formatted(source));
|
||||
return connectionDetails;
|
||||
}
|
||||
|
||||
List<ContainerConnectionSource<?, ?, ?>> getSources() {
|
||||
List<ContainerConnectionSource<?, ?>> getSources() {
|
||||
return this.sources;
|
||||
}
|
||||
|
||||
|
||||
@@ -21,9 +21,9 @@ import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.testcontainers.containers.GenericContainer;
|
||||
import org.testcontainers.containers.Container;
|
||||
|
||||
import org.springframework.boot.autoconfigure.service.connection.ConnectionDetails;
|
||||
import org.springframework.boot.origin.Origin;
|
||||
import org.springframework.core.annotation.MergedAnnotation;
|
||||
import org.springframework.core.annotation.MergedAnnotations;
|
||||
import org.springframework.test.context.ContextConfigurationAttributes;
|
||||
@@ -31,10 +31,14 @@ import org.springframework.test.context.ContextCustomizer;
|
||||
import org.springframework.test.context.ContextCustomizerFactory;
|
||||
import org.springframework.test.context.TestContextAnnotationUtils;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* {@link ContextCustomizerFactory} to support service connections in tests.
|
||||
* Spring Test {@link ContextCustomizerFactory} to support
|
||||
* {@link ServiceConnection @ServiceConnection} annotated {@link Container} fields in
|
||||
* tests.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
@@ -45,12 +49,12 @@ class ServiceConnectionContextCustomizerFactory implements ContextCustomizerFact
|
||||
@Override
|
||||
public ContextCustomizer createContextCustomizer(Class<?> testClass,
|
||||
List<ContextConfigurationAttributes> configAttributes) {
|
||||
List<ContainerConnectionSource<?, ?, ?>> sources = new ArrayList<>();
|
||||
List<ContainerConnectionSource<?, ?>> sources = new ArrayList<>();
|
||||
findSources(testClass, sources);
|
||||
return (sources.isEmpty()) ? null : new ServiceConnectionContextCustomizer(sources);
|
||||
}
|
||||
|
||||
private void findSources(Class<?> clazz, List<ContainerConnectionSource<?, ?, ?>> sources) {
|
||||
private void findSources(Class<?> clazz, List<ContainerConnectionSource<?, ?>> sources) {
|
||||
ReflectionUtils.doWithFields(clazz, (field) -> {
|
||||
MergedAnnotations annotations = MergedAnnotations.from(field);
|
||||
annotations.stream(ServiceConnection.class)
|
||||
@@ -61,23 +65,17 @@ class ServiceConnectionContextCustomizerFactory implements ContextCustomizerFact
|
||||
}
|
||||
}
|
||||
|
||||
private ContainerConnectionSource<?, ?, ?> createSource(Field field,
|
||||
MergedAnnotation<ServiceConnection> annotation) {
|
||||
if (!Modifier.isStatic(field.getModifiers())) {
|
||||
throw new IllegalStateException("@ServiceConnection field '%s' must be static".formatted(field.getName()));
|
||||
}
|
||||
Class<? extends ConnectionDetails> connectionDetailsType = getConnectionDetailsType(annotation);
|
||||
private ContainerConnectionSource<?, ?> createSource(Field field, MergedAnnotation<ServiceConnection> annotation) {
|
||||
Assert.state(Modifier.isStatic(field.getModifiers()),
|
||||
() -> "@ServiceConnection field '%s' must be static".formatted(field.getName()));
|
||||
String beanNameSuffix = StringUtils.capitalize(ClassUtils.getShortNameAsProperty(field.getDeclaringClass()))
|
||||
+ StringUtils.capitalize(field.getName());
|
||||
Origin origin = new FieldOrigin(field);
|
||||
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);
|
||||
Assert.state(Container.class.isInstance(fieldValue), () -> "Field '%s' in %s must be a %s"
|
||||
.formatted(field.getName(), field.getDeclaringClass().getName(), Container.class.getName()));
|
||||
Container<?> container = (Container<?>) fieldValue;
|
||||
return new ContainerConnectionSource<>(beanNameSuffix, origin, container, annotation);
|
||||
}
|
||||
|
||||
private Object getFieldValue(Field field) {
|
||||
|
||||
@@ -24,22 +24,23 @@ import org.testcontainers.containers.RabbitMQContainer;
|
||||
import org.springframework.boot.autoconfigure.amqp.RabbitConnectionDetails;
|
||||
import org.springframework.boot.testcontainers.service.connection.ContainerConnectionDetailsFactory;
|
||||
import org.springframework.boot.testcontainers.service.connection.ContainerConnectionSource;
|
||||
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
|
||||
|
||||
/**
|
||||
* {@link ContainerConnectionDetailsFactory} for
|
||||
* {@link RabbitServiceConnection @RabbitServiceConnection}-annotated
|
||||
* {@link RabbitMQContainer} fields.
|
||||
* {@link ContainerConnectionDetailsFactory} to create {@link RabbitConnectionDetails}
|
||||
* from a {@link ServiceConnection @ServiceConnection}-annotated
|
||||
* {@link RabbitMQContainer}.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class RabbitContainerConnectionDetailsFactory
|
||||
extends ContainerConnectionDetailsFactory<RabbitServiceConnection, RabbitConnectionDetails, RabbitMQContainer> {
|
||||
extends ContainerConnectionDetailsFactory<RabbitConnectionDetails, RabbitMQContainer> {
|
||||
|
||||
@Override
|
||||
protected RabbitConnectionDetails getContainerConnectionDetails(
|
||||
ContainerConnectionSource<RabbitServiceConnection, RabbitConnectionDetails, RabbitMQContainer> source) {
|
||||
ContainerConnectionSource<RabbitConnectionDetails, RabbitMQContainer> source) {
|
||||
return new RabbitMqContainerConnectionDetails(source);
|
||||
}
|
||||
|
||||
@@ -52,7 +53,7 @@ class RabbitContainerConnectionDetailsFactory
|
||||
private final RabbitMQContainer container;
|
||||
|
||||
private RabbitMqContainerConnectionDetails(
|
||||
ContainerConnectionSource<RabbitServiceConnection, RabbitConnectionDetails, RabbitMQContainer> source) {
|
||||
ContainerConnectionSource<RabbitConnectionDetails, RabbitMQContainer> source) {
|
||||
super(source);
|
||||
this.container = source.getContainer();
|
||||
}
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
* 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.testcontainers.service.connection.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.testcontainers.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 {
|
||||
|
||||
}
|
||||
@@ -23,22 +23,23 @@ import org.testcontainers.containers.CassandraContainer;
|
||||
import org.springframework.boot.autoconfigure.cassandra.CassandraConnectionDetails;
|
||||
import org.springframework.boot.testcontainers.service.connection.ContainerConnectionDetailsFactory;
|
||||
import org.springframework.boot.testcontainers.service.connection.ContainerConnectionSource;
|
||||
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
|
||||
|
||||
/**
|
||||
* {@link ContainerConnectionDetailsFactory} for
|
||||
* {@link CassandraServiceConnection @CassandraServiceConnection}-annotated
|
||||
* {@link CassandraContainer} fields.
|
||||
* {@link ContainerConnectionDetailsFactory} to create {@link CassandraConnectionDetails}
|
||||
* from a {@link ServiceConnection @ServiceConnection}-annotated
|
||||
* {@link CassandraContainer}.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class CassandraContainerConnectionDetailsFactory extends
|
||||
ContainerConnectionDetailsFactory<CassandraServiceConnection, CassandraConnectionDetails, CassandraContainer<?>> {
|
||||
class CassandraContainerConnectionDetailsFactory
|
||||
extends ContainerConnectionDetailsFactory<CassandraConnectionDetails, CassandraContainer<?>> {
|
||||
|
||||
@Override
|
||||
protected CassandraConnectionDetails getContainerConnectionDetails(
|
||||
ContainerConnectionSource<CassandraServiceConnection, CassandraConnectionDetails, CassandraContainer<?>> source) {
|
||||
ContainerConnectionSource<CassandraConnectionDetails, CassandraContainer<?>> source) {
|
||||
return new CassandraContainerConnectionDetails(source);
|
||||
}
|
||||
|
||||
@@ -51,7 +52,7 @@ class CassandraContainerConnectionDetailsFactory extends
|
||||
private final CassandraContainer<?> container;
|
||||
|
||||
private CassandraContainerConnectionDetails(
|
||||
ContainerConnectionSource<CassandraServiceConnection, CassandraConnectionDetails, CassandraContainer<?>> source) {
|
||||
ContainerConnectionSource<CassandraConnectionDetails, CassandraContainer<?>> source) {
|
||||
super(source);
|
||||
this.container = source.getContainer();
|
||||
}
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
* 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.testcontainers.service.connection.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.testcontainers.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 {
|
||||
|
||||
}
|
||||
@@ -21,22 +21,23 @@ import org.testcontainers.couchbase.CouchbaseContainer;
|
||||
import org.springframework.boot.autoconfigure.couchbase.CouchbaseConnectionDetails;
|
||||
import org.springframework.boot.testcontainers.service.connection.ContainerConnectionDetailsFactory;
|
||||
import org.springframework.boot.testcontainers.service.connection.ContainerConnectionSource;
|
||||
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
|
||||
|
||||
/**
|
||||
* {@link ContainerConnectionDetailsFactory} for
|
||||
* {@link CouchbaseServiceConnection @CouchbaseServiceConnection}-annotated
|
||||
* {@link CouchbaseContainer} fields.
|
||||
* {@link ContainerConnectionDetailsFactory} to create {@link CouchbaseConnectionDetails}
|
||||
* from a {@link ServiceConnection @ServiceConnection}-annotated
|
||||
* {@link CouchbaseContainer}.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class CouchbaseContainerConnectionDetailsFactory extends
|
||||
ContainerConnectionDetailsFactory<CouchbaseServiceConnection, CouchbaseConnectionDetails, CouchbaseContainer> {
|
||||
class CouchbaseContainerConnectionDetailsFactory
|
||||
extends ContainerConnectionDetailsFactory<CouchbaseConnectionDetails, CouchbaseContainer> {
|
||||
|
||||
@Override
|
||||
protected CouchbaseConnectionDetails getContainerConnectionDetails(
|
||||
ContainerConnectionSource<CouchbaseServiceConnection, CouchbaseConnectionDetails, CouchbaseContainer> source) {
|
||||
ContainerConnectionSource<CouchbaseConnectionDetails, CouchbaseContainer> source) {
|
||||
return new CouchbaseContainerConnectionDetails(source);
|
||||
}
|
||||
|
||||
@@ -49,7 +50,7 @@ class CouchbaseContainerConnectionDetailsFactory extends
|
||||
private final CouchbaseContainer container;
|
||||
|
||||
private CouchbaseContainerConnectionDetails(
|
||||
ContainerConnectionSource<CouchbaseServiceConnection, CouchbaseConnectionDetails, CouchbaseContainer> source) {
|
||||
ContainerConnectionSource<CouchbaseConnectionDetails, CouchbaseContainer> source) {
|
||||
super(source);
|
||||
this.container = source.getContainer();
|
||||
}
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
* 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.testcontainers.service.connection.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.testcontainers.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 {
|
||||
|
||||
}
|
||||
@@ -18,30 +18,31 @@ package org.springframework.boot.testcontainers.service.connection.elasticsearch
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.testcontainers.containers.GenericContainer;
|
||||
import org.testcontainers.elasticsearch.ElasticsearchContainer;
|
||||
|
||||
import org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchConnectionDetails;
|
||||
import org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchConnectionDetails.Node.Protocol;
|
||||
import org.springframework.boot.testcontainers.service.connection.ContainerConnectionDetailsFactory;
|
||||
import org.springframework.boot.testcontainers.service.connection.ContainerConnectionSource;
|
||||
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
|
||||
|
||||
/**
|
||||
* {@link ContainerConnectionDetailsFactory} for
|
||||
* {@link ElasticsearchServiceConnection @ElasticsearchServiceConnection}-annotated
|
||||
* {@link GenericContainer} fields.
|
||||
* {@link ContainerConnectionDetailsFactory} to create
|
||||
* {@link ElasticsearchConnectionDetails} from a
|
||||
* {@link ServiceConnection @ServiceConnection}-annotated {@link ElasticsearchContainer}.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class ElasticsearchContainerConnectionDetailsFactory extends
|
||||
ContainerConnectionDetailsFactory<ElasticsearchServiceConnection, ElasticsearchConnectionDetails, GenericContainer<?>> {
|
||||
class ElasticsearchContainerConnectionDetailsFactory
|
||||
extends ContainerConnectionDetailsFactory<ElasticsearchConnectionDetails, ElasticsearchContainer> {
|
||||
|
||||
private static final int DEFAULT_PORT = 9200;
|
||||
|
||||
@Override
|
||||
protected ElasticsearchConnectionDetails getContainerConnectionDetails(
|
||||
ContainerConnectionSource<ElasticsearchServiceConnection, ElasticsearchConnectionDetails, GenericContainer<?>> source) {
|
||||
ContainerConnectionSource<ElasticsearchConnectionDetails, ElasticsearchContainer> source) {
|
||||
return new ElasticsearchContainerConnectionDetails(source);
|
||||
}
|
||||
|
||||
@@ -55,7 +56,7 @@ class ElasticsearchContainerConnectionDetailsFactory extends
|
||||
private final List<Node> nodes;
|
||||
|
||||
private ElasticsearchContainerConnectionDetails(
|
||||
ContainerConnectionSource<ElasticsearchServiceConnection, ElasticsearchConnectionDetails, GenericContainer<?>> source) {
|
||||
ContainerConnectionSource<ElasticsearchConnectionDetails, ElasticsearchContainer> source) {
|
||||
super(source);
|
||||
this.nodes = List.of(new Node(source.getContainer().getHost(),
|
||||
source.getContainer().getMappedPort(DEFAULT_PORT), Protocol.HTTP, null, null));
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
* 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.testcontainers.service.connection.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.testcontainers.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 {
|
||||
|
||||
}
|
||||
@@ -24,18 +24,18 @@ import org.springframework.boot.testcontainers.service.connection.ContainerConne
|
||||
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
|
||||
|
||||
/**
|
||||
* {@link ContainerConnectionDetailsFactory} for
|
||||
* {@link ServiceConnection @ServiceConnection}-annotated {@link JdbcDatabaseContainer}
|
||||
* fields that should produce {@link FlywayConnectionDetails}.
|
||||
* {@link ContainerConnectionDetailsFactory} to create {@link FlywayConnectionDetails}
|
||||
* from a {@link ServiceConnection @ServiceConnection}-annotated
|
||||
* {@link JdbcDatabaseContainer}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class FlywayContainerConnectionDetailsFactory extends
|
||||
ContainerConnectionDetailsFactory<ServiceConnection, FlywayConnectionDetails, JdbcDatabaseContainer<?>> {
|
||||
class FlywayContainerConnectionDetailsFactory
|
||||
extends ContainerConnectionDetailsFactory<FlywayConnectionDetails, JdbcDatabaseContainer<?>> {
|
||||
|
||||
@Override
|
||||
protected FlywayConnectionDetails getContainerConnectionDetails(
|
||||
ContainerConnectionSource<ServiceConnection, FlywayConnectionDetails, JdbcDatabaseContainer<?>> source) {
|
||||
ContainerConnectionSource<FlywayConnectionDetails, JdbcDatabaseContainer<?>> source) {
|
||||
return new FlywayContainerConnectionDetails(source);
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ class FlywayContainerConnectionDetailsFactory extends
|
||||
private final JdbcDatabaseContainer<?> container;
|
||||
|
||||
private FlywayContainerConnectionDetails(
|
||||
ContainerConnectionSource<ServiceConnection, FlywayConnectionDetails, JdbcDatabaseContainer<?>> source) {
|
||||
ContainerConnectionSource<FlywayConnectionDetails, JdbcDatabaseContainer<?>> source) {
|
||||
super(source);
|
||||
this.container = source.getContainer();
|
||||
}
|
||||
|
||||
@@ -23,22 +23,23 @@ import org.testcontainers.containers.InfluxDBContainer;
|
||||
import org.springframework.boot.autoconfigure.influx.InfluxDbConnectionDetails;
|
||||
import org.springframework.boot.testcontainers.service.connection.ContainerConnectionDetailsFactory;
|
||||
import org.springframework.boot.testcontainers.service.connection.ContainerConnectionSource;
|
||||
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
|
||||
|
||||
/**
|
||||
* {@link ContainerConnectionDetailsFactory} for
|
||||
* {@link InfluxDbServiceConnection @InfluxDbServiceConnection}-annotated
|
||||
* {@link InfluxDBContainer} fields.
|
||||
* {@link ContainerConnectionDetailsFactory} to create {@link InfluxDbConnectionDetails}
|
||||
* from a {@link ServiceConnection @ServiceConnection}-annotated
|
||||
* {@link InfluxDBContainer}.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class InfluxDbContainerConnectionDetailsFactory extends
|
||||
ContainerConnectionDetailsFactory<InfluxDbServiceConnection, InfluxDbConnectionDetails, InfluxDBContainer<?>> {
|
||||
class InfluxDbContainerConnectionDetailsFactory
|
||||
extends ContainerConnectionDetailsFactory<InfluxDbConnectionDetails, InfluxDBContainer<?>> {
|
||||
|
||||
@Override
|
||||
protected InfluxDbConnectionDetails getContainerConnectionDetails(
|
||||
ContainerConnectionSource<InfluxDbServiceConnection, InfluxDbConnectionDetails, InfluxDBContainer<?>> source) {
|
||||
ContainerConnectionSource<InfluxDbConnectionDetails, InfluxDBContainer<?>> source) {
|
||||
return new InfluxDbContainerConnectionDetails(source);
|
||||
}
|
||||
|
||||
@@ -51,7 +52,7 @@ class InfluxDbContainerConnectionDetailsFactory extends
|
||||
private final InfluxDBContainer<?> container;
|
||||
|
||||
private InfluxDbContainerConnectionDetails(
|
||||
ContainerConnectionSource<InfluxDbServiceConnection, InfluxDbConnectionDetails, InfluxDBContainer<?>> source) {
|
||||
ContainerConnectionSource<InfluxDbConnectionDetails, InfluxDBContainer<?>> source) {
|
||||
super(source);
|
||||
this.container = source.getContainer();
|
||||
}
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
* 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.testcontainers.service.connection.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.testcontainers.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 {
|
||||
|
||||
}
|
||||
@@ -21,22 +21,22 @@ import org.testcontainers.containers.JdbcDatabaseContainer;
|
||||
import org.springframework.boot.autoconfigure.jdbc.JdbcConnectionDetails;
|
||||
import org.springframework.boot.testcontainers.service.connection.ContainerConnectionDetailsFactory;
|
||||
import org.springframework.boot.testcontainers.service.connection.ContainerConnectionSource;
|
||||
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
|
||||
|
||||
/**
|
||||
* {@link ContainerConnectionDetailsFactory} for
|
||||
* {@link JdbcServiceConnection @JdbcServiceConnection}-annotated
|
||||
* {@link JdbcDatabaseContainer} fields.
|
||||
* {@link ContainerConnectionDetailsFactory} to create {@link JdbcConnectionDetails} from
|
||||
* a {@link ServiceConnection @ServiceConnection}-annotated {@link JdbcDatabaseContainer}.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class JdbcContainerConnectionDetailsFactory extends
|
||||
ContainerConnectionDetailsFactory<JdbcServiceConnection, JdbcConnectionDetails, JdbcDatabaseContainer<?>> {
|
||||
class JdbcContainerConnectionDetailsFactory
|
||||
extends ContainerConnectionDetailsFactory<JdbcConnectionDetails, JdbcDatabaseContainer<?>> {
|
||||
|
||||
@Override
|
||||
protected JdbcConnectionDetails getContainerConnectionDetails(
|
||||
ContainerConnectionSource<JdbcServiceConnection, JdbcConnectionDetails, JdbcDatabaseContainer<?>> source) {
|
||||
ContainerConnectionSource<JdbcConnectionDetails, JdbcDatabaseContainer<?>> source) {
|
||||
return new JdbcContainerConnectionDetails(source);
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ class JdbcContainerConnectionDetailsFactory extends
|
||||
private final JdbcDatabaseContainer<?> container;
|
||||
|
||||
private JdbcContainerConnectionDetails(
|
||||
ContainerConnectionSource<JdbcServiceConnection, JdbcConnectionDetails, JdbcDatabaseContainer<?>> source) {
|
||||
ContainerConnectionSource<JdbcConnectionDetails, JdbcDatabaseContainer<?>> source) {
|
||||
super(source);
|
||||
this.container = source.getContainer();
|
||||
}
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
* 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.testcontainers.service.connection.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.testcontainers.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 {
|
||||
|
||||
}
|
||||
@@ -24,22 +24,22 @@ import org.testcontainers.containers.KafkaContainer;
|
||||
import org.springframework.boot.autoconfigure.kafka.KafkaConnectionDetails;
|
||||
import org.springframework.boot.testcontainers.service.connection.ContainerConnectionDetailsFactory;
|
||||
import org.springframework.boot.testcontainers.service.connection.ContainerConnectionSource;
|
||||
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
|
||||
|
||||
/**
|
||||
* {@link ContainerConnectionDetailsFactory} for
|
||||
* {@link KafkaServiceConnection @KafkaServiceConnection}-annotated {@link KafkaContainer}
|
||||
* fields.
|
||||
* {@link ContainerConnectionDetailsFactory} to create {@link KafkaConnectionDetails} from
|
||||
* a {@link ServiceConnection @ServiceConnection}-annotated {@link KafkaContainer}.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class KafkaContainerConnectionDetailsFactory
|
||||
extends ContainerConnectionDetailsFactory<KafkaServiceConnection, KafkaConnectionDetails, KafkaContainer> {
|
||||
extends ContainerConnectionDetailsFactory<KafkaConnectionDetails, KafkaContainer> {
|
||||
|
||||
@Override
|
||||
protected KafkaConnectionDetails getContainerConnectionDetails(
|
||||
ContainerConnectionSource<KafkaServiceConnection, KafkaConnectionDetails, KafkaContainer> source) {
|
||||
ContainerConnectionSource<KafkaConnectionDetails, KafkaContainer> source) {
|
||||
return new KafkaContainerConnectionDetails(source);
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ class KafkaContainerConnectionDetailsFactory
|
||||
private final KafkaContainer container;
|
||||
|
||||
private KafkaContainerConnectionDetails(
|
||||
ContainerConnectionSource<KafkaServiceConnection, KafkaConnectionDetails, KafkaContainer> source) {
|
||||
ContainerConnectionSource<KafkaConnectionDetails, KafkaContainer> source) {
|
||||
super(source);
|
||||
this.container = source.getContainer();
|
||||
}
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
* 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.testcontainers.service.connection.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.testcontainers.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 {
|
||||
|
||||
}
|
||||
@@ -24,18 +24,18 @@ import org.springframework.boot.testcontainers.service.connection.ContainerConne
|
||||
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
|
||||
|
||||
/**
|
||||
* {@link ContainerConnectionDetailsFactory} for
|
||||
* {@link ServiceConnection @ServiceConnection}-annotated {@link JdbcDatabaseContainer}
|
||||
* fields that should produce {@link LiquibaseConnectionDetails}.
|
||||
* {@link ContainerConnectionDetailsFactory} to create {@link LiquibaseConnectionDetails}
|
||||
* from a {@link ServiceConnection @ServiceConnection}-annotated
|
||||
* {@link JdbcDatabaseContainer}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class LiquibaseContainerConnectionDetailsFactory extends
|
||||
ContainerConnectionDetailsFactory<ServiceConnection, LiquibaseConnectionDetails, JdbcDatabaseContainer<?>> {
|
||||
class LiquibaseContainerConnectionDetailsFactory
|
||||
extends ContainerConnectionDetailsFactory<LiquibaseConnectionDetails, JdbcDatabaseContainer<?>> {
|
||||
|
||||
@Override
|
||||
protected LiquibaseConnectionDetails getContainerConnectionDetails(
|
||||
ContainerConnectionSource<ServiceConnection, LiquibaseConnectionDetails, JdbcDatabaseContainer<?>> source) {
|
||||
ContainerConnectionSource<LiquibaseConnectionDetails, JdbcDatabaseContainer<?>> source) {
|
||||
return new LiquibaseContainerConnectionDetails(source);
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ class LiquibaseContainerConnectionDetailsFactory extends
|
||||
private final JdbcDatabaseContainer<?> container;
|
||||
|
||||
private LiquibaseContainerConnectionDetails(
|
||||
ContainerConnectionSource<ServiceConnection, LiquibaseConnectionDetails, JdbcDatabaseContainer<?>> source) {
|
||||
ContainerConnectionSource<LiquibaseConnectionDetails, JdbcDatabaseContainer<?>> source) {
|
||||
super(source);
|
||||
this.container = source.getContainer();
|
||||
}
|
||||
|
||||
@@ -22,22 +22,26 @@ import org.testcontainers.containers.MongoDBContainer;
|
||||
import org.springframework.boot.autoconfigure.mongo.MongoConnectionDetails;
|
||||
import org.springframework.boot.testcontainers.service.connection.ContainerConnectionDetailsFactory;
|
||||
import org.springframework.boot.testcontainers.service.connection.ContainerConnectionSource;
|
||||
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
|
||||
|
||||
/**
|
||||
* {@link ContainerConnectionDetailsFactory} for
|
||||
* {@link MongoServiceConnection @MongoServiceConnection}-annotated
|
||||
* {@link MongoDBContainer} fields.
|
||||
* {@link ContainerConnectionDetailsFactory} to create {@link MongoConnectionDetails} from
|
||||
* a {@link ServiceConnection @ServiceConnection}-annotated {@link MongoDBContainer}.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class MongoContainerConnectionDetailsFactory
|
||||
extends ContainerConnectionDetailsFactory<MongoServiceConnection, MongoConnectionDetails, MongoDBContainer> {
|
||||
extends ContainerConnectionDetailsFactory<MongoConnectionDetails, MongoDBContainer> {
|
||||
|
||||
MongoContainerConnectionDetailsFactory() {
|
||||
super(ANY_CONNECTION_NAME, "com.mongodb.ConnectionString");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MongoConnectionDetails getContainerConnectionDetails(
|
||||
ContainerConnectionSource<MongoServiceConnection, MongoConnectionDetails, MongoDBContainer> source) {
|
||||
ContainerConnectionSource<MongoConnectionDetails, MongoDBContainer> source) {
|
||||
return new MongoContainerConnectionDetails(source);
|
||||
}
|
||||
|
||||
@@ -50,7 +54,7 @@ class MongoContainerConnectionDetailsFactory
|
||||
private final ConnectionString connectionString;
|
||||
|
||||
private MongoContainerConnectionDetails(
|
||||
ContainerConnectionSource<MongoServiceConnection, MongoConnectionDetails, MongoDBContainer> source) {
|
||||
ContainerConnectionSource<MongoConnectionDetails, MongoDBContainer> source) {
|
||||
super(source);
|
||||
this.connectionString = new ConnectionString(source.getContainer().getReplicaSetUrl());
|
||||
}
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
* 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.testcontainers.service.connection.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.testcontainers.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 {
|
||||
|
||||
}
|
||||
@@ -25,22 +25,26 @@ import org.testcontainers.containers.Neo4jContainer;
|
||||
import org.springframework.boot.autoconfigure.neo4j.Neo4jConnectionDetails;
|
||||
import org.springframework.boot.testcontainers.service.connection.ContainerConnectionDetailsFactory;
|
||||
import org.springframework.boot.testcontainers.service.connection.ContainerConnectionSource;
|
||||
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
|
||||
|
||||
/**
|
||||
* {@link ContainerConnectionDetailsFactory} for
|
||||
* {@link Neo4jServiceConnection @Neo4jServiceConnection}-annotated {@link Neo4jContainer}
|
||||
* fields.
|
||||
* {@link ContainerConnectionDetailsFactory} to create {@link Neo4jConnectionDetails} from
|
||||
* a {@link ServiceConnection @ServiceConnection}-annotated {@link Neo4jContainer}.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class Neo4jContainerConnectionDetailsFactory
|
||||
extends ContainerConnectionDetailsFactory<Neo4jServiceConnection, Neo4jConnectionDetails, Neo4jContainer<?>> {
|
||||
extends ContainerConnectionDetailsFactory<Neo4jConnectionDetails, Neo4jContainer<?>> {
|
||||
|
||||
Neo4jContainerConnectionDetailsFactory() {
|
||||
super(ANY_CONNECTION_NAME, "org.neo4j.driver.AuthToken");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Neo4jConnectionDetails getContainerConnectionDetails(
|
||||
ContainerConnectionSource<Neo4jServiceConnection, Neo4jConnectionDetails, Neo4jContainer<?>> source) {
|
||||
ContainerConnectionSource<Neo4jConnectionDetails, Neo4jContainer<?>> source) {
|
||||
return new Neo4jContainerConnectionDetails(source);
|
||||
}
|
||||
|
||||
@@ -53,7 +57,7 @@ class Neo4jContainerConnectionDetailsFactory
|
||||
private final Neo4jContainer<?> container;
|
||||
|
||||
private Neo4jContainerConnectionDetails(
|
||||
ContainerConnectionSource<Neo4jServiceConnection, Neo4jConnectionDetails, Neo4jContainer<?>> source) {
|
||||
ContainerConnectionSource<Neo4jConnectionDetails, Neo4jContainer<?>> source) {
|
||||
super(source);
|
||||
this.container = source.getContainer();
|
||||
}
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
* 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.testcontainers.service.connection.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.testcontainers.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 {
|
||||
|
||||
}
|
||||
@@ -15,6 +15,6 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* General support for auto-configuration of service connections in tests.
|
||||
* General support for service connections in tests.
|
||||
*/
|
||||
package org.springframework.boot.testcontainers.service.connection;
|
||||
|
||||
@@ -21,25 +21,28 @@ 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.testcontainers.service.connection.ContainerConnectionDetailsFactory;
|
||||
import org.springframework.boot.testcontainers.service.connection.ContainerConnectionSource;
|
||||
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
|
||||
|
||||
/**
|
||||
* {@link ConnectionDetailsFactory} for
|
||||
* {@link R2dbcServiceConnection @R2dbcServiceConnection}- annotated
|
||||
* {@link MariaDBContainer} fields.
|
||||
* {@link ContainerConnectionDetailsFactory} to create {@link R2dbcConnectionDetails} from
|
||||
* a {@link ServiceConnection @ServiceConnection}-annotated {@link MariaDBContainer}.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class MariaDbR2dbcContainerConnectionDetailsFactory
|
||||
extends ContainerConnectionDetailsFactory<R2dbcServiceConnection, R2dbcConnectionDetails, MariaDBContainer<?>> {
|
||||
extends ContainerConnectionDetailsFactory<R2dbcConnectionDetails, MariaDBContainer<?>> {
|
||||
|
||||
MariaDbR2dbcContainerConnectionDetailsFactory() {
|
||||
super(ANY_CONNECTION_NAME, "io.r2dbc.spi.ConnectionFactoryOptions");
|
||||
}
|
||||
|
||||
@Override
|
||||
public R2dbcConnectionDetails getContainerConnectionDetails(
|
||||
ContainerConnectionSource<R2dbcServiceConnection, R2dbcConnectionDetails, MariaDBContainer<?>> source) {
|
||||
ContainerConnectionSource<R2dbcConnectionDetails, MariaDBContainer<?>> source) {
|
||||
return new R2dbcDatabaseContainerConnectionDetails(source.getContainer());
|
||||
}
|
||||
|
||||
|
||||
@@ -21,25 +21,28 @@ 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.testcontainers.service.connection.ContainerConnectionDetailsFactory;
|
||||
import org.springframework.boot.testcontainers.service.connection.ContainerConnectionSource;
|
||||
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
|
||||
|
||||
/**
|
||||
* {@link ConnectionDetailsFactory} for
|
||||
* {@link R2dbcServiceConnection @R2dbcServiceConnection}- annotated
|
||||
* {@link MSSQLServerContainer} fields.
|
||||
* {@link ContainerConnectionDetailsFactory} to create {@link R2dbcConnectionDetails} from
|
||||
* a {@link ServiceConnection @ServiceConnection}-annotated {@link MSSQLServerContainer}.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class SqlServerR2dbcContainerConnectionDetailsFactory extends
|
||||
ContainerConnectionDetailsFactory<R2dbcServiceConnection, R2dbcConnectionDetails, MSSQLServerContainer<?>> {
|
||||
class MsSqlServerR2dbcContainerConnectionDetailsFactory
|
||||
extends ContainerConnectionDetailsFactory<R2dbcConnectionDetails, MSSQLServerContainer<?>> {
|
||||
|
||||
MsSqlServerR2dbcContainerConnectionDetailsFactory() {
|
||||
super(ANY_CONNECTION_NAME, "io.r2dbc.spi.ConnectionFactoryOptions");
|
||||
}
|
||||
|
||||
@Override
|
||||
public R2dbcConnectionDetails getContainerConnectionDetails(
|
||||
ContainerConnectionSource<R2dbcServiceConnection, R2dbcConnectionDetails, MSSQLServerContainer<?>> source) {
|
||||
ContainerConnectionSource<R2dbcConnectionDetails, MSSQLServerContainer<?>> source) {
|
||||
return new R2dbcDatabaseContainerConnectionDetails(source.getContainer());
|
||||
}
|
||||
|
||||
@@ -21,25 +21,28 @@ 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.testcontainers.service.connection.ContainerConnectionDetailsFactory;
|
||||
import org.springframework.boot.testcontainers.service.connection.ContainerConnectionSource;
|
||||
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
|
||||
|
||||
/**
|
||||
* {@link ConnectionDetailsFactory} for
|
||||
* {@link R2dbcServiceConnection @R2dbcServiceConnection}- annotated
|
||||
* {@link MySQLContainer} fields.
|
||||
* {@link ContainerConnectionDetailsFactory} to create {@link R2dbcConnectionDetails} from
|
||||
* a {@link ServiceConnection @ServiceConnection}-annotated {@link MySQLContainer}.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class MySqlR2dbcContainerConnectionDetailsFactory
|
||||
extends ContainerConnectionDetailsFactory<R2dbcServiceConnection, R2dbcConnectionDetails, MySQLContainer<?>> {
|
||||
extends ContainerConnectionDetailsFactory<R2dbcConnectionDetails, MySQLContainer<?>> {
|
||||
|
||||
MySqlR2dbcContainerConnectionDetailsFactory() {
|
||||
super(ANY_CONNECTION_NAME, "io.r2dbc.spi.ConnectionFactoryOptions");
|
||||
}
|
||||
|
||||
@Override
|
||||
public R2dbcConnectionDetails getContainerConnectionDetails(
|
||||
ContainerConnectionSource<R2dbcServiceConnection, R2dbcConnectionDetails, MySQLContainer<?>> source) {
|
||||
ContainerConnectionSource<R2dbcConnectionDetails, MySQLContainer<?>> source) {
|
||||
return new R2dbcDatabaseContainerConnectionDetails(source.getContainer());
|
||||
}
|
||||
|
||||
|
||||
@@ -21,24 +21,28 @@ 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.testcontainers.service.connection.ContainerConnectionDetailsFactory;
|
||||
import org.springframework.boot.testcontainers.service.connection.ContainerConnectionSource;
|
||||
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
|
||||
|
||||
/**
|
||||
* {@link ConnectionDetailsFactory} for {@link R2dbcServiceConnection @R2dbcConnection}
|
||||
* annotated {@link PostgreSQLContainer} fields.
|
||||
* {@link ContainerConnectionDetailsFactory} to create {@link R2dbcConnectionDetails} from
|
||||
* a {@link ServiceConnection @ServiceConnection}-annotated {@link PostgreSQLContainer}.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class PostgresR2dbcContainerConnectionDetailsFactory extends
|
||||
ContainerConnectionDetailsFactory<R2dbcServiceConnection, R2dbcConnectionDetails, PostgreSQLContainer<?>> {
|
||||
class PostgresR2dbcContainerConnectionDetailsFactory
|
||||
extends ContainerConnectionDetailsFactory<R2dbcConnectionDetails, PostgreSQLContainer<?>> {
|
||||
|
||||
PostgresR2dbcContainerConnectionDetailsFactory() {
|
||||
super(ANY_CONNECTION_NAME, "io.r2dbc.spi.ConnectionFactoryOptions");
|
||||
}
|
||||
|
||||
@Override
|
||||
public R2dbcConnectionDetails getContainerConnectionDetails(
|
||||
ContainerConnectionSource<R2dbcServiceConnection, R2dbcConnectionDetails, PostgreSQLContainer<?>> source) {
|
||||
ContainerConnectionSource<R2dbcConnectionDetails, PostgreSQLContainer<?>> source) {
|
||||
return new R2dbcDatabaseContainerConnectionDetails(source.getContainer());
|
||||
}
|
||||
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
* 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.testcontainers.service.connection.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.testcontainers.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 {
|
||||
|
||||
}
|
||||
@@ -16,27 +16,33 @@
|
||||
|
||||
package org.springframework.boot.testcontainers.service.connection.redis;
|
||||
|
||||
import org.testcontainers.containers.Container;
|
||||
import org.testcontainers.containers.GenericContainer;
|
||||
|
||||
import org.springframework.boot.autoconfigure.data.redis.RedisConnectionDetails;
|
||||
import org.springframework.boot.testcontainers.service.connection.ContainerConnectionDetailsFactory;
|
||||
import org.springframework.boot.testcontainers.service.connection.ContainerConnectionSource;
|
||||
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
|
||||
|
||||
/**
|
||||
* {@link ContainerConnectionDetailsFactory} for
|
||||
* {@link RedisServiceConnection @RedisServiceConnection}-annotated
|
||||
* {@link GenericContainer} fields.
|
||||
* {@link ContainerConnectionDetailsFactory} to create {@link RedisConnectionDetails} from
|
||||
* a {@link ServiceConnection @ServiceConnection}-annotated {@link GenericContainer} using
|
||||
* the {@code "redis"} image.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class RedisContainerConnectionDetailsFactory
|
||||
extends ContainerConnectionDetailsFactory<RedisServiceConnection, RedisConnectionDetails, GenericContainer<?>> {
|
||||
extends ContainerConnectionDetailsFactory<RedisConnectionDetails, Container<?>> {
|
||||
|
||||
RedisContainerConnectionDetailsFactory() {
|
||||
super("redis");
|
||||
}
|
||||
|
||||
@Override
|
||||
public RedisConnectionDetails getContainerConnectionDetails(
|
||||
ContainerConnectionSource<RedisServiceConnection, RedisConnectionDetails, GenericContainer<?>> source) {
|
||||
ContainerConnectionSource<RedisConnectionDetails, Container<?>> source) {
|
||||
return new RedisContainerConnectionDetails(source);
|
||||
}
|
||||
|
||||
@@ -49,7 +55,7 @@ class RedisContainerConnectionDetailsFactory
|
||||
private final Standalone standalone;
|
||||
|
||||
private RedisContainerConnectionDetails(
|
||||
ContainerConnectionSource<RedisServiceConnection, RedisConnectionDetails, GenericContainer<?>> source) {
|
||||
ContainerConnectionSource<RedisConnectionDetails, Container<?>> source) {
|
||||
super(source);
|
||||
this.standalone = Standalone.of(source.getContainer().getHost(),
|
||||
source.getContainer().getFirstMappedPort());
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
* 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.testcontainers.service.connection.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.testcontainers.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 {
|
||||
|
||||
}
|
||||
@@ -8,7 +8,6 @@ org.springframework.boot.testcontainers.service.connection.amqp.RabbitContainerC
|
||||
org.springframework.boot.testcontainers.service.connection.cassandra.CassandraContainerConnectionDetailsFactory,\
|
||||
org.springframework.boot.testcontainers.service.connection.couchbase.CouchbaseContainerConnectionDetailsFactory,\
|
||||
org.springframework.boot.testcontainers.service.connection.flyway.FlywayContainerConnectionDetailsFactory,\
|
||||
org.springframework.boot.testcontainers.service.connection.redis.RedisContainerConnectionDetailsFactory,\
|
||||
org.springframework.boot.testcontainers.service.connection.elasticsearch.ElasticsearchContainerConnectionDetailsFactory,\
|
||||
org.springframework.boot.testcontainers.service.connection.influx.InfluxDbContainerConnectionDetailsFactory,\
|
||||
org.springframework.boot.testcontainers.service.connection.jdbc.JdbcContainerConnectionDetailsFactory,\
|
||||
@@ -17,6 +16,7 @@ org.springframework.boot.testcontainers.service.connection.liquibase.LiquibaseCo
|
||||
org.springframework.boot.testcontainers.service.connection.mongo.MongoContainerConnectionDetailsFactory,\
|
||||
org.springframework.boot.testcontainers.service.connection.neo4j.Neo4jContainerConnectionDetailsFactory,\
|
||||
org.springframework.boot.testcontainers.service.connection.r2dbc.MariaDbR2dbcContainerConnectionDetailsFactory,\
|
||||
org.springframework.boot.testcontainers.service.connection.r2dbc.MsSqlServerR2dbcContainerConnectionDetailsFactory,\
|
||||
org.springframework.boot.testcontainers.service.connection.r2dbc.MySqlR2dbcContainerConnectionDetailsFactory,\
|
||||
org.springframework.boot.testcontainers.service.connection.r2dbc.PostgresR2dbcContainerConnectionDetailsFactory,\
|
||||
org.springframework.boot.testcontainers.service.connection.r2dbc.SqlServerR2dbcContainerConnectionDetailsFactory
|
||||
org.springframework.boot.testcontainers.service.connection.redis.RedisContainerConnectionDetailsFactory
|
||||
|
||||
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* 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.testcontainers.service.connection;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.testcontainers.containers.JdbcDatabaseContainer;
|
||||
import org.testcontainers.containers.PostgreSQLContainer;
|
||||
import org.testcontainers.elasticsearch.ElasticsearchContainer;
|
||||
|
||||
import org.springframework.boot.autoconfigure.jdbc.JdbcConnectionDetails;
|
||||
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.core.annotation.MergedAnnotation;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link ContainerConnectionDetailsFactory}.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class ContainerConnectionDetailsFactoryTests {
|
||||
|
||||
private String beanNameSuffix;
|
||||
|
||||
private Origin origin;
|
||||
|
||||
private JdbcDatabaseContainer<?> container;
|
||||
|
||||
private MergedAnnotation<ServiceConnection> annotation;
|
||||
|
||||
private ContainerConnectionSource<?, ?> source;
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
this.beanNameSuffix = "MyBean";
|
||||
this.origin = mock(Origin.class);
|
||||
this.container = mock(PostgreSQLContainer.class);
|
||||
this.annotation = MergedAnnotation.of(ServiceConnection.class,
|
||||
Map.of("name", "myname", "type", new Class<?>[0]));
|
||||
this.source = new ContainerConnectionSource<>(this.beanNameSuffix, this.origin, this.container,
|
||||
this.annotation);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getConnectionDetailsWhenTypesMatchAndNoNameRestrictionReturnsDetails() {
|
||||
TestContainerConnectionDetailsFactory factory = new TestContainerConnectionDetailsFactory();
|
||||
ConnectionDetails connectionDetails = getConnectionDetails(factory, this.source);
|
||||
assertThat(connectionDetails).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getConnectionDetailsWhenTypesMatchAndNameRestrictionMatchesReturnsDetails() {
|
||||
TestContainerConnectionDetailsFactory factory = new TestContainerConnectionDetailsFactory("myname");
|
||||
ConnectionDetails connectionDetails = getConnectionDetails(factory, this.source);
|
||||
assertThat(connectionDetails).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getConnectionDetailsWhenTypesMatchAndNameRestrictionDoesNotMatchReturnsNull() {
|
||||
TestContainerConnectionDetailsFactory factory = new TestContainerConnectionDetailsFactory("notmyname");
|
||||
ConnectionDetails connectionDetails = getConnectionDetails(factory, this.source);
|
||||
assertThat(connectionDetails).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getConnectionDetailsWhenContainerTypeDoesNotMatchReturnsNull() {
|
||||
ElasticsearchContainer container = mock(ElasticsearchContainer.class);
|
||||
ContainerConnectionSource<?, ?> source = new ContainerConnectionSource<>(this.beanNameSuffix, this.origin,
|
||||
container, this.annotation);
|
||||
TestContainerConnectionDetailsFactory factory = new TestContainerConnectionDetailsFactory();
|
||||
ConnectionDetails connectionDetails = getConnectionDetails(factory, source);
|
||||
assertThat(connectionDetails).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getConnectionDetailsHasOrigin() {
|
||||
TestContainerConnectionDetailsFactory factory = new TestContainerConnectionDetailsFactory();
|
||||
ConnectionDetails connectionDetails = getConnectionDetails(factory, this.source);
|
||||
assertThat(Origin.from(connectionDetails)).isSameAs(this.origin);
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
private ConnectionDetails getConnectionDetails(ConnectionDetailsFactory<?, ?> factory,
|
||||
ContainerConnectionSource<?, ?> source) {
|
||||
return ((ConnectionDetailsFactory) factory).getConnectionDetails(source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test {@link ContainerConnectionDetailsFactory}.
|
||||
*/
|
||||
static class TestContainerConnectionDetailsFactory
|
||||
extends ContainerConnectionDetailsFactory<JdbcConnectionDetails, JdbcDatabaseContainer<?>> {
|
||||
|
||||
TestContainerConnectionDetailsFactory() {
|
||||
this(ANY_CONNECTION_NAME);
|
||||
}
|
||||
|
||||
TestContainerConnectionDetailsFactory(String connectionName) {
|
||||
super(connectionName);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JdbcConnectionDetails getContainerConnectionDetails(
|
||||
ContainerConnectionSource<JdbcConnectionDetails, JdbcDatabaseContainer<?>> source) {
|
||||
return new TestContainerConnectionDetails(source);
|
||||
}
|
||||
|
||||
static class TestContainerConnectionDetails extends ContainerConnectionDetails
|
||||
implements JdbcConnectionDetails {
|
||||
|
||||
TestContainerConnectionDetails(ContainerConnectionSource<?, ?> source) {
|
||||
super(source);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return "user";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPassword() {
|
||||
return "secret";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getJdbcUrl() {
|
||||
return "jdbc:example";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
/*
|
||||
* 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.testcontainers.service.connection;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.testcontainers.containers.JdbcDatabaseContainer;
|
||||
import org.testcontainers.containers.PostgreSQLContainer;
|
||||
import org.testcontainers.elasticsearch.ElasticsearchContainer;
|
||||
|
||||
import org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchConnectionDetails;
|
||||
import org.springframework.boot.autoconfigure.jdbc.JdbcConnectionDetails;
|
||||
import org.springframework.boot.origin.Origin;
|
||||
import org.springframework.core.annotation.MergedAnnotation;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link ContainerConnectionSource}.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class ContainerConnectionSourceTests {
|
||||
|
||||
private String beanNameSuffix;
|
||||
|
||||
private Origin origin;
|
||||
|
||||
private JdbcDatabaseContainer<?> container;
|
||||
|
||||
private MergedAnnotation<ServiceConnection> annotation;
|
||||
|
||||
private ContainerConnectionSource<?, ?> source;
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
this.beanNameSuffix = "MyBean";
|
||||
this.origin = mock(Origin.class);
|
||||
this.container = mock(PostgreSQLContainer.class);
|
||||
given(this.container.getDockerImageName()).willReturn("postgres");
|
||||
this.annotation = MergedAnnotation.of(ServiceConnection.class, Map.of("name", "", "type", new Class<?>[0]));
|
||||
this.source = new ContainerConnectionSource<>(this.beanNameSuffix, this.origin, this.container,
|
||||
this.annotation);
|
||||
}
|
||||
|
||||
@Test
|
||||
void acceptsWhenContainerIsNotInstanceOfContainerTypeReturnsFalse() {
|
||||
String connectionName = null;
|
||||
Class<?> connectionDetailsType = JdbcConnectionDetails.class;
|
||||
Class<?> containerType = ElasticsearchContainer.class;
|
||||
assertThat(this.source.accepts(connectionName, connectionDetailsType, containerType)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void acceptsWhenContainerIsInstanceOfContainerTypeReturnsTrue() {
|
||||
String connectionName = null;
|
||||
Class<?> connectionDetailsType = JdbcConnectionDetails.class;
|
||||
Class<?> containerType = JdbcDatabaseContainer.class;
|
||||
assertThat(this.source.accepts(connectionName, connectionDetailsType, containerType)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void acceptsWhenConnectionNameDoesNotMatchNameTakenFromAnnotationReturnsFalse() {
|
||||
setupSourceAnnotatedWithName("myname");
|
||||
String connectionName = "othername";
|
||||
Class<?> connectionDetailsType = JdbcConnectionDetails.class;
|
||||
Class<?> containerType = JdbcDatabaseContainer.class;
|
||||
assertThat(this.source.accepts(connectionName, connectionDetailsType, containerType)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void acceptsWhenConnectionNameDoesNotMatchNameTakenFromContainerReturnsFalse() {
|
||||
String connectionName = "othername";
|
||||
Class<?> connectionDetailsType = JdbcConnectionDetails.class;
|
||||
Class<?> containerType = JdbcDatabaseContainer.class;
|
||||
assertThat(this.source.accepts(connectionName, connectionDetailsType, containerType)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void acceptsWhenConnectionNameIsUnrestrictedReturnsTrue() {
|
||||
String connectionName = null;
|
||||
Class<?> connectionDetailsType = JdbcConnectionDetails.class;
|
||||
Class<?> containerType = JdbcDatabaseContainer.class;
|
||||
assertThat(this.source.accepts(connectionName, connectionDetailsType, containerType)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void acceptsWhenConnectionNameMatchesNameTakenFromAnnotationReturnsTrue() {
|
||||
setupSourceAnnotatedWithName("myname");
|
||||
String connectionName = "myname";
|
||||
Class<?> connectionDetailsType = JdbcConnectionDetails.class;
|
||||
Class<?> containerType = JdbcDatabaseContainer.class;
|
||||
assertThat(this.source.accepts(connectionName, connectionDetailsType, containerType)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void acceptsWhenConnectionNameMatchesNameTakenFromContainerReturnsTrue() {
|
||||
String connectionName = "postgres";
|
||||
Class<?> connectionDetailsType = JdbcConnectionDetails.class;
|
||||
Class<?> containerType = JdbcDatabaseContainer.class;
|
||||
assertThat(this.source.accepts(connectionName, connectionDetailsType, containerType)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void acceptsWhenConnectionDetailsTypeNotInAnnotationRestrictionReturnsFalse() {
|
||||
setupSourceAnnotatedWithType(ElasticsearchConnectionDetails.class);
|
||||
String connectionName = null;
|
||||
Class<?> connectionDetailsType = JdbcConnectionDetails.class;
|
||||
Class<?> containerType = JdbcDatabaseContainer.class;
|
||||
assertThat(this.source.accepts(connectionName, connectionDetailsType, containerType)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void acceptsWhenConnectionDetailsTypeInAnnotationRestrictionReturnsTrue() {
|
||||
setupSourceAnnotatedWithType(JdbcConnectionDetails.class);
|
||||
String connectionName = null;
|
||||
Class<?> connectionDetailsType = JdbcConnectionDetails.class;
|
||||
Class<?> containerType = JdbcDatabaseContainer.class;
|
||||
assertThat(this.source.accepts(connectionName, connectionDetailsType, containerType)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void acceptsWhenConnectionDetailsTypeIsNotRestrictedReturnsTrue() {
|
||||
String connectionName = null;
|
||||
Class<?> connectionDetailsType = JdbcConnectionDetails.class;
|
||||
Class<?> containerType = JdbcDatabaseContainer.class;
|
||||
assertThat(this.source.accepts(connectionName, connectionDetailsType, containerType)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getBeanNameSuffixReturnsBeanNameSuffix() {
|
||||
assertThat(this.source.getBeanNameSuffix()).isEqualTo(this.beanNameSuffix);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getOriginReturnsOrigin() {
|
||||
assertThat(this.source.getOrigin()).isEqualTo(this.origin);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getContainerReturnsContainer() {
|
||||
assertThat(this.source.getContainer()).isSameAs(this.container);
|
||||
}
|
||||
|
||||
@Test
|
||||
void toStringReturnsSensibleString() {
|
||||
assertThat(this.source.toString()).startsWith("@ServiceConnection source for Mock for Origin");
|
||||
}
|
||||
|
||||
private void setupSourceAnnotatedWithName(String name) {
|
||||
this.annotation = MergedAnnotation.of(ServiceConnection.class, Map.of("name", name, "type", new Class<?>[0]));
|
||||
this.source = new ContainerConnectionSource<>(this.beanNameSuffix, this.origin, this.container,
|
||||
this.annotation);
|
||||
}
|
||||
|
||||
private void setupSourceAnnotatedWithType(Class<?> type) {
|
||||
this.annotation = MergedAnnotation.of(ServiceConnection.class,
|
||||
Map.of("name", "", "type", new Class<?>[] { type }));
|
||||
this.source = new ContainerConnectionSource<>(this.beanNameSuffix, this.origin, this.container,
|
||||
this.annotation);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.testcontainers.service.connection;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.origin.Origin;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
|
||||
/**
|
||||
* Tests for {@link FieldOrigin}.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class FieldOriginTests {
|
||||
|
||||
@Test
|
||||
void createWhenFieldIsNullThrowsException() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> new FieldOrigin(null))
|
||||
.withMessage("Field must not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
void equalsAndHashCode() {
|
||||
Origin o1 = new FieldOrigin(ReflectionUtils.findField(Fields.class, "one"));
|
||||
Origin o2 = new FieldOrigin(ReflectionUtils.findField(Fields.class, "one"));
|
||||
Origin o3 = new FieldOrigin(ReflectionUtils.findField(Fields.class, "two"));
|
||||
assertThat(o1).isEqualTo(o1).isEqualTo(o2).isNotEqualTo(o3);
|
||||
assertThat(o1.hashCode()).isEqualTo(o2.hashCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
void toStringReturnsSensibleString() {
|
||||
Origin origin = new FieldOrigin(ReflectionUtils.findField(Fields.class, "one"));
|
||||
assertThat(origin).hasToString("FieldOriginTests.Fields.one");
|
||||
}
|
||||
|
||||
static class Fields {
|
||||
|
||||
String one;
|
||||
|
||||
String two;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,32 +16,35 @@
|
||||
|
||||
package org.springframework.boot.testcontainers.service.connection;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.testcontainers.containers.Container;
|
||||
import org.testcontainers.containers.GenericContainer;
|
||||
|
||||
import org.springframework.boot.autoconfigure.service.connection.ConnectionDetails;
|
||||
import org.springframework.boot.testcontainers.service.connection.ServiceConnectionContextCustomizerFactoryTests.ServiceConnections.NestedClass;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||
|
||||
/**
|
||||
* Tests for {@link ServiceConnectionContextCustomizerFactory}.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class ServiceConnectionContextCustomizerFactoryTests {
|
||||
class ServiceConnectionContextCustomizerFactoryTests {
|
||||
|
||||
private final ServiceConnectionContextCustomizerFactory factory = new ServiceConnectionContextCustomizerFactory();
|
||||
|
||||
@Test
|
||||
void whenClassHasNoServiceConnectionsThenCreateReturnsNull() {
|
||||
void createContextCustomizerWhenNoServiceConnectionsReturnsNull() {
|
||||
assertThat(this.factory.createContextCustomizer(NoServiceConnections.class, null)).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenClassHasServiceConnectionsThenCreateReturnsCustomizer() {
|
||||
void createContextCustomizerWhenClassHasServiceConnectionsReturnsCustomizer() {
|
||||
ServiceConnectionContextCustomizer customizer = (ServiceConnectionContextCustomizer) this.factory
|
||||
.createContextCustomizer(ServiceConnections.class, null);
|
||||
assertThat(customizer).isNotNull();
|
||||
@@ -49,37 +52,79 @@ public class ServiceConnectionContextCustomizerFactoryTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenEnclosingClassHasServiceConnectionsThenCreateReturnsCustomizer() {
|
||||
void createContextCustomizerWhenEnclosingClassHasServiceConnectionsReturnsCustomizer() {
|
||||
ServiceConnectionContextCustomizer customizer = (ServiceConnectionContextCustomizer) this.factory
|
||||
.createContextCustomizer(NestedClass.class, null);
|
||||
.createContextCustomizer(ServiceConnections.NestedClass.class, null);
|
||||
assertThat(customizer).isNotNull();
|
||||
assertThat(customizer.getSources()).hasSize(3);
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenClassHasNonStaticServiceConnectionThenCreateShouldFailWithHelpfulIllegalStateException() {
|
||||
void createContextCustomizerWhenClassHasNonStaticServiceConnectionFailsWithHepfulException() {
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(() -> this.factory.createContextCustomizer(NonStaticServiceConnection.class, null))
|
||||
.withMessage("@ServiceConnection field 'service' must be static");
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
void createContextCustomizerWhenClassHasAnnotationOnNonConnectionFieldFailsWithHepfulException() {
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(() -> this.factory.createContextCustomizer(ServiceConnectionOnWrongFieldType.class, null))
|
||||
.withMessage("Field 'service2' in " + ServiceConnectionOnWrongFieldType.class.getName()
|
||||
+ " must be a org.testcontainers.containers.Container");
|
||||
}
|
||||
|
||||
@Test
|
||||
void createContextCustomizerCreatesCustomizerSourceWithSensibleBeanNameSuffix() {
|
||||
ServiceConnectionContextCustomizer customizer = (ServiceConnectionContextCustomizer) this.factory
|
||||
.createContextCustomizer(SingleServiceConnection.class, null);
|
||||
ContainerConnectionSource<?, ?> source = customizer.getSources().get(0);
|
||||
assertThat(source.getBeanNameSuffix()).isEqualTo("SingleServiceConnectionService1");
|
||||
}
|
||||
|
||||
@Test
|
||||
void createContextCustomizerCreatesCustomizerSourceWithSensibleOrigin() {
|
||||
ServiceConnectionContextCustomizer customizer = (ServiceConnectionContextCustomizer) this.factory
|
||||
.createContextCustomizer(SingleServiceConnection.class, null);
|
||||
ContainerConnectionSource<?, ?> source = customizer.getSources().get(0);
|
||||
assertThat(source.getOrigin())
|
||||
.hasToString("ServiceConnectionContextCustomizerFactoryTests.SingleServiceConnection.service1");
|
||||
}
|
||||
|
||||
@Test
|
||||
void createContextCustomizerCreatesCustomizerSourceWithSensibleToString() {
|
||||
ServiceConnectionContextCustomizer customizer = (ServiceConnectionContextCustomizer) this.factory
|
||||
.createContextCustomizer(SingleServiceConnection.class, null);
|
||||
ContainerConnectionSource<?, ?> source = customizer.getSources().get(0);
|
||||
assertThat(source).hasToString(
|
||||
"@ServiceConnection source for ServiceConnectionContextCustomizerFactoryTests.SingleServiceConnection.service1");
|
||||
}
|
||||
|
||||
static class NoServiceConnections {
|
||||
|
||||
}
|
||||
|
||||
static class SingleServiceConnection {
|
||||
|
||||
@ServiceConnection
|
||||
private static GenericContainer<?> service1 = new MockContainer();
|
||||
|
||||
}
|
||||
|
||||
static class ServiceConnections {
|
||||
|
||||
@ServiceConnection(TestConnectionDetails.class)
|
||||
private static GenericContainer<?> service1 = new GenericContainer<>("example");
|
||||
@ServiceConnection
|
||||
private static Container<?> service1 = new MockContainer();
|
||||
|
||||
@ServiceConnection(TestConnectionDetails.class)
|
||||
private static GenericContainer<?> service2 = new GenericContainer<>("example");
|
||||
@ServiceConnection
|
||||
private static Container<?> service2 = new MockContainer();
|
||||
|
||||
@Nested
|
||||
class NestedClass {
|
||||
|
||||
@ServiceConnection(TestConnectionDetails.class)
|
||||
private static GenericContainer<?> service3 = new GenericContainer<>("example");
|
||||
@ServiceConnection
|
||||
private static Container<?> service3 = new MockContainer();
|
||||
|
||||
}
|
||||
|
||||
@@ -87,12 +132,35 @@ public class ServiceConnectionContextCustomizerFactoryTests {
|
||||
|
||||
static class NonStaticServiceConnection {
|
||||
|
||||
@ServiceConnection(TestConnectionDetails.class)
|
||||
private GenericContainer<?> service = new GenericContainer<>("example");
|
||||
@ServiceConnection
|
||||
private Container<?> service = new MockContainer("example");
|
||||
|
||||
}
|
||||
|
||||
static class TestConnectionDetails implements ConnectionDetails {
|
||||
static class ServiceConnectionOnWrongFieldType {
|
||||
|
||||
@ServiceConnection
|
||||
private static InputStream service2 = new ByteArrayInputStream(new byte[0]);
|
||||
|
||||
}
|
||||
|
||||
static class MockContainer extends GenericContainer<MockContainer> {
|
||||
|
||||
private final String dockerImageName;
|
||||
|
||||
MockContainer() {
|
||||
this("example");
|
||||
}
|
||||
|
||||
MockContainer(String dockerImageName) {
|
||||
super(dockerImageName);
|
||||
this.dockerImageName = dockerImageName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDockerImageName() {
|
||||
return this.dockerImageName;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* 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.testcontainers.service.connection;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.testcontainers.containers.JdbcDatabaseContainer;
|
||||
import org.testcontainers.containers.PostgreSQLContainer;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.boot.autoconfigure.jdbc.JdbcConnectionDetails;
|
||||
import org.springframework.boot.autoconfigure.service.connection.ConnectionDetailsFactories;
|
||||
import org.springframework.boot.origin.Origin;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.core.annotation.MergedAnnotation;
|
||||
import org.springframework.test.context.MergedContextConfiguration;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.BDDMockito.then;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
|
||||
/**
|
||||
* Tests for {@link ServiceConnectionContextCustomizer}.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class ServiceConnectionContextCustomizerTests {
|
||||
|
||||
private String beanNameSuffix;
|
||||
|
||||
private Origin origin;
|
||||
|
||||
private JdbcDatabaseContainer<?> container;
|
||||
|
||||
private MergedAnnotation<ServiceConnection> annotation;
|
||||
|
||||
private ContainerConnectionSource<?, ?> source;
|
||||
|
||||
private ConnectionDetailsFactories factories;
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
this.beanNameSuffix = "MyBean";
|
||||
this.origin = mock(Origin.class);
|
||||
this.container = mock(PostgreSQLContainer.class);
|
||||
this.annotation = MergedAnnotation.of(ServiceConnection.class,
|
||||
Map.of("name", "myname", "type", new Class<?>[0]));
|
||||
this.source = new ContainerConnectionSource<>(this.beanNameSuffix, this.origin, this.container,
|
||||
this.annotation);
|
||||
this.factories = mock(ConnectionDetailsFactories.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void customizeContextRegistersServiceConnections() {
|
||||
ServiceConnectionContextCustomizer customizer = new ServiceConnectionContextCustomizer(List.of(this.source),
|
||||
this.factories);
|
||||
ConfigurableApplicationContext context = mock(ConfigurableApplicationContext.class);
|
||||
DefaultListableBeanFactory beanFactory = spy(new DefaultListableBeanFactory());
|
||||
given(context.getBeanFactory()).willReturn(beanFactory);
|
||||
MergedContextConfiguration mergedConfig = mock(MergedContextConfiguration.class);
|
||||
JdbcConnectionDetails connectionDetails = new TestJdbcConnectionDetails();
|
||||
given(this.factories.getConnectionDetails(this.source))
|
||||
.willReturn(Map.of(JdbcConnectionDetails.class, connectionDetails));
|
||||
customizer.customizeContext(context, mergedConfig);
|
||||
ArgumentCaptor<BeanDefinition> beanDefinitionCaptor = ArgumentCaptor.forClass(BeanDefinition.class);
|
||||
then(beanFactory).should()
|
||||
.registerBeanDefinition(eq("testJdbcConnectionDetailsForMyBean"), beanDefinitionCaptor.capture());
|
||||
RootBeanDefinition beanDefinition = (RootBeanDefinition) beanDefinitionCaptor.getValue();
|
||||
assertThat(beanDefinition.getInstanceSupplier().get()).isSameAs(connectionDetails);
|
||||
assertThat(beanDefinition.getBeanClass()).isEqualTo(TestJdbcConnectionDetails.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void customizeContextWhenFactoriesHasNoConnectionDetailsThrowsException() {
|
||||
ServiceConnectionContextCustomizer customizer = new ServiceConnectionContextCustomizer(List.of(this.source),
|
||||
this.factories);
|
||||
ConfigurableApplicationContext context = mock(ConfigurableApplicationContext.class);
|
||||
DefaultListableBeanFactory beanFactory = spy(new DefaultListableBeanFactory());
|
||||
given(context.getBeanFactory()).willReturn(beanFactory);
|
||||
MergedContextConfiguration mergedConfig = mock(MergedContextConfiguration.class);
|
||||
assertThatIllegalStateException().isThrownBy(() -> customizer.customizeContext(context, mergedConfig))
|
||||
.withMessageStartingWith("No connection details created for @ServiceConnection source");
|
||||
}
|
||||
|
||||
/**
|
||||
* Test {@link JdbcConnectionDetails}.
|
||||
*/
|
||||
static class TestJdbcConnectionDetails implements JdbcConnectionDetails {
|
||||
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPassword() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getJdbcUrl() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -25,7 +25,6 @@ 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.testcontainers.utility.DockerImageName;
|
||||
|
||||
import org.springframework.amqp.rabbit.annotation.Queue;
|
||||
import org.springframework.amqp.rabbit.annotation.RabbitListener;
|
||||
@@ -34,6 +33,8 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.amqp.RabbitConnectionDetails;
|
||||
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
|
||||
import org.springframework.boot.testsupport.testcontainers.DockerImageNames;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
|
||||
@@ -41,7 +42,7 @@ import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link RabbitServiceConnection}.
|
||||
* Tests for {@link RabbitContainerConnectionDetailsFactory}.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
@@ -49,11 +50,12 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
*/
|
||||
@SpringJUnitConfig
|
||||
@Testcontainers(disabledWithoutDocker = true)
|
||||
class RabbitServiceConnectionTests {
|
||||
class RabbitContainerConnectionDetailsFactoryIntegrationTests {
|
||||
|
||||
@Container
|
||||
@RabbitServiceConnection
|
||||
static final RabbitMQContainer rabbit = new RabbitMQContainer(DockerImageName.parse("rabbitmq:3.11-alpine"));
|
||||
@ServiceConnection
|
||||
static final RabbitMQContainer rabbit = new RabbitMQContainer(DockerImageNames.rabbit())
|
||||
.withStartupTimeout(Duration.ofMinutes(4));
|
||||
|
||||
@Autowired(required = false)
|
||||
private RabbitConnectionDetails connectionDetails;
|
||||
@@ -21,18 +21,19 @@ 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.testcontainers.utility.DockerImageName;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.influx.InfluxDbAutoConfiguration;
|
||||
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
|
||||
import org.springframework.boot.testsupport.testcontainers.DockerImageNames;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link InfluxDbServiceConnection}.
|
||||
* Tests for {@link InfluxDbContainerConnectionDetailsFactory}.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
@@ -40,21 +41,18 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
*/
|
||||
@SpringJUnitConfig
|
||||
@Testcontainers(disabledWithoutDocker = true)
|
||||
class InfluxDbServiceConnectionTests {
|
||||
|
||||
private static final String INFLUXDB_VERSION = "2.6.1";
|
||||
class InfluxDbContainerConnectionDetailsFactoryIntegrationTests {
|
||||
|
||||
@Container
|
||||
@InfluxDbServiceConnection
|
||||
static final InfluxDBContainer<?> influxDbService = new InfluxDBContainer<>(
|
||||
DockerImageName.parse("influxdb").withTag(INFLUXDB_VERSION));
|
||||
@ServiceConnection
|
||||
static final InfluxDBContainer<?> influxDbService = new InfluxDBContainer<>(DockerImageNames.influxDb());
|
||||
|
||||
@Autowired
|
||||
private InfluxDB influxDb;
|
||||
|
||||
@Test
|
||||
void connectionCanBeMadeToInfluxDbContainer() {
|
||||
assertThat(this.influxDb.version()).isEqualTo("v" + INFLUXDB_VERSION);
|
||||
assertThat(this.influxDb.version()).isEqualTo("v" + DockerImageNames.influxDb().getVersionPart());
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@@ -25,11 +25,12 @@ 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.testcontainers.utility.DockerImageName;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration;
|
||||
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
|
||||
import org.springframework.boot.testsupport.testcontainers.DockerImageNames;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.kafka.annotation.KafkaListener;
|
||||
@@ -40,7 +41,7 @@ import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link KafkaServiceConnection}.
|
||||
* Tests for {@link KafkaContainerConnectionDetailsFactory}.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
@@ -50,11 +51,11 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
@Testcontainers(disabledWithoutDocker = true)
|
||||
@TestPropertySource(properties = { "spring.kafka.consumer.group-id=test-group",
|
||||
"spring.kafka.consumer.auto-offset-reset=earliest" })
|
||||
class KafkaServiceConnectionTests {
|
||||
class KafkaContainerConnectionDetailsFactoryIntegrationTests {
|
||||
|
||||
@Container
|
||||
@KafkaServiceConnection
|
||||
static final KafkaContainer kafka = new KafkaContainer(DockerImageName.parse("confluentinc/cp-kafka:5.4.3"));
|
||||
@ServiceConnection
|
||||
static final KafkaContainer kafka = new KafkaContainer(DockerImageNames.kafka());
|
||||
|
||||
@Autowired
|
||||
private KafkaTemplate<String, String> kafkaTemplate;
|
||||
@@ -22,7 +22,7 @@ import org.testcontainers.junit.jupiter.Testcontainers;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.testcontainers.service.connection.redis.RedisServiceConnection;
|
||||
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
|
||||
import org.springframework.boot.testsupport.testcontainers.RedisContainer;
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.CacheManager;
|
||||
@@ -34,7 +34,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
class SampleCacheApplicationRedisTests {
|
||||
|
||||
@Container
|
||||
@RedisServiceConnection
|
||||
@ServiceConnection
|
||||
private static final RedisContainer redis = new RedisContainer();
|
||||
|
||||
@Autowired
|
||||
|
||||
@@ -23,10 +23,8 @@ import org.testcontainers.junit.jupiter.Testcontainers;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.flyway.FlywayConnectionDetails;
|
||||
import org.springframework.boot.test.autoconfigure.data.r2dbc.DataR2dbcTest;
|
||||
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
|
||||
import org.springframework.boot.testcontainers.service.connection.r2dbc.R2dbcServiceConnection;
|
||||
import org.springframework.boot.testsupport.testcontainers.DockerImageNames;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -43,8 +41,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
class CityRepositoryTests {
|
||||
|
||||
@Container
|
||||
@R2dbcServiceConnection
|
||||
@ServiceConnection(FlywayConnectionDetails.class)
|
||||
@ServiceConnection
|
||||
static PostgreSQLContainer<?> postgresql = new PostgreSQLContainer<>(DockerImageNames.postgresql())
|
||||
.withDatabaseName("test_flyway");
|
||||
|
||||
|
||||
@@ -23,10 +23,8 @@ import org.testcontainers.junit.jupiter.Testcontainers;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.liquibase.LiquibaseConnectionDetails;
|
||||
import org.springframework.boot.test.autoconfigure.data.r2dbc.DataR2dbcTest;
|
||||
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
|
||||
import org.springframework.boot.testcontainers.service.connection.r2dbc.R2dbcServiceConnection;
|
||||
import org.springframework.boot.testsupport.testcontainers.DockerImageNames;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -43,8 +41,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
class CityRepositoryTests {
|
||||
|
||||
@Container
|
||||
@R2dbcServiceConnection
|
||||
@ServiceConnection(LiquibaseConnectionDetails.class)
|
||||
@ServiceConnection
|
||||
static PostgreSQLContainer<?> postgresql = new PostgreSQLContainer<>(DockerImageNames.postgresql())
|
||||
.withDatabaseName("test_liquibase");
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.web.client.TestRestTemplate;
|
||||
import org.springframework.boot.test.web.server.LocalServerPort;
|
||||
import org.springframework.boot.testcontainers.service.connection.mongo.MongoServiceConnection;
|
||||
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
|
||||
import org.springframework.boot.testsupport.testcontainers.DockerImageNames;
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.http.HttpEntity;
|
||||
@@ -65,7 +65,7 @@ class SampleSessionMongoApplicationTests {
|
||||
private int port;
|
||||
|
||||
@Container
|
||||
@MongoServiceConnection
|
||||
@ServiceConnection
|
||||
static MongoDBContainer mongo = new MongoDBContainer(DockerImageNames.mongo()).withStartupAttempts(3)
|
||||
.withStartupTimeout(Duration.ofMinutes(2));
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ import org.testcontainers.junit.jupiter.Testcontainers;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.web.client.TestRestTemplate;
|
||||
import org.springframework.boot.testcontainers.service.connection.redis.RedisServiceConnection;
|
||||
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
|
||||
import org.springframework.boot.testsupport.testcontainers.RedisContainer;
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.http.HttpEntity;
|
||||
@@ -56,7 +56,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
class SampleSessionRedisApplicationTests {
|
||||
|
||||
@Container
|
||||
@RedisServiceConnection
|
||||
@ServiceConnection
|
||||
static RedisContainer redis = new RedisContainer();
|
||||
|
||||
@Autowired
|
||||
|
||||
@@ -28,7 +28,7 @@ import reactor.util.function.Tuples;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.web.server.LocalServerPort;
|
||||
import org.springframework.boot.testcontainers.service.connection.mongo.MongoServiceConnection;
|
||||
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
|
||||
import org.springframework.boot.testsupport.testcontainers.DockerImageNames;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
@@ -49,7 +49,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
class SampleSessionWebFluxMongoApplicationTests {
|
||||
|
||||
@Container
|
||||
@MongoServiceConnection
|
||||
@ServiceConnection
|
||||
private static final MongoDBContainer mongo = new MongoDBContainer(DockerImageNames.mongo()).withStartupAttempts(3)
|
||||
.withStartupTimeout(Duration.ofMinutes(2));
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ import reactor.util.function.Tuples;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.web.server.LocalServerPort;
|
||||
import org.springframework.boot.testcontainers.service.connection.redis.RedisServiceConnection;
|
||||
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
|
||||
import org.springframework.boot.testsupport.testcontainers.RedisContainer;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
@@ -48,7 +48,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
class SampleSessionWebFluxRedisApplicationTests {
|
||||
|
||||
@Container
|
||||
@RedisServiceConnection
|
||||
@ServiceConnection
|
||||
private static final RedisContainer redis = new RedisContainer();
|
||||
|
||||
@LocalServerPort
|
||||
|
||||
Reference in New Issue
Block a user