1pub mod cluster;
27pub mod sentinel;
28pub mod server;
29pub mod utils;
30
31use std::collections::{HashMap, HashSet, VecDeque};
32use std::sync::{Arc, Mutex};
33
34use redis::{
35 Cmd, ConnectionLike, ErrorKind, Pipeline, RedisError, RedisResult, ServerError, Value,
36};
37
38#[cfg(feature = "aio")]
39use futures::{FutureExt, future};
40
41#[cfg(feature = "aio")]
42use redis::{RedisFuture, aio::ConnectionLike as AioConnectionLike};
43
44pub trait IntoRedisValue {
48 fn into_redis_value(self) -> Value;
50}
51
52macro_rules! into_redis_value_impl_int {
53 ($t:ty) => {
54 impl IntoRedisValue for $t {
55 fn into_redis_value(self) -> Value {
56 Value::Int(self as i64)
57 }
58 }
59 };
60}
61
62into_redis_value_impl_int!(i8);
63into_redis_value_impl_int!(i16);
64into_redis_value_impl_int!(i32);
65into_redis_value_impl_int!(i64);
66into_redis_value_impl_int!(u8);
67into_redis_value_impl_int!(u16);
68into_redis_value_impl_int!(u32);
69
70macro_rules! into_redis_value_impl_float {
71 ($t:ty) => {
72 impl IntoRedisValue for $t {
73 fn into_redis_value(self) -> Value {
74 Value::Double(self as f64)
75 }
76 }
77 };
78}
79
80into_redis_value_impl_float!(f32);
81into_redis_value_impl_float!(f64);
82
83impl IntoRedisValue for String {
84 fn into_redis_value(self) -> Value {
85 Value::BulkString(self.as_bytes().to_vec())
86 }
87}
88
89impl IntoRedisValue for &str {
90 fn into_redis_value(self) -> Value {
91 Value::BulkString(self.as_bytes().to_vec())
92 }
93}
94
95impl IntoRedisValue for bool {
96 fn into_redis_value(self) -> Value {
97 Value::Boolean(self)
98 }
99}
100
101#[cfg(feature = "bytes")]
102impl IntoRedisValue for bytes::Bytes {
103 fn into_redis_value(self) -> Value {
104 Value::BulkString(self.to_vec())
105 }
106}
107
108impl IntoRedisValue for Vec<u8> {
109 fn into_redis_value(self) -> Value {
110 Value::BulkString(self)
111 }
112}
113
114impl IntoRedisValue for Vec<Value> {
115 fn into_redis_value(self) -> Value {
116 Value::Array(self)
117 }
118}
119
120impl IntoRedisValue for Vec<(Value, Value)> {
121 fn into_redis_value(self) -> Value {
122 Value::Map(self)
123 }
124}
125
126impl<K, V> IntoRedisValue for HashMap<K, V>
127where
128 K: IntoRedisValue,
129 V: IntoRedisValue,
130{
131 fn into_redis_value(self) -> Value {
132 Value::Map(
133 self.into_iter()
134 .map(|(k, v)| (k.into_redis_value(), v.into_redis_value()))
135 .collect(),
136 )
137 }
138}
139
140impl<V> IntoRedisValue for HashSet<V>
141where
142 V: IntoRedisValue,
143{
144 fn into_redis_value(self) -> Value {
145 Value::Set(
146 self.into_iter()
147 .map(IntoRedisValue::into_redis_value)
148 .collect(),
149 )
150 }
151}
152
153impl IntoRedisValue for Value {
154 fn into_redis_value(self) -> Value {
155 self
156 }
157}
158
159impl IntoRedisValue for ServerError {
160 fn into_redis_value(self) -> Value {
161 Value::ServerError(self)
162 }
163}
164
165#[macro_export]
194macro_rules! redis_value {
195 ({$($k:tt: $v:tt),* $(,)*}) => {
197 redis::Value::Map(vec![$(($crate::redis_value!($k), $crate::redis_value!($v))),*])
198 };
199
200 ([$($e:tt),* $(,)*]) => {
202 redis::Value::Array(vec![$($crate::redis_value!($e)),*])
203 };
204
205 (set:[$($e:tt),* $(,)*]) => {
207 redis::Value::Set(vec![$($crate::redis_value!($e)),*])
208 };
209
210 (simple:$e:tt) => {
212 redis::Value::SimpleString($e.to_string())
213 };
214
215 (nil) => {
217 redis::Value::Nil
218 };
219
220 (ok) => {
222 $crate::redis_value!(okay)
223 };
224
225 (okay) => {
226 redis::Value::Okay
227 };
228
229 (($context:tt:$e:tt)) => {
231 $crate::redis_value!($context:$e)
232 };
233
234 ($e:expr) => {
236 $crate::IntoRedisValue::into_redis_value($e)
237 };
238}
239
240pub trait IntoRedisCmdBytes {
243 fn into_redis_cmd_bytes(self) -> Vec<u8>;
245}
246
247impl IntoRedisCmdBytes for Cmd {
248 fn into_redis_cmd_bytes(self) -> Vec<u8> {
249 self.get_packed_command()
250 }
251}
252
253impl IntoRedisCmdBytes for &Cmd {
254 fn into_redis_cmd_bytes(self) -> Vec<u8> {
255 self.get_packed_command()
256 }
257}
258
259impl IntoRedisCmdBytes for &mut Cmd {
260 fn into_redis_cmd_bytes(self) -> Vec<u8> {
261 self.get_packed_command()
262 }
263}
264
265impl IntoRedisCmdBytes for Pipeline {
266 fn into_redis_cmd_bytes(self) -> Vec<u8> {
267 self.get_packed_pipeline()
268 }
269}
270
271impl IntoRedisCmdBytes for &Pipeline {
272 fn into_redis_cmd_bytes(self) -> Vec<u8> {
273 self.get_packed_pipeline()
274 }
275}
276
277impl IntoRedisCmdBytes for &mut Pipeline {
278 fn into_redis_cmd_bytes(self) -> Vec<u8> {
279 self.get_packed_pipeline()
280 }
281}
282
283pub struct MockCmd {
285 cmd_bytes: Vec<u8>,
286 responses: Result<Vec<Value>, RedisError>,
287}
288
289impl MockCmd {
290 pub fn new<C, V>(cmd: C, response: Result<V, RedisError>) -> Self
293 where
294 C: IntoRedisCmdBytes,
295 V: IntoRedisValue,
296 {
297 MockCmd {
298 cmd_bytes: cmd.into_redis_cmd_bytes(),
299 responses: response.map(|r| vec![r.into_redis_value()]),
300 }
301 }
302
303 pub fn with_values<C, V>(cmd: C, responses: Result<Vec<V>, RedisError>) -> Self
306 where
307 C: IntoRedisCmdBytes,
308 V: IntoRedisValue,
309 {
310 MockCmd {
311 cmd_bytes: cmd.into_redis_cmd_bytes(),
312 responses: responses.map(|xs| xs.into_iter().map(|x| x.into_redis_value()).collect()),
313 }
314 }
315}
316
317#[derive(Clone)]
320pub struct MockRedisConnection {
321 commands: Arc<Mutex<VecDeque<MockCmd>>>,
322 assert_is_empty_on_drop: bool,
323}
324
325impl MockRedisConnection {
326 pub fn new<I>(commands: I) -> Self
328 where
329 I: IntoIterator<Item = MockCmd>,
330 {
331 MockRedisConnection {
332 commands: Arc::new(Mutex::new(VecDeque::from_iter(commands))),
333 assert_is_empty_on_drop: false,
334 }
335 }
336
337 pub fn assert_all_commands_consumed(mut self) -> Self {
339 self.assert_is_empty_on_drop = true;
340 self
341 }
342}
343
344impl Drop for MockRedisConnection {
345 fn drop(&mut self) {
346 if self.assert_is_empty_on_drop {
347 let commands = self.commands.lock().unwrap();
348 if Arc::strong_count(&self.commands) == 1 {
349 assert!(commands.back().is_none());
350 }
351 }
352 }
353}
354
355impl MockRedisConnection {
356 pub fn is_empty(&self) -> bool {
357 self.commands.lock().unwrap().is_empty()
358 }
359}
360
361impl ConnectionLike for MockRedisConnection {
362 fn req_packed_command(&mut self, cmd: &[u8]) -> RedisResult<Value> {
363 let mut commands = self.commands.lock().unwrap();
364 let next_cmd = commands.pop_front().ok_or_else(|| {
365 self.assert_is_empty_on_drop = false;
366 RedisError::from((ErrorKind::Client, "TEST", "unexpected command".to_owned()))
367 })?;
368
369 if cmd != next_cmd.cmd_bytes {
370 self.assert_is_empty_on_drop = false;
371 return Err(RedisError::from((
372 ErrorKind::Client,
373 "TEST",
374 format!(
375 "unexpected command: expected={}, actual={}",
376 String::from_utf8(next_cmd.cmd_bytes)
377 .unwrap_or_else(|_| "decode error".to_owned()),
378 String::from_utf8(Vec::from(cmd)).unwrap_or_else(|_| "decode error".to_owned()),
379 ),
380 )));
381 }
382
383 next_cmd
384 .responses
385 .and_then(|values| match values.as_slice() {
386 [value] => Ok(value.clone()),
387 [] => {
388 self.assert_is_empty_on_drop = false;
389 Err(RedisError::from((
390 ErrorKind::Client,
391 "no value configured as response",
392 )))},
393 _ => {
394 self.assert_is_empty_on_drop = false;
395 Err(RedisError::from((
396 ErrorKind::Client,
397 "multiple values configured as response for command expecting a single value",
398 )))},
399 })
400 }
401
402 fn req_packed_commands(
403 &mut self,
404 cmd: &[u8],
405 _offset: usize,
406 _count: usize,
407 ) -> RedisResult<Vec<Value>> {
408 let mut commands = self.commands.lock().unwrap();
409 let next_cmd = commands.pop_front().ok_or_else(|| {
410 RedisError::from((ErrorKind::Client, "TEST", "unexpected command".to_owned()))
411 })?;
412
413 if cmd != next_cmd.cmd_bytes {
414 return Err(RedisError::from((
415 ErrorKind::Client,
416 "TEST",
417 format!(
418 "unexpected command: expected={}, actual={}",
419 String::from_utf8(next_cmd.cmd_bytes)
420 .unwrap_or_else(|_| "decode error".to_owned()),
421 String::from_utf8(Vec::from(cmd)).unwrap_or_else(|_| "decode error".to_owned()),
422 ),
423 )));
424 }
425
426 next_cmd.responses
427 }
428
429 fn get_db(&self) -> i64 {
430 0
431 }
432
433 fn check_connection(&mut self) -> bool {
434 true
435 }
436
437 fn is_open(&self) -> bool {
438 true
439 }
440}
441
442#[cfg(feature = "aio")]
443impl AioConnectionLike for MockRedisConnection {
444 fn req_packed_command<'a>(&'a mut self, cmd: &'a Cmd) -> RedisFuture<'a, Value> {
445 let packed_cmd = cmd.get_packed_command();
446 let response = <MockRedisConnection as ConnectionLike>::req_packed_command(
447 self,
448 packed_cmd.as_slice(),
449 );
450 future::ready(response).boxed()
451 }
452
453 fn req_packed_commands<'a>(
454 &'a mut self,
455 cmd: &'a Pipeline,
456 offset: usize,
457 count: usize,
458 ) -> RedisFuture<'a, Vec<Value>> {
459 let packed_cmd = cmd.get_packed_pipeline();
460 let response = <MockRedisConnection as ConnectionLike>::req_packed_commands(
461 self,
462 packed_cmd.as_slice(),
463 offset,
464 count,
465 );
466 future::ready(response).boxed()
467 }
468
469 fn get_db(&self) -> i64 {
470 0
471 }
472}
473
474#[cfg(test)]
475mod tests {
476 use super::{IntoRedisValue, MockCmd, MockRedisConnection};
477 use redis::{ErrorKind, ServerError, Value, cmd, make_extension_error, pipe};
478 use std::collections::{HashMap, HashSet};
479
480 #[test]
481 fn into_redis_value_i8() {
482 assert_eq!(42_i8.into_redis_value(), Value::Int(42));
483 }
484
485 #[test]
486 fn into_redis_value_i16() {
487 assert_eq!(42_i16.into_redis_value(), Value::Int(42));
488 }
489
490 #[test]
491 fn into_redis_value_i32() {
492 assert_eq!(42_i32.into_redis_value(), Value::Int(42));
493 }
494
495 #[test]
496 fn into_redis_value_i64() {
497 assert_eq!(42_i64.into_redis_value(), Value::Int(42));
498 }
499
500 #[test]
501 fn into_redis_value_u8() {
502 assert_eq!(42_u8.into_redis_value(), Value::Int(42));
503 }
504
505 #[test]
506 fn into_redis_value_u16() {
507 assert_eq!(42_u16.into_redis_value(), Value::Int(42));
508 }
509
510 #[test]
511 fn into_redis_value_u32() {
512 assert_eq!(42_u32.into_redis_value(), Value::Int(42));
513 }
514
515 #[test]
516 fn into_redis_value_string() {
517 let input = "foo".to_string();
518
519 let actual = input.into_redis_value();
520
521 let expected = Value::BulkString(vec![
522 0x66, 0x6f, 0x6f, ]);
526 assert_eq!(actual, expected);
527 }
528
529 #[test]
530 fn into_redis_value_str_ref() {
531 let input = "foo";
532
533 let actual = input.into_redis_value();
534
535 let expected = Value::BulkString(vec![
536 0x66, 0x6f, 0x6f, ]);
540 assert_eq!(actual, expected);
541 }
542
543 #[test]
544 fn into_redis_value_bool_true() {
545 assert_eq!(true.into_redis_value(), Value::Boolean(true));
546 }
547
548 #[test]
549 fn into_redis_value_bool_false() {
550 assert_eq!(false.into_redis_value(), Value::Boolean(false));
551 }
552
553 #[cfg(feature = "bytes")]
554 #[test]
555 fn into_redis_value_bytes() {
556 let input = bytes::Bytes::from("foo");
557
558 let actual = input.into_redis_value();
559
560 let expected = Value::BulkString(vec![
561 0x66, 0x6f, 0x6f, ]);
565 assert_eq!(actual, expected);
566 }
567
568 #[test]
569 fn into_redis_value_vec_u8() {
570 let input = vec![0x66 , 0x6f , 0x6f ];
571
572 let actual = input.into_redis_value();
573
574 let expected = Value::BulkString(vec![
575 0x66, 0x6f, 0x6f, ]);
579 assert_eq!(actual, expected);
580 }
581
582 #[test]
583 fn into_redis_value_vec_value() {
584 let input = vec![Value::Int(42), Value::Boolean(true)];
585
586 let actual = input.into_redis_value();
587
588 let expected = Value::Array(vec![Value::Int(42), Value::Boolean(true)]);
589 assert_eq!(actual, expected);
590 }
591
592 #[test]
593 fn into_redis_value_vec_value_value() {
594 let input = vec![
595 (Value::Int(42), Value::Boolean(true)),
596 (Value::Int(23), Value::Nil),
597 ];
598
599 let actual = input.into_redis_value();
600
601 let expected = Value::Map(vec![
602 (Value::Int(42), Value::Boolean(true)),
603 (Value::Int(23), Value::Nil),
604 ]);
605 assert_eq!(actual, expected);
606 }
607
608 #[test]
609 fn into_redis_value_hashmap() {
610 let input = HashMap::from([(23, true), (42, false)]);
611
612 let actual = input.into_redis_value();
613
614 let mut actual_entries = actual
615 .into_map_iter()
616 .expect("extracting elements should work")
617 .collect::<Vec<(Value, Value)>>();
618
619 actual_entries.sort_by(|a, b| {
621 let Value::Int(int_key_a) = a.0 else {
622 panic!("left-hand argument has to be a `Value::Int`");
623 };
624 let Value::Int(int_key_b) = b.0 else {
625 panic!("right-hand argument has to be a `Value::Int`");
626 };
627 int_key_a.cmp(&int_key_b)
628 });
629
630 let expected_entries = vec![
631 (Value::Int(23), Value::Boolean(true)),
632 (Value::Int(42), Value::Boolean(false)),
633 ];
634
635 assert_eq!(actual_entries, expected_entries);
636 }
637
638 #[test]
639 fn into_redis_value_hashset() {
640 let input = HashSet::from([23, 42]);
641
642 let actual = input.into_redis_value();
643
644 let mut actual_entries = actual
645 .into_sequence()
646 .expect("extracting elements should work");
647
648 actual_entries.sort_by(|a, b| {
650 let Value::Int(int_a) = a else {
651 panic!("left-hand argument has to be a `Value::Int`");
652 };
653 let Value::Int(int_b) = b else {
654 panic!("right-hand argument has to be a `Value::Int`");
655 };
656 int_a.cmp(int_b)
657 });
658
659 let expected_entries = vec![Value::Int(23), Value::Int(42)];
660
661 assert_eq!(actual_entries, expected_entries);
662 }
663
664 #[test]
665 fn into_redis_value_value() {
666 let input = Value::Int(42);
667
668 let actual = input.into_redis_value();
669
670 assert_eq!(actual, Value::Int(42));
671 }
672
673 #[test]
674 fn into_redis_value_server_error() {
675 let server_error = ServerError::try_from(make_extension_error("FOO".to_string(), None))
676 .expect("conversion should work");
677
678 let actual = server_error.clone().into_redis_value();
679
680 assert_eq!(actual, Value::ServerError(server_error));
681 }
682
683 #[test]
684 fn redis_simple_direct() {
685 assert_eq!(
686 redis_value!(simple:"foo"),
687 Value::SimpleString("foo".to_string())
688 );
689 }
690
691 #[test]
692 fn redis_simple_in_complex() {
693 let actual = redis_value!([(simple:"foo")]);
694
695 let expected = Value::Array(vec![Value::SimpleString("foo".to_string())]);
696 assert_eq!(actual, expected);
697 }
698
699 #[test]
700 fn redis_nil() {
701 assert_eq!(redis_value!(nil), Value::Nil);
702 }
703
704 #[test]
705 fn redis_ok() {
706 assert_eq!(redis_value!(ok), Value::Okay);
707 }
708
709 #[test]
710 fn redis_okay() {
711 assert_eq!(redis_value!(okay), Value::Okay);
712 }
713
714 #[test]
715 fn redis_i8() {
716 assert_eq!(redis_value!(42_i8), Value::Int(42));
717 }
718
719 #[test]
720 fn redis_i16() {
721 assert_eq!(redis_value!(42_i16), Value::Int(42));
722 }
723
724 #[test]
725 fn redis_i32() {
726 assert_eq!(redis_value!(42_i32), Value::Int(42));
727 }
728
729 #[test]
730 fn redis_i64() {
731 assert_eq!(redis_value!(42_i64), Value::Int(42));
732 }
733
734 #[test]
735 fn redis_u8() {
736 assert_eq!(redis_value!(42_u8), Value::Int(42));
737 }
738
739 #[test]
740 fn redis_u16() {
741 assert_eq!(redis_value!(42_u16), Value::Int(42));
742 }
743
744 #[test]
745 fn redis_u32() {
746 assert_eq!(redis_value!(42_u32), Value::Int(42));
747 }
748
749 #[test]
750 fn redis_string() {
751 let actual = redis_value!("foo".to_string());
752
753 let expected = Value::BulkString(vec![
754 0x66, 0x6f, 0x6f, ]);
758 assert_eq!(actual, expected);
759 }
760
761 #[test]
762 fn redis_str_ref() {
763 let actual = redis_value!("foo");
764
765 let expected = Value::BulkString(vec![
766 0x66, 0x6f, 0x6f, ]);
770 assert_eq!(actual, expected);
771 }
772
773 #[test]
774 fn redis_true() {
775 assert_eq!(redis_value!(true), Value::Boolean(true));
776 }
777
778 #[test]
779 fn redis_false() {
780 assert_eq!(redis_value!(false), Value::Boolean(false));
781 }
782
783 #[test]
784 fn redis_value() {
785 let actual = Value::Int(42);
786
787 assert_eq!(redis_value!(actual), Value::Int(42));
788 }
789
790 #[test]
791 fn redis_array_empty() {
792 assert_eq!(redis_value!([]), Value::Array(vec![]));
793 }
794
795 #[test]
796 fn redis_array_single_entry() {
797 let actual = redis_value!([42]);
798
799 let expected = Value::Array(vec![Value::Int(42)]);
800 assert_eq!(actual, expected);
801 }
802
803 #[test]
804 fn redis_array_single_entry_trailing_comma() {
805 let actual = redis_value!([42,]);
806
807 let expected = Value::Array(vec![Value::Int(42)]);
808 assert_eq!(actual, expected);
809 }
810
811 #[test]
812 fn redis_array_multiple_primitive_entries() {
813 let last_arg = Value::Boolean(true); let actual = redis_value!([42, "foo", nil, last_arg]);
815
816 let expected1 = Value::Int(42);
817 let expected2 = Value::BulkString(vec![
818 0x66, 0x6f, 0x6f, ]);
822 let expected3 = Value::Nil;
823 let expected4 = Value::Boolean(true);
824 let expected = Value::Array(vec![expected1, expected2, expected3, expected4]);
825 assert_eq!(actual, expected);
826 }
827
828 #[test]
829 fn redis_array_multiple_entries() {
830 let last_arg = Value::Boolean(true); let actual = redis_value!([42, ["foo", nil,], last_arg]);
832
833 let expected1 = Value::Int(42);
834 let expected21 = Value::BulkString(vec![
835 0x66, 0x6f, 0x6f, ]);
839 let expected22 = Value::Nil;
840 let expected2 = Value::Array(vec![expected21, expected22]);
841 let expected3 = Value::Boolean(true);
842 let expected = Value::Array(vec![expected1, expected2, expected3]);
843 assert_eq!(actual, expected);
844 }
845
846 #[test]
847 fn redis_set_empty() {
848 assert_eq!(redis_value!(set:[]), Value::Set(vec![]));
849 }
850
851 #[test]
852 fn redis_set_single_entry() {
853 let actual = redis_value!(set:[42]);
854
855 let expected = Value::Set(vec![Value::Int(42)]);
856 assert_eq!(actual, expected);
857 }
858
859 #[test]
860 fn redis_set_single_entry_trailing_comma() {
861 let actual = redis_value!(set:[42,]);
862
863 let expected = Value::Set(vec![Value::Int(42)]);
864 assert_eq!(actual, expected);
865 }
866
867 #[test]
868 fn redis_set_multiple_primitive_entries() {
869 let last_arg = Value::Boolean(true); let actual = redis_value!(set:[42, "foo", nil, last_arg]);
871
872 let expected1 = Value::Int(42);
873 let expected2 = Value::BulkString(vec![
874 0x66, 0x6f, 0x6f, ]);
878 let expected3 = Value::Nil;
879 let expected4 = Value::Boolean(true);
880 let expected = Value::Set(vec![expected1, expected2, expected3, expected4]);
881 assert_eq!(actual, expected);
882 }
883
884 #[test]
885 fn redis_set_multiple_entries() {
886 let last_arg = Value::Boolean(true); let actual = redis_value!(set:[42, (set:["foo", nil,]), last_arg]);
888
889 let expected1 = Value::Int(42);
890 let expected21 = Value::BulkString(vec![
891 0x66, 0x6f, 0x6f, ]);
895 let expected22 = Value::Nil;
896 let expected2 = Value::Set(vec![expected21, expected22]);
897 let expected3 = Value::Boolean(true);
898 let expected = Value::Set(vec![expected1, expected2, expected3]);
899 assert_eq!(actual, expected);
900 }
901
902 #[test]
903 fn redis_map_empty() {
904 assert_eq!(redis_value!({}), Value::Map(vec![]));
905 }
906
907 #[test]
908 fn redis_map_single_entry() {
909 let actual = redis_value!({42: true});
910
911 let expected = Value::Map(vec![(Value::Int(42), Value::Boolean(true))]);
912 assert_eq!(actual, expected);
913 }
914
915 #[test]
916 fn redis_map_single_entry_trailing_comma() {
917 let actual = redis_value!({42: true,});
918
919 let expected = Value::Map(vec![(Value::Int(42), Value::Boolean(true))]);
920 assert_eq!(actual, expected);
921 }
922
923 #[test]
924 fn redis_map_multiple_primitive_entries() {
925 let actual = redis_value!({42: true, nil: "foo"});
926
927 let expected1 = (Value::Int(42), Value::Boolean(true));
928 let expected2 = (
929 Value::Nil,
930 Value::BulkString(vec![
931 0x66, 0x6f, 0x6f, ]),
935 );
936 let expected = Value::Map(vec![expected1, expected2]);
937 assert_eq!(actual, expected);
938 }
939
940 #[test]
941 fn redis_map_multiple_entries() {
942 let actual = redis_value!({[42, false]: {true: [23, 4711],}, nil: "foo"});
943
944 let expected1_key = Value::Array(vec![Value::Int(42), Value::Boolean(false)]);
945 let expected1_value_key = Value::Boolean(true);
946 let expected1_value_value = Value::Array(vec![Value::Int(23), Value::Int(4711)]);
947 let expected1_value = Value::Map(vec![(expected1_value_key, expected1_value_value)]);
948 let expected1 = (expected1_key, expected1_value);
949 let expected2 = (
950 Value::Nil,
951 Value::BulkString(vec![
952 0x66, 0x6f, 0x6f, ]),
956 );
957 let expected = Value::Map(vec![expected1, expected2]);
958 assert_eq!(actual, expected);
959 }
960
961 #[test]
962 fn sync_basic_test() {
963 let mut conn = MockRedisConnection::new(vec![
964 MockCmd::new(cmd("SET").arg("foo").arg(42), Ok("")),
965 MockCmd::new(cmd("GET").arg("foo"), Ok(42)),
966 MockCmd::new(cmd("SET").arg("bar").arg("foo"), Ok("")),
967 MockCmd::new(cmd("GET").arg("bar"), Ok("foo")),
968 ])
969 .assert_all_commands_consumed();
970
971 cmd("SET").arg("foo").arg(42).exec(&mut conn).unwrap();
972 assert_eq!(cmd("GET").arg("foo").query(&mut conn), Ok(42));
973
974 cmd("SET").arg("bar").arg("foo").exec(&mut conn).unwrap();
975 assert_eq!(
976 cmd("GET").arg("bar").query(&mut conn),
977 Ok(Value::BulkString(b"foo".as_ref().into()))
978 );
979 }
980
981 #[cfg(feature = "aio")]
982 #[tokio::test]
983 async fn async_basic_test() {
984 let mut conn = MockRedisConnection::new(vec![
985 MockCmd::new(cmd("SET").arg("foo").arg(42), Ok("")),
986 MockCmd::new(cmd("GET").arg("foo"), Ok(42)),
987 MockCmd::new(cmd("SET").arg("bar").arg("foo"), Ok("")),
988 MockCmd::new(cmd("GET").arg("bar"), Ok("foo")),
989 ])
990 .assert_all_commands_consumed();
991
992 cmd("SET")
993 .arg("foo")
994 .arg("42")
995 .exec_async(&mut conn)
996 .await
997 .unwrap();
998 let result: Result<usize, _> = cmd("GET").arg("foo").query_async(&mut conn).await;
999 assert_eq!(result, Ok(42));
1000
1001 cmd("SET")
1002 .arg("bar")
1003 .arg("foo")
1004 .exec_async(&mut conn)
1005 .await
1006 .unwrap();
1007 let result: Result<Vec<u8>, _> = cmd("GET").arg("bar").query_async(&mut conn).await;
1008 assert_eq!(result.as_deref(), Ok(&b"foo"[..]));
1009 }
1010
1011 #[test]
1012 fn errors_for_unexpected_commands() {
1013 let mut conn = MockRedisConnection::new(vec![
1014 MockCmd::new(cmd("SET").arg("foo").arg(42), Ok("")),
1015 MockCmd::new(cmd("GET").arg("foo"), Ok(42)),
1016 ])
1017 .assert_all_commands_consumed();
1018
1019 cmd("SET").arg("foo").arg(42).exec(&mut conn).unwrap();
1020 assert_eq!(cmd("GET").arg("foo").query(&mut conn), Ok(42));
1021
1022 let err = cmd("SET")
1023 .arg("bar")
1024 .arg("foo")
1025 .exec(&mut conn)
1026 .unwrap_err();
1027 assert_eq!(err.kind(), ErrorKind::Client);
1028 assert_eq!(err.detail(), Some("unexpected command"));
1029 }
1030
1031 #[test]
1032 fn errors_for_mismatched_commands() {
1033 let mut conn = MockRedisConnection::new(vec![
1034 MockCmd::new(cmd("SET").arg("foo").arg(42), Ok("")),
1035 MockCmd::new(cmd("GET").arg("foo"), Ok(42)),
1036 MockCmd::new(cmd("SET").arg("bar").arg("foo"), Ok("")),
1037 ])
1038 .assert_all_commands_consumed();
1039
1040 cmd("SET").arg("foo").arg(42).exec(&mut conn).unwrap();
1041 let err = cmd("SET")
1042 .arg("bar")
1043 .arg("foo")
1044 .exec(&mut conn)
1045 .unwrap_err();
1046 assert_eq!(err.kind(), ErrorKind::Client);
1047 assert!(err.detail().unwrap().contains("unexpected command"));
1048 }
1049
1050 #[test]
1051 fn pipeline_basic_test() {
1052 let mut conn = MockRedisConnection::new(vec![MockCmd::with_values(
1053 pipe().cmd("GET").arg("foo").cmd("GET").arg("bar"),
1054 Ok(vec!["hello", "world"]),
1055 )])
1056 .assert_all_commands_consumed();
1057
1058 let results: Vec<String> = pipe()
1059 .cmd("GET")
1060 .arg("foo")
1061 .cmd("GET")
1062 .arg("bar")
1063 .query(&mut conn)
1064 .expect("success");
1065 assert_eq!(results, vec!["hello", "world"]);
1066 }
1067
1068 #[test]
1069 fn pipeline_atomic_test() {
1070 let mut conn = MockRedisConnection::new(vec![MockCmd::with_values(
1071 pipe().atomic().cmd("GET").arg("foo").cmd("GET").arg("bar"),
1072 Ok(vec![Value::Array(
1073 vec!["hello", "world"]
1074 .into_iter()
1075 .map(|x| Value::BulkString(x.as_bytes().into()))
1076 .collect(),
1077 )]),
1078 )])
1079 .assert_all_commands_consumed();
1080
1081 let results: Vec<String> = pipe()
1082 .atomic()
1083 .cmd("GET")
1084 .arg("foo")
1085 .cmd("GET")
1086 .arg("bar")
1087 .query(&mut conn)
1088 .expect("success");
1089 assert_eq!(results, vec!["hello", "world"]);
1090 }
1091}