Repository: fppt/jedis-mock
Pull request #63 (2019-10-03 01:36:08)

 Add support for incrbyfloat, hincrby and hincrbyfloat operations 
 Add support for incrbyfloat, hincrby and hincrbyfloat operations. Added some test cases for those operations.


Patch:
diff --git a/src/main/java/com/github/fppt/jedismock/operations/OperationFactory.java b/src/main/java/com/github/fppt/jedismock/operations/OperationFactory.java
index 64d17e3..3b9ebeb 100644
--- a/src/main/java/com/github/fppt/jedismock/operations/OperationFactory.java
+++ b/src/main/java/com/github/fppt/jedismock/operations/OperationFactory.java
@@ -28,6 +28,7 @@ public class OperationFactory {
         TRANSACTIONAL_OPERATIONS.put("pexpire", RO_pexpire::new);
         TRANSACTIONAL_OPERATIONS.put("incr", RO_incr::new);
         TRANSACTIONAL_OPERATIONS.put("incrby", RO_incrby::new);
+        TRANSACTIONAL_OPERATIONS.put("incrbyfloat", RO_incrbyfloat::new);
         TRANSACTIONAL_OPERATIONS.put("decr", RO_decr::new);
         TRANSACTIONAL_OPERATIONS.put("decrby", RO_decrby::new);
         TRANSACTIONAL_OPERATIONS.put("pfcount", RO_pfcount::new);
@@ -68,7 +69,10 @@ public class OperationFactory {
         TRANSACTIONAL_OPERATIONS.put("hset", RO_hset::new);
         TRANSACTIONAL_OPERATIONS.put("hdel", RO_hdel::new);
         TRANSACTIONAL_OPERATIONS.put("hkeys", RO_hkeys::new);
+        TRANSACTIONAL_OPERATIONS.put("hlen", RO_hlen::new);
         TRANSACTIONAL_OPERATIONS.put("hgetall", RO_hgetall::new);
+        TRANSACTIONAL_OPERATIONS.put("hincrby", RO_hincrby::new);
+        TRANSACTIONAL_OPERATIONS.put("hincrbyfloat", RO_hincrbyfloat::new);
         TRANSACTIONAL_OPERATIONS.put("sinter", RO_sinter::new);
         TRANSACTIONAL_OPERATIONS.put("hmget", RO_hmget::new);
         TRANSACTIONAL_OPERATIONS.put("hmset", RO_hmset::new);
@@ -80,6 +84,7 @@ public class OperationFactory {
         TRANSACTIONAL_OPERATIONS.put("zadd", RO_zadd::new);
         TRANSACTIONAL_OPERATIONS.put("zcard", RO_zcard::new);
         TRANSACTIONAL_OPERATIONS.put("zrange", RO_zrange::new);
+        TRANSACTIONAL_OPERATIONS.put("zrangebylex", RO_zrangebylex::new);
         TRANSACTIONAL_OPERATIONS.put("zrem", RO_zrem::new);
     }
 
@@ -104,6 +109,8 @@ public class OperationFactory {
                 return Optional.of(new RO_unsubscribe(state, params));
             case "quit":
                 return Optional.of(new RO_quit(state));
+            case "auth":
+                return Optional.of(new RO_auth(state));
             case "exec":
                 return Optional.of(new RO_exec(state));
             default:
diff --git a/src/main/java/com/github/fppt/jedismock/operations/RO_auth.java b/src/main/java/com/github/fppt/jedismock/operations/RO_auth.java
new file mode 100644
index 0000000..75d4b92
--- /dev/null
+++ b/src/main/java/com/github/fppt/jedismock/operations/RO_auth.java
@@ -0,0 +1,19 @@
+package com.github.fppt.jedismock.operations;
+
+import com.github.fppt.jedismock.server.Response;
+import com.github.fppt.jedismock.server.Slice;
+import com.github.fppt.jedismock.storage.OperationExecutorState;
+
+public class RO_auth implements RedisOperation {
+    private OperationExecutorState state;
+
+    public RO_auth(OperationExecutorState state) {
+        this.state = state;
+    }
+
+    @Override
+    public Slice execute() {
+        state.owner().sendResponse(Response.clientResponse("auth", Response.OK), "auth");
+        return Response.SKIP;
+    }
+}
diff --git a/src/main/java/com/github/fppt/jedismock/operations/RO_hincrby.java b/src/main/java/com/github/fppt/jedismock/operations/RO_hincrby.java
new file mode 100644
index 0000000..ccb5892
--- /dev/null
+++ b/src/main/java/com/github/fppt/jedismock/operations/RO_hincrby.java
@@ -0,0 +1,32 @@
+package com.github.fppt.jedismock.operations;
+
+import com.github.fppt.jedismock.server.Response;
+import com.github.fppt.jedismock.server.Slice;
+import com.github.fppt.jedismock.storage.RedisBase;
+
+import java.util.List;
+
+import static com.github.fppt.jedismock.Utils.convertToLong;
+
+class RO_hincrby extends AbstractRedisOperation {
+    RO_hincrby(RedisBase base, List<Slice> params) {
+        super(base, params);
+    }
+
+    Slice hsetValue(Slice key1, Slice key2, Slice value) {
+        long numericValue = convertToLong(String.valueOf(value));
+        Slice foundValue = base().getValue(key1, key2);
+        if (foundValue != null) {
+            numericValue = convertToLong(new String(foundValue.data())) + numericValue;
+        }
+        base().putValue(key1, key2, Slice.create(String.valueOf(numericValue)), -1L);
+        return Response.integer(numericValue);
+    }
+
+    Slice response() {
+        Slice key1 = params().get(0);
+        Slice key2 = params().get(1);
+        Slice value = params().get(2);
+        return hsetValue(key1, key2, value);
+    }
+}
diff --git a/src/main/java/com/github/fppt/jedismock/operations/RO_hincrbyfloat.java b/src/main/java/com/github/fppt/jedismock/operations/RO_hincrbyfloat.java
new file mode 100644
index 0000000..cd9d41b
--- /dev/null
+++ b/src/main/java/com/github/fppt/jedismock/operations/RO_hincrbyfloat.java
@@ -0,0 +1,27 @@
+package com.github.fppt.jedismock.operations;
+
+import com.github.fppt.jedismock.server.Response;
+import com.github.fppt.jedismock.server.Slice;
+import com.github.fppt.jedismock.storage.RedisBase;
+
+import java.util.List;
+
+import static com.github.fppt.jedismock.Utils.convertToDouble;
+
+class RO_hincrbyfloat extends RO_hincrby {
+    RO_hincrbyfloat(RedisBase base, List<Slice> params) {
+        super(base, params);
+    }
+
+    Slice hsetValue(Slice key1, Slice key2, Slice value) {
+        double numericValue = convertToDouble(String.valueOf(value));
+        Slice foundValue = base().getValue(key1, key2);
+        if (foundValue != null) {
+            numericValue = convertToDouble(new String(foundValue.data())) + numericValue;
+        }
+        Slice res = Slice.create(String.valueOf(numericValue));
+        base().putValue(key1, key2, res, -1L);
+
+        return Response.bulkString(res);
+    }
+}
diff --git a/src/main/java/com/github/fppt/jedismock/operations/RO_hlen.java b/src/main/java/com/github/fppt/jedismock/operations/RO_hlen.java
new file mode 100644
index 0000000..1a1f4bf
--- /dev/null
+++ b/src/main/java/com/github/fppt/jedismock/operations/RO_hlen.java
@@ -0,0 +1,20 @@
+package com.github.fppt.jedismock.operations;
+
+import com.github.fppt.jedismock.server.Response;
+import com.github.fppt.jedismock.server.Slice;
+import com.github.fppt.jedismock.storage.RedisBase;
+
+import java.util.List;
+import java.util.Map;
+
+public class RO_hlen extends AbstractRedisOperation {
+    public RO_hlen(RedisBase base, List<Slice> params) {
+        super(base, params);
+    }
+
+    Slice response() {
+        Slice key = params().get(0);
+        Map<Slice, Slice> map = base().getFieldsAndValues(key);
+        return Response.integer(map.size());
+    }
+}
diff --git a/src/main/java/com/github/fppt/jedismock/operations/RO_incrOrDecrByFloat.java b/src/main/java/com/github/fppt/jedismock/operations/RO_incrOrDecrByFloat.java
new file mode 100644
index 0000000..e7ef657
--- /dev/null
+++ b/src/main/java/com/github/fppt/jedismock/operations/RO_incrOrDecrByFloat.java
@@ -0,0 +1,31 @@
+package com.github.fppt.jedismock.operations;
+
+import com.github.fppt.jedismock.server.Response;
+import com.github.fppt.jedismock.server.Slice;
+import com.github.fppt.jedismock.storage.RedisBase;
+
+import java.util.List;
+
+import static com.github.fppt.jedismock.Utils.convertToDouble;
+
+abstract class RO_incrOrDecrByFloat extends AbstractRedisOperation {
+    RO_incrOrDecrByFloat(RedisBase base, List<Slice> params) {
+        super(base, params);
+    }
+
+    abstract double incrementOrDecrementValue(List<Slice> params);
+
+    Slice response() {
+        Slice key = params().get(0);
+        double d = incrementOrDecrementValue(params());
+        Slice v = base().getValue(key);
+        if (v == null) {
+            base().putValue(key, Slice.create(String.valueOf(d)));
+            return Response.doubleValue(d);
+        }
+
+        double r = convertToDouble(new String(v.data())) + d;
+        base().putValue(key, Slice.create(String.valueOf(r)));
+        return Response.doubleValue(r);
+    }
+}
diff --git a/src/main/java/com/github/fppt/jedismock/operations/RO_incrbyfloat.java b/src/main/java/com/github/fppt/jedismock/operations/RO_incrbyfloat.java
new file mode 100644
index 0000000..3ea852b
--- /dev/null
+++ b/src/main/java/com/github/fppt/jedismock/operations/RO_incrbyfloat.java
@@ -0,0 +1,18 @@
+package com.github.fppt.jedismock.operations;
+
+import com.github.fppt.jedismock.storage.RedisBase;
+import com.github.fppt.jedismock.server.Slice;
+
+import java.util.List;
+
+import static com.github.fppt.jedismock.Utils.convertToDouble;
+
+class RO_incrbyfloat extends RO_incrOrDecrByFloat {
+    RO_incrbyfloat(RedisBase base, List<Slice> params) {
+        super(base, params);
+    }
+
+    double incrementOrDecrementValue(List<Slice> params){
+        return convertToDouble(String.valueOf(params.get(1)));
+    }
+}
diff --git a/src/main/java/com/github/fppt/jedismock/operations/RO_zrangebylex.java b/src/main/java/com/github/fppt/jedismock/operations/RO_zrangebylex.java
new file mode 100644
index 0000000..8eaabd6
--- /dev/null
+++ b/src/main/java/com/github/fppt/jedismock/operations/RO_zrangebylex.java
@@ -0,0 +1,62 @@
+package com.github.fppt.jedismock.operations;
+
+import com.github.fppt.jedismock.server.Response;
+import com.github.fppt.jedismock.server.Slice;
+import com.github.fppt.jedismock.storage.RedisBase;
+
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+
+class RO_zrangebylex extends AbstractRedisOperation {
+
+    private static final String START_UNBOUNDED = "-";
+    private static final String END_UNBOUNDED = "+";
+    private static final String INCLUSIVE_PREFIX = "[";
+    private static final String EXCLUSIVE_PREFIX = "(";
+
+    RO_zrangebylex(RedisBase base, List<Slice> params) {
+        super(base, params);
+    }
+
+    @Override
+    Slice response() {
+        Slice key = params().get(0);
+        LinkedHashMap<Slice, Double> map = getDataFromBase(key, new LinkedHashMap<>());
+
+        String start = params().get(1).toString();
+        if (!START_UNBOUNDED.equals(start)
+                && !start.startsWith(INCLUSIVE_PREFIX)
+                && !start.startsWith(EXCLUSIVE_PREFIX)) {
+            throw new RuntimeException("Valid start must start with '" + INCLUSIVE_PREFIX + "' or '"
+                    + EXCLUSIVE_PREFIX + "'");
+        }
+
+        final Predicate<Slice> compareToStart = p -> START_UNBOUNDED.equals(start) ||
+                (start.startsWith(INCLUSIVE_PREFIX)
+                        ? p.toString().compareTo(start.substring(1)) >= 0
+                        : p.toString().compareTo(start.substring(1)) > 0);
+
+        String end = params().get(2).toString();
+        if (!END_UNBOUNDED.equals(end)
+                && !end.startsWith(INCLUSIVE_PREFIX)
+                && !end.startsWith(EXCLUSIVE_PREFIX)) {
+            throw new RuntimeException("Valid end must start with '" + INCLUSIVE_PREFIX + "' or '"
+                    + EXCLUSIVE_PREFIX + "'");
+        }
+
+        final Predicate<Slice> compareToEnd = p -> END_UNBOUNDED.equals(end) ||
+                (end.startsWith(INCLUSIVE_PREFIX)
+                        ? p.toString().compareTo(end.substring(1)) <= 0
+                        : p.toString().compareTo(end.substring(1)) < 0);
+
+        List<Slice> values = map.keySet().stream()
+                .filter(compareToStart.and(compareToEnd))
+                .sorted()
+                .map(Response::bulkString)
+                .collect(Collectors.toList());
+
+        return Response.array(values);
+    }
+}
diff --git a/src/main/java/com/github/fppt/jedismock/server/Response.java b/src/main/java/com/github/fppt/jedismock/server/Response.java
index 2a820c4..f7af673 100644
--- a/src/main/java/com/github/fppt/jedismock/server/Response.java
+++ b/src/main/java/com/github/fppt/jedismock/server/Response.java
@@ -39,6 +39,10 @@ public class Response {
         return Slice.create(String.format(":%d%s", v, LINE_SEPARATOR));
     }
 
+    public static Slice doubleValue(double v) {
+        return Slice.create(String.format(":%g%s", v, LINE_SEPARATOR));
+    }
+
     public static Slice array(List<Slice> values) {
         ByteArrayDataOutput bo = ByteStreams.newDataOutput();
         bo.write(String.format("*%d%s", values.size(), LINE_SEPARATOR).getBytes());


Test patch:
diff --git a/src/test/java/com/github/fppt/jedismock/TestRedisOperationExecutor.java b/src/test/java/com/github/fppt/jedismock/TestRedisOperationExecutor.java
index aa1b765..8be072b 100644
--- a/src/test/java/com/github/fppt/jedismock/TestRedisOperationExecutor.java
+++ b/src/test/java/com/github/fppt/jedismock/TestRedisOperationExecutor.java
@@ -54,6 +54,10 @@ public class TestRedisOperationExecutor {
         assertEquals(Response.integer(expect), executor.execCommand(RedisCommandParser.parse(command)));
     }
 
+    private void assertCommandEquals(double expect, String command) throws ParseErrorException, EOFException {
+        assertEquals(Response.doubleValue(expect), executor.execCommand(RedisCommandParser.parse(command)));
+    }
+
     private void assertCommandArrayEquals(String expectedArray, String command) throws ParseErrorException, EOFException {
         assertEquals(expectedArray, executor.execCommand(RedisCommandParser.parse(command)).toString());
     }
@@ -145,6 +149,17 @@ public class TestRedisOperationExecutor {
         assertCommandEquals(11, array("incrby", "a", "6"));
         assertCommandOK(array("set", "a", "b"));
         assertCommandError(array("incrby", "a", "1"));
+        assertCommandOK(array("set", "a", "3.14"));
+        assertCommandError(array("incrby", "a", "1"));
+    }
+
+    @Test
+    public void testIncrByFloat() throws ParseErrorException, EOFException {
+        assertCommandEquals(5.0, array("incrbyfloat", "a", "5"));
+        assertCommandEquals(11.01, array("incrbyfloat", "a", "6.01"));
+        assertCommandEquals(9.51, array("incrbyfloat", "a", "-1.5"));
+        assertCommandOK(array("set", "a", "b"));
+        assertCommandError(array("incrbyfloat", "a", "1"));
     }
 
     @Test
diff --git a/src/test/java/com/github/fppt/jedismock/comparisontests/SimpleOperationsTest.java b/src/test/java/com/github/fppt/jedismock/comparisontests/SimpleOperationsTest.java
index c8112e0..17974db 100644
--- a/src/test/java/com/github/fppt/jedismock/comparisontests/SimpleOperationsTest.java
+++ b/src/test/java/com/github/fppt/jedismock/comparisontests/SimpleOperationsTest.java
@@ -1,11 +1,11 @@
 package com.github.fppt.jedismock.comparisontests;
 
-
 import org.junit.experimental.theories.Theories;
 import org.junit.experimental.theories.Theory;
 import org.junit.runner.RunWith;
 import redis.clients.jedis.*;
 import redis.clients.jedis.exceptions.JedisConnectionException;
+import redis.clients.jedis.exceptions.JedisDataException;
 
 import java.util.*;
 import java.util.concurrent.ExecutionException;
@@ -151,7 +151,8 @@ public class SimpleOperationsTest extends ComparisonBase {
     }
 
     @Theory
-    public void whenConcurrentlyIncrementingAndDecrementingCount_EnsureFinalCountIsCorrect(Jedis jedis) throws ExecutionException, InterruptedException {
+    public void whenConcurrentlyIncrementingAndDecrementingCount_EnsureFinalCountIsCorrect(
+            Jedis jedis) throws ExecutionException, InterruptedException {
         String key = "my-count-tracker";
         int[] count = new int[]{1, 5, 6, 2, -9, -2, 10, 11, 5, -2, -2};
 
@@ -169,7 +170,6 @@ public class SimpleOperationsTest extends ComparisonBase {
             }));
         }
 
-
         for (Future futue : futues) {
             futue.get();
         }
@@ -201,7 +201,8 @@ public class SimpleOperationsTest extends ComparisonBase {
         //All Keys
         results = jedis.keys("*");
         assertEquals(4, results.size());
-        assertTrue(results.contains("one") && results.contains("two") && results.contains("three") && results.contains("four"));
+        assertTrue(results.contains("one") && results.contains("two") && results.contains("three") && results.contains(
+                "four"));
     }
 
     @Theory
@@ -223,6 +224,40 @@ public class SimpleOperationsTest extends ComparisonBase {
         assertEquals(1, jedis.sadd(key, "A", "C", "E", "B").intValue());
     }
 
+    @Theory
+    public void whenIncrementingSet_ensureValuesAreCorrect(Jedis jedis) {
+        String key = "my-set-key-hincr";
+        jedis.hset(key, "E", "3.14e1");
+        jedis.hset(key, "F", "not-a-number");
+
+        assertEquals(3, jedis.hincrBy(key, "A", 3).intValue());
+        assertEquals(4.5, jedis.hincrByFloat(key, "A", 1.5), 0.00001);
+        assertEquals(-1.5, jedis.hincrByFloat(key, "B", -1.5), 0.00001);
+
+        try {
+            jedis.hincrBy(key, "F", 1);
+            fail("Exception not thrown");
+        } catch (JedisDataException ignored) {
+            // Non-integer value
+        }
+
+        try {
+            jedis.hincrBy(key, "E", 1);
+            fail("Exception not thrown");
+        } catch (JedisDataException ignored) {
+            // Non-integer value
+        }
+
+        try {
+            jedis.hincrByFloat(key, "F", 1);
+            fail("Exception not thrown");
+        } catch (JedisDataException ignored) {
+            // Non-numeric value
+        }
+
+        assertEquals(31.41, jedis.hincrByFloat(key, "E", 0.01), 0.00001);
+    }
+
     @Theory
     public void whenAddingToASet_ensureCountIsUpdated(Jedis jedis) {
         String key = "my-counted-set-key";
@@ -272,7 +307,9 @@ public class SimpleOperationsTest extends ComparisonBase {
         String poppedValue;
         do {
             poppedValue = jedis.spop(key);
-            if (poppedValue != null) assertTrue("Popped value not in set", mySet.contains(poppedValue));
+            if (poppedValue != null) {
+                assertTrue("Popped value not in set", mySet.contains(poppedValue));
+            }
         } while (poppedValue != null);
     }
 
@@ -294,7 +331,7 @@ public class SimpleOperationsTest extends ComparisonBase {
     }
 
     @Theory
-    public void whenHSettingAndHGetting_EnsureValuesAreSetAndExist(Jedis jedis){
+    public void whenHSettingAndHGetting_EnsureValuesAreSetAndExist(Jedis jedis) {
         String field = "my-field";
         String hash = "my-hash";
         String value = "my-value";
@@ -343,7 +380,7 @@ public class SimpleOperationsTest extends ComparisonBase {
     }
 
     @Theory
-    public void whenHKeys_EnsureAllKeysReturned(Jedis jedis){
+    public void whenHKeys_EnsureAllKeysReturned(Jedis jedis) {
         jedis.hset(HASH, FIELD_1, VALUE_1);
         jedis.hset(HASH, FIELD_2, VALUE_2);
 
@@ -361,6 +398,18 @@ public class SimpleOperationsTest extends ComparisonBase {
         assertEquals(result, toCompare);
     }
 
+    @Theory
+    public void whenHLen_EnsureCorrectLengthReturned(Jedis jedis) {
+        jedis.flushDB();
+
+        jedis.hset(HASH, FIELD_1, VALUE_1);
+        jedis.hset(HASH, FIELD_2, VALUE_2);
+
+        long result = jedis.hlen(HASH);
+
+        assertEquals(2, result);
+    }
+
     @Theory
     public void whenUsingHsinter_EnsureSetIntersectionIsReturned(Jedis jedis) {
         String key1 = "my-set-key-1";
@@ -496,7 +545,8 @@ public class SimpleOperationsTest extends ComparisonBase {
         jedis.set(key, value);
         jedis.set(key2, value);
 
-        ScanResult<String> result = jedis.scan(ScanParams.SCAN_POINTER_START, new ScanParams().match("scankeymatch:1*"));
+        ScanResult<String> result = jedis.scan(ScanParams.SCAN_POINTER_START,
+                new ScanParams().match("scankeymatch:1*"));
 
         assertEquals(ScanParams.SCAN_POINTER_START, result.getStringCursor());
         assertEquals(1, result.getResult().size());
@@ -513,7 +563,8 @@ public class SimpleOperationsTest extends ComparisonBase {
             jedis.set("scankeyi:" + i, value);
         }
 
-        ScanResult<String> result = jedis.scan(ScanParams.SCAN_POINTER_START, new ScanParams().match("scankeyi:1*").count(10));
+        ScanResult<String> result = jedis.scan(ScanParams.SCAN_POINTER_START,
+                new ScanParams().match("scankeyi:1*").count(10));
 
         assertNotEquals(ScanParams.SCAN_POINTER_START, result.getStringCursor());
     }
@@ -567,7 +618,8 @@ public class SimpleOperationsTest extends ComparisonBase {
         }
         jedis.sadd(key, values);
 
-        ScanResult<String> result = jedis.sscan(key, ScanParams.SCAN_POINTER_START, new ScanParams().match("21_value_0"));
+        ScanResult<String> result = jedis.sscan(key, ScanParams.SCAN_POINTER_START,
+                new ScanParams().match("21_value_0"));
 
         assertEquals(ScanParams.SCAN_POINTER_START, result.getStringCursor());
         assertEquals(1, result.getResult().size());
@@ -580,7 +632,7 @@ public class SimpleOperationsTest extends ComparisonBase {
         jedis.flushDB();
 
         String key = "sscankey";
-        jedis.del(key);
+
         String[] values = new String[45];
         for (int i = 0; i < 45; i++) {
             values[i] = (45 - i) + "_value_" + i;
@@ -588,16 +640,17 @@ public class SimpleOperationsTest extends ComparisonBase {
         jedis.sadd(key, values);
         String cursor = null;
 
-        int iterations = 0;
+        Set<String> results = new HashSet<>();
         while (cursor == null || !cursor.equals(ScanParams.SCAN_POINTER_START)) {
             if (cursor == null) {
                 cursor = ScanParams.SCAN_POINTER_START;
             }
             ScanResult<String> result = jedis.sscan(key, cursor);
             cursor = result.getStringCursor();
-            iterations++;
+            results.addAll(result.getResult());
         }
-        assertEquals(5, iterations);
+
+        assertTrue(results.containsAll(Arrays.asList(values)));
     }
 
     @Theory
@@ -770,4 +823,34 @@ public class SimpleOperationsTest extends ComparisonBase {
         assertEquals("myvalue4", results.get(3).getElement());
         assertEquals(20d, results.get(3).getScore(), 0);
     }
+
+    @Theory
+    public void zrangebylexKeysCorrectOrder(Jedis jedis) {
+        jedis.flushDB();
+
+        String key = "mykey";
+        Map<String, Double> members = new HashMap<>();
+        members.put("bbb", 0d);
+        members.put("ddd", 0d);
+        members.put("ccc", 0d);
+        members.put("aaa", 0d);
+
+        long result = jedis.zadd(key, members);
+
+        assertEquals(4L, result);
+
+        List<String> results = new ArrayList<>(jedis.zrangeByLex(key, "-", "+"));
+
+        assertEquals(4, results.size());
+        assertEquals("aaa", results.get(0));
+        assertEquals("bbb", results.get(1));
+        assertEquals("ccc", results.get(2));
+        assertEquals("ddd", results.get(3));
+
+        results = new ArrayList<>(jedis.zrangeByLex(key, "[bbb", "(ddd"));
+
+        assertEquals(2, results.size());
+        assertEquals("bbb", results.get(0));
+        assertEquals("ccc", results.get(1));
+    }
 }