-
- {
- site.featureHighlights.map((s) => (
-
-
-
{s.title}
-
{s.description}
-
-
- ))
- }
-
+
+
DiceDB is
+
+
+ Redis-compliant - a drop-in (36.4% )
+ replacement of Redis (here's why )
+
+
+ Scalable - scales out and scales in with load *
+
+
+ Highly available - failovers and durability *
+
+
+ Unified cache - multi-tenant and shared everything architecture *
+
+
+ Optimized for modern hardware - multi-threaded with shared-nothing
+ architecture *
+
+
diff --git a/docs/src/pages/redis-compatability.astro b/docs/src/pages/redis-compatability.astro
new file mode 100644
index 000000000..ff62bb602
--- /dev/null
+++ b/docs/src/pages/redis-compatability.astro
@@ -0,0 +1,1424 @@
+---
+import Layout from "../layouts/Layout.astro";
+const title = "DiceDB's Redis Compatability";
+const description = "";
+---
+
+
+
+
+
+
36.4%
+
Redis 7.4.1 compatability
+
+
+
+
+
+
+
+
+ Supported Commands
+ Unsupported Commands
+ New commands in DiceDB
+
+
+ APPEND
+ ACL
+ ABORT
+
+
+ AUTH
+ ACL|CAT
+ BF.ADD
+
+
+ BGREWRITEAOF
+ ACL|DELUSER
+ BF.EXISTS
+
+
+ BITCOUNT
+ ACL|DRYRUN
+ BF.INFO
+
+
+ BITFIELD
+ ACL|GENPASS
+ BF.RESERVE
+
+
+ BITFIELD_RO
+ ACL|GETUSER
+ CMS.INCRBY
+
+
+ BITOP
+ ACL|HELP
+ CMS.INFO
+
+
+ BITPOS
+ ACL|LIST
+ CMS.INITBYDIM
+
+
+ CLIENT
+ ACL|LOAD
+ CMS.INITBYPROB
+
+
+ COMMAND
+ ACL|LOG
+ CMS.MERGE
+
+
+ COMMAND|COUNT
+ ACL|SAVE
+ CMS.QUERY
+
+
+ COMMAND|DOCS
+ ACL|SETUSER
+ JSON.ARRAPPEND
+
+
+ COMMAND|GETKEYS
+ ACL|USERS
+ JSON.ARRINSERT
+
+
+ COMMAND|GETKEYSANDFLAGS
+ ACL|WHOAMI
+ JSON.ARRLEN
+
+
+ COMMAND|HELP
+ ASKING
+ JSON.ARRPOP
+
+
+ COMMAND|INFO
+ BGSAVE
+ JSON.ARRTRIM
+
+
+ COMMAND|LIST
+ BLMOVE
+ JSON.CLEAR
+
+
+ COPY
+ BLMPOP
+ JSON.DEBUG
+
+
+ DBSIZE
+ BLPOP
+ JSON.DEL
+
+
+ DECR
+ BRPOP
+ JSON.FORGET
+
+
+ DECRBY
+ BRPOPLPUSH
+ JSON.GET
+
+
+ DEL
+ BZMPOP
+ JSON.INGEST
+
+
+ DISCARD
+ BZPOPMAX
+ JSON.MGET
+
+
+ DUMP
+ BZPOPMIN
+ JSON.NUMINCRBY
+
+
+ ECHO
+ CLIENT|CACHING
+ JSON.NUMMULTBY
+
+
+ EXEC
+ CLIENT|GETNAME
+ JSON.OBJKEYS
+
+
+ EXISTS
+ CLIENT|GETREDIR
+ JSON.OBJLEN
+
+
+ EXPIRE
+ CLIENT|HELP
+ JSON.RESP
+
+
+ EXPIREAT
+ CLIENT|ID
+ JSON.SET
+
+
+ EXPIRETIME
+ CLIENT|INFO
+ JSON.STRAPPEND
+
+
+ FLUSHDB
+ CLIENT|KILL
+ JSON.STRLEN
+
+
+ GEOADD
+ CLIENT|LIST
+ JSON.TOGGLE
+
+
+ GEODIST
+ CLIENT|NO-EVICT
+ JSON.TYPE
+
+
+ GET
+ CLIENT|NO-TOUCH
+ LRU
+
+
+ GETBIT
+ CLIENT|PAUSE
+ Q.UNWATCH
+
+
+ GETDEL
+ CLIENT|REPLY
+ Q.WATCH
+
+
+ GETEX
+ CLIENT|SETINFO
+ SLEEP
+
+
+ GETRANGE
+ CLIENT|SETNAME
+
+
+
+ GETSET
+ CLIENT|TRACKING
+
+
+
+ HDEL
+ CLIENT|TRACKINGINFO
+
+
+
+ HELLO
+ CLIENT|UNBLOCK
+
+
+
+ HEXISTS
+ CLIENT|UNPAUSE
+
+
+
+ HGET
+ CLUSTER
+
+
+
+ HGETALL
+ CLUSTER|ADDSLOTS
+
+
+
+ HINCRBY
+ CLUSTER|ADDSLOTSRANGE
+
+
+
+ HINCRBYFLOAT
+ CLUSTER|BUMPEPOCH
+
+
+
+ HKEYS
+ CLUSTER|COUNT-FAILURE-REPORTS
+
+
+
+ HLEN
+ CLUSTER|COUNTKEYSINSLOT
+
+
+
+ HMGET
+ CLUSTER|DELSLOTS
+
+
+
+ HMSET
+ CLUSTER|DELSLOTSRANGE
+
+
+
+ HRANDFIELD
+ CLUSTER|FAILOVER
+
+
+
+ HSCAN
+ CLUSTER|FLUSHSLOTS
+
+
+
+ HSET
+ CLUSTER|FORGET
+
+
+
+ HSETNX
+ CLUSTER|GETKEYSINSLOT
+
+
+
+ HSTRLEN
+ CLUSTER|HELP
+
+
+
+ HVALS
+ CLUSTER|INFO
+
+
+
+ INCR
+ CLUSTER|KEYSLOT
+
+
+
+ INCRBY
+ CLUSTER|LINKS
+
+
+
+ INCRBYFLOAT
+ CLUSTER|MEET
+
+
+
+ INFO
+ CLUSTER|MYID
+
+
+
+ KEYS
+ CLUSTER|MYSHARDID
+
+
+
+ LATENCY
+ CLUSTER|NODES
+
+
+
+ LLEN
+ CLUSTER|REPLICAS
+
+
+
+ LPOP
+ CLUSTER|REPLICATE
+
+
+
+ LPUSH
+ CLUSTER|RESET
+
+
+
+ MGET
+ CLUSTER|SAVECONFIG
+
+
+
+ MSET
+ CLUSTER|SET-CONFIG-EPOCH
+
+
+
+ MULTI
+ CLUSTER|SETSLOT
+
+
+
+ OBJECT
+ CLUSTER|SHARDS
+
+
+
+ PERSIST
+ CLUSTER|SLAVES
+
+
+
+ PFADD
+ CLUSTER|SLOTS
+
+
+
+ PFCOUNT
+ CONFIG
+
+
+
+ PFMERGE
+ CONFIG|GET
+
+
+
+ PING
+ CONFIG|HELP
+
+
+
+ PTTL
+ CONFIG|RESETSTAT
+
+
+
+ RENAME
+ CONFIG|REWRITE
+
+
+
+ RESTORE
+ CONFIG|SET
+
+
+
+ RPOP
+ DEBUG
+
+
+
+ RPUSH
+ EVAL
+
+
+
+ SADD
+ EVALSHA
+
+
+
+ SCARD
+ EVALSHA_RO
+
+
+
+ SDIFF
+ EVAL_RO
+
+
+
+ SELECT
+ FAILOVER
+
+
+
+ SET
+ FCALL
+
+
+
+ SETBIT
+ FCALL_RO
+
+
+
+ SETEX
+ FLUSHALL
+
+
+
+ SINTER
+ FUNCTION
+
+
+
+ SMEMBERS
+ FUNCTION|DELETE
+
+
+
+ SREM
+ FUNCTION|DUMP
+
+
+
+ SUBSCRIBE
+ FUNCTION|FLUSH
+
+
+
+ TOUCH
+ FUNCTION|HELP
+
+
+
+ TTL
+ FUNCTION|KILL
+
+
+
+ TYPE
+ FUNCTION|LIST
+
+
+
+ ZADD
+ FUNCTION|LOAD
+
+
+
+ ZCARD
+ FUNCTION|RESTORE
+
+
+
+ ZCOUNT
+ FUNCTION|STATS
+
+
+
+ ZPOPMAX
+ GEOHASH
+
+
+
+ ZPOPMIN
+ GEOPOS
+
+
+
+ ZRANGE
+ GEORADIUS
+
+
+
+ ZRANK
+ GEORADIUSBYMEMBER
+
+
+
+ ZREM
+ GEORADIUSBYMEMBER_RO
+
+
+
+
+ GEORADIUS_RO
+
+
+
+
+ GEOSEARCH
+
+
+
+
+ GEOSEARCHSTORE
+
+
+
+
+ HEXPIRE
+
+
+
+
+ HEXPIREAT
+
+
+
+
+ HEXPIRETIME
+
+
+
+
+ HPERSIST
+
+
+
+
+ HPEXPIRE
+
+
+
+
+ HPEXPIREAT
+
+
+
+
+ HPEXPIRETIME
+
+
+
+
+ HPTTL
+
+
+
+
+ HTTL
+
+
+
+
+ LASTSAVE
+
+
+
+
+ LATENCY|DOCTOR
+
+
+
+
+ LATENCY|GRAPH
+
+
+
+
+ LATENCY|HELP
+
+
+
+
+ LATENCY|HISTOGRAM
+
+
+
+
+ LATENCY|HISTORY
+
+
+
+
+ LATENCY|LATEST
+
+
+
+
+ LATENCY|RESET
+
+
+
+
+ LCS
+
+
+
+
+ LINDEX
+
+
+
+
+ LINSERT
+
+
+
+
+ LMOVE
+
+
+
+
+ LMPOP
+
+
+
+
+ LOLWUT
+
+
+
+
+ LPOS
+
+
+
+
+ LPUSHX
+
+
+
+
+ LRANGE
+
+
+
+
+ LREM
+
+
+
+
+ LSET
+
+
+
+
+ LTRIM
+
+
+
+
+ MEMORY
+
+
+
+
+ MEMORY|DOCTOR
+
+
+
+
+ MEMORY|HELP
+
+
+
+
+ MEMORY|MALLOC-STATS
+
+
+
+
+ MEMORY|PURGE
+
+
+
+
+ MEMORY|STATS
+
+
+
+
+ MEMORY|USAGE
+
+
+
+
+ MIGRATE
+
+
+
+
+ MODULE
+
+
+
+
+ MODULE|HELP
+
+
+
+
+ MODULE|LIST
+
+
+
+
+ MODULE|LOAD
+
+
+
+
+ MODULE|LOADEX
+
+
+
+
+ MODULE|UNLOAD
+
+
+
+
+ MONITOR
+
+
+
+
+ MOVE
+
+
+
+
+ MSETNX
+
+
+
+
+ OBJECT|ENCODING
+
+
+
+
+ OBJECT|FREQ
+
+
+
+
+ OBJECT|HELP
+
+
+
+
+ OBJECT|IDLETIME
+
+
+
+
+ OBJECT|REFCOUNT
+
+
+
+
+ PEXPIRE
+
+
+
+
+ PEXPIREAT
+
+
+
+
+ PEXPIRETIME
+
+
+
+
+ PFDEBUG
+
+
+
+
+ PFSELFTEST
+
+
+
+
+ PSETEX
+
+
+
+
+ PSUBSCRIBE
+
+
+
+
+ PSYNC
+
+
+
+
+ PUBLISH
+
+
+
+
+ PUBSUB
+
+
+
+
+ PUBSUB|CHANNELS
+
+
+
+
+ PUBSUB|HELP
+
+
+
+
+ PUBSUB|NUMPAT
+
+
+
+
+ PUBSUB|NUMSUB
+
+
+
+
+ PUBSUB|SHARDCHANNELS
+
+
+
+
+ PUBSUB|SHARDNUMSUB
+
+
+
+
+ PUNSUBSCRIBE
+
+
+
+
+ QUIT
+
+
+
+
+ RANDOMKEY
+
+
+
+
+ READONLY
+
+
+
+
+ READWRITE
+
+
+
+
+ RENAMENX
+
+
+
+
+ REPLCONF
+
+
+
+
+ REPLICAOF
+
+
+
+
+ RESET
+
+
+
+
+ RESTORE-ASKING
+
+
+
+
+ ROLE
+
+
+
+
+ RPOPLPUSH
+
+
+
+
+ RPUSHX
+
+
+
+
+ SAVE
+
+
+
+
+ SCAN
+
+
+
+
+ SCRIPT
+
+
+
+
+ SCRIPT|DEBUG
+
+
+
+
+ SCRIPT|EXISTS
+
+
+
+
+ SCRIPT|FLUSH
+
+
+
+
+ SCRIPT|HELP
+
+
+
+
+ SCRIPT|KILL
+
+
+
+
+ SCRIPT|LOAD
+
+
+
+
+ SDIFFSTORE
+
+
+
+
+ SETNX
+
+
+
+
+ SETRANGE
+
+
+
+
+ SHUTDOWN
+
+
+
+
+ SINTERCARD
+
+
+
+
+ SINTERSTORE
+
+
+
+
+ SISMEMBER
+
+
+
+
+ SLAVEOF
+
+
+
+
+ SLOWLOG
+
+
+
+
+ SLOWLOG|GET
+
+
+
+
+ SLOWLOG|HELP
+
+
+
+
+ SLOWLOG|LEN
+
+
+
+
+ SLOWLOG|RESET
+
+
+
+
+ SMISMEMBER
+
+
+
+
+ SMOVE
+
+
+
+
+ SORT
+
+
+
+
+ SORT_RO
+
+
+
+
+ SPOP
+
+
+
+
+ SPUBLISH
+
+
+
+
+ SRANDMEMBER
+
+
+
+
+ SSCAN
+
+
+
+
+ SSUBSCRIBE
+
+
+
+
+ STRLEN
+
+
+
+
+ SUBSTR
+
+
+
+
+ SUNION
+
+
+
+
+ SUNIONSTORE
+
+
+
+
+ SUNSUBSCRIBE
+
+
+
+
+ SWAPDB
+
+
+
+
+ SYNC
+
+
+
+
+ TIME
+
+
+
+
+ UNLINK
+
+
+
+
+ UNSUBSCRIBE
+
+
+
+
+ UNWATCH
+
+
+
+
+ WAIT
+
+
+
+
+ WAITAOF
+
+
+
+
+ WATCH
+
+
+
+
+ XACK
+
+
+
+
+ XADD
+
+
+
+
+ XAUTOCLAIM
+
+
+
+
+ XCLAIM
+
+
+
+
+ XDEL
+
+
+
+
+ XGROUP
+
+
+
+
+ XGROUP|CREATE
+
+
+
+
+ XGROUP|CREATECONSUMER
+
+
+
+
+ XGROUP|DELCONSUMER
+
+
+
+
+ XGROUP|DESTROY
+
+
+
+
+ XGROUP|HELP
+
+
+
+
+ XGROUP|SETID
+
+
+
+
+ XINFO
+
+
+
+
+ XINFO|CONSUMERS
+
+
+
+
+ XINFO|GROUPS
+
+
+
+
+ XINFO|HELP
+
+
+
+
+ XINFO|STREAM
+
+
+
+
+ XLEN
+
+
+
+
+ XPENDING
+
+
+
+
+ XRANGE
+
+
+
+
+ XREAD
+
+
+
+
+ XREADGROUP
+
+
+
+
+ XREVRANGE
+
+
+
+
+ XSETID
+
+
+
+
+ XTRIM
+
+
+
+
+ ZDIFF
+
+
+
+
+ ZDIFFSTORE
+
+
+
+
+ ZINCRBY
+
+
+
+
+ ZINTER
+
+
+
+
+ ZINTERCARD
+
+
+
+
+ ZINTERSTORE
+
+
+
+
+ ZLEXCOUNT
+
+
+
+
+ ZMPOP
+
+
+
+
+ ZMSCORE
+
+
+
+
+ ZRANDMEMBER
+
+
+
+
+ ZRANGEBYLEX
+
+
+
+
+ ZRANGEBYSCORE
+
+
+
+
+ ZRANGESTORE
+
+
+
+
+ ZREMRANGEBYLEX
+
+
+
+
+ ZREMRANGEBYRANK
+
+
+
+
+ ZREMRANGEBYSCORE
+
+
+
+
+ ZREVRANGE
+
+
+
+
+ ZREVRANGEBYLEX
+
+
+
+
+ ZREVRANGEBYSCORE
+
+
+
+
+ ZREVRANK
+
+
+
+
+ ZSCAN
+
+
+
+
+ ZSCORE
+
+
+
+
+ ZUNION
+
+
+
+
+ ZUNIONSTORE
+
+
+
+
+
+
+
+
diff --git a/docs/src/pages/roadmap.astro b/docs/src/pages/roadmap.astro
new file mode 100644
index 000000000..c4679964a
--- /dev/null
+++ b/docs/src/pages/roadmap.astro
@@ -0,0 +1,17 @@
+---
+import Layout from "../layouts/Layout.astro";
+import Content from "../data/roadmap.md";
+
+const title = "DiceDB Roadmap";
+const description = "";
+---
+
+
+
+
diff --git a/docs/src/styles/main.scss b/docs/src/styles/main.scss
index f2b53340d..99117939c 100644
--- a/docs/src/styles/main.scss
+++ b/docs/src/styles/main.scss
@@ -1,7 +1,7 @@
-@import url("https://fonts.googleapis.com/css2?family=Assistant:wght@500;700&display=swap");
+@import url("https://fonts.googleapis.com/css2?family=Spline+Sans+Mono:ital,wght@0,300..700;1,300..700&display=swap");
@import url("https://fonts.googleapis.com/css2?family=Merriweather:ital,wght@0,300;0,400;0,700;0,900;1,300;1,400;1,700;1,900&display=swap");
-$family-serif: "Assistant";
+$family-serif: "Spline Sans Mono", monospace;
$family-primary: $family-serif;
$family-secondary: "Merriweather", serif;
@@ -33,11 +33,11 @@ $button-border-color: #111;
$button-hover-border-color: $link;
pre {
- background-color: #111 !important;
+ background-color: #111 !important;
}
figcaption.header {
- display: none !important;
+ display: none !important;
}
.hnavbar {
@@ -107,6 +107,10 @@ ul.horizontal a {
cursor: pointer;
}
+.content img {
+ border-radius: 0.5em;
+}
+
img.logo {
filter: url("data:image/svg+xml;utf8,
#grayscale"); /* Firefox 10+, Firefox on Android */
-webkit-filter: grayscale(100%);
diff --git a/go.mod b/go.mod
index dd1f08f29..8d93328fb 100644
--- a/go.mod
+++ b/go.mod
@@ -5,7 +5,7 @@ go 1.23.0
require gotest.tools/v3 v3.5.1
require (
- github.com/bytedance/sonic/loader v0.2.0 // indirect
+ github.com/bytedance/sonic/loader v0.2.1 // indirect
github.com/cloudwego/base64x v0.1.4 // indirect
github.com/cloudwego/iasm v0.2.0 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
@@ -28,9 +28,9 @@ require (
github.com/subosito/gotenv v1.6.0 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
go.uber.org/multierr v1.11.0 // indirect
- golang.org/x/arch v0.10.0 // indirect
- golang.org/x/sys v0.25.0 // indirect
- golang.org/x/text v0.18.0 // indirect
+ golang.org/x/arch v0.11.0 // indirect
+ golang.org/x/sys v0.26.0 // indirect
+ golang.org/x/text v0.19.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
@@ -41,14 +41,14 @@ require (
github.com/cespare/xxhash/v2 v2.3.0
github.com/cockroachdb/swiss v0.0.0-20240612210725-f4de07ae6964
github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da
- github.com/dicedb/dicedb-go v0.0.0-20241015181607-d31c1df12107
+ github.com/dicedb/dicedb-go v0.0.0-20241026093718-570de4575be3
github.com/gobwas/glob v0.2.3
github.com/google/btree v1.1.3
github.com/google/go-cmp v0.6.0
github.com/google/uuid v1.6.0
github.com/gorilla/websocket v1.5.3
github.com/mmcloughlin/geohash v0.10.0
- github.com/ohler55/ojg v1.24.0
+ github.com/ohler55/ojg v1.25.0
github.com/pelletier/go-toml/v2 v2.2.3
github.com/rs/xid v1.6.0
github.com/rs/zerolog v1.33.0
@@ -56,6 +56,6 @@ require (
github.com/stretchr/testify v1.9.0
github.com/twmb/murmur3 v1.1.8
github.com/xwb1989/sqlparser v0.0.0-20180606152119-120387863bf2
- golang.org/x/crypto v0.27.0
- golang.org/x/exp v0.0.0-20240909161429-701f63a606c0
+ golang.org/x/crypto v0.28.0
+ golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c
)
diff --git a/go.sum b/go.sum
index 68fe43ac1..aefcf136c 100644
--- a/go.sum
+++ b/go.sum
@@ -9,8 +9,8 @@ github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0
github.com/bytedance/sonic v1.12.3 h1:W2MGa7RCU1QTeYRTPE3+88mVC0yXmsRQRChiyVocVjU=
github.com/bytedance/sonic v1.12.3/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk=
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
-github.com/bytedance/sonic/loader v0.2.0 h1:zNprn+lsIP06C/IqCHs3gPQIvnvpKbbxyXQP1iU4kWM=
-github.com/bytedance/sonic/loader v0.2.0/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
+github.com/bytedance/sonic/loader v0.2.1 h1:1GgorWTqf12TA8mma4DDSbaQigE2wOgQo7iCjjJv3+E=
+github.com/bytedance/sonic/loader v0.2.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
@@ -30,8 +30,8 @@ github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140 h1:y7y0Oa6UawqTFP
github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140/go.mod h1:c9O8+fpSOX1DM8cPNSkX/qsBWdkD4yd2dpciOWQjpBw=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
-github.com/dicedb/dicedb-go v0.0.0-20241015181607-d31c1df12107 h1:sL5dXtCsogSMP/afS2K2vVMMYFqJy02EezeRXpZnGy0=
-github.com/dicedb/dicedb-go v0.0.0-20241015181607-d31c1df12107/go.mod h1:iaOsphlvjJ87VL/5d32ZgeQYxhYS51k7/bvFKro7lWk=
+github.com/dicedb/dicedb-go v0.0.0-20241026093718-570de4575be3 h1:JvnAibMNGA0vQH+T47Y/d5/POURIvfJl3fFk0GIEBkQ=
+github.com/dicedb/dicedb-go v0.0.0-20241026093718-570de4575be3/go.mod h1:p7x5/3S6wBEmiRMwxavj1I1P1xsSVQS6fcSbeai5ic4=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
@@ -69,8 +69,8 @@ github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyua
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mmcloughlin/geohash v0.10.0 h1:9w1HchfDfdeLc+jFEf/04D27KP7E2QmpDu52wPbJWRE=
github.com/mmcloughlin/geohash v0.10.0/go.mod h1:oNZxQo5yWJh0eMQEP/8hwQuVx9Z9tjwFUqcTB1SmG0c=
-github.com/ohler55/ojg v1.24.0 h1:y2AVez6fPTszK/jPhaAYMCAzAoSleConMqSDD5wJKJg=
-github.com/ohler55/ojg v1.24.0/go.mod h1:gQhDVpQLqrmnd2eqGAvJtn+NfKoYJbe/A4Sj3/Vro4o=
+github.com/ohler55/ojg v1.25.0 h1:sDwc4u4zex65Uz5Nm7O1QwDKTT+YRcpeZQTy1pffRkw=
+github.com/ohler55/ojg v1.25.0/go.mod h1:gQhDVpQLqrmnd2eqGAvJtn+NfKoYJbe/A4Sj3/Vro4o=
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@@ -117,20 +117,20 @@ github.com/xwb1989/sqlparser v0.0.0-20180606152119-120387863bf2 h1:zzrxE1FKn5ryB
github.com/xwb1989/sqlparser v0.0.0-20180606152119-120387863bf2/go.mod h1:hzfGeIUDq/j97IG+FhNqkowIyEcD88LrW6fyU3K3WqY=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
-golang.org/x/arch v0.10.0 h1:S3huipmSclq3PJMNe76NGwkBR504WFkQ5dhzWzP8ZW8=
-golang.org/x/arch v0.10.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
-golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
-golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
-golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk=
-golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY=
+golang.org/x/arch v0.11.0 h1:KXV8WWKCXm6tRpLirl2szsO5j/oOODwZf4hATmGVNs4=
+golang.org/x/arch v0.11.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
+golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
+golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
+golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY=
+golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
-golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
-golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
-golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
+golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
+golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
+golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
diff --git a/integration_tests/commands/async/bit_operation_test.go b/integration_tests/commands/async/bit_operation_test.go
index 512109b92..959745f58 100644
--- a/integration_tests/commands/async/bit_operation_test.go
+++ b/integration_tests/commands/async/bit_operation_test.go
@@ -7,7 +7,7 @@ import (
"strings"
"testing"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func TestBitOp(t *testing.T) {
diff --git a/integration_tests/commands/async/bit_ops_string_int_test.go b/integration_tests/commands/async/bit_ops_string_int_test.go
index 94c1c0e75..13379fdfc 100644
--- a/integration_tests/commands/async/bit_ops_string_int_test.go
+++ b/integration_tests/commands/async/bit_ops_string_int_test.go
@@ -5,7 +5,7 @@ import (
"math/rand"
"testing"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func TestBitOpsString(t *testing.T) {
@@ -180,7 +180,7 @@ func TestBitOpsString(t *testing.T) {
case "equal":
assert.Equal(t, res, tc.expected[i])
case "less":
- assert.Assert(t, res.(int64) <= tc.expected[i].(int64), "CMD: %s Expected %d to be less than or equal to %d", tc.cmds[i], res, tc.expected[i])
+ assert.True(t, res.(int64) <= tc.expected[i].(int64), "CMD: %s Expected %d to be less than or equal to %d", tc.cmds[i], res, tc.expected[i])
}
}
})
diff --git a/integration_tests/commands/async/bitfield_test.go b/integration_tests/commands/async/bitfield_test.go
index 0beb9139d..4664e8ebe 100644
--- a/integration_tests/commands/async/bitfield_test.go
+++ b/integration_tests/commands/async/bitfield_test.go
@@ -4,7 +4,7 @@ import (
"testing"
"time"
- testifyAssert "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/assert"
)
func TestBitfield(t *testing.T) {
@@ -245,7 +245,7 @@ func TestBitfield(t *testing.T) {
}
result := FireCommand(conn, tc.Commands[i])
expected := tc.Expected[i]
- testifyAssert.Equal(t, expected, result)
+ assert.Equal(t, expected, result)
}
for _, cmd := range tc.CleanUp {
@@ -366,7 +366,7 @@ func TestBitfieldRO(t *testing.T) {
}
result := FireCommand(conn, tc.Commands[i])
expected := tc.Expected[i]
- testifyAssert.Equal(t, expected, result)
+ assert.Equal(t, expected, result)
}
for _, cmd := range tc.CleanUp {
diff --git a/integration_tests/commands/async/check_type_test.go b/integration_tests/commands/async/check_type_test.go
index 86f710f5c..eca785760 100644
--- a/integration_tests/commands/async/check_type_test.go
+++ b/integration_tests/commands/async/check_type_test.go
@@ -4,7 +4,7 @@ import (
"testing"
"time"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
// this file may contain test cases for checking error messages across all commands
diff --git a/integration_tests/commands/async/command_count_test.go b/integration_tests/commands/async/command_count_test.go
index 639f50bd6..9f7e33673 100644
--- a/integration_tests/commands/async/command_count_test.go
+++ b/integration_tests/commands/async/command_count_test.go
@@ -5,7 +5,7 @@ import (
"net"
"testing"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func TestCommandCount(t *testing.T) {
@@ -14,7 +14,7 @@ func TestCommandCount(t *testing.T) {
t.Run("Command count should be positive", func(t *testing.T) {
commandCount := getCommandCount(conn)
- assert.Assert(t, commandCount > 0,
+ assert.True(t, commandCount > 0,
fmt.Sprintf("Unexpected number of CLI commands found. expected greater than 0, %d found", commandCount))
})
}
diff --git a/integration_tests/commands/async/command_default_test.go b/integration_tests/commands/async/command_default_test.go
index 757404ef6..f5ca6e542 100644
--- a/integration_tests/commands/async/command_default_test.go
+++ b/integration_tests/commands/async/command_default_test.go
@@ -6,7 +6,7 @@ import (
"testing"
"github.com/dicedb/dice/internal/eval"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func TestCommandDefault(t *testing.T) {
@@ -15,12 +15,12 @@ func TestCommandDefault(t *testing.T) {
commands := getCommandDefault(conn)
t.Run("Command should not be empty", func(t *testing.T) {
- assert.Assert(t, len(commands) > 0,
+ assert.True(t, len(commands) > 0,
fmt.Sprintf("Unexpected number of CLI commands found. expected greater than 0, %d found", len(commands)))
})
t.Run("Command count matches", func(t *testing.T) {
- assert.Assert(t, len(commands) == len(eval.DiceCmds),
+ assert.True(t, len(commands) == len(eval.DiceCmds),
fmt.Sprintf("Unexpected number of CLI commands found. expected %d, %d found", len(eval.DiceCmds), len(commands)))
})
}
diff --git a/integration_tests/commands/async/command_getkeys_test.go b/integration_tests/commands/async/command_getkeys_test.go
index 0c2628291..0a7d94f65 100644
--- a/integration_tests/commands/async/command_getkeys_test.go
+++ b/integration_tests/commands/async/command_getkeys_test.go
@@ -3,7 +3,7 @@ package async
import (
"testing"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
var getKeysTestCases = []struct {
@@ -31,7 +31,7 @@ func TestCommandGetKeys(t *testing.T) {
for _, tc := range getKeysTestCases {
t.Run(tc.name, func(t *testing.T) {
result := FireCommand(conn, "COMMAND GETKEYS "+tc.inCmd)
- assert.DeepEqual(t, tc.expected, result)
+ assert.Equal(t, tc.expected, result)
})
}
}
diff --git a/integration_tests/commands/async/command_info_test.go b/integration_tests/commands/async/command_info_test.go
index 7e3e8ce64..4b58576f4 100644
--- a/integration_tests/commands/async/command_info_test.go
+++ b/integration_tests/commands/async/command_info_test.go
@@ -3,7 +3,7 @@ package async
import (
"testing"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
var getInfoTestCases = []struct {
@@ -32,7 +32,7 @@ func TestCommandInfo(t *testing.T) {
for _, tc := range getInfoTestCases {
t.Run(tc.name, func(t *testing.T) {
result := FireCommand(conn, "COMMAND INFO "+tc.inCmd)
- assert.DeepEqual(t, tc.expected, result)
+ assert.Equal(t, tc.expected, result)
})
}
}
diff --git a/integration_tests/commands/async/command_list_test.go b/integration_tests/commands/async/command_list_test.go
index ec9182cf7..56722db3c 100644
--- a/integration_tests/commands/async/command_list_test.go
+++ b/integration_tests/commands/async/command_list_test.go
@@ -5,7 +5,7 @@ import (
"net"
"testing"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func TestCommandList(t *testing.T) {
@@ -14,7 +14,7 @@ func TestCommandList(t *testing.T) {
t.Run("Command list should not be empty", func(t *testing.T) {
commandList := getCommandList(conn)
- assert.Assert(t, len(commandList) > 0,
+ assert.True(t, len(commandList) > 0,
fmt.Sprintf("Unexpected number of CLI commands found. expected greater than 0, %d found", len(commandList)))
})
}
diff --git a/integration_tests/commands/async/command_rename_test.go b/integration_tests/commands/async/command_rename_test.go
index 6d3d0d499..4d5be2889 100644
--- a/integration_tests/commands/async/command_rename_test.go
+++ b/integration_tests/commands/async/command_rename_test.go
@@ -3,7 +3,7 @@ package async
import (
"testing"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
var renameKeysTestCases = []struct {
diff --git a/integration_tests/commands/async/copy_test.go b/integration_tests/commands/async/copy_test.go
index 4008cea1a..3fad1db47 100644
--- a/integration_tests/commands/async/copy_test.go
+++ b/integration_tests/commands/async/copy_test.go
@@ -4,8 +4,7 @@ import (
"testing"
"github.com/dicedb/dice/testutils"
- testifyAssert "github.com/stretchr/testify/assert"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func TestCopy(t *testing.T) {
@@ -84,7 +83,7 @@ func TestCopy(t *testing.T) {
// else compare the values as is.
// This is to handle cases where the expected value is a json string with a different key order.
if resOk && expOk && testutils.IsJSONResponse(resStr) && testutils.IsJSONResponse(expStr) {
- testifyAssert.JSONEq(t, expStr, resStr)
+ assert.JSONEq(t, expStr, resStr)
} else {
assert.Equal(t, tc.expected[i], result, "Value mismatch for cmd %s", cmd)
}
diff --git a/integration_tests/commands/async/dbsize_test.go b/integration_tests/commands/async/dbsize_test.go
index 901f5497a..7278127d8 100644
--- a/integration_tests/commands/async/dbsize_test.go
+++ b/integration_tests/commands/async/dbsize_test.go
@@ -4,7 +4,7 @@ import (
"testing"
"time"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func TestDBSIZE(t *testing.T) {
diff --git a/integration_tests/commands/async/del_test.go b/integration_tests/commands/async/del_test.go
index d66c5334b..58305866f 100644
--- a/integration_tests/commands/async/del_test.go
+++ b/integration_tests/commands/async/del_test.go
@@ -3,7 +3,7 @@ package async
import (
"testing"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func TestDel(t *testing.T) {
diff --git a/integration_tests/commands/async/deque_test.go b/integration_tests/commands/async/deque_test.go
index 9119f682b..2e48bc04e 100644
--- a/integration_tests/commands/async/deque_test.go
+++ b/integration_tests/commands/async/deque_test.go
@@ -8,7 +8,7 @@ import (
"testing"
"time"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
var deqRandGenerator *rand.Rand
diff --git a/integration_tests/commands/async/discard_test.go b/integration_tests/commands/async/discard_test.go
index 009aab70e..b26ef5c0a 100644
--- a/integration_tests/commands/async/discard_test.go
+++ b/integration_tests/commands/async/discard_test.go
@@ -3,7 +3,7 @@ package async
import (
"testing"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func TestDiscard(t *testing.T) {
diff --git a/integration_tests/commands/async/dump_test.go b/integration_tests/commands/async/dump_test.go
index 35adf1f4d..7ec050b91 100644
--- a/integration_tests/commands/async/dump_test.go
+++ b/integration_tests/commands/async/dump_test.go
@@ -5,7 +5,7 @@ import (
"testing"
"github.com/dicedb/dice/testutils"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func TestDumpRestore(t *testing.T) {
@@ -96,13 +96,13 @@ func TestDumpRestore(t *testing.T) {
switch exp := expected.(type) {
case string:
- assert.DeepEqual(t, exp, result)
+ assert.Equal(t, exp, result)
case []interface{}:
- assert.Assert(t, testutils.UnorderedEqual(exp, result))
+ assert.True(t, testutils.UnorderedEqual(exp, result))
case func(interface{}) bool:
- assert.Assert(t, exp(result), cmd)
+ assert.True(t, exp(result), cmd)
default:
- assert.DeepEqual(t, expected, result)
+ assert.Equal(t, expected, result)
}
}
})
diff --git a/integration_tests/commands/async/echo_test.go b/integration_tests/commands/async/echo_test.go
index 1c46dadbd..06d14c022 100644
--- a/integration_tests/commands/async/echo_test.go
+++ b/integration_tests/commands/async/echo_test.go
@@ -3,7 +3,7 @@ package async
import (
"testing"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func TestEcho(t *testing.T) {
diff --git a/integration_tests/commands/async/exists_test.go b/integration_tests/commands/async/exists_test.go
index 73d7cea8c..05d4da1f1 100644
--- a/integration_tests/commands/async/exists_test.go
+++ b/integration_tests/commands/async/exists_test.go
@@ -4,7 +4,7 @@ import (
"testing"
"time"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func TestExists(t *testing.T) {
diff --git a/integration_tests/commands/async/expire_test.go b/integration_tests/commands/async/expire_test.go
deleted file mode 100644
index 519057662..000000000
--- a/integration_tests/commands/async/expire_test.go
+++ /dev/null
@@ -1,243 +0,0 @@
-package async
-
-import (
- "strconv"
- "testing"
- "time"
-
- "gotest.tools/v3/assert"
-)
-
-func TestExpire(t *testing.T) {
- conn := getLocalConnection()
- defer conn.Close()
-
- testCases := []struct {
- name string
- setup string
- commands []string
- expected []interface{}
- delay []time.Duration
- }{
- {
- name: "Set with EXPIRE command",
- setup: "",
- commands: []string{
- "SET test_key test_value",
- "EXPIRE test_key 1",
- },
- expected: []interface{}{"OK", int64(1)},
- delay: []time.Duration{0, 0},
- },
- {
- name: "Check if key is nil after expiration",
- setup: "SET test_key test_value",
- commands: []string{
- "EXPIRE test_key 1",
- "GET test_key",
- },
- expected: []interface{}{int64(1), "(nil)"},
- delay: []time.Duration{0, 1100 * time.Millisecond},
- },
- {
- name: "EXPIRE non-existent key",
- setup: "",
- commands: []string{
- "EXPIRE non_existent_key 1",
- },
- expected: []interface{}{int64(0)},
- delay: []time.Duration{0, 0},
- },
- {
- name: "EXPIRE with past time",
- setup: "SET test_key test_value",
- commands: []string{
- "EXPIRE test_key -1",
- "GET test_key",
- },
- expected: []interface{}{"ERR invalid expire time in 'expire' command", "test_value"},
- delay: []time.Duration{0, 0},
- },
- {
- name: "EXPIRE with invalid syntax",
- setup: "SET test_key test_value",
- commands: []string{
- "EXPIRE test_key",
- },
- expected: []interface{}{"ERR wrong number of arguments for 'expire' command"},
- delay: []time.Duration{0},
- },
- {
- name: "Test(NX): Set the expiration only if the key has no expiration time",
- setup: "",
- commands: []string{
- "SET test_key test_value",
- "EXPIRE test_key " + strconv.FormatInt(1, 10) + " NX",
- "EXPIRE test_key " + strconv.FormatInt(1, 10) + " NX",
- },
- expected: []interface{}{"OK", int64(1), int64(0)},
- delay: []time.Duration{0, 0, 0},
- },
-
- {
- name: "Test(XX): Set the expiration only if the key already has an expiration time",
- setup: "",
- commands: []string{
- "SET test_key test_value",
- "EXPIRE test_key " + strconv.FormatInt(10, 10) + " XX",
- "TTL test_key",
- "EXPIRE test_key " + strconv.FormatInt(10, 10),
- "EXPIRE test_key " + strconv.FormatInt(10, 10) + " XX",
- },
- expected: []interface{}{"OK", int64(0), int64(-1), int64(1), int64(1)},
- delay: []time.Duration{0, 0, 0, 0, 0},
- },
-
- {
- name: "TEST(GT): Set the expiration only if the new expiration time is greater than the current one",
- setup: "",
- commands: []string{
- "SET test_key test_value",
- "EXPIRE test_key " + strconv.FormatInt(10, 10) + " GT",
- "TTL test_key",
- "EXPIRE test_key " + strconv.FormatInt(10, 10),
- "EXPIRE test_key " + strconv.FormatInt(20, 10) + " GT",
- },
- expected: []interface{}{"OK", int64(0), int64(-1), int64(1), int64(1)},
- delay: []time.Duration{0, 0, 0, 0, 0},
- },
-
- {
- name: "TEST(LT): Set the expiration only if the new expiration time is less than the current one",
- setup: "",
- commands: []string{
- "SET test_key test_value",
- "EXPIRE test_key " + strconv.FormatInt(10, 10) + " LT",
- "EXPIRE test_key " + strconv.FormatInt(20, 10) + " LT",
- },
- expected: []interface{}{"OK", int64(1), int64(0)},
- delay: []time.Duration{0, 0, 0},
- },
-
- {
- name: "TEST(LT): Set the expiration only if the new expiration time is less than the current one",
- setup: "",
- commands: []string{
- "SET test_key test_value",
- "EXPIRE test_key " + strconv.FormatInt(10, 10) + " LT",
- "EXPIRE test_key " + strconv.FormatInt(20, 10) + " LT",
- },
- expected: []interface{}{"OK", int64(1), int64(0)},
- delay: []time.Duration{0, 0, 0},
- },
-
- {
- name: "TEST(NX + LT/GT)",
- setup: "",
- commands: []string{
- "SET test_key test_value",
- "EXPIRE test_key " + strconv.FormatInt(20, 10) + " NX",
- "EXPIRE test_key " + strconv.FormatInt(20, 10) + " NX" + " LT",
- "EXPIRE test_key " + strconv.FormatInt(20, 10) + " NX" + " GT",
- "GET test_key",
- },
- expected: []interface{}{"OK", int64(1),
- "ERR NX and XX, GT or LT options at the same time are not compatible",
- "ERR NX and XX, GT or LT options at the same time are not compatible",
- "test_value"},
- delay: []time.Duration{0, 0, 0, 0, 0},
- },
- {
- name: "TEST(XX + LT/GT)",
- setup: "",
- commands: []string{
- "SET test_key test_value",
- "EXPIRE test_key " + strconv.FormatInt(20, 10),
- "EXPIRE test_key " + strconv.FormatInt(5, 10) + " XX" + " LT",
- "EXPIRE test_key " + strconv.FormatInt(10, 10) + " XX" + " GT",
- "EXPIRE test_key " + strconv.FormatInt(20, 10) + " XX" + " GT",
- "GET test_key",
- },
- expected: []interface{}{"OK", int64(1), int64(1), int64(1), int64(1), "test_value"},
- delay: []time.Duration{0, 0, 0, 0, 0, 0},
- },
- {
- name: "Test if value is nil after expiration",
- setup: "",
- commands: []string{
- "SET test_key test_value",
- "EXPIRE test_key " + strconv.FormatInt(20, 10),
- "EXPIRE test_key " + strconv.FormatInt(2, 10) + " XX" + " LT",
- "GET test_key",
- },
- expected: []interface{}{"OK", int64(1), int64(1), "(nil)"},
- delay: []time.Duration{0, 0, 0, 2 * time.Second},
- },
- {
- name: "Test if value is nil after expiration",
- setup: "",
- commands: []string{
- "SET test_key test_value",
- "EXPIRE test_key " + strconv.FormatInt(2, 10) + " NX",
- "GET test_key",
- },
- expected: []interface{}{"OK", int64(1), "(nil)"},
- delay: []time.Duration{0, 0, 2 * time.Second},
- },
- {
- name: "Invalid Command Test",
- setup: "",
- commands: []string{
- "SET test_key test_value",
- "EXPIRE test_key " + strconv.FormatInt(1, 10) + " XX" + " " + "rr",
- "EXPIRE test_key " + strconv.FormatInt(1, 10) + " XX" + " " + "NX",
- "EXPIRE test_key " + strconv.FormatInt(1, 10) + " GT" + " " + "lt",
- "EXPIRE test_key " + strconv.FormatInt(1, 10) + " GT" + " " + "lt" + " " + "xx",
- "EXPIRE test_key " + strconv.FormatInt(1, 10) + " GT" + " " + "lt" + " " + "nx",
- "EXPIRE test_key " + strconv.FormatInt(1, 10) + " nx" + " " + "xx" + " " + "gt",
- "EXPIRE test_key " + strconv.FormatInt(1, 10) + " nx" + " " + "xx" + " " + "lt",
- },
- expected: []interface{}{"OK", "ERR Unsupported option rr",
- "ERR NX and XX, GT or LT options at the same time are not compatible",
- "ERR GT and LT options at the same time are not compatible",
- "ERR GT and LT options at the same time are not compatible",
- "ERR NX and XX, GT or LT options at the same time are not compatible",
- "ERR NX and XX, GT or LT options at the same time are not compatible",
- "ERR NX and XX, GT or LT options at the same time are not compatible"},
- delay: []time.Duration{0, 0, 0, 0, 0, 0, 0, 0},
- },
- }
- for _, tc := range testCases {
- t.Run(tc.name, func(t *testing.T) {
- // Setup
- if tc.setup != "" {
- FireCommand(conn, tc.setup)
- }
-
- // Execute commands
- var results []interface{}
- for i, cmd := range tc.commands {
- // Wait if delay is specified
- if tc.delay[i] > 0 {
- time.Sleep(tc.delay[i])
- }
- result := FireCommand(conn, cmd)
- results = append(results, result)
- }
-
- // Validate results
- for i, expected := range tc.expected {
- if i >= len(results) {
- t.Fatalf("Not enough results. Expected %d, got %d", len(tc.expected), len(results))
- }
-
- if expected == "(nil)" {
- assert.Assert(t, results[i] == "(nil)" || results[i] == "",
- "Expected nil or empty result, got %v", results[i])
- } else {
- assert.DeepEqual(t, expected, results[i])
- }
- }
- })
- }
-}
diff --git a/integration_tests/commands/async/expireat_test.go b/integration_tests/commands/async/expireat_test.go
deleted file mode 100644
index b6c9210d3..000000000
--- a/integration_tests/commands/async/expireat_test.go
+++ /dev/null
@@ -1,242 +0,0 @@
-package async
-
-import (
- "strconv"
- "testing"
- "time"
-
- "gotest.tools/v3/assert"
-)
-
-func TestExpireat(t *testing.T) {
- conn := getLocalConnection()
- defer conn.Close()
-
- testCases := []struct {
- name string
- setup string
- commands []string
- expected []interface{}
- delay []time.Duration
- }{
- {
- name: "Set with EXPIREAT command",
- setup: "",
- commands: []string{
- "SET test_key test_value",
- "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+1, 10),
- },
- expected: []interface{}{"OK", int64(1)},
- delay: []time.Duration{0, 0},
- },
- {
- name: "Check if key is nil after expiration",
- setup: "SET test_key test_value",
- commands: []string{
- "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+1, 10),
- "GET test_key",
- },
- expected: []interface{}{int64(1), "(nil)"},
- delay: []time.Duration{0, 1100 * time.Millisecond},
- },
- {
- name: "EXPIREAT non-existent key",
- setup: "",
- commands: []string{
- "EXPIREAT non_existent_key " + strconv.FormatInt(time.Now().Unix()+1, 10),
- },
- expected: []interface{}{int64(0)},
- delay: []time.Duration{0, 0},
- },
- {
- name: "EXPIREAT with past time",
- setup: "SET test_key test_value",
- commands: []string{
- "EXPIREAT test_key " + strconv.FormatInt(-1, 10),
- "GET test_key",
- },
- expected: []interface{}{"ERR invalid expire time in 'expireat' command", "test_value"},
- delay: []time.Duration{0, 0},
- },
- {
- name: "EXPIREAT with invalid syntax",
- setup: "SET test_key test_value",
- commands: []string{
- "EXPIREAT test_key",
- },
- expected: []interface{}{"ERR wrong number of arguments for 'expireat' command"},
- delay: []time.Duration{0},
- },
- {
- name: "Test(NX): Set the expiration only if the key has no expiration time",
- setup: "",
- commands: []string{
- "SET test_key test_value",
- "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+10, 10) + " NX",
- "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+1, 10) + " NX",
- },
- expected: []interface{}{"OK", int64(1), int64(0)},
- delay: []time.Duration{0, 0, 0},
- },
-
- {
- name: "Test(XX): Set the expiration only if the key already has an expiration time",
- setup: "",
- commands: []string{
- "SET test_key test_value",
- "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+10, 10) + " XX",
- "TTL test_key",
- "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+10, 10),
- "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+10, 10) + " XX",
- },
- expected: []interface{}{"OK", int64(0), int64(-1), int64(1), int64(1)},
- delay: []time.Duration{0, 0, 0, 0, 0},
- },
-
- {
- name: "TEST(GT): Set the expiration only if the new expiration time is greater than the current one",
- setup: "",
- commands: []string{
- "SET test_key test_value",
- "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+10, 10) + " GT",
- "TTL test_key",
- "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+10, 10),
- "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+20, 10) + " GT",
- },
- expected: []interface{}{"OK", int64(0), int64(-1), int64(1), int64(1)},
- delay: []time.Duration{0, 0, 0, 0, 0},
- },
-
- {
- name: "TEST(LT): Set the expiration only if the new expiration time is less than the current one",
- setup: "",
- commands: []string{
- "SET test_key test_value",
- "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+10, 10) + " LT",
- "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+20, 10) + " LT",
- },
- expected: []interface{}{"OK", int64(1), int64(0)},
- delay: []time.Duration{0, 0, 0},
- },
-
- {
- name: "TEST(LT): Set the expiration only if the new expiration time is less than the current one",
- setup: "",
- commands: []string{
- "SET test_key test_value",
- "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+10, 10) + " LT",
- "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+20, 10) + " LT",
- },
- expected: []interface{}{"OK", int64(1), int64(0)},
- delay: []time.Duration{0, 0, 0},
- },
-
- {
- name: "TEST(NX + LT/GT)",
- setup: "",
- commands: []string{
- "SET test_key test_value",
- "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+20, 10) + " NX",
- "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+20, 10) + " NX" + " LT",
- "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+20, 10) + " NX" + " GT",
- "GET test_key",
- },
- expected: []interface{}{"OK", int64(1),
- "ERR NX and XX, GT or LT options at the same time are not compatible",
- "ERR NX and XX, GT or LT options at the same time are not compatible",
- "test_value"},
- delay: []time.Duration{0, 0, 0, 0, 0},
- },
- {
- name: "TEST(XX + LT/GT)",
- setup: "",
- commands: []string{
- "SET test_key test_value",
- "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+20, 10),
- "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+5, 10) + " XX" + " LT",
- "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+10, 10) + " XX" + " GT",
- "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+20, 10) + " XX" + " GT",
- "GET test_key",
- },
- expected: []interface{}{"OK", int64(1), int64(1), int64(1), int64(1), "test_value"},
- delay: []time.Duration{0, 0, 0, 0, 0, 0},
- },
- {
- name: "Test if value is nil after expiration",
- setup: "",
- commands: []string{
- "SET test_key test_value",
- "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+20, 10),
- "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+2, 10) + " XX" + " LT",
- "GET test_key",
- },
- expected: []interface{}{"OK", int64(1), int64(1), "(nil)"},
- delay: []time.Duration{0, 0, 0, 2 * time.Second},
- },
- {
- name: "Test if value is nil after expiration",
- setup: "",
- commands: []string{
- "SET test_key test_value",
- "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+2, 10) + " NX",
- "GET test_key",
- },
- expected: []interface{}{"OK", int64(1), "(nil)"},
- delay: []time.Duration{0, 0, 2 * time.Second},
- },
- {
- name: "Invalid Command Test",
- setup: "",
- commands: []string{
- "SET test_key test_value",
- "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+1, 10) + " XX" + " " + "rr",
- "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+1, 10) + " XX" + " " + "NX",
- "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+1, 10) + " GT" + " " + "lt",
- "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+1, 10) + " GT" + " " + "lt" + " " + "xx",
- "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+1, 10) + " GT" + " " + "lt" + " " + "nx",
- "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+1, 10) + " nx" + " " + "xx" + " " + "gt",
- "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+1, 10) + " nx" + " " + "xx" + " " + "lt",
- },
- expected: []interface{}{"OK", "ERR Unsupported option rr",
- "ERR NX and XX, GT or LT options at the same time are not compatible",
- "ERR GT and LT options at the same time are not compatible",
- "ERR GT and LT options at the same time are not compatible",
- "ERR NX and XX, GT or LT options at the same time are not compatible",
- "ERR NX and XX, GT or LT options at the same time are not compatible",
- "ERR NX and XX, GT or LT options at the same time are not compatible"},
- delay: []time.Duration{0, 0, 0, 0, 0, 0, 0, 0},
- },
- }
- for _, tc := range testCases {
- t.Run(tc.name, func(t *testing.T) {
- // Setup
- if tc.setup != "" {
- FireCommand(conn, tc.setup)
- }
-
- // Execute commands
- var results []interface{}
- for i, cmd := range tc.commands {
- // Wait if delay is specified
- if tc.delay[i] > 0 {
- time.Sleep(tc.delay[i])
- }
- result := FireCommand(conn, cmd)
- results = append(results, result)
- }
- // Validate results
- for i, expected := range tc.expected {
- if i >= len(results) {
- t.Fatalf("Not enough results. Expected %d, got %d", len(tc.expected), len(results))
- }
-
- if expected == "(nil)" {
- assert.Assert(t, results[i] == "(nil)" || results[i] == "",
- "Expected nil or empty result, got %v", results[i])
- } else {
- assert.DeepEqual(t, expected, results[i])
- }
- }
- })
- }
-}
diff --git a/integration_tests/commands/async/expiretime_test.go b/integration_tests/commands/async/expiretime_test.go
deleted file mode 100644
index 017216583..000000000
--- a/integration_tests/commands/async/expiretime_test.go
+++ /dev/null
@@ -1,97 +0,0 @@
-package async
-
-import (
- "strconv"
- "testing"
- "time"
-
- "gotest.tools/v3/assert"
-)
-
-func TestExpiretime(t *testing.T) {
- conn := getLocalConnection()
- defer conn.Close()
-
- futureUnixTimestamp := time.Now().Unix() + 1
-
- testCases := []struct {
- name string
- setup string
- commands []string
- expected []interface{}
- delay []time.Duration
- }{
- {
- name: "EXPIRETIME command",
- setup: "SET test_key test_value",
- commands: []string{
- "EXPIREAT test_key " + strconv.FormatInt(futureUnixTimestamp, 10),
- "EXPIRETIME test_key",
- },
- expected: []interface{}{int64(1), futureUnixTimestamp},
- delay: []time.Duration{0, 0},
- },
- {
- name: "EXPIRETIME non-existent key",
- setup: "",
- commands: []string{
- "EXPIRETIME non_existent_key",
- },
- expected: []interface{}{int64(-2)},
- delay: []time.Duration{0},
- },
- {
- name: "EXPIRETIME with past time",
- setup: "SET test_key test_value",
- commands: []string{
- "EXPIREAT test_key 1724167183",
- "EXPIRETIME test_key",
- },
- expected: []interface{}{int64(1), int64(-2)},
- delay: []time.Duration{0, 0},
- },
- {
- name: "EXPIRETIME with invalid syntax",
- setup: "SET test_key test_value",
- commands: []string{
- "EXPIRETIME",
- "EXPIRETIME key1 key2",
- },
- expected: []interface{}{"ERR wrong number of arguments for 'expiretime' command", "ERR wrong number of arguments for 'expiretime' command"},
- delay: []time.Duration{0, 0},
- },
- }
- for _, tc := range testCases {
- t.Run(tc.name, func(t *testing.T) {
- // Setup
- if tc.setup != "" {
- FireCommand(conn, tc.setup)
- }
-
- // Execute commands
- var results []interface{}
- for i, cmd := range tc.commands {
- // Wait if delay is specified
- if tc.delay[i] > 0 {
- time.Sleep(tc.delay[i])
- }
- result := FireCommand(conn, cmd)
- results = append(results, result)
- }
-
- // Validate results
- for i, expected := range tc.expected {
- if i >= len(results) {
- t.Fatalf("Not enough results. Expected %d, got %d", len(tc.expected), len(results))
- }
-
- if expected == "(nil)" {
- assert.Assert(t, results[i] == "(nil)" || results[i] == "",
- "Expected nil or empty result, got %v", results[i])
- } else {
- assert.DeepEqual(t, expected, results[i])
- }
- }
- })
- }
-}
diff --git a/integration_tests/commands/async/flushdb_test.go b/integration_tests/commands/async/flushdb_test.go
index 45b34dc8a..b11d9e804 100644
--- a/integration_tests/commands/async/flushdb_test.go
+++ b/integration_tests/commands/async/flushdb_test.go
@@ -4,7 +4,7 @@ import (
"testing"
"time"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func TestFLUSHDB(t *testing.T) {
diff --git a/integration_tests/commands/async/get_test.go b/integration_tests/commands/async/get_test.go
index f427a8f1d..697d4a03f 100644
--- a/integration_tests/commands/async/get_test.go
+++ b/integration_tests/commands/async/get_test.go
@@ -4,7 +4,7 @@ import (
"testing"
"time"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func TestGet(t *testing.T) {
diff --git a/integration_tests/commands/async/getset_test.go b/integration_tests/commands/async/getset_test.go
index 0f4323232..ce43696e2 100644
--- a/integration_tests/commands/async/getset_test.go
+++ b/integration_tests/commands/async/getset_test.go
@@ -4,7 +4,7 @@ import (
"testing"
"time"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func TestGetSet(t *testing.T) {
diff --git a/integration_tests/commands/async/hello_test.go b/integration_tests/commands/async/hello_test.go
index 7b8620128..263865f35 100644
--- a/integration_tests/commands/async/hello_test.go
+++ b/integration_tests/commands/async/hello_test.go
@@ -3,7 +3,7 @@ package async
import (
"testing"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func TestHello(t *testing.T) {
@@ -20,6 +20,6 @@ func TestHello(t *testing.T) {
t.Run("HELLO command response", func(t *testing.T) {
actual := FireCommand(conn, "HELLO")
- assert.DeepEqual(t, expected, actual)
+ assert.Equal(t, expected, actual)
})
}
diff --git a/integration_tests/commands/async/hgetall_test.go b/integration_tests/commands/async/hgetall_test.go
index 24b2e7b2f..7971f0cc4 100644
--- a/integration_tests/commands/async/hgetall_test.go
+++ b/integration_tests/commands/async/hgetall_test.go
@@ -4,9 +4,13 @@ import (
"reflect"
"testing"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
+var ZERO int64 = 0
+var ONE int64 = 1
+var TWO int64 = 2
+
func TestHGETALL(t *testing.T) {
conn := getLocalConnection()
defer conn.Close()
@@ -52,7 +56,7 @@ func TestHGETALL(t *testing.T) {
}
} else {
- assert.DeepEqual(t, tc.expected[i], result)
+ assert.Equal(t, tc.expected[i], result)
}
}
})
diff --git a/integration_tests/commands/async/json_arrpop_test.go b/integration_tests/commands/async/json_arrpop_test.go
index 5993d0e62..cbfc7bf6b 100644
--- a/integration_tests/commands/async/json_arrpop_test.go
+++ b/integration_tests/commands/async/json_arrpop_test.go
@@ -4,8 +4,7 @@ import (
"testing"
"github.com/dicedb/dice/testutils"
- testifyAssert "github.com/stretchr/testify/assert"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func TestJSONARRPOP(t *testing.T) {
@@ -49,14 +48,14 @@ func TestJSONARRPOP(t *testing.T) {
jsonResult, isString := result.(string)
if isString && testutils.IsJSONResponse(jsonResult) {
- testifyAssert.JSONEq(t, out.(string), jsonResult)
+ assert.JSONEq(t, out.(string), jsonResult)
continue
}
if tcase.assertType[i] == "equal" {
assert.Equal(t, out, result)
} else if tcase.assertType[i] == "deep_equal" {
- assert.Assert(t, arraysArePermutations(out.([]interface{}), result.([]interface{})))
+ assert.True(t, arraysArePermutations(out.([]interface{}), result.([]interface{})))
}
}
})
diff --git a/integration_tests/commands/async/json_test.go b/integration_tests/commands/async/json_test.go
index dc6957b35..9273daae3 100644
--- a/integration_tests/commands/async/json_test.go
+++ b/integration_tests/commands/async/json_test.go
@@ -8,8 +8,7 @@ import (
"github.com/bytedance/sonic"
"github.com/dicedb/dice/testutils"
- testifyAssert "github.com/stretchr/testify/assert"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func TestJSONOperations(t *testing.T) {
@@ -189,7 +188,7 @@ func TestJSONOperations(t *testing.T) {
if tc.getCmd != "" {
result := FireCommand(conn, tc.getCmd)
if testutils.IsJSONResponse(result.(string)) {
- testifyAssert.JSONEq(t, tc.expected, result.(string))
+ assert.JSONEq(t, tc.expected, result.(string))
} else {
assert.Equal(t, tc.expected, result)
}
@@ -239,7 +238,7 @@ func TestJSONSetWithInvalidJSON(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
result := FireCommand(conn, tc.command)
- assert.Check(t, strings.HasPrefix(result.(string), tc.expected), fmt.Sprintf("Expected: %s, Got: %s", tc.expected, result))
+ assert.True(t, strings.HasPrefix(result.(string), tc.expected), fmt.Sprintf("Expected: %s, Got: %s", tc.expected, result))
})
}
}
@@ -340,7 +339,7 @@ func TestJSONSetWithNXAndXX(t *testing.T) {
result := FireCommand(conn, cmd)
jsonResult, isString := result.(string)
if isString && testutils.IsJSONResponse(jsonResult) {
- testifyAssert.JSONEq(t, tc.expected[i].(string), jsonResult)
+ assert.JSONEq(t, tc.expected[i].(string), jsonResult)
} else {
assert.Equal(t, tc.expected[i], result)
}
@@ -525,7 +524,7 @@ func TestJSONDelOperations(t *testing.T) {
result := FireCommand(conn, cmd)
stringResult, ok := result.(string)
if ok && testutils.IsJSONResponse(stringResult) {
- testifyAssert.JSONEq(t, tc.expected[i].(string), stringResult)
+ assert.JSONEq(t, tc.expected[i].(string), stringResult)
} else {
assert.Equal(t, tc.expected[i], result)
}
@@ -620,7 +619,7 @@ func TestJSONForgetOperations(t *testing.T) {
result := FireCommand(conn, cmd)
stringResult, ok := result.(string)
if ok && testutils.IsJSONResponse(stringResult) {
- testifyAssert.JSONEq(t, tc.expected[i].(string), stringResult)
+ assert.JSONEq(t, tc.expected[i].(string), stringResult)
} else {
assert.Equal(t, tc.expected[i], result)
}
@@ -735,7 +734,7 @@ func TestJsonStrlen(t *testing.T) {
if ok {
assert.Equal(t, tc.expected[i], stringResult)
} else {
- assert.Assert(t, arraysArePermutations(tc.expected[i].([]interface{}), result.([]interface{})))
+ assert.True(t, arraysArePermutations(tc.expected[i].([]interface{}), result.([]interface{})))
}
}
})
@@ -795,7 +794,7 @@ func TestJSONMGET(t *testing.T) {
assert.Equal(t, len(tc.expected), len(results))
for i := range results {
if testutils.IsJSONResponse(tc.expected[i].(string)) {
- testifyAssert.JSONEq(t, tc.expected[i].(string), results[i].(string))
+ assert.JSONEq(t, tc.expected[i].(string), results[i].(string))
} else {
assert.Equal(t, tc.expected[i], results[i])
}
@@ -813,7 +812,7 @@ func testJSONMGETRecursive(conn net.Conn) func(*testing.T) {
return func(t *testing.T) {
result := FireCommand(conn, "JSON.MGET doc1 doc2 $..a")
results, ok := result.([]interface{})
- assert.Assert(t, ok, "Expected result to be a slice of interface{}")
+ assert.True(t, ok, "Expected result to be a slice of interface{}")
assert.Equal(t, 2, len(results), "Expected 2 results")
expectedSets := [][]int{
@@ -824,13 +823,13 @@ func testJSONMGETRecursive(conn net.Conn) func(*testing.T) {
for i, res := range results {
var actualSet []int
err := sonic.UnmarshalString(res.(string), &actualSet)
- assert.NilError(t, err, "Failed to unmarshal JSON")
+ assert.Nil(t, err, "Failed to unmarshal JSON")
- assert.Assert(t, len(actualSet) == len(expectedSets[i]),
+ assert.True(t, len(actualSet) == len(expectedSets[i]),
"Mismatch in number of elements for set %d", i)
for _, expected := range expectedSets[i] {
- assert.Assert(t, sliceContainsItem(actualSet, expected),
+ assert.True(t, sliceContainsItem(actualSet, expected),
"Set %d does not contain expected value %d", i, expected)
}
}
@@ -897,7 +896,7 @@ func TestJsonARRAPPEND(t *testing.T) {
if tcase.assertType[i] == "equal" {
assert.Equal(t, out, result)
} else if tcase.assertType[i] == "deep_equal" {
- assert.Assert(t, arraysArePermutations(out.([]interface{}), result.([]interface{})))
+ assert.True(t, arraysArePermutations(out.([]interface{}), result.([]interface{})))
}
}
})
@@ -975,7 +974,7 @@ func TestJsonNummultby(t *testing.T) {
if tcase.assertType[i] == "equal" {
assert.Equal(t, out, result)
} else if tcase.assertType[i] == "deep_equal" {
- assert.Assert(t, arraysArePermutations(deStringify(out.(string)), deStringify(result.(string))))
+ assert.True(t, arraysArePermutations(deStringify(out.(string)), deStringify(result.(string))))
}
}
})
@@ -1095,7 +1094,8 @@ func TestJsonObjLen(t *testing.T) {
cmd := tcase.commands[i]
out := tcase.expected[i]
result := FireCommand(conn, cmd)
- assert.DeepEqual(t, out, result)
+
+ assert.Equal(t, out, result);
}
})
}
@@ -1187,11 +1187,11 @@ func TestJSONNumIncrBy(t *testing.T) {
case "equal":
assert.Equal(t, out, result)
case "perm_equal":
- assert.Assert(t, arraysArePermutations(convertToArray(out.(string)), convertToArray(result.(string))))
+ assert.True(t, arraysArePermutations(convertToArray(out.(string)), convertToArray(result.(string))))
case "range":
- assert.Assert(t, result.(int64) <= tc.expected[i].(int64) && result.(int64) > 0, "Expected %v to be within 0 to %v", result, tc.expected[i])
+ assert.True(t, result.(int64) <= tc.expected[i].(int64) && result.(int64) > 0, "Expected %v to be within 0 to %v", result, tc.expected[i])
case "json_equal":
- testifyAssert.JSONEq(t, out.(string), result.(string))
+ assert.JSONEq(t, out.(string), result.(string))
}
}
for i := 0; i < len(tc.cleanUp); i++ {
@@ -1256,7 +1256,7 @@ func TestJsonSTRAPPEND(t *testing.T) {
assert.Equal(t, "OK", result)
result = FireCommand(conn, tc.getCmd)
- assert.DeepEqual(t, tc.expected, result)
+ assert.ElementsMatch(t, tc.expected, result)
})
}
diff --git a/integration_tests/commands/async/jsonresp_test.go b/integration_tests/commands/async/jsonresp_test.go
index 2a1ee58d4..82d72c216 100644
--- a/integration_tests/commands/async/jsonresp_test.go
+++ b/integration_tests/commands/async/jsonresp_test.go
@@ -3,8 +3,7 @@ package async
import (
"testing"
- testifyAssert "github.com/stretchr/testify/assert"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func TestJSONRESP(t *testing.T) {
@@ -52,9 +51,9 @@ func TestJSONRESP(t *testing.T) {
result := FireCommand(conn, cmd)
if tcase.assert_type[i] == "equal" {
- testifyAssert.Equal(t, out, result)
+ assert.Equal(t, out, result)
} else if tcase.assert_type[i] == "deep_equal" {
- assert.Assert(t, arraysArePermutations(out.([]interface{}), result.([]interface{})))
+ assert.True(t, arraysArePermutations(out.([]interface{}), result.([]interface{})))
}
}
})
diff --git a/integration_tests/commands/async/keys_test.go b/integration_tests/commands/async/keys_test.go
index 654a4942b..9cd90f504 100644
--- a/integration_tests/commands/async/keys_test.go
+++ b/integration_tests/commands/async/keys_test.go
@@ -4,7 +4,7 @@ import (
"testing"
"github.com/dicedb/dice/testutils"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func TestKeys(t *testing.T) {
@@ -54,9 +54,9 @@ func TestKeys(t *testing.T) {
// because the order of keys is not guaranteed, we need to check if the result is an array
if slice, ok := tc.expected[i].([]interface{}); ok {
- assert.Assert(t, testutils.UnorderedEqual(slice, result))
+ assert.True(t, testutils.UnorderedEqual(slice, result))
} else {
- assert.DeepEqual(t, tc.expected[i], result)
+ assert.Equal(t, tc.expected[i], result)
}
}
diff --git a/integration_tests/commands/async/mget_test.go b/integration_tests/commands/async/mget_test.go
index f7a2dbdff..715c372ee 100644
--- a/integration_tests/commands/async/mget_test.go
+++ b/integration_tests/commands/async/mget_test.go
@@ -4,7 +4,7 @@ import (
"testing"
"github.com/dicedb/dice/testutils"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func TestMGET(t *testing.T) {
@@ -49,9 +49,9 @@ func TestMGET(t *testing.T) {
for i, cmd := range tc.commands {
result := FireCommand(conn, cmd)
if slice, ok := tc.expected[i].([]interface{}); ok {
- assert.Assert(t, testutils.UnorderedEqual(slice, result))
+ assert.True(t, testutils.UnorderedEqual(slice, result))
} else {
- assert.DeepEqual(t, tc.expected[i], result)
+ assert.Equal(t, tc.expected[i], result)
}
}
})
diff --git a/integration_tests/commands/async/mset_test.go b/integration_tests/commands/async/mset_test.go
index 1b1413b53..d0d8ba647 100644
--- a/integration_tests/commands/async/mset_test.go
+++ b/integration_tests/commands/async/mset_test.go
@@ -3,7 +3,7 @@ package async
import (
"testing"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func TestMset(t *testing.T) {
@@ -40,7 +40,7 @@ func TestMset(t *testing.T) {
for i, cmd := range tc.commands {
result := FireCommand(conn, cmd)
- assert.DeepEqual(t, tc.expected[i], result)
+ assert.Equal(t, tc.expected[i], result)
}
})
}
@@ -90,7 +90,7 @@ func TestMSETInconsistency(t *testing.T) {
for i, cmd := range tc.commands {
result := FireCommand(conn, cmd)
- assert.DeepEqual(t, tc.expected[i], result)
+ assert.Equal(t, tc.expected[i], result)
}
})
}
diff --git a/integration_tests/commands/async/object_test.go b/integration_tests/commands/async/object_test.go
index dd44dd032..93329411d 100644
--- a/integration_tests/commands/async/object_test.go
+++ b/integration_tests/commands/async/object_test.go
@@ -5,7 +5,7 @@ import (
"testing"
"time"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func TestObjectCommand(t *testing.T) {
@@ -126,9 +126,9 @@ func TestObjectCommand(t *testing.T) {
fmt.Println(cmd, result, tc.expected[i])
if tc.assertType[i] == "equal" {
- assert.DeepEqual(t, tc.expected[i], result)
+ assert.Equal(t, tc.expected[i], result)
} else {
- assert.Assert(t, result.(int64) >= tc.expected[i].(int64), "Expected %v to be less than or equal to %v", result, tc.expected[i])
+ assert.True(t, result.(int64) >= tc.expected[i].(int64), "Expected %v to be less than or equal to %v", result, tc.expected[i])
}
}
for _, cmd := range tc.cleanup { // run cleanup
diff --git a/integration_tests/commands/async/qunwatch_test.go b/integration_tests/commands/async/qunwatch_test.go
index b425b7f4f..536875d56 100644
--- a/integration_tests/commands/async/qunwatch_test.go
+++ b/integration_tests/commands/async/qunwatch_test.go
@@ -8,7 +8,7 @@ import (
"github.com/dicedb/dice/internal/clientio"
"github.com/dicedb/dice/internal/sql"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
// need to test the following here:
@@ -40,7 +40,7 @@ func TestQWatchUnwatch(t *testing.T) {
// Check if the response is OK
resp, err := rp.DecodeOne()
- assert.NilError(t, err)
+ assert.Nil(t, err)
assert.Equal(t, 3, len(resp.([]interface{})))
}
@@ -51,7 +51,7 @@ func TestQWatchUnwatch(t *testing.T) {
for _, sub := range subscribers[0:2] {
rp := fireCommandAndGetRESPParser(sub, "Q.UNWATCH \""+qWatchQuery+"\"")
resp, err := rp.DecodeOne()
- assert.NilError(t, err)
+ assert.Nil(t, err)
assert.Equal(t, "OK", resp)
}
@@ -60,21 +60,21 @@ func TestQWatchUnwatch(t *testing.T) {
// continue from the qwatch scenarios that ran previously
FireCommand(publisher, "SET match:100:user:1 62")
resp, err := respParsers[2].DecodeOne()
- assert.NilError(t, err)
+ assert.Nil(t, err)
expectedUpdate := []interface{}{[]interface{}{"match:100:user:5", int64(70)}, []interface{}{"match:100:user:1", int64(62)}, []interface{}{"match:100:user:0", int64(60)}}
- assert.DeepEqual(t, []interface{}{sql.Qwatch, qWatchQuery, expectedUpdate}, resp)
+ assert.Equal(t, []interface{}{sql.Qwatch, qWatchQuery, expectedUpdate}, resp)
FireCommand(publisher, "SET match:100:user:5 75")
resp, err = respParsers[2].DecodeOne()
- assert.NilError(t, err)
+ assert.Nil(t, err)
expectedUpdate = []interface{}{[]interface{}{"match:100:user:5", int64(75)}, []interface{}{"match:100:user:1", int64(62)}, []interface{}{"match:100:user:0", int64(60)}}
- assert.DeepEqual(t, []interface{}{sql.Qwatch, qWatchQuery, expectedUpdate}, resp)
+ assert.Equal(t, []interface{}{sql.Qwatch, qWatchQuery, expectedUpdate}, resp)
FireCommand(publisher, "SET match:100:user:0 80")
resp, err = respParsers[2].DecodeOne()
- assert.NilError(t, err)
+ assert.Nil(t, err)
expectedUpdate = []interface{}{[]interface{}{"match:100:user:0", int64(80)}, []interface{}{"match:100:user:5", int64(75)}, []interface{}{"match:100:user:1", int64(62)}}
- assert.DeepEqual(t, []interface{}{sql.Qwatch, qWatchQuery, expectedUpdate}, resp)
+ assert.Equal(t, []interface{}{sql.Qwatch, qWatchQuery, expectedUpdate}, resp)
// Cleanup store for next tests
for _, tc := range qWatchTestCases {
diff --git a/integration_tests/commands/async/qwatch_test.go b/integration_tests/commands/async/qwatch_test.go
index 119eb9019..87e08cfd3 100644
--- a/integration_tests/commands/async/qwatch_test.go
+++ b/integration_tests/commands/async/qwatch_test.go
@@ -11,7 +11,7 @@ import (
"github.com/dicedb/dice/internal/clientio"
"github.com/dicedb/dice/internal/sql"
dicedb "github.com/dicedb/dicedb-go"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
type qWatchTestCase struct {
@@ -142,11 +142,11 @@ func subscribeToQWATCH(t *testing.T, subscribers []net.Conn, query string) []*cl
respParsers := make([]*clientio.RESPParser, len(subscribers))
for i, subscriber := range subscribers {
rp := fireCommandAndGetRESPParser(subscriber, fmt.Sprintf("Q.WATCH \"%s\"", query))
- assert.Assert(t, rp != nil)
+ assert.True(t, rp != nil)
respParsers[i] = rp
v, err := rp.DecodeOne()
- assert.NilError(t, err)
+ assert.Nil(t, err)
castedValue, ok := v.([]interface{})
if !ok {
t.Errorf("Type assertion to []interface{} failed for value: %v", v)
@@ -164,9 +164,9 @@ func subscribeToQWATCHWithSDK(t *testing.T, subscribers []qWatchSDKSubscriber) [
for i, subscriber := range subscribers {
qwatch := subscriber.client.QWatch(ctx)
subscribers[i].qwatch = qwatch
- assert.Assert(t, qwatch != nil)
+ assert.True(t, qwatch != nil)
err := qwatch.WatchQuery(ctx, qWatchQuery)
- assert.NilError(t, err)
+ assert.Nil(t, err)
channels[i] = qwatch.Channel()
<-channels[i] // Get the first message
}
@@ -188,7 +188,7 @@ func publishUpdate(t *testing.T, publisher interface{}, tc qWatchTestCase) {
FireCommand(p, fmt.Sprintf("SET %s %d", key, tc.score))
case *dicedb.Client:
err := p.Set(context.Background(), key, tc.score, 0).Err()
- assert.NilError(t, err)
+ assert.Nil(t, err)
}
}
@@ -206,13 +206,13 @@ func verifyUpdates(t *testing.T, receivers interface{}, expectedUpdates [][]inte
func verifyRESPUpdates(t *testing.T, respParsers []*clientio.RESPParser, expectedUpdate []interface{}, query string) {
for _, rp := range respParsers {
v, err := rp.DecodeOne()
- assert.NilError(t, err)
+ assert.Nil(t, err)
update, ok := v.([]interface{})
if !ok {
t.Errorf("Type assertion to []interface{} failed for value: %v", v)
return
}
- assert.DeepEqual(t, []interface{}{sql.Qwatch, query, expectedUpdate}, update)
+ assert.Equal(t, []interface{}{sql.Qwatch, query, expectedUpdate}, update)
}
}
@@ -221,7 +221,7 @@ func verifySDKUpdates(t *testing.T, channels []<-chan *dicedb.QMessage, expected
v := <-ch
assert.Equal(t, len(v.Updates), len(expectedUpdate), v.Updates)
for i, update := range v.Updates {
- assert.DeepEqual(t, expectedUpdate[i], []interface{}{update.Key, update.Value})
+ assert.Equal(t, expectedUpdate[i], []interface{}{update.Key, update.Value})
}
}
}
@@ -339,11 +339,11 @@ func subscribeToJSONQueries(t *testing.T, subscribers []net.Conn, tests []JSONTe
respParsers := make([]*clientio.RESPParser, len(subscribers))
for i, testCase := range tests {
rp := fireCommandAndGetRESPParser(subscribers[i], fmt.Sprintf("Q.WATCH \"%s\"", testCase.qwatchQuery))
- assert.Assert(t, rp != nil)
+ assert.True(t, rp != nil)
respParsers[i] = rp
v, err := rp.DecodeOne()
- assert.NilError(t, err)
+ assert.Nil(t, err)
assert.Equal(t, 3, len(v.([]interface{})), fmt.Sprintf("Expected 3 elements, got %v", v))
}
return respParsers
@@ -359,7 +359,7 @@ func runJSONScenarios(t *testing.T, publisher net.Conn, respParsers []*clientio.
func verifyJSONUpdates(t *testing.T, rp *clientio.RESPParser, tc JSONTestCase) {
for _, expectedUpdate := range tc.expectedUpdates {
v, err := rp.DecodeOne()
- assert.NilError(t, err)
+ assert.Nil(t, err)
response, ok := v.([]interface{})
if !ok {
t.Errorf("Type assertion to []interface{} failed for value: %v", v)
@@ -377,9 +377,9 @@ func verifyJSONUpdates(t *testing.T, rp *clientio.RESPParser, tc JSONTestCase) {
assert.Equal(t, expectedUpdate[0].([]interface{})[0], update[0].([]interface{})[0], "Key mismatch")
var expectedJSON, actualJSON interface{}
- assert.NilError(t, sonic.UnmarshalString(tc.value, &expectedJSON))
- assert.NilError(t, sonic.UnmarshalString(update[0].([]interface{})[1].(string), &actualJSON))
- assert.DeepEqual(t, expectedJSON, actualJSON)
+ assert.Nil(t, sonic.UnmarshalString(tc.value, &expectedJSON))
+ assert.Nil(t, sonic.UnmarshalString(update[0].([]interface{})[1].(string), &actualJSON))
+ assert.Equal(t, expectedJSON, actualJSON)
}
}
@@ -418,10 +418,10 @@ func setupJSONOrderByTest(t *testing.T) (net.Conn, net.Conn, func(), string) {
func subscribeToJSONOrderByQuery(t *testing.T, subscriber net.Conn, watchquery string) *clientio.RESPParser {
rp := fireCommandAndGetRESPParser(subscriber, fmt.Sprintf("Q.WATCH \"%s\"", watchquery))
- assert.Assert(t, rp != nil)
+ assert.True(t, rp != nil)
v, err := rp.DecodeOne()
- assert.NilError(t, err)
+ assert.Nil(t, err)
assert.Equal(t, 3, len(v.([]interface{})), fmt.Sprintf("Expected 3 elements, got %v", v))
return rp
@@ -483,11 +483,11 @@ func verifyJSONOrderByUpdates(t *testing.T, rp *clientio.RESPParser, tc struct {
// Decode the response
v, err := rp.DecodeOne()
- assert.NilError(t, err, "Failed to decode response")
+ assert.Nil(t, err, "Failed to decode response")
// Cast the response to []interface{}
response, ok := v.([]interface{})
- assert.Assert(t, ok, "Response is not of type []interface{}: %v", v)
+ assert.True(t, ok, "Response is not of type []interface{}: %v", v)
// Verify response structure
assert.Equal(t, 3, len(response), "Expected response to have 3 elements")
@@ -495,7 +495,7 @@ func verifyJSONOrderByUpdates(t *testing.T, rp *clientio.RESPParser, tc struct {
// Extract updates from the response
updates, ok := response[2].([]interface{})
- assert.Assert(t, ok, "Updates are not of type []interface{}: %v", response[2])
+ assert.True(t, ok, "Updates are not of type []interface{}: %v", response[2])
// Verify number of updates
assert.Equal(t, len(expectedUpdates), len(updates),
@@ -504,7 +504,7 @@ func verifyJSONOrderByUpdates(t *testing.T, rp *clientio.RESPParser, tc struct {
// Verify each update
for i, expectedRow := range expectedUpdates {
actualRow, ok := updates[i].([]interface{})
- assert.Assert(t, ok, "Update row is not of type []interface{}: %v", updates[i])
+ assert.True(t, ok, "Update row is not of type []interface{}: %v", updates[i])
// Verify key
assert.Equal(t, expectedRow.([]interface{})[0], actualRow[0],
@@ -513,9 +513,9 @@ func verifyJSONOrderByUpdates(t *testing.T, rp *clientio.RESPParser, tc struct {
// Verify JSON value
var actualJSON interface{}
err := sonic.UnmarshalString(actualRow[1].(string), &actualJSON)
- assert.NilError(t, err, "Failed to unmarshal JSON at index %d", i)
+ assert.Nil(t, err, "Failed to unmarshal JSON at index %d", i)
- assert.DeepEqual(t, expectedRow.([]interface{})[1], actualJSON)
+ assert.Equal(t, expectedRow.([]interface{})[1], actualJSON)
}
}
diff --git a/integration_tests/commands/async/select_test.go b/integration_tests/commands/async/select_test.go
index ff7beb4ed..e016339f9 100644
--- a/integration_tests/commands/async/select_test.go
+++ b/integration_tests/commands/async/select_test.go
@@ -3,7 +3,7 @@ package async
import (
"testing"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func TestSelect(t *testing.T) {
@@ -12,11 +12,11 @@ func TestSelect(t *testing.T) {
t.Run("SELECT command response", func(t *testing.T) {
actual := FireCommand(conn, "SELECT 1")
- assert.DeepEqual(t, "OK", actual)
+ assert.Equal(t, "OK", actual)
})
t.Run("SELECT command error response", func(t *testing.T) {
actual := FireCommand(conn, "SELECT")
- assert.DeepEqual(t, "ERR wrong number of arguments for 'select' command", actual)
+ assert.Equal(t, "ERR wrong number of arguments for 'select' command", actual)
})
}
diff --git a/integration_tests/commands/async/set_data_cmd_test.go b/integration_tests/commands/async/set_data_cmd_test.go
index 9dad64c19..5b16dde3b 100644
--- a/integration_tests/commands/async/set_data_cmd_test.go
+++ b/integration_tests/commands/async/set_data_cmd_test.go
@@ -5,12 +5,12 @@ import (
"testing"
"time"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func CustomDeepEqual(t *testing.T, a, b interface{}) {
if a == nil || b == nil {
- assert.DeepEqual(t, a, b)
+ assert.Equal(t, a, b)
}
switch a.(type) {
@@ -23,7 +23,7 @@ func CustomDeepEqual(t *testing.T, a, b interface{}) {
})
}
- assert.DeepEqual(t, a, b)
+ assert.Equal(t, a, b)
}
func TestSetDataCommand(t *testing.T) {
conn := getLocalConnection()
@@ -122,7 +122,7 @@ func TestSetDataCommand(t *testing.T) {
if tc.assertType[i] == "equal" {
CustomDeepEqual(t, result, tc.expected[i])
} else if tc.assertType[i] == "assert" {
- assert.Assert(t, result.(int64) <= tc.expected[i].(int64), "Expected %v to be less than or equal to %v", result, tc.expected[i])
+ assert.True(t, result.(int64) <= tc.expected[i].(int64), "Expected %v to be less than or equal to %v", result, tc.expected[i])
}
}
})
diff --git a/integration_tests/commands/async/set_test.go b/integration_tests/commands/async/set_test.go
index 18476a3de..fe9710e42 100644
--- a/integration_tests/commands/async/set_test.go
+++ b/integration_tests/commands/async/set_test.go
@@ -169,12 +169,13 @@ func TestSetWithExat(t *testing.T) {
func TestWithKeepTTLFlag(t *testing.T) {
conn := getLocalConnection()
+ expiryTime := strconv.FormatInt(time.Now().Add(1*time.Minute).UnixMilli(), 10)
defer conn.Close()
for _, tcase := range []TestCase{
{
- commands: []string{"SET k v EX 2", "SET k vv KEEPTTL", "GET k", "SET kk vv", "SET kk vvv KEEPTTL", "GET kk"},
- expected: []interface{}{"OK", "OK", "vv", "OK", "OK", "vvv"},
+ commands: []string{"SET k v EX 2", "SET k vv KEEPTTL", "GET k", "SET kk vv", "SET kk vvv KEEPTTL", "GET kk", "SET K V EX 2 KEEPTTL", "SET K1 vv PX 2000 KEEPTTL", "SET K2 vv EXAT " + expiryTime + " KEEPTTL"},
+ expected: []interface{}{"OK", "OK", "vv", "OK", "OK", "vvv", "ERR syntax error", "ERR syntax error", "ERR syntax error"},
},
} {
for i := 0; i < len(tcase.commands); i++ {
diff --git a/integration_tests/commands/async/toggle_test.go b/integration_tests/commands/async/toggle_test.go
index 3e32663f6..c7846f211 100644
--- a/integration_tests/commands/async/toggle_test.go
+++ b/integration_tests/commands/async/toggle_test.go
@@ -6,7 +6,7 @@ import (
"testing"
"github.com/dicedb/dice/testutils"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func compareJSON(t *testing.T, expected, actual string) {
@@ -16,10 +16,10 @@ func compareJSON(t *testing.T, expected, actual string) {
err1 := json.Unmarshal([]byte(expected), &expectedMap)
err2 := json.Unmarshal([]byte(actual), &actualMap)
- assert.NilError(t, err1)
- assert.NilError(t, err2)
+ assert.Nil(t, err1)
+ assert.Nil(t, err2)
- assert.DeepEqual(t, expectedMap, actualMap)
+ assert.Equal(t, expectedMap, actualMap)
}
func TestJSONToggle(t *testing.T) {
@@ -84,9 +84,9 @@ func TestJSONToggle(t *testing.T) {
assert.Equal(t, expected, result)
}
case []interface{}:
- assert.Assert(t, testutils.UnorderedEqual(expected, result))
+ assert.True(t, testutils.UnorderedEqual(expected, result))
default:
- assert.DeepEqual(t, expected, result)
+ assert.Equal(t, expected, result)
}
}
})
diff --git a/integration_tests/commands/async/touch_test.go b/integration_tests/commands/async/touch_test.go
index bf51f5088..5efe3f259 100644
--- a/integration_tests/commands/async/touch_test.go
+++ b/integration_tests/commands/async/touch_test.go
@@ -4,7 +4,7 @@ import (
"testing"
"time"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func TestTouch(t *testing.T) {
@@ -52,9 +52,9 @@ func TestTouch(t *testing.T) {
}
result := FireCommand(conn, cmd)
if tc.assertType[i] == "equal" {
- assert.DeepEqual(t, tc.expected[i], result)
+ assert.Equal(t, tc.expected[i], result)
} else {
- assert.Assert(t, result.(int64) >= tc.expected[i].(int64), "Expected %v to be less than or equal to %v", result, tc.expected[i])
+ assert.True(t, result.(int64) >= tc.expected[i].(int64), "Expected %v to be less than or equal to %v", result, tc.expected[i])
}
}
})
diff --git a/integration_tests/commands/async/type_test.go b/integration_tests/commands/async/type_test.go
index 5f8bcbb00..4ba168cc9 100644
--- a/integration_tests/commands/async/type_test.go
+++ b/integration_tests/commands/async/type_test.go
@@ -3,7 +3,7 @@ package async
import (
"testing"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func TestType(t *testing.T) {
diff --git a/integration_tests/commands/http/append_test.go b/integration_tests/commands/http/append_test.go
index fc2473055..8a1236be6 100644
--- a/integration_tests/commands/http/append_test.go
+++ b/integration_tests/commands/http/append_test.go
@@ -3,7 +3,7 @@ package http
import (
"testing"
- testifyAssert "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/assert"
)
func TestAPPEND(t *testing.T) {
@@ -96,7 +96,7 @@ func TestAPPEND(t *testing.T) {
for i, cmd := range tc.commands {
result, _ := exec.FireCommand(cmd)
- testifyAssert.Equal(t, tc.expected[i], result)
+ assert.Equal(t, tc.expected[i], result)
}
exec.FireCommand(tc.cleanup[0])
})
diff --git a/integration_tests/commands/http/bit_test.go b/integration_tests/commands/http/bit_test.go
index f318c9132..783ec4249 100644
--- a/integration_tests/commands/http/bit_test.go
+++ b/integration_tests/commands/http/bit_test.go
@@ -2,7 +2,7 @@ package http
import (
"fmt"
- testifyAssert "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/assert"
"testing"
)
@@ -98,7 +98,7 @@ func TestBitPos(t *testing.T) {
}
result, _ := exec.FireCommand(tc.inCmd)
- testifyAssert.Equal(t, tc.out, result, "Mismatch for cmd %s\n", tc.inCmd)
+ assert.Equal(t, tc.out, result, "Mismatch for cmd %s\n", tc.inCmd)
})
}
}
diff --git a/integration_tests/commands/http/bloom_test.go b/integration_tests/commands/http/bloom_test.go
index c29b782ea..f0cae9a12 100644
--- a/integration_tests/commands/http/bloom_test.go
+++ b/integration_tests/commands/http/bloom_test.go
@@ -3,7 +3,7 @@ package http
import (
"testing"
- assert "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/assert"
)
func TestBloomFilter(t *testing.T) {
diff --git a/integration_tests/commands/http/check_type_test.go b/integration_tests/commands/http/check_type_test.go
index 7353b36cd..81127f284 100644
--- a/integration_tests/commands/http/check_type_test.go
+++ b/integration_tests/commands/http/check_type_test.go
@@ -4,7 +4,7 @@ import (
"testing"
"time"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
// This file may contain test cases for checking error messages across all commands
diff --git a/integration_tests/commands/http/command_count_test.go b/integration_tests/commands/http/command_count_test.go
index 265d3a7be..dd9e55338 100644
--- a/integration_tests/commands/http/command_count_test.go
+++ b/integration_tests/commands/http/command_count_test.go
@@ -3,7 +3,7 @@ package http
import (
"testing"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func TestCommandCount(t *testing.T) {
@@ -39,9 +39,9 @@ func TestCommandCount(t *testing.T) {
result, _ := exec.FireCommand(cmd)
switch tc.assertType[c] {
case "equal":
- assert.DeepEqual(t, tc.expected[c], result)
+ assert.Equal(t, tc.expected[c], result)
case "greater":
- assert.Assert(t, result.(float64) >= tc.expected[c].(float64))
+ assert.True(t, result.(float64) >= tc.expected[c].(float64))
}
}
diff --git a/integration_tests/commands/http/command_default_test.go b/integration_tests/commands/http/command_default_test.go
index e3d2c8dfe..c4a67a8c7 100644
--- a/integration_tests/commands/http/command_default_test.go
+++ b/integration_tests/commands/http/command_default_test.go
@@ -5,19 +5,19 @@ import (
"testing"
"github.com/dicedb/dice/internal/eval"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func TestCommandDefault(t *testing.T) {
exec := NewHTTPCommandExecutor()
commands := getCommandDefault(exec)
t.Run("Command should not be empty", func(t *testing.T) {
- assert.Assert(t, len(commands) > 0,
+ assert.True(t, len(commands) > 0,
fmt.Sprintf("Unexpected number of CLI commands found. expected greater than 0, %d found", len(commands)))
})
t.Run("Command count matches", func(t *testing.T) {
- assert.Assert(t, len(commands) == len(eval.DiceCmds),
+ assert.True(t, len(commands) == len(eval.DiceCmds),
fmt.Sprintf("Unexpected number of CLI commands found. expected %d, %d found", len(eval.DiceCmds), len(commands)))
})
}
diff --git a/integration_tests/commands/http/command_getkeys_test.go b/integration_tests/commands/http/command_getkeys_test.go
index f2cca2161..906117296 100644
--- a/integration_tests/commands/http/command_getkeys_test.go
+++ b/integration_tests/commands/http/command_getkeys_test.go
@@ -3,7 +3,7 @@ package http
import (
"testing"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func TestCommandGetKeys(t *testing.T) {
@@ -93,7 +93,7 @@ func TestCommandGetKeys(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
for i, cmd := range tc.commands {
result, _ := exec.FireCommand(cmd)
- assert.DeepEqual(t, tc.expected[i], result)
+ assert.Equal(t, tc.expected[i], result)
}
})
}
diff --git a/integration_tests/commands/http/command_help_test.go b/integration_tests/commands/http/command_help_test.go
index 79ac368e3..d39af3044 100644
--- a/integration_tests/commands/http/command_help_test.go
+++ b/integration_tests/commands/http/command_help_test.go
@@ -3,7 +3,7 @@ package http
import (
"testing"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func TestCommandHelp(t *testing.T) {
@@ -23,7 +23,7 @@ func TestCommandHelp(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
for _, cmd := range tc.commands {
result, _ := exec.FireCommand(cmd)
- assert.DeepEqual(t, tc.expected[i], result)
+ assert.Equal(t, tc.expected[i], result)
}
diff --git a/integration_tests/commands/http/command_info_test.go b/integration_tests/commands/http/command_info_test.go
index a0f3726a8..034258007 100644
--- a/integration_tests/commands/http/command_info_test.go
+++ b/integration_tests/commands/http/command_info_test.go
@@ -3,7 +3,7 @@ package http
import (
"testing"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func TestCommandInfo(t *testing.T) {
@@ -64,7 +64,7 @@ func TestCommandInfo(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
for i, cmd := range tc.commands {
result, _ := exec.FireCommand(cmd)
- assert.DeepEqual(t, tc.expected[i], result)
+ assert.Equal(t, tc.expected[i], result)
}
})
}
diff --git a/integration_tests/commands/http/command_list_test.go b/integration_tests/commands/http/command_list_test.go
index 8740b3332..87ed4508f 100644
--- a/integration_tests/commands/http/command_list_test.go
+++ b/integration_tests/commands/http/command_list_test.go
@@ -3,7 +3,7 @@ package http
import (
"testing"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func TestCommandList(t *testing.T) {
@@ -23,10 +23,8 @@ func TestCommandList(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
for _, cmd := range tc.commands {
result, _ := exec.FireCommand(cmd)
- assert.DeepEqual(t, tc.expected[i], result)
-
+ assert.Equal(t, tc.expected[i], result)
}
-
})
}
}
diff --git a/integration_tests/commands/http/command_rename_test.go b/integration_tests/commands/http/command_rename_test.go
index 0bc9f3ddb..caef9ed8c 100644
--- a/integration_tests/commands/http/command_rename_test.go
+++ b/integration_tests/commands/http/command_rename_test.go
@@ -3,7 +3,7 @@ package http
import (
"testing"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func TestCommandRename(t *testing.T) {
@@ -62,7 +62,7 @@ func TestCommandRename(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
for i, cmd := range tc.commands {
result, _ := exec.FireCommand(cmd)
- assert.DeepEqual(t, tc.expected[i], result)
+ assert.Equal(t, tc.expected[i], result)
}
})
}
diff --git a/integration_tests/commands/http/copy_test.go b/integration_tests/commands/http/copy_test.go
index 7e6d90850..51fdaa41a 100644
--- a/integration_tests/commands/http/copy_test.go
+++ b/integration_tests/commands/http/copy_test.go
@@ -5,7 +5,7 @@ import (
"github.com/dicedb/dice/testutils"
- testifyAssert "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/assert"
)
func TestCopy(t *testing.T) {
@@ -110,20 +110,20 @@ func TestCopy(t *testing.T) {
result, _ := exec.FireCommand(cmd)
if result == nil {
- testifyAssert.Equal(t, tc.expected[i], result, "Expected result to be nil for command %v", cmd)
+ assert.Equal(t, tc.expected[i], result, "Expected result to be nil for command %v", cmd)
continue
}
if floatResult, ok := result.(float64); ok {
- testifyAssert.Equal(t, tc.expected[i], floatResult, "Mismatch for command %v", cmd)
+ assert.Equal(t, tc.expected[i], floatResult, "Mismatch for command %v", cmd)
continue
}
if resultStr, ok := result.(string); ok {
if testutils.IsJSONResponse(resultStr) {
- testifyAssert.JSONEq(t, tc.expected[i].(string), resultStr, "Mismatch in JSON response for command %v", cmd)
+ assert.JSONEq(t, tc.expected[i].(string), resultStr, "Mismatch in JSON response for command %v", cmd)
} else {
- testifyAssert.Equal(t, tc.expected[i], resultStr, "Mismatch for command %v", cmd)
+ assert.Equal(t, tc.expected[i], resultStr, "Mismatch for command %v", cmd)
}
} else {
t.Fatalf("command %v returned unexpected type: %T", cmd, result)
diff --git a/integration_tests/commands/http/countminsketch_test.go b/integration_tests/commands/http/countminsketch_test.go
index 5f6ff09d4..bc051a147 100644
--- a/integration_tests/commands/http/countminsketch_test.go
+++ b/integration_tests/commands/http/countminsketch_test.go
@@ -4,7 +4,7 @@ import (
"log"
"testing"
- assert "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/assert"
)
func TestCMSInitByDim(t *testing.T) {
diff --git a/integration_tests/commands/http/dbsize_test.go b/integration_tests/commands/http/dbsize_test.go
index e5393a621..a76929d66 100644
--- a/integration_tests/commands/http/dbsize_test.go
+++ b/integration_tests/commands/http/dbsize_test.go
@@ -3,7 +3,7 @@ package http
import (
"testing"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func TestDBSize(t *testing.T) {
@@ -66,7 +66,7 @@ func TestDBSize(t *testing.T) {
for i, cmd := range tc.commands {
result, _ := exec.FireCommand(cmd)
- assert.DeepEqual(t, tc.expected[i], result)
+ assert.Equal(t, tc.expected[i], result)
}
})
}
diff --git a/integration_tests/commands/http/decr_test.go b/integration_tests/commands/http/decr_test.go
index 585d91d96..a41fc53d3 100644
--- a/integration_tests/commands/http/decr_test.go
+++ b/integration_tests/commands/http/decr_test.go
@@ -6,7 +6,7 @@ import (
"testing"
"time"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func TestDECR(t *testing.T) {
diff --git a/integration_tests/commands/http/del_test.go b/integration_tests/commands/http/del_test.go
index f9732fb05..c7f8d693c 100644
--- a/integration_tests/commands/http/del_test.go
+++ b/integration_tests/commands/http/del_test.go
@@ -1,7 +1,7 @@
package http
import (
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
"testing"
)
@@ -53,7 +53,7 @@ func TestDel(t *testing.T) {
for i, cmd := range tc.commands {
result, _ := exec.FireCommand(cmd)
- assert.DeepEqual(t, tc.expected[i], result)
+ assert.Equal(t, tc.expected[i], result)
}
})
}
diff --git a/integration_tests/commands/http/deque_test.go b/integration_tests/commands/http/deque_test.go
index 23a577d15..234d551f0 100644
--- a/integration_tests/commands/http/deque_test.go
+++ b/integration_tests/commands/http/deque_test.go
@@ -6,7 +6,7 @@ import (
"testing"
"time"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
var deqRandGenerator *rand.Rand
@@ -103,7 +103,7 @@ func TestLPush(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
for i, cmd := range tc.commands {
result, _ := exec.FireCommand(cmd)
- assert.DeepEqual(t, tc.expected[i], result)
+ assert.Equal(t, tc.expected[i], result)
}
})
}
@@ -146,7 +146,7 @@ func TestRPush(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
for i, cmd := range tc.commands {
result, _ := exec.FireCommand(cmd)
- assert.DeepEqual(t, tc.expected[i], result)
+ assert.Equal(t, tc.expected[i], result)
}
})
}
@@ -224,7 +224,7 @@ func TestLPushLPop(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
for i, cmd := range tc.commands {
result, _ := exec.FireCommand(cmd)
- assert.DeepEqual(t, tc.expected[i], result)
+ assert.Equal(t, tc.expected[i], result)
}
})
}
@@ -303,7 +303,7 @@ func TestLPushRPop(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
for i, cmd := range tc.commands {
result, _ := exec.FireCommand(cmd)
- assert.DeepEqual(t, tc.expected[i], result)
+ assert.Equal(t, tc.expected[i], result)
}
})
}
@@ -382,7 +382,7 @@ func TestRPushLPop(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
for i, cmd := range tc.commands {
result, _ := exec.FireCommand(cmd)
- assert.DeepEqual(t, tc.expected[i], result)
+ assert.Equal(t, tc.expected[i], result)
}
})
}
@@ -461,7 +461,7 @@ func TestRPushRPop(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
for i, cmd := range tc.commands {
result, _ := exec.FireCommand(cmd)
- assert.DeepEqual(t, tc.expected[i], result)
+ assert.Equal(t, tc.expected[i], result)
}
})
}
@@ -502,7 +502,7 @@ func TestLRPushLRPop(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
for i, cmd := range tc.commands {
result, _ := exec.FireCommand(cmd)
- assert.DeepEqual(t, tc.expected[i], result)
+ assert.Equal(t, tc.expected[i], result)
}
})
}
@@ -549,7 +549,7 @@ func TestLLEN(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
for i, cmd := range tc.commands {
result, _ := exec.FireCommand(cmd)
- assert.DeepEqual(t, tc.expected[i], result)
+ assert.Equal(t, tc.expected[i], result)
}
})
}
diff --git a/integration_tests/commands/http/echo_test.go b/integration_tests/commands/http/echo_test.go
index 17af745b6..d2f8db08c 100644
--- a/integration_tests/commands/http/echo_test.go
+++ b/integration_tests/commands/http/echo_test.go
@@ -3,7 +3,7 @@ package http
import (
"testing"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func TestEchoHttp(t *testing.T) {
@@ -36,7 +36,7 @@ func TestEchoHttp(t *testing.T) {
for i, cmd := range tc.commands {
result, _ := exec.FireCommand(cmd)
- assert.DeepEqual(t, tc.expected[i], result)
+ assert.Equal(t, tc.expected[i], result)
}
})
}
diff --git a/integration_tests/commands/http/exists_test.go b/integration_tests/commands/http/exists_test.go
index 52b1ab893..a7da3bd66 100644
--- a/integration_tests/commands/http/exists_test.go
+++ b/integration_tests/commands/http/exists_test.go
@@ -5,7 +5,7 @@ import (
"testing"
"time"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func TestExistsHttp(t *testing.T) {
@@ -86,7 +86,7 @@ func TestExistsHttp(t *testing.T) {
if err != nil {
log.Printf("Error executing command: %v", err)
}
- assert.DeepEqual(t, tc.expected[i], result)
+ assert.Equal(t, tc.expected[i], result)
}
})
}
diff --git a/integration_tests/commands/http/expire_test.go b/integration_tests/commands/http/expire_test.go
index 4ba1d04e3..f785e89f8 100644
--- a/integration_tests/commands/http/expire_test.go
+++ b/integration_tests/commands/http/expire_test.go
@@ -5,7 +5,7 @@ import (
"testing"
"time"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func TestExpireHttp(t *testing.T) {
@@ -224,10 +224,10 @@ func TestExpireHttp(t *testing.T) {
t.Fatalf("Not enough results. Expected %d, got %d", len(tc.expected), len(results))
}
if expected == nil {
- assert.Assert(t, results[i] == nil || results[i] == "",
+ assert.True(t, results[i] == nil || results[i] == "",
"Expected nil or empty result, got %v", results[i])
} else {
- assert.DeepEqual(t, expected, results[i])
+ assert.Equal(t, expected, results[i])
}
}
})
diff --git a/integration_tests/commands/http/expireat_test.go b/integration_tests/commands/http/expireat_test.go
index f33f8d6f2..371c233d4 100644
--- a/integration_tests/commands/http/expireat_test.go
+++ b/integration_tests/commands/http/expireat_test.go
@@ -5,7 +5,7 @@ import (
"testing"
"time"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func TestExpireAtHttp(t *testing.T) {
@@ -230,10 +230,10 @@ func TestExpireAtHttp(t *testing.T) {
}
if expected == nil {
- assert.Assert(t, results[i] == nil || results[i] == "",
+ assert.True(t, results[i] == nil || results[i] == "",
"Expected nil or empty result, got %v", results[i])
} else {
- assert.DeepEqual(t, expected, results[i])
+ assert.Equal(t, expected, results[i])
}
}
})
diff --git a/integration_tests/commands/http/expiretime_test.go b/integration_tests/commands/http/expiretime_test.go
index df5cdad79..f3d4f0491 100644
--- a/integration_tests/commands/http/expiretime_test.go
+++ b/integration_tests/commands/http/expiretime_test.go
@@ -5,7 +5,7 @@ import (
"testing"
"time"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func TestExpireTimeHttp(t *testing.T) {
@@ -96,10 +96,10 @@ func TestExpireTimeHttp(t *testing.T) {
}
if expected == nil {
- assert.Assert(t, results[i] == nil || results[i] == "",
+ assert.True(t, results[i] == nil || results[i] == "",
"Expected nil or empty result, got %v", results[i])
} else {
- assert.DeepEqual(t, expected, results[i])
+ assert.Equal(t, expected, results[i])
}
}
})
diff --git a/integration_tests/commands/http/get_test.go b/integration_tests/commands/http/get_test.go
index 962920bd4..392ea42c6 100644
--- a/integration_tests/commands/http/get_test.go
+++ b/integration_tests/commands/http/get_test.go
@@ -4,7 +4,7 @@ import (
"testing"
"time"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func TestGet(t *testing.T) {
diff --git a/integration_tests/commands/http/getdel_test.go b/integration_tests/commands/http/getdel_test.go
index 28a9454a2..9233a0b90 100644
--- a/integration_tests/commands/http/getdel_test.go
+++ b/integration_tests/commands/http/getdel_test.go
@@ -4,7 +4,7 @@ import (
"testing"
"time"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func TestGetDel(t *testing.T) {
diff --git a/integration_tests/commands/http/getex_test.go b/integration_tests/commands/http/getex_test.go
index 81f02c3d9..12fb9d03b 100644
--- a/integration_tests/commands/http/getex_test.go
+++ b/integration_tests/commands/http/getex_test.go
@@ -5,7 +5,7 @@ import (
"testing"
"time"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func TestGetEx(t *testing.T) {
@@ -259,11 +259,11 @@ func TestGetEx(t *testing.T) {
time.Sleep(tc.delay[i])
}
result, err := exec.FireCommand(cmd)
- assert.NilError(t, err)
+ assert.Nil(t, err)
if tc.assertType[i] == "equal" {
- assert.DeepEqual(t, tc.expected[i], result)
+ assert.Equal(t, tc.expected[i], result)
} else if tc.assertType[i] == "assert" {
- assert.Assert(t, result.(float64) <= tc.expected[i].(float64), "Expected %v to be less than or equal to %v", result, tc.expected[i])
+ assert.True(t, result.(float64) <= tc.expected[i].(float64), "Expected %v to be less than or equal to %v", result, tc.expected[i])
}
}
})
diff --git a/integration_tests/commands/http/getrange_test.go b/integration_tests/commands/http/getrange_test.go
index 82f1c32a1..5de0e3444 100644
--- a/integration_tests/commands/http/getrange_test.go
+++ b/integration_tests/commands/http/getrange_test.go
@@ -3,7 +3,7 @@ package http
import (
"testing"
- testifyAssert "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/assert"
)
func TestGETRANGE(t *testing.T) {
@@ -75,7 +75,7 @@ func TestGETRANGE(t *testing.T) {
for i, cmd := range tc.commands {
result, _ := exec.FireCommand(cmd)
- testifyAssert.Equal(t, tc.expected[i], result)
+ assert.Equal(t, tc.expected[i], result)
}
exec.FireCommand(tc.cleanup[0])
})
diff --git a/integration_tests/commands/http/getset_test.go b/integration_tests/commands/http/getset_test.go
index b8bd21582..0072eee92 100644
--- a/integration_tests/commands/http/getset_test.go
+++ b/integration_tests/commands/http/getset_test.go
@@ -4,7 +4,7 @@ import (
"testing"
"time"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func TestGetSet(t *testing.T) {
diff --git a/integration_tests/commands/http/hincrby_test.go b/integration_tests/commands/http/hincrby_test.go
index 75c153c72..c01590762 100644
--- a/integration_tests/commands/http/hincrby_test.go
+++ b/integration_tests/commands/http/hincrby_test.go
@@ -4,7 +4,7 @@ import (
"testing"
"time"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func TestHINCRBY(t *testing.T) {
diff --git a/integration_tests/commands/http/hincrbyfloat_test.go b/integration_tests/commands/http/hincrbyfloat_test.go
index 5cfb655e9..94b42e8e9 100644
--- a/integration_tests/commands/http/hincrbyfloat_test.go
+++ b/integration_tests/commands/http/hincrbyfloat_test.go
@@ -4,7 +4,7 @@ import (
"testing"
"time"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func TestHINCRBYFLOAT(t *testing.T) {
diff --git a/integration_tests/commands/http/hlen_test.go b/integration_tests/commands/http/hlen_test.go
index 0658ff096..91daef6f4 100644
--- a/integration_tests/commands/http/hlen_test.go
+++ b/integration_tests/commands/http/hlen_test.go
@@ -5,7 +5,7 @@ import (
"testing"
"time"
- testifyAssert "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/assert"
)
func TestHLen(t *testing.T) {
@@ -79,9 +79,9 @@ func TestHLen(t *testing.T) {
if err != nil {
// Check if the error message matches the expected result
log.Println(tc.expected[i])
- testifyAssert.Equal(t, tc.expected[i], err.Error(), "Error message mismatch for cmd %s", cmd)
+ assert.Equal(t, tc.expected[i], err.Error(), "Error message mismatch for cmd %s", cmd)
} else {
- testifyAssert.Equal(t, tc.expected[i], result, "Value mismatch for cmd %s, expected %v, got %v", cmd, tc.expected[i], result)
+ assert.Equal(t, tc.expected[i], result, "Value mismatch for cmd %s, expected %v, got %v", cmd, tc.expected[i], result)
}
}
})
diff --git a/integration_tests/commands/http/hrandfield_test.go b/integration_tests/commands/http/hrandfield_test.go
index b47d6447f..2067e9fe8 100644
--- a/integration_tests/commands/http/hrandfield_test.go
+++ b/integration_tests/commands/http/hrandfield_test.go
@@ -5,7 +5,7 @@ import (
"time"
"github.com/google/go-cmp/cmp/cmpopts"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func TestHRANDFIELD(t *testing.T) {
@@ -86,7 +86,7 @@ func TestHRANDFIELD(t *testing.T) {
if str, ok := result.(string); ok {
assert.Equal(t, str, expected, "Unexpected result for command: %s", cmd)
} else {
- assert.DeepEqual(t, result, expected, cmpopts.EquateEmpty())
+ assert.Equal(t, result, expected, cmpopts.EquateEmpty())
}
}
}
@@ -126,7 +126,7 @@ func assertRandomFieldResult(t *testing.T, result interface{}, expected []string
}
// assert that all results are in the expected set or that there is a single valid result
- assert.Assert(t, count == len(resultsList) || count == 1,
+ assert.True(t, count == len(resultsList) || count == 1,
"Expected all results to be in the expected set or a single valid result. Got %d out of %d",
count, len(resultsList))
}
diff --git a/integration_tests/commands/http/hscan_test.go b/integration_tests/commands/http/hscan_test.go
index 2af2f2398..d3c048210 100644
--- a/integration_tests/commands/http/hscan_test.go
+++ b/integration_tests/commands/http/hscan_test.go
@@ -5,7 +5,7 @@ import (
"testing"
"time"
- testifyAssert "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/assert"
)
func TestHScan(t *testing.T) {
@@ -139,9 +139,9 @@ func TestHScan(t *testing.T) {
if err != nil {
// Check if the error message matches the expected result
log.Println(tc.expected[i])
- testifyAssert.Equal(t, tc.expected[i], err.Error(), "Error message mismatch for cmd %s", cmd)
+ assert.Equal(t, tc.expected[i], err.Error(), "Error message mismatch for cmd %s", cmd)
} else {
- testifyAssert.Equal(t, tc.expected[i], result, "Value mismatch for cmd %s, expected %v, got %v", cmd, tc.expected[i], result)
+ assert.Equal(t, tc.expected[i], result, "Value mismatch for cmd %s, expected %v, got %v", cmd, tc.expected[i], result)
}
}
})
diff --git a/integration_tests/commands/http/hsetnx_test.go b/integration_tests/commands/http/hsetnx_test.go
index f4925f26e..133ab883c 100644
--- a/integration_tests/commands/http/hsetnx_test.go
+++ b/integration_tests/commands/http/hsetnx_test.go
@@ -4,7 +4,7 @@ import (
"testing"
"time"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func TestHSetNX(t *testing.T) {
diff --git a/integration_tests/commands/http/hstrlen_test.go b/integration_tests/commands/http/hstrlen_test.go
index f91d53b0b..7e4685463 100644
--- a/integration_tests/commands/http/hstrlen_test.go
+++ b/integration_tests/commands/http/hstrlen_test.go
@@ -5,7 +5,7 @@ import (
"testing"
"time"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func TestHStrLen(t *testing.T) {
diff --git a/integration_tests/commands/http/hyperloglog_test.go b/integration_tests/commands/http/hyperloglog_test.go
index c2b9c4e40..70ac1e807 100644
--- a/integration_tests/commands/http/hyperloglog_test.go
+++ b/integration_tests/commands/http/hyperloglog_test.go
@@ -4,7 +4,7 @@ import (
"testing"
"time"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func TestHyperLogLogCommands(t *testing.T) {
diff --git a/integration_tests/commands/http/incr_by_float_test.go b/integration_tests/commands/http/incr_by_float_test.go
index 0ad0054a6..d38c2fe0a 100644
--- a/integration_tests/commands/http/incr_by_float_test.go
+++ b/integration_tests/commands/http/incr_by_float_test.go
@@ -4,7 +4,7 @@ import (
"testing"
"time"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func TestINCRBYFLOAT(t *testing.T) {
diff --git a/integration_tests/commands/http/incr_test.go b/integration_tests/commands/http/incr_test.go
index 23da18c21..fa2aeaf3a 100644
--- a/integration_tests/commands/http/incr_test.go
+++ b/integration_tests/commands/http/incr_test.go
@@ -6,7 +6,7 @@ import (
"testing"
"time"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func TestINCR(t *testing.T) {
diff --git a/integration_tests/commands/http/json_arrpop_test.go b/integration_tests/commands/http/json_arrpop_test.go
index 4ea6cfe2e..4ee78384e 100644
--- a/integration_tests/commands/http/json_arrpop_test.go
+++ b/integration_tests/commands/http/json_arrpop_test.go
@@ -4,8 +4,7 @@ import (
"testing"
"github.com/dicedb/dice/testutils"
- testifyAssert "github.com/stretchr/testify/assert"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func TestJSONARRPOP(t *testing.T) {
@@ -169,14 +168,14 @@ func TestJSONARRPOP(t *testing.T) {
jsonResult, isString := result.(string)
if isString && testutils.IsJSONResponse(jsonResult) {
- testifyAssert.JSONEq(t, tc.expected[i].(string), jsonResult)
+ assert.JSONEq(t, tc.expected[i].(string), jsonResult)
continue
}
if slice, ok := tc.expected[i].([]interface{}); ok {
- assert.Assert(t, testutils.UnorderedEqual(slice, result))
+ assert.True(t, testutils.UnorderedEqual(slice, result))
} else {
- assert.DeepEqual(t, tc.expected[i], result)
+ assert.Equal(t, tc.expected[i], result)
}
}
})
diff --git a/integration_tests/commands/http/json_test.go b/integration_tests/commands/http/json_test.go
index 983a66ac2..9a8e8f4ae 100644
--- a/integration_tests/commands/http/json_test.go
+++ b/integration_tests/commands/http/json_test.go
@@ -9,9 +9,7 @@ import (
"github.com/bytedance/sonic"
"github.com/dicedb/dice/testutils"
"github.com/google/go-cmp/cmp/cmpopts"
- testifyAssert "github.com/stretchr/testify/assert"
-
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func TestJSONOperations(t *testing.T) {
@@ -194,7 +192,7 @@ func TestJSONOperations(t *testing.T) {
result, _ := exec.FireCommand(cmd)
if jsonResult, ok := result.(string); ok && testutils.IsJSONResponse(jsonResult) {
- testifyAssert.JSONEq(t, tc.expected[i].(string), jsonResult)
+ assert.JSONEq(t, tc.expected[i].(string), jsonResult)
} else {
assert.Equal(t, tc.expected[i], result)
}
@@ -212,9 +210,9 @@ func TestJSONOperations(t *testing.T) {
if jsonResult, ok := result.(string); ok && testutils.IsJSONResponse(jsonResult) {
var jsonPayload []interface{}
json.Unmarshal([]byte(jsonResult), &jsonPayload)
- assert.Assert(t, testutils.UnorderedEqual(tc.expected[i], jsonPayload))
+ assert.True(t, testutils.UnorderedEqual(tc.expected[i], jsonPayload))
} else {
- assert.DeepEqual(t, tc.expected[i], result)
+ assert.Equal(t, tc.expected[i], result)
}
}
})
@@ -253,7 +251,7 @@ func TestJSONSetWithInvalidCases(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
for i, cmd := range tc.commands {
result, _ := exec.FireCommand(cmd)
- assert.Check(t, strings.HasPrefix(result.(string), tc.expected[i].(string)), fmt.Sprintf("Expected: %s, Got: %s", tc.expected[i], result))
+ assert.True(t, strings.HasPrefix(result.(string), tc.expected[i].(string)), fmt.Sprintf("Expected: %s, Got: %s", tc.expected[i], result))
}
})
}
@@ -321,7 +319,7 @@ func TestJSONSetWithNXAndXX(t *testing.T) {
result, _ := exec.FireCommand(cmd)
jsonResult, isString := result.(string)
if isString && testutils.IsJSONResponse(jsonResult) {
- testifyAssert.JSONEq(t, tc.expected[i].(string), jsonResult)
+ assert.JSONEq(t, tc.expected[i].(string), jsonResult)
} else {
assert.Equal(t, tc.expected[i], result)
}
@@ -511,7 +509,7 @@ func TestJSONDelOperations(t *testing.T) {
result, _ := exec.FireCommand(cmd)
jsonResult, isString := result.(string)
if isString && testutils.IsJSONResponse(jsonResult) {
- testifyAssert.JSONEq(t, tc.expected[i].(string), jsonResult)
+ assert.JSONEq(t, tc.expected[i].(string), jsonResult)
} else {
assert.Equal(t, tc.expected[i], result)
}
@@ -616,7 +614,7 @@ func TestJSONForgetOperations(t *testing.T) {
result, _ := exec.FireCommand(cmd)
jsonResult, isString := result.(string)
if isString && testutils.IsJSONResponse(jsonResult) {
- testifyAssert.JSONEq(t, tc.expected[i].(string), jsonResult)
+ assert.JSONEq(t, tc.expected[i].(string), jsonResult)
} else {
assert.Equal(t, tc.expected[i], result)
}
@@ -697,7 +695,7 @@ func TestJsonStrlen(t *testing.T) {
if stringResult, ok := result.(string); ok {
assert.Equal(t, tc.expected[i], stringResult)
} else {
- assert.Assert(t, testutils.UnorderedEqual(tc.expected[i], result.([]interface{})))
+ assert.True(t, testutils.UnorderedEqual(tc.expected[i], result.([]interface{})))
}
}
})
@@ -789,7 +787,7 @@ func TestJSONMGET(t *testing.T) {
resultStr, resultIsString := resultVal.(string)
if isString && resultIsString && testutils.IsJSONResponse(expectedStr) {
- testifyAssert.JSONEq(t, expectedStr, resultStr)
+ assert.JSONEq(t, expectedStr, resultStr)
} else {
assert.Equal(t, expectedVal, resultVal)
}
@@ -805,7 +803,7 @@ func TestJSONMGET(t *testing.T) {
t.Run("MGET with recursive path", func(t *testing.T) {
result, _ := exec.FireCommand(HTTPCommand{Command: "JSON.MGET", Body: map[string]interface{}{"keys": []interface{}{"doc1", "doc2"}, "path": "$..a"}})
results, ok := result.([]interface{})
- assert.Assert(t, ok, "Expected result to be a slice of interface{}")
+ assert.True(t, ok, "Expected result to be a slice of interface{}")
expectedResults := [][]int{{1, 3}, {4, 6}}
assert.Equal(t, len(expectedResults), len(results), "Expected 2 results")
@@ -881,11 +879,11 @@ func TestJsonARRAPPEND(t *testing.T) {
// because the order of keys is not guaranteed, we need to check if the result is an array
if slice, ok := tc.expected[i].([]interface{}); ok {
- assert.Assert(t, testutils.UnorderedEqual(slice, result))
+ assert.True(t, testutils.UnorderedEqual(slice, result))
} else if testutils.IsJSONResponse(tc.expected[i].(string)) {
- testifyAssert.JSONEq(t, tc.expected[i].(string), result.(string))
+ assert.JSONEq(t, tc.expected[i].(string), result.(string))
} else {
- assert.DeepEqual(t, tc.expected[i], result)
+ assert.Equal(t, tc.expected[i], result)
}
}
})
@@ -968,11 +966,11 @@ func TestJsonNummultby(t *testing.T) {
if slice, ok := tc.expected[i].([]interface{}); ok {
var resultPayload []interface{}
sonic.UnmarshalString(result.(string), &resultPayload)
- assert.Assert(t, testutils.UnorderedEqual(slice, resultPayload))
+ assert.True(t, testutils.UnorderedEqual(slice, resultPayload))
} else if testutils.IsJSONResponse(tc.expected[i].(string)) {
- testifyAssert.JSONEq(t, tc.expected[i].(string), result.(string))
+ assert.JSONEq(t, tc.expected[i].(string), result.(string))
} else {
- assert.DeepEqual(t, tc.expected[i], result)
+ assert.Equal(t, tc.expected[i], result)
}
}
})
@@ -1126,9 +1124,9 @@ func TestJsonObjLen(t *testing.T) {
result, _ := exec.FireCommand(cmd)
if slice, ok := tc.expected[i].([]interface{}); ok {
- assert.Assert(t, testutils.UnorderedEqual(slice, result))
+ assert.True(t, testutils.UnorderedEqual(slice, result))
} else {
- assert.DeepEqual(t, tc.expected[i], result)
+ assert.Equal(t, tc.expected[i], result)
}
}
})
@@ -1214,11 +1212,11 @@ func TestJSONNumIncrBy(t *testing.T) {
if slice, ok := tc.expected[i].([]interface{}); ok {
var resultPayload []interface{}
sonic.UnmarshalString(result.(string), &resultPayload)
- assert.Assert(t, testutils.UnorderedEqual(slice, resultPayload))
+ assert.True(t, testutils.UnorderedEqual(slice, resultPayload))
} else if testutils.IsJSONResponse(tc.expected[i].(string)) {
- testifyAssert.JSONEq(t, tc.expected[i].(string), result.(string))
+ assert.JSONEq(t, tc.expected[i].(string), result.(string))
} else {
- assert.DeepEqual(t, tc.expected[i], result)
+ assert.Equal(t, tc.expected[i], result)
}
}
})
@@ -1301,11 +1299,11 @@ func TestJsonARRINSERT(t *testing.T) {
// because the order of keys is not guaranteed, we need to check if the result is an array
if slice, ok := tc.expected[i].([]interface{}); ok {
- assert.Assert(t, testutils.UnorderedEqual(slice, result))
+ assert.True(t, testutils.UnorderedEqual(slice, result))
} else if testutils.IsJSONResponse(tc.expected[i].(string)) {
- testifyAssert.JSONEq(t, tc.expected[i].(string), result.(string))
+ assert.JSONEq(t, tc.expected[i].(string), result.(string))
} else {
- assert.DeepEqual(t, tc.expected[i], result)
+ assert.Equal(t, tc.expected[i], result)
}
}
})
@@ -1428,11 +1426,17 @@ func TestJsonObjKeys(t *testing.T) {
result, _ := exec.FireCommand(cmd)
if slice, ok := tc.expected[i].([]interface{}); ok {
- assert.DeepEqual(t, slice, tc.expected[i], cmpopts.SortSlices(func(a, b interface{}) bool {
+ assert.Equal(t, slice, tc.expected[i], cmpopts.SortSlices(func(a, b interface{}) bool {
return fmt.Sprintf("%v", a) < fmt.Sprintf("%v", b)
}))
} else {
- assert.DeepEqual(t, tc.expected[i], result)
+ if _, ok := result.([]interface{}); ok {
+ assert.ElementsMatch(t, tc.expected[i].([]interface{}), result.([]interface{}))
+ } else {
+ // handle the case where result is not a []interface{}
+ assert.Equal(t, tc.expected[i], result)
+ }
+
}
}
})
@@ -1531,11 +1535,11 @@ func TestJsonARRTRIM(t *testing.T) {
result, _ := exec.FireCommand(cmd)
if slice, ok := tc.expected[i].([]interface{}); ok {
- assert.Assert(t, testutils.UnorderedEqual(slice, result))
+ assert.True(t, testutils.UnorderedEqual(slice, result))
} else if testutils.IsJSONResponse(tc.expected[i].(string)) {
- testifyAssert.JSONEq(t, tc.expected[i].(string), result.(string))
+ assert.JSONEq(t, tc.expected[i].(string), result.(string))
} else {
- assert.DeepEqual(t, tc.expected[i], result)
+ assert.Equal(t, tc.expected[i], result)
}
}
})
diff --git a/integration_tests/commands/http/keys_test.go b/integration_tests/commands/http/keys_test.go
index 603ad8397..75c9b05b1 100644
--- a/integration_tests/commands/http/keys_test.go
+++ b/integration_tests/commands/http/keys_test.go
@@ -4,7 +4,7 @@ import (
"testing"
"github.com/dicedb/dice/testutils"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func TestKeys(t *testing.T) {
@@ -74,9 +74,9 @@ func TestKeys(t *testing.T) {
// because the order of keys is not guaranteed, we need to check if the result is an array
if slice, ok := tc.expected[i].([]interface{}); ok {
- assert.Assert(t, testutils.UnorderedEqual(slice, result))
+ assert.True(t, testutils.UnorderedEqual(slice, result))
} else {
- assert.DeepEqual(t, tc.expected[i], result)
+ assert.Equal(t, tc.expected[i], result)
}
}
})
diff --git a/integration_tests/commands/http/mget_test.go b/integration_tests/commands/http/mget_test.go
index 21aa58100..93c97e0a7 100644
--- a/integration_tests/commands/http/mget_test.go
+++ b/integration_tests/commands/http/mget_test.go
@@ -4,7 +4,7 @@ import (
"testing"
"github.com/dicedb/dice/testutils"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func TestMGET(t *testing.T) {
@@ -50,9 +50,9 @@ func TestMGET(t *testing.T) {
for i, cmd := range tc.commands {
result, _ := exec.FireCommand(cmd)
if slice, ok := tc.expected[i].([]interface{}); ok {
- assert.Assert(t, testutils.UnorderedEqual(slice, result))
+ assert.True(t, testutils.UnorderedEqual(slice, result))
} else {
- assert.DeepEqual(t, tc.expected[i], result)
+ assert.Equal(t, tc.expected[i], result)
}
}
})
diff --git a/integration_tests/commands/http/mset_test.go b/integration_tests/commands/http/mset_test.go
index dec941a2f..8b8182e1f 100644
--- a/integration_tests/commands/http/mset_test.go
+++ b/integration_tests/commands/http/mset_test.go
@@ -3,7 +3,7 @@ package http
import (
"testing"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func TestMSET(t *testing.T) {
@@ -46,7 +46,7 @@ func TestMSET(t *testing.T) {
for i, cmd := range tc.commands {
result, _ := exec.FireCommand(cmd)
- assert.DeepEqual(t, tc.expected[i], result)
+ assert.Equal(t, tc.expected[i], result)
}
})
}
diff --git a/integration_tests/commands/http/object_test.go b/integration_tests/commands/http/object_test.go
index 749f9cbf9..e16cdab27 100644
--- a/integration_tests/commands/http/object_test.go
+++ b/integration_tests/commands/http/object_test.go
@@ -4,7 +4,7 @@ import (
"testing"
"time"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func TestOBJECT(t *testing.T) {
@@ -45,9 +45,9 @@ func TestOBJECT(t *testing.T) {
}
result, _ := exec.FireCommand(cmd)
if tc.assert_type[i] == "equal" {
- assert.DeepEqual(t, tc.expected[i], result)
+ assert.Equal(t, tc.expected[i], result)
} else if tc.assert_type[i] == "assert" {
- assert.Assert(t, result.(float64) >= tc.expected[i].(float64), "Expected %v to be less than or equal to %v", result, tc.expected[i])
+ assert.True(t, result.(float64) >= tc.expected[i].(float64), "Expected %v to be less than or equal to %v", result, tc.expected[i])
}
}
})
diff --git a/integration_tests/commands/http/set_data_cmd_test.go b/integration_tests/commands/http/set_data_cmd_test.go
index d354aa0db..e850a7569 100644
--- a/integration_tests/commands/http/set_data_cmd_test.go
+++ b/integration_tests/commands/http/set_data_cmd_test.go
@@ -3,7 +3,7 @@ package http
import (
"testing"
- testifyAssert "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/assert"
)
func TestSetDataCmd(t *testing.T) {
@@ -299,9 +299,9 @@ func TestSetDataCmd(t *testing.T) {
result, _ := exec.FireCommand(cmd)
switch tc.assert_type[i] {
case "array":
- testifyAssert.ElementsMatch(t, tc.expected[i], result)
+ assert.ElementsMatch(t, tc.expected[i], result)
default:
- testifyAssert.Equal(t, tc.expected[i], result)
+ assert.Equal(t, tc.expected[i], result)
}
}
})
diff --git a/integration_tests/commands/http/set_test.go b/integration_tests/commands/http/set_test.go
index 259bf2ac5..f4b3f3781 100644
--- a/integration_tests/commands/http/set_test.go
+++ b/integration_tests/commands/http/set_test.go
@@ -5,7 +5,7 @@ import (
"testing"
"time"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
type TestCase struct {
@@ -55,7 +55,7 @@ func TestSet(t *testing.T) {
for i, cmd := range tc.commands {
result, _ := exec.FireCommand(cmd)
- assert.DeepEqual(t, tc.expected[i], result)
+ assert.Equal(t, tc.expected[i], result)
}
})
}
@@ -203,6 +203,94 @@ func TestSetWithOptions(t *testing.T) {
}
}
+func TestWithKeepTTLFlag(t *testing.T) {
+ exec := NewHTTPCommandExecutor()
+ expiryTime := strconv.FormatInt(time.Now().Add(1*time.Minute).UnixMilli(), 10)
+
+ testCases := []TestCase {
+ {
+ name: "SET WITH KEEP TTL",
+ commands: []HTTPCommand{
+ {Command: "SET", Body: map[string]interface{}{"key": "k", "value": "v", "ex": 3}},
+ {Command: "GET", Body: map[string]interface{}{"key": "k"}},
+ {Command: "SET", Body: map[string]interface{}{"key": "k", "value": "v2", "keepttl": true}},
+ {Command: "GET", Body: map[string]interface{}{"key": "k"}},
+ },
+ expected: []interface{}{"OK", "v", "OK", "v2"},
+ },
+ {
+ name: "SET WITH KEEP TTL on non-existing key",
+ commands: []HTTPCommand{
+ {Command: "SET", Body: map[string]interface{}{"key": "k", "value": "v", "keepttl": true}},
+ {Command: "GET", Body: map[string]interface{}{"key": "k"}},
+ },
+ expected: []interface{}{"OK", "v"},
+ },
+ {
+ name: "SET WITH KEEPTTL with PX",
+ commands: []HTTPCommand {
+ {Command: "SET", Body: map[string]interface{}{"key": "k", "value": "v", "px": 2000, "keepttl": true}},
+ {Command: "GET", Body: map[string]interface{}{"key": "k"}},
+ },
+ expected: []interface{}{"ERR syntax error", nil},
+ },
+ {
+ name: "SET WITH KEEPTTL with EX",
+ commands: []HTTPCommand {
+ {Command: "SET", Body: map[string]interface{}{"key": "k", "value": "v", "ex": 3, "keepttl": true}},
+ {Command: "GET", Body: map[string]interface{}{"key": "k"}},
+ },
+ expected: []interface{}{"ERR syntax error", nil},
+ },
+ {
+ name: "SET WITH KEEPTTL with NX",
+ commands: []HTTPCommand {
+ {Command: "SET", Body: map[string]interface{}{"key": "k", "value": "v", "nx": true, "keepttl": true}},
+ {Command: "GET", Body: map[string]interface{}{"key": "k"}},
+ },
+ expected: []interface{}{"OK", "v"},
+ },
+ {
+ name: "SET WITH KEEPTTL with XX",
+ commands: []HTTPCommand {
+ {Command: "SET", Body: map[string]interface{}{"key": "k", "value": "v", "xx": true, "keepttl": true}},
+ {Command: "GET", Body: map[string]interface{}{"key": "k"}},
+ },
+ expected: []interface{}{nil, nil},
+ },
+ {
+ name: "SET WITH KEEPTTL with PXAT",
+ commands: []HTTPCommand {
+ {Command: "SET", Body: map[string]interface{}{"key": "k", "value": "v", "pxat": expiryTime, "keepttl": true}},
+ {Command: "GET", Body: map[string]interface{}{"key": "k"}},
+ },
+ expected: []interface{}{"ERR syntax error", nil},
+ },
+ {
+
+ name: "SET WITH KEEPTTL with EXAT",
+ commands: []HTTPCommand {
+ {Command: "SET", Body: map[string]interface{}{"key": "k", "value": "v", "exat": expiryTime, "keepttl": true}},
+ {Command: "GET", Body: map[string]interface{}{"key": "k"}},
+ },
+ expected: []interface{}{"ERR syntax error", nil},
+ },
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.name, func(t *testing.T) {
+ exec.FireCommand(HTTPCommand{Command: "DEL", Body: map[string]interface{}{"key": "k"}})
+ exec.FireCommand(HTTPCommand{Command: "DEL", Body: map[string]interface{}{"key": "k1"}})
+ exec.FireCommand(HTTPCommand{Command: "DEL", Body: map[string]interface{}{"key": "k2"}})
+ for i, cmd := range tc.commands {
+ result, _ := exec.FireCommand(cmd)
+ assert.Equal(t, tc.expected[i], result)
+ }
+ })
+ }
+
+}
+
func TestSetWithExat(t *testing.T) {
exec := NewHTTPCommandExecutor()
Etime := strconv.FormatInt(time.Now().Unix()+5, 10)
@@ -242,9 +330,9 @@ func TestSetWithExat(t *testing.T) {
for i, cmd := range tc.commands {
result, _ := exec.FireCommand(cmd)
if cmd.Command == "TTL" {
- assert.Assert(t, result.(float64) <= tc.expected[i].(float64))
+ assert.True(t, result.(float64) <= tc.expected[i].(float64))
} else {
- assert.DeepEqual(t, tc.expected[i], result)
+ assert.Equal(t, tc.expected[i], result)
}
}
})
diff --git a/integration_tests/commands/http/toggle_test.go b/integration_tests/commands/http/toggle_test.go
index 7337e197d..4fd6529ec 100644
--- a/integration_tests/commands/http/toggle_test.go
+++ b/integration_tests/commands/http/toggle_test.go
@@ -5,7 +5,7 @@ import (
"testing"
"github.com/dicedb/dice/testutils"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func compareJSON(t *testing.T, expected, actual string) {
@@ -15,10 +15,10 @@ func compareJSON(t *testing.T, expected, actual string) {
err1 := json.Unmarshal([]byte(expected), &expectedMap)
err2 := json.Unmarshal([]byte(actual), &actualMap)
- assert.NilError(t, err1)
- assert.NilError(t, err2)
+ assert.Nil(t, err1)
+ assert.Nil(t, err2)
- assert.DeepEqual(t, expectedMap, actualMap)
+ assert.Equal(t, expectedMap, actualMap)
}
func TestJSONTOGGLE(t *testing.T) {
exec := NewHTTPCommandExecutor()
@@ -93,9 +93,9 @@ func TestJSONTOGGLE(t *testing.T) {
assert.Equal(t, expected, result)
}
case []interface{}:
- assert.Assert(t, testutils.UnorderedEqual(expected, result))
+ assert.True(t, testutils.UnorderedEqual(expected, result))
default:
- assert.DeepEqual(t, expected, result)
+ assert.Equal(t, expected, result)
}
}
})
diff --git a/integration_tests/commands/http/touch_test.go b/integration_tests/commands/http/touch_test.go
index c389f80e6..88400e51c 100644
--- a/integration_tests/commands/http/touch_test.go
+++ b/integration_tests/commands/http/touch_test.go
@@ -4,7 +4,7 @@ import (
"testing"
"time"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func TestTouch(t *testing.T) {
diff --git a/integration_tests/commands/http/ttl_pttl_test.go b/integration_tests/commands/http/ttl_pttl_test.go
index 1fc710ce5..0d3f8ce37 100644
--- a/integration_tests/commands/http/ttl_pttl_test.go
+++ b/integration_tests/commands/http/ttl_pttl_test.go
@@ -4,7 +4,7 @@ import (
"testing"
"time"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func TestTTLPTTL(t *testing.T) {
@@ -111,9 +111,9 @@ func TestTTLPTTL(t *testing.T) {
}
result, _ := exec.FireCommand(cmd)
if tc.assert_type[i] == "equal" {
- assert.DeepEqual(t, tc.expected[i], result)
+ assert.Equal(t, tc.expected[i], result)
} else if tc.assert_type[i] == "assert" {
- assert.Assert(t, result.(float64) <= tc.expected[i].(float64), "Expected %v to be less than or equal to %v", result, tc.expected[i])
+ assert.True(t, result.(float64) <= tc.expected[i].(float64), "Expected %v to be less than or equal to %v", result, tc.expected[i])
}
}
})
diff --git a/integration_tests/commands/http/type_test.go b/integration_tests/commands/http/type_test.go
index 136dd0ccc..550d451e2 100644
--- a/integration_tests/commands/http/type_test.go
+++ b/integration_tests/commands/http/type_test.go
@@ -3,7 +3,7 @@ package http
import (
"testing"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func TestType(t *testing.T) {
diff --git a/integration_tests/commands/http/zcard_test.go b/integration_tests/commands/http/zcard_test.go
index 71ba1c443..29c869d97 100644
--- a/integration_tests/commands/http/zcard_test.go
+++ b/integration_tests/commands/http/zcard_test.go
@@ -5,7 +5,7 @@ import (
"testing"
"time"
- testifyAssert "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/assert"
)
func TestZCARD(t *testing.T) {
@@ -82,9 +82,9 @@ func TestZCARD(t *testing.T) {
if err != nil {
// Check if the error message matches the expected result
log.Println(tc.expected[i])
- testifyAssert.Equal(t, tc.expected[i], err.Error(), "Error message mismatch for cmd %s", cmd)
+ assert.Equal(t, tc.expected[i], err.Error(), "Error message mismatch for cmd %s", cmd)
} else {
- testifyAssert.Equal(t, tc.expected[i], result, "Value mismatch for cmd %s, expected %v, got %v", cmd, tc.expected[i], result)
+ assert.Equal(t, tc.expected[i], result, "Value mismatch for cmd %s, expected %v, got %v", cmd, tc.expected[i], result)
}
}
})
diff --git a/integration_tests/commands/http/zrem_test.go b/integration_tests/commands/http/zrem_test.go
index 42718be51..733c5c793 100644
--- a/integration_tests/commands/http/zrem_test.go
+++ b/integration_tests/commands/http/zrem_test.go
@@ -5,7 +5,7 @@ import (
"testing"
"time"
- testifyAssert "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/assert"
)
func TestZREM(t *testing.T) {
@@ -88,9 +88,9 @@ func TestZREM(t *testing.T) {
if err != nil {
// Check if the error message matches the expected result
log.Println(tc.expected[i])
- testifyAssert.Equal(t, tc.expected[i], err.Error(), "Error message mismatch for cmd %s", cmd)
+ assert.Equal(t, tc.expected[i], err.Error(), "Error message mismatch for cmd %s", cmd)
} else {
- testifyAssert.Equal(t, tc.expected[i], result, "Value mismatch for cmd %s, expected %v, got %v", cmd, tc.expected[i], result)
+ assert.Equal(t, tc.expected[i], result, "Value mismatch for cmd %s, expected %v, got %v", cmd, tc.expected[i], result)
}
}
})
diff --git a/integration_tests/commands/resp/append_test.go b/integration_tests/commands/resp/append_test.go
index 4f252a84a..fc70851cf 100644
--- a/integration_tests/commands/resp/append_test.go
+++ b/integration_tests/commands/resp/append_test.go
@@ -3,7 +3,7 @@ package resp
import (
"testing"
- testifyAssert "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/assert"
)
func TestAPPEND(t *testing.T) {
@@ -65,7 +65,7 @@ func TestAPPEND(t *testing.T) {
for i := 0; i < len(tc.commands); i++ {
result := FireCommand(conn, tc.commands[i])
expected := tc.expected[i]
- testifyAssert.Equal(t, expected, result)
+ assert.Equal(t, expected, result)
}
for _, cmd := range tc.cleanup {
diff --git a/integration_tests/commands/resp/bloom_test.go b/integration_tests/commands/resp/bloom_test.go
index c5c37f116..bda85c284 100644
--- a/integration_tests/commands/resp/bloom_test.go
+++ b/integration_tests/commands/resp/bloom_test.go
@@ -4,7 +4,7 @@ import (
"testing"
"time"
- assert "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/assert"
)
func TestBFReserveAddInfoExists(t *testing.T) {
diff --git a/integration_tests/commands/resp/command_docs_test.go b/integration_tests/commands/resp/command_docs_test.go
index 55ff02196..f566a3c26 100644
--- a/integration_tests/commands/resp/command_docs_test.go
+++ b/integration_tests/commands/resp/command_docs_test.go
@@ -3,7 +3,7 @@ package resp
import (
"testing"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
var getDocsTestCases = []struct {
@@ -86,7 +86,7 @@ func TestCommandDocs(t *testing.T) {
for _, tc := range getDocsTestCases {
t.Run(tc.name, func(t *testing.T) {
result := FireCommand(conn, "COMMAND DOCS "+tc.inCmd)
- assert.DeepEqual(t, tc.expected, result)
+ assert.Equal(t, tc.expected, result)
})
}
}
diff --git a/integration_tests/commands/resp/command_getkeys_test.go b/integration_tests/commands/resp/command_getkeys_test.go
index d1fd162a1..607bba994 100644
--- a/integration_tests/commands/resp/command_getkeys_test.go
+++ b/integration_tests/commands/resp/command_getkeys_test.go
@@ -3,7 +3,7 @@ package resp
import (
"testing"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
var getKeysTestCases = []struct {
@@ -31,7 +31,7 @@ func TestCommandGetKeys(t *testing.T) {
for _, tc := range getKeysTestCases {
t.Run(tc.name, func(t *testing.T) {
result := FireCommand(conn, "COMMAND GETKEYS "+tc.inCmd)
- assert.DeepEqual(t, tc.expected, result)
+ assert.Equal(t, tc.expected, result)
})
}
}
diff --git a/integration_tests/commands/resp/command_info_test.go b/integration_tests/commands/resp/command_info_test.go
index 545d81984..e9d71ff5a 100644
--- a/integration_tests/commands/resp/command_info_test.go
+++ b/integration_tests/commands/resp/command_info_test.go
@@ -3,7 +3,7 @@ package resp
import (
"testing"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
var getInfoTestCases = []struct {
@@ -32,7 +32,7 @@ func TestCommandInfo(t *testing.T) {
for _, tc := range getInfoTestCases {
t.Run(tc.name, func(t *testing.T) {
result := FireCommand(conn, "COMMAND INFO "+tc.inCmd)
- assert.DeepEqual(t, tc.expected, result)
+ assert.Equal(t, tc.expected, result)
})
}
}
diff --git a/integration_tests/commands/resp/countminsketch_test.go b/integration_tests/commands/resp/countminsketch_test.go
index 574905157..e1c66575c 100644
--- a/integration_tests/commands/resp/countminsketch_test.go
+++ b/integration_tests/commands/resp/countminsketch_test.go
@@ -3,7 +3,7 @@ package resp
import (
"testing"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func TestCMSInitByDim(t *testing.T) {
@@ -154,7 +154,7 @@ func TestCMSInfo(t *testing.T) {
FireCommand(conn, "DEL cms_key2")
for i, cmd := range tc.commands {
result := FireCommand(conn, cmd)
- assert.DeepEqual(t, tc.expected[i], result)
+ assert.Equal(t, tc.expected[i], result)
}
})
}
@@ -219,7 +219,7 @@ func TestCMSIncrBy(t *testing.T) {
FireCommand(conn, "DEL cms_key3")
for i, cmd := range tc.commands {
result := FireCommand(conn, cmd)
- assert.DeepEqual(t, tc.expected[i], result)
+ assert.Equal(t, tc.expected[i], result)
}
})
}
@@ -261,7 +261,7 @@ func TestCMSQuery(t *testing.T) {
FireCommand(conn, "DEL cms_key4")
for i, cmd := range tc.commands {
result := FireCommand(conn, cmd)
- assert.DeepEqual(t, tc.expected[i], result)
+ assert.Equal(t, tc.expected[i], result)
}
})
}
@@ -408,7 +408,7 @@ func TestCMSMerge(t *testing.T) {
FireCommand(conn, "DEL cms_key5 test test1")
for i, cmd := range tc.commands {
result := FireCommand(conn, cmd)
- assert.DeepEqual(t, tc.expected[i], result)
+ assert.Equal(t, tc.expected[i], result)
}
})
}
diff --git a/integration_tests/commands/resp/expire_test.go b/integration_tests/commands/resp/expire_test.go
new file mode 100644
index 000000000..12b2e71c9
--- /dev/null
+++ b/integration_tests/commands/resp/expire_test.go
@@ -0,0 +1,564 @@
+package resp
+
+import (
+ "strconv"
+ "testing"
+ "time"
+
+ "gotest.tools/v3/assert"
+)
+
+func TestEXPIRE(t *testing.T) {
+ conn := getLocalConnection()
+ defer conn.Close()
+
+ testCases := []struct {
+ name string
+ setup string
+ commands []string
+ expected []interface{}
+ delay []time.Duration
+ }{
+ {
+ name: "Set with EXPIRE command",
+ setup: "",
+ commands: []string{
+ "SET test_key test_value",
+ "EXPIRE test_key 1",
+ },
+ expected: []interface{}{"OK", int64(1)},
+ delay: []time.Duration{0, 0},
+ },
+ {
+ name: "Check if key is nil after expiration",
+ setup: "SET test_key test_value",
+ commands: []string{
+ "EXPIRE test_key 1",
+ "GET test_key",
+ },
+ expected: []interface{}{int64(1), "(nil)"},
+ delay: []time.Duration{0, 1100 * time.Millisecond},
+ },
+ {
+ name: "EXPIRE non-existent key",
+ setup: "",
+ commands: []string{
+ "EXPIRE non_existent_key 1",
+ },
+ expected: []interface{}{int64(0)},
+ delay: []time.Duration{0, 0},
+ },
+ {
+ name: "EXPIRE with past time",
+ setup: "SET test_key test_value",
+ commands: []string{
+ "EXPIRE test_key -1",
+ "GET test_key",
+ },
+ expected: []interface{}{"ERR invalid expire time in 'expire' command", "test_value"},
+ delay: []time.Duration{0, 0},
+ },
+ {
+ name: "EXPIRE with invalid syntax",
+ setup: "SET test_key test_value",
+ commands: []string{
+ "EXPIRE test_key",
+ },
+ expected: []interface{}{"ERR wrong number of arguments for 'expire' command"},
+ delay: []time.Duration{0},
+ },
+ {
+ name: "Test(NX): Set the expiration only if the key has no expiration time",
+ setup: "",
+ commands: []string{
+ "SET test_key test_value",
+ "EXPIRE test_key " + strconv.FormatInt(1, 10) + " NX",
+ "EXPIRE test_key " + strconv.FormatInt(1, 10) + " NX",
+ },
+ expected: []interface{}{"OK", int64(1), int64(0)},
+ delay: []time.Duration{0, 0, 0},
+ },
+
+ {
+ name: "Test(XX): Set the expiration only if the key already has an expiration time",
+ setup: "",
+ commands: []string{
+ "SET test_key test_value",
+ "EXPIRE test_key " + strconv.FormatInt(10, 10) + " XX",
+ "TTL test_key",
+ "EXPIRE test_key " + strconv.FormatInt(10, 10),
+ "EXPIRE test_key " + strconv.FormatInt(10, 10) + " XX",
+ },
+ expected: []interface{}{"OK", int64(0), int64(-1), int64(1), int64(1)},
+ delay: []time.Duration{0, 0, 0, 0, 0},
+ },
+
+ {
+ name: "TEST(GT): Set the expiration only if the new expiration time is greater than the current one",
+ setup: "",
+ commands: []string{
+ "SET test_key test_value",
+ "EXPIRE test_key " + strconv.FormatInt(10, 10) + " GT",
+ "TTL test_key",
+ "EXPIRE test_key " + strconv.FormatInt(10, 10),
+ "EXPIRE test_key " + strconv.FormatInt(20, 10) + " GT",
+ },
+ expected: []interface{}{"OK", int64(0), int64(-1), int64(1), int64(1)},
+ delay: []time.Duration{0, 0, 0, 0, 0},
+ },
+
+ {
+ name: "TEST(LT): Set the expiration only if the new expiration time is less than the current one",
+ setup: "",
+ commands: []string{
+ "SET test_key test_value",
+ "EXPIRE test_key " + strconv.FormatInt(10, 10) + " LT",
+ "EXPIRE test_key " + strconv.FormatInt(20, 10) + " LT",
+ },
+ expected: []interface{}{"OK", int64(1), int64(0)},
+ delay: []time.Duration{0, 0, 0},
+ },
+
+ {
+ name: "TEST(LT): Set the expiration only if the new expiration time is less than the current one",
+ setup: "",
+ commands: []string{
+ "SET test_key test_value",
+ "EXPIRE test_key " + strconv.FormatInt(10, 10) + " LT",
+ "EXPIRE test_key " + strconv.FormatInt(20, 10) + " LT",
+ },
+ expected: []interface{}{"OK", int64(1), int64(0)},
+ delay: []time.Duration{0, 0, 0},
+ },
+
+ {
+ name: "TEST(NX + LT/GT)",
+ setup: "",
+ commands: []string{
+ "SET test_key test_value",
+ "EXPIRE test_key " + strconv.FormatInt(20, 10) + " NX",
+ "EXPIRE test_key " + strconv.FormatInt(20, 10) + " NX" + " LT",
+ "EXPIRE test_key " + strconv.FormatInt(20, 10) + " NX" + " GT",
+ "GET test_key",
+ },
+ expected: []interface{}{"OK", int64(1),
+ "ERR NX and XX, GT or LT options at the same time are not compatible",
+ "ERR NX and XX, GT or LT options at the same time are not compatible",
+ "test_value"},
+ delay: []time.Duration{0, 0, 0, 0, 0},
+ },
+ {
+ name: "TEST(XX + LT/GT)",
+ setup: "",
+ commands: []string{
+ "SET test_key test_value",
+ "EXPIRE test_key " + strconv.FormatInt(20, 10),
+ "EXPIRE test_key " + strconv.FormatInt(5, 10) + " XX" + " LT",
+ "EXPIRE test_key " + strconv.FormatInt(10, 10) + " XX" + " GT",
+ "EXPIRE test_key " + strconv.FormatInt(20, 10) + " XX" + " GT",
+ "GET test_key",
+ },
+ expected: []interface{}{"OK", int64(1), int64(1), int64(1), int64(1), "test_value"},
+ delay: []time.Duration{0, 0, 0, 0, 0, 0},
+ },
+ {
+ name: "Test if value is nil after expiration",
+ setup: "",
+ commands: []string{
+ "SET test_key test_value",
+ "EXPIRE test_key " + strconv.FormatInt(20, 10),
+ "EXPIRE test_key " + strconv.FormatInt(2, 10) + " XX" + " LT",
+ "GET test_key",
+ },
+ expected: []interface{}{"OK", int64(1), int64(1), "(nil)"},
+ delay: []time.Duration{0, 0, 0, 2 * time.Second},
+ },
+ {
+ name: "Test if value is nil after expiration",
+ setup: "",
+ commands: []string{
+ "SET test_key test_value",
+ "EXPIRE test_key " + strconv.FormatInt(2, 10) + " NX",
+ "GET test_key",
+ },
+ expected: []interface{}{"OK", int64(1), "(nil)"},
+ delay: []time.Duration{0, 0, 2 * time.Second},
+ },
+ {
+ name: "Invalid Command Test",
+ setup: "",
+ commands: []string{
+ "SET test_key test_value",
+ "EXPIRE test_key " + strconv.FormatInt(1, 10) + " XX" + " " + "rr",
+ "EXPIRE test_key " + strconv.FormatInt(1, 10) + " XX" + " " + "NX",
+ "EXPIRE test_key " + strconv.FormatInt(1, 10) + " GT" + " " + "lt",
+ "EXPIRE test_key " + strconv.FormatInt(1, 10) + " GT" + " " + "lt" + " " + "xx",
+ "EXPIRE test_key " + strconv.FormatInt(1, 10) + " GT" + " " + "lt" + " " + "nx",
+ "EXPIRE test_key " + strconv.FormatInt(1, 10) + " nx" + " " + "xx" + " " + "gt",
+ "EXPIRE test_key " + strconv.FormatInt(1, 10) + " nx" + " " + "xx" + " " + "lt",
+ },
+ expected: []interface{}{"OK", "ERR Unsupported option rr",
+ "ERR NX and XX, GT or LT options at the same time are not compatible",
+ "ERR GT and LT options at the same time are not compatible",
+ "ERR GT and LT options at the same time are not compatible",
+ "ERR NX and XX, GT or LT options at the same time are not compatible",
+ "ERR NX and XX, GT or LT options at the same time are not compatible",
+ "ERR NX and XX, GT or LT options at the same time are not compatible"},
+ delay: []time.Duration{0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ }
+ for _, tc := range testCases {
+ t.Run(tc.name, func(t *testing.T) {
+ // Setup
+ if tc.setup != "" {
+ FireCommand(conn, tc.setup)
+ }
+
+ // Execute commands
+ var results []interface{}
+ for i, cmd := range tc.commands {
+ // Wait if delay is specified
+ if tc.delay[i] > 0 {
+ time.Sleep(tc.delay[i])
+ }
+ result := FireCommand(conn, cmd)
+ results = append(results, result)
+ }
+
+ // Validate results
+ for i, expected := range tc.expected {
+ if i >= len(results) {
+ t.Fatalf("Not enough results. Expected %d, got %d", len(tc.expected), len(results))
+ }
+
+ if expected == "(nil)" {
+ assert.Assert(t, results[i] == "(nil)" || results[i] == "",
+ "Expected nil or empty result, got %v", results[i])
+ } else {
+ assert.DeepEqual(t, expected, results[i])
+ }
+ }
+ })
+ }
+}
+
+func TestEXPIREAT(t *testing.T) {
+ conn := getLocalConnection()
+ defer conn.Close()
+
+ testCases := []struct {
+ name string
+ setup string
+ commands []string
+ expected []interface{}
+ delay []time.Duration
+ }{
+ {
+ name: "Set with EXPIREAT command",
+ setup: "",
+ commands: []string{
+ "SET test_key test_value",
+ "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+1, 10),
+ },
+ expected: []interface{}{"OK", int64(1)},
+ delay: []time.Duration{0, 0},
+ },
+ {
+ name: "Check if key is nil after expiration",
+ setup: "SET test_key test_value",
+ commands: []string{
+ "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+1, 10),
+ "GET test_key",
+ },
+ expected: []interface{}{int64(1), "(nil)"},
+ delay: []time.Duration{0, 1100 * time.Millisecond},
+ },
+ {
+ name: "EXPIREAT non-existent key",
+ setup: "",
+ commands: []string{
+ "EXPIREAT non_existent_key " + strconv.FormatInt(time.Now().Unix()+1, 10),
+ },
+ expected: []interface{}{int64(0)},
+ delay: []time.Duration{0, 0},
+ },
+ {
+ name: "EXPIREAT with past time",
+ setup: "SET test_key test_value",
+ commands: []string{
+ "EXPIREAT test_key " + strconv.FormatInt(-1, 10),
+ "GET test_key",
+ },
+ expected: []interface{}{"ERR invalid expire time in 'expireat' command", "test_value"},
+ delay: []time.Duration{0, 0},
+ },
+ {
+ name: "EXPIREAT with invalid syntax",
+ setup: "SET test_key test_value",
+ commands: []string{
+ "EXPIREAT test_key",
+ },
+ expected: []interface{}{"ERR wrong number of arguments for 'expireat' command"},
+ delay: []time.Duration{0},
+ },
+ {
+ name: "Test(NX): Set the expiration only if the key has no expiration time",
+ setup: "",
+ commands: []string{
+ "SET test_key test_value",
+ "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+10, 10) + " NX",
+ "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+1, 10) + " NX",
+ },
+ expected: []interface{}{"OK", int64(1), int64(0)},
+ delay: []time.Duration{0, 0, 0},
+ },
+
+ {
+ name: "Test(XX): Set the expiration only if the key already has an expiration time",
+ setup: "",
+ commands: []string{
+ "SET test_key test_value",
+ "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+10, 10) + " XX",
+ "TTL test_key",
+ "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+10, 10),
+ "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+10, 10) + " XX",
+ },
+ expected: []interface{}{"OK", int64(0), int64(-1), int64(1), int64(1)},
+ delay: []time.Duration{0, 0, 0, 0, 0},
+ },
+
+ {
+ name: "TEST(GT): Set the expiration only if the new expiration time is greater than the current one",
+ setup: "",
+ commands: []string{
+ "SET test_key test_value",
+ "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+10, 10) + " GT",
+ "TTL test_key",
+ "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+10, 10),
+ "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+20, 10) + " GT",
+ },
+ expected: []interface{}{"OK", int64(0), int64(-1), int64(1), int64(1)},
+ delay: []time.Duration{0, 0, 0, 0, 0},
+ },
+
+ {
+ name: "TEST(LT): Set the expiration only if the new expiration time is less than the current one",
+ setup: "",
+ commands: []string{
+ "SET test_key test_value",
+ "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+10, 10) + " LT",
+ "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+20, 10) + " LT",
+ },
+ expected: []interface{}{"OK", int64(1), int64(0)},
+ delay: []time.Duration{0, 0, 0},
+ },
+
+ {
+ name: "TEST(LT): Set the expiration only if the new expiration time is less than the current one",
+ setup: "",
+ commands: []string{
+ "SET test_key test_value",
+ "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+10, 10) + " LT",
+ "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+20, 10) + " LT",
+ },
+ expected: []interface{}{"OK", int64(1), int64(0)},
+ delay: []time.Duration{0, 0, 0},
+ },
+
+ {
+ name: "TEST(NX + LT/GT)",
+ setup: "",
+ commands: []string{
+ "SET test_key test_value",
+ "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+20, 10) + " NX",
+ "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+20, 10) + " NX" + " LT",
+ "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+20, 10) + " NX" + " GT",
+ "GET test_key",
+ },
+ expected: []interface{}{"OK", int64(1),
+ "ERR NX and XX, GT or LT options at the same time are not compatible",
+ "ERR NX and XX, GT or LT options at the same time are not compatible",
+ "test_value"},
+ delay: []time.Duration{0, 0, 0, 0, 0},
+ },
+ {
+ name: "TEST(XX + LT/GT)",
+ setup: "",
+ commands: []string{
+ "SET test_key test_value",
+ "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+20, 10),
+ "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+5, 10) + " XX" + " LT",
+ "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+10, 10) + " XX" + " GT",
+ "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+20, 10) + " XX" + " GT",
+ "GET test_key",
+ },
+ expected: []interface{}{"OK", int64(1), int64(1), int64(1), int64(1), "test_value"},
+ delay: []time.Duration{0, 0, 0, 0, 0, 0},
+ },
+ {
+ name: "Test if value is nil after expiration",
+ setup: "",
+ commands: []string{
+ "SET test_key test_value",
+ "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+20, 10),
+ "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+2, 10) + " XX" + " LT",
+ "GET test_key",
+ },
+ expected: []interface{}{"OK", int64(1), int64(1), "(nil)"},
+ delay: []time.Duration{0, 0, 0, 2 * time.Second},
+ },
+ {
+ name: "Test if value is nil after expiration",
+ setup: "",
+ commands: []string{
+ "SET test_key test_value",
+ "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+2, 10) + " NX",
+ "GET test_key",
+ },
+ expected: []interface{}{"OK", int64(1), "(nil)"},
+ delay: []time.Duration{0, 0, 2 * time.Second},
+ },
+ {
+ name: "Invalid Command Test",
+ setup: "",
+ commands: []string{
+ "SET test_key test_value",
+ "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+1, 10) + " XX" + " " + "rr",
+ "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+1, 10) + " XX" + " " + "NX",
+ "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+1, 10) + " GT" + " " + "lt",
+ "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+1, 10) + " GT" + " " + "lt" + " " + "xx",
+ "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+1, 10) + " GT" + " " + "lt" + " " + "nx",
+ "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+1, 10) + " nx" + " " + "xx" + " " + "gt",
+ "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+1, 10) + " nx" + " " + "xx" + " " + "lt",
+ },
+ expected: []interface{}{"OK", "ERR Unsupported option rr",
+ "ERR NX and XX, GT or LT options at the same time are not compatible",
+ "ERR GT and LT options at the same time are not compatible",
+ "ERR GT and LT options at the same time are not compatible",
+ "ERR NX and XX, GT or LT options at the same time are not compatible",
+ "ERR NX and XX, GT or LT options at the same time are not compatible",
+ "ERR NX and XX, GT or LT options at the same time are not compatible"},
+ delay: []time.Duration{0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ }
+ for _, tc := range testCases {
+ t.Run(tc.name, func(t *testing.T) {
+ // Setup
+ if tc.setup != "" {
+ FireCommand(conn, tc.setup)
+ }
+
+ // Execute commands
+ var results []interface{}
+ for i, cmd := range tc.commands {
+ // Wait if delay is specified
+ if tc.delay[i] > 0 {
+ time.Sleep(tc.delay[i])
+ }
+ result := FireCommand(conn, cmd)
+ results = append(results, result)
+ }
+ // Validate results
+ for i, expected := range tc.expected {
+ if i >= len(results) {
+ t.Fatalf("Not enough results. Expected %d, got %d", len(tc.expected), len(results))
+ }
+
+ if expected == "(nil)" {
+ assert.Assert(t, results[i] == "(nil)" || results[i] == "",
+ "Expected nil or empty result, got %v", results[i])
+ } else {
+ assert.DeepEqual(t, expected, results[i])
+ }
+ }
+ })
+ }
+}
+
+func TestEXPIRETIME(t *testing.T) {
+ conn := getLocalConnection()
+ defer conn.Close()
+
+ futureUnixTimestamp := time.Now().Unix() + 1
+
+ testCases := []struct {
+ name string
+ setup string
+ commands []string
+ expected []interface{}
+ delay []time.Duration
+ }{
+ {
+ name: "EXPIRETIME command",
+ setup: "SET test_key test_value",
+ commands: []string{
+ "EXPIREAT test_key " + strconv.FormatInt(futureUnixTimestamp, 10),
+ "EXPIRETIME test_key",
+ },
+ expected: []interface{}{int64(1), futureUnixTimestamp},
+ delay: []time.Duration{0, 0},
+ },
+ {
+ name: "EXPIRETIME non-existent key",
+ setup: "",
+ commands: []string{
+ "EXPIRETIME non_existent_key",
+ },
+ expected: []interface{}{int64(-2)},
+ delay: []time.Duration{0},
+ },
+ {
+ name: "EXPIRETIME with past time",
+ setup: "SET test_key test_value",
+ commands: []string{
+ "EXPIREAT test_key 1724167183",
+ "EXPIRETIME test_key",
+ },
+ expected: []interface{}{int64(1), int64(-2)},
+ delay: []time.Duration{0, 0},
+ },
+ {
+ name: "EXPIRETIME with invalid syntax",
+ setup: "SET test_key test_value",
+ commands: []string{
+ "EXPIRETIME",
+ "EXPIRETIME key1 key2",
+ },
+ expected: []interface{}{"ERR wrong number of arguments for 'expiretime' command", "ERR wrong number of arguments for 'expiretime' command"},
+ delay: []time.Duration{0, 0},
+ },
+ }
+ for _, tc := range testCases {
+ t.Run(tc.name, func(t *testing.T) {
+ // Setup
+ if tc.setup != "" {
+ FireCommand(conn, tc.setup)
+ }
+
+ // Execute commands
+ var results []interface{}
+ for i, cmd := range tc.commands {
+ // Wait if delay is specified
+ if tc.delay[i] > 0 {
+ time.Sleep(tc.delay[i])
+ }
+ result := FireCommand(conn, cmd)
+ results = append(results, result)
+ }
+
+ // Validate results
+ for i, expected := range tc.expected {
+ if i >= len(results) {
+ t.Fatalf("Not enough results. Expected %d, got %d", len(tc.expected), len(results))
+ }
+
+ if expected == "(nil)" {
+ assert.Assert(t, results[i] == "(nil)" || results[i] == "",
+ "Expected nil or empty result, got %v", results[i])
+ } else {
+ assert.DeepEqual(t, expected, results[i])
+ }
+ }
+ })
+ }
+}
diff --git a/integration_tests/commands/resp/get_test.go b/integration_tests/commands/resp/get_test.go
index bb826a499..a7dc5f118 100644
--- a/integration_tests/commands/resp/get_test.go
+++ b/integration_tests/commands/resp/get_test.go
@@ -4,7 +4,7 @@ import (
"testing"
"time"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func TestGet(t *testing.T) {
diff --git a/integration_tests/commands/async/getdel_test.go b/integration_tests/commands/resp/getdel_test.go
similarity index 99%
rename from integration_tests/commands/async/getdel_test.go
rename to integration_tests/commands/resp/getdel_test.go
index 262bffc6b..b92cf7582 100644
--- a/integration_tests/commands/async/getdel_test.go
+++ b/integration_tests/commands/resp/getdel_test.go
@@ -1,4 +1,4 @@
-package async
+package resp
import (
"testing"
diff --git a/integration_tests/commands/async/getex_test.go b/integration_tests/commands/resp/getex_test.go
similarity index 99%
rename from integration_tests/commands/async/getex_test.go
rename to integration_tests/commands/resp/getex_test.go
index 8266cc671..94fa5434f 100644
--- a/integration_tests/commands/async/getex_test.go
+++ b/integration_tests/commands/resp/getex_test.go
@@ -1,4 +1,4 @@
-package async
+package resp
import (
"strconv"
@@ -147,7 +147,6 @@ func TestGetEx(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
- // deleteTestKeys([]string{"foo"}, store)
FireCommand(conn, "DEL foo")
for i, cmd := range tc.commands {
diff --git a/integration_tests/commands/resp/getrange_test.go b/integration_tests/commands/resp/getrange_test.go
index db5700769..14f068b3e 100644
--- a/integration_tests/commands/resp/getrange_test.go
+++ b/integration_tests/commands/resp/getrange_test.go
@@ -3,7 +3,7 @@ package resp
import (
"testing"
- testifyAssert "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/assert"
)
func TestGETRANGE(t *testing.T) {
@@ -62,7 +62,7 @@ func TestGETRANGE(t *testing.T) {
for i := 0; i < len(tc.commands); i++ {
result := FireCommand(conn, tc.commands[i])
expected := tc.expected[i]
- testifyAssert.Equal(t, expected, result)
+ assert.Equal(t, expected, result)
}
for _, cmd := range tc.cleanup {
diff --git a/integration_tests/commands/resp/getset_test.go b/integration_tests/commands/resp/getset_test.go
index 09a1eff69..8846bae9e 100644
--- a/integration_tests/commands/resp/getset_test.go
+++ b/integration_tests/commands/resp/getset_test.go
@@ -4,7 +4,7 @@ import (
"testing"
"time"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func TestGetSet(t *testing.T) {
diff --git a/integration_tests/commands/resp/getunwatch_test.go b/integration_tests/commands/resp/getunwatch_test.go
new file mode 100644
index 000000000..542260974
--- /dev/null
+++ b/integration_tests/commands/resp/getunwatch_test.go
@@ -0,0 +1,181 @@
+package resp
+
+import (
+ "context"
+ "fmt"
+ "net"
+ "testing"
+ "time"
+
+ "github.com/dicedb/dice/internal/clientio"
+ "github.com/dicedb/dicedb-go"
+ "github.com/stretchr/testify/assert"
+)
+
+const (
+ getUnwatchKey = "getunwatchkey"
+ fingerprint = "3557732805"
+)
+
+type getUnwatchTestCase struct {
+ key string
+ val string
+}
+
+var getUnwatchTestCases = []getUnwatchTestCase{
+ {getUnwatchKey, "value1"},
+ {getUnwatchKey, "value2"},
+ {getUnwatchKey, "value3"},
+ {getUnwatchKey, "value4"},
+}
+
+func TestGETUNWATCH(t *testing.T) {
+ publisher := getLocalConnection()
+ subscribers := []net.Conn{getLocalConnection(), getLocalConnection(), getLocalConnection()}
+
+ defer func() {
+ if err := publisher.Close(); err != nil {
+ t.Errorf("Error closing publisher connection: %v", err)
+ }
+ for _, sub := range subscribers {
+ if err := sub.Close(); err != nil {
+ t.Errorf("Error closing subscriber connection: %v", err)
+ }
+ }
+ }()
+
+ FireCommand(publisher, fmt.Sprintf("DEL %s", getUnwatchKey))
+
+ // Fire SET command to set the key
+ res := FireCommand(publisher, fmt.Sprintf("SET %s %s", getUnwatchKey, "value"))
+ assert.Equal(t, "OK", res)
+
+ // subscribe for updates
+ respParsers := make([]*clientio.RESPParser, len(subscribers))
+ for i, subscriber := range subscribers {
+ rp := fireCommandAndGetRESPParser(subscriber, fmt.Sprintf("GET.WATCH %s", getUnwatchKey))
+ assert.NotNil(t, rp)
+ respParsers[i] = rp
+
+ v, err := rp.DecodeOne()
+ assert.Nil(t, err)
+ castedValue, ok := v.([]interface{})
+ if !ok {
+ t.Errorf("Type assertion to []interface{} failed for value: %v", v)
+ }
+ assert.Equal(t, 3, len(castedValue))
+ }
+
+ // Fire updates to the key using the publisher, then check if the subscribers receive the updates in the push-response form (i.e. array of three elements, with third element being the value)
+ for _, tc := range getUnwatchTestCases {
+ res := FireCommand(publisher, fmt.Sprintf("SET %s %s", tc.key, tc.val))
+ assert.Equal(t, "OK", res)
+
+ for _, rp := range respParsers {
+ v, err := rp.DecodeOne()
+ assert.Nil(t, err)
+ castedValue, ok := v.([]interface{})
+ if !ok {
+ t.Errorf("Type assertion to []interface{} failed for value: %v", v)
+ }
+ assert.Equal(t, 3, len(castedValue))
+ assert.Equal(t, "GET", castedValue[0])
+ assert.Equal(t, fingerprint, castedValue[1])
+ assert.Equal(t, tc.val, castedValue[2])
+ }
+ }
+
+ // unsubscribe from updates
+ for _, subscriber := range subscribers {
+ rp := fireCommandAndGetRESPParser(subscriber, fmt.Sprintf("GET.UNWATCH %s", fingerprint))
+ assert.NotNil(t, rp)
+
+ v, err := rp.DecodeOne()
+ assert.NoError(t, err)
+ castedValue, ok := v.(string)
+ if !ok {
+ t.Errorf("Type assertion to string failed for value: %v", v)
+ }
+ assert.Equal(t, castedValue, "OK")
+ }
+
+ // Test updates are not sent after unsubscribing
+ for _, tc := range getUnwatchTestCases[2:] {
+ res := FireCommand(publisher, fmt.Sprintf("SET %s %s", tc.key, tc.val))
+ assert.Equal(t, "OK", res)
+
+ for _, rp := range respParsers {
+ responseChan := make(chan interface{})
+ errChan := make(chan error)
+
+ go func() {
+ v, err := rp.DecodeOne()
+ if err != nil {
+ errChan <- err
+ } else {
+ responseChan <- v
+ }
+ }()
+
+ select {
+ case v := <-responseChan:
+ t.Errorf("Unexpected response after unwatch: %v", v)
+ case err := <-errChan:
+ t.Errorf("Error while decoding: %v", err)
+ case <-time.After(100 * time.Millisecond):
+ // This is the expected behavior - no response within the timeout
+ }
+ }
+ }
+}
+
+func TestGETUNWATCHWithSDK(t *testing.T) {
+ publisher := getLocalSdk()
+ subscribers := []WatchSubscriber{{client: getLocalSdk()}, {client: getLocalSdk()}, {client: getLocalSdk()}}
+
+ publisher.Del(context.Background(), getUnwatchKey)
+
+ // subscribe for updates
+ channels := make([]<-chan *dicedb.WatchResult, len(subscribers))
+ for i, subscriber := range subscribers {
+ watch := subscriber.client.WatchConn(context.Background())
+ subscribers[i].watch = watch
+ assert.NotNil(t, watch)
+ firstMsg, err := watch.Watch(context.Background(), "GET", getUnwatchKey)
+ assert.Nil(t, err)
+ assert.Equal(t, firstMsg.Command, "GET")
+ assert.Equal(t, firstMsg.Fingerprint, fingerprint)
+ channels[i] = watch.Channel()
+ }
+
+ // Fire updates and validate receipt
+ err := publisher.Set(context.Background(), getUnwatchKey, "check", 0).Err()
+ assert.Nil(t, err)
+
+ for _, channel := range channels {
+ v := <-channel
+ assert.Equal(t, "GET", v.Command) // command
+ assert.Equal(t, fingerprint, v.Fingerprint) // Fingerprint
+ assert.Equal(t, "check", v.Data.(string)) // data
+ }
+
+ // unsubscribe from updates
+ for _, subscriber := range subscribers {
+ err := subscriber.watch.Unwatch(context.Background(), "GET", fingerprint)
+ assert.Nil(t, err)
+ }
+
+ // fire updates and validate that they are not received
+ err = publisher.Set(context.Background(), getUnwatchKey, "final", 0).Err()
+ assert.Nil(t, err)
+ for _, channel := range channels {
+ go func(ch <-chan *dicedb.WatchResult) {
+ select {
+ case v := <-ch:
+ assert.Fail(t, fmt.Sprintf("%v", v))
+ case <-time.After(100 * time.Millisecond):
+ // This is the expected behavior - no response within the timeout
+ }
+ }(channel)
+ }
+}
diff --git a/integration_tests/commands/resp/getwatch_test.go b/integration_tests/commands/resp/getwatch_test.go
index c474ace47..537644b8f 100644
--- a/integration_tests/commands/resp/getwatch_test.go
+++ b/integration_tests/commands/resp/getwatch_test.go
@@ -9,7 +9,7 @@ import (
"github.com/dicedb/dice/internal/clientio"
"github.com/dicedb/dicedb-go"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
type WatchSubscriber struct {
@@ -57,11 +57,11 @@ func TestGETWATCH(t *testing.T) {
respParsers := make([]*clientio.RESPParser, len(subscribers))
for i, subscriber := range subscribers {
rp := fireCommandAndGetRESPParser(subscriber, fmt.Sprintf("GET.WATCH %s", getWatchKey))
- assert.Assert(t, rp != nil)
+ assert.True(t, rp != nil)
respParsers[i] = rp
v, err := rp.DecodeOne()
- assert.NilError(t, err)
+ assert.Nil(t, err)
castedValue, ok := v.([]interface{})
if !ok {
t.Errorf("Type assertion to []interface{} failed for value: %v", v)
@@ -76,7 +76,7 @@ func TestGETWATCH(t *testing.T) {
for _, rp := range respParsers {
v, err := rp.DecodeOne()
- assert.NilError(t, err)
+ assert.Nil(t, err)
castedValue, ok := v.([]interface{})
if !ok {
t.Errorf("Type assertion to []interface{} failed for value: %v", v)
@@ -99,9 +99,9 @@ func TestGETWATCHWithSDK(t *testing.T) {
for i, subscriber := range subscribers {
watch := subscriber.client.WatchConn(context.Background())
subscribers[i].watch = watch
- assert.Assert(t, watch != nil)
+ assert.True(t, watch != nil)
firstMsg, err := watch.Watch(context.Background(), "GET", getWatchKey)
- assert.NilError(t, err)
+ assert.Nil(t, err)
assert.Equal(t, firstMsg.Command, "GET")
assert.Equal(t, firstMsg.Fingerprint, "1768826704")
channels[i] = watch.Channel()
@@ -109,7 +109,7 @@ func TestGETWATCHWithSDK(t *testing.T) {
for _, tc := range getWatchTestCases {
err := publisher.Set(context.Background(), tc.key, tc.val, 0).Err()
- assert.NilError(t, err)
+ assert.Nil(t, err)
for _, channel := range channels {
v := <-channel
@@ -130,9 +130,9 @@ func TestGETWATCHWithSDK2(t *testing.T) {
for i, subscriber := range subscribers {
watch := subscriber.client.WatchConn(context.Background())
subscribers[i].watch = watch
- assert.Assert(t, watch != nil)
+ assert.True(t, watch != nil)
firstMsg, err := watch.GetWatch(context.Background(), getWatchKey)
- assert.NilError(t, err)
+ assert.Nil(t, err)
assert.Equal(t, firstMsg.Command, "GET")
assert.Equal(t, firstMsg.Fingerprint, "1768826704")
channels[i] = watch.Channel()
@@ -140,7 +140,7 @@ func TestGETWATCHWithSDK2(t *testing.T) {
for _, tc := range getWatchTestCases {
err := publisher.Set(context.Background(), tc.key, tc.val, 0).Err()
- assert.NilError(t, err)
+ assert.Nil(t, err)
for _, channel := range channels {
v := <-channel
diff --git a/integration_tests/commands/async/hdel_test.go b/integration_tests/commands/resp/hdel_test.go
similarity index 91%
rename from integration_tests/commands/async/hdel_test.go
rename to integration_tests/commands/resp/hdel_test.go
index 3f9fe5d0b..f931f781a 100644
--- a/integration_tests/commands/async/hdel_test.go
+++ b/integration_tests/commands/resp/hdel_test.go
@@ -1,9 +1,9 @@
-package async
+package resp
import (
"testing"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func TestHDEL(t *testing.T) {
@@ -36,7 +36,7 @@ func TestHDEL(t *testing.T) {
for _, tc := range testCases {
for i, cmd := range tc.commands {
result := FireCommand(conn, cmd)
- assert.DeepEqual(t, tc.expected[i], result)
+ assert.Equal(t, tc.expected[i], result)
}
}
}
diff --git a/integration_tests/commands/async/hget_test.go b/integration_tests/commands/resp/hget_test.go
similarity index 92%
rename from integration_tests/commands/async/hget_test.go
rename to integration_tests/commands/resp/hget_test.go
index d604ceff1..79489550b 100644
--- a/integration_tests/commands/async/hget_test.go
+++ b/integration_tests/commands/resp/hget_test.go
@@ -1,9 +1,9 @@
-package async
+package resp
import (
"testing"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func TestHGET(t *testing.T) {
@@ -43,7 +43,7 @@ func TestHGET(t *testing.T) {
for _, tc := range testCases {
for i, cmd := range tc.commands {
result := FireCommand(conn, cmd)
- assert.DeepEqual(t, tc.expected[i], result)
+ assert.Equal(t, tc.expected[i], result)
}
}
}
diff --git a/integration_tests/commands/resp/hincrby_test.go b/integration_tests/commands/resp/hincrby_test.go
index 4d02ec07e..01f694a3b 100644
--- a/integration_tests/commands/resp/hincrby_test.go
+++ b/integration_tests/commands/resp/hincrby_test.go
@@ -4,7 +4,7 @@ import (
"testing"
"time"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func TestHINCRBY(t *testing.T) {
diff --git a/integration_tests/commands/resp/hincrbyfloat_test.go b/integration_tests/commands/resp/hincrbyfloat_test.go
index 7696106b5..df16faeb7 100644
--- a/integration_tests/commands/resp/hincrbyfloat_test.go
+++ b/integration_tests/commands/resp/hincrbyfloat_test.go
@@ -4,7 +4,7 @@ import (
"testing"
"time"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func TestHINCRBYFLOAT(t *testing.T) {
diff --git a/integration_tests/commands/resp/hlen_test.go b/integration_tests/commands/resp/hlen_test.go
index e0a4b6e51..eae9b1be8 100644
--- a/integration_tests/commands/resp/hlen_test.go
+++ b/integration_tests/commands/resp/hlen_test.go
@@ -4,7 +4,7 @@ import (
"testing"
"time"
- testifyAssert "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/assert"
)
func TestHLEN(t *testing.T) {
@@ -83,7 +83,7 @@ func TestHLEN(t *testing.T) {
time.Sleep(tc.delays[i])
}
result := FireCommand(conn, cmd)
- testifyAssert.Equal(t, tc.expect[i], result, "Value mismatch for cmd %s", cmd)
+ assert.Equal(t, tc.expect[i], result, "Value mismatch for cmd %s", cmd)
}
})
}
diff --git a/integration_tests/commands/async/hmget_test.go b/integration_tests/commands/resp/hmget_test.go
similarity index 94%
rename from integration_tests/commands/async/hmget_test.go
rename to integration_tests/commands/resp/hmget_test.go
index 72cb9804d..ad946e9cf 100644
--- a/integration_tests/commands/async/hmget_test.go
+++ b/integration_tests/commands/resp/hmget_test.go
@@ -1,9 +1,9 @@
-package async
+package resp
import (
"testing"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func TestHMGET(t *testing.T) {
@@ -50,7 +50,7 @@ func TestHMGET(t *testing.T) {
for i, cmd := range tc.commands {
// Fire the command and get the result
result := FireCommand(conn, cmd)
- assert.DeepEqual(t, result, tc.expected[i])
+ assert.Equal(t, result, tc.expected[i])
}
})
}
diff --git a/integration_tests/commands/async/hmset_test.go b/integration_tests/commands/resp/hmset_test.go
similarity index 93%
rename from integration_tests/commands/async/hmset_test.go
rename to integration_tests/commands/resp/hmset_test.go
index 8cbd20e60..44e68d0a6 100644
--- a/integration_tests/commands/async/hmset_test.go
+++ b/integration_tests/commands/resp/hmset_test.go
@@ -1,7 +1,7 @@
-package async
+package resp
import (
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
"testing"
)
@@ -39,7 +39,7 @@ func TestHMSET(t *testing.T) {
for _, tc := range testCases {
for i, cmd := range tc.commands {
result := FireCommand(conn, cmd)
- assert.DeepEqual(t, tc.expected[i], result)
+ assert.Equal(t, tc.expected[i], result)
}
}
}
diff --git a/integration_tests/commands/resp/hrandfield_test.go b/integration_tests/commands/resp/hrandfield_test.go
index 5ba4b847c..45f879a7d 100644
--- a/integration_tests/commands/resp/hrandfield_test.go
+++ b/integration_tests/commands/resp/hrandfield_test.go
@@ -5,7 +5,7 @@ import (
"time"
"github.com/google/go-cmp/cmp/cmpopts"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func TestHRANDFIELD(t *testing.T) {
@@ -71,7 +71,7 @@ func TestHRANDFIELD(t *testing.T) {
if str, ok := result.(string); ok {
assert.Equal(t, str, expected, "Unexpected result for command: %s", cmd)
} else {
- assert.DeepEqual(t, result, expected, cmpopts.EquateEmpty())
+ assert.Equal(t, result, expected, cmpopts.EquateEmpty())
}
}
}
@@ -111,7 +111,7 @@ func assertRandomFieldResult(t *testing.T, result interface{}, expected []string
}
// assert that all results are in the expected set or that there is a single valid result
- assert.Assert(t, count == len(resultsList) || count == 1,
+ assert.True(t, count == len(resultsList) || count == 1,
"Expected all results to be in the expected set or a single valid result. Got %d out of %d",
count, len(resultsList))
}
diff --git a/integration_tests/commands/resp/hscan_test.go b/integration_tests/commands/resp/hscan_test.go
index aa540174a..d019cb852 100644
--- a/integration_tests/commands/resp/hscan_test.go
+++ b/integration_tests/commands/resp/hscan_test.go
@@ -4,7 +4,7 @@ import (
"testing"
"time"
- testifyAssert "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/assert"
)
func TestHSCAN(t *testing.T) {
@@ -128,7 +128,7 @@ func TestHSCAN(t *testing.T) {
time.Sleep(tc.delays[i])
}
result := FireCommand(conn, cmd)
- testifyAssert.Equal(t, tc.expect[i], result, "Value mismatch for cmd %s", cmd)
+ assert.Equal(t, tc.expect[i], result, "Value mismatch for cmd %s", cmd)
}
})
}
diff --git a/integration_tests/commands/async/hset_test.go b/integration_tests/commands/resp/hset_test.go
similarity index 91%
rename from integration_tests/commands/async/hset_test.go
rename to integration_tests/commands/resp/hset_test.go
index 4e41c1898..272df8889 100644
--- a/integration_tests/commands/async/hset_test.go
+++ b/integration_tests/commands/resp/hset_test.go
@@ -1,9 +1,9 @@
-package async
+package resp
import (
"testing"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
var ZERO int64 = 0
@@ -40,7 +40,7 @@ func TestHSET(t *testing.T) {
for _, tc := range testCases {
for i, cmd := range tc.commands {
result := FireCommand(conn, cmd)
- assert.DeepEqual(t, tc.expected[i], result)
+ assert.Equal(t, tc.expected[i], result)
}
}
}
diff --git a/integration_tests/commands/async/hsetnx_test.go b/integration_tests/commands/resp/hsetnx_test.go
similarity index 90%
rename from integration_tests/commands/async/hsetnx_test.go
rename to integration_tests/commands/resp/hsetnx_test.go
index 1046e8ac7..8e59e3160 100644
--- a/integration_tests/commands/async/hsetnx_test.go
+++ b/integration_tests/commands/resp/hsetnx_test.go
@@ -1,9 +1,9 @@
-package async
+package resp
import (
"testing"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func TestHSETNX(t *testing.T) {
@@ -32,7 +32,7 @@ func TestHSETNX(t *testing.T) {
for _, tc := range testCases {
for i, cmd := range tc.commands {
result := FireCommand(conn, cmd)
- assert.DeepEqual(t, tc.expected[i], result)
+ assert.Equal(t, tc.expected[i], result)
}
}
}
diff --git a/integration_tests/commands/resp/hstrlen_test.go b/integration_tests/commands/resp/hstrlen_test.go
index aff2d49cf..28c51bf13 100644
--- a/integration_tests/commands/resp/hstrlen_test.go
+++ b/integration_tests/commands/resp/hstrlen_test.go
@@ -4,7 +4,7 @@ import (
"testing"
"time"
- testifyAssert "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/assert"
)
func TestHSTRLEN(t *testing.T) {
@@ -66,7 +66,7 @@ func TestHSTRLEN(t *testing.T) {
time.Sleep(tc.delays[i])
}
result := FireCommand(conn, cmd)
- testifyAssert.Equal(t, tc.expect[i], result, "Value mismatch for cmd %s", cmd)
+ assert.Equal(t, tc.expect[i], result, "Value mismatch for cmd %s", cmd)
}
})
}
diff --git a/integration_tests/commands/resp/hyperloglog_test.go b/integration_tests/commands/resp/hyperloglog_test.go
index 75382c5d5..d6e09a3c7 100644
--- a/integration_tests/commands/resp/hyperloglog_test.go
+++ b/integration_tests/commands/resp/hyperloglog_test.go
@@ -5,7 +5,7 @@ package resp
import (
"testing"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func TestHyperLogLogCommands(t *testing.T) {
@@ -90,7 +90,7 @@ func TestHyperLogLogCommands(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
for i, cmd := range tc.commands {
result := FireCommand(conn, cmd)
- assert.DeepEqual(t, tc.expected[i], result)
+ assert.Equal(t, tc.expected[i], result)
}
})
}
diff --git a/integration_tests/commands/resp/incr_by_float_test.go b/integration_tests/commands/resp/incr_by_float_test.go
index 95dd4443f..e47bdf717 100644
--- a/integration_tests/commands/resp/incr_by_float_test.go
+++ b/integration_tests/commands/resp/incr_by_float_test.go
@@ -3,7 +3,7 @@ package resp
import (
"testing"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func TestINCRBYFLOAT(t *testing.T) {
diff --git a/integration_tests/commands/resp/incr_test.go b/integration_tests/commands/resp/incr_test.go
index 298b42059..24077f925 100644
--- a/integration_tests/commands/resp/incr_test.go
+++ b/integration_tests/commands/resp/incr_test.go
@@ -7,7 +7,7 @@ import (
"time"
"github.com/dicedb/dice/internal/server/utils"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func TestINCR(t *testing.T) {
diff --git a/integration_tests/commands/resp/json_test.go b/integration_tests/commands/resp/json_test.go
index 7915ed6d1..765c9babf 100644
--- a/integration_tests/commands/resp/json_test.go
+++ b/integration_tests/commands/resp/json_test.go
@@ -2,12 +2,11 @@ package resp
import (
"fmt"
+ "sort"
"testing"
"github.com/dicedb/dice/testutils"
- "github.com/google/go-cmp/cmp/cmpopts"
- testifyAssert "github.com/stretchr/testify/assert"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func TestJsonStrlen(t *testing.T) {
@@ -86,7 +85,7 @@ func TestJsonStrlen(t *testing.T) {
if ok {
assert.Equal(t, tc.expected[i], stringResult)
} else {
- assert.Assert(t, arraysArePermutations(tc.expected[i].([]interface{}), result.([]interface{})))
+ assert.True(t, arraysArePermutations(tc.expected[i].([]interface{}), result.([]interface{})))
}
}
})
@@ -289,7 +288,7 @@ func TestJsonObjLen(t *testing.T) {
cmd := tcase.commands[i]
out := tcase.expected[i]
result := FireCommand(conn, cmd)
- assert.DeepEqual(t, out, result)
+ assert.Equal(t, out, result)
}
})
}
@@ -366,14 +365,14 @@ func TestJSONARRPOP(t *testing.T) {
jsonResult, isString := result.(string)
if isString && testutils.IsJSONResponse(jsonResult) {
- testifyAssert.JSONEq(t, out.(string), jsonResult)
+ assert.JSONEq(t, out.(string), jsonResult)
continue
}
if tcase.assertType[i] == "equal" {
assert.Equal(t, out, result)
} else if tcase.assertType[i] == "deep_equal" {
- assert.Assert(t, arraysArePermutations(out.([]interface{}), result.([]interface{})))
+ assert.True(t, arraysArePermutations(out.([]interface{}), result.([]interface{})))
}
}
})
@@ -430,7 +429,7 @@ func TestJsonARRAPPEND(t *testing.T) {
if tcase.assertType[i] == "equal" {
assert.Equal(t, out, result)
} else if tcase.assertType[i] == "deep_equal" {
- assert.Assert(t, arraysArePermutations(out.([]interface{}), result.([]interface{})))
+ assert.True(t, arraysArePermutations(out.([]interface{}), result.([]interface{})))
}
}
})
@@ -502,9 +501,9 @@ func TestJsonARRINSERT(t *testing.T) {
if tcase.assertType[i] == "equal" {
assert.Equal(t, out, result)
} else if tcase.assertType[i] == "deep_equal" {
- assert.Assert(t, arraysArePermutations(out.([]interface{}), result.([]interface{})))
+ assert.True(t, arraysArePermutations(out.([]interface{}), result.([]interface{})))
} else if tcase.assertType[i] == "jsoneq" {
- testifyAssert.JSONEq(t, out.(string), result.(string))
+ assert.JSONEq(t, out.(string), result.(string))
}
}
})
@@ -616,16 +615,32 @@ func TestJsonObjKeys(t *testing.T) {
_, isString := out.(string)
if isString {
outInterface := []interface{}{out}
- assert.DeepEqual(t, outInterface, expected)
+ assert.Equal(t, outInterface, expected)
} else {
- assert.DeepEqual(t, out.([]interface{}), expected,
- cmpopts.SortSlices(func(a, b interface{}) bool {
- return fmt.Sprintf("%v", a) < fmt.Sprintf("%v", b)
- }))
+ assert.ElementsMatch(t,
+ sortNestedSlices(expected),
+ sortNestedSlices(out.([]interface{})),
+ "Mismatch in JSON object keys")
}
})
}
+}
+func sortNestedSlices(data []interface{}) []interface{} {
+ result := make([]interface{}, len(data))
+ for i, item := range data {
+ if slice, ok := item.([]interface{}); ok {
+ sorted := make([]interface{}, len(slice))
+ copy(sorted, slice)
+ sort.Slice(sorted, func(i, j int) bool {
+ return fmt.Sprintf("%v", sorted[i]) < fmt.Sprintf("%v", sorted[j])
+ })
+ result[i] = sorted
+ } else {
+ result[i] = item
+ }
+ }
+ return result
}
func TestJsonARRTRIM(t *testing.T) {
@@ -702,9 +717,9 @@ func TestJsonARRTRIM(t *testing.T) {
if tcase.assertType[i] == "equal" {
assert.Equal(t, out, result)
} else if tcase.assertType[i] == "deep_equal" {
- assert.Assert(t, arraysArePermutations(out.([]interface{}), result.([]interface{})))
+ assert.True(t, arraysArePermutations(out.([]interface{}), result.([]interface{})))
} else if tcase.assertType[i] == "jsoneq" {
- testifyAssert.JSONEq(t, out.(string), result.(string))
+ assert.JSONEq(t, out.(string), result.(string))
}
}
})
diff --git a/integration_tests/commands/resp/set_test.go b/integration_tests/commands/resp/set_test.go
index e6fa927c3..e5ddb036c 100644
--- a/integration_tests/commands/resp/set_test.go
+++ b/integration_tests/commands/resp/set_test.go
@@ -5,7 +5,7 @@ import (
"testing"
"time"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
type TestCase struct {
@@ -43,7 +43,7 @@ func TestSet(t *testing.T) {
for i, cmd := range tc.commands {
result := FireCommand(conn, cmd)
- assert.DeepEqual(t, tc.expected[i], result)
+ assert.Equal(t, tc.expected[i], result)
}
})
}
@@ -148,9 +148,9 @@ func TestSetWithExat(t *testing.T) {
FireCommand(conn, "DEL k")
assert.Equal(t, "OK", FireCommand(conn, "SET k v EXAT "+Etime), "Value mismatch for cmd SET k v EXAT "+Etime)
assert.Equal(t, "v", FireCommand(conn, "GET k"), "Value mismatch for cmd GET k")
- assert.Assert(t, FireCommand(conn, "TTL k").(int64) <= 5, "Value mismatch for cmd TTL k")
+ assert.True(t, FireCommand(conn, "TTL k").(int64) <= 5, "Value mismatch for cmd TTL k")
time.Sleep(3 * time.Second)
- assert.Assert(t, FireCommand(conn, "TTL k").(int64) <= 3, "Value mismatch for cmd TTL k")
+ assert.True(t, FireCommand(conn, "TTL k").(int64) <= 3, "Value mismatch for cmd TTL k")
time.Sleep(3 * time.Second)
assert.Equal(t, "(nil)", FireCommand(conn, "GET k"), "Value mismatch for cmd GET k")
assert.Equal(t, int64(-2), FireCommand(conn, "TTL k"), "Value mismatch for cmd TTL k")
@@ -176,12 +176,13 @@ func TestSetWithExat(t *testing.T) {
func TestWithKeepTTLFlag(t *testing.T) {
conn := getLocalConnection()
+ expiryTime := strconv.FormatInt(time.Now().Add(1*time.Minute).UnixMilli(), 10)
defer conn.Close()
for _, tcase := range []TestCase{
{
- commands: []string{"SET k v EX 2", "SET k vv KEEPTTL", "GET k", "SET kk vv", "SET kk vvv KEEPTTL", "GET kk"},
- expected: []interface{}{"OK", "OK", "vv", "OK", "OK", "vvv"},
+ commands: []string{"SET k v EX 2", "SET k vv KEEPTTL", "GET k", "SET kk vv", "SET kk vvv KEEPTTL", "GET kk", "SET K V EX 2 KEEPTTL", "SET K1 vv PX 2000 KEEPTTL", "SET K2 vv EXAT " + expiryTime + " KEEPTTL"},
+ expected: []interface{}{"OK", "OK", "vv", "OK", "OK", "vvv", "ERR syntax error", "ERR syntax error", "ERR syntax error"},
},
} {
for i := 0; i < len(tcase.commands); i++ {
diff --git a/integration_tests/commands/async/ttl_pttl_test.go b/integration_tests/commands/resp/ttl_pttl_test.go
similarity index 91%
rename from integration_tests/commands/async/ttl_pttl_test.go
rename to integration_tests/commands/resp/ttl_pttl_test.go
index 8c5dd82d1..4853196d4 100644
--- a/integration_tests/commands/async/ttl_pttl_test.go
+++ b/integration_tests/commands/resp/ttl_pttl_test.go
@@ -1,10 +1,10 @@
-package async
+package resp
import (
"testing"
"time"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func TestTTLPTTL(t *testing.T) {
@@ -72,9 +72,9 @@ func TestTTLPTTL(t *testing.T) {
}
result := FireCommand(conn, cmd)
if tc.assertType[i] == "equal" {
- assert.DeepEqual(t, tc.expected[i], result)
+ assert.Equal(t, tc.expected[i], result)
} else if tc.assertType[i] == "assert" {
- assert.Assert(t, result.(int64) <= tc.expected[i].(int64), "Expected %v to be less than or equal to %v", result, tc.expected[i])
+ assert.True(t, result.(int64) <= tc.expected[i].(int64), "Expected %v to be less than or equal to %v", result, tc.expected[i])
}
}
})
diff --git a/integration_tests/commands/websocket/expire_test.go b/integration_tests/commands/websocket/expire_test.go
new file mode 100644
index 000000000..9cf9e8713
--- /dev/null
+++ b/integration_tests/commands/websocket/expire_test.go
@@ -0,0 +1,585 @@
+package websocket
+
+import (
+ "strconv"
+ "testing"
+ "time"
+
+ "gotest.tools/v3/assert"
+)
+
+func TestEXPIRE(t *testing.T) {
+ exec := NewWebsocketCommandExecutor()
+ conn := exec.ConnectToServer()
+ defer conn.Close()
+
+ testCases := []struct {
+ name string
+ setup string
+ commands []string
+ expected []interface{}
+ delay []time.Duration
+ }{
+ {
+ name: "Set with EXPIRE command",
+ setup: "",
+ commands: []string{
+ "SET test_key test_value",
+ "EXPIRE test_key 1",
+ },
+ expected: []interface{}{"OK", float64(1)},
+ delay: []time.Duration{0, 0},
+ },
+ {
+ name: "Check if key is nil after expiration",
+ setup: "SET test_key test_value",
+ commands: []string{
+ "EXPIRE test_key 1",
+ "GET test_key",
+ },
+ expected: []interface{}{float64(1), nil},
+ delay: []time.Duration{0, 1100 * time.Millisecond},
+ },
+ {
+ name: "EXPIRE non-existent key",
+ setup: "",
+ commands: []string{
+ "EXPIRE non_existent_key 1",
+ },
+ expected: []interface{}{float64(0)},
+ delay: []time.Duration{0, 0},
+ },
+ {
+ name: "EXPIRE with past time",
+ setup: "SET test_key test_value",
+ commands: []string{
+ "EXPIRE test_key -1",
+ "GET test_key",
+ },
+ expected: []interface{}{"ERR invalid expire time in 'expire' command", "test_value"},
+ delay: []time.Duration{0, 0},
+ },
+ {
+ name: "EXPIRE with invalid syntax",
+ setup: "SET test_key test_value",
+ commands: []string{
+ "EXPIRE test_key",
+ },
+ expected: []interface{}{"ERR wrong number of arguments for 'expire' command"},
+ delay: []time.Duration{0},
+ },
+ {
+ name: "Test(NX): Set the expiration only if the key has no expiration time",
+ setup: "",
+ commands: []string{
+ "SET test_key test_value",
+ "EXPIRE test_key " + strconv.FormatInt(1, 10) + " NX",
+ "EXPIRE test_key " + strconv.FormatInt(1, 10) + " NX",
+ },
+ expected: []interface{}{"OK", float64(1), float64(0)},
+ delay: []time.Duration{0, 0, 0},
+ },
+
+ {
+ name: "Test(XX): Set the expiration only if the key already has an expiration time",
+ setup: "",
+ commands: []string{
+ "SET test_key test_value",
+ "EXPIRE test_key " + strconv.FormatInt(10, 10) + " XX",
+ "TTL test_key",
+ "EXPIRE test_key " + strconv.FormatInt(10, 10),
+ "EXPIRE test_key " + strconv.FormatInt(10, 10) + " XX",
+ },
+ expected: []interface{}{"OK", float64(0), float64(-1), float64(1), float64(1)},
+ delay: []time.Duration{0, 0, 0, 0, 0},
+ },
+
+ {
+ name: "TEST(GT): Set the expiration only if the new expiration time is greater than the current one",
+ setup: "",
+ commands: []string{
+ "SET test_key test_value",
+ "EXPIRE test_key " + strconv.FormatInt(10, 10) + " GT",
+ "TTL test_key",
+ "EXPIRE test_key " + strconv.FormatInt(10, 10),
+ "EXPIRE test_key " + strconv.FormatInt(20, 10) + " GT",
+ },
+ expected: []interface{}{"OK", float64(0), float64(-1), float64(1), float64(1)},
+ delay: []time.Duration{0, 0, 0, 0, 0},
+ },
+
+ {
+ name: "TEST(LT): Set the expiration only if the new expiration time is less than the current one",
+ setup: "",
+ commands: []string{
+ "SET test_key test_value",
+ "EXPIRE test_key " + strconv.FormatInt(10, 10) + " LT",
+ "EXPIRE test_key " + strconv.FormatInt(20, 10) + " LT",
+ },
+ expected: []interface{}{"OK", float64(1), float64(0)},
+ delay: []time.Duration{0, 0, 0},
+ },
+
+ {
+ name: "TEST(LT): Set the expiration only if the new expiration time is less than the current one",
+ setup: "",
+ commands: []string{
+ "SET test_key test_value",
+ "EXPIRE test_key " + strconv.FormatInt(10, 10) + " LT",
+ "EXPIRE test_key " + strconv.FormatInt(20, 10) + " LT",
+ },
+ expected: []interface{}{"OK", float64(1), float64(0)},
+ delay: []time.Duration{0, 0, 0},
+ },
+
+ {
+ name: "TEST(NX + LT/GT)",
+ setup: "",
+ commands: []string{
+ "SET test_key test_value",
+ "EXPIRE test_key " + strconv.FormatInt(20, 10) + " NX",
+ "EXPIRE test_key " + strconv.FormatInt(20, 10) + " NX" + " LT",
+ "EXPIRE test_key " + strconv.FormatInt(20, 10) + " NX" + " GT",
+ "GET test_key",
+ },
+ expected: []interface{}{"OK", float64(1),
+ "ERR NX and XX, GT or LT options at the same time are not compatible",
+ "ERR NX and XX, GT or LT options at the same time are not compatible",
+ "test_value"},
+ delay: []time.Duration{0, 0, 0, 0, 0},
+ },
+ {
+ name: "TEST(XX + LT/GT)",
+ setup: "",
+ commands: []string{
+ "SET test_key test_value",
+ "EXPIRE test_key " + strconv.FormatInt(20, 10),
+ "EXPIRE test_key " + strconv.FormatInt(5, 10) + " XX" + " LT",
+ "EXPIRE test_key " + strconv.FormatInt(10, 10) + " XX" + " GT",
+ "EXPIRE test_key " + strconv.FormatInt(20, 10) + " XX" + " GT",
+ "GET test_key",
+ },
+ expected: []interface{}{"OK", float64(1), float64(1), float64(1), float64(1), "test_value"},
+ delay: []time.Duration{0, 0, 0, 0, 0, 0},
+ },
+ {
+ name: "Test if value is nil after expiration",
+ setup: "",
+ commands: []string{
+ "SET test_key test_value",
+ "EXPIRE test_key " + strconv.FormatInt(20, 10),
+ "EXPIRE test_key " + strconv.FormatInt(2, 10) + " XX" + " LT",
+ "GET test_key",
+ },
+ expected: []interface{}{"OK", float64(1), float64(1), nil},
+ delay: []time.Duration{0, 0, 0, 2 * time.Second},
+ },
+ {
+ name: "Test if value is nil after expiration",
+ setup: "",
+ commands: []string{
+ "SET test_key test_value",
+ "EXPIRE test_key " + strconv.FormatInt(2, 10) + " NX",
+ "GET test_key",
+ },
+ expected: []interface{}{"OK", float64(1), nil},
+ delay: []time.Duration{0, 0, 2 * time.Second},
+ },
+ {
+ name: "Invalid Command Test",
+ setup: "",
+ commands: []string{
+ "SET test_key test_value",
+ "EXPIRE test_key " + strconv.FormatInt(1, 10) + " XX" + " " + "rr",
+ "EXPIRE test_key " + strconv.FormatInt(1, 10) + " XX" + " " + "NX",
+ "EXPIRE test_key " + strconv.FormatInt(1, 10) + " GT" + " " + "lt",
+ "EXPIRE test_key " + strconv.FormatInt(1, 10) + " GT" + " " + "lt" + " " + "xx",
+ "EXPIRE test_key " + strconv.FormatInt(1, 10) + " GT" + " " + "lt" + " " + "nx",
+ "EXPIRE test_key " + strconv.FormatInt(1, 10) + " nx" + " " + "xx" + " " + "gt",
+ "EXPIRE test_key " + strconv.FormatInt(1, 10) + " nx" + " " + "xx" + " " + "lt",
+ },
+ expected: []interface{}{"OK", "ERR Unsupported option rr",
+ "ERR NX and XX, GT or LT options at the same time are not compatible",
+ "ERR GT and LT options at the same time are not compatible",
+ "ERR GT and LT options at the same time are not compatible",
+ "ERR NX and XX, GT or LT options at the same time are not compatible",
+ "ERR NX and XX, GT or LT options at the same time are not compatible",
+ "ERR NX and XX, GT or LT options at the same time are not compatible"},
+ delay: []time.Duration{0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ }
+ for _, tc := range testCases {
+ t.Run(tc.name, func(t *testing.T) {
+ // Setup
+ if tc.setup != "" {
+ _, err := exec.FireCommandAndReadResponse(conn, tc.setup)
+ if err != nil {
+ t.Fatalf("Error executing command: %v", err)
+ }
+ }
+
+ // Execute commands
+ var results []interface{}
+ for i, cmd := range tc.commands {
+ // Wait if delay is specified
+ if tc.delay[i] > 0 {
+ time.Sleep(tc.delay[i])
+ }
+ result, err := exec.FireCommandAndReadResponse(conn, cmd)
+ if err != nil {
+ t.Fatalf("Error executing command: %v", err)
+ }
+ results = append(results, result)
+ }
+
+ // Validate results
+ for i, expected := range tc.expected {
+ if i >= len(results) {
+ t.Fatalf("Not enough results. Expected %d, got %d", len(tc.expected), len(results))
+ }
+
+ if expected == nil {
+ assert.Assert(t, results[i] == nil || results[i] == "",
+ "Expected nil or empty result, got %v", results[i])
+ } else {
+ assert.DeepEqual(t, expected, results[i])
+ }
+ }
+ })
+ }
+}
+
+func TestEXPIREAT(t *testing.T) {
+ exec := NewWebsocketCommandExecutor()
+ conn := exec.ConnectToServer()
+ defer conn.Close()
+
+ testCases := []struct {
+ name string
+ setup string
+ commands []string
+ expected []interface{}
+ delay []time.Duration
+ }{
+ {
+ name: "Set with EXPIREAT command",
+ setup: "",
+ commands: []string{
+ "SET test_key test_value",
+ "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+1, 10),
+ },
+ expected: []interface{}{"OK", float64(1)},
+ delay: []time.Duration{0, 0},
+ },
+ {
+ name: "Check if key is nil after expiration",
+ setup: "SET test_key test_value",
+ commands: []string{
+ "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+1, 10),
+ "GET test_key",
+ },
+ expected: []interface{}{float64(1), nil},
+ delay: []time.Duration{0, 1100 * time.Millisecond},
+ },
+ {
+ name: "EXPIREAT non-existent key",
+ setup: "",
+ commands: []string{
+ "EXPIREAT non_existent_key " + strconv.FormatInt(time.Now().Unix()+1, 10),
+ },
+ expected: []interface{}{float64(0)},
+ delay: []time.Duration{0, 0},
+ },
+ {
+ name: "EXPIREAT with past time",
+ setup: "SET test_key test_value",
+ commands: []string{
+ "EXPIREAT test_key " + strconv.FormatInt(-1, 10),
+ "GET test_key",
+ },
+ expected: []interface{}{"ERR invalid expire time in 'expireat' command", "test_value"},
+ delay: []time.Duration{0, 0},
+ },
+ {
+ name: "EXPIREAT with invalid syntax",
+ setup: "SET test_key test_value",
+ commands: []string{
+ "EXPIREAT test_key",
+ },
+ expected: []interface{}{"ERR wrong number of arguments for 'expireat' command"},
+ delay: []time.Duration{0},
+ },
+ {
+ name: "Test(NX): Set the expiration only if the key has no expiration time",
+ setup: "",
+ commands: []string{
+ "SET test_key test_value",
+ "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+10, 10) + " NX",
+ "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+1, 10) + " NX",
+ },
+ expected: []interface{}{"OK", float64(1), float64(0)},
+ delay: []time.Duration{0, 0, 0},
+ },
+
+ {
+ name: "Test(XX): Set the expiration only if the key already has an expiration time",
+ setup: "",
+ commands: []string{
+ "SET test_key test_value",
+ "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+10, 10) + " XX",
+ "TTL test_key",
+ "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+10, 10),
+ "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+10, 10) + " XX",
+ },
+ expected: []interface{}{"OK", float64(0), float64(-1), float64(1), float64(1)},
+ delay: []time.Duration{0, 0, 0, 0, 0},
+ },
+
+ {
+ name: "TEST(GT): Set the expiration only if the new expiration time is greater than the current one",
+ setup: "",
+ commands: []string{
+ "SET test_key test_value",
+ "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+10, 10) + " GT",
+ "TTL test_key",
+ "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+10, 10),
+ "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+20, 10) + " GT",
+ },
+ expected: []interface{}{"OK", float64(0), float64(-1), float64(1), float64(1)},
+ delay: []time.Duration{0, 0, 0, 0, 0},
+ },
+
+ {
+ name: "TEST(LT): Set the expiration only if the new expiration time is less than the current one",
+ setup: "",
+ commands: []string{
+ "SET test_key test_value",
+ "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+10, 10) + " LT",
+ "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+20, 10) + " LT",
+ },
+ expected: []interface{}{"OK", float64(1), float64(0)},
+ delay: []time.Duration{0, 0, 0},
+ },
+
+ {
+ name: "TEST(LT): Set the expiration only if the new expiration time is less than the current one",
+ setup: "",
+ commands: []string{
+ "SET test_key test_value",
+ "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+10, 10) + " LT",
+ "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+20, 10) + " LT",
+ },
+ expected: []interface{}{"OK", float64(1), float64(0)},
+ delay: []time.Duration{0, 0, 0},
+ },
+
+ {
+ name: "TEST(NX + LT/GT)",
+ setup: "",
+ commands: []string{
+ "SET test_key test_value",
+ "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+20, 10) + " NX",
+ "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+20, 10) + " NX" + " LT",
+ "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+20, 10) + " NX" + " GT",
+ "GET test_key",
+ },
+ expected: []interface{}{"OK", float64(1),
+ "ERR NX and XX, GT or LT options at the same time are not compatible",
+ "ERR NX and XX, GT or LT options at the same time are not compatible",
+ "test_value"},
+ delay: []time.Duration{0, 0, 0, 0, 0},
+ },
+ {
+ name: "TEST(XX + LT/GT)",
+ setup: "",
+ commands: []string{
+ "SET test_key test_value",
+ "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+20, 10),
+ "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+5, 10) + " XX" + " LT",
+ "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+10, 10) + " XX" + " GT",
+ "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+20, 10) + " XX" + " GT",
+ "GET test_key",
+ },
+ expected: []interface{}{"OK", float64(1), float64(1), float64(1), float64(1), "test_value"},
+ delay: []time.Duration{0, 0, 0, 0, 0, 0},
+ },
+ {
+ name: "Test if value is nil after expiration",
+ setup: "",
+ commands: []string{
+ "SET test_key test_value",
+ "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+20, 10),
+ "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+2, 10) + " XX" + " LT",
+ "GET test_key",
+ },
+ expected: []interface{}{"OK", float64(1), float64(1), nil},
+ delay: []time.Duration{0, 0, 0, 2 * time.Second},
+ },
+ {
+ name: "Test if value is nil after expiration",
+ setup: "",
+ commands: []string{
+ "SET test_key test_value",
+ "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+2, 10) + " NX",
+ "GET test_key",
+ },
+ expected: []interface{}{"OK", float64(1), nil},
+ delay: []time.Duration{0, 0, 2 * time.Second},
+ },
+ {
+ name: "Invalid Command Test",
+ setup: "",
+ commands: []string{
+ "SET test_key test_value",
+ "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+1, 10) + " XX" + " " + "rr",
+ "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+1, 10) + " XX" + " " + "NX",
+ "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+1, 10) + " GT" + " " + "lt",
+ "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+1, 10) + " GT" + " " + "lt" + " " + "xx",
+ "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+1, 10) + " GT" + " " + "lt" + " " + "nx",
+ "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+1, 10) + " nx" + " " + "xx" + " " + "gt",
+ "EXPIREAT test_key " + strconv.FormatInt(time.Now().Unix()+1, 10) + " nx" + " " + "xx" + " " + "lt",
+ },
+ expected: []interface{}{"OK", "ERR Unsupported option rr",
+ "ERR NX and XX, GT or LT options at the same time are not compatible",
+ "ERR GT and LT options at the same time are not compatible",
+ "ERR GT and LT options at the same time are not compatible",
+ "ERR NX and XX, GT or LT options at the same time are not compatible",
+ "ERR NX and XX, GT or LT options at the same time are not compatible",
+ "ERR NX and XX, GT or LT options at the same time are not compatible"},
+ delay: []time.Duration{0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ }
+ for _, tc := range testCases {
+ t.Run(tc.name, func(t *testing.T) {
+ // Setup
+ if tc.setup != "" {
+ _, err := exec.FireCommandAndReadResponse(conn, tc.setup)
+ if err != nil {
+ t.Fatalf("Error executing command: %v", err)
+ }
+ }
+
+ // Execute commands
+ var results []interface{}
+ for i, cmd := range tc.commands {
+ // Wait if delay is specified
+ if tc.delay[i] > 0 {
+ time.Sleep(tc.delay[i])
+ }
+ result, err := exec.FireCommandAndReadResponse(conn, cmd)
+ if err != nil {
+ t.Fatalf("Error executing command: %v", err)
+ }
+ results = append(results, result)
+ }
+ // Validate results
+ for i, expected := range tc.expected {
+ if i >= len(results) {
+ t.Fatalf("Not enough results. Expected %d, got %d", len(tc.expected), len(results))
+ }
+
+ if expected == nil {
+ assert.Assert(t, results[i] == nil || results[i] == "",
+ "Expected nil or empty result, got %v", results[i])
+ } else {
+ assert.DeepEqual(t, expected, results[i])
+ }
+ }
+ })
+ }
+}
+
+func TestEXPIRETIME(t *testing.T) {
+ exec := NewWebsocketCommandExecutor()
+ conn := exec.ConnectToServer()
+ defer conn.Close()
+
+ futureUnixTimestamp := time.Now().Unix() + 1
+
+ testCases := []struct {
+ name string
+ setup string
+ commands []string
+ expected []interface{}
+ delay []time.Duration
+ }{
+ {
+ name: "EXPIRETIME command",
+ setup: "SET test_key test_value",
+ commands: []string{
+ "EXPIREAT test_key " + strconv.FormatInt(futureUnixTimestamp, 10),
+ "EXPIRETIME test_key",
+ },
+ expected: []interface{}{float64(1), float64(futureUnixTimestamp)},
+ delay: []time.Duration{0, 0},
+ },
+ {
+ name: "EXPIRETIME non-existent key",
+ setup: "",
+ commands: []string{
+ "EXPIRETIME non_existent_key",
+ },
+ expected: []interface{}{float64(-2)},
+ delay: []time.Duration{0},
+ },
+ {
+ name: "EXPIRETIME with past time",
+ setup: "SET test_key test_value",
+ commands: []string{
+ "EXPIREAT test_key 1724167183",
+ "EXPIRETIME test_key",
+ },
+ expected: []interface{}{float64(1), float64(-2)},
+ delay: []time.Duration{0, 0},
+ },
+ {
+ name: "EXPIRETIME with invalid syntax",
+ setup: "SET test_key test_value",
+ commands: []string{
+ "EXPIRETIME",
+ "EXPIRETIME key1 key2",
+ },
+ expected: []interface{}{"ERR wrong number of arguments for 'expiretime' command", "ERR wrong number of arguments for 'expiretime' command"},
+ delay: []time.Duration{0, 0},
+ },
+ }
+ for _, tc := range testCases {
+ t.Run(tc.name, func(t *testing.T) {
+ // Setup
+ if tc.setup != "" {
+ _, err := exec.FireCommandAndReadResponse(conn, tc.setup)
+ if err != nil {
+ t.Fatalf("Error executing command: %v", err)
+ }
+ }
+
+ // Execute commands
+ var results []interface{}
+ for i, cmd := range tc.commands {
+ // Wait if delay is specified
+ if tc.delay[i] > 0 {
+ time.Sleep(tc.delay[i])
+ }
+ result, err := exec.FireCommandAndReadResponse(conn, cmd)
+ if err != nil {
+ t.Fatalf("Error executing command: %v", err)
+ }
+ results = append(results, result)
+ }
+
+ // Validate results
+ for i, expected := range tc.expected {
+ if i >= len(results) {
+ t.Fatalf("Not enough results. Expected %d, got %d", len(tc.expected), len(results))
+ }
+
+ if expected == nil {
+ assert.Assert(t, results[i] == nil || results[i] == "",
+ "Expected nil or empty result, got %v", results[i])
+ } else {
+ assert.DeepEqual(t, expected, results[i])
+ }
+ }
+ })
+ }
+}
diff --git a/integration_tests/commands/websocket/hlen_test.go b/integration_tests/commands/websocket/hlen_test.go
index a55e2ba42..093a89dd6 100644
--- a/integration_tests/commands/websocket/hlen_test.go
+++ b/integration_tests/commands/websocket/hlen_test.go
@@ -4,7 +4,7 @@ import (
"testing"
"time"
- testifyAssert "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/assert"
)
func TestHLEN(t *testing.T) {
@@ -68,8 +68,8 @@ func TestHLEN(t *testing.T) {
time.Sleep(tc.delays[i])
}
result, err := exec.FireCommandAndReadResponse(conn, cmd)
- testifyAssert.Nil(t, err)
- testifyAssert.Equal(t, tc.expect[i], result, "Value mismatch for cmd %s", cmd)
+ assert.Nil(t, err)
+ assert.Equal(t, tc.expect[i], result, "Value mismatch for cmd %s", cmd)
}
})
}
diff --git a/integration_tests/commands/websocket/hscan_test.go b/integration_tests/commands/websocket/hscan_test.go
index 2f65ae805..d9f1f8ca2 100644
--- a/integration_tests/commands/websocket/hscan_test.go
+++ b/integration_tests/commands/websocket/hscan_test.go
@@ -4,7 +4,7 @@ import (
"testing"
"time"
- testifyAssert "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/assert"
)
func TestHSCAN(t *testing.T) {
@@ -110,8 +110,8 @@ func TestHSCAN(t *testing.T) {
time.Sleep(tc.delays[i])
}
result, err := exec.FireCommandAndReadResponse(conn, cmd)
- testifyAssert.Nil(t, err)
- testifyAssert.Equal(t, tc.expect[i], result, "Value mismatch for cmd %s", cmd)
+ assert.Nil(t, err)
+ assert.Equal(t, tc.expect[i], result, "Value mismatch for cmd %s", cmd)
}
})
}
diff --git a/integration_tests/commands/websocket/hstrlen_test.go b/integration_tests/commands/websocket/hstrlen_test.go
index 736dbfbcc..3215a0b9e 100644
--- a/integration_tests/commands/websocket/hstrlen_test.go
+++ b/integration_tests/commands/websocket/hstrlen_test.go
@@ -4,7 +4,7 @@ import (
"testing"
"time"
- testifyAssert "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/assert"
)
func TestHSTRLEN(t *testing.T) {
@@ -62,8 +62,8 @@ func TestHSTRLEN(t *testing.T) {
time.Sleep(tc.delays[i])
}
result, err := exec.FireCommandAndReadResponse(conn, cmd)
- testifyAssert.Nil(t, err)
- testifyAssert.Equal(t, tc.expect[i], result, "Value mismatch for cmd %s", cmd)
+ assert.Nil(t, err)
+ assert.Equal(t, tc.expect[i], result, "Value mismatch for cmd %s", cmd)
}
})
}
diff --git a/integration_tests/commands/websocket/json_test.go b/integration_tests/commands/websocket/json_test.go
index 32f244ec1..b5399d93b 100644
--- a/integration_tests/commands/websocket/json_test.go
+++ b/integration_tests/commands/websocket/json_test.go
@@ -1,13 +1,12 @@
package websocket
import (
- "fmt"
- "github.com/dicedb/dice/testutils"
+ "sort"
"testing"
- "github.com/google/go-cmp/cmp/cmpopts"
- testifyAssert "github.com/stretchr/testify/assert"
- "gotest.tools/v3/assert"
+ "github.com/dicedb/dice/testutils"
+
+ "github.com/stretchr/testify/assert"
)
func TestJSONClearOperations(t *testing.T) {
@@ -19,8 +18,8 @@ func TestJSONClearOperations(t *testing.T) {
defer func() {
resp, err := exec.FireCommandAndReadResponse(conn, "DEL user")
- testifyAssert.Nil(t, err)
- testifyAssert.Equal(t, float64(1), resp)
+ assert.Nil(t, err)
+ assert.Equal(t, float64(1), resp)
}()
testCases := []struct {
@@ -93,8 +92,8 @@ func TestJSONClearOperations(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
for i, cmd := range tc.commands {
result, err := exec.FireCommandAndReadResponse(conn, cmd)
- testifyAssert.Nil(t, err)
- testifyAssert.Equal(t, tc.expected[i], result)
+ assert.Nil(t, err)
+ assert.Equal(t, tc.expected[i], result)
}
})
}
@@ -110,8 +109,8 @@ func TestJsonStrlen(t *testing.T) {
defer func() {
resp, err := exec.FireCommandAndReadResponse(conn, "DEL doc")
- testifyAssert.Nil(t, err)
- testifyAssert.Equal(t, float64(1), resp)
+ assert.Nil(t, err)
+ assert.Equal(t, float64(1), resp)
}()
testCases := []struct {
@@ -181,12 +180,12 @@ func TestJsonStrlen(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
for i, cmd := range tc.commands {
result, err := exec.FireCommandAndReadResponse(conn, cmd)
- testifyAssert.Nil(t, err, "error: %v", err)
+ assert.Nil(t, err, "error: %v", err)
stringResult, ok := result.(string)
if ok {
- testifyAssert.Equal(t, tc.expected[i], stringResult)
+ assert.Equal(t, tc.expected[i], stringResult)
} else {
- testifyAssert.True(t, arraysArePermutations(tc.expected[i].([]interface{}), result.([]interface{})))
+ assert.True(t, arraysArePermutations(tc.expected[i].([]interface{}), result.([]interface{})))
}
}
})
@@ -207,8 +206,8 @@ func TestJsonObjLen(t *testing.T) {
defer func() {
resp, err := exec.FireCommandAndReadResponse(conn, "DEL obj")
- testifyAssert.Nil(t, err)
- testifyAssert.Equal(t, float64(1), resp)
+ assert.Nil(t, err)
+ assert.Equal(t, float64(1), resp)
}()
testCases := []struct {
@@ -310,8 +309,8 @@ func TestJsonObjLen(t *testing.T) {
cmd := tcase.commands[i]
out := tcase.expected[i]
result, err := exec.FireCommandAndReadResponse(conn, cmd)
- testifyAssert.Nil(t, err)
- testifyAssert.Equal(t, out, result)
+ assert.Nil(t, err)
+ assert.Equal(t, out, result)
}
})
}
@@ -328,9 +327,9 @@ func TestJsonARRTRIM(t *testing.T) {
defer func() {
resp1, err := exec.FireCommandAndReadResponse(conn, "DEL a")
resp2, err := exec.FireCommandAndReadResponse(conn, "DEL b")
- testifyAssert.Nil(t, err)
- testifyAssert.Equal(t, float64(1), resp1)
- testifyAssert.Equal(t, float64(1), resp2)
+ assert.Nil(t, err)
+ assert.Equal(t, float64(1), resp1)
+ assert.Equal(t, float64(1), resp2)
}()
testCases := []struct {
@@ -395,13 +394,13 @@ func TestJsonARRTRIM(t *testing.T) {
cmd := tcase.commands[i]
out := tcase.expected[i]
result, err := exec.FireCommandAndReadResponse(conn, cmd)
- testifyAssert.Nil(t, err)
+ assert.Nil(t, err)
if tcase.assertType[i] == "equal" {
- testifyAssert.Equal(t, out, result)
+ assert.Equal(t, out, result)
} else if tcase.assertType[i] == "deep_equal" {
- assert.Assert(t, arraysArePermutations(out.([]interface{}), result.([]interface{})))
+ assert.True(t, arraysArePermutations(out.([]interface{}), result.([]interface{})))
} else if tcase.assertType[i] == "jsoneq" {
- testifyAssert.JSONEq(t, out.(string), result.(string))
+ assert.JSONEq(t, out.(string), result.(string))
}
}
})
@@ -419,9 +418,9 @@ func TestJsonARRINSERT(t *testing.T) {
defer func() {
resp1, err := exec.FireCommandAndReadResponse(conn, "DEL a")
resp2, err := exec.FireCommandAndReadResponse(conn, "DEL b")
- testifyAssert.Nil(t, err)
- testifyAssert.Equal(t, float64(1), resp1)
- testifyAssert.Equal(t, float64(1), resp2)
+ assert.Nil(t, err)
+ assert.Equal(t, float64(1), resp1)
+ assert.Equal(t, float64(1), resp2)
}()
testCases := []struct {
@@ -474,13 +473,13 @@ func TestJsonARRINSERT(t *testing.T) {
cmd := tcase.commands[i]
out := tcase.expected[i]
result, err := exec.FireCommandAndReadResponse(conn, cmd)
- testifyAssert.Nil(t, err)
+ assert.Nil(t, err)
if tcase.assertType[i] == "equal" {
- testifyAssert.Equal(t, out, result)
+ assert.Equal(t, out, result)
} else if tcase.assertType[i] == "deep_equal" {
- assert.Assert(t, arraysArePermutations(out.([]interface{}), result.([]interface{})))
+ assert.True(t, arraysArePermutations(out.([]interface{}), result.([]interface{})))
} else if tcase.assertType[i] == "jsoneq" {
- testifyAssert.JSONEq(t, out.(string), result.(string))
+ assert.JSONEq(t, out.(string), result.(string))
}
}
})
@@ -584,20 +583,30 @@ func TestJsonObjKeyslmao(t *testing.T) {
for _, tcase := range testCases {
t.Run(tcase.name, func(t *testing.T) {
_, err := exec.FireCommandAndReadResponse(conn, tcase.setCommand)
- testifyAssert.Nil(t, err)
+ assert.Nil(t, err)
expected := tcase.expected
- out, err := exec.FireCommandAndReadResponse(conn, tcase.testCommand)
+ out, _ := exec.FireCommandAndReadResponse(conn, tcase.testCommand)
+
+ sortNested := func(data []interface{}) {
+ for _, elem := range data {
+ if innerSlice, ok := elem.([]interface{}); ok {
+ sort.Slice(innerSlice, func(i, j int) bool {
+ return innerSlice[i].(string) < innerSlice[j].(string)
+ })
+ }
+ }
+ }
- _, isString := out.(string)
- if isString {
- outInterface := []interface{}{out}
- assert.DeepEqual(t, outInterface, expected)
- } else {
- assert.DeepEqual(t, out.([]interface{}), expected,
- cmpopts.SortSlices(func(a, b interface{}) bool {
- return fmt.Sprintf("%v", a) < fmt.Sprintf("%v", b)
- }))
+ if expected != nil {
+ sortNested(expected)
}
+ if outSlice, ok := out.([]interface{}); ok {
+ sortNested(outSlice)
+ assert.ElementsMatch(t, expected, outSlice)
+ } else {
+ outInterface := []interface{}{out}
+ assert.ElementsMatch(t, expected, outInterface)
+ }
})
}
}
@@ -671,18 +680,18 @@ func TestJSONARRPOP(t *testing.T) {
cmd := tcase.commands[i]
out := tcase.expected[i]
result, err := exec.FireCommandAndReadResponse(conn, cmd)
- testifyAssert.Nil(t, err)
+ assert.Nil(t, err)
jsonResult, isString := result.(string)
if isString && testutils.IsJSONResponse(jsonResult) {
- testifyAssert.JSONEq(t, out.(string), jsonResult)
+ assert.JSONEq(t, out.(string), jsonResult)
continue
}
if tcase.assertType[i] == "equal" {
assert.Equal(t, out, result)
} else if tcase.assertType[i] == "deep_equal" {
- testifyAssert.True(t, arraysArePermutations(tcase.expected[i].([]interface{}), result.([]interface{})))
+ assert.True(t, arraysArePermutations(tcase.expected[i].([]interface{}), result.([]interface{})))
}
}
})
@@ -739,18 +748,18 @@ func TestJsonARRAPPEND(t *testing.T) {
cmd := tcase.commands[i]
out := tcase.expected[i]
result, err := exec.FireCommandAndReadResponse(conn, cmd)
- testifyAssert.Nil(t, err)
+ assert.Nil(t, err)
jsonResult, isString := result.(string)
if isString && testutils.IsJSONResponse(jsonResult) {
- testifyAssert.JSONEq(t, out.(string), jsonResult)
+ assert.JSONEq(t, out.(string), jsonResult)
continue
}
if tcase.assertType[i] == "equal" {
assert.Equal(t, out, result)
} else if tcase.assertType[i] == "deep_equal" {
- testifyAssert.True(t, arraysArePermutations(tcase.expected[i].([]interface{}), result.([]interface{})))
+ assert.True(t, arraysArePermutations(tcase.expected[i].([]interface{}), result.([]interface{})))
}
}
})
diff --git a/integration_tests/commands/websocket/set_test.go b/integration_tests/commands/websocket/set_test.go
index ec77008dd..94480ebde 100644
--- a/integration_tests/commands/websocket/set_test.go
+++ b/integration_tests/commands/websocket/set_test.go
@@ -223,12 +223,13 @@ func TestSetWithExat(t *testing.T) {
func TestWithKeepTTLFlag(t *testing.T) {
exec := NewWebsocketCommandExecutor()
+ expiryTime := strconv.FormatInt(time.Now().Add(1*time.Minute).UnixMilli(), 10)
conn := exec.ConnectToServer()
for _, tcase := range []TestCase{
{
- commands: []string{"SET k v EX 2", "SET k vv KEEPTTL", "GET k", "SET kk vv", "SET kk vvv KEEPTTL", "GET kk"},
- expected: []interface{}{"OK", "OK", "vv", "OK", "OK", "vvv"},
+ commands: []string{"SET k v EX 2", "SET k vv KEEPTTL", "GET k", "SET kk vv", "SET kk vvv KEEPTTL", "GET kk", "SET K V EX 2 KEEPTTL", "SET K1 vv PX 2000 KEEPTTL", "SET K2 vv EXAT " + expiryTime + " KEEPTTL"},
+ expected: []interface{}{"OK", "OK", "vv", "OK", "OK", "vvv", "ERR syntax error", "ERR syntax error", "ERR syntax error"},
},
} {
for i := 0; i < len(tcase.commands); i++ {
diff --git a/integration_tests/commands/websocket/ttl_pttl_test.go b/integration_tests/commands/websocket/ttl_pttl_test.go
new file mode 100644
index 000000000..a87ef2208
--- /dev/null
+++ b/integration_tests/commands/websocket/ttl_pttl_test.go
@@ -0,0 +1,85 @@
+package websocket
+
+import (
+ "testing"
+ "time"
+
+ "gotest.tools/v3/assert"
+)
+
+func TestTTLPTTL(t *testing.T) {
+ exec := NewWebsocketCommandExecutor()
+ conn := exec.ConnectToServer()
+ defer conn.Close()
+
+ testCases := []struct {
+ name string
+ commands []string
+ expected []interface{}
+ assertType []string
+ delay []time.Duration
+ }{
+ {
+ name: "TTL Simple Value",
+ commands: []string{"SET foo bar", "GETEX foo ex 5", "GETEX foo", "TTL foo"},
+ expected: []interface{}{"OK", "bar", "bar", float64(5)},
+ assertType: []string{"equal", "equal", "equal", "assert"},
+ delay: []time.Duration{0, 0, 0, 0},
+ },
+ {
+ name: "PTTL Simple Value",
+ commands: []string{"SET foo bar", "GETEX foo px 5000", "GETEX foo", "PTTL foo"},
+ expected: []interface{}{"OK", "bar", "bar", float64(5000)},
+ assertType: []string{"equal", "equal", "equal", "assert"},
+ delay: []time.Duration{0, 0, 0, 0},
+ },
+ {
+ name: "TTL & PTTL Non-Existent Key",
+ commands: []string{"TTL foo", "PTTL foo"},
+ expected: []interface{}{float64(-2), float64(-2)},
+ assertType: []string{"equal", "equal"},
+ delay: []time.Duration{0, 0},
+ },
+ {
+ name: "TTL & PTTL without Expiry",
+ commands: []string{"SET foo bar", "GET foo", "TTL foo", "PTTL foo"},
+ expected: []interface{}{"OK", "bar", float64(-1), float64(-1)},
+ assertType: []string{"equal", "equal", "equal", "equal"},
+ delay: []time.Duration{0, 0, 0, 0},
+ },
+ {
+ name: "TTL & PTTL with Persist",
+ commands: []string{"SET foo bar", "GETEX foo persist", "TTL foo", "PTTL foo"},
+ expected: []interface{}{"OK", "bar", float64(-1), float64(-1)},
+ assertType: []string{"equal", "equal", "equal", "equal"},
+ delay: []time.Duration{0, 0, 0, 0},
+ },
+ {
+ name: "TTL & PTTL with Expire and Expired Key",
+ commands: []string{"SET foo bar", "GETEX foo ex 5", "GET foo", "TTL foo", "PTTL foo", "TTL foo", "PTTL foo"},
+ expected: []interface{}{"OK", "bar", "bar", float64(5), float64(5000), float64(-2), float64(-2)},
+ assertType: []string{"equal", "equal", "equal", "assert", "assert", "equal", "equal"},
+ delay: []time.Duration{0, 0, 0, 0, 0, 5 * time.Second, 0},
+ },
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.name, func(t *testing.T) {
+ DeleteKey(t, conn, exec, "foo")
+ for i, cmd := range tc.commands {
+ if tc.delay[i] > 0 {
+ time.Sleep(tc.delay[i])
+ }
+ result, err := exec.FireCommandAndReadResponse(conn, cmd)
+ if err != nil {
+ t.Fatalf("Error executing command: %v", err)
+ }
+ if tc.assertType[i] == "equal" {
+ assert.DeepEqual(t, tc.expected[i], result)
+ } else if tc.assertType[i] == "assert" {
+ assert.Assert(t, result.(float64) <= tc.expected[i].(float64), "Expected %v to be less than or equal to %v", result, tc.expected[i])
+ }
+ }
+ })
+ }
+}
diff --git a/integration_tests/commands/websocket/writeretry_test.go b/integration_tests/commands/websocket/writeretry_test.go
index b0b270fc8..00aba1e1e 100644
--- a/integration_tests/commands/websocket/writeretry_test.go
+++ b/integration_tests/commands/websocket/writeretry_test.go
@@ -94,11 +94,20 @@ func startWebSocketServer() {
// Helper to create a WebSocket connection for testing
func createWebSocketConn(t *testing.T) *websocket.Conn {
once.Do(startWebSocketServer)
+ var conn *websocket.Conn
+ var err error
u := url.URL{Scheme: "ws", Host: serverAddr, Path: "/ws"}
- conn, _, err := websocket.DefaultDialer.Dial(u.String(), nil)
- if err != nil {
- t.Fatalf("Failed to connect to WebSocket server: %v", err)
+
+ // Retry up to 5 times with a short delay
+ for i := 0; i < 5; i++ {
+ conn, _, err = websocket.DefaultDialer.Dial(u.String(), nil)
+ if err == nil {
+ return conn
+ }
+ time.Sleep(200 * time.Millisecond) // Adjust delay as necessary
}
- return conn
+
+ t.Fatalf("Failed to connect to WebSocket server: %v", err)
+ return nil
}
diff --git a/integration_tests/commands/websocket/zcard_test.go b/integration_tests/commands/websocket/zcard_test.go
index bd28f952a..34561134b 100644
--- a/integration_tests/commands/websocket/zcard_test.go
+++ b/integration_tests/commands/websocket/zcard_test.go
@@ -4,7 +4,7 @@ import (
"testing"
"time"
- testifyAssert "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/assert"
)
func TestZCARD(t *testing.T) {
@@ -63,8 +63,8 @@ func TestZCARD(t *testing.T) {
time.Sleep(tc.delays[i])
}
result, err := exec.FireCommandAndReadResponse(conn, cmd)
- testifyAssert.Nil(t, err)
- testifyAssert.Equal(t, tc.expect[i], result, "Value mismatch for cmd %s", cmd)
+ assert.Nil(t, err)
+ assert.Equal(t, tc.expect[i], result, "Value mismatch for cmd %s", cmd)
}
})
}
diff --git a/integration_tests/commands/websocket/zrem_test.go b/integration_tests/commands/websocket/zrem_test.go
index 0b78f6ed8..ef8f9e3ae 100644
--- a/integration_tests/commands/websocket/zrem_test.go
+++ b/integration_tests/commands/websocket/zrem_test.go
@@ -4,7 +4,7 @@ import (
"testing"
"time"
- testifyAssert "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/assert"
)
func TestZREM(t *testing.T) {
@@ -70,8 +70,8 @@ func TestZREM(t *testing.T) {
time.Sleep(tc.delays[i])
}
result, err := exec.FireCommandAndReadResponse(conn, cmd)
- testifyAssert.Nil(t, err)
- testifyAssert.Equal(t, tc.expect[i], result, "Value mismatch for cmd %s", cmd)
+ assert.Nil(t, err)
+ assert.Equal(t, tc.expect[i], result, "Value mismatch for cmd %s", cmd)
}
})
}
diff --git a/integration_tests/server/max_conn_test.go b/integration_tests/server/max_conn_test.go
index d8d667ccc..3c18a0a3f 100644
--- a/integration_tests/server/max_conn_test.go
+++ b/integration_tests/server/max_conn_test.go
@@ -9,7 +9,7 @@ import (
"testing"
"time"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
commands "github.com/dicedb/dice/integration_tests/commands/async"
)
diff --git a/internal/clientio/io_test.go b/internal/clientio/io_test.go
index 3d4db2e13..f99e641ba 100644
--- a/internal/clientio/io_test.go
+++ b/internal/clientio/io_test.go
@@ -9,7 +9,7 @@ import (
"github.com/dicedb/dice/config"
"github.com/dicedb/dice/internal/server/utils"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
// MockReadWriter to simulate different io behaviors
diff --git a/internal/clientio/resp_test.go b/internal/clientio/resp_test.go
index 2a58f5b3e..97cbacd3e 100644
--- a/internal/clientio/resp_test.go
+++ b/internal/clientio/resp_test.go
@@ -8,7 +8,7 @@ import (
"github.com/dicedb/dice/internal/clientio"
"github.com/dicedb/dice/internal/server/utils"
- testifyAssert "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/assert"
)
func TestSimpleStringDecode(t *testing.T) {
@@ -194,7 +194,7 @@ func TestBoolean(t *testing.T) {
for _, v := range tests {
ev := clientio.Encode(v.input, false)
- testifyAssert.Equal(t, ev, v.output)
+ assert.Equal(t, ev, v.output)
}
}
@@ -215,6 +215,6 @@ func TestInteger(t *testing.T) {
for _, v := range tests {
ev := clientio.Encode(v.input, false)
- testifyAssert.Equal(t, ev, v.output)
+ assert.Equal(t, ev, v.output)
}
}
diff --git a/internal/cmd/cmds.go b/internal/cmd/cmds.go
index 82270029e..68143983a 100644
--- a/internal/cmd/cmds.go
+++ b/internal/cmd/cmds.go
@@ -28,5 +28,9 @@ func (cmd *DiceDBCmd) GetFingerprint() uint32 {
// This is not true for all commands, however, for now this is only used by the watch manager,
// which as of now only supports a small subset of commands (all of which fit this implementation).
func (cmd *DiceDBCmd) GetKey() string {
- return cmd.Args[0]
+ var c string
+ if len(cmd.Args) > 0 {
+ c = cmd.Args[0]
+ }
+ return c
}
diff --git a/internal/dencoding/dencoding_benchmark_test.go b/internal/dencoding/dencoding_benchmark_test.go
index 686a1da4d..66787c403 100644
--- a/internal/dencoding/dencoding_benchmark_test.go
+++ b/internal/dencoding/dencoding_benchmark_test.go
@@ -5,7 +5,7 @@ import (
"testing"
"github.com/dicedb/dice/internal/dencoding"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func BenchmarkEncodeDecodeInt(b *testing.B) {
diff --git a/internal/dencoding/int_test.go b/internal/dencoding/int_test.go
index a9bc93bf2..9d2663044 100644
--- a/internal/dencoding/int_test.go
+++ b/internal/dencoding/int_test.go
@@ -7,7 +7,7 @@ import (
"github.com/dicedb/dice/internal/dencoding"
"github.com/dicedb/dice/testutils"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func TestDencodingUInt(t *testing.T) {
@@ -51,7 +51,7 @@ func TestDencodingUInt(t *testing.T) {
for value, expected := range testCases {
t.Run(fmt.Sprintf("value_%d", value), func(t *testing.T) {
encoded := dencoding.EncodeUInt(uint64(value))
- assert.Assert(t, testutils.EqualByteSlice(encoded, expected), "Unexpected encoding")
+ assert.True(t, testutils.EqualByteSlice(encoded, expected), "Unexpected encoding")
})
}
})
@@ -102,7 +102,7 @@ func TestDencodingInt(t *testing.T) {
for value, expected := range testCases {
t.Run(fmt.Sprintf("value_%d", value), func(t *testing.T) {
encoded := dencoding.EncodeInt(int64(value))
- assert.Assert(t, testutils.EqualByteSlice(encoded, expected), "Unexpected encoding")
+ assert.True(t, testutils.EqualByteSlice(encoded, expected), "Unexpected encoding")
})
}
})
diff --git a/internal/errors/migrated_errors.go b/internal/errors/migrated_errors.go
index d9a8dc864..60630f252 100644
--- a/internal/errors/migrated_errors.go
+++ b/internal/errors/migrated_errors.go
@@ -31,6 +31,7 @@ var (
ErrAborted = errors.New("server received ABORT command")
ErrEmptyCommand = errors.New("empty command")
ErrInvalidIPAddress = errors.New("invalid IP address")
+ ErrInvalidFingerprint = errors.New("invalid fingerprint")
// Error generation functions for specific error messages with dynamic parameters.
ErrWrongArgumentCount = func(command string) error {
diff --git a/internal/eval/bloom_test.go b/internal/eval/bloom_test.go
index 32452ec7d..8e027eaa4 100644
--- a/internal/eval/bloom_test.go
+++ b/internal/eval/bloom_test.go
@@ -9,7 +9,7 @@ import (
"github.com/dicedb/dice/internal/clientio"
dstore "github.com/dicedb/dice/internal/store"
- assert "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/assert"
)
func TestBloomFilter(t *testing.T) {
diff --git a/internal/eval/bytearray_test.go b/internal/eval/bytearray_test.go
index 86b945a90..efaa10f06 100644
--- a/internal/eval/bytearray_test.go
+++ b/internal/eval/bytearray_test.go
@@ -3,7 +3,7 @@ package eval
import (
"testing"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func TestMixedOperations(t *testing.T) {
@@ -143,9 +143,9 @@ func TestDeepCopy(t *testing.T) {
// Modify the deepCopy's data to ensure it's independent of the original
deepCopy.data[0] = 9
- assert.Assert(t, deepCopy.data[0] != original.data[0], "ByteArray DeepCopy did not create an independent deepCopy, original and deepCopy data are linked")
+ assert.True(t, deepCopy.data[0] != original.data[0], "ByteArray DeepCopy did not create an independent deepCopy, original and deepCopy data are linked")
// Modify the original's data to ensure it doesn't affect the deepCopy
original.data[1] = 8
- assert.Assert(t, deepCopy.data[1] != original.data[1], "ByteArray DeepCopy did not create an independent deepCopy, original and deepCopy data are linked")
+ assert.True(t, deepCopy.data[1] != original.data[1], "ByteArray DeepCopy did not create an independent deepCopy, original and deepCopy data are linked")
}
diff --git a/internal/eval/bytelist_test.go b/internal/eval/bytelist_test.go
index 822974eb9..2d22d2ebd 100644
--- a/internal/eval/bytelist_test.go
+++ b/internal/eval/bytelist_test.go
@@ -2,7 +2,7 @@ package eval
import (
"bytes"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
"testing"
)
@@ -95,9 +95,9 @@ func TestByteListDeepCopy(t *testing.T) {
// Verify that changes to the deepCopy do not affect the original
deepCopy.head.buf[0] = 9
- assert.Assert(t, original.head.buf[0] != deepCopy.head.buf[0], "Original and deepCopy head buffer should not be linked")
+ assert.True(t, original.head.buf[0] != deepCopy.head.buf[0], "Original and deepCopy head buffer should not be linked")
// Verify that changes to the original do not affect the deepCopy
original.head.buf[1] = 8
- assert.Assert(t, original.head.buf[1] != deepCopy.head.buf[1], "Original and deepCopy head buffer should not be linked")
+ assert.True(t, original.head.buf[1] != deepCopy.head.buf[1], "Original and deepCopy head buffer should not be linked")
}
diff --git a/internal/eval/commands.go b/internal/eval/commands.go
index b3df45911..fa25e00d8 100644
--- a/internal/eval/commands.go
+++ b/internal/eval/commands.go
@@ -103,9 +103,10 @@ var (
The key should be the only param in args And If the key exists, it will be deleted before its value is returned.
The RESP value of the key is encoded and then returned
GETDEL returns RespNIL if key is expired or it does not exist`,
- Eval: evalGETDEL,
- Arity: 2,
- KeySpecs: KeySpecs{BeginIndex: 1},
+ Arity: 2,
+ KeySpecs: KeySpecs{BeginIndex: 1},
+ IsMigrated: true,
+ NewEval: evalGETDEL,
}
msetCmdMeta = DiceCmdMeta{
Name: "MSET",
@@ -326,9 +327,10 @@ var (
RESP encoded time (in secs) remaining for the key to expire
RESP encoded -2 stating key doesn't exist or key is expired
RESP encoded -1 in case no expiration is set on the key`,
- Eval: evalTTL,
- Arity: 2,
- KeySpecs: KeySpecs{BeginIndex: 1},
+ NewEval: evalTTL,
+ IsMigrated: true,
+ Arity: 2,
+ KeySpecs: KeySpecs{BeginIndex: 1},
}
delCmdMeta = DiceCmdMeta{
Name: "DEL",
@@ -345,9 +347,31 @@ var (
The expiry time should be in integer format; if not, it returns encoded error response
Returns RespOne if expiry was set on the key successfully.
Once the time is lapsed, the key will be deleted automatically`,
- Eval: evalEXPIRE,
- Arity: -3,
- KeySpecs: KeySpecs{BeginIndex: 1, Step: 1},
+ NewEval: evalEXPIRE,
+ IsMigrated: true,
+ Arity: -3,
+ KeySpecs: KeySpecs{BeginIndex: 1, Step: 1},
+ }
+ expiretimeCmdMeta = DiceCmdMeta{
+ Name: "EXPIRETIME",
+ Info: `EXPIRETIME returns the absolute Unix timestamp (since January 1, 1970) in seconds
+ at which the given key will expire`,
+ NewEval: evalEXPIRETIME,
+ IsMigrated: true,
+ Arity: -2,
+ KeySpecs: KeySpecs{BeginIndex: 1, Step: 1},
+ }
+ expireatCmdMeta = DiceCmdMeta{
+ Name: "EXPIREAT",
+ Info: `EXPIREAT sets a expiry time(in unix-time-seconds) on the specified key in args
+ args should contain 2 values, key and the expiry time to be set for the key
+ The expiry time should be in integer format; if not, it returns encoded error response
+ Returns RespOne if expiry was set on the key successfully.
+ Once the time is lapsed, the key will be deleted automatically`,
+ NewEval: evalEXPIREAT,
+ IsMigrated: true,
+ Arity: -3,
+ KeySpecs: KeySpecs{BeginIndex: 1, Step: 1},
}
helloCmdMeta = DiceCmdMeta{
Name: "HELLO",
@@ -655,9 +679,10 @@ var (
Name: "GETEX",
Info: `Get the value of key and optionally set its expiration.
GETEX is similar to GET, but is a write command with additional options.`,
- Eval: evalGETEX,
- Arity: -2,
- KeySpecs: KeySpecs{BeginIndex: 1},
+ Arity: -2,
+ KeySpecs: KeySpecs{BeginIndex: 1},
+ IsMigrated: true,
+ NewEval: evalGETEX,
}
pttlCmdMeta = DiceCmdMeta{
Name: "PTTL",
@@ -667,9 +692,10 @@ var (
RESP encoded time (in secs) remaining for the key to expire
RESP encoded -2 stating key doesn't exist or key is expired
RESP encoded -1 in case no expiration is set on the key`,
- Eval: evalPTTL,
- Arity: 2,
- KeySpecs: KeySpecs{BeginIndex: 1},
+ NewEval: evalPTTL,
+ IsMigrated: true,
+ Arity: 2,
+ KeySpecs: KeySpecs{BeginIndex: 1},
}
hsetCmdMeta = DiceCmdMeta{
Name: "HSET",
@@ -679,9 +705,10 @@ var (
Returns
This command returns the number of keys that are stored at given key.
`,
- Eval: evalHSET,
- Arity: -4,
- KeySpecs: KeySpecs{BeginIndex: 1},
+ NewEval: evalHSET,
+ Arity: -4,
+ KeySpecs: KeySpecs{BeginIndex: 1},
+ IsMigrated: true,
}
hmsetCmdMeta = DiceCmdMeta{
Name: "HMSET",
@@ -691,9 +718,10 @@ var (
Returns
This command returns the number of keys that are stored at given key.
`,
- Eval: evalHMSET,
- Arity: -4,
- KeySpecs: KeySpecs{BeginIndex: 1},
+ NewEval: evalHMSET,
+ Arity: -4,
+ KeySpecs: KeySpecs{BeginIndex: 1},
+ IsMigrated: true,
}
hkeysCmdMeta = DiceCmdMeta{
Name: "HKEYS",
@@ -708,23 +736,26 @@ var (
Info: `Sets field in the hash stored at key to value, only if field does not yet exist.
If key does not exist, a new key holding a hash is created. If field already exists,
this operation has no effect.`,
- Eval: evalHSETNX,
- Arity: 4,
- KeySpecs: KeySpecs{BeginIndex: 1},
+ NewEval: evalHSETNX,
+ Arity: 4,
+ KeySpecs: KeySpecs{BeginIndex: 1},
+ IsMigrated: true,
}
hgetCmdMeta = DiceCmdMeta{
- Name: "HGET",
- Info: `Returns the value associated with field in the hash stored at key.`,
- Eval: evalHGET,
- Arity: -3,
- KeySpecs: KeySpecs{BeginIndex: 1},
+ Name: "HGET",
+ Info: `Returns the value associated with field in the hash stored at key.`,
+ NewEval: evalHGET,
+ Arity: -3,
+ KeySpecs: KeySpecs{BeginIndex: 1},
+ IsMigrated: true,
}
hmgetCmdMeta = DiceCmdMeta{
- Name: "HMGET",
- Info: `Returns the values associated with the specified fields in the hash stored at key.`,
- Eval: evalHMGET,
- Arity: -2,
- KeySpecs: KeySpecs{BeginIndex: 1},
+ Name: "HMGET",
+ Info: `Returns the values associated with the specified fields in the hash stored at key.`,
+ NewEval: evalHMGET,
+ Arity: -2,
+ KeySpecs: KeySpecs{BeginIndex: 1},
+ IsMigrated: true,
}
hgetAllCmdMeta = DiceCmdMeta{
Name: "HGETALL",
@@ -769,9 +800,10 @@ var (
If key does not exist, it is treated as an empty hash and this command returns 0.
Returns
The number of fields that were removed from the hash, not including specified but non-existing fields.`,
- Eval: evalHDEL,
- Arity: -3,
- KeySpecs: KeySpecs{BeginIndex: 1},
+ NewEval: evalHDEL,
+ Arity: -3,
+ KeySpecs: KeySpecs{BeginIndex: 1},
+ IsMigrated: true,
}
hscanCmdMeta = DiceCmdMeta{
Name: "HSCAN",
@@ -810,25 +842,6 @@ var (
Arity: -2,
KeySpecs: KeySpecs{BeginIndex: 1},
}
- expiretimeCmdMeta = DiceCmdMeta{
- Name: "EXPIRETIME",
- Info: `EXPIRETIME returns the absolute Unix timestamp (since January 1, 1970) in seconds
- at which the given key will expire`,
- Eval: evalEXPIRETIME,
- Arity: -2,
- KeySpecs: KeySpecs{BeginIndex: 1, Step: 1},
- }
- expireatCmdMeta = DiceCmdMeta{
- Name: "EXPIREAT",
- Info: `EXPIREAT sets a expiry time(in unix-time-seconds) on the specified key in args
- args should contain 2 values, key and the expiry time to be set for the key
- The expiry time should be in integer format; if not, it returns encoded error response
- Returns RespOne if expiry was set on the key successfully.
- Once the time is lapsed, the key will be deleted automatically`,
- Eval: evalEXPIREAT,
- Arity: -3,
- KeySpecs: KeySpecs{BeginIndex: 1, Step: 1},
- }
lpushCmdMeta = DiceCmdMeta{
Name: "LPUSH",
Info: "LPUSH pushes values into the left side of the deque",
diff --git a/internal/eval/constants.go b/internal/eval/constants.go
index d75a6e5c0..2d7a7cecc 100644
--- a/internal/eval/constants.go
+++ b/internal/eval/constants.go
@@ -11,6 +11,7 @@ const (
Ex string = "EX"
Px string = "PX"
+ Persist string = "PERSIST"
Pxat string = "PXAT"
Exat string = "EXAT"
XX string = "XX"
diff --git a/internal/eval/deque_test.go b/internal/eval/deque_test.go
index da2422908..b21ee3243 100644
--- a/internal/eval/deque_test.go
+++ b/internal/eval/deque_test.go
@@ -8,7 +8,7 @@ import (
"testing"
"time"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
var deqRandGenerator *rand.Rand
diff --git a/internal/eval/eval.go b/internal/eval/eval.go
index 56c51107d..1108e7c80 100644
--- a/internal/eval/eval.go
+++ b/internal/eval/eval.go
@@ -53,7 +53,16 @@ type EvalResponse struct {
Error error // Error holds any error that occurred during the operation. If no error, it will be nil.
}
-//go:inline
+// Following functions should be used to create a new EvalResponse with the given result and error.
+// These ensure that result and error are mutually exclusive.
+// If result is nil, then error should be non-nil and vice versa.
+
+// makeEvalResult creates a new EvalResponse with the given result and nil error.
+// This is a helper function to create a new EvalResponse with the given result and nil error.
+/**
+ * @param {interface{}} result - The result of the store operation.
+ * @returns {EvalResponse} A new EvalResponse with the given result and nil error.
+ */
func makeEvalResult(result interface{}) *EvalResponse {
return &EvalResponse{
Result: result,
@@ -61,7 +70,12 @@ func makeEvalResult(result interface{}) *EvalResponse {
}
}
-//go:inline
+// makeEvalError creates a new EvalResponse with the given error and nil result.
+// This is a helper function to create a new EvalResponse with the given error and nil result.
+/**
+ * @param {error} err - The error that occurred during the store operation.
+ * @returns {EvalResponse} A new EvalResponse with the given error and nil result.
+ */
func makeEvalError(err error) *EvalResponse {
return &EvalResponse{
Result: nil,
@@ -190,67 +204,6 @@ func evalDBSIZE(args []string, store *dstore.Store) []byte {
return clientio.Encode(store.GetDBSize(), false)
}
-// evalGETDEL returns the value for the queried key in args
-// The key should be the only param in args
-// The RESP value of the key is encoded and then returned
-// In evalGETDEL If the key exists, it will be deleted before its value is returned.
-// evalGETDEL returns response.RespNIL if key is expired or it does not exist
-func evalGETDEL(args []string, store *dstore.Store) []byte {
- if len(args) != 1 {
- return diceerrors.NewErrArity("GETDEL")
- }
-
- key := args[0]
-
- // getting the key based on previous touch value
- obj := store.GetNoTouch(key)
-
- // if key does not exist, return RESP encoded nil
- if obj == nil {
- return clientio.RespNIL
- }
-
- // If the object exists, check if it is a Set object.
- if err := object.AssertType(obj.TypeEncoding, object.ObjTypeSet); err == nil {
- return diceerrors.NewErrWithFormattedMessage(diceerrors.WrongTypeErr)
- }
-
- // If the object exists, check if it is a JSON object.
- if err := object.AssertType(obj.TypeEncoding, object.ObjTypeJSON); err == nil {
- return diceerrors.NewErrWithFormattedMessage(diceerrors.WrongTypeErr)
- }
-
- // Get the key from the hash table
- objVal := store.GetDel(key)
-
- // Decode and return the value based on its encoding
- switch _, oEnc := object.ExtractTypeEncoding(objVal); oEnc {
- case object.ObjEncodingInt:
- // Value is stored as an int64, so use type assertion
- if val, ok := objVal.Value.(int64); ok {
- return clientio.Encode(val, false)
- }
- return diceerrors.NewErrWithFormattedMessage("expected int64 but got another type: %s", objVal.Value)
-
- case object.ObjEncodingEmbStr, object.ObjEncodingRaw:
- // Value is stored as a string, use type assertion
- if val, ok := objVal.Value.(string); ok {
- return clientio.Encode(val, false)
- }
- return diceerrors.NewErrWithMessage("expected string but got another type")
-
- case object.ObjEncodingByteArray:
- // Value is stored as a bytearray, use type assertion
- if val, ok := objVal.Value.(*ByteArray); ok {
- return clientio.Encode(string(val.data), false)
- }
- return diceerrors.NewErrWithMessage(diceerrors.WrongTypeErr)
-
- default:
- return diceerrors.NewErrWithMessage(diceerrors.WrongTypeErr)
- }
-}
-
// evalJSONDEBUG reports value's memory usage in bytes
// Returns arity error if subcommand is missing
// Supports only two subcommand as of now - HELP and MEMORY
@@ -1058,39 +1011,6 @@ func evalJSONINGEST(args []string, store *dstore.Store) []byte {
return result
}
-// evalTTL returns Time-to-Live in secs for the queried key in args
-// The key should be the only param in args else returns with an error
-// Returns RESP encoded time (in secs) remaining for the key to expire
-//
-// RESP encoded -2 stating key doesn't exist or key is expired
-// RESP encoded -1 in case no expiration is set on the key
-func evalTTL(args []string, store *dstore.Store) []byte {
- if len(args) != 1 {
- return diceerrors.NewErrArity("TTL")
- }
-
- key := args[0]
-
- obj := store.Get(key)
-
- // if key does not exist, return RESP encoded -2 denoting key does not exist
- if obj == nil {
- return clientio.RespMinusTwo
- }
-
- // if object exist, but no expiration is set on it then send -1
- exp, isExpirySet := dstore.GetExpiry(obj, store)
- if !isExpirySet {
- return clientio.RespMinusOne
- }
-
- // compute the time remaining for the key to expire and
- // return the RESP encoded form of it
- durationMs := exp - uint64(utils.GetCurrentTime().UnixMilli())
-
- return clientio.Encode(int64(durationMs/1000), false)
-}
-
// evalDEL deletes all the specified keys in args list
// returns the count of total deleted keys after encoding
func evalDEL(args []string, store *dstore.Store) []byte {
@@ -1109,174 +1029,6 @@ func evalDEL(args []string, store *dstore.Store) []byte {
return clientio.Encode(countDeleted, false)
}
-// evalEXPIRE sets an expiry time(in secs) on the specified key in args
-// args should contain 2 values, key and the expiry time to be set for the key
-// The expiry time should be in integer format; if not, it returns encoded error response
-// Returns response.RespOne if expiry was set on the key successfully.
-// Once the time is lapsed, the key will be deleted automatically
-func evalEXPIRE(args []string, store *dstore.Store) []byte {
- if len(args) <= 1 {
- return diceerrors.NewErrArity("EXPIRE")
- }
-
- key := args[0]
- exDurationSec, err := strconv.ParseInt(args[1], 10, 64)
- if err != nil {
- return diceerrors.NewErrWithMessage(diceerrors.IntOrOutOfRangeErr)
- }
-
- if exDurationSec < 0 || exDurationSec > maxExDuration {
- return diceerrors.NewErrExpireTime("EXPIRE")
- }
-
- obj := store.Get(key)
-
- // 0 if the timeout was not set. e.g. key doesn't exist, or operation skipped due to the provided arguments
- if obj == nil {
- return clientio.RespZero
- }
- isExpirySet, err2 := evaluateAndSetExpiry(args[2:], utils.AddSecondsToUnixEpoch(exDurationSec), key, store)
-
- if isExpirySet {
- return clientio.RespOne
- } else if err2 != nil {
- return err2
- }
- return clientio.RespZero
-}
-
-// evalEXPIRETIME returns the absolute Unix timestamp (since January 1, 1970) in seconds at which the given key will expire
-// args should contain only 1 value, the key
-// Returns expiration Unix timestamp in seconds.
-// Returns -1 if the key exists but has no associated expiration time.
-// Returns -2 if the key does not exist.
-func evalEXPIRETIME(args []string, store *dstore.Store) []byte {
- if len(args) != 1 {
- return diceerrors.NewErrArity("EXPIRETIME")
- }
-
- key := args[0]
-
- obj := store.Get(key)
-
- // -2 if key doesn't exist
- if obj == nil {
- return clientio.RespMinusTwo
- }
-
- exTimeMili, ok := dstore.GetExpiry(obj, store)
- // -1 if key doesn't have expiration time set
- if !ok {
- return clientio.RespMinusOne
- }
-
- return clientio.Encode(int(exTimeMili/1000), false)
-}
-
-// evalEXPIREAT sets a expiry time(in unix-time-seconds) on the specified key in args
-// args should contain 2 values, key and the expiry time to be set for the key
-// The expiry time should be in integer format; if not, it returns encoded error response
-// Returns response.RespOne if expiry was set on the key successfully.
-// Once the time is lapsed, the key will be deleted automatically
-func evalEXPIREAT(args []string, store *dstore.Store) []byte {
- if len(args) <= 1 {
- return clientio.Encode(errors.New("ERR wrong number of arguments for 'expireat' command"), false)
- }
-
- key := args[0]
- exUnixTimeSec, err := strconv.ParseInt(args[1], 10, 64)
- if exUnixTimeSec < 0 || exUnixTimeSec > maxExDuration {
- return diceerrors.NewErrExpireTime("EXPIREAT")
- }
-
- if err != nil {
- return clientio.Encode(errors.New(diceerrors.InvalidIntErr), false)
- }
-
- isExpirySet, err2 := evaluateAndSetExpiry(args[2:], exUnixTimeSec, key, store)
- if isExpirySet {
- return clientio.RespOne
- } else if err2 != nil {
- return err2
- }
- return clientio.RespZero
-}
-
-// NX: Set the expiration only if the key does not already have an expiration time.
-// XX: Set the expiration only if the key already has an expiration time.
-// GT: Set the expiration only if the new expiration time is greater than the current one.
-// LT: Set the expiration only if the new expiration time is less than the current one.
-// Returns Boolean True and error nil if expiry was set on the key successfully.
-// Returns Boolean False and error nil if conditions didn't met.
-// Returns Boolean False and error not-nil if invalid combination of subCommands or if subCommand is invalid
-func evaluateAndSetExpiry(subCommands []string, newExpiry int64, key string,
- store *dstore.Store,
-) (shouldSetExpiry bool, err []byte) {
- newExpInMilli := newExpiry * 1000
- var prevExpiry *uint64 = nil
- var nxCmd, xxCmd, gtCmd, ltCmd bool
-
- obj := store.Get(key)
- // key doesn't exist
- if obj == nil {
- return false, nil
- }
- shouldSetExpiry = true
- // if no condition exists
- if len(subCommands) == 0 {
- store.SetUnixTimeExpiry(obj, newExpiry)
- return shouldSetExpiry, nil
- }
-
- expireTime, ok := dstore.GetExpiry(obj, store)
- if ok {
- prevExpiry = &expireTime
- }
-
- for i := range subCommands {
- subCommand := strings.ToUpper(subCommands[i])
-
- switch subCommand {
- case NX:
- nxCmd = true
- if prevExpiry != nil {
- shouldSetExpiry = false
- }
- case XX:
- xxCmd = true
- if prevExpiry == nil {
- shouldSetExpiry = false
- }
- case GT:
- gtCmd = true
- if prevExpiry == nil || *prevExpiry > uint64(newExpInMilli) {
- shouldSetExpiry = false
- }
- case LT:
- ltCmd = true
- if prevExpiry != nil && *prevExpiry < uint64(newExpInMilli) {
- shouldSetExpiry = false
- }
- default:
- return false, diceerrors.NewErrWithMessage("Unsupported option " + subCommands[i])
- }
- }
-
- if !nxCmd && gtCmd && ltCmd {
- return false, diceerrors.NewErrWithMessage("GT and LT options at the same time are not compatible")
- }
-
- if nxCmd && (xxCmd || gtCmd || ltCmd) {
- return false, diceerrors.NewErrWithMessage("NX and XX," +
- " GT or LT options at the same time are not compatible")
- }
-
- if shouldSetExpiry {
- store.SetUnixTimeExpiry(obj, newExpiry)
- }
- return shouldSetExpiry, nil
-}
-
func evalHELLO(args []string, store *dstore.Store) []byte {
if len(args) > 1 {
return diceerrors.NewErrArity("HELLO")
@@ -2176,243 +1928,6 @@ func evalCOPY(args []string, store *dstore.Store) []byte {
return clientio.RespOne
}
-// GETEX key [EX seconds | PX milliseconds | EXAT unix-time-seconds |
-// PXAT unix-time-milliseconds | PERSIST]
-// Get the value of key and optionally set its expiration.
-// GETEX is similar to GET, but is a write command with additional options.
-// The GETEX command supports a set of options that modify its behavior:
-// EX seconds -- Set the specified expire time, in seconds.
-// PX milliseconds -- Set the specified expire time, in milliseconds.
-// EXAT timestamp-seconds -- Set the specified Unix time at which the key will expire, in seconds.
-// PXAT timestamp-milliseconds -- Set the specified Unix time at which the key will expire, in milliseconds.
-// PERSIST -- Remove the time to live associated with the key.
-// The RESP value of the key is encoded and then returned
-// evalGET returns response.RespNIL if key is expired or it does not exist
-func evalGETEX(args []string, store *dstore.Store) []byte {
- if len(args) < 1 {
- return diceerrors.NewErrArity("GETEX")
- }
-
- key := args[0]
-
- // Get the key from the hash table
- obj := store.Get(key)
-
- // if key does not exist, return RESP encoded nil
- if obj == nil {
- return clientio.RespNIL
- }
-
- // check if the object is set type or json type if yes then return error
- if object.AssertType(obj.TypeEncoding, object.ObjTypeSet) == nil ||
- object.AssertType(obj.TypeEncoding, object.ObjTypeJSON) == nil {
- return diceerrors.NewErrWithFormattedMessage(diceerrors.WrongTypeErr)
- }
-
- var exDurationMs int64 = -1
- state := Uninitialized
- persist := false
- for i := 1; i < len(args); i++ {
- arg := strings.ToUpper(args[i])
- switch arg {
- case Ex, Px:
- if state != Uninitialized {
- return diceerrors.NewErrWithMessage(diceerrors.SyntaxErr)
- }
- i++
- if i == len(args) {
- return diceerrors.NewErrWithMessage(diceerrors.SyntaxErr)
- }
-
- exDuration, err := strconv.ParseInt(args[i], 10, 64)
- if err != nil {
- return diceerrors.NewErrWithMessage(diceerrors.IntOrOutOfRangeErr)
- }
- if exDuration <= 0 || exDuration > maxExDuration {
- return diceerrors.NewErrExpireTime("GETEX")
- }
-
- // converting seconds to milliseconds
- if arg == Ex {
- exDuration *= 1000
- }
- exDurationMs = exDuration
- state = Initialized
-
- case Pxat, Exat:
- if state != Uninitialized {
- return diceerrors.NewErrWithMessage(diceerrors.SyntaxErr)
- }
- i++
- if i == len(args) {
- return diceerrors.NewErrWithMessage(diceerrors.SyntaxErr)
- }
- exDuration, err := strconv.ParseInt(args[i], 10, 64)
- if err != nil {
- return diceerrors.NewErrWithMessage(diceerrors.IntOrOutOfRangeErr)
- }
-
- if exDuration < 0 || exDuration > maxExDuration {
- return diceerrors.NewErrExpireTime("GETEX")
- }
-
- if arg == Exat {
- exDuration *= 1000
- }
- exDurationMs = exDuration - utils.GetCurrentTime().UnixMilli()
- // If the expiry time is in the past, set exDurationMs to 0
- // This will be used to signal immediate expiration
- if exDurationMs < 0 {
- exDurationMs = 0
- }
- state = Initialized
-
- case "PERSIST":
- if state != Uninitialized {
- return diceerrors.NewErrWithMessage(diceerrors.SyntaxErr)
- }
- persist = true
- state = Initialized
- default:
- return diceerrors.NewErrWithMessage(diceerrors.SyntaxErr)
- }
- }
-
- if state == Initialized {
- if persist {
- dstore.DelExpiry(obj, store)
- } else {
- store.SetExpiry(obj, exDurationMs)
- }
- }
-
- // return the RESP encoded value
- return clientio.Encode(obj.Value, false)
-}
-
-// evalPTTL returns Time-to-Live in millisecs for the queried key in args
-// The key should be the only param in args else returns with an error
-// Returns RESP encoded time (in secs) remaining for the key to expire
-//
-// RESP encoded -2 stating key doesn't exist or key is expired
-// RESP encoded -1 in case no expiration is set on the key
-func evalPTTL(args []string, store *dstore.Store) []byte {
- if len(args) != 1 {
- return diceerrors.NewErrArity("PTTL")
- }
-
- key := args[0]
-
- obj := store.Get(key)
-
- if obj == nil {
- return clientio.RespMinusTwo
- }
-
- exp, isExpirySet := dstore.GetExpiry(obj, store)
-
- if !isExpirySet {
- return clientio.RespMinusOne
- }
-
- // compute the time remaining for the key to expire and
- // return the RESP encoded form of it
- durationMs := exp - uint64(utils.GetCurrentTime().UnixMilli())
- return clientio.Encode(int64(durationMs), false)
-}
-
-// evalHSET sets the specified fields to their
-// respective values in a hashmap stored at key
-//
-// This command overwrites the values of specified
-// fields that exist in the hash.
-//
-// If key doesn't exist, a new key holding a hash is created.
-//
-// Usage: HSET key field value [field value ...]
-func evalHSET(args []string, store *dstore.Store) []byte {
- if len(args) < 3 {
- return diceerrors.NewErrArity("HSET")
- }
-
- numKeys, err := insertInHashMap(args, store)
- if err != nil {
- return err
- }
-
- return clientio.Encode(numKeys, false)
-}
-
-// evalHMSET sets the specified fields to their
-// respective values in a hashmap stored at key
-//
-// This command overwrites the values of specified
-// fields that exist in the hash.
-//
-// If key doesn't exist, a new key holding a hash is created.
-//
-// Usage: HMSET key field value [field value ...]
-func evalHMSET(args []string, store *dstore.Store) []byte {
- if len(args) < 3 {
- return diceerrors.NewErrArity("HMSET")
- }
-
- _, err := insertInHashMap(args, store)
- if err != nil {
- return err
- }
-
- return clientio.RespOK
-}
-
-// helper function to insert key value in hashmap associated with the given hash
-func insertInHashMap(args []string, store *dstore.Store) (numKeys int64, err2 []byte) {
- key := args[0]
-
- obj := store.Get(key)
-
- var hashMap HashMap
-
- if obj != nil {
- if err := object.AssertTypeAndEncoding(obj.TypeEncoding, object.ObjTypeHashMap, object.ObjEncodingHashMap); err != nil {
- return 0, diceerrors.NewErrWithMessage(diceerrors.WrongTypeErr)
- }
- hashMap = obj.Value.(HashMap)
- }
-
- keyValuePairs := args[1:]
- hashMap, numKeys, err := hashMapBuilder(keyValuePairs, hashMap)
- if err != nil {
- return 0, diceerrors.NewErrWithMessage(err.Error())
- }
-
- obj = store.NewObj(hashMap, -1, object.ObjTypeHashMap, object.ObjEncodingHashMap)
-
- store.Put(key, obj)
-
- return numKeys, nil
-}
-
-func evalHSETNX(args []string, store *dstore.Store) []byte {
- if len(args) != 3 {
- return diceerrors.NewErrArity("HSETNX")
- }
-
- key := args[0]
- hmKey := args[1]
-
- val, errWithMessage := getValueFromHashMap(key, hmKey, store)
- if errWithMessage != nil {
- return errWithMessage
- }
- if !bytes.Equal(val, clientio.RespNIL) { // hmKey is already present in hash map
- return clientio.RespZero
- }
-
- evalHSET(args, store)
- return clientio.RespOne
-}
-
func evalHGETALL(args []string, store *dstore.Store) []byte {
if len(args) != 1 {
return diceerrors.NewErrArity("HGETALL")
@@ -2439,88 +1954,6 @@ func evalHGETALL(args []string, store *dstore.Store) []byte {
return clientio.Encode(results, false)
}
-func evalHGET(args []string, store *dstore.Store) []byte {
- if len(args) != 2 {
- return diceerrors.NewErrArity("HGET")
- }
-
- key := args[0]
- hmKey := args[1]
-
- val, errWithMessage := getValueFromHashMap(key, hmKey, store)
- if errWithMessage != nil {
- return errWithMessage
- }
- return val
-}
-
-// evalHMGET returns an array of values associated with the given fields,
-// in the same order as they are requested.
-// If a field does not exist, returns a corresponding nil value in the array.
-// If the key does not exist, returns an array of nil values.
-func evalHMGET(args []string, store *dstore.Store) []byte {
- if len(args) < 2 {
- return diceerrors.NewErrArity("HMGET")
- }
- key := args[0]
-
- obj := store.Get(key)
-
- results := make([]interface{}, len(args[1:]))
- if obj == nil {
- return clientio.Encode(results, false)
- }
- if err := object.AssertTypeAndEncoding(obj.TypeEncoding, object.ObjTypeHashMap, object.ObjEncodingHashMap); err != nil {
- return diceerrors.NewErrWithMessage(diceerrors.WrongTypeErr)
- }
-
- hashMap := obj.Value.(HashMap)
-
- for i, hmKey := range args[1:] {
- hmValue, ok := hashMap.Get(hmKey)
- if ok {
- results[i] = *hmValue
- } else {
- results[i] = clientio.RespNIL
- }
- }
-
- return clientio.Encode(results, false)
-}
-
-func evalHDEL(args []string, store *dstore.Store) []byte {
- if len(args) < 2 {
- return diceerrors.NewErrArity("HDEL")
- }
-
- key := args[0]
- fields := args[1:]
-
- obj := store.Get(key)
- if obj == nil {
- return clientio.Encode(0, false)
- }
-
- if err := object.AssertTypeAndEncoding(obj.TypeEncoding, object.ObjTypeHashMap, object.ObjEncodingHashMap); err != nil {
- return diceerrors.NewErrWithFormattedMessage(diceerrors.WrongTypeErr)
- }
-
- hashMap := obj.Value.(HashMap)
- count := 0
- for _, field := range fields {
- if _, ok := hashMap[field]; ok {
- delete(hashMap, field)
- count++
- }
- }
-
- if count > 0 {
- store.Put(key, obj)
- }
-
- return clientio.Encode(count, false)
-}
-
func evalObjectIdleTime(key string, store *dstore.Store) []byte {
obj := store.GetNoTouch(key)
if obj == nil {
diff --git a/internal/eval/eval_test.go b/internal/eval/eval_test.go
index 686d01ca5..f5a0a3193 100644
--- a/internal/eval/eval_test.go
+++ b/internal/eval/eval_test.go
@@ -23,8 +23,7 @@ import (
diceerrors "github.com/dicedb/dice/internal/errors"
"github.com/dicedb/dice/internal/object"
dstore "github.com/dicedb/dice/internal/store"
- testifyAssert "github.com/stretchr/testify/assert"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
type evalTestCase struct {
@@ -67,6 +66,7 @@ func TestEval(t *testing.T) {
testEvalJSONARRAPPEND(t, store)
testEvalJSONRESP(t, store)
testEvalTTL(t, store)
+ testEvalPTTL(t, store)
testEvalDel(t, store)
testEvalPersist(t, store)
testEvalEXPIRE(t, store)
@@ -91,7 +91,7 @@ func TestEval(t *testing.T) {
testEvalHLEN(t, store)
testEvalSELECT(t, store)
testEvalLLEN(t, store)
- testEvalGETEX(t, store)
+ testEvalGETDEL(t, store)
testEvalJSONNUMINCRBY(t, store)
testEvalDUMP(t, store)
testEvalTYPE(t, store)
@@ -311,6 +311,51 @@ func testEvalSET(t *testing.T, store *dstore.Store) {
input: []string{"KEY", "VAL", Pxat, strconv.FormatInt(time.Now().Add(2*time.Minute).UnixMilli(), 10)},
migratedOutput: EvalResponse{Result: clientio.OK, Error: nil},
},
+ {
+ name: "key val pair and invalid EX and PX",
+ input: []string{"KEY", "VAL", Ex, "2", Px, "2000"},
+ migratedOutput: EvalResponse{Result: nil, Error: errors.New("ERR syntax error")},
+ },
+ {
+ name: "key val pair and invalid EX and PXAT",
+ input: []string{"KEY", "VAL", Ex, "2", Pxat, "2"},
+ migratedOutput: EvalResponse{Result: nil, Error: errors.New("ERR syntax error")},
+ },
+ {
+ name: "key val pair and invalid PX and PXAT",
+ input: []string{"KEY", "VAL", Px, "2000", Pxat, "2"},
+ migratedOutput: EvalResponse{Result: nil, Error: errors.New("ERR syntax error")},
+ },
+ {
+ name: "key val pair and KeepTTL",
+ input: []string{"KEY", "VAL", KeepTTL},
+ migratedOutput: EvalResponse{Result: clientio.OK, Error: nil},
+ },
+ {
+ name: "key val pair and invalid KeepTTL",
+ input: []string{"KEY", "VAL", KeepTTL, "2"},
+ migratedOutput: EvalResponse{Result: nil, Error: errors.New("ERR syntax error")},
+ },
+ {
+ name: "key val pair and KeepTTL, EX",
+ input: []string{"KEY", "VAL", Ex, "2", KeepTTL},
+ migratedOutput: EvalResponse{Result: nil, Error: errors.New("ERR syntax error")},
+ },
+ {
+ name: "key val pair and KeepTTL, PX",
+ input: []string{"KEY", "VAL", Px, "2000", KeepTTL},
+ migratedOutput: EvalResponse{Result: nil, Error: errors.New("ERR syntax error")},
+ },
+ {
+ name: "key val pair and KeepTTL, PXAT",
+ input: []string{"KEY", "VAL", Pxat, strconv.FormatInt(time.Now().Add(2*time.Minute).UnixMilli(), 10), KeepTTL},
+ migratedOutput: EvalResponse{Result: nil, Error: errors.New("ERR syntax error")},
+ },
+ {
+ name: "key val pair and KeepTTL, invalid PXAT",
+ input: []string{"KEY", "VAL", Pxat, "invalid_expiry_val", KeepTTL},
+ migratedOutput: EvalResponse{Result: nil, Error: errors.New("ERR value is not an integer or out of range")},
+ },
}
for _, tt := range tests {
@@ -320,16 +365,16 @@ func testEvalSET(t *testing.T, store *dstore.Store) {
// Handle comparison for byte slices
if b, ok := response.Result.([]byte); ok && tt.migratedOutput.Result != nil {
if expectedBytes, ok := tt.migratedOutput.Result.([]byte); ok {
- testifyAssert.True(t, bytes.Equal(b, expectedBytes), "expected and actual byte slices should be equal")
+ assert.True(t, bytes.Equal(b, expectedBytes), "expected and actual byte slices should be equal")
}
} else {
assert.Equal(t, tt.migratedOutput.Result, response.Result)
}
if tt.migratedOutput.Error != nil {
- testifyAssert.EqualError(t, response.Error, tt.migratedOutput.Error.Error())
+ assert.EqualError(t, response.Error, tt.migratedOutput.Error.Error())
} else {
- testifyAssert.NoError(t, response.Error)
+ assert.NoError(t, response.Error)
}
})
}
@@ -346,8 +391,11 @@ func testEvalGETEX(t *testing.T, store *dstore.Store) {
}
store.Put(key, obj)
},
- input: []string{"foo", Ex, "10"},
- output: clientio.Encode("bar", false),
+ input: []string{"foo", Ex, "10"},
+ migratedOutput: EvalResponse{
+ Result: "bar",
+ Error: nil,
+ },
},
"key val pair and invalid EX": {
setup: func() {
@@ -358,26 +406,129 @@ func testEvalGETEX(t *testing.T, store *dstore.Store) {
}
store.Put(key, obj)
},
- input: []string{"foo", Ex, "10000000000000000"},
- output: []byte("-ERR invalid expire time in 'getex' command\r\n"),
+ input: []string{"foo", Ex, "10000000000000000"},
+ migratedOutput: EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrInvalidExpireTime("GETEX"),
+ },
+ },
+ "key val pair and EX and string expire time": {
+ setup: func() {
+ key := "foo"
+ value := "bar"
+ obj := &object.Obj{
+ Value: value,
+ }
+ store.Put(key, obj)
+ },
+ input: []string{"foo", Ex, "string"},
+ migratedOutput: EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrIntegerOutOfRange,
+ },
+ },
+ "key val pair and both EX and PERSIST": {
+ setup: func() {
+ key := "foo"
+ value := "bar"
+ obj := &object.Obj{
+ Value: value,
+ }
+ store.Put(key, obj)
+ },
+ input: []string{"foo", Ex, Persist},
+ migratedOutput: EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrIntegerOutOfRange,
+ },
},
"key holding json type": {
setup: func() {
evalJSONSET([]string{"JSONKEY", "$", "1"}, store)
},
- input: []string{"JSONKEY"},
- output: []byte("-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"),
+ input: []string{"JSONKEY"},
+ migratedOutput: EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrWrongTypeOperation,
+ },
},
"key holding set type": {
setup: func() {
evalSADD([]string{"SETKEY", "FRUITS", "APPLE", "MANGO", "BANANA"}, store)
},
- input: []string{"SETKEY"},
- output: []byte("-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"),
+ input: []string{"SETKEY"},
+ migratedOutput: EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrWrongTypeOperation,
+ },
},
}
- runEvalTests(t, tests, evalGETEX, store)
+ runMigratedEvalTests(t, tests, evalGETEX, store)
+}
+
+func testEvalGETDEL(t *testing.T, store *dstore.Store) {
+ tests := map[string]evalTestCase{
+ "nil value": {
+ input: nil,
+ migratedOutput: EvalResponse{Result: nil, Error: errors.New("ERR wrong number of arguments for 'getdel' command")},
+ },
+ "empty array": {
+ input: []string{},
+ migratedOutput: EvalResponse{Result: nil, Error: errors.New("ERR wrong number of arguments for 'getdel' command")},
+ },
+ "key does not exist": {
+ input: []string{"NONEXISTENT_KEY"},
+ migratedOutput: EvalResponse{Result: clientio.NIL, Error: nil},
+ },
+ "multiple arguments": {
+ input: []string{"KEY1", "KEY2"},
+ migratedOutput: EvalResponse{Result: nil, Error: errors.New("ERR wrong number of arguments for 'getdel' command")},
+ },
+ "key exists": {
+ setup: func() {
+ key := "diceKey"
+ value := "diceVal"
+ obj := &object.Obj{
+ Value: value,
+ LastAccessedAt: uint32(time.Now().Unix()),
+ }
+ store.Put(key, obj)
+ },
+ input: []string{"diceKey"},
+ migratedOutput: EvalResponse{Result: "diceVal", Error: nil},
+ },
+ "key exists but expired": {
+ setup: func() {
+ key := "EXISTING_KEY"
+ value := "mock_value"
+ obj := &object.Obj{
+ Value: value,
+ LastAccessedAt: uint32(time.Now().Unix()),
+ }
+ store.Put(key, obj)
+ store.SetExpiry(obj, int64(-2*time.Millisecond))
+ },
+ input: []string{"EXISTING_KEY"},
+ migratedOutput: EvalResponse{Result: clientio.NIL, Error: nil},
+ },
+ "key deleted by previous call of GETDEL": {
+ setup: func() {
+ key := "DELETED_KEY"
+ value := "mock_value"
+ obj := &object.Obj{
+ Value: value,
+ LastAccessedAt: uint32(time.Now().Unix()),
+ }
+ store.Put(key, obj)
+ evalGETDEL([]string{key}, store)
+ },
+ input: []string{"DELETED_KEY"},
+ migratedOutput: EvalResponse{Result: clientio.NIL, Error: nil},
+ },
+ }
+
+ runMigratedEvalTests(t, tests, evalGETDEL, store)
}
func testEvalMSET(t *testing.T, store *dstore.Store) {
@@ -458,16 +609,16 @@ func testEvalGET(t *testing.T, store *dstore.Store) {
// Handle comparison for byte slices
if b, ok := response.Result.([]byte); ok && tt.migratedOutput.Result != nil {
if expectedBytes, ok := tt.migratedOutput.Result.([]byte); ok {
- testifyAssert.True(t, bytes.Equal(b, expectedBytes), "expected and actual byte slices should be equal")
+ assert.True(t, bytes.Equal(b, expectedBytes), "expected and actual byte slices should be equal")
}
} else {
assert.Equal(t, tt.migratedOutput.Result, response.Result)
}
if tt.migratedOutput.Error != nil {
- testifyAssert.EqualError(t, response.Error, tt.migratedOutput.Error.Error())
+ assert.EqualError(t, response.Error, tt.migratedOutput.Error.Error())
} else {
- testifyAssert.NoError(t, response.Error)
+ assert.NoError(t, response.Error)
}
})
}
@@ -532,16 +683,16 @@ func testEvalGETSET(t *testing.T, store *dstore.Store) {
// Handle comparison for byte slices
if b, ok := response.Result.([]byte); ok && tt.migratedOutput.Result != nil {
if expectedBytes, ok := tt.migratedOutput.Result.([]byte); ok {
- testifyAssert.True(t, bytes.Equal(b, expectedBytes), "expected and actual byte slices should be equal")
+ assert.True(t, bytes.Equal(b, expectedBytes), "expected and actual byte slices should be equal")
}
} else {
assert.Equal(t, tt.migratedOutput.Result, response.Result)
}
if tt.migratedOutput.Error != nil {
- testifyAssert.EqualError(t, response.Error, tt.migratedOutput.Error.Error())
+ assert.EqualError(t, response.Error, tt.migratedOutput.Error.Error())
} else {
- testifyAssert.NoError(t, response.Error)
+ assert.NoError(t, response.Error)
}
})
}
@@ -550,20 +701,48 @@ func testEvalGETSET(t *testing.T, store *dstore.Store) {
func testEvalEXPIRE(t *testing.T, store *dstore.Store) {
tests := map[string]evalTestCase{
"nil value": {
- input: nil,
- output: []byte("-ERR wrong number of arguments for 'expire' command\r\n"),
+ input: nil,
+ migratedOutput: EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrWrongArgumentCount("EXPIRE"),
+ },
},
"empty args": {
- input: []string{},
- output: []byte("-ERR wrong number of arguments for 'expire' command\r\n"),
+ input: []string{},
+ migratedOutput: EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrWrongArgumentCount("EXPIRE"),
+ },
},
"wrong number of args": {
- input: []string{"KEY1"},
- output: []byte("-ERR wrong number of arguments for 'expire' command\r\n"),
+ input: []string{"KEY1"},
+ migratedOutput: EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrWrongArgumentCount("EXPIRE"),
+ },
},
"key does not exist": {
- input: []string{"NONEXISTENT_KEY", strconv.FormatInt(1, 10)},
- output: clientio.RespZero,
+ input: []string{"NONEXISTENT_KEY", strconv.FormatInt(1, 10)},
+ migratedOutput: EvalResponse{
+ Result: clientio.IntegerZero,
+ Error: nil,
+ },
+ },
+ "invalid expiry time - 0": {
+ setup: func() {
+ key := "EXISTING_KEY"
+ value := "mock_value"
+ obj := &object.Obj{
+ Value: value,
+ LastAccessedAt: uint32(time.Now().Unix()),
+ }
+ store.Put(key, obj)
+ },
+ input: []string{"EXISTING_KEY", "0"},
+ migratedOutput: EvalResponse{
+ Result: clientio.IntegerOne,
+ Error: nil,
+ },
},
"key exists": {
setup: func() {
@@ -575,10 +754,13 @@ func testEvalEXPIRE(t *testing.T, store *dstore.Store) {
}
store.Put(key, obj)
},
- input: []string{"EXISTING_KEY", strconv.FormatInt(1, 10)},
- output: clientio.RespOne,
+ input: []string{"EXISTING_KEY", strconv.FormatInt(1, 10)},
+ migratedOutput: EvalResponse{
+ Result: clientio.IntegerOne,
+ Error: nil,
+ },
},
- "invalid expiry time exists - very large integer": {
+ "invalid expiry time - very large integer": {
setup: func() {
key := "EXISTING_KEY"
value := "mock_value"
@@ -588,11 +770,14 @@ func testEvalEXPIRE(t *testing.T, store *dstore.Store) {
}
store.Put(key, obj)
},
- input: []string{"EXISTING_KEY", strconv.FormatInt(9223372036854776, 10)},
- output: []byte("-ERR invalid expire time in 'expire' command\r\n"),
+ input: []string{"EXISTING_KEY", strconv.FormatInt(9223372036854776, 10)},
+ migratedOutput: EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrInvalidExpireTime("EXPIRE"),
+ },
},
- "invalid expiry time exists - negative integer": {
+ "invalid expiry time - negative integer": {
setup: func() {
key := "EXISTING_KEY"
value := "mock_value"
@@ -602,10 +787,13 @@ func testEvalEXPIRE(t *testing.T, store *dstore.Store) {
}
store.Put(key, obj)
},
- input: []string{"EXISTING_KEY", strconv.FormatInt(-1, 10)},
- output: []byte("-ERR invalid expire time in 'expire' command\r\n"),
+ input: []string{"EXISTING_KEY", strconv.FormatInt(-1, 10)},
+ migratedOutput: EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrInvalidExpireTime("EXPIRE"),
+ },
},
- "invalid expiry time exists - empty string": {
+ "invalid expiry time - empty string": {
setup: func() {
key := "EXISTING_KEY"
value := "mock_value"
@@ -615,10 +803,13 @@ func testEvalEXPIRE(t *testing.T, store *dstore.Store) {
}
store.Put(key, obj)
},
- input: []string{"EXISTING_KEY", ""},
- output: []byte("-ERR value is not an integer or out of range\r\n"),
+ input: []string{"EXISTING_KEY", ""},
+ migratedOutput: EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrIntegerOutOfRange,
+ },
},
- "invalid expiry time exists - with float number": {
+ "invalid expiry time - with float number": {
setup: func() {
key := "EXISTING_KEY"
value := "mock_value"
@@ -628,23 +819,32 @@ func testEvalEXPIRE(t *testing.T, store *dstore.Store) {
}
store.Put(key, obj)
},
- input: []string{"EXISTING_KEY", "0.456"},
- output: []byte("-ERR value is not an integer or out of range\r\n"),
+ input: []string{"EXISTING_KEY", "0.456"},
+ migratedOutput: EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrIntegerOutOfRange,
+ },
},
}
- runEvalTests(t, tests, evalEXPIRE, store)
+ runMigratedEvalTests(t, tests, evalEXPIRE, store)
}
func testEvalEXPIRETIME(t *testing.T, store *dstore.Store) {
tests := map[string]evalTestCase{
"wrong number of args": {
- input: []string{"KEY1", "KEY2"},
- output: []byte("-ERR wrong number of arguments for 'expiretime' command\r\n"),
+ input: []string{"KEY1", "KEY2"},
+ migratedOutput: EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrWrongArgumentCount("EXPIRETIME"),
+ },
},
"key does not exist": {
- input: []string{"NONEXISTENT_KEY"},
- output: clientio.RespMinusTwo,
+ input: []string{"NONEXISTENT_KEY"},
+ migratedOutput: EvalResponse{
+ Result: clientio.IntegerNegativeTwo,
+ Error: nil,
+ },
},
"key exists without expiry": {
setup: func() {
@@ -656,8 +856,11 @@ func testEvalEXPIRETIME(t *testing.T, store *dstore.Store) {
}
store.Put(key, obj)
},
- input: []string{"EXISTING_KEY"},
- output: clientio.RespMinusOne,
+ input: []string{"EXISTING_KEY"},
+ migratedOutput: EvalResponse{
+ Result: clientio.IntegerNegativeOne,
+ Error: nil,
+ },
},
"key exists with expiry": {
setup: func() {
@@ -671,31 +874,46 @@ func testEvalEXPIRETIME(t *testing.T, store *dstore.Store) {
store.SetUnixTimeExpiry(obj, 2724123456123)
},
- input: []string{"EXISTING_KEY"},
- output: []byte(fmt.Sprintf(":%d\r\n", 2724123456123)),
+ input: []string{"EXISTING_KEY"},
+ migratedOutput: EvalResponse{
+ Result: uint64(2724123456123),
+ Error: nil,
+ },
},
}
- runEvalTests(t, tests, evalEXPIRETIME, store)
+ runMigratedEvalTests(t, tests, evalEXPIRETIME, store)
}
func testEvalEXPIREAT(t *testing.T, store *dstore.Store) {
tests := map[string]evalTestCase{
"nil value": {
- input: nil,
- output: []byte("-ERR wrong number of arguments for 'expireat' command\r\n"),
+ input: nil,
+ migratedOutput: EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrWrongArgumentCount("EXPIREAT"),
+ },
},
"empty args": {
- input: []string{},
- output: []byte("-ERR wrong number of arguments for 'expireat' command\r\n"),
+ input: []string{},
+ migratedOutput: EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrWrongArgumentCount("EXPIREAT"),
+ },
},
"wrong number of args": {
- input: []string{"KEY1"},
- output: []byte("-ERR wrong number of arguments for 'expireat' command\r\n"),
+ input: []string{"KEY1"},
+ migratedOutput: EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrWrongArgumentCount("EXPIREAT"),
+ },
},
"key does not exist": {
- input: []string{"NONEXISTENT_KEY", strconv.FormatInt(time.Now().Add(2*time.Minute).Unix(), 10)},
- output: clientio.RespZero,
+ input: []string{"NONEXISTENT_KEY", strconv.FormatInt(time.Now().Add(2*time.Minute).Unix(), 10)},
+ migratedOutput: EvalResponse{
+ Result: clientio.IntegerZero,
+ Error: nil,
+ },
},
"key exists": {
setup: func() {
@@ -707,8 +925,11 @@ func testEvalEXPIREAT(t *testing.T, store *dstore.Store) {
}
store.Put(key, obj)
},
- input: []string{"EXISTING_KEY", strconv.FormatInt(time.Now().Add(2*time.Minute).Unix(), 10)},
- output: clientio.RespOne,
+ input: []string{"EXISTING_KEY", strconv.FormatInt(time.Now().Add(2*time.Minute).Unix(), 10)},
+ migratedOutput: EvalResponse{
+ Result: clientio.IntegerOne,
+ Error: nil,
+ },
},
"invalid expire time - very large integer": {
setup: func() {
@@ -720,8 +941,11 @@ func testEvalEXPIREAT(t *testing.T, store *dstore.Store) {
}
store.Put(key, obj)
},
- input: []string{"EXISTING_KEY", strconv.FormatInt(9223372036854776, 10)},
- output: []byte("-ERR invalid expire time in 'expireat' command\r\n"),
+ input: []string{"EXISTING_KEY", strconv.FormatInt(9223372036854776, 10)},
+ migratedOutput: EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrInvalidExpireTime("EXPIREAT"),
+ },
},
"invalid expire time - negative integer": {
setup: func() {
@@ -733,12 +957,15 @@ func testEvalEXPIREAT(t *testing.T, store *dstore.Store) {
}
store.Put(key, obj)
},
- input: []string{"EXISTING_KEY", strconv.FormatInt(-1, 10)},
- output: []byte("-ERR invalid expire time in 'expireat' command\r\n"),
+ input: []string{"EXISTING_KEY", strconv.FormatInt(-1, 10)},
+ migratedOutput: EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrInvalidExpireTime("EXPIREAT"),
+ },
},
}
- runEvalTests(t, tests, evalEXPIREAT, store)
+ runMigratedEvalTests(t, tests, evalEXPIREAT, store)
}
func testEvalJSONARRTRIM(t *testing.T, store *dstore.Store) {
@@ -926,16 +1153,16 @@ func testEvalJSONARRTRIM(t *testing.T, store *dstore.Store) {
// Handle comparison for byte slices
if b, ok := response.Result.([]byte); ok && tt.migratedOutput.Result != nil {
if expectedBytes, ok := tt.migratedOutput.Result.([]byte); ok {
- testifyAssert.True(t, bytes.Equal(b, expectedBytes), "expected and actual byte slices should be equal")
+ assert.True(t, bytes.Equal(b, expectedBytes), "expected and actual byte slices should be equal")
}
} else {
- testifyAssert.Equal(t, tt.migratedOutput.Result, response.Result)
+ assert.Equal(t, tt.migratedOutput.Result, response.Result)
}
if tt.migratedOutput.Error != nil {
- testifyAssert.EqualError(t, response.Error, tt.migratedOutput.Error.Error())
+ assert.EqualError(t, response.Error, tt.migratedOutput.Error.Error())
} else {
- testifyAssert.NoError(t, response.Error)
+ assert.NoError(t, response.Error)
}
})
}
@@ -1094,16 +1321,16 @@ func testEvalJSONARRINSERT(t *testing.T, store *dstore.Store) {
if b, ok := response.Result.([]byte); ok && tt.migratedOutput.Result != nil {
if expectedBytes, ok := tt.migratedOutput.Result.([]byte); ok {
- testifyAssert.True(t, bytes.Equal(b, expectedBytes), "expected and actual byte slices should be equal")
+ assert.True(t, bytes.Equal(b, expectedBytes), "expected and actual byte slices should be equal")
}
} else {
- testifyAssert.Equal(t, tt.migratedOutput.Result, response.Result)
+ assert.Equal(t, tt.migratedOutput.Result, response.Result)
}
if tt.migratedOutput.Error != nil {
- testifyAssert.EqualError(t, response.Error, tt.migratedOutput.Error.Error())
+ assert.EqualError(t, response.Error, tt.migratedOutput.Error.Error())
} else {
- testifyAssert.NoError(t, response.Error)
+ assert.NoError(t, response.Error)
}
})
}
@@ -1208,16 +1435,16 @@ func testEvalJSONARRLEN(t *testing.T, store *dstore.Store) {
if tt.migratedOutput.Result != nil {
if slice, ok := tt.migratedOutput.Result.([]interface{}); ok {
- assert.DeepEqual(t, slice, response.Result)
+ assert.Equal(t, slice, response.Result)
} else {
assert.Equal(t, tt.migratedOutput.Result, response.Result)
}
}
if tt.migratedOutput.Error != nil {
- testifyAssert.EqualError(t, response.Error, tt.migratedOutput.Error.Error())
+ assert.EqualError(t, response.Error, tt.migratedOutput.Error.Error())
} else {
- testifyAssert.NoError(t, response.Error)
+ assert.NoError(t, response.Error)
}
})
}
@@ -1392,16 +1619,16 @@ func testEvalJSONOBJLEN(t *testing.T, store *dstore.Store) {
response := evalJSONOBJLEN(tt.input, store)
if tt.migratedOutput.Result != nil {
if slice, ok := tt.migratedOutput.Result.([]interface{}); ok {
- assert.DeepEqual(t, slice, response.Result)
+ assert.Equal(t, slice, response.Result)
} else {
assert.Equal(t, tt.migratedOutput.Result, response.Result)
}
}
if tt.migratedOutput.Error != nil {
- testifyAssert.EqualError(t, response.Error, tt.migratedOutput.Error.Error())
+ assert.EqualError(t, response.Error, tt.migratedOutput.Error.Error())
} else {
- testifyAssert.NoError(t, response.Error)
+ assert.NoError(t, response.Error)
}
})
}
@@ -1708,16 +1935,16 @@ func testEvalJSONCLEAR(t *testing.T, store *dstore.Store) {
response := evalJSONCLEAR(tt.input, store)
if tt.migratedOutput.Result != nil {
if slice, ok := tt.migratedOutput.Result.([]interface{}); ok {
- assert.DeepEqual(t, slice, response.Result)
+ assert.Equal(t, slice, response.Result)
} else {
assert.Equal(t, tt.migratedOutput.Result, response.Result)
}
}
if tt.migratedOutput.Error != nil {
- testifyAssert.EqualError(t, response.Error, tt.migratedOutput.Error.Error())
+ assert.EqualError(t, response.Error, tt.migratedOutput.Error.Error())
} else {
- testifyAssert.NoError(t, response.Error)
+ assert.NoError(t, response.Error)
}
})
}
@@ -1921,8 +2148,8 @@ func testEvalJSONSET(t *testing.T, store *dstore.Store) {
input: []string{"doc", "$", "{\"a\":}"},
output: nil,
validator: func(output []byte) {
- assert.Assert(t, output != nil)
- assert.Assert(t, strings.Contains(string(output), "-ERR invalid JSON:"))
+ assert.True(t, output != nil)
+ assert.True(t, strings.Contains(string(output), "-ERR invalid JSON:"))
},
},
"valid json path": {
@@ -2181,9 +2408,9 @@ func testEvalJSONARRAPPEND(t *testing.T, store *dstore.Store) {
}
if tt.migratedOutput.Error != nil {
- testifyAssert.EqualError(t, response.Error, tt.migratedOutput.Error.Error())
+ assert.EqualError(t, response.Error, tt.migratedOutput.Error.Error())
} else {
- testifyAssert.NoError(t, response.Error)
+ assert.NoError(t, response.Error)
}
})
}
@@ -2278,27 +2505,131 @@ func testEvalJSONTOGGLE(t *testing.T, store *dstore.Store) {
runEvalTests(t, tests, evalJSONTOGGLE, store)
}
+func testEvalPTTL(t *testing.T, store *dstore.Store) {
+ tests := map[string]evalTestCase{
+ "nil value": {
+ setup: func() {},
+ input: nil,
+ migratedOutput: EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrWrongArgumentCount("PTTL"),
+ },
+ },
+ "empty array": {
+ setup: func() {},
+ input: []string{},
+ migratedOutput: EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrWrongArgumentCount("PTTL"),
+ },
+ },
+ "key does not exist": {
+ setup: func() {},
+ input: []string{"NONEXISTENT_KEY"},
+ migratedOutput: EvalResponse{
+ Result: clientio.IntegerNegativeTwo,
+ Error: nil,
+ },
+ },
+ "multiple arguments": {
+ setup: func() {},
+ input: []string{"KEY1", "KEY2"},
+ migratedOutput: EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrWrongArgumentCount("PTTL"),
+ },
+ },
+ "key exists expiry not set": {
+ setup: func() {
+ key := "EXISTING_KEY"
+ value := "mock_value"
+ obj := &object.Obj{
+ Value: value,
+ LastAccessedAt: uint32(time.Now().Unix()),
+ }
+ store.Put(key, obj)
+ },
+ input: []string{"EXISTING_KEY"},
+ migratedOutput: EvalResponse{
+ Result: clientio.IntegerNegativeOne,
+ Error: nil,
+ },
+ },
+ "key exists not expired": {
+ setup: func() {
+ key := "EXISTING_KEY"
+ value := "mock_value"
+ obj := &object.Obj{
+ Value: value,
+ LastAccessedAt: uint32(time.Now().Unix()),
+ }
+ store.Put(key, obj)
+
+ store.SetExpiry(obj, int64(2*time.Millisecond))
+ },
+ input: []string{"EXISTING_KEY"},
+ newValidator: func(output interface{}) {
+ assert.True(t, output != nil)
+ assert.True(t, output != clientio.IntegerNegativeOne)
+ assert.True(t, output != clientio.IntegerNegativeTwo)
+ },
+ },
+ "key exists but expired": {
+ setup: func() {
+ key := "EXISTING_EXPIRED_KEY"
+ value := "mock_value"
+ obj := &object.Obj{
+ Value: value,
+ LastAccessedAt: uint32(time.Now().Unix()),
+ }
+ store.Put(key, obj)
+
+ store.SetExpiry(obj, int64(-2*time.Millisecond))
+ },
+ input: []string{"EXISTING_KEY"},
+ migratedOutput: EvalResponse{
+ Result: clientio.IntegerNegativeTwo,
+ Error: nil,
+ },
+ },
+ }
+
+ runMigratedEvalTests(t, tests, evalPTTL, store)
+}
+
func testEvalTTL(t *testing.T, store *dstore.Store) {
tests := map[string]evalTestCase{
"nil value": {
- setup: func() {},
- input: nil,
- output: []byte("-ERR wrong number of arguments for 'ttl' command\r\n"),
+ setup: func() {},
+ input: nil,
+ migratedOutput: EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrWrongArgumentCount("TTL"),
+ },
},
"empty array": {
- setup: func() {},
- input: []string{},
- output: []byte("-ERR wrong number of arguments for 'ttl' command\r\n"),
+ setup: func() {},
+ input: []string{},
+ migratedOutput: EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrWrongArgumentCount("TTL"),
+ },
},
"key does not exist": {
- setup: func() {},
- input: []string{"NONEXISTENT_KEY"},
- output: clientio.RespMinusTwo,
+ setup: func() {},
+ input: []string{"NONEXISTENT_KEY"},
+ migratedOutput: EvalResponse{
+ Result: clientio.IntegerNegativeTwo,
+ Error: nil,
+ },
},
"multiple arguments": {
- setup: func() {},
- input: []string{"KEY1", "KEY2"},
- output: []byte("-ERR wrong number of arguments for 'ttl' command\r\n"),
+ setup: func() {},
+ input: []string{"KEY1", "KEY2"},
+ migratedOutput: EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrWrongArgumentCount("TTL"),
+ },
},
"key exists expiry not set": {
setup: func() {
@@ -2310,8 +2641,11 @@ func testEvalTTL(t *testing.T, store *dstore.Store) {
}
store.Put(key, obj)
},
- input: []string{"EXISTING_KEY"},
- output: clientio.RespMinusOne,
+ input: []string{"EXISTING_KEY"},
+ migratedOutput: EvalResponse{
+ Result: clientio.IntegerNegativeOne,
+ Error: nil,
+ },
},
"key exists not expired": {
setup: func() {
@@ -2326,10 +2660,10 @@ func testEvalTTL(t *testing.T, store *dstore.Store) {
store.SetExpiry(obj, int64(2*time.Millisecond))
},
input: []string{"EXISTING_KEY"},
- validator: func(output []byte) {
- assert.Assert(t, output != nil)
- assert.Assert(t, !bytes.Equal(output, clientio.RespMinusOne))
- assert.Assert(t, !bytes.Equal(output, clientio.RespMinusTwo))
+ newValidator: func(output interface{}) {
+ assert.True(t, output != nil)
+ assert.True(t, output != clientio.IntegerNegativeOne)
+ assert.True(t, output != clientio.IntegerNegativeTwo)
},
},
"key exists but expired": {
@@ -2344,12 +2678,15 @@ func testEvalTTL(t *testing.T, store *dstore.Store) {
store.SetExpiry(obj, int64(-2*time.Millisecond))
},
- input: []string{"EXISTING_KEY"},
- output: clientio.RespMinusTwo,
+ input: []string{"EXISTING_KEY"},
+ migratedOutput: EvalResponse{
+ Result: clientio.IntegerNegativeTwo,
+ Error: nil,
+ },
},
}
- runEvalTests(t, tests, evalTTL, store)
+ runMigratedEvalTests(t, tests, evalTTL, store)
}
func testEvalDel(t *testing.T, store *dstore.Store) {
@@ -2713,21 +3050,30 @@ func testEvalPFMERGE(t *testing.T, store *dstore.Store) {
func testEvalHGET(t *testing.T, store *dstore.Store) {
tests := map[string]evalTestCase{
"wrong number of args passed": {
- setup: func() {},
- input: nil,
- output: []byte("-ERR wrong number of arguments for 'hget' command\r\n"),
+ setup: func() {},
+ input: nil,
+ migratedOutput: EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrWrongArgumentCount("HGET"),
+ },
},
"only key passed": {
- setup: func() {},
- input: []string{"KEY"},
- output: []byte("-ERR wrong number of arguments for 'hget' command\r\n"),
+ setup: func() {},
+ input: []string{"KEY"},
+ migratedOutput: EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrWrongArgumentCount("HGET"),
+ },
},
- "key doesn't exists": {
- setup: func() {},
- input: []string{"KEY", "field_name"},
- output: clientio.RespNIL,
+ "key doesn't exist": {
+ setup: func() {},
+ input: []string{"KEY", "field_name"},
+ migratedOutput: EvalResponse{
+ Result: clientio.NIL,
+ Error: nil,
+ },
},
- "key exists but field_name doesn't exists": {
+ "key exists but field_name doesn't exist": {
setup: func() {
key := "KEY_MOCK"
field := "mock_field_name"
@@ -2742,10 +3088,13 @@ func testEvalHGET(t *testing.T, store *dstore.Store) {
store.Put(key, obj)
},
- input: []string{"KEY_MOCK", "non_existent_key"},
- output: clientio.RespNIL,
+ input: []string{"KEY_MOCK", "non_existent_key"},
+ migratedOutput: EvalResponse{
+ Result: clientio.NIL,
+ Error: nil,
+ },
},
- "both key and field_name exists": {
+ "both key and field_name exist": {
setup: func() {
key := "KEY_MOCK"
field := "mock_field_name"
@@ -2760,32 +3109,44 @@ func testEvalHGET(t *testing.T, store *dstore.Store) {
store.Put(key, obj)
},
- input: []string{"KEY_MOCK", "mock_field_name"},
- output: clientio.Encode("mock_field_value", false),
+ input: []string{"KEY_MOCK", "mock_field_name"},
+ migratedOutput: EvalResponse{
+ Result: "mock_field_value",
+ Error: nil,
+ },
},
}
- runEvalTests(t, tests, evalHGET, store)
+ runMigratedEvalTests(t, tests, evalHGET, store)
}
func testEvalHMGET(t *testing.T, store *dstore.Store) {
tests := map[string]evalTestCase{
"wrong number of args passed": {
- setup: func() {},
- input: nil,
- output: []byte("-ERR wrong number of arguments for 'hmget' command\r\n"),
+ setup: func() {},
+ input: nil,
+ migratedOutput: EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrWrongArgumentCount("HMGET"),
+ },
},
"only key passed": {
- setup: func() {},
- input: []string{"KEY"},
- output: []byte("-ERR wrong number of arguments for 'hmget' command\r\n"),
+ setup: func() {},
+ input: []string{"KEY"},
+ migratedOutput: EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrWrongArgumentCount("HMGET"),
+ },
},
- "key doesn't exists": {
- setup: func() {},
- input: []string{"KEY", "field_name"},
- output: clientio.Encode([]interface{}{nil}, false),
+ "key doesn't exist": {
+ setup: func() {},
+ input: []string{"KEY", "field_name"},
+ migratedOutput: EvalResponse{
+ Result: []interface{}{nil},
+ Error: nil,
+ },
},
- "key exists but field_name doesn't exists": {
+ "key exists but field_name doesn't exist": {
setup: func() {
key := "KEY_MOCK"
field := "mock_field_name"
@@ -2800,10 +3161,13 @@ func testEvalHMGET(t *testing.T, store *dstore.Store) {
store.Put(key, obj)
},
- input: []string{"KEY_MOCK", "non_existent_key"},
- output: clientio.Encode([]interface{}{nil}, false),
+ input: []string{"KEY_MOCK", "non_existent_key"},
+ migratedOutput: EvalResponse{
+ Result: []interface{}{nil},
+ Error: nil,
+ },
},
- "both key and field_name exists": {
+ "both key and field_name exist": {
setup: func() {
key := "KEY_MOCK"
field := "mock_field_name"
@@ -2818,8 +3182,11 @@ func testEvalHMGET(t *testing.T, store *dstore.Store) {
store.Put(key, obj)
},
- input: []string{"KEY_MOCK", "mock_field_name"},
- output: clientio.Encode([]interface{}{"mock_field_value"}, false),
+ input: []string{"KEY_MOCK", "mock_field_name"},
+ migratedOutput: EvalResponse{
+ Result: []interface{}{"mock_field_value"},
+ Error: nil,
+ },
},
"some fields exist some do not": {
setup: func() {
@@ -2836,12 +3203,15 @@ func testEvalHMGET(t *testing.T, store *dstore.Store) {
store.Put(key, obj)
},
- input: []string{"KEY_MOCK", "field1", "field2", "field3", "field4"},
- output: clientio.Encode([]interface{}{"value1", "value2", nil, nil}, false),
+ input: []string{"KEY_MOCK", "field1", "field2", "field3", "field4"},
+ migratedOutput: EvalResponse{
+ Result: []interface{}{"value1", "value2", nil, nil}, // Use nil for non-existent fields
+ Error: nil,
+ },
},
}
- runEvalTests(t, tests, evalHMGET, store)
+ runMigratedEvalTests(t, tests, evalHMGET, store)
}
func testEvalHVALS(t *testing.T, store *dstore.Store) {
@@ -2893,22 +3263,22 @@ func testEvalHVALS(t *testing.T, store *dstore.Store) {
// Handle comparison for byte slices
if responseBytes, ok := response.Result.([]byte); ok && tt.migratedOutput.Result != nil {
if expectedBytes, ok := tt.migratedOutput.Result.([]byte); ok {
- testifyAssert.True(t, bytes.Equal(responseBytes, expectedBytes), "expected and actual byte slices should be equal")
+ assert.True(t, bytes.Equal(responseBytes, expectedBytes), "expected and actual byte slices should be equal")
}
} else {
fmt.Printf("G1: %v | %v\n", response.Result, tt.migratedOutput.Result)
switch e := tt.migratedOutput.Result.(type) {
case []interface{}, []string:
- testifyAssert.ElementsMatch(t, e, response.Result)
+ assert.ElementsMatch(t, e, response.Result)
default:
assert.Equal(t, tt.migratedOutput.Result, response.Result)
}
}
if tt.migratedOutput.Error != nil {
- testifyAssert.EqualError(t, response.Error, tt.migratedOutput.Error.Error())
+ assert.EqualError(t, response.Error, tt.migratedOutput.Error.Error())
} else {
- testifyAssert.NoError(t, response.Error)
+ assert.NoError(t, response.Error)
}
})
}
@@ -3046,7 +3416,7 @@ func testEvalHEXISTS(t *testing.T, store *dstore.Store) {
// If has result
if expectedBytes, ok := tt.migratedOutput.Result.([]byte); ok {
// fmt.Printf("%v | %v\n", responseBytes, expectedBytes)
- testifyAssert.True(t, bytes.Equal(responseBytes, expectedBytes), "expected and actual byte slices should be equal")
+ assert.True(t, bytes.Equal(responseBytes, expectedBytes), "expected and actual byte slices should be equal")
}
} else {
// If has error
@@ -3054,9 +3424,9 @@ func testEvalHEXISTS(t *testing.T, store *dstore.Store) {
}
if tt.migratedOutput.Error != nil {
- testifyAssert.EqualError(t, response.Error, tt.migratedOutput.Error.Error())
+ assert.EqualError(t, response.Error, tt.migratedOutput.Error.Error())
} else {
- testifyAssert.NoError(t, response.Error)
+ assert.NoError(t, response.Error)
}
})
}
@@ -3065,37 +3435,51 @@ func testEvalHEXISTS(t *testing.T, store *dstore.Store) {
func testEvalHDEL(t *testing.T, store *dstore.Store) {
tests := map[string]evalTestCase{
"HDEL with wrong number of args": {
- input: []string{"key"},
- output: []byte("-ERR wrong number of arguments for 'hdel' command\r\n"),
+ input: []string{"key"},
+ migratedOutput: EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrWrongArgumentCount("HDEL"),
+ },
},
"HDEL with key does not exist": {
- input: []string{"nonexistent", "field"},
- output: clientio.RespZero,
+ input: []string{"nonexistent", "field"},
+ migratedOutput: EvalResponse{
+ Result: int64(0),
+ Error: nil,
+ },
},
"HDEL with key exists but not a hash": {
setup: func() {
evalSET([]string{"string_key", "string_value"}, store)
},
- input: []string{"string_key", "field"},
- output: []byte("-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"),
+ input: []string{"string_key", "field"},
+ migratedOutput: EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrWrongTypeOperation,
+ },
},
"HDEL with delete existing fields": {
setup: func() {
evalHSET([]string{"hash_key", "field1", "value1", "field2", "value2"}, store)
},
- input: []string{"hash_key", "field1", "field2", "nonexistent"},
- output: clientio.Encode(int64(2), false),
+ input: []string{"hash_key", "field1", "field2", "nonexistent"},
+ migratedOutput: EvalResponse{
+ Result: int64(2),
+ Error: nil,
+ },
},
"HDEL with delete non-existing fields": {
setup: func() {
evalHSET([]string{"hash_key", "field1", "value1"}, store)
},
- input: []string{"hash_key", "nonexistent1", "nonexistent2"},
- output: clientio.RespZero,
+ input: []string{"hash_key", "nonexistent1", "nonexistent2"},
+ migratedOutput: EvalResponse{
+ Result: int64(0),
+ Error: nil,
+ },
},
}
-
- runEvalTests(t, tests, evalHDEL, store)
+ runMigratedEvalTests(t, tests, evalHDEL, store)
}
func testEvalHSCAN(t *testing.T, store *dstore.Store) {
@@ -3344,16 +3728,16 @@ func testEvalJSONSTRLEN(t *testing.T, store *dstore.Store) {
response := evalJSONSTRLEN(tt.input, store)
if tt.migratedOutput.Result != nil {
if slice, ok := tt.migratedOutput.Result.([]interface{}); ok {
- assert.DeepEqual(t, slice, response.Result)
+ assert.Equal(t, slice, response.Result)
} else {
assert.Equal(t, tt.migratedOutput.Result, response.Result)
}
}
if tt.migratedOutput.Error != nil {
- testifyAssert.EqualError(t, response.Error, tt.migratedOutput.Error.Error())
+ assert.EqualError(t, response.Error, tt.migratedOutput.Error.Error())
} else {
- testifyAssert.NoError(t, response.Error)
+ assert.NoError(t, response.Error)
}
})
}
@@ -3441,7 +3825,7 @@ func testEvalJSONNUMINCRBY(t *testing.T, store *dstore.Store) {
endIndex := strings.Index(outPutString, "]")
arrayString := outPutString[startIndex+1 : endIndex]
arr := strings.Split(arrayString, ",")
- testifyAssert.ElementsMatch(t, arr, []string{"25", "20", "7", "15", "null", "null"})
+ assert.ElementsMatch(t, arr, []string{"25", "20", "7", "15", "null", "null"})
},
},
@@ -3486,7 +3870,7 @@ func testEvalJSONNUMINCRBY(t *testing.T, store *dstore.Store) {
endIndex := strings.Index(outPutString, "]")
arrayString := outPutString[startIndex+1 : endIndex]
arr := strings.Split(arrayString, ",")
- testifyAssert.ElementsMatch(t, arr, []string{"3", "4", "7", "null", "null"})
+ assert.ElementsMatch(t, arr, []string{"3", "4", "7", "null", "null"})
},
},
@@ -3548,7 +3932,7 @@ func runMigratedEvalTests(t *testing.T, tests map[string]evalTestCase, evalFunc
}
if tc.migratedOutput.Error != nil {
- testifyAssert.EqualError(t, output.Error, tc.migratedOutput.Error.Error())
+ assert.EqualError(t, output.Error, tc.migratedOutput.Error.Error())
return
}
@@ -3556,17 +3940,17 @@ func runMigratedEvalTests(t *testing.T, tests map[string]evalTestCase, evalFunc
// TODO: Make this generic so that all kind of slices can be handled
if b, ok := output.Result.([]byte); ok && tc.migratedOutput.Result != nil {
if expectedBytes, ok := tc.migratedOutput.Result.([]byte); ok {
- testifyAssert.True(t, bytes.Equal(b, expectedBytes), "expected and actual byte slices should be equal")
+ assert.True(t, bytes.Equal(b, expectedBytes), "expected and actual byte slices should be equal")
}
} else if a, ok := output.Result.([]string); ok && tc.migratedOutput.Result != nil {
if expectedStringSlice, ok := tc.migratedOutput.Result.([]string); ok {
- testifyAssert.ElementsMatch(t, a, expectedStringSlice)
+ assert.ElementsMatch(t, a, expectedStringSlice)
}
} else {
- testifyAssert.Equal(t, tc.migratedOutput.Result, output.Result)
+ assert.Equal(t, tc.migratedOutput.Result, output.Result)
}
- testifyAssert.NoError(t, output.Error)
+ assert.NoError(t, output.Error)
})
}
}
@@ -3589,34 +3973,52 @@ func BenchmarkEvalHSET(b *testing.B) {
func testEvalHSET(t *testing.T, store *dstore.Store) {
tests := map[string]evalTestCase{
"wrong number of args passed": {
- setup: func() {},
- input: nil,
- output: []byte("-ERR wrong number of arguments for 'hset' command\r\n"),
+ setup: func() {},
+ input: nil,
+ migratedOutput: EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrWrongArgumentCount("HSET"),
+ },
},
"only key passed": {
- setup: func() {},
- input: []string{"key"},
- output: []byte("-ERR wrong number of arguments for 'hset' command\r\n"),
+ setup: func() {},
+ input: []string{"key"},
+ migratedOutput: EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrWrongArgumentCount("HSET"),
+ },
},
"only key and field_name passed": {
- setup: func() {},
- input: []string{"KEY", "field_name"},
- output: []byte("-ERR wrong number of arguments for 'hset' command\r\n"),
+ setup: func() {},
+ input: []string{"KEY", "field_name"},
+ migratedOutput: EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrWrongArgumentCount("HSET"),
+ },
},
"key, field and value passed": {
- setup: func() {},
- input: []string{"KEY1", "field_name", "value"},
- output: clientio.Encode(int64(1), false),
+ setup: func() {},
+ input: []string{"KEY1", "field_name", "value"},
+ migratedOutput: EvalResponse{
+ Result: int64(1),
+ Error: nil,
+ },
},
"key, field and value updated": {
- setup: func() {},
- input: []string{"KEY1", "field_name", "value_new"},
- output: clientio.Encode(int64(1), false),
+ setup: func() {},
+ input: []string{"KEY1", "field_name", "value_new"},
+ migratedOutput: EvalResponse{
+ Result: int64(1),
+ Error: nil,
+ },
},
"new set of key, field and value added": {
- setup: func() {},
- input: []string{"KEY2", "field_name_new", "value_new_new"},
- output: clientio.Encode(int64(1), false),
+ setup: func() {},
+ input: []string{"KEY2", "field_name_new", "value_new_new"},
+ migratedOutput: EvalResponse{
+ Result: int64(1),
+ Error: nil,
+ },
},
"apply with duplicate key, field and value names": {
setup: func() {
@@ -3633,8 +4035,11 @@ func testEvalHSET(t *testing.T, store *dstore.Store) {
store.Put(key, obj)
},
- input: []string{"KEY_MOCK", "mock_field_name", "mock_field_value"},
- output: clientio.Encode(int64(0), false),
+ input: []string{"KEY_MOCK", "mock_field_name", "mock_field_value"},
+ migratedOutput: EvalResponse{
+ Result: int64(0),
+ Error: nil,
+ },
},
"same key -> update value, add new field and value": {
setup: func() {
@@ -3651,12 +4056,6 @@ func testEvalHSET(t *testing.T, store *dstore.Store) {
}
store.Put(key, obj)
-
- // Check if the map is saved correctly in the store
- res, err := getValueFromHashMap(key, field, store)
-
- assert.Assert(t, err == nil)
- assert.DeepEqual(t, res, clientio.Encode(mockValue, false))
},
input: []string{
"KEY_MOCK",
@@ -3665,44 +4064,64 @@ func testEvalHSET(t *testing.T, store *dstore.Store) {
"mock_field_name_new",
"mock_value_new",
},
- output: clientio.Encode(int64(1), false),
+ migratedOutput: EvalResponse{
+ Result: int64(1),
+ Error: nil,
+ },
},
}
-
- runEvalTests(t, tests, evalHSET, store)
+ runMigratedEvalTests(t, tests, evalHSET, store)
}
func testEvalHMSET(t *testing.T, store *dstore.Store) {
tests := map[string]evalTestCase{
"wrong number of args passed": {
- setup: func() {},
- input: nil,
- output: []byte("-ERR wrong number of arguments for 'hmset' command\r\n"),
+ setup: func() {},
+ input: nil,
+ migratedOutput: EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrWrongArgumentCount("HMSET"),
+ },
},
"only key passed": {
- setup: func() {},
- input: []string{"key"},
- output: []byte("-ERR wrong number of arguments for 'hmset' command\r\n"),
+ setup: func() {},
+ input: []string{"key"},
+ migratedOutput: EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrWrongArgumentCount("HMSET"),
+ },
},
"only key and field_name passed": {
- setup: func() {},
- input: []string{"KEY", "field_name"},
- output: []byte("-ERR wrong number of arguments for 'hmset' command\r\n"),
+ setup: func() {},
+ input: []string{"KEY", "field_name"},
+ migratedOutput: EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrWrongArgumentCount("HMSET"),
+ },
},
"key, field and value passed": {
- setup: func() {},
- input: []string{"KEY1", "field_name", "value"},
- output: clientio.RespOK,
+ setup: func() {},
+ input: []string{"KEY1", "field_name", "value"},
+ migratedOutput: EvalResponse{
+ Result: clientio.OK,
+ Error: nil,
+ },
},
"key, field and value updated": {
- setup: func() {},
- input: []string{"KEY1", "field_name", "value_new"},
- output: clientio.RespOK,
+ setup: func() {},
+ input: []string{"KEY1", "field_name", "value_new"},
+ migratedOutput: EvalResponse{
+ Result: clientio.OK,
+ Error: nil,
+ },
},
"new set of key, field and value added": {
- setup: func() {},
- input: []string{"KEY2", "field_name_new", "value_new_new"},
- output: clientio.RespOK,
+ setup: func() {},
+ input: []string{"KEY2", "field_name_new", "value_new_new"},
+ migratedOutput: EvalResponse{
+ Result: clientio.OK,
+ Error: nil,
+ },
},
"apply with duplicate key, field and value names": {
setup: func() {
@@ -3719,8 +4138,11 @@ func testEvalHMSET(t *testing.T, store *dstore.Store) {
store.Put(key, obj)
},
- input: []string{"KEY_MOCK", "mock_field_name", "mock_field_value"},
- output: clientio.RespOK,
+ input: []string{"KEY_MOCK", "mock_field_name", "mock_field_value"},
+ migratedOutput: EvalResponse{
+ Result: clientio.OK,
+ Error: nil,
+ },
},
"same key -> update value, add new field and value": {
setup: func() {
@@ -3737,12 +4159,6 @@ func testEvalHMSET(t *testing.T, store *dstore.Store) {
}
store.Put(key, obj)
-
- // Check if the map is saved correctly in the store
- res, err := getValueFromHashMap(key, field, store)
-
- assert.Assert(t, err == nil)
- assert.DeepEqual(t, res, clientio.Encode(mockValue, false))
},
input: []string{
"KEY_MOCK",
@@ -3751,11 +4167,13 @@ func testEvalHMSET(t *testing.T, store *dstore.Store) {
"mock_field_name_new",
"mock_value_new",
},
- output: clientio.RespOK,
+ migratedOutput: EvalResponse{
+ Result: clientio.OK,
+ Error: nil,
+ },
},
}
-
- runEvalTests(t, tests, evalHMSET, store)
+ runMigratedEvalTests(t, tests, evalHMSET, store)
}
func testEvalHKEYS(t *testing.T, store *dstore.Store) {
@@ -3816,13 +4234,13 @@ func testEvalHKEYS(t *testing.T, store *dstore.Store) {
if responseBytes, ok := response.Result.([]byte); ok && tt.migratedOutput.Result != nil {
if expectedBytes, ok := tt.migratedOutput.Result.([]byte); ok {
// fmt.Printf("G: %v | %v\n", responseBytes, expectedBytes)
- testifyAssert.True(t, bytes.Equal(responseBytes, expectedBytes), "expected and actual byte slices should be equal")
+ assert.True(t, bytes.Equal(responseBytes, expectedBytes), "expected and actual byte slices should be equal")
}
} else {
// fmt.Printf("G1: %v | %v\n", response.Result, tt.migratedOutput.Result)
switch e := tt.migratedOutput.Result.(type) {
case []interface{}, []string:
- testifyAssert.ElementsMatch(t, e, response.Result)
+ assert.ElementsMatch(t, e, response.Result)
default:
assert.Equal(t, tt.migratedOutput.Result, response.Result)
}
@@ -3830,9 +4248,9 @@ func testEvalHKEYS(t *testing.T, store *dstore.Store) {
if tt.migratedOutput.Error != nil {
// fmt.Printf("E: %v | %v\n", response.Error, tt.migratedOutput.Error.Error())
- testifyAssert.EqualError(t, response.Error, tt.migratedOutput.Error.Error())
+ assert.EqualError(t, response.Error, tt.migratedOutput.Error.Error())
} else {
- testifyAssert.NoError(t, response.Error)
+ assert.NoError(t, response.Error)
}
})
}
@@ -4423,7 +4841,7 @@ func testEvalJSONARRPOP(t *testing.T, store *dstore.Store) {
obj := store.Get(key)
expr, err := jp.ParseString(path)
- assert.NilError(t, err, "error parsing path")
+ assert.Nil(t, err, "error parsing path")
results := expr.Get(obj.Value)
assert.Equal(t, len(results), 1)
@@ -4450,16 +4868,16 @@ func testEvalJSONARRPOP(t *testing.T, store *dstore.Store) {
if tt.migratedOutput.Result != nil {
if slice, ok := tt.migratedOutput.Result.([]interface{}); ok {
- assert.DeepEqual(t, slice, response.Result)
+ assert.Equal(t, slice, response.Result)
} else {
assert.Equal(t, tt.migratedOutput.Result, response.Result)
}
}
if tt.migratedOutput.Error != nil {
- testifyAssert.EqualError(t, response.Error, tt.migratedOutput.Error.Error())
+ assert.EqualError(t, response.Error, tt.migratedOutput.Error.Error())
} else {
- testifyAssert.NoError(t, response.Error)
+ assert.NoError(t, response.Error)
}
})
}
@@ -4837,16 +5255,16 @@ func testEvalJSONOBJKEYS(t *testing.T, store *dstore.Store) {
if b, ok := response.Result.([]byte); ok && tt.migratedOutput.Result != nil {
if expectedBytes, ok := tt.migratedOutput.Result.([]byte); ok {
- testifyAssert.True(t, bytes.Equal(b, expectedBytes), "expected and actual byte slices should be equal")
+ assert.True(t, bytes.Equal(b, expectedBytes), "expected and actual byte slices should be equal")
}
} else {
- testifyAssert.Equal(t, tt.migratedOutput.Result, response.Result)
+ assert.Equal(t, tt.migratedOutput.Result, response.Result)
}
if tt.migratedOutput.Error != nil {
- testifyAssert.EqualError(t, response.Error, tt.migratedOutput.Error.Error())
+ assert.EqualError(t, response.Error, tt.migratedOutput.Error.Error())
} else {
- testifyAssert.NoError(t, response.Error)
+ assert.NoError(t, response.Error)
}
})
}
@@ -5122,34 +5540,52 @@ func BenchmarkEvalHSETNX(b *testing.B) {
func testEvalHSETNX(t *testing.T, store *dstore.Store) {
tests := map[string]evalTestCase{
"no args passed": {
- setup: func() {},
- input: nil,
- output: []byte("-ERR wrong number of arguments for 'hsetnx' command\r\n"),
+ setup: func() {},
+ input: nil,
+ migratedOutput: EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrWrongArgumentCount("HSETNX"),
+ },
},
"only key passed": {
- setup: func() {},
- input: []string{"key"},
- output: []byte("-ERR wrong number of arguments for 'hsetnx' command\r\n"),
+ setup: func() {},
+ input: []string{"key"},
+ migratedOutput: EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrWrongArgumentCount("HSETNX"),
+ },
},
"only key and field_name passed": {
- setup: func() {},
- input: []string{"KEY", "field_name"},
- output: []byte("-ERR wrong number of arguments for 'hsetnx' command\r\n"),
+ setup: func() {},
+ input: []string{"KEY", "field_name"},
+ migratedOutput: EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrWrongArgumentCount("HSETNX"),
+ },
},
"more than one field and value passed": {
- setup: func() {},
- input: []string{"KEY", "field1", "value1", "field2", "value2"},
- output: []byte("-ERR wrong number of arguments for 'hsetnx' command\r\n"),
+ setup: func() {},
+ input: []string{"KEY", "field1", "value1", "field2", "value2"},
+ migratedOutput: EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrWrongArgumentCount("HSETNX"),
+ },
},
"key, field and value passed": {
- setup: func() {},
- input: []string{"KEY1", "field_name", "value"},
- output: clientio.Encode(int64(1), false),
+ setup: func() {},
+ input: []string{"KEY1", "field_name", "value"},
+ migratedOutput: EvalResponse{
+ Result: int64(1),
+ Error: nil,
+ },
},
"new set of key, field and value added": {
- setup: func() {},
- input: []string{"KEY2", "field_name_new", "value_new_new"},
- output: clientio.Encode(int64(1), false),
+ setup: func() {},
+ input: []string{"KEY2", "field_name_new", "value_new"},
+ migratedOutput: EvalResponse{
+ Result: int64(1),
+ Error: nil,
+ },
},
"apply with duplicate key, field and value names": {
setup: func() {
@@ -5166,14 +5602,37 @@ func testEvalHSETNX(t *testing.T, store *dstore.Store) {
store.Put(key, obj)
},
- input: []string{"KEY_MOCK", "mock_field_name", "mock_field_value_2"},
- output: clientio.Encode(int64(0), false),
+ input: []string{"KEY_MOCK", "mock_field_name", "mock_field_value_2"},
+ migratedOutput: EvalResponse{
+ Result: int64(0),
+ Error: nil,
+ },
+ },
+ "key exists, field exists but value is nil": {
+ setup: func() {
+ key := "KEY_EXISTING"
+ field := "existing_field"
+ newMap := make(HashMap)
+ newMap[field] = "existing_value"
+
+ obj := &object.Obj{
+ TypeEncoding: object.ObjTypeHashMap | object.ObjEncodingHashMap,
+ Value: newMap,
+ LastAccessedAt: uint32(time.Now().Unix()),
+ }
+
+ store.Put(key, obj)
+ },
+ input: []string{"KEY_EXISTING", "existing_field", "new_value"},
+ migratedOutput: EvalResponse{
+ Result: int64(0),
+ Error: nil,
+ },
},
}
- runEvalTests(t, tests, evalHSETNX, store)
+ runMigratedEvalTests(t, tests, evalHSETNX, store)
}
-
func TestMSETConsistency(t *testing.T) {
store := dstore.NewStore(nil, nil)
evalMSET([]string{"KEY", "VAL", "KEY2", "VAL2"}, store)
@@ -5368,10 +5827,11 @@ func testEvalSETEX(t *testing.T, store *dstore.Store) {
assert.Equal(t, "TEST_VALUE", getValue.Result)
// Check if the TTL is set correctly (should be 5 seconds or less)
- ttlValue := evalTTL([]string{"TEST_KEY"}, store)
- ttl, err := strconv.Atoi(strings.TrimPrefix(strings.TrimSpace(string(ttlValue)), ":"))
- assert.NilError(t, err, "Failed to parse TTL")
- assert.Assert(t, ttl > 0 && ttl <= 5)
+ ttlResponse := evalTTL([]string{"TEST_KEY"}, store)
+ ttl, ok := ttlResponse.Result.(uint64)
+ assert.True(t, ok, "TTL result should be an uint64")
+ assert.True(t, ttl > 0 && ttl <= 5, "TTL should be between 0 and 5 seconds")
+ assert.Nil(t, ttlResponse.Error, "TTL command should not return an error")
// Wait for the key to expire
mockTime.SetTime(mockTime.CurrTime.Add(6 * time.Second))
@@ -5394,10 +5854,11 @@ func testEvalSETEX(t *testing.T, store *dstore.Store) {
assert.Equal(t, "NEW_VALUE", getValue.Result)
// Check if the TTL is set correctly
- ttlValue := evalTTL([]string{"EXISTING_KEY"}, store)
- ttl, err := strconv.Atoi(strings.TrimPrefix(strings.TrimSpace(string(ttlValue)), ":"))
- assert.NilError(t, err, "Failed to parse TTL")
- assert.Assert(t, ttl > 0 && ttl <= 10)
+ ttlResponse := evalTTL([]string{"EXISTING_KEY"}, store)
+ ttl, ok := ttlResponse.Result.(uint64)
+ assert.True(t, ok, "TTL result should be an uint64")
+ assert.True(t, ttl > 0 && ttl <= 10, "TTL should be between 0 and 10 seconds")
+ assert.Nil(t, ttlResponse.Error, "TTL command should not return an error")
},
},
}
@@ -5416,16 +5877,16 @@ func testEvalSETEX(t *testing.T, store *dstore.Store) {
// Handle comparison for byte slices
if b, ok := response.Result.([]byte); ok && tt.migratedOutput.Result != nil {
if expectedBytes, ok := tt.migratedOutput.Result.([]byte); ok {
- testifyAssert.True(t, bytes.Equal(b, expectedBytes), "expected and actual byte slices should be equal")
+ assert.True(t, bytes.Equal(b, expectedBytes), "expected and actual byte slices should be equal")
}
} else {
assert.Equal(t, tt.migratedOutput.Result, response.Result)
}
if tt.migratedOutput.Error != nil {
- testifyAssert.EqualError(t, response.Error, tt.migratedOutput.Error.Error())
+ assert.EqualError(t, response.Error, tt.migratedOutput.Error.Error())
} else {
- testifyAssert.NoError(t, response.Error)
+ assert.NoError(t, response.Error)
}
}
})
@@ -5596,16 +6057,16 @@ func testEvalINCRBYFLOAT(t *testing.T, store *dstore.Store) {
// Handle comparison for byte slices
if b, ok := response.Result.([]byte); ok && tt.migratedOutput.Result != nil {
if expectedBytes, ok := tt.migratedOutput.Result.([]byte); ok {
- testifyAssert.True(t, bytes.Equal(b, expectedBytes), "expected and actual byte slices should be equal")
+ assert.True(t, bytes.Equal(b, expectedBytes), "expected and actual byte slices should be equal")
}
} else {
assert.Equal(t, tt.migratedOutput.Result, response.Result)
}
if tt.migratedOutput.Error != nil {
- testifyAssert.EqualError(t, response.Error, tt.migratedOutput.Error.Error())
+ assert.EqualError(t, response.Error, tt.migratedOutput.Error.Error())
} else {
- testifyAssert.NoError(t, response.Error)
+ assert.NoError(t, response.Error)
}
})
}
@@ -5646,7 +6107,7 @@ func testEvalBITOP(t *testing.T, store *dstore.Store) {
output: clientio.Encode(0, true),
validator: func(output []byte) {
expectedResult := []byte{}
- assert.DeepEqual(t, expectedResult, store.Get("dest{t}").Value.(*ByteArray).data)
+ assert.Equal(t, expectedResult, store.Get("dest{t}").Value.(*ByteArray).data)
},
},
"BITOP NOT (known string)": {
@@ -5657,7 +6118,7 @@ func testEvalBITOP(t *testing.T, store *dstore.Store) {
output: clientio.Encode(4, true),
validator: func(output []byte) {
expectedResult := []byte{0x55, 0xff, 0x00, 0xaa}
- assert.DeepEqual(t, expectedResult, store.Get("dest{t}").Value.(*ByteArray).data)
+ assert.Equal(t, expectedResult, store.Get("dest{t}").Value.(*ByteArray).data)
},
},
"BITOP where dest and target are the same key": {
@@ -5668,7 +6129,7 @@ func testEvalBITOP(t *testing.T, store *dstore.Store) {
output: clientio.Encode(4, true),
validator: func(output []byte) {
expectedResult := []byte{0x55, 0xff, 0x00, 0xaa}
- assert.DeepEqual(t, expectedResult, store.Get("s").Value.(*ByteArray).data)
+ assert.Equal(t, expectedResult, store.Get("s").Value.(*ByteArray).data)
},
},
"BITOP AND|OR|XOR don't change the string with single input key": {
@@ -5679,7 +6140,7 @@ func testEvalBITOP(t *testing.T, store *dstore.Store) {
output: clientio.Encode(3, true),
validator: func(output []byte) {
expectedResult := []byte{0x01, 0x02, 0xff}
- assert.DeepEqual(t, expectedResult, store.Get("res1{t}").Value.(*ByteArray).data)
+ assert.Equal(t, expectedResult, store.Get("res1{t}").Value.(*ByteArray).data)
},
},
"BITOP missing key is considered a stream of zero": {
@@ -5690,7 +6151,7 @@ func testEvalBITOP(t *testing.T, store *dstore.Store) {
output: clientio.Encode(3, true),
validator: func(output []byte) {
expectedResult := []byte{0x00, 0x00, 0x00}
- assert.DeepEqual(t, expectedResult, store.Get("res1{t}").Value.(*ByteArray).data)
+ assert.Equal(t, expectedResult, store.Get("res1{t}").Value.(*ByteArray).data)
},
},
"BITOP shorter keys are zero-padded to the key with max length": {
@@ -5702,7 +6163,7 @@ func testEvalBITOP(t *testing.T, store *dstore.Store) {
output: clientio.Encode(4, true),
validator: func(output []byte) {
expectedResult := []byte{0x01, 0x02, 0xff, 0x00}
- assert.DeepEqual(t, expectedResult, store.Get("res1{t}").Value.(*ByteArray).data)
+ assert.Equal(t, expectedResult, store.Get("res1{t}").Value.(*ByteArray).data)
},
},
"BITOP with non string source key": {
@@ -5789,13 +6250,13 @@ func testEvalHRANDFIELD(t *testing.T, store *dstore.Store) {
},
input: []string{"KEY_MOCK"},
newValidator: func(output interface{}) {
- assert.Assert(t, output != nil)
+ assert.True(t, output != nil)
stringSlice, ok := output.([]string)
if !ok {
- testifyAssert.Error(t, diceerrors.ErrUnexpectedType("[]string", reflect.TypeOf(output)))
+ assert.Error(t, diceerrors.ErrUnexpectedType("[]string", reflect.TypeOf(output)))
}
resultString := strings.Join(stringSlice, " ")
- assert.Assert(t,
+ assert.True(t,
resultString == "field1" || resultString == "field2",
"Unexpected field returned: %s", resultString)
},
@@ -5818,10 +6279,10 @@ func testEvalHRANDFIELD(t *testing.T, store *dstore.Store) {
},
input: []string{"KEY_MOCK", "2"},
newValidator: func(output interface{}) {
- assert.Assert(t, output != nil)
+ assert.True(t, output != nil)
stringSlice, ok := output.([]string)
if !ok {
- testifyAssert.Error(t, diceerrors.ErrUnexpectedType("[]string", reflect.TypeOf(output)))
+ assert.Error(t, diceerrors.ErrUnexpectedType("[]string", reflect.TypeOf(output)))
}
decodedResult := strings.Join(stringSlice, " ")
fields := []string{"field1", "field2", "field3"}
@@ -5833,7 +6294,7 @@ func testEvalHRANDFIELD(t *testing.T, store *dstore.Store) {
}
}
- assert.Assert(t, count == 2)
+ assert.True(t, count == 2)
},
},
"key exists with count and WITHVALUES argument": {
@@ -5854,10 +6315,10 @@ func testEvalHRANDFIELD(t *testing.T, store *dstore.Store) {
},
input: []string{"KEY_MOCK", "2", WithValues},
newValidator: func(output interface{}) {
- assert.Assert(t, output != nil)
+ assert.True(t, output != nil)
stringSlice, ok := output.([]string)
if !ok {
- testifyAssert.Error(t, diceerrors.ErrUnexpectedType("[]string", reflect.TypeOf(output)))
+ assert.Error(t, diceerrors.ErrUnexpectedType("[]string", reflect.TypeOf(output)))
}
decodedResult := strings.Join(stringSlice, " ")
fieldsAndValues := []string{"field1", "value1", "field2", "value2", "field3", "value3"}
@@ -7826,16 +8287,16 @@ func testEvalINCR(t *testing.T, store *dstore.Store) {
// Handle comparison for byte slices
if b, ok := response.Result.([]byte); ok && tt.migratedOutput.Result != nil {
if expectedBytes, ok := tt.migratedOutput.Result.([]byte); ok {
- testifyAssert.True(t, bytes.Equal(b, expectedBytes), "expected and actual byte slices should be equal")
+ assert.True(t, bytes.Equal(b, expectedBytes), "expected and actual byte slices should be equal")
}
} else {
assert.Equal(t, tt.migratedOutput.Result, response.Result)
}
if tt.migratedOutput.Error != nil {
- testifyAssert.EqualError(t, response.Error, tt.migratedOutput.Error.Error())
+ assert.EqualError(t, response.Error, tt.migratedOutput.Error.Error())
} else {
- testifyAssert.NoError(t, response.Error)
+ assert.NoError(t, response.Error)
}
})
}
@@ -7913,16 +8374,16 @@ func testEvalINCRBY(t *testing.T, store *dstore.Store) {
// Handle comparison for byte slices
if b, ok := response.Result.([]byte); ok && tt.migratedOutput.Result != nil {
if expectedBytes, ok := tt.migratedOutput.Result.([]byte); ok {
- testifyAssert.True(t, bytes.Equal(b, expectedBytes), "expected and actual byte slices should be equal")
+ assert.True(t, bytes.Equal(b, expectedBytes), "expected and actual byte slices should be equal")
}
} else {
assert.Equal(t, tt.migratedOutput.Result, response.Result)
}
if tt.migratedOutput.Error != nil {
- testifyAssert.EqualError(t, response.Error, tt.migratedOutput.Error.Error())
+ assert.EqualError(t, response.Error, tt.migratedOutput.Error.Error())
} else {
- testifyAssert.NoError(t, response.Error)
+ assert.NoError(t, response.Error)
}
})
}
@@ -8000,16 +8461,16 @@ func testEvalDECR(t *testing.T, store *dstore.Store) {
// Handle comparison for byte slices
if b, ok := response.Result.([]byte); ok && tt.migratedOutput.Result != nil {
if expectedBytes, ok := tt.migratedOutput.Result.([]byte); ok {
- testifyAssert.True(t, bytes.Equal(b, expectedBytes), "expected and actual byte slices should be equal")
+ assert.True(t, bytes.Equal(b, expectedBytes), "expected and actual byte slices should be equal")
}
} else {
assert.Equal(t, tt.migratedOutput.Result, response.Result)
}
if tt.migratedOutput.Error != nil {
- testifyAssert.EqualError(t, response.Error, tt.migratedOutput.Error.Error())
+ assert.EqualError(t, response.Error, tt.migratedOutput.Error.Error())
} else {
- testifyAssert.NoError(t, response.Error)
+ assert.NoError(t, response.Error)
}
})
}
@@ -8087,16 +8548,16 @@ func testEvalDECRBY(t *testing.T, store *dstore.Store) {
// Handle comparison for byte slices
if b, ok := response.Result.([]byte); ok && tt.migratedOutput.Result != nil {
if expectedBytes, ok := tt.migratedOutput.Result.([]byte); ok {
- testifyAssert.True(t, bytes.Equal(b, expectedBytes), "expected and actual byte slices should be equal")
+ assert.True(t, bytes.Equal(b, expectedBytes), "expected and actual byte slices should be equal")
}
} else {
assert.Equal(t, tt.migratedOutput.Result, response.Result)
}
if tt.migratedOutput.Error != nil {
- testifyAssert.EqualError(t, response.Error, tt.migratedOutput.Error.Error())
+ assert.EqualError(t, response.Error, tt.migratedOutput.Error.Error())
} else {
- testifyAssert.NoError(t, response.Error)
+ assert.NoError(t, response.Error)
}
})
}
diff --git a/internal/eval/hmap.go b/internal/eval/hmap.go
index de332eb7b..ccc507577 100644
--- a/internal/eval/hmap.go
+++ b/internal/eval/hmap.go
@@ -49,7 +49,7 @@ func hashMapBuilder(keyValuePairs []string, currentHashMap HashMap) (HashMap, in
for iter <= argLength-1 {
if iter >= argLength-1 || iter+1 > argLength-1 {
- return hmap, -1, diceerrors.NewErr(fmt.Sprintf(diceerrors.ArityErr, "HSET"))
+ return hmap, -1, diceerrors.ErrWrongArgumentCount("HSET")
}
k := keyValuePairs[iter]
@@ -65,27 +65,35 @@ func hashMapBuilder(keyValuePairs []string, currentHashMap HashMap) (HashMap, in
return hmap, numKeysNewlySet, nil
}
-func getValueFromHashMap(key, field string, store *dstore.Store) (val, err []byte) {
- var value string
-
+func getValueFromHashMap(key, field string, store *dstore.Store) *EvalResponse {
obj := store.Get(key)
-
if obj == nil {
- return clientio.RespNIL, nil
+ return &EvalResponse{
+ Result: clientio.NIL,
+ Error: nil,
+ }
}
- switch currentVal := obj.Value.(type) {
- case HashMap:
- val, present := currentVal.Get(field)
- if !present {
- return clientio.RespNIL, nil
+ hashMap, ok := obj.Value.(HashMap)
+ if !ok {
+ return &EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrWrongTypeOperation,
}
- value = *val
- default:
- return nil, diceerrors.NewErrWithFormattedMessage(diceerrors.WrongTypeErr)
}
- return clientio.Encode(value, false), nil
+ val, present := hashMap.Get(field)
+ if !present {
+ return &EvalResponse{
+ Result: clientio.NIL,
+ Error: nil,
+ }
+ }
+
+ return &EvalResponse{
+ Result: *val,
+ Error: nil,
+ }
}
func (h HashMap) incrementValue(field string, increment int64) (int64, error) {
diff --git a/internal/eval/hmap_test.go b/internal/eval/hmap_test.go
index cba318717..62acca4ad 100644
--- a/internal/eval/hmap_test.go
+++ b/internal/eval/hmap_test.go
@@ -86,19 +86,21 @@ func TestGetValueFromHashMap(t *testing.T) {
store.Put(key, obj)
- val, err := getValueFromHashMap(key, field, store)
- assert.Nil(t, err, "Expected no error when fetching an existing value from the hashmap")
- assert.Equal(t, clientio.Encode("value1", false), val, "Expected value1 to be fetched for key1 and field1")
-
- // Fetching a non-existing field (should return RESP NIL)
- val, err = getValueFromHashMap(key, "nonfield", store)
- assert.Nil(t, err, "Expected no error when fetching a non-existing value from the hashmap")
- assert.Equal(t, clientio.RespNIL, val, "Expected the value to give RespNIL")
-
- // Fetching a non-existing key (should return RESP NIL)
- val, err = getValueFromHashMap("nonkey", field, store)
- assert.Nil(t, err, "Expected no error when fetching a non-existing key from the hashmap")
- assert.Equal(t, clientio.RespNIL, val, "Expected the value to give RespNIL")
+ // Test case: Fetching an existing field
+ response := getValueFromHashMap(key, field, store)
+ assert.Nil(t, response.Error, "Expected no error when fetching an existing value from the hashmap")
+ assert.NotNil(t, response.Result, "Expected a non-nil value to be fetched for key 'key1' and field 'field1'")
+ assert.Equal(t, value, response.Result, "Expected 'value1' to be fetched for key 'key1' and field 'field1'")
+
+ // Test case: Fetching a non-existing field (should return clientio.NIL and no error)
+ response = getValueFromHashMap(key, "nonfield", store)
+ assert.Equal(t, clientio.NIL, response.Result, "Expected clientio.NIL for a non-existing field")
+ assert.Nil(t, response.Error, "Expected no error when fetching a non-existing field from the hashmap")
+
+ // Test case: Fetching a non-existing key (should return clientio.NIL and ErrKeyNotFound)
+ response = getValueFromHashMap("nonkey", field, store)
+ assert.Equal(t, clientio.NIL, response.Result, "Expected clientio.NIL for a non-existing key")
+ assert.Nil(t, response.Error, "Expected no error for a non-existing key")
}
func TestHashMapIncrementFloatValue(t *testing.T) {
diff --git a/internal/eval/store_eval.go b/internal/eval/store_eval.go
index 2a59b1e57..542b67eaa 100644
--- a/internal/eval/store_eval.go
+++ b/internal/eval/store_eval.go
@@ -19,6 +19,151 @@ import (
"github.com/ohler55/ojg/jp"
)
+// evalEXPIRE sets an expiry time(in secs) on the specified key in args
+// args should contain 2 values, key and the expiry time to be set for the key
+// The expiry time should be in integer format; if not, it returns encoded error response
+// Returns clientio.IntegerOne if expiry was set on the key successfully.
+// Once the time is lapsed, the key will be deleted automatically
+func evalEXPIRE(args []string, store *dstore.Store) *EvalResponse {
+ if len(args) <= 1 {
+ return &EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrWrongArgumentCount("EXPIRE"),
+ }
+ }
+
+ var key = args[0]
+ exDurationSec, err := strconv.ParseInt(args[1], 10, 64)
+ if err != nil {
+ return &EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrIntegerOutOfRange,
+ }
+ }
+
+ if exDurationSec < 0 || exDurationSec > maxExDuration {
+ return &EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrInvalidExpireTime("EXPIRE"),
+ }
+ }
+
+ obj := store.Get(key)
+
+ // 0 if the timeout was not set. e.g. key doesn't exist, or operation skipped due to the provided arguments
+ if obj == nil {
+ return &EvalResponse{
+ Result: clientio.IntegerZero,
+ Error: nil,
+ }
+ }
+ isExpirySet, err2 := dstore.EvaluateAndSetExpiry(args[2:], utils.AddSecondsToUnixEpoch(exDurationSec), key, store)
+
+ if isExpirySet {
+ return &EvalResponse{
+ Result: clientio.IntegerOne,
+ Error: nil,
+ }
+ } else if err2 != nil {
+ return &EvalResponse{
+ Result: nil,
+ Error: err2,
+ }
+ }
+
+ return &EvalResponse{
+ Result: clientio.IntegerZero,
+ Error: nil,
+ }
+}
+
+// evalEXPIREAT sets a expiry time(in unix-time-seconds) on the specified key in args
+// args should contain 2 values, key and the expiry time to be set for the key
+// The expiry time should be in integer format; if not, it returns encoded error response
+// Returns response.IntegerOne if expiry was set on the key successfully.
+// Once the time is lapsed, the key will be deleted automatically
+func evalEXPIREAT(args []string, store *dstore.Store) *EvalResponse {
+ if len(args) <= 1 {
+ return &EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrWrongArgumentCount("EXPIREAT"),
+ }
+ }
+
+ var key = args[0]
+ exUnixTimeSec, err := strconv.ParseInt(args[1], 10, 64)
+ if exUnixTimeSec < 0 || exUnixTimeSec > maxExDuration {
+ return &EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrInvalidExpireTime("EXPIREAT"),
+ }
+ }
+
+ if err != nil {
+ return &EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrIntegerOutOfRange,
+ }
+ }
+
+ isExpirySet, err := dstore.EvaluateAndSetExpiry(args[2:], exUnixTimeSec, key, store)
+ if isExpirySet {
+ return &EvalResponse{
+ Result: clientio.IntegerOne,
+ Error: nil,
+ }
+ } else if err != nil {
+ return &EvalResponse{
+ Result: nil,
+ Error: err,
+ }
+ }
+ return &EvalResponse{
+ Result: clientio.IntegerZero,
+ Error: nil,
+ }
+}
+
+// evalEXPIRETIME returns the absolute Unix timestamp (since January 1, 1970) in seconds at which the given key will expire
+// args should contain only 1 value, the key
+// Returns expiration Unix timestamp in seconds.
+// Returns -1 if the key exists but has no associated expiration time.
+// Returns -2 if the key does not exist.
+func evalEXPIRETIME(args []string, store *dstore.Store) *EvalResponse {
+ if len(args) != 1 {
+ return &EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrWrongArgumentCount("EXPIRETIME"),
+ }
+ }
+
+ var key = args[0]
+
+ obj := store.Get(key)
+
+ // -2 if key doesn't exist
+ if obj == nil {
+ return &EvalResponse{
+ Result: clientio.IntegerNegativeTwo,
+ Error: nil,
+ }
+ }
+
+ exTimeMili, ok := dstore.GetExpiry(obj, store)
+ // -1 if key doesn't have expiration time set
+ if !ok {
+ return &EvalResponse{
+ Result: clientio.IntegerNegativeOne,
+ Error: nil,
+ }
+ }
+
+ return &EvalResponse{
+ Result: exTimeMili / 1000,
+ Error: nil,
+ }
+}
+
// evalSET puts a new
pair in db as in the args
// args must contain key and value.
// args can also contain multiple options -
@@ -61,6 +206,12 @@ func evalSET(args []string, store *dstore.Store) *EvalResponse {
Error: diceerrors.ErrSyntax,
}
}
+ if keepttl {
+ return &EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrSyntax,
+ }
+ }
i++
if i == len(args) {
return &EvalResponse{
@@ -98,6 +249,12 @@ func evalSET(args []string, store *dstore.Store) *EvalResponse {
Error: diceerrors.ErrSyntax,
}
}
+ if keepttl {
+ return &EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrSyntax,
+ }
+ }
i++
if i == len(args) {
return &EvalResponse{
@@ -151,6 +308,12 @@ func evalSET(args []string, store *dstore.Store) *EvalResponse {
}
}
case KeepTTL:
+ if state != Uninitialized {
+ return &EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrSyntax,
+ }
+ }
keepttl = true
default:
return &EvalResponse{
@@ -211,29 +374,40 @@ func evalGET(args []string, store *dstore.Store) *EvalResponse {
switch _, oEnc := object.ExtractTypeEncoding(obj); oEnc {
case object.ObjEncodingInt:
// Value is stored as an int64, so use type assertion
- if val, ok := obj.Value.(int64); ok {
+ if IsInt64(obj.Value) {
return &EvalResponse{
- Result: val,
+ Result: obj.Value,
Error: nil,
}
- }
-
- return &EvalResponse{
- Result: nil,
- Error: diceerrors.ErrUnexpectedType("int64", obj.Value),
+ } else if IsString(obj.Value) {
+ return &EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrUnexpectedType("int64", "string"),
+ }
+ } else {
+ return &EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrUnexpectedType("int64", "unknown"),
+ }
}
case object.ObjEncodingEmbStr, object.ObjEncodingRaw:
// Value is stored as a string, use type assertion
- if val, ok := obj.Value.(string); ok {
+ if IsString(obj.Value) {
return &EvalResponse{
- Result: val,
+ Result: obj.Value,
Error: nil,
}
- }
- return &EvalResponse{
- Result: nil,
- Error: diceerrors.ErrUnexpectedType("string", obj.Value),
+ } else if IsInt64(obj.Value) {
+ return &EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrUnexpectedType("string", "int64"),
+ }
+ } else {
+ return &EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrUnexpectedType("string", "unknown"),
+ }
}
case object.ObjEncodingByteArray:
@@ -1438,6 +1612,94 @@ func evalPFMERGE(args []string, store *dstore.Store) *EvalResponse {
}
}
+// evalPTTL returns Time-to-Live in millisecs for the queried key in args
+// The key should be the only param in args else returns with an error
+// Returns RESP encoded time (in secs) remaining for the key to expire
+//
+// RESP encoded -2 stating key doesn't exist or key is expired
+// RESP encoded -1 in case no expiration is set on the key
+func evalPTTL(args []string, store *dstore.Store) *EvalResponse {
+ if len(args) != 1 {
+ return &EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrWrongArgumentCount("PTTL"),
+ }
+ }
+
+ key := args[0]
+
+ obj := store.Get(key)
+
+ if obj == nil {
+ return &EvalResponse{
+ Result: clientio.IntegerNegativeTwo,
+ Error: nil,
+ }
+ }
+
+ exp, isExpirySet := dstore.GetExpiry(obj, store)
+
+ if !isExpirySet {
+ return &EvalResponse{
+ Result: clientio.IntegerNegativeOne,
+ Error: nil,
+ }
+ }
+
+ // compute the time remaining for the key to expire and
+ // return the RESP encoded form of it
+ durationMs := exp - uint64(utils.GetCurrentTime().UnixMilli())
+ return &EvalResponse{
+ Result: durationMs,
+ Error: nil,
+ }
+}
+
+// evalTTL returns Time-to-Live in secs for the queried key in args
+// The key should be the only param in args else returns with an error
+// Returns RESP encoded time (in secs) remaining for the key to expire
+//
+// RESP encoded -2 stating key doesn't exist or key is expired
+// RESP encoded -1 in case no expiration is set on the key
+func evalTTL(args []string, store *dstore.Store) *EvalResponse {
+ if len(args) != 1 {
+ return &EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrWrongArgumentCount("TTL"),
+ }
+ }
+
+ var key = args[0]
+
+ obj := store.Get(key)
+
+ // if key does not exist, return RESP encoded -2 denoting key does not exist
+ if obj == nil {
+ return &EvalResponse{
+ Result: clientio.IntegerNegativeTwo,
+ Error: nil,
+ }
+ }
+
+ // if object exist, but no expiration is set on it then send -1
+ exp, isExpirySet := dstore.GetExpiry(obj, store)
+ if !isExpirySet {
+ return &EvalResponse{
+ Result: clientio.IntegerNegativeOne,
+ Error: nil,
+ }
+ }
+
+ // compute the time remaining for the key to expire and
+ // return the RESP encoded form of it
+ durationMs := exp - uint64(utils.GetCurrentTime().UnixMilli())
+
+ return &EvalResponse{
+ Result: durationMs / 1000,
+ Error: nil,
+ }
+}
+
// Increments the number stored at field in the hash stored at key by increment.
//
// If key does not exist, a new key holding a hash is created.
@@ -2956,6 +3218,523 @@ func evalJSONOBJKEYS(args []string, store *dstore.Store) *EvalResponse {
}
}
+// GETEX key [EX seconds | PX milliseconds | EXAT unix-time-seconds |
+// PXAT unix-time-milliseconds | PERSIST]
+// Get the value of key and optionally set its expiration.
+// GETEX is similar to GET, but is a write command with additional options.
+// The GETEX command supports a set of options that modify its behavior:
+// EX seconds -- Set the specified expire time, in seconds.
+// PX milliseconds -- Set the specified expire time, in milliseconds.
+// EXAT timestamp-seconds -- Set the specified Unix time at which the key will expire, in seconds.
+// PXAT timestamp-milliseconds -- Set the specified Unix time at which the key will expire, in milliseconds.
+// PERSIST -- Remove the time to live associated with the key.
+// The RESP value of the key is encoded and then returned
+// evalGET returns response.RespNIL if key is expired or it does not exist
+func evalGETEX(args []string, store *dstore.Store) *EvalResponse {
+ if len(args) < 1 {
+ return &EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrWrongArgumentCount("GETEX"),
+ }
+ }
+
+ var key = args[0]
+
+ var exDurationMs int64 = -1
+ var state = Uninitialized
+ var persist = false
+ for i := 1; i < len(args); i++ {
+ arg := strings.ToUpper(args[i])
+ switch arg {
+ case Ex, Px:
+ if state != Uninitialized {
+ return &EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrSyntax,
+ }
+ }
+ i++
+ if i == len(args) {
+ return &EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrSyntax,
+ }
+ }
+
+ exDuration, err := strconv.ParseInt(args[i], 10, 64)
+ if err != nil {
+ return &EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrIntegerOutOfRange,
+ }
+ }
+ if exDuration <= 0 || exDuration > maxExDuration {
+ return &EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrInvalidExpireTime("GETEX"),
+ }
+ }
+
+ // converting seconds to milliseconds
+ if arg == Ex {
+ exDuration *= 1000
+ }
+ exDurationMs = exDuration
+ state = Initialized
+
+ case Pxat, Exat:
+ if state != Uninitialized {
+ return &EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrSyntax,
+ }
+ }
+ i++
+ if i == len(args) {
+ return &EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrSyntax,
+ }
+ }
+ exDuration, err := strconv.ParseInt(args[i], 10, 64)
+ if err != nil {
+ return &EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrIntegerOutOfRange,
+ }
+ }
+
+ if exDuration < 0 || exDuration > maxExDuration {
+ return &EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrInvalidExpireTime("GETEX"),
+ }
+ }
+
+ if arg == Exat {
+ exDuration *= 1000
+ }
+ exDurationMs = exDuration - utils.GetCurrentTime().UnixMilli()
+ // If the expiry time is in the past, set exDurationMs to 0
+ // This will be used to signal immediate expiration
+ if exDurationMs < 0 {
+ exDurationMs = 0
+ }
+ state = Initialized
+
+ case Persist:
+ if state != Uninitialized {
+ return &EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrSyntax,
+ }
+ }
+ persist = true
+ state = Initialized
+ default:
+ return &EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrIntegerOutOfRange,
+ }
+ }
+ }
+
+ // Get the key from the hash table
+ obj := store.Get(key)
+
+ if obj == nil {
+ return &EvalResponse{
+ Result: clientio.NIL,
+ Error: nil,
+ }
+ }
+
+ if object.AssertType(obj.TypeEncoding, object.ObjTypeSet) == nil ||
+ object.AssertType(obj.TypeEncoding, object.ObjTypeJSON) == nil {
+ return &EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrWrongTypeOperation,
+ }
+ }
+
+ // Get EvalResponse with correct data type
+ getResp := evalGET([]string{key}, store)
+
+ // If there is an error return the error response
+ if getResp.Error != nil {
+ return getResp
+ }
+
+ if state == Initialized {
+ if persist {
+ dstore.DelExpiry(obj, store)
+ } else {
+ store.SetExpiry(obj, exDurationMs)
+ }
+ }
+
+ // return an EvalResponse with the value
+ return getResp
+}
+
+// evalGETDEL returns the value for the queried key in args
+// The key should be the only param in args
+// The RESP value of the key is encoded and then returned
+// In evalGETDEL If the key exists, it will be deleted before its value is returned.
+// evalGETDEL returns response.RespNIL if key is expired or it does not exist
+func evalGETDEL(args []string, store *dstore.Store) *EvalResponse {
+ if len(args) != 1 {
+ return &EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrWrongArgumentCount("GETDEL"),
+ }
+ }
+
+ key := args[0]
+
+ // getting the key based on previous touch value
+ obj := store.GetNoTouch(key)
+
+ // if key does not exist, return RESP encoded nil
+ if obj == nil {
+ return &EvalResponse{
+ Result: clientio.NIL,
+ Error: nil,
+ }
+ }
+
+ // If the object exists, check if it is a Set object.
+ if err := object.AssertType(obj.TypeEncoding, object.ObjTypeSet); err == nil {
+ return &EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrWrongTypeOperation,
+ }
+ }
+
+ // If the object exists, check if it is a JSON object.
+ if err := object.AssertType(obj.TypeEncoding, object.ObjTypeJSON); err == nil {
+ return &EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrWrongTypeOperation,
+ }
+ }
+
+ // Get the key from the hash table
+ objVal := store.GetDel(key)
+
+ // Decode and return the value based on its encoding
+ switch _, oEnc := object.ExtractTypeEncoding(objVal); oEnc {
+ case object.ObjEncodingInt:
+ // Value is stored as an int64, so use type assertion
+ if IsInt64(objVal.Value) {
+ return &EvalResponse{
+ Result: objVal.Value,
+ Error: nil,
+ }
+ } else if IsString(objVal.Value) {
+ return &EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrUnexpectedType("int64", "string"),
+ }
+ } else {
+ return &EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrUnexpectedType("int64", "unknown"),
+ }
+ }
+
+ case object.ObjEncodingEmbStr, object.ObjEncodingRaw:
+ // Value is stored as a string, use type assertion
+ if IsString(objVal.Value) {
+ return &EvalResponse{
+ Result: objVal.Value,
+ Error: nil,
+ }
+ } else if IsInt64(objVal.Value) {
+ return &EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrUnexpectedType("string", "int64"),
+ }
+ } else {
+ return &EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrUnexpectedType("string", "unknown"),
+ }
+ }
+
+ case object.ObjEncodingByteArray:
+ // Value is stored as a bytearray, use type assertion
+ if val, ok := objVal.Value.(*ByteArray); ok {
+ return &EvalResponse{
+ Result: string(val.data),
+ Error: nil,
+ }
+ }
+ return &EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrWrongTypeOperation,
+ }
+
+ default:
+ return &EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrWrongTypeOperation,
+ }
+ }
+}
+
+// helper function to insert key value in hashmap associated with the given hash
+func insertInHashMap(args []string, store *dstore.Store) (int64, error) {
+ key := args[0]
+
+ obj := store.Get(key)
+
+ var hashMap HashMap
+
+ if obj != nil {
+ if err := object.AssertTypeAndEncoding(obj.TypeEncoding, object.ObjTypeHashMap, object.ObjEncodingHashMap); err != nil {
+ return 0, diceerrors.ErrWrongTypeOperation
+ }
+ hashMap = obj.Value.(HashMap)
+ }
+
+ keyValuePairs := args[1:]
+
+ hashMap, numKeys, err := hashMapBuilder(keyValuePairs, hashMap)
+ if err != nil {
+ return 0, err
+ }
+
+ obj = store.NewObj(hashMap, -1, object.ObjTypeHashMap, object.ObjEncodingHashMap)
+ store.Put(key, obj)
+
+ return numKeys, nil
+}
+
+// evalHSET sets the specified fields to their
+// respective values in a hashmap stored at key
+//
+// This command overwrites the values of specified
+// fields that exist in the hash.
+//
+// If key doesn't exist, a new key holding a hash is created.
+//
+// Usage: HSET key field value [field value ...]
+func evalHSET(args []string, store *dstore.Store) *EvalResponse {
+ if len(args) < 3 {
+ return &EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrWrongArgumentCount("HSET"),
+ }
+ }
+
+ numKeys, err := insertInHashMap(args, store)
+ if err != nil {
+ return &EvalResponse{
+ Result: nil,
+ Error: err,
+ }
+ }
+
+ return &EvalResponse{
+ Result: numKeys,
+ Error: nil,
+ }
+}
+
+// evalHMSET sets the specified fields to their
+// respective values in a hashmap stored at key
+//
+// This command overwrites the values of specified
+// fields that exist in the hash.
+//
+// If key doesn't exist, a new key holding a hash is created.
+//
+// Usage: HMSET key field value [field value ...]
+func evalHMSET(args []string, store *dstore.Store) *EvalResponse {
+ if len(args) < 3 {
+ return &EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrWrongArgumentCount("HMSET"),
+ }
+ }
+
+ _, err := insertInHashMap(args, store)
+ if err != nil {
+ return &EvalResponse{
+ Result: nil,
+ Error: err,
+ }
+ }
+
+ return &EvalResponse{
+ Result: clientio.OK,
+ Error: nil,
+ }
+}
+
+// evalHMGET returns an array of values associated with the given fields,
+// in the same order as they are requested.
+// If a field does not exist, returns a corresponding nil value in the array.
+// If the key does not exist, returns an array of nil values.
+func evalHMGET(args []string, store *dstore.Store) *EvalResponse {
+ if len(args) < 2 {
+ return &EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrWrongArgumentCount("HMGET"),
+ }
+ }
+ key := args[0]
+
+ // Fetch the object from the store using the key
+ obj := store.Get(key)
+
+ // Initialize the results slice
+ results := make([]interface{}, len(args[1:]))
+
+ // If the object is nil, return empty results for all requested fields
+ if obj == nil {
+ for i := range results {
+ results[i] = nil // Return nil for non-existent fields
+ }
+ return &EvalResponse{
+ Result: results,
+ Error: nil,
+ }
+ }
+
+ // Assert that the object is of type HashMap
+ if err := object.AssertTypeAndEncoding(obj.TypeEncoding, object.ObjTypeHashMap, object.ObjEncodingHashMap); err != nil {
+ return &EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrWrongTypeOperation,
+ }
+ }
+
+ hashMap := obj.Value.(HashMap)
+
+ // Loop through the requested fields
+ for i, hmKey := range args[1:] {
+ hmValue, present := hashMap.Get(hmKey)
+ if present {
+ results[i] = *hmValue // Set the value if it exists
+ } else {
+ results[i] = nil // Set to nil if field does not exist
+ }
+ }
+
+ // Return the results and no error
+ return &EvalResponse{
+ Result: results,
+ Error: nil,
+ }
+}
+
+func evalHGET(args []string, store *dstore.Store) *EvalResponse {
+ if len(args) != 2 {
+ return &EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrWrongArgumentCount("HGET"),
+ }
+ }
+
+ key := args[0]
+ hmKey := args[1]
+
+ response := getValueFromHashMap(key, hmKey, store)
+ if response.Error != nil {
+ return &EvalResponse{
+ Result: nil,
+ Error: response.Error,
+ }
+ }
+
+ return &EvalResponse{
+ Result: response.Result,
+ Error: nil,
+ }
+}
+
+func evalHSETNX(args []string, store *dstore.Store) *EvalResponse {
+ if len(args) != 3 {
+ return &EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrWrongArgumentCount("HSETNX"),
+ }
+ }
+
+ key := args[0]
+ hmKey := args[1]
+
+ response := getValueFromHashMap(key, hmKey, store)
+ if response.Error != nil {
+ return &EvalResponse{
+ Result: nil,
+ Error: response.Error,
+ }
+ }
+
+ if response.Result != clientio.NIL {
+ return &EvalResponse{
+ Result: int64(0),
+ Error: nil,
+ }
+ }
+
+ evalHSET(args, store)
+
+ return &EvalResponse{
+ Result: int64(1),
+ Error: nil,
+ }
+}
+
+func evalHDEL(args []string, store *dstore.Store) *EvalResponse {
+ if len(args) < 2 {
+ return &EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrWrongArgumentCount("HDEL"),
+ }
+ }
+
+ key := args[0]
+ fields := args[1:]
+
+ obj := store.Get(key)
+
+ if obj == nil {
+ return &EvalResponse{
+ Result: int64(0),
+ Error: nil,
+ }
+ }
+
+ if err := object.AssertTypeAndEncoding(obj.TypeEncoding, object.ObjTypeHashMap, object.ObjEncodingHashMap); err != nil {
+ return &EvalResponse{
+ Result: nil,
+ Error: diceerrors.ErrWrongTypeOperation,
+ }
+ }
+
+ hashMap := obj.Value.(HashMap)
+ count := int64(0)
+ for _, field := range fields {
+ if _, ok := hashMap[field]; ok {
+ delete(hashMap, field)
+ count++
+ }
+ }
+
+ if count > 0 {
+ store.Put(key, obj)
+ }
+
+ return &EvalResponse{
+ Result: count,
+ Error: nil,
+ }
+}
+
// evalSADD adds one or more members to a set
// args must contain a key and one or more members to add the set
// If the set does not exist, a new set is created and members are added to it
diff --git a/internal/eval/type_asserts.go b/internal/eval/type_asserts.go
new file mode 100644
index 000000000..ef159ffba
--- /dev/null
+++ b/internal/eval/type_asserts.go
@@ -0,0 +1,13 @@
+package eval
+
+// IsInt64 checks if the variable is of type int64.
+func IsInt64(v interface{}) bool {
+ _, ok := v.(int64)
+ return ok
+}
+
+// IsString checks if the variable is of type string.
+func IsString(v interface{}) bool {
+ _, ok := v.(string)
+ return ok
+}
diff --git a/internal/server/cmd_meta.go b/internal/server/cmd_meta.go
index e84f211a2..c5f9bd4b7 100644
--- a/internal/server/cmd_meta.go
+++ b/internal/server/cmd_meta.go
@@ -50,6 +50,18 @@ var (
Cmd: "SET",
CmdType: SingleShard,
}
+ expireCmdMeta = CmdsMeta{
+ Cmd: "EXPIRE",
+ CmdType: SingleShard,
+ }
+ expireAtCmdMeta = CmdsMeta{
+ Cmd: "EXPIREAT",
+ CmdType: SingleShard,
+ }
+ expireTimeCmdMeta = CmdsMeta{
+ Cmd: "EXPIRETIME",
+ CmdType: SingleShard,
+ }
getCmdMeta = CmdsMeta{
Cmd: "GET",
CmdType: SingleShard,
@@ -151,6 +163,14 @@ var (
Cmd: "PFMERGE",
CmdType: SingleShard,
}
+ ttlCmdMeta = CmdsMeta{
+ Cmd: "TTL",
+ CmdType: SingleShard,
+ }
+ pttlCmdMeta = CmdsMeta{
+ Cmd: "PTTL",
+ CmdType: SingleShard,
+ }
jsonclearCmdMeta = CmdsMeta{
Cmd: "JSON.CLEAR",
@@ -276,6 +296,38 @@ var (
Cmd: "CMS.MERGE",
CmdType: SingleShard,
}
+ getexCmdMeta = CmdsMeta{
+ Cmd: "GETEX",
+ CmdType: SingleShard,
+ }
+ getdelCmdMeta = CmdsMeta{
+ Cmd: "GETDEL",
+ CmdType: SingleShard,
+ }
+ hsetCmdMeta = CmdsMeta{
+ Cmd: "HSET",
+ CmdType: SingleShard,
+ }
+ hgetCmdMeta = CmdsMeta{
+ Cmd: "HGET",
+ CmdType: SingleShard,
+ }
+ hsetnxCmdMeta = CmdsMeta{
+ Cmd: "HSETNX",
+ CmdType: SingleShard,
+ }
+ hdelCmdMeta = CmdsMeta{
+ Cmd: "HDEL",
+ CmdType: SingleShard,
+ }
+ hmsetCmdMeta = CmdsMeta{
+ Cmd: "HMSET",
+ CmdType: SingleShard,
+ }
+ hmgetCmdMeta = CmdsMeta{
+ Cmd: "HMGET",
+ CmdType: SingleShard,
+ }
// Metadata for multishard commands would go here.
// These commands require both breakup and gather logic.
@@ -290,6 +342,9 @@ func init() {
// Single-shard commands.
WorkerCmdsMeta["SET"] = setCmdMeta
+ WorkerCmdsMeta["EXPIRE"] = expireCmdMeta
+ WorkerCmdsMeta["EXPIREAT"] = expireAtCmdMeta
+ WorkerCmdsMeta["EXPIRETIME"] = expireTimeCmdMeta
WorkerCmdsMeta["GET"] = getCmdMeta
WorkerCmdsMeta["GETSET"] = getsetCmdMeta
WorkerCmdsMeta["SETEX"] = setexCmdMeta
@@ -340,6 +395,8 @@ func init() {
WorkerCmdsMeta["ZPOPMIN"] = zpopminCmdMeta
WorkerCmdsMeta["PFCOUNT"] = pfcountCmdMeta
WorkerCmdsMeta["PFMERGE"] = pfmergeCmdMeta
+ WorkerCmdsMeta["TTL"] = ttlCmdMeta
+ WorkerCmdsMeta["PTTL"] = pttlCmdMeta
WorkerCmdsMeta["HINCRBY"] = hincrbyCmdMeta
WorkerCmdsMeta["HINCRBYFLOAT"] = hincrbyfloatCmdMeta
WorkerCmdsMeta["HRANDFIELD"] = hrandfieldCmdMeta
@@ -360,5 +417,13 @@ func init() {
WorkerCmdsMeta["CMS.INCRBY"] = cmsIncrByCmdMeta
WorkerCmdsMeta["CMS.QUERY"] = cmsQueryCmdMeta
WorkerCmdsMeta["CMS.MERGE"] = cmsMergeCmdMeta
+ WorkerCmdsMeta["GETEX"] = getexCmdMeta
+ WorkerCmdsMeta["GETDEL"] = getdelCmdMeta
+ WorkerCmdsMeta["HSET"] = hsetCmdMeta
+ WorkerCmdsMeta["HGET"] = hgetCmdMeta
+ WorkerCmdsMeta["HSETNX"] = hsetnxCmdMeta
+ WorkerCmdsMeta["HDEL"] = hdelCmdMeta
+ WorkerCmdsMeta["HMSET"] = hmsetCmdMeta
+ WorkerCmdsMeta["HMGET"] = hmgetCmdMeta
// Additional commands (multishard, custom) can be added here as needed.
}
diff --git a/internal/server/utils/jsontype_test.go b/internal/server/utils/jsontype_test.go
index b2ecf2502..768568fe7 100644
--- a/internal/server/utils/jsontype_test.go
+++ b/internal/server/utils/jsontype_test.go
@@ -1,7 +1,7 @@
package utils
import (
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
"testing"
)
diff --git a/internal/sql/dsql_test.go b/internal/sql/dsql_test.go
index c44f15196..0976679c7 100644
--- a/internal/sql/dsql_test.go
+++ b/internal/sql/dsql_test.go
@@ -5,7 +5,7 @@ import (
"github.com/dicedb/dice/internal/server/utils"
"github.com/xwb1989/sqlparser"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func TestParseQuery(t *testing.T) {
@@ -225,16 +225,16 @@ func TestParseQuery(t *testing.T) {
if tt.wantErr {
assert.Error(t, err, tt.error)
} else {
- assert.NilError(t, err)
- assert.DeepEqual(t, tt.want.Selection, got.Selection)
- assert.DeepEqual(t, tt.want.OrderBy, got.OrderBy)
+ assert.Nil(t, err)
+ assert.Equal(t, tt.want.Selection, got.Selection)
+ assert.Equal(t, tt.want.OrderBy, got.OrderBy)
assert.Equal(t, tt.want.Limit, got.Limit)
//if tt.want.Where == nil {
// assert.Assert(t, got.Where == nil)
//} else {
- assert.Assert(t, got.Where != nil)
- assert.DeepEqual(t, tt.want.Where, got.Where)
+ assert.True(t, got.Where != nil)
+ assert.Equal(t, tt.want.Where, got.Where)
//}
}
})
@@ -279,17 +279,17 @@ func TestParseSelectExpressions(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
stmt, err := sqlparser.Parse(replaceCustomSyntax(tt.sql))
- assert.NilError(t, err)
+ assert.Nil(t, err)
selectStmt, ok := stmt.(*sqlparser.Select)
- assert.Assert(t, ok)
+ assert.True(t, ok)
got, err := parseSelectExpressions(selectStmt)
if tt.wantErr {
- assert.Assert(t, err != nil)
+ assert.True(t, err != nil)
} else {
- assert.NilError(t, err)
- assert.DeepEqual(t, tt.want, got)
+ assert.Nil(t, err)
+ assert.Equal(t, tt.want, got)
}
})
}
@@ -367,17 +367,17 @@ func TestParseOrderBy(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
stmt, err := sqlparser.Parse(replaceCustomSyntax(tt.sql))
- assert.NilError(t, err)
+ assert.Nil(t, err)
selectStmt, ok := stmt.(*sqlparser.Select)
- assert.Assert(t, ok)
+ assert.True(t, ok)
got, err := parseOrderBy(selectStmt)
if tt.wantErr {
- assert.Assert(t, err != nil)
+ assert.True(t, err != nil)
} else {
- assert.NilError(t, err)
- assert.DeepEqual(t, tt.want, got)
+ assert.Nil(t, err)
+ assert.Equal(t, tt.want, got)
}
})
}
@@ -410,16 +410,16 @@ func TestParseLimit(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
stmt, err := sqlparser.Parse(replaceCustomSyntax(tt.sql))
- assert.NilError(t, err)
+ assert.Nil(t, err)
selectStmt, ok := stmt.(*sqlparser.Select)
- assert.Assert(t, ok)
+ assert.True(t, ok)
got, err := parseLimit(selectStmt)
if tt.wantErr {
- assert.Assert(t, err != nil)
+ assert.True(t, err != nil)
} else {
- assert.NilError(t, err)
+ assert.Nil(t, err)
assert.Equal(t, tt.want, got)
}
})
diff --git a/internal/sql/executor_test.go b/internal/sql/executor_test.go
index 7feb9f4d3..918c61f95 100644
--- a/internal/sql/executor_test.go
+++ b/internal/sql/executor_test.go
@@ -1,17 +1,17 @@
package sql_test
import (
- "github.com/dicedb/dice/internal/object"
- "github.com/dicedb/dice/internal/sql"
"sort"
"testing"
+ "github.com/dicedb/dice/internal/object"
+ "github.com/dicedb/dice/internal/sql"
+
"github.com/bytedance/sonic"
"github.com/dicedb/dice/internal/server/utils"
dstore "github.com/dicedb/dice/internal/store"
+ "github.com/stretchr/testify/assert"
"github.com/xwb1989/sqlparser"
- "gotest.tools/v3/assert"
- "gotest.tools/v3/assert/cmp"
)
type keyValue struct {
@@ -47,11 +47,11 @@ func TestExecuteQueryOrderBykey(t *testing.T) {
queryString := "SELECT $key, $value WHERE $key like 'k*' ORDER BY $key ASC"
query, err := sql.ParseQuery(queryString)
- assert.NilError(t, err)
+ assert.Nil(t, err)
result, err := sql.ExecuteQuery(&query, store.GetStore())
- assert.NilError(t, err)
+ assert.Nil(t, err)
assert.Equal(t, len(result), len(simpleKVDataset))
sortedDataset := make([]keyValue, len(simpleKVDataset))
@@ -64,7 +64,7 @@ func TestExecuteQueryOrderBykey(t *testing.T) {
for i, data := range sortedDataset {
assert.Equal(t, result[i].Key, data.key)
- assert.DeepEqual(t, result[i].Value.Value, data.value)
+ assert.Equal(t, result[i].Value.Value, data.value)
}
}
@@ -74,11 +74,11 @@ func TestExecuteQueryBasicOrderByValue(t *testing.T) {
queryStr := "SELECT $key, $value WHERE $key like 'k*' ORDER BY $value ASC"
query, err := sql.ParseQuery(queryStr)
- assert.NilError(t, err)
+ assert.Nil(t, err)
result, err := sql.ExecuteQuery(&query, store.GetStore())
- assert.NilError(t, err)
+ assert.Nil(t, err)
assert.Equal(t, len(result), len(simpleKVDataset))
sortedDataset := make([]keyValue, len(simpleKVDataset))
@@ -91,7 +91,7 @@ func TestExecuteQueryBasicOrderByValue(t *testing.T) {
for i, data := range sortedDataset {
assert.Equal(t, result[i].Key, data.key)
- assert.DeepEqual(t, result[i].Value.Value, data.value)
+ assert.Equal(t, result[i].Value.Value, data.value)
}
}
@@ -101,12 +101,12 @@ func TestExecuteQueryLimit(t *testing.T) {
queryStr := "SELECT $value WHERE $key like 'k*' ORDER BY $key ASC LIMIT 3"
query, err := sql.ParseQuery(queryStr)
- assert.NilError(t, err)
+ assert.Nil(t, err)
result, err := sql.ExecuteQuery(&query, store.GetStore())
- assert.NilError(t, err)
- assert.Assert(t, cmp.Len(result, 3)) // Checks if limit is respected
+ assert.Nil(t, err)
+ assert.Equal(t, len(result), 3) // Checks if limit is respected
sortedDataset := make([]keyValue, len(simpleKVDataset))
copy(sortedDataset, simpleKVDataset)
@@ -118,7 +118,7 @@ func TestExecuteQueryLimit(t *testing.T) {
for i, data := range sortedDataset[:3] {
assert.Equal(t, result[i].Key, utils.EmptyStr)
- assert.DeepEqual(t, result[i].Value.Value, data.value)
+ assert.Equal(t, result[i].Value.Value, data.value)
}
}
@@ -128,12 +128,12 @@ func TestExecuteQueryNoMatch(t *testing.T) {
queryStr := "SELECT $key, $value WHERE $key like 'x*'"
query, err := sql.ParseQuery(queryStr)
- assert.NilError(t, err)
+ assert.Nil(t, err)
result, err := sql.ExecuteQuery(&query, store.GetStore())
- assert.NilError(t, err)
- assert.Assert(t, cmp.Len(result, 0)) // No keys match "x*"
+ assert.Nil(t, err)
+ assert.Equal(t, len(result), 0) // No keys match "x*"
}
func TestExecuteQueryWithWhere(t *testing.T) {
@@ -142,50 +142,50 @@ func TestExecuteQueryWithWhere(t *testing.T) {
t.Run("BasicWhereClause", func(t *testing.T) {
queryStr := "SELECT $key, $value WHERE $value = 'v3' AND $key like 'k*'"
query, err := sql.ParseQuery(queryStr)
- assert.NilError(t, err)
+ assert.Nil(t, err)
result, err := sql.ExecuteQuery(&query, store.GetStore())
- assert.NilError(t, err)
+ assert.Nil(t, err)
assert.Equal(t, len(result), 1, "Expected 1 result for WHERE clause")
assert.Equal(t, result[0].Key, "k3")
- assert.DeepEqual(t, result[0].Value.Value, "v3")
+ assert.Equal(t, result[0].Value.Value, "v3")
})
t.Run("EmptyResult", func(t *testing.T) {
queryStr := "SELECT $key, $value WHERE $value = 'nonexistent' AND $key like 'k*'"
query, err := sql.ParseQuery(queryStr)
- assert.NilError(t, err)
+ assert.Nil(t, err)
result, err := sql.ExecuteQuery(&query, store.GetStore())
- assert.NilError(t, err)
+ assert.Nil(t, err)
assert.Equal(t, len(result), 0, "Expected empty result for non-matching WHERE clause")
})
t.Run("ComplexWhereClause", func(t *testing.T) {
queryStr := "SELECT $key, $value WHERE $value > 'v2' AND $value < 'v5' AND $key like 'k*' ORDER BY $key ASC"
query, err := sql.ParseQuery(queryStr)
- assert.NilError(t, err)
+ assert.Nil(t, err)
result, err := sql.ExecuteQuery(&query, store.GetStore())
- assert.NilError(t, err)
+ assert.Nil(t, err)
assert.Equal(t, len(result), 2, "Expected 2 results for complex WHERE clause")
- assert.DeepEqual(t, []string{result[0].Key, result[1].Key}, []string{"k2", "k3"})
+ assert.Equal(t, []string{result[0].Key, result[1].Key}, []string{"k2", "k3"})
})
t.Run("ComparingKeyWithValue", func(t *testing.T) {
queryStr := "SELECT $key, $value WHERE $key = $value"
query, err := sql.ParseQuery(queryStr)
- assert.NilError(t, err)
+ assert.Nil(t, err)
result, err := sql.ExecuteQuery(&query, store.GetStore())
- assert.NilError(t, err)
+ assert.Nil(t, err)
assert.Equal(t, len(result), 1, "Expected 1 result for comparison between key and value")
assert.Equal(t, result[0].Key, "k")
- assert.DeepEqual(t, result[0].Value.Value, "k")
+ assert.Equal(t, result[0].Value.Value, "k")
})
}
@@ -196,7 +196,7 @@ func TestExecuteQueryWithIncompatibleTypes(t *testing.T) {
t.Run("ComparingStrWithInt", func(t *testing.T) {
queryStr := "SELECT $key, $value WHERE $value = 42 AND $key like 'k*'"
query, err := sql.ParseQuery(queryStr)
- assert.NilError(t, err)
+ assert.Nil(t, err)
_, err = sql.ExecuteQuery(&query, store.GetStore())
@@ -223,7 +223,7 @@ func TestExecuteQueryWithEdgeCases(t *testing.T) {
result, err := sql.ExecuteQuery(&query, store.GetStore())
- assert.NilError(t, err)
+ assert.Nil(t, err)
assert.Equal(t, len(result), 0, "Expected 0 results due to case sensitivity")
})
@@ -233,15 +233,15 @@ func TestExecuteQueryWithEdgeCases(t *testing.T) {
result, err := sql.ExecuteQuery(&query, store.GetStore())
- assert.NilError(t, err)
+ assert.Nil(t, err)
assert.Equal(t, len(result), 2, "Expected 2 results for WHERE clause on key")
- assert.DeepEqual(t, []string{result[0].Key, result[1].Key}, []string{"k4", "k5"})
+ assert.Equal(t, []string{result[0].Key, result[1].Key}, []string{"k4", "k5"})
})
t.Run("UnsupportedOperator", func(t *testing.T) {
queryStr := "SELECT $key, $value WHERE $value regexp '%3' AND $key like 'k*'"
query, err := sql.ParseQuery(queryStr)
- assert.NilError(t, err)
+ assert.Nil(t, err)
_, err = sql.ExecuteQuery(&query, store.GetStore())
@@ -251,11 +251,11 @@ func TestExecuteQueryWithEdgeCases(t *testing.T) {
t.Run("EmptyKeyRegex", func(t *testing.T) {
queryStr := "SELECT $key, $value WHERE $key like ''"
query, err := sql.ParseQuery(queryStr)
- assert.NilError(t, err)
+ assert.Nil(t, err)
result, err := sql.ExecuteQuery(&query, store.GetStore())
- assert.NilError(t, err)
+ assert.Nil(t, err)
assert.Equal(t, len(result), 0, "Expected no keys to be returned for empty regex")
})
}
@@ -291,97 +291,97 @@ func TestExecuteQueryWithJsonExpressionInWhere(t *testing.T) {
t.Run("BasicWhereClauseWithJSON", func(t *testing.T) {
queryStr := "SELECT $key, $value WHERE '$value.name' = 'Tom' AND $key like 'json*'"
query, err := sql.ParseQuery(queryStr)
- assert.NilError(t, err)
+ assert.Nil(t, err)
result, err := sql.ExecuteQuery(&query, store.GetStore())
- assert.NilError(t, err)
+ assert.Nil(t, err)
assert.Equal(t, len(result), 1, "Expected 1 results for WHERE clause")
assert.Equal(t, result[0].Key, "json1")
var expected, actual interface{}
- assert.NilError(t, sonic.UnmarshalString(`{"name":"Tom"}`, &expected))
- assert.NilError(t, sonic.UnmarshalString(result[0].Value.Value.(string), &actual))
- assert.DeepEqual(t, actual, expected)
+ assert.Nil(t, sonic.UnmarshalString(`{"name":"Tom"}`, &expected))
+ assert.Nil(t, sonic.UnmarshalString(result[0].Value.Value.(string), &actual))
+ assert.Equal(t, actual, expected)
})
t.Run("EmptyResult", func(t *testing.T) {
queryStr := "SELECT $key, $value WHERE '$value.name' = 'Bill' AND $key like 'json*'"
query, err := sql.ParseQuery(queryStr)
- assert.NilError(t, err)
+ assert.Nil(t, err)
result, err := sql.ExecuteQuery(&query, store.GetStore())
- assert.NilError(t, err)
+ assert.Nil(t, err)
assert.Equal(t, len(result), 0, "Expected empty result for non-matching WHERE clause")
})
t.Run("WhereClauseWithFloats", func(t *testing.T) {
queryStr := "SELECT $key, $value WHERE '$value.score' > 13.15 AND $key like 'json*'"
query, err := sql.ParseQuery(queryStr)
- assert.NilError(t, err)
+ assert.Nil(t, err)
result, err := sql.ExecuteQuery(&query, store.GetStore())
- assert.NilError(t, err)
+ assert.Nil(t, err)
assert.Equal(t, len(result), 1, "Expected 1 result for WHERE clause with floating point values")
assert.Equal(t, result[0].Key, "json2")
var expected, actual interface{}
- assert.NilError(t, sonic.UnmarshalString(`{"name":"Bob","score":18.1}`, &expected))
- assert.NilError(t, sonic.UnmarshalString(result[0].Value.Value.(string), &actual))
- assert.DeepEqual(t, actual, expected)
+ assert.Nil(t, sonic.UnmarshalString(`{"name":"Bob","score":18.1}`, &expected))
+ assert.Nil(t, sonic.UnmarshalString(result[0].Value.Value.(string), &actual))
+ assert.Equal(t, actual, expected)
})
t.Run("WhereClauseWithInteger", func(t *testing.T) {
queryStr := "SELECT $key, $value WHERE '$value.scoreInt' > 13 AND $key like 'json*'"
query, err := sql.ParseQuery(queryStr)
- assert.NilError(t, err)
+ assert.Nil(t, err)
result, err := sql.ExecuteQuery(&query, store.GetStore())
- assert.NilError(t, err)
+ assert.Nil(t, err)
assert.Equal(t, len(result), 1, "Expected 1 result for WHERE clause with integer values")
assert.Equal(t, result[0].Key, "json3")
var expected, actual interface{}
- assert.NilError(t, sonic.UnmarshalString(`{"scoreInt":20}`, &expected))
- assert.NilError(t, sonic.UnmarshalString(result[0].Value.Value.(string), &actual))
- assert.DeepEqual(t, actual, expected)
+ assert.Nil(t, sonic.UnmarshalString(`{"scoreInt":20}`, &expected))
+ assert.Nil(t, sonic.UnmarshalString(result[0].Value.Value.(string), &actual))
+ assert.Equal(t, actual, expected)
})
t.Run("NestedWhereClause", func(t *testing.T) {
queryStr := "SELECT $key, $value WHERE '$value.field1.field2.field3.score' < 13 AND $key like 'json*'"
query, err := sql.ParseQuery(queryStr)
- assert.NilError(t, err)
+ assert.Nil(t, err)
result, err := sql.ExecuteQuery(&query, store.GetStore())
- assert.NilError(t, err)
+ assert.Nil(t, err)
assert.Equal(t, len(result), 1, "Expected 1 result for WHERE clause with nested json")
assert.Equal(t, result[0].Key, "json4")
var expected, actual interface{}
- assert.NilError(t, sonic.UnmarshalString(`{"field1":{"field2":{"field3":{"score":2}}}}`, &expected))
- assert.NilError(t, sonic.UnmarshalString(result[0].Value.Value.(string), &actual))
- assert.DeepEqual(t, actual, expected)
+ assert.Nil(t, sonic.UnmarshalString(`{"field1":{"field2":{"field3":{"score":2}}}}`, &expected))
+ assert.Nil(t, sonic.UnmarshalString(result[0].Value.Value.(string), &actual))
+ assert.Equal(t, actual, expected)
})
t.Run("ComplexWhereClause", func(t *testing.T) {
queryStr := "SELECT $key, $value WHERE '$value.field1.field2.field3.score' > '$value.field1.score2' AND $key like 'json*'"
query, err := sql.ParseQuery(queryStr)
- assert.NilError(t, err)
+ assert.Nil(t, err)
result, err := sql.ExecuteQuery(&query, store.GetStore())
- assert.NilError(t, err)
+ assert.Nil(t, err)
assert.Equal(t, len(result), 1, "Expected 1 result for Complex WHERE clause expression")
assert.Equal(t, result[0].Key, "json5")
var expected, actual interface{}
- assert.NilError(t, sonic.UnmarshalString(`{"field1":{"field2":{"field3":{"score":18}},"score2":5}}`, &expected))
- assert.NilError(t, sonic.UnmarshalString(result[0].Value.Value.(string), &actual))
- assert.DeepEqual(t, actual, expected)
+ assert.Nil(t, sonic.UnmarshalString(`{"field1":{"field2":{"field3":{"score":18}},"score2":5}}`, &expected))
+ assert.Nil(t, sonic.UnmarshalString(result[0].Value.Value.(string), &actual))
+ assert.Equal(t, actual, expected)
})
}
@@ -400,11 +400,11 @@ func TestExecuteQueryWithJsonOrderBy(t *testing.T) {
t.Run("OrderBySimpleJSONField", func(t *testing.T) {
queryStr := "SELECT $key, $value WHERE $key like 'json*' ORDER BY $value.name ASC"
query, err := sql.ParseQuery(queryStr)
- assert.NilError(t, err)
+ assert.Nil(t, err)
result, err := sql.ExecuteQuery(&query, store.GetStore())
- assert.NilError(t, err)
+ assert.Nil(t, err)
assert.Equal(t, 5, len(result), "Expected 5 results")
assert.Equal(t, "json3", result[0].Key) // Alice
@@ -426,11 +426,11 @@ func TestExecuteQueryWithJsonOrderBy(t *testing.T) {
t.Run("OrderByNumericJSONField", func(t *testing.T) {
queryStr := "SELECT $key, $value WHERE $key like 'json*' ORDER BY $value.age DESC"
query, err := sql.ParseQuery(queryStr)
- assert.NilError(t, err)
+ assert.Nil(t, err)
result, err := sql.ExecuteQuery(&query, store.GetStore())
- assert.NilError(t, err)
+ assert.Nil(t, err)
assert.Equal(t, 5, len(result))
assert.Equal(t, "json5", result[0].Key) // Charlie, age 50
@@ -452,11 +452,11 @@ func TestExecuteQueryWithJsonOrderBy(t *testing.T) {
t.Run("OrderByNestedJSONField", func(t *testing.T) {
queryStr := "SELECT $key, $value WHERE $key like 'json*' ORDER BY '$value.nested.field.value' ASC"
query, err := sql.ParseQuery(queryStr)
- assert.NilError(t, err)
+ assert.Nil(t, err)
result, err := sql.ExecuteQuery(&query, store.GetStore())
- assert.NilError(t, err)
+ assert.Nil(t, err)
assert.Equal(t, 5, len(result))
assert.Equal(t, "json3", result[0].Key) // Alice, nested.field.value: 15
validateJSONStringRepresentationsAreEqual(t, jsonOrderDataset[0].value, result[0].Value.Value.(string))
@@ -477,11 +477,11 @@ func TestExecuteQueryWithJsonOrderBy(t *testing.T) {
t.Run("OrderByMixedTypes", func(t *testing.T) {
queryStr := "SELECT $key, $value WHERE $key like 'json*' ORDER BY $value.score DESC"
query, err := sql.ParseQuery(queryStr)
- assert.NilError(t, err)
+ assert.Nil(t, err)
result, err := sql.ExecuteQuery(&query, store.GetStore())
- assert.NilError(t, err)
+ assert.Nil(t, err)
// No ordering guarantees for mixed types.
assert.Equal(t, 5, len(result))
})
@@ -489,11 +489,11 @@ func TestExecuteQueryWithJsonOrderBy(t *testing.T) {
t.Run("OrderByWithWhereClause", func(t *testing.T) {
queryStr := "SELECT $key, $value WHERE $key like 'json*' AND '$value.age' > 30 ORDER BY $value.name DESC"
query, err := sql.ParseQuery(queryStr)
- assert.NilError(t, err)
+ assert.Nil(t, err)
result, err := sql.ExecuteQuery(&query, store.GetStore())
- assert.NilError(t, err)
+ assert.Nil(t, err)
assert.Equal(t, 3, len(result), "Expected 3 results (age > 30, ordered by name)")
assert.Equal(t, "json4", result[0].Key) // Eve, age 32
validateJSONStringRepresentationsAreEqual(t, jsonOrderDataset[4].value, result[0].Value.Value.(string))
@@ -508,11 +508,11 @@ func TestExecuteQueryWithJsonOrderBy(t *testing.T) {
t.Run("OrderByNonExistentField", func(t *testing.T) {
queryStr := "SELECT $key, $value WHERE $key like 'json*' ORDER BY $value.nonexistent ASC"
query, err := sql.ParseQuery(queryStr)
- assert.NilError(t, err)
+ assert.Nil(t, err)
result, err := sql.ExecuteQuery(&query, store.GetStore())
- assert.NilError(t, err)
+ assert.Nil(t, err)
// No ordering guarantees for non-existent field references.
assert.Equal(t, 5, len(result), "Expected 5 results")
})
@@ -522,9 +522,9 @@ func TestExecuteQueryWithJsonOrderBy(t *testing.T) {
func validateJSONStringRepresentationsAreEqual(t *testing.T, expectedJSONString, actualJSONString string) {
t.Helper()
var expectedValue, actualValue interface{}
- assert.NilError(t, sonic.UnmarshalString(expectedJSONString, &expectedValue))
- assert.NilError(t, sonic.UnmarshalString(actualJSONString, &actualValue))
- assert.DeepEqual(t, actualValue, expectedValue)
+ assert.Nil(t, sonic.UnmarshalString(expectedJSONString, &expectedValue))
+ assert.Nil(t, sonic.UnmarshalString(actualJSONString, &actualValue))
+ assert.Equal(t, actualValue, expectedValue)
}
// Dataset will be used for LIKE comparisons
@@ -625,10 +625,10 @@ func TestExecuteQueryWithLikeStringComparisons(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
query, err := sql.ParseQuery(tc.query)
- assert.NilError(t, err)
+ assert.Nil(t, err)
result, err := sql.ExecuteQuery(&query, store.GetStore())
- assert.NilError(t, err)
+ assert.Nil(t, err)
assert.Equal(t, len(result), tc.expectLen, "Expected %d results, got %d", tc.expectLen, len(result))
resultKeys := make([]string, len(result))
@@ -636,7 +636,7 @@ func TestExecuteQueryWithLikeStringComparisons(t *testing.T) {
resultKeys[i] = r.Key
}
- assert.DeepEqual(t, resultKeys, tc.expectKeys)
+ assert.Equal(t, resultKeys, tc.expectKeys)
})
}
}
@@ -704,10 +704,10 @@ func TestExecuteQueryWithStringNotLikeComparisons(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
query, err := sql.ParseQuery(tc.query)
- assert.NilError(t, err)
+ assert.Nil(t, err)
result, err := sql.ExecuteQuery(&query, store.GetStore())
- assert.NilError(t, err)
+ assert.Nil(t, err)
assert.Equal(t, len(result), tc.expectLen, "Expected %d results, got %d", tc.expectLen, len(result))
resultKeys := make([]string, len(result))
@@ -715,7 +715,7 @@ func TestExecuteQueryWithStringNotLikeComparisons(t *testing.T) {
resultKeys[i] = r.Key
}
- assert.DeepEqual(t, resultKeys, tc.expectKeys)
+ assert.Equal(t, resultKeys, tc.expectKeys)
})
}
}
diff --git a/internal/sql/fingerprint_test.go b/internal/sql/fingerprint_test.go
index 38fecf40c..baedca8a9 100644
--- a/internal/sql/fingerprint_test.go
+++ b/internal/sql/fingerprint_test.go
@@ -4,7 +4,7 @@ import (
"testing"
"github.com/xwb1989/sqlparser"
- "gotest.tools/v3/assert"
+ "github.com/stretchr/testify/assert"
)
func TestExpressionString(t *testing.T) {
@@ -112,7 +112,7 @@ func TestCombineOr(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
- assert.DeepEqual(t, tt.expected, combineOr(tt.a, tt.b))
+ assert.Equal(t, tt.expected, combineOr(tt.a, tt.b))
})
}
}
@@ -207,7 +207,7 @@ func TestCombineAnd(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
- assert.DeepEqual(t, tt.expected, combineAnd(tt.a, tt.b))
+ assert.Equal(t, tt.expected, combineAnd(tt.a, tt.b))
})
}
}
@@ -350,8 +350,8 @@ func TestGenerateFingerprintAndParseAstExpression(t *testing.T) {
if err != nil {
t.Fail()
}
- assert.DeepEqual(t, tt.expression, parseAstExpression(where).String())
- assert.DeepEqual(t, tt.fingerprint, generateFingerprint(where))
+ assert.Equal(t, tt.expression, parseAstExpression(where).String())
+ assert.Equal(t, tt.fingerprint, generateFingerprint(where))
}
})
}
diff --git a/internal/store/expire.go b/internal/store/expire.go
index 4f0d217a9..60181ee42 100644
--- a/internal/store/expire.go
+++ b/internal/store/expire.go
@@ -1,10 +1,20 @@
package store
import (
+ "strings"
+
+ diceerrors "github.com/dicedb/dice/internal/errors"
"github.com/dicedb/dice/internal/object"
"github.com/dicedb/dice/internal/server/utils"
)
+const (
+ NX string = "NX"
+ XX string = "XX"
+ GT string = "GT"
+ LT string = "LT"
+)
+
func hasExpired(obj *object.Obj, store *Store) bool {
exp, ok := store.expires.Get(obj)
if !ok {
@@ -62,3 +72,77 @@ func DeleteExpiredKeys(store *Store) {
}
}
}
+
+// NX: Set the expiration only if the key does not already have an expiration time.
+// XX: Set the expiration only if the key already has an expiration time.
+// GT: Set the expiration only if the new expiration time is greater than the current one.
+// LT: Set the expiration only if the new expiration time is less than the current one.
+// Returns Boolean True and error nil if expiry was set on the key successfully.
+// Returns Boolean False and error nil if conditions didn't met.
+// Returns Boolean False and error not-nil if invalid combination of subCommands or if subCommand is invalid
+func EvaluateAndSetExpiry(subCommands []string, newExpiry int64, key string,
+ store *Store) (shouldSetExpiry bool, err error) {
+ var newExpInMilli = newExpiry * 1000
+ var prevExpiry *uint64 = nil
+ var nxCmd, xxCmd, gtCmd, ltCmd bool
+
+ obj := store.Get(key)
+ // key doesn't exist
+ if obj == nil {
+ return false, nil
+ }
+ shouldSetExpiry = true
+ // if no condition exists
+ if len(subCommands) == 0 {
+ store.SetUnixTimeExpiry(obj, newExpiry)
+ return shouldSetExpiry, nil
+ }
+
+ expireTime, ok := GetExpiry(obj, store)
+ if ok {
+ prevExpiry = &expireTime
+ }
+
+ for i := range subCommands {
+ subCommand := strings.ToUpper(subCommands[i])
+
+ switch subCommand {
+ case NX:
+ nxCmd = true
+ if prevExpiry != nil {
+ shouldSetExpiry = false
+ }
+ case XX:
+ xxCmd = true
+ if prevExpiry == nil {
+ shouldSetExpiry = false
+ }
+ case GT:
+ gtCmd = true
+ if prevExpiry == nil || *prevExpiry > uint64(newExpInMilli) {
+ shouldSetExpiry = false
+ }
+ case LT:
+ ltCmd = true
+ if prevExpiry != nil && *prevExpiry < uint64(newExpInMilli) {
+ shouldSetExpiry = false
+ }
+ default:
+ return false, diceerrors.ErrGeneral("Unsupported option " + subCommands[i])
+ }
+ }
+
+ if !nxCmd && gtCmd && ltCmd {
+ return false, diceerrors.ErrGeneral("GT and LT options at the same time are not compatible")
+ }
+
+ if nxCmd && (xxCmd || gtCmd || ltCmd) {
+ return false, diceerrors.ErrGeneral("NX and XX," +
+ " GT or LT options at the same time are not compatible")
+ }
+
+ if shouldSetExpiry {
+ store.SetUnixTimeExpiry(obj, newExpiry)
+ }
+ return shouldSetExpiry, nil
+}
diff --git a/internal/watchmanager/watch_manager.go b/internal/watchmanager/watch_manager.go
index 5dae688cd..12451c654 100644
--- a/internal/watchmanager/watch_manager.go
+++ b/internal/watchmanager/watch_manager.go
@@ -107,8 +107,6 @@ func (m *Manager) handleUnsubscription(sub WatchSubscription) {
if len(clients) == 0 {
// Remove the fingerprint from tcpSubscriptionMap
delete(m.tcpSubscriptionMap, fingerprint)
- // Also remove the fingerprint from fingerprintCmdMap
- delete(m.fingerprintCmdMap, fingerprint)
}
}
@@ -123,6 +121,8 @@ func (m *Manager) handleUnsubscription(sub WatchSubscription) {
delete(m.querySubscriptionMap, key)
}
}
+ // Also remove the fingerprint from fingerprintCmdMap
+ delete(m.fingerprintCmdMap, fingerprint)
}
}
diff --git a/internal/worker/cmd_compose.go b/internal/worker/cmd_compose.go
index c42784569..5a46c4986 100644
--- a/internal/worker/cmd_compose.go
+++ b/internal/worker/cmd_compose.go
@@ -33,11 +33,10 @@ func composeRename(responses ...ops.StoreResponse) interface{} {
return clientio.OK
}
-// composeRename processes responses from multiple shards for a "Copy" operation.
+// composeCopy processes responses from multiple shards for a "Copy" operation.
// It iterates through all shard responses, checking for any errors. If an error is found
// in any shard response, it returns that error immediately. If all responses are successful,
-// it returns an "OK" response to indicate that the Rename operation succeeded across all shards.
-
+// it returns an "OK" response to indicate that the Copy operation succeeded across all shards.
func composeCopy(responses ...ops.StoreResponse) interface{} {
for idx := range responses {
if responses[idx].EvalResponse.Error != nil {
diff --git a/internal/worker/cmd_meta.go b/internal/worker/cmd_meta.go
index bc029d901..ca9cc021d 100644
--- a/internal/worker/cmd_meta.go
+++ b/internal/worker/cmd_meta.go
@@ -32,6 +32,10 @@ const (
// Watch represents a command that is used to monitor changes or events.
// This type of command listens for changes on specific keys or resources and responds accordingly.
Watch
+
+ // Unwatch represents a command that is used to stop monitoring changes or events.
+ // This type of command stops listening for changes on specific keys or resources.
+ Unwatch
)
// Global commands
@@ -43,9 +47,14 @@ const (
// Single-shard commands.
const (
+ CmdExpire = "EXPIRE"
+ CmdExpireAt = "EXPIREAT"
+ CmdExpireTime = "EXPIRETIME"
CmdSet = "SET"
CmdGet = "GET"
CmdGetSet = "GETSET"
+ CmdGetEx = "GETEX"
+ CmdGetDel = "GETDEL"
CmdJSONArrAppend = "JSON.ARRAPPEND"
CmdJSONArrLen = "JSON.ARRLEN"
CmdJSONArrPop = "JSON.ARRPOP"
@@ -71,6 +80,7 @@ const (
CmdSmembers = "SMEMBERS"
CmdGetWatch = "GET.WATCH"
+ CmdGetUnWatch = "GET.UNWATCH"
CmdZRangeWatch = "ZRANGE.WATCH"
CmdHExists = "HEXISTS"
CmdHKeys = "HKEYS"
@@ -88,6 +98,8 @@ const (
CmdPFAdd = "PFADD"
CmdPFCount = "PFCOUNT"
CmdPFMerge = "PFMERGE"
+ CmdTTL = "TTL"
+ CmdPTTL = "PTTL"
CmdIncr = "INCR"
CmdIncrBy = "INCRBY"
CmdDecr = "DECR"
@@ -112,6 +124,12 @@ const (
CmdCMSInitByProb = "CMS.INITBYPROB"
CmdCMSMerge = "CMS.MERGE"
CmdCMSIncrBy = "CMS.INCRBY"
+ CmdHSet = "HSET"
+ CmdHGet = "HGET"
+ CmdHSetnx = "HSETNX"
+ CmdHDel = "HDEL"
+ CmdHMSet = "HMSET"
+ CmdHMGet = "HMGET"
)
type CmdMeta struct {
@@ -153,12 +171,27 @@ var CommandsMeta = map[string]CmdMeta{
CmdSet: {
CmdType: SingleShard,
},
+ CmdExpire: {
+ CmdType: SingleShard,
+ },
+ CmdExpireAt: {
+ CmdType: SingleShard,
+ },
+ CmdExpireTime: {
+ CmdType: SingleShard,
+ },
CmdGet: {
CmdType: SingleShard,
},
CmdGetSet: {
CmdType: SingleShard,
},
+ CmdGetEx: {
+ CmdType: SingleShard,
+ },
+ CmdGetDel: {
+ CmdType: SingleShard,
+ },
CmdSadd: {
CmdType: SingleShard,
},
@@ -210,6 +243,12 @@ var CommandsMeta = map[string]CmdMeta{
CmdPFMerge: {
CmdType: SingleShard,
},
+ CmdTTL: {
+ CmdType: SingleShard,
+ },
+ CmdPTTL: {
+ CmdType: SingleShard,
+ },
CmdHLen: {
CmdType: SingleShard,
},
@@ -275,6 +314,24 @@ var CommandsMeta = map[string]CmdMeta{
CmdCMSMerge: {
CmdType: SingleShard,
},
+ CmdHSet: {
+ CmdType: SingleShard,
+ },
+ CmdHGet: {
+ CmdType: SingleShard,
+ },
+ CmdHSetnx: {
+ CmdType: SingleShard,
+ },
+ CmdHDel: {
+ CmdType: SingleShard,
+ },
+ CmdHMSet: {
+ CmdType: SingleShard,
+ },
+ CmdHMGet: {
+ CmdType: SingleShard,
+ },
// Custom commands.
CmdAbort: {
@@ -292,6 +349,11 @@ var CommandsMeta = map[string]CmdMeta{
CmdType: Watch,
},
+ // Unwatch commands
+ CmdGetUnWatch: {
+ CmdType: Unwatch,
+ },
+
// Sorted set commands
CmdZAdd: {
CmdType: SingleShard,
@@ -370,8 +432,8 @@ func validateCmdMeta(c string, meta CmdMeta) error {
if meta.decomposeCommand == nil || meta.composeResponse == nil {
return fmt.Errorf("multi-shard command %s must have both decomposeCommand and composeResponse implemented", c)
}
- case SingleShard, Watch, Custom:
- // No specific validations for these types currently
+ case SingleShard, Watch, Unwatch, Custom:
+ // No specific validations for these types currently
default:
return fmt.Errorf("unknown command type for %s", c)
}
diff --git a/internal/worker/worker.go b/internal/worker/worker.go
index 67f127001..304d3aced 100644
--- a/internal/worker/worker.go
+++ b/internal/worker/worker.go
@@ -8,6 +8,7 @@ import (
"fmt"
"log/slog"
"net"
+ "strconv"
"sync/atomic"
"syscall"
"time"
@@ -248,7 +249,47 @@ func (w *BaseWorker) executeCommand(ctx context.Context, diceDBCmd *cmd.DiceDBCm
}
cmdList = append(cmdList, watchCmd)
isWatchNotification = true
+
+ case Unwatch:
+ // Generate the Cmd being unwatched. All we need to do is remove the .UNWATCH suffix from the command and pass
+ // it along as is.
+ // Modify the command name to remove the .UNWATCH suffix, this will allow us to generate a consistent
+ // fingerprint (which uses the command name without the suffix)
+ diceDBCmd.Cmd = diceDBCmd.Cmd[:len(diceDBCmd.Cmd)-8]
+ watchCmd := &cmd.DiceDBCmd{
+ Cmd: diceDBCmd.Cmd,
+ Args: diceDBCmd.Args,
+ }
+ cmdList = append(cmdList, watchCmd)
+ isWatchNotification = false
+ }
+ }
+
+ // Unsubscribe Unwatch command type
+ if meta.CmdType == Unwatch {
+ // extract the fingerprint
+ command := cmdList[len(cmdList)-1]
+ fp, fperr := strconv.ParseUint(command.Args[0], 10, 32)
+ if fperr != nil {
+ err := w.ioHandler.Write(ctx, diceerrors.ErrInvalidFingerprint)
+ if err != nil {
+ return fmt.Errorf("error sending push response to client: %v", err)
+ }
+ return fperr
+ }
+
+ // send the unsubscribe request
+ w.cmdWatchSubscriptionChan <- watchmanager.WatchSubscription{
+ Subscribe: false,
+ AdhocReqChan: w.adhocReqChan,
+ Fingerprint: uint32(fp),
+ }
+
+ err := w.ioHandler.Write(ctx, "OK")
+ if err != nil {
+ return fmt.Errorf("error sending push response to client: %v", err)
}
+ return nil
}
// Scatter the broken-down commands to the appropriate shards.
diff --git a/main.go b/main.go
index 551f8ad37..20fbc109b 100644
--- a/main.go
+++ b/main.go
@@ -12,6 +12,7 @@ import (
"runtime"
"runtime/pprof"
"runtime/trace"
+ "strings"
"sync"
"syscall"
@@ -29,6 +30,13 @@ import (
"github.com/dicedb/dice/internal/worker"
)
+type configEntry struct {
+ Key string
+ Value interface{}
+}
+
+var configTable = []configEntry{}
+
func init() {
flag.StringVar(&config.Host, "host", "0.0.0.0", "host for the DiceDB server")
@@ -66,24 +74,95 @@ func init() {
slog.SetDefault(logger.New())
}
-func main() {
+func printSplash() {
fmt.Print(`
-██████╗ ██╗ ██████╗███████╗██████╗ ██████╗
-██╔══██╗██║██╔════╝██╔════╝██╔══██╗██╔══██╗
-██║ ██║██║██║ █████╗ ██║ ██║██████╔╝
-██║ ██║██║██║ ██╔══╝ ██║ ██║██╔══██╗
-██████╔╝██║╚██████╗███████╗██████╔╝██████╔╝
-╚═════╝ ╚═╝ ╚═════╝╚══════╝╚═════╝ ╚═════╝
-
-`)
- slog.Info("starting DiceDB", slog.String("version", config.DiceDBVersion))
- slog.Info("running with", slog.Int("port", config.Port))
- slog.Info("running with", slog.Bool("enable-watch", config.EnableWatch))
-
- if config.EnableProfiling {
- slog.Info("running with", slog.Bool("enable-profiling", config.EnableProfiling))
+ ██████╗ ██╗ ██████╗███████╗██████╗ ██████╗
+ ██╔══██╗██║██╔════╝██╔════╝██╔══██╗██╔══██╗
+ ██║ ██║██║██║ █████╗ ██║ ██║██████╔╝
+ ██║ ██║██║██║ ██╔══╝ ██║ ██║██╔══██╗
+ ██████╔╝██║╚██████╗███████╗██████╔╝██████╔╝
+ ╚═════╝ ╚═╝ ╚═════╝╚══════╝╚═════╝ ╚═════╝
+
+ `)
+}
+
+// configuration function used to add configuration values to the print table at the startup.
+// add entry to this function to add a new row in the startup configuration table.
+func configuration() {
+ // Add the version of the DiceDB to the configuration table
+ addEntry("Version", config.DiceDBVersion)
+
+ // Add the port number on which DiceDB is running to the configuration table
+ addEntry("Port", config.Port)
+
+ // Add whether multi-threading is enabled to the configuration table
+ addEntry("Multi Threading Enabled", config.EnableMultiThreading)
+
+ // Add the number of CPU cores available on the machine to the configuration table
+ addEntry("Cores", runtime.NumCPU())
+
+ // Conditionally add the number of shards to be used for DiceDB to the configuration table
+ if config.EnableMultiThreading {
+ if config.NumShards > 0 {
+ configTable = append(configTable, configEntry{"Shards", config.NumShards})
+ } else {
+ configTable = append(configTable, configEntry{"Shards", runtime.NumCPU()})
+ }
+ } else {
+ configTable = append(configTable, configEntry{"Shards", 1})
}
+ // Add whether the watch feature is enabled to the configuration table
+ addEntry("Watch Enabled", config.EnableWatch)
+
+ // Add whether the watch feature is enabled to the configuration table
+ addEntry("HTTP Enabled", config.EnableHTTP)
+
+ // Add whether the watch feature is enabled to the configuration table
+ addEntry("Websocket Enabled", config.EnableWebsocket)
+}
+
+func addEntry(k string, v interface{}) {
+ configTable = append(configTable, configEntry{k, v})
+}
+
+// printConfigTable prints key-value pairs in a vertical table format.
+func printConfigTable() {
+ configuration()
+
+ // Find the longest key to align the values properly
+ maxKeyLength := 0
+ maxValueLength := 20 // Default value length for alignment
+ for _, entry := range configTable {
+ if len(entry.Key) > maxKeyLength {
+ maxKeyLength = len(entry.Key)
+ }
+ if len(fmt.Sprintf("%v", entry.Value)) > maxValueLength {
+ maxValueLength = len(fmt.Sprintf("%v", entry.Value))
+ }
+ }
+
+ // Create the table header and separator line
+ fmt.Println()
+ totalWidth := maxKeyLength + maxValueLength + 7 // 7 is for spacing and pipes
+ fmt.Println(strings.Repeat("-", totalWidth))
+ fmt.Printf("| %-*s | %-*s |\n", maxKeyLength, "Configuration", maxValueLength, "Value")
+ fmt.Println(strings.Repeat("-", totalWidth))
+
+ // Print each configuration key-value pair without row lines
+ for _, entry := range configTable {
+ fmt.Printf("| %-*s | %-20v |\n", maxKeyLength, entry.Key, entry.Value)
+ }
+
+ // Final bottom line
+ fmt.Println(strings.Repeat("-", totalWidth))
+ fmt.Println()
+}
+
+func main() {
+ printSplash()
+ printConfigTable()
+
go observability.Ping()
ctx, cancel := context.WithCancel(context.Background())
@@ -95,7 +174,7 @@ func main() {
var (
queryWatchChan chan dstore.QueryWatchEvent
cmdWatchChan chan dstore.CmdWatchEvent
- cmdWatchSubscriptionChan chan watchmanager.WatchSubscription
+ cmdWatchSubscriptionChan = make(chan watchmanager.WatchSubscription)
serverErrCh = make(chan error, 2)
)
@@ -116,10 +195,8 @@ func main() {
if config.NumShards > 0 {
numShards = config.NumShards
}
- slog.Info("running with", slog.String("mode", "multi-threaded"), slog.Int("num-shards", numShards))
} else {
numShards = 1
- slog.Info("running with", slog.String("mode", "single-threaded"))
}
// The runtime.GOMAXPROCS(numShards) call limits the number of operating system