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