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:
Phillip Webb
2023-04-12 22:36:59 -07:00
parent 11dac5b5b7
commit 81a972af8d
78 changed files with 1297 additions and 903 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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