├── scripts └── release.sh ├── src ├── test │ └── java │ │ └── com │ │ └── github │ │ └── fppt │ │ └── jedismock │ │ ├── comparisontests │ │ ├── TestErrorMessages.java │ │ ├── cluster │ │ │ └── TestCluster.java │ │ ├── lists │ │ │ ├── LRangeTest.java │ │ │ ├── ListPopTest.java │ │ │ ├── RPopLPushTest.java │ │ │ └── WatchTest.java │ │ ├── strings │ │ │ ├── TestMSetNX.java │ │ │ ├── TestMGet.java │ │ │ ├── TestAppend.java │ │ │ └── TestGetDel.java │ │ ├── sortedsets │ │ │ ├── TestZRevRank.java │ │ │ ├── TestZRank.java │ │ │ └── TestZMScore.java │ │ ├── server │ │ │ └── TestFlushOperations.java │ │ ├── hashes │ │ │ ├── TestHStrlen.java │ │ │ └── HKeysOperation.java │ │ └── keys │ │ │ ├── TestPersist.java │ │ │ ├── TestType.java │ │ │ └── MoveTests.java │ │ ├── commands │ │ └── RedisCommandTest.java │ │ ├── TestUtils.java │ │ ├── RedisClientTest.java │ │ ├── util │ │ ├── OperationCategory.java │ │ ├── MockSubscriber.java │ │ └── MockPSubscriber.java │ │ └── operations │ │ └── keys │ │ └── paramsparser │ │ └── ExpirationTimeParamTest.java └── main │ ├── java │ └── com │ │ └── github │ │ └── fppt │ │ └── jedismock │ │ ├── exception │ │ ├── ArgumentException.java │ │ ├── WrongStreamKeyException.java │ │ ├── EOFException.java │ │ ├── DeserializationException.java │ │ ├── ParseErrorException.java │ │ └── WrongValueTypeException.java │ │ ├── operations │ │ ├── keys │ │ │ ├── paramsparser │ │ │ │ ├── ExpirationParamsException.java │ │ │ │ ├── ExpirationTimeParam.java │ │ │ │ └── ExpirationFieldsParam.java │ │ │ ├── Unlink.java │ │ │ ├── Expire.java │ │ │ ├── ExpireAt.java │ │ │ ├── PTTL.java │ │ │ ├── Type.java │ │ │ ├── Persist.java │ │ │ ├── Exists.java │ │ │ ├── Del.java │ │ │ ├── TTL.java │ │ │ ├── Keys.java │ │ │ ├── PExpireTime.java │ │ │ ├── ExpireTime.java │ │ │ └── Rename.java │ │ ├── RedisOperation.java │ │ ├── RedisCommand.java │ │ ├── sortedsets │ │ │ ├── ZPopMax.java │ │ │ ├── ZPopMin.java │ │ │ ├── ZDiff.java │ │ │ ├── ZInter.java │ │ │ ├── ZUnion.java │ │ │ ├── ZInterStore.java │ │ │ ├── ZUnionStore.java │ │ │ ├── ZDiffStore.java │ │ │ ├── BZPop.java │ │ │ ├── ZRemRangeByLex.java │ │ │ ├── ZCard.java │ │ │ ├── ZRevRank.java │ │ │ ├── ZRemRangeByScore.java │ │ │ ├── ZInterCard.java │ │ │ ├── BZPopMax.java │ │ │ ├── BZPopMin.java │ │ │ ├── ZCount.java │ │ │ ├── AbstractZInter.java │ │ │ ├── ZRemRangeByRank.java │ │ │ ├── AbstractZDiff.java │ │ │ ├── ZRem.java │ │ │ ├── ZRangeByScore.java │ │ │ ├── ZRevRangeByScore.java │ │ │ ├── AbstractZUnion.java │ │ │ ├── ZMScore.java │ │ │ ├── BZMPop.java │ │ │ ├── ZLexCount.java │ │ │ ├── ZScan.java │ │ │ ├── ZIncrBy.java │ │ │ ├── ZScore.java │ │ │ ├── ZRangeByLex.java │ │ │ ├── ZRevRangeByLex.java │ │ │ └── AbstractZRangeByScore.java │ │ ├── strings │ │ │ ├── IncrByFloat.java │ │ │ ├── Decr.java │ │ │ ├── Incr.java │ │ │ ├── IncrBy.java │ │ │ ├── DecrBy.java │ │ │ ├── PSetEx.java │ │ │ ├── Get.java │ │ │ ├── GetSet.java │ │ │ ├── MSet.java │ │ │ ├── GetDel.java │ │ │ ├── SetNX.java │ │ │ ├── StrLen.java │ │ │ ├── MGet.java │ │ │ ├── Append.java │ │ │ ├── MSetNX.java │ │ │ ├── IncrOrDecrBy.java │ │ │ └── SetEx.java │ │ ├── sets │ │ │ ├── SDiffStore.java │ │ │ ├── SUnionStore.java │ │ │ ├── SInterStore.java │ │ │ ├── SCard.java │ │ │ ├── SIsMember.java │ │ │ ├── SAdd.java │ │ │ ├── SStore.java │ │ │ ├── SMembers.java │ │ │ ├── SRem.java │ │ │ ├── SScan.java │ │ │ ├── SDiff.java │ │ │ ├── SUnion.java │ │ │ ├── SInter.java │ │ │ ├── SMove.java │ │ │ └── SMIsMember.java │ │ ├── lists │ │ │ ├── LPop.java │ │ │ ├── RPop.java │ │ │ ├── RPush.java │ │ │ ├── LPush.java │ │ │ ├── BLPop.java │ │ │ ├── BPop.java │ │ │ ├── BRPop.java │ │ │ ├── LPushX.java │ │ │ ├── RPushX.java │ │ │ ├── LLen.java │ │ │ ├── Add.java │ │ │ ├── LIndex.java │ │ │ ├── LSet.java │ │ │ ├── LRange.java │ │ │ └── LInsert.java │ │ ├── hashes │ │ │ ├── expiration │ │ │ │ ├── HExpire.java │ │ │ │ ├── HExpireAt.java │ │ │ │ ├── HTTL.java │ │ │ │ └── HExpireTime.java │ │ │ ├── HMSet.java │ │ │ ├── HGet.java │ │ │ ├── HSetNX.java │ │ │ ├── HExists.java │ │ │ ├── HLen.java │ │ │ ├── HStrlen.java │ │ │ ├── HKeys.java │ │ │ ├── HVals.java │ │ │ ├── HDel.java │ │ │ ├── HMGet.java │ │ │ ├── HGetAll.java │ │ │ ├── HIncrBy.java │ │ │ ├── HScan.java │ │ │ └── HSet.java │ │ ├── server │ │ │ ├── Info.java │ │ │ ├── DBSize.java │ │ │ ├── FlushDB.java │ │ │ ├── FlushAll.java │ │ │ └── Time.java │ │ ├── scripting │ │ │ ├── cjson │ │ │ │ ├── LuaCjsonLib.java │ │ │ │ └── ImmutableLuaTable.java │ │ │ └── EvalSha.java │ │ ├── streams │ │ │ ├── XRevRange.java │ │ │ ├── XRange.java │ │ │ └── XLen.java │ │ ├── transactions │ │ │ ├── Unwatch.java │ │ │ ├── Discard.java │ │ │ ├── Multi.java │ │ │ └── Watch.java │ │ ├── connection │ │ │ ├── Ping.java │ │ │ ├── Auth.java │ │ │ ├── Quit.java │ │ │ ├── Echo.java │ │ │ ├── Hello.java │ │ │ ├── Select.java │ │ │ └── Client.java │ │ ├── pubsub │ │ │ ├── Subscribe.java │ │ │ ├── PSubscribe.java │ │ │ └── PubSub.java │ │ ├── bitmaps │ │ │ ├── GetBit.java │ │ │ └── SetBit.java │ │ └── hyperloglog │ │ │ ├── PFCount.java │ │ │ ├── PFMerge.java │ │ │ └── PFAdd.java │ │ ├── datastructures │ │ ├── StringCompatible.java │ │ ├── RMDataStructure.java │ │ ├── streams │ │ │ ├── SequencedMapIterator.java │ │ │ └── StreamErrors.java │ │ ├── RMList.java │ │ ├── RMSet.java │ │ └── ZSetEntryBound.java │ │ ├── server │ │ ├── RedisCommandInterceptor.java │ │ └── RedisOperationExecutor.java │ │ └── commands │ │ ├── RedisCommandParser.java │ │ └── RedisCommand.java │ └── resources │ └── redis.lua ├── native_tests ├── linux │ ├── tests │ │ ├── unit │ │ │ └── type │ │ │ │ └── list-common.tcl │ │ └── support │ │ │ ├── tmpfile.tcl │ │ │ └── cli.tcl │ ├── runtest │ ├── gen-test-certs.sh │ └── README.md ├── jm-test-server │ └── src │ │ └── main │ │ └── resources │ │ └── logback.xml └── testnative ├── .github ├── dependabot.yml └── workflows │ └── maven.yml └── .gitignore /scripts/release.sh: -------------------------------------------------------------------------------- 1 | export GPG_TTY=$(tty) 2 | mvn -P release clean deploy -DskipTests -------------------------------------------------------------------------------- /src/test/java/com/github/fppt/jedismock/comparisontests/TestErrorMessages.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.comparisontests; 2 | 3 | public class TestErrorMessages { 4 | public static final String DEADLOCK_ERROR_MESSAGE = "Redis is deadlocked"; 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/exception/ArgumentException.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.exception; 2 | 3 | public class ArgumentException extends RuntimeException { 4 | 5 | public ArgumentException(String message) { 6 | super(message); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/exception/WrongStreamKeyException.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.exception; 2 | 3 | public class WrongStreamKeyException extends Exception { 4 | public WrongStreamKeyException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /native_tests/linux/tests/unit/type/list-common.tcl: -------------------------------------------------------------------------------- 1 | # We need a value larger than list-max-ziplist-value to make sure 2 | # the list has the right encoding when it is swapped in again. 3 | array set largevalue {} 4 | set largevalue(ziplist) "hello" 5 | set largevalue(linkedlist) [string repeat "hello" 4] 6 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/exception/EOFException.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.exception; 2 | 3 | /** 4 | * Created by Xiaolu on 2015/4/21. 5 | */ 6 | public class EOFException extends RuntimeException { 7 | 8 | public EOFException() { 9 | super(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: maven 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | open-pull-requests-limit: 10 8 | ignore: 9 | - dependency-name: com.puppycrawl.tools:checkstyle 10 | versions: 11 | - ">= 10" 12 | 13 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/exception/DeserializationException.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.exception; 2 | 3 | 4 | public class DeserializationException extends RuntimeException { 5 | 6 | public DeserializationException(String message) { 7 | super(message); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Mac files # 2 | .DS_Store 3 | 4 | # IDE files # 5 | .idea/ 6 | *.iml 7 | *.geany 8 | 9 | # Compiled files # 10 | out/ 11 | target/ 12 | *.class 13 | 14 | # Mobile Tools for Java (J2ME) 15 | .mtj.tmp/ 16 | 17 | # Package Files # 18 | *.jar 19 | *.war 20 | *.ear 21 | 22 | # Debug files # 23 | dep.tree 24 | 25 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/exception/ParseErrorException.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.exception; 2 | 3 | /** 4 | * Created by Xiaolu on 2015/4/20. 5 | */ 6 | public class ParseErrorException extends RuntimeException { 7 | 8 | public ParseErrorException() { 9 | super(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/keys/paramsparser/ExpirationParamsException.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.keys.paramsparser; 2 | 3 | public class ExpirationParamsException extends Exception { 4 | public ExpirationParamsException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/datastructures/StringCompatible.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.datastructures; 2 | 3 | 4 | public abstract class StringCompatible implements RMDataStructure { 5 | 6 | @Override 7 | public final String getTypeName() { 8 | return "string"; 9 | } 10 | 11 | } 12 | -------------------------------------------------------------------------------- /native_tests/linux/runtest: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | TCL_VERSIONS="8.5 8.6" 3 | TCLSH="" 4 | 5 | for VERSION in $TCL_VERSIONS; do 6 | TCL=`which tclsh$VERSION 2>/dev/null` && TCLSH=$TCL 7 | done 8 | 9 | if [ -z $TCLSH ] 10 | then 11 | echo "You need tcl 8.5 or newer in order to run the Redis test" 12 | exit 1 13 | fi 14 | $TCLSH tests/test_helper.tcl "${@}" 15 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/exception/WrongValueTypeException.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.exception; 2 | 3 | /** 4 | * Created by Xiaolu on 2015/4/22. 5 | */ 6 | public class WrongValueTypeException extends RuntimeException { 7 | 8 | public WrongValueTypeException(String message) { 9 | super(message); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/datastructures/RMDataStructure.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.datastructures; 2 | 3 | public interface RMDataStructure { 4 | void raiseTypeCastException(); 5 | String getTypeName(); 6 | 7 | default Slice getAsSlice() { 8 | raiseTypeCastException(); 9 | return null; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/datastructures/streams/SequencedMapIterator.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.datastructures.streams; 2 | 3 | import java.util.Iterator; 4 | import java.util.Map; 5 | 6 | public interface SequencedMapIterator, V> extends Iterator> { 7 | @Override 8 | Map.Entry next(); 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/RedisOperation.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations; 2 | 3 | import com.github.fppt.jedismock.storage.RedisBase; 4 | import com.github.fppt.jedismock.datastructures.Slice; 5 | 6 | /** 7 | * Represents a Redis Operation which can be executed against {@link RedisBase} 8 | */ 9 | public interface RedisOperation { 10 | Slice execute(); 11 | } 12 | -------------------------------------------------------------------------------- /src/test/java/com/github/fppt/jedismock/commands/RedisCommandTest.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.commands; 2 | 3 | import nl.jqno.equalsverifier.EqualsVerifier; 4 | import org.junit.jupiter.api.Test; 5 | 6 | class RedisCommandTest { 7 | @Test 8 | void equalsHashcode() { 9 | EqualsVerifier.forClass(RedisCommand.class) 10 | .withNonnullFields("parameters") 11 | .verify(); 12 | } 13 | } -------------------------------------------------------------------------------- /native_tests/jm-test-server/src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} -%kvp- %msg%n 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/RedisCommand.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Target(ElementType.TYPE) 9 | @Retention(RetentionPolicy.RUNTIME) 10 | public @interface RedisCommand { 11 | String value(); 12 | boolean transactional() default true; 13 | } 14 | -------------------------------------------------------------------------------- /native_tests/linux/tests/support/tmpfile.tcl: -------------------------------------------------------------------------------- 1 | set ::tmpcounter 0 2 | set ::tmproot "./tests/tmp" 3 | file mkdir $::tmproot 4 | 5 | # returns a dirname unique to this process to write to 6 | proc tmpdir {basename} { 7 | set dir [file join $::tmproot $basename.[pid].[incr ::tmpcounter]] 8 | file mkdir $dir 9 | set _ $dir 10 | } 11 | 12 | # return a filename unique to this process to write to 13 | proc tmpfile {basename} { 14 | file join $::tmproot $basename.[pid].[incr ::tmpcounter] 15 | } 16 | -------------------------------------------------------------------------------- /native_tests/testnative: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -x 4 | cd linux || exit 5 | 6 | java -jar ../jm-test-server/target/jm-test-server-1.0-SNAPSHOT-jar-with-dependencies.jar & 7 | PID=$! 8 | 9 | wait4x redis redis://127.0.0.1:39807 10 | 11 | success=true 12 | for param in "$@"; do 13 | ./runtest --host 127.0.0.1 --port 39807 --single "$param" 14 | if [[ $? -ne 0 ]]; then 15 | success=false 16 | break 17 | fi 18 | done 19 | 20 | kill $PID 21 | 22 | if $success; then 23 | exit 0 24 | else 25 | exit 1 26 | fi 27 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/sortedsets/ZPopMax.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.sortedsets; 2 | 3 | import com.github.fppt.jedismock.datastructures.Slice; 4 | import com.github.fppt.jedismock.operations.RedisCommand; 5 | import com.github.fppt.jedismock.storage.RedisBase; 6 | 7 | import java.util.List; 8 | 9 | @RedisCommand("zpopmax") 10 | public class ZPopMax extends ZPop { 11 | 12 | ZPopMax(RedisBase base, List params) { 13 | super(base, params, true); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/sortedsets/ZPopMin.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.sortedsets; 2 | 3 | import com.github.fppt.jedismock.datastructures.Slice; 4 | import com.github.fppt.jedismock.operations.RedisCommand; 5 | import com.github.fppt.jedismock.storage.RedisBase; 6 | 7 | import java.util.List; 8 | 9 | @RedisCommand("zpopmin") 10 | public class ZPopMin extends ZPop { 11 | 12 | ZPopMin(RedisBase base, List params) { 13 | super(base, params, false); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/strings/IncrByFloat.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.strings; 2 | 3 | import com.github.fppt.jedismock.operations.RedisCommand; 4 | import com.github.fppt.jedismock.storage.RedisBase; 5 | import com.github.fppt.jedismock.datastructures.Slice; 6 | 7 | import java.util.List; 8 | 9 | @RedisCommand("incrbyfloat") 10 | class IncrByFloat extends IncrOrDecrByFloat { 11 | IncrByFloat(RedisBase base, List params) { 12 | super(base, params); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/keys/Unlink.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.keys; 2 | 3 | import com.github.fppt.jedismock.datastructures.Slice; 4 | import com.github.fppt.jedismock.operations.RedisCommand; 5 | import com.github.fppt.jedismock.storage.RedisBase; 6 | 7 | import java.util.List; 8 | 9 | @RedisCommand("unlink") 10 | public class Unlink extends Del { 11 | Unlink(RedisBase base, List params) { 12 | super(base, params); 13 | } 14 | /*Note: this command behaves exactly like 'DEL'*/ 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/sets/SDiffStore.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.sets; 2 | 3 | import com.github.fppt.jedismock.datastructures.Slice; 4 | import com.github.fppt.jedismock.operations.RedisCommand; 5 | import com.github.fppt.jedismock.storage.RedisBase; 6 | 7 | import java.util.List; 8 | 9 | 10 | @RedisCommand("sdiffstore") 11 | class SDiffStore extends SStore { 12 | SDiffStore(RedisBase base, List params) { 13 | super(base, params, 14 | (b, p) -> new SDiff(b, p).getDifference()); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/sets/SUnionStore.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.sets; 2 | 3 | import com.github.fppt.jedismock.datastructures.Slice; 4 | import com.github.fppt.jedismock.operations.RedisCommand; 5 | import com.github.fppt.jedismock.storage.RedisBase; 6 | 7 | import java.util.List; 8 | 9 | 10 | @RedisCommand("sunionstore") 11 | class SUnionStore extends SStore { 12 | SUnionStore(RedisBase base, List params) { 13 | super(base, params, 14 | (b, p) -> new SUnion(b, p).getUnion()); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/sets/SInterStore.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.sets; 2 | 3 | import com.github.fppt.jedismock.datastructures.Slice; 4 | import com.github.fppt.jedismock.operations.RedisCommand; 5 | import com.github.fppt.jedismock.storage.RedisBase; 6 | 7 | import java.util.List; 8 | 9 | 10 | @RedisCommand("sinterstore") 11 | class SInterStore extends SStore { 12 | SInterStore(RedisBase base, List params) { 13 | super(base, params, 14 | (b, p) -> new SInter(b, p).getIntersection()); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/lists/LPop.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.lists; 2 | 3 | import com.github.fppt.jedismock.datastructures.Slice; 4 | import com.github.fppt.jedismock.operations.RedisCommand; 5 | import com.github.fppt.jedismock.storage.RedisBase; 6 | 7 | import java.util.List; 8 | 9 | @RedisCommand("lpop") 10 | class LPop extends ListPopper { 11 | LPop(RedisBase base, List params ) { 12 | super(base, params); 13 | } 14 | 15 | Slice popper(List list) { 16 | return list.remove(0); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/keys/Expire.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.keys; 2 | 3 | import com.github.fppt.jedismock.operations.RedisCommand; 4 | import com.github.fppt.jedismock.storage.RedisBase; 5 | import com.github.fppt.jedismock.datastructures.Slice; 6 | 7 | import java.util.List; 8 | 9 | @RedisCommand("expire") 10 | class Expire extends PExpire { 11 | Expire(RedisBase base, List params) { 12 | super(base, params); 13 | } 14 | 15 | @Override 16 | protected boolean useMillis() { 17 | return false; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/lists/RPop.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.lists; 2 | 3 | import com.github.fppt.jedismock.datastructures.Slice; 4 | import com.github.fppt.jedismock.operations.RedisCommand; 5 | import com.github.fppt.jedismock.storage.RedisBase; 6 | 7 | import java.util.List; 8 | 9 | @RedisCommand("rpop") 10 | class RPop extends ListPopper { 11 | RPop(RedisBase base, List params) { 12 | super(base, params); 13 | } 14 | 15 | Slice popper(List list) { 16 | return list.remove(list.size() - 1); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/keys/ExpireAt.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.keys; 2 | 3 | import com.github.fppt.jedismock.datastructures.Slice; 4 | import com.github.fppt.jedismock.operations.RedisCommand; 5 | import com.github.fppt.jedismock.storage.RedisBase; 6 | 7 | import java.util.List; 8 | 9 | @RedisCommand("expireat") 10 | class ExpireAt extends PExpireAt { 11 | ExpireAt(RedisBase base, List params) { 12 | super(base, params); 13 | } 14 | 15 | @Override 16 | protected boolean useMillis() { 17 | return false; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/strings/Decr.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.strings; 2 | 3 | import com.github.fppt.jedismock.operations.RedisCommand; 4 | import com.github.fppt.jedismock.storage.RedisBase; 5 | import com.github.fppt.jedismock.datastructures.Slice; 6 | 7 | import java.util.List; 8 | 9 | @RedisCommand("decr") 10 | class Decr extends DecrBy { 11 | Decr(RedisBase base, List params) { 12 | super(base, params); 13 | } 14 | 15 | @Override 16 | long incrementOrDecrementValue(List params){ 17 | return -1L; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/strings/Incr.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.strings; 2 | 3 | import com.github.fppt.jedismock.operations.RedisCommand; 4 | import com.github.fppt.jedismock.storage.RedisBase; 5 | import com.github.fppt.jedismock.datastructures.Slice; 6 | 7 | import java.util.List; 8 | 9 | @RedisCommand("incr") 10 | class Incr extends IncrBy { 11 | Incr(RedisBase base, List params) { 12 | super(base, params); 13 | } 14 | 15 | @Override 16 | long incrementOrDecrementValue(List params){ 17 | return 1L; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /native_tests/linux/tests/support/cli.tcl: -------------------------------------------------------------------------------- 1 | proc rediscli_tls_config {testsdir} { 2 | set tlsdir [file join $testsdir tls] 3 | set cert [file join $tlsdir redis.crt] 4 | set key [file join $tlsdir redis.key] 5 | set cacert [file join $tlsdir ca.crt] 6 | 7 | if {$::tls} { 8 | return [list --tls --cert $cert --key $key --cacert $cacert] 9 | } else { 10 | return {} 11 | } 12 | } 13 | 14 | proc rediscli {host port {opts {}}} { 15 | set cmd [list src/redis-cli -h $host -p $port] 16 | lappend cmd {*}[rediscli_tls_config "tests"] 17 | lappend cmd {*}$opts 18 | return $cmd 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/keys/PTTL.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.keys; 2 | 3 | import com.github.fppt.jedismock.operations.RedisCommand; 4 | import com.github.fppt.jedismock.storage.RedisBase; 5 | import com.github.fppt.jedismock.server.Response; 6 | import com.github.fppt.jedismock.datastructures.Slice; 7 | 8 | import java.util.List; 9 | 10 | @RedisCommand("pttl") 11 | class PTTL extends TTL { 12 | PTTL(RedisBase base, List params) { 13 | super(base, params); 14 | } 15 | 16 | Slice finalReturn(Long pttl){ 17 | return Response.integer(pttl); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/lists/RPush.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.lists; 2 | 3 | import com.github.fppt.jedismock.operations.RedisCommand; 4 | import com.github.fppt.jedismock.storage.OperationExecutorState; 5 | import com.github.fppt.jedismock.datastructures.Slice; 6 | 7 | import java.util.List; 8 | 9 | @RedisCommand("rpush") 10 | class RPush extends Add { 11 | RPush(OperationExecutorState state, List params) { 12 | super(state, params); 13 | } 14 | 15 | @Override 16 | void addSliceToList(List list, Slice slice) { 17 | list.add(slice); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/lists/LPush.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.lists; 2 | 3 | import com.github.fppt.jedismock.operations.RedisCommand; 4 | import com.github.fppt.jedismock.storage.OperationExecutorState; 5 | import com.github.fppt.jedismock.datastructures.Slice; 6 | 7 | import java.util.List; 8 | 9 | @RedisCommand("lpush") 10 | class LPush extends Add { 11 | LPush(OperationExecutorState state, List params) { 12 | super(state, params); 13 | } 14 | 15 | @Override 16 | void addSliceToList(List list, Slice slice) { 17 | list.add(0, slice); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/hashes/expiration/HExpire.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.hashes.expiration; 2 | 3 | import com.github.fppt.jedismock.datastructures.Slice; 4 | import com.github.fppt.jedismock.operations.RedisCommand; 5 | import com.github.fppt.jedismock.storage.RedisBase; 6 | 7 | import java.util.List; 8 | 9 | @RedisCommand("hexpire") 10 | public class HExpire extends HPExpire { 11 | public HExpire(RedisBase base, List params) { 12 | super(base, params); 13 | } 14 | 15 | @Override 16 | protected boolean useMillis() { 17 | return false; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/hashes/expiration/HExpireAt.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.hashes.expiration; 2 | 3 | import com.github.fppt.jedismock.datastructures.Slice; 4 | import com.github.fppt.jedismock.operations.RedisCommand; 5 | import com.github.fppt.jedismock.storage.RedisBase; 6 | 7 | import java.util.List; 8 | 9 | @RedisCommand("hexpireat") 10 | public class HExpireAt extends HPExpireAt { 11 | public HExpireAt(RedisBase base, List params) { 12 | super(base, params); 13 | } 14 | 15 | @Override 16 | protected boolean useMillis() { 17 | return false; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/server/Info.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.server; 2 | 3 | import com.github.fppt.jedismock.operations.RedisCommand; 4 | import com.github.fppt.jedismock.operations.RedisOperation; 5 | import com.github.fppt.jedismock.server.Response; 6 | import com.github.fppt.jedismock.datastructures.Slice; 7 | 8 | @RedisCommand(value = "info", transactional = false) 9 | class Info implements RedisOperation { 10 | @Override 11 | public Slice execute() { 12 | //role:master line is needed for Lettuce client 13 | return Response.bulkString(Slice.create("Redis Mock Server Info\nrole:master")); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/scripting/cjson/LuaCjsonLib.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.scripting.cjson; 2 | 3 | import org.luaj.vm2.LuaValue; 4 | import org.luaj.vm2.lib.TwoArgFunction; 5 | 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | 9 | public class LuaCjsonLib extends TwoArgFunction { 10 | 11 | @Override 12 | public LuaValue call(LuaValue modname, LuaValue env) { 13 | Map cjsonMap = new HashMap<>(); 14 | cjsonMap.put(LuaValue.valueOf("encode"), new Encode()); 15 | cjsonMap.put(LuaValue.valueOf("decode"), new Decode()); 16 | return new ImmutableLuaTable(cjsonMap); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/hashes/HMSet.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.hashes; 2 | 3 | import com.github.fppt.jedismock.operations.RedisCommand; 4 | import com.github.fppt.jedismock.server.Response; 5 | import com.github.fppt.jedismock.datastructures.Slice; 6 | import com.github.fppt.jedismock.storage.RedisBase; 7 | 8 | import java.util.List; 9 | 10 | @RedisCommand("hmset") 11 | public class HMSet extends HSet { 12 | public HMSet(RedisBase base, List params) { 13 | super(base, params); 14 | } 15 | 16 | @Override 17 | protected Slice response() { 18 | super.response(); 19 | 20 | return Response.OK; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/resources/redis.lua: -------------------------------------------------------------------------------- 1 | -- Global `redis` object available for Lua scripts 2 | 3 | return { 4 | LOG_DEBUG = 0, 5 | LOG_VERBOSE = 1, 6 | LOG_NOTICE = 2, 7 | LOG_WARNING = 3, 8 | 9 | call = function(...) 10 | return _mock:call({...}) 11 | end, 12 | 13 | pcall = function(...) 14 | return _mock:pcall({...}) 15 | end, 16 | 17 | sha1hex = function(x) 18 | return _mock:sha1hex(x) 19 | end, 20 | 21 | status_reply = function(x) 22 | return { ok = x } 23 | end, 24 | 25 | error_reply = function(x) 26 | return { err = x } 27 | end, 28 | 29 | log = function(level, message) 30 | return _mock:log(level, message) 31 | end, 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/strings/IncrBy.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.strings; 2 | 3 | import com.github.fppt.jedismock.operations.RedisCommand; 4 | import com.github.fppt.jedismock.storage.RedisBase; 5 | import com.github.fppt.jedismock.datastructures.Slice; 6 | 7 | import java.util.List; 8 | 9 | import static com.github.fppt.jedismock.Utils.convertToLong; 10 | 11 | @RedisCommand("incrby") 12 | class IncrBy extends IncrOrDecrBy { 13 | IncrBy(RedisBase base, List params) { 14 | super(base, params); 15 | } 16 | 17 | long incrementOrDecrementValue(List params){ 18 | return convertToLong(String.valueOf(params.get(1))); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/strings/DecrBy.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.strings; 2 | 3 | import com.github.fppt.jedismock.operations.RedisCommand; 4 | import com.github.fppt.jedismock.storage.RedisBase; 5 | import com.github.fppt.jedismock.datastructures.Slice; 6 | 7 | import java.util.List; 8 | 9 | import static com.github.fppt.jedismock.Utils.convertToLong; 10 | 11 | @RedisCommand("decrby") 12 | class DecrBy extends IncrOrDecrBy { 13 | DecrBy(RedisBase base, List params) { 14 | super(base, params); 15 | } 16 | 17 | long incrementOrDecrementValue(List params){ 18 | return convertToLong(String.valueOf(params.get(1))) * -1; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/strings/PSetEx.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.strings; 2 | 3 | import com.github.fppt.jedismock.operations.RedisCommand; 4 | import com.github.fppt.jedismock.storage.RedisBase; 5 | import com.github.fppt.jedismock.datastructures.Slice; 6 | 7 | import java.util.List; 8 | 9 | import static com.github.fppt.jedismock.Utils.convertToLong; 10 | 11 | @RedisCommand("psetex") 12 | class PSetEx extends SetEx { 13 | PSetEx(RedisBase base, List params) { 14 | super(base, params); 15 | } 16 | 17 | @Override 18 | long timeoutToSet(List params){ 19 | return convertToLong(new String(params.get(1).data())); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/sortedsets/ZDiff.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.sortedsets; 2 | 3 | import com.github.fppt.jedismock.datastructures.Slice; 4 | import com.github.fppt.jedismock.operations.RedisCommand; 5 | import com.github.fppt.jedismock.server.Response; 6 | import com.github.fppt.jedismock.storage.OperationExecutorState; 7 | 8 | import java.util.List; 9 | 10 | @RedisCommand("zdiff") 11 | class ZDiff extends AbstractZDiff { 12 | 13 | ZDiff(OperationExecutorState state, List params) { 14 | super(state, params); 15 | } 16 | 17 | @Override 18 | protected Slice response() { 19 | return Response.array(getResultArray()); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/sortedsets/ZInter.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.sortedsets; 2 | 3 | import com.github.fppt.jedismock.datastructures.Slice; 4 | import com.github.fppt.jedismock.operations.RedisCommand; 5 | import com.github.fppt.jedismock.server.Response; 6 | import com.github.fppt.jedismock.storage.OperationExecutorState; 7 | 8 | import java.util.List; 9 | 10 | @RedisCommand("zinter") 11 | class ZInter extends AbstractZInter { 12 | 13 | ZInter(OperationExecutorState state, List params) { 14 | super(state, params); 15 | } 16 | 17 | @Override 18 | protected Slice response() { 19 | return Response.array(getResultArray()); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/sortedsets/ZUnion.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.sortedsets; 2 | 3 | import com.github.fppt.jedismock.datastructures.Slice; 4 | import com.github.fppt.jedismock.operations.RedisCommand; 5 | import com.github.fppt.jedismock.server.Response; 6 | import com.github.fppt.jedismock.storage.OperationExecutorState; 7 | 8 | import java.util.List; 9 | 10 | @RedisCommand("zunion") 11 | class ZUnion extends AbstractZUnion { 12 | 13 | ZUnion(OperationExecutorState state, List params) { 14 | super(state, params); 15 | } 16 | 17 | @Override 18 | protected Slice response() { 19 | return Response.array(getResultArray()); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/hashes/expiration/HTTL.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.hashes.expiration; 2 | 3 | import com.github.fppt.jedismock.datastructures.Slice; 4 | import com.github.fppt.jedismock.operations.RedisCommand; 5 | import com.github.fppt.jedismock.server.Response; 6 | import com.github.fppt.jedismock.storage.RedisBase; 7 | 8 | import java.util.List; 9 | 10 | @RedisCommand("httl") 11 | public class HTTL extends HPTTL { 12 | public HTTL(RedisBase base, List params) { 13 | super(base, params); 14 | } 15 | 16 | @Override 17 | protected Slice wrap(long pttl) { 18 | return Response.integer((pttl + 999) / 1000); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/test/java/com/github/fppt/jedismock/TestUtils.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | import java.io.IOException; 6 | import java.io.InputStream; 7 | 8 | /** 9 | * Created by Xiaolu on 2015/4/20. 10 | */ 11 | public class TestUtils { 12 | 13 | @Test 14 | public void testCloseQuietly() { 15 | Utils.closeQuietly(null); 16 | Utils.closeQuietly(new InputStream() { 17 | @Override 18 | public int read() { 19 | return 0; 20 | } 21 | 22 | @Override 23 | public void close() throws IOException { 24 | throw new IOException(); 25 | } 26 | }); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/sortedsets/ZInterStore.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.sortedsets; 2 | 3 | import com.github.fppt.jedismock.datastructures.Slice; 4 | import com.github.fppt.jedismock.operations.RedisCommand; 5 | import com.github.fppt.jedismock.server.Response; 6 | import com.github.fppt.jedismock.storage.OperationExecutorState; 7 | 8 | import java.util.List; 9 | 10 | @RedisCommand("zinterstore") 11 | class ZInterStore extends AbstractZInter { 12 | 13 | ZInterStore(OperationExecutorState state, List params) { 14 | super(state, params); 15 | } 16 | 17 | @Override 18 | protected Slice response() { 19 | return Response.integer(getResultSize()); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/sortedsets/ZUnionStore.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.sortedsets; 2 | 3 | import com.github.fppt.jedismock.datastructures.Slice; 4 | import com.github.fppt.jedismock.operations.RedisCommand; 5 | import com.github.fppt.jedismock.server.Response; 6 | import com.github.fppt.jedismock.storage.OperationExecutorState; 7 | 8 | import java.util.List; 9 | 10 | @RedisCommand("zunionstore") 11 | class ZUnionStore extends AbstractZUnion { 12 | 13 | ZUnionStore(OperationExecutorState state, List params) { 14 | super(state, params); 15 | } 16 | 17 | @Override 18 | protected Slice response() { 19 | return Response.integer(getResultSize()); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/keys/Type.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.keys; 2 | 3 | import com.github.fppt.jedismock.operations.AbstractRedisOperation; 4 | import com.github.fppt.jedismock.operations.RedisCommand; 5 | import com.github.fppt.jedismock.server.Response; 6 | import com.github.fppt.jedismock.datastructures.Slice; 7 | import com.github.fppt.jedismock.storage.RedisBase; 8 | 9 | import java.util.List; 10 | 11 | @RedisCommand("type") 12 | class Type extends AbstractRedisOperation { 13 | Type(RedisBase base, List params) { 14 | super(base, params); 15 | } 16 | 17 | protected Slice response() { 18 | return Response.bulkString(base().type(params().get(0))); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/server/DBSize.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.server; 2 | 3 | import com.github.fppt.jedismock.operations.AbstractRedisOperation; 4 | import com.github.fppt.jedismock.operations.RedisCommand; 5 | import com.github.fppt.jedismock.server.Response; 6 | import com.github.fppt.jedismock.datastructures.Slice; 7 | import com.github.fppt.jedismock.storage.RedisBase; 8 | 9 | import java.util.List; 10 | 11 | @RedisCommand("dbsize") 12 | class DBSize extends AbstractRedisOperation { 13 | DBSize(RedisBase base, List params) { 14 | super(base, params); 15 | } 16 | 17 | protected Slice response() { 18 | return Response.integer(base().keys().size()); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/sortedsets/ZDiffStore.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.sortedsets; 2 | 3 | import com.github.fppt.jedismock.datastructures.Slice; 4 | import com.github.fppt.jedismock.operations.RedisCommand; 5 | import com.github.fppt.jedismock.server.Response; 6 | import com.github.fppt.jedismock.storage.OperationExecutorState; 7 | 8 | import java.util.List; 9 | 10 | @RedisCommand("zdiffstore") 11 | class ZDiffStore extends AbstractZDiff { 12 | 13 | ZDiffStore(OperationExecutorState state, List params) { 14 | super(state, params); 15 | } 16 | 17 | @Override 18 | protected Slice response() { 19 | return Response.integer(getResultSize()); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/streams/XRevRange.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.streams; 2 | 3 | import com.github.fppt.jedismock.datastructures.Slice; 4 | import com.github.fppt.jedismock.operations.RedisCommand; 5 | import com.github.fppt.jedismock.storage.RedisBase; 6 | 7 | import java.util.List; 8 | 9 | @RedisCommand("xrevrange") 10 | public class XRevRange extends Ranges { 11 | public XRevRange(RedisBase base, List params) { 12 | super(base, params); 13 | } 14 | 15 | @Override 16 | protected int minArgs() { 17 | return 3; 18 | } 19 | 20 | @Override 21 | protected Slice response() { 22 | multiplier = -1; 23 | return range(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/server/FlushDB.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.server; 2 | 3 | import com.github.fppt.jedismock.operations.AbstractRedisOperation; 4 | import com.github.fppt.jedismock.operations.RedisCommand; 5 | import com.github.fppt.jedismock.server.Response; 6 | import com.github.fppt.jedismock.datastructures.Slice; 7 | import com.github.fppt.jedismock.storage.RedisBase; 8 | 9 | import java.util.List; 10 | 11 | @RedisCommand("flushdb") 12 | class FlushDB extends AbstractRedisOperation { 13 | FlushDB(RedisBase base, List params) { 14 | super(base, params); 15 | } 16 | 17 | protected Slice response(){ 18 | base().clear(); 19 | return Response.OK; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/strings/Get.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.strings; 2 | 3 | import com.github.fppt.jedismock.operations.AbstractRedisOperation; 4 | import com.github.fppt.jedismock.operations.RedisCommand; 5 | import com.github.fppt.jedismock.server.Response; 6 | import com.github.fppt.jedismock.datastructures.Slice; 7 | import com.github.fppt.jedismock.storage.RedisBase; 8 | 9 | import java.util.List; 10 | 11 | @RedisCommand("get") 12 | class Get extends AbstractRedisOperation { 13 | Get(RedisBase base, List params) { 14 | super(base, params); 15 | } 16 | 17 | protected Slice response() { 18 | return Response.bulkString(base().getSlice(params().get(0))); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/hashes/expiration/HExpireTime.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.hashes.expiration; 2 | 3 | import com.github.fppt.jedismock.datastructures.Slice; 4 | import com.github.fppt.jedismock.operations.RedisCommand; 5 | import com.github.fppt.jedismock.server.Response; 6 | import com.github.fppt.jedismock.storage.RedisBase; 7 | 8 | import java.util.List; 9 | 10 | @RedisCommand("hexpiretime") 11 | public class HExpireTime extends HPExpireTime { 12 | public HExpireTime(RedisBase base, List params) { 13 | super(base, params); 14 | } 15 | 16 | @Override 17 | protected Slice wrap(long deadline) { 18 | return Response.integer(deadline / 1000); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/keys/Persist.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.keys; 2 | 3 | import com.github.fppt.jedismock.datastructures.Slice; 4 | import com.github.fppt.jedismock.operations.AbstractRedisOperation; 5 | import com.github.fppt.jedismock.operations.RedisCommand; 6 | import com.github.fppt.jedismock.server.Response; 7 | import com.github.fppt.jedismock.storage.RedisBase; 8 | 9 | import java.util.List; 10 | 11 | @RedisCommand("persist") 12 | class Persist extends AbstractRedisOperation { 13 | Persist(RedisBase base, List params) { 14 | super(base, params); 15 | } 16 | 17 | protected Slice response() { 18 | return Response.integer(base().setDeadline(params().get(0), -1)); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/hashes/HGet.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.hashes; 2 | 3 | import com.github.fppt.jedismock.operations.AbstractRedisOperation; 4 | import com.github.fppt.jedismock.operations.RedisCommand; 5 | import com.github.fppt.jedismock.server.Response; 6 | import com.github.fppt.jedismock.datastructures.Slice; 7 | import com.github.fppt.jedismock.storage.RedisBase; 8 | 9 | import java.util.List; 10 | 11 | @RedisCommand("hget") 12 | class HGet extends AbstractRedisOperation { 13 | HGet(RedisBase base, List params) { 14 | super(base, params); 15 | } 16 | 17 | protected Slice response() { 18 | return Response.bulkString(base().getSlice(params().get(0), params().get(1))); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/hashes/HSetNX.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.hashes; 2 | 3 | import com.github.fppt.jedismock.datastructures.Slice; 4 | import com.github.fppt.jedismock.operations.RedisCommand; 5 | import com.github.fppt.jedismock.storage.RedisBase; 6 | 7 | import java.util.List; 8 | 9 | @RedisCommand("hsetnx") 10 | class HSetNX extends HSet { 11 | HSetNX(RedisBase base, List params) { 12 | super(base, params); 13 | } 14 | 15 | Slice hsetValue(Slice key1, Slice key2, Slice value){ 16 | Slice foundValue = base().getSlice(key1, key2); 17 | if(foundValue == null){ 18 | base().putSlice(key1, key2, value, -1L); 19 | } 20 | return foundValue; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /native_tests/linux/gen-test-certs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | mkdir -p tests/tls 3 | openssl genrsa -out tests/tls/ca.key 4096 4 | openssl req \ 5 | -x509 -new -nodes -sha256 \ 6 | -key tests/tls/ca.key \ 7 | -days 3650 \ 8 | -subj '/O=Redis Test/CN=Certificate Authority' \ 9 | -out tests/tls/ca.crt 10 | openssl genrsa -out tests/tls/redis.key 2048 11 | openssl req \ 12 | -new -sha256 \ 13 | -key tests/tls/redis.key \ 14 | -subj '/O=Redis Test/CN=Server' | \ 15 | openssl x509 \ 16 | -req -sha256 \ 17 | -CA tests/tls/ca.crt \ 18 | -CAkey tests/tls/ca.key \ 19 | -CAserial tests/tls/ca.txt \ 20 | -CAcreateserial \ 21 | -days 365 \ 22 | -out tests/tls/redis.crt 23 | openssl dhparam -out tests/tls/redis.dh 2048 24 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/lists/BLPop.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.lists; 2 | 3 | import com.github.fppt.jedismock.datastructures.Slice; 4 | import com.github.fppt.jedismock.operations.RedisCommand; 5 | import com.github.fppt.jedismock.server.Response; 6 | import com.github.fppt.jedismock.storage.OperationExecutorState; 7 | 8 | import java.util.List; 9 | 10 | @RedisCommand("blpop") 11 | class BLPop extends BPop { 12 | 13 | BLPop(OperationExecutorState state, List params) { 14 | super(state, params); 15 | } 16 | 17 | @Override 18 | public Slice popper(List params) { 19 | Slice result = new LPop(base(), params).execute(); 20 | return Response.array(Response.bulkString(params.get(0)), result); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/lists/BPop.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.lists; 2 | 3 | import com.github.fppt.jedismock.datastructures.Slice; 4 | import com.github.fppt.jedismock.operations.AbstractBPop; 5 | import com.github.fppt.jedismock.operations.AbstractRedisOperation; 6 | import com.github.fppt.jedismock.storage.OperationExecutorState; 7 | import com.github.fppt.jedismock.storage.RedisBase; 8 | 9 | import java.util.List; 10 | 11 | abstract class BPop extends AbstractBPop { 12 | 13 | BPop(OperationExecutorState state, List params) { 14 | super(state, params); 15 | } 16 | 17 | @Override 18 | protected AbstractRedisOperation getSize(RedisBase base, List params) { 19 | return new LLen(base(), params); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/lists/BRPop.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.lists; 2 | 3 | import com.github.fppt.jedismock.datastructures.Slice; 4 | import com.github.fppt.jedismock.operations.RedisCommand; 5 | import com.github.fppt.jedismock.server.Response; 6 | import com.github.fppt.jedismock.storage.OperationExecutorState; 7 | 8 | import java.util.List; 9 | 10 | @RedisCommand("brpop") 11 | class BRPop extends BPop { 12 | 13 | BRPop(OperationExecutorState state, List params) { 14 | super(state, params); 15 | } 16 | 17 | @Override 18 | public Slice popper(List params) { 19 | Slice result = new RPop(base(), params).execute(); 20 | return Response.array(Response.bulkString(params.get(0)), result); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/server/FlushAll.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.server; 2 | 3 | import com.github.fppt.jedismock.operations.RedisCommand; 4 | import com.github.fppt.jedismock.operations.RedisOperation; 5 | import com.github.fppt.jedismock.server.Response; 6 | import com.github.fppt.jedismock.datastructures.Slice; 7 | import com.github.fppt.jedismock.storage.OperationExecutorState; 8 | 9 | @RedisCommand(value = "flushall") 10 | class FlushAll implements RedisOperation { 11 | private final OperationExecutorState state; 12 | 13 | FlushAll(OperationExecutorState state) { 14 | this.state = state; 15 | } 16 | 17 | @Override 18 | public Slice execute() { 19 | state.clearAll(); 20 | return Response.OK; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/sortedsets/BZPop.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.sortedsets; 2 | 3 | import com.github.fppt.jedismock.datastructures.Slice; 4 | import com.github.fppt.jedismock.operations.AbstractBPop; 5 | import com.github.fppt.jedismock.operations.AbstractRedisOperation; 6 | import com.github.fppt.jedismock.storage.OperationExecutorState; 7 | import com.github.fppt.jedismock.storage.RedisBase; 8 | 9 | import java.util.List; 10 | 11 | abstract class BZPop extends AbstractBPop { 12 | 13 | BZPop(OperationExecutorState state, List params) { 14 | super(state, params); 15 | } 16 | 17 | @Override 18 | protected AbstractRedisOperation getSize(RedisBase base, List params) { 19 | return new ZCard(base(), params); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/streams/XRange.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.streams; 2 | 3 | import com.github.fppt.jedismock.datastructures.Slice; 4 | import com.github.fppt.jedismock.operations.RedisCommand; 5 | import com.github.fppt.jedismock.storage.RedisBase; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * XRANGE key start end [COUNT count]
11 | * Supported options: COUNT 12 | */ 13 | @RedisCommand("xrange") 14 | public class XRange extends Ranges { 15 | XRange(RedisBase base, List params) { 16 | super(base, params); 17 | } 18 | 19 | @Override 20 | protected int minArgs() { 21 | return 3; 22 | } 23 | 24 | @Override 25 | protected Slice response() { 26 | multiplier = 1; 27 | return range(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/keys/Exists.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.keys; 2 | 3 | import com.github.fppt.jedismock.operations.AbstractRedisOperation; 4 | import com.github.fppt.jedismock.operations.RedisCommand; 5 | import com.github.fppt.jedismock.server.Response; 6 | import com.github.fppt.jedismock.datastructures.Slice; 7 | import com.github.fppt.jedismock.storage.RedisBase; 8 | 9 | import java.util.List; 10 | 11 | @RedisCommand("exists") 12 | class Exists extends AbstractRedisOperation { 13 | Exists(RedisBase base, List params) { 14 | super(base, params); 15 | } 16 | 17 | protected Slice response() { 18 | if (base().exists(params().get(0))) { 19 | return Response.integer(1); 20 | } 21 | return Response.integer(0); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/transactions/Unwatch.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.transactions; 2 | 3 | import com.github.fppt.jedismock.datastructures.Slice; 4 | import com.github.fppt.jedismock.operations.RedisCommand; 5 | import com.github.fppt.jedismock.operations.RedisOperation; 6 | import com.github.fppt.jedismock.server.Response; 7 | import com.github.fppt.jedismock.storage.OperationExecutorState; 8 | 9 | @RedisCommand(value = "unwatch", transactional = false) 10 | public class Unwatch implements RedisOperation { 11 | private final OperationExecutorState state; 12 | 13 | Unwatch(OperationExecutorState state) { 14 | this.state = state; 15 | } 16 | 17 | @Override 18 | public Slice execute() { 19 | state.unwatch(); 20 | return Response.OK; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /native_tests/linux/README.md: -------------------------------------------------------------------------------- 1 | This directory contains some tests from the original [redis](https://github.com/redis/redis) 2 | 3 | These tests cover a significant part of the functionality of redis 4 | 5 | To run these tests, first you need to make sure that the following conditions are met: 6 | 7 | * `tcl-tls`installed 8 | * The necessary certificates have been generated 9 | 10 | Certificates can be generated using: 11 | 12 | % ./gen-test-certs.sh 13 | 14 | When all the conditions are met, run the tests with the following command: 15 | 16 | % ./runtest --host --port 17 | 18 | For more flexible testing, see [original redis tests](https://github.com/redis/redis/tree/7.0/tests) 19 | 20 | NOTE: do not forget to comment out the content of `assert_encoding` from `support/test`, as encodings are not 21 | supported by JedisMock. 22 | -------------------------------------------------------------------------------- /src/test/java/com/github/fppt/jedismock/comparisontests/cluster/TestCluster.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.comparisontests.cluster; 2 | 3 | import com.github.fppt.jedismock.comparisontests.ComparisonBase; 4 | import org.junit.jupiter.api.TestTemplate; 5 | import org.junit.jupiter.api.extension.ExtendWith; 6 | import redis.clients.jedis.Jedis; 7 | import redis.clients.jedis.exceptions.JedisDataException; 8 | 9 | import static org.assertj.core.api.Assertions.assertThatThrownBy; 10 | 11 | @ExtendWith(ComparisonBase.class) 12 | public class TestCluster { 13 | @TestTemplate 14 | void testClusterInNonClusterMode(Jedis jedis) { 15 | assertThatThrownBy(jedis::clusterMyId) 16 | .isInstanceOf(JedisDataException.class) 17 | .hasMessage("ERR This instance has cluster support disabled"); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/connection/Ping.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.connection; 2 | 3 | import com.github.fppt.jedismock.operations.AbstractRedisOperation; 4 | import com.github.fppt.jedismock.operations.RedisCommand; 5 | import com.github.fppt.jedismock.server.Response; 6 | import com.github.fppt.jedismock.datastructures.Slice; 7 | import com.github.fppt.jedismock.storage.RedisBase; 8 | 9 | import java.util.List; 10 | 11 | @RedisCommand("ping") 12 | class Ping extends AbstractRedisOperation { 13 | Ping(RedisBase base, List params) { 14 | super(base, params); 15 | } 16 | 17 | protected Slice response() { 18 | if (params().isEmpty()){ 19 | return Response.bulkString(Slice.create("PONG")); 20 | } 21 | 22 | return Response.bulkString(params().get(0)); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/lists/LPushX.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.lists; 2 | 3 | import com.github.fppt.jedismock.operations.RedisCommand; 4 | import com.github.fppt.jedismock.storage.OperationExecutorState; 5 | import com.github.fppt.jedismock.server.Response; 6 | import com.github.fppt.jedismock.datastructures.Slice; 7 | 8 | import java.util.List; 9 | 10 | @RedisCommand("lpushx") 11 | class LPushX extends LPush { 12 | LPushX(OperationExecutorState state, List params) { 13 | super(state, params); 14 | } 15 | 16 | @Override 17 | protected Slice response(){ 18 | Slice key = params().get(0); 19 | boolean exists = base().exists(key); 20 | 21 | if(exists){ 22 | return super.response(); 23 | } 24 | 25 | return Response.integer(0); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/strings/GetSet.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.strings; 2 | 3 | import com.github.fppt.jedismock.operations.AbstractRedisOperation; 4 | import com.github.fppt.jedismock.operations.RedisCommand; 5 | import com.github.fppt.jedismock.server.Response; 6 | import com.github.fppt.jedismock.datastructures.Slice; 7 | import com.github.fppt.jedismock.storage.RedisBase; 8 | 9 | import java.util.List; 10 | 11 | @RedisCommand("getset") 12 | class GetSet extends AbstractRedisOperation { 13 | GetSet(RedisBase base, List params) { 14 | super(base, params); 15 | } 16 | 17 | protected Slice response() { 18 | Slice value = base().getSlice(params().get(0)); 19 | base().putValue(params().get(0), params().get(1).extract()); 20 | return Response.bulkString(value); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/strings/MSet.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.strings; 2 | 3 | import com.github.fppt.jedismock.operations.AbstractRedisOperation; 4 | import com.github.fppt.jedismock.operations.RedisCommand; 5 | import com.github.fppt.jedismock.server.Response; 6 | import com.github.fppt.jedismock.datastructures.Slice; 7 | import com.github.fppt.jedismock.storage.RedisBase; 8 | 9 | import java.util.List; 10 | 11 | @RedisCommand("mset") 12 | class MSet extends AbstractRedisOperation { 13 | MSet(RedisBase base, List params ) { 14 | super(base, params); 15 | } 16 | 17 | protected Slice response() { 18 | for (int i = 0; i < params().size(); i += 2) { 19 | base().putValue(params().get(i), params().get(i + 1).extract()); 20 | } 21 | return Response.OK; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/hashes/HExists.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.hashes; 2 | 3 | import com.github.fppt.jedismock.operations.AbstractRedisOperation; 4 | import com.github.fppt.jedismock.operations.RedisCommand; 5 | import com.github.fppt.jedismock.server.Response; 6 | import com.github.fppt.jedismock.datastructures.Slice; 7 | import com.github.fppt.jedismock.storage.RedisBase; 8 | 9 | import java.util.List; 10 | 11 | @RedisCommand("hexists") 12 | public class HExists extends AbstractRedisOperation { 13 | HExists(RedisBase base, List params) { 14 | super(base, params); 15 | } 16 | 17 | protected Slice response() { 18 | if (base().getSlice(params().get(0), params().get(1)) == null){ 19 | return Response.integer(0); 20 | } 21 | return Response.integer(1); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /.github/workflows/maven.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | branches: 9 | - master 10 | 11 | jobs: 12 | build: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: checkout 16 | uses: actions/checkout@v4 17 | 18 | - name: Set up JDK 8 19 | uses: actions/setup-java@v4 20 | with: 21 | distribution: 'temurin' 22 | java-version: '8' 23 | cache: 'maven' 24 | 25 | - name: Build with Maven 26 | run: mvn -B -V verify -Dmaven.javadoc.skip 27 | 28 | - name: Update list of supported operations 29 | if: success() && github.ref == 'refs/heads/master' 30 | uses: stefanzweifel/git-auto-commit-action@v4 31 | with: 32 | commit_message: Update list of supported operations 33 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/lists/RPushX.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.lists; 2 | 3 | import com.github.fppt.jedismock.datastructures.Slice; 4 | import com.github.fppt.jedismock.operations.RedisCommand; 5 | import com.github.fppt.jedismock.server.Response; 6 | import com.github.fppt.jedismock.storage.OperationExecutorState; 7 | 8 | import java.util.List; 9 | 10 | @RedisCommand("rpushx") 11 | public class RPushX extends RPush { 12 | RPushX(OperationExecutorState state, List params) { 13 | super(state, params); 14 | } 15 | 16 | @Override 17 | protected Slice response() { 18 | Slice key = params().get(0); 19 | boolean exists = base().exists(key); 20 | 21 | if (exists) { 22 | return super.response(); 23 | } 24 | 25 | return Response.integer(0); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/connection/Auth.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.connection; 2 | 3 | import com.github.fppt.jedismock.operations.RedisCommand; 4 | import com.github.fppt.jedismock.operations.RedisOperation; 5 | import com.github.fppt.jedismock.server.Response; 6 | import com.github.fppt.jedismock.datastructures.Slice; 7 | import com.github.fppt.jedismock.storage.OperationExecutorState; 8 | 9 | @RedisCommand(value = "auth", transactional = false) 10 | public class Auth implements RedisOperation { 11 | private final OperationExecutorState state; 12 | 13 | public Auth(OperationExecutorState state) { 14 | this.state = state; 15 | } 16 | 17 | @Override 18 | public Slice execute() { 19 | state.owner().sendResponse(Response.clientResponse("auth", Response.OK), "auth"); 20 | return Response.SKIP; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/test/java/com/github/fppt/jedismock/comparisontests/lists/LRangeTest.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.comparisontests.lists; 2 | 3 | import com.github.fppt.jedismock.comparisontests.ComparisonBase; 4 | import org.junit.jupiter.api.BeforeEach; 5 | import org.junit.jupiter.api.TestTemplate; 6 | import org.junit.jupiter.api.extension.ExtendWith; 7 | import redis.clients.jedis.Jedis; 8 | 9 | import static org.assertj.core.api.Assertions.assertThat; 10 | 11 | @ExtendWith(ComparisonBase.class) 12 | public class LRangeTest { 13 | @BeforeEach 14 | public void setUp(Jedis jedis) { 15 | jedis.flushAll(); 16 | } 17 | 18 | @TestTemplate 19 | public void whenUsingLRange_checkOutOfRangeNegativeEndIndex(Jedis jedis) { 20 | String key = "lrange_key"; 21 | jedis.lpush(key, "1", "2", "3"); 22 | 23 | assertThat(jedis.lrange(key, 0, -5)).isEmpty(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/transactions/Discard.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.transactions; 2 | 3 | import com.github.fppt.jedismock.datastructures.Slice; 4 | import com.github.fppt.jedismock.operations.RedisCommand; 5 | import com.github.fppt.jedismock.operations.RedisOperation; 6 | import com.github.fppt.jedismock.server.Response; 7 | import com.github.fppt.jedismock.storage.OperationExecutorState; 8 | 9 | @RedisCommand(value = "discard", transactional = false) 10 | public class Discard implements RedisOperation { 11 | private final OperationExecutorState state; 12 | 13 | Discard(OperationExecutorState state){ 14 | this.state = state; 15 | } 16 | 17 | @Override 18 | public Slice execute() { 19 | state.transactionMode(false); 20 | state.tx().clear(); 21 | state.unwatch(); 22 | return Response.OK; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/strings/GetDel.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.strings; 2 | 3 | import com.github.fppt.jedismock.datastructures.Slice; 4 | import com.github.fppt.jedismock.operations.AbstractRedisOperation; 5 | import com.github.fppt.jedismock.operations.RedisCommand; 6 | import com.github.fppt.jedismock.server.Response; 7 | import com.github.fppt.jedismock.storage.RedisBase; 8 | 9 | import java.util.List; 10 | 11 | @RedisCommand("getdel") 12 | public class GetDel extends AbstractRedisOperation { 13 | public GetDel(RedisBase base, List params) { 14 | super(base, params); 15 | } 16 | 17 | protected Slice response() { 18 | Slice slice = base().getSlice(params().get(0)); 19 | if (slice != null) { 20 | base().deleteValue(params().get(0)); 21 | } 22 | return Response.bulkString(slice); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/hashes/HLen.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.hashes; 2 | 3 | import com.github.fppt.jedismock.datastructures.RMHash; 4 | import com.github.fppt.jedismock.operations.AbstractRedisOperation; 5 | import com.github.fppt.jedismock.operations.RedisCommand; 6 | import com.github.fppt.jedismock.server.Response; 7 | import com.github.fppt.jedismock.datastructures.Slice; 8 | import com.github.fppt.jedismock.storage.RedisBase; 9 | 10 | import java.util.List; 11 | 12 | @RedisCommand("hlen") 13 | public class HLen extends AbstractRedisOperation { 14 | public HLen(RedisBase base, List params) { 15 | super(base, params); 16 | } 17 | 18 | protected Slice response() { 19 | Slice key = params().get(0); 20 | RMHash map = base().getHash(key); 21 | return Response.integer(map == null ? 0 : map.sizeIncludingExpired()); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/strings/SetNX.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.strings; 2 | 3 | import com.github.fppt.jedismock.operations.AbstractRedisOperation; 4 | import com.github.fppt.jedismock.operations.RedisCommand; 5 | import com.github.fppt.jedismock.server.Response; 6 | import com.github.fppt.jedismock.datastructures.Slice; 7 | import com.github.fppt.jedismock.storage.RedisBase; 8 | 9 | import java.util.List; 10 | 11 | @RedisCommand("setnx") 12 | class SetNX extends AbstractRedisOperation { 13 | SetNX(RedisBase base, List params) { 14 | super(base, params); 15 | } 16 | 17 | protected Slice response(){ 18 | if (base().getValue(params().get(0)) == null) { 19 | base().putValue(params().get(0), params().get(1).extract()); 20 | return Response.integer(1); 21 | } 22 | return Response.integer(0); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/datastructures/RMList.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.datastructures; 2 | 3 | import com.github.fppt.jedismock.exception.WrongValueTypeException; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | public class RMList implements RMDataStructure { 9 | private final List storedData; 10 | 11 | public RMList() { 12 | this.storedData = new ArrayList<>(); 13 | } 14 | 15 | public RMList(List storedData) { 16 | this.storedData = storedData; 17 | } 18 | 19 | public List getStoredData() { 20 | return storedData; 21 | } 22 | 23 | @Override 24 | public void raiseTypeCastException() { 25 | throw new WrongValueTypeException("WRONGTYPE RMList value is used in the wrong place"); 26 | } 27 | 28 | @Override 29 | public String getTypeName() { 30 | return "list"; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/connection/Quit.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.connection; 2 | 3 | import com.github.fppt.jedismock.operations.RedisCommand; 4 | import com.github.fppt.jedismock.operations.RedisOperation; 5 | import com.github.fppt.jedismock.server.Response; 6 | import com.github.fppt.jedismock.datastructures.Slice; 7 | import com.github.fppt.jedismock.storage.OperationExecutorState; 8 | 9 | @RedisCommand(value = "quit", transactional = false) 10 | public class Quit implements RedisOperation { 11 | private final OperationExecutorState state; 12 | 13 | public Quit(OperationExecutorState state) { 14 | this.state = state; 15 | } 16 | 17 | @Override 18 | public Slice execute() { 19 | state.owner().sendResponse(Response.clientResponse("quit", Response.OK), "quit"); 20 | state.owner().close(); 21 | return Response.SKIP; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/connection/Echo.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.connection; 2 | 3 | import com.github.fppt.jedismock.datastructures.Slice; 4 | import com.github.fppt.jedismock.operations.AbstractRedisOperation; 5 | import com.github.fppt.jedismock.operations.RedisCommand; 6 | import com.github.fppt.jedismock.server.Response; 7 | import com.github.fppt.jedismock.storage.RedisBase; 8 | 9 | import java.util.List; 10 | 11 | @RedisCommand("echo") 12 | class Echo extends AbstractRedisOperation { 13 | Echo(RedisBase base, List params) { 14 | super(base, params); 15 | } 16 | 17 | @Override 18 | protected int minArgs() { 19 | return 1; 20 | } 21 | 22 | @Override 23 | protected int maxArgs() { 24 | return 1; 25 | } 26 | 27 | protected Slice response() { 28 | return Response.bulkString(params().get(0)); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/keys/Del.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.keys; 2 | 3 | import com.github.fppt.jedismock.operations.AbstractRedisOperation; 4 | import com.github.fppt.jedismock.operations.RedisCommand; 5 | import com.github.fppt.jedismock.server.Response; 6 | import com.github.fppt.jedismock.datastructures.Slice; 7 | import com.github.fppt.jedismock.storage.RedisBase; 8 | 9 | import java.util.List; 10 | 11 | @RedisCommand("del") 12 | class Del extends AbstractRedisOperation { 13 | Del(RedisBase base, List params) { 14 | super(base, params); 15 | } 16 | 17 | protected Slice response() { 18 | int count = 0; 19 | for (Slice key : params()) { 20 | if (base().exists(key)) { 21 | base().deleteValue(key); 22 | count++; 23 | } 24 | } 25 | return Response.integer(count); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/sortedsets/ZRemRangeByLex.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.sortedsets; 2 | 3 | import com.github.fppt.jedismock.datastructures.Slice; 4 | import com.github.fppt.jedismock.operations.RedisCommand; 5 | import com.github.fppt.jedismock.storage.RedisBase; 6 | 7 | import java.util.List; 8 | 9 | @RedisCommand("zremrangebylex") 10 | class ZRemRangeByLex extends AbstractZRangeByLex { 11 | 12 | ZRemRangeByLex(RedisBase base, List params) { 13 | super(base, params); 14 | } 15 | 16 | @Override 17 | protected Slice response() { 18 | expectNoOptions(); 19 | key = params().get(0); 20 | mapDBObj = getZSetFromBaseOrCreateEmpty(key); 21 | 22 | final Slice start = params().get(1); 23 | final Slice end = params().get(2); 24 | return remRangeFromKey(getRange(getStartBound(start), getEndBound(end))); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/sortedsets/ZCard.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.sortedsets; 2 | 3 | import com.github.fppt.jedismock.datastructures.RMZSet; 4 | import com.github.fppt.jedismock.operations.AbstractRedisOperation; 5 | import com.github.fppt.jedismock.operations.RedisCommand; 6 | import com.github.fppt.jedismock.server.Response; 7 | import com.github.fppt.jedismock.datastructures.Slice; 8 | import com.github.fppt.jedismock.storage.RedisBase; 9 | 10 | import java.util.List; 11 | 12 | @RedisCommand("zcard") 13 | class ZCard extends AbstractRedisOperation { 14 | 15 | ZCard(RedisBase base, List params) { 16 | super(base, params); 17 | } 18 | 19 | @Override 20 | protected Slice response() { 21 | Slice key = params().get(0); 22 | final RMZSet mapDBObj = getZSetFromBaseOrCreateEmpty(key); 23 | return Response.integer(mapDBObj.size()); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/sortedsets/ZRevRank.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.sortedsets; 2 | 3 | import com.github.fppt.jedismock.datastructures.Slice; 4 | import com.github.fppt.jedismock.operations.RedisCommand; 5 | import com.github.fppt.jedismock.storage.RedisBase; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | @RedisCommand("zrevrank") 11 | public class ZRevRank extends AbstractByScoreOperation { 12 | private static final String IS_REV = "REV"; 13 | private final ZRank zRank; 14 | 15 | ZRevRank(RedisBase base, List params) { 16 | super(base, params); 17 | List updatedParams = new ArrayList<>(params); 18 | updatedParams.add(Slice.create(IS_REV)); 19 | this.zRank = new ZRank(base, updatedParams); 20 | } 21 | 22 | @Override 23 | protected Slice response() { 24 | return zRank.response(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/lists/LLen.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.lists; 2 | 3 | import com.github.fppt.jedismock.datastructures.RMList; 4 | import com.github.fppt.jedismock.operations.AbstractRedisOperation; 5 | import com.github.fppt.jedismock.operations.RedisCommand; 6 | import com.github.fppt.jedismock.server.Response; 7 | import com.github.fppt.jedismock.datastructures.Slice; 8 | import com.github.fppt.jedismock.storage.RedisBase; 9 | 10 | import java.util.List; 11 | 12 | @RedisCommand("llen") 13 | class LLen extends AbstractRedisOperation { 14 | LLen(RedisBase base, List params) { 15 | super(base, params); 16 | } 17 | 18 | protected Slice response() { 19 | Slice key = params().get(0); 20 | RMList listDBObj = getListFromBaseOrCreateEmpty(key); 21 | List list = listDBObj.getStoredData(); 22 | return Response.integer(list.size()); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/sortedsets/ZRemRangeByScore.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.sortedsets; 2 | 3 | import com.github.fppt.jedismock.datastructures.Slice; 4 | import com.github.fppt.jedismock.operations.RedisCommand; 5 | import com.github.fppt.jedismock.storage.RedisBase; 6 | 7 | import java.util.List; 8 | 9 | @RedisCommand("zremrangebyscore") 10 | public class ZRemRangeByScore extends AbstractZRangeByScore { 11 | 12 | ZRemRangeByScore(RedisBase base, List params) { 13 | super(base, params); 14 | } 15 | 16 | @Override 17 | protected Slice response() { 18 | expectNoOptions(); 19 | key = params().get(0); 20 | mapDBObj = getZSetFromBaseOrCreateEmpty(key); 21 | 22 | final Slice start = params().get(1); 23 | final Slice end = params().get(2); 24 | return remRangeFromKey(getRange(getStartBound(start), getEndBound(end))); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/strings/StrLen.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.strings; 2 | 3 | import com.github.fppt.jedismock.datastructures.RMString; 4 | import com.github.fppt.jedismock.operations.AbstractRedisOperation; 5 | import com.github.fppt.jedismock.operations.RedisCommand; 6 | import com.github.fppt.jedismock.server.Response; 7 | import com.github.fppt.jedismock.datastructures.Slice; 8 | import com.github.fppt.jedismock.storage.RedisBase; 9 | 10 | import java.util.List; 11 | 12 | @RedisCommand("strlen") 13 | class StrLen extends AbstractRedisOperation { 14 | StrLen(RedisBase base, List params) { 15 | super(base, params); 16 | } 17 | 18 | protected Slice response() { 19 | RMString value = base().getRMString(params().get(0)); 20 | if (value == null) { 21 | return Response.integer(0); 22 | } 23 | return Response.integer(value.size()); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/datastructures/RMSet.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.datastructures; 2 | 3 | import com.github.fppt.jedismock.exception.WrongValueTypeException; 4 | 5 | import java.util.HashSet; 6 | import java.util.Objects; 7 | import java.util.Set; 8 | 9 | public class RMSet implements RMDataStructure { 10 | private final Set storedData; 11 | 12 | public RMSet() { 13 | storedData = new HashSet<>(); 14 | } 15 | 16 | public RMSet(Set data) { 17 | Objects.requireNonNull(data); 18 | storedData = data; 19 | } 20 | 21 | public Set getStoredData() { 22 | return storedData; 23 | } 24 | 25 | @Override 26 | public void raiseTypeCastException() { 27 | throw new WrongValueTypeException("WRONGTYPE RMSet value is used in the wrong place"); 28 | } 29 | 30 | @Override 31 | public String getTypeName() { 32 | return "set"; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/hashes/HStrlen.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.hashes; 2 | 3 | import com.github.fppt.jedismock.datastructures.Slice; 4 | import com.github.fppt.jedismock.operations.AbstractRedisOperation; 5 | import com.github.fppt.jedismock.operations.RedisCommand; 6 | import com.github.fppt.jedismock.server.Response; 7 | import com.github.fppt.jedismock.storage.RedisBase; 8 | 9 | import java.util.List; 10 | 11 | @RedisCommand("hstrlen") 12 | public class HStrlen extends AbstractRedisOperation { 13 | public HStrlen(RedisBase base, List params) { 14 | super(base, params); 15 | } 16 | 17 | @Override 18 | protected Slice response() { 19 | Slice key = params().get(0); 20 | Slice field = params().get(1); 21 | Slice result = base().getSlice(key, field); 22 | return Response.integer(result == null ? 0 : result.toString().length()); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/sortedsets/ZInterCard.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.sortedsets; 2 | 3 | import com.github.fppt.jedismock.datastructures.Slice; 4 | import com.github.fppt.jedismock.operations.RedisCommand; 5 | import com.github.fppt.jedismock.server.Response; 6 | import com.github.fppt.jedismock.storage.OperationExecutorState; 7 | 8 | import java.util.List; 9 | import java.util.stream.Collectors; 10 | 11 | @RedisCommand("zintercard") 12 | class ZInterCard extends AbstractZInter { 13 | 14 | ZInterCard(OperationExecutorState state, List params) { 15 | super(state, params); 16 | } 17 | 18 | @Override 19 | protected Slice response() { 20 | List resultArray = getResultArray(); 21 | if (isLimit && limit != 0) { 22 | resultArray = resultArray.stream().limit(limit).collect(Collectors.toList()); 23 | } 24 | return Response.integer(resultArray.size()); 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/transactions/Multi.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.transactions; 2 | 3 | import com.github.fppt.jedismock.operations.RedisCommand; 4 | import com.github.fppt.jedismock.operations.RedisOperation; 5 | import com.github.fppt.jedismock.server.Response; 6 | import com.github.fppt.jedismock.datastructures.Slice; 7 | import com.github.fppt.jedismock.storage.OperationExecutorState; 8 | 9 | @RedisCommand(value = "multi", transactional = false) 10 | public class Multi implements RedisOperation { 11 | private final OperationExecutorState state; 12 | 13 | Multi(OperationExecutorState state) { 14 | this.state = state; 15 | } 16 | 17 | @Override 18 | public Slice execute() { 19 | if (state.isTransactionModeOn()) { 20 | return Response.error("ERR MULTI calls can not be nested"); 21 | } else { 22 | state.transactionMode(true); 23 | return Response.OK; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/test/java/com/github/fppt/jedismock/comparisontests/strings/TestMSetNX.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.comparisontests.strings; 2 | 3 | import com.github.fppt.jedismock.comparisontests.ComparisonBase; 4 | import org.junit.jupiter.api.BeforeEach; 5 | import org.junit.jupiter.api.TestTemplate; 6 | import org.junit.jupiter.api.extension.ExtendWith; 7 | import redis.clients.jedis.Jedis; 8 | 9 | import static org.assertj.core.api.Assertions.assertThat; 10 | 11 | @ExtendWith(ComparisonBase.class) 12 | public class TestMSetNX { 13 | @BeforeEach 14 | public void setUp(Jedis jedis) { 15 | jedis.flushAll(); 16 | } 17 | 18 | @TestTemplate 19 | public void msetnx(Jedis jedis) { 20 | assertThat(jedis.msetnx("key1", "Hello", "key2", "there")).isEqualTo(1); 21 | assertThat(jedis.msetnx("key2", "new", "key3", "world")).isEqualTo(0); 22 | assertThat(jedis.mget("key1", "key2", "key3")).containsExactly("Hello", "there", null); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/test/java/com/github/fppt/jedismock/RedisClientTest.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock; 2 | 3 | import org.junit.jupiter.api.BeforeEach; 4 | import org.junit.jupiter.api.Test; 5 | import org.mockito.Mockito; 6 | 7 | import java.io.IOException; 8 | import java.io.InputStream; 9 | import java.io.OutputStream; 10 | import java.net.Socket; 11 | 12 | class RedisClientTest { 13 | Socket s; 14 | RedisClient redisClient; 15 | 16 | @BeforeEach 17 | void init() throws IOException { 18 | s = Mockito.mock(Socket.class); 19 | Mockito.when(s.getInputStream()).thenReturn(Mockito.mock(InputStream.class)); 20 | Mockito.when(s.getOutputStream()).thenReturn(Mockito.mock(OutputStream.class)); 21 | redisClient = new RedisClient(Mockito.mock(RedisServer.class), s, c -> { 22 | }); 23 | } 24 | 25 | @Test 26 | void testClosedSocket() { 27 | Mockito.when(s.isClosed()).thenReturn(true); 28 | redisClient.run(); 29 | } 30 | 31 | } -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/sortedsets/BZPopMax.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.sortedsets; 2 | 3 | import com.github.fppt.jedismock.datastructures.Slice; 4 | import com.github.fppt.jedismock.operations.RedisCommand; 5 | import com.github.fppt.jedismock.server.Response; 6 | import com.github.fppt.jedismock.storage.OperationExecutorState; 7 | 8 | import java.util.List; 9 | 10 | import static com.github.fppt.jedismock.Utils.toNanoTimeout; 11 | 12 | @RedisCommand("bzpopmax") 13 | public class BZPopMax extends BZPop { 14 | 15 | BZPopMax(OperationExecutorState state, List params) { 16 | super(state, params); 17 | timeoutNanos = toNanoTimeout(params().get(params().size() - 1).toString()); 18 | } 19 | 20 | @Override 21 | protected Slice popper(List params) { 22 | List result = new ZPop(base(), params, true).pop(); 23 | return Response.array(Response.bulkString(params.get(0)), result.get(0), result.get(1)); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/sortedsets/BZPopMin.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.sortedsets; 2 | 3 | import com.github.fppt.jedismock.datastructures.Slice; 4 | import com.github.fppt.jedismock.operations.RedisCommand; 5 | import com.github.fppt.jedismock.server.Response; 6 | import com.github.fppt.jedismock.storage.OperationExecutorState; 7 | 8 | import java.util.List; 9 | 10 | import static com.github.fppt.jedismock.Utils.toNanoTimeout; 11 | 12 | @RedisCommand("bzpopmin") 13 | public class BZPopMin extends BZPop { 14 | 15 | BZPopMin(OperationExecutorState state, List params) { 16 | super(state, params); 17 | timeoutNanos = toNanoTimeout(params().get(params().size() - 1).toString()); 18 | } 19 | 20 | @Override 21 | protected Slice popper(List params) { 22 | List result = new ZPop(base(), params, false).pop(); 23 | return Response.array(Response.bulkString(params.get(0)), result.get(0), result.get(1)); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/streams/XLen.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.streams; 2 | 3 | import com.github.fppt.jedismock.datastructures.Slice; 4 | import com.github.fppt.jedismock.operations.AbstractRedisOperation; 5 | import com.github.fppt.jedismock.operations.RedisCommand; 6 | import com.github.fppt.jedismock.server.Response; 7 | import com.github.fppt.jedismock.storage.RedisBase; 8 | 9 | import java.util.List; 10 | 11 | /** 12 | * XLEN key 13 | */ 14 | @RedisCommand("xlen") 15 | public class XLen extends AbstractRedisOperation { 16 | XLen(RedisBase base, List params) { 17 | super(base, params); 18 | } 19 | 20 | @Override 21 | protected int minArgs() { 22 | return 1; 23 | } 24 | 25 | @Override 26 | protected int maxArgs() { 27 | return 1; 28 | } 29 | 30 | @Override 31 | protected Slice response() { 32 | return Response.integer(getStreamFromBaseOrCreateEmpty(params().get(0)).getStoredData().size()); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/sets/SCard.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.sets; 2 | 3 | import com.github.fppt.jedismock.datastructures.RMSet; 4 | import com.github.fppt.jedismock.operations.AbstractRedisOperation; 5 | import com.github.fppt.jedismock.operations.RedisCommand; 6 | import com.github.fppt.jedismock.server.Response; 7 | import com.github.fppt.jedismock.datastructures.Slice; 8 | import com.github.fppt.jedismock.storage.RedisBase; 9 | 10 | import java.util.List; 11 | import java.util.Set; 12 | 13 | @RedisCommand("scard") 14 | class SCard extends AbstractRedisOperation { 15 | 16 | SCard(RedisBase base, List params) { 17 | super(base, params); 18 | } 19 | 20 | protected Slice response() { 21 | Slice key = params().get(0); 22 | RMSet setDBObj = getSetFromBaseOrCreateEmpty(key); 23 | Set set = setDBObj.getStoredData(); 24 | if(set == null || set.isEmpty()) return Response.integer(0); 25 | return Response.integer(set.size()); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/sortedsets/ZCount.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.sortedsets; 2 | 3 | import com.github.fppt.jedismock.datastructures.Slice; 4 | import com.github.fppt.jedismock.operations.RedisCommand; 5 | import com.github.fppt.jedismock.server.Response; 6 | import com.github.fppt.jedismock.storage.RedisBase; 7 | 8 | import java.util.List; 9 | 10 | 11 | @RedisCommand("zcount") 12 | public class ZCount extends AbstractZRangeByScore { 13 | public ZCount(RedisBase base, List params) { 14 | super(base, params); 15 | } 16 | 17 | @Override 18 | protected Slice response() { 19 | expectNoOptions(); 20 | key = params().get(0); 21 | mapDBObj = getZSetFromBaseOrCreateEmpty(key); 22 | 23 | final Slice start = params().get(1); 24 | final Slice end = params().get(2); 25 | 26 | int result = getRange(getStartBound(start), getEndBound(end)).size(); 27 | 28 | return Response.integer(result); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/keys/TTL.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.keys; 2 | 3 | import com.github.fppt.jedismock.operations.AbstractRedisOperation; 4 | import com.github.fppt.jedismock.operations.RedisCommand; 5 | import com.github.fppt.jedismock.server.Response; 6 | import com.github.fppt.jedismock.datastructures.Slice; 7 | import com.github.fppt.jedismock.storage.RedisBase; 8 | 9 | import java.util.List; 10 | 11 | @RedisCommand("ttl") 12 | class TTL extends AbstractRedisOperation { 13 | TTL(RedisBase base, List params) { 14 | super(base, params); 15 | } 16 | 17 | Slice finalReturn(Long pttl){ 18 | return Response.integer((pttl + 999) / 1000); 19 | } 20 | 21 | protected Slice response() { 22 | Long pttl = base().getTTL(params().get(0)); 23 | if (pttl == null) { 24 | return Response.integer(-2L); 25 | } 26 | if (pttl == -1) { 27 | return Response.integer(-1L); 28 | } 29 | return finalReturn(pttl); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/sortedsets/AbstractZInter.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.sortedsets; 2 | 3 | import com.github.fppt.jedismock.datastructures.RMZSet; 4 | import com.github.fppt.jedismock.datastructures.Slice; 5 | import com.github.fppt.jedismock.datastructures.ZSetEntry; 6 | import com.github.fppt.jedismock.storage.OperationExecutorState; 7 | 8 | import java.util.List; 9 | 10 | abstract class AbstractZInter extends ZStore { 11 | 12 | AbstractZInter(OperationExecutorState state, List params) { 13 | super(state, params); 14 | } 15 | 16 | protected RMZSet getResult(RMZSet zset1, RMZSet zset2, double weight) { 17 | RMZSet result = new RMZSet(); 18 | for (ZSetEntry entry : 19 | zset1.entries(false)) { 20 | if (zset2.hasMember(entry.getValue())) { 21 | result.put(entry.getValue(), aggregate.apply(entry.getScore(), getMultiple(zset2.getScore(entry.getValue()), weight))); 22 | } 23 | } 24 | return result; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/sortedsets/ZRemRangeByRank.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.sortedsets; 2 | 3 | import com.github.fppt.jedismock.datastructures.Slice; 4 | import com.github.fppt.jedismock.operations.RedisCommand; 5 | import com.github.fppt.jedismock.server.Response; 6 | import com.github.fppt.jedismock.storage.RedisBase; 7 | 8 | import java.util.List; 9 | 10 | @RedisCommand("zremrangebyrank") 11 | public class ZRemRangeByRank extends AbstractZRangeByIndex { 12 | 13 | ZRemRangeByRank(RedisBase base, List params) { 14 | super(base, params); 15 | } 16 | 17 | @Override 18 | protected Slice response() { 19 | expectNoOptions(); 20 | key = params().get(0); 21 | mapDBObj = getZSetFromBaseOrCreateEmpty(key); 22 | 23 | if (checkWrongIndex()) { 24 | return Response.integer(0); 25 | } 26 | 27 | return remRangeFromKey(getRange(getStartBound(Slice.create(String.valueOf(startIndex))), getStartBound(Slice.create(String.valueOf(endIndex))))); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/test/java/com/github/fppt/jedismock/comparisontests/sortedsets/TestZRevRank.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.comparisontests.sortedsets; 2 | 3 | import com.github.fppt.jedismock.comparisontests.ComparisonBase; 4 | import org.junit.jupiter.api.BeforeEach; 5 | import org.junit.jupiter.api.TestTemplate; 6 | import org.junit.jupiter.api.extension.ExtendWith; 7 | import redis.clients.jedis.Jedis; 8 | 9 | import static org.assertj.core.api.Assertions.assertThat; 10 | 11 | @ExtendWith(ComparisonBase.class) 12 | public class TestZRevRank { 13 | 14 | private static final String ZSET_KEY = "myzset"; 15 | 16 | @BeforeEach 17 | public void setUp(Jedis jedis) { 18 | jedis.flushDB(); 19 | jedis.zadd(ZSET_KEY, 2, "bbb"); 20 | jedis.zadd(ZSET_KEY, 3, "ccc"); 21 | jedis.zadd(ZSET_KEY, 1, "aaa"); 22 | jedis.zadd(ZSET_KEY, 5, "yyy"); 23 | jedis.zadd(ZSET_KEY, 4, "xxx"); 24 | } 25 | 26 | @TestTemplate 27 | public void getFirstRank(Jedis jedis) { 28 | assertThat(jedis.zrevrank(ZSET_KEY, "aaa")).isEqualTo(4); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/server/RedisCommandInterceptor.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.server; 2 | 3 | import com.github.fppt.jedismock.datastructures.Slice; 4 | import com.github.fppt.jedismock.storage.OperationExecutorState; 5 | 6 | import java.util.List; 7 | 8 | @FunctionalInterface 9 | public interface RedisCommandInterceptor { 10 | /** 11 | * This method is called on operation execution in JedisMock. 12 | * 13 | * You can either use it for overriding the default behaviour, or for checking the fact that specific 14 | * command is set to Redis. 15 | * 16 | * WARNING: if you are going to mutate state, synchronize on state.lock() first! 17 | * (see com.github.fppt.jedismock.operations.server.MockExecutor#proceed) 18 | * 19 | * @param state Executor state, which includes shared database and connection-specific state. 20 | * @param name Operation name. 21 | * @param params Operation parameters. 22 | * 23 | */ 24 | Slice execCommand(OperationExecutorState state, String name, List params); 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/strings/MGet.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.strings; 2 | 3 | import com.github.fppt.jedismock.datastructures.RMString; 4 | import com.github.fppt.jedismock.operations.AbstractRedisOperation; 5 | import com.github.fppt.jedismock.operations.RedisCommand; 6 | import com.github.fppt.jedismock.server.Response; 7 | import com.github.fppt.jedismock.datastructures.Slice; 8 | import com.github.fppt.jedismock.storage.RedisBase; 9 | 10 | import static java.util.stream.Collectors.toList; 11 | 12 | import java.util.List; 13 | 14 | @RedisCommand("mget") 15 | class MGet extends AbstractRedisOperation { 16 | MGet(RedisBase base, List params) { 17 | super(base, params); 18 | } 19 | 20 | protected Slice response() { 21 | RedisBase base = base(); 22 | return Response.array(params().stream() 23 | .map(base::getValue) 24 | .map(s -> s instanceof RMString ? (s.getAsSlice()) : null) 25 | .map(Response::bulkString) 26 | .collect(toList())); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/pubsub/Subscribe.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.pubsub; 2 | 3 | import com.github.fppt.jedismock.operations.AbstractRedisOperation; 4 | import com.github.fppt.jedismock.operations.RedisCommand; 5 | import com.github.fppt.jedismock.server.Response; 6 | import com.github.fppt.jedismock.datastructures.Slice; 7 | import com.github.fppt.jedismock.storage.OperationExecutorState; 8 | 9 | import java.util.List; 10 | 11 | @RedisCommand(value = "subscribe", transactional = false) 12 | public class Subscribe extends AbstractRedisOperation { 13 | private final OperationExecutorState state; 14 | 15 | public Subscribe(OperationExecutorState state, List params) { 16 | super(state.base(), params); 17 | this.state = state; 18 | } 19 | 20 | @Override 21 | protected Slice response() { 22 | params().forEach(channel -> base().addSubscriber(channel, state.owner())); 23 | List numSubscriptions = base().getSubscriptions(state.owner()); 24 | 25 | return Response.subscribedToChannel(numSubscriptions); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/transactions/Watch.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.transactions; 2 | 3 | import com.github.fppt.jedismock.datastructures.Slice; 4 | import com.github.fppt.jedismock.operations.RedisCommand; 5 | 6 | import com.github.fppt.jedismock.operations.RedisOperation; 7 | import com.github.fppt.jedismock.server.Response; 8 | import com.github.fppt.jedismock.storage.OperationExecutorState; 9 | 10 | import java.util.List; 11 | 12 | @RedisCommand(value = "watch", transactional = false) 13 | public class Watch implements RedisOperation { 14 | private final OperationExecutorState state; 15 | private final List keys; 16 | 17 | Watch(OperationExecutorState state, List keys) { 18 | this.state = state; 19 | this.keys = keys; 20 | } 21 | 22 | @Override 23 | public Slice execute() { 24 | if (state.isTransactionModeOn()) { 25 | return Response.error("ERR WATCH inside MULTI is not allowed"); 26 | } else { 27 | state.watch(keys); 28 | return Response.OK; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/pubsub/PSubscribe.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.pubsub; 2 | 3 | import com.github.fppt.jedismock.datastructures.Slice; 4 | import com.github.fppt.jedismock.operations.AbstractRedisOperation; 5 | import com.github.fppt.jedismock.operations.RedisCommand; 6 | import com.github.fppt.jedismock.server.Response; 7 | import com.github.fppt.jedismock.storage.OperationExecutorState; 8 | 9 | import java.util.List; 10 | 11 | @RedisCommand(value = "psubscribe", transactional = false) 12 | public class PSubscribe extends AbstractRedisOperation { 13 | private final OperationExecutorState state; 14 | public PSubscribe(OperationExecutorState state, List params) { 15 | super(state.base(), params); 16 | this.state = state; 17 | } 18 | 19 | @Override 20 | protected Slice response() { 21 | params().forEach(pattern -> base().subscribeByPattern(pattern, state.owner())); 22 | List numSubscriptions = base().getPSubscriptions(state.owner()); 23 | 24 | return Response.psubscribedToChannel(numSubscriptions); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/server/RedisOperationExecutor.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.server; 2 | 3 | import com.github.fppt.jedismock.commands.RedisCommand; 4 | import com.github.fppt.jedismock.datastructures.Slice; 5 | import com.github.fppt.jedismock.storage.OperationExecutorState; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * Created by Xiaolu on 2015/4/20. 11 | */ 12 | public class RedisOperationExecutor { 13 | private final OperationExecutorState state; 14 | 15 | public RedisOperationExecutor(OperationExecutorState state) { 16 | this.state = state; 17 | } 18 | 19 | public Slice execCommand(RedisCommand command) { 20 | if (command.parameters().isEmpty()) { 21 | throw new IllegalStateException(); 22 | } 23 | List params = command.parameters(); 24 | List commandParams = params.subList(1, params.size()); 25 | String name = new String(params.get(0).data()).toLowerCase(); 26 | return state.owner().options().getCommandInterceptor() 27 | .execCommand(state, name, commandParams); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/sortedsets/AbstractZDiff.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.sortedsets; 2 | 3 | import com.github.fppt.jedismock.datastructures.RMZSet; 4 | import com.github.fppt.jedismock.datastructures.Slice; 5 | import com.github.fppt.jedismock.datastructures.ZSetEntry; 6 | import com.github.fppt.jedismock.storage.OperationExecutorState; 7 | 8 | import java.util.List; 9 | 10 | abstract class AbstractZDiff extends ZStore { 11 | 12 | AbstractZDiff(OperationExecutorState state, List params) { 13 | super(state, params); 14 | } 15 | 16 | protected RMZSet getResult(RMZSet zset1, RMZSet zset2, double weight) { 17 | RMZSet result = new RMZSet(); 18 | for (ZSetEntry entry: zset1.entries(false)) { 19 | result.put(entry.getValue(), entry.getScore()); 20 | } 21 | 22 | for (ZSetEntry entry: zset2.entries(false)) { 23 | Slice value = entry.getValue(); 24 | if (result.hasMember(value)) { 25 | result.remove(value); 26 | } 27 | } 28 | 29 | return result; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/bitmaps/GetBit.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.bitmaps; 2 | 3 | import com.github.fppt.jedismock.datastructures.RMBitMap; 4 | import com.github.fppt.jedismock.operations.AbstractRedisOperation; 5 | import com.github.fppt.jedismock.operations.RedisCommand; 6 | import com.github.fppt.jedismock.server.Response; 7 | import com.github.fppt.jedismock.datastructures.Slice; 8 | import com.github.fppt.jedismock.storage.RedisBase; 9 | 10 | import java.util.List; 11 | 12 | import static com.github.fppt.jedismock.Utils.convertToNonNegativeInteger; 13 | 14 | @RedisCommand("getbit") 15 | class GetBit extends AbstractRedisOperation { 16 | GetBit(RedisBase base, List params) { 17 | super(base, params); 18 | } 19 | 20 | protected Slice response() { 21 | RMBitMap value = base().getBitMap(params().get(0)); 22 | int pos = convertToNonNegativeInteger(params().get(1).toString()); 23 | 24 | if (value == null) { 25 | return Response.integer(0L); 26 | } 27 | 28 | return Response.integer(value.getBit(pos) ? 1 : 0); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/hashes/HKeys.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.hashes; 2 | 3 | import com.github.fppt.jedismock.datastructures.Slice; 4 | import com.github.fppt.jedismock.operations.AbstractRedisOperation; 5 | import com.github.fppt.jedismock.operations.RedisCommand; 6 | import com.github.fppt.jedismock.server.Response; 7 | import com.github.fppt.jedismock.storage.RedisBase; 8 | 9 | import java.util.List; 10 | import java.util.Map; 11 | import java.util.stream.Collectors; 12 | 13 | @RedisCommand("hkeys") 14 | public class HKeys extends AbstractRedisOperation { 15 | public HKeys(RedisBase base, List params) { 16 | super(base, params); 17 | } 18 | 19 | @Override 20 | protected Slice response() { 21 | Slice hash = params().get(0); 22 | 23 | Map fieldAndValueMap = base().getFieldsAndValuesReadOnly(hash); 24 | 25 | return Response.array( 26 | fieldAndValueMap.keySet().stream() 27 | .map(Response::bulkString) 28 | .collect(Collectors.toList()) 29 | ); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/hashes/HVals.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.hashes; 2 | 3 | import com.github.fppt.jedismock.datastructures.Slice; 4 | import com.github.fppt.jedismock.operations.AbstractRedisOperation; 5 | import com.github.fppt.jedismock.operations.RedisCommand; 6 | import com.github.fppt.jedismock.server.Response; 7 | import com.github.fppt.jedismock.storage.RedisBase; 8 | 9 | import java.util.List; 10 | import java.util.Map; 11 | import java.util.stream.Collectors; 12 | 13 | @RedisCommand("hvals") 14 | public class HVals extends AbstractRedisOperation { 15 | public HVals(RedisBase base, List params) { 16 | super(base, params); 17 | } 18 | 19 | @Override 20 | protected Slice response() { 21 | Slice hash = params().get(0); 22 | 23 | Map fieldAndValueMap = base().getFieldsAndValuesReadOnly(hash); 24 | 25 | return Response.array( 26 | fieldAndValueMap.values().stream() 27 | .map(Response::bulkString) 28 | .collect(Collectors.toList()) 29 | ); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/sets/SIsMember.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.sets; 2 | 3 | import com.github.fppt.jedismock.datastructures.RMSet; 4 | import com.github.fppt.jedismock.operations.AbstractRedisOperation; 5 | import com.github.fppt.jedismock.operations.RedisCommand; 6 | import com.github.fppt.jedismock.server.Response; 7 | import com.github.fppt.jedismock.datastructures.Slice; 8 | import com.github.fppt.jedismock.storage.RedisBase; 9 | 10 | import java.util.List; 11 | import java.util.Set; 12 | 13 | @RedisCommand("sismember") 14 | public class SIsMember extends AbstractRedisOperation { 15 | 16 | SIsMember(RedisBase base, List params) { 17 | super(base, params); 18 | } 19 | 20 | @Override 21 | protected Slice response() { 22 | Slice key = params().get(0); 23 | Slice member = params().get(1); 24 | RMSet setDBObj = getSetFromBaseOrCreateEmpty(key); 25 | Set set = setDBObj.getStoredData(); 26 | if (set == null || set.isEmpty()) return Response.integer(0); 27 | return Response.integer(set.contains(member) ? 1 : 0); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/datastructures/ZSetEntryBound.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.datastructures; 2 | 3 | import java.util.Objects; 4 | 5 | public class ZSetEntryBound { 6 | public static final ZSetEntryBound MINUS_INF = 7 | new ZSetEntryBound(new ZSetEntry(ZSetEntry.MIN_SCORE, Slice.empty()), false); 8 | public static final ZSetEntryBound PLUS_INF = 9 | new ZSetEntryBound(new ZSetEntry(ZSetEntry.MAX_SCORE, Slice.empty()), false); 10 | 11 | private final ZSetEntry bound; 12 | private final boolean inclusive; 13 | 14 | public ZSetEntryBound(ZSetEntry bound, boolean inclusive) { 15 | Objects.requireNonNull(bound); 16 | this.bound = bound; 17 | this.inclusive = inclusive; 18 | } 19 | 20 | public ZSetEntryBound(double score, Slice value, boolean inclusive) { 21 | this.bound = new ZSetEntry(score, value); 22 | this.inclusive = inclusive; 23 | } 24 | 25 | public ZSetEntry getBound() { 26 | return bound; 27 | } 28 | 29 | public boolean isInclusive() { 30 | return inclusive; 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/strings/Append.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.strings; 2 | 3 | import com.github.fppt.jedismock.datastructures.RMString; 4 | import com.github.fppt.jedismock.operations.AbstractRedisOperation; 5 | import com.github.fppt.jedismock.operations.RedisCommand; 6 | import com.github.fppt.jedismock.server.Response; 7 | import com.github.fppt.jedismock.datastructures.Slice; 8 | import com.github.fppt.jedismock.storage.RedisBase; 9 | 10 | import java.util.List; 11 | 12 | @RedisCommand("append") 13 | class Append extends AbstractRedisOperation { 14 | Append(RedisBase base, List params) { 15 | super(base, params); 16 | } 17 | 18 | protected Slice response() { 19 | Slice key = params().get(0); 20 | Slice value = params().get(1); 21 | RMString s = base().getRMString(key); 22 | 23 | if (s == null) { 24 | base().putValue(key, value.extract()); 25 | return Response.integer(value.length()); 26 | } 27 | 28 | s.add(value.data()); 29 | base().putValue(key, s); 30 | return Response.integer(s.size()); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/strings/MSetNX.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.strings; 2 | 3 | import com.github.fppt.jedismock.datastructures.Slice; 4 | import com.github.fppt.jedismock.operations.AbstractRedisOperation; 5 | import com.github.fppt.jedismock.operations.RedisCommand; 6 | import com.github.fppt.jedismock.server.Response; 7 | import com.github.fppt.jedismock.storage.RedisBase; 8 | 9 | import java.util.List; 10 | 11 | @RedisCommand("msetnx") 12 | public class MSetNX extends AbstractRedisOperation { 13 | public MSetNX(RedisBase base, List params) { 14 | super(base, params); 15 | } 16 | 17 | @Override 18 | protected Slice response() { 19 | RedisBase base = base(); 20 | for (int i = 0; i < params().size(); i += 2) { 21 | if (base.exists(params().get(i))) { 22 | return Response.integer(0); 23 | } 24 | } 25 | for (int i = 0; i < params().size(); i += 2) { 26 | base.putValue(params().get(i), params().get(i + 1).extract()); 27 | } 28 | return Response.integer(1); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/test/java/com/github/fppt/jedismock/util/OperationCategory.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.util; 2 | 3 | public enum OperationCategory { 4 | ADMIN("Administration", "admin"), 5 | BITMAPS("Bitmaps", "bitmap"), 6 | CONNECTION("Connection", "connection"), 7 | GEO("Geo", "geo"), 8 | HASHES("Hashes", "hash"), 9 | HYPERLOGLOG("HyperLogLog", "hyperloglog"), 10 | KEYS("Keys", "keyspace"), 11 | LISTS("Lists", "list"), 12 | PUBSUB("Pub/Sub", "pubsub"), 13 | SCRIPTING("Scripting", "scripting"), 14 | SETS("Sets", "set"), 15 | SORTEDSETS("Sorted Sets", "sortedset"), 16 | STREAMS("Streams", "stream"), 17 | STRINGS("Strings", "string"), 18 | TRANSACTIONS("Transactions", "transaction"); 19 | 20 | private final String name; 21 | private final String annotationName; 22 | 23 | OperationCategory(String name, String annotationName) { 24 | this.name = name; 25 | this.annotationName = annotationName; 26 | } 27 | 28 | @Override 29 | public String toString() { 30 | return name; 31 | } 32 | 33 | public String getAnnotationName() { 34 | return annotationName; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/test/java/com/github/fppt/jedismock/comparisontests/strings/TestMGet.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.comparisontests.strings; 2 | 3 | import com.github.fppt.jedismock.comparisontests.ComparisonBase; 4 | import org.junit.jupiter.api.BeforeEach; 5 | import org.junit.jupiter.api.TestTemplate; 6 | import org.junit.jupiter.api.extension.ExtendWith; 7 | import redis.clients.jedis.Jedis; 8 | 9 | import static org.assertj.core.api.Assertions.assertThat; 10 | 11 | @ExtendWith(ComparisonBase.class) 12 | public class TestMGet { 13 | @BeforeEach 14 | public void setUp(Jedis jedis) { 15 | jedis.flushAll(); 16 | } 17 | 18 | @TestTemplate 19 | public void mget(Jedis jedis) { 20 | jedis.set("key1", "Hello"); 21 | jedis.set("key2", "World"); 22 | assertThat(jedis.mget("key1", "key2", "key3")).containsExactly("Hello", "World", null); 23 | } 24 | 25 | 26 | @TestTemplate 27 | public void mgetWithNonText(Jedis jedis) { 28 | jedis.set("key1", "Hello"); 29 | jedis.hset("key2", "field", "World"); 30 | assertThat(jedis.mget("key1", "key2")).containsExactly("Hello", null); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/hyperloglog/PFCount.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.hyperloglog; 2 | 3 | import com.github.fppt.jedismock.datastructures.RMHyperLogLog; 4 | import com.github.fppt.jedismock.operations.AbstractRedisOperation; 5 | import com.github.fppt.jedismock.operations.RedisCommand; 6 | import com.github.fppt.jedismock.server.Response; 7 | import com.github.fppt.jedismock.datastructures.Slice; 8 | import com.github.fppt.jedismock.storage.RedisBase; 9 | 10 | import java.util.HashSet; 11 | import java.util.List; 12 | import java.util.Set; 13 | 14 | @RedisCommand("pfcount") 15 | class PFCount extends AbstractRedisOperation { 16 | PFCount(RedisBase base, List params) { 17 | super(base, params); 18 | } 19 | 20 | protected Slice response() { 21 | Set set = new HashSet<>(); 22 | for (Slice key : params()) { 23 | RMHyperLogLog data = base().getHLL(key); 24 | if (data == null) { 25 | continue; 26 | } 27 | 28 | Set s = data.getStoredData(); 29 | set.addAll(s); 30 | } 31 | return Response.integer(set.size()); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/keys/Keys.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.keys; 2 | 3 | import com.github.fppt.jedismock.Utils; 4 | import com.github.fppt.jedismock.datastructures.Slice; 5 | import com.github.fppt.jedismock.operations.AbstractRedisOperation; 6 | import com.github.fppt.jedismock.operations.RedisCommand; 7 | import com.github.fppt.jedismock.server.Response; 8 | import com.github.fppt.jedismock.storage.RedisBase; 9 | 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | 13 | @RedisCommand("keys") 14 | class Keys extends AbstractRedisOperation { 15 | Keys(RedisBase base, List params) { 16 | super(base, params); 17 | } 18 | 19 | protected Slice response() { 20 | List matchingKeys = new ArrayList<>(); 21 | String regex = Utils.createRegexFromGlob(new String(params().get(0).data())); 22 | 23 | base().keys().forEach(keyData -> { 24 | String key = new String(keyData.data()); 25 | if (key.matches(regex)) { 26 | matchingKeys.add(Response.bulkString(keyData)); 27 | } 28 | }); 29 | 30 | return Response.array(matchingKeys); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/hashes/HDel.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.hashes; 2 | 3 | import com.github.fppt.jedismock.operations.AbstractRedisOperation; 4 | import com.github.fppt.jedismock.operations.RedisCommand; 5 | import com.github.fppt.jedismock.server.Response; 6 | import com.github.fppt.jedismock.datastructures.Slice; 7 | import com.github.fppt.jedismock.storage.RedisBase; 8 | 9 | import java.util.List; 10 | 11 | @RedisCommand("hdel") 12 | class HDel extends AbstractRedisOperation { 13 | HDel(RedisBase base, List params) { 14 | super(base, params); 15 | } 16 | 17 | @Override 18 | protected int minArgs() { 19 | return 2; 20 | } 21 | 22 | protected Slice response(){ 23 | Slice key = params().get(0); 24 | int count = 0; 25 | 26 | for (int i = 1; i < params().size(); ++i) { 27 | Slice currKey = params().get(i); 28 | Slice oldValue = base().getSlice(key, currKey); 29 | base().deleteValue(key, currKey); 30 | 31 | if (oldValue != null) { 32 | ++count; 33 | } 34 | } 35 | 36 | return Response.integer(count); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/commands/RedisCommandParser.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.commands; 2 | 3 | import com.github.fppt.jedismock.server.SliceParser; 4 | import com.github.fppt.jedismock.exception.ParseErrorException; 5 | 6 | import java.io.ByteArrayInputStream; 7 | import java.io.InputStream; 8 | import java.util.Objects; 9 | 10 | /** 11 | * Created by Xiaolu on 2015/4/20. 12 | */ 13 | public class RedisCommandParser { 14 | 15 | public static RedisCommand parse(String stringInput) throws ParseErrorException { 16 | Objects.requireNonNull(stringInput); 17 | return parse(new ByteArrayInputStream(stringInput.getBytes())); 18 | } 19 | 20 | public static RedisCommand parse(InputStream messageInput) throws ParseErrorException { 21 | Objects.requireNonNull(messageInput); 22 | long count = SliceParser.consumeCount(messageInput); 23 | if (count == 0) { 24 | throw new ParseErrorException(); 25 | } 26 | RedisCommand command = RedisCommand.create(); 27 | for (long i = 0; i < count; i++) { 28 | command.parameters().add(SliceParser.consumeParameter(messageInput)); 29 | } 30 | return command; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/test/java/com/github/fppt/jedismock/comparisontests/lists/ListPopTest.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.comparisontests.lists; 2 | 3 | import com.github.fppt.jedismock.comparisontests.ComparisonBase; 4 | import org.junit.jupiter.api.BeforeEach; 5 | import org.junit.jupiter.api.TestTemplate; 6 | import org.junit.jupiter.api.extension.ExtendWith; 7 | import redis.clients.jedis.Jedis; 8 | import redis.clients.jedis.exceptions.JedisDataException; 9 | 10 | import static org.assertj.core.api.Assertions.assertThatThrownBy; 11 | 12 | @ExtendWith(ComparisonBase.class) 13 | public class ListPopTest { 14 | 15 | @BeforeEach 16 | public void setUp(Jedis jedis) { 17 | jedis.flushAll(); 18 | } 19 | 20 | @TestTemplate 21 | public void whenUsingListPop_ensureCheckForType(Jedis jedis) { 22 | String key = "pop_key"; 23 | jedis.set(key, "notalist"); 24 | assertThatThrownBy(() -> jedis.rpop(key)) 25 | .isInstanceOf(JedisDataException.class) 26 | .hasMessageStartingWith("WRONGTYPE"); 27 | assertThatThrownBy(() -> jedis.lpop(key)) 28 | .isInstanceOf(JedisDataException.class) 29 | .hasMessageStartingWith("WRONGTYPE"); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/keys/PExpireTime.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.keys; 2 | 3 | import com.github.fppt.jedismock.datastructures.Slice; 4 | import com.github.fppt.jedismock.operations.AbstractRedisOperation; 5 | import com.github.fppt.jedismock.operations.RedisCommand; 6 | import com.github.fppt.jedismock.server.Response; 7 | import com.github.fppt.jedismock.storage.RedisBase; 8 | 9 | import java.util.List; 10 | 11 | @RedisCommand("pexpiretime") 12 | class PExpireTime extends AbstractRedisOperation { 13 | PExpireTime(RedisBase base, List params) { 14 | super(base, params); 15 | } 16 | 17 | @Override 18 | protected int maxArgs() { 19 | return 1; 20 | } 21 | 22 | @Override 23 | protected int minArgs() { 24 | return 1; 25 | } 26 | 27 | protected Slice response() { 28 | Slice key = params().get(0); 29 | if (!base().exists(key)) { 30 | return Response.integer(-2L); 31 | } 32 | Long deadline = base().getDeadline(params().get(0)); 33 | if (deadline == null) { 34 | return Response.integer(-1L); 35 | } 36 | return Response.integer(deadline); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/sets/SAdd.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.sets; 2 | 3 | import com.github.fppt.jedismock.datastructures.RMSet; 4 | import com.github.fppt.jedismock.operations.AbstractRedisOperation; 5 | import com.github.fppt.jedismock.operations.RedisCommand; 6 | import com.github.fppt.jedismock.server.Response; 7 | import com.github.fppt.jedismock.storage.RedisBase; 8 | import com.github.fppt.jedismock.datastructures.Slice; 9 | 10 | import java.util.List; 11 | import java.util.Set; 12 | 13 | @RedisCommand("sadd") 14 | class SAdd extends AbstractRedisOperation { 15 | SAdd(RedisBase base, List params) { 16 | super(base, params); 17 | } 18 | 19 | @Override 20 | protected Slice response() { 21 | Slice key = params().get(0); 22 | RMSet setDBObj = getSetFromBaseOrCreateEmpty(key); 23 | Set set = setDBObj.getStoredData(); 24 | 25 | int count = 0; 26 | for (int i = 1; i < params().size(); i++) { 27 | if (set.add(params().get(i))){ 28 | count++; 29 | } 30 | } 31 | 32 | base().putValue(key, setDBObj); 33 | 34 | return Response.integer(count); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/connection/Hello.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.connection; 2 | 3 | import com.github.fppt.jedismock.operations.RedisCommand; 4 | import com.github.fppt.jedismock.operations.RedisOperation; 5 | import com.github.fppt.jedismock.server.Response; 6 | import com.github.fppt.jedismock.datastructures.Slice; 7 | 8 | import java.util.Arrays; 9 | import java.util.List; 10 | 11 | @RedisCommand(value = "hello", transactional = false) 12 | public class Hello implements RedisOperation { 13 | private final Slice protover; 14 | 15 | Hello(List params) { 16 | super(); 17 | protover = params.isEmpty() ? null : params.get(0); 18 | } 19 | 20 | @Override 21 | public Slice execute() { 22 | if (protover != null && "3".equals(protover.toString())) { 23 | return Response.error("NOPROTO Resp3 not supported by JedisMock"); 24 | } else { 25 | return Response.array( 26 | Arrays.asList( 27 | Response.bulkString(Slice.create("proto")), 28 | Response.bulkString(Response.integer(2)) 29 | ) 30 | ); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/connection/Select.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.connection; 2 | 3 | import com.github.fppt.jedismock.operations.RedisCommand; 4 | import com.github.fppt.jedismock.operations.RedisOperation; 5 | import com.github.fppt.jedismock.server.Response; 6 | import com.github.fppt.jedismock.datastructures.Slice; 7 | import com.github.fppt.jedismock.storage.OperationExecutorState; 8 | 9 | import java.util.List; 10 | 11 | @RedisCommand(value = "select", transactional = false) 12 | public class Select implements RedisOperation { 13 | private final OperationExecutorState state; 14 | private final List params; 15 | 16 | public Select(OperationExecutorState state, List params){ 17 | this.params = params; 18 | this.state = state; 19 | } 20 | 21 | @Override 22 | public Slice execute() { 23 | int selectedRedisBase = Integer.parseInt(new String(params.get(0).data())); 24 | if (state.owner().options().isClusterModeEnabled()) { 25 | return Response.error("ERR SELECT is not allowed in cluster mode"); 26 | } 27 | state.changeActiveRedisBase(selectedRedisBase); 28 | return Response.OK; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/keys/ExpireTime.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.keys; 2 | 3 | import com.github.fppt.jedismock.datastructures.Slice; 4 | import com.github.fppt.jedismock.operations.AbstractRedisOperation; 5 | import com.github.fppt.jedismock.operations.RedisCommand; 6 | import com.github.fppt.jedismock.server.Response; 7 | import com.github.fppt.jedismock.storage.RedisBase; 8 | 9 | import java.util.List; 10 | 11 | @RedisCommand("expiretime") 12 | class ExpireTime extends AbstractRedisOperation { 13 | ExpireTime(RedisBase base, List params) { 14 | super(base, params); 15 | } 16 | 17 | @Override 18 | protected int maxArgs() { 19 | return 1; 20 | } 21 | 22 | @Override 23 | protected int minArgs() { 24 | return 1; 25 | } 26 | 27 | protected Slice response() { 28 | Slice key = params().get(0); 29 | if (!base().exists(key)) { 30 | return Response.integer(-2L); 31 | } 32 | Long deadline = base().getDeadline(params().get(0)); 33 | if (deadline == null || deadline < 0) { 34 | return Response.integer(-1L); 35 | } 36 | return Response.integer(deadline / 1000); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/hashes/HMGet.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.hashes; 2 | 3 | import com.github.fppt.jedismock.operations.AbstractRedisOperation; 4 | import com.github.fppt.jedismock.operations.RedisCommand; 5 | import com.github.fppt.jedismock.server.Response; 6 | import com.github.fppt.jedismock.datastructures.Slice; 7 | import com.github.fppt.jedismock.storage.RedisBase; 8 | 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | 12 | @RedisCommand("hmget") 13 | public class HMGet extends AbstractRedisOperation { 14 | public HMGet(RedisBase base, List params) { 15 | super(base, params); 16 | } 17 | 18 | @Override 19 | protected Slice response() { 20 | ArrayList result = new ArrayList<>(); 21 | Slice hash = params().get(0); 22 | 23 | for (int i = 1; i < params().size(); i++) { 24 | Slice field = params().get(i); 25 | Slice value = base().getSlice(hash, field); 26 | 27 | if (value == null) { 28 | result.add(Response.NULL); 29 | } else { 30 | result.add(Response.bulkString(value)); 31 | } 32 | } 33 | 34 | return Response.array(result); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/hyperloglog/PFMerge.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.hyperloglog; 2 | 3 | import com.github.fppt.jedismock.datastructures.RMHyperLogLog; 4 | import com.github.fppt.jedismock.operations.AbstractRedisOperation; 5 | import com.github.fppt.jedismock.operations.RedisCommand; 6 | import com.github.fppt.jedismock.server.Response; 7 | import com.github.fppt.jedismock.datastructures.Slice; 8 | import com.github.fppt.jedismock.storage.RedisBase; 9 | 10 | import java.util.List; 11 | 12 | @RedisCommand("pfmerge") 13 | class PFMerge extends AbstractRedisOperation { 14 | PFMerge(RedisBase base, List params) { 15 | super(base, params); 16 | } 17 | 18 | protected Slice response() { 19 | Slice key = params().get(0); 20 | RMHyperLogLog rmData = base().getHLL(key); 21 | RMHyperLogLog set = rmData == null ? new RMHyperLogLog() : rmData; 22 | 23 | for (Slice v : params().subList(1, params().size())) { 24 | RMHyperLogLog valueToMerge = base().getHLL(v); 25 | 26 | if (valueToMerge != null) { 27 | set.addAll(valueToMerge.getStoredData()); 28 | } 29 | } 30 | 31 | base().putValue(key, set); 32 | return Response.OK; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/keys/paramsparser/ExpirationTimeParam.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.keys.paramsparser; 2 | 3 | import com.github.fppt.jedismock.datastructures.Slice; 4 | 5 | public final class ExpirationTimeParam { 6 | private final long millis; 7 | 8 | public ExpirationTimeParam(String commandName, 9 | Slice param, 10 | boolean useMillis, 11 | long timestampToCheckOverflow) throws ExpirationParamsException { 12 | long value; 13 | try { 14 | value = Long.parseLong(new String(param.data())); 15 | } catch (NumberFormatException e) { 16 | throw new ExpirationParamsException("ERR value is not an integer or out of range"); 17 | } 18 | try { 19 | millis = useMillis ? value : Math.multiplyExact(value, 1000L); 20 | Math.addExact(millis, timestampToCheckOverflow); 21 | } catch (ArithmeticException e) { 22 | throw new ExpirationParamsException(String.format("ERR invalid expire time in '%s' command", 23 | commandName)); 24 | } 25 | } 26 | 27 | public long getMillis() { 28 | return millis; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/server/Time.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.server; 2 | 3 | import com.github.fppt.jedismock.datastructures.Slice; 4 | import com.github.fppt.jedismock.operations.AbstractRedisOperation; 5 | import com.github.fppt.jedismock.operations.RedisCommand; 6 | import com.github.fppt.jedismock.server.Response; 7 | import com.github.fppt.jedismock.storage.RedisBase; 8 | 9 | import java.util.List; 10 | 11 | @RedisCommand("time") 12 | public class Time extends AbstractRedisOperation { 13 | 14 | Time(RedisBase base, List params) { 15 | super(base, params); 16 | } 17 | 18 | @Override 19 | protected int maxArgs() { 20 | return 0; 21 | } 22 | 23 | @Override 24 | protected Slice response() { 25 | //Java8 has wallclock with microseconds precision, 26 | //so this mock returns results truncated up to a millisecond 27 | long time = base().getClock().millis(); 28 | long seconds = time / 1000L; 29 | long microseconds = (time % 1000L) * 1000L; 30 | return Response.array( 31 | Response.bulkString(Slice.create(Long.toString(seconds))), 32 | Response.bulkString(Slice.create(Long.toString(microseconds))) 33 | ); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/test/java/com/github/fppt/jedismock/util/MockSubscriber.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.util; 2 | 3 | import redis.clients.jedis.JedisPubSub; 4 | 5 | import java.util.concurrent.CountDownLatch; 6 | import java.util.concurrent.TimeUnit; 7 | import java.util.concurrent.atomic.AtomicInteger; 8 | 9 | public class MockSubscriber extends JedisPubSub { 10 | 11 | private String latestReceivedFromChannel; 12 | private String latestReceivedMessage; 13 | private final AtomicInteger msgCount = new AtomicInteger(); 14 | private final CountDownLatch latch = new CountDownLatch(1); 15 | 16 | @Override 17 | public void onMessage(String channel, String message) { 18 | latestReceivedFromChannel = channel; 19 | latestReceivedMessage = message; 20 | latch.countDown(); 21 | msgCount.incrementAndGet(); 22 | } 23 | 24 | public String latestChannel() throws InterruptedException { 25 | latch.await(10, TimeUnit.SECONDS); 26 | return latestReceivedFromChannel; 27 | } 28 | 29 | public String latestMessage() throws InterruptedException { 30 | latch.await(10, TimeUnit.SECONDS); 31 | return latestReceivedMessage; 32 | } 33 | 34 | public int getMsgCount() { 35 | return msgCount.get(); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/sortedsets/ZRem.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.sortedsets; 2 | 3 | import com.github.fppt.jedismock.datastructures.RMZSet; 4 | import com.github.fppt.jedismock.operations.AbstractRedisOperation; 5 | import com.github.fppt.jedismock.operations.RedisCommand; 6 | import com.github.fppt.jedismock.server.Response; 7 | import com.github.fppt.jedismock.datastructures.Slice; 8 | import com.github.fppt.jedismock.storage.RedisBase; 9 | 10 | import java.util.List; 11 | 12 | @RedisCommand("zrem") 13 | class ZRem extends AbstractRedisOperation { 14 | 15 | ZRem(RedisBase base, List params) { 16 | super(base, params); 17 | } 18 | 19 | protected Slice response() { 20 | Slice key = params().get(0); 21 | final RMZSet mapDBObj = getZSetFromBaseOrCreateEmpty(key); 22 | if (mapDBObj.isEmpty()) { 23 | return Response.integer(0); 24 | } 25 | int count = 0; 26 | for (int i = 1; i < params().size(); i++) { 27 | if (mapDBObj.remove(params().get(i))) { 28 | count++; 29 | } 30 | } 31 | 32 | if (mapDBObj.isEmpty()) { 33 | base().deleteValue(key); 34 | } 35 | return Response.integer(count); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/sortedsets/ZRangeByScore.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.sortedsets; 2 | 3 | import com.github.fppt.jedismock.datastructures.Slice; 4 | import com.github.fppt.jedismock.exception.ArgumentException; 5 | import com.github.fppt.jedismock.operations.RedisCommand; 6 | import com.github.fppt.jedismock.storage.RedisBase; 7 | 8 | import java.util.List; 9 | 10 | import static com.github.fppt.jedismock.operations.sortedsets.AbstractZRange.Options.BYLEX; 11 | import static com.github.fppt.jedismock.operations.sortedsets.AbstractZRange.Options.REV; 12 | 13 | @RedisCommand("zrangebyscore") 14 | public class ZRangeByScore extends AbstractZRangeByScore { 15 | 16 | public ZRangeByScore(RedisBase base, List params) { 17 | super(base, params); 18 | } 19 | 20 | @Override 21 | protected Slice response() { 22 | if (options.contains(BYLEX) || options.contains(REV)) { 23 | throw new ArgumentException("*syntax*"); 24 | } 25 | key = params().get(0); 26 | mapDBObj = getZSetFromBaseOrCreateEmpty(key); 27 | 28 | final Slice start = params().get(1); 29 | final Slice end = params().get(2); 30 | return getSliceFromRange(getRange(getStartBound(start), getEndBound(end))); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/lists/Add.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.lists; 2 | 3 | import com.github.fppt.jedismock.datastructures.RMList; 4 | import com.github.fppt.jedismock.operations.AbstractRedisOperation; 5 | import com.github.fppt.jedismock.server.Response; 6 | import com.github.fppt.jedismock.datastructures.Slice; 7 | import com.github.fppt.jedismock.storage.OperationExecutorState; 8 | 9 | import java.util.List; 10 | 11 | abstract class Add extends AbstractRedisOperation { 12 | private final Object lock; 13 | Add(OperationExecutorState state, List params) { 14 | super(state.base(), params); 15 | this.lock = state.lock(); 16 | } 17 | 18 | abstract void addSliceToList(List list, Slice slice); 19 | 20 | protected Slice response() { 21 | Slice key = params().get(0); 22 | final RMList listDBObj = getListFromBaseOrCreateEmpty(key); 23 | final List list = listDBObj.getStoredData(); 24 | 25 | for (int i = 1; i < params().size(); i++) { 26 | addSliceToList(list, params().get(i)); 27 | } 28 | 29 | base().putValue(key, listDBObj); 30 | 31 | //Notify all waiting operations 32 | lock.notifyAll(); 33 | return Response.integer(list.size()); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/sortedsets/ZRevRangeByScore.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.sortedsets; 2 | 3 | import com.github.fppt.jedismock.datastructures.Slice; 4 | import com.github.fppt.jedismock.exception.ArgumentException; 5 | import com.github.fppt.jedismock.operations.RedisCommand; 6 | import com.github.fppt.jedismock.storage.RedisBase; 7 | 8 | import java.util.List; 9 | 10 | import static com.github.fppt.jedismock.operations.sortedsets.AbstractZRange.Options.BYLEX; 11 | import static com.github.fppt.jedismock.operations.sortedsets.AbstractZRange.Options.REV; 12 | 13 | @RedisCommand("zrevrangebyscore") 14 | public class ZRevRangeByScore extends AbstractZRangeByScore { 15 | 16 | ZRevRangeByScore(RedisBase base, List params) { 17 | super(base, params); 18 | } 19 | 20 | @Override 21 | protected Slice response() { 22 | if (options.contains(BYLEX)) { 23 | throw new ArgumentException("*syntax*"); 24 | } 25 | key = params().get(0); 26 | mapDBObj = getZSetFromBaseOrCreateEmpty(key); 27 | 28 | final Slice start = params().get(2); 29 | final Slice end = params().get(1); 30 | options.add(REV); 31 | return getSliceFromRange(getRange(getStartBound(start), getEndBound(end))); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/test/java/com/github/fppt/jedismock/comparisontests/server/TestFlushOperations.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.comparisontests.server; 2 | 3 | import com.github.fppt.jedismock.comparisontests.ComparisonBase; 4 | import org.junit.jupiter.api.TestTemplate; 5 | import org.junit.jupiter.api.extension.ExtendWith; 6 | import redis.clients.jedis.Jedis; 7 | 8 | import static org.assertj.core.api.Assertions.assertThat; 9 | 10 | @ExtendWith(ComparisonBase.class) 11 | public class TestFlushOperations { 12 | @TestTemplate 13 | void whenFlushDBCalled_ensureKeysAreErased(Jedis jedis) { 14 | jedis.set("foo", "val1"); 15 | jedis.set("bar", "val2"); 16 | assertThat(jedis.dbSize()).isEqualTo(2); 17 | jedis.flushDB(); 18 | assertThat(jedis.dbSize()).isEqualTo(0); 19 | } 20 | 21 | @TestTemplate 22 | void whenFlushAllCalled_ensureAllDatabasesAreErased(Jedis jedis) { 23 | for (int i = 0; i < 3; i++) { 24 | jedis.select(i); 25 | jedis.set("foo" + i, "val1"); 26 | jedis.set("bar" + i, "val2"); 27 | assertThat(jedis.dbSize()).isEqualTo(2); 28 | } 29 | jedis.flushAll(); 30 | for (int i = 0; i < 3; i++) { 31 | jedis.select(i); 32 | assertThat(jedis.dbSize()).isEqualTo(0); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/test/java/com/github/fppt/jedismock/util/MockPSubscriber.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.util; 2 | 3 | import redis.clients.jedis.JedisPubSub; 4 | 5 | import java.util.concurrent.CountDownLatch; 6 | import java.util.concurrent.TimeUnit; 7 | 8 | public class MockPSubscriber extends JedisPubSub { 9 | 10 | private String latestReceivedFromChannel; 11 | private String latestReceivedFromPattern; 12 | private String latestReceivedMessage; 13 | private final CountDownLatch latch = new CountDownLatch(1); 14 | 15 | @Override 16 | public void onPMessage(String pattern, String channel, String message) { 17 | latestReceivedFromChannel = channel; 18 | latestReceivedMessage = message; 19 | latestReceivedFromPattern = pattern; 20 | latch.countDown(); 21 | } 22 | 23 | public String latestChannel() throws InterruptedException { 24 | latch.await(10, TimeUnit.SECONDS); 25 | return latestReceivedFromChannel; 26 | } 27 | 28 | public String latestPattern() throws InterruptedException { 29 | latch.await(10, TimeUnit.SECONDS); 30 | return latestReceivedFromPattern; 31 | } 32 | 33 | public String latestMessage() throws InterruptedException { 34 | latch.await(10, TimeUnit.SECONDS); 35 | return latestReceivedMessage; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/sortedsets/AbstractZUnion.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.sortedsets; 2 | 3 | import com.github.fppt.jedismock.datastructures.RMZSet; 4 | import com.github.fppt.jedismock.datastructures.Slice; 5 | import com.github.fppt.jedismock.datastructures.ZSetEntry; 6 | import com.github.fppt.jedismock.storage.OperationExecutorState; 7 | 8 | import java.util.List; 9 | 10 | abstract class AbstractZUnion extends ZStore { 11 | 12 | AbstractZUnion(OperationExecutorState state, List params) { 13 | super(state, params); 14 | } 15 | 16 | protected RMZSet getResult(RMZSet zset1, RMZSet zset2, double weight) { 17 | RMZSet result = new RMZSet(); 18 | for (ZSetEntry entry : 19 | zset1.entries(false)) { 20 | result.put(entry.getValue(), entry.getScore()); 21 | } 22 | for (ZSetEntry entry : 23 | zset2.entries(false)) { 24 | if (result.hasMember(entry.getValue())) { 25 | result.put(entry.getValue(), aggregate.apply(result.getScore(entry.getValue()), getMultiple(entry.getScore(), weight))); 26 | } else { 27 | result.put(entry.getValue(), getMultiple(entry.getScore(), weight)); 28 | } 29 | } 30 | return result; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/strings/IncrOrDecrBy.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.strings; 2 | 3 | import com.github.fppt.jedismock.datastructures.RMString; 4 | import com.github.fppt.jedismock.operations.AbstractRedisOperation; 5 | import com.github.fppt.jedismock.server.Response; 6 | import com.github.fppt.jedismock.datastructures.Slice; 7 | import com.github.fppt.jedismock.storage.RedisBase; 8 | 9 | import java.util.List; 10 | 11 | import static com.github.fppt.jedismock.Utils.convertToLong; 12 | 13 | abstract class IncrOrDecrBy extends AbstractRedisOperation { 14 | IncrOrDecrBy(RedisBase base, List params) { 15 | super(base, params); 16 | } 17 | 18 | abstract long incrementOrDecrementValue(List params); 19 | 20 | protected Slice response() { 21 | Slice key = params().get(0); 22 | long d = incrementOrDecrementValue(params()); 23 | RMString v = base().getRMString(key); 24 | 25 | if (v == null) { 26 | base().putValue(key, RMString.create(String.valueOf(d))); 27 | return Response.integer(d); 28 | } 29 | 30 | long r = convertToLong(v.getStoredDataAsString()) + d; 31 | base().putValueWithoutClearingTtl(key, RMString.create(String.valueOf(r))); 32 | return Response.integer(r); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/hashes/HGetAll.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.hashes; 2 | 3 | import com.github.fppt.jedismock.datastructures.Slice; 4 | import com.github.fppt.jedismock.operations.AbstractRedisOperation; 5 | import com.github.fppt.jedismock.operations.RedisCommand; 6 | import com.github.fppt.jedismock.server.Response; 7 | import com.github.fppt.jedismock.storage.RedisBase; 8 | 9 | import java.util.ArrayList; 10 | import java.util.HashMap; 11 | import java.util.List; 12 | import java.util.Map; 13 | 14 | @RedisCommand("hgetall") 15 | public class HGetAll extends AbstractRedisOperation { 16 | public HGetAll(RedisBase base, List params) { 17 | super(base, params); 18 | } 19 | 20 | @Override 21 | protected Slice response() { 22 | Slice hash = params().get(0); 23 | 24 | Map fieldAndValueMap = base().getFieldsAndValuesReadOnly(hash); 25 | 26 | if (fieldAndValueMap == null) { 27 | fieldAndValueMap = new HashMap<>(); 28 | } 29 | 30 | List output = new ArrayList<>(); 31 | 32 | fieldAndValueMap.forEach((key, value) -> { 33 | output.add(Response.bulkString(key)); 34 | output.add(Response.bulkString(value)); 35 | }); 36 | 37 | return Response.array(output); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/datastructures/streams/StreamErrors.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.datastructures.streams; 2 | 3 | public class StreamErrors { 4 | public static final String ID_OVERFLOW_ERROR = "ERR The stream has exhausted the last possible ID, unable to add more items"; 5 | public static final String TOP_ERROR = "ERR The ID specified in XADD is equal or smaller than the target stream top item"; 6 | public static final String INVALID_ID_ERROR = "ERR Invalid stream ID specified as stream command argument"; 7 | public static final String ZERO_ERROR = "ERR The ID specified in XADD must be greater than 0-0"; 8 | public static final String SYNTAX_ERROR = "ERR syntax error"; 9 | public static final String NOT_AN_INTEGER_ERROR = "ERR value is not an integer or out of range"; 10 | public static final String LIMIT_OPTION_ERROR = "ERR syntax error, LIMIT cannot be used without the special ~ option"; 11 | public static final String XREAD_ARGS_ERROR = "ERR Unbalanced 'xread' list of streams: for each stream key an ID or '$' must be specified"; 12 | public static final String NEGATIVE_TIMEOUT_ERROR = "ERR timeout is negative"; 13 | public static final String RANGES_START_ID_ERROR = "ERR invalid start ID for the interval"; 14 | public static final String RANGES_END_ID_ERROR = "ERR invalid end ID for the interval"; 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/sets/SStore.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.sets; 2 | 3 | import com.github.fppt.jedismock.datastructures.RMSet; 4 | import com.github.fppt.jedismock.datastructures.Slice; 5 | import com.github.fppt.jedismock.operations.AbstractRedisOperation; 6 | import com.github.fppt.jedismock.server.Response; 7 | import com.github.fppt.jedismock.storage.RedisBase; 8 | 9 | import java.util.List; 10 | import java.util.Set; 11 | import java.util.function.BiFunction; 12 | 13 | public abstract class SStore extends AbstractRedisOperation { 14 | private final BiFunction, Set> operation; 15 | 16 | public SStore(RedisBase base, 17 | List params, 18 | BiFunction, Set> operation) { 19 | super(base, params); 20 | this.operation = operation; 21 | } 22 | 23 | @Override 24 | protected final Slice response() { 25 | Slice key = params().get(0); 26 | Set result = operation.apply(base(), params().subList(1, params().size())); 27 | if (result.isEmpty()) { 28 | base().deleteValue(key); 29 | } else { 30 | base().putValue(key, new RMSet(result)); 31 | } 32 | return Response.integer(result.size()); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/sortedsets/ZMScore.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.sortedsets; 2 | 3 | import com.github.fppt.jedismock.datastructures.RMZSet; 4 | import com.github.fppt.jedismock.datastructures.Slice; 5 | import com.github.fppt.jedismock.operations.AbstractRedisOperation; 6 | import com.github.fppt.jedismock.operations.RedisCommand; 7 | import com.github.fppt.jedismock.server.Response; 8 | import com.github.fppt.jedismock.storage.RedisBase; 9 | 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | 13 | @RedisCommand("zmscore") 14 | class ZMScore extends AbstractRedisOperation { 15 | 16 | ZMScore(RedisBase base, List params) { 17 | super(base, params); 18 | } 19 | 20 | @Override 21 | protected int minArgs() { 22 | return 2; 23 | } 24 | 25 | @Override 26 | protected Slice response() { 27 | Slice key = params().get(0); 28 | 29 | final RMZSet mapDBObj = getZSetFromBaseOrCreateEmpty(key); 30 | List result = new ArrayList<>(); 31 | 32 | for (int i = 1; i < params().size(); i++) { 33 | Double score = mapDBObj.getScore(params().get(i)); 34 | result.add(score == null ? Response.NULL : Response.bulkString(Slice.create(String.valueOf(Math.round(score))))); 35 | } 36 | return Response.array(result); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/scripting/EvalSha.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.scripting; 2 | 3 | import com.github.fppt.jedismock.datastructures.Slice; 4 | import com.github.fppt.jedismock.operations.AbstractRedisOperation; 5 | import com.github.fppt.jedismock.operations.RedisCommand; 6 | import com.github.fppt.jedismock.server.Response; 7 | import com.github.fppt.jedismock.storage.OperationExecutorState; 8 | import com.github.fppt.jedismock.storage.RedisBase; 9 | 10 | import java.util.List; 11 | 12 | @RedisCommand("evalsha") 13 | public class EvalSha extends AbstractRedisOperation { 14 | private final OperationExecutorState state; 15 | 16 | public EvalSha(final RedisBase base, final List params, OperationExecutorState state) { 17 | super(base, params); 18 | this.state = state; 19 | } 20 | 21 | @Override 22 | protected int minArgs() { 23 | return 2; 24 | } 25 | 26 | @Override 27 | protected Slice response() { 28 | final String sha = params().get(0).toString(); 29 | final String script = base().getCachedLuaScript(sha); 30 | if (script == null) { 31 | return Response.error("NOSCRIPT No matching script. Please use EVAL."); 32 | } 33 | params().set(0, Slice.create(script)); 34 | return new Eval(base(), params(), state).response(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/test/java/com/github/fppt/jedismock/comparisontests/hashes/TestHStrlen.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.comparisontests.hashes; 2 | 3 | import com.github.fppt.jedismock.comparisontests.ComparisonBase; 4 | import org.junit.jupiter.api.BeforeEach; 5 | import org.junit.jupiter.api.TestTemplate; 6 | import org.junit.jupiter.api.extension.ExtendWith; 7 | import redis.clients.jedis.Jedis; 8 | 9 | import static org.assertj.core.api.Assertions.assertThat; 10 | 11 | @ExtendWith(ComparisonBase.class) 12 | public class TestHStrlen { 13 | @BeforeEach 14 | public void setUp(Jedis jedis) { 15 | jedis.flushDB(); 16 | } 17 | 18 | @TestTemplate 19 | public void hstrlenReturnsLengthOfFields(Jedis jedis) { 20 | jedis.hset("myhash", "f1", "HelloWorld"); 21 | jedis.hset("myhash", "f2", "99"); 22 | jedis.hset("myhash", "f3", "-256"); 23 | 24 | assertThat(jedis.hstrlen("myhash", "f1")).isEqualTo(10); 25 | assertThat(jedis.hstrlen("myhash", "f2")).isEqualTo(2); 26 | assertThat(jedis.hstrlen("myhash", "f3")).isEqualTo(4); 27 | assertThat(jedis.hstrlen("myhash", "no_such_field")).isEqualTo(0); 28 | } 29 | 30 | @TestTemplate 31 | public void hstrlenReturnsZeroForNonExistent(Jedis jedis) { 32 | assertThat(jedis.hstrlen("no_such_hash", "no_such_field")).isEqualTo(0); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/test/java/com/github/fppt/jedismock/comparisontests/lists/RPopLPushTest.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.comparisontests.lists; 2 | 3 | import com.github.fppt.jedismock.comparisontests.ComparisonBase; 4 | import org.junit.jupiter.api.BeforeEach; 5 | import org.junit.jupiter.api.TestTemplate; 6 | import org.junit.jupiter.api.extension.ExtendWith; 7 | import redis.clients.jedis.Jedis; 8 | import redis.clients.jedis.exceptions.JedisDataException; 9 | 10 | import static org.assertj.core.api.Assertions.assertThat; 11 | import static org.assertj.core.api.Assertions.assertThatThrownBy; 12 | 13 | @ExtendWith(ComparisonBase.class) 14 | public class RPopLPushTest { 15 | 16 | @BeforeEach 17 | public void setUp(Jedis jedis) { 18 | jedis.flushAll(); 19 | } 20 | 21 | @TestTemplate 22 | public void whenUsingRRopLPush_ensureDontPopOnInvalidTargetType(Jedis jedis) { 23 | String listKey = "rpoplpush_1_list"; 24 | String valueKey = "rpoplpush_1_value"; 25 | jedis.rpush(listKey, "1", "2", "3"); 26 | jedis.set(valueKey, "some_value"); 27 | 28 | assertThatThrownBy(() -> jedis.rpoplpush(listKey, valueKey)) 29 | .isInstanceOf(JedisDataException.class) 30 | .hasMessage("WRONGTYPE Operation against a key holding the wrong kind of value"); 31 | 32 | assertThat(jedis.llen(listKey)).isEqualTo(3); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/test/java/com/github/fppt/jedismock/comparisontests/lists/WatchTest.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.comparisontests.lists; 2 | 3 | import com.github.fppt.jedismock.comparisontests.ComparisonBase; 4 | import org.junit.jupiter.api.BeforeEach; 5 | import org.junit.jupiter.api.TestTemplate; 6 | import org.junit.jupiter.api.extension.ExtendWith; 7 | import redis.clients.jedis.HostAndPort; 8 | import redis.clients.jedis.Jedis; 9 | import redis.clients.jedis.Transaction; 10 | 11 | import static org.assertj.core.api.Assertions.assertThat; 12 | 13 | @ExtendWith(ComparisonBase.class) 14 | public class WatchTest { 15 | 16 | @BeforeEach 17 | public void setUp(Jedis jedis) { 18 | jedis.flushAll(); 19 | } 20 | 21 | @TestTemplate 22 | public void whenUsingPop_ensureAffectsWatchedKey(Jedis jedis, HostAndPort hostAndPort) { 23 | String key = "watch_key"; 24 | String setKey = "set_key"; 25 | String initialValue = "initial_value"; 26 | 27 | Jedis secondClient = new Jedis(hostAndPort); 28 | 29 | jedis.set(setKey, initialValue); 30 | jedis.lpush(key, "1", "2", "3"); 31 | jedis.watch(key); 32 | 33 | Transaction t = jedis.multi(); 34 | t.set(setKey, "some_value"); 35 | secondClient.rpop(key); 36 | t.exec(); 37 | 38 | assertThat(jedis.get(setKey)).isEqualTo(initialValue); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/commands/RedisCommand.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.commands; 2 | 3 | import com.github.fppt.jedismock.datastructures.Slice; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | import java.util.Objects; 8 | import java.util.stream.Collectors; 9 | 10 | public final class RedisCommand { 11 | 12 | private final List parameters; 13 | 14 | private RedisCommand(List parameters) { 15 | if (parameters == null) { 16 | throw new NullPointerException("Null parameters"); 17 | } 18 | this.parameters = parameters; 19 | } 20 | 21 | public List parameters() { 22 | return parameters; 23 | } 24 | 25 | @Override 26 | public boolean equals(Object o) { 27 | if (this == o) return true; 28 | if (o == null || getClass() != o.getClass()) return false; 29 | RedisCommand that = (RedisCommand) o; 30 | return parameters.equals(that.parameters); 31 | } 32 | 33 | @Override 34 | public int hashCode() { 35 | return Objects.hash(parameters); 36 | } 37 | 38 | @Override 39 | public String toString(){ 40 | return parameters().stream().map(Slice::toString).collect(Collectors.joining(" ")); 41 | } 42 | 43 | public static RedisCommand create(){ 44 | return new RedisCommand(new ArrayList<>()); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/sets/SMembers.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.sets; 2 | 3 | import com.github.fppt.jedismock.datastructures.RMSet; 4 | import com.github.fppt.jedismock.operations.AbstractRedisOperation; 5 | import com.github.fppt.jedismock.operations.RedisCommand; 6 | import com.github.fppt.jedismock.server.Response; 7 | import com.github.fppt.jedismock.datastructures.Slice; 8 | import com.github.fppt.jedismock.storage.RedisBase; 9 | 10 | import java.util.LinkedList; 11 | import java.util.List; 12 | import java.util.Set; 13 | 14 | import static java.util.stream.Collectors.toList; 15 | 16 | @RedisCommand("smembers") 17 | class SMembers extends AbstractRedisOperation { 18 | SMembers(RedisBase base, List params) { 19 | super(base, params); 20 | } 21 | 22 | protected Slice response() { 23 | final Slice key = params().get(0); 24 | final RMSet setDBObj = getSetFromBaseOrCreateEmpty(key); 25 | final Set data = setDBObj.getStoredData(); 26 | //Has to be a list because Jedis can only deserialize lists 27 | LinkedList list; 28 | if (data != null) { 29 | list = new LinkedList<>(data); 30 | } else { 31 | list = new LinkedList<>(); 32 | } 33 | 34 | return Response.array(list.stream().map(Response::bulkString).collect(toList())); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/sets/SRem.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.sets; 2 | 3 | import com.github.fppt.jedismock.datastructures.RMSet; 4 | import com.github.fppt.jedismock.operations.AbstractRedisOperation; 5 | import com.github.fppt.jedismock.operations.RedisCommand; 6 | import com.github.fppt.jedismock.server.Response; 7 | import com.github.fppt.jedismock.datastructures.Slice; 8 | import com.github.fppt.jedismock.storage.RedisBase; 9 | 10 | import java.util.List; 11 | import java.util.Set; 12 | 13 | @RedisCommand("srem") 14 | class SRem extends AbstractRedisOperation { 15 | 16 | 17 | SRem(RedisBase base, List params) { 18 | super(base, params); 19 | } 20 | 21 | final int remove() { 22 | Slice key = params().get(0); 23 | RMSet setDBObj = getSetFromBaseOrCreateEmpty(key); 24 | Set set = setDBObj.getStoredData(); 25 | if (set == null) { 26 | return 0; 27 | } 28 | int count = 0; 29 | for (int i = 1; i < params().size(); i++) { 30 | if (set.remove(params().get(i))) { 31 | count++; 32 | } 33 | } 34 | if (set.isEmpty()) { 35 | base().deleteValue(key); 36 | } 37 | return count; 38 | } 39 | 40 | protected Slice response() { 41 | return Response.integer(remove()); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/sortedsets/BZMPop.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.sortedsets; 2 | 3 | import com.github.fppt.jedismock.datastructures.Slice; 4 | import com.github.fppt.jedismock.operations.RedisCommand; 5 | import com.github.fppt.jedismock.storage.OperationExecutorState; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | import static com.github.fppt.jedismock.Utils.toNanoTimeout; 11 | 12 | @RedisCommand("bzmpop") 13 | public class BZMPop extends BZPop { 14 | private Slice numKeys; 15 | 16 | BZMPop(OperationExecutorState state, List params) { 17 | super(state, params); 18 | } 19 | 20 | @Override 21 | protected int minArgs() { 22 | return 4; 23 | } 24 | 25 | @Override 26 | protected void doOptionalWork(){ 27 | timeoutNanos = toNanoTimeout(params().get(0).toString()); 28 | params().remove(0); 29 | ZMPop zmPop = new ZMPop(base(), new ArrayList<>(params())); 30 | numKeys = params().remove(0); 31 | keys = zmPop.parseArgs(); 32 | keys.remove(0); 33 | } 34 | 35 | @Override 36 | protected Slice popper(List params) { 37 | List newParams = new ArrayList<>(params()); 38 | newParams.add(0, numKeys); 39 | ZMPop zmPop = new ZMPop(base(), newParams); 40 | return zmPop.response(); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/hyperloglog/PFAdd.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.hyperloglog; 2 | 3 | import com.github.fppt.jedismock.datastructures.RMHyperLogLog; 4 | import com.github.fppt.jedismock.operations.AbstractRedisOperation; 5 | import com.github.fppt.jedismock.operations.RedisCommand; 6 | import com.github.fppt.jedismock.server.Response; 7 | import com.github.fppt.jedismock.datastructures.Slice; 8 | import com.github.fppt.jedismock.storage.RedisBase; 9 | 10 | import java.util.List; 11 | 12 | @RedisCommand("pfadd") 13 | class PFAdd extends AbstractRedisOperation { 14 | PFAdd(RedisBase base, List params) { 15 | super(base, params); 16 | } 17 | 18 | protected Slice response(){ 19 | Slice key = params().get(0); 20 | RMHyperLogLog dataSet = base().getHLL(key); 21 | boolean first = true; 22 | 23 | int prev = 0; 24 | if (dataSet == null) { 25 | dataSet = new RMHyperLogLog(); 26 | } else { 27 | first = false; 28 | prev = dataSet.size(); 29 | } 30 | 31 | dataSet.addAll(params().subList(1, params().size())); 32 | 33 | if (first) { 34 | base().putValue(key, dataSet); 35 | } else { 36 | base().putValue(key, dataSet, null); 37 | } 38 | 39 | return Response.integer((prev != dataSet.size()) ? 1 : 0); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/test/java/com/github/fppt/jedismock/comparisontests/hashes/HKeysOperation.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.comparisontests.hashes; 2 | 3 | import com.github.fppt.jedismock.comparisontests.ComparisonBase; 4 | import org.junit.jupiter.api.BeforeEach; 5 | import org.junit.jupiter.api.TestTemplate; 6 | import org.junit.jupiter.api.extension.ExtendWith; 7 | import redis.clients.jedis.Jedis; 8 | 9 | import java.util.List; 10 | import java.util.Map; 11 | import java.util.Set; 12 | 13 | import static org.assertj.core.api.Assertions.assertThat; 14 | 15 | @ExtendWith(ComparisonBase.class) 16 | public class HKeysOperation { 17 | @BeforeEach 18 | public void setUp(Jedis jedis) { 19 | jedis.flushAll(); 20 | } 21 | 22 | @TestTemplate 23 | void hkeysUnknownKey(Jedis jedis) { 24 | Set res = jedis.hkeys("foo"); 25 | assertThat(res).isEmpty(); 26 | } 27 | 28 | @TestTemplate 29 | void hvalsUnknownKey(Jedis jedis) { 30 | List res = jedis.hvals("foo"); 31 | assertThat(res).isEmpty(); 32 | } 33 | 34 | @TestTemplate 35 | void hlenUnknownKey(Jedis jedis) { 36 | long hlen = jedis.hlen("foo"); 37 | assertThat(hlen).isEqualTo(0); 38 | } 39 | 40 | @TestTemplate 41 | void hGetAllUnknownKey(Jedis jedis) { 42 | Map result = jedis.hgetAll("foo"); 43 | assertThat(result).isEmpty(); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/strings/SetEx.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.strings; 2 | 3 | import com.github.fppt.jedismock.operations.RedisCommand; 4 | import com.github.fppt.jedismock.storage.RedisBase; 5 | import com.github.fppt.jedismock.server.Response; 6 | import com.github.fppt.jedismock.datastructures.Slice; 7 | 8 | import java.util.List; 9 | 10 | import static com.github.fppt.jedismock.Utils.convertToLong; 11 | 12 | @RedisCommand("setex") 13 | class SetEx extends Set { 14 | SetEx(RedisBase base, List params) { 15 | super(base, params); 16 | } 17 | 18 | long timeoutToSet(List params) { 19 | return Math.multiplyExact(convertToLong(new String(params.get(1).data())), 1000L); 20 | } 21 | 22 | protected Slice response() { 23 | final long timeout; 24 | try { 25 | timeout = timeoutToSet(params()); 26 | Math.addExact(base().getClock().millis(), timeout); 27 | } catch (ArithmeticException e) { 28 | return Response.error(String.format("ERR invalid expire time in '%s' command", self().value())); 29 | } 30 | if (timeout <= 0){ 31 | return Response.error(String.format("ERR invalid expire time in '%s' command", self().value())); 32 | } 33 | base().putValue(params().get(0), params().get(2).extract(), timeout); 34 | return Response.OK; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/sortedsets/ZLexCount.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.sortedsets; 2 | 3 | import com.github.fppt.jedismock.datastructures.Slice; 4 | import com.github.fppt.jedismock.operations.RedisCommand; 5 | import com.github.fppt.jedismock.server.Response; 6 | import com.github.fppt.jedismock.storage.RedisBase; 7 | 8 | import java.util.List; 9 | 10 | @RedisCommand("zlexcount") 11 | class ZLexCount extends AbstractZRangeByLex { 12 | 13 | ZLexCount(RedisBase base, List params) { 14 | super(base, params); 15 | } 16 | 17 | @Override 18 | protected Slice response() { 19 | expectNoOptions(); 20 | key = params().get(0); 21 | mapDBObj = getZSetFromBaseOrCreateEmpty(key); 22 | 23 | Slice start = params().get(1); 24 | if (notValidate(start.toString())) { 25 | return buildErrorResponse("start"); 26 | } 27 | 28 | Slice end = params().get(2); 29 | if (notValidate(end.toString())) { 30 | return buildErrorResponse("end"); 31 | } 32 | 33 | return Response.integer(getRange(getStartBound(start), getEndBound(end)).size()); 34 | } 35 | 36 | private boolean notValidate(String forValidate) { 37 | return !NEGATIVELY_INFINITE.equals(forValidate) && !POSITIVELY_INFINITE.equals(forValidate) && 38 | !startsWithAnyPrefix(forValidate); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/hashes/HIncrBy.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.hashes; 2 | 3 | import com.github.fppt.jedismock.operations.AbstractRedisOperation; 4 | import com.github.fppt.jedismock.operations.RedisCommand; 5 | import com.github.fppt.jedismock.server.Response; 6 | import com.github.fppt.jedismock.datastructures.Slice; 7 | import com.github.fppt.jedismock.storage.RedisBase; 8 | 9 | import java.util.List; 10 | 11 | import static com.github.fppt.jedismock.Utils.convertToLong; 12 | 13 | @RedisCommand("hincrby") 14 | class HIncrBy extends AbstractRedisOperation { 15 | HIncrBy(RedisBase base, List params) { 16 | super(base, params); 17 | } 18 | 19 | Slice hsetValue(Slice key1, Slice key2, Slice value) { 20 | long numericValue = convertToLong(String.valueOf(value)); 21 | Slice foundValue = base().getSlice(key1, key2); 22 | if (foundValue != null) { 23 | numericValue = Math.addExact(convertToLong(new String(foundValue.data())), numericValue); 24 | } 25 | base().putSlice(key1, key2, Slice.create(String.valueOf(numericValue)), -1L); 26 | return Response.integer(numericValue); 27 | } 28 | 29 | protected Slice response() { 30 | Slice key1 = params().get(0); 31 | Slice key2 = params().get(1); 32 | Slice value = params().get(2); 33 | return hsetValue(key1, key2, value); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/lists/LIndex.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.lists; 2 | 3 | import com.github.fppt.jedismock.datastructures.RMList; 4 | import com.github.fppt.jedismock.operations.AbstractRedisOperation; 5 | import com.github.fppt.jedismock.operations.RedisCommand; 6 | import com.github.fppt.jedismock.server.Response; 7 | import com.github.fppt.jedismock.datastructures.Slice; 8 | import com.github.fppt.jedismock.storage.RedisBase; 9 | 10 | import java.util.List; 11 | 12 | import static com.github.fppt.jedismock.Utils.convertToInteger; 13 | 14 | @RedisCommand("lindex") 15 | class LIndex extends AbstractRedisOperation { 16 | LIndex(RedisBase base, List params) { 17 | super(base, params); 18 | } 19 | 20 | protected Slice response() { 21 | Slice key = params().get(0); 22 | RMList listDBObj = getListFromBaseOrCreateEmpty(key); 23 | List list = listDBObj.getStoredData(); 24 | if(list.isEmpty()) return Response.NULL; 25 | 26 | int index = convertToInteger(params().get(1).toString()); 27 | if (index < 0) { 28 | index = list.size() + index; 29 | if (index < 0) { 30 | return Response.NULL; 31 | } 32 | } 33 | if (index >= list.size()) { 34 | return Response.NULL; 35 | } 36 | return Response.bulkString(list.get(index)); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/sets/SScan.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.sets; 2 | 3 | import com.github.fppt.jedismock.datastructures.RMSet; 4 | import com.github.fppt.jedismock.operations.RedisCommand; 5 | import com.github.fppt.jedismock.operations.keys.Scan; 6 | import com.github.fppt.jedismock.server.Response; 7 | import com.github.fppt.jedismock.datastructures.Slice; 8 | import com.github.fppt.jedismock.storage.RedisBase; 9 | 10 | import java.util.List; 11 | import java.util.Set; 12 | import java.util.stream.Collectors; 13 | 14 | @RedisCommand("sscan") 15 | class SScan extends Scan { 16 | 17 | private Slice keySlice; 18 | 19 | SScan(RedisBase base, List params) { 20 | super(base, params); 21 | } 22 | 23 | @Override 24 | protected void doOptionalWork() { 25 | this.keySlice = params().get(0); 26 | this.cursorSlice = params().get(1); 27 | } 28 | 29 | @Override 30 | protected List getMatchingValues(String regex, long cursor, long count) { 31 | RMSet setDBObj = getSetFromBaseOrCreateEmpty(keySlice); 32 | Set set = setDBObj.getStoredData(); 33 | this.size = set.size(); 34 | return set.stream().skip(cursor) 35 | .limit(count) 36 | .filter(x -> x.toString().matches(regex)) 37 | .map(Response::bulkString) 38 | .collect(Collectors.toList()); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/test/java/com/github/fppt/jedismock/comparisontests/strings/TestAppend.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.comparisontests.strings; 2 | 3 | import com.github.fppt.jedismock.comparisontests.ComparisonBase; 4 | import org.junit.jupiter.api.BeforeEach; 5 | import org.junit.jupiter.api.TestTemplate; 6 | import org.junit.jupiter.api.extension.ExtendWith; 7 | import redis.clients.jedis.Jedis; 8 | 9 | import static org.assertj.core.api.Assertions.assertThat; 10 | 11 | @ExtendWith(ComparisonBase.class) 12 | public class TestAppend { 13 | @BeforeEach 14 | public void setUp(Jedis jedis) { 15 | jedis.flushDB(); 16 | } 17 | 18 | @TestTemplate 19 | public void testAppendEmpty(Jedis jedis) { 20 | jedis.append("foo", "bar"); 21 | assertThat(jedis.get("foo")).isEqualTo("bar"); 22 | } 23 | 24 | @TestTemplate 25 | public void testAppendNonEmpty(Jedis jedis) { 26 | jedis.set("baz", "foo"); 27 | jedis.append("baz", "bar"); 28 | assertThat(jedis.get("baz")).isEqualTo("foobar"); 29 | } 30 | 31 | @TestTemplate 32 | public void testAppendBinary(Jedis jedis) { 33 | jedis.set("baz".getBytes(), new byte[]{(byte) 0x01, (byte) 0x02}); 34 | jedis.append("baz".getBytes(), new byte[]{(byte) 0x03, (byte) 0x04}); 35 | assertThat(jedis.get("baz".getBytes())).containsExactly((byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/lists/LSet.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.lists; 2 | 3 | import com.github.fppt.jedismock.Utils; 4 | import com.github.fppt.jedismock.datastructures.Slice; 5 | import com.github.fppt.jedismock.operations.AbstractRedisOperation; 6 | import com.github.fppt.jedismock.operations.RedisCommand; 7 | import com.github.fppt.jedismock.server.Response; 8 | import com.github.fppt.jedismock.storage.RedisBase; 9 | 10 | import java.util.List; 11 | 12 | @RedisCommand("lset") 13 | public class LSet extends AbstractRedisOperation { 14 | public LSet(RedisBase base, List params) { 15 | super(base, params); 16 | } 17 | 18 | @Override 19 | protected Slice response() { 20 | Slice key = params().get(0); 21 | int index = Utils.convertToInteger(params().get(1).toString()); 22 | Slice element = params().get(2); 23 | 24 | if (!base().exists(key)) { 25 | throw new IllegalArgumentException("ERR no such key"); 26 | } 27 | 28 | List storedData = base().getList(key).getStoredData(); 29 | 30 | if (index < 0) { 31 | index = storedData.size() + index; 32 | } 33 | 34 | if (index < 0 || index >= storedData.size()) { 35 | throw new IllegalArgumentException("ERR index out of range"); 36 | } 37 | 38 | storedData.set(index, element); 39 | return Response.OK; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/hashes/HScan.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.hashes; 2 | 3 | import com.github.fppt.jedismock.operations.RedisCommand; 4 | import com.github.fppt.jedismock.server.Response; 5 | import com.github.fppt.jedismock.datastructures.Slice; 6 | import com.github.fppt.jedismock.storage.RedisBase; 7 | 8 | import java.util.List; 9 | import java.util.Map; 10 | import java.util.stream.Collectors; 11 | import java.util.stream.Stream; 12 | 13 | @RedisCommand("hscan") 14 | public class HScan extends com.github.fppt.jedismock.operations.keys.Scan { 15 | private Slice keySlice; 16 | 17 | HScan(RedisBase base, List params) { 18 | super(base, params); 19 | } 20 | 21 | @Override 22 | protected void doOptionalWork() { 23 | this.keySlice = params().get(0); 24 | this.cursorSlice = params().get(1); 25 | } 26 | 27 | @Override 28 | protected List getMatchingValues(String regex, long cursor, long count) { 29 | Map fieldAndValueMap = base().getFieldsAndValuesReadOnly(keySlice); 30 | this.size = fieldAndValueMap.size(); 31 | return fieldAndValueMap.entrySet().stream().skip(cursor) 32 | .limit(count) 33 | .filter(e -> e.getKey().toString().matches(regex)) 34 | .flatMap(e-> Stream.of(e.getKey(), e.getValue())) 35 | .map(Response::bulkString) 36 | .collect(Collectors.toList()); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/sets/SDiff.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.sets; 2 | 3 | import com.github.fppt.jedismock.datastructures.RMSet; 4 | import com.github.fppt.jedismock.operations.AbstractRedisOperation; 5 | import com.github.fppt.jedismock.operations.RedisCommand; 6 | import com.github.fppt.jedismock.server.Response; 7 | import com.github.fppt.jedismock.datastructures.Slice; 8 | import com.github.fppt.jedismock.storage.RedisBase; 9 | 10 | import java.util.List; 11 | import java.util.Set; 12 | 13 | import static java.util.stream.Collectors.toList; 14 | 15 | import java.util.HashSet; 16 | 17 | @RedisCommand("sdiff") 18 | class SDiff extends AbstractRedisOperation { 19 | SDiff(RedisBase base, List params) { 20 | super(base, params); 21 | } 22 | 23 | final Set getDifference() { 24 | Slice key = params().get(0); 25 | RMSet setObj = getSetFromBaseOrCreateEmpty(key); 26 | Set result = new HashSet<>(setObj.getStoredData()); 27 | 28 | for(int i = 1; i < params().size(); i++){ 29 | RMSet secondSetObj = getSetFromBaseOrCreateEmpty(params().get(i)); 30 | Set secondSet = secondSetObj.getStoredData(); 31 | result.removeAll(secondSet); 32 | } 33 | 34 | return result; 35 | } 36 | 37 | @Override 38 | protected Slice response() { 39 | return Response.array(getDifference().stream().map(Response::bulkString).collect(toList())); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/connection/Client.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.connection; 2 | 3 | import com.github.fppt.jedismock.operations.RedisCommand; 4 | import com.github.fppt.jedismock.operations.RedisOperation; 5 | import com.github.fppt.jedismock.server.Response; 6 | import com.github.fppt.jedismock.datastructures.Slice; 7 | import com.github.fppt.jedismock.storage.OperationExecutorState; 8 | 9 | import java.util.List; 10 | 11 | @RedisCommand(value = "client", transactional = false) 12 | public class Client implements RedisOperation { 13 | 14 | private final OperationExecutorState state; 15 | private final List params; 16 | 17 | public Client(OperationExecutorState state, List params) { 18 | this.state = state; 19 | this.params = params; 20 | } 21 | 22 | @Override 23 | public Slice execute() { 24 | if (params.isEmpty()) { 25 | return Response.error("wrong number of arguments for 'client' command"); 26 | } 27 | final String subcommand = params.get(0).toString(); 28 | if ("setname".equalsIgnoreCase(subcommand)) { 29 | state.setClientName(params.get(1).toString()); 30 | } else if ("getname".equalsIgnoreCase(subcommand)) { 31 | String name = state.getClientName(); 32 | return name == null ? Response.NULL : Response.bulkString(Slice.create(name)); 33 | } 34 | return Response.clientResponse("client", Response.OK); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/sets/SUnion.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.sets; 2 | 3 | import com.github.fppt.jedismock.datastructures.RMSet; 4 | import com.github.fppt.jedismock.operations.AbstractRedisOperation; 5 | import com.github.fppt.jedismock.operations.RedisCommand; 6 | import com.github.fppt.jedismock.server.Response; 7 | import com.github.fppt.jedismock.datastructures.Slice; 8 | import com.github.fppt.jedismock.storage.RedisBase; 9 | 10 | import java.util.List; 11 | import java.util.Set; 12 | 13 | import static java.util.stream.Collectors.toList; 14 | 15 | import java.util.HashSet; 16 | 17 | @RedisCommand("sunion") 18 | class SUnion extends AbstractRedisOperation { 19 | SUnion(RedisBase base, List params) { 20 | super(base, params); 21 | } 22 | 23 | final Set getUnion() { 24 | Slice key = params().get(0); 25 | RMSet setObj = getSetFromBaseOrCreateEmpty(key); 26 | Set resultSoFar = new HashSet<>(setObj.getStoredData()); 27 | 28 | for(int i = 1; i < params().size(); i++){ 29 | RMSet secondSetObj = getSetFromBaseOrCreateEmpty(params().get(i)); 30 | Set secondSet = secondSetObj.getStoredData(); 31 | resultSoFar.addAll(secondSet); 32 | } 33 | 34 | return resultSoFar; 35 | } 36 | 37 | @Override 38 | protected Slice response() { 39 | return Response.array(getUnion().stream().map(Response::bulkString).collect(toList())); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/sets/SInter.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.sets; 2 | 3 | import com.github.fppt.jedismock.datastructures.RMSet; 4 | import com.github.fppt.jedismock.operations.AbstractRedisOperation; 5 | import com.github.fppt.jedismock.operations.RedisCommand; 6 | import com.github.fppt.jedismock.server.Response; 7 | import com.github.fppt.jedismock.datastructures.Slice; 8 | import com.github.fppt.jedismock.storage.RedisBase; 9 | 10 | import java.util.List; 11 | import java.util.Set; 12 | 13 | import static java.util.stream.Collectors.toList; 14 | 15 | import java.util.HashSet; 16 | 17 | @RedisCommand("sinter") 18 | class SInter extends AbstractRedisOperation { 19 | SInter(RedisBase base, List params) { 20 | super(base, params); 21 | } 22 | 23 | final Set getIntersection() { 24 | Slice key = params().get(0); 25 | RMSet setObj = getSetFromBaseOrCreateEmpty(key); 26 | Set result = new HashSet<>(setObj.getStoredData()); 27 | 28 | for(int i = 1; i < params().size(); i++){ 29 | RMSet secondSetObj = getSetFromBaseOrCreateEmpty(params().get(i)); 30 | Set secondSet = secondSetObj.getStoredData(); 31 | result.retainAll(secondSet); 32 | } 33 | 34 | return result; 35 | } 36 | 37 | @Override 38 | protected Slice response() { 39 | return Response.array(getIntersection().stream().map(Response::bulkString).collect(toList())); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/sets/SMove.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.sets; 2 | 3 | import com.github.fppt.jedismock.datastructures.Slice; 4 | import com.github.fppt.jedismock.exception.WrongValueTypeException; 5 | import com.github.fppt.jedismock.operations.AbstractRedisOperation; 6 | import com.github.fppt.jedismock.operations.RedisCommand; 7 | import com.github.fppt.jedismock.server.Response; 8 | import com.github.fppt.jedismock.storage.RedisBase; 9 | 10 | import java.util.Arrays; 11 | import java.util.List; 12 | 13 | @RedisCommand("smove") 14 | public class SMove extends AbstractRedisOperation { 15 | SMove(RedisBase base, List params) { 16 | super(base, params); 17 | } 18 | 19 | @Override 20 | protected Slice response() { 21 | Slice src = params().get(0); 22 | Slice dest = params().get(1); 23 | Slice member = params().get(2); 24 | 25 | // check destination type BEFORE deleting from src 26 | if (base().getValue(dest) != null && base().getSet(dest) == null) { 27 | throw new WrongValueTypeException("WRONGTYPE dest is not a set"); 28 | } 29 | 30 | final int result = new SRem(base(), Arrays.asList(src, member)).remove(); 31 | 32 | if (result > 0 && !getSetFromBaseOrCreateEmpty(dest).getStoredData().contains(member)) { 33 | new SAdd(base(), Arrays.asList(dest, member)).execute(); 34 | } 35 | return Response.integer(result); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/bitmaps/SetBit.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.bitmaps; 2 | 3 | import com.github.fppt.jedismock.datastructures.RMBitMap; 4 | import com.github.fppt.jedismock.operations.AbstractRedisOperation; 5 | import com.github.fppt.jedismock.operations.RedisCommand; 6 | import com.github.fppt.jedismock.server.Response; 7 | import com.github.fppt.jedismock.datastructures.Slice; 8 | import com.github.fppt.jedismock.storage.RedisBase; 9 | 10 | import java.util.List; 11 | 12 | import static com.github.fppt.jedismock.Utils.convertToByte; 13 | import static com.github.fppt.jedismock.Utils.convertToNonNegativeInteger; 14 | 15 | @RedisCommand("setbit") 16 | class SetBit extends AbstractRedisOperation { 17 | SetBit(RedisBase base, List params) { 18 | super(base, params); 19 | } 20 | 21 | protected Slice response() { 22 | Slice key = params().get(0); 23 | RMBitMap value = base().getBitMap(key); 24 | byte bit = convertToByte(params().get(2).toString()); 25 | int pos = convertToNonNegativeInteger(params().get(1).toString()); 26 | 27 | if (value == null) { 28 | RMBitMap bitMap = new RMBitMap(); 29 | bitMap.setBit(bit, pos); 30 | base().putValue(key, bitMap); 31 | 32 | return Response.integer(0); 33 | } 34 | 35 | boolean res = value.getBit(pos); 36 | value.setBit(bit, pos); 37 | base().putValue(key, value); 38 | return Response.integer(res ? 1 : 0); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/sortedsets/ZScan.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.sortedsets; 2 | 3 | import com.github.fppt.jedismock.datastructures.RMZSet; 4 | import com.github.fppt.jedismock.datastructures.Slice; 5 | import com.github.fppt.jedismock.operations.RedisCommand; 6 | import com.github.fppt.jedismock.operations.keys.Scan; 7 | import com.github.fppt.jedismock.server.Response; 8 | import com.github.fppt.jedismock.storage.RedisBase; 9 | 10 | import java.util.stream.Collectors; 11 | import java.util.stream.Stream; 12 | import java.util.List; 13 | 14 | @RedisCommand("zscan") 15 | class ZScan extends Scan { 16 | private Slice keySlice; 17 | ZScan(RedisBase base, List params) { 18 | super(base, params); 19 | } 20 | 21 | @Override 22 | protected void doOptionalWork() { 23 | this.keySlice = params().get(0); 24 | this.cursorSlice = params().get(1); 25 | } 26 | 27 | @Override 28 | protected List getMatchingValues(String regex, long cursor, long count) { 29 | RMZSet mapDBObj = getZSetFromBaseOrCreateEmpty(keySlice); 30 | return mapDBObj.entries(false).stream() 31 | .skip(cursor) 32 | .limit(count) 33 | .filter(e -> e.getValue().toString().matches(regex)) 34 | .flatMap(e -> Stream.of(e.getValue(), 35 | Slice.create(String.valueOf(e.getScore())))) 36 | .map(Response::bulkString) 37 | .collect(Collectors.toList()); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/sortedsets/ZIncrBy.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.sortedsets; 2 | 3 | import com.github.fppt.jedismock.datastructures.RMZSet; 4 | import com.github.fppt.jedismock.datastructures.Slice; 5 | import com.github.fppt.jedismock.operations.RedisCommand; 6 | import com.github.fppt.jedismock.server.Response; 7 | import com.github.fppt.jedismock.storage.RedisBase; 8 | 9 | import java.util.List; 10 | 11 | @RedisCommand("zincrby") 12 | public class ZIncrBy extends AbstractByScoreOperation { 13 | public ZIncrBy(RedisBase base, List params) { 14 | super(base, params); 15 | } 16 | 17 | @Override 18 | protected int minArgs() { 19 | return 3; 20 | } 21 | 22 | @Override 23 | protected int maxArgs() { 24 | return 3; 25 | } 26 | 27 | @Override 28 | protected Slice response() { 29 | return Response.bulkString(Slice.create(String.valueOf(getNewScore()))); 30 | } 31 | 32 | protected double getNewScore() { 33 | Slice key = params().get(0); 34 | String increment = params().get(1).toString(); 35 | Slice member = params().get(2); 36 | final RMZSet mapDBObj = getZSetFromBaseOrCreateEmpty(key); 37 | double score = (mapDBObj.getScore(member) == null) ? 0d : 38 | mapDBObj.getScore(member); 39 | 40 | double newScore = getSum(score, increment); 41 | 42 | mapDBObj.put(member, newScore); 43 | base().putValue(key, mapDBObj); 44 | return newScore; 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/test/java/com/github/fppt/jedismock/comparisontests/sortedsets/TestZRank.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.comparisontests.sortedsets; 2 | 3 | import com.github.fppt.jedismock.comparisontests.ComparisonBase; 4 | import org.junit.jupiter.api.BeforeEach; 5 | import org.junit.jupiter.api.TestTemplate; 6 | import org.junit.jupiter.api.extension.ExtendWith; 7 | import redis.clients.jedis.Jedis; 8 | 9 | import static org.assertj.core.api.Assertions.assertThat; 10 | 11 | @ExtendWith(ComparisonBase.class) 12 | public class TestZRank { 13 | 14 | private static final String ZSET_KEY = "myzset"; 15 | 16 | @BeforeEach 17 | public void setUp(Jedis jedis) { 18 | jedis.flushDB(); 19 | } 20 | 21 | @TestTemplate 22 | public void testZRankGetFirstRank(Jedis jedis) { 23 | jedis.zadd(ZSET_KEY, 2, "bbb"); 24 | jedis.zadd(ZSET_KEY, 3, "ccc"); 25 | jedis.zadd(ZSET_KEY, 1, "aaa"); 26 | jedis.zadd(ZSET_KEY, 5, "yyy"); 27 | jedis.zadd(ZSET_KEY, 4, "xxx"); 28 | 29 | assertThat(jedis.zrank(ZSET_KEY, "aaa")).isEqualTo(0); 30 | } 31 | 32 | @TestTemplate 33 | public void testZRankGetAllRanks(Jedis jedis) { 34 | jedis.zadd(ZSET_KEY, 10, "x"); 35 | jedis.zadd(ZSET_KEY, 20, "y"); 36 | jedis.zadd(ZSET_KEY, 30, "z"); 37 | 38 | assertThat(jedis.zrank(ZSET_KEY, "x")).isEqualTo(0); 39 | assertThat(jedis.zrank(ZSET_KEY, "y")).isEqualTo(1); 40 | assertThat(jedis.zrank(ZSET_KEY, "z")).isEqualTo(2); 41 | 42 | assertThat(jedis.zrank(ZSET_KEY, "foo")).isNull(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/keys/Rename.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.keys; 2 | 3 | import com.github.fppt.jedismock.datastructures.RMDataStructure; 4 | import com.github.fppt.jedismock.datastructures.Slice; 5 | import com.github.fppt.jedismock.operations.AbstractRedisOperation; 6 | import com.github.fppt.jedismock.operations.RedisCommand; 7 | import com.github.fppt.jedismock.server.Response; 8 | import com.github.fppt.jedismock.storage.OperationExecutorState; 9 | 10 | import java.util.List; 11 | 12 | @RedisCommand("rename") 13 | class Rename extends AbstractRedisOperation { 14 | 15 | private final Object lock; 16 | 17 | Rename(OperationExecutorState state, List params) { 18 | super(state.base(), params); 19 | this.lock = state.lock(); 20 | } 21 | 22 | private boolean rename(Slice key, Slice newKey) { 23 | RMDataStructure value = base().getValue(key); 24 | final Long ttl = base().getTTL(key); 25 | if (ttl == null || value == null) { 26 | return false; 27 | } 28 | base().deleteValue(newKey); 29 | base().putValue(newKey, value, ttl); 30 | base().deleteValue(key); 31 | lock.notifyAll(); 32 | 33 | return true; 34 | } 35 | 36 | @Override 37 | protected Slice response() { 38 | final Slice key = params().get(0); 39 | final Slice newKey = params().get(1); 40 | if (!rename(key, newKey)) { 41 | return Response.error("ERR no such key"); 42 | } 43 | return Response.OK; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/pubsub/PubSub.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.pubsub; 2 | 3 | import com.github.fppt.jedismock.Utils; 4 | import com.github.fppt.jedismock.datastructures.Slice; 5 | import com.github.fppt.jedismock.operations.AbstractRedisOperation; 6 | import com.github.fppt.jedismock.operations.RedisCommand; 7 | import com.github.fppt.jedismock.server.Response; 8 | import com.github.fppt.jedismock.storage.RedisBase; 9 | 10 | import java.util.List; 11 | import java.util.stream.Collectors; 12 | 13 | @RedisCommand(value = "pubsub", transactional = false) 14 | public class PubSub extends AbstractRedisOperation { 15 | PubSub(RedisBase base, List params) { 16 | super(base, params); 17 | } 18 | 19 | @Override 20 | protected Slice response() { 21 | Slice subcommand = params().get(0); 22 | if ("channels".equalsIgnoreCase(subcommand.toString())) { 23 | String pattern = 24 | Utils.createRegexFromGlob( 25 | params().size() > 1 ? params().get(1).toString() : "*"); 26 | return Response.array(base().getChannels().stream().filter( 27 | s -> s.toString().matches(pattern) 28 | ).map(Response::bulkString).collect(Collectors.toList())); 29 | } else if ("numpat".equalsIgnoreCase(subcommand.toString())) { 30 | return Response.integer(base().getNumpat()); 31 | } else { 32 | return Response.error(String.format("Unsupported operation: pubsub %s", subcommand)); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/scripting/cjson/ImmutableLuaTable.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.scripting.cjson; 2 | 3 | import org.luaj.vm2.LuaTable; 4 | import org.luaj.vm2.LuaValue; 5 | 6 | import java.util.Map; 7 | 8 | class ImmutableLuaTable extends LuaTable { 9 | 10 | private static final String ATTEMPT_TO_MODIFY_A_READONLY_TABLE = "Attempt to modify a readonly table"; 11 | 12 | ImmutableLuaTable(Map entries) { 13 | super(); 14 | entries.forEach(super::hashset); 15 | } 16 | 17 | @Override 18 | public void set(String key, LuaValue value) { 19 | throw new UnsupportedOperationException(ATTEMPT_TO_MODIFY_A_READONLY_TABLE); 20 | } 21 | 22 | @Override 23 | public void set(int key, LuaValue value) { 24 | throw new UnsupportedOperationException(ATTEMPT_TO_MODIFY_A_READONLY_TABLE); 25 | } 26 | 27 | @Override 28 | public void set(LuaValue key, LuaValue value) { 29 | throw new UnsupportedOperationException(ATTEMPT_TO_MODIFY_A_READONLY_TABLE); 30 | } 31 | 32 | @Override 33 | public void hashset(LuaValue key, LuaValue value) { 34 | throw new UnsupportedOperationException(ATTEMPT_TO_MODIFY_A_READONLY_TABLE); 35 | } 36 | 37 | @Override 38 | public void rawset(LuaValue key, LuaValue value) { 39 | throw new UnsupportedOperationException(ATTEMPT_TO_MODIFY_A_READONLY_TABLE); 40 | } 41 | 42 | @Override 43 | public void rawset(int key, LuaValue value) { 44 | throw new UnsupportedOperationException(ATTEMPT_TO_MODIFY_A_READONLY_TABLE); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/sortedsets/ZScore.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.sortedsets; 2 | 3 | import com.github.fppt.jedismock.datastructures.RMZSet; 4 | import com.github.fppt.jedismock.operations.AbstractRedisOperation; 5 | import com.github.fppt.jedismock.operations.RedisCommand; 6 | import com.github.fppt.jedismock.server.Response; 7 | import com.github.fppt.jedismock.datastructures.Slice; 8 | import com.github.fppt.jedismock.storage.RedisBase; 9 | 10 | import java.util.List; 11 | 12 | @RedisCommand("zscore") 13 | class ZScore extends AbstractRedisOperation { 14 | 15 | private static final double DELTA = 1e-6; 16 | 17 | ZScore(RedisBase base, List params) { 18 | super(base, params); 19 | } 20 | 21 | @Override 22 | protected Slice response() { 23 | Slice key = params().get(0); 24 | Slice val = params().get(1); 25 | 26 | final RMZSet mapDBObj = getZSetFromBaseOrCreateEmpty(key); 27 | 28 | Double score = mapDBObj.getScore(val); 29 | 30 | if (score == null) { 31 | return Response.NULL; 32 | } 33 | if (score.isInfinite()) { 34 | return score > 0 ? Response.bulkString(Slice.create("inf")) 35 | : Response.bulkString(Slice.create("-inf")); 36 | } 37 | long round = Math.round(score); 38 | if (Math.abs(score - round) < DELTA) { 39 | return Response.bulkString(Slice.create(String.format("%.0f", score))); 40 | } 41 | return Response.bulkString(Slice.create(String.format("%10.16e", score).replace(',','.'))); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/sets/SMIsMember.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.sets; 2 | 3 | import com.github.fppt.jedismock.datastructures.RMSet; 4 | import com.github.fppt.jedismock.datastructures.Slice; 5 | import com.github.fppt.jedismock.operations.AbstractRedisOperation; 6 | import com.github.fppt.jedismock.operations.RedisCommand; 7 | import com.github.fppt.jedismock.server.Response; 8 | import com.github.fppt.jedismock.storage.RedisBase; 9 | 10 | import java.util.List; 11 | import java.util.Set; 12 | import java.util.stream.Collectors; 13 | import java.util.stream.Stream; 14 | 15 | @RedisCommand("smismember") 16 | public class SMIsMember extends AbstractRedisOperation { 17 | SMIsMember(RedisBase base, List params) { 18 | super(base, params); 19 | } 20 | 21 | @Override 22 | protected int minArgs() { 23 | return 2; 24 | } 25 | 26 | @Override 27 | protected Slice response() { 28 | Slice key = params().get(0); 29 | Stream paramStream = params().subList(1, params().size()).stream(); 30 | RMSet setDBObj = getSetFromBaseOrCreateEmpty(key); 31 | Set set = setDBObj.getStoredData(); 32 | if (set == null || set.isEmpty()) { 33 | return Response.array(paramStream.map(el -> Response.integer(0)).collect(Collectors.toList())); 34 | } 35 | 36 | return Response.array( 37 | paramStream 38 | .map(el -> set.contains(el) ? Response.integer(1) : Response.integer(0)) 39 | .collect(Collectors.toList()) 40 | ); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/hashes/HSet.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.hashes; 2 | 3 | import com.github.fppt.jedismock.operations.AbstractRedisOperation; 4 | import com.github.fppt.jedismock.operations.RedisCommand; 5 | import com.github.fppt.jedismock.server.Response; 6 | import com.github.fppt.jedismock.datastructures.Slice; 7 | import com.github.fppt.jedismock.storage.RedisBase; 8 | 9 | import java.util.List; 10 | 11 | @RedisCommand("hset") 12 | class HSet extends AbstractRedisOperation { 13 | HSet(RedisBase base, List params) { 14 | super(base, params); 15 | } 16 | 17 | Slice hsetValue(Slice key1, Slice key2, Slice value) { 18 | Slice foundValue = base().getSlice(key1, key2); 19 | base().putSlice(key1, key2, value, null); 20 | return foundValue; 21 | } 22 | 23 | @Override 24 | protected Slice response() { 25 | Slice hash = params().get(0); 26 | int count = 0; 27 | 28 | if (params().size() % 2 == 0){ 29 | // throw exception before doing anything if wrong number of args has been recieved 30 | throw new IllegalArgumentException("Recieved wrong number of arguments when executing command HSET"); 31 | } 32 | 33 | for(int i = 1; i < params().size(); i = i + 2){ 34 | Slice field = params().get(i); 35 | Slice value = params().get(i+1); 36 | Slice oldValue = hsetValue(hash, field, value); 37 | if (oldValue == null) { 38 | count++; 39 | } 40 | } 41 | 42 | return Response.integer(count); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/keys/paramsparser/ExpirationFieldsParam.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.keys.paramsparser; 2 | 3 | import com.github.fppt.jedismock.datastructures.Slice; 4 | 5 | import java.util.Collections; 6 | import java.util.List; 7 | 8 | public final class ExpirationFieldsParam { 9 | private final List fields; 10 | 11 | public ExpirationFieldsParam(List params, int position) throws ExpirationParamsException { 12 | if (!"FIELDS".equalsIgnoreCase(params.get(position).toString())) { 13 | throw new ExpirationParamsException("ERR Mandatory argument FIELDS is missing " + 14 | "or not at the right position"); 15 | } 16 | long numFields = 0; 17 | try { 18 | numFields = Long.parseLong(new String(params.get(position + 1).data())); 19 | } catch (NumberFormatException e) { 20 | // do nothing. Even when the number format is wrong, "real" Redis says 21 | // "numFields should be > 0". 22 | } 23 | if (numFields <= 0) { 24 | throw new ExpirationParamsException("ERR Parameter `numFields` should be greater than 0"); 25 | } 26 | if (params.size() - position - 2 != numFields) { 27 | throw new ExpirationParamsException("ERR The `numfields` parameter must match the number of arguments"); 28 | } 29 | fields = Collections.unmodifiableList(params.subList(position + 2, params.size())); 30 | } 31 | 32 | public List getFields() { 33 | return fields; 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/lists/LRange.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.lists; 2 | 3 | import com.github.fppt.jedismock.datastructures.RMList; 4 | import com.github.fppt.jedismock.datastructures.Slice; 5 | import com.github.fppt.jedismock.operations.AbstractRedisOperation; 6 | import com.github.fppt.jedismock.operations.RedisCommand; 7 | import com.github.fppt.jedismock.server.Response; 8 | import com.github.fppt.jedismock.storage.RedisBase; 9 | 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | 13 | import static com.github.fppt.jedismock.Utils.convertToInteger; 14 | 15 | @RedisCommand("lrange") 16 | class LRange extends AbstractRedisOperation { 17 | LRange(RedisBase base, List params) { 18 | super(base, params); 19 | } 20 | 21 | protected Slice response() { 22 | Slice key = params().get(0); 23 | RMList listDBObj = getListFromBaseOrCreateEmpty(key); 24 | List list = listDBObj.getStoredData(); 25 | 26 | int start = convertToInteger(params().get(1).toString()); 27 | int end = convertToInteger(params().get(2).toString()); 28 | 29 | if (start < 0) { 30 | start = list.size() + start; 31 | if (start < 0) { 32 | start = 0; 33 | } 34 | } 35 | if (end < 0) { 36 | end = list.size() + end; 37 | } 38 | List result = new ArrayList<>(); 39 | for (int i = start; i <= end && i < list.size(); i++) { 40 | result.add(Response.bulkString(list.get(i))); 41 | } 42 | return Response.array(result); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/sortedsets/ZRangeByLex.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.sortedsets; 2 | 3 | import com.github.fppt.jedismock.datastructures.Slice; 4 | import com.github.fppt.jedismock.exception.ArgumentException; 5 | import com.github.fppt.jedismock.operations.RedisCommand; 6 | import com.github.fppt.jedismock.storage.RedisBase; 7 | 8 | import java.util.List; 9 | 10 | import static com.github.fppt.jedismock.operations.sortedsets.AbstractZRange.Options.BYSCORE; 11 | import static com.github.fppt.jedismock.operations.sortedsets.AbstractZRange.Options.REV; 12 | import static com.github.fppt.jedismock.operations.sortedsets.AbstractZRange.Options.WITHSCORES; 13 | 14 | @RedisCommand("zrangebylex") 15 | class ZRangeByLex extends AbstractZRangeByLex { 16 | 17 | ZRangeByLex(RedisBase base, List params) { 18 | super(base, params); 19 | } 20 | 21 | @Override 22 | protected Slice response() { 23 | if (options.contains(BYSCORE) || options.contains(REV) || options.contains(WITHSCORES)) { 24 | throw new ArgumentException("*syntax*"); 25 | } 26 | key = params().get(0); 27 | mapDBObj = getZSetFromBaseOrCreateEmpty(key); 28 | 29 | final Slice start = params().get(1); 30 | final Slice end = params().get(2); 31 | if (invalidateStart(start.toString())) { 32 | return buildErrorResponse("start"); 33 | } 34 | if (invalidateEnd(end.toString())) { 35 | return buildErrorResponse("end"); 36 | } 37 | return getSliceFromRange(getRange(getStartBound(start), getEndBound(end))); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/test/java/com/github/fppt/jedismock/comparisontests/keys/TestPersist.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.comparisontests.keys; 2 | 3 | import com.github.fppt.jedismock.comparisontests.ComparisonBase; 4 | import org.junit.jupiter.api.BeforeEach; 5 | import org.junit.jupiter.api.TestTemplate; 6 | import org.junit.jupiter.api.extension.ExtendWith; 7 | import redis.clients.jedis.Jedis; 8 | 9 | import static org.assertj.core.api.Assertions.assertThat; 10 | 11 | @ExtendWith(ComparisonBase.class) 12 | public class TestPersist { 13 | @BeforeEach 14 | public void setUp(Jedis jedis) { 15 | jedis.flushDB(); 16 | } 17 | 18 | @TestTemplate 19 | public void testPersistExistingWithTTL(Jedis jedis) throws Exception { 20 | jedis.psetex("a", 300, "v"); 21 | assertThat(jedis.ttl("a")).isLessThanOrEqualTo(300); 22 | assertThat(jedis.persist("a")).isEqualTo(1); 23 | assertThat(jedis.ttl("a")).isEqualTo(-1); 24 | Thread.sleep(500); 25 | //Check that the value is still there 26 | assertThat(jedis.get("a")).isEqualTo("v"); 27 | } 28 | 29 | @TestTemplate 30 | public void testPersistExistingWithoutTTL(Jedis jedis) { 31 | jedis.set("key", "value"); 32 | assertThat(jedis.persist("key")).isEqualTo(0); 33 | assertThat(jedis.ttl("key")).isEqualTo(-1); 34 | assertThat(jedis.get("key")).isEqualTo("value"); 35 | } 36 | 37 | @TestTemplate 38 | public void testPersistNotExisting(Jedis jedis) { 39 | assertThat(jedis.persist("foo")).isEqualTo(0); 40 | assertThat(jedis.ttl("foo")).isEqualTo(-2); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/sortedsets/ZRevRangeByLex.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.sortedsets; 2 | 3 | import com.github.fppt.jedismock.datastructures.Slice; 4 | import com.github.fppt.jedismock.exception.ArgumentException; 5 | import com.github.fppt.jedismock.operations.RedisCommand; 6 | import com.github.fppt.jedismock.storage.RedisBase; 7 | 8 | import java.util.List; 9 | 10 | import static com.github.fppt.jedismock.operations.sortedsets.AbstractZRange.Options.BYSCORE; 11 | import static com.github.fppt.jedismock.operations.sortedsets.AbstractZRange.Options.REV; 12 | import static com.github.fppt.jedismock.operations.sortedsets.AbstractZRange.Options.WITHSCORES; 13 | 14 | @RedisCommand("zrevrangebylex") 15 | class ZRevRangeByLex extends AbstractZRangeByLex { 16 | 17 | ZRevRangeByLex(RedisBase base, List params) { 18 | super(base, params); 19 | } 20 | 21 | @Override 22 | protected Slice response() { 23 | if (options.contains(BYSCORE) || options.contains(WITHSCORES)) { 24 | throw new ArgumentException("*syntax*"); 25 | } 26 | key = params().get(0); 27 | mapDBObj = getZSetFromBaseOrCreateEmpty(key); 28 | 29 | final Slice start = params().get(2); 30 | final Slice end = params().get(1); 31 | options.add(REV); 32 | if (invalidateStart(start.toString())) { 33 | return buildErrorResponse("start"); 34 | } 35 | if (invalidateEnd(end.toString())) { 36 | return buildErrorResponse("end"); 37 | } 38 | return getSliceFromRange(getRange(getStartBound(start), getEndBound(end))); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/test/java/com/github/fppt/jedismock/operations/keys/paramsparser/ExpirationTimeParamTest.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.keys.paramsparser; 2 | 3 | import com.github.fppt.jedismock.datastructures.Slice; 4 | import org.junit.jupiter.api.Test; 5 | 6 | import static org.assertj.core.api.Assertions.assertThat; 7 | import static org.assertj.core.api.Assertions.assertThatThrownBy; 8 | 9 | class ExpirationTimeParamTest { 10 | @Test 11 | void normalSeconds() throws ExpirationParamsException { 12 | ExpirationTimeParam param = new ExpirationTimeParam(null, 13 | Slice.create("42"), false, 0); 14 | assertThat(param.getMillis()).isEqualTo(42000); 15 | } 16 | 17 | @Test 18 | void normalMillis() throws ExpirationParamsException { 19 | ExpirationTimeParam param = new ExpirationTimeParam(null, 20 | Slice.create("42"), true, 0); 21 | assertThat(param.getMillis()).isEqualTo(42); 22 | } 23 | 24 | @Test 25 | void notAnInteger() { 26 | assertThatThrownBy(() -> new ExpirationTimeParam 27 | (null, Slice.create("foo"), false, 0)) 28 | .isInstanceOf(ExpirationParamsException.class) 29 | .hasMessageContaining("not an integer"); 30 | } 31 | 32 | @Test 33 | void overflow() { 34 | assertThatThrownBy(() -> new ExpirationTimeParam 35 | ("cmd", Slice.create(Long.toString(Long.MAX_VALUE)), false, 10)) 36 | .isInstanceOf(ExpirationParamsException.class) 37 | .hasMessageContaining("cmd") 38 | .hasMessageContaining("invalid expire time"); 39 | 40 | } 41 | } -------------------------------------------------------------------------------- /src/test/java/com/github/fppt/jedismock/comparisontests/sortedsets/TestZMScore.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.comparisontests.sortedsets; 2 | 3 | import com.github.fppt.jedismock.comparisontests.ComparisonBase; 4 | import org.junit.jupiter.api.BeforeEach; 5 | import org.junit.jupiter.api.TestTemplate; 6 | import org.junit.jupiter.api.extension.ExtendWith; 7 | import redis.clients.jedis.Jedis; 8 | 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | 12 | import static org.assertj.core.api.Assertions.assertThat; 13 | 14 | @ExtendWith(ComparisonBase.class) 15 | public class TestZMScore { 16 | 17 | private static final String ZSET_KEY = "myzset"; 18 | 19 | @BeforeEach 20 | public void setUp(Jedis jedis) { 21 | jedis.flushAll(); 22 | } 23 | 24 | @TestTemplate 25 | public void testZScoreNotExistKey(Jedis jedis) { 26 | List expected = new ArrayList<>(); 27 | expected.add(null); 28 | assertThat(jedis.zmscore(ZSET_KEY, "a")).isEqualTo(expected); 29 | } 30 | 31 | @TestTemplate 32 | public void testZScoreNotExistMember(Jedis jedis) { 33 | jedis.zadd(ZSET_KEY, 1, "a"); 34 | jedis.zadd(ZSET_KEY, 2, "b"); 35 | List expected = new ArrayList<>(); 36 | expected.add(1.0); 37 | expected.add(null); 38 | expected.add(2.0); 39 | assertThat(jedis.zmscore(ZSET_KEY, "a", "c", "b")).isEqualTo(expected); 40 | } 41 | 42 | @TestTemplate 43 | public void testZScoreOK(Jedis jedis) { 44 | jedis.zadd(ZSET_KEY, 2, "a"); 45 | List expected = new ArrayList<>(); 46 | expected.add(2.0); 47 | assertThat(jedis.zmscore(ZSET_KEY, "a")).isEqualTo(expected); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/test/java/com/github/fppt/jedismock/comparisontests/keys/TestType.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.comparisontests.keys; 2 | 3 | import com.github.fppt.jedismock.comparisontests.ComparisonBase; 4 | import org.junit.jupiter.api.BeforeEach; 5 | import org.junit.jupiter.api.TestTemplate; 6 | import org.junit.jupiter.api.extension.ExtendWith; 7 | import redis.clients.jedis.Jedis; 8 | 9 | import static org.assertj.core.api.Assertions.assertThat; 10 | 11 | @ExtendWith(ComparisonBase.class) 12 | public class TestType { 13 | @BeforeEach 14 | void setUp(Jedis jedis) { 15 | jedis.flushDB(); 16 | jedis.set("key", "string"); 17 | jedis.lpush("lkey", "value1", "value2"); 18 | jedis.sadd("skey", "val1", "val2"); 19 | jedis.zadd("zkey", 1, "foo"); 20 | jedis.hset("hkey", "k", "v"); 21 | jedis.setbit("bitmap", 22, true); 22 | jedis.pfadd("hll", "foo"); 23 | } 24 | 25 | @TestTemplate 26 | void type(Jedis jedis) { 27 | assertThat(jedis.type("not.exists")).isEqualTo("none"); 28 | assertThat(jedis.type("key")).isEqualTo("string"); 29 | assertThat(jedis.type("lkey")).isEqualTo("list"); 30 | assertThat(jedis.type("skey")).isEqualTo("set"); 31 | assertThat(jedis.type("zkey")).isEqualTo("zset"); 32 | assertThat(jedis.type("hkey")).isEqualTo("hash"); 33 | assertThat(jedis.type("bitmap")).isEqualTo("string"); 34 | assertThat(jedis.type("hll")).isEqualTo("string"); 35 | } 36 | 37 | @TestTemplate 38 | void typeRespectsTTL(Jedis jedis) throws InterruptedException { 39 | jedis.pexpire("key", 50); 40 | Thread.sleep(100); 41 | assertThat(jedis.type("key")).isEqualTo("none"); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/lists/LInsert.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.lists; 2 | 3 | import com.github.fppt.jedismock.datastructures.Slice; 4 | import com.github.fppt.jedismock.operations.AbstractRedisOperation; 5 | import com.github.fppt.jedismock.operations.RedisCommand; 6 | import com.github.fppt.jedismock.server.Response; 7 | import com.github.fppt.jedismock.storage.RedisBase; 8 | 9 | import java.util.Arrays; 10 | import java.util.List; 11 | 12 | @RedisCommand("linsert") 13 | public class LInsert extends AbstractRedisOperation { 14 | private static final String BEFORE = "BEFORE"; 15 | private static final String AFTER = "AFTER"; 16 | 17 | public LInsert(RedisBase base, List params) { 18 | super(base, params); 19 | } 20 | 21 | @Override 22 | protected Slice response() { 23 | Slice key = params().get(0); 24 | String direction = params().get(1).toString(); 25 | 26 | if (!Arrays.asList(BEFORE, AFTER).contains(direction.toUpperCase())) { 27 | throw new IllegalArgumentException("ERR syntax error"); 28 | } 29 | 30 | Slice pivot = params().get(2); 31 | Slice element = params().get(3); 32 | 33 | if (!base().exists(key)) { 34 | return Response.integer(0); 35 | } 36 | 37 | List storedElements = base().getList(key).getStoredData(); 38 | int i = storedElements.indexOf(pivot); 39 | 40 | if (i == -1) { 41 | return Response.integer(-1); 42 | } 43 | 44 | int index = direction.equalsIgnoreCase(BEFORE) ? i : i + 1; 45 | storedElements.add(index, element); 46 | return Response.integer(storedElements.size()); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/test/java/com/github/fppt/jedismock/comparisontests/strings/TestGetDel.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.comparisontests.strings; 2 | 3 | import com.github.fppt.jedismock.comparisontests.ComparisonBase; 4 | import org.junit.jupiter.api.BeforeEach; 5 | import org.junit.jupiter.api.TestTemplate; 6 | import org.junit.jupiter.api.extension.ExtendWith; 7 | import redis.clients.jedis.Jedis; 8 | import redis.clients.jedis.exceptions.JedisDataException; 9 | 10 | import static org.assertj.core.api.Assertions.assertThat; 11 | import static org.assertj.core.api.Assertions.assertThatThrownBy; 12 | 13 | @ExtendWith(ComparisonBase.class) 14 | public class TestGetDel { 15 | 16 | String key = "key"; 17 | String value = "value"; 18 | 19 | @BeforeEach 20 | public void setUp(Jedis jedis) { 21 | jedis.flushAll(); 22 | } 23 | 24 | @TestTemplate 25 | public void testGetAndDel(Jedis jedis) { 26 | jedis.set(key, value); 27 | String deletedValue = jedis.getDel(key); 28 | assertThat(deletedValue).isEqualTo(value); 29 | assertThat(jedis.exists(key)).isFalse(); 30 | } 31 | 32 | @TestTemplate 33 | public void testGetAndDelNonExistKey(Jedis jedis) { 34 | String deletedValue = jedis.getDel(key); 35 | assertThat(deletedValue).isNull(); 36 | assertThat(jedis.exists(key)).isFalse(); 37 | } 38 | 39 | @TestTemplate 40 | public void getAndDelNonStringKey(Jedis jedis) { 41 | jedis.hset(key, "foo", "bar"); 42 | assertThatThrownBy(() -> jedis.getDel(key)) 43 | .isInstanceOf(JedisDataException.class) 44 | .hasMessageStartingWith("WRONGTYPE"); 45 | assertThat(jedis.hget(key, "foo")).isEqualTo("bar"); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/test/java/com/github/fppt/jedismock/comparisontests/keys/MoveTests.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.comparisontests.keys; 2 | 3 | import com.github.fppt.jedismock.comparisontests.ComparisonBase; 4 | import org.junit.jupiter.api.BeforeEach; 5 | import org.junit.jupiter.api.TestTemplate; 6 | import org.junit.jupiter.api.extension.ExtendWith; 7 | import redis.clients.jedis.Jedis; 8 | 9 | import static org.assertj.core.api.Assertions.assertThat; 10 | 11 | @ExtendWith(ComparisonBase.class) 12 | public class MoveTests { 13 | private final String srcKey = "first"; 14 | private final int dstDb = 11; 15 | private final String val = "abracadabra"; 16 | 17 | 18 | @BeforeEach 19 | public void setUp(Jedis jedis) { 20 | jedis.flushAll(); 21 | } 22 | 23 | @TestTemplate 24 | public void successfulMove(Jedis jedis) { 25 | jedis.setex(srcKey, 100, val); 26 | long expireTime = jedis.pexpireTime(srcKey); 27 | assertThat(jedis.move(srcKey, dstDb)).isEqualTo(1); 28 | jedis.select(dstDb); 29 | assertThat(jedis.get(srcKey)).isEqualTo(val); 30 | assertThat(jedis.pexpireTime(srcKey)).isEqualTo(expireTime); 31 | } 32 | 33 | @TestTemplate 34 | public void copyOfNonExistent(Jedis jedis) { 35 | assertThat(jedis.move("nonexistent", dstDb)).isZero(); 36 | } 37 | 38 | @TestTemplate 39 | public void unsuccessfulMove(Jedis jedis) { 40 | jedis.select(0); 41 | jedis.set(srcKey, val); 42 | jedis.select(dstDb); 43 | jedis.set(srcKey, "oldValue"); 44 | jedis.select(0); 45 | assertThat(jedis.move(srcKey, dstDb)).isZero(); 46 | jedis.select(dstDb); 47 | assertThat(jedis.get(srcKey)).isEqualTo("oldValue"); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/com/github/fppt/jedismock/operations/sortedsets/AbstractZRangeByScore.java: -------------------------------------------------------------------------------- 1 | package com.github.fppt.jedismock.operations.sortedsets; 2 | 3 | import com.github.fppt.jedismock.datastructures.Slice; 4 | import com.github.fppt.jedismock.datastructures.ZSetEntry; 5 | import com.github.fppt.jedismock.datastructures.ZSetEntryBound; 6 | import com.github.fppt.jedismock.storage.RedisBase; 7 | 8 | import java.util.List; 9 | 10 | abstract class AbstractZRangeByScore extends AbstractZRange { 11 | 12 | AbstractZRangeByScore(RedisBase base, List params) { 13 | super(base, params); 14 | } 15 | 16 | @Override 17 | public final ZSetEntryBound getStartBound(Slice startSlice) { 18 | String start = startSlice.toString(); 19 | if (LOWEST_POSSIBLE_SCORE.equalsIgnoreCase(start)) { 20 | return ZSetEntryBound.MINUS_INF; 21 | } else if (start.startsWith(EXCLUSIVE_PREFIX)) { 22 | return new ZSetEntryBound(toDouble(start.substring(1)), ZSetEntry.MAX_VALUE, false); 23 | } else { 24 | return new ZSetEntryBound(toDouble(start), ZSetEntry.MIN_VALUE, true); 25 | } 26 | } 27 | 28 | @Override 29 | public final ZSetEntryBound getEndBound(Slice endSlice) { 30 | String end = endSlice.toString(); 31 | if (HIGHEST_POSSIBLE_SCORE.equalsIgnoreCase(end)) { 32 | return new ZSetEntryBound(Double.POSITIVE_INFINITY, ZSetEntry.MAX_VALUE, end.startsWith(EXCLUSIVE_PREFIX)); 33 | } else if (end.startsWith(EXCLUSIVE_PREFIX)) { 34 | return new ZSetEntryBound(toDouble(end.substring(1)), ZSetEntry.MIN_VALUE, false); 35 | } else { 36 | return new ZSetEntryBound(toDouble(end), ZSetEntry.MAX_VALUE, true); 37 | } 38 | } 39 | 40 | } 41 | --------------------------------------------------------------------------------