Add nullability annotations to tests in module/spring-boot-data-redis

See gh-47263
This commit is contained in:
Moritz Halbritter 2025-09-19 12:51:59 +02:00
parent 3fdc1db7ce
commit 266f9c44e0
9 changed files with 76 additions and 27 deletions

View File

@ -61,3 +61,12 @@ dependencies {
testRuntimeOnly("ch.qos.logback:logback-classic")
}
tasks.named("compileTestJava") {
options.nullability.checking = "tests"
}
tasks.named("compileDockerTestJava") {
options.nullability.checking = "tests"
}

View File

@ -17,6 +17,7 @@
package org.springframework.boot.data.redis.testcontainers;
import java.util.Map;
import java.util.function.Supplier;
import com.redis.testcontainers.RedisContainer;
import com.redis.testcontainers.RedisStackContainer;
@ -25,12 +26,14 @@ import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.service.connection.ConnectionDetails;
import org.springframework.boot.autoconfigure.service.connection.ConnectionDetailsFactories;
import org.springframework.boot.data.redis.autoconfigure.DataRedisConnectionDetails;
import org.springframework.boot.origin.Origin;
import org.springframework.boot.testcontainers.service.connection.ContainerConnectionSource;
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
import org.springframework.boot.testcontainers.service.connection.TestContainerConnectionSource;
import org.springframework.core.annotation.MergedAnnotation;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
/**
* Test for {@link RedisContainerConnectionDetailsFactory} when using a custom container
@ -45,8 +48,9 @@ class CustomRedisContainerConnectionDetailsFactoryTests {
ConnectionDetailsFactories factories = new ConnectionDetailsFactories(null);
MergedAnnotation<ServiceConnection> annotation = MergedAnnotation.of(ServiceConnection.class,
Map.of("value", ""));
ContainerConnectionSource<RedisContainer> source = TestContainerConnectionSource.create("test", null,
RedisContainer.class, "mycustomimage", annotation, null);
Supplier<RedisContainer> containerSupplier = () -> new RedisContainer("redis");
ContainerConnectionSource<RedisContainer> source = TestContainerConnectionSource.create("test",
mock(Origin.class), RedisContainer.class, "mycustomimage", annotation, containerSupplier);
Map<Class<?>, ConnectionDetails> connectionDetails = factories.getConnectionDetails(source, true);
assertThat(connectionDetails.get(DataRedisConnectionDetails.class)).isNotNull();
}
@ -56,8 +60,9 @@ class CustomRedisContainerConnectionDetailsFactoryTests {
ConnectionDetailsFactories factories = new ConnectionDetailsFactories(null);
MergedAnnotation<ServiceConnection> annotation = MergedAnnotation.of(ServiceConnection.class,
Map.of("value", ""));
ContainerConnectionSource<RedisStackContainer> source = TestContainerConnectionSource.create("test", null,
RedisStackContainer.class, "mycustomimage", annotation, null);
Supplier<RedisStackContainer> containerSupplier = () -> new RedisStackContainer("redis");
ContainerConnectionSource<RedisStackContainer> source = TestContainerConnectionSource.create("test",
mock(Origin.class), RedisStackContainer.class, "mycustomimage", annotation, containerSupplier);
Map<Class<?>, ConnectionDetails> connectionDetails = factories.getConnectionDetails(source, true);
assertThat(connectionDetails.get(DataRedisConnectionDetails.class)).isNotNull();
}

View File

@ -18,6 +18,7 @@ package org.springframework.boot.data.redis.autoconfigure;
import java.time.Duration;
import org.jspecify.annotations.Nullable;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.EnabledForJreRange;
import org.junit.jupiter.api.condition.JRE;
@ -221,8 +222,11 @@ class DataRedisAutoConfigurationJedisTests {
.withPropertyValues("spring.data.redis.sentinel.master:mymaster",
"spring.data.redis.sentinel.nodes:127.0.0.1:26379,127.0.0.1:26380")
.withUserConfiguration(JedisConnectionFactoryCaptorConfiguration.class)
.run((context) -> assertThat(JedisConnectionFactoryCaptor.connectionFactory.isRedisSentinelAware())
.isTrue());
.run((context) -> {
JedisConnectionFactory connectionFactory = JedisConnectionFactoryCaptor.connectionFactory;
assertThat(connectionFactory).isNotNull();
assertThat(connectionFactory.isRedisSentinelAware()).isTrue();
});
}
@Test
@ -233,9 +237,11 @@ class DataRedisAutoConfigurationJedisTests {
"spring.data.redis.sentinel.nodes:127.0.0.1:26379,127.0.0.1:26380")
.withUserConfiguration(JedisConnectionFactoryCaptorConfiguration.class)
.run((context) -> {
assertThat(JedisConnectionFactoryCaptor.connectionFactory.isRedisSentinelAware()).isTrue();
assertThat(getUserName(JedisConnectionFactoryCaptor.connectionFactory)).isEqualTo("user");
assertThat(JedisConnectionFactoryCaptor.connectionFactory.getPassword()).isEqualTo("password");
JedisConnectionFactory connectionFactory = JedisConnectionFactoryCaptor.connectionFactory;
assertThat(connectionFactory).isNotNull();
assertThat(connectionFactory.isRedisSentinelAware()).isTrue();
assertThat(getUserName(connectionFactory)).isEqualTo("user");
assertThat(connectionFactory.getPassword()).isEqualTo("password");
});
}
@ -243,8 +249,11 @@ class DataRedisAutoConfigurationJedisTests {
void testRedisConfigurationWithCluster() {
this.contextRunner.withPropertyValues("spring.data.redis.cluster.nodes=127.0.0.1:27379,127.0.0.1:27380")
.withUserConfiguration(JedisConnectionFactoryCaptorConfiguration.class)
.run((context) -> assertThat(JedisConnectionFactoryCaptor.connectionFactory.isRedisClusterAware())
.isTrue());
.run((context) -> {
JedisConnectionFactory connectionFactory = JedisConnectionFactoryCaptor.connectionFactory;
assertThat(connectionFactory).isNotNull();
assertThat(connectionFactory.isRedisClusterAware()).isTrue();
});
}
@Test
@ -307,7 +316,7 @@ class DataRedisAutoConfigurationJedisTests {
});
}
private String getUserName(JedisConnectionFactory factory) {
private @Nullable String getUserName(JedisConnectionFactory factory) {
return ReflectionTestUtils.invokeMethod(factory, "getRedisUsername");
}
@ -362,7 +371,7 @@ class DataRedisAutoConfigurationJedisTests {
static class JedisConnectionFactoryCaptor implements BeanPostProcessor {
static JedisConnectionFactory connectionFactory;
static @Nullable JedisConnectionFactory connectionFactory;
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {

View File

@ -37,6 +37,7 @@ import io.lettuce.core.models.role.RedisNodeDescription;
import io.lettuce.core.resource.DefaultClientResources;
import io.lettuce.core.tracing.Tracing;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.jspecify.annotations.Nullable;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.EnabledForJreRange;
import org.junit.jupiter.api.condition.JRE;
@ -55,6 +56,7 @@ import org.springframework.boot.testsupport.classpath.resources.WithPackageResou
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.data.redis.connection.NamedNode;
import org.springframework.data.redis.connection.RedisClusterConfiguration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisNode;
@ -383,7 +385,9 @@ class DataRedisAutoConfigurationTests {
.run((context) -> {
LettuceConnectionFactory connectionFactory = context.getBean(LettuceConnectionFactory.class);
assertThat(connectionFactory.isRedisSentinelAware()).isTrue();
assertThat(connectionFactory.getSentinelConfiguration().getSentinels()).isNotNull()
RedisSentinelConfiguration sentinelConfiguration = connectionFactory.getSentinelConfiguration();
assertThat(sentinelConfiguration).isNotNull();
assertThat(sentinelConfiguration.getSentinels()).isNotNull()
.containsExactlyInAnyOrder(new RedisNode("[0:0:0:0:0:0:0:1]", 26379),
new RedisNode("[0:0:0:0:0:0:0:1]", 26380));
});
@ -446,8 +450,8 @@ class DataRedisAutoConfigurationTests {
}));
}
private ContextConsumer<AssertableApplicationContext> assertSentinelConfiguration(String userName, String password,
Consumer<RedisSentinelConfiguration> sentinelConfiguration) {
private ContextConsumer<AssertableApplicationContext> assertSentinelConfiguration(@Nullable String userName,
String password, Consumer<RedisSentinelConfiguration> sentinelConfiguration) {
return (context) -> {
LettuceConnectionFactory connectionFactory = context.getBean(LettuceConnectionFactory.class);
assertThat(getUserName(connectionFactory)).isEqualTo(userName);
@ -479,6 +483,7 @@ class DataRedisAutoConfigurationTests {
.run((context) -> {
RedisClusterConfiguration clusterConfiguration = context.getBean(LettuceConnectionFactory.class)
.getClusterConfiguration();
assertThat(clusterConfiguration).isNotNull();
assertThat(clusterConfiguration.getClusterNodes()).hasSize(3);
assertThat(clusterConfiguration.getClusterNodes()).containsExactlyInAnyOrder(
new RedisNode("127.0.0.1", 27379), new RedisNode("127.0.0.1", 27380),
@ -648,7 +653,9 @@ class DataRedisAutoConfigurationTests {
assertThat(configuration.getUsername()).isEqualTo("user-1");
assertThat(configuration.getPassword()).isEqualTo(RedisPassword.of("password-1"));
assertThat(configuration.getDatabase()).isOne();
assertThat(configuration.getMaster().getName()).isEqualTo("master.redis.example.com");
NamedNode master = configuration.getMaster();
assertThat(master).isNotNull();
assertThat(master.getName()).isEqualTo("master.redis.example.com");
});
}
@ -756,7 +763,7 @@ class DataRedisAutoConfigurationTests {
return (LettucePoolingClientConfiguration) factory.getClientConfiguration();
}
private String getUserName(LettuceConnectionFactory factory) {
private @Nullable String getUserName(LettuceConnectionFactory factory) {
return ReflectionTestUtils.invokeMethod(factory, "getRedisUsername");
}

View File

@ -21,7 +21,10 @@ import java.util.List;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.boot.data.redis.autoconfigure.DataRedisConnectionDetails.Cluster;
import org.springframework.boot.data.redis.autoconfigure.DataRedisConnectionDetails.MasterReplica;
import org.springframework.boot.data.redis.autoconfigure.DataRedisConnectionDetails.Node;
import org.springframework.boot.data.redis.autoconfigure.DataRedisConnectionDetails.Sentinel;
import org.springframework.boot.ssl.DefaultSslBundleRegistry;
import org.springframework.boot.ssl.SslBundle;
@ -144,8 +147,10 @@ class PropertiesRedisConnectionDetailsTests {
DataRedisProperties.Cluster cluster = new DataRedisProperties.Cluster();
cluster.setNodes(List.of("localhost:1111", "127.0.0.1:2222", "[::1]:3333"));
this.properties.setCluster(cluster);
assertThat(this.connectionDetails.getCluster().getNodes()).containsExactly(new Node("localhost", 1111),
new Node("127.0.0.1", 2222), new Node("[::1]", 3333));
Cluster actualCluster = this.connectionDetails.getCluster();
assertThat(actualCluster).isNotNull();
assertThat(actualCluster.getNodes()).containsExactly(new Node("localhost", 1111), new Node("127.0.0.1", 2222),
new Node("[::1]", 3333));
}
@Test
@ -153,7 +158,9 @@ class PropertiesRedisConnectionDetailsTests {
DataRedisProperties.Masterreplica masterReplica = new DataRedisProperties.Masterreplica();
masterReplica.setNodes(List.of("localhost:1111", "127.0.0.1:2222", "[::1]:3333"));
this.properties.setMasterreplica(masterReplica);
assertThat(this.connectionDetails.getMasterReplica().getNodes()).containsExactly(new Node("localhost", 1111),
MasterReplica actualMasterReplica = this.connectionDetails.getMasterReplica();
assertThat(actualMasterReplica).isNotNull();
assertThat(actualMasterReplica.getNodes()).containsExactly(new Node("localhost", 1111),
new Node("127.0.0.1", 2222), new Node("[::1]", 3333));
}
@ -165,9 +172,11 @@ class PropertiesRedisConnectionDetailsTests {
this.properties.setDatabase(5);
PropertiesDataRedisConnectionDetails connectionDetails = new PropertiesDataRedisConnectionDetails(
this.properties, null);
assertThat(connectionDetails.getSentinel().getNodes()).containsExactly(new Node("localhost", 1111),
new Node("127.0.0.1", 2222), new Node("[::1]", 3333));
assertThat(connectionDetails.getSentinel().getDatabase()).isEqualTo(5);
Sentinel actualSentinel = connectionDetails.getSentinel();
assertThat(actualSentinel).isNotNull();
assertThat(actualSentinel.getNodes()).containsExactly(new Node("localhost", 1111), new Node("127.0.0.1", 2222),
new Node("[::1]", 3333));
assertThat(actualSentinel.getDatabase()).isEqualTo(5);
}
@Test
@ -179,7 +188,9 @@ class PropertiesRedisConnectionDetailsTests {
this.properties.setDatabase(5);
PropertiesDataRedisConnectionDetails connectionDetails = new PropertiesDataRedisConnectionDetails(
this.properties, null);
assertThat(connectionDetails.getSentinel().getDatabase()).isEqualTo(9999);
Sentinel actualSentinel = connectionDetails.getSentinel();
assertThat(actualSentinel).isNotNull();
assertThat(actualSentinel.getDatabase()).isEqualTo(9999);
}
@Test

View File

@ -33,6 +33,7 @@ class RedisUrlSyntaxFailureAnalyzerTests {
void analyzeInvalidUrlSyntax() {
DataRedisUrlSyntaxException exception = new DataRedisUrlSyntaxException("redis://invalid");
FailureAnalysis analysis = new DataRedisUrlSyntaxFailureAnalyzer().analyze(exception);
assertThat(analysis).isNotNull();
assertThat(analysis.getDescription()).contains("The URL 'redis://invalid' is not valid");
assertThat(analysis.getAction()).contains("Review the value of the property 'spring.data.redis.url'");
}
@ -41,6 +42,7 @@ class RedisUrlSyntaxFailureAnalyzerTests {
void analyzeRedisHttpUrl() {
DataRedisUrlSyntaxException exception = new DataRedisUrlSyntaxException("http://127.0.0.1:26379/mymaster");
FailureAnalysis analysis = new DataRedisUrlSyntaxFailureAnalyzer().analyze(exception);
assertThat(analysis).isNotNull();
assertThat(analysis.getDescription()).contains("The URL 'http://127.0.0.1:26379/mymaster' is not valid")
.contains("The scheme 'http' is not supported");
assertThat(analysis.getAction()).contains("Use the scheme 'redis://' for insecure or 'rediss://' for secure");
@ -51,6 +53,7 @@ class RedisUrlSyntaxFailureAnalyzerTests {
DataRedisUrlSyntaxException exception = new DataRedisUrlSyntaxException(
"redis-sentinel://username:password@127.0.0.1:26379,127.0.0.1:26380/mymaster");
FailureAnalysis analysis = new DataRedisUrlSyntaxFailureAnalyzer().analyze(exception);
assertThat(analysis).isNotNull();
assertThat(analysis.getDescription()).contains(
"The URL 'redis-sentinel://username:password@127.0.0.1:26379,127.0.0.1:26380/mymaster' is not valid")
.contains("The scheme 'redis-sentinel' is not supported");
@ -61,6 +64,7 @@ class RedisUrlSyntaxFailureAnalyzerTests {
void analyzeRedisSocketUrl() {
DataRedisUrlSyntaxException exception = new DataRedisUrlSyntaxException("redis-socket:///redis/redis.sock");
FailureAnalysis analysis = new DataRedisUrlSyntaxFailureAnalyzer().analyze(exception);
assertThat(analysis).isNotNull();
assertThat(analysis.getDescription()).contains("The URL 'redis-socket:///redis/redis.sock' is not valid")
.contains("The scheme 'redis-socket' is not supported");
assertThat(analysis.getAction()).contains("Configure the appropriate Spring Data Redis connection beans");

View File

@ -45,6 +45,7 @@ class LettuceObservationAutoConfigurationTests {
.withConfiguration(AutoConfigurations.of(DataRedisAutoConfiguration.class))
.run((context) -> {
ClientResources clientResources = context.getBean(LettuceConnectionFactory.class).getClientResources();
assertThat(clientResources).isNotNull();
assertThat(clientResources.tracing()).isInstanceOf(MicrometerTracing.class);
});
@ -54,6 +55,7 @@ class LettuceObservationAutoConfigurationTests {
void whenThereIsNoObservationRegistryThenClientResourcesCustomizationBacksOff() {
this.contextRunner.withConfiguration(AutoConfigurations.of(DataRedisAutoConfiguration.class)).run((context) -> {
ClientResources clientResources = context.getBean(LettuceConnectionFactory.class).getClientResources();
assertThat(clientResources).isNotNull();
assertThat(clientResources.tracing()).isNotInstanceOf(MicrometerTracing.class);
});
}

View File

@ -20,6 +20,7 @@ import java.util.Arrays;
import java.util.List;
import java.util.Properties;
import org.jspecify.annotations.Nullable;
import org.junit.jupiter.api.Test;
import org.springframework.boot.health.contributor.Health;
@ -115,7 +116,7 @@ class RedisHealthIndicatorTests {
return new DataRedisHealthIndicator(redisConnectionFactory);
}
private RedisConnectionFactory createClusterConnectionFactory(String state) {
private RedisConnectionFactory createClusterConnectionFactory(@Nullable String state) {
Properties clusterProperties = new Properties();
if (state != null) {
clusterProperties.setProperty("cluster_state", state);

View File

@ -20,6 +20,7 @@ import java.time.Duration;
import java.util.Properties;
import io.lettuce.core.RedisConnectionException;
import org.jspecify.annotations.Nullable;
import org.junit.jupiter.api.Test;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
@ -142,7 +143,7 @@ class RedisReactiveHealthIndicatorTests {
return new DataRedisReactiveHealthIndicator(redisConnectionFactory);
}
private ReactiveRedisConnectionFactory createClusterConnectionFactory(String state) {
private ReactiveRedisConnectionFactory createClusterConnectionFactory(@Nullable String state) {
Properties clusterProperties = new Properties();
if (state != null) {
clusterProperties.setProperty("cluster_state", state);