1package miniredis 2 3import ( 4 "strings" 5 "testing" 6 7 "github.com/gomodule/redigo/redis" 8) 9 10func TestEval(t *testing.T) { 11 s, err := Run() 12 ok(t, err) 13 defer s.Close() 14 c, err := redis.Dial("tcp", s.Addr()) 15 ok(t, err) 16 defer c.Close() 17 18 { 19 b, err := redis.Int(c.Do("EVAL", "return 42", 0)) 20 ok(t, err) 21 equals(t, 42, b) 22 } 23 24 { 25 b, err := redis.Strings(c.Do("EVAL", "return {KEYS[1], ARGV[1]}", 1, "key1", "key2")) 26 ok(t, err) 27 equals(t, []string{"key1", "key2"}, b) 28 } 29 30 { 31 b, err := redis.Strings(c.Do("EVAL", "return {ARGV[1]}", 0, "key1")) 32 ok(t, err) 33 equals(t, []string{"key1"}, b) 34 } 35 36 // Invalid args 37 _, err = c.Do("EVAL", 42, 0) 38 assert(t, err != nil, "no EVAL error") 39 40 _, err = c.Do("EVAL", "return 42") 41 mustFail(t, err, errWrongNumber("eval")) 42 43 _, err = c.Do("EVAL", "return 42", 1) 44 mustFail(t, err, msgInvalidKeysNumber) 45 46 _, err = c.Do("EVAL", "return 42", -1) 47 mustFail(t, err, msgNegativeKeysNumber) 48 49 _, err = c.Do("EVAL", "return 42", "letter") 50 mustFail(t, err, msgInvalidInt) 51 52 _, err = c.Do("EVAL", "[", 0) 53 assert(t, err != nil, "no EVAL error") 54 55 _, err = c.Do("EVAL", "os.exit(42)", 0) 56 assert(t, err != nil, "no EVAL error") 57 58 { 59 b, err := redis.String(c.Do("EVAL", `return string.gsub("foo", "o", "a")`, 0)) 60 ok(t, err) 61 equals(t, "faa", b) 62 } 63 64 _, err = c.Do("EVAL", "return someGlobal", 0) 65 if err == nil || !strings.Contains(err.Error(), "Script attempted to access nonexistent global variable 'someGlobal'") { 66 t.Error("unexpected error", err) 67 } 68 69 _, err = c.Do("EVAL", "someGlobal = 5", 0) 70 if err == nil || !strings.Contains(err.Error(), "Script attempted to create global variable 'someGlobal'") { 71 t.Error("unexpected error", err) 72 } 73 74 t.Run("bigger float value", func(t *testing.T) { 75 _, err = c.Do("EVAL", "return redis.call('expire','foo', 999999)", 0) 76 ok(t, err) 77 _, err = c.Do("EVAL", "return redis.call('expire','foo',1000000)", 0) 78 ok(t, err) 79 }) 80} 81 82func TestEvalCall(t *testing.T) { 83 s, err := Run() 84 ok(t, err) 85 defer s.Close() 86 c, err := redis.Dial("tcp", s.Addr()) 87 ok(t, err) 88 defer c.Close() 89 90 _, err = c.Do("EVAL", "redis.call()", "0") 91 assert(t, err != nil, "no EVAL error") 92 93 _, err = c.Do("EVAL", "redis.call({})", "0") 94 assert(t, err != nil, "no EVAL error") 95 96 _, err = c.Do("EVAL", "redis.call(1)", "0") 97 assert(t, err != nil, "no EVAL error") 98} 99 100func TestScript(t *testing.T) { 101 s, err := Run() 102 ok(t, err) 103 defer s.Close() 104 c, err := redis.Dial("tcp", s.Addr()) 105 ok(t, err) 106 defer c.Close() 107 108 var ( 109 script1sha = "a42059b356c875f0717db19a51f6aaca9ae659ea" 110 script2sha = "1fa00e76656cc152ad327c13fe365858fd7be306" // "return 42" 111 ) 112 { 113 v, err := redis.String(c.Do("SCRIPT", "LOAD", "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}")) 114 ok(t, err) 115 equals(t, script1sha, v) 116 } 117 118 { 119 v, err := redis.String(c.Do("SCRIPT", "LOAD", "return 42")) 120 ok(t, err) 121 equals(t, script2sha, v) 122 } 123 124 { 125 v, err := redis.Int64s(c.Do("SCRIPT", "EXISTS", script1sha, script2sha, "invalid sha")) 126 ok(t, err) 127 equals(t, []int64{1, 1, 0}, v) 128 } 129 130 { 131 v, err := redis.String(c.Do("SCRIPT", "FLUSH")) 132 ok(t, err) 133 equals(t, "OK", v) 134 } 135 136 { 137 v, err := redis.Int64s(c.Do("SCRIPT", "EXISTS", script1sha)) 138 ok(t, err) 139 equals(t, []int64{0}, v) 140 } 141 142 { 143 v, err := redis.Int64s(c.Do("SCRIPT", "EXISTS")) 144 ok(t, err) 145 equals(t, []int64{}, v) 146 } 147 148 _, err = c.Do("SCRIPT") 149 mustFail(t, err, errWrongNumber("script")) 150 151 _, err = c.Do("SCRIPT", "LOAD") 152 mustFail(t, err, "ERR Unknown subcommand or wrong number of arguments for 'LOAD'. Try SCRIPT HELP.") 153 154 _, err = c.Do("SCRIPT", "LOAD", "return 42", "FOO") 155 mustFail(t, err, "ERR Unknown subcommand or wrong number of arguments for 'LOAD'. Try SCRIPT HELP.") 156 157 _, err = c.Do("SCRIPT", "LOAD", "[") 158 assert(t, err != nil, "no SCRIPT lOAD error") 159 160 _, err = c.Do("SCRIPT", "FLUSH", "1") 161 mustFail(t, err, "ERR Unknown subcommand or wrong number of arguments for 'FLUSH'. Try SCRIPT HELP.") 162 163 _, err = c.Do("SCRIPT", "FOO") 164 mustFail(t, err, "ERR Unknown subcommand or wrong number of arguments for 'FOO'. Try SCRIPT HELP.") 165} 166 167func TestCJSON(t *testing.T) { 168 s, err := Run() 169 ok(t, err) 170 defer s.Close() 171 c, err := redis.Dial("tcp", s.Addr()) 172 ok(t, err) 173 defer c.Close() 174 175 test := func(expr, want string) { 176 t.Helper() 177 str, err := redis.String(c.Do("EVAL", expr, 0)) 178 ok(t, err) 179 equals(t, str, want) 180 } 181 test( 182 `return cjson.decode('{"id":"foo"}')['id']`, 183 "foo", 184 ) 185 test( 186 `return cjson.encode({foo=42})`, 187 `{"foo":42}`, 188 ) 189 190 _, err = c.Do("EVAL", `redis.encode()`, 0) 191 assert(t, err != nil, "lua error") 192 _, err = c.Do("EVAL", `redis.encode("1", "2")`, 0) 193 assert(t, err != nil, "lua error") 194 _, err = c.Do("EVAL", `redis.decode()`, 0) 195 assert(t, err != nil, "lua error") 196 _, err = c.Do("EVAL", `redis.decode("{")`, 0) 197 assert(t, err != nil, "lua error") 198 _, err = c.Do("EVAL", `redis.decode("1", "2")`, 0) 199 assert(t, err != nil, "lua error") 200} 201 202func TestSha1Hex(t *testing.T) { 203 s, err := Run() 204 ok(t, err) 205 defer s.Close() 206 c, err := redis.Dial("tcp", s.Addr()) 207 ok(t, err) 208 defer c.Close() 209 210 test1 := func(val interface{}, want string) { 211 t.Helper() 212 str, err := redis.String(c.Do("EVAL", "return redis.sha1hex(ARGV[1])", 0, val)) 213 ok(t, err) 214 equals(t, str, want) 215 } 216 test1("foo", "0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33") 217 test1("bar", "62cdb7020ff920e5aa642c3d4066950dd1f01f4d") 218 test1("0", "b6589fc6ab0dc82cf12099d1c2d40ab994e8410c") 219 test1(0, "b6589fc6ab0dc82cf12099d1c2d40ab994e8410c") 220 test1(nil, "da39a3ee5e6b4b0d3255bfef95601890afd80709") 221 222 test2 := func(eval, want string) { 223 t.Helper() 224 have, err := redis.String(c.Do("EVAL", eval, 0)) 225 ok(t, err) 226 equals(t, have, want) 227 } 228 test2("return redis.sha1hex({})", "da39a3ee5e6b4b0d3255bfef95601890afd80709") 229 test2("return redis.sha1hex(nil)", "da39a3ee5e6b4b0d3255bfef95601890afd80709") 230 test2("return redis.sha1hex(42)", "92cfceb39d57d914ed8b14d0e37643de0797ae56") 231 232 _, err = c.Do("EVAL", "redis.sha1hex()", 0) 233 assert(t, err != nil, "lua error") 234} 235 236func TestEvalsha(t *testing.T) { 237 s, err := Run() 238 ok(t, err) 239 defer s.Close() 240 c, err := redis.Dial("tcp", s.Addr()) 241 ok(t, err) 242 defer c.Close() 243 244 script1sha := "bfbf458525d6a0b19200bfd6db3af481156b367b" 245 { 246 v, err := redis.String(c.Do("SCRIPT", "LOAD", "return {KEYS[1],ARGV[1]}")) 247 ok(t, err) 248 equals(t, script1sha, v) 249 } 250 251 { 252 b, err := redis.Strings(c.Do("EVALSHA", script1sha, 1, "key1", "key2")) 253 ok(t, err) 254 equals(t, []string{"key1", "key2"}, b) 255 } 256 257 _, err = c.Do("EVALSHA") 258 mustFail(t, err, errWrongNumber("evalsha")) 259 260 _, err = c.Do("EVALSHA", "foo") 261 mustFail(t, err, errWrongNumber("evalsha")) 262 263 _, err = c.Do("EVALSHA", "foo", 0) 264 mustFail(t, err, msgNoScriptFound) 265 266 _, err = c.Do("EVALSHA", script1sha, script1sha) 267 mustFail(t, err, msgInvalidInt) 268 269 _, err = c.Do("EVALSHA", script1sha, -1) 270 mustFail(t, err, msgNegativeKeysNumber) 271 272 _, err = c.Do("EVALSHA", script1sha, 1) 273 mustFail(t, err, msgInvalidKeysNumber) 274 275 _, err = c.Do("EVALSHA", "foo", 1, "bar") 276 mustFail(t, err, msgNoScriptFound) 277} 278 279func TestCmdEvalReply(t *testing.T) { 280 s, err := Run() 281 ok(t, err) 282 defer s.Close() 283 c, err := redis.Dial("tcp", s.Addr()) 284 ok(t, err) 285 defer c.Close() 286 287 test := func(script string, args []interface{}, expected interface{}) { 288 t.Helper() 289 reply, err := c.Do("EVAL", append([]interface{}{script}, args...)...) 290 if err != nil { 291 t.Errorf("unexpected error: %v", err) 292 return 293 } 294 equals(t, expected, reply) 295 } 296 297 // return nil 298 test( 299 "", 300 []interface{}{ 301 0, 302 }, 303 nil, 304 ) 305 // return boolean true 306 test( 307 "return true", 308 []interface{}{ 309 0, 310 }, 311 int64(1), 312 ) 313 // return boolean false 314 test( 315 "return false", 316 []interface{}{ 317 0, 318 }, 319 nil, 320 ) 321 // return single number 322 test( 323 "return 10", 324 []interface{}{ 325 0, 326 }, 327 int64(10), 328 ) 329 // return single float 330 test( 331 "return 12.345", 332 []interface{}{ 333 0, 334 }, 335 int64(12), 336 ) 337 // return multiple numbers 338 test( 339 "return 10, 20", 340 []interface{}{ 341 0, 342 }, 343 int64(10), 344 ) 345 // return single string 346 test( 347 "return 'test'", 348 []interface{}{ 349 0, 350 }, 351 []byte("test"), 352 ) 353 // return multiple string 354 test( 355 "return 'test1', 'test2'", 356 []interface{}{ 357 0, 358 }, 359 []byte("test1"), 360 ) 361 // return single table multiple integer 362 test( 363 "return {10, 20}", 364 []interface{}{ 365 0, 366 }, 367 []interface{}{ 368 int64(10), 369 int64(20), 370 }, 371 ) 372 // return single table multiple string 373 test( 374 "return {'test1', 'test2'}", 375 []interface{}{ 376 0, 377 }, 378 []interface{}{ 379 []byte("test1"), 380 []byte("test2"), 381 }, 382 ) 383 // return nested table 384 test( 385 "return {10, 20, {30, 40}}", 386 []interface{}{ 387 0, 388 }, 389 []interface{}{ 390 int64(10), 391 int64(20), 392 []interface{}{ 393 int64(30), 394 int64(40), 395 }, 396 }, 397 ) 398 // return combination table 399 test( 400 "return {10, 20, {30, 'test', true, 40}, false}", 401 []interface{}{ 402 0, 403 }, 404 []interface{}{ 405 int64(10), 406 int64(20), 407 []interface{}{ 408 int64(30), 409 []byte("test"), 410 int64(1), 411 int64(40), 412 }, 413 nil, 414 }, 415 ) 416 // KEYS and ARGV 417 test( 418 "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}", 419 []interface{}{ 420 2, 421 "key1", 422 "key2", 423 "first", 424 "second", 425 }, 426 []interface{}{ 427 []byte("key1"), 428 []byte("key2"), 429 []byte("first"), 430 []byte("second"), 431 }, 432 ) 433 434 { 435 _, err := c.Do("EVAL", `return {err="broken"}`, 0) 436 mustFail(t, err, "broken") 437 438 _, err = c.Do("EVAL", `return redis.error_reply("broken")`, 0) 439 mustFail(t, err, "broken") 440 } 441 442 { 443 v, err := redis.String(c.Do("EVAL", `return {ok="good"}`, 0)) 444 ok(t, err) 445 equals(t, "good", v) 446 447 v, err = redis.String(c.Do("EVAL", `return redis.status_reply("good")`, 0)) 448 ok(t, err) 449 equals(t, "good", v) 450 } 451 452 _, err = c.Do("EVAL", `return redis.error_reply()`, 0) 453 assert(t, err != nil, "no EVAL error") 454 455 _, err = c.Do("EVAL", `return redis.error_reply(1)`, 0) 456 assert(t, err != nil, "no EVAL error") 457 458 _, err = c.Do("EVAL", `return redis.status_reply()`, 0) 459 assert(t, err != nil, "no EVAL error") 460 461 _, err = c.Do("EVAL", `return redis.status_reply(1)`, 0) 462 assert(t, err != nil, "no EVAL error") 463} 464 465func TestCmdEvalResponse(t *testing.T) { 466 s, err := Run() 467 ok(t, err) 468 defer s.Close() 469 470 c, err := redis.Dial("tcp", s.Addr()) 471 ok(t, err) 472 defer c.Close() 473 474 { 475 v, err := redis.String(c.Do("EVAL", "return redis.call('set','foo','bar')", 0)) 476 ok(t, err) 477 equals(t, "OK", v) 478 } 479 480 { 481 v, err := redis.String(c.Do("EVAL", "return redis.call('get','foo')", 0)) 482 ok(t, err) 483 equals(t, "bar", v) 484 } 485 486 { 487 v, err := c.Do("EVAL", "return redis.call('get','nosuch')", 0) 488 ok(t, err) 489 equals(t, nil, v) 490 } 491 492 { 493 v, err := redis.String(c.Do("EVAL", "return redis.call('HMSET', 'mkey', 'foo','bar','foo1','bar1')", 0)) 494 ok(t, err) 495 equals(t, "OK", v) 496 } 497 498 { 499 v, err := redis.Strings(c.Do("EVAL", "return redis.call('HGETALL','mkey')", 0)) 500 ok(t, err) 501 equals(t, []string{"foo", "bar", "foo1", "bar1"}, v) 502 } 503 504 { 505 v, err := redis.Strings(c.Do("EVAL", "return redis.call('HMGET','mkey', 'foo1')", 0)) 506 ok(t, err) 507 equals(t, []string{"bar1"}, v) 508 } 509 510 { 511 v, err := redis.Strings(c.Do("EVAL", "return redis.call('HMGET','mkey', 'foo')", 0)) 512 ok(t, err) 513 equals(t, []string{"bar"}, v) 514 } 515 516 { 517 v, err := c.Do("EVAL", "return redis.call('HMGET','mkey', 'bad', 'key')", 0) 518 ok(t, err) 519 equals(t, []interface{}{nil, nil}, v) 520 } 521} 522 523func TestCmdEvalAuth(t *testing.T) { 524 s, err := Run() 525 ok(t, err) 526 defer s.Close() 527 528 c, err := redis.Dial("tcp", s.Addr()) 529 ok(t, err) 530 defer c.Close() 531 532 eval := "return redis.call('set','foo','bar')" 533 534 s.RequireAuth("123password") 535 536 _, err = c.Do("EVAL", eval, 0) 537 mustFail(t, err, "NOAUTH Authentication required.") 538 539 _, err = c.Do("AUTH", "123password") 540 ok(t, err) 541 542 _, err = c.Do("EVAL", eval, 0) 543 ok(t, err) 544} 545 546func TestLuaReplicate(t *testing.T) { 547 s, err := Run() 548 ok(t, err) 549 defer s.Close() 550 c, err := redis.Dial("tcp", s.Addr()) 551 ok(t, err) 552 defer c.Close() 553 554 _, err = c.Do("EVAL", "redis.replicate_commands()", 0) 555 ok(t, err) 556} 557 558func TestLuaTX(t *testing.T) { 559 s, err := Run() 560 ok(t, err) 561 defer s.Close() 562 c, err := redis.Dial("tcp", s.Addr()) 563 ok(t, err) 564 defer c.Close() 565 566 // EVAL 567 { 568 b, err := redis.String(c.Do("MULTI")) 569 ok(t, err) 570 equals(t, "OK", b) 571 572 b, err = redis.String(c.Do("EVAL", "return {ARGV[1]}", 0, "key1")) 573 ok(t, err) 574 equals(t, "QUEUED", b) 575 576 v, err := redis.Values(c.Do("EXEC")) 577 ok(t, err) 578 equals(t, 1, len(redis.Args(v))) 579 v0, err := redis.Strings(v[0], nil) 580 ok(t, err) 581 equals(t, []string{"key1"}, v0) 582 } 583 584 // EVALSHA 585 { 586 script1sha := "bfbf458525d6a0b19200bfd6db3af481156b367b" 587 588 b, err := redis.String(c.Do("MULTI")) 589 ok(t, err) 590 equals(t, "OK", b) 591 592 b, err = redis.String(c.Do("SCRIPT", "LOAD", "return {KEYS[1],ARGV[1]}")) 593 ok(t, err) 594 equals(t, "QUEUED", b) 595 596 b, err = redis.String(c.Do("EVALSHA", script1sha, 1, "key1", "key2")) 597 ok(t, err) 598 equals(t, "QUEUED", b) 599 600 v, err := redis.Values(c.Do("EXEC")) 601 ok(t, err) 602 equals(t, 2, len(redis.Args(v))) 603 v0, err := redis.String(v[0], nil) 604 ok(t, err) 605 v1, err := redis.Strings(v[1], nil) 606 ok(t, err) 607 equals(t, script1sha, v0) // SCRIPT 608 equals(t, []string{"key1", "key2"}, v1) // EVALSHA 609 } 610 611 // compiling is done inside the transaction 612 { 613 b, err := redis.String(c.Do("SET", "foo", "12")) 614 ok(t, err) 615 equals(t, "OK", b) 616 617 b, err = redis.String(c.Do("MULTI")) 618 ok(t, err) 619 equals(t, "OK", b) 620 621 b, err = redis.String(c.Do("SCRIPT", "LOAD", "foobar")) 622 ok(t, err) 623 equals(t, "QUEUED", b) 624 625 b, err = redis.String(c.Do("GET", "foo")) 626 ok(t, err) 627 equals(t, "QUEUED", b) 628 629 v, err := redis.Values(c.Do("EXEC")) 630 ok(t, err) 631 equals(t, 2, len(redis.Args(v))) 632 _, err = redis.String(v[0], nil) 633 mustFail(t, err, "ERR Error compiling script (new function): user_script at EOF: parse error ") 634 v1, err := redis.String(v[1], nil) 635 ok(t, err) 636 equals(t, "12", v1) 637 } 638 639 // misc SCRIPT subcommands 640 { 641 b, err := redis.String(c.Do("MULTI")) 642 ok(t, err) 643 equals(t, "OK", b) 644 645 b, err = redis.String(c.Do("SCRIPT", "EXISTS", "123")) 646 ok(t, err) 647 equals(t, "QUEUED", b) 648 649 b, err = redis.String(c.Do("SCRIPT", "FLUSH")) 650 ok(t, err) 651 equals(t, "QUEUED", b) 652 653 v, err := redis.Values(c.Do("EXEC")) 654 ok(t, err) 655 equals(t, 2, len(redis.Args(v))) 656 } 657} 658