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 286# Start a new server since the last test in this stanza will kill the 287# instance at all. 288start_server {tags {"scripting"}} { 289 test {Timedout read-only scripts can be killed by SCRIPT KILL} { 290 set rd [redis_deferring_client] 291 r config set lua-time-limit 10 292 $rd eval {while true do end} 0 293 after 200 294 catch {r ping} e 295 assert_match {BUSY*} $e 296 r script kill 297 assert_equal [r ping] "PONG" 298 } 299 300 test {Timedout script link is still usable after Lua returns} { 301 r config set lua-time-limit 10 302 r eval {for i=1,100000 do redis.call('ping') end return 'ok'} 0 303 r ping 304 } {PONG} 305 306 test {Timedout scripts that modified data can't be killed by SCRIPT KILL} { 307 set rd [redis_deferring_client] 308 r config set lua-time-limit 10 309 $rd eval {redis.call('set','x','y'); while true do end} 0 310 after 200 311 catch {r ping} e 312 assert_match {BUSY*} $e 313 catch {r script kill} e 314 assert_match {UNKILLABLE*} $e 315 catch {r ping} e 316 assert_match {BUSY*} $e 317 } 318 319 # Note: keep this test at the end of this server stanza because it 320 # kills the server. 321 test {SHUTDOWN NOSAVE can kill a timedout script anyway} { 322 # The server sould be still unresponding to normal commands. 323 catch {r ping} e 324 assert_match {BUSY*} $e 325 catch {r shutdown nosave} 326 # Make sure the server was killed 327 catch {set rd [redis_deferring_client]} e 328 assert_match {*connection refused*} $e 329 } 330} 331 332start_server {tags {"scripting repl"}} { 333 start_server {} { 334 test {Before the slave connects we issue two EVAL commands} { 335 # One with an error, but still executing a command. 336 # SHA is: 6e8bd6bdccbe78899e3cc06b31b6dbf4324c2e56 337 catch { 338 r eval {redis.call('incr','x'); redis.call('nonexisting')} 0 339 } 340 # One command is correct: 341 # SHA is: ae3477e27be955de7e1bc9adfdca626b478d3cb2 342 r eval {return redis.call('incr','x')} 0 343 } {2} 344 345 test {Connect a slave to the main instance} { 346 r -1 slaveof [srv 0 host] [srv 0 port] 347 wait_for_condition 50 100 { 348 [s -1 role] eq {slave} && 349 [string match {*master_link_status:up*} [r -1 info replication]] 350 } else { 351 fail "Can't turn the instance into a slave" 352 } 353 } 354 355 test {Now use EVALSHA against the master, with both SHAs} { 356 # The server should replicate successful and unsuccessful 357 # commands as EVAL instead of EVALSHA. 358 catch { 359 r evalsha 6e8bd6bdccbe78899e3cc06b31b6dbf4324c2e56 0 360 } 361 r evalsha ae3477e27be955de7e1bc9adfdca626b478d3cb2 0 362 } {4} 363 364 test {If EVALSHA was replicated as EVAL, 'x' should be '4'} { 365 wait_for_condition 50 100 { 366 [r -1 get x] eq {4} 367 } else { 368 fail "Expected 4 in x, but value is '[r -1 get x]'" 369 } 370 } 371 372 test {Replication of script multiple pushes to list with BLPOP} { 373 set rd [redis_deferring_client] 374 $rd brpop a 0 375 r eval { 376 redis.call("lpush","a","1"); 377 redis.call("lpush","a","2"); 378 } 0 379 set res [$rd read] 380 $rd close 381 wait_for_condition 50 100 { 382 [r -1 lrange a 0 -1] eq [r lrange a 0 -1] 383 } else { 384 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]'" 385 } 386 set res 387 } {a 1} 388 } 389} 390