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','mykey')} 0 44 } {myval} 45 46 test {EVALSHA - Can we call a SHA1 if already defined?} { 47 r evalsha 9bd632c7d33e571e9f24556ebed26c3479a87129 0 48 } {myval} 49 50 test {EVALSHA - Can we call a SHA1 in uppercase?} { 51 r evalsha 9BD632C7D33E571E9F24556EBED26C3479A87129 0 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 eval { 66 local foo = redis.pcall('incr','x') 67 return {type(foo),foo} 68 } 0 69 } {number 1} 70 71 test {EVAL - Redis bulk -> Lua type conversion} { 72 r set mykey myval 73 r eval { 74 local foo = redis.pcall('get','mykey') 75 return {type(foo),foo} 76 } 0 77 } {string myval} 78 79 test {EVAL - Redis multi bulk -> Lua type conversion} { 80 r del mylist 81 r rpush mylist a 82 r rpush mylist b 83 r rpush mylist c 84 r eval { 85 local foo = redis.pcall('lrange','mylist',0,-1) 86 return {type(foo),foo[1],foo[2],foo[3],# foo} 87 } 0 88 } {table a b c 3} 89 90 test {EVAL - Redis status reply -> Lua type conversion} { 91 r eval { 92 local foo = redis.pcall('set','mykey','myval') 93 return {type(foo),foo['ok']} 94 } 0 95 } {table OK} 96 97 test {EVAL - Redis error reply -> Lua type conversion} { 98 r set mykey myval 99 r eval { 100 local foo = redis.pcall('incr','mykey') 101 return {type(foo),foo['err']} 102 } 0 103 } {table {ERR value is not an integer or out of range}} 104 105 test {EVAL - Redis nil bulk reply -> Lua type conversion} { 106 r del mykey 107 r eval { 108 local foo = redis.pcall('get','mykey') 109 return {type(foo),foo == false} 110 } 0 111 } {boolean 1} 112 113 test {EVAL - Is Lua affecting the currently selected DB?} { 114 r set mykey "this is DB 9" 115 r select 10 116 r set mykey "this is DB 10" 117 r eval {return redis.pcall('get','mykey')} 0 118 } {this is DB 10} 119 120 test {EVAL - Is Lua seleced DB retained?} { 121 r eval {return redis.pcall('select','9')} 0 122 r get mykey 123 } {this is DB 9} 124 125 if 0 { 126 test {EVAL - Script can't run more than configured time limit} { 127 r config set lua-time-limit 1 128 catch { 129 r eval { 130 local i = 0 131 while true do i=i+1 end 132 } 0 133 } e 134 set _ $e 135 } {*execution time*} 136 } 137 138 test {EVAL - Scripts can't run certain commands} { 139 set e {} 140 catch {r eval {return redis.pcall('spop','x')} 0} e 141 set e 142 } {*not allowed*} 143 144 test {EVAL - Scripts can't run certain commands} { 145 set e {} 146 catch { 147 r eval "redis.pcall('randomkey'); return redis.pcall('set','x','ciao')" 0 148 } e 149 set e 150 } {*not allowed after*} 151 152 test {EVAL - No arguments to redis.call/pcall is considered an error} { 153 set e {} 154 catch {r eval {return redis.call()} 0} e 155 set e 156 } {*one argument*} 157 158 test {EVAL - redis.call variant raises a Lua error on Redis cmd error (1)} { 159 set e {} 160 catch { 161 r eval "redis.call('nosuchcommand')" 0 162 } e 163 set e 164 } {*Unknown Redis*} 165 166 test {EVAL - redis.call variant raises a Lua error on Redis cmd error (1)} { 167 set e {} 168 catch { 169 r eval "redis.call('get','a','b','c')" 0 170 } e 171 set e 172 } {*number of args*} 173 174 test {EVAL - redis.call variant raises a Lua error on Redis cmd error (1)} { 175 set e {} 176 r set foo bar 177 catch { 178 r eval "redis.call('lpush','foo','val')" 0 179 } e 180 set e 181 } {*against a key*} 182 183 test {SCRIPTING FLUSH - is able to clear the scripts cache?} { 184 r set mykey myval 185 set v [r evalsha 9bd632c7d33e571e9f24556ebed26c3479a87129 0] 186 assert_equal $v myval 187 set e "" 188 r script flush 189 catch {r evalsha 9bd632c7d33e571e9f24556ebed26c3479a87129 0} e 190 set e 191 } {NOSCRIPT*} 192 193 test {SCRIPT EXISTS - can detect already defined scripts?} { 194 r eval "return 1+1" 0 195 r script exists a27e7e8a43702b7046d4f6a7ccf5b60cef6b9bd9 a27e7e8a43702b7046d4f6a7ccf5b60cef6b9bda 196 } {1 0} 197 198 test {SCRIPT LOAD - is able to register scripts in the scripting cache} { 199 list \ 200 [r script load "return 'loaded'"] \ 201 [r evalsha b534286061d4b9e4026607613b95c06c06015ae8 0] 202 } {b534286061d4b9e4026607613b95c06c06015ae8 loaded} 203 204 test "In the context of Lua the output of random commands gets ordered" { 205 r del myset 206 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 207 r eval {return redis.call('smembers','myset')} 0 208 } {a aa aaa azz b c d e f g h i l m n o p q r s t u v z} 209 210 test "SORT is normally not alpha re-ordered for the scripting engine" { 211 r del myset 212 r sadd myset 1 2 3 4 10 213 r eval {return redis.call('sort','myset','desc')} 0 214 } {10 4 3 2 1} 215 216 test "SORT BY <constant> output gets ordered for scripting" { 217 r del myset 218 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 219 r eval {return redis.call('sort','myset','by','_')} 0 220 } {a aa aaa azz b c d e f g h i l m n o p q r s t u v z} 221 222 test "SORT BY <constant> with GET gets ordered for scripting" { 223 r del myset 224 r sadd myset a b c 225 r eval {return redis.call('sort','myset','by','_','get','#','get','_:*')} 0 226 } {a {} b {} c {}} 227 228 test "redis.sha1hex() implementation" { 229 list [r eval {return redis.sha1hex('')} 0] \ 230 [r eval {return redis.sha1hex('Pizza & Mandolino')} 0] 231 } {da39a3ee5e6b4b0d3255bfef95601890afd80709 74822d82031af7493c20eefa13bd07ec4fada82f} 232 233 test {Globals protection reading an undeclared global variable} { 234 catch {r eval {return a} 0} e 235 set e 236 } {*ERR*attempted to access unexisting global*} 237 238 test {Globals protection setting an undeclared global*} { 239 catch {r eval {a=10} 0} e 240 set e 241 } {*ERR*attempted to create global*} 242 243 test {Test an example script DECR_IF_GT} { 244 set decr_if_gt { 245 local current 246 247 current = redis.call('get',KEYS[1]) 248 if not current then return nil end 249 if current > ARGV[1] then 250 return redis.call('decr',KEYS[1]) 251 else 252 return redis.call('get',KEYS[1]) 253 end 254 } 255 r set foo 5 256 set res {} 257 lappend res [r eval $decr_if_gt 1 foo 2] 258 lappend res [r eval $decr_if_gt 1 foo 2] 259 lappend res [r eval $decr_if_gt 1 foo 2] 260 lappend res [r eval $decr_if_gt 1 foo 2] 261 lappend res [r eval $decr_if_gt 1 foo 2] 262 set res 263 } {4 3 2 2 2} 264 265 test {Scripting engine resets PRNG at every script execution} { 266 set rand1 [r eval {return tostring(math.random())} 0] 267 set rand2 [r eval {return tostring(math.random())} 0] 268 assert_equal $rand1 $rand2 269 } 270 271 test {Scripting engine PRNG can be seeded correctly} { 272 set rand1 [r eval { 273 math.randomseed(ARGV[1]); return tostring(math.random()) 274 } 0 10] 275 set rand2 [r eval { 276 math.randomseed(ARGV[1]); return tostring(math.random()) 277 } 0 10] 278 set rand3 [r eval { 279 math.randomseed(ARGV[1]); return tostring(math.random()) 280 } 0 20] 281 assert_equal $rand1 $rand2 282 assert {$rand2 ne $rand3} 283 } 284 285 test {EVAL does not leak in the Lua stack} { 286 r set x 0 287 # Use a non blocking client to speedup the loop. 288 set rd [redis_deferring_client] 289 for {set j 0} {$j < 10000} {incr j} { 290 $rd eval {return redis.call("incr",KEYS[1])} 1 x 291 } 292 for {set j 0} {$j < 10000} {incr j} { 293 $rd read 294 } 295 assert {[s used_memory_lua] < 1024*100} 296 $rd close 297 r get x 298 } {10000} 299 300 test {EVAL processes writes from AOF in read-only slaves} { 301 r flushall 302 r config set appendonly yes 303 r eval {redis.call("set","foo","100")} 0 304 r eval {redis.call("incr","foo")} 0 305 r eval {redis.call("incr","foo")} 0 306 wait_for_condition 50 100 { 307 [s aof_rewrite_in_progress] == 0 308 } else { 309 fail "AOF rewrite can't complete after CONFIG SET appendonly yes." 310 } 311 r config set slave-read-only yes 312 r slaveof 127.0.0.1 0 313 r debug loadaof 314 r get foo 315 } {102} 316} 317 318# Start a new server since the last test in this stanza will kill the 319# instance at all. 320start_server {tags {"scripting"}} { 321 test {Timedout read-only scripts can be killed by SCRIPT KILL} { 322 set rd [redis_deferring_client] 323 r config set lua-time-limit 10 324 $rd eval {while true do end} 0 325 after 200 326 catch {r ping} e 327 assert_match {BUSY*} $e 328 r script kill 329 assert_equal [r ping] "PONG" 330 } 331 332 test {Timedout script link is still usable after Lua returns} { 333 r config set lua-time-limit 10 334 r eval {for i=1,100000 do redis.call('ping') end return 'ok'} 0 335 r ping 336 } {PONG} 337 338 test {Timedout scripts that modified data can't be killed by SCRIPT KILL} { 339 set rd [redis_deferring_client] 340 r config set lua-time-limit 10 341 $rd eval {redis.call('set','x','y'); while true do end} 0 342 after 200 343 catch {r ping} e 344 assert_match {BUSY*} $e 345 catch {r script kill} e 346 assert_match {UNKILLABLE*} $e 347 catch {r ping} e 348 assert_match {BUSY*} $e 349 } 350 351 # Note: keep this test at the end of this server stanza because it 352 # kills the server. 353 test {SHUTDOWN NOSAVE can kill a timedout script anyway} { 354 # The server sould be still unresponding to normal commands. 355 catch {r ping} e 356 assert_match {BUSY*} $e 357 catch {r shutdown nosave} 358 # Make sure the server was killed 359 catch {set rd [redis_deferring_client]} e 360 assert_match {*connection refused*} $e 361 } 362} 363 364start_server {tags {"scripting repl"}} { 365 start_server {} { 366 test {Before the slave connects we issue two EVAL commands} { 367 # One with an error, but still executing a command. 368 # SHA is: 6e8bd6bdccbe78899e3cc06b31b6dbf4324c2e56 369 catch { 370 r eval {redis.call('incr','x'); redis.call('nonexisting')} 0 371 } 372 # One command is correct: 373 # SHA is: ae3477e27be955de7e1bc9adfdca626b478d3cb2 374 r eval {return redis.call('incr','x')} 0 375 } {2} 376 377 test {Connect a slave to the main instance} { 378 r -1 slaveof [srv 0 host] [srv 0 port] 379 wait_for_condition 50 100 { 380 [s -1 role] eq {slave} && 381 [string match {*master_link_status:up*} [r -1 info replication]] 382 } else { 383 fail "Can't turn the instance into a slave" 384 } 385 } 386 387 test {Now use EVALSHA against the master, with both SHAs} { 388 # The server should replicate successful and unsuccessful 389 # commands as EVAL instead of EVALSHA. 390 catch { 391 r evalsha 6e8bd6bdccbe78899e3cc06b31b6dbf4324c2e56 0 392 } 393 r evalsha ae3477e27be955de7e1bc9adfdca626b478d3cb2 0 394 } {4} 395 396 test {If EVALSHA was replicated as EVAL, 'x' should be '4'} { 397 wait_for_condition 50 100 { 398 [r -1 get x] eq {4} 399 } else { 400 fail "Expected 4 in x, but value is '[r -1 get x]'" 401 } 402 } 403 404 test {Replication of script multiple pushes to list with BLPOP} { 405 set rd [redis_deferring_client] 406 $rd brpop a 0 407 r eval { 408 redis.call("lpush","a","1"); 409 redis.call("lpush","a","2"); 410 } 0 411 set res [$rd read] 412 $rd close 413 wait_for_condition 50 100 { 414 [r -1 lrange a 0 -1] eq [r lrange a 0 -1] 415 } else { 416 fail "Expected list 'a' in slave and master to be the same, but they are respectively '[r -1 lrange a 0 -1]' and '[r lrange a 0 -1]'" 417 } 418 set res 419 } {a 1} 420 421 test {EVALSHA replication when first call is readonly} { 422 r del x 423 r eval {if tonumber(KEYS[1]) > 0 then redis.call('incr', 'x') end} 1 0 424 r evalsha 38fe3ddf5284a1d48f37f824b4c4e826879f3cb9 1 0 425 r evalsha 38fe3ddf5284a1d48f37f824b4c4e826879f3cb9 1 1 426 wait_for_condition 50 100 { 427 [r -1 get x] eq {1} 428 } else { 429 fail "Expected 1 in x, but value is '[r -1 get x]'" 430 } 431 } 432 } 433} 434