You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@geode.apache.org by js...@apache.org on 2017/10/06 22:52:10 UTC

[geode] branch develop updated: GEODE-3539: Add tests for 'remove' command

This is an automated email from the ASF dual-hosted git repository.

jstewart 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 5212362  GEODE-3539: Add tests for 'remove' command
5212362 is described below

commit 5212362cee996b20e4552116ac303adec6aa4479
Author: Jared Stewart <js...@pivotal.io>
AuthorDate: Wed Oct 4 16:23:27 2017 -0700

    GEODE-3539: Add tests for 'remove' command
    
    This closes #872.
---
 .../internal/cli/commands/RemoveCommand.java       |  36 ++--
 .../management/internal/cli/i18n/CliStrings.java   |   3 -
 .../cli/commands/GemfireDataCommandsDUnitTest.java | 205 --------------------
 .../cli/commands/RemoveCommandDUnitTest.java       | 210 +++++++++++++++++++++
 .../cli/commands/RemoveCommandJsonDUnitTest.java   | 171 +++++++++++++++++
 .../test/junit/rules/GfshShellConnectionRule.java  |  11 ++
 6 files changed, 409 insertions(+), 227 deletions(-)

diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/RemoveCommand.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/RemoveCommand.java
index 9bba7b7..e48f1b5 100644
--- a/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/RemoveCommand.java
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/RemoveCommand.java
@@ -18,6 +18,7 @@ package org.apache.geode.management.internal.cli.commands;
 import static org.apache.geode.management.internal.cli.commands.DataCommandsUtils.callFunctionForRegion;
 import static org.apache.geode.management.internal.cli.commands.DataCommandsUtils.getRegionAssociatedMembers;
 import static org.apache.geode.management.internal.cli.commands.DataCommandsUtils.makePresentationResult;
+import static org.apache.geode.management.internal.cli.result.ResultBuilder.createUserErrorResult;
 
 import java.util.Set;
 
@@ -40,6 +41,8 @@ import org.apache.geode.security.ResourcePermission.Operation;
 import org.apache.geode.security.ResourcePermission.Resource;
 
 public class RemoveCommand implements GfshCommand {
+  public static final String REGION_NOT_FOUND = "Region <%s> not found in any of the members";
+
   @CliMetaData(relatedTopic = {CliStrings.TOPIC_GEODE_DATA, CliStrings.TOPIC_GEODE_REGION})
   @CliCommand(value = {CliStrings.REMOVE}, help = CliStrings.REMOVE__HELP)
   public Result remove(
@@ -53,16 +56,13 @@ public class RemoveCommand implements GfshCommand {
       @CliOption(key = {CliStrings.REMOVE__KEYCLASS},
           help = CliStrings.REMOVE__KEYCLASS__HELP) String keyClass) {
     InternalCache cache = getCache();
-    DataCommandResult dataResult;
 
     if (StringUtils.isEmpty(regionPath)) {
-      return makePresentationResult(DataCommandResult.createRemoveResult(key, null, null,
-          CliStrings.REMOVE__MSG__REGIONNAME_EMPTY, false));
+      return createUserErrorResult(CliStrings.REMOVE__MSG__REGIONNAME_EMPTY);
     }
 
     if (!removeAllKeys && (key == null)) {
-      return makePresentationResult(DataCommandResult.createRemoveResult(null, null, null,
-          CliStrings.REMOVE__MSG__KEY_EMPTY, false));
+      return createUserErrorResult(CliStrings.REMOVE__MSG__KEY_EMPTY);
     }
 
     if (removeAllKeys) {
@@ -71,30 +71,28 @@ public class RemoveCommand implements GfshCommand {
       cache.getSecurityService().authorize(Resource.DATA, Operation.WRITE, regionPath, key);
     }
 
-    @SuppressWarnings("rawtypes")
     Region region = cache.getRegion(regionPath);
     DataCommandFunction removefn = new DataCommandFunction();
+    DataCommandResult dataResult;
     if (region == null) {
       Set<DistributedMember> memberList = getRegionAssociatedMembers(regionPath, getCache(), false);
-      if (CollectionUtils.isNotEmpty(memberList)) {
-        DataCommandRequest request = new DataCommandRequest();
-        request.setCommand(CliStrings.REMOVE);
-        request.setKey(key);
-        request.setKeyClass(keyClass);
-        request.setRemoveAllKeys(removeAllKeys ? "ALL" : null);
-        request.setRegionName(regionPath);
-        dataResult = callFunctionForRegion(request, removefn, memberList);
-      } else {
-        dataResult = DataCommandResult.createRemoveInfoResult(key, null, null,
-            CliStrings.format(CliStrings.REMOVE__MSG__REGION_NOT_FOUND_ON_ALL_MEMBERS, regionPath),
-            false);
+
+      if (CollectionUtils.isEmpty(memberList)) {
+        return createUserErrorResult(String.format(REGION_NOT_FOUND, regionPath));
       }
 
+      DataCommandRequest request = new DataCommandRequest();
+      request.setCommand(CliStrings.REMOVE);
+      request.setKey(key);
+      request.setKeyClass(keyClass);
+      request.setRemoveAllKeys(removeAllKeys ? "ALL" : null);
+      request.setRegionName(regionPath);
+      dataResult = callFunctionForRegion(request, removefn, memberList);
     } else {
       dataResult = removefn.remove(key, keyClass, regionPath, removeAllKeys ? "ALL" : null);
     }
-    dataResult.setKeyClass(keyClass);
 
+    dataResult.setKeyClass(keyClass);
     return makePresentationResult(dataResult);
   }
 }
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/i18n/CliStrings.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/i18n/CliStrings.java
index 89eb4f3..07d1a19 100644
--- a/geode-core/src/main/java/org/apache/geode/management/internal/cli/i18n/CliStrings.java
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/i18n/CliStrings.java
@@ -1883,9 +1883,6 @@ public class CliStrings {
       "Clears the region by removing all entries. Partitioned region does not support remove-all";
   public static final String REMOVE__MSG__REGIONNAME_EMPTY = "Region name is either empty or Null";
   public static final String REMOVE__MSG__KEY_EMPTY = "Key is Null";
-  public static final String REMOVE__MSG__VALUE_EMPTY = "Value is either empty or Null";
-  public static final String REMOVE__MSG__REGION_NOT_FOUND_ON_ALL_MEMBERS =
-      "Region <{0}> not found in any of the members";
   public static final String REMOVE__MSG__REGION_NOT_FOUND = "Region <{0}> Not Found";
   public static final String REMOVE__MSG__KEY_NOT_FOUND_REGION = "Key is not present in the region";
   public static final String REMOVE__MSG__CLEARED_ALL_CLEARS = "Cleared all keys in the region";
diff --git a/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/GemfireDataCommandsDUnitTest.java b/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/GemfireDataCommandsDUnitTest.java
index f2a7432..fe7b179 100644
--- a/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/GemfireDataCommandsDUnitTest.java
+++ b/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/GemfireDataCommandsDUnitTest.java
@@ -753,87 +753,6 @@ public class GemfireDataCommandsDUnitTest extends CliCommandTestBase {
     }
   }
 
-  /**
-   * Test remove an a region with a zero length string GEODE-2269
-   * 
-   * @author Gregory Green
-   */
-  @Test
-  public void testRemoveEmptyKey() {
-    // set the test region/data
-    setupForGetPutRemoveLocateEntry("testRemoveEmptyKey");
-
-    // Test across two virtual machines
-    final VM vm1 = Host.getHost(0).getVM(1);
-    final VM vm2 = Host.getHost(0).getVM(2);
-
-    // Initial empty key/value
-    String key = "";
-    String value = "";
-
-    // Get empty key in region
-    SerializableRunnable checkPutKeys = new SerializableRunnable() {
-      @Override
-      public void run() {
-        Cache cache = getCache();
-        Region<Object, Object> region = cache.getRegion(DATA_REGION_NAME_PATH);
-        assertNotNull(region);
-        region.put(key, value);
-      }
-    };
-
-    vm1.invoke(checkPutKeys);
-    vm2.invoke(checkPutKeys);
-
-    // Check if keys were put correctly
-    SerializableRunnable checkPutKeysExists = new SerializableRunnable() {
-      @Override
-      public void run() {
-        Cache cache = getCache();
-        Region<Object, Object> region = cache.getRegion(DATA_REGION_NAME_PATH);
-        assertNotNull(region);
-        assertEquals(value, region.get(key));
-      }
-    };
-
-    vm1.invoke(checkPutKeysExists);
-    vm2.invoke(checkPutKeysExists);
-
-    // Remove empty key entry using gfsh remove command
-    String command = "remove ";
-
-    command = command + " " + "--key=\"" + key + "\" --region=" + DATA_REGION_NAME_PATH;
-    CommandResult cmdResult = executeCommand(command);
-    printCommandOutput(cmdResult);
-
-    assertNotNull(cmdResult);
-    assertNotNull(cmdResult.getResultData());
-    assertNotNull(cmdResult.getResultData().getGfJsonObject());
-    assertTrue(
-        cmdResult.getResultData().getGfJsonObject() + " not contains message:"
-            + CliStrings.REMOVE__MSG__KEY_EMPTY,
-        !cmdResult.getResultData().getGfJsonObject().toString()
-            .contains(CliStrings.REMOVE__MSG__KEY_EMPTY));
-
-    validateResult(cmdResult, true);
-    assertEquals(Result.Status.OK, cmdResult.getStatus());
-
-    // Check that key were removed
-    SerializableRunnable checkPutKeysDoesNotExists = new SerializableRunnable() {
-      @Override
-      public void run() {
-        Cache cache = getCache();
-        Region<Object, Object> region = cache.getRegion(DATA_REGION_NAME_PATH);
-        assertNotNull(region);
-        assertFalse(value, region.containsKey(key));
-      }
-    };
-
-    vm1.invoke(checkPutKeysDoesNotExists);
-    vm2.invoke(checkPutKeysDoesNotExists);
-  }
-
-
   @Test
   public void testSimplePutCommand() {
     final String keyPrefix = "testKey";
@@ -1053,63 +972,6 @@ public class GemfireDataCommandsDUnitTest extends CliCommandTestBase {
     vm2.invoke(checkPutIfAbsentKeys);
   }
 
-  @Test // FlakyTest: GEODE-1496 (http)
-  public void testSimpleRemoveCommand() {
-    final String keyPrefix = "testKey";
-    final String valuePrefix = "testValue";
-
-    setupForGetPutRemoveLocateEntry("testSimpleRemove");
-
-    final VM vm1 = Host.getHost(0).getVM(1);
-    final VM vm2 = Host.getHost(0).getVM(2);
-
-
-    SerializableRunnable putKeys = new SerializableRunnable() {
-      @Override
-      public void run() {
-        Cache cache = getCache();
-        Region region = cache.getRegion(DATA_REGION_NAME_PATH);
-        assertNotNull(region);
-        region.clear();
-        for (int i = 0; i < COUNT; i++) {
-          String key = keyPrefix + i;
-          String value = valuePrefix + i;
-          region.put(key, value);
-        }
-        assertEquals(COUNT, region.size());
-      }
-    };
-    vm1.invoke(putKeys);
-
-    for (int i = 0; i < COUNT; i++) {
-      String command = "remove";
-      String key = keyPrefix + i;
-      String value = valuePrefix + i;
-      command = command + " " + "--key=" + key + " --region=" + DATA_REGION_NAME_PATH;
-      CommandResult cmdResult = executeCommand(command);
-      printCommandOutput(cmdResult);
-      assertEquals(Result.Status.OK, cmdResult.getStatus());
-      validateResult(cmdResult, true);
-    }
-
-    SerializableRunnable checkRemoveKeys = new SerializableRunnable() {
-      @Override
-      public void run() {
-        Cache cache = getCache();
-        Region region = cache.getRegion(DATA_REGION_NAME_PATH);
-        assertNotNull(region);
-        for (int i = 0; i < COUNT; i++) {
-          String key = keyPrefix + i;
-          assertEquals(false, region.containsKey(key));
-        }
-        assertEquals(0, region.size());
-      }
-    };
-
-    vm1.invoke(checkRemoveKeys);
-    vm2.invoke(checkRemoveKeys);
-  }
-
   @Test
   public void testSimpleGetLocateEntryCommand() {
     final String keyPrefix = "testKey";
@@ -1640,73 +1502,6 @@ public class GemfireDataCommandsDUnitTest extends CliCommandTestBase {
     validateResult(cmdResult, false);
   }
 
-  @Test
-  public void testRemoveJsonCommand() {
-    final String keyPrefix = "testKey";
-
-    setupForGetPutRemoveLocateEntry("testRemoveJsonCommand");
-
-    final VM vm1 = Host.getHost(0).getVM(1);
-    final VM vm2 = Host.getHost(0).getVM(2);
-
-    SerializableRunnable putKeys = new SerializableRunnable() {
-      @Override
-      public void run() {
-        Cache cache = getCache();
-        Region region = cache.getRegion(DATA_REGION_NAME_PATH);
-        assertNotNull(region);
-        region.clear();
-        for (int i = 0; i < COUNT; i++) {
-          String keyString = keyPrefix + i;
-          Key1 key = new Key1();
-          key.setId(keyString);
-          key.setName("name" + keyString);
-          Value2 value2 = new Value2();
-          value2.setStateName("State" + keyString);
-          value2.setCapitalCity("capital" + keyString);
-          value2.setPopulation(i * 100);
-          value2.setAreaInSqKm(i * 100.4365);
-          region.put(key, value2);
-        }
-      }
-    };
-
-    vm1.invoke(putKeys);
-
-    for (int i = 0; i < COUNT; i++) {
-      String command = "remove";
-      String keyString = keyPrefix + i;
-      String keyJson = keyTemplate.replaceAll("\\?", keyString);
-      getLogWriter().info("Removing key with json key : " + keyJson);
-      command = command + " " + "--key=" + keyJson + " --region=" + DATA_REGION_NAME_PATH
-          + " --key-class=" + Key1.class.getCanonicalName();
-      CommandResult cmdResult = executeCommand(command);
-      printCommandOutput(cmdResult);
-      assertEquals(Result.Status.OK, cmdResult.getStatus());
-      validateResult(cmdResult, true);
-    }
-
-    SerializableRunnable checkRemoveKeys = new SerializableRunnable() {
-      @Override
-      public void run() {
-        Cache cache = getCache();
-        Region region = cache.getRegion(DATA_REGION_NAME_PATH);
-        assertNotNull(region);
-        for (int i = 0; i < COUNT; i++) {
-          String keyString = keyPrefix + i;
-          Key1 key = new Key1();
-          key.setId(keyString);
-          key.setName("name" + keyString);
-          assertEquals(false, region.containsKey(key));
-        }
-        assertEquals(0, region.size());
-      }
-    };
-
-    vm1.invoke(checkRemoveKeys);
-    vm2.invoke(checkRemoveKeys);
-  }
-
   private Region<?, ?> createParReg(String regionName, Cache cache) {
     RegionFactory regionFactory = cache.createRegionFactory();
     regionFactory.setDataPolicy(DataPolicy.PARTITION);
diff --git a/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/RemoveCommandDUnitTest.java b/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/RemoveCommandDUnitTest.java
new file mode 100644
index 0000000..655eeb8
--- /dev/null
+++ b/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/RemoveCommandDUnitTest.java
@@ -0,0 +1,210 @@
+/*
+ * 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.management.internal.cli.commands;
+
+import static org.apache.geode.management.internal.cli.commands.DataCommandsUtils.getRegionAssociatedMembers;
+import static org.apache.geode.management.internal.cli.commands.RemoveCommand.*;
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.io.Serializable;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.commons.collections.CollectionUtils;
+import org.awaitility.Awaitility;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+import org.apache.geode.cache.Cache;
+import org.apache.geode.cache.CacheFactory;
+import org.apache.geode.cache.Region;
+import org.apache.geode.cache.RegionShortcut;
+import org.apache.geode.distributed.DistributedMember;
+import org.apache.geode.internal.cache.InternalCache;
+import org.apache.geode.test.dunit.SerializableCallableIF;
+import org.apache.geode.test.dunit.rules.LocatorServerStartupRule;
+import org.apache.geode.test.dunit.rules.MemberVM;
+import org.apache.geode.test.junit.categories.DistributedTest;
+import org.apache.geode.test.junit.rules.GfshShellConnectionRule;
+
+@Category(DistributedTest.class)
+public class RemoveCommandDUnitTest implements Serializable {
+  private static final String REPLICATE_REGION_NAME = "replicateRegion";
+  private static final String PARTITIONED_REGION_NAME = "partitionedRegion";
+  private static final String EMPTY_STRING = "";
+
+  @Rule
+  public LocatorServerStartupRule locatorServerStartupRule = new LocatorServerStartupRule();
+
+  @Rule
+  public transient GfshShellConnectionRule gfsh = new GfshShellConnectionRule();
+
+  private transient MemberVM locator;
+  private transient MemberVM server1;
+  private transient MemberVM server2;
+
+  @Before
+  public void setup() throws Exception {
+    locator = locatorServerStartupRule.startLocatorVM(0);
+
+    server1 = locatorServerStartupRule.startServerVM(1, locator.getPort());
+    server2 = locatorServerStartupRule.startServerVM(2, locator.getPort());
+
+    server1.invoke(this::populateTestRegions);
+    server2.invoke(this::populateTestRegions);
+
+    gfsh.connectAndVerify(locator);
+
+    Awaitility.await().atMost(2, TimeUnit.MINUTES).until(() -> locator.getVM()
+        .invoke((SerializableCallableIF<Boolean>) this::regionMBeansAreInitialized));
+  }
+
+  private void populateTestRegions() {
+    Cache cache = CacheFactory.getAnyInstance();
+    Region<String, String> replicateRegion =
+        cache.<String, String>createRegionFactory(RegionShortcut.REPLICATE)
+            .create(REPLICATE_REGION_NAME);
+    Region<String, String> partitionedRegion =
+        cache.<String, String>createRegionFactory(RegionShortcut.PARTITION)
+            .create(PARTITIONED_REGION_NAME);
+
+    replicateRegion.put(EMPTY_STRING, "valueForEmptyKey");
+    replicateRegion.put("key1", "value1");
+    replicateRegion.put("key2", "value2");
+
+    partitionedRegion.put("key1", "value1");
+    partitionedRegion.put("key2", "value2");
+  }
+
+  @Test
+  public void removeFromInvalidRegion() throws Exception {
+    String command = "remove --all --region=NotAValidRegion";
+
+    gfsh.executeAndVerifyCommandError(command);
+    assertThat(gfsh.getGfshOutput()).contains(String.format(REGION_NOT_FOUND, "NotAValidRegion"));
+  }
+
+  @Test
+  public void removeWithNoKeyOrAllSpecified() throws Exception {
+    String command = "remove --region=" + REPLICATE_REGION_NAME;
+
+    gfsh.executeAndVerifyCommandError(command);
+    assertThat(gfsh.getGfshOutput()).contains("Key is Null");
+  }
+
+  @Test
+  public void removeKeyFromReplicateRegion() {
+    String command = "remove --key=key1 --region=" + REPLICATE_REGION_NAME;
+
+    gfsh.executeAndVerifyCommand(command);
+
+    String output = gfsh.getGfshOutput();
+    assertThat(output).containsPattern("Result\\s+:\\s+true");
+    assertThat(output).containsPattern("Key Class\\s+:\\s+java.lang.String");
+    assertThat(output).containsPattern("Key\\s+:\\s+key1");
+
+    server1.invoke(() -> verifyKeyIsRemoved(REPLICATE_REGION_NAME, "key1"));
+    server2.invoke(() -> verifyKeyIsRemoved(REPLICATE_REGION_NAME, "key1"));
+
+    server1.invoke(() -> verifyKeyIsPresent(REPLICATE_REGION_NAME, "key2"));
+    server2.invoke(() -> verifyKeyIsPresent(REPLICATE_REGION_NAME, "key2"));
+  }
+
+  @Test
+  public void removeKeyFromPartitionedRegion() {
+    String command = "remove --key=key1 --region=" + PARTITIONED_REGION_NAME;
+
+    gfsh.executeAndVerifyCommand(command);
+
+    String output = gfsh.getGfshOutput();
+    assertThat(output).containsPattern("Result\\s+:\\s+true");
+    assertThat(output).containsPattern("Key Class\\s+:\\s+java.lang.String");
+    assertThat(output).containsPattern("Key\\s+:\\s+key1");
+
+    server1.invoke(() -> verifyKeyIsRemoved(PARTITIONED_REGION_NAME, "key1"));
+    server2.invoke(() -> verifyKeyIsRemoved(PARTITIONED_REGION_NAME, "key1"));
+
+    server1.invoke(() -> verifyKeyIsPresent(PARTITIONED_REGION_NAME, "key2"));
+    server2.invoke(() -> verifyKeyIsPresent(PARTITIONED_REGION_NAME, "key2"));
+  }
+
+  @Test
+  public void removeAllFromReplicateRegion() {
+    String command = "remove --all --region=" + REPLICATE_REGION_NAME;
+
+    gfsh.executeAndVerifyCommand("list regions");
+    gfsh.executeAndVerifyCommand(command);
+
+    assertThat(gfsh.getGfshOutput()).contains("Cleared all keys in the region");
+
+    server1.invoke(() -> verifyAllKeysAreRemoved(REPLICATE_REGION_NAME));
+    server2.invoke(() -> verifyAllKeysAreRemoved(REPLICATE_REGION_NAME));
+  }
+
+
+  @Test
+  public void removeAllFromPartitionedRegion() {
+    String command = "remove --all --region=" + PARTITIONED_REGION_NAME;
+
+    // Maybe this should return an "error" status, but the current behavior is status "OK"
+    gfsh.executeAndVerifyCommand(command);
+
+    assertThat(gfsh.getGfshOutput())
+        .contains("Option --all is not supported on partitioned region");
+  }
+
+  /**
+   * Test remove from a region with a zero-length string key (GEODE-2269)
+   */
+  @Test
+  public void removeEmptyKey() {
+    server1.invoke(() -> verifyKeyIsPresent(REPLICATE_REGION_NAME, EMPTY_STRING));
+    server2.invoke(() -> verifyKeyIsPresent(REPLICATE_REGION_NAME, EMPTY_STRING));
+
+    String command = "remove --key=\"\" --region=" + REPLICATE_REGION_NAME;
+    gfsh.executeAndVerifyCommand(command);
+
+    server1.invoke(() -> verifyKeyIsRemoved(REPLICATE_REGION_NAME, EMPTY_STRING));
+    server2.invoke(() -> verifyKeyIsRemoved(REPLICATE_REGION_NAME, EMPTY_STRING));
+  }
+
+  private boolean regionMBeansAreInitialized() {
+    Set<DistributedMember> members = getRegionAssociatedMembers(REPLICATE_REGION_NAME,
+        (InternalCache) CacheFactory.getAnyInstance(), false);
+
+    return CollectionUtils.isNotEmpty(members);
+  }
+
+  private void verifyAllKeysAreRemoved(String regionName) {
+    Region region = getRegion(regionName);
+    assertThat(region.size()).isEqualTo(0);
+  }
+
+  private void verifyKeyIsRemoved(String regionName, String key) {
+    Region region = getRegion(regionName);
+    assertThat(region.get(key)).isNull();
+  }
+
+  private void verifyKeyIsPresent(String regionName, String key) {
+    Region region = getRegion(regionName);
+    assertThat(region.get(key)).isNotNull();
+  }
+
+  private Region getRegion(String regionName) {
+    return CacheFactory.getAnyInstance().getRegion(regionName);
+  }
+}
diff --git a/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/RemoveCommandJsonDUnitTest.java b/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/RemoveCommandJsonDUnitTest.java
new file mode 100644
index 0000000..b084ab5
--- /dev/null
+++ b/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/RemoveCommandJsonDUnitTest.java
@@ -0,0 +1,171 @@
+/*
+ * 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.management.internal.cli.commands;
+
+import static org.apache.geode.management.internal.cli.commands.DataCommandsUtils.getRegionAssociatedMembers;
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.io.Serializable;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.commons.collections.CollectionUtils;
+import org.awaitility.Awaitility;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+import org.apache.geode.cache.Cache;
+import org.apache.geode.cache.CacheFactory;
+import org.apache.geode.cache.Region;
+import org.apache.geode.cache.RegionShortcut;
+import org.apache.geode.distributed.DistributedMember;
+import org.apache.geode.internal.cache.InternalCache;
+import org.apache.geode.management.internal.cli.dto.Key1;
+import org.apache.geode.management.internal.cli.dto.Value2;
+import org.apache.geode.test.dunit.SerializableCallableIF;
+import org.apache.geode.test.dunit.rules.LocatorServerStartupRule;
+import org.apache.geode.test.dunit.rules.MemberVM;
+import org.apache.geode.test.junit.categories.DistributedTest;
+import org.apache.geode.test.junit.rules.GfshShellConnectionRule;
+
+@Category(DistributedTest.class)
+public class RemoveCommandJsonDUnitTest implements Serializable {
+  private static final String JSON_REGION_NAME = "jsonReplicateRegion";
+
+  @Rule
+  public LocatorServerStartupRule locatorServerStartupRule = new LocatorServerStartupRule();
+
+  @Rule
+  public transient GfshShellConnectionRule gfsh = new GfshShellConnectionRule();
+
+  private transient MemberVM locator;
+  private transient MemberVM server1;
+  private transient MemberVM server2;
+
+  @Before
+  public void setup() throws Exception {
+    locator = locatorServerStartupRule.startLocatorVM(0);
+
+    server1 = locatorServerStartupRule.startServerVM(1, locator.getPort());
+    server2 = locatorServerStartupRule.startServerVM(2, locator.getPort());
+
+    server1.invoke(this::populateTestRegions);
+    server2.invoke(this::populateTestRegions);
+
+    gfsh.connectAndVerify(locator);
+
+    Awaitility.await().atMost(2, TimeUnit.MINUTES).until(() -> locator.getVM()
+        .invoke((SerializableCallableIF<Boolean>) this::regionMBeansAreInitialized));
+  }
+
+  private void populateTestRegions() {
+    Cache cache = CacheFactory.getAnyInstance();
+
+    Region<Key1, Value2> complexRegion =
+        cache.<Key1, Value2>createRegionFactory(RegionShortcut.REPLICATE).create(JSON_REGION_NAME);
+
+    Value2 value1 = new Value2();
+    value1.setStateName("State1");
+    value1.setCapitalCity("capital1");
+    value1.setPopulation(100);
+    value1.setAreaInSqKm(100.4365);
+    complexRegion.put(key(1), value1);
+
+    Value2 value2 = new Value2();
+    value1.setStateName("State2");
+    value1.setCapitalCity("capital2");
+    value1.setPopulation(200);
+    value1.setAreaInSqKm(200.4365);
+    complexRegion.put(key(2), value2);
+  }
+
+  @Test
+  public void testRemoveJsonCommand() {
+    String keyJson = "('id':'key1','name':'name1')";
+
+    String command = removeCommand(keyJson);
+    gfsh.executeAndVerifyCommand(command);
+
+    server1.invoke(() -> verifyKeyIsRemoved(1));
+    server2.invoke(() -> verifyKeyIsRemoved(1));
+
+    server1.invoke(() -> verifyKeyIsPresent(2));
+    server2.invoke(() -> verifyKeyIsPresent(2));
+  }
+
+  @Test
+  public void testRemoveJsonCommandWithOnlyId() {
+    String keyJson = "('id':'key1')";
+
+    String command = removeCommand(keyJson);
+    gfsh.executeAndVerifyCommand(command);
+
+    server1.invoke(() -> verifyKeyIsRemoved(1));
+    server2.invoke(() -> verifyKeyIsRemoved(1));
+
+    server1.invoke(() -> verifyKeyIsPresent(2));
+    server2.invoke(() -> verifyKeyIsPresent(2));
+  }
+
+  @Test
+  public void testRemoveJsonCommandWithInvalidJson() {
+    String keyJson = "('foo':'bar')";
+
+    String command = removeCommand(keyJson);
+    gfsh.executeAndVerifyCommand(command);
+
+    server1.invoke(() -> verifyKeyIsPresent(1));
+    server2.invoke(() -> verifyKeyIsPresent(1));
+
+    server1.invoke(() -> verifyKeyIsPresent(2));
+    server2.invoke(() -> verifyKeyIsPresent(2));
+  }
+
+  private String removeCommand(String keyJson) {
+    return "remove --key=" + keyJson + " --region=" + JSON_REGION_NAME + " --key-class="
+        + Key1.class.getCanonicalName();
+  }
+
+  private Key1 key(int n) {
+    Key1 key = new Key1();
+    key.setId("key" + n);
+    key.setName("name" + n);
+
+    return key;
+  }
+
+  private boolean regionMBeansAreInitialized() {
+    Set<DistributedMember> members = getRegionAssociatedMembers(JSON_REGION_NAME,
+        (InternalCache) CacheFactory.getAnyInstance(), false);
+
+    return CollectionUtils.isNotEmpty(members);
+  }
+
+  private void verifyKeyIsRemoved(int n) {
+    Region region = getJsonRegion();
+    assertThat(region.get(key(n))).isNull();
+  }
+
+  private void verifyKeyIsPresent(int n) {
+    Region region = getJsonRegion();
+    assertThat(region.get(key(n))).isNotNull();
+  }
+
+  private Region getJsonRegion() {
+    return CacheFactory.getAnyInstance().getRegion(JSON_REGION_NAME);
+  }
+}
diff --git a/geode-core/src/test/java/org/apache/geode/test/junit/rules/GfshShellConnectionRule.java b/geode-core/src/test/java/org/apache/geode/test/junit/rules/GfshShellConnectionRule.java
index 7122679..5640fef 100644
--- a/geode-core/src/test/java/org/apache/geode/test/junit/rules/GfshShellConnectionRule.java
+++ b/geode-core/src/test/java/org/apache/geode/test/junit/rules/GfshShellConnectionRule.java
@@ -256,6 +256,17 @@ public class GfshShellConnectionRule extends DescribedExternalResource {
     return result;
   }
 
+  public CommandResult executeAndVerifyCommandError(String command) {
+    CommandResult result = null;
+    try {
+      result = executeCommand(command);
+    } catch (Exception e) {
+      throw new RuntimeException(e);
+    }
+    assertThat(result.getStatus()).describedAs(getGfshOutput()).isEqualTo(Result.Status.ERROR);
+    return result;
+  }
+
   public String execute(String command) throws Exception {
     executeCommand(command);
     return gfsh.outputString;

-- 
To stop receiving notification emails like this one, please contact
['"commits@geode.apache.org" <co...@geode.apache.org>'].