Unverified Commit d2b5a579 authored by Oran Agra's avatar Oran Agra Committed by GitHub
Browse files

Merge pull request #10355 from oranagra/release-7.0-rc2

Release 7.0 RC2
parents d5915a16 10dc57ab
...@@ -294,6 +294,31 @@ start_server {tags {"geo"}} { ...@@ -294,6 +294,31 @@ start_server {tags {"geo"}} {
r georadiusbymember nyc "wtc one" 7 km r georadiusbymember nyc "wtc one" 7 km
} {{wtc one} {union square} {central park n/q/r} 4545 {lic market}} } {{wtc one} {union square} {central park n/q/r} 4545 {lic market}}
test {GEORADIUSBYMEMBER search areas contain satisfied points in oblique direction} {
r del k1
r geoadd k1 -0.15307903289794921875 85 n1 0.3515625 85.00019260486917005437 n2
set ret1 [r GEORADIUSBYMEMBER k1 n1 4891.94 m]
assert_equal $ret1 {n1 n2}
r zrem k1 n1 n2
r geoadd k1 -4.95211958885192871094 85 n3 11.25 85.0511 n4
set ret2 [r GEORADIUSBYMEMBER k1 n3 156544 m]
assert_equal $ret2 {n3 n4}
r zrem k1 n3 n4
r geoadd k1 -45 65.50900022111811438208 n5 90 85.0511 n6
set ret3 [r GEORADIUSBYMEMBER k1 n5 5009431 m]
assert_equal $ret3 {n5 n6}
}
test {GEORADIUSBYMEMBER crossing pole search} {
r del k1
r geoadd k1 45 65 n1 -135 85.05 n2
set ret [r GEORADIUSBYMEMBER k1 n1 5009431 m]
assert_equal $ret {n1 n2}
}
test {GEOSEARCH FROMMEMBER simple (sorted)} { test {GEOSEARCH FROMMEMBER simple (sorted)} {
r geosearch nyc frommember "wtc one" bybox 14 14 km r geosearch nyc frommember "wtc one" bybox 14 14 km
} {{wtc one} {union square} {central park n/q/r} 4545 {lic market} q4} } {{wtc one} {union square} {central park n/q/r} 4545 {lic market} q4}
......
start_server {tags {"info and its relative command"}} {
test "info command with at most one sub command" {
foreach arg {"" "all" "default" "everything"} {
if {$arg == ""} {
set info [r 0 info]
} else {
set info [r 0 info $arg]
}
assert { [string match "*redis_version*" $info] }
assert { [string match "*used_cpu_user*" $info] }
assert { ![string match "*sentinel_tilt*" $info] }
assert { [string match "*used_memory*" $info] }
if {$arg == "" || $arg == "default"} {
assert { ![string match "*rejected_calls*" $info] }
} else {
assert { [string match "*rejected_calls*" $info] }
}
}
}
test "info command with one sub-section" {
set info [r info cpu]
assert { [string match "*used_cpu_user*" $info] }
assert { ![string match "*sentinel_tilt*" $info] }
assert { ![string match "*used_memory*" $info] }
set info [r info sentinel]
assert { ![string match "*sentinel_tilt*" $info] }
assert { ![string match "*used_memory*" $info] }
set info [r info commandSTATS] ;# test case insensitive compare
assert { ![string match "*used_memory*" $info] }
assert { [string match "*rejected_calls*" $info] }
}
test "info command with multiple sub-sections" {
set info [r info cpu sentinel]
assert { [string match "*used_cpu_user*" $info] }
assert { ![string match "*sentinel_tilt*" $info] }
assert { ![string match "*master_repl_offset*" $info] }
set info [r info cpu all]
assert { [string match "*used_cpu_user*" $info] }
assert { ![string match "*sentinel_tilt*" $info] }
assert { [string match "*used_memory*" $info] }
assert { [string match "*master_repl_offset*" $info] }
assert { [string match "*rejected_calls*" $info] }
# check that we didn't get the same info twice
assert { ![string match "*used_cpu_user_children*used_cpu_user_children*" $info] }
set info [r info cpu default]
assert { [string match "*used_cpu_user*" $info] }
assert { ![string match "*sentinel_tilt*" $info] }
assert { [string match "*used_memory*" $info] }
assert { [string match "*master_repl_offset*" $info] }
assert { ![string match "*rejected_calls*" $info] }
# check that we didn't get the same info twice
assert { ![string match "*used_cpu_user_children*used_cpu_user_children*" $info] }
}
}
...@@ -238,6 +238,7 @@ start_server {tags {"info" "external:skip"}} { ...@@ -238,6 +238,7 @@ start_server {tags {"info" "external:skip"}} {
assert_equal [s total_error_replies] 1 assert_equal [s total_error_replies] 1
r config resetstat r config resetstat
assert_match {} [errorstat OOM] assert_match {} [errorstat OOM]
r config set maxmemory 0
} }
test {errorstats: rejected call by authorization error} { test {errorstats: rejected call by authorization error} {
...@@ -253,6 +254,25 @@ start_server {tags {"info" "external:skip"}} { ...@@ -253,6 +254,25 @@ start_server {tags {"info" "external:skip"}} {
assert_equal [s total_error_replies] 1 assert_equal [s total_error_replies] 1
r config resetstat r config resetstat
assert_match {} [errorstat NOPERM] assert_match {} [errorstat NOPERM]
r auth default ""
} }
test {errorstats: blocking commands} {
r config resetstat
set rd [redis_deferring_client]
$rd client id
set rd_id [$rd read]
r del list1{t}
$rd blpop list1{t} 0
wait_for_blocked_client
r client unblock $rd_id error
assert_error {UNBLOCKED*} {$rd read}
assert_match {*count=1*} [errorstat UNBLOCKED]
assert_match {*calls=1,*,rejected_calls=0,failed_calls=1} [cmdstat blpop]
assert_equal [s total_error_replies] 1
$rd close
}
} }
} }
...@@ -33,6 +33,15 @@ start_server {tags {"introspection"}} { ...@@ -33,6 +33,15 @@ start_server {tags {"introspection"}} {
assert_match {} [cmdstat zadd] assert_match {} [cmdstat zadd]
} {} {needs:config-resetstat} } {} {needs:config-resetstat}
test {errors stats for GEOADD} {
r config resetstat
# make sure geo command will failed
r set foo 1
assert_error {WRONGTYPE Operation against a key holding the wrong kind of value*} {r GEOADD foo 0 0 bar}
assert_match {*calls=1*,rejected_calls=0,failed_calls=1*} [cmdstat geoadd]
assert_match {} [cmdstat zadd]
} {} {needs:config-resetstat}
test {command stats for EXPIRE} { test {command stats for EXPIRE} {
r config resetstat r config resetstat
r SET foo bar r SET foo bar
...@@ -81,6 +90,13 @@ start_server {tags {"introspection"}} { ...@@ -81,6 +90,13 @@ start_server {tags {"introspection"}} {
assert_equal {key} [r command getkeys get key] assert_equal {key} [r command getkeys get key]
} }
test {COMMAND GETKEYSANDFLAGS} {
assert_equal {{k1 {OW update}}} [r command getkeysandflags set k1 v1]
assert_equal {{k1 {OW update}} {k2 {OW update}}} [r command getkeysandflags mset k1 v1 k2 v2]
assert_equal {{k1 {RW access delete}} {k2 {RW insert}}} [r command getkeysandflags LMOVE k1 k2 left right]
assert_equal {{k1 {RO access}} {k2 {OW update}}} [r command getkeysandflags sort k1 store k2]
}
test {COMMAND GETKEYS MEMORY USAGE} { test {COMMAND GETKEYS MEMORY USAGE} {
assert_equal {key} [r command getkeys memory usage key] assert_equal {key} [r command getkeys memory usage key]
} }
......
...@@ -7,7 +7,7 @@ start_server {tags {"introspection"}} { ...@@ -7,7 +7,7 @@ start_server {tags {"introspection"}} {
test {CLIENT LIST} { test {CLIENT LIST} {
r client list r client list
} {id=* addr=*:* laddr=*:* fd=* name=* age=* idle=* flags=N db=* sub=0 psub=0 multi=-1 qbuf=26 qbuf-free=* argv-mem=* multi-mem=0 obl=0 oll=0 omem=0 tot-mem=* events=r cmd=client|list user=* redir=-1 resp=2*} } {id=* addr=*:* laddr=*:* fd=* name=* age=* idle=* flags=N db=* sub=0 psub=0 multi=-1 qbuf=26 qbuf-free=* argv-mem=* multi-mem=0 rbs=* rbp=* obl=0 oll=0 omem=0 tot-mem=* events=r cmd=client|list user=* redir=-1 resp=2*}
test {CLIENT LIST with IDs} { test {CLIENT LIST with IDs} {
set myid [r client id] set myid [r client id]
...@@ -17,7 +17,7 @@ start_server {tags {"introspection"}} { ...@@ -17,7 +17,7 @@ start_server {tags {"introspection"}} {
test {CLIENT INFO} { test {CLIENT INFO} {
r client info r client info
} {id=* addr=*:* laddr=*:* fd=* name=* age=* idle=* flags=N db=* sub=0 psub=0 multi=-1 qbuf=26 qbuf-free=* argv-mem=* multi-mem=0 obl=0 oll=0 omem=0 tot-mem=* events=r cmd=client|info user=* redir=-1 resp=2*} } {id=* addr=*:* laddr=*:* fd=* name=* age=* idle=* flags=N db=* sub=0 psub=0 multi=-1 qbuf=26 qbuf-free=* argv-mem=* multi-mem=0 rbs=* rbp=* obl=0 oll=0 omem=0 tot-mem=* events=r cmd=client|info user=* redir=-1 resp=2*}
test {CLIENT KILL with illegal arguments} { test {CLIENT KILL with illegal arguments} {
assert_error "ERR wrong number of arguments for 'client|kill' command" {r client kill} assert_error "ERR wrong number of arguments for 'client|kill' command" {r client kill}
......
...@@ -158,6 +158,88 @@ start_server {tags {"defrag external:skip"} overrides {appendonly yes auto-aof-r ...@@ -158,6 +158,88 @@ start_server {tags {"defrag external:skip"} overrides {appendonly yes auto-aof-r
r config set appendonly no r config set appendonly no
r config set key-load-delay 0 r config set key-load-delay 0
test "Active defrag eval scripts" {
r flushdb
r script flush sync
r config resetstat
r config set hz 100
r config set activedefrag no
r config set active-defrag-threshold-lower 5
r config set active-defrag-cycle-min 65
r config set active-defrag-cycle-max 75
r config set active-defrag-ignore-bytes 1500kb
r config set maxmemory 0
set n 50000
# Populate memory with interleaving script-key pattern of same size
set dummy_script "--[string repeat x 400]\nreturn "
set rd [redis_deferring_client]
for {set j 0} {$j < $n} {incr j} {
set val "$dummy_script[format "%06d" $j]"
$rd script load $val
$rd set k$j $val
}
for {set j 0} {$j < $n} {incr j} {
$rd read ; # Discard script load replies
$rd read ; # Discard set replies
}
after 120 ;# serverCron only updates the info once in 100ms
if {$::verbose} {
puts "used [s allocator_allocated]"
puts "rss [s allocator_active]"
puts "frag [s allocator_frag_ratio]"
puts "frag_bytes [s allocator_frag_bytes]"
}
assert_lessthan [s allocator_frag_ratio] 1.05
# Delete all the keys to create fragmentation
for {set j 0} {$j < $n} {incr j} { $rd del k$j }
for {set j 0} {$j < $n} {incr j} { $rd read } ; # Discard del replies
$rd close
after 120 ;# serverCron only updates the info once in 100ms
if {$::verbose} {
puts "used [s allocator_allocated]"
puts "rss [s allocator_active]"
puts "frag [s allocator_frag_ratio]"
puts "frag_bytes [s allocator_frag_bytes]"
}
assert_morethan [s allocator_frag_ratio] 1.4
catch {r config set activedefrag yes} e
if {[r config get activedefrag] eq "activedefrag yes"} {
# wait for the active defrag to start working (decision once a second)
wait_for_condition 50 100 {
[s active_defrag_running] ne 0
} else {
fail "defrag not started."
}
# wait for the active defrag to stop working
wait_for_condition 500 100 {
[s active_defrag_running] eq 0
} else {
after 120 ;# serverCron only updates the info once in 100ms
puts [r info memory]
puts [r memory malloc-stats]
fail "defrag didn't stop."
}
# test the the fragmentation is lower
after 120 ;# serverCron only updates the info once in 100ms
if {$::verbose} {
puts "used [s allocator_allocated]"
puts "rss [s allocator_active]"
puts "frag [s allocator_frag_ratio]"
puts "frag_bytes [s allocator_frag_bytes]"
}
assert_lessthan_equal [s allocator_frag_ratio] 1.05
}
# Flush all script to make sure we don't crash after defragging them
r script flush sync
} {OK}
test "Active defrag big keys" { test "Active defrag big keys" {
r flushdb r flushdb
r config resetstat r config resetstat
......
...@@ -26,6 +26,12 @@ start_server {tags {"modules acl"}} { ...@@ -26,6 +26,12 @@ start_server {tags {"modules acl"}} {
catch {r aclcheck.set.check.key "*" v 5} e catch {r aclcheck.set.check.key "*" v 5} e
assert_match "*DENIED KEY*" $e assert_match "*DENIED KEY*" $e
assert_equal [r aclcheck.set.check.key "~" x 5] OK
assert_equal [r aclcheck.set.check.key "~" y 5] OK
assert_equal [r aclcheck.set.check.key "~" z 5] OK
catch {r aclcheck.set.check.key "~" v 5} e
assert_match "*DENIED KEY*" $e
assert_equal [r aclcheck.set.check.key "W" y 5] OK assert_equal [r aclcheck.set.check.key "W" y 5] OK
catch {r aclcheck.set.check.key "W" v 5} e catch {r aclcheck.set.check.key "W" v 5} e
assert_match "*DENIED KEY*" $e assert_match "*DENIED KEY*" $e
......
...@@ -180,6 +180,37 @@ start_server {tags {"modules"}} { ...@@ -180,6 +180,37 @@ start_server {tags {"modules"}} {
assert_no_match "*name=myclient*" $clients assert_no_match "*name=myclient*" $clients
} }
test {module client error stats} {
r config resetstat
# simple module command that replies with string error
assert_error "NULL reply returned" {r do_rm_call hgetalllll}
assert_equal [errorrstat NULL r] {count=1}
# module command that replies with string error from bg thread
assert_error "NULL reply returned" {r do_bg_rm_call hgetalllll}
assert_equal [errorrstat NULL r] {count=2}
# module command that returns an arity error
r do_rm_call set x x
assert_error "ERR wrong number of arguments for 'do_rm_call' command" {r do_rm_call}
assert_equal [errorrstat ERR r] {count=1}
# RM_Call that propagates an error
assert_error "WRONGTYPE*" {r do_rm_call hgetall x}
assert_equal [errorrstat WRONGTYPE r] {count=1}
assert_match {*calls=1,*,rejected_calls=0,failed_calls=1} [cmdrstat hgetall r]
# RM_Call from bg thread that propagates an error
assert_error "WRONGTYPE*" {r do_bg_rm_call hgetall x}
assert_equal [errorrstat WRONGTYPE r] {count=2}
assert_match {*calls=2,*,rejected_calls=0,failed_calls=2} [cmdrstat hgetall r]
assert_equal [s total_error_replies] 5
assert_match {*calls=4,*,rejected_calls=0,failed_calls=3} [cmdrstat do_rm_call r]
assert_match {*calls=2,*,rejected_calls=0,failed_calls=2} [cmdrstat do_bg_rm_call r]
}
test "Unload the module - blockedclient" { test "Unload the module - blockedclient" {
assert_equal {OK} [r module unload blockedclient] assert_equal {OK} [r module unload blockedclient]
} }
......
set testmodule [file normalize tests/modules/cmdintrospection.so]
start_server {tags {"modules"}} {
r module load $testmodule
# cmdintrospection.xadd mimics XADD with regards to how
# what COMMAND exposes. There are two differences:
#
# 1. cmdintrospection.xadd (and all module commands) do not have ACL categories
# 2. cmdintrospection.xadd's `group` is "module"
#
# This tests verify that, apart from the above differences, the output of
# COMMAND INFO and COMMAND DOCS are identical for the two commands.
test "Module command introspection via COMMAND INFO" {
set redis_reply [lindex [r command info xadd] 0]
set module_reply [lindex [r command info cmdintrospection.xadd] 0]
for {set i 1} {$i < [llength $redis_reply]} {incr i} {
if {$i == 2} {
# Remove the "module" flag
set mylist [lindex $module_reply $i]
set idx [lsearch $mylist "module"]
set mylist [lreplace $mylist $idx $idx]
lset module_reply $i $mylist
}
if {$i == 6} {
# Skip ACL categories
continue
}
assert_equal [lindex $redis_reply $i] [lindex $module_reply $i]
}
}
test "Module command introspection via COMMAND DOCS" {
set redis_reply [dict create {*}[lindex [r command docs xadd] 1]]
set module_reply [dict create {*}[lindex [r command docs cmdintrospection.xadd] 1]]
# Compare the maps. We need to pop "group" first.
dict unset redis_reply group
dict unset module_reply group
assert_equal $redis_reply $module_reply
}
}
set testmodule [file normalize tests/modules/getchannels.so]
start_server {tags {"modules"}} {
r module load $testmodule
# Channels are currently used to just validate ACLs, so test them here
r ACL setuser testuser +@all resetchannels &channel &pattern*
test "module getchannels-api with literals - ACL" {
assert_equal "OK" [r ACL DRYRUN testuser getchannels.command subscribe literal channel subscribe literal pattern1]
assert_equal "OK" [r ACL DRYRUN testuser getchannels.command publish literal channel publish literal pattern1]
assert_equal "OK" [r ACL DRYRUN testuser getchannels.command unsubscribe literal channel unsubscribe literal pattern1]
assert_equal "This user has no permissions to access the 'nopattern1' channel" [r ACL DRYRUN testuser getchannels.command subscribe literal channel subscribe literal nopattern1]
assert_equal "This user has no permissions to access the 'nopattern1' channel" [r ACL DRYRUN testuser getchannels.command publish literal channel subscribe literal nopattern1]
assert_equal "OK" [r ACL DRYRUN testuser getchannels.command unsubscribe literal channel unsubscribe literal nopattern1]
assert_equal "This user has no permissions to access the 'otherchannel' channel" [r ACL DRYRUN testuser getchannels.command subscribe literal otherchannel subscribe literal pattern1]
assert_equal "This user has no permissions to access the 'otherchannel' channel" [r ACL DRYRUN testuser getchannels.command publish literal otherchannel subscribe literal pattern1]
assert_equal "OK" [r ACL DRYRUN testuser getchannels.command unsubscribe literal otherchannel unsubscribe literal pattern1]
}
test "module getchannels-api with patterns - ACL" {
assert_equal "OK" [r ACL DRYRUN testuser getchannels.command subscribe pattern pattern*]
assert_equal "OK" [r ACL DRYRUN testuser getchannels.command publish pattern pattern*]
assert_equal "OK" [r ACL DRYRUN testuser getchannels.command unsubscribe pattern pattern*]
assert_equal "This user has no permissions to access the 'pattern1' channel" [r ACL DRYRUN testuser getchannels.command subscribe pattern pattern1 subscribe pattern pattern*]
assert_equal "This user has no permissions to access the 'pattern1' channel" [r ACL DRYRUN testuser getchannels.command publish pattern pattern1 subscribe pattern pattern*]
assert_equal "OK" [r ACL DRYRUN testuser getchannels.command unsubscribe pattern pattern1 unsubscribe pattern pattern*]
assert_equal "This user has no permissions to access the 'otherpattern*' channel" [r ACL DRYRUN testuser getchannels.command subscribe pattern otherpattern* subscribe pattern pattern*]
assert_equal "This user has no permissions to access the 'otherpattern*' channel" [r ACL DRYRUN testuser getchannels.command publish pattern otherpattern* subscribe pattern pattern*]
assert_equal "OK" [r ACL DRYRUN testuser getchannels.command unsubscribe pattern otherpattern* unsubscribe pattern pattern*]
}
test "Unload the module - getchannels" {
assert_equal {OK} [r module unload getchannels]
}
}
...@@ -16,32 +16,64 @@ start_server {tags {"modules"}} { ...@@ -16,32 +16,64 @@ start_server {tags {"modules"}} {
r command getkeys getkeys.command arg1 arg2 key key1 arg3 key key2 key key3 r command getkeys getkeys.command arg1 arg2 key key1 arg3 key key2 key key3
} {key1 key2 key3} } {key1 key2 key3}
test {COMMAND GETKEYS correctly reports a movable keys module command using flags} {
r command getkeys getkeys.command_with_flags arg1 arg2 key key1 arg3 key key2 key key3
} {key1 key2 key3}
test {COMMAND GETKEYSANDFLAGS correctly reports a movable keys module command not using flags} {
r command getkeysandflags getkeys.command arg1 arg2 key key1 arg3 key key2
} {{key1 {RW access update}} {key2 {RW access update}}}
test {COMMAND GETKEYSANDFLAGS correctly reports a movable keys module command using flags} {
r command getkeysandflags getkeys.command_with_flags arg1 arg2 key key1 arg3 key key2 key key3
} {{key1 {RO access}} {key2 {RO access}} {key3 {RO access}}}
test {RM_GetCommandKeys on non-existing command} { test {RM_GetCommandKeys on non-existing command} {
catch {r getkeys.introspect non-command key1 key2} e catch {r getkeys.introspect 0 non-command key1 key2} e
set _ $e set _ $e
} {*ENOENT*} } {*ENOENT*}
test {RM_GetCommandKeys on built-in fixed keys command} { test {RM_GetCommandKeys on built-in fixed keys command} {
r getkeys.introspect set key1 value1 r getkeys.introspect 0 set key1 value1
} {key1} } {key1}
test {RM_GetCommandKeys on built-in fixed keys command with flags} {
r getkeys.introspect 1 set key1 value1
} {{key1 OW}}
test {RM_GetCommandKeys on EVAL} { test {RM_GetCommandKeys on EVAL} {
r getkeys.introspect eval "" 4 key1 key2 key3 key4 arg1 arg2 r getkeys.introspect 0 eval "" 4 key1 key2 key3 key4 arg1 arg2
} {key1 key2 key3 key4} } {key1 key2 key3 key4}
test {RM_GetCommandKeys on a movable keys module command} { test {RM_GetCommandKeys on a movable keys module command} {
r getkeys.introspect getkeys.command arg1 arg2 key key1 arg3 key key2 key key3 r getkeys.introspect 0 getkeys.command arg1 arg2 key key1 arg3 key key2 key key3
} {key1 key2 key3} } {key1 key2 key3}
test {RM_GetCommandKeys on a non-movable module command} { test {RM_GetCommandKeys on a non-movable module command} {
r getkeys.introspect getkeys.fixed arg1 key1 key2 key3 arg2 r getkeys.introspect 0 getkeys.fixed arg1 key1 key2 key3 arg2
} {key1 key2 key3} } {key1 key2 key3}
test {RM_GetCommandKeys with bad arity} { test {RM_GetCommandKeys with bad arity} {
catch {r getkeys.introspect set key} e catch {r getkeys.introspect 0 set key} e
set _ $e set _ $e
} {*EINVAL*} } {*EINVAL*}
# user that can only read from "read" keys, write to "write" keys, and read+write to "RW" keys
r ACL setuser testuser +@all %R~read* %W~write* %RW~rw*
test "module getkeys-api - ACL" {
# legacy triple didn't provide flags, so they require both read and write
assert_equal "OK" [r ACL DRYRUN testuser getkeys.command key rw]
assert_equal "This user has no permissions to access the 'read' key" [r ACL DRYRUN testuser getkeys.command key read]
assert_equal "This user has no permissions to access the 'write' key" [r ACL DRYRUN testuser getkeys.command key write]
}
test "module getkeys-api with flags - ACL" {
assert_equal "OK" [r ACL DRYRUN testuser getkeys.command_with_flags key rw]
assert_equal "OK" [r ACL DRYRUN testuser getkeys.command_with_flags key read]
assert_equal "This user has no permissions to access the 'write' key" [r ACL DRYRUN testuser getkeys.command_with_flags key write]
}
test "Unload the module - getkeys" { test "Unload the module - getkeys" {
assert_equal {OK} [r module unload getkeys] assert_equal {OK} [r module unload getkeys]
} }
......
...@@ -64,7 +64,7 @@ start_server {tags {"modules"}} { ...@@ -64,7 +64,7 @@ start_server {tags {"modules"}} {
} }
test {module info one module} { test {module info one module} {
set info [r info INFOTEST] set info [r info INFOtest] ;# test case insensitive compare
# info all does not contain modules # info all does not contain modules
assert { [string match "*Spanish*" $info] } assert { [string match "*Spanish*" $info] }
assert { ![string match "*used_memory*" $info] } assert { ![string match "*used_memory*" $info] }
...@@ -72,7 +72,7 @@ start_server {tags {"modules"}} { ...@@ -72,7 +72,7 @@ start_server {tags {"modules"}} {
} {-2} } {-2}
test {module info one section} { test {module info one section} {
set info [r info INFOTEST_SPANISH] set info [r info INFOtest_SpanisH] ;# test case insensitive compare
assert { ![string match "*used_memory*" $info] } assert { ![string match "*used_memory*" $info] }
assert { ![string match "*Italian*" $info] } assert { ![string match "*Italian*" $info] }
assert { ![string match "*infotest_global*" $info] } assert { ![string match "*infotest_global*" $info] }
...@@ -90,6 +90,31 @@ start_server {tags {"modules"}} { ...@@ -90,6 +90,31 @@ start_server {tags {"modules"}} {
assert_match {*infotest_unsafe_field:value=1*} $info assert_match {*infotest_unsafe_field:value=1*} $info
} }
test {module info multiply sections without all, everything, default keywords} {
set info [r info replication INFOTEST]
assert { [string match "*Spanish*" $info] }
assert { ![string match "*used_memory*" $info] }
assert { [string match "*repl_offset*" $info] }
}
test {module info multiply sections with all keyword and modules} {
set info [r info all modules]
assert { [string match "*cluster*" $info] }
assert { [string match "*cmdstat_info*" $info] }
assert { [string match "*infotest_global*" $info] }
}
test {module info multiply sections with everything keyword} {
set info [r info replication everything cpu]
assert { [string match "*client_recent*" $info] }
assert { [string match "*cmdstat_info*" $info] }
assert { [string match "*Italian*" $info] }
# check that we didn't get the same info twice
assert { ![string match "*used_cpu_user_children*used_cpu_user_children*" $info] }
assert { ![string match "*Italian*Italian*" $info] }
field $info infotest_dos
} {2}
test "Unload the module - infotest" { test "Unload the module - infotest" {
assert_equal {OK} [r module unload infotest] assert_equal {OK} [r module unload infotest]
} }
......
set testmodule [file normalize tests/modules/keyspecs.so] set testmodule [file normalize tests/modules/keyspecs.so]
if 0 { ; # Test suite disabled due to planned API changes
start_server {tags {"modules"}} { start_server {tags {"modules"}} {
r module load $testmodule r module load $testmodule
test "Module key specs: Legacy" { test "Module key specs: No spec, only legacy triple" {
set reply [lindex [r command info kspec.legacy] 0] set reply [lindex [r command info kspec.none] 0]
# Verify (first, last, step) # Verify (first, last, step) and not movablekeys
assert_equal [lindex $reply 2] {module}
assert_equal [lindex $reply 3] 1
assert_equal [lindex $reply 4] -1
assert_equal [lindex $reply 5] 2
# Verify key-spec auto-generated from the legacy triple
set keyspecs [lindex $reply 8]
assert_equal [llength $keyspecs] 1
assert_equal [lindex $keyspecs 0] {flags {RW access update variable_flags} begin_search {type index spec {index 1}} find_keys {type range spec {lastkey -1 keystep 2 limit 0}}}
assert_equal [r command getkeys kspec.none key1 val1 key2 val2] {key1 key2}
}
test "Module key specs: Two ranges" {
set reply [lindex [r command info kspec.tworanges] 0]
# Verify (first, last, step) and not movablekeys
assert_equal [lindex $reply 2] {module}
assert_equal [lindex $reply 3] 1 assert_equal [lindex $reply 3] 1
assert_equal [lindex $reply 4] 2 assert_equal [lindex $reply 4] 2
assert_equal [lindex $reply 5] 1 assert_equal [lindex $reply 5] 1
...@@ -14,24 +28,41 @@ start_server {tags {"modules"}} { ...@@ -14,24 +28,41 @@ start_server {tags {"modules"}} {
set keyspecs [lindex $reply 8] set keyspecs [lindex $reply 8]
assert_equal [lindex $keyspecs 0] {flags {RO access} begin_search {type index spec {index 1}} find_keys {type range spec {lastkey 0 keystep 1 limit 0}}} assert_equal [lindex $keyspecs 0] {flags {RO access} begin_search {type index spec {index 1}} find_keys {type range spec {lastkey 0 keystep 1 limit 0}}}
assert_equal [lindex $keyspecs 1] {flags {RW update} begin_search {type index spec {index 2}} find_keys {type range spec {lastkey 0 keystep 1 limit 0}}} assert_equal [lindex $keyspecs 1] {flags {RW update} begin_search {type index spec {index 2}} find_keys {type range spec {lastkey 0 keystep 1 limit 0}}}
assert_equal [r command getkeys kspec.tworanges foo bar baz quux] {foo bar}
}
test "Module key specs: Keyword-only spec clears the legacy triple" {
set reply [lindex [r command info kspec.keyword] 0]
# Verify (first, last, step) and movablekeys
assert_equal [lindex $reply 2] {module movablekeys}
assert_equal [lindex $reply 3] 0
assert_equal [lindex $reply 4] 0
assert_equal [lindex $reply 5] 0
# Verify key-specs
set keyspecs [lindex $reply 8]
assert_equal [lindex $keyspecs 0] {flags {RO access} begin_search {type keyword spec {keyword KEYS startfrom 1}} find_keys {type range spec {lastkey -1 keystep 1 limit 0}}}
assert_equal [r command getkeys kspec.keyword foo KEYS bar baz] {bar baz}
} }
test "Module key specs: Complex specs, case 1" { test "Module key specs: Complex specs, case 1" {
set reply [lindex [r command info kspec.complex1] 0] set reply [lindex [r command info kspec.complex1] 0]
# Verify (first, last, step) # Verify (first, last, step) and movablekeys
assert_equal [lindex $reply 2] {module movablekeys}
assert_equal [lindex $reply 3] 1 assert_equal [lindex $reply 3] 1
assert_equal [lindex $reply 4] 1 assert_equal [lindex $reply 4] 1
assert_equal [lindex $reply 5] 1 assert_equal [lindex $reply 5] 1
# Verify key-specs # Verify key-specs
set keyspecs [lindex $reply 8] set keyspecs [lindex $reply 8]
assert_equal [lindex $keyspecs 0] {flags {} begin_search {type index spec {index 1}} find_keys {type range spec {lastkey 0 keystep 1 limit 0}}} assert_equal [lindex $keyspecs 0] {flags RO begin_search {type index spec {index 1}} find_keys {type range spec {lastkey 0 keystep 1 limit 0}}}
assert_equal [lindex $keyspecs 1] {flags {RW update} begin_search {type keyword spec {keyword STORE startfrom 2}} find_keys {type range spec {lastkey 0 keystep 1 limit 0}}} assert_equal [lindex $keyspecs 1] {flags {RW update} begin_search {type keyword spec {keyword STORE startfrom 2}} find_keys {type range spec {lastkey 0 keystep 1 limit 0}}}
assert_equal [lindex $keyspecs 2] {flags {RO access} begin_search {type keyword spec {keyword KEYS startfrom 2}} find_keys {type keynum spec {keynumidx 0 firstkey 1 keystep 1}}} assert_equal [lindex $keyspecs 2] {flags {RO access} begin_search {type keyword spec {keyword KEYS startfrom 2}} find_keys {type keynum spec {keynumidx 0 firstkey 1 keystep 1}}}
assert_equal [r command getkeys kspec.complex1 foo dummy KEYS 1 bar baz STORE quux] {foo quux bar}
} }
test "Module key specs: Complex specs, case 2" { test "Module key specs: Complex specs, case 2" {
set reply [lindex [r command info kspec.complex2] 0] set reply [lindex [r command info kspec.complex2] 0]
# Verify (first, last, step) # Verify (first, last, step) and movablekeys
assert_equal [lindex $reply 2] {module movablekeys}
assert_equal [lindex $reply 3] 1 assert_equal [lindex $reply 3] 1
assert_equal [lindex $reply 4] 2 assert_equal [lindex $reply 4] 2
assert_equal [lindex $reply 5] 1 assert_equal [lindex $reply 5] 1
...@@ -42,17 +73,42 @@ start_server {tags {"modules"}} { ...@@ -42,17 +73,42 @@ start_server {tags {"modules"}} {
assert_equal [lindex $keyspecs 2] {flags {RO access} begin_search {type index spec {index 2}} find_keys {type range spec {lastkey 0 keystep 1 limit 0}}} assert_equal [lindex $keyspecs 2] {flags {RO access} begin_search {type index spec {index 2}} find_keys {type range spec {lastkey 0 keystep 1 limit 0}}}
assert_equal [lindex $keyspecs 3] {flags {RW update} begin_search {type index spec {index 3}} find_keys {type keynum spec {keynumidx 0 firstkey 1 keystep 1}}} assert_equal [lindex $keyspecs 3] {flags {RW update} begin_search {type index spec {index 3}} find_keys {type keynum spec {keynumidx 0 firstkey 1 keystep 1}}}
assert_equal [lindex $keyspecs 4] {flags {RW update} begin_search {type keyword spec {keyword MOREKEYS startfrom 5}} find_keys {type range spec {lastkey -1 keystep 1 limit 0}}} assert_equal [lindex $keyspecs 4] {flags {RW update} begin_search {type keyword spec {keyword MOREKEYS startfrom 5}} find_keys {type range spec {lastkey -1 keystep 1 limit 0}}}
assert_equal [r command getkeys kspec.complex2 foo bar 2 baz quux banana STORE dst dummy MOREKEYS hey ho] {dst foo bar baz quux hey ho}
} }
test "Module command list filtering" { test "Module command list filtering" {
;# Note: we piggyback this tcl file to test the general functionality of command list filtering ;# Note: we piggyback this tcl file to test the general functionality of command list filtering
set reply [r command list filterby module keyspecs] set reply [r command list filterby module keyspecs]
assert_equal [lsort $reply] {kspec.complex1 kspec.complex2 kspec.legacy} assert_equal [lsort $reply] {kspec.complex1 kspec.complex2 kspec.keyword kspec.none kspec.tworanges}
assert_equal [r command getkeys kspec.complex2 foo bar 2 baz quux banana STORE dst dummy MOREKEYS hey ho] {dst foo bar baz quux hey ho}
}
test {COMMAND GETKEYSANDFLAGS correctly reports module key-spec without flags} {
r command getkeysandflags kspec.none key1 val1 key2 val2
} {{key1 {RW access update variable_flags}} {key2 {RW access update variable_flags}}}
test {COMMAND GETKEYSANDFLAGS correctly reports module key-spec flags} {
r command getkeysandflags kspec.keyword keys key1 key2 key3
} {{key1 {RO access}} {key2 {RO access}} {key3 {RO access}}}
# user that can only read from "read" keys, write to "write" keys, and read+write to "RW" keys
r ACL setuser testuser +@all %R~read* %W~write* %RW~rw*
test "Module key specs: No spec, only legacy triple - ACL" {
# legacy triple didn't provide flags, so they require both read and write
assert_equal "OK" [r ACL DRYRUN testuser kspec.none rw val1]
assert_equal "This user has no permissions to access the 'read' key" [r ACL DRYRUN testuser kspec.none read val1]
assert_equal "This user has no permissions to access the 'write' key" [r ACL DRYRUN testuser kspec.none write val1]
}
test "Module key specs: tworanges - ACL" {
assert_equal "OK" [r ACL DRYRUN testuser kspec.tworanges read write]
assert_equal "OK" [r ACL DRYRUN testuser kspec.tworanges rw rw]
assert_equal "This user has no permissions to access the 'read' key" [r ACL DRYRUN testuser kspec.tworanges rw read]
assert_equal "This user has no permissions to access the 'write' key" [r ACL DRYRUN testuser kspec.tworanges write rw]
} }
test "Unload the module - keyspecs" { test "Unload the module - keyspecs" {
assert_equal {OK} [r module unload keyspecs] assert_equal {OK} [r module unload keyspecs]
} }
} }
} ; # Test suite disabled
...@@ -8,20 +8,15 @@ start_server {tags {"modules"}} { ...@@ -8,20 +8,15 @@ start_server {tags {"modules"}} {
set command_reply [r command info subcommands.bitarray] set command_reply [r command info subcommands.bitarray]
set first_cmd [lindex $command_reply 0] set first_cmd [lindex $command_reply 0]
set subcmds_in_command [lsort [lindex $first_cmd 9]] set subcmds_in_command [lsort [lindex $first_cmd 9]]
if 0 { ; # Keyspecs disabled due to planned changes in keyspec API
assert_equal [lindex $subcmds_in_command 0] {subcommands.bitarray|get -2 module 1 1 1 {} {} {{flags {RO access} begin_search {type index spec {index 1}} find_keys {type range spec {lastkey 0 keystep 1 limit 0}}}} {}} assert_equal [lindex $subcmds_in_command 0] {subcommands.bitarray|get -2 module 1 1 1 {} {} {{flags {RO access} begin_search {type index spec {index 1}} find_keys {type range spec {lastkey 0 keystep 1 limit 0}}}} {}}
assert_equal [lindex $subcmds_in_command 1] {subcommands.bitarray|set -2 module 1 1 1 {} {} {{flags {RW update} begin_search {type index spec {index 1}} find_keys {type range spec {lastkey 0 keystep 1 limit 0}}}} {}} assert_equal [lindex $subcmds_in_command 1] {subcommands.bitarray|set -2 module 1 1 1 {} {} {{flags {RW update} begin_search {type index spec {index 1}} find_keys {type range spec {lastkey 0 keystep 1 limit 0}}}} {}}
} else { ; # The same asserts without the key specs
assert_equal [lindex $subcmds_in_command 0] {subcommands.bitarray|get -2 module 0 0 0 {} {} {} {}}
assert_equal [lindex $subcmds_in_command 1] {subcommands.bitarray|set -2 module 0 0 0 {} {} {} {}}
}
# Verify that module subcommands are displayed correctly in COMMAND DOCS # Verify that module subcommands are displayed correctly in COMMAND DOCS
set docs_reply [r command docs subcommands.bitarray] set docs_reply [r command docs subcommands.bitarray]
set docs [dict create {*}[lindex $docs_reply 1]] set docs [dict create {*}[lindex $docs_reply 1]]
set subcmds_in_cmd_docs [dict create {*}[dict get $docs subcommands]] set subcmds_in_cmd_docs [dict create {*}[dict get $docs subcommands]]
assert_equal [dict get $subcmds_in_cmd_docs "subcommands.bitarray|get"] {summary {} since {} group module} assert_equal [dict get $subcmds_in_cmd_docs "subcommands.bitarray|get"] {group module}
assert_equal [dict get $subcmds_in_cmd_docs "subcommands.bitarray|set"] {summary {} since {} group module} assert_equal [dict get $subcmds_in_cmd_docs "subcommands.bitarray|set"] {group module}
} }
test "Module pure-container command fails on arity error" { test "Module pure-container command fails on arity error" {
......
...@@ -47,6 +47,12 @@ tags "modules" { ...@@ -47,6 +47,12 @@ tags "modules" {
} }
} }
test {Verify module options info} {
start_server [list overrides [list loadmodule "$testmodule"]] {
assert_match "*\[handle-io-errors|handle-repl-async-load\]*" [r info modules]
}
}
tags {repl} { tags {repl} {
test {diskless loading short read with module} { test {diskless loading short read with module} {
start_server [list overrides [list loadmodule "$testmodule"]] { start_server [list overrides [list loadmodule "$testmodule"]] {
......
...@@ -26,6 +26,8 @@ start_server {tags {"modules"}} { ...@@ -26,6 +26,8 @@ start_server {tags {"modules"}} {
assert_equal "timer-incr-key" [lindex $info 0] assert_equal "timer-incr-key" [lindex $info 0]
set remaining [lindex $info 1] set remaining [lindex $info 1]
assert {$remaining < 10000 && $remaining > 1} assert {$remaining < 10000 && $remaining > 1}
# Stop the timer after get timer test
assert_equal 1 [r test.stoptimer $id]
} }
test {RM_StopTimer: basic sanity} { test {RM_StopTimer: basic sanity} {
...@@ -54,7 +56,43 @@ start_server {tags {"modules"}} { ...@@ -54,7 +56,43 @@ start_server {tags {"modules"}} {
assert_equal {} [r test.gettimer $id] assert_equal {} [r test.gettimer $id]
} }
test "Unload the module - timer" { test "Module can be unloaded when timer was finished" {
r set "timer-incr-key" 0
r test.createtimer 500 timer-incr-key
# Make sure the Timer has not been fired
assert_equal 0 [r get timer-incr-key]
# Module can not be unloaded since the timer was ongoing
catch {r module unload timer} err
assert_match {*the module holds timer that is not fired*} $err
# Wait to be sure timer has been finished
wait_for_condition 10 500 {
[r get timer-incr-key] == 1
} else {
fail "Timer not fired"
}
# Timer fired, can be unloaded now.
assert_equal {OK} [r module unload timer]
}
test "Module can be unloaded when timer was stopped" {
r module load $testmodule
r set "timer-incr-key" 0
set id [r test.createtimer 5000 timer-incr-key]
# Module can not be unloaded since the timer was ongoing
catch {r module unload timer} err
assert_match {*the module holds timer that is not fired*} $err
# Stop the timer
assert_equal 1 [r test.stoptimer $id]
# Make sure the Timer has not been fired
assert_equal 0 [r get timer-incr-key]
# Timer has stopped, can be unloaded now.
assert_equal {OK} [r module unload timer] assert_equal {OK} [r module unload timer]
} }
} }
......
...@@ -132,18 +132,61 @@ start_server {tags {"multi"}} { ...@@ -132,18 +132,61 @@ start_server {tags {"multi"}} {
} {} {cluster:skip} } {} {cluster:skip}
test {EXEC fail on lazy expired WATCHed key} { test {EXEC fail on lazy expired WATCHed key} {
r flushall r del key
r debug set-active-expire 0 r debug set-active-expire 0
r del key for {set j 0} {$j < 10} {incr j} {
r set key 1 px 2 r set key 1 px 100
r watch key r watch key
after 101
r multi
r incr key
after 100 set res [r exec]
if {$res eq {}} break
}
if {$::verbose} { puts "EXEC fail on lazy expired WATCHed key attempts: $j" }
r debug set-active-expire 1
set _ $res
} {} {needs:debug}
test {WATCH stale keys should not fail EXEC} {
r del x
r debug set-active-expire 0
r set x foo px 1
after 2
r watch x
r multi r multi
r incr key r ping
assert_equal [r exec] {} assert_equal {PONG} [r exec]
r debug set-active-expire 1
} {OK} {needs:debug}
test {Delete WATCHed stale keys should not fail EXEC} {
r del x
r debug set-active-expire 0
r set x foo px 1
after 2
r watch x
# EXISTS triggers lazy expiry/deletion
assert_equal 0 [r exists x]
r multi
r ping
assert_equal {PONG} [r exec]
r debug set-active-expire 1
} {OK} {needs:debug}
test {FLUSHDB while watching stale keys should not fail EXEC} {
r del x
r debug set-active-expire 0
r set x foo px 1
after 2
r watch x
r flushdb
r multi
r ping
assert_equal {PONG} [r exec]
r debug set-active-expire 1 r debug set-active-expire 1
} {OK} {needs:debug} } {OK} {needs:debug}
...@@ -245,6 +288,52 @@ start_server {tags {"multi"}} { ...@@ -245,6 +288,52 @@ start_server {tags {"multi"}} {
r exec r exec
} {} {singledb:skip} } {} {singledb:skip}
test {SWAPDB does not touch watched stale keys} {
r flushall
r select 1
r debug set-active-expire 0
r set x foo px 1
after 2
r watch x
r swapdb 0 1 ; # expired key replaced with no key => no change
r multi
r ping
assert_equal {PONG} [r exec]
r debug set-active-expire 1
} {OK} {singledb:skip needs:debug}
test {SWAPDB does not touch non-existing key replaced with stale key} {
r flushall
r select 0
r debug set-active-expire 0
r set x foo px 1
after 2
r select 1
r watch x
r swapdb 0 1 ; # no key replaced with expired key => no change
r multi
r ping
assert_equal {PONG} [r exec]
r debug set-active-expire 1
} {OK} {singledb:skip needs:debug}
test {SWAPDB does not touch stale key replaced with another stale key} {
r flushall
r debug set-active-expire 0
r select 1
r set x foo px 1
r select 0
r set x bar px 1
after 2
r select 1
r watch x
r swapdb 0 1 ; # no key replaced with expired key => no change
r multi
r ping
assert_equal {PONG} [r exec]
r debug set-active-expire 1
} {OK} {singledb:skip needs:debug}
test {WATCH is able to remember the DB a key belongs to} { test {WATCH is able to remember the DB a key belongs to} {
r select 5 r select 5
r set x 30 r set x 30
......
...@@ -139,13 +139,17 @@ start_server {tags {"protocol network"}} { ...@@ -139,13 +139,17 @@ start_server {tags {"protocol network"}} {
test {RESP3 attributes} { test {RESP3 attributes} {
r hello 3 r hello 3
set res [r debug protocol attrib] assert_equal {Some real reply following the attribute} [r debug protocol attrib]
# currently the parser in redis.tcl ignores the attributes assert_equal {key-popularity {key:123 90}} [r attributes]
# make sure attributes are not kept from previous command
r ping
assert_error {*attributes* no such element in array} {r attributes}
# restore state # restore state
r hello 2 r hello 2
set _ $res set _ ""
} {Some real reply following the attribute} {needs:debug resp3} } {} {needs:debug resp3}
test {RESP3 attributes readraw} { test {RESP3 attributes readraw} {
r hello 3 r hello 3
......
proc get_reply_buffer_size {cname} {
set clients [split [string trim [r client list]] "\r\n"]
set c [lsearch -inline $clients *name=$cname*]
if {![regexp rbs=(\[a-zA-Z0-9-\]+) $c - rbufsize]} {
error "field rbus not found in $c"
}
return $rbufsize
}
start_server {tags {"replybufsize"}} {
test {verify reply buffer limits} {
# In order to reduce test time we can set the peak reset time very low
r debug replybuffer-peak-reset-time 100
# Create a simple idle test client
variable tc [redis_client]
$tc client setname test_client
# make sure the client is idle for 1 seconds to make it shrink the reply buffer
wait_for_condition 10 100 {
[get_reply_buffer_size test_client] >= 1024 && [get_reply_buffer_size test_client] < 2046
} else {
set rbs [get_reply_buffer_size test_client]
fail "reply buffer of idle client is $rbs after 1 seconds"
}
r set bigval [string repeat x 32768]
# In order to reduce test time we can set the peak reset time very low
r debug replybuffer-peak-reset-time never
wait_for_condition 10 100 {
[$tc get bigval ; get_reply_buffer_size test_client] >= 16384 && [get_reply_buffer_size test_client] < 32768
} else {
set rbs [get_reply_buffer_size test_client]
fail "reply buffer of busy client is $rbs after 1 seconds"
}
# Restore the peak reset time to default
r debug replybuffer-peak-reset-time reset
$tc close
} {0} {needs:debug}
}
\ No newline at end of file
...@@ -73,10 +73,10 @@ start_server {tags {"scripting"}} { ...@@ -73,10 +73,10 @@ start_server {tags {"scripting"}} {
test {EVAL - Lua error reply -> Redis protocol type conversion} { test {EVAL - Lua error reply -> Redis protocol type conversion} {
catch { catch {
run_script {return {err='this is an error'}} 0 run_script {return {err='ERR this is an error'}} 0
} e } e
set _ $e set _ $e
} {this is an error} } {ERR this is an error}
test {EVAL - Lua table -> Redis protocol type conversion} { test {EVAL - Lua table -> Redis protocol type conversion} {
run_script {return {1,2,3,'ciao',{1,2}}} 0 run_script {return {1,2,3,'ciao',{1,2}}} 0
...@@ -378,7 +378,7 @@ start_server {tags {"scripting"}} { ...@@ -378,7 +378,7 @@ start_server {tags {"scripting"}} {
r set foo bar r set foo bar
catch {run_script_ro {redis.call('del', KEYS[1]);} 1 foo} e catch {run_script_ro {redis.call('del', KEYS[1]);} 1 foo} e
set e set e
} {*Write commands are not allowed from read-only scripts*} } {ERR Write commands are not allowed from read-only scripts*}
if {$is_eval eq 1} { if {$is_eval eq 1} {
# script command is only relevant for is_eval Lua # script command is only relevant for is_eval Lua
...@@ -439,12 +439,12 @@ start_server {tags {"scripting"}} { ...@@ -439,12 +439,12 @@ start_server {tags {"scripting"}} {
test {Globals protection reading an undeclared global variable} { test {Globals protection reading an undeclared global variable} {
catch {run_script {return a} 0} e catch {run_script {return a} 0} e
set e set e
} {*ERR*attempted to access * global*} } {ERR*attempted to access * global*}
test {Globals protection setting an undeclared global*} { test {Globals protection setting an undeclared global*} {
catch {run_script {a=10} 0} e catch {run_script {a=10} 0} e
set e set e
} {*ERR*attempted to create global*} } {ERR*attempted to create global*}
test {Test an example script DECR_IF_GT} { test {Test an example script DECR_IF_GT} {
set decr_if_gt { set decr_if_gt {
...@@ -599,8 +599,8 @@ start_server {tags {"scripting"}} { ...@@ -599,8 +599,8 @@ start_server {tags {"scripting"}} {
} {ERR Number of keys can't be negative} } {ERR Number of keys can't be negative}
test {Scripts can handle commands with incorrect arity} { test {Scripts can handle commands with incorrect arity} {
assert_error "*Wrong number of args calling Redis command from script" {run_script "redis.call('set','invalid')" 0} assert_error "ERR Wrong number of args calling Redis command from script*" {run_script "redis.call('set','invalid')" 0}
assert_error "*Wrong number of args calling Redis command from script" {run_script "redis.call('incr')" 0} assert_error "ERR Wrong number of args calling Redis command from script*" {run_script "redis.call('incr')" 0}
} }
test {Correct handling of reused argv (issue #1939)} { test {Correct handling of reused argv (issue #1939)} {
...@@ -701,6 +701,32 @@ start_server {tags {"scripting"}} { ...@@ -701,6 +701,32 @@ start_server {tags {"scripting"}} {
return redis.call("EXISTS", "key") return redis.call("EXISTS", "key")
} 0] 0 } 0] 0
} }
test "Script ACL check" {
r acl setuser bob on {>123} {+@scripting} {+set} {~x*}
assert_equal [r auth bob 123] {OK}
# Check permission granted
assert_equal [run_script {
return redis.acl_check_cmd('set','xx',1)
} 1 xx] 1
# Check permission denied unauthorised command
assert_equal [run_script {
return redis.acl_check_cmd('hset','xx','f',1)
} 1 xx] {}
# Check permission denied unauthorised key
# Note: we don't pass the "yy" key as an argument to the script so key acl checks won't block the script
assert_equal [run_script {
return redis.acl_check_cmd('set','yy',1)
} 0] {}
# Check error due to invalid command
assert_error {ERR *Invalid command passed to redis.acl_check_cmd()*} {run_script {
return redis.acl_check_cmd('invalid-cmd','arg')
} 0}
}
} }
# Start a new server since the last test in this stanza will kill the # Start a new server since the last test in this stanza will kill the
...@@ -1262,7 +1288,7 @@ start_server {tags {"scripting"}} { ...@@ -1262,7 +1288,7 @@ start_server {tags {"scripting"}} {
r config set maxmemory 1 r config set maxmemory 1
# Fail to execute deny-oom command in OOM condition (backwards compatibility mode without flags) # Fail to execute deny-oom command in OOM condition (backwards compatibility mode without flags)
assert_error {ERR Error running script *OOM command not allowed when used memory > 'maxmemory'.} { assert_error {OOM command not allowed when used memory > 'maxmemory'*} {
r eval { r eval {
redis.call('set','x',1) redis.call('set','x',1)
return 1 return 1
...@@ -1293,7 +1319,7 @@ start_server {tags {"scripting"}} { ...@@ -1293,7 +1319,7 @@ start_server {tags {"scripting"}} {
} }
test "no-writes shebang flag" { test "no-writes shebang flag" {
assert_error {ERR Error running script *Write commands are not allowed from read-only scripts.} { assert_error {ERR Write commands are not allowed from read-only scripts*} {
r eval {#!lua flags=no-writes r eval {#!lua flags=no-writes
redis.call('set','x',1) redis.call('set','x',1)
return 1 return 1
...@@ -1374,3 +1400,154 @@ start_server {tags {"scripting"}} { ...@@ -1374,3 +1400,154 @@ start_server {tags {"scripting"}} {
set _ {} set _ {}
} {} {external:skip} } {} {external:skip}
} }
# Additional eval only tests
start_server {tags {"scripting"}} {
test "Consistent eval error reporting" {
r config resetstat
r config set maxmemory 1
# Script aborted due to Redis state (OOM) should report script execution error with detailed internal error
assert_error {OOM command not allowed when used memory > 'maxmemory'*} {
r eval {return redis.call('set','x','y')} 1 x
}
assert_equal [errorrstat OOM r] {count=1}
assert_equal [s total_error_replies] {1}
assert_match {calls=0*rejected_calls=1,failed_calls=0*} [cmdrstat set r]
assert_match {calls=1*rejected_calls=0,failed_calls=1*} [cmdrstat eval r]
# redis.pcall() failure due to Redis state (OOM) returns lua error table with Redis error message without '-' prefix
r config resetstat
assert_equal [
r eval {
local t = redis.pcall('set','x','y')
if t['err'] == "OOM command not allowed when used memory > 'maxmemory'." then
return 1
else
return 0
end
} 1 x
] 1
# error stats were not incremented
assert_equal [errorrstat ERR r] {}
assert_equal [errorrstat OOM r] {count=1}
assert_equal [s total_error_replies] {1}
assert_match {calls=0*rejected_calls=1,failed_calls=0*} [cmdrstat set r]
assert_match {calls=1*rejected_calls=0,failed_calls=0*} [cmdrstat eval r]
# Returning an error object from lua is handled as a valid RESP error result.
r config resetstat
assert_error {OOM command not allowed when used memory > 'maxmemory'.} {
r eval { return redis.pcall('set','x','y') } 1 x
}
assert_equal [errorrstat ERR r] {}
assert_equal [errorrstat OOM r] {count=1}
assert_equal [s total_error_replies] {1}
assert_match {calls=0*rejected_calls=1,failed_calls=0*} [cmdrstat set r]
assert_match {calls=1*rejected_calls=0,failed_calls=1*} [cmdrstat eval r]
r config set maxmemory 0
r config resetstat
# Script aborted due to error result of Redis command
assert_error {ERR DB index is out of range*} {
r eval {return redis.call('select',99)} 0
}
assert_equal [errorrstat ERR r] {count=1}
assert_equal [s total_error_replies] {1}
assert_match {calls=1*rejected_calls=0,failed_calls=1*} [cmdrstat select r]
assert_match {calls=1*rejected_calls=0,failed_calls=1*} [cmdrstat eval r]
# redis.pcall() failure due to error in Redis command returns lua error table with redis error message without '-' prefix
r config resetstat
assert_equal [
r eval {
local t = redis.pcall('select',99)
if t['err'] == "ERR DB index is out of range" then
return 1
else
return 0
end
} 0
] 1
assert_equal [errorrstat ERR r] {count=1} ;
assert_equal [s total_error_replies] {1}
assert_match {calls=1*rejected_calls=0,failed_calls=1*} [cmdrstat select r]
assert_match {calls=1*rejected_calls=0,failed_calls=0*} [cmdrstat eval r]
# Script aborted due to scripting specific error state (write cmd with eval_ro) should report script execution error with detailed internal error
r config resetstat
assert_error {ERR Write commands are not allowed from read-only scripts*} {
r eval_ro {return redis.call('set','x','y')} 1 x
}
assert_equal [errorrstat ERR r] {count=1}
assert_equal [s total_error_replies] {1}
assert_match {calls=0*rejected_calls=1,failed_calls=0*} [cmdrstat set r]
assert_match {calls=1*rejected_calls=0,failed_calls=1*} [cmdrstat eval_ro r]
# redis.pcall() failure due to scripting specific error state (write cmd with eval_ro) returns lua error table with Redis error message without '-' prefix
r config resetstat
assert_equal [
r eval_ro {
local t = redis.pcall('set','x','y')
if t['err'] == "ERR Write commands are not allowed from read-only scripts." then
return 1
else
return 0
end
} 1 x
] 1
assert_equal [errorrstat ERR r] {count=1}
assert_equal [s total_error_replies] {1}
assert_match {calls=0*rejected_calls=1,failed_calls=0*} [cmdrstat set r]
assert_match {calls=1*rejected_calls=0,failed_calls=0*} [cmdrstat eval_ro r]
r config resetstat
# make sure geoadd will failed
r set Sicily 1
assert_error {WRONGTYPE Operation against a key holding the wrong kind of value*} {
r eval {return redis.call('GEOADD', 'Sicily', '13.361389', '38.115556', 'Palermo', '15.087269', '37.502669', 'Catania')} 1 x
}
assert_equal [errorrstat WRONGTYPE r] {count=1}
assert_equal [s total_error_replies] {1}
assert_match {calls=1*rejected_calls=0,failed_calls=1*} [cmdrstat geoadd r]
assert_match {calls=1*rejected_calls=0,failed_calls=1*} [cmdrstat eval r]
} {} {cluster:skip}
test "LUA redis.error_reply API" {
r config resetstat
assert_error {MY_ERR_CODE custom msg} {
r eval {return redis.error_reply("MY_ERR_CODE custom msg")} 0
}
assert_equal [errorrstat MY_ERR_CODE r] {count=1}
}
test "LUA redis.error_reply API with empty string" {
r config resetstat
assert_error {ERR} {
r eval {return redis.error_reply("")} 0
}
assert_equal [errorrstat ERR r] {count=1}
}
test "LUA redis.status_reply API" {
r config resetstat
r readraw 1
assert_equal [
r eval {return redis.status_reply("MY_OK_CODE custom msg")} 0
] {+MY_OK_CODE custom msg}
r readraw 0
assert_equal [errorrstat MY_ERR_CODE r] {} ;# error stats were not incremented
}
test "LUA test pcall" {
assert_equal [
r eval {local status, res = pcall(function() return 1 end); return 'status: ' .. tostring(status) .. ' result: ' .. res} 0
] {status: true result: 1}
}
test "LUA test pcall with error" {
assert_match {status: false result:*Script attempted to access nonexistent global variable 'foo'} [
r eval {local status, res = pcall(function() return foo end); return 'status: ' .. tostring(status) .. ' result: ' .. res} 0
]
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment