Repository: fppt/jedis-mock
Pull request #7 (2018-12-07 07:54:19)

More mocks: hgetall, sinter, hmset, hmget


Patch:
diff --git a/pom.xml b/pom.xml
index 9160e1c..0860866 100644
--- a/pom.xml
+++ b/pom.xml
@@ -10,7 +10,7 @@
 
     <groupId>com.github.fppt</groupId>
     <artifactId>jedis-mock</artifactId>
-    <version>0.1.7</version>
+    <version>0.1.8-SNAPSHOT</version>
     <packaging>jar</packaging>
     <name>jedis-mock</name>
     <description>In memory Redis mock for testing</description>
diff --git a/src/main/java/com/github/fppt/jedismock/RedisBase.java b/src/main/java/com/github/fppt/jedismock/RedisBase.java
index d1f3945..8b29f36 100644
--- a/src/main/java/com/github/fppt/jedismock/RedisBase.java
+++ b/src/main/java/com/github/fppt/jedismock/RedisBase.java
@@ -34,6 +34,10 @@ public class RedisBase {
         return keyValueStorage.get(key);
     }
 
+    public Map<Slice, Slice> getFieldsAndValues(Slice hash){
+        return keyValueStorage.getFieldsAndValues(hash);
+    }
+
     public Slice getValue(Slice key1, Slice key2) {
         return keyValueStorage.get(key1, key2);
     }
diff --git a/src/main/java/com/github/fppt/jedismock/commands/RO_hegetall.java b/src/main/java/com/github/fppt/jedismock/commands/RO_hegetall.java
new file mode 100644
index 0000000..b5ed1b7
--- /dev/null
+++ b/src/main/java/com/github/fppt/jedismock/commands/RO_hegetall.java
@@ -0,0 +1,36 @@
+package com.github.fppt.jedismock.commands;
+
+import com.github.fppt.jedismock.RedisBase;
+import com.github.fppt.jedismock.Response;
+import com.github.fppt.jedismock.Slice;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+public class RO_hegetall extends AbstractRedisOperation {
+    public RO_hegetall(RedisBase base, List<Slice> params) {
+        super(base, params, 1, null, null);
+    }
+
+    @Override
+    Slice response() {
+        Slice hash = params().get(0);
+
+        Map<Slice, Slice> fieldAndValueMap = base().getFieldsAndValues(hash);
+        int arraySize = fieldAndValueMap.size() * 2;
+        Slice [] fieldAndValueList = new Slice[arraySize];
+
+        int currentIndex = arraySize - 1;
+        for (Map.Entry<Slice, Slice> entry: fieldAndValueMap.entrySet()){
+            fieldAndValueList[currentIndex] = Response.bulkString(entry.getValue());
+            currentIndex--;
+
+            fieldAndValueList[currentIndex] = Response.bulkString(entry.getKey());
+            currentIndex--;
+        }
+
+        List<Slice> values = Arrays.asList(fieldAndValueList);
+        return Response.array(values);
+    }
+}
diff --git a/src/main/java/com/github/fppt/jedismock/commands/RO_hmget.java b/src/main/java/com/github/fppt/jedismock/commands/RO_hmget.java
new file mode 100644
index 0000000..f8c8fdf
--- /dev/null
+++ b/src/main/java/com/github/fppt/jedismock/commands/RO_hmget.java
@@ -0,0 +1,33 @@
+package com.github.fppt.jedismock.commands;
+
+import com.github.fppt.jedismock.RedisBase;
+import com.github.fppt.jedismock.Response;
+import com.github.fppt.jedismock.Slice;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class RO_hmget extends AbstractRedisOperation {
+    public RO_hmget(RedisBase base, List<Slice> params) {
+        super(base, params, null, 2, null);
+    }
+
+    @Override
+    Slice response() {
+        ArrayList<Slice> result = new ArrayList<>();
+        Slice hash = params().get(0);
+
+        for(int i = 1; i < params().size(); i ++){
+            Slice field = params().get(i);
+            Slice value = base().getValue(hash, field);
+
+            if(value == null){
+                result.add(Response.NULL);
+            } else {
+                result.add(Response.bulkString(value));
+            }
+        }
+
+        return Response.array(result);
+    }
+}
diff --git a/src/main/java/com/github/fppt/jedismock/commands/RO_hmset.java b/src/main/java/com/github/fppt/jedismock/commands/RO_hmset.java
new file mode 100644
index 0000000..f517c26
--- /dev/null
+++ b/src/main/java/com/github/fppt/jedismock/commands/RO_hmset.java
@@ -0,0 +1,26 @@
+package com.github.fppt.jedismock.commands;
+
+import com.github.fppt.jedismock.RedisBase;
+import com.github.fppt.jedismock.Response;
+import com.github.fppt.jedismock.Slice;
+
+import java.util.List;
+
+public class RO_hmset extends AbstractRedisOperation {
+    public RO_hmset(RedisBase base, List<Slice> params) {
+        super(base, params, null, 1, null);
+    }
+
+    @Override
+    Slice response() {
+        Slice hash = params().get(0);
+
+        for(int i = 1; i < params().size(); i = i + 2){
+            Slice field = params().get(i);
+            Slice value = params().get(i+1);
+            base().putValue(hash, field, value, -1L);
+        }
+
+        return Response.OK;
+    }
+}
diff --git a/src/main/java/com/github/fppt/jedismock/commands/RO_sinter.java b/src/main/java/com/github/fppt/jedismock/commands/RO_sinter.java
new file mode 100644
index 0000000..177f957
--- /dev/null
+++ b/src/main/java/com/github/fppt/jedismock/commands/RO_sinter.java
@@ -0,0 +1,46 @@
+package com.github.fppt.jedismock.commands;
+
+import com.github.fppt.jedismock.RedisBase;
+import com.github.fppt.jedismock.Response;
+import com.github.fppt.jedismock.Slice;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Sets;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import static com.github.fppt.jedismock.Utils.deserializeObject;
+
+public class RO_sinter extends AbstractRedisOperation {
+    public RO_sinter(RedisBase base, List<Slice> params) {
+        super(base, params,  null, null, null);
+    }
+
+    @Override
+    Slice response() {
+        Slice key = params().get(0);
+        Set<Slice> resultSoFar = getSet(key);
+
+        for(int i = 1; i < params().size(); i++){
+            Set<Slice> set = getSet(params().get(i));
+            resultSoFar = Sets.intersection(resultSoFar, set);
+        }
+
+        ImmutableList.Builder<Slice> builder = new ImmutableList.Builder<Slice>();
+        resultSoFar.forEach(element -> builder.add(Response.bulkString(element)));
+
+        return Response.array(builder.build());
+    }
+
+    private Set<Slice> getSet(Slice key){
+        Set<Slice> set;
+        Slice data = base().getValue(key);
+        if (data != null) {
+            set = new HashSet<>(deserializeObject(data));
+        } else {
+            set = new HashSet<>();
+        }
+        return set;
+    }
+}
diff --git a/src/main/java/com/github/fppt/jedismock/commands/RedisOperationExecutor.java b/src/main/java/com/github/fppt/jedismock/commands/RedisOperationExecutor.java
index e5b56cd..5f5b5c1 100644
--- a/src/main/java/com/github/fppt/jedismock/commands/RedisOperationExecutor.java
+++ b/src/main/java/com/github/fppt/jedismock/commands/RedisOperationExecutor.java
@@ -136,6 +136,14 @@ public class RedisOperationExecutor {
                 return new RO_hset(base, params);
             case "hdel":
                 return new RO_hdel(base, params);
+            case "hgetall":
+                return new RO_hegetall(base, params);
+            case "sinter":
+                return new RO_sinter(base, params);
+            case "hmget":
+                return new RO_hmget(base, params);
+            case "hmset":
+                return new RO_hmset(base, params);
             default:
                 throw new UnsupportedOperationException(String.format("Unsupported operation '%s'", name));
         }
diff --git a/src/main/java/com/github/fppt/jedismock/storage/ExpiringKeyValueStorage.java b/src/main/java/com/github/fppt/jedismock/storage/ExpiringKeyValueStorage.java
index 2cbd721..44de6d6 100644
--- a/src/main/java/com/github/fppt/jedismock/storage/ExpiringKeyValueStorage.java
+++ b/src/main/java/com/github/fppt/jedismock/storage/ExpiringKeyValueStorage.java
@@ -6,6 +6,8 @@ import com.google.common.base.Preconditions;
 import com.google.common.collect.HashBasedTable;
 import com.google.common.collect.Table;
 
+import java.util.Map;
+
 
 /**
  * Used to represent an expiring storage layer.
@@ -39,6 +41,10 @@ public abstract class ExpiringKeyValueStorage {
         return get(key, Slice.reserved());
     }
 
+    public Map<Slice, Slice> getFieldsAndValues(Slice hash){
+        return values().row(hash);
+    }
+
     public Slice get(Slice key1, Slice key2){
         Preconditions.checkNotNull(key1);
         Preconditions.checkNotNull(key2);


Test patch:
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 3937df4..7598aaa 100644
--- a/src/test/java/com/github/fppt/jedismock/comparisontests/SimpleOperationsTest.java
+++ b/src/test/java/com/github/fppt/jedismock/comparisontests/SimpleOperationsTest.java
@@ -8,10 +8,7 @@ import redis.clients.jedis.Client;
 import redis.clients.jedis.Jedis;
 import redis.clients.jedis.exceptions.JedisConnectionException;
 
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
+import java.util.*;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
@@ -25,6 +22,16 @@ import static org.junit.Assert.assertNull;
 @RunWith(Theories.class)
 public class SimpleOperationsTest extends ComparisonBase {
 
+    private String HASH = "hash";
+    private String FIELD_1 = "field1";
+    private String VALUE_1 = "value1";
+    private String FIELD_2 = "field2";
+    private String VALUE_2 = "value2";
+    private String FIELD_3 = "field3";
+    private String VALUE_3 = "value3";
+    private String FIELD_4 = "field4";
+    private String FIELD_5 = "field5";
+
     @Theory
     public void whenSettingKeyAndRetrievingIt_CorrectResultIsReturned(Jedis jedis) {
         String key = "key";
@@ -214,10 +221,8 @@ public class SimpleOperationsTest extends ComparisonBase {
 
     @Theory
     public void whenHSettingOnTheSameKeys_EnsureReturnTypeIs1WhenKeysAreNew(Jedis jedis){
-        String field = "my-field";
-        String hash = "my-hash";
-        assertEquals(new Long(1L), jedis.hset(hash, field, "some value"));
-        assertEquals(new Long(0L), jedis.hset(hash, field, "some other value"));
+        assertEquals(new Long(1L), jedis.hset(HASH, FIELD_1, VALUE_1));
+        assertEquals(new Long(0L), jedis.hset(HASH, FIELD_1, VALUE_1));
     }
 
     @Theory
@@ -243,4 +248,85 @@ public class SimpleOperationsTest extends ComparisonBase {
         assertEquals(new Long(1L), jedis.hdel(hash, field));
         assertNull(jedis.hget(hash, field));
     }
+
+    @Theory
+    public void whenHGetAll_EnsureAllKeysAndValuesReturned(Jedis jedis){
+        jedis.hset(HASH, FIELD_1, VALUE_1);
+        jedis.hset(HASH, FIELD_2, VALUE_2);
+
+        //Check first returns
+        Map<String, String> result = jedis.hgetAll(HASH);
+        assertEquals(2, result.size());
+        assertEquals(VALUE_1, result.get(FIELD_1));
+        assertEquals(VALUE_2, result.get(FIELD_2));
+
+        jedis.hset(HASH, FIELD_3, VALUE_3);
+
+        //Check first returns
+        result = jedis.hgetAll(HASH);
+        assertEquals(3, result.size());
+        assertEquals(VALUE_1, result.get(FIELD_1));
+        assertEquals(VALUE_2, result.get(FIELD_2));
+        assertEquals(VALUE_3, result.get(FIELD_3));
+
+        //Check empty case
+        result = jedis.hgetAll("rubbish");
+        assertEquals(0, result.size());
+    }
+
+    @Theory
+    public void whenUsingHsinter_EnsureSetIntersectionIsReturned(Jedis jedis){
+        String key1 = "my-set-key-1";
+        Set<String> mySet1 = new HashSet<>(Arrays.asList("a", "b", "c", "d"));
+        String key2 = "my-set-key-2";
+        Set<String> mySet2 = new HashSet<>(Arrays.asList("b", "d", "e", "f"));
+        String key3 = "my-set-key-3";
+        Set<String> mySet3 = new HashSet<>(Arrays.asList("b", "e", "f"));
+
+        Set<String> expectedIntersection1 = new HashSet<>(Arrays.asList("b", "d"));
+        Set<String> expectedIntersection2 = new HashSet<>(Collections.singletonList("b"));
+
+        //Add everything from the sets
+        mySet1.forEach(value -> jedis.sadd(key1, value));
+        mySet2.forEach(value -> jedis.sadd(key2, value));
+        mySet3.forEach(value -> jedis.sadd(key3, value));
+
+        Set<String> intersection = jedis.sinter(key1, key2);
+        assertEquals(expectedIntersection1, intersection);
+
+        intersection = jedis.sinter(key1, key2, key3);
+        assertEquals(expectedIntersection2, intersection);
+    }
+
+    @Theory
+    public void whenUsingHMget_EnsureAllValuesReturnedForEachField(Jedis jedis){
+        jedis.hset(HASH, FIELD_1, VALUE_1);
+        jedis.hset(HASH, FIELD_2, VALUE_2);
+        jedis.hset(HASH, FIELD_3, VALUE_3);
+
+        List<String> result = jedis.hmget(HASH, FIELD_1, FIELD_2, FIELD_5, FIELD_3, FIELD_4);
+
+        assertEquals(5, result.size());
+        assertEquals(VALUE_1, result.get(0));
+        assertEquals(VALUE_2, result.get(1));
+        assertNull(result.get(2));
+        assertEquals(VALUE_3, result.get(3));
+        assertNull(result.get(4));
+    }
+
+    @Theory
+    public void whenUsingHMset_EnsureAllValuesAreSetForEachField(Jedis jedis){
+        Map<String, String> map = new HashMap<>();
+        map.put(FIELD_1, VALUE_1);
+        map.put(FIELD_2, VALUE_2);
+
+        jedis.hmset(HASH, map);
+        assertEquals(VALUE_1, jedis.hget(HASH, FIELD_1));
+        assertEquals(VALUE_2, jedis.hget(HASH, FIELD_2));
+
+        map.put(FIELD_2, VALUE_1);
+        jedis.hmset(HASH, map);
+        assertEquals(VALUE_1, jedis.hget(HASH, FIELD_1));
+        assertEquals(VALUE_1, jedis.hget(HASH, FIELD_2));
+    }
 }