aboutsummaryrefslogtreecommitdiff
path: root/examples/redis-unstable/tests/unit/functions.tcl
diff options
context:
space:
mode:
authorMitja Felicijan <mitja.felicijan@gmail.com>2026-01-21 22:52:54 +0100
committerMitja Felicijan <mitja.felicijan@gmail.com>2026-01-21 22:52:54 +0100
commitdcacc00e3750300617ba6e16eb346713f91a783a (patch)
tree38e2d4fb5ed9d119711d4295c6eda4b014af73fd /examples/redis-unstable/tests/unit/functions.tcl
parent58dac10aeb8f5a041c46bddbeaf4c7966a99b998 (diff)
downloadcrep-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.tcl1259
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 @@
1proc 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
5proc 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
9start_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
331start_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
457test {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
485start_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
1025start_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}