GEODE-8855: fix hitmiss and stats integration tests (#5937)

GEODE-8855: improve RedisInfoStatsIntegrationTest

- organize tests
- remove tests that don't belong in this class
- fix a formatting issue in RedisCommandType
- make the RedisInfoStatsIntegrationTest run against our redis and native redis
- test all the stats in info
- add and use getExposedPort() so tests work in docker
- make hincrby behavior match that of native
redis

Co-authored-by: Jens Deppe <jdeppe@pivotal.io>
diff --git a/geode-redis/src/acceptanceTest/java/org/apache/geode/redis/internal/executor/server/InfoNativeRedisAcceptanceTest.java b/geode-redis/src/acceptanceTest/java/org/apache/geode/redis/internal/executor/server/InfoNativeRedisAcceptanceTest.java
index 4b07949..3157e03 100644
--- a/geode-redis/src/acceptanceTest/java/org/apache/geode/redis/internal/executor/server/InfoNativeRedisAcceptanceTest.java
+++ b/geode-redis/src/acceptanceTest/java/org/apache/geode/redis/internal/executor/server/InfoNativeRedisAcceptanceTest.java
@@ -30,7 +30,7 @@
   }
 
   @Override
-  int getExposedPort() {
+  public int getExposedPort() {
     return redis.getExposedPort();
   }
 }
diff --git a/geode-redis/src/acceptanceTest/java/org/apache/geode/redis/internal/executor/server/InfoStatsNativeRedisAcceptanceTest.java b/geode-redis/src/acceptanceTest/java/org/apache/geode/redis/internal/executor/server/InfoStatsNativeRedisAcceptanceTest.java
new file mode 100644
index 0000000..c4f56e0
--- /dev/null
+++ b/geode-redis/src/acceptanceTest/java/org/apache/geode/redis/internal/executor/server/InfoStatsNativeRedisAcceptanceTest.java
@@ -0,0 +1,30 @@
+/*
+ * 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.server;
+
+import org.junit.ClassRule;
+
+import org.apache.geode.NativeRedisTestRule;
+
+public class InfoStatsNativeRedisAcceptanceTest extends AbstractRedisInfoStatsIntegrationTest {
+  @ClassRule
+  public static NativeRedisTestRule redis = new NativeRedisTestRule();
+
+  @Override
+  public int getPort() {
+    return redis.getPort();
+  }
+}
diff --git a/geode-redis/src/commonTest/java/org/apache/geode/NativeRedisTestRule.java b/geode-redis/src/commonTest/java/org/apache/geode/NativeRedisTestRule.java
index 5d0710d..fabff75 100644
--- a/geode-redis/src/commonTest/java/org/apache/geode/NativeRedisTestRule.java
+++ b/geode-redis/src/commonTest/java/org/apache/geode/NativeRedisTestRule.java
@@ -34,6 +34,7 @@
   private GenericContainer<?> redisContainer;
   private final RuleChain delegate;
   private final int PORT_TO_EXPOSE = 6379;
+  private int max_clients = 10000;
 
   public NativeRedisTestRule() {
     delegate = RuleChain
@@ -50,6 +51,12 @@
     return redisContainer.getExposedPorts().get(0);
   }
 
+  public NativeRedisTestRule withMaxConnections(int max_connections) {
+    this.max_clients = max_connections;
+
+    return this;
+  }
+
   @Override
   public Statement apply(Statement base, Description description) {
     Statement containerStatement = new Statement() {
@@ -58,7 +65,8 @@
 
         redisContainer =
             new GenericContainer<>("redis:5.0.6")
-                .withExposedPorts(PORT_TO_EXPOSE);
+                .withExposedPorts(PORT_TO_EXPOSE)
+                .withCommand("redis-server --maxclients " + max_clients);
 
         redisContainer.start();
         logger.info("Started redis container with exposed port {} -> {}", PORT_TO_EXPOSE,
diff --git a/geode-redis/src/integrationTest/java/org/apache/geode/redis/internal/RedisStatsIntegrationTest.java b/geode-redis/src/integrationTest/java/org/apache/geode/redis/internal/RedisStatsIntegrationTest.java
deleted file mode 100644
index 7e3f2b9..0000000
--- a/geode-redis/src/integrationTest/java/org/apache/geode/redis/internal/RedisStatsIntegrationTest.java
+++ /dev/null
@@ -1,617 +0,0 @@
-/*
- * 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;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-import java.time.Duration;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicReference;
-
-import com.google.common.util.concurrent.AtomicDouble;
-import org.assertj.core.data.Offset;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.BeforeClass;
-import org.junit.ClassRule;
-import org.junit.Test;
-import redis.clients.jedis.BitOP;
-import redis.clients.jedis.Jedis;
-
-import org.apache.geode.Statistics;
-import org.apache.geode.StatisticsType;
-import org.apache.geode.cache.CacheFactory;
-import org.apache.geode.distributed.internal.InternalDistributedSystem;
-import org.apache.geode.internal.cache.InternalCache;
-import org.apache.geode.internal.statistics.EnabledStatisticsClock;
-import org.apache.geode.internal.statistics.StatisticsClock;
-import org.apache.geode.redis.GeodeRedisServerRule;
-import org.apache.geode.redis.internal.statistics.RedisStats;
-import org.apache.geode.test.awaitility.GeodeAwaitility;
-
-public class RedisStatsIntegrationTest {
-
-  private static final int TIMEOUT = (int) GeodeAwaitility.getTimeout().toMillis();
-  private static final String EXISTING_HASH_KEY = "Existing_Hash";
-  private static final String EXISTING_STRING_KEY = "Existing_String";
-  private static final String EXISTING_SET_KEY_1 = "Existing_Set_1";
-  private static final String EXISTING_SET_KEY_2 = "Existing_Set_2";
-  private static final String NONEXISTENT_KEY = "Nonexistent_Key";
-
-  private Jedis jedis;
-  private RedisStats redisStats;
-  private static long START_TIME;
-  private static StatisticsClock statisticsClock;
-
-  private long preTestKeySpaceHits = 0;
-  private long preTestKeySpaceMisses = 0;
-  private long preTestConnectionsReceived = 0;
-  private long preTestConnectedClients = 0;
-
-  @ClassRule
-  public static GeodeRedisServerRule server = new GeodeRedisServerRule();
-
-  private static StatsValidator statsValidator;
-
-  private static class StatsValidator {
-
-    private final RedisStats redisStats;
-    private final Statistics geodeStats;
-
-    public StatsValidator(GeodeRedisServerRule serverRule) {
-      redisStats = serverRule.getServer().getStats();
-
-      InternalCache cache = (InternalCache) CacheFactory.getAnyInstance();
-      InternalDistributedSystem system = (InternalDistributedSystem) cache.getDistributedSystem();
-
-      StatisticsType statSamplerType = system.findType("RedisStats");
-      Statistics[] statsArray = system.findStatisticsByType(statSamplerType);
-      assertThat(statsArray).hasSize(1);
-
-      geodeStats = statsArray[0];
-    }
-
-    public void validateKeyspaceHits(long before, long delta) {
-      assertThat(redisStats.getKeyspaceHits()).isEqualTo(before + delta);
-
-      long geodeKeyspaceHits = geodeStats.getLong("keyspaceHits");
-      assertThat(geodeKeyspaceHits).isEqualTo(before + delta);
-    }
-
-    public void validateKeyspaceMisses(long before, long delta) {
-      assertThat(redisStats.getKeyspaceMisses()).isEqualTo(before + delta);
-
-      long geodeKeyspaceMisses = geodeStats.getLong("keyspaceMisses");
-      assertThat(geodeKeyspaceMisses).isEqualTo(before + delta);
-    }
-
-    public void validateCommandsProcessed(long before, long delta) {
-      GeodeAwaitility.await().atMost(Duration.ofSeconds(5))
-          .untilAsserted(() -> assertThat(redisStats.getCommandsProcessed())
-              .isEqualTo(before + delta));
-
-      GeodeAwaitility.await().atMost(Duration.ofSeconds(5))
-          .untilAsserted(() -> {
-            long geodeCommandsProcessed = geodeStats.getLong("commandsProcessed");
-            assertThat(geodeCommandsProcessed).isEqualTo(before + delta);
-          });
-    }
-
-    public void validateNetworkBytesRead(long before, long delta) {
-      assertThat(redisStats.getTotalNetworkBytesRead()).isEqualTo(before + delta);
-
-      long geodeNetworkBytesRead = geodeStats.getLong("totalNetworkBytesRead");
-      assertThat(geodeNetworkBytesRead).isEqualTo(before + delta);
-    }
-
-    public void validateConnectedClients(long before, long delta) {
-      GeodeAwaitility.await().atMost(Duration.ofSeconds(2))
-          .untilAsserted(() -> {
-            assertThat(redisStats.getConnectedClients()).isEqualTo(before + delta);
-          });
-
-      GeodeAwaitility.await().atMost(Duration.ofSeconds(2))
-          .untilAsserted(() -> {
-            long geodeConnectedClients = geodeStats.getLong("connectedClients");
-            assertThat(geodeConnectedClients).isEqualTo(before + delta);
-          });
-    }
-
-    public void validateConnectionsReceived(long before, long delta) {
-      assertThat(redisStats.getTotalConnectionsReceived()).isEqualTo(before + delta);
-
-      long geodeConnectionsReceived = geodeStats.getLong("totalConnectionsReceived");
-      assertThat(geodeConnectionsReceived).isEqualTo(before + delta);
-    }
-
-  }
-
-  @BeforeClass
-  public static void beforeClass() {
-    statisticsClock = new EnabledStatisticsClock();
-    START_TIME = statisticsClock.getTime();
-
-    statsValidator = new StatsValidator(server);
-  }
-
-  @Before
-  public void before() {
-    jedis = new Jedis("localhost", server.getPort(), TIMEOUT);
-
-    redisStats = server.getServer().getStats();
-
-    long preSetupCommandsProcessed = redisStats.getCommandsProcessed();
-
-    jedis.set(EXISTING_STRING_KEY, "A_Value");
-    jedis.hset(EXISTING_HASH_KEY, "Field1", "Value1");
-    jedis.sadd(EXISTING_SET_KEY_1, "m1", "m2", "m3");
-    jedis.sadd(EXISTING_SET_KEY_2, "m4", "m5", "m6");
-
-    GeodeAwaitility.await().atMost(Duration.ofSeconds(2))
-        .untilAsserted(() -> assertThat(redisStats.getCommandsProcessed())
-            .isEqualTo(preSetupCommandsProcessed + 4));
-
-    preTestKeySpaceHits = redisStats.getKeyspaceHits();
-    preTestKeySpaceMisses = redisStats.getKeyspaceMisses();
-    preTestConnectionsReceived = redisStats.getTotalConnectionsReceived();
-    preTestConnectedClients = redisStats.getConnectedClients();
-  }
-
-  @After
-  public void after() {
-    jedis.flushAll();
-    jedis.close();
-    GeodeAwaitility.await().atMost(Duration.ofSeconds(2))
-        .untilAsserted(() -> assertThat(redisStats.getConnectedClients())
-            .isEqualTo(0));
-  }
-
-  // #############Stats Section###################################
-
-  @Test
-  public void keyspaceHitsStat_shouldIncrement_whenKeyAccessed() {
-    jedis.get(EXISTING_STRING_KEY);
-
-    statsValidator.validateKeyspaceHits(preTestKeySpaceHits, 1);
-    statsValidator.validateKeyspaceMisses(preTestKeySpaceMisses, 0);
-  }
-
-  @Test
-  public void keyspaceHitsStat_shouldNotIncrement_whenNonexistentKeyAccessed() {
-    jedis.get("Nonexistent_Key");
-
-    statsValidator.validateKeyspaceHits(preTestKeySpaceHits, 0);
-    statsValidator.validateKeyspaceMisses(preTestKeySpaceMisses, 1);
-  }
-
-  @Test
-  public void keyspaceStats_setCommand_existingKey() {
-    jedis.set(EXISTING_STRING_KEY, "New_Value");
-
-    statsValidator.validateKeyspaceHits(preTestKeySpaceHits, 0);
-    statsValidator.validateKeyspaceMisses(preTestKeySpaceMisses, 0);
-  }
-
-  @Test
-  public void keyspaceStats_setCommand_nonexistentKey() {
-    jedis.set("Another_Key", "Another_Value");
-
-    statsValidator.validateKeyspaceHits(preTestKeySpaceHits, 0);
-    statsValidator.validateKeyspaceMisses(preTestKeySpaceMisses, 0);
-  }
-
-  @Test
-  public void keyspaceStats_getBitCommand_existingKey() {
-    jedis.getbit(EXISTING_STRING_KEY, 0);
-
-    statsValidator.validateKeyspaceHits(preTestKeySpaceHits, 1);
-    statsValidator.validateKeyspaceMisses(preTestKeySpaceMisses, 0);
-  }
-
-  @Test
-  public void keyspaceStats_getBitCommand_nonexistentKey() {
-    jedis.getbit("Nonexistent_Key", 0);
-
-    statsValidator.validateKeyspaceHits(preTestKeySpaceHits, 0);
-    statsValidator.validateKeyspaceMisses(preTestKeySpaceMisses, 1);
-  }
-
-  @Test
-  public void keyspaceStats_getRangeCommand_existingKey() {
-    jedis.getrange(EXISTING_STRING_KEY, 0, 1);
-
-    statsValidator.validateKeyspaceHits(preTestKeySpaceHits, 1);
-    statsValidator.validateKeyspaceMisses(preTestKeySpaceMisses, 0);
-  }
-
-  @Test
-  public void keyspaceStats_getRangeCommand_nonexistentKey() {
-    jedis.getrange("Nonexistent_Key", 0, 1);
-
-    statsValidator.validateKeyspaceHits(preTestKeySpaceHits, 0);
-    statsValidator.validateKeyspaceMisses(preTestKeySpaceMisses, 1);
-  }
-
-  @Test
-  public void keyspaceStats_getSetCommand_existingKey() {
-    jedis.getSet(EXISTING_STRING_KEY, "New_Value");
-
-    statsValidator.validateKeyspaceHits(preTestKeySpaceHits, 1);
-    statsValidator.validateKeyspaceMisses(preTestKeySpaceMisses, 0);
-  }
-
-  @Test
-  public void keyspaceStats_getSetCommand_nonexistentKey() {
-    jedis.getSet("Nonexistent_Key", "FakeValue");
-
-    statsValidator.validateKeyspaceHits(preTestKeySpaceHits, 0);
-    statsValidator.validateKeyspaceMisses(preTestKeySpaceMisses, 1);
-  }
-
-  @Test
-  public void keyspaceStats_strlenCommand_existingKey() {
-    jedis.strlen(EXISTING_STRING_KEY);
-
-    statsValidator.validateKeyspaceHits(preTestKeySpaceHits, 1);
-    statsValidator.validateKeyspaceMisses(preTestKeySpaceMisses, 0);
-  }
-
-  @Test
-  public void keyspaceStats_strlenCommand_nonexistentKey() {
-    jedis.strlen(NONEXISTENT_KEY);
-
-    statsValidator.validateKeyspaceHits(preTestKeySpaceHits, 0);
-    statsValidator.validateKeyspaceMisses(preTestKeySpaceMisses, 1);
-  }
-
-  @Test
-  public void keyspaceStats_mgetCommand() {
-    jedis.mget(EXISTING_STRING_KEY, "Nonexistent_Key");
-
-    statsValidator.validateKeyspaceHits(preTestKeySpaceHits, 1);
-    statsValidator.validateKeyspaceMisses(preTestKeySpaceMisses, 1);
-  }
-
-  @Test
-  public void keyspaceStats_bitopCommand_existingKey() {
-    jedis.bitop(BitOP.AND, EXISTING_STRING_KEY, EXISTING_STRING_KEY, "Nonexistent_Key");
-
-    statsValidator.validateKeyspaceHits(preTestKeySpaceHits, 1);
-    statsValidator.validateKeyspaceMisses(preTestKeySpaceMisses, 1);
-  }
-
-  @Test
-  public void keyspaceStats_bitopCommand_newKey() {
-    jedis.bitop(BitOP.AND, "destination-key", EXISTING_STRING_KEY, "Nonexistent_Key");
-
-    statsValidator.validateKeyspaceHits(preTestKeySpaceHits, 1);
-    statsValidator.validateKeyspaceMisses(preTestKeySpaceMisses, 1);
-  }
-
-  @Test
-  public void keyspaceStats_bitcountCommand_existingKey() {
-    jedis.bitcount(EXISTING_STRING_KEY);
-
-    statsValidator.validateKeyspaceHits(preTestKeySpaceHits, 1);
-    statsValidator.validateKeyspaceMisses(preTestKeySpaceMisses, 0);
-  }
-
-  @Test
-  public void keyspaceStats_bitcountCommand_nonexistentKey() {
-    jedis.bitcount("Nonexistent_Key");
-
-    statsValidator.validateKeyspaceHits(preTestKeySpaceHits, 0);
-    statsValidator.validateKeyspaceMisses(preTestKeySpaceMisses, 1);
-  }
-
-  @Test
-  public void keyspaceStats_bitposCommand_existingKey() {
-    jedis.bitpos(EXISTING_STRING_KEY, true);
-
-    statsValidator.validateKeyspaceHits(preTestKeySpaceHits, 1);
-    statsValidator.validateKeyspaceMisses(preTestKeySpaceMisses, 0);
-  }
-
-  @Test
-  public void keyspaceStats_bitposCommand_nonexistentKey() {
-    jedis.bitpos("Nonexistent_Key", true);
-
-    statsValidator.validateKeyspaceHits(preTestKeySpaceHits, 0);
-    statsValidator.validateKeyspaceMisses(preTestKeySpaceMisses, 1);
-  }
-
-  @Test
-  public void keyspaceStats_hgetCommand_existingKey() {
-    jedis.hget(EXISTING_HASH_KEY, "Field1");
-
-    statsValidator.validateKeyspaceHits(preTestKeySpaceHits, 1);
-    statsValidator.validateKeyspaceMisses(preTestKeySpaceMisses, 0);
-  }
-
-  @Test
-  public void keyspaceStats_hgetCommand_nonexistentKey() {
-    jedis.hget("Nonexistent_Hash", "Field1");
-
-    statsValidator.validateKeyspaceHits(preTestKeySpaceHits, 0);
-    statsValidator.validateKeyspaceMisses(preTestKeySpaceMisses, 1);
-  }
-
-  @Test
-  public void keyspaceStats_smembersCommand_existingKey() {
-    jedis.smembers(EXISTING_SET_KEY_1);
-
-    statsValidator.validateKeyspaceHits(preTestKeySpaceHits, 1);
-    statsValidator.validateKeyspaceMisses(preTestKeySpaceMisses, 0);
-  }
-
-  @Test
-  public void keyspaceStats_smembersCommand_nonexistentKey() {
-    jedis.smembers("Nonexistent_Set");
-
-    statsValidator.validateKeyspaceHits(preTestKeySpaceHits, 0);
-    statsValidator.validateKeyspaceMisses(preTestKeySpaceMisses, 1);
-  }
-
-  @Test
-  public void keyspaceStats_sunionstoreCommand_existingKey() {
-    jedis.sunionstore(
-        EXISTING_SET_KEY_1,
-        EXISTING_SET_KEY_1,
-        EXISTING_SET_KEY_2,
-        "Nonexistent_Set");
-
-    statsValidator.validateKeyspaceHits(preTestKeySpaceHits, 0);
-    statsValidator.validateKeyspaceMisses(preTestKeySpaceMisses, 0);
-  }
-
-  @Test
-  public void keyspaceStats_sunionstoreCommand_newKey() {
-    jedis.sunionstore(
-        "New_Set",
-        EXISTING_SET_KEY_1,
-        EXISTING_SET_KEY_2,
-        "Nonexistent_Set");
-
-    statsValidator.validateKeyspaceHits(preTestKeySpaceHits, 0);
-    statsValidator.validateKeyspaceMisses(preTestKeySpaceMisses, 0);
-  }
-
-  @Test
-  public void keyspaceStats_sdiffstoreCommand_newKey() {
-    jedis.sdiffstore(
-        "New_Set",
-        EXISTING_SET_KEY_1,
-        EXISTING_SET_KEY_2,
-        "Nonexistent_Set");
-
-    statsValidator.validateKeyspaceHits(preTestKeySpaceHits, 0);
-    statsValidator.validateKeyspaceMisses(preTestKeySpaceMisses, 0);
-  }
-
-  @Test
-  public void keyspaceStats_sinterstoreCommand_newKey() {
-    jedis.sinterstore(
-        "New_Set",
-        EXISTING_SET_KEY_1,
-        EXISTING_SET_KEY_2,
-        "Nonexistent_Set");
-
-    statsValidator.validateKeyspaceHits(preTestKeySpaceHits, 0);
-    statsValidator.validateKeyspaceMisses(preTestKeySpaceMisses, 0);
-  }
-
-  @Test
-  public void keyspaceStats_ExistsCommand_existingKey() {
-    jedis.exists(EXISTING_STRING_KEY);
-
-    statsValidator.validateKeyspaceHits(preTestKeySpaceHits, 1);
-    statsValidator.validateKeyspaceMisses(preTestKeySpaceMisses, 0);
-  }
-
-  @Test
-  public void keyspaceStats_ExistsCommand_nonexistentKey() {
-    jedis.exists(NONEXISTENT_KEY);
-
-    statsValidator.validateKeyspaceHits(preTestKeySpaceHits, 0);
-    statsValidator.validateKeyspaceMisses(preTestKeySpaceMisses, 1);
-  }
-
-  @Test
-  public void keyspaceStats_TypeCommand_existingKey() {
-    jedis.type(EXISTING_STRING_KEY);
-
-    statsValidator.validateKeyspaceHits(preTestKeySpaceHits, 1);
-    statsValidator.validateKeyspaceMisses(preTestKeySpaceMisses, 0);
-  }
-
-  @Test
-  public void keyspaceStats_TypeCommand_nonexistentKey() {
-    jedis.type(NONEXISTENT_KEY);
-
-    statsValidator.validateKeyspaceHits(preTestKeySpaceHits, 0);
-    statsValidator.validateKeyspaceMisses(preTestKeySpaceMisses, 1);
-  }
-
-  @Test
-  public void keyspaceStats_PTTL_TTLCommand_existingKey() {
-    jedis.ttl(EXISTING_STRING_KEY);
-
-    statsValidator.validateKeyspaceHits(preTestKeySpaceHits, 1);
-    statsValidator.validateKeyspaceMisses(preTestKeySpaceMisses, 0);
-  }
-
-  @Test
-  public void keyspaceStats_PTTL_TTL_Command_nonexistentKey() {
-    jedis.ttl(NONEXISTENT_KEY);
-
-    statsValidator.validateKeyspaceHits(preTestKeySpaceHits, 0);
-    statsValidator.validateKeyspaceMisses(preTestKeySpaceMisses, 1);
-  }
-
-  @Test
-  public void commandsProcessed_shouldIncrement_givenSuccessfulCommand() {
-    long initialCommandsProcessed = redisStats.getCommandsProcessed();
-    jedis.ttl("key");
-
-    statsValidator.validateCommandsProcessed(initialCommandsProcessed, 1);
-  }
-
-  @Test
-  public void opsPerformedOverLastSecond_ShouldUpdate_givenOperationsOccurring() {
-
-    int NUMBER_SECONDS_TO_RUN = 3;
-    AtomicInteger numberOfCommandsExecuted = new AtomicInteger();
-    AtomicDouble actual_commandsProcessed = new AtomicDouble();
-    GeodeAwaitility
-        .await()
-        .during(Duration.ofSeconds(NUMBER_SECONDS_TO_RUN))
-        .until(() -> {
-          jedis.set("key", "value");
-          numberOfCommandsExecuted.getAndIncrement();
-          actual_commandsProcessed.set(redisStats.getOpsPerformedOverLastSecond());
-          return true;
-        });
-
-    long expected =
-        (numberOfCommandsExecuted.get() / NUMBER_SECONDS_TO_RUN);
-
-    assertThat(actual_commandsProcessed.get())
-        .isCloseTo(expected, Offset.offset(
-            getTenPercentOf(actual_commandsProcessed.get())));
-
-    // if time passes w/o operations
-    GeodeAwaitility
-        .await()
-        .during(NUMBER_SECONDS_TO_RUN, TimeUnit.SECONDS)
-        .until(() -> true);
-
-    assertThat(redisStats.getOpsPerformedOverLastSecond())
-        .isEqualTo(0);
-  }
-
-  @Test
-  public void networkBytesRead_shouldIncrementBySizeOfCommandSent() {
-    long initialNetworkBytesRead = redisStats.getTotalNetworkBytesRead();
-    String respCommandString = "*3\r\n$3\r\nset\r\n$3\r\nkey\r\n$5\r\nvalue\r\n";
-
-    jedis.set("key", "value");
-
-    statsValidator.validateNetworkBytesRead(initialNetworkBytesRead, respCommandString.length());
-  }
-
-  @Test
-  public void networkKiloBytesReadOverLastSecond_shouldReturnCorrectData() {
-
-    double REASONABLE_SOUNDING_OFFSET = .8;
-    int NUMBER_SECONDS_TO_RUN = 2;
-    String RESP_COMMAND_STRING = "*3\r\n$3\r\nset\r\n$3\r\nkey\r\n$5\r\nvalue\r\n";
-    int BYTES_SENT_PER_COMMAND = RESP_COMMAND_STRING.length();
-    AtomicInteger totalBytesSent = new AtomicInteger();
-    AtomicReference<Double> actual_kbs = new AtomicReference<>((double) 0);
-
-    GeodeAwaitility
-        .await()
-        .during(Duration.ofSeconds(NUMBER_SECONDS_TO_RUN))
-        .until(() -> {
-          jedis.set("key", "value");
-          totalBytesSent.addAndGet(BYTES_SENT_PER_COMMAND);
-          actual_kbs.set(redisStats.getNetworkKiloBytesReadOverLastSecond());
-          return true;
-        });
-
-    double expectedBytesReceived = totalBytesSent.get() / NUMBER_SECONDS_TO_RUN;
-    double expected_kbs = expectedBytesReceived / 1000;
-
-    assertThat(actual_kbs.get()).isCloseTo(expected_kbs,
-        Offset.offset(REASONABLE_SOUNDING_OFFSET));
-
-    // if time passes w/o operations
-    GeodeAwaitility
-        .await()
-        .during(NUMBER_SECONDS_TO_RUN, TimeUnit.SECONDS)
-        .until(() -> true);
-
-    assertThat(redisStats.getNetworkKiloBytesReadOverLastSecond())
-        .isEqualTo(0);
-
-  }
-
-  // ######################### Clients Section #################################
-
-  @Test
-  public void clientsStat_withConnectAndClose_isCorrect() {
-    Jedis jedis2 = new Jedis("localhost", server.getPort(), TIMEOUT);
-    jedis2.ping();
-
-    statsValidator.validateConnectedClients(preTestConnectedClients, 1);
-
-    jedis2.close();
-
-    statsValidator.validateConnectedClients(preTestConnectedClients, 0);
-  }
-
-  @Test
-  public void totalConnectionsReceivedStat_shouldIncrement_whenNewConnectionOccurs() {
-    Jedis jedis2 = new Jedis("localhost", server.getPort(), TIMEOUT);
-    jedis2.ping();
-
-    statsValidator.validateConnectedClients(preTestConnectedClients, 1);
-
-    jedis2.close();
-
-    statsValidator.validateConnectionsReceived(preTestConnectionsReceived, 1);
-  }
-
-  // ######################## Server Section ################
-
-  @Test
-  public void uptimeInSeconds_shouldReturnCorrectValue() {
-    long serverUptimeAtStartOfTestInNanos = getCurrentTime();
-    long statsUpTimeAtStartOfTest = redisStats.getUptimeInSeconds();
-
-    GeodeAwaitility.await().during(Duration.ofSeconds(3)).until(() -> true);
-
-    long expectedNanos = getCurrentTime() - serverUptimeAtStartOfTestInNanos;
-    long expectedSeconds = TimeUnit.NANOSECONDS.toSeconds(expectedNanos);
-
-    assertThat(redisStats.getUptimeInSeconds() - statsUpTimeAtStartOfTest)
-        .isCloseTo(expectedSeconds, Offset.offset(1l));
-  }
-
-  @Test
-  public void upTimeInDays_shouldReturnCorrectValue() {
-    long startTimeInNanos = getStartTime();
-    long currentTimeInNanos = getCurrentTime();
-
-    long expectedNanos = currentTimeInNanos - startTimeInNanos;
-    long expectedDays = TimeUnit.NANOSECONDS.toDays(expectedNanos);
-
-    assertThat(redisStats.getUptimeInDays())
-        .isEqualTo(expectedDays);
-  }
-
-  public long getStartTime() {
-    return START_TIME;
-  }
-
-  public long getCurrentTime() {
-    return this.statisticsClock.getTime();
-  }
-
-  private double getTenPercentOf(Double value) {
-    return Math.ceil(value * .1);
-  }
-}
diff --git a/geode-redis/src/integrationTest/java/org/apache/geode/redis/internal/executor/server/AbstractHitsMissesIntegrationTest.java b/geode-redis/src/integrationTest/java/org/apache/geode/redis/internal/executor/server/AbstractHitsMissesIntegrationTest.java
index ae7d04d..3bc2ac0a 100644
--- a/geode-redis/src/integrationTest/java/org/apache/geode/redis/internal/executor/server/AbstractHitsMissesIntegrationTest.java
+++ b/geode-redis/src/integrationTest/java/org/apache/geode/redis/internal/executor/server/AbstractHitsMissesIntegrationTest.java
@@ -26,6 +26,7 @@
 import org.apache.logging.log4j.util.TriConsumer;
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import redis.clients.jedis.BitOP;
 import redis.clients.jedis.Jedis;
@@ -47,8 +48,10 @@
     jedis = new Jedis("localhost", getPort(), REDIS_CLIENT_TIMEOUT);
 
     jedis.set("string", "yarn");
+    jedis.set("int", "5");
     jedis.sadd("set", "cotton");
     jedis.hset("hash", "green", "eggs");
+    jedis.mset("mapKey1", "fox", "mapKey2", "box");
   }
 
   @After
@@ -57,6 +60,9 @@
     jedis.close();
   }
 
+  /***********************************************
+   ************* Supported Commands **************
+   **********************************************/
   // ------------ Key related commands -----------
 
   @Test
@@ -89,6 +95,11 @@
     runCommandAndAssertNoStatUpdates("string", (k, v) -> jedis.rename(k, v));
   }
 
+  @Test
+  public void testDel() {
+    runCommandAndAssertNoStatUpdates("string", k -> jedis.del(k));
+  }
+
   // ------------ String related commands -----------
 
   @Test
@@ -97,6 +108,107 @@
   }
 
   @Test
+  public void testAppend() {
+    runCommandAndAssertNoStatUpdates("string", (k, v) -> jedis.append(k, v));
+  }
+
+  @Test
+  public void testSet() {
+    runCommandAndAssertNoStatUpdates("string", (k, v) -> jedis.set(k, v));
+  }
+
+  @Test
+  public void testSet_wrongType() {
+    runCommandAndAssertNoStatUpdates("set", (k, v) -> jedis.set(k, v));
+  }
+
+  // ------------ Set related commands -----------
+  @Test
+  public void testSadd() {
+    runCommandAndAssertNoStatUpdates("set", (k, v) -> jedis.sadd(k, v));
+  }
+
+  @Test
+  public void testSrem() {
+    runCommandAndAssertNoStatUpdates("set", (k, v) -> jedis.srem(k, v));
+  }
+
+  @Test
+  public void testSmembers() {
+    runCommandAndAssertHitsAndMisses("set", k -> jedis.smembers(k));
+  }
+
+  // ------------ Hash related commands -----------
+  @Test
+  public void testHset() {
+    runCommandAndAssertNoStatUpdates("hash", (k, v, s) -> jedis.hset(k, v, s));
+  }
+
+  @Test
+  public void testHgetall() {
+    runCommandAndAssertHitsAndMisses("hash", k -> jedis.hgetAll(k));
+  }
+
+  @Test
+  public void testHMSet() {
+    Map<String, String> map = new HashMap<>();
+    map.put("key1", "value1");
+    map.put("key2", "value2");
+
+    runCommandAndAssertNoStatUpdates("key", (k) -> jedis.hmset(k, map));
+  }
+
+  // ------------ Key related commands -----------
+
+  @Test
+  public void testExpire() {
+    runCommandAndAssertNoStatUpdates("hash", (k) -> jedis.expire(k, 5));
+  }
+
+  @Test
+  public void testPassiveExpiration() {
+    runCommandAndAssertNoStatUpdates("hash", (k) -> {
+      jedis.expire(k, 1);
+      GeodeAwaitility.await().during(Duration.ofSeconds(3)).until(() -> true);
+    });
+  }
+
+  @Test
+  public void testExpireAt() {
+    runCommandAndAssertNoStatUpdates("hash", (k) -> jedis.expireAt(k, 2145916800));
+  }
+
+  @Test
+  public void testPExpire() {
+    runCommandAndAssertNoStatUpdates("hash", (k) -> jedis.pexpire(k, 1024));
+  }
+
+  @Test
+  public void testPExpireAt() {
+    runCommandAndAssertNoStatUpdates("hash", (k) -> jedis.pexpireAt(k, 1608247597));
+  }
+
+  @Test
+  public void testPersist() {
+    runCommandAndAssertNoStatUpdates("hash", (k) -> jedis.persist(k));
+  }
+
+  /**********************************************
+   ********** Unsupported Commands **************
+   *********************************************/
+  // ---------- Key related commands -----------
+  @Test
+  public void testScan() {
+    runCommandAndAssertNoStatUpdates("0", k -> jedis.scan(k));
+  }
+
+  @Test
+  public void testUnlink() {
+    runCommandAndAssertNoStatUpdates("string", k -> jedis.unlink(k));
+  }
+
+  // ------------ String related commands -----------
+  @Test
   public void testGetset() {
     runCommandAndAssertHitsAndMisses("string", (k, v) -> jedis.getSet(k, v));
   }
@@ -107,27 +219,68 @@
   }
 
   @Test
-  public void testDel() {
-    runCommandAndAssertNoStatUpdates("string", k -> jedis.del(k));
+  public void testDecr() {
+    runCommandAndAssertNoStatUpdates("int", k -> jedis.decr(k));
   }
 
   @Test
-  public void testSet() {
-    runCommandAndAssertNoStatUpdates("string", (k, v) -> jedis.set(k, v));
+  public void testDecrby() {
+    runCommandAndAssertNoStatUpdates("int", k -> jedis.decrBy(k, 1));
   }
 
   @Test
-  public void testAppend() {
-    runCommandAndAssertNoStatUpdates("string", (k, v) -> jedis.append(k, v));
+  public void testGetrange() {
+    runCommandAndAssertHitsAndMisses("string", k -> jedis.getrange(k, 1l, 2l));
   }
 
   @Test
-  public void testSetWrongType() {
-    runCommandAndAssertNoStatUpdates("set", (k, v) -> jedis.set(k, v));
+  public void testIncr() {
+    runCommandAndAssertNoStatUpdates("int", k -> jedis.incr(k));
+  }
+
+  @Test
+  public void testIncrby() {
+    runCommandAndAssertNoStatUpdates("int", k -> jedis.incrBy(k, 1l));
+  }
+
+  @Test
+  public void testIncrbyfloat() {
+    runCommandAndAssertNoStatUpdates("int", k -> jedis.incrByFloat(k, 1.0));
+  }
+
+  @Test
+  public void testMget() {
+    runCommandAndAssertHitsAndMisses("mapKey1", "mapKey2", (k1, k2) -> jedis.mget(k1, k2));
+  }
+
+  @Test
+  public void testMset() {
+    runCommandAndAssertNoStatUpdates("mapKey1", (k, v) -> jedis.mset(k, v));
+  }
+
+  // todo updates stats when it shouldn't. not implemented in the function executor
+  @Ignore
+  @Test
+  public void testMsetnx() {
+    runCommandAndAssertNoStatUpdates("mapKey1", (k, v) -> jedis.msetnx(k, v));
+  }
+
+  @Test
+  public void testSetex() {
+    runCommandAndAssertNoStatUpdates("string", (k, v) -> jedis.setex(k, 200, v));
+  }
+
+  @Test
+  public void testSetnx() {
+    runCommandAndAssertNoStatUpdates("string", (k, v) -> jedis.setnx(k, v));
+  }
+
+  @Test
+  public void testSetrange() {
+    runCommandAndAssertNoStatUpdates("string", (k, v) -> jedis.setrange(k, 1l, v));
   }
 
   // ------------ Bit related commands -----------
-
   @Test
   public void testBitcount() {
     runCommandAndAssertHitsAndMisses("string", k -> jedis.bitcount(k));
@@ -171,6 +324,16 @@
     assertThat(info.get(MISSES)).isEqualTo(String.valueOf(currentMisses + 1 + 1));
   }
 
+  @Test
+  public void testGetbit() {
+    runCommandAndAssertHitsAndMisses("string", k -> jedis.getbit(k, 1));
+  }
+
+  @Test
+  public void testSetbit() {
+    runCommandAndAssertNoStatUpdates("int", (k, v) -> jedis.setbit(k, 0l, "1"));
+  }
+
   // ------------ Set related commands -----------
   // FYI - In Redis 5.x SPOP produces inconsistent results depending on whether a count was given
   // or not. In Redis 6.x SPOP does not update any stats.
@@ -180,21 +343,6 @@
   }
 
   @Test
-  public void testSadd() {
-    runCommandAndAssertNoStatUpdates("set", (k, v) -> jedis.sadd(k, v));
-  }
-
-  @Test
-  public void testSrem() {
-    runCommandAndAssertNoStatUpdates("set", (k, v) -> jedis.srem(k, v));
-  }
-
-  @Test
-  public void testSmembers() {
-    runCommandAndAssertHitsAndMisses("set", k -> jedis.smembers(k));
-  }
-
-  @Test
   public void testSismember() {
     runCommandAndAssertHitsAndMisses("set", (k, v) -> jedis.sismember(k, v));
   }
@@ -244,29 +392,25 @@
     runDiffStoreCommandAndAssertNoStatUpdates("set", (k, v, s) -> jedis.sunionstore(k, v, s));
   }
 
-  // ------------ Hash related commands -----------
+  // TODO GEODE-8857: our implementation updates the stats when it shouldn't
+  @Ignore
+  @Test
+  public void testSmove() {
+    runCommandAndAssertNoStatUpdates("set", (k, d, m) -> jedis.smove(k, d, m));
+  }
 
+  // ------------ Hash related commands -----------
   @Test
   public void testHdel() {
     runCommandAndAssertNoStatUpdates("hash", (k, v) -> jedis.hdel(k, v));
   }
 
   @Test
-  public void testHset() {
-    runCommandAndAssertNoStatUpdates("hash", (k, v, s) -> jedis.hset(k, v, s));
-  }
-
-  @Test
   public void testHget() {
     runCommandAndAssertHitsAndMisses("hash", (k, v) -> jedis.hget(k, v));
   }
 
   @Test
-  public void testHgetall() {
-    runCommandAndAssertHitsAndMisses("hash", k -> jedis.hgetAll(k));
-  }
-
-  @Test
   public void testHkeys() {
     runCommandAndAssertHitsAndMisses("hash", k -> jedis.hkeys(k));
   }
@@ -302,47 +446,18 @@
   }
 
   @Test
-  public void testHMSet() {
-    Map<String, String> map = new HashMap<>();
-    map.put("key1", "value1");
-    map.put("key2", "value2");
-
-    runCommandAndAssertNoStatUpdates("key", (k) -> jedis.hmset(k, map));
-  }
-
-  // ------------ Key related commands -----------
-
-  @Test
-  public void testExpire() {
-    runCommandAndAssertNoStatUpdates("hash", (k) -> jedis.expire(k, 5));
+  public void testHincrby() {
+    runCommandAndAssertNoStatUpdates("hash", (k, f) -> jedis.hincrBy(k, f, 1l));
   }
 
   @Test
-  public void testPassiveExpiration() {
-    runCommandAndAssertNoStatUpdates("hash", (k) -> {
-      jedis.expire(k, 1);
-      GeodeAwaitility.await().during(Duration.ofSeconds(3)).until(() -> true);
-    });
+  public void testHincrbyfloat() {
+    runCommandAndAssertNoStatUpdates("hash", (k, f) -> jedis.hincrByFloat(k, f, 1.0));
   }
 
   @Test
-  public void testExpireAt() {
-    runCommandAndAssertNoStatUpdates("hash", (k) -> jedis.expireAt(k, 2145916800));
-  }
-
-  @Test
-  public void testPExpire() {
-    runCommandAndAssertNoStatUpdates("hash", (k) -> jedis.pexpire(k, 1024));
-  }
-
-  @Test
-  public void testPExpireAt() {
-    runCommandAndAssertNoStatUpdates("hash", (k) -> jedis.pexpireAt(k, 1608247597));
-  }
-
-  @Test
-  public void testPersist() {
-    runCommandAndAssertNoStatUpdates("hash", (k) -> jedis.persist(k));
+  public void testHsetnx() {
+    runCommandAndAssertNoStatUpdates("hash", (k, f, v) -> jedis.hsetnx(k, f, v));
   }
 
   // ------------ Helper Methods -----------
@@ -383,6 +498,19 @@
     assertThat(info.get(MISSES)).isEqualTo(String.valueOf(currentMisses + 1));
   }
 
+  private void runCommandAndAssertHitsAndMisses(String key1, String key2,
+      BiConsumer<String, String> command) {
+    Map<String, String> info = getInfo(jedis);
+    Long currentHits = Long.parseLong(info.get(HITS));
+    Long currentMisses = Long.parseLong(info.get(MISSES));
+
+    command.accept(key1, key2);
+    info = getInfo(jedis);
+
+    assertThat(info.get(HITS)).isEqualTo(String.valueOf(currentHits + 2));
+    assertThat(info.get(MISSES)).isEqualTo(String.valueOf(currentMisses));
+  }
+
   private void runDiffCommandAndAssertHitsAndMisses(String key,
       BiConsumer<String, String> command) {
     Map<String, String> info = getInfo(jedis);
diff --git a/geode-redis/src/integrationTest/java/org/apache/geode/redis/internal/executor/server/AbstractRedisInfoStatsIntegrationTest.java b/geode-redis/src/integrationTest/java/org/apache/geode/redis/internal/executor/server/AbstractRedisInfoStatsIntegrationTest.java
new file mode 100644
index 0000000..4e72595
--- /dev/null
+++ b/geode-redis/src/integrationTest/java/org/apache/geode/redis/internal/executor/server/AbstractRedisInfoStatsIntegrationTest.java
@@ -0,0 +1,318 @@
+/*
+ * 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.server;
+
+import static org.apache.geode.test.awaitility.GeodeAwaitility.await;
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.time.Duration;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+
+import com.google.common.util.concurrent.AtomicDouble;
+import org.assertj.core.data.Offset;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import redis.clients.jedis.Jedis;
+
+import org.apache.geode.internal.statistics.EnabledStatisticsClock;
+import org.apache.geode.internal.statistics.StatisticsClock;
+import org.apache.geode.test.awaitility.GeodeAwaitility;
+import org.apache.geode.test.dunit.rules.RedisPortSupplier;
+
+public abstract class AbstractRedisInfoStatsIntegrationTest implements RedisPortSupplier {
+
+  private static final int TIMEOUT = (int) GeodeAwaitility.getTimeout().toMillis();
+  private static final String EXISTING_HASH_KEY = "Existing_Hash";
+  private static final String EXISTING_STRING_KEY = "Existing_String";
+  private static final String EXISTING_SET_KEY_1 = "Existing_Set_1";
+  private static final String EXISTING_SET_KEY_2 = "Existing_Set_2";
+
+  private Jedis jedis;
+  private static long START_TIME;
+  private static StatisticsClock statisticsClock;
+
+  private long preTestConnectionsReceived = 0;
+  private long preTestConnectedClients = 0;
+
+  private static final String COMMANDS_PROCESSED = "total_commands_processed";
+  private static final String TOTAL_CONNECTIONS_RECEIVED = "total_connections_received";
+  private static final String CONNECTED_CLIENTS = "connected_clients";
+  private static final String OPS_PERFORMED_OVER_LAST_SECOND = "instantaneous_ops_per_sec";
+  private static final String TOTAL_NETWORK_BYTES_READ = "total_net_input_bytes";
+  private static final String NETWORK_KB_READ_OVER_LAST_SECOND = "instantaneous_input_kbps";
+  private static final String UPTIME_IN_DAYS = "uptime_in_days";
+  private static final String UPTIME_IN_SECONDS = "uptime_in_seconds";
+
+  private static final AtomicInteger numInfoCalled = new AtomicInteger(0);
+
+
+  // ------------------- Setup -------------------------- //
+  @BeforeClass
+  public static void beforeClass() {
+    statisticsClock = new EnabledStatisticsClock();
+    START_TIME = statisticsClock.getTime();
+  }
+
+  @Before
+  public void before() {
+    jedis = new Jedis("localhost", getPort(), TIMEOUT);
+    numInfoCalled.set(0);
+
+    long preSetupCommandsProcessed = Long.valueOf(getInfo(jedis).get(COMMANDS_PROCESSED));
+
+    jedis.set(EXISTING_STRING_KEY, "A_Value");
+    jedis.hset(EXISTING_HASH_KEY, "Field1", "Value1");
+    jedis.sadd(EXISTING_SET_KEY_1, "m1", "m2", "m3");
+    jedis.sadd(EXISTING_SET_KEY_2, "m4", "m5", "m6");
+
+    // the info command increments command processed so we need to account for that.
+    // the +1 is needed because info returns the number of commands processed before that call to
+    // info
+    await().atMost(Duration.ofSeconds(2))
+        .untilAsserted(() -> assertThat(
+            Long.valueOf(getInfo(jedis).get(COMMANDS_PROCESSED)) - numInfoCalled.get() + 1)
+                .isEqualTo(preSetupCommandsProcessed + 4));
+
+    preTestConnectionsReceived = Long.valueOf(getInfo(jedis).get(TOTAL_CONNECTIONS_RECEIVED));
+    preTestConnectedClients = Long.valueOf(getInfo(jedis).get(CONNECTED_CLIENTS));
+    numInfoCalled.set(0);
+  }
+
+  @After
+  public void after() {
+    jedis.flushAll();
+    await().atMost(Duration.ofSeconds(5))
+        .untilAsserted(
+            () -> assertThat(Long.valueOf(getInfo(jedis).get(CONNECTED_CLIENTS))).isEqualTo(1));
+    jedis.close();
+  }
+
+  // ------------------- Stats Section -------------------------- //
+
+  // note: see AbstractHitsMissesIntegrationTest for testing of hits/misses
+
+  @Test
+  public void commandsProcessed_shouldIncrement_givenSuccessfulCommand() {
+    long initialCommandsProcessed = Long.valueOf(getInfo(jedis).get(COMMANDS_PROCESSED));
+    jedis.ttl("key");
+
+    validateCommandsProcessed(jedis, initialCommandsProcessed, 1);
+  }
+
+  @Test
+  public void opsPerformedOverLastSecond_ShouldUpdate_givenOperationsOccurring() {
+    int NUMBER_SECONDS_TO_RUN = 3;
+
+    AtomicInteger numberOfCommandsExecuted = new AtomicInteger();
+    AtomicDouble actualCommandsProcessedOverLastSecond = new AtomicDouble();
+
+    await().during(Duration.ofSeconds(NUMBER_SECONDS_TO_RUN)).until(() -> {
+      jedis.set("key", "value");
+      numberOfCommandsExecuted.getAndIncrement();
+      actualCommandsProcessedOverLastSecond.set(
+          Double.valueOf(getInfo(jedis).get(OPS_PERFORMED_OVER_LAST_SECOND)));
+
+      return true;
+    });
+
+    long expected = (numberOfCommandsExecuted.get() + numInfoCalled.get()) / NUMBER_SECONDS_TO_RUN;
+
+    assertThat(actualCommandsProcessedOverLastSecond.get())
+        .isCloseTo(expected,
+            Offset.offset(getTenPercentOf(actualCommandsProcessedOverLastSecond.get())));
+
+    // if time passes w/o operations
+    await().during(NUMBER_SECONDS_TO_RUN, TimeUnit.SECONDS).until(() -> true);
+
+    assertThat(Double.valueOf(getInfo(jedis).get(OPS_PERFORMED_OVER_LAST_SECOND))).isEqualTo(0D);
+  }
+
+  @Test
+  public void networkBytesRead_shouldIncrementBySizeOfCommandSent() {
+    long initialNetworkBytesRead = Long.valueOf(getInfo(jedis).get(TOTAL_NETWORK_BYTES_READ));
+    String infoCommandString = "*3\r\n$3\r\ninfo\r\n";
+    String respCommandString = "*3\r\n$3\r\nset\r\n$3\r\nkey\r\n$5\r\nvalue\r\n";
+
+    jedis.set("key", "value");
+
+    validateNetworkBytesRead(jedis, initialNetworkBytesRead,
+        respCommandString.length() + infoCommandString.length());
+  }
+
+  @Test
+  public void networkKiloBytesReadOverLastSecond_shouldBeCloseToBytesReadOverLastSecond() {
+
+    double REASONABLE_SOUNDING_OFFSET = .8;
+    int NUMBER_SECONDS_TO_RUN = 2;
+    String RESP_COMMAND_STRING = "*3\r\n$3\r\nset\r\n$3\r\nkey\r\n$5\r\nvalue\r\n";
+    int BYTES_SENT_PER_COMMAND = RESP_COMMAND_STRING.length();
+    AtomicInteger totalBytesSent = new AtomicInteger();
+    AtomicReference<Double> actual_kbs = new AtomicReference<>((double) 0);
+
+    await().during(Duration.ofSeconds(NUMBER_SECONDS_TO_RUN)).until(() -> {
+      jedis.set("key", "value");
+      totalBytesSent.addAndGet(BYTES_SENT_PER_COMMAND);
+      actual_kbs.set(Double.valueOf(getInfo(jedis).get(NETWORK_KB_READ_OVER_LAST_SECOND)));
+      return true;
+    });
+
+    double expectedBytesReceived = totalBytesSent.get() / NUMBER_SECONDS_TO_RUN;
+    double expected_kbs = expectedBytesReceived / 1000;
+
+    assertThat(actual_kbs.get()).isCloseTo(expected_kbs, Offset.offset(REASONABLE_SOUNDING_OFFSET));
+
+    // if time passes w/o operations
+    await()
+        .during(NUMBER_SECONDS_TO_RUN, TimeUnit.SECONDS)
+        .until(() -> true);
+
+    assertThat(Double.valueOf(getInfo(jedis).get(NETWORK_KB_READ_OVER_LAST_SECOND))).isEqualTo(0);
+
+  }
+
+  // todo test rejected connections
+  @Test
+  public void should_UpdateRejectedConnections() {
+    Jedis jedis2 = new Jedis("localhost", getPort(), TIMEOUT);
+    Jedis jedis3 = new Jedis("localhost", getPort(), TIMEOUT);
+
+    jedis2.ping();
+    jedis3.ping();
+
+    validateConnectedClients(jedis, preTestConnectedClients, 2);
+
+    jedis2.close();
+    jedis3.close();
+
+    validateConnectedClients(jedis, preTestConnectedClients, 0);
+  }
+
+  // ------------------- Clients Section -------------------------- //
+
+  @Test
+  public void connectedClients_incrAndDecrWhenClientConnectsAndDisconnects() {
+    Jedis jedis2 = new Jedis("localhost", getPort(), TIMEOUT);
+    jedis2.ping();
+
+    validateConnectedClients(jedis, preTestConnectedClients, 1);
+
+    jedis2.close();
+
+    validateConnectedClients(jedis, preTestConnectedClients, 0);
+  }
+
+  @Test
+  public void totalConnectionsReceivedStat_shouldIncrement_whenNewConnectionOccurs() {
+    Jedis jedis2 = new Jedis("localhost", getPort(), TIMEOUT);
+    jedis2.ping();
+
+    validateConnectionsReceived(jedis, preTestConnectionsReceived, 1);
+
+    jedis2.close();
+
+    validateConnectedClients(jedis, preTestConnectedClients, 0);
+  }
+
+  // ------------------- Server Section -------------------------- //
+
+  @Test
+  public void upTimeInDays_shouldBeEqualToTimeSinceStartInDays() {
+    long startTimeInNanos = getStartTime();
+    long currentTimeInNanos = getCurrentTime();
+
+    long expectedNanos = currentTimeInNanos - startTimeInNanos;
+    long expectedDays = TimeUnit.NANOSECONDS.toDays(expectedNanos);
+
+    assertThat(Long.valueOf(getInfo(jedis).get(UPTIME_IN_DAYS))).isEqualTo(expectedDays);
+  }
+
+  @Test
+  public void uptimeInSeconds_shouldReturnTimeSinceStartInSeconds() {
+    long serverUptimeAtStartOfTestInNanos = getCurrentTime();
+    long statsUpTimeAtStartOfTest = Long.valueOf(getInfo(jedis).get(UPTIME_IN_SECONDS));
+
+    await().during(Duration.ofSeconds(3)).until(() -> true);
+
+    long expectedNanos = getCurrentTime() - serverUptimeAtStartOfTestInNanos;
+    long expectedSeconds = TimeUnit.NANOSECONDS.toSeconds(expectedNanos);
+
+    assertThat(Long.valueOf(getInfo(jedis).get(UPTIME_IN_SECONDS)) - statsUpTimeAtStartOfTest)
+        .isCloseTo(expectedSeconds, Offset.offset(1l));
+  }
+
+  // ------------------- Helper Methods ----------------------------- //
+  public long getStartTime() {
+    return START_TIME;
+  }
+
+  public long getCurrentTime() {
+    return this.statisticsClock.getTime();
+  }
+
+  private double getTenPercentOf(Double value) {
+    return Math.ceil(value * .1);
+  }
+
+  /**
+   * Convert the values returned by the INFO command into a basic param:value map.
+   */
+  static synchronized Map<String, String> getInfo(Jedis jedis) {
+    Map<String, String> results = new HashMap<>();
+    String rawInfo = jedis.info();
+    numInfoCalled.incrementAndGet();
+
+    for (String line : rawInfo.split("\r\n")) {
+      int colonIndex = line.indexOf(":");
+      if (colonIndex > 0) {
+        String key = line.substring(0, colonIndex);
+        String value = line.substring(colonIndex + 1);
+        results.put(key, value);
+      }
+    }
+
+    return results;
+  }
+
+  private void validateNetworkBytesRead(Jedis jedis, long initialNetworkBytesRead,
+      int responseLength) {
+    await().atMost(Duration.ofSeconds(2)).untilAsserted(
+        () -> assertThat(Long.valueOf(getInfo(jedis).get(TOTAL_NETWORK_BYTES_READ)))
+            .isEqualTo(initialNetworkBytesRead + responseLength));
+  }
+
+  private void validateCommandsProcessed(Jedis jedis, long initialCommandsProcessed, int diff) {
+    await().atMost(Duration.ofSeconds(2)).untilAsserted(
+        () -> assertThat(
+            Long.valueOf(getInfo(jedis).get(COMMANDS_PROCESSED)) - numInfoCalled.get() + 1)
+                .isEqualTo(initialCommandsProcessed + diff));
+  }
+
+  private void validateConnectedClients(Jedis jedis, long initialConnectedClients, int diff) {
+    await().atMost(Duration.ofSeconds(2)).untilAsserted(
+        () -> assertThat(Long.valueOf(getInfo(jedis).get(CONNECTED_CLIENTS)))
+            .isEqualTo(initialConnectedClients + diff));
+  }
+
+  private void validateConnectionsReceived(Jedis jedis, long initialConnectionsReceived, int diff) {
+    assertThat(Long.valueOf(getInfo(jedis).get(TOTAL_CONNECTIONS_RECEIVED)))
+        .isEqualTo(initialConnectionsReceived + diff);
+  }
+}
diff --git a/geode-redis/src/integrationTest/java/org/apache/geode/redis/internal/executor/server/InfoStatsIntegrationTest.java b/geode-redis/src/integrationTest/java/org/apache/geode/redis/internal/executor/server/InfoStatsIntegrationTest.java
new file mode 100644
index 0000000..979a129
--- /dev/null
+++ b/geode-redis/src/integrationTest/java/org/apache/geode/redis/internal/executor/server/InfoStatsIntegrationTest.java
@@ -0,0 +1,30 @@
+/*
+ * 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.server;
+
+import org.junit.ClassRule;
+
+import org.apache.geode.redis.GeodeRedisServerRule;
+
+public class InfoStatsIntegrationTest extends AbstractRedisInfoStatsIntegrationTest {
+  @ClassRule
+  public static GeodeRedisServerRule server = new GeodeRedisServerRule();
+
+  @Override
+  public int getPort() {
+    return server.getPort();
+  }
+}
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 044a1d1..4efdb0c 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
@@ -176,8 +176,7 @@
   UNSUBSCRIBE(new UnsubscribeExecutor(), SUPPORTED, new MinimumParameterRequirements(1)),
 
   /***************************************
-   ********** Internal Commands **********
-   * /
+   ********* Internal Commands ***********
    ***************************************/
   // do not call these directly, only to be used in other commands
   INTERNALPTTL(null, INTERNAL, new ExactParameterRequirements(2)),
diff --git a/geode-redis/src/main/java/org/apache/geode/redis/internal/data/RedisHashCommandsFunctionExecutor.java b/geode-redis/src/main/java/org/apache/geode/redis/internal/data/RedisHashCommandsFunctionExecutor.java
index 4fc72c6..c621034 100644
--- a/geode-redis/src/main/java/org/apache/geode/redis/internal/data/RedisHashCommandsFunctionExecutor.java
+++ b/geode-redis/src/main/java/org/apache/geode/redis/internal/data/RedisHashCommandsFunctionExecutor.java
@@ -102,14 +102,14 @@
   @Override
   public long hincrby(ByteArrayWrapper key, ByteArrayWrapper field, long increment) {
     return stripedExecute(key,
-        () -> getRedisHash(key, true)
+        () -> getRedisHash(key, false)
             .hincrby(getRegion(), key, field, increment));
   }
 
   @Override
   public double hincrbyfloat(ByteArrayWrapper key, ByteArrayWrapper field, double increment) {
     return stripedExecute(key,
-        () -> getRedisHash(key, true)
+        () -> getRedisHash(key, false)
             .hincrbyfloat(getRegion(), key, field, increment));
   }
 
diff --git a/geode-redis/src/main/java/org/apache/geode/redis/internal/data/RedisStringCommandsFunctionExecutor.java b/geode-redis/src/main/java/org/apache/geode/redis/internal/data/RedisStringCommandsFunctionExecutor.java
index 38643f0..f162bc0 100644
--- a/geode-redis/src/main/java/org/apache/geode/redis/internal/data/RedisStringCommandsFunctionExecutor.java
+++ b/geode-redis/src/main/java/org/apache/geode/redis/internal/data/RedisStringCommandsFunctionExecutor.java
@@ -64,12 +64,12 @@
 
   @Override
   public long incr(ByteArrayWrapper key) {
-    return stripedExecute(key, () -> getRedisString(key, true).incr(getRegion(), key));
+    return stripedExecute(key, () -> getRedisString(key, false).incr(getRegion(), key));
   }
 
   @Override
   public long decr(ByteArrayWrapper key) {
-    return stripedExecute(key, () -> getRedisString(key, true).decr(getRegion(), key));
+    return stripedExecute(key, () -> getRedisString(key, false).decr(getRegion(), key));
   }
 
   @Override
@@ -81,13 +81,13 @@
   @Override
   public long incrby(ByteArrayWrapper key, long increment) {
     return stripedExecute(key,
-        () -> getRedisString(key, true).incrby(getRegion(), key, increment));
+        () -> getRedisString(key, false).incrby(getRegion(), key, increment));
   }
 
   @Override
   public double incrbyfloat(ByteArrayWrapper key, double increment) {
     return stripedExecute(key,
-        () -> getRedisString(key, true)
+        () -> getRedisString(key, false)
             .incrbyfloat(getRegion(), key, increment));
   }
 
@@ -100,7 +100,7 @@
   @Override
   public long decrby(ByteArrayWrapper key, long decrement) {
     return stripedExecute(key,
-        () -> getRedisString(key, true).decrby(getRegion(), key, decrement));
+        () -> getRedisString(key, false).decrby(getRegion(), key, decrement));
   }
 
   @Override
@@ -111,7 +111,7 @@
   @Override
   public int setrange(ByteArrayWrapper key, int offset, byte[] value) {
     return stripedExecute(key,
-        () -> getRedisString(key, true)
+        () -> getRedisString(key, false)
             .setrange(getRegion(), key, offset, value));
   }
 
@@ -147,7 +147,7 @@
     int byteIndex = (int) (offset / 8);
     byte bitIndex = (byte) (offset % 8);
     return stripedExecute(key,
-        () -> getRedisString(key, true)
+        () -> getRedisString(key, false)
             .setbit(getRegion(), key, value, byteIndex, bitIndex));
   }
 
diff --git a/geode-redis/src/main/java/org/apache/geode/redis/internal/statistics/RedisStats.java b/geode-redis/src/main/java/org/apache/geode/redis/internal/statistics/RedisStats.java
index fbaa99e..9ec23d5 100644
--- a/geode-redis/src/main/java/org/apache/geode/redis/internal/statistics/RedisStats.java
+++ b/geode-redis/src/main/java/org/apache/geode/redis/internal/statistics/RedisStats.java
@@ -44,6 +44,7 @@
   private final GeodeRedisStats geodeRedisStats;
   private final long START_TIME_IN_NANOS;
 
+
   public RedisStats(StatisticsClock clock,
       GeodeRedisStats geodeRedisStats) {
 
@@ -53,7 +54,6 @@
     START_TIME_IN_NANOS = clock.getTime();
   }
 
-
   public void incCommandsProcessed() {
     commandsProcessed.incrementAndGet();
     geodeRedisStats.incrementCommandsProcessed();
@@ -197,8 +197,7 @@
 
   private void updateOpsPerformedOverLastSecond() {
     long totalOpsPerformed = getCommandsProcessed();
-    long opsPerformedThisTick = totalOpsPerformed - opsPerformedLastTick;
-    opsPerformedOverLastSecond = opsPerformedThisTick;
+    opsPerformedOverLastSecond = totalOpsPerformed - opsPerformedLastTick;
     opsPerformedLastTick = getCommandsProcessed();
   }
 }