DonalEvans commented on a change in pull request #7278:
URL: https://github.com/apache/geode/pull/7278#discussion_r796013240



##########
File path: 
geode-for-redis/src/integrationTest/java/org/apache/geode/redis/internal/commands/executor/set/AbstractSScanIntegrationTest.java
##########
@@ -54,295 +72,394 @@ public void tearDown() {
   }
 
   @Test
-  public void givenNoKeyArgument_returnsWrongNumberOfArgumentsError() {
-    assertThatThrownBy(() -> jedis.sendCommand("key", Protocol.Command.SSCAN))
-        .hasMessageContaining("ERR wrong number of arguments for 'sscan' 
command");
+  public void givenLessThanTwoArguments_returnsWrongNumberOfArgumentsError() {
+    assertAtLeastNArgs(jedis, Protocol.Command.SSCAN, 2);
   }
 
   @Test
-  public void givenNoCursorArgument_returnsWrongNumberOfArgumentsError() {
-    assertThatThrownBy(() -> jedis.sendCommand("key", Protocol.Command.SSCAN, 
"key"))
-        .hasMessageContaining("ERR wrong number of arguments for 'sscan' 
command");
+  public void givenNonexistentKey_returnsEmptyArray() {
+    ScanResult<String> result = jedis.sscan("nonexistentKey", ZERO_CURSOR);
+    assertThat(result.isCompleteIteration()).isTrue();
+    assertThat(result.getResult()).isEmpty();
   }
 
   @Test
-  public void givenArgumentsAreNotOddAndKeyExists_returnsSyntaxError() {
-    jedis.sadd("a", "1");
-    assertThatThrownBy(() -> jedis.sendCommand("a", Protocol.Command.SSCAN, 
"a", "0", "a*"))
-        .hasMessageContaining(ERROR_SYNTAX);
+  public void 
givenNonexistentKeyAndIncorrectOptionalArguments_returnsEmptyArray() {
+    ScanResult<byte[]> result =
+        sendCustomSscanCommand("nonexistentKey", "nonexistentKey", 
ZERO_CURSOR, "ANY");
+    assertThat(result.getResult()).isEmpty();
   }
 
   @Test
-  @SuppressWarnings("unchecked")
-  public void givenArgumentsAreNotOddAndKeyDoesNotExist_returnsEmptyArray() {
-    List<Object> result =
-        (List<Object>) jedis.sendCommand("key!", Protocol.Command.SSCAN, 
"key!", "0", "a*");
+  public void givenIncorrectOptionalArgumentsAndKeyExists_returnsSyntaxError() 
{
+    jedis.sadd(KEY, MEMBER_ONE);
+    assertThatThrownBy(
+        () -> jedis.sendCommand(KEY, Protocol.Command.SSCAN, KEY, ZERO_CURSOR, 
"a*"))
+            .hasMessageContaining(ERROR_SYNTAX);
+  }
 
-    assertThat((byte[]) result.get(0)).isEqualTo("0".getBytes());
-    assertThat((List<Object>) result.get(1)).isEmpty();
+  @Test
+  public void 
givenMatchArgumentWithoutPatternOnExistingKey_returnsSyntaxError() {
+    jedis.sadd(KEY, MEMBER_ONE);
+    assertThatThrownBy(
+        () -> jedis.sendCommand(KEY, Protocol.Command.SSCAN, KEY, ZERO_CURSOR, 
"MATCH"))
+            .hasMessageContaining(ERROR_SYNTAX);
   }
 
   @Test
-  public void givenMatchOrCountKeywordNotSpecified_returnsSyntaxError() {
-    jedis.sadd("a", "1");
-    assertThatThrownBy(() -> jedis.sendCommand("a", Protocol.Command.SSCAN, 
"a", "0", "a*", "1"))
-        .hasMessageContaining(ERROR_SYNTAX);
+  public void 
givenCountArgumentWithoutNumberOnExistingKey_returnsSyntaxError() {
+    jedis.sadd(KEY, MEMBER_ONE);
+    assertThatThrownBy(
+        () -> jedis.sendCommand(KEY, Protocol.Command.SSCAN, KEY, ZERO_CURSOR, 
"COUNT"))
+            .hasMessageContaining(ERROR_SYNTAX);
+  }
+
+  @Test
+  public void 
givenAdditionalArgumentNotEqualToMatchOrCount_returnsSyntaxError() {
+    jedis.sadd(KEY, MEMBER_ONE);
+    assertThatThrownBy(
+        () -> jedis.sendCommand(KEY, Protocol.Command.SSCAN, KEY, ZERO_CURSOR, 
"a*", "1"))
+            .hasMessageContaining(ERROR_SYNTAX);
   }
 
   @Test
   public void 
givenCount_whenCountParameterIsNotAnInteger_returnsNotIntegerError() {
-    jedis.sadd("a", "1");
+    jedis.sadd(KEY, MEMBER_ONE);
     assertThatThrownBy(
-        () -> jedis.sendCommand("a", Protocol.Command.SSCAN, "a", "0", 
"COUNT", "MATCH"))
-            .hasMessageContaining(ERROR_NOT_INTEGER);
+        () -> jedis.sendCommand(KEY, Protocol.Command.SSCAN, KEY, ZERO_CURSOR, 
"COUNT",
+            "notAnInteger"))
+                .hasMessageContaining(ERROR_NOT_INTEGER);
   }
 
   @Test
   public void 
givenMultipleCounts_whenAnyCountParameterIsNotAnInteger_returnsNotIntegerError()
 {
-    jedis.sadd("a", "1");
-    assertThatThrownBy(() -> jedis.sendCommand("a", Protocol.Command.SSCAN, 
"a", "0", "COUNT", "2",
-        "COUNT", "sjlfs", "COUNT", "1"))
-            .hasMessageContaining(ERROR_NOT_INTEGER);
+    jedis.sadd(KEY, MEMBER_ONE);
+    assertThatThrownBy(
+        () -> jedis.sendCommand(KEY, Protocol.Command.SSCAN, KEY, ZERO_CURSOR, 
"COUNT", "12",
+            "COUNT", "notAnInteger", "COUNT", "1"))
+                .hasMessageContaining(ERROR_NOT_INTEGER);
   }
 
   @Test
   public void 
givenMultipleCounts_whenAnyCountParameterIsLessThanOne_returnsSyntaxError() {
-    jedis.sadd("a", "1");
-    assertThatThrownBy(() -> jedis.sendCommand("a", Protocol.Command.SSCAN, 
"a", "0", "COUNT", "2",
-        "COUNT", "0", "COUNT", "1"))
-            .hasMessageContaining(ERROR_SYNTAX);
+    jedis.sadd(KEY, MEMBER_ONE);
+    assertThatThrownBy(
+        () -> jedis.sendCommand(KEY, Protocol.Command.SSCAN, KEY, ZERO_CURSOR, 
"COUNT", "12",
+            "COUNT", "0", "COUNT", "1"))
+                .hasMessageContaining(ERROR_SYNTAX);
   }
 
   @Test
   public void givenCount_whenCountParameterIsZero_returnsSyntaxError() {
-    jedis.sadd("a", "1");
-
-    assertThatThrownBy(() -> jedis.sendCommand("a", Protocol.Command.SSCAN, 
"a", "0", "COUNT", "0"))
-        .hasMessageContaining(ERROR_SYNTAX);
+    jedis.sadd(KEY, MEMBER_ONE);
+    assertThatThrownBy(
+        () -> jedis.sscan(KEY_BYTES, ZERO_CURSOR_BYTES, new 
ScanParams().count(0)))
+            .hasMessageContaining(ERROR_SYNTAX);
   }
 
   @Test
   public void givenCount_whenCountParameterIsNegative_returnsSyntaxError() {
-    jedis.sadd("a", "1");
-
+    jedis.sadd(KEY, MEMBER_ONE);
     assertThatThrownBy(
-        () -> jedis.sendCommand("a", Protocol.Command.SSCAN, "a", "0", 
"COUNT", "-37"))
+        () -> jedis.sscan(KEY_BYTES, ZERO_CURSOR_BYTES, new 
ScanParams().count(-37)))
             .hasMessageContaining(ERROR_SYNTAX);
   }
 
   @Test
   public void givenKeyIsNotASet_returnsWrongTypeError() {
-    jedis.hset("a", "b", "1");
-
+    jedis.hset(KEY, "b", MEMBER_ONE);
     assertThatThrownBy(
-        () -> jedis.sendCommand("a", Protocol.Command.SSCAN, "a", "0", 
"COUNT", "-37"))
+        () -> jedis.sscan(KEY, ZERO_CURSOR))
             .hasMessageContaining(ERROR_WRONG_TYPE);
   }
 
   @Test
   public void 
givenKeyIsNotASet_andCursorIsNotAnInteger_returnsInvalidCursorError() {
-    jedis.hset("a", "b", "1");
-
-    assertThatThrownBy(() -> jedis.sendCommand("a", Protocol.Command.SSCAN, 
"a", "sjfls"))
-        .hasMessageContaining(ERROR_CURSOR);
+    jedis.hset(KEY, "b", MEMBER_ONE);
+    assertThatThrownBy(
+        () -> jedis.sscan(KEY, "notAnInteger"))
+            .hasMessageContaining(ERROR_CURSOR);
   }
 
   @Test
   public void 
givenNonexistentKey_andCursorIsNotInteger_returnsInvalidCursorError() {
     assertThatThrownBy(
-        () -> jedis.sendCommand("notReal", Protocol.Command.SSCAN, "notReal", 
"notReal", "sjfls"))
+        () -> jedis.sscan("nonexistentKey", "notAnInteger"))
             .hasMessageContaining(ERROR_CURSOR);
   }
 
   @Test
   public void 
givenExistentSetKey_andCursorIsNotAnInteger_returnsInvalidCursorError() {
-    jedis.set("a", "b");
-
-    assertThatThrownBy(() -> jedis.sendCommand("a", Protocol.Command.SSCAN, 
"a", "sjfls"))
-        .hasMessageContaining(ERROR_CURSOR);
+    jedis.set(KEY, "b");
+    assertThatThrownBy(
+        () -> jedis.sscan(KEY, "notAnInteger"))
+            .hasMessageContaining(ERROR_CURSOR);
   }
 
   @Test
-  public void givenNonexistentKey_returnsEmptyArray() {
-    ScanResult<String> result = jedis.sscan("nonexistent", "0");
-
-    assertThat(result.isCompleteIteration()).isTrue();
-    assertThat(result.getResult()).isEmpty();
+  public void givenNegativeCursor_doesNotError() {
+    initializeThousandMemberSet();
+    assertThatNoException().isThrownBy(() -> jedis.sscan(KEY, "-1"));
   }
 
   @Test
   public void givenSetWithOneMember_returnsMember() {
-    jedis.sadd("a", "1");
-    ScanResult<String> result = jedis.sscan("a", "0");
+    jedis.sadd(KEY, MEMBER_ONE);
+
+    ScanResult<String> result = jedis.sscan(KEY, ZERO_CURSOR);
 
     assertThat(result.isCompleteIteration()).isTrue();
-    assertThat(result.getResult()).containsExactly("1");
+    assertThat(result.getResult()).containsOnly(MEMBER_ONE);
   }
 
   @Test
-  public void givenSetWithMultipleMembers_returnsAllMembers() {
-    jedis.sadd("a", "1", "2", "3");
-    ScanResult<String> result = jedis.sscan("a", "0");
+  public void givenSetWithMultipleMembers_returnsSubsetOfMembers() {
+    Set<String> initialMemberData = initializeThousandMemberSet();
 
-    assertThat(result.isCompleteIteration()).isTrue();
-    assertThat(result.getResult()).containsExactlyInAnyOrder("1", "2", "3");
+    ScanResult<String> result = jedis.sscan(KEY, ZERO_CURSOR);
+
+    assertThat(result.getResult()).isSubsetOf(initialMemberData);
   }
 
   @Test
   public void givenCount_returnsAllMembersWithoutDuplicates() {
-    jedis.sadd("a", "1", "2", "3");
+    Set<byte[]> initialTotalSet = initializeThousandMemberByteSet();
+    int count = 99;
 
-    ScanParams scanParams = new ScanParams();
-    scanParams.count(1);
-    String cursor = "0";
-    ScanResult<byte[]> result;
-    List<byte[]> allMembersFromScan = new ArrayList<>();
+    ScanResult<byte[]> result =
+        jedis.sscan(KEY_BYTES, ZERO_CURSOR_BYTES, new 
ScanParams().count(count));
 
-    do {
-      result = jedis.sscan("a".getBytes(), cursor.getBytes(), scanParams);
-      allMembersFromScan.addAll(result.getResult());
-      cursor = result.getCursor();
-    } while (!result.isCompleteIteration());
+    assertThat(result.getResult().size()).isGreaterThanOrEqualTo(count);
+    assertThat(result.getResult()).isSubsetOf(initialTotalSet);
+  }
 
-    assertThat(allMembersFromScan).containsExactlyInAnyOrder("1".getBytes(),
-        "2".getBytes(),
-        "3".getBytes());
+  @Test
+  public void givenMultipleCounts_usesLastCountSpecified() {
+    Set<byte[]> initialMemberData = initializeThousandMemberByteSet();
+    // Choose two COUNT arguments with a large difference, so that it's 
extremely unlikely that if
+    // the first COUNT is used, a number of members greater than or equal to 
the second COUNT will
+    // be returned.
+    int firstCount = 1;
+    int secondCount = 500;
+    ScanParams scanParams = new 
ScanParams().count(firstCount).count(secondCount);

Review comment:
       It's not possible to test the server's behaviour with multiple MATCH or 
COUNT parameters used if you try to specify them using `ScanParameters`, 
because internally, `ScanParameters` uses a map to store the values, meaning 
the multiple calls to `match()` or `count()` just overwrite previous calls. In 
order to test the behaviour we're interested in, you need to use the 
sendCustomSscanCommand helper method:
   ```
       result = sendCustomSscanCommand(KEY, KEY, ZERO_CURSOR,
           "COUNT", String.valueOf(firstCount),
           "COUNT", String.valueOf(secondCount));
   ```

##########
File path: 
geode-for-redis/src/integrationTest/java/org/apache/geode/redis/internal/commands/executor/set/AbstractSScanIntegrationTest.java
##########
@@ -54,295 +72,394 @@ public void tearDown() {
   }
 
   @Test
-  public void givenNoKeyArgument_returnsWrongNumberOfArgumentsError() {
-    assertThatThrownBy(() -> jedis.sendCommand("key", Protocol.Command.SSCAN))
-        .hasMessageContaining("ERR wrong number of arguments for 'sscan' 
command");
+  public void givenLessThanTwoArguments_returnsWrongNumberOfArgumentsError() {
+    assertAtLeastNArgs(jedis, Protocol.Command.SSCAN, 2);
   }
 
   @Test
-  public void givenNoCursorArgument_returnsWrongNumberOfArgumentsError() {
-    assertThatThrownBy(() -> jedis.sendCommand("key", Protocol.Command.SSCAN, 
"key"))
-        .hasMessageContaining("ERR wrong number of arguments for 'sscan' 
command");
+  public void givenNonexistentKey_returnsEmptyArray() {
+    ScanResult<String> result = jedis.sscan("nonexistentKey", ZERO_CURSOR);
+    assertThat(result.isCompleteIteration()).isTrue();
+    assertThat(result.getResult()).isEmpty();
   }
 
   @Test
-  public void givenArgumentsAreNotOddAndKeyExists_returnsSyntaxError() {
-    jedis.sadd("a", "1");
-    assertThatThrownBy(() -> jedis.sendCommand("a", Protocol.Command.SSCAN, 
"a", "0", "a*"))
-        .hasMessageContaining(ERROR_SYNTAX);
+  public void 
givenNonexistentKeyAndIncorrectOptionalArguments_returnsEmptyArray() {
+    ScanResult<byte[]> result =
+        sendCustomSscanCommand("nonexistentKey", "nonexistentKey", 
ZERO_CURSOR, "ANY");
+    assertThat(result.getResult()).isEmpty();
   }
 
   @Test
-  @SuppressWarnings("unchecked")
-  public void givenArgumentsAreNotOddAndKeyDoesNotExist_returnsEmptyArray() {
-    List<Object> result =
-        (List<Object>) jedis.sendCommand("key!", Protocol.Command.SSCAN, 
"key!", "0", "a*");
+  public void givenIncorrectOptionalArgumentsAndKeyExists_returnsSyntaxError() 
{
+    jedis.sadd(KEY, MEMBER_ONE);
+    assertThatThrownBy(
+        () -> jedis.sendCommand(KEY, Protocol.Command.SSCAN, KEY, ZERO_CURSOR, 
"a*"))
+            .hasMessageContaining(ERROR_SYNTAX);
+  }
 
-    assertThat((byte[]) result.get(0)).isEqualTo("0".getBytes());
-    assertThat((List<Object>) result.get(1)).isEmpty();
+  @Test
+  public void 
givenMatchArgumentWithoutPatternOnExistingKey_returnsSyntaxError() {
+    jedis.sadd(KEY, MEMBER_ONE);
+    assertThatThrownBy(
+        () -> jedis.sendCommand(KEY, Protocol.Command.SSCAN, KEY, ZERO_CURSOR, 
"MATCH"))
+            .hasMessageContaining(ERROR_SYNTAX);
   }
 
   @Test
-  public void givenMatchOrCountKeywordNotSpecified_returnsSyntaxError() {
-    jedis.sadd("a", "1");
-    assertThatThrownBy(() -> jedis.sendCommand("a", Protocol.Command.SSCAN, 
"a", "0", "a*", "1"))
-        .hasMessageContaining(ERROR_SYNTAX);
+  public void 
givenCountArgumentWithoutNumberOnExistingKey_returnsSyntaxError() {
+    jedis.sadd(KEY, MEMBER_ONE);
+    assertThatThrownBy(
+        () -> jedis.sendCommand(KEY, Protocol.Command.SSCAN, KEY, ZERO_CURSOR, 
"COUNT"))
+            .hasMessageContaining(ERROR_SYNTAX);
+  }
+
+  @Test
+  public void 
givenAdditionalArgumentNotEqualToMatchOrCount_returnsSyntaxError() {
+    jedis.sadd(KEY, MEMBER_ONE);
+    assertThatThrownBy(
+        () -> jedis.sendCommand(KEY, Protocol.Command.SSCAN, KEY, ZERO_CURSOR, 
"a*", "1"))
+            .hasMessageContaining(ERROR_SYNTAX);
   }
 
   @Test
   public void 
givenCount_whenCountParameterIsNotAnInteger_returnsNotIntegerError() {
-    jedis.sadd("a", "1");
+    jedis.sadd(KEY, MEMBER_ONE);
     assertThatThrownBy(
-        () -> jedis.sendCommand("a", Protocol.Command.SSCAN, "a", "0", 
"COUNT", "MATCH"))
-            .hasMessageContaining(ERROR_NOT_INTEGER);
+        () -> jedis.sendCommand(KEY, Protocol.Command.SSCAN, KEY, ZERO_CURSOR, 
"COUNT",
+            "notAnInteger"))
+                .hasMessageContaining(ERROR_NOT_INTEGER);
   }
 
   @Test
   public void 
givenMultipleCounts_whenAnyCountParameterIsNotAnInteger_returnsNotIntegerError()
 {
-    jedis.sadd("a", "1");
-    assertThatThrownBy(() -> jedis.sendCommand("a", Protocol.Command.SSCAN, 
"a", "0", "COUNT", "2",
-        "COUNT", "sjlfs", "COUNT", "1"))
-            .hasMessageContaining(ERROR_NOT_INTEGER);
+    jedis.sadd(KEY, MEMBER_ONE);
+    assertThatThrownBy(
+        () -> jedis.sendCommand(KEY, Protocol.Command.SSCAN, KEY, ZERO_CURSOR, 
"COUNT", "12",
+            "COUNT", "notAnInteger", "COUNT", "1"))
+                .hasMessageContaining(ERROR_NOT_INTEGER);
   }
 
   @Test
   public void 
givenMultipleCounts_whenAnyCountParameterIsLessThanOne_returnsSyntaxError() {
-    jedis.sadd("a", "1");
-    assertThatThrownBy(() -> jedis.sendCommand("a", Protocol.Command.SSCAN, 
"a", "0", "COUNT", "2",
-        "COUNT", "0", "COUNT", "1"))
-            .hasMessageContaining(ERROR_SYNTAX);
+    jedis.sadd(KEY, MEMBER_ONE);
+    assertThatThrownBy(
+        () -> jedis.sendCommand(KEY, Protocol.Command.SSCAN, KEY, ZERO_CURSOR, 
"COUNT", "12",
+            "COUNT", "0", "COUNT", "1"))
+                .hasMessageContaining(ERROR_SYNTAX);
   }
 
   @Test
   public void givenCount_whenCountParameterIsZero_returnsSyntaxError() {
-    jedis.sadd("a", "1");
-
-    assertThatThrownBy(() -> jedis.sendCommand("a", Protocol.Command.SSCAN, 
"a", "0", "COUNT", "0"))
-        .hasMessageContaining(ERROR_SYNTAX);
+    jedis.sadd(KEY, MEMBER_ONE);
+    assertThatThrownBy(
+        () -> jedis.sscan(KEY_BYTES, ZERO_CURSOR_BYTES, new 
ScanParams().count(0)))
+            .hasMessageContaining(ERROR_SYNTAX);
   }
 
   @Test
   public void givenCount_whenCountParameterIsNegative_returnsSyntaxError() {
-    jedis.sadd("a", "1");
-
+    jedis.sadd(KEY, MEMBER_ONE);
     assertThatThrownBy(
-        () -> jedis.sendCommand("a", Protocol.Command.SSCAN, "a", "0", 
"COUNT", "-37"))
+        () -> jedis.sscan(KEY_BYTES, ZERO_CURSOR_BYTES, new 
ScanParams().count(-37)))
             .hasMessageContaining(ERROR_SYNTAX);
   }
 
   @Test
   public void givenKeyIsNotASet_returnsWrongTypeError() {
-    jedis.hset("a", "b", "1");
-
+    jedis.hset(KEY, "b", MEMBER_ONE);
     assertThatThrownBy(
-        () -> jedis.sendCommand("a", Protocol.Command.SSCAN, "a", "0", 
"COUNT", "-37"))
+        () -> jedis.sscan(KEY, ZERO_CURSOR))
             .hasMessageContaining(ERROR_WRONG_TYPE);
   }
 
   @Test
   public void 
givenKeyIsNotASet_andCursorIsNotAnInteger_returnsInvalidCursorError() {
-    jedis.hset("a", "b", "1");
-
-    assertThatThrownBy(() -> jedis.sendCommand("a", Protocol.Command.SSCAN, 
"a", "sjfls"))
-        .hasMessageContaining(ERROR_CURSOR);
+    jedis.hset(KEY, "b", MEMBER_ONE);
+    assertThatThrownBy(
+        () -> jedis.sscan(KEY, "notAnInteger"))
+            .hasMessageContaining(ERROR_CURSOR);
   }
 
   @Test
   public void 
givenNonexistentKey_andCursorIsNotInteger_returnsInvalidCursorError() {
     assertThatThrownBy(
-        () -> jedis.sendCommand("notReal", Protocol.Command.SSCAN, "notReal", 
"notReal", "sjfls"))
+        () -> jedis.sscan("nonexistentKey", "notAnInteger"))
             .hasMessageContaining(ERROR_CURSOR);
   }
 
   @Test
   public void 
givenExistentSetKey_andCursorIsNotAnInteger_returnsInvalidCursorError() {
-    jedis.set("a", "b");
-
-    assertThatThrownBy(() -> jedis.sendCommand("a", Protocol.Command.SSCAN, 
"a", "sjfls"))
-        .hasMessageContaining(ERROR_CURSOR);
+    jedis.set(KEY, "b");
+    assertThatThrownBy(
+        () -> jedis.sscan(KEY, "notAnInteger"))
+            .hasMessageContaining(ERROR_CURSOR);
   }
 
   @Test
-  public void givenNonexistentKey_returnsEmptyArray() {
-    ScanResult<String> result = jedis.sscan("nonexistent", "0");
-
-    assertThat(result.isCompleteIteration()).isTrue();
-    assertThat(result.getResult()).isEmpty();
+  public void givenNegativeCursor_doesNotError() {
+    initializeThousandMemberSet();
+    assertThatNoException().isThrownBy(() -> jedis.sscan(KEY, "-1"));
   }
 
   @Test
   public void givenSetWithOneMember_returnsMember() {
-    jedis.sadd("a", "1");
-    ScanResult<String> result = jedis.sscan("a", "0");
+    jedis.sadd(KEY, MEMBER_ONE);
+
+    ScanResult<String> result = jedis.sscan(KEY, ZERO_CURSOR);
 
     assertThat(result.isCompleteIteration()).isTrue();
-    assertThat(result.getResult()).containsExactly("1");
+    assertThat(result.getResult()).containsOnly(MEMBER_ONE);
   }
 
   @Test
-  public void givenSetWithMultipleMembers_returnsAllMembers() {
-    jedis.sadd("a", "1", "2", "3");
-    ScanResult<String> result = jedis.sscan("a", "0");
+  public void givenSetWithMultipleMembers_returnsSubsetOfMembers() {
+    Set<String> initialMemberData = initializeThousandMemberSet();
 
-    assertThat(result.isCompleteIteration()).isTrue();
-    assertThat(result.getResult()).containsExactlyInAnyOrder("1", "2", "3");
+    ScanResult<String> result = jedis.sscan(KEY, ZERO_CURSOR);
+
+    assertThat(result.getResult()).isSubsetOf(initialMemberData);
   }
 
   @Test
   public void givenCount_returnsAllMembersWithoutDuplicates() {
-    jedis.sadd("a", "1", "2", "3");
+    Set<byte[]> initialTotalSet = initializeThousandMemberByteSet();
+    int count = 99;
 
-    ScanParams scanParams = new ScanParams();
-    scanParams.count(1);
-    String cursor = "0";
-    ScanResult<byte[]> result;
-    List<byte[]> allMembersFromScan = new ArrayList<>();
+    ScanResult<byte[]> result =
+        jedis.sscan(KEY_BYTES, ZERO_CURSOR_BYTES, new 
ScanParams().count(count));
 
-    do {
-      result = jedis.sscan("a".getBytes(), cursor.getBytes(), scanParams);
-      allMembersFromScan.addAll(result.getResult());
-      cursor = result.getCursor();
-    } while (!result.isCompleteIteration());
+    assertThat(result.getResult().size()).isGreaterThanOrEqualTo(count);
+    assertThat(result.getResult()).isSubsetOf(initialTotalSet);
+  }
 
-    assertThat(allMembersFromScan).containsExactlyInAnyOrder("1".getBytes(),
-        "2".getBytes(),
-        "3".getBytes());
+  @Test
+  public void givenMultipleCounts_usesLastCountSpecified() {
+    Set<byte[]> initialMemberData = initializeThousandMemberByteSet();
+    // Choose two COUNT arguments with a large difference, so that it's 
extremely unlikely that if
+    // the first COUNT is used, a number of members greater than or equal to 
the second COUNT will
+    // be returned.
+    int firstCount = 1;
+    int secondCount = 500;
+    ScanParams scanParams = new 
ScanParams().count(firstCount).count(secondCount);
+
+    ScanResult<byte[]> result = jedis.sscan(KEY_BYTES, ZERO_CURSOR_BYTES, 
scanParams);
+
+    List<byte[]> returnedMembers = result.getResult();
+    assertThat(returnedMembers.size()).isGreaterThanOrEqualTo(secondCount);
+    assertThat(returnedMembers).isSubsetOf(initialMemberData);
   }
 
   @Test
-  @SuppressWarnings("unchecked")
-  public void givenMultipleCounts_returnsAllEntriesWithoutDuplicates() {
-    jedis.sadd("a", "1", "12", "3");
+  public void givenSetWithThreeEntriesAndMatch_returnsOnlyMatchingElements() {
+    jedis.sadd(KEY, MEMBER_ONE, MEMBER_TWELVE, MEMBER_THREE);
+    ScanParams scanParams = new ScanParams().match("1*");
 
-    List<Object> result;
+    ScanResult<byte[]> result = jedis.sscan(KEY_BYTES, ZERO_CURSOR_BYTES, 
scanParams);
 
-    List<byte[]> allEntries = new ArrayList<>();
-    String cursor = "0";
+    assertThat(result.isCompleteIteration()).isTrue();
+    assertThat(result.getResult()).containsOnly(MEMBER_ONE.getBytes(),
+        MEMBER_TWELVE.getBytes());
+  }
 
-    do {
-      result =
-          (List<Object>) jedis.sendCommand("a", Protocol.Command.SSCAN, "a", 
cursor, "COUNT", "2",
-              "COUNT", "1");
-      allEntries.addAll((List<byte[]>) result.get(1));
-      cursor = new String((byte[]) result.get(0));
-    } while (!Arrays.equals((byte[]) result.get(0), "0".getBytes()));
+  @Test
+  public void 
givenSetWithThreeEntriesAndMultipleMatchArguments_returnsOnlyElementsMatchingLastMatchArgument()
 {
+    jedis.sadd(KEY, MEMBER_ONE, MEMBER_TWELVE, MEMBER_THREE);
+    ScanParams scanParams = new ScanParams().match("3*").match("1*");
+
+    ScanResult<byte[]> result = jedis.sscan(KEY_BYTES, ZERO_CURSOR_BYTES, 
scanParams);
 
-    assertThat((byte[]) result.get(0)).isEqualTo("0".getBytes());
-    assertThat(allEntries).containsExactlyInAnyOrder("1".getBytes(),
-        "12".getBytes(),
-        "3".getBytes());
+    assertThat(result.getCursor()).isEqualTo(ZERO_CURSOR);
+    assertThat(result.getResult()).containsOnly(MEMBER_ONE.getBytes(),
+        MEMBER_TWELVE.getBytes());
   }
 
   @Test
-  public void givenMatch_returnsAllMatchingMembersWithoutDuplicates() {
-    jedis.sadd("a", "1", "12", "3");
-
+  public void givenLargeCountAndMatch_returnsOnlyMatchingMembers() {
+    Set<byte[]> initialMemberData = initializeThousandMemberByteSet();
     ScanParams scanParams = new ScanParams();
-    scanParams.match("1*");
+    // There are 111 matching members in the set 0..999
+    scanParams.match("9*");
+    // Choose a large COUNT to ensure that some matching members are returned
+    scanParams.count(950);
+
+    ScanResult<byte[]> result = jedis.sscan(KEY_BYTES, ZERO_CURSOR_BYTES, 
scanParams);
+
+    List<byte[]> returnedMembers = result.getResult();
+    // We know that we must have found at least 61 matching members, given the 
size of COUNT and the
+    // number of matching members in the set
+    assertThat(returnedMembers.size()).isGreaterThanOrEqualTo(61);
+    assertThat(returnedMembers).isSubsetOf(initialMemberData);
+    assertThat(returnedMembers).allSatisfy(bytes -> assertThat(new 
String(bytes)).startsWith("9"));
+  }
 
-    ScanResult<byte[]> result =
-        jedis.sscan("a".getBytes(), "0".getBytes(), scanParams);
+  @Test
+  public void givenMultipleCountAndMatch_usesLastSpecified() {
+    Set<byte[]> initialMemberData = initializeThousandMemberByteSet();
+    // Choose a large COUNT to ensure that some matching members are returned
+    // There are 111 matching members in the set 0..999
+    ScanParams scanParams = new 
ScanParams().count(20).match("1*").count(950).match("9*");

Review comment:
       See the comment above about using `sendCustomSscanCommand()` instead of 
multiple calls to `ScanParams` methods here.

##########
File path: 
geode-for-redis/src/integrationTest/java/org/apache/geode/redis/internal/commands/executor/set/AbstractSScanIntegrationTest.java
##########
@@ -54,295 +72,394 @@ public void tearDown() {
   }
 
   @Test
-  public void givenNoKeyArgument_returnsWrongNumberOfArgumentsError() {
-    assertThatThrownBy(() -> jedis.sendCommand("key", Protocol.Command.SSCAN))
-        .hasMessageContaining("ERR wrong number of arguments for 'sscan' 
command");
+  public void givenLessThanTwoArguments_returnsWrongNumberOfArgumentsError() {
+    assertAtLeastNArgs(jedis, Protocol.Command.SSCAN, 2);
   }
 
   @Test
-  public void givenNoCursorArgument_returnsWrongNumberOfArgumentsError() {
-    assertThatThrownBy(() -> jedis.sendCommand("key", Protocol.Command.SSCAN, 
"key"))
-        .hasMessageContaining("ERR wrong number of arguments for 'sscan' 
command");
+  public void givenNonexistentKey_returnsEmptyArray() {
+    ScanResult<String> result = jedis.sscan("nonexistentKey", ZERO_CURSOR);
+    assertThat(result.isCompleteIteration()).isTrue();
+    assertThat(result.getResult()).isEmpty();
   }
 
   @Test
-  public void givenArgumentsAreNotOddAndKeyExists_returnsSyntaxError() {
-    jedis.sadd("a", "1");
-    assertThatThrownBy(() -> jedis.sendCommand("a", Protocol.Command.SSCAN, 
"a", "0", "a*"))
-        .hasMessageContaining(ERROR_SYNTAX);
+  public void 
givenNonexistentKeyAndIncorrectOptionalArguments_returnsEmptyArray() {
+    ScanResult<byte[]> result =
+        sendCustomSscanCommand("nonexistentKey", "nonexistentKey", 
ZERO_CURSOR, "ANY");
+    assertThat(result.getResult()).isEmpty();
   }
 
   @Test
-  @SuppressWarnings("unchecked")
-  public void givenArgumentsAreNotOddAndKeyDoesNotExist_returnsEmptyArray() {
-    List<Object> result =
-        (List<Object>) jedis.sendCommand("key!", Protocol.Command.SSCAN, 
"key!", "0", "a*");
+  public void givenIncorrectOptionalArgumentsAndKeyExists_returnsSyntaxError() 
{
+    jedis.sadd(KEY, MEMBER_ONE);
+    assertThatThrownBy(
+        () -> jedis.sendCommand(KEY, Protocol.Command.SSCAN, KEY, ZERO_CURSOR, 
"a*"))
+            .hasMessageContaining(ERROR_SYNTAX);
+  }
 
-    assertThat((byte[]) result.get(0)).isEqualTo("0".getBytes());
-    assertThat((List<Object>) result.get(1)).isEmpty();
+  @Test
+  public void 
givenMatchArgumentWithoutPatternOnExistingKey_returnsSyntaxError() {
+    jedis.sadd(KEY, MEMBER_ONE);
+    assertThatThrownBy(
+        () -> jedis.sendCommand(KEY, Protocol.Command.SSCAN, KEY, ZERO_CURSOR, 
"MATCH"))
+            .hasMessageContaining(ERROR_SYNTAX);
   }
 
   @Test
-  public void givenMatchOrCountKeywordNotSpecified_returnsSyntaxError() {
-    jedis.sadd("a", "1");
-    assertThatThrownBy(() -> jedis.sendCommand("a", Protocol.Command.SSCAN, 
"a", "0", "a*", "1"))
-        .hasMessageContaining(ERROR_SYNTAX);
+  public void 
givenCountArgumentWithoutNumberOnExistingKey_returnsSyntaxError() {
+    jedis.sadd(KEY, MEMBER_ONE);
+    assertThatThrownBy(
+        () -> jedis.sendCommand(KEY, Protocol.Command.SSCAN, KEY, ZERO_CURSOR, 
"COUNT"))
+            .hasMessageContaining(ERROR_SYNTAX);
+  }
+
+  @Test
+  public void 
givenAdditionalArgumentNotEqualToMatchOrCount_returnsSyntaxError() {
+    jedis.sadd(KEY, MEMBER_ONE);
+    assertThatThrownBy(
+        () -> jedis.sendCommand(KEY, Protocol.Command.SSCAN, KEY, ZERO_CURSOR, 
"a*", "1"))
+            .hasMessageContaining(ERROR_SYNTAX);
   }
 
   @Test
   public void 
givenCount_whenCountParameterIsNotAnInteger_returnsNotIntegerError() {
-    jedis.sadd("a", "1");
+    jedis.sadd(KEY, MEMBER_ONE);
     assertThatThrownBy(
-        () -> jedis.sendCommand("a", Protocol.Command.SSCAN, "a", "0", 
"COUNT", "MATCH"))
-            .hasMessageContaining(ERROR_NOT_INTEGER);
+        () -> jedis.sendCommand(KEY, Protocol.Command.SSCAN, KEY, ZERO_CURSOR, 
"COUNT",
+            "notAnInteger"))
+                .hasMessageContaining(ERROR_NOT_INTEGER);
   }
 
   @Test
   public void 
givenMultipleCounts_whenAnyCountParameterIsNotAnInteger_returnsNotIntegerError()
 {
-    jedis.sadd("a", "1");
-    assertThatThrownBy(() -> jedis.sendCommand("a", Protocol.Command.SSCAN, 
"a", "0", "COUNT", "2",
-        "COUNT", "sjlfs", "COUNT", "1"))
-            .hasMessageContaining(ERROR_NOT_INTEGER);
+    jedis.sadd(KEY, MEMBER_ONE);
+    assertThatThrownBy(
+        () -> jedis.sendCommand(KEY, Protocol.Command.SSCAN, KEY, ZERO_CURSOR, 
"COUNT", "12",
+            "COUNT", "notAnInteger", "COUNT", "1"))
+                .hasMessageContaining(ERROR_NOT_INTEGER);
   }
 
   @Test
   public void 
givenMultipleCounts_whenAnyCountParameterIsLessThanOne_returnsSyntaxError() {
-    jedis.sadd("a", "1");
-    assertThatThrownBy(() -> jedis.sendCommand("a", Protocol.Command.SSCAN, 
"a", "0", "COUNT", "2",
-        "COUNT", "0", "COUNT", "1"))
-            .hasMessageContaining(ERROR_SYNTAX);
+    jedis.sadd(KEY, MEMBER_ONE);
+    assertThatThrownBy(
+        () -> jedis.sendCommand(KEY, Protocol.Command.SSCAN, KEY, ZERO_CURSOR, 
"COUNT", "12",
+            "COUNT", "0", "COUNT", "1"))
+                .hasMessageContaining(ERROR_SYNTAX);
   }
 
   @Test
   public void givenCount_whenCountParameterIsZero_returnsSyntaxError() {
-    jedis.sadd("a", "1");
-
-    assertThatThrownBy(() -> jedis.sendCommand("a", Protocol.Command.SSCAN, 
"a", "0", "COUNT", "0"))
-        .hasMessageContaining(ERROR_SYNTAX);
+    jedis.sadd(KEY, MEMBER_ONE);
+    assertThatThrownBy(
+        () -> jedis.sscan(KEY_BYTES, ZERO_CURSOR_BYTES, new 
ScanParams().count(0)))
+            .hasMessageContaining(ERROR_SYNTAX);
   }
 
   @Test
   public void givenCount_whenCountParameterIsNegative_returnsSyntaxError() {
-    jedis.sadd("a", "1");
-
+    jedis.sadd(KEY, MEMBER_ONE);
     assertThatThrownBy(
-        () -> jedis.sendCommand("a", Protocol.Command.SSCAN, "a", "0", 
"COUNT", "-37"))
+        () -> jedis.sscan(KEY_BYTES, ZERO_CURSOR_BYTES, new 
ScanParams().count(-37)))
             .hasMessageContaining(ERROR_SYNTAX);
   }
 
   @Test
   public void givenKeyIsNotASet_returnsWrongTypeError() {
-    jedis.hset("a", "b", "1");
-
+    jedis.hset(KEY, "b", MEMBER_ONE);
     assertThatThrownBy(
-        () -> jedis.sendCommand("a", Protocol.Command.SSCAN, "a", "0", 
"COUNT", "-37"))
+        () -> jedis.sscan(KEY, ZERO_CURSOR))
             .hasMessageContaining(ERROR_WRONG_TYPE);
   }
 
   @Test
   public void 
givenKeyIsNotASet_andCursorIsNotAnInteger_returnsInvalidCursorError() {
-    jedis.hset("a", "b", "1");
-
-    assertThatThrownBy(() -> jedis.sendCommand("a", Protocol.Command.SSCAN, 
"a", "sjfls"))
-        .hasMessageContaining(ERROR_CURSOR);
+    jedis.hset(KEY, "b", MEMBER_ONE);
+    assertThatThrownBy(
+        () -> jedis.sscan(KEY, "notAnInteger"))
+            .hasMessageContaining(ERROR_CURSOR);
   }
 
   @Test
   public void 
givenNonexistentKey_andCursorIsNotInteger_returnsInvalidCursorError() {
     assertThatThrownBy(
-        () -> jedis.sendCommand("notReal", Protocol.Command.SSCAN, "notReal", 
"notReal", "sjfls"))
+        () -> jedis.sscan("nonexistentKey", "notAnInteger"))
             .hasMessageContaining(ERROR_CURSOR);
   }
 
   @Test
   public void 
givenExistentSetKey_andCursorIsNotAnInteger_returnsInvalidCursorError() {
-    jedis.set("a", "b");
-
-    assertThatThrownBy(() -> jedis.sendCommand("a", Protocol.Command.SSCAN, 
"a", "sjfls"))
-        .hasMessageContaining(ERROR_CURSOR);
+    jedis.set(KEY, "b");
+    assertThatThrownBy(
+        () -> jedis.sscan(KEY, "notAnInteger"))
+            .hasMessageContaining(ERROR_CURSOR);
   }
 
   @Test
-  public void givenNonexistentKey_returnsEmptyArray() {
-    ScanResult<String> result = jedis.sscan("nonexistent", "0");
-
-    assertThat(result.isCompleteIteration()).isTrue();
-    assertThat(result.getResult()).isEmpty();
+  public void givenNegativeCursor_doesNotError() {
+    initializeThousandMemberSet();
+    assertThatNoException().isThrownBy(() -> jedis.sscan(KEY, "-1"));
   }
 
   @Test
   public void givenSetWithOneMember_returnsMember() {
-    jedis.sadd("a", "1");
-    ScanResult<String> result = jedis.sscan("a", "0");
+    jedis.sadd(KEY, MEMBER_ONE);
+
+    ScanResult<String> result = jedis.sscan(KEY, ZERO_CURSOR);
 
     assertThat(result.isCompleteIteration()).isTrue();
-    assertThat(result.getResult()).containsExactly("1");
+    assertThat(result.getResult()).containsOnly(MEMBER_ONE);
   }
 
   @Test
-  public void givenSetWithMultipleMembers_returnsAllMembers() {
-    jedis.sadd("a", "1", "2", "3");
-    ScanResult<String> result = jedis.sscan("a", "0");
+  public void givenSetWithMultipleMembers_returnsSubsetOfMembers() {
+    Set<String> initialMemberData = initializeThousandMemberSet();
 
-    assertThat(result.isCompleteIteration()).isTrue();
-    assertThat(result.getResult()).containsExactlyInAnyOrder("1", "2", "3");
+    ScanResult<String> result = jedis.sscan(KEY, ZERO_CURSOR);
+
+    assertThat(result.getResult()).isSubsetOf(initialMemberData);
   }
 
   @Test
   public void givenCount_returnsAllMembersWithoutDuplicates() {
-    jedis.sadd("a", "1", "2", "3");
+    Set<byte[]> initialTotalSet = initializeThousandMemberByteSet();
+    int count = 99;
 
-    ScanParams scanParams = new ScanParams();
-    scanParams.count(1);
-    String cursor = "0";
-    ScanResult<byte[]> result;
-    List<byte[]> allMembersFromScan = new ArrayList<>();
+    ScanResult<byte[]> result =
+        jedis.sscan(KEY_BYTES, ZERO_CURSOR_BYTES, new 
ScanParams().count(count));
 
-    do {
-      result = jedis.sscan("a".getBytes(), cursor.getBytes(), scanParams);
-      allMembersFromScan.addAll(result.getResult());
-      cursor = result.getCursor();
-    } while (!result.isCompleteIteration());
+    assertThat(result.getResult().size()).isGreaterThanOrEqualTo(count);
+    assertThat(result.getResult()).isSubsetOf(initialTotalSet);
+  }
 
-    assertThat(allMembersFromScan).containsExactlyInAnyOrder("1".getBytes(),
-        "2".getBytes(),
-        "3".getBytes());
+  @Test
+  public void givenMultipleCounts_usesLastCountSpecified() {
+    Set<byte[]> initialMemberData = initializeThousandMemberByteSet();
+    // Choose two COUNT arguments with a large difference, so that it's 
extremely unlikely that if
+    // the first COUNT is used, a number of members greater than or equal to 
the second COUNT will
+    // be returned.
+    int firstCount = 1;
+    int secondCount = 500;
+    ScanParams scanParams = new 
ScanParams().count(firstCount).count(secondCount);
+
+    ScanResult<byte[]> result = jedis.sscan(KEY_BYTES, ZERO_CURSOR_BYTES, 
scanParams);
+
+    List<byte[]> returnedMembers = result.getResult();
+    assertThat(returnedMembers.size()).isGreaterThanOrEqualTo(secondCount);
+    assertThat(returnedMembers).isSubsetOf(initialMemberData);
   }
 
   @Test
-  @SuppressWarnings("unchecked")
-  public void givenMultipleCounts_returnsAllEntriesWithoutDuplicates() {
-    jedis.sadd("a", "1", "12", "3");
+  public void givenSetWithThreeEntriesAndMatch_returnsOnlyMatchingElements() {
+    jedis.sadd(KEY, MEMBER_ONE, MEMBER_TWELVE, MEMBER_THREE);
+    ScanParams scanParams = new ScanParams().match("1*");
 
-    List<Object> result;
+    ScanResult<byte[]> result = jedis.sscan(KEY_BYTES, ZERO_CURSOR_BYTES, 
scanParams);
 
-    List<byte[]> allEntries = new ArrayList<>();
-    String cursor = "0";
+    assertThat(result.isCompleteIteration()).isTrue();
+    assertThat(result.getResult()).containsOnly(MEMBER_ONE.getBytes(),
+        MEMBER_TWELVE.getBytes());
+  }
 
-    do {
-      result =
-          (List<Object>) jedis.sendCommand("a", Protocol.Command.SSCAN, "a", 
cursor, "COUNT", "2",
-              "COUNT", "1");
-      allEntries.addAll((List<byte[]>) result.get(1));
-      cursor = new String((byte[]) result.get(0));
-    } while (!Arrays.equals((byte[]) result.get(0), "0".getBytes()));
+  @Test
+  public void 
givenSetWithThreeEntriesAndMultipleMatchArguments_returnsOnlyElementsMatchingLastMatchArgument()
 {
+    jedis.sadd(KEY, MEMBER_ONE, MEMBER_TWELVE, MEMBER_THREE);
+    ScanParams scanParams = new ScanParams().match("3*").match("1*");

Review comment:
       See the comment above about using `sendCustomSscanCommand()` instead of 
multiple calls to `ScanParams` methods here.




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscr...@geode.apache.org

For queries about this service, please contact Infrastructure at:
us...@infra.apache.org


Reply via email to