Skip to content

Commit fa686bb

Browse files
committed
Support Testcontainer Redis with custom image names
Update `RedisContainerConnectionDetailsFactory` so that it can also support `RedisContainer` with a custom name. Closes gh-41450
1 parent e7faca3 commit fa686bb

File tree

5 files changed

+138
-9
lines changed

5 files changed

+138
-9
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*
2+
* Copyright 2012-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.testcontainers.service.connection.redis;
18+
19+
import java.util.Map;
20+
21+
import com.redis.testcontainers.RedisContainer;
22+
import org.junit.jupiter.api.Test;
23+
24+
import org.springframework.boot.autoconfigure.data.redis.RedisConnectionDetails;
25+
import org.springframework.boot.autoconfigure.service.connection.ConnectionDetails;
26+
import org.springframework.boot.autoconfigure.service.connection.ConnectionDetailsFactories;
27+
import org.springframework.boot.testcontainers.service.connection.ContainerConnectionSource;
28+
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
29+
import org.springframework.boot.testcontainers.service.connection.TestContainerConnectionSource;
30+
import org.springframework.core.annotation.MergedAnnotation;
31+
32+
import static org.assertj.core.api.Assertions.assertThat;
33+
34+
/**
35+
* Test for {@link RedisContainerConnectionDetailsFactory} when using a custom contain
36+
* without "redis" as the name.
37+
*
38+
* @author Phillip Webb
39+
*/
40+
class CustomRedisContainerConnectionDetailsFactoryTests {
41+
42+
@Test
43+
void getConnectionDetailsWhenRedisContainerWithCustomName() {
44+
ConnectionDetailsFactories factories = new ConnectionDetailsFactories();
45+
MergedAnnotation<ServiceConnection> annotation = MergedAnnotation.of(ServiceConnection.class,
46+
Map.of("value", ""));
47+
ContainerConnectionSource<RedisContainer> source = TestContainerConnectionSource.create("test", null,
48+
RedisContainer.class, "mycustomimage", annotation, null);
49+
Map<Class<?>, ConnectionDetails> connectionDetails = factories.getConnectionDetails(source, true);
50+
assertThat(connectionDetails.get(RedisConnectionDetails.class)).isNotNull();
51+
}
52+
53+
}

spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/ContainerConnectionDetailsFactory.java

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -104,12 +104,10 @@ public final D getConnectionDetails(ContainerConnectionSource<C> source) {
104104
}
105105
try {
106106
Class<?>[] generics = resolveGenerics();
107-
Class<?> containerType = generics[0];
108-
Class<?> connectionDetailsType = generics[1];
109-
for (String connectionName : this.connectionNames) {
110-
if (source.accepts(connectionName, containerType, connectionDetailsType)) {
111-
return getContainerConnectionDetails(source);
112-
}
107+
Class<?> requiredContainerType = generics[0];
108+
Class<?> requiredConnectionDetailsType = generics[1];
109+
if (sourceAccepts(source, requiredContainerType, requiredConnectionDetailsType)) {
110+
return getContainerConnectionDetails(source);
113111
}
114112
}
115113
catch (NoClassDefFoundError ex) {
@@ -118,6 +116,25 @@ public final D getConnectionDetails(ContainerConnectionSource<C> source) {
118116
return null;
119117
}
120118

119+
/**
120+
* Return if the give source accepts the connection. By default this method checks
121+
* each connection name.
122+
* @param source the container connection source
123+
* @param requiredContainerType the required container type
124+
* @param requiredConnectionDetailsType the required connection details type
125+
* @return if the source accepts the connection
126+
* @since 3.4.0
127+
*/
128+
protected boolean sourceAccepts(ContainerConnectionSource<C> source, Class<?> requiredContainerType,
129+
Class<?> requiredConnectionDetailsType) {
130+
for (String requiredConnectionName : this.connectionNames) {
131+
if (source.accepts(requiredConnectionName, requiredContainerType, requiredConnectionDetailsType)) {
132+
return true;
133+
}
134+
}
135+
return false;
136+
}
137+
121138
private boolean hasRequiredClasses() {
122139
return ObjectUtils.isEmpty(this.requiredClassNames) || Arrays.stream(this.requiredClassNames)
123140
.allMatch((requiredClassName) -> ClassUtils.isPresent(requiredClassName, null));

spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/ContainerConnectionSource.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2023 the original author or authors.
2+
* Copyright 2012-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -90,7 +90,15 @@ private static String getOrDeduceConnectionName(String connectionName, String co
9090
return null;
9191
}
9292

93-
boolean accepts(String requiredConnectionName, Class<?> requiredContainerType,
93+
/**
94+
* Return is this source accepts the given connection.
95+
* @param requiredConnectionName the required connection name or {@code null}
96+
* @param requiredContainerType the required container type
97+
* @param requiredConnectionDetailsType the required connection details type
98+
* @return if the connection is accepted by this source
99+
* @since 3.4.0
100+
*/
101+
public boolean accepts(String requiredConnectionName, Class<?> requiredContainerType,
94102
Class<?> requiredConnectionDetailsType) {
95103
if (StringUtils.hasText(requiredConnectionName)
96104
&& !requiredConnectionName.equalsIgnoreCase(this.connectionName)) {

spring-boot-project/spring-boot-testcontainers/src/main/java/org/springframework/boot/testcontainers/service/connection/redis/RedisContainerConnectionDetailsFactory.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import java.util.List;
2020

21+
import com.redis.testcontainers.RedisContainer;
2122
import org.testcontainers.containers.Container;
2223
import org.testcontainers.containers.GenericContainer;
2324

@@ -49,7 +50,14 @@ class RedisContainerConnectionDetailsFactory
4950
}
5051

5152
@Override
52-
public RedisConnectionDetails getContainerConnectionDetails(ContainerConnectionSource<Container<?>> source) {
53+
protected boolean sourceAccepts(ContainerConnectionSource<Container<?>> source, Class<?> requiredContainerType,
54+
Class<?> requiredConnectionDetailsType) {
55+
return super.sourceAccepts(source, requiredContainerType, requiredConnectionDetailsType)
56+
|| source.accepts(ANY_CONNECTION_NAME, RedisContainer.class, requiredConnectionDetailsType);
57+
}
58+
59+
@Override
60+
protected RedisConnectionDetails getContainerConnectionDetails(ContainerConnectionSource<Container<?>> source) {
5361
return new RedisContainerConnectionDetails(source);
5462
}
5563

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* Copyright 2012-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.testcontainers.service.connection;
18+
19+
import java.util.function.Supplier;
20+
21+
import org.testcontainers.containers.Container;
22+
23+
import org.springframework.boot.origin.Origin;
24+
import org.springframework.core.annotation.MergedAnnotation;
25+
26+
/**
27+
* Factory for tests to create a {@link ContainerConnectionSource}.
28+
*
29+
* @author Phillip Webb
30+
*/
31+
public final class TestContainerConnectionSource {
32+
33+
private TestContainerConnectionSource() {
34+
}
35+
36+
public static <C extends Container<?>> ContainerConnectionSource<C> create(String beanNameSuffix, Origin origin,
37+
Class<C> containerType, String containerImageName, MergedAnnotation<ServiceConnection> annotation,
38+
Supplier<C> containerSupplier) {
39+
return new ContainerConnectionSource<>(beanNameSuffix, origin, containerType, containerImageName, annotation,
40+
containerSupplier);
41+
}
42+
43+
}

0 commit comments

Comments
 (0)