Polishing.
Replace blocking lock wait with non-blocking flow. Add support for asynchronous storage to persist the cache value after retrieval from the value supplier. Introduce AsyncCacheWriter abstraction to improve functional guards. Reformat code. Remove redundant tests. Revisit deprecation notices with consistent mention of the version in which the deprecation was introduced. Refine exception messages when RedisCache does not support async retrieval. See #2650 Original pull request: #2717
This commit is contained in:
118
src/test/java/org/springframework/data/redis/cache/DefaultRedisCachWriterUnitTests.java
vendored
Normal file
118
src/test/java/org/springframework/data/redis/cache/DefaultRedisCachWriterUnitTests.java
vendored
Normal file
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Copyright 2017-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.data.redis.cache;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnectionFactory;
|
||||
import org.springframework.data.redis.connection.RedisConnection;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.redis.connection.RedisStringCommands;
|
||||
import org.springframework.data.redis.core.types.Expiration;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link DefaultRedisCacheWriter}
|
||||
*
|
||||
* @author John Blum
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class DefaultRedisCacheWriterUnitTests {
|
||||
|
||||
@Mock private CacheStatisticsCollector mockCacheStatisticsCollector = mock(CacheStatisticsCollector.class);
|
||||
|
||||
@Mock private RedisConnection mockConnection;
|
||||
|
||||
@Mock(strictness = Mock.Strictness.LENIENT) private RedisConnectionFactory mockConnectionFactory;
|
||||
|
||||
@Mock private ReactiveRedisConnection mockReactiveConnection;
|
||||
|
||||
@Mock(strictness = Mock.Strictness.LENIENT) private TestReactiveRedisConnectionFactory mockReactiveConnectionFactory;
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
doReturn(this.mockConnection).when(this.mockConnectionFactory).getConnection();
|
||||
doReturn(this.mockConnection).when(this.mockReactiveConnectionFactory).getConnection();
|
||||
doReturn(this.mockReactiveConnection).when(this.mockReactiveConnectionFactory).getReactiveConnection();
|
||||
}
|
||||
|
||||
private RedisCacheWriter newRedisCacheWriter() {
|
||||
return spy(new DefaultRedisCacheWriter(this.mockConnectionFactory, mock(BatchStrategy.class))
|
||||
.withStatisticsCollector(this.mockCacheStatisticsCollector));
|
||||
}
|
||||
|
||||
private RedisCacheWriter newReactiveRedisCacheWriter() {
|
||||
return spy(new DefaultRedisCacheWriter(this.mockReactiveConnectionFactory, Duration.ZERO, mock(BatchStrategy.class))
|
||||
.withStatisticsCollector(this.mockCacheStatisticsCollector));
|
||||
}
|
||||
|
||||
@Test // GH-2351
|
||||
void getWithNonNullTtl() {
|
||||
|
||||
byte[] key = "TestKey".getBytes();
|
||||
byte[] value = "TestValue".getBytes();
|
||||
|
||||
Duration ttl = Duration.ofSeconds(15);
|
||||
Expiration expiration = Expiration.from(ttl);
|
||||
|
||||
RedisStringCommands mockStringCommands = mock(RedisStringCommands.class);
|
||||
|
||||
doReturn(mockStringCommands).when(this.mockConnection).stringCommands();
|
||||
doReturn(value).when(mockStringCommands).getEx(any(), any());
|
||||
|
||||
RedisCacheWriter cacheWriter = newRedisCacheWriter();
|
||||
|
||||
assertThat(cacheWriter.get("TestCache", key, ttl)).isEqualTo(value);
|
||||
|
||||
verify(this.mockConnection).stringCommands();
|
||||
verify(mockStringCommands).getEx(eq(key), eq(expiration));
|
||||
verify(this.mockConnection).close();
|
||||
verifyNoMoreInteractions(this.mockConnection, mockStringCommands);
|
||||
}
|
||||
|
||||
@Test // GH-2351
|
||||
void getWithNullTtl() {
|
||||
|
||||
byte[] key = "TestKey".getBytes();
|
||||
byte[] value = "TestValue".getBytes();
|
||||
|
||||
RedisStringCommands mockStringCommands = mock(RedisStringCommands.class);
|
||||
|
||||
doReturn(mockStringCommands).when(this.mockConnection).stringCommands();
|
||||
doReturn(value).when(mockStringCommands).get(any());
|
||||
|
||||
RedisCacheWriter cacheWriter = newRedisCacheWriter();
|
||||
|
||||
assertThat(cacheWriter.get("TestCache", key, null)).isEqualTo(value);
|
||||
|
||||
verify(this.mockConnection).stringCommands();
|
||||
verify(mockStringCommands).get(eq(key));
|
||||
verify(this.mockConnection).close();
|
||||
verifyNoMoreInteractions(this.mockConnection, mockStringCommands);
|
||||
}
|
||||
|
||||
interface TestReactiveRedisConnectionFactory extends ReactiveRedisConnectionFactory, RedisConnectionFactory {}
|
||||
|
||||
}
|
||||
@@ -16,6 +16,7 @@
|
||||
package org.springframework.data.redis.cache;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.assertj.core.api.Assumptions.*;
|
||||
import static org.springframework.data.redis.cache.RedisCacheWriter.*;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
@@ -23,6 +24,7 @@ import java.nio.charset.StandardCharsets;
|
||||
import java.time.Duration;
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Consumer;
|
||||
@@ -31,10 +33,8 @@ import org.junit.jupiter.api.BeforeEach;
|
||||
import org.springframework.data.redis.connection.RedisConnection;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.redis.connection.RedisStringCommands.SetOption;
|
||||
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
|
||||
import org.springframework.data.redis.connection.jedis.extension.JedisConnectionFactoryExtension;
|
||||
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
|
||||
import org.springframework.data.redis.core.types.Expiration;
|
||||
import org.springframework.data.redis.test.extension.RedisStanalone;
|
||||
import org.springframework.data.redis.test.extension.parametrized.MethodSource;
|
||||
import org.springframework.data.redis.test.extension.parametrized.ParameterizedRedisTest;
|
||||
|
||||
@@ -67,12 +67,6 @@ public class DefaultRedisCacheWriterTests {
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
|
||||
JedisConnectionFactory connectionFactory =
|
||||
JedisConnectionFactoryExtension.getConnectionFactory(RedisStanalone.class);
|
||||
|
||||
this.connectionFactory = connectionFactory;
|
||||
|
||||
doWithConnection(RedisConnection::flushAll);
|
||||
}
|
||||
|
||||
@@ -152,6 +146,49 @@ public class DefaultRedisCacheWriterTests {
|
||||
assertThat(nonLockingRedisCacheWriter(connectionFactory).get(CACHE_NAME, binaryCacheKey)).isNull();
|
||||
}
|
||||
|
||||
@ParameterizedRedisTest // GH-2650
|
||||
void cacheHitRetrieveShouldIncrementStatistics() throws ExecutionException, InterruptedException {
|
||||
|
||||
assumeThat(connectionFactory).isInstanceOf(LettuceConnectionFactory.class);
|
||||
|
||||
doWithConnection(connection -> connection.set(binaryCacheKey, binaryCacheValue));
|
||||
|
||||
RedisCacheWriter writer = nonLockingRedisCacheWriter(connectionFactory)
|
||||
.withStatisticsCollector(CacheStatisticsCollector.create());
|
||||
|
||||
writer.retrieve(CACHE_NAME, binaryCacheKey).get();
|
||||
|
||||
assertThat(writer.getCacheStatistics(CACHE_NAME).getGets()).isOne();
|
||||
assertThat(writer.getCacheStatistics(CACHE_NAME).getHits()).isOne();
|
||||
}
|
||||
|
||||
@ParameterizedRedisTest // GH-2650
|
||||
void storeShouldIncrementStatistics() throws ExecutionException, InterruptedException {
|
||||
|
||||
assumeThat(connectionFactory).isInstanceOf(LettuceConnectionFactory.class);
|
||||
|
||||
RedisCacheWriter writer = nonLockingRedisCacheWriter(connectionFactory)
|
||||
.withStatisticsCollector(CacheStatisticsCollector.create());
|
||||
|
||||
writer.store(CACHE_NAME, binaryCacheKey, binaryCacheValue, null).get();
|
||||
|
||||
assertThat(writer.getCacheStatistics(CACHE_NAME).getPuts()).isOne();
|
||||
}
|
||||
|
||||
@ParameterizedRedisTest // GH-2650
|
||||
void cacheMissRetrieveWithLoaderAsyncShouldIncrementStatistics() throws ExecutionException, InterruptedException {
|
||||
|
||||
assumeThat(connectionFactory).isInstanceOf(LettuceConnectionFactory.class);
|
||||
|
||||
RedisCacheWriter writer = nonLockingRedisCacheWriter(connectionFactory)
|
||||
.withStatisticsCollector(CacheStatisticsCollector.create());
|
||||
|
||||
writer.retrieve(CACHE_NAME, binaryCacheKey).get();
|
||||
|
||||
assertThat(writer.getCacheStatistics(CACHE_NAME).getGets()).isOne();
|
||||
assertThat(writer.getCacheStatistics(CACHE_NAME).getMisses()).isOne();
|
||||
}
|
||||
|
||||
@ParameterizedRedisTest // DATAREDIS-481, DATAREDIS-1082
|
||||
void putIfAbsentShouldAddEternalEntryWhenKeyDoesNotExist() {
|
||||
|
||||
@@ -253,8 +290,8 @@ public class DefaultRedisCacheWriterTests {
|
||||
|
||||
((DefaultRedisCacheWriter) lockingRedisCacheWriter(connectionFactory)).lock(CACHE_NAME);
|
||||
|
||||
lockingRedisCacheWriter(connectionFactory).put(CACHE_NAME + "-no-the-other-cache", binaryCacheKey,
|
||||
binaryCacheValue, Duration.ZERO);
|
||||
lockingRedisCacheWriter(connectionFactory).put(CACHE_NAME + "-no-the-other-cache", binaryCacheKey, binaryCacheValue,
|
||||
Duration.ZERO);
|
||||
|
||||
doWithConnection(connection -> {
|
||||
assertThat(connection.exists(binaryCacheKey)).isTrue();
|
||||
@@ -341,8 +378,7 @@ public class DefaultRedisCacheWriterTests {
|
||||
|
||||
afterWrite.await();
|
||||
|
||||
assertThat(exceptionRef.get()).hasMessageContaining("Interrupted while waiting to unlock")
|
||||
.hasCauseInstanceOf(InterruptedException.class);
|
||||
assertThat(exceptionRef.get()).hasRootCauseInstanceOf(InterruptedException.class);
|
||||
}
|
||||
|
||||
@ParameterizedRedisTest // GH-2300
|
||||
|
||||
@@ -1,324 +0,0 @@
|
||||
/*
|
||||
* Copyright 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.data.redis.cache;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoInteractions;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.time.Duration;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnectionFactory;
|
||||
import org.springframework.data.redis.connection.ReactiveStringCommands;
|
||||
import org.springframework.data.redis.connection.RedisConnection;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.redis.connection.RedisKeyCommands;
|
||||
import org.springframework.data.redis.connection.RedisStringCommands;
|
||||
import org.springframework.data.redis.core.types.Expiration;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link DefaultRedisCacheWriter}
|
||||
*
|
||||
* @author John Blum
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class DefaultRedisCacheWriterUnitTests {
|
||||
|
||||
@Mock
|
||||
private CacheStatisticsCollector mockCacheStatisticsCollector = mock(CacheStatisticsCollector.class);
|
||||
|
||||
@Mock
|
||||
private RedisConnection mockConnection;
|
||||
|
||||
@Mock(strictness = Mock.Strictness.LENIENT)
|
||||
private RedisConnectionFactory mockConnectionFactory;
|
||||
|
||||
@Mock
|
||||
private ReactiveRedisConnection mockReactiveConnection;
|
||||
|
||||
@Mock(strictness = Mock.Strictness.LENIENT)
|
||||
private TestReactiveRedisConnectionFactory mockReactiveConnectionFactory;
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
doReturn(this.mockConnection).when(this.mockConnectionFactory).getConnection();
|
||||
doReturn(this.mockConnection).when(this.mockReactiveConnectionFactory).getConnection();
|
||||
doReturn(this.mockReactiveConnection).when(this.mockReactiveConnectionFactory).getReactiveConnection();
|
||||
}
|
||||
|
||||
private RedisCacheWriter newRedisCacheWriter() {
|
||||
return new DefaultRedisCacheWriter(this.mockConnectionFactory, mock(BatchStrategy.class))
|
||||
.withStatisticsCollector(this.mockCacheStatisticsCollector);
|
||||
}
|
||||
|
||||
private RedisCacheWriter newReactiveRedisCacheWriter() {
|
||||
return newReactiveRedisCacheWriter(Duration.ZERO);
|
||||
}
|
||||
|
||||
private RedisCacheWriter newReactiveRedisCacheWriter(Duration sleepTime) {
|
||||
return new DefaultRedisCacheWriter(this.mockReactiveConnectionFactory, sleepTime, mock(BatchStrategy.class))
|
||||
.withStatisticsCollector(this.mockCacheStatisticsCollector);
|
||||
}
|
||||
|
||||
@Test // GH-2351
|
||||
void getWithNonNullTtl() {
|
||||
|
||||
byte[] key = "TestKey".getBytes();
|
||||
byte[] value = "TestValue".getBytes();
|
||||
|
||||
Duration ttl = Duration.ofSeconds(15);
|
||||
Expiration expiration = Expiration.from(ttl);
|
||||
|
||||
RedisStringCommands mockStringCommands = mock(RedisStringCommands.class);
|
||||
|
||||
doReturn(mockStringCommands).when(this.mockConnection).stringCommands();
|
||||
doReturn(value).when(mockStringCommands).getEx(any(), any());
|
||||
|
||||
RedisCacheWriter cacheWriter = newRedisCacheWriter();
|
||||
|
||||
assertThat(cacheWriter.get("TestCache", key, ttl)).isEqualTo(value);
|
||||
|
||||
verify(this.mockConnection, times(1)).stringCommands();
|
||||
verify(mockStringCommands, times(1)).getEx(eq(key), eq(expiration));
|
||||
verify(this.mockConnection).close();
|
||||
verifyNoMoreInteractions(this.mockConnection, mockStringCommands);
|
||||
}
|
||||
|
||||
@Test // GH-2351
|
||||
void getWithNullTtl() {
|
||||
|
||||
byte[] key = "TestKey".getBytes();
|
||||
byte[] value = "TestValue".getBytes();
|
||||
|
||||
RedisStringCommands mockStringCommands = mock(RedisStringCommands.class);
|
||||
|
||||
doReturn(mockStringCommands).when(this.mockConnection).stringCommands();
|
||||
doReturn(value).when(mockStringCommands).get(any());
|
||||
|
||||
RedisCacheWriter cacheWriter = newRedisCacheWriter();
|
||||
|
||||
assertThat(cacheWriter.get("TestCache", key, null)).isEqualTo(value);
|
||||
|
||||
verify(this.mockConnection, times(1)).stringCommands();
|
||||
verify(mockStringCommands, times(1)).get(eq(key));
|
||||
verify(this.mockConnection).close();
|
||||
verifyNoMoreInteractions(this.mockConnection, mockStringCommands);
|
||||
}
|
||||
|
||||
@Test // GH-2650
|
||||
@SuppressWarnings("all")
|
||||
void retrieveWithNoCacheName() {
|
||||
|
||||
byte[] key = "TestKey".getBytes();
|
||||
|
||||
RedisCacheWriter cacheWriter = newReactiveRedisCacheWriter();
|
||||
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> cacheWriter.retrieve(null, key))
|
||||
.withMessage("Name must not be null")
|
||||
.withNoCause();
|
||||
|
||||
verifyNoInteractions(this.mockReactiveConnectionFactory);
|
||||
}
|
||||
|
||||
@Test // GH-2650
|
||||
@SuppressWarnings("all")
|
||||
void retrieveWithNoKey() {
|
||||
|
||||
RedisCacheWriter cacheWriter = newReactiveRedisCacheWriter();
|
||||
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> cacheWriter.retrieve("TestCacheName", null))
|
||||
.withMessage("Key must not be null")
|
||||
.withNoCause();
|
||||
|
||||
verifyNoInteractions(this.mockReactiveConnectionFactory);
|
||||
}
|
||||
|
||||
@Test // GH-2650
|
||||
void retrieveReturnsAsyncFutureWithValue() throws Exception {
|
||||
|
||||
byte[] key = "TestKey".getBytes();
|
||||
|
||||
RedisStringCommands mockStringCommands = mock(RedisStringCommands.class);
|
||||
|
||||
doReturn(mockStringCommands).when(this.mockConnection).stringCommands();
|
||||
doReturn("test".getBytes()).when(mockStringCommands).get(any(byte[].class));
|
||||
|
||||
RedisCacheWriter cacheWriter = newRedisCacheWriter();
|
||||
|
||||
CompletableFuture<byte[]> result = cacheWriter.retrieve("TestCacheName", key);
|
||||
|
||||
assertThat(result).isNotNull();
|
||||
verifyNoInteractions(this.mockCacheStatisticsCollector);
|
||||
|
||||
byte[] value = result.get();
|
||||
|
||||
assertThat(value).isNotNull();
|
||||
assertThat(new String(value)).isEqualTo("test");
|
||||
|
||||
verify(mockStringCommands, times(1)).get(eq(key));
|
||||
verify(this.mockCacheStatisticsCollector, times(1)).incGets(eq("TestCacheName"));
|
||||
verify(this.mockCacheStatisticsCollector, times(1)).incHits(eq("TestCacheName"));
|
||||
verifyNoMoreInteractions(mockStringCommands, this.mockCacheStatisticsCollector);
|
||||
}
|
||||
|
||||
@Test // GH-2650
|
||||
void retrieveWithExpirationReturnsAsyncFutureWithValue() throws Exception {
|
||||
|
||||
byte[] key = "TestKey".getBytes();
|
||||
|
||||
Duration thirtySeconds = Duration.ofSeconds(30L);
|
||||
|
||||
RedisStringCommands mockStringCommands = mock(RedisStringCommands.class);
|
||||
|
||||
doReturn(mockStringCommands).when(this.mockConnection).stringCommands();
|
||||
doReturn("test".getBytes()).when(mockStringCommands).getEx(any(byte[].class), any(Expiration.class));
|
||||
|
||||
RedisCacheWriter cacheWriter = newRedisCacheWriter();
|
||||
|
||||
CompletableFuture<byte[]> result = cacheWriter.retrieve("TestCacheName", key, thirtySeconds);
|
||||
|
||||
assertThat(result).isNotNull();
|
||||
|
||||
byte[] value = result.get();
|
||||
|
||||
assertThat(value).isNotNull();
|
||||
assertThat(new String(value)).isEqualTo("test");
|
||||
|
||||
verify(mockStringCommands, times(1)).getEx(eq(key), eq(Expiration.from(thirtySeconds)));
|
||||
verify(this.mockCacheStatisticsCollector, times(1)).incGets(eq("TestCacheName"));
|
||||
verify(this.mockCacheStatisticsCollector, times(1)).incHits(eq("TestCacheName"));
|
||||
verifyNoMoreInteractions(mockStringCommands, this.mockCacheStatisticsCollector);
|
||||
}
|
||||
|
||||
@Test // GH-2650
|
||||
void retrieveReturnsReactiveFutureWithValue() throws Exception {
|
||||
|
||||
byte[] key = "TestKey".getBytes();
|
||||
|
||||
Duration sixtySeconds = Duration.ofMillis(60L);
|
||||
|
||||
RedisKeyCommands mockKeyCommands = mock(RedisKeyCommands.class);
|
||||
ReactiveStringCommands mockStringCommands = mock(ReactiveStringCommands.class);
|
||||
|
||||
doReturn(mockKeyCommands).when(this.mockConnection).keyCommands();
|
||||
doReturn(false).when(mockKeyCommands).exists(any(byte[].class));
|
||||
doReturn(mockStringCommands).when(this.mockReactiveConnection).stringCommands();
|
||||
doReturn(Mono.just(ByteBuffer.wrap("test".getBytes()))).when(mockStringCommands).get(any(ByteBuffer.class));
|
||||
|
||||
RedisCacheWriter cacheWriter = newReactiveRedisCacheWriter(sixtySeconds);
|
||||
|
||||
CompletableFuture<byte[]> result = cacheWriter.retrieve("TestCacheName", key);
|
||||
|
||||
assertThat(result).isNotNull();
|
||||
|
||||
byte[] value = result.get();
|
||||
|
||||
assertThat(value).isNotNull();
|
||||
assertThat(new String(value)).isEqualTo("test");
|
||||
|
||||
verify(mockKeyCommands, times(1)).exists(any(byte[].class));
|
||||
verify(mockStringCommands, times(1)).get(eq(ByteBuffer.wrap(key)));
|
||||
verify(this.mockCacheStatisticsCollector, times(1)).incGets(eq("TestCacheName"));
|
||||
verify(this.mockCacheStatisticsCollector, times(1)).incHits(eq("TestCacheName"));
|
||||
verifyNoMoreInteractions(mockKeyCommands, mockStringCommands, this.mockCacheStatisticsCollector);
|
||||
}
|
||||
|
||||
@Test // GH-2650
|
||||
void retrieveReturnsReactiveFutureWithNoValue() throws Exception {
|
||||
|
||||
byte[] key = "TestKey".getBytes();
|
||||
|
||||
RedisKeyCommands mockKeyCommands = mock(RedisKeyCommands.class);
|
||||
ReactiveStringCommands mockStringCommands = mock(ReactiveStringCommands.class);
|
||||
|
||||
doReturn(mockKeyCommands).when(this.mockConnection).keyCommands();
|
||||
doReturn(false).when(mockKeyCommands).exists(any(byte[].class));
|
||||
doReturn(mockStringCommands).when(this.mockReactiveConnection).stringCommands();
|
||||
doReturn(Mono.empty()).when(mockStringCommands).get(any(ByteBuffer.class));
|
||||
|
||||
RedisCacheWriter cacheWriter = newReactiveRedisCacheWriter();
|
||||
|
||||
CompletableFuture<byte[]> result = cacheWriter.retrieve("TestCacheName", key);
|
||||
|
||||
assertThat(result).isNotNull();
|
||||
|
||||
byte[] value = result.get();
|
||||
|
||||
assertThat(value).isNull();
|
||||
|
||||
verify(mockKeyCommands, times(1)).exists(any(byte[].class));
|
||||
verify(mockStringCommands, times(1)).get(eq(ByteBuffer.wrap(key)));
|
||||
verify(this.mockCacheStatisticsCollector, times(1)).incGets(eq("TestCacheName"));
|
||||
verify(this.mockCacheStatisticsCollector, times(1)).incMisses(eq("TestCacheName"));
|
||||
verifyNoMoreInteractions(mockKeyCommands, mockStringCommands, this.mockCacheStatisticsCollector);
|
||||
}
|
||||
|
||||
@Test // GH-2650
|
||||
void retrieveWithExpirationReturnsReactiveFutureWithValue() throws Exception {
|
||||
|
||||
byte[] key = "TestKey".getBytes();
|
||||
|
||||
Duration twoMinutes = Duration.ofMinutes(2L);
|
||||
|
||||
RedisKeyCommands mockKeyCommands = mock(RedisKeyCommands.class);
|
||||
ReactiveStringCommands mockStringCommands = mock(ReactiveStringCommands.class);
|
||||
|
||||
doReturn(mockKeyCommands).when(this.mockConnection).keyCommands();
|
||||
doReturn(false).when(mockKeyCommands).exists(any(byte[].class));
|
||||
doReturn(mockStringCommands).when(this.mockReactiveConnection).stringCommands();
|
||||
doReturn(Mono.just(ByteBuffer.wrap("test".getBytes()))).when(mockStringCommands).getEx(any(ByteBuffer.class), any());
|
||||
|
||||
RedisCacheWriter cacheWriter = newReactiveRedisCacheWriter();
|
||||
|
||||
CompletableFuture<byte[]> result = cacheWriter.retrieve("TestCacheName", key, twoMinutes);
|
||||
|
||||
assertThat(result).isNotNull();
|
||||
|
||||
byte[] value = result.get();
|
||||
|
||||
assertThat(value).isNotNull();
|
||||
assertThat(new String(value)).isEqualTo("test");
|
||||
|
||||
verify(mockKeyCommands, times(1)).exists(any(byte[].class));
|
||||
verify(mockStringCommands, times(1)).getEx(eq(ByteBuffer.wrap(key)), eq(Expiration.from(twoMinutes)));
|
||||
verify(this.mockCacheStatisticsCollector, times(1)).incGets(eq("TestCacheName"));
|
||||
verify(this.mockCacheStatisticsCollector, times(1)).incHits(eq("TestCacheName"));
|
||||
verifyNoMoreInteractions(mockKeyCommands, mockStringCommands, this.mockCacheStatisticsCollector);
|
||||
}
|
||||
|
||||
interface TestReactiveRedisConnectionFactory extends ReactiveRedisConnectionFactory, RedisConnectionFactory { }
|
||||
|
||||
}
|
||||
@@ -15,18 +15,16 @@
|
||||
*/
|
||||
package org.springframework.data.redis.cache;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||
import static org.assertj.core.api.Assumptions.assumeThat;
|
||||
import static org.awaitility.Awaitility.await;
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.assertj.core.api.Assumptions.*;
|
||||
import static org.awaitility.Awaitility.*;
|
||||
|
||||
import io.netty.util.concurrent.DefaultThreadFactory;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Duration;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.Month;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.Instant;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
@@ -45,7 +43,6 @@ import java.util.function.Supplier;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
|
||||
import org.springframework.cache.Cache.ValueWrapper;
|
||||
import org.springframework.cache.interceptor.SimpleKey;
|
||||
import org.springframework.cache.interceptor.SimpleKeyGenerator;
|
||||
@@ -57,14 +54,10 @@ import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactor
|
||||
import org.springframework.data.redis.serializer.RedisSerializationContext.SerializationPair;
|
||||
import org.springframework.data.redis.serializer.RedisSerializer;
|
||||
import org.springframework.data.redis.test.condition.EnabledOnCommand;
|
||||
import org.springframework.data.redis.test.condition.EnabledOnRedisDriver;
|
||||
import org.springframework.data.redis.test.condition.RedisDriver;
|
||||
import org.springframework.data.redis.test.extension.parametrized.MethodSource;
|
||||
import org.springframework.data.redis.test.extension.parametrized.ParameterizedRedisTest;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
import io.netty.util.concurrent.DefaultThreadFactory;
|
||||
|
||||
/**
|
||||
* Tests for {@link RedisCache} with {@link DefaultRedisCacheWriter} using different {@link RedisSerializer} and
|
||||
* {@link RedisConnectionFactory} pairs.
|
||||
@@ -353,8 +346,9 @@ public class RedisCacheTests {
|
||||
|
||||
cacheWithCustomPrefix.put("key-1", sample);
|
||||
|
||||
doWithConnection(connection -> assertThat(connection.stringCommands()
|
||||
.get("_cache_key-1".getBytes(StandardCharsets.UTF_8))).isEqualTo(binarySample));
|
||||
doWithConnection(
|
||||
connection -> assertThat(connection.stringCommands().get("_cache_key-1".getBytes(StandardCharsets.UTF_8)))
|
||||
.isEqualTo(binarySample));
|
||||
}
|
||||
|
||||
@ParameterizedRedisTest // DATAREDIS-1041
|
||||
@@ -366,8 +360,9 @@ public class RedisCacheTests {
|
||||
|
||||
cacheWithCustomPrefix.put("key-1", sample);
|
||||
|
||||
doWithConnection(connection -> assertThat(connection.stringCommands()
|
||||
.get("redis::cache::key-1".getBytes(StandardCharsets.UTF_8))).isEqualTo(binarySample));
|
||||
doWithConnection(connection -> assertThat(
|
||||
connection.stringCommands().get("redis::cache::key-1".getBytes(StandardCharsets.UTF_8)))
|
||||
.isEqualTo(binarySample));
|
||||
}
|
||||
|
||||
@ParameterizedRedisTest // DATAREDIS-715
|
||||
@@ -429,8 +424,8 @@ public class RedisCacheTests {
|
||||
.generateKey(Collections.singletonMap("map-key", new ComplexKey(sample.getFirstname(), sample.getBirthdate())));
|
||||
cache.put(key, sample);
|
||||
|
||||
ValueWrapper target = cache.get(SimpleKeyGenerator
|
||||
.generateKey(Collections.singletonMap("map-key", new ComplexKey(sample.getFirstname(), sample.getBirthdate()))));
|
||||
ValueWrapper target = cache.get(SimpleKeyGenerator.generateKey(
|
||||
Collections.singletonMap("map-key", new ComplexKey(sample.getFirstname(), sample.getBirthdate()))));
|
||||
|
||||
assertThat(target.get()).isEqualTo(sample);
|
||||
}
|
||||
@@ -481,6 +476,11 @@ public class RedisCacheTests {
|
||||
return CompletableFuture.completedFuture(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> store(String name, byte[] key, byte[] value, @Nullable Duration ttl) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(String name, byte[] key, byte[] value, @Nullable Duration ttl) {
|
||||
storage.set(value);
|
||||
@@ -572,28 +572,18 @@ public class RedisCacheTests {
|
||||
assertThat(cache.get(this.cacheKey, Person.class)).isNull();
|
||||
}
|
||||
|
||||
@ParameterizedRedisTest
|
||||
@ParameterizedRedisTest // GH-2650
|
||||
void retrieveCacheValueUsingJedis() {
|
||||
|
||||
// TODO: Is there a better way to do this? @EnableOnRedisDriver(RedisDriver.JEDIS) does not work!
|
||||
assumeThat(this.connectionFactory instanceof JedisConnectionFactory).isTrue();
|
||||
|
||||
assertThatExceptionOfType(UnsupportedOperationException.class)
|
||||
.isThrownBy(() -> this.cache.retrieve(this.binaryCacheKey))
|
||||
.withMessageContaining(RedisCache.class.getName())
|
||||
.withNoCause();
|
||||
}
|
||||
|
||||
@ParameterizedRedisTest
|
||||
void retrieveCacheValueWithLoaderUsingJedis() {
|
||||
|
||||
// TODO: Is there a better way to do this? @EnableOnRedisDriver(RedisDriver.JEDIS) does not work!
|
||||
assumeThat(this.connectionFactory instanceof JedisConnectionFactory).isTrue();
|
||||
.isThrownBy(() -> this.cache.retrieve(this.binaryCacheKey)).withMessageContaining("RedisCache");
|
||||
|
||||
assertThatExceptionOfType(UnsupportedOperationException.class)
|
||||
.isThrownBy(() -> this.cache.retrieve(this.binaryCacheKey, () -> CompletableFuture.completedFuture("TEST")))
|
||||
.withMessageContaining(RedisCache.class.getName())
|
||||
.withNoCause();
|
||||
.isThrownBy(() -> this.cache.retrieve(this.binaryCacheKey, () -> CompletableFuture.completedFuture("TEST")))
|
||||
.withMessageContaining("RedisCache");
|
||||
}
|
||||
|
||||
@ParameterizedRedisTest // GH-2650
|
||||
@@ -631,19 +621,13 @@ public class RedisCacheTests {
|
||||
RedisCache cache = new RedisCache("cache", usingLockingRedisCacheWriter(Duration.ofMillis(5L)),
|
||||
usingRedisCacheConfiguration());
|
||||
|
||||
RedisCacheWriter cacheWriter = cache.getCacheWriter();
|
||||
|
||||
assertThat(cacheWriter).isInstanceOf(DefaultRedisCacheWriter.class);
|
||||
|
||||
((DefaultRedisCacheWriter) cacheWriter).lock("cache");
|
||||
DefaultRedisCacheWriter cacheWriter = (DefaultRedisCacheWriter) cache.getCacheWriter();
|
||||
cacheWriter.lock("cache");
|
||||
|
||||
CompletableFuture<String> value = (CompletableFuture<String>) cache.retrieve(this.key);
|
||||
|
||||
assertThat(value).isNotNull();
|
||||
assertThat(value.getNow(mockValue)).isEqualTo(mockValue);
|
||||
assertThat(value).isNotDone();
|
||||
|
||||
((DefaultRedisCacheWriter) cacheWriter).unlock("cache");
|
||||
cacheWriter.unlock("cache");
|
||||
|
||||
assertThat(value.get(15L, TimeUnit.MILLISECONDS)).isEqualTo(testValue);
|
||||
assertThat(value).isDone();
|
||||
@@ -656,14 +640,8 @@ public class RedisCacheTests {
|
||||
assumeThat(this.connectionFactory instanceof LettuceConnectionFactory).isTrue();
|
||||
|
||||
RedisCache cache = new RedisCache("cache", usingLockingRedisCacheWriter(), usingRedisCacheConfiguration());
|
||||
|
||||
AtomicBoolean loaded = new AtomicBoolean(false);
|
||||
|
||||
Date birthdate = Date.from(LocalDateTime.of(2023, Month.SEPTEMBER, 22, 17, 3)
|
||||
.toInstant(ZoneOffset.UTC));
|
||||
|
||||
Person jon = new Person("Jon", birthdate);
|
||||
|
||||
Person jon = new Person("Jon", Date.from(Instant.now()));
|
||||
CompletableFuture<Person> valueLoader = CompletableFuture.completedFuture(jon);
|
||||
|
||||
Supplier<CompletableFuture<Person>> valueLoaderSupplier = () -> {
|
||||
@@ -673,13 +651,29 @@ public class RedisCacheTests {
|
||||
|
||||
CompletableFuture<Person> value = cache.retrieve(this.key, valueLoaderSupplier);
|
||||
|
||||
assertThat(value).isNotNull();
|
||||
assertThat(loaded.get()).isFalse();
|
||||
assertThat(value.get()).isEqualTo(jon);
|
||||
assertThat(loaded.get()).isTrue();
|
||||
assertThat(value).isDone();
|
||||
}
|
||||
|
||||
@ParameterizedRedisTest // GH-2650
|
||||
void retrieveStoresLoadedValue() throws Exception {
|
||||
|
||||
// TODO: Is there a better way to do this? @EnableOnRedisDriver(RedisDriver.LETTUCE) does not work!
|
||||
assumeThat(this.connectionFactory instanceof LettuceConnectionFactory).isTrue();
|
||||
|
||||
RedisCache cache = new RedisCache("cache", usingLockingRedisCacheWriter(), usingRedisCacheConfiguration());
|
||||
Person jon = new Person("Jon", Date.from(Instant.now()));
|
||||
Supplier<CompletableFuture<Person>> valueLoaderSupplier = () -> CompletableFuture.completedFuture(jon);
|
||||
|
||||
cache.retrieve(this.key, valueLoaderSupplier).get();
|
||||
|
||||
doWithConnection(
|
||||
connection -> assertThat(connection.keyCommands().exists("cache::key-1".getBytes(StandardCharsets.UTF_8)))
|
||||
.isTrue());
|
||||
}
|
||||
|
||||
@ParameterizedRedisTest // GH-2650
|
||||
void retrieveReturnsNull() throws Exception {
|
||||
|
||||
@@ -732,8 +726,8 @@ public class RedisCacheTests {
|
||||
|
||||
private Function<RedisCacheConfiguration, RedisCacheConfiguration> withTtiExpiration() {
|
||||
|
||||
Function<RedisCacheConfiguration, RedisCacheConfiguration> entryTtlFunction =
|
||||
cacheConfiguration -> cacheConfiguration.entryTtl(Duration.ofMillis(100));
|
||||
Function<RedisCacheConfiguration, RedisCacheConfiguration> entryTtlFunction = cacheConfiguration -> cacheConfiguration
|
||||
.entryTtl(Duration.ofMillis(100));
|
||||
|
||||
return entryTtlFunction.andThen(RedisCacheConfiguration::enableTimeToIdle);
|
||||
}
|
||||
@@ -752,7 +746,7 @@ public class RedisCacheTests {
|
||||
private String firstname;
|
||||
private Date birthdate;
|
||||
|
||||
public Person() { }
|
||||
public Person() {}
|
||||
|
||||
public Person(String firstname, Date birthdate) {
|
||||
this.firstname = firstname;
|
||||
@@ -787,7 +781,7 @@ public class RedisCacheTests {
|
||||
}
|
||||
|
||||
return Objects.equals(this.getFirstname(), that.getFirstname())
|
||||
&& Objects.equals(this.getBirthdate(), that.getBirthdate());
|
||||
&& Objects.equals(this.getBirthdate(), that.getBirthdate());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -797,8 +791,7 @@ public class RedisCacheTests {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "RedisCacheTests.Person(firstname=" + this.getFirstname()
|
||||
+ ", birthdate=" + this.getBirthdate() + ")";
|
||||
return "RedisCacheTests.Person(firstname=" + this.getFirstname() + ", birthdate=" + this.getBirthdate() + ")";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -844,7 +837,7 @@ public class RedisCacheTests {
|
||||
}
|
||||
|
||||
return Objects.equals(this.getFirstname(), that.getFirstname())
|
||||
&& Objects.equals(this.getBirthdate(), that.getBirthdate());
|
||||
&& Objects.equals(this.getBirthdate(), that.getBirthdate());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -854,8 +847,7 @@ public class RedisCacheTests {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "RedisCacheTests.ComplexKey(firstame=" + this.getFirstname()
|
||||
+ ", birthdate=" + this.getBirthdate() + ")";
|
||||
return "RedisCacheTests.ComplexKey(firstame=" + this.getFirstname() + ", birthdate=" + this.getBirthdate() + ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,29 +15,20 @@
|
||||
*/
|
||||
package org.springframework.data.redis.cache;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.ArgumentMatchers.isA;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoInteractions;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.cache.support.NullValue;
|
||||
import org.springframework.data.redis.util.ByteUtils;
|
||||
import org.springframework.data.redis.serializer.RedisSerializationContext.SerializationPair;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link RedisCache}.
|
||||
*
|
||||
* @author John Blum
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
class RedisCacheUnitTests {
|
||||
|
||||
@@ -46,76 +37,21 @@ class RedisCacheUnitTests {
|
||||
|
||||
RedisCacheWriter mockCacheWriter = mock(RedisCacheWriter.class);
|
||||
|
||||
doReturn(CompletableFuture.completedFuture("TEST".getBytes()))
|
||||
.when(mockCacheWriter).retrieve(anyString(), any(byte[].class));
|
||||
when(mockCacheWriter.supportsAsyncRetrieve()).thenReturn(true);
|
||||
when(mockCacheWriter.retrieve(anyString(), any(byte[].class)))
|
||||
.thenReturn(CompletableFuture.completedFuture("TEST".getBytes()));
|
||||
|
||||
RedisCache cache = new RedisCache("TestCache", mockCacheWriter,
|
||||
RedisCacheConfiguration.defaultCacheConfig());
|
||||
RedisCacheConfiguration.defaultCacheConfig().serializeValuesWith(SerializationPair.byteArray()));
|
||||
|
||||
CompletableFuture<byte[]> value = cache.retrieveValue("TestKey");
|
||||
CompletableFuture<byte[]> value = (CompletableFuture<byte[]>) cache.retrieve("TestKey");
|
||||
|
||||
assertThat(value).isNotNull();
|
||||
assertThat(new String(value.get())).isEqualTo("TEST");
|
||||
|
||||
verify(mockCacheWriter, times(1)).retrieve(eq("TestCache"), isA(byte[].class));
|
||||
verify(mockCacheWriter).supportsAsyncRetrieve();
|
||||
verifyNoMoreInteractions(mockCacheWriter);
|
||||
}
|
||||
|
||||
@Test // GH-2650
|
||||
void nullSafeDeserializedStoreValueWithNullValueIsNullSafe() {
|
||||
|
||||
RedisCacheConfiguration cacheConfiguration = RedisCacheConfiguration.defaultCacheConfig();
|
||||
RedisCacheWriter mockCacheWriter = mock(RedisCacheWriter.class);
|
||||
RedisCache cache = new RedisCache("TestCache", mockCacheWriter, cacheConfiguration);
|
||||
|
||||
assertThat(cache.nullSafeDeserializedStoreValue(null)).isNull();
|
||||
|
||||
verifyNoInteractions(mockCacheWriter);
|
||||
}
|
||||
|
||||
@Test // GH-2650
|
||||
void nullSafeDeserializedStoreValueWithBinaryNullValueAllowingNullValues() {
|
||||
|
||||
RedisCacheConfiguration cacheConfiguration = RedisCacheConfiguration.defaultCacheConfig();
|
||||
RedisCacheWriter mockCacheWriter = mock(RedisCacheWriter.class);
|
||||
RedisCache cache = new RedisCache("TestCache", mockCacheWriter, cacheConfiguration);
|
||||
|
||||
assertThat(cacheConfiguration.getAllowCacheNullValues()).isTrue();
|
||||
assertThat(cache.nullSafeDeserializedStoreValue(RedisCache.BINARY_NULL_VALUE)).isNull();
|
||||
|
||||
verifyNoInteractions(mockCacheWriter);
|
||||
}
|
||||
|
||||
@Test // GH-2650
|
||||
void nullSafeDeserializedStoreValueWithBinaryNullValueDisablingNullValues() {
|
||||
|
||||
RedisCacheConfiguration cacheConfiguration =
|
||||
RedisCacheConfiguration.defaultCacheConfig().disableCachingNullValues();
|
||||
|
||||
RedisCacheWriter mockCacheWriter = mock(RedisCacheWriter.class);
|
||||
|
||||
RedisCache cache = new RedisCache("TestCache", mockCacheWriter, cacheConfiguration);
|
||||
|
||||
assertThat(cacheConfiguration.getAllowCacheNullValues()).isFalse();
|
||||
assertThat(cache.nullSafeDeserializedStoreValue(RedisCache.BINARY_NULL_VALUE)).isEqualTo(NullValue.INSTANCE);
|
||||
|
||||
verifyNoInteractions(mockCacheWriter);
|
||||
}
|
||||
|
||||
@Test // GH-2650
|
||||
void nullSafeDeserializedStoreValueWithNonNullValue() {
|
||||
|
||||
RedisCacheConfiguration cacheConfiguration = RedisCacheConfiguration.defaultCacheConfig();
|
||||
|
||||
byte[] serializedValue = ByteUtils.getBytes(cacheConfiguration.getValueSerializationPair()
|
||||
.write("TestValue"));
|
||||
|
||||
RedisCacheWriter mockCacheWriter = mock(RedisCacheWriter.class);
|
||||
|
||||
RedisCache cache = new RedisCache("TestCache", mockCacheWriter, cacheConfiguration);
|
||||
|
||||
assertThat(cache.nullSafeDeserializedStoreValue(serializedValue)).isEqualTo("TestValue");
|
||||
|
||||
verifyNoInteractions(mockCacheWriter);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user