diff options
| author | Mitja Felicijan <mitja.felicijan@gmail.com> | 2026-01-21 22:52:54 +0100 |
|---|---|---|
| committer | Mitja Felicijan <mitja.felicijan@gmail.com> | 2026-01-21 22:52:54 +0100 |
| commit | dcacc00e3750300617ba6e16eb346713f91a783a (patch) | |
| tree | 38e2d4fb5ed9d119711d4295c6eda4b014af73fd /examples/redis-unstable/tests/unit/functions.tcl | |
| parent | 58dac10aeb8f5a041c46bddbeaf4c7966a99b998 (diff) | |
| download | crep-dcacc00e3750300617ba6e16eb346713f91a783a.tar.gz | |
Remove testing data
Diffstat (limited to 'examples/redis-unstable/tests/unit/functions.tcl')
| -rw-r--r-- | examples/redis-unstable/tests/unit/functions.tcl | 1259 |
1 files changed, 0 insertions, 1259 deletions
diff --git a/examples/redis-unstable/tests/unit/functions.tcl b/examples/redis-unstable/tests/unit/functions.tcl deleted file mode 100644 index b9496c9..0000000 --- a/examples/redis-unstable/tests/unit/functions.tcl +++ /dev/null | |||
| @@ -1,1259 +0,0 @@ | |||
| 1 | proc get_function_code {args} { | ||
| 2 | return [format "#!%s name=%s\nredis.register_function('%s', function(KEYS, ARGV)\n %s \nend)" [lindex $args 0] [lindex $args 1] [lindex $args 2] [lindex $args 3]] | ||
| 3 | } | ||
| 4 | |||
| 5 | proc get_no_writes_function_code {args} { | ||
| 6 | return [format "#!%s name=%s\nredis.register_function{function_name='%s', callback=function(KEYS, ARGV)\n %s \nend, flags={'no-writes'}}" [lindex $args 0] [lindex $args 1] [lindex $args 2] [lindex $args 3]] | ||
| 7 | } | ||
| 8 | |||
| 9 | start_server {tags {"scripting"}} { | ||
| 10 | test {FUNCTION - Basic usage} { | ||
| 11 | r function load [get_function_code LUA test test {return 'hello'}] | ||
| 12 | r fcall test 0 | ||
| 13 | } {hello} | ||
| 14 | |||
| 15 | test {FUNCTION - Load with unknown argument} { | ||
| 16 | catch { | ||
| 17 | r function load foo bar [get_function_code LUA test test {return 'hello'}] | ||
| 18 | } e | ||
| 19 | set _ $e | ||
| 20 | } {*Unknown option given*} | ||
| 21 | |||
| 22 | test {FUNCTION - Create an already exiting library raise error} { | ||
| 23 | catch { | ||
| 24 | r function load [get_function_code LUA test test {return 'hello1'}] | ||
| 25 | } e | ||
| 26 | set _ $e | ||
| 27 | } {*already exists*} | ||
| 28 | |||
| 29 | test {FUNCTION - Create an already exiting library raise error (case insensitive)} { | ||
| 30 | catch { | ||
| 31 | r function load [get_function_code LUA test test {return 'hello1'}] | ||
| 32 | } e | ||
| 33 | set _ $e | ||
| 34 | } {*already exists*} | ||
| 35 | |||
| 36 | test {FUNCTION - Create a library with wrong name format} { | ||
| 37 | catch { | ||
| 38 | r function load [get_function_code LUA {bad\0foramat} test {return 'hello1'}] | ||
| 39 | } e | ||
| 40 | set _ $e | ||
| 41 | } {*Library names can only contain letters, numbers, or underscores(_)*} | ||
| 42 | |||
| 43 | test {FUNCTION - Create library with unexisting engine} { | ||
| 44 | catch { | ||
| 45 | r function load [get_function_code bad_engine test test {return 'hello1'}] | ||
| 46 | } e | ||
| 47 | set _ $e | ||
| 48 | } {*Engine 'bad_engine' not found*} | ||
| 49 | |||
| 50 | test {FUNCTION - Test uncompiled script} { | ||
| 51 | catch { | ||
| 52 | r function load replace [get_function_code LUA test test {bad script}] | ||
| 53 | } e | ||
| 54 | set _ $e | ||
| 55 | } {*Error compiling function*} | ||
| 56 | |||
| 57 | test {FUNCTION - test replace argument} { | ||
| 58 | r function load REPLACE [get_function_code LUA test test {return 'hello1'}] | ||
| 59 | r fcall test 0 | ||
| 60 | } {hello1} | ||
| 61 | |||
| 62 | test {FUNCTION - test function case insensitive} { | ||
| 63 | r fcall TEST 0 | ||
| 64 | } {hello1} | ||
| 65 | |||
| 66 | test {FUNCTION - test replace argument with failure keeps old libraries} { | ||
| 67 | catch {r function load REPLACE [get_function_code LUA test test {error}]} e | ||
| 68 | assert_match {ERR Error compiling function*} $e | ||
| 69 | r fcall test 0 | ||
| 70 | } {hello1} | ||
| 71 | |||
| 72 | test {FUNCTION - test function delete} { | ||
| 73 | r function delete test | ||
| 74 | catch { | ||
| 75 | r fcall test 0 | ||
| 76 | } e | ||
| 77 | set _ $e | ||
| 78 | } {*Function not found*} | ||
| 79 | |||
| 80 | test {FUNCTION - test fcall bad arguments} { | ||
| 81 | r function load [get_function_code LUA test test {return 'hello'}] | ||
| 82 | catch { | ||
| 83 | r fcall test bad_arg | ||
| 84 | } e | ||
| 85 | set _ $e | ||
| 86 | } {*Bad number of keys provided*} | ||
| 87 | |||
| 88 | test {FUNCTION - test fcall bad number of keys arguments} { | ||
| 89 | catch { | ||
| 90 | r fcall test 10 key1 | ||
| 91 | } e | ||
| 92 | set _ $e | ||
| 93 | } {*Number of keys can't be greater than number of args*} | ||
| 94 | |||
| 95 | test {FUNCTION - test fcall negative number of keys} { | ||
| 96 | catch { | ||
| 97 | r fcall test -1 key1 | ||
| 98 | } e | ||
| 99 | set _ $e | ||
| 100 | } {*Number of keys can't be negative*} | ||
| 101 | |||
| 102 | test {FUNCTION - test delete on not exiting library} { | ||
| 103 | catch { | ||
| 104 | r function delete test1 | ||
| 105 | } e | ||
| 106 | set _ $e | ||
| 107 | } {*Library not found*} | ||
| 108 | |||
| 109 | test {FUNCTION - test function kill when function is not running} { | ||
| 110 | catch { | ||
| 111 | r function kill | ||
| 112 | } e | ||
| 113 | set _ $e | ||
| 114 | } {*No scripts in execution*} | ||
| 115 | |||
| 116 | test {FUNCTION - test wrong subcommand} { | ||
| 117 | catch { | ||
| 118 | r function bad_subcommand | ||
| 119 | } e | ||
| 120 | set _ $e | ||
| 121 | } {*unknown subcommand*} | ||
| 122 | |||
| 123 | test {FUNCTION - test loading from rdb} { | ||
| 124 | r debug reload | ||
| 125 | r fcall test 0 | ||
| 126 | } {hello} {needs:debug} | ||
| 127 | |||
| 128 | test {FUNCTION - test debug reload different options} { | ||
| 129 | catch {r debug reload noflush} e | ||
| 130 | assert_match "*Error trying to load the RDB*" $e | ||
| 131 | r debug reload noflush merge | ||
| 132 | r function list | ||
| 133 | } {{library_name test engine LUA functions {{name test description {} flags {}}}}} {needs:debug} | ||
| 134 | |||
| 135 | test {FUNCTION - test debug reload with nosave and noflush} { | ||
| 136 | r function delete test | ||
| 137 | r set x 1 | ||
| 138 | r function load [get_function_code LUA test1 test1 {return 'hello'}] | ||
| 139 | r debug reload | ||
| 140 | r function load [get_function_code LUA test2 test2 {return 'hello'}] | ||
| 141 | r debug reload nosave noflush merge | ||
| 142 | assert_equal [r fcall test1 0] {hello} | ||
| 143 | assert_equal [r fcall test2 0] {hello} | ||
| 144 | } {} {needs:debug} | ||
| 145 | |||
| 146 | test {FUNCTION - test flushall and flushdb do not clean functions} { | ||
| 147 | r function flush | ||
| 148 | r function load REPLACE [get_function_code lua test test {return redis.call('set', 'x', '1')}] | ||
| 149 | r flushall | ||
| 150 | r flushdb | ||
| 151 | r function list | ||
| 152 | } {{library_name test engine LUA functions {{name test description {} flags {}}}}} | ||
| 153 | |||
| 154 | test {FUNCTION - test function dump and restore} { | ||
| 155 | r function flush | ||
| 156 | r function load [get_function_code lua test test {return 'hello'}] | ||
| 157 | set e [r function dump] | ||
| 158 | r function delete test | ||
| 159 | assert_match {} [r function list] | ||
| 160 | r function restore $e | ||
| 161 | r function list | ||
| 162 | } {{library_name test engine LUA functions {{name test description {} flags {}}}}} | ||
| 163 | |||
| 164 | test {FUNCTION - test function dump and restore with flush argument} { | ||
| 165 | set e [r function dump] | ||
| 166 | r function flush | ||
| 167 | assert_match {} [r function list] | ||
| 168 | r function restore $e FLUSH | ||
| 169 | r function list | ||
| 170 | } {{library_name test engine LUA functions {{name test description {} flags {}}}}} | ||
| 171 | |||
| 172 | test {FUNCTION - test function dump and restore with append argument} { | ||
| 173 | set e [r function dump] | ||
| 174 | r function flush | ||
| 175 | assert_match {} [r function list] | ||
| 176 | r function load [get_function_code lua test test {return 'hello1'}] | ||
| 177 | catch {r function restore $e APPEND} err | ||
| 178 | assert_match {*already exists*} $err | ||
| 179 | r function flush | ||
| 180 | r function load [get_function_code lua test1 test1 {return 'hello1'}] | ||
| 181 | r function restore $e APPEND | ||
| 182 | assert_match {hello} [r fcall test 0] | ||
| 183 | assert_match {hello1} [r fcall test1 0] | ||
| 184 | } | ||
| 185 | |||
| 186 | test {FUNCTION - test function dump and restore with replace argument} { | ||
| 187 | r function flush | ||
| 188 | r function load [get_function_code LUA test test {return 'hello'}] | ||
| 189 | set e [r function dump] | ||
| 190 | r function flush | ||
| 191 | assert_match {} [r function list] | ||
| 192 | r function load [get_function_code lua test test {return 'hello1'}] | ||
| 193 | assert_match {hello1} [r fcall test 0] | ||
| 194 | r function restore $e REPLACE | ||
| 195 | assert_match {hello} [r fcall test 0] | ||
| 196 | } | ||
| 197 | |||
| 198 | test {FUNCTION - test function restore with bad payload do not drop existing functions} { | ||
| 199 | r function flush | ||
| 200 | r function load [get_function_code LUA test test {return 'hello'}] | ||
| 201 | catch {r function restore bad_payload} e | ||
| 202 | assert_match {*payload version or checksum are wrong*} $e | ||
| 203 | r function list | ||
| 204 | } {{library_name test engine LUA functions {{name test description {} flags {}}}}} | ||
| 205 | |||
| 206 | test {FUNCTION - test function restore with wrong number of arguments} { | ||
| 207 | catch {r function restore arg1 args2 arg3} e | ||
| 208 | set _ $e | ||
| 209 | } {*unknown subcommand or wrong number of arguments for 'restore'. Try FUNCTION HELP.} | ||
| 210 | |||
| 211 | test {FUNCTION - test fcall_ro with write command} { | ||
| 212 | r function load REPLACE [get_no_writes_function_code lua test test {return redis.call('set', 'x', '1')}] | ||
| 213 | catch { r fcall_ro test 1 x } e | ||
| 214 | set _ $e | ||
| 215 | } {*Write commands are not allowed from read-only scripts*} | ||
| 216 | |||
| 217 | test {FUNCTION - test fcall_ro with read only commands} { | ||
| 218 | r function load REPLACE [get_no_writes_function_code lua test test {return redis.call('get', 'x')}] | ||
| 219 | r set x 1 | ||
| 220 | r fcall_ro test 1 x | ||
| 221 | } {1} | ||
| 222 | |||
| 223 | test {FUNCTION - test keys and argv} { | ||
| 224 | r function load REPLACE [get_function_code lua test test {return redis.call('set', KEYS[1], ARGV[1])}] | ||
| 225 | r fcall test 1 x foo | ||
| 226 | r get x | ||
| 227 | } {foo} | ||
| 228 | |||
| 229 | test {FUNCTION - test command get keys on fcall} { | ||
| 230 | r COMMAND GETKEYS fcall test 1 x foo | ||
| 231 | } {x} | ||
| 232 | |||
| 233 | test {FUNCTION - test command get keys on fcall_ro} { | ||
| 234 | r COMMAND GETKEYS fcall_ro test 1 x foo | ||
| 235 | } {x} | ||
| 236 | |||
| 237 | test {FUNCTION - test function kill} { | ||
| 238 | set rd [redis_deferring_client] | ||
| 239 | r config set busy-reply-threshold 10 | ||
| 240 | r function load REPLACE [get_function_code lua test test {local a = 1 while true do a = a + 1 end}] | ||
| 241 | $rd fcall test 0 | ||
| 242 | after 200 | ||
| 243 | catch {r ping} e | ||
| 244 | assert_match {BUSY*} $e | ||
| 245 | assert_match {running_script {name test command {fcall test 0} duration_ms *} engines {*}} [r FUNCTION STATS] | ||
| 246 | r function kill | ||
| 247 | after 200 ; # Give some time to Lua to call the hook again... | ||
| 248 | assert_equal [r ping] "PONG" | ||
| 249 | } | ||
| 250 | |||
| 251 | test {FUNCTION - test script kill not working on function} { | ||
| 252 | set rd [redis_deferring_client] | ||
| 253 | r config set busy-reply-threshold 10 | ||
| 254 | r function load REPLACE [get_function_code lua test test {local a = 1 while true do a = a + 1 end}] | ||
| 255 | $rd fcall test 0 | ||
| 256 | after 200 | ||
| 257 | catch {r ping} e | ||
| 258 | assert_match {BUSY*} $e | ||
| 259 | catch {r script kill} e | ||
| 260 | assert_match {BUSY*} $e | ||
| 261 | r function kill | ||
| 262 | after 200 ; # Give some time to Lua to call the hook again... | ||
| 263 | assert_equal [r ping] "PONG" | ||
| 264 | } | ||
| 265 | |||
| 266 | test {FUNCTION - test function kill not working on eval} { | ||
| 267 | set rd [redis_deferring_client] | ||
| 268 | r config set busy-reply-threshold 10 | ||
| 269 | $rd eval {local a = 1 while true do a = a + 1 end} 0 | ||
| 270 | after 200 | ||
| 271 | catch {r ping} e | ||
| 272 | assert_match {BUSY*} $e | ||
| 273 | catch {r function kill} e | ||
| 274 | assert_match {BUSY*} $e | ||
| 275 | r script kill | ||
| 276 | after 200 ; # Give some time to Lua to call the hook again... | ||
| 277 | assert_equal [r ping] "PONG" | ||
| 278 | } | ||
| 279 | |||
| 280 | test {FUNCTION - test function flush} { | ||
| 281 | r function load REPLACE [get_function_code lua test test {local a = 1 while true do a = a + 1 end}] | ||
| 282 | assert_match {{library_name test engine LUA functions {{name test description {} flags {}}}}} [r function list] | ||
| 283 | r function flush | ||
| 284 | assert_match {} [r function list] | ||
| 285 | |||
| 286 | r function load REPLACE [get_function_code lua test test {local a = 1 while true do a = a + 1 end}] | ||
| 287 | assert_match {{library_name test engine LUA functions {{name test description {} flags {}}}}} [r function list] | ||
| 288 | r function flush async | ||
| 289 | assert_match {} [r function list] | ||
| 290 | |||
| 291 | r function load REPLACE [get_function_code lua test test {local a = 1 while true do a = a + 1 end}] | ||
| 292 | assert_match {{library_name test engine LUA functions {{name test description {} flags {}}}}} [r function list] | ||
| 293 | r function flush sync | ||
| 294 | assert_match {} [r function list] | ||
| 295 | } | ||
| 296 | |||
| 297 | test {FUNCTION - async function flush rebuilds Lua VM without causing race condition between main and lazyfree thread} { | ||
| 298 | # LAZYFREE_THRESHOLD is 64 | ||
| 299 | for {set i 0} {$i < 1000} {incr i} { | ||
| 300 | r function load [get_function_code lua test$i test$i {local a = 1 while true do a = a + 1 end}] | ||
| 301 | } | ||
| 302 | assert_morethan [s used_memory_vm_functions] 100000 | ||
| 303 | r config resetstat | ||
| 304 | r function flush async | ||
| 305 | assert_lessthan [s used_memory_vm_functions] 40000 | ||
| 306 | |||
| 307 | # Wait for the completion of lazy free for both functions and engines. | ||
| 308 | set start_time [clock seconds] | ||
| 309 | while {1} { | ||
| 310 | # Tests for race conditions between async function flushes and main thread Lua VM operations. | ||
| 311 | r function load REPLACE [get_function_code lua test test {local a = 1 while true do a = a + 1 end}] | ||
| 312 | if {[s lazyfreed_objects] == 1001 || [expr {[clock seconds] - $start_time}] > 5} { | ||
| 313 | break | ||
| 314 | } | ||
| 315 | } | ||
| 316 | if {[s lazyfreed_objects] != 1001} { | ||
| 317 | error "Timeout or unexpected number of lazyfreed_objects: [s lazyfreed_objects]" | ||
| 318 | } | ||
| 319 | assert_match {{library_name test engine LUA functions {{name test description {} flags {}}}}} [r function list] | ||
| 320 | } | ||
| 321 | |||
| 322 | test {FUNCTION - test function wrong argument} { | ||
| 323 | catch {r function flush bad_arg} e | ||
| 324 | assert_match {*only supports SYNC|ASYNC*} $e | ||
| 325 | |||
| 326 | catch {r function flush sync extra_arg} e | ||
| 327 | assert_match {*unknown subcommand or wrong number of arguments for 'flush'. Try FUNCTION HELP.} $e | ||
| 328 | } | ||
| 329 | } | ||
| 330 | |||
| 331 | start_server {tags {"scripting repl external:skip"}} { | ||
| 332 | start_server {} { | ||
| 333 | test "Connect a replica to the master instance" { | ||
| 334 | r -1 slaveof [srv 0 host] [srv 0 port] | ||
| 335 | wait_for_condition 150 100 { | ||
| 336 | [s -1 role] eq {slave} && | ||
| 337 | [string match {*master_link_status:up*} [r -1 info replication]] | ||
| 338 | } else { | ||
| 339 | fail "Can't turn the instance into a replica" | ||
| 340 | } | ||
| 341 | } | ||
| 342 | |||
| 343 | test {FUNCTION - creation is replicated to replica} { | ||
| 344 | r function load [get_no_writes_function_code LUA test test {return 'hello'}] | ||
| 345 | wait_for_condition 150 100 { | ||
| 346 | [r -1 function list] eq {{library_name test engine LUA functions {{name test description {} flags no-writes}}}} | ||
| 347 | } else { | ||
| 348 | fail "Failed waiting for function to replicate to replica" | ||
| 349 | } | ||
| 350 | } | ||
| 351 | |||
| 352 | test {FUNCTION - call on replica} { | ||
| 353 | r -1 fcall test 0 | ||
| 354 | } {hello} | ||
| 355 | |||
| 356 | test {FUNCTION - restore is replicated to replica} { | ||
| 357 | set e [r function dump] | ||
| 358 | |||
| 359 | r function delete test | ||
| 360 | wait_for_condition 150 100 { | ||
| 361 | [r -1 function list] eq {} | ||
| 362 | } else { | ||
| 363 | fail "Failed waiting for function to replicate to replica" | ||
| 364 | } | ||
| 365 | |||
| 366 | assert_equal [r function restore $e] {OK} | ||
| 367 | |||
| 368 | wait_for_condition 150 100 { | ||
| 369 | [r -1 function list] eq {{library_name test engine LUA functions {{name test description {} flags no-writes}}}} | ||
| 370 | } else { | ||
| 371 | fail "Failed waiting for function to replicate to replica" | ||
| 372 | } | ||
| 373 | } | ||
| 374 | |||
| 375 | test {FUNCTION - delete is replicated to replica} { | ||
| 376 | r function delete test | ||
| 377 | wait_for_condition 150 100 { | ||
| 378 | [r -1 function list] eq {} | ||
| 379 | } else { | ||
| 380 | fail "Failed waiting for function to replicate to replica" | ||
| 381 | } | ||
| 382 | } | ||
| 383 | |||
| 384 | test {FUNCTION - flush is replicated to replica} { | ||
| 385 | r function load [get_function_code LUA test test {return 'hello'}] | ||
| 386 | wait_for_condition 150 100 { | ||
| 387 | [r -1 function list] eq {{library_name test engine LUA functions {{name test description {} flags {}}}}} | ||
| 388 | } else { | ||
| 389 | fail "Failed waiting for function to replicate to replica" | ||
| 390 | } | ||
| 391 | r function flush | ||
| 392 | wait_for_condition 150 100 { | ||
| 393 | [r -1 function list] eq {} | ||
| 394 | } else { | ||
| 395 | fail "Failed waiting for function to replicate to replica" | ||
| 396 | } | ||
| 397 | } | ||
| 398 | |||
| 399 | test "Disconnecting the replica from master instance" { | ||
| 400 | r -1 slaveof no one | ||
| 401 | # creating a function after disconnect to make sure function | ||
| 402 | # is replicated on rdb phase | ||
| 403 | r function load [get_no_writes_function_code LUA test test {return 'hello'}] | ||
| 404 | |||
| 405 | # reconnect the replica | ||
| 406 | r -1 slaveof [srv 0 host] [srv 0 port] | ||
| 407 | wait_for_condition 150 100 { | ||
| 408 | [s -1 role] eq {slave} && | ||
| 409 | [string match {*master_link_status:up*} [r -1 info replication]] | ||
| 410 | } else { | ||
| 411 | fail "Can't turn the instance into a replica" | ||
| 412 | } | ||
| 413 | } | ||
| 414 | |||
| 415 | test "FUNCTION - test replication to replica on rdb phase" { | ||
| 416 | r -1 fcall test 0 | ||
| 417 | } {hello} | ||
| 418 | |||
| 419 | test "FUNCTION - test replication to replica on rdb phase info command" { | ||
| 420 | r -1 function list | ||
| 421 | } {{library_name test engine LUA functions {{name test description {} flags no-writes}}}} | ||
| 422 | |||
| 423 | test "FUNCTION - create on read only replica" { | ||
| 424 | catch { | ||
| 425 | r -1 function load [get_function_code LUA test test {return 'hello'}] | ||
| 426 | } e | ||
| 427 | set _ $e | ||
| 428 | } {*can't write against a read only replica*} | ||
| 429 | |||
| 430 | test "FUNCTION - delete on read only replica" { | ||
| 431 | catch { | ||
| 432 | r -1 function delete test | ||
| 433 | } e | ||
| 434 | set _ $e | ||
| 435 | } {*can't write against a read only replica*} | ||
| 436 | |||
| 437 | test "FUNCTION - function effect is replicated to replica" { | ||
| 438 | r function load REPLACE [get_function_code LUA test test {return redis.call('set', 'x', '1')}] | ||
| 439 | r fcall test 1 x | ||
| 440 | assert {[r get x] eq {1}} | ||
| 441 | wait_for_condition 150 100 { | ||
| 442 | [r -1 get x] eq {1} | ||
| 443 | } else { | ||
| 444 | fail "Failed waiting function effect to be replicated to replica" | ||
| 445 | } | ||
| 446 | } | ||
| 447 | |||
| 448 | test "FUNCTION - modify key space of read only replica" { | ||
| 449 | catch { | ||
| 450 | r -1 fcall test 1 x | ||
| 451 | } e | ||
| 452 | set _ $e | ||
| 453 | } {READONLY You can't write against a read only replica.} | ||
| 454 | } | ||
| 455 | } | ||
| 456 | |||
| 457 | test {FUNCTION can processes create, delete and flush commands in AOF when doing "debug loadaof" in read-only slaves} { | ||
| 458 | start_server {} { | ||
| 459 | r config set appendonly yes | ||
| 460 | waitForBgrewriteaof r | ||
| 461 | r FUNCTION LOAD "#!lua name=test\nredis.register_function('test', function() return 'hello' end)" | ||
| 462 | r config set slave-read-only yes | ||
| 463 | r slaveof 127.0.0.1 0 | ||
| 464 | r debug loadaof | ||
| 465 | r slaveof no one | ||
| 466 | assert_equal [r function list] {{library_name test engine LUA functions {{name test description {} flags {}}}}} | ||
| 467 | |||
| 468 | r FUNCTION DELETE test | ||
| 469 | |||
| 470 | r slaveof 127.0.0.1 0 | ||
| 471 | r debug loadaof | ||
| 472 | r slaveof no one | ||
| 473 | assert_equal [r function list] {} | ||
| 474 | |||
| 475 | r FUNCTION LOAD "#!lua name=test\nredis.register_function('test', function() return 'hello' end)" | ||
| 476 | r FUNCTION FLUSH | ||
| 477 | |||
| 478 | r slaveof 127.0.0.1 0 | ||
| 479 | r debug loadaof | ||
| 480 | r slaveof no one | ||
| 481 | assert_equal [r function list] {} | ||
| 482 | } | ||
| 483 | } {} {needs:debug external:skip} | ||
| 484 | |||
| 485 | start_server {tags {"scripting"}} { | ||
| 486 | test {LIBRARIES - test shared function can access default globals} { | ||
| 487 | r function load {#!lua name=lib1 | ||
| 488 | local function ping() | ||
| 489 | return redis.call('ping') | ||
| 490 | end | ||
| 491 | redis.register_function( | ||
| 492 | 'f1', | ||
| 493 | function(keys, args) | ||
| 494 | return ping() | ||
| 495 | end | ||
| 496 | ) | ||
| 497 | } | ||
| 498 | r fcall f1 0 | ||
| 499 | } {PONG} | ||
| 500 | |||
| 501 | test {LIBRARIES - usage and code sharing} { | ||
| 502 | r function load REPLACE {#!lua name=lib1 | ||
| 503 | local function add1(a) | ||
| 504 | return a + 1 | ||
| 505 | end | ||
| 506 | redis.register_function( | ||
| 507 | 'f1', | ||
| 508 | function(keys, args) | ||
| 509 | return add1(1) | ||
| 510 | end | ||
| 511 | ) | ||
| 512 | redis.register_function( | ||
| 513 | 'f2', | ||
| 514 | function(keys, args) | ||
| 515 | return add1(2) | ||
| 516 | end | ||
| 517 | ) | ||
| 518 | } | ||
| 519 | assert_equal [r fcall f1 0] {2} | ||
| 520 | assert_equal [r fcall f2 0] {3} | ||
| 521 | r function list | ||
| 522 | } {{library_name lib1 engine LUA functions {*}}} | ||
| 523 | |||
| 524 | test {LIBRARIES - test registration failure revert the entire load} { | ||
| 525 | catch { | ||
| 526 | r function load replace {#!lua name=lib1 | ||
| 527 | local function add1(a) | ||
| 528 | return a + 2 | ||
| 529 | end | ||
| 530 | redis.register_function( | ||
| 531 | 'f1', | ||
| 532 | function(keys, args) | ||
| 533 | return add1(1) | ||
| 534 | end | ||
| 535 | ) | ||
| 536 | redis.register_function( | ||
| 537 | 'f2', | ||
| 538 | 'not a function' | ||
| 539 | ) | ||
| 540 | } | ||
| 541 | } e | ||
| 542 | assert_match {*second argument to redis.register_function must be a function*} $e | ||
| 543 | assert_equal [r fcall f1 0] {2} | ||
| 544 | assert_equal [r fcall f2 0] {3} | ||
| 545 | } | ||
| 546 | |||
| 547 | test {LIBRARIES - test registration function name collision} { | ||
| 548 | catch { | ||
| 549 | r function load replace {#!lua name=lib2 | ||
| 550 | redis.register_function( | ||
| 551 | 'f1', | ||
| 552 | function(keys, args) | ||
| 553 | return 1 | ||
| 554 | end | ||
| 555 | ) | ||
| 556 | } | ||
| 557 | } e | ||
| 558 | assert_match {*Function f1 already exists*} $e | ||
| 559 | assert_equal [r fcall f1 0] {2} | ||
| 560 | assert_equal [r fcall f2 0] {3} | ||
| 561 | } | ||
| 562 | |||
| 563 | test {LIBRARIES - test registration function name collision on same library} { | ||
| 564 | catch { | ||
| 565 | r function load replace {#!lua name=lib2 | ||
| 566 | redis.register_function( | ||
| 567 | 'f1', | ||
| 568 | function(keys, args) | ||
| 569 | return 1 | ||
| 570 | end | ||
| 571 | ) | ||
| 572 | redis.register_function( | ||
| 573 | 'f1', | ||
| 574 | function(keys, args) | ||
| 575 | return 1 | ||
| 576 | end | ||
| 577 | ) | ||
| 578 | } | ||
| 579 | } e | ||
| 580 | set _ $e | ||
| 581 | } {*Function already exists in the library*} | ||
| 582 | |||
| 583 | test {LIBRARIES - test registration with no argument} { | ||
| 584 | catch { | ||
| 585 | r function load replace {#!lua name=lib2 | ||
| 586 | redis.register_function() | ||
| 587 | } | ||
| 588 | } e | ||
| 589 | set _ $e | ||
| 590 | } {*wrong number of arguments to redis.register_function*} | ||
| 591 | |||
| 592 | test {LIBRARIES - test registration with only name} { | ||
| 593 | catch { | ||
| 594 | r function load replace {#!lua name=lib2 | ||
| 595 | redis.register_function('f1') | ||
| 596 | } | ||
| 597 | } e | ||
| 598 | set _ $e | ||
| 599 | } {*calling redis.register_function with a single argument is only applicable to Lua table*} | ||
| 600 | |||
| 601 | test {LIBRARIES - test registration with to many arguments} { | ||
| 602 | catch { | ||
| 603 | r function load replace {#!lua name=lib2 | ||
| 604 | redis.register_function('f1', function() return 1 end, {}, 'description', 'extra arg') | ||
| 605 | } | ||
| 606 | } e | ||
| 607 | set _ $e | ||
| 608 | } {*wrong number of arguments to redis.register_function*} | ||
| 609 | |||
| 610 | test {LIBRARIES - test registration with no string name} { | ||
| 611 | catch { | ||
| 612 | r function load replace {#!lua name=lib2 | ||
| 613 | redis.register_function(nil, function() return 1 end) | ||
| 614 | } | ||
| 615 | } e | ||
| 616 | set _ $e | ||
| 617 | } {*first argument to redis.register_function must be a string*} | ||
| 618 | |||
| 619 | test {LIBRARIES - test registration with wrong name format} { | ||
| 620 | catch { | ||
| 621 | r function load replace {#!lua name=lib2 | ||
| 622 | redis.register_function('test\0test', function() return 1 end) | ||
| 623 | } | ||
| 624 | } e | ||
| 625 | set _ $e | ||
| 626 | } {*Library names can only contain letters, numbers, or underscores(_) and must be at least one character long*} | ||
| 627 | |||
| 628 | test {LIBRARIES - test registration with empty name} { | ||
| 629 | catch { | ||
| 630 | r function load replace {#!lua name=lib2 | ||
| 631 | redis.register_function('', function() return 1 end) | ||
| 632 | } | ||
| 633 | } e | ||
| 634 | set _ $e | ||
| 635 | } {*Library names can only contain letters, numbers, or underscores(_) and must be at least one character long*} | ||
| 636 | |||
| 637 | test {LIBRARIES - math.random from function load} { | ||
| 638 | catch { | ||
| 639 | r function load replace {#!lua name=lib2 | ||
| 640 | return math.random() | ||
| 641 | } | ||
| 642 | } e | ||
| 643 | set _ $e | ||
| 644 | } {*attempted to access nonexistent global variable 'math'*} | ||
| 645 | |||
| 646 | test {LIBRARIES - redis.call from function load} { | ||
| 647 | catch { | ||
| 648 | r function load replace {#!lua name=lib2 | ||
| 649 | return redis.call('ping') | ||
| 650 | } | ||
| 651 | } e | ||
| 652 | set _ $e | ||
| 653 | } {*attempted to access nonexistent global variable 'call'*} | ||
| 654 | |||
| 655 | test {LIBRARIES - redis.setresp from function load} { | ||
| 656 | catch { | ||
| 657 | r function load replace {#!lua name=lib2 | ||
| 658 | return redis.setresp(3) | ||
| 659 | } | ||
| 660 | } e | ||
| 661 | set _ $e | ||
| 662 | } {*attempted to access nonexistent global variable 'setresp'*} | ||
| 663 | |||
| 664 | test {LIBRARIES - redis.set_repl from function load} { | ||
| 665 | catch { | ||
| 666 | r function load replace {#!lua name=lib2 | ||
| 667 | return redis.set_repl(redis.REPL_NONE) | ||
| 668 | } | ||
| 669 | } e | ||
| 670 | set _ $e | ||
| 671 | } {*attempted to access nonexistent global variable 'set_repl'*} | ||
| 672 | |||
| 673 | test {LIBRARIES - redis.acl_check_cmd from function load} { | ||
| 674 | catch { | ||
| 675 | r function load replace {#!lua name=lib2 | ||
| 676 | return redis.acl_check_cmd('set','xx',1) | ||
| 677 | } | ||
| 678 | } e | ||
| 679 | set _ $e | ||
| 680 | } {*attempted to access nonexistent global variable 'acl_check_cmd'*} | ||
| 681 | |||
| 682 | test {LIBRARIES - malicious access test} { | ||
| 683 | # the 'library' API is not exposed inside a | ||
| 684 | # function context and the 'redis' API is not | ||
| 685 | # expose on the library registration context. | ||
| 686 | # But a malicious user might find a way to hack it | ||
| 687 | # (as demonstrated in this test). This is why we | ||
| 688 | # have another level of protection on the C | ||
| 689 | # code itself and we want to test it and verify | ||
| 690 | # that it works properly. | ||
| 691 | r function load replace {#!lua name=lib1 | ||
| 692 | local lib = redis | ||
| 693 | lib.register_function('f1', function () | ||
| 694 | lib.redis = redis | ||
| 695 | lib.math = math | ||
| 696 | return {ok='OK'} | ||
| 697 | end) | ||
| 698 | |||
| 699 | lib.register_function('f2', function () | ||
| 700 | lib.register_function('f1', function () | ||
| 701 | lib.redis = redis | ||
| 702 | lib.math = math | ||
| 703 | return {ok='OK'} | ||
| 704 | end) | ||
| 705 | end) | ||
| 706 | } | ||
| 707 | catch {[r fcall f1 0]} e | ||
| 708 | assert_match {*Attempt to modify a readonly table*} $e | ||
| 709 | |||
| 710 | catch {[r function load {#!lua name=lib2 | ||
| 711 | redis.math.random() | ||
| 712 | }]} e | ||
| 713 | assert_match {*Script attempted to access nonexistent global variable 'math'*} $e | ||
| 714 | |||
| 715 | catch {[r function load {#!lua name=lib2 | ||
| 716 | redis.redis.call('ping') | ||
| 717 | }]} e | ||
| 718 | assert_match {*Script attempted to access nonexistent global variable 'redis'*} $e | ||
| 719 | |||
| 720 | catch {[r fcall f2 0]} e | ||
| 721 | assert_match {*can only be called on FUNCTION LOAD command*} $e | ||
| 722 | } | ||
| 723 | |||
| 724 | test {LIBRARIES - delete removed all functions on library} { | ||
| 725 | r function delete lib1 | ||
| 726 | r function list | ||
| 727 | } {} | ||
| 728 | |||
| 729 | test {LIBRARIES - register function inside a function} { | ||
| 730 | r function load {#!lua name=lib | ||
| 731 | redis.register_function( | ||
| 732 | 'f1', | ||
| 733 | function(keys, args) | ||
| 734 | redis.register_function( | ||
| 735 | 'f2', | ||
| 736 | function(key, args) | ||
| 737 | return 2 | ||
| 738 | end | ||
| 739 | ) | ||
| 740 | return 1 | ||
| 741 | end | ||
| 742 | ) | ||
| 743 | } | ||
| 744 | catch {r fcall f1 0} e | ||
| 745 | set _ $e | ||
| 746 | } {*attempt to call field 'register_function' (a nil value)*} | ||
| 747 | |||
| 748 | test {LIBRARIES - register library with no functions} { | ||
| 749 | r function flush | ||
| 750 | catch { | ||
| 751 | r function load {#!lua name=lib | ||
| 752 | return 1 | ||
| 753 | } | ||
| 754 | } e | ||
| 755 | set _ $e | ||
| 756 | } {*No functions registered*} | ||
| 757 | |||
| 758 | test {LIBRARIES - load timeout} { | ||
| 759 | catch { | ||
| 760 | r function load {#!lua name=lib | ||
| 761 | local a = 1 | ||
| 762 | while 1 do a = a + 1 end | ||
| 763 | } | ||
| 764 | } e | ||
| 765 | set _ $e | ||
| 766 | } {*FUNCTION LOAD timeout*} | ||
| 767 | |||
| 768 | test {LIBRARIES - verify global protection on the load run} { | ||
| 769 | catch { | ||
| 770 | r function load {#!lua name=lib | ||
| 771 | a = 1 | ||
| 772 | } | ||
| 773 | } e | ||
| 774 | set _ $e | ||
| 775 | } {*Attempt to modify a readonly table*} | ||
| 776 | |||
| 777 | test {LIBRARIES - named arguments} { | ||
| 778 | r function load {#!lua name=lib | ||
| 779 | redis.register_function{ | ||
| 780 | function_name='f1', | ||
| 781 | callback=function() | ||
| 782 | return 'hello' | ||
| 783 | end, | ||
| 784 | description='some desc' | ||
| 785 | } | ||
| 786 | } | ||
| 787 | r function list | ||
| 788 | } {{library_name lib engine LUA functions {{name f1 description {some desc} flags {}}}}} | ||
| 789 | |||
| 790 | test {LIBRARIES - named arguments, bad function name} { | ||
| 791 | catch { | ||
| 792 | r function load replace {#!lua name=lib | ||
| 793 | redis.register_function{ | ||
| 794 | function_name=function() return 1 end, | ||
| 795 | callback=function() | ||
| 796 | return 'hello' | ||
| 797 | end, | ||
| 798 | description='some desc' | ||
| 799 | } | ||
| 800 | } | ||
| 801 | } e | ||
| 802 | set _ $e | ||
| 803 | } {*function_name argument given to redis.register_function must be a string*} | ||
| 804 | |||
| 805 | test {LIBRARIES - named arguments, bad callback type} { | ||
| 806 | catch { | ||
| 807 | r function load replace {#!lua name=lib | ||
| 808 | redis.register_function{ | ||
| 809 | function_name='f1', | ||
| 810 | callback='bad', | ||
| 811 | description='some desc' | ||
| 812 | } | ||
| 813 | } | ||
| 814 | } e | ||
| 815 | set _ $e | ||
| 816 | } {*callback argument given to redis.register_function must be a function*} | ||
| 817 | |||
| 818 | test {LIBRARIES - named arguments, bad description} { | ||
| 819 | catch { | ||
| 820 | r function load replace {#!lua name=lib | ||
| 821 | redis.register_function{ | ||
| 822 | function_name='f1', | ||
| 823 | callback=function() | ||
| 824 | return 'hello' | ||
| 825 | end, | ||
| 826 | description=function() return 1 end | ||
| 827 | } | ||
| 828 | } | ||
| 829 | } e | ||
| 830 | set _ $e | ||
| 831 | } {*description argument given to redis.register_function must be a string*} | ||
| 832 | |||
| 833 | test {LIBRARIES - named arguments, unknown argument} { | ||
| 834 | catch { | ||
| 835 | r function load replace {#!lua name=lib | ||
| 836 | redis.register_function{ | ||
| 837 | function_name='f1', | ||
| 838 | callback=function() | ||
| 839 | return 'hello' | ||
| 840 | end, | ||
| 841 | description='desc', | ||
| 842 | some_unknown='unknown' | ||
| 843 | } | ||
| 844 | } | ||
| 845 | } e | ||
| 846 | set _ $e | ||
| 847 | } {*unknown argument given to redis.register_function*} | ||
| 848 | |||
| 849 | test {LIBRARIES - named arguments, missing function name} { | ||
| 850 | catch { | ||
| 851 | r function load replace {#!lua name=lib | ||
| 852 | redis.register_function{ | ||
| 853 | callback=function() | ||
| 854 | return 'hello' | ||
| 855 | end, | ||
| 856 | description='desc' | ||
| 857 | } | ||
| 858 | } | ||
| 859 | } e | ||
| 860 | set _ $e | ||
| 861 | } {*redis.register_function must get a function name argument*} | ||
| 862 | |||
| 863 | test {LIBRARIES - named arguments, missing callback} { | ||
| 864 | catch { | ||
| 865 | r function load replace {#!lua name=lib | ||
| 866 | redis.register_function{ | ||
| 867 | function_name='f1', | ||
| 868 | description='desc' | ||
| 869 | } | ||
| 870 | } | ||
| 871 | } e | ||
| 872 | set _ $e | ||
| 873 | } {*redis.register_function must get a callback argument*} | ||
| 874 | |||
| 875 | test {FUNCTION - test function restore with function name collision} { | ||
| 876 | r function flush | ||
| 877 | r function load {#!lua name=lib1 | ||
| 878 | local function add1(a) | ||
| 879 | return a + 1 | ||
| 880 | end | ||
| 881 | redis.register_function( | ||
| 882 | 'f1', | ||
| 883 | function(keys, args) | ||
| 884 | return add1(1) | ||
| 885 | end | ||
| 886 | ) | ||
| 887 | redis.register_function( | ||
| 888 | 'f2', | ||
| 889 | function(keys, args) | ||
| 890 | return add1(2) | ||
| 891 | end | ||
| 892 | ) | ||
| 893 | redis.register_function( | ||
| 894 | 'f3', | ||
| 895 | function(keys, args) | ||
| 896 | return add1(3) | ||
| 897 | end | ||
| 898 | ) | ||
| 899 | } | ||
| 900 | set e [r function dump] | ||
| 901 | r function flush | ||
| 902 | |||
| 903 | # load a library with different name but with the same function name | ||
| 904 | r function load {#!lua name=lib1 | ||
| 905 | redis.register_function( | ||
| 906 | 'f6', | ||
| 907 | function(keys, args) | ||
| 908 | return 7 | ||
| 909 | end | ||
| 910 | ) | ||
| 911 | } | ||
| 912 | r function load {#!lua name=lib2 | ||
| 913 | local function add1(a) | ||
| 914 | return a + 1 | ||
| 915 | end | ||
| 916 | redis.register_function( | ||
| 917 | 'f4', | ||
| 918 | function(keys, args) | ||
| 919 | return add1(4) | ||
| 920 | end | ||
| 921 | ) | ||
| 922 | redis.register_function( | ||
| 923 | 'f5', | ||
| 924 | function(keys, args) | ||
| 925 | return add1(5) | ||
| 926 | end | ||
| 927 | ) | ||
| 928 | redis.register_function( | ||
| 929 | 'f3', | ||
| 930 | function(keys, args) | ||
| 931 | return add1(3) | ||
| 932 | end | ||
| 933 | ) | ||
| 934 | } | ||
| 935 | |||
| 936 | catch {r function restore $e} error | ||
| 937 | assert_match {*Library lib1 already exists*} $error | ||
| 938 | assert_equal [r fcall f3 0] {4} | ||
| 939 | assert_equal [r fcall f4 0] {5} | ||
| 940 | assert_equal [r fcall f5 0] {6} | ||
| 941 | assert_equal [r fcall f6 0] {7} | ||
| 942 | |||
| 943 | catch {r function restore $e replace} error | ||
| 944 | assert_match {*Function f3 already exists*} $error | ||
| 945 | assert_equal [r fcall f3 0] {4} | ||
| 946 | assert_equal [r fcall f4 0] {5} | ||
| 947 | assert_equal [r fcall f5 0] {6} | ||
| 948 | assert_equal [r fcall f6 0] {7} | ||
| 949 | } | ||
| 950 | |||
| 951 | test {FUNCTION - test function list with code} { | ||
| 952 | r function flush | ||
| 953 | r function load {#!lua name=library1 | ||
| 954 | redis.register_function('f6', function(keys, args) return 7 end) | ||
| 955 | } | ||
| 956 | r function list withcode | ||
| 957 | } {{library_name library1 engine LUA functions {{name f6 description {} flags {}}} library_code {*redis.register_function('f6', function(keys, args) return 7 end)*}}} | ||
| 958 | |||
| 959 | test {FUNCTION - test function list with pattern} { | ||
| 960 | r function load {#!lua name=lib1 | ||
| 961 | redis.register_function('f7', function(keys, args) return 7 end) | ||
| 962 | } | ||
| 963 | r function list libraryname library* | ||
| 964 | } {{library_name library1 engine LUA functions {{name f6 description {} flags {}}}}} | ||
| 965 | |||
| 966 | test {FUNCTION - test function list wrong argument} { | ||
| 967 | catch {r function list bad_argument} e | ||
| 968 | set _ $e | ||
| 969 | } {*Unknown argument bad_argument*} | ||
| 970 | |||
| 971 | test {FUNCTION - test function list with bad argument to library name} { | ||
| 972 | catch {r function list libraryname} e | ||
| 973 | set _ $e | ||
| 974 | } {*library name argument was not given*} | ||
| 975 | |||
| 976 | test {FUNCTION - test function list withcode multiple times} { | ||
| 977 | catch {r function list withcode withcode} e | ||
| 978 | set _ $e | ||
| 979 | } {*Unknown argument withcode*} | ||
| 980 | |||
| 981 | test {FUNCTION - test function list libraryname multiple times} { | ||
| 982 | catch {r function list withcode libraryname foo libraryname foo} e | ||
| 983 | set _ $e | ||
| 984 | } {*Unknown argument libraryname*} | ||
| 985 | |||
| 986 | test {FUNCTION - verify OOM on function load and function restore} { | ||
| 987 | r function flush | ||
| 988 | r function load replace {#!lua name=test | ||
| 989 | redis.register_function('f1', function() return 1 end) | ||
| 990 | } | ||
| 991 | set payload [r function dump] | ||
| 992 | r config set maxmemory 1 | ||
| 993 | |||
| 994 | r function flush | ||
| 995 | catch {r function load replace {#!lua name=test | ||
| 996 | redis.register_function('f1', function() return 1 end) | ||
| 997 | }} e | ||
| 998 | assert_match {*command not allowed when used memory*} $e | ||
| 999 | |||
| 1000 | r function flush | ||
| 1001 | catch {r function restore $payload} e | ||
| 1002 | assert_match {*command not allowed when used memory*} $e | ||
| 1003 | |||
| 1004 | r config set maxmemory 0 | ||
| 1005 | } {OK} {needs:config-maxmemory} | ||
| 1006 | |||
| 1007 | test {FUNCTION - verify allow-omm allows running any command} { | ||
| 1008 | r FUNCTION load replace {#!lua name=f1 | ||
| 1009 | redis.register_function{ | ||
| 1010 | function_name='f1', | ||
| 1011 | callback=function() return redis.call('set', 'x', '1') end, | ||
| 1012 | flags={'allow-oom'} | ||
| 1013 | } | ||
| 1014 | } | ||
| 1015 | |||
| 1016 | r config set maxmemory 1 | ||
| 1017 | |||
| 1018 | assert_match {OK} [r fcall f1 1 x] | ||
| 1019 | assert_match {1} [r get x] | ||
| 1020 | |||
| 1021 | r config set maxmemory 0 | ||
| 1022 | } {OK} {needs:config-maxmemory} | ||
| 1023 | } | ||
| 1024 | |||
| 1025 | start_server {tags {"scripting"}} { | ||
| 1026 | test {FUNCTION - wrong flags type named arguments} { | ||
| 1027 | catch {r function load replace {#!lua name=test | ||
| 1028 | redis.register_function{ | ||
| 1029 | function_name = 'f1', | ||
| 1030 | callback = function() return 1 end, | ||
| 1031 | flags = 'bad flags type' | ||
| 1032 | } | ||
| 1033 | }} e | ||
| 1034 | set _ $e | ||
| 1035 | } {*flags argument to redis.register_function must be a table representing function flags*} | ||
| 1036 | |||
| 1037 | test {FUNCTION - wrong flag type} { | ||
| 1038 | catch {r function load replace {#!lua name=test | ||
| 1039 | redis.register_function{ | ||
| 1040 | function_name = 'f1', | ||
| 1041 | callback = function() return 1 end, | ||
| 1042 | flags = {function() return 1 end} | ||
| 1043 | } | ||
| 1044 | }} e | ||
| 1045 | set _ $e | ||
| 1046 | } {*unknown flag given*} | ||
| 1047 | |||
| 1048 | test {FUNCTION - unknown flag} { | ||
| 1049 | catch {r function load replace {#!lua name=test | ||
| 1050 | redis.register_function{ | ||
| 1051 | function_name = 'f1', | ||
| 1052 | callback = function() return 1 end, | ||
| 1053 | flags = {'unknown'} | ||
| 1054 | } | ||
| 1055 | }} e | ||
| 1056 | set _ $e | ||
| 1057 | } {*unknown flag given*} | ||
| 1058 | |||
| 1059 | test {FUNCTION - write script on fcall_ro} { | ||
| 1060 | r function load replace {#!lua name=test | ||
| 1061 | redis.register_function{ | ||
| 1062 | function_name = 'f1', | ||
| 1063 | callback = function() return redis.call('set', 'x', 1) end | ||
| 1064 | } | ||
| 1065 | } | ||
| 1066 | catch {r fcall_ro f1 1 x} e | ||
| 1067 | set _ $e | ||
| 1068 | } {*Can not execute a script with write flag using \*_ro command*} | ||
| 1069 | |||
| 1070 | test {FUNCTION - write script with no-writes flag} { | ||
| 1071 | r function load replace {#!lua name=test | ||
| 1072 | redis.register_function{ | ||
| 1073 | function_name = 'f1', | ||
| 1074 | callback = function() return redis.call('set', 'x', 1) end, | ||
| 1075 | flags = {'no-writes'} | ||
| 1076 | } | ||
| 1077 | } | ||
| 1078 | catch {r fcall f1 1 x} e | ||
| 1079 | set _ $e | ||
| 1080 | } {*Write commands are not allowed from read-only scripts*} | ||
| 1081 | |||
| 1082 | test {FUNCTION - deny oom} { | ||
| 1083 | r FUNCTION load replace {#!lua name=test | ||
| 1084 | redis.register_function('f1', function() return redis.call('set', 'x', '1') end) | ||
| 1085 | } | ||
| 1086 | |||
| 1087 | r config set maxmemory 1 | ||
| 1088 | |||
| 1089 | catch {[r fcall f1 1 x]} e | ||
| 1090 | assert_match {OOM *when used memory > 'maxmemory'*} $e | ||
| 1091 | |||
| 1092 | r config set maxmemory 0 | ||
| 1093 | } {OK} {needs:config-maxmemory} | ||
| 1094 | |||
| 1095 | test {FUNCTION - deny oom on no-writes function} { | ||
| 1096 | r FUNCTION load replace {#!lua name=test | ||
| 1097 | redis.register_function{function_name='f1', callback=function() return 'hello' end, flags={'no-writes'}} | ||
| 1098 | } | ||
| 1099 | |||
| 1100 | r config set maxmemory 1 | ||
| 1101 | |||
| 1102 | assert_equal [r fcall f1 1 k] hello | ||
| 1103 | assert_equal [r fcall_ro f1 1 k] hello | ||
| 1104 | |||
| 1105 | r config set maxmemory 0 | ||
| 1106 | } {OK} {needs:config-maxmemory} | ||
| 1107 | |||
| 1108 | test {FUNCTION - allow stale} { | ||
| 1109 | r FUNCTION load replace {#!lua name=test | ||
| 1110 | redis.register_function{function_name='f1', callback=function() return 'hello' end, flags={'no-writes'}} | ||
| 1111 | redis.register_function{function_name='f2', callback=function() return 'hello' end, flags={'allow-stale', 'no-writes'}} | ||
| 1112 | redis.register_function{function_name='f3', callback=function() return redis.call('get', 'x') end, flags={'allow-stale', 'no-writes'}} | ||
| 1113 | redis.register_function{function_name='f4', callback=function() return redis.call('info', 'server') end, flags={'allow-stale', 'no-writes'}} | ||
| 1114 | } | ||
| 1115 | |||
| 1116 | r config set replica-serve-stale-data no | ||
| 1117 | r replicaof 127.0.0.1 1 | ||
| 1118 | |||
| 1119 | catch {[r fcall f1 0]} e | ||
| 1120 | assert_match {MASTERDOWN *} $e | ||
| 1121 | |||
| 1122 | assert_equal {hello} [r fcall f2 0] | ||
| 1123 | |||
| 1124 | catch {[r fcall f3 1 x]} e | ||
| 1125 | assert_match {ERR *Can not execute the command on a stale replica*} $e | ||
| 1126 | |||
| 1127 | assert_match {*redis_version*} [r fcall f4 0] | ||
| 1128 | |||
| 1129 | r replicaof no one | ||
| 1130 | r config set replica-serve-stale-data yes | ||
| 1131 | set _ {} | ||
| 1132 | } {} {external:skip} | ||
| 1133 | |||
| 1134 | test {FUNCTION - redis version api} { | ||
| 1135 | r FUNCTION load replace {#!lua name=test | ||
| 1136 | local version = redis.REDIS_VERSION_NUM | ||
| 1137 | |||
| 1138 | redis.register_function{function_name='get_version_v1', callback=function() | ||
| 1139 | return string.format('%s.%s.%s', | ||
| 1140 | bit.band(bit.rshift(version, 16), 0x000000ff), | ||
| 1141 | bit.band(bit.rshift(version, 8), 0x000000ff), | ||
| 1142 | bit.band(version, 0x000000ff)) | ||
| 1143 | end} | ||
| 1144 | redis.register_function{function_name='get_version_v2', callback=function() return redis.REDIS_VERSION end} | ||
| 1145 | } | ||
| 1146 | |||
| 1147 | catch {[r fcall f1 0]} e | ||
| 1148 | assert_equal [r fcall get_version_v1 0] [r fcall get_version_v2 0] | ||
| 1149 | } | ||
| 1150 | |||
| 1151 | test {FUNCTION - function stats} { | ||
| 1152 | r FUNCTION FLUSH | ||
| 1153 | |||
| 1154 | r FUNCTION load {#!lua name=test1 | ||
| 1155 | redis.register_function('f1', function() return 1 end) | ||
| 1156 | redis.register_function('f2', function() return 1 end) | ||
| 1157 | } | ||
| 1158 | |||
| 1159 | r FUNCTION load {#!lua name=test2 | ||
| 1160 | redis.register_function('f3', function() return 1 end) | ||
| 1161 | } | ||
| 1162 | |||
| 1163 | r function stats | ||
| 1164 | } {running_script {} engines {LUA {libraries_count 2 functions_count 3}}} | ||
| 1165 | |||
| 1166 | test {FUNCTION - function stats reloaded correctly from rdb} { | ||
| 1167 | r debug reload | ||
| 1168 | r function stats | ||
| 1169 | } {running_script {} engines {LUA {libraries_count 2 functions_count 3}}} {needs:debug} | ||
| 1170 | |||
| 1171 | test {FUNCTION - function stats delete library} { | ||
| 1172 | r function delete test1 | ||
| 1173 | r function stats | ||
| 1174 | } {running_script {} engines {LUA {libraries_count 1 functions_count 1}}} | ||
| 1175 | |||
| 1176 | test {FUNCTION - test function stats on loading failure} { | ||
| 1177 | r FUNCTION FLUSH | ||
| 1178 | |||
| 1179 | r FUNCTION load {#!lua name=test1 | ||
| 1180 | redis.register_function('f1', function() return 1 end) | ||
| 1181 | redis.register_function('f2', function() return 1 end) | ||
| 1182 | } | ||
| 1183 | |||
| 1184 | catch {r FUNCTION load {#!lua name=test1 | ||
| 1185 | redis.register_function('f3', function() return 1 end) | ||
| 1186 | }} e | ||
| 1187 | assert_match "*Library 'test1' already exists*" $e | ||
| 1188 | |||
| 1189 | |||
| 1190 | r function stats | ||
| 1191 | } {running_script {} engines {LUA {libraries_count 1 functions_count 2}}} | ||
| 1192 | |||
| 1193 | test {FUNCTION - function stats cleaned after flush} { | ||
| 1194 | r function flush | ||
| 1195 | r function stats | ||
| 1196 | } {running_script {} engines {LUA {libraries_count 0 functions_count 0}}} | ||
| 1197 | |||
| 1198 | test {FUNCTION - function test empty engine} { | ||
| 1199 | catch {r function load replace {#! name=test | ||
| 1200 | redis.register_function('foo', function() return 1 end) | ||
| 1201 | }} e | ||
| 1202 | set _ $e | ||
| 1203 | } {ERR Engine '' not found} | ||
| 1204 | |||
| 1205 | test {FUNCTION - function test unknown metadata value} { | ||
| 1206 | catch {r function load replace {#!lua name=test foo=bar | ||
| 1207 | redis.register_function('foo', function() return 1 end) | ||
| 1208 | }} e | ||
| 1209 | set _ $e | ||
| 1210 | } {ERR Invalid metadata value given: foo=bar} | ||
| 1211 | |||
| 1212 | test {FUNCTION - function test no name} { | ||
| 1213 | catch {r function load replace {#!lua | ||
| 1214 | redis.register_function('foo', function() return 1 end) | ||
| 1215 | }} e | ||
| 1216 | set _ $e | ||
| 1217 | } {ERR Library name was not given} | ||
| 1218 | |||
| 1219 | test {FUNCTION - function test multiple names} { | ||
| 1220 | catch {r function load replace {#!lua name=foo name=bar | ||
| 1221 | redis.register_function('foo', function() return 1 end) | ||
| 1222 | }} e | ||
| 1223 | set _ $e | ||
| 1224 | } {ERR Invalid metadata value, name argument was given multiple times} | ||
| 1225 | |||
| 1226 | test {FUNCTION - function test name with quotes} { | ||
| 1227 | r function load replace {#!lua name="foo" | ||
| 1228 | redis.register_function('foo', function() return 1 end) | ||
| 1229 | } | ||
| 1230 | } {foo} | ||
| 1231 | |||
| 1232 | test {FUNCTION - trick global protection 1} { | ||
| 1233 | r FUNCTION FLUSH | ||
| 1234 | |||
| 1235 | r FUNCTION load {#!lua name=test1 | ||
| 1236 | redis.register_function('f1', function() | ||
| 1237 | mt = getmetatable(_G) | ||
| 1238 | original_globals = mt.__index | ||
| 1239 | original_globals['redis'] = function() return 1 end | ||
| 1240 | end) | ||
| 1241 | } | ||
| 1242 | |||
| 1243 | catch {[r fcall f1 0]} e | ||
| 1244 | set _ $e | ||
| 1245 | } {*Attempt to modify a readonly table*} | ||
| 1246 | |||
| 1247 | test {FUNCTION - test getmetatable on script load} { | ||
| 1248 | r FUNCTION FLUSH | ||
| 1249 | |||
| 1250 | catch { | ||
| 1251 | r FUNCTION load {#!lua name=test1 | ||
| 1252 | mt = getmetatable(_G) | ||
| 1253 | } | ||
| 1254 | } e | ||
| 1255 | |||
| 1256 | set _ $e | ||
| 1257 | } {*Script attempted to access nonexistent global variable 'getmetatable'*} | ||
| 1258 | |||
| 1259 | } | ||
