-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: implement lpush and lrange (#7)
Implements the list commands `lpush` and `lrange`. A limitation on `lrange` is that while Redis allows for `long` offsets into the list, Momento expects integer offsets. Therefore we test if the offset is out of range and throw an error in that event.
- Loading branch information
Showing
7 changed files
with
238 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package momento.lettuce.utils; | ||
|
||
public class ValidatorUtils { | ||
public static void ensureInIntegerRange(long value, String argumentName) { | ||
if (value < Integer.MIN_VALUE || value > Integer.MAX_VALUE) { | ||
throw MomentoToLettuceExceptionMapper.createIntegerOutOfRangeException(argumentName, value); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
package momento.lettuce; | ||
|
||
import static momento.lettuce.TestUtils.generateListOfRandomStrings; | ||
import static momento.lettuce.TestUtils.randomString; | ||
import static org.junit.jupiter.api.Assertions.assertEquals; | ||
import static org.junit.jupiter.api.Assertions.assertThrows; | ||
|
||
import java.util.ArrayList; | ||
import java.util.Collections; | ||
import java.util.List; | ||
import momento.sdk.exceptions.InvalidArgumentException; | ||
import org.junit.jupiter.api.Test; | ||
|
||
final class ListTest extends BaseTestClass { | ||
private static List<String> reverseList(List<String> list) { | ||
List<String> reversedList = new ArrayList<>(list); | ||
Collections.reverse(reversedList); | ||
return reversedList; | ||
} | ||
|
||
@Test | ||
public void testLPushHappyPath() { | ||
var key = randomString(); | ||
var values = generateListOfRandomStrings(3); | ||
var lPushResponse = client.lpush(key, values.toArray(new String[0])).block(); | ||
assertEquals(3, lPushResponse); | ||
} | ||
|
||
@Test | ||
public void testLRangeHappyPath() { | ||
var key = randomString(); | ||
var values = generateListOfRandomStrings(5); | ||
var valuesReversed = reverseList(values); | ||
|
||
var lPushResponse = client.lpush(key, values.toArray(new String[0])).block(); | ||
assertEquals(5, lPushResponse); | ||
|
||
// Fetch the whole list | ||
var lRangeResponse = client.lrange(key, 0, -1).collectList().block(); | ||
assertEquals(5, lRangeResponse.size()); | ||
|
||
// The values backwards should be the same as the original values due to the reduce semantics of | ||
// LPUSH | ||
assertEquals(valuesReversed, lRangeResponse); | ||
|
||
// Test positive offsets | ||
lRangeResponse = client.lrange(key, 2, 4).collectList().block(); | ||
assertEquals(3, lRangeResponse.size()); | ||
assertEquals(valuesReversed.subList(2, 5), lRangeResponse); | ||
|
||
// Test negative offsets | ||
lRangeResponse = client.lrange(key, -3, -1).collectList().block(); | ||
assertEquals(3, lRangeResponse.size()); | ||
assertEquals(valuesReversed.subList(2, 5), lRangeResponse); | ||
} | ||
|
||
@Test | ||
public void LRangeOffsetsNotInIntegerRangeTest() { | ||
// While Lettuce accepts longs for the offsets, Momento only supports integers. | ||
// Thus we should throw an exception if the offsets are out of integer range. | ||
if (isRedisTest()) { | ||
return; | ||
} | ||
|
||
// Test exception thrown when the offsets are out of integer range | ||
var key = randomString(); | ||
var values = generateListOfRandomStrings(5); | ||
|
||
var lPushResponse = client.lpush(key, values.toArray(new String[0])).block(); | ||
assertEquals(5, lPushResponse); | ||
|
||
long lessThanIntegerMin = (long) Integer.MIN_VALUE - 1; | ||
long moreThanIntegerMax = (long) Integer.MAX_VALUE + 1; | ||
assertThrows( | ||
InvalidArgumentException.class, | ||
() -> client.lrange(key, lessThanIntegerMin, 1).collectList().block()); | ||
assertThrows( | ||
InvalidArgumentException.class, | ||
() -> client.lrange(key, 1, moreThanIntegerMax).collectList().block()); | ||
} | ||
|
||
@Test | ||
public void testLPushMultipleTimes() { | ||
var key = randomString(); | ||
var values = generateListOfRandomStrings(3); | ||
var valuesReversed = reverseList(values); | ||
var lPushResponse = client.lpush(key, values.toArray(new String[0])).block(); | ||
assertEquals(3, lPushResponse); | ||
|
||
// Push a new list of values | ||
var newValues = generateListOfRandomStrings(3); | ||
var newValuesReversed = reverseList(newValues); | ||
lPushResponse = client.lpush(key, newValues.toArray(new String[0])).block(); | ||
assertEquals(6, lPushResponse); | ||
|
||
// Verify the list is the concatenation of the two lists in reverse order | ||
var lRangeResponse = client.lrange(key, 0, -1).collectList().block(); | ||
assertEquals(6, lRangeResponse.size()); | ||
// should be newValuesReversed + valuesReversed; make a new list with this order | ||
var expectedValues = new ArrayList<>(newValuesReversed); | ||
expectedValues.addAll(valuesReversed); | ||
assertEquals(expectedValues, lRangeResponse); | ||
} | ||
|
||
@Test | ||
public void pExpireWorksOnListValues() { | ||
// Add a list to the cache | ||
var key = randomString(); | ||
var values = generateListOfRandomStrings(3); | ||
var lPushResponse = client.lpush(key, values.toArray(new String[0])).block(); | ||
assertEquals(3, lPushResponse); | ||
|
||
// Verify it's there with lrange | ||
var lRangeResponse = client.lrange(key, 0, -1).collectList().block(); | ||
assertEquals(3, lRangeResponse.size()); | ||
|
||
// Set the expiry so low it will expire before we can check it | ||
var pExpireResponse = client.pexpire(key, 1).block(); | ||
assertEquals(true, pExpireResponse); | ||
|
||
// Wait for the key to expire | ||
try { | ||
Thread.sleep(1000); | ||
} catch (InterruptedException e) { | ||
e.printStackTrace(); | ||
} | ||
|
||
// Verify it's gone | ||
lRangeResponse = client.lrange(key, 0, -1).collectList().block(); | ||
assertEquals(0, lRangeResponse.size()); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,16 @@ | ||
package momento.lettuce; | ||
|
||
import java.util.List; | ||
import java.util.UUID; | ||
import java.util.stream.Collectors; | ||
import java.util.stream.IntStream; | ||
|
||
public class TestUtils { | ||
public static String randomString() { | ||
return UUID.randomUUID().toString(); | ||
} | ||
|
||
public static List<String> generateListOfRandomStrings(int size) { | ||
return IntStream.range(0, size).mapToObj(i -> randomString()).collect(Collectors.toList()); | ||
} | ||
} |