Repository: fppt/jedis-mock
Pull request #230 (2022-06-05 20:22:29)

Refactor Sorted Sets
Fix https://github.com/fppt/jedis-mock/issues/219
Fix https://github.com/fppt/jedis-mock/issues/220


Patch:
diff --git a/src/main/java/com/github/fppt/jedismock/datastructures/RMList.java b/src/main/java/com/github/fppt/jedismock/datastructures/RMList.java
index f051ff7..9f41912 100644
--- a/src/main/java/com/github/fppt/jedismock/datastructures/RMList.java
+++ b/src/main/java/com/github/fppt/jedismock/datastructures/RMList.java
@@ -1,7 +1,8 @@
 package com.github.fppt.jedismock.datastructures;
 
 import com.github.fppt.jedismock.exception.WrongValueTypeException;
-import java.util.LinkedList;
+
+import java.util.ArrayList;
 import java.util.List;
 
 public class RMList implements RMDataStructure {
@@ -12,7 +13,7 @@ public class RMList implements RMDataStructure {
     }
 
     public RMList() {
-        storedData = new LinkedList<>();
+        storedData = new ArrayList<>();
     }
 
     @Override
diff --git a/src/main/java/com/github/fppt/jedismock/datastructures/RMZSet.java b/src/main/java/com/github/fppt/jedismock/datastructures/RMZSet.java
index 3b31569..3791564 100644
--- a/src/main/java/com/github/fppt/jedismock/datastructures/RMZSet.java
+++ b/src/main/java/com/github/fppt/jedismock/datastructures/RMZSet.java
@@ -2,22 +2,55 @@ package com.github.fppt.jedismock.datastructures;
 
 import com.github.fppt.jedismock.exception.WrongValueTypeException;
 
-import java.util.LinkedHashMap;
+import java.util.HashMap;
 import java.util.Map;
+import java.util.NavigableSet;
+import java.util.TreeSet;
+
 
 public class RMZSet implements RMDataStructure {
-    private final Map<Slice, Double> storedData;
+    private final Map<String, Double> scores = new HashMap<>();
+    private final NavigableSet<ZSetEntry> entries = new TreeSet<>();
+
+    public Double put(String value, double score) {
+        Double previous = scores.put(value, score);
+        if (previous != null) {
+            entries.remove(new ZSetEntry(previous, value));
+        }
+        entries.add(new ZSetEntry(score, value));
+        return previous;
+    }
 
-    public Map<Slice, Double> getStoredData() {
-        return storedData;
+    public Double getScore(String value) {
+        return scores.get(value);
     }
 
-    public RMZSet() {
-        storedData = new LinkedHashMap<>();
+    public boolean remove(String value) {
+        final Double previous = scores.remove(value);
+        if (previous == null) {
+            return false;
+        } else {
+            entries.remove(new ZSetEntry(previous, value));
+            return true;
+        }
     }
 
-    public RMZSet(Map<Slice, Double> data) {
-        storedData = data;
+    public int size() {
+        return scores.size();
+    }
+
+    public boolean isEmpty() {
+        return size() == 0;
+    }
+
+    public NavigableSet<ZSetEntry> subset(ZSetEntryBound start,
+                                          ZSetEntryBound end) {
+        return entries.subSet(start.getBound(), start.isInclusive(),
+                end.getBound(), end.isInclusive());
+    }
+
+    public NavigableSet<ZSetEntry> entries(boolean reversed) {
+        return reversed ? entries.descendingSet() : entries;
     }
 
     @Override
@@ -29,4 +62,6 @@ public class RMZSet implements RMDataStructure {
     public String getTypeName() {
         return "zset";
     }
+
+
 }
diff --git a/src/main/java/com/github/fppt/jedismock/datastructures/ZSetEntry.java b/src/main/java/com/github/fppt/jedismock/datastructures/ZSetEntry.java
new file mode 100644
index 0000000..cb7938a
--- /dev/null
+++ b/src/main/java/com/github/fppt/jedismock/datastructures/ZSetEntry.java
@@ -0,0 +1,61 @@
+package com.github.fppt.jedismock.datastructures;
+
+import java.util.Objects;
+
+public class ZSetEntry implements Comparable<ZSetEntry> {
+    public final static Double MIN_SCORE = Double.NEGATIVE_INFINITY;
+    public final static Double MAX_SCORE = Double.POSITIVE_INFINITY;
+    public final static String MIN_VALUE = "";
+    //magic string that is considered to be lexicographically greater than any other string
+    public final static String MAX_VALUE = "2815bbb7-cacd-4ce1-ba76-11fee7937b0e";
+
+    private final double score;
+
+    private final String value;
+
+    ZSetEntry(double score, String value) {
+        Objects.requireNonNull(value);
+        this.score = score;
+        this.value = value;
+    }
+
+    public double getScore() {
+        return score;
+    }
+
+    public String getValue() {
+        return value;
+    }
+
+    @Override
+    public int compareTo(ZSetEntry o) {
+        int result = Double.compare(score, o.score);
+        if (result == 0) {
+            if (value.equals(o.value)) {
+                return 0;
+            } else if (MAX_VALUE.equals(value)) {
+                return 1;
+            } else if (MAX_VALUE.equals(o.value)) {
+                return -1;
+            } else {
+                return value.compareTo(o.value);
+            }
+        } else {
+            return result;
+        }
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        ZSetEntry zSetEntry = (ZSetEntry) o;
+        return Double.compare(zSetEntry.score, score) == 0 && value.equals(zSetEntry.value);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(score, value);
+    }
+
+}
diff --git a/src/main/java/com/github/fppt/jedismock/datastructures/ZSetEntryBound.java b/src/main/java/com/github/fppt/jedismock/datastructures/ZSetEntryBound.java
new file mode 100644
index 0000000..d634160
--- /dev/null
+++ b/src/main/java/com/github/fppt/jedismock/datastructures/ZSetEntryBound.java
@@ -0,0 +1,33 @@
+package com.github.fppt.jedismock.datastructures;
+
+import java.util.Objects;
+
+public class ZSetEntryBound {
+    public static final ZSetEntryBound MINUS_INF =
+            new ZSetEntryBound(new ZSetEntry(ZSetEntry.MIN_SCORE, ""), false);
+    public static final ZSetEntryBound PLUS_INF =
+            new ZSetEntryBound(new ZSetEntry(ZSetEntry.MAX_SCORE, ""), false);
+
+    private final ZSetEntry bound;
+    private final boolean inclusive;
+
+    public ZSetEntryBound(ZSetEntry bound, boolean inclusive) {
+        Objects.requireNonNull(bound);
+        this.bound = bound;
+        this.inclusive = inclusive;
+    }
+
+    public ZSetEntryBound(double score, String value, boolean inclusive) {
+        this.bound = new ZSetEntry(score, value);
+        this.inclusive = inclusive;
+    }
+
+    public ZSetEntry getBound() {
+        return bound;
+    }
+
+    public boolean isInclusive() {
+        return inclusive;
+    }
+
+}
diff --git a/src/main/java/com/github/fppt/jedismock/operations/AbstractRedisOperation.java b/src/main/java/com/github/fppt/jedismock/operations/AbstractRedisOperation.java
index 53a7461..0300399 100644
--- a/src/main/java/com/github/fppt/jedismock/operations/AbstractRedisOperation.java
+++ b/src/main/java/com/github/fppt/jedismock/operations/AbstractRedisOperation.java
@@ -6,10 +6,8 @@ import com.github.fppt.jedismock.datastructures.RMSet;
 import com.github.fppt.jedismock.datastructures.Slice;
 import com.github.fppt.jedismock.storage.RedisBase;
 
-import java.io.IOException;
 import java.util.List;
 
-
 public abstract class AbstractRedisOperation implements RedisOperation {
     private final RedisBase base;
     private final List<Slice> params;
@@ -23,7 +21,7 @@ public abstract class AbstractRedisOperation implements RedisOperation {
         //Place Holder For Ops which need to so some operational work
     }
 
-    protected abstract Slice response() throws IOException;
+    protected abstract Slice response();
 
     protected RedisBase base(){
         return base;
@@ -51,12 +49,11 @@ public abstract class AbstractRedisOperation implements RedisOperation {
         return data;
     }
 
-    public RMZSet getHMapFromBaseOrCreateEmpty(Slice key) {
+    public RMZSet getZSetFromBaseOrCreateEmpty(Slice key) {
         RMZSet data = base().getZSet(key);
         if(data == null) {
             return new RMZSet();
         }
-
         return data;
     }
 
@@ -65,7 +62,7 @@ public abstract class AbstractRedisOperation implements RedisOperation {
         try {
             doOptionalWork();
             return response();
-        } catch (IndexOutOfBoundsException | IOException e){
+        } catch (IndexOutOfBoundsException e){
             throw new IllegalArgumentException("Invalid number of arguments when executing command [" + getClass().getSimpleName() + "]", e);
         }
     }
diff --git a/src/main/java/com/github/fppt/jedismock/operations/lists/BRPopLPush.java b/src/main/java/com/github/fppt/jedismock/operations/lists/BRPopLPush.java
index 653561b..6675751 100644
--- a/src/main/java/com/github/fppt/jedismock/operations/lists/BRPopLPush.java
+++ b/src/main/java/com/github/fppt/jedismock/operations/lists/BRPopLPush.java
@@ -35,7 +35,6 @@ class BRPopLPush extends RPopLPush {
         } catch (InterruptedException e) {
             //wait interrupted prematurely
             Thread.currentThread().interrupt();
-            return;
         }
     }
 
diff --git a/src/main/java/com/github/fppt/jedismock/operations/pubsub/PSubscribe.java b/src/main/java/com/github/fppt/jedismock/operations/pubsub/PSubscribe.java
index 722edb3..93ba97d 100644
--- a/src/main/java/com/github/fppt/jedismock/operations/pubsub/PSubscribe.java
+++ b/src/main/java/com/github/fppt/jedismock/operations/pubsub/PSubscribe.java
@@ -6,7 +6,6 @@ import com.github.fppt.jedismock.operations.RedisCommand;
 import com.github.fppt.jedismock.server.Response;
 import com.github.fppt.jedismock.storage.OperationExecutorState;
 
-import java.io.IOException;
 import java.util.List;
 
 @RedisCommand(value = "psubscribe", transactional = false)
@@ -17,7 +16,8 @@ public class PSubscribe extends AbstractRedisOperation {
         this.state = state;
     }
 
-    protected Slice response() throws IOException {
+    @Override
+    protected Slice response() {
         params().forEach(pattern -> base().subscribeByPattern(pattern, state.owner()));
         List<Slice> numSubscriptions = base().getPSubscriptions(state.owner());
 
diff --git a/src/main/java/com/github/fppt/jedismock/operations/pubsub/PUnsubscribe.java b/src/main/java/com/github/fppt/jedismock/operations/pubsub/PUnsubscribe.java
index 7ee5772..4cd3d1a 100644
--- a/src/main/java/com/github/fppt/jedismock/operations/pubsub/PUnsubscribe.java
+++ b/src/main/java/com/github/fppt/jedismock/operations/pubsub/PUnsubscribe.java
@@ -7,7 +7,6 @@ import com.github.fppt.jedismock.datastructures.Slice;
 import com.github.fppt.jedismock.storage.OperationExecutorState;
 import org.slf4j.LoggerFactory;
 
-import java.io.IOException;
 import java.util.List;
 
 @RedisCommand(value = "punsubscribe", transactional = false)
@@ -20,7 +19,8 @@ public class PUnsubscribe extends AbstractRedisOperation {
         this.state = state;
     }
 
-    protected Slice response() throws IOException {
+    @Override
+    protected Slice response() {
         List<Slice> channelsToUbsubscribeFrom;
         if(params().isEmpty()){
             LOG.debug("No channels specified therefore unsubscribing from all channels");
diff --git a/src/main/java/com/github/fppt/jedismock/operations/pubsub/PubSub.java b/src/main/java/com/github/fppt/jedismock/operations/pubsub/PubSub.java
index 70ea88b..65ea1d2 100644
--- a/src/main/java/com/github/fppt/jedismock/operations/pubsub/PubSub.java
+++ b/src/main/java/com/github/fppt/jedismock/operations/pubsub/PubSub.java
@@ -7,7 +7,6 @@ import com.github.fppt.jedismock.operations.RedisCommand;
 import com.github.fppt.jedismock.server.Response;
 import com.github.fppt.jedismock.storage.RedisBase;
 
-import java.io.IOException;
 import java.util.List;
 import java.util.stream.Collectors;
 
@@ -18,7 +17,7 @@ public class PubSub extends AbstractRedisOperation {
     }
 
     @Override
-    protected Slice response() throws IOException {
+    protected Slice response() {
         Slice subcommand = params().get(0);
         if ("channels".equalsIgnoreCase(subcommand.toString())) {
             String pattern =
diff --git a/src/main/java/com/github/fppt/jedismock/operations/pubsub/Subscribe.java b/src/main/java/com/github/fppt/jedismock/operations/pubsub/Subscribe.java
index 28a6cfb..ab183b1 100644
--- a/src/main/java/com/github/fppt/jedismock/operations/pubsub/Subscribe.java
+++ b/src/main/java/com/github/fppt/jedismock/operations/pubsub/Subscribe.java
@@ -6,7 +6,6 @@ import com.github.fppt.jedismock.server.Response;
 import com.github.fppt.jedismock.datastructures.Slice;
 import com.github.fppt.jedismock.storage.OperationExecutorState;
 
-import java.io.IOException;
 import java.util.List;
 
 @RedisCommand(value = "subscribe", transactional = false)
@@ -18,7 +17,8 @@ public class Subscribe extends AbstractRedisOperation {
         this.state = state;
     }
 
-    protected Slice response() throws IOException {
+    @Override
+    protected Slice response() {
         params().forEach(channel -> base().addSubscriber(channel, state.owner()));
         List<Slice> numSubscriptions = base().getSubscriptions(state.owner());
 
diff --git a/src/main/java/com/github/fppt/jedismock/operations/pubsub/Unsubscribe.java b/src/main/java/com/github/fppt/jedismock/operations/pubsub/Unsubscribe.java
index 88bc13b..c45dc54 100644
--- a/src/main/java/com/github/fppt/jedismock/operations/pubsub/Unsubscribe.java
+++ b/src/main/java/com/github/fppt/jedismock/operations/pubsub/Unsubscribe.java
@@ -7,7 +7,6 @@ import com.github.fppt.jedismock.datastructures.Slice;
 import com.github.fppt.jedismock.storage.OperationExecutorState;
 import org.slf4j.LoggerFactory;
 
-import java.io.IOException;
 import java.util.List;
 
 @RedisCommand(value = "unsubscribe", transactional = false)
@@ -20,7 +19,8 @@ public class Unsubscribe extends AbstractRedisOperation {
         this.state = state;
     }
 
-    protected Slice response() throws IOException {
+    @Override
+    protected Slice response() {
         List<Slice> channelsToUbsubscribeFrom;
         if(params().isEmpty()){
             LOG.debug("No channels specified therefore unsubscribing from all channels");
diff --git a/src/main/java/com/github/fppt/jedismock/operations/sortedsets/AbstractByScoreOperation.java b/src/main/java/com/github/fppt/jedismock/operations/sortedsets/AbstractByScoreOperation.java
index 36cfb12..df6c524 100644
--- a/src/main/java/com/github/fppt/jedismock/operations/sortedsets/AbstractByScoreOperation.java
+++ b/src/main/java/com/github/fppt/jedismock/operations/sortedsets/AbstractByScoreOperation.java
@@ -1,6 +1,8 @@
 package com.github.fppt.jedismock.operations.sortedsets;
 
 import com.github.fppt.jedismock.datastructures.RMZSet;
+import com.github.fppt.jedismock.datastructures.ZSetEntry;
+import com.github.fppt.jedismock.datastructures.ZSetEntryBound;
 import com.github.fppt.jedismock.exception.WrongValueTypeException;
 import com.github.fppt.jedismock.datastructures.Slice;
 import com.github.fppt.jedismock.operations.AbstractRedisOperation;
@@ -9,12 +11,10 @@ import com.github.fppt.jedismock.storage.RedisBase;
 
 import java.util.Collections;
 import java.util.List;
-import java.util.Map;
-import java.util.function.Predicate;
+import java.util.NavigableSet;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
-import static com.github.fppt.jedismock.Utils.convertToDouble;
 import static com.github.fppt.jedismock.Utils.convertToLong;
 
 public abstract class AbstractByScoreOperation extends AbstractRedisOperation {
@@ -26,87 +26,72 @@ public abstract class AbstractByScoreOperation extends AbstractRedisOperation {
         super(base, params);
     }
 
-    private double getStartScore(String start) {
+    private static double toDouble(String value) {
+        try {
+            return Double.parseDouble(value);
+        } catch (NumberFormatException e) {
+            throw new WrongValueTypeException("Valid start must be a number or start with '" + EXCLUSIVE_PREFIX + "' or be equal to '"
+                    + LOWEST_POSSIBLE_SCORE + "'");
+        }
+    }
+
+    final ZSetEntryBound getStartBound(String start) {
         if (LOWEST_POSSIBLE_SCORE.equalsIgnoreCase(start)) {
-            return Double.MIN_VALUE;
+            return ZSetEntryBound.MINUS_INF;
         } else if (start.startsWith(EXCLUSIVE_PREFIX)) {
-            return convertToDouble(start.substring(1));
+            return new ZSetEntryBound(toDouble(start.substring(1)), ZSetEntry.MAX_VALUE, false);
         } else {
-            try {
-                return Double.parseDouble(start);
-            } catch (NumberFormatException e) {
-                throw new WrongValueTypeException("Valid start must be a number or start with '" + EXCLUSIVE_PREFIX + "' or be equal to '"
-                        + LOWEST_POSSIBLE_SCORE + "'");
-            }
+            return new ZSetEntryBound(toDouble(start), ZSetEntry.MIN_VALUE, true);
         }
     }
 
-    private double getEndScore(String end) {
+    final ZSetEntryBound getEndBound(String end) {
         if (HIGHEST_POSSIBLE_SCORE.equalsIgnoreCase(end)) {
-            return Double.MAX_VALUE;
+            return ZSetEntryBound.PLUS_INF;
         } else if (end.startsWith(EXCLUSIVE_PREFIX)) {
-            return convertToDouble(end.substring(1));
+            return new ZSetEntryBound(toDouble(end.substring(1)), ZSetEntry.MIN_VALUE, false);
         } else {
-            try {
-                return Double.parseDouble(end);
-            } catch (NumberFormatException e) {
-                throw new WrongValueTypeException("Valid end must be a number or start with '" + EXCLUSIVE_PREFIX + "' or be equal to '"
-                        + HIGHEST_POSSIBLE_SCORE + "'");
-            }
+            return new ZSetEntryBound(toDouble(end), ZSetEntry.MAX_VALUE, true);
         }
     }
 
-    protected Predicate<Double> getFilterPredicate(String start, String end) {
-        final double startScore = getStartScore(start);
-        final Predicate<Double> compareToStart = p -> start.startsWith(EXCLUSIVE_PREFIX)
-                ? p.compareTo(startScore) > 0
-                : p.compareTo(startScore) >= 0;
-
-        final double endScore = getEndScore(end);
-        final Predicate<Double> compareToEnd = p -> (end.startsWith(EXCLUSIVE_PREFIX)
-                ? p.compareTo(endScore) < 0
-                : p.compareTo(endScore) <= 0);
-        return compareToStart.and(compareToEnd);
-    }
-
     final Slice rangeByScore(Slice start, Slice end, boolean rev) {
         final Slice key = params().get(0);
-        final RMZSet mapDBObj = getHMapFromBaseOrCreateEmpty(key);
-        final Map<Slice, Double> map = mapDBObj.getStoredData();
-
-        if (map == null || map.isEmpty()) return
+        final RMZSet mapDBObj = getZSetFromBaseOrCreateEmpty(key);
+        if (mapDBObj.isEmpty()) return
                 Response.array(Collections.emptyList());
 
-        Predicate<Double> filterPredicate = getFilterPredicate(start.toString(), end.toString());
-
-        Stream<Map.Entry<Slice, Double>> entryStream = map.entrySet().stream()
-                .filter(e -> filterPredicate.test(e.getValue()));
-
+        NavigableSet<ZSetEntry> subset =
+                mapDBObj.subset(getStartBound(start.toString()), getEndBound(end.toString()));
+        if (rev) {
+            subset = subset.descendingSet();
+        }
+        Stream<ZSetEntry> entries = subset.stream();
         boolean withScores = false;
         for (int i = 3; i < params().size(); i++) {
             String param = params().get(i).toString();
             if ("withscores".equalsIgnoreCase(param)) {
                 withScores = true;
-            }
-            else if ("limit".equalsIgnoreCase(param)) {
+            } else if ("limit".equalsIgnoreCase(param)) {
                 long offset = convertToLong(params().get(++i).toString());
                 long count = convertToLong(params().get(++i).toString());
-                entryStream = entryStream.skip(offset).limit(count);
+                entries = entries.skip(offset).limit(count);
             }
         }
-        entryStream = entryStream.sorted(rev ? ZRange.zRangeComparator.reversed() : ZRange.zRangeComparator);
 
-        Stream<Slice> result;
+        Stream<String> result;
         if (withScores) {
-            result = entryStream
-                    .flatMap(e -> Stream.of(e.getKey(),
-                            Slice.create(e.getValue().toString())));
+            result = entries
+                    .flatMap(e -> Stream.of(e.getValue(),
+                            Double.toString(e.getScore())));
         } else {
-            result = entryStream
-                    .map(Map.Entry::getKey);
+            result = entries.map(ZSetEntry::getValue);
         }
-        return Response.array(result
+        final List<Slice> list = result
+                .map(Slice::create)
                 .map(Response::bulkString)
-                .collect(Collectors.toList()));
+                .collect(Collectors.toList());
+
+        return Response.array(list);
     }
 }
diff --git a/src/main/java/com/github/fppt/jedismock/operations/sortedsets/ZAdd.java b/src/main/java/com/github/fppt/jedismock/operations/sortedsets/ZAdd.java
index c711a1b..dcccf22 100644
--- a/src/main/java/com/github/fppt/jedismock/operations/sortedsets/ZAdd.java
+++ b/src/main/java/com/github/fppt/jedismock/operations/sortedsets/ZAdd.java
@@ -1,19 +1,15 @@
 package com.github.fppt.jedismock.operations.sortedsets;
 
 import com.github.fppt.jedismock.datastructures.RMZSet;
+import com.github.fppt.jedismock.datastructures.Slice;
 import com.github.fppt.jedismock.operations.AbstractRedisOperation;
 import com.github.fppt.jedismock.operations.RedisCommand;
 import com.github.fppt.jedismock.server.Response;
-import com.github.fppt.jedismock.datastructures.Slice;
 import com.github.fppt.jedismock.storage.RedisBase;
 
-import java.util.AbstractMap;
-import java.util.LinkedHashMap;
 import java.util.List;
-import java.util.Map;
 
 import static com.github.fppt.jedismock.Utils.convertToDouble;
-import static java.util.stream.Collectors.toMap;
 
 @RedisCommand("zadd")
 class ZAdd extends AbstractRedisOperation {
@@ -25,10 +21,7 @@ class ZAdd extends AbstractRedisOperation {
     @Override
     protected Slice response() {
         Slice key = params().get(0);
-
-        final RMZSet mapDBObj = getHMapFromBaseOrCreateEmpty(key);
-        final Map<Slice, Double> map = mapDBObj.getStoredData();
-
+        final RMZSet mapDBObj = getZSetFromBaseOrCreateEmpty(key);
         int count = 0;
         for (int i = 1; i < params().size(); i += 2) {
             Slice score = params().get(i);
@@ -37,19 +30,10 @@ class ZAdd extends AbstractRedisOperation {
             // Score must be a double. Will throw an exception if it's not.
             double s = convertToDouble(score.toString());
 
-            Double prevScore = map.put(value, s);
+            Double prevScore = mapDBObj.put(value.toString(), s);
             if (prevScore == null) count++;
         }
-
-        // Sort the map by value (the score) before saving
-        Map<Slice, Double> sortedMap = map.entrySet()
-            .stream()
-            .map(x -> new AbstractMap.SimpleEntry<>(x.getKey(), convertToDouble(x.getValue().toString())))
-            .sorted(Map.Entry.comparingByValue())
-            .collect(
-                toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e2, LinkedHashMap::new));
-
-        base().putValue(key, new RMZSet(sortedMap));
+        base().putValue(key, mapDBObj);
         return Response.integer(count);
     }
 
diff --git a/src/main/java/com/github/fppt/jedismock/operations/sortedsets/ZCard.java b/src/main/java/com/github/fppt/jedismock/operations/sortedsets/ZCard.java
index 090ff25..9e43d09 100644
--- a/src/main/java/com/github/fppt/jedismock/operations/sortedsets/ZCard.java
+++ b/src/main/java/com/github/fppt/jedismock/operations/sortedsets/ZCard.java
@@ -8,7 +8,6 @@ import com.github.fppt.jedismock.datastructures.Slice;
 import com.github.fppt.jedismock.storage.RedisBase;
 
 import java.util.List;
-import java.util.Map;
 
 @RedisCommand("zcard")
 class ZCard extends AbstractRedisOperation {
@@ -20,9 +19,7 @@ class ZCard extends AbstractRedisOperation {
     @Override
     protected Slice response() {
         Slice key = params().get(0);
-        final RMZSet mapDBObj = getHMapFromBaseOrCreateEmpty(key);
-        final Map<Slice, Double> map = mapDBObj.getStoredData();
-        if (map == null || map.isEmpty()) return Response.integer(0);
-        return Response.integer(map.size());
+        final RMZSet mapDBObj = getZSetFromBaseOrCreateEmpty(key);
+        return Response.integer(mapDBObj.size());
     }
 }
diff --git a/src/main/java/com/github/fppt/jedismock/operations/sortedsets/ZCount.java b/src/main/java/com/github/fppt/jedismock/operations/sortedsets/ZCount.java
index 24d6ab6..45b3ad8 100644
--- a/src/main/java/com/github/fppt/jedismock/operations/sortedsets/ZCount.java
+++ b/src/main/java/com/github/fppt/jedismock/operations/sortedsets/ZCount.java
@@ -6,10 +6,7 @@ import com.github.fppt.jedismock.operations.RedisCommand;
 import com.github.fppt.jedismock.server.Response;
 import com.github.fppt.jedismock.storage.RedisBase;
 
-import java.io.IOException;
 import java.util.List;
-import java.util.Map;
-import java.util.function.Predicate;
 
 @RedisCommand("zcount")
 public class ZCount extends AbstractByScoreOperation {
@@ -18,21 +15,17 @@ public class ZCount extends AbstractByScoreOperation {
     }
 
     @Override
-    protected Slice response() throws IOException {
+    protected Slice response() {
         final Slice key = params().get(0);
-        final RMZSet mapDBObj = getHMapFromBaseOrCreateEmpty(key);
-        final Map<Slice, Double> map = mapDBObj.getStoredData();
+        final RMZSet mapDBObj = getZSetFromBaseOrCreateEmpty(key);
 
-        if (map == null || map.isEmpty()) return
-                Response.integer(0);
+        if (mapDBObj.isEmpty()) return Response.integer(0);
 
         final String start = params().get(1).toString();
         final String end = params().get(2).toString();
-        Predicate<Double> filterPredicate = getFilterPredicate(start, end);
 
-        long result = map.values().stream()
-                .filter(filterPredicate)
-                .count();
+        long result = mapDBObj.subset(getStartBound(start), getEndBound(end)).size();
+
         return Response.integer(result);
     }
 }
diff --git a/src/main/java/com/github/fppt/jedismock/operations/sortedsets/ZRange.java b/src/main/java/com/github/fppt/jedismock/operations/sortedsets/ZRange.java
index 4fa4570..866a7da 100644
--- a/src/main/java/com/github/fppt/jedismock/operations/sortedsets/ZRange.java
+++ b/src/main/java/com/github/fppt/jedismock/operations/sortedsets/ZRange.java
@@ -6,10 +6,7 @@ import com.github.fppt.jedismock.operations.RedisCommand;
 import com.github.fppt.jedismock.server.Response;
 import com.github.fppt.jedismock.storage.RedisBase;
 
-import java.util.Comparator;
 import java.util.List;
-import java.util.Map;
-import java.util.function.ToDoubleFunction;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
@@ -18,10 +15,6 @@ import static com.github.fppt.jedismock.Utils.convertToInteger;
 @RedisCommand("zrange")
 class ZRange extends AbstractByScoreOperation {
 
-    public static final Comparator<Map.Entry<Slice, Double>> zRangeComparator = Comparator
-            .comparingDouble((ToDoubleFunction<Map.Entry<Slice, Double>>) Map.Entry::getValue)
-            .thenComparing(Map.Entry::getKey);
-
     private static final String WITH_SCORES = "WITHSCORES";
     private static final String IS_REV = "REV";
     private static final String IS_BYSCORE = "BYSCORE";
@@ -41,8 +34,7 @@ class ZRange extends AbstractByScoreOperation {
     @Override
     protected Slice response() {
         Slice key = params().get(0);
-        final RMZSet mapDBObj = getHMapFromBaseOrCreateEmpty(key);
-        final Map<Slice, Double> map = mapDBObj.getStoredData();
+        final RMZSet mapDBObj = getZSetFromBaseOrCreateEmpty(key);
 
         parseArgs();
 
@@ -63,24 +55,24 @@ class ZRange extends AbstractByScoreOperation {
             return zRevRangeByLex.response();
         }
 
-        calculateIndexes(map);
+        calculateIndexes(mapDBObj);
 
         boolean finalWithScores = withScores;
 
-        List<Slice> values = map.entrySet().stream()
-                .sorted(isRev ? zRangeComparator.reversed() : zRangeComparator)
+        final List<Slice> values = mapDBObj.entries(isRev).stream()
                 .skip(start)
                 .limit(end - start + 1)
                 .flatMap(e -> finalWithScores
-                        ? Stream.of(e.getKey(), Slice.create(e.getValue().toString()))
-                        : Stream.of(e.getKey()))
+                        ? Stream.of(e.getValue(), Double.toString(e.getScore()))
+                        : Stream.of(e.getValue()))
+                .map(Slice::create)
                 .map(Response::bulkString)
                 .collect(Collectors.toList());
 
         return Response.array(values);
     }
 
-    private void calculateIndexes(Map<Slice, Double> map) {
+    private void calculateIndexes(RMZSet map) {
         start = convertToInteger(params().get(1).toString());
         end = convertToInteger(params().get(2).toString());
 
diff --git a/src/main/java/com/github/fppt/jedismock/operations/sortedsets/ZRangeByLex.java b/src/main/java/com/github/fppt/jedismock/operations/sortedsets/ZRangeByLex.java
index cf21451..9116cf1 100644
--- a/src/main/java/com/github/fppt/jedismock/operations/sortedsets/ZRangeByLex.java
+++ b/src/main/java/com/github/fppt/jedismock/operations/sortedsets/ZRangeByLex.java
@@ -1,15 +1,16 @@
 package com.github.fppt.jedismock.operations.sortedsets;
 
 import com.github.fppt.jedismock.datastructures.RMZSet;
+import com.github.fppt.jedismock.datastructures.ZSetEntry;
+import com.github.fppt.jedismock.datastructures.ZSetEntryBound;
 import com.github.fppt.jedismock.operations.AbstractRedisOperation;
 import com.github.fppt.jedismock.operations.RedisCommand;
 import com.github.fppt.jedismock.server.Response;
 import com.github.fppt.jedismock.datastructures.Slice;
 import com.github.fppt.jedismock.storage.RedisBase;
 
+import java.util.Collections;
 import java.util.List;
-import java.util.Map;
-import java.util.function.Predicate;
 import java.util.stream.Collectors;
 
 @RedisCommand("zrangebylex")
@@ -27,20 +28,25 @@ class ZRangeByLex extends AbstractRedisOperation {
     @Override
     protected Slice response() {
         Slice key = params().get(0);
-        final RMZSet mapDBObj = getHMapFromBaseOrCreateEmpty(key);
-        final Map<Slice, Double> map = mapDBObj.getStoredData();
+        RMZSet mapDBObj = getZSetFromBaseOrCreateEmpty(key);
 
-        String start = params().get(1).toString();
+        String start = min();
         if (!validateStart(start)) {
             return buildErrorResponse("start");
         }
 
-        String end = params().get(2).toString();
+        String end = max();
         if (!validateEnd(end)) {
             return buildErrorResponse("end");
         }
 
-        return Response.array(doProcess(map, start, end));
+        if (mapDBObj.isEmpty()) {
+            return Response.array(Collections.emptyList());
+        } else {
+            //We assume that all the elements have the same score
+            double score = mapDBObj.entries(false).first().getScore();
+            return Response.array(doProcess(mapDBObj, start, end, score));
+        }
     }
 
     private Slice buildErrorResponse(String param) {
@@ -49,44 +55,48 @@ class ZRangeByLex extends AbstractRedisOperation {
     }
     
     protected boolean validateStart(String start) {
-        return getStartUnbounded().equals(start) || startsWithAnyPrefix(start);
+        return NEGATIVELY_INFINITE.equals(start) || startsWithAnyPrefix(start);
     }
 
     protected boolean validateEnd(String end) {
-        return getEndUnbounded().equals(end) || startsWithAnyPrefix(end);
+        return POSITIVELY_INFINITE.equals(end) || startsWithAnyPrefix(end);
     }
     
     protected boolean startsWithAnyPrefix(String s) {
         return s.startsWith(INCLUSIVE_PREFIX) || s.startsWith(EXCLUSIVE_PREFIX);
     }
 
-    protected List<Slice> doProcess(Map<Slice, Double> map, String start, String end) {
-        return map.keySet().stream()
-                .filter(buildStartPredicate(start).and(buildEndPredicate(end)))
-                .sorted()
+    protected List<Slice> doProcess(RMZSet map, String start, String end, double score) {
+        return map.subset(buildStartEntryBound(score, start),
+                buildEndEntryBound(score, end))
+                .stream()
+                .map(ZSetEntry::getValue)
+                .map(Slice::create)
                 .map(Response::bulkString)
                 .collect(Collectors.toList());
     }
 
-    protected Predicate<Slice> buildStartPredicate(String start) {
-        return p -> getStartUnbounded().equals(start) ||
-                (start.startsWith(INCLUSIVE_PREFIX)
-                        ? p.toString().compareTo(start.substring(1)) >= 0
-                        : p.toString().compareTo(start.substring(1)) > 0);
+    protected ZSetEntryBound buildStartEntryBound(double score, String start) {
+        if (NEGATIVELY_INFINITE.equals(start)) {
+            return new ZSetEntryBound(score, ZSetEntry.MIN_VALUE, true);
+        } else {
+            return new ZSetEntryBound(score, start.substring(1), start.startsWith(INCLUSIVE_PREFIX));
+        }
     }
 
-    protected Predicate<Slice> buildEndPredicate(String end) {
-        return p -> getEndUnbounded().equals(end) ||
-                (end.startsWith(INCLUSIVE_PREFIX)
-                        ? p.toString().compareTo(end.substring(1)) <= 0
-                        : p.toString().compareTo(end.substring(1)) < 0);
+    protected ZSetEntryBound buildEndEntryBound(double score, String end)  {
+        if (POSITIVELY_INFINITE.equals(end)) {
+            return new ZSetEntryBound(score, ZSetEntry.MAX_VALUE, false);
+        } else {
+            return new ZSetEntryBound(score, end.substring(1), end.startsWith(INCLUSIVE_PREFIX));
+        }
     }
 
-    protected String getStartUnbounded() {
-        return NEGATIVELY_INFINITE;
+    protected String min(){
+        return params().get(1).toString();
     }
 
-    protected String getEndUnbounded() {
-        return POSITIVELY_INFINITE;
+    protected String max() {
+        return params().get(2).toString();
     }
 }
diff --git a/src/main/java/com/github/fppt/jedismock/operations/sortedsets/ZRem.java b/src/main/java/com/github/fppt/jedismock/operations/sortedsets/ZRem.java
index eeb8a47..b8bcf34 100644
--- a/src/main/java/com/github/fppt/jedismock/operations/sortedsets/ZRem.java
+++ b/src/main/java/com/github/fppt/jedismock/operations/sortedsets/ZRem.java
@@ -8,7 +8,6 @@ import com.github.fppt.jedismock.datastructures.Slice;
 import com.github.fppt.jedismock.storage.RedisBase;
 
 import java.util.List;
-import java.util.Map;
 
 @RedisCommand("zrem")
 class ZRem extends AbstractRedisOperation {
@@ -19,12 +18,13 @@ class ZRem extends AbstractRedisOperation {
 
     protected Slice response() {
         Slice key = params().get(0);
-        final RMZSet mapDBObj = getHMapFromBaseOrCreateEmpty(key);
-        final Map<Slice, Double> map = mapDBObj.getStoredData();
-        if(map == null || map.isEmpty()) return Response.integer(0);
+        final RMZSet mapDBObj = getZSetFromBaseOrCreateEmpty(key);
+        if (mapDBObj.isEmpty()) {
+            return Response.integer(0);
+        }
         int count = 0;
         for (int i = 1; i < params().size(); i++) {
-            if (map.remove(params().get(i)) != null) {
+            if (mapDBObj.remove(params().get(i).toString())) {
                 count++;
             }
         }
diff --git a/src/main/java/com/github/fppt/jedismock/operations/sortedsets/ZRemRangeByScore.java b/src/main/java/com/github/fppt/jedismock/operations/sortedsets/ZRemRangeByScore.java
index 8f4c83a..7e38d7f 100644
--- a/src/main/java/com/github/fppt/jedismock/operations/sortedsets/ZRemRangeByScore.java
+++ b/src/main/java/com/github/fppt/jedismock/operations/sortedsets/ZRemRangeByScore.java
@@ -1,16 +1,14 @@
 package com.github.fppt.jedismock.operations.sortedsets;
 
 import com.github.fppt.jedismock.datastructures.RMZSet;
+import com.github.fppt.jedismock.datastructures.ZSetEntry;
 import com.github.fppt.jedismock.operations.RedisCommand;
 import com.github.fppt.jedismock.server.Response;
 import com.github.fppt.jedismock.datastructures.Slice;
 import com.github.fppt.jedismock.storage.RedisBase;
 
-import java.util.LinkedHashMap;
 import java.util.List;
-import java.util.Map;
-import java.util.function.Predicate;
-import java.util.stream.Collectors;
+import java.util.NavigableSet;
 
 @RedisCommand("zremrangebyscore")
 public class ZRemRangeByScore extends AbstractByScoreOperation {
@@ -22,29 +20,15 @@ public class ZRemRangeByScore extends AbstractByScoreOperation {
     @Override
     protected Slice response() {
         final Slice key = params().get(0);
-        final RMZSet mapDBObj = getHMapFromBaseOrCreateEmpty(key);
-        final Map<Slice, Double> map = mapDBObj.getStoredData();
-
-        if (map == null || map.isEmpty()) return Response.integer(0);
-
+        final RMZSet mapDBObj = getZSetFromBaseOrCreateEmpty(key);
+        if (mapDBObj.isEmpty()) return Response.integer(0);
         final String start = params().get(1).toString();
         final String end = params().get(2).toString();
-        Predicate<Double> filterPredicate = getFilterPredicate(start, end);
-
-        List<Double> values = map.values().stream()
-                .filter(filterPredicate)
-                .collect(Collectors.toList());
-
-        final Map<Slice, Double> result = map.entrySet().stream()
-                .filter(entry -> filterPredicate.negate().test(entry.getValue()))
-                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue,
-                        (u, v) -> {
-                            //duplicate key
-                            throw new IllegalStateException();
-                        }, LinkedHashMap::new));
-
-        base().putValue(key, new RMZSet(result));
-        return Response.integer(values.size());
+        final NavigableSet<ZSetEntry> subset = mapDBObj.subset(getStartBound(start), getEndBound(end));
+        int count = subset.size();
+        subset.clear();
+        base().putValue(key, mapDBObj);
+        return Response.integer(count);
     }
 
 }
diff --git a/src/main/java/com/github/fppt/jedismock/operations/sortedsets/ZRevRangeByLex.java b/src/main/java/com/github/fppt/jedismock/operations/sortedsets/ZRevRangeByLex.java
index 7cfc6c2..f507c98 100644
--- a/src/main/java/com/github/fppt/jedismock/operations/sortedsets/ZRevRangeByLex.java
+++ b/src/main/java/com/github/fppt/jedismock/operations/sortedsets/ZRevRangeByLex.java
@@ -1,15 +1,12 @@
 package com.github.fppt.jedismock.operations.sortedsets;
 
-import com.github.fppt.jedismock.operations.RedisCommand;
-import com.github.fppt.jedismock.server.Response;
+import com.github.fppt.jedismock.datastructures.RMZSet;
 import com.github.fppt.jedismock.datastructures.Slice;
+import com.github.fppt.jedismock.operations.RedisCommand;
 import com.github.fppt.jedismock.storage.RedisBase;
-import java.util.Comparator;
 
+import java.util.Collections;
 import java.util.List;
-import java.util.Map;
-import java.util.function.Predicate;
-import java.util.stream.Collectors;
 
 @RedisCommand("zrevrangebylex")
 class ZRevRangeByLex extends ZRangeByLex {
@@ -19,37 +16,19 @@ class ZRevRangeByLex extends ZRangeByLex {
     }
 
     @Override
-    protected List<Slice> doProcess(Map<Slice, Double> map, String start, String end) {
-        return map.keySet().stream()
-                .filter(buildStartPredicate(start).and(buildEndPredicate(end)))
-                .sorted(Comparator.reverseOrder())
-                .map(Response::bulkString)
-                .collect(Collectors.toList());
+    protected List<Slice> doProcess(RMZSet map, String start, String end, double score) {
+        final List<Slice> list = super.doProcess(map, start, end, score);
+        Collections.reverse(list);
+        return list;
     }
 
     @Override
-    protected Predicate<Slice> buildStartPredicate(String start) {
-        return p -> getStartUnbounded().equals(start) ||
-                (start.startsWith(INCLUSIVE_PREFIX)
-                        ? p.toString().compareTo(start.substring(1)) <= 0
-                        : p.toString().compareTo(start.substring(1)) < 0);
+    protected String min() {
+        return params().get(2).toString();
     }
 
     @Override
-    protected Predicate<Slice> buildEndPredicate(String end) {
-        return p -> getEndUnbounded().equals(end) ||
-                (end.startsWith(INCLUSIVE_PREFIX)
-                        ? p.toString().compareTo(end.substring(1)) >= 0
-                        : p.toString().compareTo(end.substring(1)) > 0);
-    }
-    
-    @Override
-    protected String getStartUnbounded() {
-        return POSITIVELY_INFINITE;
+    protected String max() {
+        return params().get(1).toString();
     }
-
-    @Override
-    protected String getEndUnbounded() {
-        return NEGATIVELY_INFINITE;
-    }    
 }
diff --git a/src/main/java/com/github/fppt/jedismock/operations/sortedsets/ZRevRangeByScore.java b/src/main/java/com/github/fppt/jedismock/operations/sortedsets/ZRevRangeByScore.java
index 2a37ecc..e0bbc33 100644
--- a/src/main/java/com/github/fppt/jedismock/operations/sortedsets/ZRevRangeByScore.java
+++ b/src/main/java/com/github/fppt/jedismock/operations/sortedsets/ZRevRangeByScore.java
@@ -11,7 +11,6 @@ public class ZRevRangeByScore extends AbstractByScoreOperation {
     private final Slice start;
     private final Slice end;
 
-
     ZRevRangeByScore(RedisBase base, List<Slice> params) {
         super(base, params);
         //NB: reverse order of arguments, cf. ZRangeByScore
diff --git a/src/main/java/com/github/fppt/jedismock/operations/sortedsets/ZScore.java b/src/main/java/com/github/fppt/jedismock/operations/sortedsets/ZScore.java
index f719877..b14e309 100644
--- a/src/main/java/com/github/fppt/jedismock/operations/sortedsets/ZScore.java
+++ b/src/main/java/com/github/fppt/jedismock/operations/sortedsets/ZScore.java
@@ -8,11 +8,10 @@ import com.github.fppt.jedismock.datastructures.Slice;
 import com.github.fppt.jedismock.storage.RedisBase;
 
 import java.util.List;
-import java.util.Map;
 
 @RedisCommand("zscore")
 class ZScore extends AbstractRedisOperation {
-    
+
     ZScore(RedisBase base, List<Slice> params) {
         super(base, params);
     }
@@ -21,16 +20,14 @@ class ZScore extends AbstractRedisOperation {
     protected Slice response() {
         Slice key = params().get(0);
         Slice val = params().get(1);
-
-        final RMZSet mapDBObj = getHMapFromBaseOrCreateEmpty(key);
-        final Map<Slice, Double> map = mapDBObj.getStoredData();
-        
-        if(val == null || val.toString().isEmpty()) {
+        if (val == null || val.toString().isEmpty()) {
             return Response.error("Valid parameter must be provided");
         }
-        
-        Double score = map.get(Slice.create(val.toString()));
-        
+
+        final RMZSet mapDBObj = getZSetFromBaseOrCreateEmpty(key);
+
+        Double score = mapDBObj.getScore(val.toString());
+
         return score == null ? Response.NULL : Response.bulkString(Slice.create(score.toString()));
     }
 }


Test patch:
diff --git a/src/test/java/com/github/fppt/jedismock/TestJedisConnect.java b/src/test/java/com/github/fppt/jedismock/TestJedisConnect.java
index 5fb72a2..49615c1 100644
--- a/src/test/java/com/github/fppt/jedismock/TestJedisConnect.java
+++ b/src/test/java/com/github/fppt/jedismock/TestJedisConnect.java
@@ -1,6 +1,5 @@
 package com.github.fppt.jedismock;
 
-import com.github.fppt.jedismock.server.ServiceOptions;
 import org.junit.jupiter.api.Test;
 import redis.clients.jedis.Jedis;
 import redis.clients.jedis.Pipeline;
@@ -17,7 +16,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
 public class TestJedisConnect {
 
     @Test
-    public void testPipeline() throws IOException, InterruptedException {
+    public void testPipeline() throws IOException {
         RedisServer server = RedisServer.newRedisServer();
         server.start();
         Jedis jedis = new Jedis(server.getHost(), server.getBindPort());
@@ -49,9 +48,9 @@ public class TestJedisConnect {
         RedisServer server = RedisServer.newRedisServer();
         server.start();
         Jedis jedis = new Jedis(server.getHost(), server.getBindPort(), 10000000);
-        assertEquals(1, (long) jedis.lpush("list", "world"));
-        assertEquals(2, (long) jedis.lpush("list", "hello"));
-        assertEquals(3, (long) jedis.rpush("list", "!"));
+        assertEquals(1, jedis.lpush("list", "world"));
+        assertEquals(2, jedis.lpush("list", "hello"));
+        assertEquals(3, jedis.rpush("list", "!"));
         assertArrayEquals(new String[]{"hello", "world", "!"}, jedis.lrange("list", 0, -1).toArray());
         jedis.disconnect();
         server.stop();
diff --git a/src/test/java/com/github/fppt/jedismock/TestUtils.java b/src/test/java/com/github/fppt/jedismock/TestUtils.java
index f2c0a49..5bbafc1 100644
--- a/src/test/java/com/github/fppt/jedismock/TestUtils.java
+++ b/src/test/java/com/github/fppt/jedismock/TestUtils.java
@@ -1,13 +1,10 @@
 package com.github.fppt.jedismock;
 
-import com.github.fppt.jedismock.datastructures.Slice;
 import org.junit.jupiter.api.Test;
 
 import java.io.IOException;
 import java.io.InputStream;
 
-import static org.junit.jupiter.api.Assertions.assertArrayEquals;
-
 /**
  * Created by Xiaolu on 2015/4/20.
  */
diff --git a/src/test/java/com/github/fppt/jedismock/comparisontests/ComparisonBase.java b/src/test/java/com/github/fppt/jedismock/comparisontests/ComparisonBase.java
index 84292fe..bada11a 100644
--- a/src/test/java/com/github/fppt/jedismock/comparisontests/ComparisonBase.java
+++ b/src/test/java/com/github/fppt/jedismock/comparisontests/ComparisonBase.java
@@ -55,7 +55,7 @@ public class ComparisonBase implements TestTemplateInvocationContextProvider,
                         new HostAndPort(redis.getContainerIpAddress(), redis.getFirstMappedPort())));
     }
 
-    private class JedisTestTemplateInvocationContext implements TestTemplateInvocationContext {
+    private static class JedisTestTemplateInvocationContext implements TestTemplateInvocationContext {
 
         private final String displayName;
         private final Jedis jedis;
diff --git a/src/test/java/com/github/fppt/jedismock/comparisontests/bitmaps/BitMapsOperationsTest.java b/src/test/java/com/github/fppt/jedismock/comparisontests/bitmaps/BitMapsOperationsTest.java
index 4990e64..599b5fa 100644
--- a/src/test/java/com/github/fppt/jedismock/comparisontests/bitmaps/BitMapsOperationsTest.java
+++ b/src/test/java/com/github/fppt/jedismock/comparisontests/bitmaps/BitMapsOperationsTest.java
@@ -5,16 +5,13 @@ import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.TestTemplate;
 import org.junit.jupiter.api.extension.ExtendWith;
 import redis.clients.jedis.Jedis;
-import redis.clients.jedis.exceptions.JedisDataException;
 
-import java.nio.charset.StandardCharsets;
 import java.util.Arrays;
 import java.util.Collections;
-import java.util.HashSet;
 import java.util.List;
-import java.util.Set;
 
-import static org.junit.jupiter.api.Assertions.*;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
 @ExtendWith(ComparisonBase.class)
 public class BitMapsOperationsTest {
diff --git a/src/test/java/com/github/fppt/jedismock/comparisontests/hashes/HashOperationsTest.java b/src/test/java/com/github/fppt/jedismock/comparisontests/hashes/HashOperationsTest.java
index 6a9b1b7..2f9ef13 100644
--- a/src/test/java/com/github/fppt/jedismock/comparisontests/hashes/HashOperationsTest.java
+++ b/src/test/java/com/github/fppt/jedismock/comparisontests/hashes/HashOperationsTest.java
@@ -1,7 +1,6 @@
 package com.github.fppt.jedismock.comparisontests.hashes;
 
 import com.github.fppt.jedismock.comparisontests.ComparisonBase;
-import com.github.fppt.jedismock.exception.WrongValueTypeException;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.TestTemplate;
 import org.junit.jupiter.api.extension.ExtendWith;
@@ -14,7 +13,12 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
-import static org.junit.jupiter.api.Assertions.*;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
 
 @ExtendWith(ComparisonBase.class)
 public class HashOperationsTest {
diff --git a/src/test/java/com/github/fppt/jedismock/comparisontests/hyperloglog/HLLOperationsTest.java b/src/test/java/com/github/fppt/jedismock/comparisontests/hyperloglog/HLLOperationsTest.java
index ae4ac00..c2f70a0 100644
--- a/src/test/java/com/github/fppt/jedismock/comparisontests/hyperloglog/HLLOperationsTest.java
+++ b/src/test/java/com/github/fppt/jedismock/comparisontests/hyperloglog/HLLOperationsTest.java
@@ -7,13 +7,9 @@ import org.junit.jupiter.api.extension.ExtendWith;
 import redis.clients.jedis.Jedis;
 import redis.clients.jedis.exceptions.JedisDataException;
 
-import java.nio.charset.StandardCharsets;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
-
-import static org.junit.jupiter.api.Assertions.*;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
 @ExtendWith(ComparisonBase.class)
 public class HLLOperationsTest {
diff --git a/src/test/java/com/github/fppt/jedismock/comparisontests/pubsub/PubSubTest.java b/src/test/java/com/github/fppt/jedismock/comparisontests/pubsub/PubSubTest.java
index 55eaddc..c56a979 100644
--- a/src/test/java/com/github/fppt/jedismock/comparisontests/pubsub/PubSubTest.java
+++ b/src/test/java/com/github/fppt/jedismock/comparisontests/pubsub/PubSubTest.java
@@ -22,7 +22,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
 @ExtendWith(ComparisonBase.class)
 public class PubSubTest {
 
-    class TestSubscription implements AutoCloseable {
+    static class TestSubscription implements AutoCloseable {
         private final Jedis client;
         private final MockSubscriber subscriber;
         private final Future<?> future;
diff --git a/src/test/java/com/github/fppt/jedismock/comparisontests/sortedsets/SortedSetOperationsTest.java b/src/test/java/com/github/fppt/jedismock/comparisontests/sortedsets/SortedSetOperationsTest.java
index a06bae0..d02d80f 100644
--- a/src/test/java/com/github/fppt/jedismock/comparisontests/sortedsets/SortedSetOperationsTest.java
+++ b/src/test/java/com/github/fppt/jedismock/comparisontests/sortedsets/SortedSetOperationsTest.java
@@ -30,7 +30,7 @@ public class SortedSetOperationsTest {
 
         assertEquals(1L, result);
 
-        List<String> results = new LinkedList<>(jedis.zrange(key, 0, -1));
+        List<String> results = jedis.zrange(key, 0, -1);
 
         assertEquals(1, results.size());
         assertEquals(value, results.get(0));
@@ -47,7 +47,7 @@ public class SortedSetOperationsTest {
 
         assertEquals(2L, result);
 
-        List<String> results = new LinkedList<>(jedis.zrange(key, 0, -1));
+        List<String> results = jedis.zrange(key, 0, -1);
 
         assertEquals(2, results.size());
         assertEquals("myvalue1", results.get(0));
@@ -88,7 +88,7 @@ public class SortedSetOperationsTest {
 
         assertEquals(2L, result);
 
-        List<String> results = new LinkedList<>(jedis.zrange(key, 0, -1));
+        List<String> results = jedis.zrange(key, 0, -1);
 
         assertEquals(2, results.size());
         assertEquals("myvalue1", results.get(0));
@@ -98,7 +98,7 @@ public class SortedSetOperationsTest {
 
         assertEquals(1L, result);
 
-        results = new LinkedList<>(jedis.zrange(key, 0, -1));
+        results = jedis.zrange(key, 0, -1);
 
         assertEquals(1, results.size());
         assertEquals("myvalue2", results.get(0));
@@ -117,7 +117,7 @@ public class SortedSetOperationsTest {
 
         assertEquals(4L, result);
 
-        List<String> results = new LinkedList<>(jedis.zrange(key, 0, -1));
+        List<String> results = jedis.zrange(key, 0, -1);
 
         assertEquals(4, results.size());
         assertEquals("myvalue1", results.get(0));
@@ -157,7 +157,7 @@ public class SortedSetOperationsTest {
 
         assertEquals(4L, result);
 
-        List<Tuple> results = new LinkedList<>(jedis.zrangeWithScores(key, 0, -1));
+        List<Tuple> results = jedis.zrangeWithScores(key, 0, -1);
 
         assertEquals(4, results.size());
         assertEquals("myvalue1", results.get(0).getElement());
diff --git a/src/test/java/com/github/fppt/jedismock/comparisontests/sortedsets/TestZCount.java b/src/test/java/com/github/fppt/jedismock/comparisontests/sortedsets/TestZCount.java
index c48ff81..c94bb55 100644
--- a/src/test/java/com/github/fppt/jedismock/comparisontests/sortedsets/TestZCount.java
+++ b/src/test/java/com/github/fppt/jedismock/comparisontests/sortedsets/TestZCount.java
@@ -5,14 +5,8 @@ import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.TestTemplate;
 import org.junit.jupiter.api.extension.ExtendWith;
 import redis.clients.jedis.Jedis;
-import redis.clients.jedis.resps.Tuple;
 import redis.clients.jedis.exceptions.JedisDataException;
 
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
-
-import static java.util.Arrays.asList;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 
@@ -63,6 +57,7 @@ public class TestZCount {
 
         // then
         assertEquals(1, jedis.zcount(ZSET_KEY, "-inf", "(2"));
+        assertEquals(1, jedis.zcount(ZSET_KEY, "(2", "+inf"));
     }
 
     @TestTemplate
@@ -114,4 +109,14 @@ public class TestZCount {
         assertThrows(RuntimeException.class,
                 () -> jedis.zcount(ZSET_KEY, "FOO", "BAR"));
     }
+
+    @TestTemplate
+    public void sameValuesDeduplication(Jedis jedis) {
+        jedis.zadd(ZSET_KEY, 1, "one");
+        jedis.zadd(ZSET_KEY, 2, "one");
+        jedis.zadd(ZSET_KEY, 3, "one");
+        assertEquals(1,
+                jedis.zcount(ZSET_KEY, "-inf", "+inf"));
+        assertEquals(3, jedis.zscore(ZSET_KEY, "one"));
+    }
 }
diff --git a/src/test/java/com/github/fppt/jedismock/comparisontests/sortedsets/TestZRange.java b/src/test/java/com/github/fppt/jedismock/comparisontests/sortedsets/TestZRange.java
index 6dca30f..d9600b2 100644
--- a/src/test/java/com/github/fppt/jedismock/comparisontests/sortedsets/TestZRange.java
+++ b/src/test/java/com/github/fppt/jedismock/comparisontests/sortedsets/TestZRange.java
@@ -7,7 +7,6 @@ import org.junit.jupiter.api.extension.ExtendWith;
 import redis.clients.jedis.Jedis;
 import redis.clients.jedis.params.ZRangeParams;
 
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 
@@ -31,27 +30,27 @@ public class TestZRange {
 
     @TestTemplate
     public void whenUsingZrange_EnsureItReturnsEverythingInRightOrderWithPlusMinusMaxInteger(Jedis jedis) {
-        assertEquals(Arrays.asList("cccc", "aaaa", "babb", "bbbb", "bcbb"), new ArrayList<>(jedis.zrange(ZSET_KEY, Integer.MIN_VALUE, Integer.MAX_VALUE)));
+        assertEquals(Arrays.asList("cccc", "aaaa", "babb", "bbbb", "bcbb"), jedis.zrange(ZSET_KEY, Integer.MIN_VALUE, Integer.MAX_VALUE));
     }
 
     @TestTemplate
     public void whenUsingZrange_EnsureItReturnsListInRightOrderWithPositiveRange(Jedis jedis) {
-        assertEquals(Arrays.asList("aaaa", "babb", "bbbb"), new ArrayList<>(jedis.zrange(ZSET_KEY, 1, 3)));
+        assertEquals(Arrays.asList("aaaa", "babb", "bbbb"), jedis.zrange(ZSET_KEY, 1, 3));
     }
 
     @TestTemplate
     public void whenUsingZrange_EnsureItReturnsListInRightOrderWithNegativeRange(Jedis jedis) {
-        assertEquals(Arrays.asList("babb", "bbbb", "bcbb"), new ArrayList<>(jedis.zrange(ZSET_KEY, -3, -1)));
+        assertEquals(Arrays.asList("babb", "bbbb", "bcbb"), jedis.zrange(ZSET_KEY, -3, -1));
     }
 
     @TestTemplate
     public void whenUsingZrange_EnsureItReturnsListInRightOrderWithNegativeStartAndPositiveEndRange(Jedis jedis) {
-        assertEquals(Arrays.asList("cccc", "aaaa", "babb"), new ArrayList<>(jedis.zrange(ZSET_KEY, -5, 2)));
+        assertEquals(Arrays.asList("cccc", "aaaa", "babb"), jedis.zrange(ZSET_KEY, -5, 2));
     }
 
     @TestTemplate
     public void whenUsingZrange_EnsureItReturnsListInRightOrderWithPositiveStartAndNegativeEndRange(Jedis jedis) {
-        assertEquals(Arrays.asList("aaaa", "babb", "bbbb", "bcbb"), new ArrayList<>(jedis.zrange(ZSET_KEY, 1, -1)));
+        assertEquals(Arrays.asList("aaaa", "babb", "bbbb", "bcbb"), jedis.zrange(ZSET_KEY, 1, -1));
     }
 
     @TestTemplate
diff --git a/src/test/java/com/github/fppt/jedismock/comparisontests/sortedsets/TestZRangeByLex.java b/src/test/java/com/github/fppt/jedismock/comparisontests/sortedsets/TestZRangeByLex.java
index a4f04ed..0fdbbe0 100644
--- a/src/test/java/com/github/fppt/jedismock/comparisontests/sortedsets/TestZRangeByLex.java
+++ b/src/test/java/com/github/fppt/jedismock/comparisontests/sortedsets/TestZRangeByLex.java
@@ -31,25 +31,25 @@ public class TestZRangeByLex {
 
     @TestTemplate
     public void zrangebylexKeysCorrectOrderUnbounded(Jedis jedis) {
-        List<String> results = new ArrayList<>(jedis.zrangeByLex(key, "-", "+"));
+        List<String> results = jedis.zrangeByLex(key, "-", "+");
         assertEquals(Arrays.asList("aaa", "bbb", "ccc", "ddd"), results);
     }
 
     @TestTemplate
     void zrangebylexKeysCorrectOrderBounded(Jedis jedis) {
-        List<String> results = new ArrayList<>(jedis.zrangeByLex(key, "[bbb", "(ddd"));
+        List<String> results = jedis.zrangeByLex(key, "[bbb", "(ddd");
         assertEquals(Arrays.asList("bbb", "ccc"), results);
     }
 
     @TestTemplate
     public void zrevrangebylexKeysCorrectOrderUnbounded(Jedis jedis) {
-        List<String> results = new ArrayList<>(jedis.zrevrangeByLex(key, "+", "-"));
+        List<String> results = jedis.zrevrangeByLex(key, "+", "-");
         assertEquals(Arrays.asList("ddd", "ccc", "bbb", "aaa"), results);
     }
 
     @TestTemplate
     void zrevrangebylexKeysCorrectOrderBounded(Jedis jedis) {
-        List<String> results = new ArrayList<>(jedis.zrevrangeByLex(key, "[ddd", "(bbb"));
+        List<String> results = jedis.zrevrangeByLex(key, "[ddd", "(bbb");
         assertEquals(Arrays.asList("ddd", "ccc"), results);
     }
 
diff --git a/src/test/java/com/github/fppt/jedismock/comparisontests/sortedsets/TestZRangeByScore.java b/src/test/java/com/github/fppt/jedismock/comparisontests/sortedsets/TestZRangeByScore.java
index 9fb15dc..d6303b7 100644
--- a/src/test/java/com/github/fppt/jedismock/comparisontests/sortedsets/TestZRangeByScore.java
+++ b/src/test/java/com/github/fppt/jedismock/comparisontests/sortedsets/TestZRangeByScore.java
@@ -8,11 +8,15 @@ import redis.clients.jedis.Jedis;
 import redis.clients.jedis.exceptions.JedisDataException;
 import redis.clients.jedis.resps.Tuple;
 
-import java.util.Collections;
+import java.util.Arrays;
 import java.util.List;
 
 import static java.util.Arrays.asList;
-import static org.junit.jupiter.api.Assertions.*;
+import static java.util.Collections.emptyList;
+import static java.util.Collections.singletonList;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
 @ExtendWith(ComparisonBase.class)
 public class TestZRangeByScore {
@@ -21,13 +25,13 @@ public class TestZRangeByScore {
 
     @BeforeEach
     public void clearKey(Jedis jedis) {
-        jedis.del(ZSET_KEY);
+        jedis.flushDB();
     }
 
     @TestTemplate
     public void whenUsingZrangeByScore_EnsureItReturnsEmptySetForNonDefinedKey(Jedis jedis) {
-        assertEquals(Collections.emptyList(), jedis.zrangeByScore(ZSET_KEY, "-inf", "+inf"));
-        assertEquals(Collections.emptyList(), jedis.zrangeByScoreWithScores(ZSET_KEY, "-inf", "+inf"));
+        assertEquals(emptyList(), jedis.zrangeByScore(ZSET_KEY, "-inf", "+inf"));
+        assertEquals(emptyList(), jedis.zrangeByScoreWithScores(ZSET_KEY, "-inf", "+inf"));
     }
 
     @TestTemplate
@@ -87,8 +91,8 @@ public class TestZRangeByScore {
         assertEquals(3, jedis.zrange(ZSET_KEY, 0, -1).size());
 
         // then
-        assertEquals(Collections.singletonList("one"), jedis.zrangeByScore(ZSET_KEY, "-inf", "(2"));
-        assertEquals(Collections.singletonList(new Tuple("one", 1.)),
+        assertEquals(singletonList("one"), jedis.zrangeByScore(ZSET_KEY, "-inf", "(2"));
+        assertEquals(singletonList(new Tuple("one", 1.)),
                 jedis.zrangeByScoreWithScores(ZSET_KEY, "-inf", "(2"));
     }
 
@@ -179,4 +183,20 @@ public class TestZRangeByScore {
         assertThrows(RuntimeException.class,
                 () -> jedis.zrangeByScore(ZSET_KEY, "FOO", "BAR"));
     }
+
+    @TestTemplate
+    void sortElementsWithSameScoreLexicographically(Jedis jedis) {
+        jedis.zadd("foo", 42, "def");
+        jedis.zadd("foo", 42, "abc");
+        final List<String> list = jedis.zrangeByScore("foo", 42, 42, 0, 1);
+        assertEquals(singletonList("abc"), list);
+    }
+
+    @TestTemplate
+    void minusInfinity(Jedis jedis) {
+        jedis.zadd("foo", 0, "abc");
+        jedis.zadd("foo", 1, "def");
+        final List<String> list = jedis.zrangeByScore("foo", "-inf", "+inf");
+        assertEquals(Arrays.asList("abc", "def"), list);
+    }
 }
diff --git a/src/test/java/com/github/fppt/jedismock/comparisontests/sortedsets/TestZRangeWithByLexArg.java b/src/test/java/com/github/fppt/jedismock/comparisontests/sortedsets/TestZRangeWithByLexArg.java
index faeac30..c8544ba 100644
--- a/src/test/java/com/github/fppt/jedismock/comparisontests/sortedsets/TestZRangeWithByLexArg.java
+++ b/src/test/java/com/github/fppt/jedismock/comparisontests/sortedsets/TestZRangeWithByLexArg.java
@@ -32,25 +32,25 @@ public class TestZRangeWithByLexArg {
 
     @TestTemplate
     public void zrangebylexKeysCorrectOrderUnbounded(Jedis jedis) {
-        List<String> results = new ArrayList<>(jedis.zrange(key, ZRangeParams.zrangeByLexParams("-", "+")));
+        List<String> results = jedis.zrange(key, ZRangeParams.zrangeByLexParams("-", "+"));
         assertEquals(Arrays.asList("aaa", "bbb", "ccc", "ddd"), results);
     }
 
     @TestTemplate
     void zrangebylexKeysCorrectOrderBounded(Jedis jedis) {
-        List<String> results = new ArrayList<>(jedis.zrange(key, ZRangeParams.zrangeByLexParams("[bbb", "(ddd")));
+        List<String> results = jedis.zrange(key, ZRangeParams.zrangeByLexParams("[bbb", "(ddd"));
         assertEquals(Arrays.asList("bbb", "ccc"), results);
     }
 
     @TestTemplate
     public void zrevrangebylexKeysCorrectOrderUnbounded(Jedis jedis) {
-        List<String> results = new ArrayList<>(jedis.zrange(key, ZRangeParams.zrangeByLexParams("+", "-").rev()));
+        List<String> results = jedis.zrange(key, ZRangeParams.zrangeByLexParams("+", "-").rev());
         assertEquals(Arrays.asList("ddd", "ccc", "bbb", "aaa"), results);
     }
 
     @TestTemplate
     void zrevrangebylexKeysCorrectOrderBounded(Jedis jedis) {
-        List<String> results = new ArrayList<>(jedis.zrange(key, ZRangeParams.zrangeByLexParams("[ddd", "(bbb").rev()));
+        List<String> results = jedis.zrange(key, ZRangeParams.zrangeByLexParams("[ddd", "(bbb").rev());
         assertEquals(Arrays.asList("ddd", "ccc"), results);
     }
 
diff --git a/src/test/java/com/github/fppt/jedismock/comparisontests/sortedsets/TestZRangeWithByScoreArg.java b/src/test/java/com/github/fppt/jedismock/comparisontests/sortedsets/TestZRangeWithByScoreArg.java
index f14fe6c..ae064b3 100644
--- a/src/test/java/com/github/fppt/jedismock/comparisontests/sortedsets/TestZRangeWithByScoreArg.java
+++ b/src/test/java/com/github/fppt/jedismock/comparisontests/sortedsets/TestZRangeWithByScoreArg.java
@@ -8,10 +8,11 @@ import redis.clients.jedis.Jedis;
 import redis.clients.jedis.params.ZRangeParams;
 import redis.clients.jedis.resps.Tuple;
 
-import java.util.Collections;
 import java.util.List;
 
 import static java.util.Arrays.asList;
+import static java.util.Collections.emptyList;
+import static java.util.Collections.singletonList;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
@@ -27,8 +28,8 @@ public class TestZRangeWithByScoreArg {
 
     @TestTemplate
     public void whenUsingZrangeByScore_EnsureItReturnsEmptySetForNonDefinedKey(Jedis jedis) {
-        assertEquals(Collections.emptyList(), jedis.zrange(ZSET_KEY, ZRangeParams.zrangeByScoreParams(Double.MIN_VALUE, Double.MAX_VALUE)));
-        assertEquals(Collections.emptyList(), jedis.zrange(ZSET_KEY + " WITHSCORES", ZRangeParams.zrangeByScoreParams(Double.MIN_VALUE, Double.MAX_VALUE)));
+        assertEquals(emptyList(), jedis.zrange(ZSET_KEY, ZRangeParams.zrangeByScoreParams(Double.MIN_VALUE, Double.MAX_VALUE)));
+        assertEquals(emptyList(), jedis.zrange(ZSET_KEY + " WITHSCORES", ZRangeParams.zrangeByScoreParams(Double.MIN_VALUE, Double.MAX_VALUE)));
     }
 
     @TestTemplate
@@ -88,8 +89,8 @@ public class TestZRangeWithByScoreArg {
         assertEquals(3, jedis.zrange(ZSET_KEY, 0, -1).size());
 
         // then
-        assertEquals(Collections.singletonList("one"), jedis.zrange(ZSET_KEY, ZRangeParams.zrangeByScoreParams(Double.MIN_VALUE, 1.99)));
-        assertEquals(Collections.singletonList(new Tuple("one", 1.)),
+        assertEquals(singletonList("one"), jedis.zrange(ZSET_KEY, ZRangeParams.zrangeByScoreParams(Double.MIN_VALUE, 1.99)));
+        assertEquals(singletonList(new Tuple("one", 1.)),
                 jedis.zrangeWithScores(ZSET_KEY, ZRangeParams.zrangeByScoreParams(Double.MIN_VALUE, 1.99)));
     }
 
diff --git a/src/test/java/com/github/fppt/jedismock/comparisontests/sortedsets/TestZRemRangeByScore.java b/src/test/java/com/github/fppt/jedismock/comparisontests/sortedsets/TestZRemRangeByScore.java
index 8bee22e..7181b36 100644
--- a/src/test/java/com/github/fppt/jedismock/comparisontests/sortedsets/TestZRemRangeByScore.java
+++ b/src/test/java/com/github/fppt/jedismock/comparisontests/sortedsets/TestZRemRangeByScore.java
@@ -8,6 +8,7 @@ import redis.clients.jedis.Jedis;
 import redis.clients.jedis.exceptions.JedisDataException;
 
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.List;
 
 import static org.junit.jupiter.api.Assertions.*;
@@ -67,9 +68,8 @@ public class TestZRemRangeByScore {
 
         // then
         assertEquals(2, zremrangeByScoreResult);
-        final List<String> zrangeResult = jedis.zrange(ZSET_KEY, 0, -1);
-        assertEquals(1, zrangeResult.size());
-        assertArrayEquals(zrangeResult.toArray(), new String[]{"three"});
+        List<String> zrangeResult = jedis.zrange(ZSET_KEY, 0, -1);
+        assertEquals(Collections.singletonList("three"), zrangeResult);
     }
 
     @TestTemplate
diff --git a/src/test/java/com/github/fppt/jedismock/comparisontests/sortedsets/TestZRevRangeByScore.java b/src/test/java/com/github/fppt/jedismock/comparisontests/sortedsets/TestZRevRangeByScore.java
index 8150eec..4f100d1 100644
--- a/src/test/java/com/github/fppt/jedismock/comparisontests/sortedsets/TestZRevRangeByScore.java
+++ b/src/test/java/com/github/fppt/jedismock/comparisontests/sortedsets/TestZRevRangeByScore.java
@@ -2,12 +2,13 @@ package com.github.fppt.jedismock.comparisontests.sortedsets;
 
 import com.github.fppt.jedismock.comparisontests.ComparisonBase;
 import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.TestTemplate;
 import org.junit.jupiter.api.extension.ExtendWith;
 import redis.clients.jedis.Jedis;
 
 import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 
@@ -27,4 +28,20 @@ public class TestZRevRangeByScore {
     void zRevRangeByScoreReturnsValues(Jedis jedis) {
         assertEquals(Arrays.asList("three", "two", "one"), jedis.zrevrangeByScore(ZSET_KEY, 3, 1));
     }
+
+    @TestTemplate
+    void sortElementsWithSameScoreLexicographically(Jedis jedis) {
+        jedis.zadd("foo", 42, "abc");
+        jedis.zadd("foo", 42, "def");
+        final List<String> list = jedis.zrevrangeByScore("foo", 42, 42, 0, 1);
+        assertEquals(Collections.singletonList("def"), list);
+    }
+
+    @TestTemplate
+    void minusInfinity(Jedis jedis) {
+        jedis.zadd("foo", 0, "abc");
+        jedis.zadd("foo", 1, "def");
+        final List<String> list = jedis.zrevrangeByScore("foo", "+inf", "-inf");
+        assertEquals(Arrays.asList("def", "abc"), list);
+    }
 }
diff --git a/src/test/java/com/github/fppt/jedismock/comparisontests/transactions/TestWatchUnwatch.java b/src/test/java/com/github/fppt/jedismock/comparisontests/transactions/TestWatchUnwatch.java
index b4d180b..b41cb7c 100644
--- a/src/test/java/com/github/fppt/jedismock/comparisontests/transactions/TestWatchUnwatch.java
+++ b/src/test/java/com/github/fppt/jedismock/comparisontests/transactions/TestWatchUnwatch.java
@@ -7,24 +7,14 @@ import org.junit.jupiter.api.extension.ExtendWith;
 import redis.clients.jedis.HostAndPort;
 import redis.clients.jedis.Jedis;
 import redis.clients.jedis.Transaction;
-import redis.clients.jedis.exceptions.JedisDataException;
 import redis.clients.jedis.params.SetParams;
 
 import java.util.List;
-import java.util.concurrent.Callable;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
 
 import static java.util.concurrent.CompletableFuture.runAsync;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNull;
-import static org.junit.jupiter.api.Assertions.assertThrows;
 
 @ExtendWith(ComparisonBase.class)
 public class TestWatchUnwatch {
diff --git a/src/test/java/com/github/fppt/jedismock/comparisontests/transactions/TransactionOperationsTest.java b/src/test/java/com/github/fppt/jedismock/comparisontests/transactions/TransactionOperationsTest.java
index 535a3d6..99e3a3f 100644
--- a/src/test/java/com/github/fppt/jedismock/comparisontests/transactions/TransactionOperationsTest.java
+++ b/src/test/java/com/github/fppt/jedismock/comparisontests/transactions/TransactionOperationsTest.java
@@ -6,9 +6,10 @@ import org.junit.jupiter.api.TestTemplate;
 import org.junit.jupiter.api.extension.ExtendWith;
 import redis.clients.jedis.Jedis;
 import redis.clients.jedis.Transaction;
-import redis.clients.jedis.exceptions.JedisDataException;
 
-import static org.junit.jupiter.api.Assertions.*;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
 
 @ExtendWith(ComparisonBase.class)
 public class TransactionOperationsTest {
diff --git a/src/test/java/com/github/fppt/jedismock/datastructures/RMZSetTest.java b/src/test/java/com/github/fppt/jedismock/datastructures/RMZSetTest.java
new file mode 100644
index 0000000..a7c59ee
--- /dev/null
+++ b/src/test/java/com/github/fppt/jedismock/datastructures/RMZSetTest.java
@@ -0,0 +1,54 @@
+package com.github.fppt.jedismock.datastructures;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class RMZSetTest {
+    @Test
+    void compareScore() {
+        assertTrue(new ZSetEntry(1, "a").compareTo(new ZSetEntry(2, "a")) < 0);
+    }
+
+    @Test
+    void compareLex() {
+        assertTrue(new ZSetEntry(1, "a").compareTo(new ZSetEntry(1, "b")) < 0);
+    }
+
+    @Test
+    void compareEquals() {
+        assertEquals(0, new ZSetEntry(1, "a").compareTo(new ZSetEntry(1, "a")));
+        assertEquals(new ZSetEntry(1, "a"), new ZSetEntry(1, "a"));
+    }
+
+    @Test
+    void compareMinScore(){
+        assertTrue(new ZSetEntry(ZSetEntry.MIN_SCORE, "a")
+                .compareTo(new ZSetEntry(2, "a")) < 0);
+    }
+
+    @Test
+    void compareMaxScore(){
+        assertTrue(new ZSetEntry(ZSetEntry.MAX_SCORE, "a")
+                .compareTo(new ZSetEntry(2, "a")) > 0);
+    }
+
+
+    @Test
+    void compareMinLex(){
+        assertTrue(new ZSetEntry(1, ZSetEntry.MIN_VALUE)
+                .compareTo(new ZSetEntry(1, "a")) < 0);
+    }
+
+    @Test
+    void compareMaxLex(){
+        assertTrue(new ZSetEntry(1, ZSetEntry.MAX_VALUE)
+                .compareTo(new ZSetEntry(1, "Z")) > 0);
+    }
+
+    @Test
+    void compareWithMaxLex(){
+        assertTrue(new ZSetEntry(1, "Z")
+                .compareTo(new ZSetEntry(1, ZSetEntry.MAX_VALUE)) < 0);
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/com/github/fppt/jedismock/server/RedisClientTest.java b/src/test/java/com/github/fppt/jedismock/server/RedisClientTest.java
index ccc11b5..59cb44b 100644
--- a/src/test/java/com/github/fppt/jedismock/server/RedisClientTest.java
+++ b/src/test/java/com/github/fppt/jedismock/server/RedisClientTest.java
@@ -24,7 +24,7 @@ class RedisClientTest {
     }
 
     @Test
-    void testClosedSocket() throws IOException {
+    void testClosedSocket() {
         Mockito.when(s.isClosed()).thenReturn(true);
         redisClient.run();
     }
diff --git a/src/test/java/com/github/fppt/jedismock/server/TestRedisOperationExecutor.java b/src/test/java/com/github/fppt/jedismock/server/TestRedisOperationExecutor.java
index d005435..fa2ffb7 100644
--- a/src/test/java/com/github/fppt/jedismock/server/TestRedisOperationExecutor.java
+++ b/src/test/java/com/github/fppt/jedismock/server/TestRedisOperationExecutor.java
@@ -2,11 +2,6 @@ package com.github.fppt.jedismock.server;
 
 import com.github.fppt.jedismock.commands.RedisCommandParser;
 import com.github.fppt.jedismock.exception.ParseErrorException;
-import com.github.fppt.jedismock.server.RedisClient;
-import com.github.fppt.jedismock.server.RedisOperationExecutor;
-import com.github.fppt.jedismock.server.Response;
-import com.github.fppt.jedismock.datastructures.Slice;
-import com.github.fppt.jedismock.server.ServiceOptions;
 import com.github.fppt.jedismock.storage.OperationExecutorState;
 import com.github.fppt.jedismock.storage.RedisBase;
 import org.junit.jupiter.api.BeforeEach;
@@ -17,7 +12,6 @@ import java.io.IOException;
 import java.net.Socket;
 import java.util.HashMap;
 import java.util.Map;
-import java.util.concurrent.locks.ReentrantLock;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertTrue;