This is an automated email from the ASF dual-hosted git repository.
jensdeppe pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/geode.git
The following commit(s) were added to refs/heads/develop by this push:
new bba1935 GEODE-8865: Create additional dunit and integration tests for
Redis HMGET (#5945)
bba1935 is described below
commit bba1935d98328c7b767adebded577f433e49ba87
Author: Jens Deppe <[email protected]>
AuthorDate: Sat Jan 23 13:20:44 2021 -0800
GEODE-8865: Create additional dunit and integration tests for Redis HMGET
(#5945)
---
.../tools_modules/redis_api_for_geode.html.md.erb | 4 +-
geode-redis/README.md | 92 ++++++++---------
.../internal/executor/hash/HMgetDUnitTest.java | 112 +++++++++++++++++++++
.../hash/AbstractHashesIntegrationTest.java | 59 +++++++++--
.../geode/redis/internal/RedisCommandType.java | 2 +-
.../redis/internal/SupportedCommandsJUnitTest.java | 2 +-
6 files changed, 215 insertions(+), 56 deletions(-)
diff --git a/geode-docs/tools_modules/redis_api_for_geode.html.md.erb
b/geode-docs/tools_modules/redis_api_for_geode.html.md.erb
index 2887109..c4327a4 100644
--- a/geode-docs/tools_modules/redis_api_for_geode.html.md.erb
+++ b/geode-docs/tools_modules/redis_api_for_geode.html.md.erb
@@ -68,7 +68,7 @@ The Redis API for <%=vars.product_name%> currently supports
the following comman
**Note**: These commands are supported for Redis 5.
- **Connection**: AUTH, PING, QUIT
-- **Hashes**: HGETALL, HMSET, HSET, HVALS
+- **Hashes**: HGETALL, HMGET, HMSET, HSET, HVALS
- **Keys**: DEL, EXISTS, EXPIRE, EXPIREAT, KEYS, PERSIST, PEXPIRE,
PEXPIREAT, PTTL, RENAME, TTL,
TYPE
- **Publish/Subscribe**: PUBLISH, PSUBSCRIBE, PUNSUBSCRIBE, SUBSCRIBE,
UNSUBSCRIBE
@@ -80,7 +80,7 @@ commands are available to use, but have not been fully
tested. There is no guara
exactly as expected.
- **Connection**: ECHO, SELECT
-- **Hashes**: HDEL, HEXISTS, HGET, HINCRBY, HINCRBYFLOAT, HKEYS, HLEN,
HMGET, HSCAN, HSETNX,
+- **Hashes**: HDEL, HEXISTS, HGET, HINCRBY, HINCRBYFLOAT, HKEYS, HLEN,
HSCAN, HSETNX,
HSTRLEN
- **Keys**: SCAN, UNLINK
- **Server**: DBSIZE, FLUSHALL (no async option), FLUSHDB (no async option),
INFO, SHUTDOWN,
diff --git a/geode-redis/README.md b/geode-redis/README.md
index 72d4a36..f690001 100644
--- a/geode-redis/README.md
+++ b/geode-redis/README.md
@@ -165,52 +165,52 @@ start server \
| EXPIREAT | DECRBY
| ACL LIST |
| GET | ECHO
| ACL LOAD |
| HGETALL | FLUSHALL
| ACL LOG |
-| HMSET | FLUSHDB
| ACL SAVE |
-| HSET | GETBIT
| ACL SETUSER |
-| HVALS | GETRANGE
| ACL USERS |
-| KEYS | GETSET
| ACL WHOAMI |
-| PERSIST | HDEL
| BGREWRITEAOF |
-| PEXPIRE | HEXISTS
| BGSAVE |
-| PEXPIREAT | HGET
| BITFIELD |
-| PING | HINCRBY
| BLPOP |
-| PSUBSCRIBE | HINCRBYFLOAT
| BRPOP |
-| PTTL | HKEYS
| BRPOPLPUSH |
-| PUBLISH | HLEN
| BZPOPMAX |
-| PUNSUBSCRIBE | HMGET
| BZPOPMIN |
-| QUIT | HSCAN
| CLIENT CACHING |
-| RENAME | HSETNX
| CLIENT GETNAME |
-| SADD | HSTRLEN
| CLIENT GETREDIR |
-| SET | INCR
| CLIENT ID |
-| SMEMBERS | INCRBY
| CLIENT KILL |
-| SREM | INCRBYFLOAT
| CLIENT LIST |
-| SUBSCRIBE | INFO
| CLIENT PAUSE |
-| TTL | MGET
| CLIENT REPLY |
-| TYPE | MSET
| CLIENT SETNAME |
-| UNSUBSCRIBE | MSETNX
| CLIENT TRACKING |
-| | PSETEX
| CLIENT UNBLOCK |
-| | SCAN
| CLUSTER ADDSLOTS |
-| | SCARD
| CLUSTER BUMPEPOCH |
-| | SDIFF
| CLUSTER COUNT-FAILURE-REPORTS |
-| | SDIFFSTORE
| CLUSTER COUNTKEYSINSLOT |
-| | SELECT
| CLUSTER DELSLOTS |
-| | SETBIT
| CLUSTER FAILOVER |
-| | SETEX
| CLUSTER FLUSHSLOTS |
-| | SETNX
| CLUSTER FORGET |
-| | SETRANGE
| CLUSTER GETKEYSINSLOT |
-| | SHUTDOWN
| CLUSTER INFO |
-| | SINTER
| CLUSTER KEYSLOT |
-| | SINTERSTORE
| CLUSTER MEET |
-| | SISMEMBER
| CLUSTER MYID |
-| | SLOWLOG
| CLUSTER NODES |
-| | SMOVE
| CLUSTER REPLICAS |
-| | SPOP
| CLUSTER REPLICATE |
-| | SRANDMEMBER
| CLUSTER RESET |
-| | SSCAN
| CLUSTER SAVECONFIG |
-| | STRLEN
| CLUSTER SET-CONFIG-EPOCH |
-| | SUNION
| CLUSTER SETSLOT |
-| | SUNIONSTORE
| CLUSTER SLAVES |
-| | TIME
| CLUSTER SLOTS |
-| | UNLINK [1]
| COMMAND |
+| HMGET | FLUSHDB
| ACL SAVE |
+| HMSET | GETBIT
| ACL SETUSER |
+| HSET | GETRANGE
| ACL USERS |
+| HVALS | GETSET
| ACL WHOAMI |
+| KEYS | HDEL
| BGREWRITEAOF |
+| PERSIST | HEXISTS
| BGSAVE |
+| PEXPIRE | HGET
| BITFIELD |
+| PEXPIREAT | HINCRBY
| BLPOP |
+| PING | HINCRBYFLOAT
| BRPOP |
+| PSUBSCRIBE | HKEYS
| BRPOPLPUSH |
+| PTTL | HLEN
| BZPOPMAX |
+| PUBLISH | HSCAN
| BZPOPMIN |
+| PUNSUBSCRIBE | HSETNX
| CLIENT CACHING |
+| QUIT | HSTRLEN
| CLIENT GETNAME |
+| RENAME | INCR
| CLIENT GETREDIR |
+| SADD | INCRBY
| CLIENT ID |
+| SET | INCRBYFLOAT
| CLIENT KILL |
+| SMEMBERS | INFO
| CLIENT LIST |
+| SREM | MGET
| CLIENT PAUSE |
+| SUBSCRIBE | MSET
| CLIENT REPLY |
+| TTL | MSETNX
| CLIENT SETNAME |
+| TYPE | PSETEX
| CLIENT TRACKING |
+| UNSUBSCRIBE | SCAN
| CLIENT UNBLOCK |
+| | SCARD
| CLUSTER ADDSLOTS |
+| | SDIFF
| CLUSTER BUMPEPOCH |
+| | SDIFFSTORE
| CLUSTER COUNT-FAILURE-REPORTS |
+| | SELECT
| CLUSTER COUNTKEYSINSLOT |
+| | SETBIT
| CLUSTER DELSLOTS |
+| | SETEX
| CLUSTER FAILOVER |
+| | SETNX
| CLUSTER FLUSHSLOTS |
+| | SETRANGE
| CLUSTER FORGET |
+| | SHUTDOWN
| CLUSTER GETKEYSINSLOT |
+| | SINTER
| CLUSTER INFO |
+| | SINTERSTORE
| CLUSTER KEYSLOT |
+| | SISMEMBER
| CLUSTER MEET |
+| | SLOWLOG
| CLUSTER MYID |
+| | SMOVE
| CLUSTER NODES |
+| | SPOP
| CLUSTER REPLICAS |
+| | SRANDMEMBER
| CLUSTER REPLICATE |
+| | SSCAN
| CLUSTER RESET |
+| | STRLEN
| CLUSTER SAVECONFIG |
+| | SUNION
| CLUSTER SET-CONFIG-EPOCH |
+| | SUNIONSTORE
| CLUSTER SETSLOT |
+| | TIME
| CLUSTER SLAVES |
+| | UNLINK [1]
| CLUSTER SLOTS |
+| |
| COMMAND |
| |
| COMMAND COUNT |
| |
| COMMAND GETKEYS |
| |
| COMMAND INFO |
diff --git
a/geode-redis/src/distributedTest/java/org/apache/geode/redis/internal/executor/hash/HMgetDUnitTest.java
b/geode-redis/src/distributedTest/java/org/apache/geode/redis/internal/executor/hash/HMgetDUnitTest.java
new file mode 100644
index 0000000..c2d7422
--- /dev/null
+++
b/geode-redis/src/distributedTest/java/org/apache/geode/redis/internal/executor/hash/HMgetDUnitTest.java
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
contributor license
+ * agreements. See the NOTICE file distributed with this work for additional
information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache
License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express
+ * or implied. See the License for the specific language governing permissions
and limitations under
+ * the License.
+ */
+
+package org.apache.geode.redis.internal.executor.hash;
+
+import static
org.apache.geode.distributed.ConfigurationProperties.MAX_WAIT_TIME_RECONNECT;
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Test;
+import redis.clients.jedis.Jedis;
+
+import org.apache.geode.redis.ConcurrentLoopingThreads;
+import org.apache.geode.test.awaitility.GeodeAwaitility;
+import org.apache.geode.test.dunit.rules.MemberVM;
+import org.apache.geode.test.dunit.rules.RedisClusterStartupRule;
+
+public class HMgetDUnitTest {
+
+ @ClassRule
+ public static RedisClusterStartupRule clusterStartUp = new
RedisClusterStartupRule(4);
+
+ private static final String LOCAL_HOST = "127.0.0.1";
+ private static final int HASH_SIZE = 1000;
+ private static final int JEDIS_TIMEOUT =
+ Math.toIntExact(GeodeAwaitility.getTimeout().toMillis());
+ private static Jedis jedis1;
+ private static Jedis jedis2;
+ private static Jedis jedis3;
+
+ private static Properties locatorProperties;
+
+ private static MemberVM locator;
+ private static MemberVM server1;
+ private static MemberVM server2;
+
+ private static int redisServerPort1;
+ private static int redisServerPort2;
+
+ @BeforeClass
+ public static void classSetup() {
+ locatorProperties = new Properties();
+ locatorProperties.setProperty(MAX_WAIT_TIME_RECONNECT, "15000");
+
+ locator = clusterStartUp.startLocatorVM(0, locatorProperties);
+ server1 = clusterStartUp.startRedisVM(1, locator.getPort());
+ server2 = clusterStartUp.startRedisVM(2, locator.getPort());
+
+ redisServerPort1 = clusterStartUp.getRedisPort(1);
+ redisServerPort2 = clusterStartUp.getRedisPort(2);
+
+ jedis1 = new Jedis(LOCAL_HOST, redisServerPort1, JEDIS_TIMEOUT);
+ jedis2 = new Jedis(LOCAL_HOST, redisServerPort2, JEDIS_TIMEOUT);
+ jedis3 = new Jedis(LOCAL_HOST, redisServerPort1, JEDIS_TIMEOUT);
+ }
+
+ @Before
+ public void testSetup() {
+ jedis1.flushAll();
+ }
+
+ @AfterClass
+ public static void tearDown() {
+ jedis1.disconnect();
+ jedis2.disconnect();
+
+ server1.stop();
+ server2.stop();
+ }
+
+ @Test
+ public void testConcurrentHMget_whileUpdatingValues() {
+ String key = "key";
+
+ Map<String, String> testMap = makeHashMap(HASH_SIZE, "field-", "value-");
+
+ jedis1.hset(key, testMap);
+
+ new ConcurrentLoopingThreads(HASH_SIZE,
+ (i) -> jedis1.hset(key, "field-" + i, "value-" + i),
+ (i) -> assertThat(jedis2.hmget(key, "field-" + i)).isNotNull(),
+ (i) -> assertThat(jedis3.hmget(key, "field-" + i)).isNotNull());
+ }
+
+ private Map<String, String> makeHashMap(int hashSize, String baseFieldName,
+ String baseValueName) {
+ Map<String, String> map = new HashMap<>();
+ for (int i = 0; i < hashSize; i++) {
+ map.put(baseFieldName + i, baseValueName + i);
+ }
+ return map;
+ }
+}
diff --git
a/geode-redis/src/integrationTest/java/org/apache/geode/redis/internal/executor/hash/AbstractHashesIntegrationTest.java
b/geode-redis/src/integrationTest/java/org/apache/geode/redis/internal/executor/hash/AbstractHashesIntegrationTest.java
index 9454e1b..f0c3f2c 100755
---
a/geode-redis/src/integrationTest/java/org/apache/geode/redis/internal/executor/hash/AbstractHashesIntegrationTest.java
+++
b/geode-redis/src/integrationTest/java/org/apache/geode/redis/internal/executor/hash/AbstractHashesIntegrationTest.java
@@ -115,6 +115,22 @@ public abstract class AbstractHashesIntegrationTest
implements RedisPortSupplier
}
@Test
+ public void testHMSetErrorMessage_givenIncorrectDataType() {
+ Map<String, String> animalMap = new HashMap<>();
+ animalMap.put("chicken", "eggs");
+
+ jedis.set("farm", "chicken");
+ assertThatThrownBy(() -> jedis.hmset("farm", animalMap))
+ .isInstanceOf(JedisDataException.class)
+ .hasMessageContaining("WRONGTYPE Operation against a key holding the
wrong kind of value");
+
+ jedis.sadd("zoo", "elephant");
+ assertThatThrownBy(() -> jedis.hmset("zoo", animalMap))
+ .isInstanceOf(JedisDataException.class)
+ .hasMessageContaining("WRONGTYPE Operation against a key holding the
wrong kind of value");
+ }
+
+ @Test
public void testHSet() {
String key = "key";
Map<String, String> hash = new HashMap<String, String>();
@@ -136,27 +152,26 @@ public abstract class AbstractHashesIntegrationTest
implements RedisPortSupplier
}
@Test
- public void testHMGetHDelHGetAllHVals() {
+ public void testHMGet_HDel_HGetAll_HVals() {
String key = "key";
- Map<String, String> hash = new HashMap<String, String>();
+ Map<String, String> hash = new HashMap<>();
for (int i = 0; i < 10; i++) {
hash.put("field_" + i, "member_" + i);
}
jedis.hmset(key, hash);
+
Set<String> keys = hash.keySet();
String[] keyArray = keys.toArray(new String[keys.size()]);
List<String> retList = jedis.hmget(key, keyArray);
- for (int i = 0; i < keys.size(); i++) {
- assertThat(hash.get(keyArray[i])).isEqualTo(retList.get(i));
- }
+ assertThat(retList).containsExactlyInAnyOrderElementsOf(hash.values());
Map<String, String> retMap = jedis.hgetAll(key);
assertThat(retMap).containsExactlyInAnyOrderEntriesOf(hash);
List<String> retVals = jedis.hvals(key);
- Set<String> retSet = new HashSet<String>(retVals);
+ Set<String> retSet = new HashSet<>(retVals);
assertThat(retSet.containsAll(hash.values())).isTrue();
@@ -165,6 +180,38 @@ public abstract class AbstractHashesIntegrationTest
implements RedisPortSupplier
}
@Test
+ public void testHMGet_returnNull_forUnknownFields() {
+ String key = "key";
+ jedis.hset(key, "rooster", "crows");
+ jedis.hset(key, "duck", "quacks");
+
+ List<String> result =
+ jedis.hmget(key, "unknown-1", "rooster", "unknown-2", "duck",
"unknown-3");
+ assertThat(result).containsExactly(null, "crows", null, "quacks", null);
+ }
+
+ @Test
+ public void testHMGet_givenWrongNumberOfArguments() {
+ assertThatThrownBy(() -> jedis.sendCommand(Protocol.Command.HMGET))
+ .hasMessage("ERR wrong number of arguments for 'hmget' command");
+ assertThatThrownBy(() -> jedis.sendCommand(Protocol.Command.HMGET, "1"))
+ .hasMessage("ERR wrong number of arguments for 'hmget' command");
+ }
+
+ @Test
+ public void testHMGetErrorMessage_givenIncorrectDataType() {
+ jedis.set("farm", "chicken");
+ assertThatThrownBy(() -> jedis.hmget("farm", "chicken"))
+ .isInstanceOf(JedisDataException.class)
+ .hasMessage("WRONGTYPE " + RedisConstants.ERROR_WRONG_TYPE);
+
+ jedis.sadd("zoo", "elephant");
+ assertThatThrownBy(() -> jedis.hmget("zoo", "chicken"))
+ .isInstanceOf(JedisDataException.class)
+ .hasMessage("WRONGTYPE " + RedisConstants.ERROR_WRONG_TYPE);
+ }
+
+ @Test
public void testHDelErrorMessage_givenIncorrectDataType() {
jedis.set("farm", "chicken");
assertThatThrownBy(() -> {
diff --git
a/geode-redis/src/main/java/org/apache/geode/redis/internal/RedisCommandType.java
b/geode-redis/src/main/java/org/apache/geode/redis/internal/RedisCommandType.java
index 4efdb0c..97d90a1 100755
---
a/geode-redis/src/main/java/org/apache/geode/redis/internal/RedisCommandType.java
+++
b/geode-redis/src/main/java/org/apache/geode/redis/internal/RedisCommandType.java
@@ -156,6 +156,7 @@ public enum RedisCommandType {
/************* Hashes *****************/
HGETALL(new HGetAllExecutor(), SUPPORTED, new ExactParameterRequirements(2)),
+ HMGET(new HMGetExecutor(), SUPPORTED, new MinimumParameterRequirements(3)),
HMSET(new HMSetExecutor(), SUPPORTED,
new MinimumParameterRequirements(4).and(new
EvenParameterRequirements())),
HSET(new HSetExecutor(), SUPPORTED,
@@ -238,7 +239,6 @@ public enum RedisCommandType {
HINCRBYFLOAT(new HIncrByFloatExecutor(), UNSUPPORTED, new
ExactParameterRequirements(4)),
HKEYS(new HKeysExecutor(), UNSUPPORTED, new ExactParameterRequirements(2)),
HLEN(new HLenExecutor(), UNSUPPORTED, new ExactParameterRequirements(2)),
- HMGET(new HMGetExecutor(), UNSUPPORTED, new MinimumParameterRequirements(3)),
HSCAN(new HScanExecutor(), UNSUPPORTED, new MinimumParameterRequirements(3),
new OddParameterRequirements(ERROR_SYNTAX)),
HSETNX(new HSetNXExecutor(), UNSUPPORTED, new ExactParameterRequirements(4)),
diff --git
a/geode-redis/src/test/java/org/apache/geode/redis/internal/SupportedCommandsJUnitTest.java
b/geode-redis/src/test/java/org/apache/geode/redis/internal/SupportedCommandsJUnitTest.java
index e0b05be..52deaff 100644
---
a/geode-redis/src/test/java/org/apache/geode/redis/internal/SupportedCommandsJUnitTest.java
+++
b/geode-redis/src/test/java/org/apache/geode/redis/internal/SupportedCommandsJUnitTest.java
@@ -38,6 +38,7 @@ public class SupportedCommandsJUnitTest {
"EXPIREAT",
"GET",
"HGETALL",
+ "HMGET",
"HMSET",
"HSET",
"HVALS",
@@ -82,7 +83,6 @@ public class SupportedCommandsJUnitTest {
"HINCRBYFLOAT",
"HKEYS",
"HLEN",
- "HMGET",
"HSCAN",
"HSETNX",
"HSTRLEN",