Skip to content

Commit

Permalink
Extensive unit tests for the CommandObjects class (#3796)
Browse files Browse the repository at this point in the history
Write isolated unit test for the different methods of the CommandObjects
class. These can be tested by themselves, without considerations like
cluster, sentinel or other orthogonal aspects.

Structure the tests into logical groups, similar to how the Redis
commands are documented on the redis.io website. For each group of
commands there is a corresponding test class.

The main goal is to have the test code as readable as possible. The
methods are kept to a minimum length in general. Sometimes, due to
required data preparation, this is not possible.

Another goal is to have this as a sort of documentation. Ideally someone
who reads these tests should be able to grasp what the Redis commands
are doing.

This being said, the tests still focus on testing the client. For
example negative cases are rarely included, and mostly when they impact
the client, for example the parsing of the response. It is not the goal
of these tests to test the Redis server itself.

Co-authored-by: Gabriel Erzse <[email protected]>
  • Loading branch information
gerzse and gerzse authored Apr 3, 2024
1 parent c7a1687 commit 7a5bce1
Show file tree
Hide file tree
Showing 26 changed files with 10,023 additions and 0 deletions.
6 changes: 6 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,12 @@
<version>${jackson.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>net.javacrumbs.json-unit</groupId>
<artifactId>json-unit</artifactId>
<version>2.38.0</version> <!-- 3.x requires Java 17 -->
<scope>test</scope>
</dependency>

<!-- circuit breaker / failover -->
<dependency>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
package redis.clients.jedis.commands.commandobjects;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.equalTo;

import java.util.List;

import org.junit.Test;
import redis.clients.jedis.RedisProtocol;
import redis.clients.jedis.args.BitCountOption;
import redis.clients.jedis.args.BitOP;
import redis.clients.jedis.params.BitPosParams;

/**
* Tests related to <a href="https://redis.io/commands/?group=bitmap">Bitmap</a> commands.
*/
public class CommandObjectsBitmapCommandsTest extends CommandObjectsStandaloneTestBase {

public CommandObjectsBitmapCommandsTest(RedisProtocol protocol) {
super(protocol);
}

@Test
public void testSetbitAndGetbit() {
String key = "bitKey";
long offset = 10;

Boolean initialValue = exec(commandObjects.getbit(key, offset));
assertThat(initialValue, equalTo(false));

Boolean setbit = exec(commandObjects.setbit(key, offset, true));
assertThat(setbit, equalTo(false)); // original value returned

Boolean finalValue = exec(commandObjects.getbit(key, offset));
assertThat(finalValue, equalTo(true));
}

@Test
public void testSetbitAndGetbitBinary() {
byte[] key = "bitKeyBytes".getBytes();
long offset = 10;

Boolean initialValue = exec(commandObjects.getbit(key, offset));
assertThat(initialValue, equalTo(false));

Boolean setbit = exec(commandObjects.setbit(key, offset, true));
assertThat(setbit, equalTo(false)); // original value returned

Boolean finalValue = exec(commandObjects.getbit(key, offset));
assertThat(finalValue, equalTo(true));
}

@Test
public void testBitcount() {
String key = "bitcountKey";
byte[] keyBytes = key.getBytes();

// Set some bits
exec(commandObjects.setbit(key, 1, true));
exec(commandObjects.setbit(key, 2, true));
exec(commandObjects.setbit(key, 7, true)); // This makes 1 byte with 3 bits set
exec(commandObjects.setbit(key, 8, true)); // Next byte, first bit set

Long bitcountFullString = exec(commandObjects.bitcount(key));
assertThat(bitcountFullString, equalTo(4L));

Long bitcountFirstByte = exec(commandObjects.bitcount(key, 0, 0));
assertThat(bitcountFirstByte, equalTo(3L));

Long bitcountFullStringBinary = exec(commandObjects.bitcount(keyBytes));
assertThat(bitcountFullStringBinary, equalTo(4L));

Long bitcountFirstByteBinary = exec(commandObjects.bitcount(keyBytes, 0, 0));
assertThat(bitcountFirstByteBinary, equalTo(3L));

Long bitcountFirstSixBits = exec(commandObjects.bitcount(key, 0, 5, BitCountOption.BIT));
assertThat(bitcountFirstSixBits, equalTo(2L));

Long bitcountFirstSixBitsBinary = exec(commandObjects.bitcount(keyBytes, 0, 5, BitCountOption.BIT));
assertThat(bitcountFirstSixBitsBinary, equalTo(2L));
}

@Test
public void testBitpos() {
String key = "bitposKey";
byte[] keyBytes = key.getBytes();

// Set some bits
exec(commandObjects.setbit(key, 10, true));
exec(commandObjects.setbit(key, 22, true));
exec(commandObjects.setbit(key, 30, true));

Long firstSetBit = exec(commandObjects.bitpos(key, true));
assertThat(firstSetBit, equalTo(10L));

Long firstUnsetBit = exec(commandObjects.bitpos(key, false));
assertThat(firstUnsetBit, equalTo(0L));

BitPosParams params = new BitPosParams(15, 25).modifier(BitCountOption.BIT);

Long firstSetBitInRange = exec(commandObjects.bitpos(key, true, params));
assertThat(firstSetBitInRange, equalTo(22L));

Long firstUnsetBitInRange = exec(commandObjects.bitpos(key, false, params));
assertThat(firstUnsetBitInRange, equalTo(15L));

Long firstSetBitBinary = exec(commandObjects.bitpos(keyBytes, true));
assertThat(firstSetBitBinary, equalTo(10L));

Long firstUnsetBitBinary = exec(commandObjects.bitpos(keyBytes, false));
assertThat(firstUnsetBitBinary, equalTo(0L));

Long firstSetBitInRangeBinary = exec(commandObjects.bitpos(keyBytes, true, params));
assertThat(firstSetBitInRangeBinary, equalTo(22L));

Long firstUnsetBitInRangeBinary = exec(commandObjects.bitpos(keyBytes, false, params));
assertThat(firstUnsetBitInRangeBinary, equalTo(15L));
}

@Test
public void testBitfield() {
String key = "bitfieldKey";

List<Long> bitfieldResult = exec(commandObjects.bitfield(
key, "INCRBY", "i5", "100", "7", "GET", "i5", "100"));

// Contains the result of the INCRBY operation, and the result of the GET operation.
assertThat(bitfieldResult, contains(7L, 7L));

List<Long> bitfieldRoResult = exec(commandObjects.bitfieldReadonly(
key, "GET", "i4", "100"));
assertThat(bitfieldRoResult, contains(3L));
}

@Test
public void testBitfieldBinary() {
byte[] key = "bitfieldKeyBytes".getBytes();

List<Long> bitfieldResult = exec(commandObjects.bitfield(key,
"INCRBY".getBytes(), "i5".getBytes(), "100".getBytes(), "7".getBytes(),
"GET".getBytes(), "i5".getBytes(), "100".getBytes()));

// Contains the result of the INCRBY operation, and the result of the GET operation.
assertThat(bitfieldResult, contains(7L, 7L));

List<Long> bitfieldRoResult = exec(commandObjects.bitfieldReadonly(key,
"GET".getBytes(), "i4".getBytes(), "100".getBytes()));
assertThat(bitfieldRoResult, contains(3L));
}

@Test
public void testBitop() {
String srcKey1 = "srcKey1";
String srcKey2 = "srcKey2";
String destKey = "destKey";

// Set some bits
exec(commandObjects.setbit(srcKey1, 1, true));
exec(commandObjects.setbit(srcKey1, 2, true));
exec(commandObjects.setbit(srcKey1, 3, true));

exec(commandObjects.setbit(srcKey2, 1, true));
exec(commandObjects.setbit(srcKey2, 3, true));

Long bitopResult = exec(commandObjects.bitop(BitOP.AND, destKey, srcKey1, srcKey2));
assertThat(bitopResult, equalTo(1L)); // 1 byte stored

assertThat(exec(commandObjects.getbit(destKey, 1)), equalTo(true));
assertThat(exec(commandObjects.getbit(destKey, 2)), equalTo(false));
assertThat(exec(commandObjects.getbit(destKey, 3)), equalTo(true));
}

@Test
public void testBitopBinary() {
byte[] srcKey1 = "srcKey1".getBytes();
byte[] srcKey2 = "srcKey2".getBytes();
byte[] destKey = "destKey".getBytes();

// Set some bits
exec(commandObjects.setbit(srcKey1, 1, true));
exec(commandObjects.setbit(srcKey1, 2, true));
exec(commandObjects.setbit(srcKey1, 3, true));

exec(commandObjects.setbit(srcKey2, 1, true));
exec(commandObjects.setbit(srcKey2, 3, true));

Long bitopResult = exec(commandObjects.bitop(BitOP.XOR, destKey, srcKey1, srcKey2));
assertThat(bitopResult, equalTo(1L)); // 1 byte stored

assertThat(exec(commandObjects.getbit(new String(destKey), 1)), equalTo(false));
assertThat(exec(commandObjects.getbit(new String(destKey), 2)), equalTo(true));
assertThat(exec(commandObjects.getbit(new String(destKey), 3)), equalTo(false));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
package redis.clients.jedis.commands.commandobjects;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasEntry;
import static org.hamcrest.Matchers.notNullValue;

import java.util.List;
import java.util.Map;

import org.junit.Test;
import redis.clients.jedis.RedisProtocol;
import redis.clients.jedis.bloom.BFInsertParams;
import redis.clients.jedis.bloom.BFReserveParams;

/**
* Tests related to <a href="https://redis.io/commands/?group=bf">Bloom Filter</a> commands.
*/
public class CommandObjectsBloomFilterCommandsTest extends CommandObjectsModulesTestBase {

public CommandObjectsBloomFilterCommandsTest(RedisProtocol protocol) {
super(protocol);
}

@Test
public void testBfAddAndExists() {
String key = "testBf";

String reserve = exec(commandObjects.bfReserve(key, 0.01, 1000));
assertThat(reserve, equalTo("OK"));

boolean add = exec(commandObjects.bfAdd(key, "item1"));
assertThat(add, equalTo(true));

boolean exists = exec(commandObjects.bfExists(key, "item1"));
assertThat(exists, equalTo(true));

boolean notExists = exec(commandObjects.bfExists(key, "item2"));
assertThat(notExists, equalTo(false));
}

@Test
public void testBfInsert() {
String key = "testBf";

String reserve = exec(commandObjects.bfReserve(key, 0.01, 1000));
assertThat(reserve, equalTo("OK"));

List<Boolean> insert = exec(commandObjects.bfInsert(key, "item1", "item2"));
assertThat(insert, contains(true, true));

BFInsertParams insertParams = new BFInsertParams().noCreate().capacity(1000);

List<Boolean> insertWithParams = exec(commandObjects.bfInsert(key, insertParams, "item1", "item2"));
assertThat(insertWithParams, contains(false, false));

assertThat(exec(commandObjects.bfExists(key, "item1")), equalTo(true));
assertThat(exec(commandObjects.bfExists(key, "item2")), equalTo(true));
assertThat(exec(commandObjects.bfExists(key, "item3")), equalTo(false));
}

@Test
public void testBfMAddMExistsAndCard() {
String key = "testBf";

String reserve = exec(commandObjects.bfReserve(key, 0.01, 1000));
assertThat(reserve, equalTo("OK"));

List<Boolean> mAdd = exec(commandObjects.bfMAdd(key, "item1", "item2", "item3"));
assertThat(mAdd, contains(true, true, true));

List<Boolean> mExists = exec(commandObjects.bfMExists(key, "item1", "item2", "item3", "item4"));
assertThat(mExists, contains(true, true, true, false));

Long card = exec(commandObjects.bfCard(key));
assertThat(card, equalTo(3L));
}

@Test
public void testBfScanDumpAndLoadChunk() {
String key = "test";

String reserve = exec(commandObjects.bfReserve(key, 0.01, 5000));
assertThat(reserve, equalTo("OK"));

for (int i = 0; i < 1000; i++) {
Boolean add = exec(commandObjects.bfAdd(key, "item" + i));
assertThat(add, equalTo(true));
}

String newKey = "testBfLoadChunk";

long iterator = 0;
do {
Map.Entry<Long, byte[]> scanDumpResult = exec(commandObjects.bfScanDump(key, iterator));

iterator = scanDumpResult.getKey();

if (iterator > 0) {
byte[] data = scanDumpResult.getValue();

assertThat(data, notNullValue());

String loadChunk = exec(commandObjects.bfLoadChunk(newKey, iterator, data));
assertThat(loadChunk, equalTo("OK"));
}
} while (iterator != 0);

// verify destination
for (int i = 0; i < 1000; i++) {
Boolean exists = exec(commandObjects.bfExists(newKey, "item" + i));
assertThat(exists, equalTo(true));
}

Boolean missingItem = exec(commandObjects.bfExists(newKey, "item1001"));
assertThat(missingItem, equalTo(false));
}

@Test
public void testBfInfo() {
String key = "testBf";

double errorRate = 0.01;
long capacity = 1000;
BFReserveParams reserveParams = new BFReserveParams().expansion(2);

String reserve = exec(commandObjects.bfReserve(key, errorRate, capacity, reserveParams));
assertThat(reserve, equalTo("OK"));

Boolean add = exec(commandObjects.bfAdd(key, "item1"));
assertThat(add, equalTo(true));

Map<String, Object> info = exec(commandObjects.bfInfo(key));
assertThat(info, hasEntry("Capacity", 1000L));
assertThat(info, hasEntry("Number of items inserted", 1L));
}
}
Loading

0 comments on commit 7a5bce1

Please sign in to comment.