DATAREDIS-852 - Return always a lower/upper bound range index using Range.Bound.unbounded() with Lettuce.

We now return in List, String, and ZSet commands a lower/upper bound range index to prevent NullPointerException. Previously, unbounded ranges could render null values that were attempted to cast to primitives.

Original pull request: #353.
This commit is contained in:
michele
2018-07-19 15:21:29 +02:00
committed by Mark Paluch
parent 2dfe1e2b4e
commit 3a7d871119
6 changed files with 136 additions and 21 deletions

View File

@@ -21,24 +21,28 @@ import static org.hamcrest.core.IsEqual.*;
import static org.hamcrest.core.IsNot.*;
import static org.junit.Assert.*;
import static org.junit.Assume.assumeThat;
import static org.springframework.data.domain.Range.Bound.*;
import java.nio.ByteBuffer;
import java.time.Duration;
import java.util.Arrays;
import org.junit.Assume;
import org.junit.Test;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.domain.Range;
import org.springframework.data.redis.connection.ReactiveListCommands.PopResult;
import org.springframework.data.redis.connection.ReactiveListCommands.PushCommand;
import org.springframework.data.redis.connection.ReactiveRedisConnection;
import org.springframework.data.redis.connection.ReactiveRedisConnection.CommandResponse;
import org.springframework.data.redis.connection.ReactiveRedisConnection.RangeCommand;
import org.springframework.data.redis.connection.RedisListCommands.Position;
import org.springframework.data.redis.test.util.LettuceRedisClientProvider;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
/**
* @author Christoph Strobl
* @author Mark Paluch
* @author Michele Mancioppi
*/
public class LettuceReactiveListCommandTests extends LettuceReactiveCommandsTestsBase {
@@ -106,6 +110,32 @@ public class LettuceReactiveListCommandTests extends LettuceReactiveCommandsTest
contains(VALUE_2_BBUFFER, VALUE_3_BBUFFER));
}
@Test // DATAREDIS-852
public void lRangeShouldReturnValuesCorrectlyWithMinUnbounded() {
nativeCommands.rpush(KEY_1, VALUE_1, VALUE_2, VALUE_3);
RangeCommand rangeCommand = RangeCommand.key(KEY_1_BBUFFER).within(Range.of(unbounded(), inclusive(1L)));
StepVerifier.create(connection.listCommands().lRange(Mono.just(rangeCommand)).flatMap(CommandResponse::getOutput)) //
.expectNext(VALUE_1_BBUFFER)
.expectNext(VALUE_2_BBUFFER)
.verifyComplete();
}
@Test // DATAREDIS-852
public void lRangeShouldReturnValuesCorrectlyWithMaxUnbounded() {
nativeCommands.rpush(KEY_1, VALUE_1, VALUE_2, VALUE_3);
RangeCommand rangeCommand = RangeCommand.key(KEY_1_BBUFFER).within(Range.of(inclusive(1L), unbounded()));
StepVerifier.create(connection.listCommands().lRange(Mono.just(rangeCommand)).flatMap(CommandResponse::getOutput)) //
.expectNext(VALUE_2_BBUFFER)
.expectNext(VALUE_3_BBUFFER)
.verifyComplete();
}
@Test // DATAREDIS-525
public void lTrimShouldReturnValuesCorrectly() {
@@ -115,6 +145,30 @@ public class LettuceReactiveListCommandTests extends LettuceReactiveCommandsTest
assertThat(nativeCommands.lrange(KEY_1, 0, -1), not(contains(VALUE_1_BBUFFER)));
}
@Test // DATAREDIS-852
public void lTrimShouldReturnValuesCorrectlyWithMinUnbounded() {
nativeCommands.rpush(KEY_1, VALUE_1, VALUE_2, VALUE_3);
RangeCommand rangeCommand = RangeCommand.key(KEY_1_BBUFFER).within(Range.of(unbounded(), inclusive(1L)));
StepVerifier.create(connection.listCommands().lTrim(Mono.just(rangeCommand))) //
.expectNext(new ReactiveRedisConnection.BooleanResponse<>(rangeCommand, true)) //
.verifyComplete();
}
@Test // DATAREDIS-852
public void lTrimShouldReturnValuesCorrectlyWithMaxUnbounded() {
nativeCommands.rpush(KEY_1, VALUE_1, VALUE_2, VALUE_3);
RangeCommand rangeCommand = RangeCommand.key(KEY_1_BBUFFER).within(Range.of(inclusive(1L), unbounded()));
StepVerifier.create(connection.listCommands().lTrim(Mono.just(rangeCommand))) //
.expectNext(new ReactiveRedisConnection.BooleanResponse<>(rangeCommand, true)) //
.verifyComplete();
}
@Test // DATAREDIS-525
public void lIndexShouldReturnValueCorrectly() {

View File

@@ -41,11 +41,14 @@ import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.junit.Test;
import org.springframework.data.domain.Range;
import org.springframework.data.domain.Range.Bound;
import org.springframework.data.redis.connection.ReactiveRedisConnection;
import org.springframework.data.redis.connection.ReactiveRedisConnection.ByteBufferResponse;
import org.springframework.data.redis.connection.ReactiveRedisConnection.CommandResponse;
import org.springframework.data.redis.connection.ReactiveRedisConnection.KeyCommand;
import org.springframework.data.redis.connection.ReactiveRedisConnection.MultiValueResponse;
import org.springframework.data.redis.connection.ReactiveRedisConnection.RangeCommand;
import org.springframework.data.redis.connection.ReactiveStringCommands.SetCommand;
import org.springframework.data.redis.connection.RedisStringCommands.BitOperation;
import org.springframework.data.redis.core.types.Expiration;
@@ -55,6 +58,7 @@ import org.springframework.data.redis.util.ByteUtils;
/**
* @author Christoph Strobl
* @author Mark Paluch
* @author Michele Mancioppi
*/
public class LettuceReactiveStringCommandsTests extends LettuceReactiveCommandsTestsBase {
@@ -295,6 +299,30 @@ public class LettuceReactiveStringCommandsTests extends LettuceReactiveCommandsT
.verifyComplete();
}
@Test // DATAREDIS-525
public void getRangeShouldReturnSubstringCorrectlyWithMinUnbound() {
nativeCommands.set(KEY_1, VALUE_1);
RangeCommand rangeCommand = RangeCommand.key(KEY_1_BBUFFER).within(Range.of(Bound.unbounded(), Bound.inclusive(2L)));
StepVerifier.create(connection.stringCommands().getRange(Mono.just(rangeCommand))) //
.expectNext(new ReactiveRedisConnection.ByteBufferResponse<>(rangeCommand, ByteBuffer.wrap("val".getBytes())))
.verifyComplete();
}
@Test // DATAREDIS-525
public void getRangeShouldReturnSubstringCorrectlyWithMaxUnbound() {
nativeCommands.set(KEY_1, VALUE_1);
RangeCommand rangeCommand = RangeCommand.key(KEY_1_BBUFFER).within(Range.of(Bound.inclusive(0L), Bound.unbounded()));
StepVerifier.create(connection.stringCommands().getRange(Mono.just(rangeCommand))) //
.expectNext(new ReactiveRedisConnection.ByteBufferResponse<>(rangeCommand, ByteBuffer.wrap(VALUE_1.getBytes())))
.verifyComplete();
}
@Test // DATAREDIS-525
public void setRangeShouldReturnNewStringLengthCorrectly() {

View File

@@ -35,6 +35,7 @@ import org.springframework.data.redis.core.ScanOptions;
*
* @author Christoph Strobl
* @author Mark Paluch
* @author Michele Mancioppi
*/
public class LettuceReactiveZSetCommandsTests extends LettuceReactiveCommandsTestsBase {
@@ -142,6 +143,32 @@ public class LettuceReactiveZSetCommandsTests extends LettuceReactiveCommandsTes
.verifyComplete();
}
@Test // DATAREDIS-852
public void zRangeByScoreShouldReturnValuesCorrectlyWithMinUnbounded() {
nativeCommands.zadd(KEY_1, 1D, VALUE_1);
nativeCommands.zadd(KEY_1, 2D, VALUE_2);
nativeCommands.zadd(KEY_1, 3D, VALUE_3);
StepVerifier.create(connection.zSetCommands().zRangeByScore(KEY_1_BBUFFER, Range.of(Range.Bound.unbounded(),
Range.Bound.inclusive(3D)))) //
.expectNext(VALUE_1_BBUFFER, VALUE_2_BBUFFER, VALUE_3_BBUFFER) //
.verifyComplete();
}
@Test // DATAREDIS-852
public void zRangeByScoreShouldReturnValuesCorrectlyWithMaxUnbounded() {
nativeCommands.zadd(KEY_1, 1D, VALUE_1);
nativeCommands.zadd(KEY_1, 2D, VALUE_2);
nativeCommands.zadd(KEY_1, 3D, VALUE_3);
StepVerifier.create(connection.zSetCommands().zRangeByScore(KEY_1_BBUFFER, Range.of(Range.Bound.inclusive(0D),
Range.Bound.unbounded()))) //
.expectNext(VALUE_1_BBUFFER, VALUE_2_BBUFFER, VALUE_3_BBUFFER) //
.verifyComplete();
}
@Test // DATAREDIS-525
public void zRangeByScoreShouldReturnValuesCorrectlyWithMinExclusion() {