@@ -602,3 +602,124 @@ func TestLuaTX(t *testing.T) {
602602 )
603603 })
604604}
605+
606+ func TestEvalRo (t * testing.T ) {
607+ _ , c := runWithClient (t )
608+
609+ t .Run ("read-only command" , func (t * testing.T ) {
610+ mustOK (t , c ,
611+ "SET" , "readonly" , "foo" ,
612+ )
613+
614+ // Test EVALRO with read-only command (should work)
615+ mustDo (t , c ,
616+ "EVAL_RO" , "return redis.call('GET', KEYS[1])" , "1" , "readonly" ,
617+ proto .String ("foo" ),
618+ )
619+ })
620+
621+ t .Run ("write command" , func (t * testing.T ) {
622+ // Test EVALRO with write command (should fail)
623+ mustContain (t , c ,
624+ "EVAL_RO" , "return redis.call('SET', KEYS[1], ARGV[1])" , "1" , "key1" , "value1" ,
625+ "Write commands are not allowed in read-only scripts" ,
626+ )
627+ })
628+ }
629+
630+ func TestEvalshaRo (t * testing.T ) {
631+ _ , c := runWithClient (t )
632+
633+ // First load a read-only script
634+ script := "return redis.call('GET', KEYS[1])"
635+ t .Run ("read-only script" , func (t * testing.T ) {
636+ mustDo (t , c ,
637+ "SCRIPT" , "LOAD" , script ,
638+ proto .String ("d3c21d0c2b9ca22f82737626a27bcaf5d288f99f" ),
639+ )
640+
641+ mustOK (t , c ,
642+ "SET" , "readonly" , "foo" ,
643+ )
644+
645+ // Test EVALSHA_RO with read-only command (should work)
646+ mustDo (t , c ,
647+ "EVALSHA_RO" , "d3c21d0c2b9ca22f82737626a27bcaf5d288f99f" , "1" , "readonly" ,
648+ proto .String ("foo" ),
649+ )
650+
651+ })
652+
653+ t .Run ("write script" , func (t * testing.T ) {
654+ // Load a write script
655+ writeScript := "return redis.call('SET', KEYS[1], ARGV[1])"
656+ mustDo (t , c ,
657+ "SCRIPT" , "LOAD" , writeScript ,
658+ proto .String ("d8f2fad9f8e86a53d2a6ebd960b33c4972cacc37" ),
659+ )
660+
661+ // Test EVALSHA_RO with write command (should fail)
662+ mustContain (t , c ,
663+ "EVALSHA_RO" , "d8f2fad9f8e86a53d2a6ebd960b33c4972cacc37" , "1" , "key1" , "value1" ,
664+ "Write commands are not allowed in read-only scripts" ,
665+ )
666+ })
667+ }
668+
669+ func TestEvalRoWriteCommandWithPcall (t * testing.T ) {
670+ _ , c := runWithClient (t )
671+
672+ t .Run ("return error" , func (t * testing.T ) {
673+ // Test EVAL with pcall and write command (should fail)
674+ mustContain (t , c ,
675+ "EVAL_RO" , "return redis.pcall('FAKECOMMAND', KEYS[1], ARGV[1])" , "1" , "key1" , "value1" ,
676+ "Unknown Redis command called from script" ,
677+ )
678+ })
679+
680+ t .Run ("extra work after error" , func (t * testing.T ) {
681+ script := `
682+ local err = redis.pcall('FAKECOMMAND', KEYS[1], ARGV[1]);
683+ local res = "pcall:" .. err['err'];
684+ return res;
685+ `
686+ // Test EVAL with pcall and write command (should fail)
687+ mustContain (t , c ,
688+ "EVAL_RO" , script , "1" , "key1" , "value1" ,
689+ "pcall:ERR Unknown Redis command called from script" ,
690+ )
691+ })
692+
693+ t .Run ("write command in read-only script" , func (t * testing.T ) {
694+ // Test EVALRO with pcall and write command (should fail)
695+ mustContain (t , c ,
696+ "EVAL_RO" , "return redis.pcall('SET', KEYS[1], ARGV[1])" , "1" , "key1" , "value1" ,
697+ "Write commands are not allowed in read-only scripts" ,
698+ )
699+ })
700+ }
701+
702+ func TestEvalWithPcall (t * testing.T ) {
703+ _ , c := runWithClient (t )
704+
705+ t .Run ("return error" , func (t * testing.T ) {
706+ // Test EVAL with pcall and write command (should fail)
707+ mustContain (t , c ,
708+ "EVAL" , "return redis.pcall('FAKECOMMAND', KEYS[1], ARGV[1])" , "1" , "key1" , "value1" ,
709+ "Unknown Redis command called from script" ,
710+ )
711+ })
712+
713+ t .Run ("continue after error" , func (t * testing.T ) {
714+ script := `
715+ local err = redis.pcall('FAKECOMMAND', KEYS[1], ARGV[1]);
716+ local res = "pcall:" .. err['err'];
717+ return res;
718+ `
719+ // Test EVAL with pcall and write command (should fail)
720+ mustContain (t , c ,
721+ "EVAL" , script , "1" , "foo" , "value1" ,
722+ "pcall:ERR Unknown Redis command called from script" ,
723+ )
724+ })
725+ }
0 commit comments