diff --git a/tests/unit/moduleapi/commandfilter.tcl b/tests/unit/moduleapi/commandfilter.tcl index bad68189a0..1faa8165f0 100644 --- a/tests/unit/moduleapi/commandfilter.tcl +++ b/tests/unit/moduleapi/commandfilter.tcl @@ -1,175 +1,22 @@ set testmodule [file normalize tests/modules/commandfilter.so] start_server {tags {"modules"}} { - r module load $testmodule log-key 0 - - test {Retain a command filter argument} { - # Retain an argument now. Later we'll try to re-read it and make sure - # it is not corrupt and that valgrind does not complain. - r rpush some-list @retain my-retained-string - r commandfilter.retained - } {my-retained-string} - - test {Command Filter handles redirected commands} { - r set mykey @log - r lrange log-key 0 -1 - } "{set mykey @log}" - - test {Command Filter can call RedisModule_CommandFilterArgDelete} { - r rpush mylist elem1 @delme elem2 - r lrange mylist 0 -1 - } {elem1 elem2} - - test {Command Filter can call RedisModule_CommandFilterArgInsert} { - r del mylist - r rpush mylist elem1 @insertbefore elem2 @insertafter elem3 - r lrange mylist 0 -1 - } {elem1 --inserted-before-- @insertbefore elem2 @insertafter --inserted-after-- elem3} - - test {Command Filter can call RedisModule_CommandFilterArgReplace} { - r del mylist - r rpush mylist elem1 @replaceme elem2 - r lrange mylist 0 -1 - } {elem1 --replaced-- elem2} - - test {Command Filter applies on RM_Call() commands} { - r del log-key - r commandfilter.ping - r lrange log-key 0 -1 - } "{ping @log}" - - test {Command Filter applies on Lua redis.call()} { - r del log-key - r eval "redis.call('ping', '@log')" 0 - r lrange log-key 0 -1 - } "{ping @log}" - - test {Command Filter applies on Lua redis.call() that calls a module} { - r del log-key - r eval "redis.call('commandfilter.ping')" 0 - r lrange log-key 0 -1 - } "{ping @log}" - - test {Command Filter strings can be retained} { - r commandfilter.retained - } {my-retained-string} - - test {Command Filter is unregistered implicitly on module unload} { - r del log-key - r module unload commandfilter - r set mykey @log - r lrange log-key 0 -1 - } {} - - r module load $testmodule log-key 0 - - test {Command Filter unregister works as expected} { - # Validate reloading succeeded - r del log-key - r set mykey @log - assert_equal "{set mykey @log}" [r lrange log-key 0 -1] - - # Unregister - r commandfilter.unregister - r del log-key - - r set mykey @log - r lrange log-key 0 -1 - } {} - - r module unload commandfilter - r module load $testmodule log-key 1 - - test {Command Filter REDISMODULE_CMDFILTER_NOSELF works as expected} { - r set mykey @log - assert_equal "{set mykey @log}" [r lrange log-key 0 -1] - - r del log-key - r commandfilter.ping - assert_equal {} [r lrange log-key 0 -1] - - r eval "redis.call('commandfilter.ping')" 0 - assert_equal {} [r lrange log-key 0 -1] - } - - test "Unload the module - commandfilter" { - assert_equal {OK} [r module unload commandfilter] - } } test {RM_CommandFilterArgInsert and script argv caching} { - # coverage for scripts calling commands that expand the argv array - # an attempt to add coverage for a possible bug in luaArgsToRedisArgv - # this test needs a fresh server so that lua_argv_size is 0. - # glibc realloc can return the same pointer even when the size changes - # still this test isn't able to trigger the issue, but we keep it anyway. - start_server {tags {"modules"}} { - r module load $testmodule log-key 0 - r del mylist - # command with 6 args - r eval {redis.call('rpush', KEYS[1], 'elem1', 'elem2', 'elem3', 'elem4')} 1 mylist - # command with 3 args that is changed to 4 - r eval {redis.call('rpush', KEYS[1], '@insertafter')} 1 mylist - # command with 6 args again - r eval {redis.call('rpush', KEYS[1], 'elem1', 'elem2', 'elem3', 'elem4')} 1 mylist - assert_equal [r lrange mylist 0 -1] {elem1 elem2 elem3 elem4 @insertafter --inserted-after-- elem1 elem2 elem3 elem4} - } } # previously, there was a bug that command filters would be rerun (which would cause args to swap back) # this test is meant to protect against that bug test {Blocking Commands don't run through command filter when reprocessed} { start_server {tags {"modules"}} { - r module load $testmodule log-key 0 - - r del list1{t} - r del list2{t} - - r lpush list2{t} a b c d e - - set rd [valkey_deferring_client] - # we're asking to pop from the left, but the command filter swaps the two arguments, - # if it didn't swap it, we would end up with e d c b a 5 (5 being the left most of the following lpush) - # but since we swap the arguments, we end up with 1 e d c b a (1 being the right most of it). - # if the command filter would run again on unblock, they would be swapped back. - $rd blmove list1{t} list2{t} left right 0 - wait_for_blocked_client - r lpush list1{t} 1 2 3 4 5 - # validate that we moved the correct element with the swapped args - assert_equal [$rd read] 1 - # validate that we moved the correct elements to the correct side of the list - assert_equal [r lpop list2{t}] 1 - - $rd close } } test {Filtering based on client id} { - start_server {tags {"modules"}} { - r module load $testmodule log-key 0 - - set rr [valkey_client] - set cid [$rr client id] - r unfilter_clientid $cid - - r rpush mylist elem1 @replaceme elem2 - assert_equal [r lrange mylist 0 -1] {elem1 --replaced-- elem2} - - r del mylist - - assert_equal [$rr rpush mylist elem1 @replaceme elem2] 3 - assert_equal [r lrange mylist 0 -1] {elem1 @replaceme elem2} - - $rr close - } } start_server {} { test {OnLoad failure will handle un-registration} { - catch {r module load $testmodule log-key 0 noload} - r set mykey @log - assert_equal [r lrange log-key 0 -1] {} - r rpush mylist elem1 @delme elem2 - assert_equal [r lrange mylist 0 -1] {elem1 @delme elem2} } } diff --git a/tests/unit/moduleapi/hooks.tcl b/tests/unit/moduleapi/hooks.tcl index c07f29c846..b08b0f1787 100644 --- a/tests/unit/moduleapi/hooks.tcl +++ b/tests/unit/moduleapi/hooks.tcl @@ -2,320 +2,9 @@ set testmodule [file normalize tests/modules/hooks.so] tags "modules" { start_server [list overrides [list loadmodule "$testmodule" appendonly yes]] { - test {Test module aof save on server start from empty} { - assert {[r hooks.event_count persistence-syncaof-start] == 1} - } - - test {Test clients connection / disconnection hooks} { - for {set j 0} {$j < 2} {incr j} { - set rd1 [valkey_deferring_client] - $rd1 close - } - assert {[r hooks.event_count client-connected] > 1} - assert {[r hooks.event_count client-disconnected] > 1} - } - - test {Test module client change event for blocked client} { - set rd [valkey_deferring_client] - # select db other than 0 - $rd select 1 - # block on key - $rd brpop foo 0 - # kill blocked client - r client kill skipme yes - # assert server is still up - assert_equal [r ping] PONG - $rd close - } - - test {Test module cron hook} { - after 100 - assert {[r hooks.event_count cron-loop] > 0} - set hz [r hooks.event_last cron-loop] - assert_equal $hz 10 - } - - test {Test module loaded / unloaded hooks} { - set othermodule [file normalize tests/modules/infotest.so] - r module load $othermodule - r module unload infotest - assert_equal [r hooks.event_last module-loaded] "infotest" - assert_equal [r hooks.event_last module-unloaded] "infotest" - } - - test {Test module aofrw hook} { - r debug populate 1000 foo 10000 ;# 10mb worth of data - r config set rdbcompression no ;# rdb progress is only checked once in 2mb - r BGREWRITEAOF - waitForBgrewriteaof r - assert_equal [string match {*module-event-persistence-aof-start*} [exec tail -20 < [srv 0 stdout]]] 1 - assert_equal [string match {*module-event-persistence-end*} [exec tail -20 < [srv 0 stdout]]] 1 - } - - test {Test module aof load and rdb/aof progress hooks} { - # create some aof tail (progress is checked only once in 1000 commands) - for {set j 0} {$j < 4000} {incr j} { - r set "bar$j" x - } - # set some configs that will cause many loading progress events during aof loading - r config set key-load-delay 500 - r config set dynamic-hz no - r config set hz 500 - r DEBUG LOADAOF - assert_equal [r hooks.event_last loading-aof-start] 0 - assert_equal [r hooks.event_last loading-end] 0 - assert {[r hooks.event_count loading-rdb-start] == 0} - assert_lessthan 2 [r hooks.event_count loading-progress-rdb] ;# comes from the preamble section - assert_lessthan 2 [r hooks.event_count loading-progress-aof] - if {$::verbose} { - puts "rdb progress events [r hooks.event_count loading-progress-rdb]" - puts "aof progress events [r hooks.event_count loading-progress-aof]" - } - } - # undo configs before next test - r config set dynamic-hz yes - r config set key-load-delay 0 - - test {Test module rdb save hook} { - # debug reload does: save, flush, load: - assert {[r hooks.event_count persistence-syncrdb-start] == 0} - assert {[r hooks.event_count loading-rdb-start] == 0} - r debug reload - assert {[r hooks.event_count persistence-syncrdb-start] == 1} - assert {[r hooks.event_count loading-rdb-start] == 1} - } - - test {Test key unlink hook} { - r set testkey1 hello - r del testkey1 - assert {[r hooks.event_count key-info-testkey1] == 1} - assert_equal [r hooks.event_last key-info-testkey1] testkey1 - r lpush testkey1 hello - r lpop testkey1 - assert {[r hooks.event_count key-info-testkey1] == 2} - assert_equal [r hooks.event_last key-info-testkey1] testkey1 - r set testkey2 world - r unlink testkey2 - assert {[r hooks.event_count key-info-testkey2] == 1} - assert_equal [r hooks.event_last key-info-testkey2] testkey2 - } - - test {Test removed key event} { - r set str abcd - r set str abcde - # For String Type value is returned - assert_equal {abcd overwritten} [r hooks.is_key_removed str] - assert_equal -1 [r hooks.pexpireat str] - - r del str - assert_equal {abcde deleted} [r hooks.is_key_removed str] - assert_equal -1 [r hooks.pexpireat str] - - # test int encoded string - r set intstr 12345678 - # incr doesn't fire event - r incr intstr - catch {[r hooks.is_key_removed intstr]} output - assert_match {ERR * removed} $output - r del intstr - assert_equal {12345679 deleted} [r hooks.is_key_removed intstr] - - catch {[r hooks.is_key_removed not-exists]} output - assert_match {ERR * removed} $output - - r hset hash f v - r hdel hash f - assert_equal {0 deleted} [r hooks.is_key_removed hash] - - r hset hash f v a b - r del hash - assert_equal {2 deleted} [r hooks.is_key_removed hash] - - r lpush list 1 - r lpop list - assert_equal {0 deleted} [r hooks.is_key_removed list] - - r lpush list 1 2 3 - r del list - assert_equal {3 deleted} [r hooks.is_key_removed list] - - r sadd set 1 - r spop set - assert_equal {0 deleted} [r hooks.is_key_removed set] - - r sadd set 1 2 3 4 - r del set - assert_equal {4 deleted} [r hooks.is_key_removed set] - - r zadd zset 1 f - r zpopmin zset - assert_equal {0 deleted} [r hooks.is_key_removed zset] - - r zadd zset 1 f 2 d - r del zset - assert_equal {2 deleted} [r hooks.is_key_removed zset] - - r xadd stream 1-1 f v - r xdel stream 1-1 - # Stream does not delete object when del entry - catch {[r hooks.is_key_removed stream]} output - assert_match {ERR * removed} $output - r del stream - assert_equal {0 deleted} [r hooks.is_key_removed stream] - - r xadd stream 2-1 f v - r del stream - assert_equal {1 deleted} [r hooks.is_key_removed stream] - - # delete key because of active expire - set size [r dbsize] - r set active-expire abcd px 1 - #ensure active expire - wait_for_condition 50 100 { - [r dbsize] == $size - } else { - fail "Active expire not trigger" - } - assert_equal {abcd expired} [r hooks.is_key_removed active-expire] - # current time is greater than pexpireat - set now [r time] - set mill [expr ([lindex $now 0]*1000)+([lindex $now 1]/1000)] - assert {$mill >= [r hooks.pexpireat active-expire]} - - # delete key because of lazy expire - r debug set-active-expire 0 - r set lazy-expire abcd px 1 - after 10 - r get lazy-expire - assert_equal {abcd expired} [r hooks.is_key_removed lazy-expire] - set now [r time] - set mill [expr ([lindex $now 0]*1000)+([lindex $now 1]/1000)] - assert {$mill >= [r hooks.pexpireat lazy-expire]} - r debug set-active-expire 1 - - # delete key not yet expired - set now [r time] - set expireat [expr ([lindex $now 0]*1000)+([lindex $now 1]/1000)+1000000] - r set not-expire abcd pxat $expireat - r del not-expire - assert_equal {abcd deleted} [r hooks.is_key_removed not-expire] - assert_equal $expireat [r hooks.pexpireat not-expire] - - # Test key evict - set used [expr {[s used_memory] - [s mem_not_counted_for_evict]}] - set limit [expr {$used+100*1024}] - set old_policy [lindex [r config get maxmemory-policy] 1] - r config set maxmemory $limit - # We set policy volatile-random, so only keys with ttl will be evicted - r config set maxmemory-policy volatile-random - r setex volatile-key 10000 x - # We use SETBIT here, so we can set a big key and get the used_memory - # bigger than maxmemory. Next command will evict volatile keys. We - # can't use SET, as SET uses big input buffer, so it will fail. - r setbit big-key 1600000 0 ;# this will consume 200kb - r getbit big-key 0 - assert_equal {x evicted} [r hooks.is_key_removed volatile-key] - r config set maxmemory-policy $old_policy - r config set maxmemory 0 - } {OK} {needs:debug} - - test {Test flushdb hooks} { - r flushdb - assert_equal [r hooks.event_last flush-start] 9 - assert_equal [r hooks.event_last flush-end] 9 - r flushall - assert_equal [r hooks.event_last flush-start] -1 - assert_equal [r hooks.event_last flush-end] -1 - } - - # replication related tests - set master [srv 0 client] - set master_host [srv 0 host] - set master_port [srv 0 port] - start_server {} { - r module load $testmodule - set replica [srv 0 client] - set replica_host [srv 0 host] - set replica_port [srv 0 port] - $replica replicaof $master_host $master_port - - wait_replica_online $master - - test {Test master link up hook} { - assert_equal [r hooks.event_count masterlink-up] 1 - assert_equal [r hooks.event_count masterlink-down] 0 - } - - test {Test role-replica hook} { - assert_equal [r hooks.event_count role-replica] 1 - assert_equal [r hooks.event_count role-master] 0 - assert_equal [r hooks.event_last role-replica] [s 0 master_host] - } - - test {Test replica-online hook} { - assert_equal [r -1 hooks.event_count replica-online] 1 - assert_equal [r -1 hooks.event_count replica-offline] 0 - } - - test {Test master link down hook} { - r client kill type master - assert_equal [r hooks.event_count masterlink-down] 1 - - wait_for_condition 50 100 { - [string match {*master_link_status:up*} [r info replication]] - } else { - fail "Replica didn't reconnect" - } - - assert_equal [r hooks.event_count masterlink-down] 1 - assert_equal [r hooks.event_count masterlink-up] 2 - } - - wait_for_condition 50 10 { - [string match {*master_link_status:up*} [r info replication]] - } else { - fail "Can't turn the instance into a replica" - } - - $replica replicaof no one - - test {Test role-master hook} { - assert_equal [r hooks.event_count role-replica] 1 - assert_equal [r hooks.event_count role-master] 1 - assert_equal [r hooks.event_last role-master] {} - } - - test {Test replica-offline hook} { - assert_equal [r -1 hooks.event_count replica-online] 2 - assert_equal [r -1 hooks.event_count replica-offline] 2 - } - # get the replica stdout, to be used by the next test - set replica_stdout [srv 0 stdout] - } - - test {Test swapdb hooks} { - r swapdb 0 10 - assert_equal [r hooks.event_last swapdb-first] 0 - assert_equal [r hooks.event_last swapdb-second] 10 - } - - test {Test configchange hooks} { - r config set rdbcompression no - assert_equal [r hooks.event_last config-change-count] 1 - assert_equal [r hooks.event_last config-change-first] rdbcompression - } - - # look into the log file of the server that just exited - test {Test shutdown hook} { - assert_equal [string match {*module-event-shutdown*} [exec tail -5 < $replica_stdout]] 1 - } } - start_server {} { test {OnLoad failure will handle un-registration} { - catch {r module load $testmodule noload} - r flushall - r ping } } } diff --git a/tests/unit/moduleapi/keyspace_events.tcl b/tests/unit/moduleapi/keyspace_events.tcl index 9c1cfa8ba4..eb76a691a9 100644 --- a/tests/unit/moduleapi/keyspace_events.tcl +++ b/tests/unit/moduleapi/keyspace_events.tcl @@ -3,116 +3,10 @@ set testmodule [file normalize tests/modules/keyspace_events.so] tags "modules" { start_server [list overrides [list loadmodule "$testmodule"]] { - test {Test loaded key space event} { - r set x 1 - r hset y f v - r lpush z 1 2 3 - r sadd p 1 2 3 - r zadd t 1 f1 2 f2 - r xadd s * f v - r debug reload - assert_equal {1 x} [r keyspace.is_key_loaded x] - assert_equal {1 y} [r keyspace.is_key_loaded y] - assert_equal {1 z} [r keyspace.is_key_loaded z] - assert_equal {1 p} [r keyspace.is_key_loaded p] - assert_equal {1 t} [r keyspace.is_key_loaded t] - assert_equal {1 s} [r keyspace.is_key_loaded s] - } - - test {Nested multi due to RM_Call} { - r del multi - r del lua - - r set x 1 - r set x_copy 1 - r keyspace.del_key_copy x - r keyspace.incr_case1 x - r keyspace.incr_case2 x - r keyspace.incr_case3 x - assert_equal {} [r get multi] - assert_equal {} [r get lua] - r get x - } {3} - - test {Nested multi due to RM_Call, with client MULTI} { - r del multi - r del lua - - r set x 1 - r set x_copy 1 - r multi - r keyspace.del_key_copy x - r keyspace.incr_case1 x - r keyspace.incr_case2 x - r keyspace.incr_case3 x - r exec - assert_equal {1} [r get multi] - assert_equal {} [r get lua] - r get x - } {3} - - test {Nested multi due to RM_Call, with EVAL} { - r del multi - r del lua - - r set x 1 - r set x_copy 1 - r eval { - redis.pcall('keyspace.del_key_copy', KEYS[1]) - redis.pcall('keyspace.incr_case1', KEYS[1]) - redis.pcall('keyspace.incr_case2', KEYS[1]) - redis.pcall('keyspace.incr_case3', KEYS[1]) - } 1 x - assert_equal {} [r get multi] - assert_equal {1} [r get lua] - r get x - } {3} - - test {Test module key space event} { - r keyspace.notify x - assert_equal {1 x} [r keyspace.is_module_key_notified x] - } - - test "Keyspace notifications: module events test" { - r config set notify-keyspace-events Kd - r del x - set rd1 [valkey_deferring_client] - assert_equal {1} [psubscribe $rd1 *] - r keyspace.notify x - assert_equal {pmessage * __keyspace@9__:x notify} [$rd1 read] - $rd1 close - } - - test {Test expired key space event} { - set prev_expired [s expired_keys] - r set exp 1 PX 10 - wait_for_condition 100 10 { - [s expired_keys] eq $prev_expired + 1 - } else { - fail "key not expired" - } - assert_equal [r get testkeyspace:expired] 1 - } - - test "Unload the module - testkeyspace" { - assert_equal {OK} [r module unload testkeyspace] - } - - test "Verify RM_StringDMA with expiration are not causing invalid memory access" { - assert_equal {OK} [r set x 1 EX 1] - } } start_server {} { test {OnLoad failure will handle un-registration} { - catch {r module load $testmodule noload} - r set x 1 - r hset y f v - r lpush z 1 2 3 - r sadd p 1 2 3 - r zadd t 1 f1 2 f2 - r xadd s * f v - r ping } } } diff --git a/tests/unit/moduleapi/moduleconfigs.tcl b/tests/unit/moduleapi/moduleconfigs.tcl index 44f994d2d0..c828ab8042 100644 --- a/tests/unit/moduleapi/moduleconfigs.tcl +++ b/tests/unit/moduleapi/moduleconfigs.tcl @@ -159,65 +159,9 @@ start_server {tags {"modules"}} { } test {test config rewrite with dynamic load} { - #translates to: super \0secret password - r module loadex $testmodule CONFIG moduleconfigs.string \x73\x75\x70\x65\x72\x20\x00\x73\x65\x63\x72\x65\x74\x20\x70\x61\x73\x73\x77\x6f\x72\x64 ARGS - assert_not_equal [lsearch [lmap x [r module list] {dict get $x name}] moduleconfigs] -1 - assert_equal [r config get moduleconfigs.string] "moduleconfigs.string {super \0secret password}" - r config set moduleconfigs.mutable_bool yes - r config set moduleconfigs.memory_numeric 750 - r config set moduleconfigs.enum two - r config set moduleconfigs.flags "four two" - r config rewrite - restart_server 0 true false - # Ensure configs we rewrote are present and that the conf file is readable - assert_equal [r config get moduleconfigs.mutable_bool] "moduleconfigs.mutable_bool yes" - assert_equal [r config get moduleconfigs.memory_numeric] "moduleconfigs.memory_numeric 750" - assert_equal [r config get moduleconfigs.string] "moduleconfigs.string {super \0secret password}" - assert_equal [r config get moduleconfigs.enum] "moduleconfigs.enum two" - assert_equal [r config get moduleconfigs.flags] "moduleconfigs.flags {two four}" - assert_equal [r config get moduleconfigs.numeric] "moduleconfigs.numeric -1" - r module unload moduleconfigs - } - - test {test multiple modules with configs} { - r module load $testmodule - r module loadex $testmoduletwo CONFIG configs.test yes - assert_equal [r config get moduleconfigs.mutable_bool] "moduleconfigs.mutable_bool yes" - assert_equal [r config get moduleconfigs.immutable_bool] "moduleconfigs.immutable_bool no" - assert_equal [r config get moduleconfigs.memory_numeric] "moduleconfigs.memory_numeric 1024" - assert_equal [r config get moduleconfigs.string] "moduleconfigs.string {secret password}" - assert_equal [r config get moduleconfigs.enum] "moduleconfigs.enum one" - assert_equal [r config get moduleconfigs.numeric] "moduleconfigs.numeric -1" - assert_equal [r config get configs.test] "configs.test yes" - r config set moduleconfigs.mutable_bool no - r config set moduleconfigs.string nice - r config set moduleconfigs.enum two - r config set configs.test no - assert_equal [r config get moduleconfigs.mutable_bool] "moduleconfigs.mutable_bool no" - assert_equal [r config get moduleconfigs.string] "moduleconfigs.string nice" - assert_equal [r config get moduleconfigs.enum] "moduleconfigs.enum two" - assert_equal [r config get configs.test] "configs.test no" - r config rewrite - # test we can load from conf file with multiple different modules. - restart_server 0 true false - assert_equal [r config get moduleconfigs.mutable_bool] "moduleconfigs.mutable_bool no" - assert_equal [r config get moduleconfigs.string] "moduleconfigs.string nice" - assert_equal [r config get moduleconfigs.enum] "moduleconfigs.enum two" - assert_equal [r config get configs.test] "configs.test no" - r module unload moduleconfigs - r module unload configs } test {test 1.module load 2.config rewrite 3.module unload 4.config rewrite works} { - # Configs need to be removed from the old config file in this case. - r module loadex $testmodule CONFIG moduleconfigs.memory_numeric 500 ARGS - assert_not_equal [lsearch [lmap x [r module list] {dict get $x name}] moduleconfigs] -1 - r config rewrite - r module unload moduleconfigs - r config rewrite - restart_server 0 true false - # Ensure configs we rewrote are no longer present - assert_equal [r config get moduleconfigs.*] "" } test {startup moduleconfigs} { # No loadmodule directive