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:
@@ -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;
|
||||
Reference in New Issue
Block a user