1start_server {tags {"scripting"}} {
2    test {EVAL - Does Lua interpreter replies to our requests?} {
3        r eval {return 'hello'} 0
4    } {hello}
5
6    test {EVAL - Lua integer -> Redis protocol type conversion} {
7        r eval {return 100.5} 0
8    } {100}
9
10    test {EVAL - Lua string -> Redis protocol type conversion} {
11        r eval {return 'hello world'} 0
12    } {hello world}
13
14    test {EVAL - Lua true boolean -> Redis protocol type conversion} {
15        r eval {return true} 0
16    } {1}
17
18    test {EVAL - Lua false boolean -> Redis protocol type conversion} {
19        r eval {return false} 0
20    } {}
21
22    test {EVAL - Lua status code reply -> Redis protocol type conversion} {
23        r eval {return {ok='fine'}} 0
24    } {fine}
25
26    test {EVAL - Lua error reply -> Redis protocol type conversion} {
27        catch {
28            r eval {return {err='this is an error'}} 0
29        } e
30        set _ $e
31    } {this is an error}
32
33    test {EVAL - Lua table -> Redis protocol type conversion} {
34        r eval {return {1,2,3,'ciao',{1,2}}} 0
35    } {1 2 3 ciao {1 2}}
36
37    test {EVAL - Are the KEYS and ARGV arrays populated correctly?} {
38        r eval {return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}} 2 a b c d
39    } {a b c d}
40
41    test {EVAL - is Lua able to call Redis API?} {
42        r set mykey myval
43        r eval {return redis.call('get',KEYS[1])} 1 mykey
44    } {myval}
45
46    test {EVALSHA - Can we call a SHA1 if already defined?} {
47        r evalsha fd758d1589d044dd850a6f05d52f2eefd27f033f 1 mykey
48    } {myval}
49
50    test {EVALSHA - Can we call a SHA1 in uppercase?} {
51        r evalsha FD758D1589D044DD850A6F05D52F2EEFD27F033F 1 mykey
52    } {myval}
53
54    test {EVALSHA - Do we get an error on invalid SHA1?} {
55        catch {r evalsha NotValidShaSUM 0} e
56        set _ $e
57    } {NOSCRIPT*}
58
59    test {EVALSHA - Do we get an error on non defined SHA1?} {
60        catch {r evalsha ffd632c7d33e571e9f24556ebed26c3479a87130 0} e
61        set _ $e
62    } {NOSCRIPT*}
63
64    test {EVAL - Redis integer -> Lua type conversion} {
65        r set x 0
66        r eval {
67            local foo = redis.pcall('incr',KEYS[1])
68            return {type(foo),foo}
69        } 1 x
70    } {number 1}
71
72    test {EVAL - Redis bulk -> Lua type conversion} {
73        r set mykey myval
74        r eval {
75            local foo = redis.pcall('get',KEYS[1])
76            return {type(foo),foo}
77        } 1 mykey
78    } {string myval}
79
80    test {EVAL - Redis multi bulk -> Lua type conversion} {
81        r del mylist
82        r rpush mylist a
83        r rpush mylist b
84        r rpush mylist c
85        r eval {
86            local foo = redis.pcall('lrange',KEYS[1],0,-1)
87            return {type(foo),foo[1],foo[2],foo[3],# foo}
88        } 1 mylist
89    } {table a b c 3}
90
91    test {EVAL - Redis status reply -> Lua type conversion} {
92        r eval {
93            local foo = redis.pcall('set',KEYS[1],'myval')
94            return {type(foo),foo['ok']}
95        } 1 mykey
96    } {table OK}
97
98    test {EVAL - Redis error reply -> Lua type conversion} {
99        r set mykey myval
100        r eval {
101            local foo = redis.pcall('incr',KEYS[1])
102            return {type(foo),foo['err']}
103        } 1 mykey
104    } {table {ERR value is not an integer or out of range}}
105
106    test {EVAL - Redis nil bulk reply -> Lua type conversion} {
107        r del mykey
108        r eval {
109            local foo = redis.pcall('get',KEYS[1])
110            return {type(foo),foo == false}
111        } 1 mykey
112    } {boolean 1}
113
114    test {EVAL - Is the Lua client using the currently selected DB?} {
115        r set mykey "this is DB 9"
116        r select 10
117        r set mykey "this is DB 10"
118        r eval {return redis.pcall('get',KEYS[1])} 1 mykey
119    } {this is DB 10}
120
121    test {EVAL - SELECT inside Lua should not affect the caller} {
122        # here we DB 10 is selected
123        r set mykey "original value"
124        r eval {return redis.pcall('select','9')} 0
125        set res [r get mykey]
126        r select 9
127        set res
128    } {original value}
129
130    if 0 {
131        test {EVAL - Script can't run more than configured time limit} {
132            r config set lua-time-limit 1
133            catch {
134                r eval {
135                    local i = 0
136                    while true do i=i+1 end
137                } 0
138            } e
139            set _ $e
140        } {*execution time*}
141    }
142
143    test {EVAL - Scripts can't run certain commands} {
144        set e {}
145        catch {r eval {return redis.pcall('blpop','x',0)} 0} e
146        set e
147    } {*not allowed*}
148
149    test {EVAL - Scripts can't run certain commands} {
150        set e {}
151        r debug lua-always-replicate-commands 0
152        catch {
153            r eval "redis.pcall('randomkey'); return redis.pcall('set','x','ciao')" 0
154        } e
155        r debug lua-always-replicate-commands 1
156        set e
157    } {*not allowed after*}
158
159    test {EVAL - No arguments to redis.call/pcall is considered an error} {
160        set e {}
161        catch {r eval {return redis.call()} 0} e
162        set e
163    } {*one argument*}
164
165    test {EVAL - redis.call variant raises a Lua error on Redis cmd error (1)} {
166        set e {}
167        catch {
168            r eval "redis.call('nosuchcommand')" 0
169        } e
170        set e
171    } {*Unknown Redis*}
172
173    test {EVAL - redis.call variant raises a Lua error on Redis cmd error (1)} {
174        set e {}
175        catch {
176            r eval "redis.call('get','a','b','c')" 0
177        } e
178        set e
179    } {*number of args*}
180
181    test {EVAL - redis.call variant raises a Lua error on Redis cmd error (1)} {
182        set e {}
183        r set foo bar
184        catch {
185            r eval {redis.call('lpush',KEYS[1],'val')} 1 foo
186        } e
187        set e
188    } {*against a key*}
189
190    test {EVAL - JSON numeric decoding} {
191        # We must return the table as a string because otherwise
192        # Redis converts floats to ints and we get 0 and 1023 instead
193        # of 0.0003 and 1023.2 as the parsed output.
194        r eval {return
195                 table.concat(
196                   cjson.decode(
197                    "[0.0, -5e3, -1, 0.3e-3, 1023.2, 0e10]"), " ")
198        } 0
199    } {0 -5000 -1 0.0003 1023.2 0}
200
201    test {EVAL - JSON string decoding} {
202        r eval {local decoded = cjson.decode('{"keya": "a", "keyb": "b"}')
203                return {decoded.keya, decoded.keyb}
204        } 0
205    } {a b}
206
207    test {EVAL - cmsgpack can pack double?} {
208        r eval {local encoded = cmsgpack.pack(0.1)
209                local h = ""
210                for i = 1, #encoded do
211                    h = h .. string.format("%02x",string.byte(encoded,i))
212                end
213                return h
214        } 0
215    } {cb3fb999999999999a}
216
217    test {EVAL - cmsgpack can pack negative int64?} {
218        r eval {local encoded = cmsgpack.pack(-1099511627776)
219                local h = ""
220                for i = 1, #encoded do
221                    h = h .. string.format("%02x",string.byte(encoded,i))
222                end
223                return h
224        } 0
225    } {d3ffffff0000000000}
226
227    test {EVAL - cmsgpack can pack and unpack circular references?} {
228        r eval {local a = {x=nil,y=5}
229                local b = {x=a}
230                a['x'] = b
231                local encoded = cmsgpack.pack(a)
232                local h = ""
233                -- cmsgpack encodes to a depth of 16, but can't encode
234                -- references, so the encoded object has a deep copy recusive
235                -- depth of 16.
236                for i = 1, #encoded do
237                    h = h .. string.format("%02x",string.byte(encoded,i))
238                end
239                -- when unpacked, re.x.x != re because the unpack creates
240                -- individual tables down to a depth of 16.
241                -- (that's why the encoded output is so large)
242                local re = cmsgpack.unpack(encoded)
243                assert(re)
244                assert(re.x)
245                assert(re.x.x.y == re.y)
246                assert(re.x.x.x.x.y == re.y)
247                assert(re.x.x.x.x.x.x.y == re.y)
248                assert(re.x.x.x.x.x.x.x.x.x.x.y == re.y)
249                -- maximum working depth:
250                assert(re.x.x.x.x.x.x.x.x.x.x.x.x.x.x.y == re.y)
251                -- now the last x would be b above and has no y
252                assert(re.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x)
253                -- so, the final x.x is at the depth limit and was assigned nil
254                assert(re.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x == nil)
255                return {h, re.x.x.x.x.x.x.x.x.y == re.y, re.y == 5}
256        } 0
257    } {82a17905a17881a17882a17905a17881a17882a17905a17881a17882a17905a17881a17882a17905a17881a17882a17905a17881a17882a17905a17881a17882a17905a17881a178c0 1 1}
258
259    test {EVAL - Numerical sanity check from bitop} {
260        r eval {assert(0x7fffffff == 2147483647, "broken hex literals");
261                assert(0xffffffff == -1 or 0xffffffff == 2^32-1,
262                    "broken hex literals");
263                assert(tostring(-1) == "-1", "broken tostring()");
264                assert(tostring(0xffffffff) == "-1" or
265                    tostring(0xffffffff) == "4294967295",
266                    "broken tostring()")
267        } 0
268    } {}
269
270    test {EVAL - Verify minimal bitop functionality} {
271        r eval {assert(bit.tobit(1) == 1);
272                assert(bit.band(1) == 1);
273                assert(bit.bxor(1,2) == 3);
274                assert(bit.bor(1,2,4,8,16,32,64,128) == 255)
275        } 0
276    } {}
277
278    test {EVAL - Able to parse trailing comments} {
279        r eval {return 'hello' --trailing comment} 0
280    } {hello}
281
282    test {SCRIPTING FLUSH - is able to clear the scripts cache?} {
283        r set mykey myval
284        set v [r evalsha fd758d1589d044dd850a6f05d52f2eefd27f033f 1 mykey]
285        assert_equal $v myval
286        set e ""
287        r script flush
288        catch {r evalsha fd758d1589d044dd850a6f05d52f2eefd27f033f 1 mykey} e
289        set e
290    } {NOSCRIPT*}
291
292    test {SCRIPT EXISTS - can detect already defined scripts?} {
293        r eval "return 1+1" 0
294        r script exists a27e7e8a43702b7046d4f6a7ccf5b60cef6b9bd9 a27e7e8a43702b7046d4f6a7ccf5b60cef6b9bda
295    } {1 0}
296
297    test {SCRIPT LOAD - is able to register scripts in the scripting cache} {
298        list \
299            [r script load "return 'loaded'"] \
300            [r evalsha b534286061d4b9e4026607613b95c06c06015ae8 0]
301    } {b534286061d4b9e4026607613b95c06c06015ae8 loaded}
302
303    test "In the context of Lua the output of random commands gets ordered" {
304        r debug lua-always-replicate-commands 0
305        r del myset
306        r sadd myset a b c d e f g h i l m n o p q r s t u v z aa aaa azz
307        set res [r eval {return redis.call('smembers',KEYS[1])} 1 myset]
308        r debug lua-always-replicate-commands 1
309        set res
310    } {a aa aaa azz b c d e f g h i l m n o p q r s t u v z}
311
312    test "SORT is normally not alpha re-ordered for the scripting engine" {
313        r del myset
314        r sadd myset 1 2 3 4 10
315        r eval {return redis.call('sort',KEYS[1],'desc')} 1 myset
316    } {10 4 3 2 1}
317
318    test "SORT BY <constant> output gets ordered for scripting" {
319        r del myset
320        r sadd myset a b c d e f g h i l m n o p q r s t u v z aa aaa azz
321        r eval {return redis.call('sort',KEYS[1],'by','_')} 1 myset
322    } {a aa aaa azz b c d e f g h i l m n o p q r s t u v z}
323
324    test "SORT BY <constant> with GET gets ordered for scripting" {
325        r del myset
326        r sadd myset a b c
327        r eval {return redis.call('sort',KEYS[1],'by','_','get','#','get','_:*')} 1 myset
328    } {a {} b {} c {}}
329
330    test "redis.sha1hex() implementation" {
331        list [r eval {return redis.sha1hex('')} 0] \
332             [r eval {return redis.sha1hex('Pizza & Mandolino')} 0]
333    } {da39a3ee5e6b4b0d3255bfef95601890afd80709 74822d82031af7493c20eefa13bd07ec4fada82f}
334
335    test {Globals protection reading an undeclared global variable} {
336        catch {r eval {return a} 0} e
337        set e
338    } {*ERR*attempted to access * global*}
339
340    test {Globals protection setting an undeclared global*} {
341        catch {r eval {a=10} 0} e
342        set e
343    } {*ERR*attempted to create global*}
344
345    test {Test an example script DECR_IF_GT} {
346        set decr_if_gt {
347            local current
348
349            current = redis.call('get',KEYS[1])
350            if not current then return nil end
351            if current > ARGV[1] then
352                return redis.call('decr',KEYS[1])
353            else
354                return redis.call('get',KEYS[1])
355            end
356        }
357        r set foo 5
358        set res {}
359        lappend res [r eval $decr_if_gt 1 foo 2]
360        lappend res [r eval $decr_if_gt 1 foo 2]
361        lappend res [r eval $decr_if_gt 1 foo 2]
362        lappend res [r eval $decr_if_gt 1 foo 2]
363        lappend res [r eval $decr_if_gt 1 foo 2]
364        set res
365    } {4 3 2 2 2}
366
367    test {Scripting engine resets PRNG at every script execution} {
368        set rand1 [r eval {return tostring(math.random())} 0]
369        set rand2 [r eval {return tostring(math.random())} 0]
370        assert_equal $rand1 $rand2
371    }
372
373    test {Scripting engine PRNG can be seeded correctly} {
374        set rand1 [r eval {
375            math.randomseed(ARGV[1]); return tostring(math.random())
376        } 0 10]
377        set rand2 [r eval {
378            math.randomseed(ARGV[1]); return tostring(math.random())
379        } 0 10]
380        set rand3 [r eval {
381            math.randomseed(ARGV[1]); return tostring(math.random())
382        } 0 20]
383        assert_equal $rand1 $rand2
384        assert {$rand2 ne $rand3}
385    }
386
387    test {EVAL does not leak in the Lua stack} {
388        r set x 0
389        # Use a non blocking client to speedup the loop.
390        set rd [redis_deferring_client]
391        for {set j 0} {$j < 10000} {incr j} {
392            $rd eval {return redis.call("incr",KEYS[1])} 1 x
393        }
394        for {set j 0} {$j < 10000} {incr j} {
395            $rd read
396        }
397        assert {[s used_memory_lua] < 1024*100}
398        $rd close
399        r get x
400    } {10000}
401
402    test {EVAL processes writes from AOF in read-only slaves} {
403        r flushall
404        r config set appendonly yes
405        r config set aof-use-rdb-preamble no
406        r eval {redis.call("set",KEYS[1],"100")} 1 foo
407        r eval {redis.call("incr",KEYS[1])} 1 foo
408        r eval {redis.call("incr",KEYS[1])} 1 foo
409        wait_for_condition 50 100 {
410            [s aof_rewrite_in_progress] == 0
411        } else {
412            fail "AOF rewrite can't complete after CONFIG SET appendonly yes."
413        }
414        r config set slave-read-only yes
415        r slaveof 127.0.0.1 0
416        r debug loadaof
417        set res [r get foo]
418        r slaveof no one
419        set res
420    } {102}
421
422    test {We can call scripts rewriting client->argv from Lua} {
423        r del myset
424        r sadd myset a b c
425        r mset a 1 b 2 c 3 d 4
426        assert {[r spop myset] ne {}}
427        assert {[r spop myset 1] ne {}}
428        assert {[r spop myset] ne {}}
429        assert {[r mget a b c d] eq {1 2 3 4}}
430        assert {[r spop myset] eq {}}
431    }
432
433    test {Call Redis command with many args from Lua (issue #1764)} {
434        r eval {
435            local i
436            local x={}
437            redis.call('del','mylist')
438            for i=1,100 do
439                table.insert(x,i)
440            end
441            redis.call('rpush','mylist',unpack(x))
442            return redis.call('lrange','mylist',0,-1)
443        } 0
444    } {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100}
445
446    test {Number conversion precision test (issue #1118)} {
447        r eval {
448              local value = 9007199254740991
449              redis.call("set","foo",value)
450              return redis.call("get","foo")
451        } 0
452    } {9007199254740991}
453
454    test {String containing number precision test (regression of issue #1118)} {
455        r eval {
456            redis.call("set", "key", "12039611435714932082")
457            return redis.call("get", "key")
458        } 0
459    } {12039611435714932082}
460
461    test {Verify negative arg count is error instead of crash (issue #1842)} {
462        catch { r eval { return "hello" } -12 } e
463        set e
464    } {ERR Number of keys can't be negative}
465
466    test {Correct handling of reused argv (issue #1939)} {
467        r eval {
468              for i = 0, 10 do
469                  redis.call('SET', 'a', '1')
470                  redis.call('MGET', 'a', 'b', 'c')
471                  redis.call('EXPIRE', 'a', 0)
472                  redis.call('GET', 'a')
473                  redis.call('MGET', 'a', 'b', 'c')
474              end
475        } 0
476    }
477
478    test {Functions in the Redis namespace are able to report errors} {
479        catch {
480            r eval {
481                  redis.sha1hex()
482            } 0
483        } e
484        set e
485    } {*wrong number*}
486}
487
488# Start a new server since the last test in this stanza will kill the
489# instance at all.
490start_server {tags {"scripting"}} {
491    test {Timedout read-only scripts can be killed by SCRIPT KILL} {
492        set rd [redis_deferring_client]
493        r config set lua-time-limit 10
494        $rd eval {while true do end} 0
495        after 200
496        catch {r ping} e
497        assert_match {BUSY*} $e
498        r script kill
499        after 200 ; # Give some time to Lua to call the hook again...
500        assert_equal [r ping] "PONG"
501    }
502
503    test {Timedout script link is still usable after Lua returns} {
504        r config set lua-time-limit 10
505        r eval {for i=1,100000 do redis.call('ping') end return 'ok'} 0
506        r ping
507    } {PONG}
508
509    test {Timedout scripts that modified data can't be killed by SCRIPT KILL} {
510        set rd [redis_deferring_client]
511        r config set lua-time-limit 10
512        $rd eval {redis.call('set',KEYS[1],'y'); while true do end} 1 x
513        after 200
514        catch {r ping} e
515        assert_match {BUSY*} $e
516        catch {r script kill} e
517        assert_match {UNKILLABLE*} $e
518        catch {r ping} e
519        assert_match {BUSY*} $e
520    }
521
522    # Note: keep this test at the end of this server stanza because it
523    # kills the server.
524    test {SHUTDOWN NOSAVE can kill a timedout script anyway} {
525        # The server could be still unresponding to normal commands.
526        catch {r ping} e
527        assert_match {BUSY*} $e
528        catch {r shutdown nosave}
529        # Make sure the server was killed
530        catch {set rd [redis_deferring_client]} e
531        assert_match {*connection refused*} $e
532    }
533}
534
535foreach cmdrepl {0 1} {
536    start_server {tags {"scripting repl"}} {
537        start_server {} {
538            if {$cmdrepl == 1} {
539                set rt "(commmands replication)"
540            } else {
541                set rt "(scripts replication)"
542                r debug lua-always-replicate-commands 1
543            }
544
545            test "Before the replica connects we issue two EVAL commands $rt" {
546                # One with an error, but still executing a command.
547                # SHA is: 67164fc43fa971f76fd1aaeeaf60c1c178d25876
548                catch {
549                    r eval {redis.call('incr',KEYS[1]); redis.call('nonexisting')} 1 x
550                }
551                # One command is correct:
552                # SHA is: 6f5ade10a69975e903c6d07b10ea44c6382381a5
553                r eval {return redis.call('incr',KEYS[1])} 1 x
554            } {2}
555
556            test "Connect a replica to the master instance $rt" {
557                r -1 slaveof [srv 0 host] [srv 0 port]
558                wait_for_condition 50 100 {
559                    [s -1 role] eq {slave} &&
560                    [string match {*master_link_status:up*} [r -1 info replication]]
561                } else {
562                    fail "Can't turn the instance into a replica"
563                }
564            }
565
566            test "Now use EVALSHA against the master, with both SHAs $rt" {
567                # The server should replicate successful and unsuccessful
568                # commands as EVAL instead of EVALSHA.
569                catch {
570                    r evalsha 67164fc43fa971f76fd1aaeeaf60c1c178d25876 1 x
571                }
572                r evalsha 6f5ade10a69975e903c6d07b10ea44c6382381a5 1 x
573            } {4}
574
575            test "If EVALSHA was replicated as EVAL, 'x' should be '4' $rt" {
576                wait_for_condition 50 100 {
577                    [r -1 get x] eq {4}
578                } else {
579                    fail "Expected 4 in x, but value is '[r -1 get x]'"
580                }
581            }
582
583            test "Replication of script multiple pushes to list with BLPOP $rt" {
584                set rd [redis_deferring_client]
585                $rd brpop a 0
586                r eval {
587                    redis.call("lpush",KEYS[1],"1");
588                    redis.call("lpush",KEYS[1],"2");
589                } 1 a
590                set res [$rd read]
591                $rd close
592                wait_for_condition 50 100 {
593                    [r -1 lrange a 0 -1] eq [r lrange a 0 -1]
594                } else {
595                    fail "Expected list 'a' in replica and master to be the same, but they are respectively '[r -1 lrange a 0 -1]' and '[r lrange a 0 -1]'"
596                }
597                set res
598            } {a 1}
599
600            test "EVALSHA replication when first call is readonly $rt" {
601                r del x
602                r eval {if tonumber(ARGV[1]) > 0 then redis.call('incr', KEYS[1]) end} 1 x 0
603                r evalsha 6e0e2745aa546d0b50b801a20983b70710aef3ce 1 x 0
604                r evalsha 6e0e2745aa546d0b50b801a20983b70710aef3ce 1 x 1
605                wait_for_condition 50 100 {
606                    [r -1 get x] eq {1}
607                } else {
608                    fail "Expected 1 in x, but value is '[r -1 get x]'"
609                }
610            }
611
612            test "Lua scripts using SELECT are replicated correctly $rt" {
613                r eval {
614                    redis.call("set","foo1","bar1")
615                    redis.call("select","10")
616                    redis.call("incr","x")
617                    redis.call("select","11")
618                    redis.call("incr","z")
619                } 0
620                r eval {
621                    redis.call("set","foo1","bar1")
622                    redis.call("select","10")
623                    redis.call("incr","x")
624                    redis.call("select","11")
625                    redis.call("incr","z")
626                } 0
627                wait_for_condition 50 100 {
628                    [r -1 debug digest] eq [r debug digest]
629                } else {
630                    fail "Master-Replica desync after Lua script using SELECT."
631                }
632            }
633        }
634    }
635}
636
637start_server {tags {"scripting repl"}} {
638    start_server {overrides {appendonly yes aof-use-rdb-preamble no}} {
639        test "Connect a replica to the master instance" {
640            r -1 slaveof [srv 0 host] [srv 0 port]
641            wait_for_condition 50 100 {
642                [s -1 role] eq {slave} &&
643                [string match {*master_link_status:up*} [r -1 info replication]]
644            } else {
645                fail "Can't turn the instance into a replica"
646            }
647        }
648
649        test "Redis.replicate_commands() must be issued before any write" {
650            r eval {
651                redis.call('set','foo','bar');
652                return redis.replicate_commands();
653            } 0
654        } {}
655
656        test "Redis.replicate_commands() must be issued before any write (2)" {
657            r eval {
658                return redis.replicate_commands();
659            } 0
660        } {1}
661
662        test "Redis.set_repl() must be issued after replicate_commands()" {
663            r debug lua-always-replicate-commands 0
664            catch {
665                r eval {
666                    redis.set_repl(redis.REPL_ALL);
667                } 0
668            } e
669            r debug lua-always-replicate-commands 1
670            set e
671        } {*only after turning on*}
672
673        test "Redis.set_repl() don't accept invalid values" {
674            catch {
675                r eval {
676                    redis.replicate_commands();
677                    redis.set_repl(12345);
678                } 0
679            } e
680            set e
681        } {*Invalid*flags*}
682
683        test "Test selective replication of certain Redis commands from Lua" {
684            r del a b c d
685            r eval {
686                redis.replicate_commands();
687                redis.call('set','a','1');
688                redis.set_repl(redis.REPL_NONE);
689                redis.call('set','b','2');
690                redis.set_repl(redis.REPL_AOF);
691                redis.call('set','c','3');
692                redis.set_repl(redis.REPL_ALL);
693                redis.call('set','d','4');
694            } 0
695
696            wait_for_condition 50 100 {
697                [r -1 mget a b c d] eq {1 {} {} 4}
698            } else {
699                fail "Only a and c should be replicated to replica"
700            }
701
702            # Master should have everything right now
703            assert {[r mget a b c d] eq {1 2 3 4}}
704
705            # After an AOF reload only a, c and d should exist
706            r debug loadaof
707
708            assert {[r mget a b c d] eq {1 {} 3 4}}
709        }
710
711        test "PRNG is seeded randomly for command replication" {
712            set a [
713                r eval {
714                    redis.replicate_commands();
715                    return math.random()*100000;
716                } 0
717            ]
718            set b [
719                r eval {
720                    redis.replicate_commands();
721                    return math.random()*100000;
722                } 0
723            ]
724            assert {$a ne $b}
725        }
726
727        test "Using side effects is not a problem with command replication" {
728            r eval {
729                redis.replicate_commands();
730                redis.call('set','time',redis.call('time')[1])
731            } 0
732
733            assert {[r get time] ne {}}
734
735            wait_for_condition 50 100 {
736                [r get time] eq [r -1 get time]
737            } else {
738                fail "Time key does not match between master and replica"
739            }
740        }
741    }
742}
743
744