1package miniredis
2
3import (
4	"sort"
5	"testing"
6
7	"github.com/alicebob/miniredis/v2/proto"
8)
9
10// Test SADD / SMEMBERS.
11func TestSadd(t *testing.T) {
12	s, err := Run()
13	ok(t, err)
14	defer s.Close()
15	c, err := proto.Dial(s.Addr())
16	ok(t, err)
17	defer c.Close()
18
19	{
20		mustDo(t, c,
21			"SADD", "s", "aap", "noot", "mies",
22			proto.Int(3),
23		)
24
25		members, err := s.Members("s")
26		ok(t, err)
27		equals(t, []string{"aap", "mies", "noot"}, members)
28
29		mustDo(t, c,
30			"SMEMBERS", "s",
31			proto.Strings("aap", "mies", "noot"),
32		)
33	}
34
35	mustDo(t, c,
36		"TYPE", "s",
37		proto.Inline("set"),
38	)
39
40	// SMEMBERS on an nonexisting key
41	mustDo(t, c,
42		"SMEMBERS", "nosuch",
43		proto.Strings(),
44	)
45
46	{
47		mustDo(t, c,
48			"SADD", "s", "new", "noot", "mies",
49			proto.Int(1), // Only one new field.
50		)
51
52		members, err := s.Members("s")
53		ok(t, err)
54		equals(t, []string{"aap", "mies", "new", "noot"}, members)
55	}
56
57	t.Run("direct usage", func(t *testing.T) {
58		added, err := s.SetAdd("s1", "aap")
59		ok(t, err)
60		equals(t, 1, added)
61
62		members, err := s.Members("s1")
63		ok(t, err)
64		equals(t, []string{"aap"}, members)
65	})
66
67	t.Run("errors", func(t *testing.T) {
68		mustOK(t, c, "SET", "str", "value")
69		mustDo(t, c,
70			"SADD", "str", "hi",
71			proto.Error(msgWrongType),
72		)
73		mustDo(t, c,
74			"SMEMBERS", "str",
75			proto.Error(msgWrongType),
76		)
77		// Wrong argument counts
78		mustDo(t, c,
79			"SADD",
80			proto.Error(errWrongNumber("sadd")),
81		)
82		mustDo(t, c,
83			"SADD", "set",
84			proto.Error(errWrongNumber("sadd")),
85		)
86		mustDo(t, c,
87			"SMEMBERS",
88			proto.Error(errWrongNumber("smembers")),
89		)
90		mustDo(t, c,
91			"SMEMBERS", "set", "spurious",
92			proto.Error(errWrongNumber("smembers")),
93		)
94	})
95}
96
97// Test SISMEMBER
98func TestSismember(t *testing.T) {
99	s, err := Run()
100	ok(t, err)
101	defer s.Close()
102	c, err := proto.Dial(s.Addr())
103	ok(t, err)
104	defer c.Close()
105
106	s.SetAdd("s", "aap", "noot", "mies")
107
108	{
109		must1(t, c, "SISMEMBER", "s", "aap")
110
111		must0(t, c, "SISMEMBER", "s", "nosuch")
112	}
113
114	// a nonexisting key
115	must0(t, c, "SISMEMBER", "nosuch", "nosuch")
116
117	t.Run("direct usage", func(t *testing.T) {
118		isMember, err := s.IsMember("s", "noot")
119		ok(t, err)
120		equals(t, true, isMember)
121	})
122
123	t.Run("errors", func(t *testing.T) {
124		mustOK(t, c, "SET", "str", "value")
125		mustDo(t, c,
126			"SISMEMBER", "str", "foo",
127			proto.Error(msgWrongType),
128		)
129		mustDo(t, c,
130			"SISMEMBER",
131			proto.Error(errWrongNumber("sismember")),
132		)
133		mustDo(t, c,
134			"SISMEMBER", "set",
135			proto.Error(errWrongNumber("sismember")),
136		)
137		mustDo(t, c,
138			"SISMEMBER", "set", "spurious", "args",
139			proto.Error(errWrongNumber("sismember")),
140		)
141	})
142}
143
144// Test SREM
145func TestSrem(t *testing.T) {
146	s, err := Run()
147	ok(t, err)
148	defer s.Close()
149	c, err := proto.Dial(s.Addr())
150	ok(t, err)
151	defer c.Close()
152
153	s.SetAdd("s", "aap", "noot", "mies", "vuur")
154
155	{
156		mustDo(t, c,
157			"SREM", "s", "aap", "noot",
158			proto.Int(2),
159		)
160
161		members, err := s.Members("s")
162		ok(t, err)
163		equals(t, []string{"mies", "vuur"}, members)
164	}
165
166	// a nonexisting key
167	must0(t, c,
168		"SREM", "s", "nosuch",
169		proto.Int(9),
170	)
171
172	// a nonexisting key
173	must0(t, c,
174		"SREM", "nosuch", "nosuch",
175	)
176
177	t.Run("direct usage", func(t *testing.T) {
178		b, err := s.SRem("s", "mies")
179		ok(t, err)
180		equals(t, 1, b)
181
182		members, err := s.Members("s")
183		ok(t, err)
184		equals(t, []string{"vuur"}, members)
185	})
186
187	t.Run("errors", func(t *testing.T) {
188		mustOK(t, c, "SET", "str", "value")
189		mustDo(t, c,
190			"SREM", "str", "value",
191			proto.Error(msgWrongType),
192		)
193		mustDo(t, c,
194			"SREM",
195			proto.Error(errWrongNumber("srem")),
196		)
197		mustDo(t, c,
198			"SREM", "set",
199			proto.Error(errWrongNumber("srem")),
200		)
201	})
202}
203
204// Test SMOVE
205func TestSmove(t *testing.T) {
206	s, err := Run()
207	ok(t, err)
208	defer s.Close()
209	c, err := proto.Dial(s.Addr())
210	ok(t, err)
211	defer c.Close()
212
213	s.SetAdd("s", "aap", "noot")
214
215	{
216		must1(t, c,
217			"SMOVE", "s", "s2", "aap",
218		)
219
220		m, err := s.IsMember("s", "aap")
221		ok(t, err)
222		equals(t, false, m)
223		m, err = s.IsMember("s2", "aap")
224		ok(t, err)
225		equals(t, true, m)
226	}
227
228	// Move away the last member
229	{
230		must1(t, c,
231			"SMOVE", "s", "s2", "noot",
232		)
233
234		equals(t, false, s.Exists("s"))
235
236		m, err := s.IsMember("s2", "noot")
237		ok(t, err)
238		equals(t, true, m)
239	}
240
241	// a nonexisting member
242	must0(t, c, "SMOVE", "s", "s2", "nosuch")
243
244	// a nonexisting key
245	must0(t, c, "SMOVE", "nosuch", "nosuch2", "nosuch")
246
247	t.Run("errors", func(t *testing.T) {
248		mustOK(t, c, "SET", "str", "value")
249		mustDo(t, c,
250			"SMOVE", "str", "dst", "value",
251			proto.Error(msgWrongType),
252		)
253		mustDo(t, c,
254			"SMOVE", "s2", "str", "value",
255			proto.Error(msgWrongType),
256		)
257
258		mustDo(t, c,
259			"SMOVE",
260			proto.Error(errWrongNumber("smove")),
261		)
262		mustDo(t, c,
263			"SMOVE", "set",
264			proto.Error(errWrongNumber("smove")),
265		)
266		mustDo(t, c,
267			"SMOVE", "set", "set2",
268			proto.Error(errWrongNumber("smove")),
269		)
270		mustDo(t, c,
271			"SMOVE", "set", "set2", "spurious", "args",
272			proto.Error(errWrongNumber("smove")),
273		)
274	})
275}
276
277// Test SPOP
278func TestSpop(t *testing.T) {
279	s, err := Run()
280	ok(t, err)
281	defer s.Close()
282	c, err := proto.Dial(s.Addr())
283	ok(t, err)
284	defer c.Close()
285
286	t.Run("basics", func(t *testing.T) {
287		s.SetAdd("s", "aap", "noot")
288
289		res, err := c.Do("SPOP", "s")
290		ok(t, err)
291		assert(t, res == proto.String("aap") || res == proto.String("noot"), "spop got something")
292
293		res, err = c.Do("SPOP", "s")
294		ok(t, err)
295		assert(t, res == proto.String("aap") || res == proto.String("noot"), "spop got something")
296
297		assert(t, !s.Exists("s"), "all spopped away")
298	})
299
300	t.Run("nonexisting key", func(t *testing.T) {
301		mustNil(t, c, "SPOP", "nosuch")
302	})
303
304	t.Run("various errors", func(t *testing.T) {
305		s.SetAdd("chk", "aap", "noot")
306		s.Set("str", "value")
307
308		mustDo(t, c,
309			"SMOVE",
310			proto.Error(errWrongNumber("smove")),
311		)
312		mustDo(t, c,
313			"SMOVE", "chk", "set2",
314			proto.Error(errWrongNumber("smove")),
315		)
316
317		mustDo(t, c,
318			"SPOP", "str",
319			proto.Error(msgWrongType),
320		)
321	})
322
323	t.Run("count argument", func(t *testing.T) {
324		s.SetAdd("s", "aap", "noot", "mies", "vuur")
325		mustDo(t, c,
326			"SPOP", "s", "2",
327			proto.Strings("vuur", "mies"),
328		)
329		members, err := s.Members("s")
330		ok(t, err)
331		assert(t, len(members) == 2, "SPOP s 2")
332
333		mustDo(t, c,
334			"SPOP", "str", "-12",
335			proto.Error(msgOutOfRange),
336		)
337	})
338}
339
340// Test SRANDMEMBER
341func TestSrandmember(t *testing.T) {
342	s, err := Run()
343	ok(t, err)
344	defer s.Close()
345	c, err := proto.Dial(s.Addr())
346	ok(t, err)
347	defer c.Close()
348
349	s.SetAdd("s", "aap", "noot", "mies")
350
351	s.Seed(42)
352	// No count
353	{
354		res, err := c.Do("SRANDMEMBER", "s")
355		ok(t, err)
356		assert(t, res == proto.String("aap") ||
357			res == proto.String("noot") ||
358			res == proto.String("mies"),
359			"srandmember got something",
360		)
361	}
362
363	// Positive count
364	mustDo(t, c,
365		"SRANDMEMBER", "s", "2",
366		proto.Strings("noot", "mies"),
367	)
368
369	// Negative count
370	mustDo(t, c,
371		"SRANDMEMBER", "s", "-2",
372		proto.Strings("aap", "mies"),
373	)
374
375	// a nonexisting key
376	mustNil(t, c,
377		"SRANDMEMBER", "nosuch",
378	)
379
380	t.Run("errors", func(t *testing.T) {
381		s.SetAdd("chk", "aap", "noot")
382		s.Set("str", "value")
383
384		mustDo(t, c,
385			"SRANDMEMBER",
386			proto.Error(errWrongNumber("srandmember")),
387		)
388		mustDo(t, c,
389			"SRANDMEMBER", "chk", "noint",
390			proto.Error("ERR value is not an integer or out of range"),
391		)
392		mustDo(t, c,
393			"SRANDMEMBER", "chk", "1", "toomanu",
394			proto.Error("ERR syntax error"),
395		)
396
397		mustDo(t, c,
398			"SRANDMEMBER", "str",
399			proto.Error(msgWrongType),
400		)
401	})
402}
403
404// Test SDIFF
405func TestSdiff(t *testing.T) {
406	s, err := Run()
407	ok(t, err)
408	defer s.Close()
409	c, err := proto.Dial(s.Addr())
410	ok(t, err)
411	defer c.Close()
412
413	s.SetAdd("s1", "aap", "noot", "mies")
414	s.SetAdd("s2", "noot", "mies", "vuur")
415	s.SetAdd("s3", "aap", "mies", "wim")
416
417	// Simple case
418	mustDo(t, c,
419		"SDIFF", "s1", "s2",
420		proto.Strings("aap"),
421	)
422
423	// No other set
424	{
425		res, err := c.DoStrings("SDIFF", "s1")
426		ok(t, err)
427		sort.Strings(res)
428		equals(t, []string{"aap", "mies", "noot"}, res)
429	}
430
431	// 3 sets
432	mustDo(t, c,
433		"SDIFF", "s1", "s2", "s3",
434		proto.Strings(),
435	)
436
437	// A nonexisting key
438	mustDo(t, c,
439		"SDIFF", "s9",
440		proto.Strings(),
441	)
442
443	t.Run("errors", func(t *testing.T) {
444		s.SetAdd("chk", "aap", "noot")
445		s.Set("str", "value")
446
447		mustDo(t, c,
448			"SDIFF",
449			proto.Error(errWrongNumber("sdiff")),
450		)
451		mustDo(t, c,
452			"SDIFF", "str",
453			proto.Error(msgWrongType),
454		)
455		mustDo(t, c,
456			"SDIFF", "chk", "str",
457			proto.Error(msgWrongType),
458		)
459	})
460}
461
462// Test SDIFFSTORE
463func TestSdiffstore(t *testing.T) {
464	s, err := Run()
465	ok(t, err)
466	defer s.Close()
467	c, err := proto.Dial(s.Addr())
468	ok(t, err)
469	defer c.Close()
470
471	s.SetAdd("s1", "aap", "noot", "mies")
472	s.SetAdd("s2", "noot", "mies", "vuur")
473	s.SetAdd("s3", "aap", "mies", "wim")
474
475	// Simple case
476	{
477		must1(t, c,
478			"SDIFFSTORE", "res", "s1", "s3",
479		)
480		s.CheckSet(t, "res", "noot")
481	}
482
483	t.Run("errors", func(t *testing.T) {
484		s.SetAdd("chk", "aap", "noot")
485		s.Set("str", "value")
486
487		mustDo(t, c,
488			"SDIFFSTORE",
489			proto.Error(errWrongNumber("sdiffstore")),
490		)
491		mustDo(t, c,
492			"SDIFFSTORE", "t",
493			proto.Error(errWrongNumber("sdiffstore")),
494		)
495		mustDo(t, c,
496			"SDIFFSTORE", "t", "str",
497			proto.Error(msgWrongType),
498		)
499	})
500}
501
502// Test SINTER
503func TestSinter(t *testing.T) {
504	s, err := Run()
505	ok(t, err)
506	defer s.Close()
507	c, err := proto.Dial(s.Addr())
508	ok(t, err)
509	defer c.Close()
510
511	s.SetAdd("s1", "aap", "noot", "mies")
512	s.SetAdd("s2", "noot", "mies", "vuur")
513	s.SetAdd("s3", "aap", "mies", "wim")
514
515	// Simple case
516	{
517		res, err := c.DoStrings("SINTER", "s1", "s2")
518		ok(t, err)
519		sort.Strings(res)
520		equals(t, []string{"mies", "noot"}, res)
521	}
522
523	// No other set
524	{
525		res, err := c.DoStrings("SINTER", "s1")
526		ok(t, err)
527		sort.Strings(res)
528		equals(t, []string{"aap", "mies", "noot"}, res)
529	}
530
531	// 3 sets
532	mustDo(t, c,
533		"SINTER", "s1", "s2", "s3",
534		proto.Strings("mies"),
535	)
536
537	// A nonexisting key
538	mustDo(t, c,
539		"SINTER", "s9",
540		proto.Strings(),
541	)
542
543	// With one of the keys being an empty set, the resulting set is also empty
544	mustDo(t, c,
545		"SINTER", "s1", "s9",
546		proto.Strings(),
547	)
548
549	t.Run("errors", func(t *testing.T) {
550		s.SetAdd("chk", "aap", "noot")
551		s.Set("str", "value")
552
553		mustDo(t, c,
554			"SINTER",
555			proto.Error(errWrongNumber("sinter")),
556		)
557		mustDo(t, c,
558			"SINTER", "str",
559			proto.Error(msgWrongType),
560		)
561		mustDo(t, c,
562			"SINTER", "chk", "str",
563			proto.Error(msgWrongType),
564		)
565	})
566}
567
568// Test SINTERSTORE
569func TestSinterstore(t *testing.T) {
570	s, err := Run()
571	ok(t, err)
572	defer s.Close()
573	c, err := proto.Dial(s.Addr())
574	ok(t, err)
575	defer c.Close()
576
577	s.SetAdd("s1", "aap", "noot", "mies")
578	s.SetAdd("s2", "noot", "mies", "vuur")
579	s.SetAdd("s3", "aap", "mies", "wim")
580
581	// Simple case
582	{
583		mustDo(t, c,
584			"SINTERSTORE", "res", "s1", "s3",
585			proto.Int(2),
586		)
587		s.CheckSet(t, "res", "aap", "mies")
588	}
589
590	// With one of the keys being an empty set, the resulting set is also empty
591	{
592		must0(t, c,
593			"SINTERSTORE", "res", "s1", "s9",
594		)
595		s.CheckSet(t, "res", []string{}...)
596	}
597
598	t.Run("errors", func(t *testing.T) {
599		s.SetAdd("chk", "aap", "noot")
600		s.Set("str", "value")
601
602		mustDo(t, c,
603			"SINTERSTORE",
604			proto.Error(errWrongNumber("sinterstore")),
605		)
606		mustDo(t, c,
607			"SINTERSTORE", "t",
608			proto.Error(errWrongNumber("sinterstore")),
609		)
610		mustDo(t, c,
611			"SINTERSTORE", "t", "str",
612			proto.Error(msgWrongType),
613		)
614	})
615}
616
617// Test SUNION
618func TestSunion(t *testing.T) {
619	s, err := Run()
620	ok(t, err)
621	defer s.Close()
622	c, err := proto.Dial(s.Addr())
623	ok(t, err)
624	defer c.Close()
625
626	s.SetAdd("s1", "aap", "noot", "mies")
627	s.SetAdd("s2", "noot", "mies", "vuur")
628	s.SetAdd("s3", "aap", "mies", "wim")
629
630	// Simple case
631	{
632		res, err := c.DoStrings("SUNION", "s1", "s2")
633		ok(t, err)
634		sort.Strings(res)
635		equals(t, []string{"aap", "mies", "noot", "vuur"}, res)
636	}
637
638	// No other set
639	{
640		res, err := c.DoStrings("SUNION", "s1")
641		ok(t, err)
642		sort.Strings(res)
643		equals(t, []string{"aap", "mies", "noot"}, res)
644	}
645
646	// 3 sets
647	{
648		res, err := c.DoStrings("SUNION", "s1", "s2", "s3")
649		ok(t, err)
650		sort.Strings(res)
651		equals(t, []string{"aap", "mies", "noot", "vuur", "wim"}, res)
652	}
653
654	// A nonexisting key
655	{
656		mustDo(t, c,
657			"SUNION", "s9",
658			proto.Strings(),
659		)
660	}
661
662	t.Run("errors", func(t *testing.T) {
663		s.SetAdd("chk", "aap", "noot")
664		s.Set("str", "value")
665
666		mustDo(t, c,
667			"SUNION",
668			proto.Error(errWrongNumber("sunion")),
669		)
670		mustDo(t, c,
671			"SUNION", "str",
672			proto.Error(msgWrongType),
673		)
674		mustDo(t, c,
675			"SUNION", "chk", "str",
676			proto.Error(msgWrongType),
677		)
678	})
679}
680
681// Test SUNIONSTORE
682func TestSunionstore(t *testing.T) {
683	s, err := Run()
684	ok(t, err)
685	defer s.Close()
686	c, err := proto.Dial(s.Addr())
687	ok(t, err)
688	defer c.Close()
689
690	s.SetAdd("s1", "aap", "noot", "mies")
691	s.SetAdd("s2", "noot", "mies", "vuur")
692	s.SetAdd("s3", "aap", "mies", "wim")
693
694	// Simple case
695	{
696		mustDo(t, c,
697			"SUNIONSTORE", "res", "s1", "s3",
698			proto.Int(4),
699		)
700		s.CheckSet(t, "res", "aap", "mies", "noot", "wim")
701	}
702
703	t.Run("errors", func(t *testing.T) {
704		s.SetAdd("chk", "aap", "noot")
705		s.Set("str", "value")
706
707		mustDo(t, c,
708			"SUNIONSTORE",
709			proto.Error(errWrongNumber("sunionstore")),
710		)
711		mustDo(t, c,
712			"SUNIONSTORE", "t",
713			proto.Error(errWrongNumber("sunionstore")),
714		)
715		mustDo(t, c,
716			"SUNIONSTORE", "t", "str",
717			proto.Error(msgWrongType),
718		)
719	})
720}
721
722func TestSscan(t *testing.T) {
723	s, err := Run()
724	ok(t, err)
725	defer s.Close()
726	c, err := proto.Dial(s.Addr())
727	ok(t, err)
728	defer c.Close()
729
730	// We cheat with sscan. It always returns everything.
731
732	s.SetAdd("set", "value1", "value2")
733
734	// No problem
735	mustDo(t, c,
736		"SSCAN", "set", "0",
737		proto.Array(
738			proto.String("0"),
739			proto.Array(
740				proto.String("value1"),
741				proto.String("value2"),
742			),
743		),
744	)
745
746	// Invalid cursor
747	mustDo(t, c,
748		"SSCAN", "set", "42",
749		proto.Array(
750			proto.String("0"),
751			proto.Strings(),
752		),
753	)
754
755	// COUNT (ignored)
756	mustDo(t, c,
757		"SSCAN", "set", "0", "COUNT", "200",
758		proto.Array(
759			proto.String("0"),
760			proto.Array(
761				proto.String("value1"),
762				proto.String("value2"),
763			),
764		),
765	)
766
767	// MATCH
768	s.SetAdd("set", "aap", "noot", "mies")
769	mustDo(t, c,
770		"SSCAN", "set", "0", "MATCH", "mi*",
771		proto.Array(
772			proto.String("0"),
773			proto.Array(
774				proto.String("mies"),
775			),
776		),
777	)
778
779	t.Run("errors", func(t *testing.T) {
780		mustDo(t, c,
781			"SSCAN",
782			proto.Error(errWrongNumber("sscan")),
783		)
784		mustDo(t, c,
785			"SSCAN", "set",
786			proto.Error(errWrongNumber("sscan")),
787		)
788		mustDo(t, c,
789			"SSCAN", "set", "noint",
790			proto.Error(msgInvalidCursor),
791		)
792		mustDo(t, c,
793			"SSCAN", "set", "0", "MATCH",
794			proto.Error(msgSyntaxError),
795		)
796		mustDo(t, c,
797			"SSCAN", "set", "0", "COUNT",
798			proto.Error(msgSyntaxError),
799		)
800		mustDo(t, c,
801			"SSCAN", "set", "0", "COUNT", "noint",
802			proto.Error(msgInvalidInt),
803		)
804		s.Set("str", "value")
805		mustDo(t, c,
806			"SSCAN", "str", "0",
807			proto.Error(msgWrongType),
808		)
809	})
810}
811