diff --git a/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/ContainerConnectionSource.java b/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/ContainerConnectionSource.java index 0bae2745bf..7ef972e1f7 100644 --- a/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/ContainerConnectionSource.java +++ b/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/ContainerConnectionSource.java @@ -136,6 +136,10 @@ public final class ContainerConnectionSource> implements return this.containerSupplier; } + Set> getConnectionDetailsTypes() { + return this.connectionDetailsTypes; + } + @Override public String toString() { return "@ServiceConnection source for %s".formatted(this.origin); diff --git a/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/ServiceConnectionContextCustomizer.java b/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/ServiceConnectionContextCustomizer.java index 8f8e72d2a3..71aa441933 100644 --- a/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/ServiceConnectionContextCustomizer.java +++ b/spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/ServiceConnectionContextCustomizer.java @@ -17,6 +17,10 @@ package org.springframework.boot.testcontainers.service.connection; import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import org.testcontainers.containers.Container; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.BeanDefinitionRegistry; @@ -37,6 +41,8 @@ class ServiceConnectionContextCustomizer implements ContextCustomizer { private final List> sources; + private final Set keys; + private final ConnectionDetailsFactories connectionDetailsFactories; ServiceConnectionContextCustomizer(List> sources) { @@ -46,6 +52,7 @@ class ServiceConnectionContextCustomizer implements ContextCustomizer { ServiceConnectionContextCustomizer(List> sources, ConnectionDetailsFactories connectionDetailsFactories) { this.sources = sources; + this.keys = sources.stream().map(CacheKey::new).collect(Collectors.toUnmodifiableSet()); this.connectionDetailsFactories = connectionDetailsFactories; } @@ -58,8 +65,37 @@ class ServiceConnectionContextCustomizer implements ContextCustomizer { } } + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + return this.keys.equals(((ServiceConnectionContextCustomizer) obj).keys); + } + + @Override + public int hashCode() { + return this.keys.hashCode(); + } + List> getSources() { return this.sources; } + /** + * Relevant details from {@link ContainerConnectionSource} used as a + * MergedContextConfiguration cache key. + */ + private static record CacheKey(String connectionName, Set> connectionDetailsTypes, + Container container) { + + CacheKey(ContainerConnectionSource source) { + this(source.getConnectionName(), source.getConnectionDetailsTypes(), source.getContainerSupplier().get()); + } + + } + } diff --git a/spring-boot-project/spring-boot-testcontainers/src/test/java/org/springframework/boot/testcontainers/service/connection/ServiceConnectionContextCustomizerTests.java b/spring-boot-project/spring-boot-testcontainers/src/test/java/org/springframework/boot/testcontainers/service/connection/ServiceConnectionContextCustomizerTests.java index 910bd0a2b9..25b2d7dad1 100644 --- a/spring-boot-project/spring-boot-testcontainers/src/test/java/org/springframework/boot/testcontainers/service/connection/ServiceConnectionContextCustomizerTests.java +++ b/spring-boot-project/spring-boot-testcontainers/src/test/java/org/springframework/boot/testcontainers/service/connection/ServiceConnectionContextCustomizerTests.java @@ -91,6 +91,54 @@ class ServiceConnectionContextCustomizerTests { assertThat(beanDefinition.getBeanClass()).isEqualTo(TestJdbcConnectionDetails.class); } + @Test + void equalsAndHashCode() { + PostgreSQLContainer container1 = mock(PostgreSQLContainer.class); + PostgreSQLContainer container2 = mock(PostgreSQLContainer.class); + MergedAnnotation annotation1 = MergedAnnotation.of(ServiceConnection.class, + Map.of("name", "", "type", new Class[0])); + MergedAnnotation annotation2 = MergedAnnotation.of(ServiceConnection.class, + Map.of("name", "", "type", new Class[0])); + MergedAnnotation annotation3 = MergedAnnotation.of(ServiceConnection.class, + Map.of("name", "", "type", new Class[] { JdbcConnectionDetails.class })); + // Connection Names + ServiceConnectionContextCustomizer n1 = new ServiceConnectionContextCustomizer( + List.of(new ContainerConnectionSource<>("test", this.origin, PostgreSQLContainer.class, "name", + annotation1, () -> container1))); + ServiceConnectionContextCustomizer n2 = new ServiceConnectionContextCustomizer( + List.of(new ContainerConnectionSource<>("test", this.origin, PostgreSQLContainer.class, "name", + annotation1, () -> container1))); + ServiceConnectionContextCustomizer n3 = new ServiceConnectionContextCustomizer( + List.of(new ContainerConnectionSource<>("test", this.origin, PostgreSQLContainer.class, "namex", + annotation1, () -> container1))); + assertThat(n1.hashCode()).isEqualTo(n2.hashCode()); + assertThat(n1).isEqualTo(n2).isNotEqualTo(n3); + // Connection Details Types + ServiceConnectionContextCustomizer t1 = new ServiceConnectionContextCustomizer( + List.of(new ContainerConnectionSource<>("test", this.origin, PostgreSQLContainer.class, "name", + annotation1, () -> container1))); + ServiceConnectionContextCustomizer t2 = new ServiceConnectionContextCustomizer( + List.of(new ContainerConnectionSource<>("test", this.origin, PostgreSQLContainer.class, "name", + annotation2, () -> container1))); + ServiceConnectionContextCustomizer t3 = new ServiceConnectionContextCustomizer( + List.of(new ContainerConnectionSource<>("test", this.origin, PostgreSQLContainer.class, "name", + annotation3, () -> container1))); + assertThat(t1.hashCode()).isEqualTo(t2.hashCode()); + assertThat(t1).isEqualTo(t2).isNotEqualTo(t3); + // Container + ServiceConnectionContextCustomizer c1 = new ServiceConnectionContextCustomizer( + List.of(new ContainerConnectionSource<>("test", this.origin, PostgreSQLContainer.class, "name", + annotation1, () -> container1))); + ServiceConnectionContextCustomizer c2 = new ServiceConnectionContextCustomizer( + List.of(new ContainerConnectionSource<>("test", this.origin, PostgreSQLContainer.class, "name", + annotation1, () -> container1))); + ServiceConnectionContextCustomizer c3 = new ServiceConnectionContextCustomizer( + List.of(new ContainerConnectionSource<>("test", this.origin, PostgreSQLContainer.class, "name", + annotation1, () -> container2))); + assertThat(c1.hashCode()).isEqualTo(c2.hashCode()); + assertThat(c1).isEqualTo(c2).isNotEqualTo(c3); + } + /** * Test {@link JdbcConnectionDetails}. */