1mod args;
55mod arrays;
56mod blocking;
57mod cpu;
58mod hashes;
59mod keyspace;
60mod lists;
61mod migrate;
62mod scan;
63mod scripting;
64mod server;
65mod sets;
66mod strings;
67pub mod table;
68mod zsets;
69
70pub use args::Args;
71pub use blocking::{Parked, Waiters};
72pub use table::{COMMANDS, Spec, arity_ok, lookup};
73
74use crate::reply::Out;
75use yo_common::{Code, Error};
76use yo_kv::{Clock, Keyspace};
77
78pub const DATABASES: usize = 16;
85
86const ALL_DATABASES: u64 = if DATABASES == 64 {
93 u64::MAX
94} else {
95 (1u64 << DATABASES) - 1
96};
97const _: () = assert!(DATABASES <= 64);
98
99const EVICT_BUDGET: usize = 64;
111
112const OOM: &[u8] = b"command not allowed when used memory > 'maxmemory'.";
117
118#[derive(Debug, Clone, Copy, PartialEq, Eq)]
120pub enum Flow {
121 Continue,
123 Close,
125 Block,
132}
133
134#[derive(Debug, Clone, Copy, Default)]
140pub struct Stats {
141 pub clients: u64,
143 pub connections: u64,
145 pub commands: u64,
147}
148
149pub struct Server {
156 dbs: Vec<Keyspace>,
157 clock: Clock,
158 started_ms: u64,
159 next_db: usize,
162 dirty: u64,
172 conn_bytes: usize,
174 maxmemory: u64,
179 used: usize,
191 evict_db: usize,
197 expire_db: usize,
204 expire_ms: u64,
207 waiters: Waiters,
209 peers: migrate::Peers,
214 pub stats: Stats,
216}
217
218impl Server {
219 #[must_use]
221 pub fn new() -> Server {
222 let clock = Clock::system();
223 Server {
224 dbs: (0..DATABASES)
225 .map(|_| Keyspace::with_clock(clock))
226 .collect(),
227 clock,
228 started_ms: clock.now_ms(),
229 next_db: 0,
230 dirty: ALL_DATABASES,
231 conn_bytes: 0,
232 maxmemory: 0,
233 used: 0,
234 evict_db: 0,
235 expire_db: 0,
236 expire_ms: 0,
237 waiters: Waiters::default(),
238 peers: migrate::Peers::default(),
239 stats: Stats::default(),
240 }
241 }
242
243 #[must_use]
245 pub fn with_clock(clock: Clock) -> Server {
246 Server {
247 dbs: (0..DATABASES)
248 .map(|_| Keyspace::with_clock(clock))
249 .collect(),
250 clock,
251 started_ms: clock.now_ms(),
252 next_db: 0,
253 dirty: ALL_DATABASES,
254 conn_bytes: 0,
255 maxmemory: 0,
256 used: 0,
257 evict_db: 0,
258 expire_db: 0,
259 expire_ms: 0,
260 waiters: Waiters::default(),
261 peers: migrate::Peers::default(),
262 stats: Stats::default(),
263 }
264 }
265
266 pub fn db(&mut self, i: usize) -> &mut Keyspace {
274 self.dirty |= 1u64 << i;
277 &mut self.dbs[i]
278 }
279
280 #[must_use]
291 pub fn db_ref(&self, i: usize) -> &Keyspace {
292 &self.dbs[i]
293 }
294
295 pub fn refresh_clock(&mut self) {
301 self.clock.refresh();
302 let now = self.clock.now_ms();
303 for db in &mut self.dbs {
304 db.clock_mut().set(now);
305 }
306 }
307
308 pub fn set_clock_ms(&mut self, ms: u64) {
316 self.clock.set(ms);
317 for db in &mut self.dbs {
318 db.clock_mut().set(ms);
319 }
320 }
321
322 #[must_use]
324 pub fn uptime_secs(&self) -> u64 {
325 self.clock.now_ms().saturating_sub(self.started_ms) / 1000
326 }
327
328 #[must_use]
336 pub fn memory_bytes(&self) -> usize {
337 self.dbs.iter().map(Keyspace::memory_bytes).sum::<usize>() + self.conn_bytes
338 }
339
340 #[must_use]
346 pub fn dataset_bytes(&self) -> usize {
347 self.dbs
348 .iter()
349 .map(|db| db.map().arena().live_bytes() as usize)
350 .sum()
351 }
352
353 #[must_use]
355 pub fn arena_bytes(&self) -> usize {
356 self.dbs
357 .iter()
358 .map(|db| db.map().arena().reserved_bytes() as usize)
359 .sum()
360 }
361
362 #[must_use]
364 pub fn index_bytes(&self) -> usize {
365 self.dbs
366 .iter()
367 .map(|db| db.map().index().memory_bytes())
368 .sum()
369 }
370
371 #[must_use]
373 pub fn segment_count(&self) -> usize {
374 self.dbs
375 .iter()
376 .map(|db| db.map().arena().resident_segments())
377 .sum()
378 }
379
380 #[must_use]
382 pub const fn conn_bytes(&self) -> usize {
383 self.conn_bytes
384 }
385
386 pub fn note_conn_bytes(&mut self, delta: isize) {
394 self.conn_bytes = self.conn_bytes.saturating_add_signed(delta);
395 }
396
397 #[must_use]
399 pub fn expired_keys(&self) -> u64 {
400 self.dbs.iter().map(Keyspace::expired_keys).sum()
401 }
402
403 #[must_use]
405 pub fn evicted_keys(&self) -> u64 {
406 self.dbs.iter().map(Keyspace::evicted_keys).sum()
407 }
408
409 #[must_use]
411 pub const fn maxmemory(&self) -> u64 {
412 self.maxmemory
413 }
414
415 pub fn set_maxmemory(&mut self, bytes: u64) {
429 self.maxmemory = bytes;
430 for db in &mut self.dbs {
431 db.track_memory(bytes != 0);
432 }
433 self.used = self.settled_memory();
434 }
435
436 pub fn refresh_memory(&mut self) {
441 if self.maxmemory != 0 {
442 self.used = self.settled_memory();
443 }
444 }
445
446 fn settled_memory(&mut self) -> usize {
453 self.dbs
454 .iter_mut()
455 .map(Keyspace::settled_memory_bytes)
456 .sum::<usize>()
457 + self.conn_bytes
458 }
459
460 pub fn make_room(&mut self) -> bool {
494 if self.maxmemory == 0 || self.used as u64 <= self.maxmemory {
495 return true;
496 }
497 self.used = self.settled_memory();
502 let mut budget = EVICT_BUDGET;
503 while self.used as u64 > self.maxmemory {
504 if !self.evict_step() {
505 return false;
506 }
507 self.compact_hard_step();
508 self.used = self.settled_memory();
509 budget -= 1;
510 if budget == 0 {
511 break;
512 }
513 }
514 true
515 }
516
517 fn evict_step(&mut self) -> bool {
524 for turn in 0..self.dbs.len() {
525 let i = (self.evict_db + turn) % self.dbs.len();
526 if self.dbs[i].evict_one() {
527 self.evict_db = (i + 1) % self.dbs.len();
528 self.dirty |= 1u64 << i;
529 return true;
530 }
531 }
532 false
533 }
534
535 pub fn expire_slice(&mut self, budget: usize) -> usize {
550 let now = self.clock.now_ms();
551 if now == self.expire_ms {
552 return 0;
553 }
554 self.expire_ms = now;
555 self.expire_step(budget)
556 }
557
558 pub fn expire_step(&mut self, budget: usize) -> usize {
574 let mut spent = 0;
575 for turn in 0..self.dbs.len() {
576 if spent >= budget {
577 break;
578 }
579 let i = (self.expire_db + turn) % self.dbs.len();
580 let c = self.dbs[i].expire_cycle(budget - spent);
581 spent += c.examined;
582 if c.expired > 0 {
583 self.expire_db = (i + 1) % self.dbs.len();
584 self.dirty |= 1u64 << i;
585 }
586 }
587 spent
588 }
589
590 fn compact_hard_step(&mut self) -> Option<usize> {
596 for turn in 0..self.dbs.len() {
597 let i = (self.next_db + turn) % self.dbs.len();
598 if let Some(moved) = self.dbs[i].compact_hard() {
599 self.next_db = (i + 1) % self.dbs.len();
600 return Some(moved);
601 }
602 }
603 None
604 }
605
606 pub fn compact_step(&mut self) -> Option<usize> {
619 for turn in 0..self.dbs.len() {
620 let i = (self.next_db + turn) % self.dbs.len();
621 if self.dirty & (1 << i) == 0 {
625 continue;
626 }
627 if let Some(moved) = self.dbs[i].compact_step() {
628 self.next_db = (i + 1) % self.dbs.len();
629 return Some(moved);
630 }
631 self.dirty &= !(1u64 << i);
632 }
633 None
634 }
635}
636
637impl Default for Server {
638 fn default() -> Server {
639 Server::new()
640 }
641}
642
643pub struct Session {
645 db: usize,
646 id: u64,
647 name: Vec<u8>,
648}
649
650impl Session {
651 #[must_use]
653 pub fn new(id: u64) -> Session {
654 Session {
655 db: 0,
656 id,
657 name: Vec::new(),
658 }
659 }
660
661 #[must_use]
663 pub const fn id(&self) -> u64 {
664 self.id
665 }
666
667 #[must_use]
669 pub const fn db(&self) -> usize {
670 self.db
671 }
672
673 #[must_use]
675 pub fn name(&self) -> &[u8] {
676 &self.name
677 }
678
679 pub fn reset(&mut self) {
684 self.db = 0;
685 self.name.clear();
686 }
687
688 fn set_name(&mut self, name: &[u8]) {
690 yo_alloc::allow(|| {
691 self.name.clear();
692 self.name.extend_from_slice(name);
693 });
694 }
695}
696
697pub fn execute(server: &mut Server, session: &mut Session, args: Args<'_>, out: &mut Out) -> Flow {
702 if args.is_empty() {
705 return Flow::Continue;
706 }
707 server.stats.commands += 1;
708
709 let Some(spec) = lookup(args.name()) else {
710 write_error(out, &args::unknown_command(args));
711 return Flow::Continue;
712 };
713 if !arity_ok(spec, args.len()) {
714 write_error(out, &args::wrong_arity(spec.name));
715 return Flow::Continue;
716 }
717
718 if server.maxmemory != 0 && !server.make_room() && spec.flags.contains(&"denyoom") {
728 out.error_line(b"OOM ", OOM);
729 return Flow::Continue;
730 }
731
732 server.dirty |= match spec.group {
739 "string" | "set" | "hash" | "list" | "zset" | "array" => 1u64 << session.db,
740 _ => ALL_DATABASES,
741 };
742
743 let mark = out.len();
744 let done = if spec.flags.contains(&"blocking") {
751 blocking::execute(server, session, spec, args, out)
752 } else {
753 match spec.group {
754 "string" => {
755 let db = session.db;
756 strings::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
757 }
758 "set" => {
759 let db = session.db;
760 sets::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
761 }
762 "hash" => {
763 let db = session.db;
764 hashes::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
765 }
766 "list" => {
767 let db = session.db;
768 lists::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
769 }
770 "zset" => {
771 let db = session.db;
772 zsets::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
773 }
774 "array" => {
775 let db = session.db;
776 arrays::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
777 }
778 "keyspace" if spec.name == "migrate" => {
782 migrate::execute(server, session.db, args, out).map(|()| Flow::Continue)
783 }
784 "keyspace" => keyspace::execute(&mut server.dbs, session.db, spec, args, out)
787 .map(|()| Flow::Continue),
788 "scripting" => scripting::execute(spec, args, out).map(|()| Flow::Continue),
789 _ => server::execute(server, session, spec, args, out),
790 }
791 };
792 match done {
793 Ok(flow) => flow,
794 Err(e) => {
795 out.truncate(mark);
796 write_error(out, &e);
797 Flow::Continue
798 }
799 }
800}
801
802fn write_error(out: &mut Out, e: &Error) {
812 let prefix: &[u8] = match e.code() {
813 Code::WrongType => b"WRONGTYPE ",
814 _ => b"ERR ",
815 };
816 out.error_line(prefix, e.message().as_bytes());
817}
818
819#[cfg(test)]
820mod tests {
821 use super::*;
822 use crate::proto::{Limits, Proto};
823 use crate::request::Argv;
824
825 pub(crate) fn encode(parts: &[&[u8]]) -> Vec<u8> {
830 let mut wire = format!("*{}\r\n", parts.len()).into_bytes();
831 for p in parts {
832 wire.extend_from_slice(format!("${}\r\n", p.len()).as_bytes());
833 wire.extend_from_slice(p);
834 wire.extend_from_slice(b"\r\n");
835 }
836 wire
837 }
838
839 struct Fixture {
841 server: Server,
842 session: Session,
843 argv: Argv,
844 out: Out,
845 }
846
847 impl Fixture {
848 fn new() -> Fixture {
849 Fixture {
850 server: Server::new(),
851 session: Session::new(7),
852 argv: Argv::new(),
853 out: Out::new(Proto::Resp2),
854 }
855 }
856
857 fn run(&mut self, parts: &[&[u8]]) -> String {
859 self.flow(parts).1
860 }
861
862 fn raw(&mut self, parts: &[&[u8]]) -> Vec<u8> {
868 let wire = encode(parts);
869 self.argv.decode(&wire, &Limits::default()).unwrap();
870 self.out.clear();
871 execute(
872 &mut self.server,
873 &mut self.session,
874 Args::new(&self.argv, &wire),
875 &mut self.out,
876 );
877 self.out.as_slice().to_vec()
878 }
879
880 fn advance(&mut self, ms: u64) {
882 for db in 0..DATABASES {
883 self.server.db(db).clock_mut().advance(ms);
884 }
885 }
886
887 fn flow(&mut self, parts: &[&[u8]]) -> (Flow, String) {
889 let wire = encode(parts);
890 self.argv.decode(&wire, &Limits::default()).unwrap();
891 self.out.clear();
892 let flow = execute(
893 &mut self.server,
894 &mut self.session,
895 Args::new(&self.argv, &wire),
896 &mut self.out,
897 );
898 (
899 flow,
900 String::from_utf8_lossy(self.out.as_slice()).into_owned(),
901 )
902 }
903 }
904
905 #[test]
909 fn rewriting_the_same_keys_does_not_grow_the_server() {
910 let mut f = Fixture::new();
911 let val = vec![b'v'; 1024];
912 let keys: Vec<Vec<u8>> = (0..64).map(|i| format!("key:{i}").into_bytes()).collect();
913
914 for k in &keys {
915 f.run(&[b"SET", k, &val]);
916 }
917 f.server.compact_step();
918 let after_first = f.server.memory_bytes();
919
920 for _ in 0..500 {
925 for k in &keys {
926 f.run(&[b"SET", k, &val]);
927 }
928 f.server.compact_step();
929 }
930
931 assert!(
932 f.server.memory_bytes() <= after_first * 2,
933 "held {} after five hundred passes against {after_first} after one",
934 f.server.memory_bytes()
935 );
936 assert_eq!(f.run(&[b"DBSIZE"]), format!(":{}\r\n", keys.len()));
937 assert_eq!(f.run(&[b"STRLEN", b"key:7"]), ":1024\r\n");
938 }
939
940 #[test]
954 fn a_database_nobody_started_on_is_still_collected() {
955 let mut f = Fixture::new();
956 assert_eq!(f.run(&[b"SELECT", b"9"]), "+OK\r\n");
957 let val = vec![b'v'; 1024];
958 let keys: Vec<Vec<u8>> = (0..64).map(|i| format!("key:{i}").into_bytes()).collect();
959
960 for k in &keys {
961 f.run(&[b"SET", k, &val]);
962 }
963 while f.server.compact_step().is_some() {}
964 assert_eq!(
965 f.server.dirty & (1 << 9),
966 0,
967 "database nine was drained and should not be asked again until it is written to"
968 );
969 let after_first = f.server.memory_bytes();
970
971 for _ in 0..500 {
972 for k in &keys {
973 f.run(&[b"SET", k, &val]);
974 }
975 f.server.compact_step();
976 }
977
978 assert!(
979 f.server.memory_bytes() <= after_first * 2,
980 "held {} after five hundred passes against {after_first} after one",
981 f.server.memory_bytes()
982 );
983 assert_eq!(f.run(&[b"DBSIZE"]), format!(":{}\r\n", keys.len()));
984 assert_eq!(f.run(&[b"STRLEN", b"key:7"]), ":1024\r\n");
985 f.run(&[b"SELECT", b"0"]);
987 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
988 }
989
990 #[test]
991 fn a_command_goes_from_bytes_to_bytes() {
992 let mut f = Fixture::new();
993 assert_eq!(f.run(&[b"SET", b"k", b"v"]), "+OK\r\n");
994 assert_eq!(f.run(&[b"GET", b"k"]), "$1\r\nv\r\n");
995 assert_eq!(f.run(&[b"GET", b"nosuch"]), "$-1\r\n");
996 assert_eq!(f.run(&[b"STRLEN", b"k"]), ":1\r\n");
997 assert_eq!(f.run(&[b"set", b"k", b"v2", b"xx"]), "+OK\r\n");
999 assert_eq!(f.run(&[b"GET", b"k"]), "$2\r\nv2\r\n");
1000 }
1001
1002 #[test]
1003 fn deleting_counts_keys_removed_and_existing_counts_arguments_matched() {
1004 let mut f = Fixture::new();
1005 f.run(&[b"MSET", b"a", b"1", b"b", b"2", b"c", b"3"]);
1006 assert_eq!(f.run(&[b"EXISTS", b"a", b"a", b"nosuch"]), ":2\r\n");
1009 assert_eq!(f.run(&[b"DEL", b"a", b"a", b"nosuch"]), ":1\r\n");
1010 assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
1011 assert_eq!(f.run(&[b"UNLINK", b"b", b"c"]), ":2\r\n");
1013 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
1014 }
1015
1016 #[test]
1017 fn type_is_a_simple_string_and_says_none_for_a_key_that_is_not_there() {
1018 let mut f = Fixture::new();
1019 f.run(&[b"SET", b"k", b"v"]);
1020 assert_eq!(f.run(&[b"TYPE", b"k"]), "+string\r\n");
1023 assert_eq!(f.run(&[b"TYPE", b"nosuch"]), "+none\r\n");
1024 }
1025
1026 #[test]
1027 fn touch_counts_the_way_exists_counts() {
1028 let mut f = Fixture::new();
1029 f.run(&[b"MSET", b"a", b"1", b"b", b"2"]);
1030 assert_eq!(f.run(&[b"TOUCH", b"a", b"b"]), ":2\r\n");
1031 assert_eq!(
1032 f.run(&[b"TOUCH", b"a", b"a"]),
1033 ":2\r\n",
1034 "twice counts twice"
1035 );
1036 assert_eq!(f.run(&[b"TOUCH", b"a", b"nosuch"]), ":1\r\n");
1037 assert_eq!(f.run(&[b"TOUCH", b"nosuch"]), ":0\r\n");
1038 }
1039
1040 #[test]
1041 fn a_rename_moves_the_deadline_with_the_value_and_drops_the_one_it_lands_on() {
1042 let mut f = Fixture::new();
1043 f.run(&[b"SET", b"a", b"v1", b"EX", b"100"]);
1044 f.run(&[b"SET", b"b", b"v2", b"EX", b"500"]);
1045
1046 assert_eq!(f.run(&[b"RENAME", b"a", b"b"]), "+OK\r\n");
1047 assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv1\r\n");
1048 assert_eq!(
1049 f.run(&[b"TTL", b"b"]),
1050 ":100\r\n",
1051 "the source's and not b's"
1052 );
1053 assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
1054 }
1055
1056 #[test]
1057 fn a_rename_with_no_source_is_an_error_and_not_a_zero() {
1058 let mut f = Fixture::new();
1059 assert_eq!(f.run(&[b"RENAME", b"a", b"b"]), "-ERR no such key\r\n");
1060 assert_eq!(f.run(&[b"RENAMENX", b"a", b"a"]), "-ERR no such key\r\n");
1063 }
1064
1065 #[test]
1066 fn renamenx_refuses_a_taken_name_including_the_one_it_already_has() {
1067 let mut f = Fixture::new();
1068 f.run(&[b"MSET", b"a", b"v1", b"b", b"v2"]);
1069
1070 assert_eq!(f.run(&[b"RENAMENX", b"a", b"b"]), ":0\r\n");
1071 assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv2\r\n");
1072 assert_eq!(f.run(&[b"RENAMENX", b"a", b"a"]), ":0\r\n");
1075 assert_eq!(f.run(&[b"RENAME", b"a", b"a"]), "+OK\r\n");
1076 assert_eq!(f.run(&[b"RENAMENX", b"a", b"c"]), ":1\r\n");
1077 assert_eq!(f.run(&[b"GET", b"c"]), "$2\r\nv1\r\n");
1078 }
1079
1080 #[test]
1081 fn renaming_a_set_does_not_touch_a_member() {
1082 let mut f = Fixture::new();
1083 for i in 0..300 {
1084 f.run(&[b"SADD", b"s", format!("m{i}").as_bytes()]);
1085 }
1086 let before = f.server.memory_bytes();
1087
1088 assert_eq!(f.run(&[b"RENAME", b"s", b"t"]), "+OK\r\n");
1089 assert_eq!(f.run(&[b"SCARD", b"t"]), ":300\r\n");
1090 assert_eq!(f.run(&[b"TYPE", b"t"]), "+set\r\n");
1091 assert!(
1092 f.server.memory_bytes().abs_diff(before) < 256,
1093 "the members were copied: {} against {before}",
1094 f.server.memory_bytes()
1095 );
1096 }
1097
1098 #[test]
1099 fn a_copy_is_a_second_value_and_not_a_second_name() {
1100 let mut f = Fixture::new();
1101 f.run(&[b"SADD", b"s", b"m1", b"m2"]);
1102
1103 assert_eq!(f.run(&[b"COPY", b"s", b"t"]), ":1\r\n");
1104 f.run(&[b"SADD", b"t", b"m3"]);
1105 assert_eq!(f.run(&[b"SCARD", b"s"]), ":2\r\n", "the original is intact");
1106 assert_eq!(f.run(&[b"SCARD", b"t"]), ":3\r\n");
1107 }
1108
1109 #[test]
1119 fn every_type_can_be_copied() {
1120 let mut f = Fixture::new();
1121 f.run(&[b"SET", b"str", b"v1"]);
1122 f.run(&[b"SADD", b"set", b"m1"]);
1123 f.run(&[b"HSET", b"hash", b"f", b"v"]);
1124 f.run(&[b"RPUSH", b"list", b"a", b"b"]);
1125 f.run(&[b"ZADD", b"zset", b"1", b"m1"]);
1126
1127 for name in [
1128 &b"str"[..],
1129 &b"set"[..],
1130 &b"hash"[..],
1131 &b"list"[..],
1132 &b"zset"[..],
1133 ] {
1134 let dst = [name, b":copy"].concat();
1135 assert_eq!(
1136 f.run(&[b"COPY", name, &dst]),
1137 ":1\r\n",
1138 "copying {}",
1139 String::from_utf8_lossy(name)
1140 );
1141 assert_eq!(f.run(&[b"TYPE", name]), f.run(&[b"TYPE", &dst]));
1142 }
1143
1144 assert_eq!(f.run(&[b"LRANGE", b"list:copy", b"0", b"-1"]), {
1145 let mut want = String::from("*2\r\n");
1146 want.push_str("$1\r\na\r\n$1\r\nb\r\n");
1147 want
1148 });
1149 assert_eq!(f.run(&[b"ZSCORE", b"zset:copy", b"m1"]), "$1\r\n1\r\n");
1150
1151 f.run(&[b"RPUSH", b"list:copy", b"c"]);
1153 assert_eq!(f.run(&[b"LLEN", b"list"]), ":2\r\n");
1154 assert_eq!(f.run(&[b"LLEN", b"list:copy"]), ":3\r\n");
1155 }
1156
1157 #[test]
1158 fn a_copy_refuses_a_taken_destination_until_it_is_told_it_can_have_it() {
1159 let mut f = Fixture::new();
1160 f.run(&[b"SET", b"a", b"v1", b"EX", b"100"]);
1161 f.run(&[b"SET", b"b", b"v2"]);
1162
1163 assert_eq!(f.run(&[b"COPY", b"a", b"b"]), ":0\r\n");
1164 assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv2\r\n");
1165 assert_eq!(f.run(&[b"COPY", b"a", b"b", b"REPLACE"]), ":1\r\n");
1166 assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv1\r\n");
1167 assert_eq!(f.run(&[b"TTL", b"b"]), ":100\r\n", "the deadline came too");
1168 assert_eq!(f.run(&[b"COPY", b"nosuch", b"z"]), ":0\r\n");
1169 }
1170
1171 #[test]
1172 fn a_copy_into_another_database_is_a_copy_and_onto_itself_there_is_too() {
1173 let mut f = Fixture::new();
1174 f.run(&[b"SET", b"a", b"v1"]);
1175
1176 assert_eq!(f.run(&[b"COPY", b"a", b"a", b"DB", b"1"]), ":1\r\n");
1179 f.run(&[b"SELECT", b"1"]);
1180 assert_eq!(f.run(&[b"GET", b"a"]), "$2\r\nv1\r\n");
1181 assert_eq!(
1182 f.run(&[b"COPY", b"a", b"a", b"DB", b"0"]),
1183 ":0\r\n",
1184 "taken"
1185 );
1186 assert_eq!(
1187 f.run(&[b"COPY", b"a", b"a", b"DB", b"0", b"REPLACE"]),
1188 ":1\r\n"
1189 );
1190 }
1191
1192 #[test]
1193 fn sort_takes_its_options_in_any_order_and_the_last_one_wins() {
1194 let mut f = Fixture::new();
1195 f.run(&[b"RPUSH", b"l", b"3", b"1", b"2"]);
1196 assert_eq!(
1197 f.run(&[b"SORT", b"l"]),
1198 "*3\r\n$1\r\n1\r\n$1\r\n2\r\n$1\r\n3\r\n"
1199 );
1200 assert_eq!(
1202 f.run(&[b"SORT", b"l", b"DESC", b"asc"]),
1203 "*3\r\n$1\r\n1\r\n$1\r\n2\r\n$1\r\n3\r\n"
1204 );
1205 assert_eq!(
1206 f.run(&[b"sort", b"l", b"LIMIT", b"1", b"1", b"DESC"]),
1207 "*1\r\n$1\r\n2\r\n"
1208 );
1209 }
1210
1211 #[test]
1212 fn sort_reads_a_key_per_element_for_by_and_for_get() {
1213 let mut f = Fixture::new();
1214 f.run(&[b"RPUSH", b"l", b"a", b"b"]);
1215 f.run(&[b"MSET", b"w_a", b"2", b"w_b", b"1", b"d_b", b"bee"]);
1216 assert_eq!(
1219 f.run(&[b"SORT", b"l", b"BY", b"w_*", b"GET", b"#", b"GET", b"d_*"]),
1220 "*4\r\n$1\r\nb\r\n$3\r\nbee\r\n$1\r\na\r\n$-1\r\n"
1221 );
1222 }
1223
1224 #[test]
1225 fn sort_store_writes_a_list_and_answers_its_length() {
1226 let mut f = Fixture::new();
1227 f.run(&[b"RPUSH", b"l", b"3", b"1", b"2"]);
1228 assert_eq!(f.run(&[b"SORT", b"l", b"STORE", b"out"]), ":3\r\n");
1229 assert_eq!(f.run(&[b"TYPE", b"out"]), "+list\r\n");
1230 assert_eq!(
1231 f.run(&[b"LRANGE", b"out", b"0", b"-1"]),
1232 "*3\r\n$1\r\n1\r\n$1\r\n2\r\n$1\r\n3\r\n"
1233 );
1234 assert_eq!(f.run(&[b"SORT", b"missing", b"STORE", b"out"]), ":0\r\n");
1237 assert_eq!(f.run(&[b"EXISTS", b"out"]), ":0\r\n");
1238 }
1239
1240 #[test]
1241 fn sort_ro_does_not_know_the_word_store() {
1242 let mut f = Fixture::new();
1243 f.run(&[b"RPUSH", b"l", b"2", b"1"]);
1244 assert_eq!(f.run(&[b"SORT_RO", b"l"]), "*2\r\n$1\r\n1\r\n$1\r\n2\r\n");
1245 assert_eq!(
1246 f.run(&[b"SORT_RO", b"l", b"STORE", b"d"]),
1247 "-ERR syntax error\r\n"
1248 );
1249 assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
1250 }
1251
1252 #[test]
1253 fn sort_refuses_what_it_cannot_sort() {
1254 let mut f = Fixture::new();
1255 assert_eq!(f.run(&[b"SORT", b"nosuchkey"]), "*0\r\n");
1256 f.run(&[b"SET", b"s", b"x"]);
1257 assert_eq!(
1258 f.run(&[b"SORT", b"s"]),
1259 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
1260 );
1261 f.run(&[b"RPUSH", b"words", b"one", b"two"]);
1262 assert_eq!(
1263 f.run(&[b"SORT", b"words"]),
1264 "-ERR One or more scores can't be converted into double\r\n"
1265 );
1266 assert_eq!(
1267 f.run(&[b"SORT", b"words", b"ALPHA"]),
1268 "*2\r\n$3\r\none\r\n$3\r\ntwo\r\n"
1269 );
1270 assert_eq!(f.run(&[b"SORT", b"words", b"BY"]), "-ERR syntax error\r\n");
1271 }
1272
1273 #[test]
1274 fn move_takes_the_key_out_of_one_database_and_puts_it_in_another() {
1275 let mut f = Fixture::new();
1276 assert_eq!(f.run(&[b"RPUSH", b"l", b"a", b"b"]), ":2\r\n");
1277 assert_eq!(f.run(&[b"MOVE", b"l", b"1"]), ":1\r\n");
1278 assert_eq!(f.run(&[b"EXISTS", b"l"]), ":0\r\n");
1279 assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
1280 assert_eq!(
1281 f.run(&[b"LRANGE", b"l", b"0", b"-1"]),
1282 "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
1283 );
1284 assert_eq!(f.run(&[b"MOVE", b"l", b"0"]), ":1\r\n");
1287 assert_eq!(f.run(&[b"EXISTS", b"l"]), ":0\r\n");
1288 }
1289
1290 #[test]
1291 fn move_answers_zero_when_either_end_says_no() {
1292 let mut f = Fixture::new();
1293 assert_eq!(f.run(&[b"MOVE", b"nope", b"1"]), ":0\r\n");
1294 assert_eq!(f.run(&[b"SET", b"a", b"here"]), "+OK\r\n");
1295 assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
1296 assert_eq!(f.run(&[b"SET", b"a", b"there"]), "+OK\r\n");
1297 assert_eq!(f.run(&[b"SELECT", b"0"]), "+OK\r\n");
1298 assert_eq!(f.run(&[b"MOVE", b"a", b"1"]), ":0\r\n");
1301 assert_eq!(f.run(&[b"GET", b"a"]), "$4\r\nhere\r\n");
1302 assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
1303 assert_eq!(f.run(&[b"GET", b"a"]), "$5\r\nthere\r\n");
1304 }
1305
1306 #[test]
1307 fn move_refuses_a_database_that_is_not_one_and_the_one_it_is_on() {
1308 let mut f = Fixture::new();
1309 assert_eq!(
1310 f.run(&[b"MOVE", b"a", b"0"]),
1311 "-ERR source and destination objects are the same\r\n"
1312 );
1313 assert_eq!(
1314 f.run(&[b"MOVE", b"a", b"99"]),
1315 "-ERR DB index is out of range\r\n"
1316 );
1317 assert_eq!(
1318 f.run(&[b"MOVE", b"a", b"-1"]),
1319 "-ERR DB index is out of range\r\n"
1320 );
1321 assert_eq!(
1322 f.run(&[b"MOVE", b"a", b"x"]),
1323 "-ERR value is not an integer or out of range\r\n"
1324 );
1325 }
1326
1327 #[test]
1328 fn swapdb_swaps_what_two_connections_would_see() {
1329 let mut f = Fixture::new();
1330 assert_eq!(f.run(&[b"SET", b"k", b"zero"]), "+OK\r\n");
1331 assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
1332 assert_eq!(f.run(&[b"SET", b"k", b"one"]), "+OK\r\n");
1333 assert_eq!(f.run(&[b"SELECT", b"0"]), "+OK\r\n");
1334
1335 assert_eq!(f.run(&[b"SWAPDB", b"0", b"1"]), "+OK\r\n");
1336 assert_eq!(f.run(&[b"GET", b"k"]), "$3\r\none\r\n");
1338 assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
1339 assert_eq!(f.run(&[b"GET", b"k"]), "$4\r\nzero\r\n");
1340 assert_eq!(f.run(&[b"SWAPDB", b"1", b"1"]), "+OK\r\n");
1342 assert_eq!(f.run(&[b"GET", b"k"]), "$4\r\nzero\r\n");
1343 }
1344
1345 #[test]
1346 fn swapdb_says_which_index_it_could_not_read() {
1347 let mut f = Fixture::new();
1348 assert_eq!(
1349 f.run(&[b"SWAPDB", b"x", b"1"]),
1350 "-ERR invalid first DB index\r\n"
1351 );
1352 assert_eq!(
1353 f.run(&[b"SWAPDB", b"0", b"y"]),
1354 "-ERR invalid second DB index\r\n"
1355 );
1356 assert_eq!(
1360 f.run(&[b"SWAPDB", b"99999999999999", b"1"]),
1361 "-ERR invalid first DB index\r\n"
1362 );
1363 assert_eq!(
1364 f.run(&[b"SWAPDB", b"0", b"99"]),
1365 "-ERR DB index is out of range\r\n"
1366 );
1367 assert_eq!(
1368 f.run(&[b"SWAPDB", b"-1", b"0"]),
1369 "-ERR DB index is out of range\r\n"
1370 );
1371 }
1372
1373 #[test]
1374 fn wait_answers_zero_replicas_without_waiting() {
1375 let mut f = Fixture::new();
1376 assert_eq!(f.run(&[b"SET", b"a", b"v"]), "+OK\r\n");
1377 assert_eq!(f.run(&[b"WAIT", b"0", b"0"]), ":0\r\n");
1378 assert_eq!(f.run(&[b"WAIT", b"3", b"1000"]), ":0\r\n");
1381 assert_eq!(f.run(&[b"WAIT", b"-1", b"0"]), ":0\r\n");
1384 assert_eq!(
1385 f.run(&[b"WAIT", b"x", b"0"]),
1386 "-ERR value is not an integer or out of range\r\n"
1387 );
1388 assert_eq!(
1389 f.run(&[b"WAIT", b"0", b"-1"]),
1390 "-ERR timeout is negative\r\n"
1391 );
1392 assert_eq!(
1393 f.run(&[b"WAIT", b"0", b"1.5"]),
1394 "-ERR timeout is not an integer or out of range\r\n"
1395 );
1396 }
1397
1398 #[test]
1399 fn waitaof_answers_two_zeroes_and_refuses_a_local_wait() {
1400 let mut f = Fixture::new();
1401 assert_eq!(f.run(&[b"WAITAOF", b"0", b"0", b"0"]), "*2\r\n:0\r\n:0\r\n");
1402 assert_eq!(
1403 f.run(&[b"WAITAOF", b"1", b"0", b"0"]),
1404 "-ERR WAITAOF cannot be used when numlocal is set but appendonly is disabled.\r\n"
1405 );
1406 assert_eq!(
1407 f.run(&[b"WAITAOF", b"2", b"0", b"0"]),
1408 "-ERR value is out of range, value must between 0 and 1\r\n"
1409 );
1410 assert_eq!(
1411 f.run(&[b"WAITAOF", b"0", b"-1", b"0"]),
1412 "-ERR value is out of range, must be positive\r\n"
1413 );
1414 assert_eq!(
1417 f.run(&[b"WAITAOF", b"1", b"0", b"-5"]),
1418 "-ERR timeout is negative\r\n"
1419 );
1420 }
1421
1422 fn payload(reply: &[u8]) -> Vec<u8> {
1426 let head = reply.windows(2).position(|w| w == b"\r\n").unwrap();
1427 reply[head + 2..reply.len() - 2].to_vec()
1428 }
1429
1430 #[test]
1431 fn a_value_survives_a_dump_and_a_restore() {
1432 let mut f = Fixture::new();
1433 f.run(&[b"SET", b"s", b"hello"]);
1434 f.run(&[b"RPUSH", b"l", b"a", b"b", b"c"]);
1435 f.run(&[b"SADD", b"t", b"1", b"2", b"3"]);
1436 f.run(&[b"SADD", b"u", b"x", b"y"]);
1437 f.run(&[b"HSET", b"h", b"f", b"1", b"g", b"2"]);
1438 f.run(&[b"ZADD", b"z", b"1.5", b"a", b"2.5", b"b"]);
1439
1440 for key in [&b"s"[..], b"l", b"t", b"u", b"h", b"z"] {
1441 let mut copy = key.to_vec();
1442 copy.push(b'2');
1443 let bytes = payload(&f.raw(&[b"DUMP", key]));
1444 assert_eq!(f.run(&[b"RESTORE", ©, b"0", &bytes]), "+OK\r\n");
1445 assert_eq!(f.run(&[b"TYPE", ©]), f.run(&[b"TYPE", key]));
1446 }
1447
1448 assert_eq!(f.run(&[b"GET", b"s2"]), "$5\r\nhello\r\n");
1449 assert_eq!(
1450 f.run(&[b"LRANGE", b"l2", b"0", b"-1"]),
1451 "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
1452 );
1453 assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"t2"])), ["1", "2", "3"]);
1454 assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"u2"])), ["x", "y"]);
1455 assert_eq!(f.run(&[b"HGET", b"h2", b"g"]), "$1\r\n2\r\n");
1456 assert_eq!(f.run(&[b"ZSCORE", b"z2", b"b"]), "$3\r\n2.5\r\n");
1457 assert_eq!(
1460 f.run(&[b"OBJECT", b"ENCODING", b"t2"]),
1461 f.run(&[b"OBJECT", b"ENCODING", b"t"])
1462 );
1463 }
1464
1465 #[test]
1466 fn a_dumped_hash_keeps_its_field_deadlines() {
1467 let mut f = Fixture::new();
1468 f.run(&[b"HSET", b"h", b"keep", b"1", b"go", b"2"]);
1469 assert_eq!(
1470 f.run(&[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"go"]),
1471 "*1\r\n:1\r\n"
1472 );
1473 let bytes = payload(&f.raw(&[b"DUMP", b"h"]));
1474 assert_eq!(f.run(&[b"RESTORE", b"h2", b"0", &bytes]), "+OK\r\n");
1475 assert_eq!(
1476 f.run(&[b"HTTL", b"h2", b"FIELDS", b"2", b"keep", b"go"]),
1477 "*2\r\n:-1\r\n:100\r\n"
1478 );
1479 }
1480
1481 #[test]
1482 fn dump_leaves_the_deadline_behind_and_restore_is_given_a_new_one() {
1483 let mut f = Fixture::new();
1484 f.run(&[b"SET", b"a", b"v", b"EX", b"100"]);
1485 let bytes = payload(&f.raw(&[b"DUMP", b"a"]));
1486 assert_eq!(f.run(&[b"RESTORE", b"b", b"0", &bytes]), "+OK\r\n");
1487 assert_eq!(f.run(&[b"TTL", b"b"]), ":-1\r\n");
1488 assert_eq!(f.run(&[b"RESTORE", b"c", b"5000", &bytes]), "+OK\r\n");
1489 assert_eq!(f.run(&[b"TTL", b"c"]), ":5\r\n");
1490 assert_eq!(
1493 f.run(&[b"RESTORE", b"d", b"1", &bytes, b"ABSTTL"]),
1494 "+OK\r\n"
1495 );
1496 assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
1497 }
1498
1499 #[test]
1500 fn dump_answers_nothing_for_a_key_that_is_not_there() {
1501 let mut f = Fixture::new();
1502 assert_eq!(f.run(&[b"DUMP", b"nope"]), "$-1\r\n");
1503 f.run(&[b"SET", b"gone", b"v", b"PX", b"10"]);
1504 f.advance(50);
1505 assert_eq!(f.run(&[b"DUMP", b"gone"]), "$-1\r\n");
1506 }
1507
1508 #[test]
1509 fn restore_refuses_a_key_that_is_there_unless_it_is_told_to_replace() {
1510 let mut f = Fixture::new();
1511 f.run(&[b"SET", b"a", b"first"]);
1512 f.run(&[b"SET", b"b", b"second"]);
1513 let bytes = payload(&f.raw(&[b"DUMP", b"b"]));
1514 assert_eq!(
1515 f.run(&[b"RESTORE", b"a", b"0", &bytes]),
1516 "-BUSYKEY Target key name already exists.\r\n"
1517 );
1518 assert_eq!(f.run(&[b"GET", b"a"]), "$5\r\nfirst\r\n");
1519 assert_eq!(
1520 f.run(&[b"RESTORE", b"a", b"0", &bytes, b"REPLACE"]),
1521 "+OK\r\n"
1522 );
1523 assert_eq!(f.run(&[b"GET", b"a"]), "$6\r\nsecond\r\n");
1524 }
1525
1526 #[test]
1530 fn restore_asks_about_the_key_before_it_looks_at_the_bytes() {
1531 let mut f = Fixture::new();
1532 f.run(&[b"SET", b"a", b"v"]);
1533 assert_eq!(
1534 f.run(&[b"RESTORE", b"a", b"0", b"rubbish"]),
1535 "-BUSYKEY Target key name already exists.\r\n"
1536 );
1537 assert_eq!(
1540 f.run(&[b"RESTORE", b"a", b"0", b"rubbish", b"FREQ", b"300"]),
1541 "-ERR Invalid FREQ value, must be >= 0 and <= 255\r\n"
1542 );
1543 }
1544
1545 #[test]
1546 fn restore_can_tell_a_bad_footer_from_bad_bytes() {
1547 let mut f = Fixture::new();
1548 f.run(&[b"SET", b"a", b"hello"]);
1549 let good = payload(&f.raw(&[b"DUMP", b"a"]));
1550
1551 let mut flipped = good.clone();
1552 flipped[2] ^= 0x40;
1553 assert_eq!(
1554 f.run(&[b"RESTORE", b"b", b"0", &flipped]),
1555 "-ERR DUMP payload version or checksum are wrong\r\n"
1556 );
1557 assert_eq!(
1558 f.run(&[b"RESTORE", b"b", b"0", b"short"]),
1559 "-ERR DUMP payload version or checksum are wrong\r\n"
1560 );
1561 let mut truncated = good[..1].to_vec();
1565 truncated.extend_from_slice(&good[good.len() - 10..good.len() - 8]);
1566 let crc = yo_common::crc::crc64(0, &truncated);
1567 truncated.extend_from_slice(&crc.to_le_bytes());
1568 assert_eq!(
1569 f.run(&[b"RESTORE", b"b", b"0", &truncated]),
1570 "-ERR Bad data format\r\n"
1571 );
1572 assert_eq!(f.run(&[b"EXISTS", b"b"]), ":0\r\n");
1573 }
1574
1575 #[test]
1576 fn restore_checks_the_three_numbers_a_client_can_get_wrong() {
1577 let mut f = Fixture::new();
1578 f.run(&[b"SET", b"a", b"v"]);
1579 let bytes = payload(&f.raw(&[b"DUMP", b"a"]));
1580 assert_eq!(
1581 f.run(&[b"RESTORE", b"b", b"-1", &bytes]),
1582 "-ERR Invalid TTL value, must be >= 0\r\n"
1583 );
1584 assert_eq!(
1585 f.run(&[b"RESTORE", b"b", b"0", &bytes, b"IDLETIME", b"-1"]),
1586 "-ERR Invalid IDLETIME value, must be >= 0\r\n"
1587 );
1588 assert_eq!(
1589 f.run(&[b"RESTORE", b"b", b"0", &bytes, b"FREQ", b"256"]),
1590 "-ERR Invalid FREQ value, must be >= 0 and <= 255\r\n"
1591 );
1592 assert_eq!(
1594 f.run(&[b"RESTORE", b"b", b"0", &bytes, b"IDLETIME", b"90"]),
1595 "+OK\r\n"
1596 );
1597 assert_eq!(
1598 f.run(&[b"RESTORE", b"c", b"0", &bytes, b"FREQ", b"200", b"REPLACE"]),
1599 "+OK\r\n"
1600 );
1601 }
1602
1603 #[test]
1607 fn restore_takes_idletime_or_freq_and_not_both() {
1608 let mut f = Fixture::new();
1609 f.run(&[b"SET", b"a", b"v"]);
1610 let bytes = payload(&f.raw(&[b"DUMP", b"a"]));
1611 assert_eq!(
1612 f.run(&[
1613 b"RESTORE",
1614 b"b",
1615 b"0",
1616 &bytes,
1617 b"IDLETIME",
1618 b"1",
1619 b"FREQ",
1620 b"2"
1621 ]),
1622 "-ERR syntax error\r\n"
1623 );
1624 assert_eq!(
1625 f.run(&[
1626 b"RESTORE",
1627 b"b",
1628 b"0",
1629 &bytes,
1630 b"FREQ",
1631 b"2",
1632 b"IDLETIME",
1633 b"1"
1634 ]),
1635 "-ERR syntax error\r\n"
1636 );
1637 assert_eq!(
1638 f.run(&[b"RESTORE", b"b", b"0", &bytes, b"FREQ"]),
1639 "-ERR syntax error\r\n"
1640 );
1641 assert_eq!(
1642 f.run(&[b"RESTORE", b"b", b"0", &bytes, b"NOSUCH"]),
1643 "-ERR syntax error\r\n"
1644 );
1645 }
1646
1647 #[test]
1648 fn copy_checks_its_options_before_it_looks_for_anything() {
1649 let mut f = Fixture::new();
1650 assert_eq!(
1653 f.run(&[b"COPY", b"a", b"b", b"DB", b"99"]),
1654 "-ERR DB index is out of range\r\n"
1655 );
1656 assert_eq!(
1657 f.run(&[b"COPY", b"a", b"b", b"DB", b"-1"]),
1658 "-ERR DB index is out of range\r\n"
1659 );
1660 assert_eq!(
1661 f.run(&[b"COPY", b"a", b"b", b"DB", b"x"]),
1662 "-ERR value is not an integer or out of range\r\n"
1663 );
1664 assert_eq!(
1665 f.run(&[b"COPY", b"a", b"b", b"nonsense"]),
1666 "-ERR syntax error\r\n"
1667 );
1668 assert_eq!(
1669 f.run(&[b"COPY", b"a", b"a"]),
1670 "-ERR source and destination objects are the same\r\n"
1671 );
1672 assert_eq!(
1674 f.run(&[b"COPY", b"a", b"b", b"dB", b"1", b"rEpLaCe", b"db", b"2"]),
1675 ":0\r\n"
1676 );
1677 }
1678
1679 #[test]
1680 fn time_is_two_bulk_strings_and_moves() {
1681 let mut f = Fixture::new();
1682 let first = f.run(&[b"TIME"]);
1683 assert!(first.starts_with("*2\r\n$"), "got {first}");
1684 let parts: Vec<&str> = first.split("\r\n").collect();
1685 let secs: i64 = parts[2].parse().expect("seconds as decimal text");
1686 let micros: i64 = parts[4].parse().expect("microseconds as decimal text");
1687 assert!(secs > 1_700_000_000, "a real wall clock, got {secs}");
1688 assert!((0..1_000_000).contains(µs), "got {micros}");
1689 assert_ne!(first, f.run(&[b"TIME"]));
1693 }
1694
1695 #[test]
1696 fn a_keyspace_scan_walks_every_key_once() {
1697 let mut f = Fixture::new();
1698 for i in 0..500 {
1699 f.run(&[b"SET", format!("k{i}").as_bytes(), b"v"]);
1700 }
1701
1702 let mut seen: Vec<String> = Vec::new();
1703 let mut cursor = "0".to_owned();
1704 let mut calls = 0;
1705 loop {
1706 let (next, keys) = scan_reply(&f.run(&[b"SCAN", cursor.as_bytes(), b"COUNT", b"32"]));
1707 seen.extend(keys);
1708 cursor = next;
1709 calls += 1;
1710 assert!(calls < 10_000, "the cursor is not advancing");
1711 if cursor == "0" {
1712 break;
1713 }
1714 }
1715
1716 seen.sort();
1717 seen.dedup();
1718 assert_eq!(seen.len(), 500, "every key once and only once");
1719 assert!(calls > 1, "500 keys came back in one batch");
1722 }
1723
1724 #[test]
1725 fn a_scan_narrows_by_pattern_and_by_type() {
1726 let mut f = Fixture::new();
1727 f.run(&[b"SET", b"str", b"v"]);
1728 f.run(&[b"SADD", b"members", b"a"]);
1729 f.run(&[b"HSET", b"fields", b"f", b"v"]);
1730
1731 let all = |f: &mut Fixture, args: &[&[u8]]| {
1732 let mut out: Vec<String> = Vec::new();
1733 let mut cursor = "0".to_owned();
1734 loop {
1735 let mut line: Vec<&[u8]> = vec![b"SCAN", cursor.as_bytes()];
1736 line.extend_from_slice(args);
1737 let (next, keys) = scan_reply(&f.run(&line));
1738 out.extend(keys);
1739 cursor = next;
1740 if cursor == "0" {
1741 break;
1742 }
1743 }
1744 out.sort();
1745 out
1746 };
1747
1748 assert_eq!(all(&mut f, &[]), ["fields", "members", "str"]);
1749 assert_eq!(all(&mut f, &[b"MATCH", b"*e*"]), ["fields", "members"]);
1750 assert_eq!(all(&mut f, &[b"TYPE", b"set"]), ["members"]);
1751 assert_eq!(all(&mut f, &[b"TYPE", b"HASH"]), ["fields"]);
1753 assert!(all(&mut f, &[b"TYPE", b"list"]).is_empty());
1755 assert!(all(&mut f, &[b"TYPE", b"banana"]).is_empty());
1756 assert!(all(&mut f, &[b"MATCH", b"str*", b"TYPE", b"set"]).is_empty());
1758 }
1759
1760 #[test]
1761 fn a_scan_says_what_is_wrong_with_it() {
1762 let mut f = Fixture::new();
1763 assert_eq!(f.run(&[b"SCAN", b"nope"]), "-ERR invalid cursor\r\n");
1764 assert_eq!(f.run(&[b"SCAN", b"-1"]), "-ERR invalid cursor\r\n");
1765 assert_eq!(f.run(&[b"SCAN", b"0", b"MATCH"]), "-ERR syntax error\r\n");
1766 assert_eq!(
1767 f.run(&[b"SCAN", b"0", b"COUNT", b"0"]),
1768 "-ERR syntax error\r\n"
1769 );
1770 assert_eq!(
1771 f.run(&[b"SCAN", b"0", b"COUNT", b"x"]),
1772 "-ERR value is not an integer or out of range\r\n"
1773 );
1774 assert_eq!(
1775 f.run(&[b"SCAN", b"0", b"WAT", b"1"]),
1776 "-ERR syntax error\r\n"
1777 );
1778 assert!(f.run(&[b"SCAN", b"18446744073709551615"]).starts_with("*2"));
1783 }
1784
1785 #[test]
1786 fn keys_and_randomkey_look_at_the_whole_database() {
1787 let mut f = Fixture::new();
1788 assert_eq!(f.run(&[b"KEYS", b"*"]), "*0\r\n");
1789 assert_eq!(f.run(&[b"RANDOMKEY"]), "$-1\r\n");
1790
1791 for name in ["one", "two", "three"] {
1792 f.run(&[b"SET", name.as_bytes(), b"v"]);
1793 }
1794 assert_eq!(sorted(&f.run(&[b"KEYS", b"*"])), ["one", "three", "two"]);
1795 assert_eq!(sorted(&f.run(&[b"KEYS", b"t*"])), ["three", "two"]);
1796 assert_eq!(f.run(&[b"KEYS", b"nothing"]), "*0\r\n");
1797
1798 for _ in 0..50 {
1799 let got = f.run(&[b"RANDOMKEY"]);
1800 assert!(
1801 ["$3\r\none\r\n", "$3\r\ntwo\r\n", "$5\r\nthree\r\n"].contains(&got.as_str()),
1802 "got {got}"
1803 );
1804 }
1805 }
1806
1807 #[test]
1808 fn a_walk_does_not_answer_keys_that_have_expired() {
1809 let mut f = Fixture::new();
1810 f.run(&[b"SET", b"alive", b"v"]);
1811 f.run(&[b"SET", b"dead", b"v", b"PX", b"1"]);
1812 f.server.db(0).clock_mut().advance(2);
1813 assert_eq!(
1814 f.run(&[b"DBSIZE"]),
1815 ":2\r\n",
1816 "nothing has collected it yet"
1817 );
1818
1819 assert_eq!(f.run(&[b"KEYS", b"*"]), "*1\r\n$5\r\nalive\r\n");
1820 let (_, keys) = scan_reply(&f.run(&[b"SCAN", b"0", b"COUNT", b"1000"]));
1821 assert_eq!(keys, ["alive"]);
1822 for _ in 0..20 {
1823 assert_eq!(f.run(&[b"RANDOMKEY"]), "$5\r\nalive\r\n");
1824 }
1825 assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
1828 }
1829
1830 #[test]
1831 fn a_key_deadline_goes_on_and_comes_back_in_all_four_units() {
1832 let mut f = Fixture::new();
1833 f.run(&[b"SET", b"k", b"v"]);
1834 assert_eq!(f.run(&[b"TTL", b"k"]), ":-1\r\n", "there and no deadline");
1835 assert_eq!(f.run(&[b"TTL", b"nosuch"]), ":-2\r\n", "not there at all");
1836
1837 assert_eq!(f.run(&[b"EXPIRE", b"k", b"100"]), ":1\r\n");
1838 assert_eq!(f.run(&[b"TTL", b"k"]), ":100\r\n");
1839 let ms = int(&f.run(&[b"PTTL", b"k"]));
1840 assert!((99_000..=100_000).contains(&ms), "got {ms}");
1841
1842 let at = int(&f.run(&[b"EXPIRETIME", b"k"]));
1844 let at_ms = int(&f.run(&[b"PEXPIRETIME", b"k"]));
1845 assert_eq!(at, (at_ms + 500) / 1000);
1846 assert!(at_ms > 1_700_000_000_000, "an absolute moment, got {at_ms}");
1847
1848 assert_eq!(f.run(&[b"PERSIST", b"k"]), ":1\r\n");
1849 assert_eq!(f.run(&[b"TTL", b"k"]), ":-1\r\n");
1850 assert_eq!(
1851 f.run(&[b"PERSIST", b"k"]),
1852 ":0\r\n",
1853 "nothing to take off the second time"
1854 );
1855 assert_eq!(f.run(&[b"PERSIST", b"nosuch"]), ":0\r\n");
1856 assert_eq!(
1857 f.run(&[b"GET", b"k"]),
1858 "$1\r\nv\r\n",
1859 "and the value went through all of that untouched"
1860 );
1861 }
1862
1863 #[test]
1864 fn every_type_can_be_given_a_deadline_and_it_is_the_same_deadline() {
1865 let mut f = Fixture::new();
1866 f.run(&[b"SET", b"str", b"v"]);
1867 f.run(&[b"SADD", b"set", b"a", b"b"]);
1868 f.run(&[b"HSET", b"hash", b"f", b"v"]);
1869
1870 for key in [b"str".as_slice(), b"set", b"hash"] {
1871 assert_eq!(f.run(&[b"EXPIRE", key, b"100"]), ":1\r\n");
1872 assert_eq!(f.run(&[b"TTL", key]), ":100\r\n");
1873 }
1874 assert_eq!(f.run(&[b"SCARD", b"set"]), ":2\r\n");
1877 assert_eq!(f.run(&[b"HGET", b"hash", b"f"]), "$1\r\nv\r\n");
1878 assert_eq!(f.run(&[b"GET", b"str"]), "$1\r\nv\r\n");
1879 }
1880
1881 #[test]
1882 fn a_deadline_that_has_already_gone_deletes_the_key_now() {
1883 let mut f = Fixture::new();
1884 for key in [b"a".as_slice(), b"b", b"c", b"d"] {
1885 f.run(&[b"SET", key, b"v"]);
1886 }
1887 assert_eq!(f.run(&[b"EXPIRE", b"a", b"0"]), ":1\r\n");
1891 assert_eq!(f.run(&[b"EXPIRE", b"b", b"-1"]), ":1\r\n");
1892 assert_eq!(f.run(&[b"EXPIREAT", b"c", b"1"]), ":1\r\n");
1893 assert_eq!(f.run(&[b"PEXPIREAT", b"d", b"1"]), ":1\r\n");
1894 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
1895 assert_eq!(
1896 f.run(&[b"EXPIRE", b"a", b"100"]),
1897 ":0\r\n",
1898 "and the key really went, so there is nothing to put a deadline on"
1899 );
1900 }
1901
1902 #[test]
1903 fn the_four_conditions_decide_whether_the_deadline_moves() {
1904 let mut f = Fixture::new();
1905 f.run(&[b"SET", b"k", b"v"]);
1906
1907 assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"XX"]), ":0\r\n");
1908 assert_eq!(f.run(&[b"TTL", b"k"]), ":-1\r\n", "and XX left it alone");
1909 assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"GT"]), ":0\r\n");
1910 assert_eq!(
1911 f.run(&[b"EXPIRE", b"k", b"100", b"LT"]),
1912 ":1\r\n",
1913 "no deadline reads as infinitely far away, so LT passes where GT fails"
1914 );
1915
1916 assert_eq!(f.run(&[b"EXPIRE", b"k", b"50", b"NX"]), ":0\r\n");
1917 assert_eq!(f.run(&[b"EXPIRE", b"k", b"50", b"GT"]), ":0\r\n");
1918 assert_eq!(f.run(&[b"TTL", b"k"]), ":100\r\n");
1919 assert_eq!(f.run(&[b"EXPIRE", b"k", b"50", b"LT"]), ":1\r\n");
1920 assert_eq!(f.run(&[b"EXPIRE", b"k", b"200", b"GT"]), ":1\r\n");
1921 assert_eq!(f.run(&[b"TTL", b"k"]), ":200\r\n");
1922
1923 assert_eq!(f.run(&[b"EXPIRE", b"k", b"0", b"NX"]), ":0\r\n");
1926 assert_eq!(f.run(&[b"EXISTS", b"k"]), ":1\r\n");
1927 assert_eq!(f.run(&[b"EXPIRE", b"k", b"0", b"XX"]), ":1\r\n");
1928 assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n", "and XX let it through");
1929 }
1930
1931 #[test]
1932 fn the_conditions_are_a_set_and_not_a_keyword() {
1933 let mut f = Fixture::new();
1934 f.run(&[b"SET", b"k", b"v"]);
1935
1936 assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"nx"]), ":1\r\n");
1937 assert_eq!(
1938 f.run(&[b"EXPIRE", b"k", b"100", b"nx", b"nx"]),
1939 ":0\r\n",
1940 "the same keyword twice means it once, and NX now has a deadline to fail on"
1941 );
1942
1943 assert_eq!(f.run(&[b"EXPIRE", b"k", b"200", b"xx", b"gt"]), ":1\r\n");
1946 assert_eq!(f.run(&[b"TTL", b"k"]), ":200\r\n");
1947 assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"gt", b"xx"]), ":0\r\n");
1948 assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"XX", b"LT"]), ":1\r\n");
1949 assert_eq!(f.run(&[b"TTL", b"k"]), ":100\r\n");
1950 f.run(&[b"PERSIST", b"k"]);
1951 assert_eq!(
1952 f.run(&[b"EXPIRE", b"k", b"100", b"XX", b"LT"]),
1953 ":0\r\n",
1954 "where LT on its own would have taken it"
1955 );
1956 assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"LT"]), ":1\r\n");
1957 }
1958
1959 #[test]
1960 fn a_key_is_gone_once_its_moment_passes() {
1961 let mut f = Fixture::new();
1962 f.run(&[b"SET", b"k", b"v"]);
1963 f.run(&[b"EXPIRE", b"k", b"100"]);
1964
1965 let at = int(&f.run(&[b"PEXPIRETIME", b"k"]));
1966 f.server.set_clock_ms(at as u64 + 1);
1967 assert_eq!(f.run(&[b"GET", b"k"]), "$-1\r\n");
1968 assert_eq!(f.run(&[b"TTL", b"k"]), ":-2\r\n");
1969 assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
1970 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
1971 }
1972
1973 #[test]
1974 fn the_expiry_commands_refuse_what_a_real_server_refuses() {
1975 let mut f = Fixture::new();
1976 f.run(&[b"SET", b"k", b"v"]);
1977 for (bad, want) in [
1978 (
1979 &[b"EXPIRE".as_slice(), b"k", b"soon"][..],
1980 "-ERR value is not an integer or out of range\r\n",
1981 ),
1982 (
1983 &[b"EXPIRE", b"k", b"100", b"MAYBE"],
1984 "-ERR Unsupported option MAYBE\r\n",
1985 ),
1986 (
1987 &[b"EXPIRE", b"k", b"100", b"NX", b"XX"],
1988 "-ERR NX and XX, GT or LT options at the same time are not compatible\r\n",
1989 ),
1990 (
1991 &[b"EXPIRE", b"k", b"100", b"NX", b"GT"],
1992 "-ERR NX and XX, GT or LT options at the same time are not compatible\r\n",
1993 ),
1994 (
1995 &[b"EXPIRE", b"k", b"100", b"GT", b"LT", b"GT"],
1996 "-ERR GT and LT options at the same time are not compatible\r\n",
1997 ),
1998 (
2001 &[b"EXPIRE", b"k", b"9223372036854775807"],
2002 "-ERR invalid expire time in 'expire' command\r\n",
2003 ),
2004 (
2005 &[b"EXPIREAT", b"k", b"9223372036854775807"],
2006 "-ERR invalid expire time in 'expireat' command\r\n",
2007 ),
2008 (
2009 &[b"PEXPIRE", b"k", b"9223372036854775807"],
2010 "-ERR invalid expire time in 'pexpire' command\r\n",
2011 ),
2012 ] {
2013 assert_eq!(f.run(bad), want, "for {bad:?}");
2014 }
2015 assert_eq!(
2016 f.run(&[b"TTL", b"k"]),
2017 ":-1\r\n",
2018 "and none of those put a deadline on anything"
2019 );
2020
2021 assert_eq!(
2025 f.run(&[b"PEXPIREAT", b"k", b"9223372036854775807"]),
2026 ":1\r\n"
2027 );
2028 assert_eq!(f.run(&[b"PEXPIRETIME", b"k"]), ":70368744177663\r\n");
2029 }
2030
2031 #[test]
2032 fn flushing_empties_this_database_or_every_one_of_them() {
2033 let mut f = Fixture::new();
2034 f.run(&[b"SELECT", b"0"]);
2035 f.run(&[b"MSET", b"a", b"1", b"b", b"2"]);
2036 f.run(&[b"SELECT", b"1"]);
2037 f.run(&[b"SET", b"c", b"3"]);
2038 assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
2039 assert_eq!(f.run(&[b"FLUSHDB", b"async"]), "+OK\r\n");
2042 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
2043 f.run(&[b"SELECT", b"0"]);
2045 assert_eq!(f.run(&[b"DBSIZE"]), ":2\r\n");
2046 assert_eq!(f.run(&[b"FLUSHALL", b"SYNC"]), "+OK\r\n");
2047 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
2048 f.run(&[b"SELECT", b"1"]);
2049 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
2050 assert_eq!(f.run(&[b"FLUSHALL", b"nope"]), "-ERR syntax error\r\n");
2053 assert_eq!(
2054 f.run(&[b"FLUSHDB", b"sync", b"sync"]),
2055 "-ERR syntax error\r\n"
2056 );
2057 }
2058
2059 #[test]
2060 fn the_script_cache_and_the_library_set_answer_for_being_empty() {
2061 let mut f = Fixture::new();
2062 assert_eq!(f.run(&[b"SCRIPT", b"FLUSH"]), "+OK\r\n");
2063 assert_eq!(f.run(&[b"SCRIPT", b"FLUSH", b"async"]), "+OK\r\n");
2064 assert_eq!(f.run(&[b"FUNCTION", b"FLUSH", b"SYNC"]), "+OK\r\n");
2065 assert_eq!(
2068 f.run(&[b"SCRIPT", b"EXISTS", b"aaaa", b"bbbb"]),
2069 "*2\r\n:0\r\n:0\r\n"
2070 );
2071 assert_eq!(f.run(&[b"FUNCTION", b"LIST"]), "*0\r\n");
2072 assert_eq!(
2073 f.run(&[b"FUNCTION", b"LIST", b"LIBRARYNAME", b"x", b"WITHCODE"]),
2074 "*0\r\n"
2075 );
2076 assert_eq!(
2077 f.run(&[b"FUNCTION", b"DELETE", b"nosuch"]),
2078 "-ERR Library not found\r\n"
2079 );
2080
2081 assert_eq!(
2084 f.run(&[b"SCRIPT", b"FLUSH", b"nope"]),
2085 "-ERR SCRIPT FLUSH only support SYNC|ASYNC option\r\n"
2086 );
2087 assert_eq!(
2088 f.run(&[b"FUNCTION", b"FLUSH", b"nope"]),
2089 "-ERR FUNCTION FLUSH only supports SYNC|ASYNC option\r\n"
2090 );
2091 assert_eq!(
2094 f.run(&[b"FUNCTION", b"FLUSH", b"sync", b"sync"]),
2095 "-ERR unknown subcommand or wrong number of arguments for 'flush'. Try FUNCTION HELP.\r\n"
2096 );
2097 assert_eq!(
2098 f.run(&[b"FUNCTION", b"LIST", b"bogus"]),
2099 "-ERR Unknown argument bogus\r\n"
2100 );
2101 assert_eq!(
2102 f.run(&[b"SCRIPT", b"EXISTS"]),
2103 "-ERR wrong number of arguments for 'script|exists' command\r\n"
2104 );
2105
2106 assert_eq!(
2109 f.run(&[b"SCRIPT", b"LOAD", b"return 1"]),
2110 "-ERR unknown subcommand 'LOAD'. Try SCRIPT HELP.\r\n"
2111 );
2112 assert_eq!(
2113 f.run(&[b"FUNCTION", b"STATS"]),
2114 "-ERR unknown subcommand 'STATS'. Try FUNCTION HELP.\r\n"
2115 );
2116 }
2117
2118 #[test]
2119 fn a_counter_is_an_integer_and_not_a_string_of_digits() {
2120 let mut f = Fixture::new();
2121 assert_eq!(f.run(&[b"INCR", b"c"]), ":1\r\n");
2122 assert_eq!(f.run(&[b"INCRBY", b"c", b"41"]), ":42\r\n");
2123 assert_eq!(f.run(&[b"DECRBY", b"c", b"2"]), ":40\r\n");
2124 assert_eq!(f.run(&[b"GET", b"c"]), "$2\r\n40\r\n");
2127 assert_eq!(f.run(&[b"INCRBYFLOAT", b"c", b"0.5"]), "$4\r\n40.5\r\n");
2128 f.run(&[b"SET", b"k", b"hello"]);
2131 assert_eq!(
2132 f.run(&[b"INCR", b"k"]),
2133 "-ERR value is not an integer or out of range\r\n"
2134 );
2135 assert_eq!(
2136 f.run(&[b"INCRBYFLOAT", b"c", b"inf"]),
2137 "-ERR increment would produce NaN or Infinity\r\n"
2138 );
2139 }
2140
2141 #[test]
2146 fn the_newer_commands_reply_in_the_shapes_a_real_server_sends() {
2147 let mut f = Fixture::new();
2148 assert_eq!(f.run(&[b"SET", b"k", b"hello"]), "+OK\r\n");
2149 assert_eq!(f.run(&[b"DIGEST", b"k"]), "$16\r\n9555e8555c62dcfd\r\n");
2152 assert_eq!(f.run(&[b"DIGEST", b"nosuch"]), "$-1\r\n");
2153 assert_eq!(f.run(&[b"MSETEX", b"1", b"a", b"1"]), ":1\r\n");
2154 assert_eq!(f.run(&[b"MSETEX", b"1", b"a", b"2", b"NX"]), ":0\r\n");
2155 assert_eq!(f.run(&[b"GET", b"a"]), "$1\r\n1\r\n");
2156 assert_eq!(f.run(&[b"INCREX", b"n"]), "*2\r\n:1\r\n:1\r\n");
2157 assert_eq!(
2158 f.run(&[b"INCREX", b"n", b"BYINT", b"5", b"UBOUND", b"3"]),
2159 "*2\r\n:1\r\n:0\r\n",
2160 "a refused increment reports the value it left alone and applied nothing"
2161 );
2162 assert_eq!(
2163 f.run(&[
2164 b"INCREX",
2165 b"n",
2166 b"BYINT",
2167 b"5",
2168 b"UBOUND",
2169 b"3",
2170 b"SATURATE"
2171 ]),
2172 "*2\r\n:3\r\n:2\r\n"
2173 );
2174 assert_eq!(f.run(&[b"DELEX", b"a", b"IFEQ", b"2"]), ":0\r\n");
2175 assert_eq!(f.run(&[b"DELEX", b"a", b"IFEQ", b"1"]), ":1\r\n");
2176 }
2177
2178 #[test]
2179 fn the_same_answers_come_out_in_resp3_spelling() {
2180 let mut f = Fixture::new();
2181 assert!(f.run(&[b"HELLO", b"3"]).starts_with("%7\r\n"));
2182 assert_eq!(f.run(&[b"GET", b"nosuch"]), "_\r\n");
2183 assert_eq!(
2186 f.run(&[b"INCREX", b"c", b"BYFLOAT", b"1.5"]),
2187 "*2\r\n,1.5\r\n,1.5\r\n"
2188 );
2189 assert_eq!(f.run(&[b"INCRBYFLOAT", b"f", b"2.5"]), "$3\r\n2.5\r\n");
2190 assert_eq!(f.run(&[b"RESET"]), "+RESET\r\n");
2193 assert_eq!(f.run(&[b"GET", b"nosuch"]), "$-1\r\n");
2194 }
2195
2196 #[test]
2197 fn a_command_nobody_has_heard_of_is_an_error_and_not_a_closed_socket() {
2198 let mut f = Fixture::new();
2199 let (flow, reply) = f.flow(&[b"NOPE", b"a", b"b"]);
2200 assert_eq!(flow, Flow::Continue);
2201 assert_eq!(
2202 reply,
2203 "-ERR unknown command 'NOPE', with args beginning with: 'a' 'b' \r\n"
2204 );
2205 let reply = f.run(&[b"NO\r\n+PONG\r\nPE"]);
2208 assert_eq!(reply.matches("\r\n").count(), 1);
2209 }
2210
2211 #[test]
2212 fn arity_is_checked_before_the_command_is() {
2213 let mut f = Fixture::new();
2214 assert_eq!(
2215 f.run(&[b"GET"]),
2216 "-ERR wrong number of arguments for 'get' command\r\n"
2217 );
2218 assert_eq!(
2219 f.run(&[b"MSET", b"k"]),
2220 "-ERR wrong number of arguments for 'mset' command\r\n"
2221 );
2222 assert_eq!(
2226 f.run(&[b"PING", b"a", b"b"]),
2227 "-ERR wrong number of arguments for 'ping' command\r\n"
2228 );
2229 assert_eq!(f.run(&[b"PING"]), "+PONG\r\n");
2230 assert_eq!(f.run(&[b"PING", b"hi"]), "$2\r\nhi\r\n");
2231 assert_eq!(
2233 f.run(&[b"DELEX", b"k", b"IFEQ"]),
2234 "-ERR wrong number of arguments for 'delex' command\r\n"
2235 );
2236 }
2237
2238 #[test]
2242 fn the_option_combinations_are_the_ones_a_real_server_accepts() {
2243 let mut f = Fixture::new();
2244 let syntax = "-ERR syntax error\r\n";
2245 assert_eq!(f.run(&[b"SET", b"k", b"v", b"NX", b"XX"]), syntax);
2246 assert_eq!(f.run(&[b"SET", b"k", b"v", b"NX", b"IFEQ", b"a"]), syntax);
2247 assert_eq!(
2248 f.run(&[b"SET", b"k", b"v", b"KEEPTTL", b"EX", b"5"]),
2249 syntax
2250 );
2251 assert_eq!(
2252 f.run(&[b"SET", b"k", b"v", b"EX", b"5", b"PX", b"5"]),
2253 syntax
2254 );
2255 assert_eq!(f.run(&[b"SET", b"k", b"v", b"PERSIST"]), syntax);
2256 assert_eq!(
2258 f.run(&[b"SET", b"k", b"v", b"EX", b"5", b"EX", b"100"]),
2259 "+OK\r\n"
2260 );
2261 assert_eq!(f.run(&[b"SET", b"k", b"v", b"XX", b"XX"]), "+OK\r\n");
2262 assert_eq!(f.run(&[b"SET", b"k", b"v", b"GET", b"GET"]), "$1\r\nv\r\n");
2263 assert_eq!(
2265 f.run(&[b"INCREX", b"n", b"BYINT", b"1", b"BYINT", b"2"]),
2266 syntax
2267 );
2268 assert_eq!(
2269 f.run(&[b"INCREX", b"n", b"ENX"]),
2270 "-ERR ENX flag requires an expiration\r\n"
2271 );
2272 assert_eq!(
2273 f.run(&[b"INCREX", b"n", b"UBOUND", b"abc"]),
2274 "-ERR UBOUND is not an integer or out of range\r\n"
2275 );
2276 assert_eq!(
2277 f.run(&[b"INCREX", b"n", b"LBOUND", b"10", b"UBOUND", b"5"]),
2278 "-ERR LBOUND can't be greater than UBOUND\r\n"
2279 );
2280 assert_eq!(
2281 f.run(&[b"LCS", b"a", b"b", b"LEN", b"IDX"]),
2282 "-ERR If you want both the length and indexes, please just use IDX.\r\n"
2283 );
2284 }
2285
2286 #[test]
2290 fn the_expiry_rules_are_redis_own() {
2291 let mut f = Fixture::new();
2292 let bad = "-ERR invalid expire time in 'set' command\r\n";
2293 assert_eq!(f.run(&[b"SET", b"k", b"v", b"EX", b"0"]), bad);
2294 assert_eq!(f.run(&[b"SET", b"k", b"v", b"EX", b"-1"]), bad);
2295 assert_eq!(f.run(&[b"SET", b"k", b"v", b"EXAT", b"0"]), bad);
2296 assert_eq!(
2297 f.run(&[b"SET", b"k", b"v", b"EX", b"9999999999999999"]),
2298 bad
2299 );
2300 assert_eq!(
2301 f.run(&[b"SET", b"k", b"v", b"PX", b"99999999999999999999"]),
2302 "-ERR value is not an integer or out of range\r\n"
2303 );
2304 assert_eq!(
2305 f.run(&[b"SETEX", b"k", b"0", b"v"]),
2306 "-ERR invalid expire time in 'setex' command\r\n"
2307 );
2308 assert_eq!(f.run(&[b"GETEX", b"nosuch", b"EX", b"0"]), "$-1\r\n");
2309 assert_eq!(f.run(&[b"GETEX", b"nosuch", b"EX", b"abc"]), "$-1\r\n");
2310 assert_eq!(
2311 f.run(&[b"GETEX", b"nosuch", b"KEEPTTL"]),
2312 "-ERR syntax error\r\n",
2313 "the option list is still checked before the key is looked up"
2314 );
2315 assert_eq!(f.run(&[b"SET", b"k", b"v"]), "+OK\r\n");
2317 assert_eq!(f.run(&[b"SET", b"k", b"v", b"EXAT", b"1"]), "+OK\r\n");
2318 assert_eq!(f.run(&[b"GET", b"k"]), "$-1\r\n");
2319 }
2320
2321 #[test]
2322 fn mset_takes_its_pairs_from_the_read_buffer() {
2323 let mut f = Fixture::new();
2324 assert_eq!(f.run(&[b"MSET", b"a", b"1", b"b", b"2"]), "+OK\r\n");
2325 assert_eq!(
2326 f.run(&[b"MGET", b"a", b"b", b"nosuch"]),
2327 "*3\r\n$1\r\n1\r\n$1\r\n2\r\n$-1\r\n"
2328 );
2329 assert_eq!(f.run(&[b"MSETNX", b"b", b"9", b"c", b"3"]), ":0\r\n");
2330 assert_eq!(f.run(&[b"MSETNX", b"c", b"3", b"d", b"4"]), ":1\r\n");
2331 assert_eq!(
2332 f.run(&[b"MSETEX", b"2", b"e", b"5"]),
2333 "-ERR wrong number of key-value pairs\r\n"
2334 );
2335 assert_eq!(
2336 f.run(&[b"MSETEX", b"0", b"e", b"5"]),
2337 "-ERR invalid numkeys value\r\n"
2338 );
2339 assert_eq!(
2340 f.run(&[b"MSETEX", b"abc", b"e", b"5"]),
2341 "-ERR invalid numkeys value\r\n"
2342 );
2343 }
2344
2345 #[test]
2346 fn lcs_answers_the_length_the_string_and_the_runs() {
2347 let mut f = Fixture::new();
2348 f.run(&[b"MSET", b"a", b"ohmytext", b"b", b"mynewtext"]);
2349 assert_eq!(f.run(&[b"LCS", b"a", b"b"]), "$6\r\nmytext\r\n");
2350 assert_eq!(f.run(&[b"LCS", b"a", b"b", b"LEN"]), ":6\r\n");
2351 assert_eq!(
2352 f.run(&[b"LCS", b"a", b"b", b"IDX", b"MINMATCHLEN", b"4"]),
2353 "*4\r\n$7\r\nmatches\r\n*1\r\n*2\r\n*2\r\n:4\r\n:7\r\n*2\r\n:5\r\n:8\r\n$3\r\nlen\r\n:6\r\n"
2354 );
2355 assert_eq!(
2358 f.run(&[b"LCS", b"a", b"b", b"MINMATCHLEN", b"4", b"WITHMATCHLEN"]),
2359 "$6\r\nmytext\r\n"
2360 );
2361 }
2362
2363 #[test]
2364 fn select_moves_the_connection_and_the_databases_stay_apart() {
2365 let mut f = Fixture::new();
2366 f.run(&[b"SET", b"k", b"zero"]);
2367 assert_eq!(f.run(&[b"SELECT", b"4"]), "+OK\r\n");
2368 assert_eq!(f.run(&[b"GET", b"k"]), "$-1\r\n");
2369 f.run(&[b"SET", b"k", b"four"]);
2370 assert_eq!(f.run(&[b"SELECT", b"0"]), "+OK\r\n");
2371 assert_eq!(f.run(&[b"GET", b"k"]), "$4\r\nzero\r\n");
2372 assert_eq!(
2373 f.run(&[b"SELECT", b"99"]),
2374 "-ERR DB index is out of range\r\n"
2375 );
2376 assert_eq!(
2377 f.run(&[b"SELECT", b"-1"]),
2378 "-ERR DB index is out of range\r\n"
2379 );
2380 assert_eq!(
2381 f.run(&[b"SELECT", b"abc"]),
2382 "-ERR value is not an integer or out of range\r\n"
2383 );
2384 f.run(&[b"SELECT", b"4"]);
2386 f.run(&[b"RESET"]);
2387 assert_eq!(f.run(&[b"GET", b"k"]), "$4\r\nzero\r\n");
2388 }
2389
2390 #[test]
2391 fn hello_agrees_on_a_protocol_and_refuses_the_ones_that_do_not_exist() {
2392 let mut f = Fixture::new();
2393 let reply = f.run(&[b"HELLO"]);
2394 assert!(reply.starts_with("*14\r\n"), "{reply}");
2395 assert!(reply.contains("$5\r\nredis\r\n"), "{reply}");
2396 assert!(reply.contains("$5\r\n8.8.0\r\n"), "{reply}");
2397 assert!(
2398 reply.contains(":7\r\n"),
2399 "the connection id is in there: {reply}"
2400 );
2401 assert_eq!(
2402 f.run(&[b"HELLO", b"4"]),
2403 "-NOPROTO unsupported protocol version\r\n"
2404 );
2405 assert_eq!(
2406 f.run(&[b"HELLO", b"abc"]),
2407 "-ERR Protocol version is not an integer or out of range\r\n"
2408 );
2409 assert_eq!(
2410 f.run(&[b"HELLO", b"3", b"SETNAME"]),
2411 "-ERR Syntax error in HELLO option 'SETNAME'\r\n"
2412 );
2413 assert!(
2414 f.run(&[b"HELLO", b"3", b"SETNAME", b"bob"])
2415 .starts_with("%7\r\n")
2416 );
2417 assert_eq!(f.session.name(), b"bob");
2418 f.run(&[b"RESET"]);
2419 assert_eq!(f.session.name(), b"");
2420 }
2421
2422 #[test]
2423 fn command_describes_this_server_in_the_shape_a_driver_reads() {
2424 let mut f = Fixture::new();
2425 let count = format!(":{}\r\n", COMMANDS.len());
2426 assert_eq!(f.run(&[b"COMMAND", b"COUNT"]), count);
2427 let info = f.run(&[b"COMMAND", b"INFO", b"get"]);
2428 assert_eq!(
2429 info,
2430 "*1\r\n*10\r\n$3\r\nget\r\n:2\r\n*2\r\n+readonly\r\n+fast\r\n:1\r\n:1\r\n:1\r\n\
2431 *3\r\n+@read\r\n+@string\r\n+@fast\r\n*0\r\n*0\r\n*0\r\n"
2432 );
2433 assert_eq!(f.run(&[b"COMMAND", b"INFO", b"nosuch"]), "*1\r\n$-1\r\n");
2435 assert_eq!(
2436 f.run(&[b"COMMAND", b"LIST", b"FILTERBY", b"PATTERN", b"getr*"]),
2437 "*1\r\n$8\r\ngetrange\r\n"
2438 );
2439 assert_eq!(
2440 f.run(&[b"COMMAND", b"NOPE"]),
2441 "-ERR unknown subcommand 'NOPE'. Try COMMAND HELP.\r\n"
2442 );
2443 }
2444
2445 #[test]
2449 fn command_getkeys_finds_the_keys_including_the_hidden_ones() {
2450 let mut f = Fixture::new();
2451 assert_eq!(
2452 f.run(&[b"COMMAND", b"GETKEYS", b"get", b"k"]),
2453 "*1\r\n$1\r\nk\r\n"
2454 );
2455 assert_eq!(
2456 f.run(&[b"COMMAND", b"GETKEYS", b"mset", b"a", b"1", b"b", b"2"]),
2457 "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
2458 );
2459 assert_eq!(
2460 f.run(&[
2461 b"COMMAND", b"GETKEYS", b"msetex", b"2", b"a", b"1", b"b", b"2"
2462 ]),
2463 "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
2464 );
2465 assert_eq!(
2466 f.run(&[b"COMMAND", b"GETKEYS", b"ping"]),
2467 "-ERR The command has no key arguments\r\n"
2468 );
2469 assert_eq!(
2470 f.run(&[b"COMMAND", b"GETKEYS", b"set"]),
2471 "-ERR Invalid number of arguments specified for command\r\n"
2472 );
2473 }
2474
2475 #[test]
2476 fn config_answers_what_it_can_and_refuses_what_it_cannot() {
2477 let mut f = Fixture::new();
2478 assert_eq!(
2479 f.run(&[b"CONFIG", b"GET", b"maxmemory"]),
2480 "*2\r\n$9\r\nmaxmemory\r\n$1\r\n0\r\n"
2481 );
2482 let both = f.run(&[b"CONFIG", b"GET", b"maxmemory*", b"maxmemory"]);
2485 assert!(both.starts_with("*6\r\n"), "{both}");
2486 assert_eq!(f.run(&[b"CONFIG", b"GET", b"nosuch"]), "*0\r\n");
2487 assert_eq!(f.run(&[b"CONFIG", b"SET", b"appendonly", b"no"]), "+OK\r\n");
2488 assert_eq!(
2489 f.run(&[b"CONFIG", b"SET", b"appendonly", b"yes"]),
2490 "-ERR CONFIG SET failed (possibly related to argument 'appendonly') - can't set immutable config\r\n"
2491 );
2492 assert_eq!(
2493 f.run(&[b"CONFIG", b"SET", b"nosuch", b"1"]),
2494 "-ERR Unknown option or number of arguments for CONFIG SET - 'nosuch'\r\n"
2495 );
2496 assert_eq!(
2497 f.run(&[b"CONFIG", b"GET"]),
2498 "-ERR wrong number of arguments for 'config|get' command\r\n"
2499 );
2500 assert_eq!(
2504 f.run(&[b"CONFIG", b"SET", b"appendonly"]),
2505 "-ERR wrong number of arguments for 'config|set' command\r\n"
2506 );
2507 assert_eq!(
2508 f.run(&[b"CONFIG", b"SET", b"appendonly", b"no", b"maxmemory"]),
2509 "-ERR syntax error\r\n"
2510 );
2511 assert_eq!(f.run(&[b"CONFIG", b"RESETSTAT"]), "+OK\r\n");
2512 assert_eq!(
2513 f.run(&[b"CONFIG", b"REWRITE"]),
2514 "-ERR The server is running without a config file\r\n"
2515 );
2516 }
2517
2518 #[test]
2519 fn the_eviction_policy_reads_back_what_was_written_to_it() {
2520 let mut f = Fixture::new();
2521 assert_eq!(
2522 f.run(&[b"CONFIG", b"GET", b"maxmemory-policy"]),
2523 "*2\r\n$16\r\nmaxmemory-policy\r\n$10\r\nnoeviction\r\n"
2524 );
2525 assert_eq!(
2526 f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"AllKeys-LFU"]),
2527 "+OK\r\n",
2528 "the name is matched without regard to case, like every other one"
2529 );
2530 assert_eq!(
2531 f.run(&[b"CONFIG", b"GET", b"maxmemory-policy"]),
2532 "*2\r\n$16\r\nmaxmemory-policy\r\n$11\r\nallkeys-lfu\r\n"
2533 );
2534 assert!(
2536 f.run(&[b"INFO", b"memory"])
2537 .contains("maxmemory_policy:allkeys-lfu"),
2538 "INFO and CONFIG disagree about the policy"
2539 );
2540 assert_eq!(
2544 f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"garbage"]),
2545 "-ERR CONFIG SET failed (possibly related to argument 'maxmemory-policy') - argument(s) must be one of the following: volatile-lru, volatile-lfu, volatile-random, volatile-ttl, volatile-lrm, allkeys-lru, allkeys-lfu, allkeys-random, allkeys-lrm, noeviction\r\n"
2546 );
2547 assert_eq!(
2550 f.run(&[b"CONFIG", b"GET", b"maxmemory-policy"]),
2551 "*2\r\n$16\r\nmaxmemory-policy\r\n$11\r\nallkeys-lfu\r\n"
2552 );
2553 f.run(&[
2554 b"CONFIG",
2555 b"SET",
2556 b"hash-max-listpack-entries",
2557 b"7",
2558 b"maxmemory-policy",
2559 b"nonsense",
2560 ]);
2561 assert_eq!(
2562 f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
2563 "*2\r\n$25\r\nhash-max-listpack-entries\r\n$3\r\n512\r\n"
2564 );
2565 }
2566
2567 #[test]
2568 fn the_three_eviction_numbers_read_back_too() {
2569 let mut f = Fixture::new();
2570 for (name, default, set) in [
2571 ("maxmemory-samples", "5", "12"),
2572 ("lfu-log-factor", "10", "3"),
2573 ("lfu-decay-time", "1", "60"),
2574 ] {
2575 let get = || {
2576 format!(
2577 "*2\r\n${}\r\n{name}\r\n${}\r\n{default}\r\n",
2578 name.len(),
2579 default.len()
2580 )
2581 };
2582 assert_eq!(f.run(&[b"CONFIG", b"GET", name.as_bytes()]), get());
2583 assert_eq!(
2584 f.run(&[b"CONFIG", b"SET", name.as_bytes(), set.as_bytes()]),
2585 "+OK\r\n"
2586 );
2587 assert_eq!(
2588 f.run(&[b"CONFIG", b"GET", name.as_bytes()]),
2589 format!(
2590 "*2\r\n${}\r\n{name}\r\n${}\r\n{set}\r\n",
2591 name.len(),
2592 set.len()
2593 )
2594 );
2595 assert_eq!(
2598 f.run(&[b"CONFIG", b"SET", name.as_bytes(), b"soon"]),
2599 format!(
2600 "-ERR CONFIG SET failed (possibly related to argument '{name}') - argument couldn't be parsed into an integer\r\n"
2601 )
2602 );
2603 }
2604 }
2605
2606 #[test]
2607 fn the_memory_limit_reads_back_in_bytes_whatever_the_unit_was() {
2608 let mut f = Fixture::new();
2609 assert_eq!(
2610 f.run(&[b"CONFIG", b"GET", b"maxmemory"]),
2611 "*2\r\n$9\r\nmaxmemory\r\n$1\r\n0\r\n",
2612 "no limit is the default"
2613 );
2614 for (typed, bytes) in [
2617 (&b"1024"[..], "1024"),
2618 (b"1k", "1000"),
2619 (b"1kb", "1024"),
2620 (b"1M", "1000000"),
2621 (b"1Mb", "1048576"),
2622 (b"1gb", "1073741824"),
2623 (b"100mb", "104857600"),
2624 ] {
2625 assert_eq!(f.run(&[b"CONFIG", b"SET", b"maxmemory", typed]), "+OK\r\n");
2626 assert_eq!(
2627 f.run(&[b"CONFIG", b"GET", b"maxmemory"]),
2628 format!("*2\r\n$9\r\nmaxmemory\r\n${}\r\n{bytes}\r\n", bytes.len()),
2629 "set {}",
2630 String::from_utf8_lossy(typed)
2631 );
2632 }
2633 assert!(
2634 f.run(&[b"INFO", b"memory"]).contains("maxmemory:104857600"),
2635 "the report agrees with the setting"
2636 );
2637
2638 for bad in [&b"1tb"[..], b"-1", b"", b"lots"] {
2641 assert_eq!(
2642 f.run(&[b"CONFIG", b"SET", b"maxmemory", bad]),
2643 "-ERR CONFIG SET failed (possibly related to argument 'maxmemory') - argument must be a memory value\r\n",
2644 "refused {}",
2645 String::from_utf8_lossy(bad)
2646 );
2647 }
2648 assert!(
2649 f.run(&[b"INFO", b"memory"]).contains("maxmemory:104857600"),
2650 "and the refusal left the old one alone"
2651 );
2652 }
2653
2654 #[test]
2655 fn a_write_is_refused_when_there_is_no_room_and_nothing_to_evict() {
2656 let mut f = Fixture::new();
2657 f.run(&[b"SET", b"here", b"already"]);
2658 f.run(&[b"CONFIG", b"SET", b"maxmemory", b"1"]);
2662 assert_eq!(
2663 f.run(&[b"SET", b"k", b"v"]),
2664 "-OOM command not allowed when used memory > 'maxmemory'.\r\n"
2665 );
2666 assert_eq!(
2667 f.run(&[b"LPUSH", b"l", b"v"]),
2668 "-OOM command not allowed when used memory > 'maxmemory'.\r\n"
2669 );
2670 assert_eq!(f.run(&[b"GET", b"here"]), "$7\r\nalready\r\n");
2672 assert_eq!(f.run(&[b"DEL", b"here"]), ":1\r\n");
2673 assert!(f.run(&[b"INFO", b"stats"]).contains("evicted_keys:0"));
2674
2675 f.run(&[b"CONFIG", b"SET", b"maxmemory", b"0"]);
2677 assert_eq!(f.run(&[b"SET", b"k", b"v"]), "+OK\r\n");
2678 }
2679
2680 #[test]
2681 fn an_allkeys_policy_makes_room_instead_of_refusing() {
2682 let mut f = Fixture::new();
2683 let val = vec![b'v'; 256];
2684 for i in 0..24000u32 {
2685 let k = format!("key:{i:08}");
2686 f.run(&[b"SET", k.as_bytes(), &val]);
2687 }
2688 let full = f.server.memory_bytes();
2689 assert!(
2690 full > 3 * 1024 * 1024,
2691 "the arena is several segments: {full}"
2692 );
2693
2694 let limit = full - 2 * 1024 * 1024;
2698 f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lru"]);
2699 f.run(&[
2700 b"CONFIG",
2701 b"SET",
2702 b"maxmemory",
2703 limit.to_string().as_bytes(),
2704 ]);
2705
2706 for i in 0..2000u32 {
2710 let k = format!("new:{i:08}");
2711 assert_eq!(
2712 f.run(&[b"SET", k.as_bytes(), &val]),
2713 "+OK\r\n",
2714 "write {i} was refused"
2715 );
2716 f.server.refresh_memory();
2717 if f.server.memory_bytes() <= limit {
2718 break;
2719 }
2720 }
2721 assert!(
2722 f.server.memory_bytes() <= limit,
2723 "it never got under: {} against {limit}",
2724 f.server.memory_bytes()
2725 );
2726 let info = f.run(&[b"INFO", b"stats"]);
2727 assert!(!info.contains("evicted_keys:0"), "{info}");
2728 assert!(
2729 f.run(&[b"DBSIZE"]) != ":0\r\n",
2730 "and it did not empty the database to get there"
2731 );
2732 }
2733
2734 #[test]
2735 fn the_running_total_and_the_walk_agree_on_a_mixed_keyspace() {
2736 let mut f = Fixture::new();
2743 f.run(&[b"CONFIG", b"SET", b"maxmemory", b"1gb"]);
2744 let big = vec![b'v'; 200];
2745
2746 for i in 0..400u32 {
2747 let n = i.to_string();
2748 let n = n.as_bytes();
2749 f.run(&[b"SADD", b"s", n]);
2750 f.run(&[b"SADD", b"s2", &big]);
2751 f.run(&[b"HSET", b"h", n, &big]);
2752 f.run(&[b"RPUSH", b"l", &big]);
2753 f.run(&[b"ZADD", b"z", n, n]);
2754 f.run(&[b"ARSET", b"a", n, &big]);
2755 if i % 7 == 0 {
2756 f.run(&[b"SREM", b"s", n]);
2757 f.run(&[b"HDEL", b"h", n]);
2758 f.run(&[b"LPOP", b"l"]);
2759 f.run(&[b"ZREM", b"z", n]);
2760 f.run(&[b"ARDEL", b"a", n]);
2761 }
2762 if i % 53 == 0 {
2763 f.run(&[b"DEL", b"s2"]);
2766 }
2767 assert_eq!(
2768 f.server.settled_memory(),
2769 f.server.memory_bytes(),
2770 "after round {i}"
2771 );
2772 }
2773
2774 assert_eq!(f.run(&[b"DBSIZE"]), ":6\r\n");
2777 assert!(
2778 f.server.memory_bytes() > 512 * 1024,
2779 "{}",
2780 f.server.memory_bytes()
2781 );
2782
2783 f.run(&[b"FLUSHALL"]);
2785 assert_eq!(f.server.settled_memory(), f.server.memory_bytes());
2786 }
2787
2788 #[test]
2789 fn taking_the_limit_away_stops_the_counting_and_putting_it_back_starts_again() {
2790 let mut f = Fixture::new();
2795 for i in 0..200u32 {
2796 let n = i.to_string();
2797 f.run(&[b"SADD", b"s", n.as_bytes()]);
2798 f.run(&[b"HSET", b"h", n.as_bytes(), b"value"]);
2799 }
2800 f.run(&[b"CONFIG", b"SET", b"maxmemory", b"1gb"]);
2801 assert_eq!(f.server.settled_memory(), f.server.memory_bytes());
2802
2803 f.run(&[b"CONFIG", b"SET", b"maxmemory", b"0"]);
2804 for i in 200..400u32 {
2805 let n = i.to_string();
2806 f.run(&[b"SADD", b"s", n.as_bytes()]);
2807 }
2808 f.run(&[b"CONFIG", b"SET", b"maxmemory", b"1gb"]);
2809 assert_eq!(
2810 f.server.settled_memory(),
2811 f.server.memory_bytes(),
2812 "the writes it was not watching are in the number it started from"
2813 );
2814 }
2815
2816 #[test]
2817 fn evicted_keys_and_expired_keys_are_different_numbers() {
2818 let mut f = Fixture::new();
2819 f.run(&[b"SET", b"gone", b"v", b"PX", b"1"]);
2822 f.server.db(0).clock_mut().advance(20);
2823 f.run(&[b"GET", b"gone"]);
2824 let info = f.run(&[b"INFO", b"stats"]);
2825 assert!(info.contains("expired_keys:1"), "{info}");
2826 assert!(info.contains("evicted_keys:0"), "{info}");
2827 }
2828
2829 #[test]
2830 fn the_object_subcommands_follow_the_policy() {
2831 let mut f = Fixture::new();
2832 f.run(&[b"SET", b"s", b"v"]);
2833 assert_eq!(f.run(&[b"OBJECT", b"IDLETIME", b"s"]), ":0\r\n");
2837 assert!(
2838 f.run(&[b"OBJECT", b"FREQ", b"s"])
2839 .starts_with("-ERR An LFU maxmemory policy is not selected"),
2840 );
2841
2842 f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lfu"]);
2843 assert!(
2844 f.run(&[b"OBJECT", b"IDLETIME", b"s"])
2845 .starts_with("-ERR An LFU maxmemory policy is selected"),
2846 );
2847 assert!(
2852 f.run(&[b"OBJECT", b"FREQ", b"s"]).starts_with(':'),
2853 "FREQ should answer under an LFU policy"
2854 );
2855 }
2856
2857 #[test]
2858 fn object_says_which_rung_of_the_ladder_a_key_is_on() {
2859 let mut f = Fixture::new();
2860 f.run(&[b"SET", b"s", b"hello"]);
2861 f.run(&[b"SET", b"n", b"123"]);
2862 f.run(&[b"SADD", b"si", b"1", b"2", b"3"]);
2863 f.run(&[b"SADD", b"ss", b"a", b"b"]);
2864 f.run(&[b"HSET", b"h", b"f", b"v"]);
2865 for (key, want) in [
2866 (b"s".as_slice(), "embstr"),
2867 (b"n", "int"),
2868 (b"si", "intset"),
2869 (b"ss", "listpack"),
2870 (b"h", "listpack"),
2871 ] {
2872 let reply = f.run(&[b"OBJECT", b"ENCODING", key]);
2873 assert_eq!(reply, format!("${}\r\n{want}\r\n", want.len()));
2874 }
2875
2876 f.run(&[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"f"]);
2879 assert_eq!(
2880 f.run(&[b"OBJECT", b"ENCODING", b"h"]),
2881 "$10\r\nlistpackex\r\n"
2882 );
2883
2884 assert_eq!(f.run(&[b"OBJECT", b"REFCOUNT", b"s"]), ":1\r\n");
2885 assert_eq!(f.run(&[b"OBJECT", b"IDLETIME", b"s"]), ":0\r\n");
2886 assert!(f.run(&[b"OBJECT", b"HELP"]).starts_with("*14\r\n+OBJECT "));
2887 }
2888
2889 #[test]
2890 fn object_answers_nil_for_a_key_that_is_not_there() {
2891 let mut f = Fixture::new();
2892 for sub in [b"ENCODING".as_slice(), b"REFCOUNT", b"IDLETIME", b"FREQ"] {
2893 assert_eq!(
2894 f.run(&[b"OBJECT", sub, b"nokey"]),
2895 "$-1\r\n",
2896 "a nil and not an error, which is what 8.10.1 does"
2897 );
2898 }
2899 f.run(&[b"SET", b"s", b"v"]);
2902 assert!(
2903 f.run(&[b"OBJECT", b"FREQ", b"s"])
2904 .starts_with("-ERR An LFU maxmemory policy is not"),
2905 );
2906 assert_eq!(
2907 f.run(&[b"OBJECT", b"NOPE", b"s"]),
2908 "-ERR unknown subcommand 'NOPE'. Try OBJECT HELP.\r\n"
2909 );
2910 assert_eq!(
2911 f.run(&[b"OBJECT", b"ENCODING"]),
2912 "-ERR wrong number of arguments for 'object|encoding' command\r\n"
2913 );
2914 assert_eq!(
2915 f.run(&[b"OBJECT", b"ENCODING", b"s", b"extra"]),
2916 "-ERR wrong number of arguments for 'object|encoding' command\r\n"
2917 );
2918 assert_eq!(
2919 f.run(&[b"OBJECT"]),
2920 "-ERR wrong number of arguments for 'object' command\r\n"
2921 );
2922 }
2923
2924 #[test]
2925 fn config_moves_the_ladder_and_object_encoding_agrees() {
2926 let mut f = Fixture::new();
2927 assert_eq!(
2928 f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
2929 "*2\r\n$25\r\nhash-max-listpack-entries\r\n$3\r\n512\r\n",
2930 "512 and not the 128 everyone remembers, which is what 8.10.1 says"
2931 );
2932 assert_eq!(
2935 f.run(&[b"CONFIG", b"GET", b"hash-max-ziplist-entries"]),
2936 "*2\r\n$24\r\nhash-max-ziplist-entries\r\n$3\r\n512\r\n"
2937 );
2938 assert!(
2939 f.run(&[b"CONFIG", b"GET", b"hash-max-*"])
2940 .starts_with("*8\r\n")
2941 );
2942 assert!(
2943 f.run(&[b"CONFIG", b"GET", b"set-max-*"])
2944 .starts_with("*6\r\n")
2945 );
2946
2947 f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2", b"c", b"3"]);
2948 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"h"]), "$8\r\nlistpack\r\n");
2949
2950 assert_eq!(
2951 f.run(&[b"CONFIG", b"SET", b"hash-max-ziplist-entries", b"2"]),
2952 "+OK\r\n",
2953 "written under the old name and read back under the new one"
2954 );
2955 assert_eq!(
2956 f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
2957 "*2\r\n$25\r\nhash-max-listpack-entries\r\n$1\r\n2\r\n"
2958 );
2959 assert_eq!(
2960 f.run(&[b"OBJECT", b"ENCODING", b"h"]),
2961 "$8\r\nlistpack\r\n",
2962 "the hash that already exists is left exactly where it was"
2963 );
2964 f.run(&[b"HSET", b"h2", b"a", b"1", b"b", b"2", b"c", b"3"]);
2965 assert_eq!(
2966 f.run(&[b"OBJECT", b"ENCODING", b"h2"]),
2967 "$9\r\nhashtable\r\n",
2968 "and the next one built goes straight to a table"
2969 );
2970
2971 f.run(&[b"CONFIG", b"SET", b"set-max-intset-entries", b"2"]);
2973 f.run(&[b"SADD", b"s", b"1", b"2", b"3"]);
2974 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"s"]), "$8\r\nlistpack\r\n");
2975 f.run(&[b"CONFIG", b"SET", b"set-max-listpack-value", b"2"]);
2976 f.run(&[b"SADD", b"s2", b"abcdefgh"]);
2977 assert_eq!(
2978 f.run(&[b"OBJECT", b"ENCODING", b"s2"]),
2979 "$9\r\nhashtable\r\n"
2980 );
2981 }
2982
2983 #[test]
2984 fn config_set_takes_all_of_the_ladder_or_none_of_it() {
2985 let mut f = Fixture::new();
2986 assert_eq!(
2987 f.run(&[
2988 b"CONFIG",
2989 b"SET",
2990 b"hash-max-listpack-entries",
2991 b"7",
2992 b"set-max-listpack-entries",
2993 b"abc"
2994 ]),
2995 "-ERR CONFIG SET failed (possibly related to argument 'set-max-listpack-entries') - argument couldn't be parsed into an integer\r\n"
2996 );
2997 assert_eq!(
2998 f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
2999 "*2\r\n$25\r\nhash-max-listpack-entries\r\n$3\r\n512\r\n",
3000 "the pair in front of the bad one did not go in"
3001 );
3002 assert_eq!(
3005 f.run(&[b"CONFIG", b"SET", b"hash-max-ziplist-entries", b"abc"]),
3006 "-ERR CONFIG SET failed (possibly related to argument 'hash-max-ziplist-entries') - argument couldn't be parsed into an integer\r\n"
3007 );
3008 assert_eq!(
3009 f.run(&[b"CONFIG", b"SET", b"set-max-intset-entries", b"-1"]),
3010 "-ERR CONFIG SET failed (possibly related to argument 'set-max-intset-entries') - argument must be between 0 and 9223372036854775807 inclusive\r\n"
3011 );
3012 assert_eq!(
3015 f.run(&[
3016 b"CONFIG",
3017 b"SET",
3018 b"set-max-intset-entries",
3019 b"99999999999999999999"
3020 ]),
3021 "-ERR CONFIG SET failed (possibly related to argument 'set-max-intset-entries') - argument couldn't be parsed into an integer\r\n"
3022 );
3023 assert_eq!(
3024 f.run(&[
3025 b"CONFIG",
3026 b"SET",
3027 b"set-max-intset-entries",
3028 b"9223372036854775807"
3029 ]),
3030 "+OK\r\n"
3031 );
3032 }
3033
3034 #[test]
3035 fn a_setting_moved_on_one_database_moved_on_all_of_them() {
3036 let mut f = Fixture::new();
3037 f.run(&[b"CONFIG", b"SET", b"hash-max-listpack-entries", b"1"]);
3038 f.run(&[b"SELECT", b"3"]);
3039 f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
3040 assert_eq!(
3041 f.run(&[b"OBJECT", b"ENCODING", b"h"]),
3042 "$9\r\nhashtable\r\n",
3043 "these are one server wide number in Redis, whatever a Keyspace carries"
3044 );
3045 }
3046
3047 #[test]
3048 fn info_reports_the_numbers_it_can_stand_behind() {
3049 let mut f = Fixture::new();
3050 f.run(&[b"MSET", b"a", b"1", b"b", b"2"]);
3051 let all = f.run(&[b"INFO"]);
3052 assert!(all.contains("redis_version:8.8.0"), "{all}");
3053 assert!(
3054 all.contains(concat!("yo_version:", env!("CARGO_PKG_VERSION"))),
3055 "{all}"
3056 );
3057 assert!(all.contains("db0:keys=2,expires=0,avg_ttl=0"), "{all}");
3058 assert!(all.contains("role:master"), "{all}");
3059 let clients = f.run(&[b"INFO", b"clients"]);
3061 assert!(clients.contains("connected_clients:0"), "{clients}");
3062 assert!(!clients.contains("redis_version"), "{clients}");
3063 assert_eq!(f.run(&[b"INFO", b"nosuch"]), "$0\r\n\r\n");
3064 }
3065
3066 #[test]
3070 fn the_active_sweep_reclaims_keys_no_client_comes_back_for() {
3071 let mut f = Fixture::new();
3072 for i in 0..3_000u32 {
3073 f.run(&[b"SET", format!("d{i}").as_bytes(), b"v", b"PX", b"50"]);
3074 }
3075 for i in 0..1_000u32 {
3076 f.run(&[b"SET", format!("k{i}").as_bytes(), b"v"]);
3077 }
3078 assert_eq!(f.run(&[b"DBSIZE"]), ":4000\r\n");
3079 f.advance(100);
3080 assert_eq!(
3081 f.run(&[b"DBSIZE"]),
3082 ":4000\r\n",
3083 "DBSIZE counts records and nothing has read past the dead ones yet"
3084 );
3085
3086 let mut spent = 0;
3088 for _ in 0..2_000 {
3089 spent += f.server.expire_step(4096);
3090 if f.run(&[b"DBSIZE"]) == ":1000\r\n" {
3091 break;
3092 }
3093 }
3094 assert_eq!(f.run(&[b"DBSIZE"]), ":1000\r\n", "spent {spent} looks");
3095 assert!(f.run(&[b"INFO", b"stats"]).contains("expired_keys:3000"));
3096 for i in 0..1_000u32 {
3097 assert_eq!(
3098 f.run(&[b"GET", format!("k{i}").as_bytes()]),
3099 "$1\r\nv\r\n",
3100 "it took a key that had no deadline"
3101 );
3102 }
3103 }
3104
3105 #[test]
3106 fn a_sweep_of_a_server_with_no_deadlines_anywhere_costs_nothing() {
3107 let mut f = Fixture::new();
3108 for i in 0..2_000u32 {
3109 f.run(&[b"SET", format!("k{i}").as_bytes(), b"v"]);
3110 }
3111 assert_eq!(f.server.expire_step(4096), 0);
3112 f.run(&[b"SELECT", b"3"]);
3114 f.run(&[b"SET", b"x", b"v", b"PX", b"50"]);
3115 f.advance(100);
3116 for _ in 0..64 {
3117 f.server.expire_step(4096);
3118 }
3119 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
3120 f.run(&[b"SELECT", b"0"]);
3121 assert_eq!(f.run(&[b"DBSIZE"]), ":2000\r\n");
3122 assert_eq!(f.server.expire_step(4096), 0, "and it is quiet again");
3123 }
3124
3125 #[test]
3128 fn the_sweep_the_loop_calls_runs_at_most_once_a_millisecond() {
3129 let mut f = Fixture::new();
3130 for i in 0..500u32 {
3131 f.run(&[b"SET", format!("d{i}").as_bytes(), b"v", b"PX", b"50"]);
3132 }
3133 f.advance(100);
3134 let at = f.server.db(0).clock().now_ms();
3135 f.server.set_clock_ms(at);
3136 assert!(f.server.expire_slice(8) > 0, "the first one works");
3140 for _ in 0..1_000 {
3141 assert_eq!(
3142 f.server.expire_slice(8),
3143 0,
3144 "the millisecond has not moved and neither should this"
3145 );
3146 }
3147 assert!(
3148 f.server.db(0).expires() > 400,
3149 "there is plenty left to take"
3150 );
3151 f.server.set_clock_ms(at + 1);
3152 assert!(f.server.expire_slice(8) > 0, "and then it goes again");
3153 }
3154
3155 #[test]
3158 fn info_keyspace_counts_the_keys_that_have_a_deadline() {
3159 let mut f = Fixture::new();
3160 f.run(&[b"MSET", b"a", b"1", b"b", b"2", b"c", b"3"]);
3161 assert!(
3162 f.run(&[b"INFO", b"keyspace"])
3163 .contains("db0:keys=3,expires=0"),
3164 "none of them has one yet"
3165 );
3166 f.run(&[b"EXPIRE", b"a", b"1000"]);
3167 f.run(&[b"EXPIRE", b"b", b"1000"]);
3168 let two = f.run(&[b"INFO", b"keyspace"]);
3169 assert!(two.contains("db0:keys=3,expires=2"), "{two}");
3170 f.run(&[b"PERSIST", b"a"]);
3171 f.run(&[b"DEL", b"b"]);
3172 let none = f.run(&[b"INFO", b"keyspace"]);
3173 assert!(none.contains("db0:keys=2,expires=0"), "{none}");
3174
3175 f.run(&[b"SELECT", b"1"]);
3177 f.run(&[b"SET", b"x", b"1", b"EX", b"1000"]);
3178 let both = f.run(&[b"INFO", b"keyspace"]);
3179 assert!(both.contains("db0:keys=2,expires=0"), "{both}");
3180 assert!(both.contains("db1:keys=1,expires=1"), "{both}");
3181 }
3182
3183 #[cfg(unix)]
3184 #[test]
3185 fn info_cpu_reports_processor_time_that_was_really_measured() {
3186 let mut f = Fixture::new();
3187 let cpu = f.run(&[b"INFO", b"cpu"]);
3188 assert!(cpu.contains("# CPU"), "{cpu}");
3189 assert!(cpu.contains("used_cpu_user:"), "{cpu}");
3191 assert!(cpu.contains("used_cpu_sys:"), "{cpu}");
3192 assert!(cpu.contains("used_cpu_user_children:0.000000"), "{cpu}");
3193 assert!(!cpu.contains("redis_version"), "{cpu}");
3194
3195 let before = used_cpu_user(&cpu);
3199 let mut n = 0u64;
3200 let mut rounds = 0;
3201 while used_cpu_user(&f.run(&[b"INFO", b"cpu"])) <= before {
3202 for i in 0..1_000_000u64 {
3203 n = n.wrapping_add(i.wrapping_mul(i));
3204 }
3205 rounds += 1;
3206 assert!(rounds < 1_000, "cpu time never moved, n is {n}");
3210 }
3211 }
3212
3213 #[cfg(unix)]
3215 fn used_cpu_user(info: &str) -> f64 {
3216 info.lines()
3217 .find_map(|l| l.strip_prefix("used_cpu_user:"))
3218 .expect("no used_cpu_user in the reply")
3219 .trim()
3220 .parse()
3221 .expect("used_cpu_user is not a number")
3222 }
3223
3224 #[test]
3230 fn a_command_that_fails_leaves_nothing_half_written() {
3231 let mut f = Fixture::new();
3232 let reply = f.run(&[b"SETRANGE", b"k", b"-1", b"x"]);
3233 assert_eq!(reply, "-ERR offset is out of range\r\n");
3234 assert!(!reply.contains(':'), "no integer went out in front of it");
3235 }
3236
3237 #[test]
3238 fn quit_answers_first_and_closes_after() {
3239 let mut f = Fixture::new();
3240 let (flow, reply) = f.flow(&[b"QUIT"]);
3241 assert_eq!(reply, "+OK\r\n");
3242 assert_eq!(flow, Flow::Close);
3243 }
3244
3245 #[test]
3246 fn the_command_counter_counts_every_command_including_the_bad_ones() {
3247 let mut f = Fixture::new();
3248 f.run(&[b"PING"]);
3249 f.run(&[b"NOPE"]);
3250 f.run(&[b"GET"]);
3251 assert_eq!(f.server.stats.commands, 3);
3252 }
3253
3254 #[test]
3255 fn a_set_goes_from_bytes_to_bytes() {
3256 let mut f = Fixture::new();
3257 assert_eq!(f.run(&[b"SADD", b"s", b"a", b"b", b"c"]), ":3\r\n");
3258 assert_eq!(f.run(&[b"SADD", b"s", b"b", b"d"]), ":1\r\n");
3259 assert_eq!(f.run(&[b"SCARD", b"s"]), ":4\r\n");
3260 assert_eq!(f.run(&[b"SISMEMBER", b"s", b"a"]), ":1\r\n");
3261 assert_eq!(f.run(&[b"SISMEMBER", b"s", b"z"]), ":0\r\n");
3262 assert_eq!(f.run(&[b"TYPE", b"s"]), "+set\r\n");
3263 assert_eq!(
3264 f.run(&[b"SMISMEMBER", b"s", b"a", b"z", b"d"]),
3265 "*3\r\n:1\r\n:0\r\n:1\r\n"
3266 );
3267 assert_eq!(f.run(&[b"SREM", b"s", b"a", b"z"]), ":1\r\n");
3268 assert_eq!(f.run(&[b"SCARD", b"s"]), ":3\r\n");
3269 }
3270
3271 #[test]
3272 fn a_set_command_at_a_key_that_is_not_there_answers_empty() {
3273 let mut f = Fixture::new();
3274 assert_eq!(f.run(&[b"SCARD", b"nope"]), ":0\r\n");
3275 assert_eq!(f.run(&[b"SISMEMBER", b"nope", b"a"]), ":0\r\n");
3276 assert_eq!(f.run(&[b"SREM", b"nope", b"a"]), ":0\r\n");
3277 assert_eq!(f.run(&[b"SMEMBERS", b"nope"]), "*0\r\n");
3278 assert_eq!(
3279 f.run(&[b"SMISMEMBER", b"nope", b"a", b"b"]),
3280 "*2\r\n:0\r\n:0\r\n"
3281 );
3282 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n", "and made nothing");
3283 }
3284
3285 #[test]
3286 fn smembers_answers_a_set_on_resp3_and_an_array_on_resp2() {
3287 let mut f = Fixture::new();
3291 f.run(&[b"SADD", b"s", b"one"]);
3292 assert_eq!(f.run(&[b"SMEMBERS", b"s"]), "*1\r\n$3\r\none\r\n");
3293
3294 f.run(&[b"HELLO", b"3"]);
3295 assert_eq!(f.run(&[b"SMEMBERS", b"s"]), "~1\r\n$3\r\none\r\n");
3296 }
3297
3298 #[test]
3299 fn an_integer_member_comes_back_as_the_digits_it_never_stored() {
3300 let mut f = Fixture::new();
3303 f.run(&[b"SADD", b"s", b"42"]);
3304 assert_eq!(f.run(&[b"SMEMBERS", b"s"]), "*1\r\n$2\r\n42\r\n");
3305 assert_eq!(f.run(&[b"SISMEMBER", b"s", b"42"]), ":1\r\n");
3306 assert_eq!(
3307 f.run(&[b"SISMEMBER", b"s", b"042"]),
3308 ":0\r\n",
3309 "the member is the bytes and not the number they parse to"
3310 );
3311 }
3312
3313 #[test]
3314 fn the_wrong_command_at_the_wrong_type_says_so_both_ways() {
3315 let mut f = Fixture::new();
3316 f.run(&[b"SET", b"str", b"v"]);
3317 f.run(&[b"SADD", b"set", b"a"]);
3318
3319 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
3320 assert_eq!(f.run(&[b"SADD", b"str", b"a"]), wrong);
3321 assert_eq!(f.run(&[b"SCARD", b"str"]), wrong);
3322 assert_eq!(f.run(&[b"SMEMBERS", b"str"]), wrong);
3323 assert_eq!(f.run(&[b"SMISMEMBER", b"str", b"a"]), wrong);
3324 assert_eq!(f.run(&[b"GET", b"set"]), wrong);
3325 assert_eq!(f.run(&[b"APPEND", b"set", b"x"]), wrong);
3326 assert_eq!(f.run(&[b"INCR", b"set"]), wrong);
3327 assert_eq!(f.run(&[b"STRLEN", b"set"]), wrong);
3328
3329 assert_eq!(
3332 f.run(&[b"MGET", b"str", b"set", b"nope"]),
3333 "*3\r\n$1\r\nv\r\n$-1\r\n$-1\r\n"
3334 );
3335 assert_eq!(f.run(&[b"SET", b"set", b"now a string"]), "+OK\r\n");
3337 assert_eq!(f.run(&[b"TYPE", b"set"]), "+string\r\n");
3338 }
3339
3340 #[test]
3341 fn a_wrongtype_leaves_nothing_half_written() {
3342 let mut f = Fixture::new();
3346 f.run(&[b"SET", b"k", b"v"]);
3347 let reply = f.run(&[b"SMISMEMBER", b"k", b"a", b"b"]);
3348 assert!(reply.starts_with("-WRONGTYPE"), "got {reply}");
3349 assert!(!reply.contains('*'), "an array header went out in front");
3350 }
3351
3352 #[test]
3353 fn emptying_a_set_takes_the_key_with_it() {
3354 let mut f = Fixture::new();
3355 f.run(&[b"SADD", b"s", b"a", b"b"]);
3356 assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
3357 assert_eq!(f.run(&[b"SREM", b"s", b"a", b"b"]), ":2\r\n");
3358 assert_eq!(f.run(&[b"EXISTS", b"s"]), ":0\r\n");
3359 assert_eq!(f.run(&[b"TYPE", b"s"]), "+none\r\n");
3360 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
3361 }
3362
3363 fn split_scan(reply: &str) -> (String, Vec<String>) {
3369 let mut lines = reply.split("\r\n");
3370 assert_eq!(lines.next(), Some("*2"), "got {reply}");
3371 lines.next().expect("the cursor header");
3372 let cursor = lines.next().expect("the cursor").to_owned();
3373 let header = lines.next().expect("the member header");
3374 let n: usize = header[1..].parse().expect("a member count");
3375 let mut members = Vec::with_capacity(n);
3376 for _ in 0..n {
3377 lines.next().expect("a member header");
3378 members.push(lines.next().expect("a member").to_owned());
3379 }
3380 (cursor, members)
3381 }
3382
3383 #[test]
3384 fn popping_takes_a_member_off_the_set_and_hands_it_back() {
3385 let mut f = Fixture::new();
3386 f.run(&[b"SADD", b"s", b"a", b"b", b"c", b"d"]);
3387
3388 let one = f.run(&[b"SPOP", b"s"]);
3389 assert!(
3390 ["$1\r\na\r\n", "$1\r\nb\r\n", "$1\r\nc\r\n", "$1\r\nd\r\n"].contains(&one.as_str()),
3391 "got {one}"
3392 );
3393 assert_eq!(f.run(&[b"SCARD", b"s"]), ":3\r\n");
3394
3395 let (_, rest) = ("", f.run(&[b"SPOP", b"s", b"3"]));
3397 assert!(rest.starts_with("*3\r\n"), "got {rest}");
3398 assert_eq!(f.run(&[b"EXISTS", b"s"]), ":0\r\n");
3399 assert_eq!(f.run(&[b"SPOP", b"s"]), "$-1\r\n");
3401 assert_eq!(f.run(&[b"SPOP", b"s", b"2"]), "*0\r\n");
3402 }
3403
3404 #[test]
3405 fn the_two_draws_disagree_about_the_reply_type_and_they_are_right_to() {
3406 let mut f = Fixture::new();
3411 f.run(&[b"HELLO", b"3"]);
3412 f.run(&[b"SADD", b"s", b"a", b"b", b"c"]);
3413
3414 assert!(f.run(&[b"SPOP", b"s", b"2"]).starts_with("~2\r\n"));
3415 assert!(f.run(&[b"SRANDMEMBER", b"s", b"1"]).starts_with("*1\r\n"));
3417
3418 f.run(&[b"SADD", b"one", b"z"]);
3422 assert_eq!(
3423 f.run(&[b"SRANDMEMBER", b"one", b"-3"]),
3424 "*3\r\n$1\r\nz\r\n$1\r\nz\r\n$1\r\nz\r\n"
3425 );
3426 }
3427
3428 #[test]
3429 fn drawing_a_member_removes_nothing_and_says_nil_at_a_missing_key() {
3430 let mut f = Fixture::new();
3431 f.run(&[b"SADD", b"s", b"only"]);
3432 assert_eq!(f.run(&[b"SRANDMEMBER", b"s"]), "$4\r\nonly\r\n");
3433 assert_eq!(f.run(&[b"SRANDMEMBER", b"s"]), "$4\r\nonly\r\n");
3434 assert_eq!(f.run(&[b"SCARD", b"s"]), ":1\r\n");
3435
3436 assert_eq!(f.run(&[b"SRANDMEMBER", b"nope"]), "$-1\r\n");
3437 assert_eq!(f.run(&[b"SRANDMEMBER", b"nope", b"3"]), "*0\r\n");
3440 assert_eq!(f.run(&[b"SRANDMEMBER", b"nope", b"-3"]), "*0\r\n");
3441 assert_eq!(f.run(&[b"SRANDMEMBER", b"s", b"9"]), "*1\r\n$4\r\nonly\r\n");
3443 }
3444
3445 #[test]
3446 fn a_pop_count_that_is_not_a_positive_number_says_so() {
3447 let mut f = Fixture::new();
3448 f.run(&[b"SADD", b"s", b"a"]);
3449 let bad = "-ERR value is out of range, must be positive\r\n";
3450 assert_eq!(f.run(&[b"SPOP", b"s", b"-1"]), bad);
3451 assert_eq!(f.run(&[b"SPOP", b"s", b"abc"]), bad);
3452 assert_eq!(f.run(&[b"SCARD", b"s"]), ":1\r\n", "and took nothing");
3453 assert_eq!(f.run(&[b"SPOP", b"s", b"0"]), "*0\r\n");
3455 assert_eq!(f.run(&[b"EXISTS", b"s"]), ":1\r\n");
3456 }
3457
3458 #[test]
3459 fn a_scan_walks_a_set_of_any_size_exactly_once() {
3460 let mut f = Fixture::new();
3461 let members: Vec<Vec<u8>> = (0..300).map(|i| format!("m{i}").into_bytes()).collect();
3462 let args: Vec<&[u8]> = [&b"SADD"[..], &b"s"[..]]
3463 .into_iter()
3464 .chain(members.iter().map(Vec::as_slice))
3465 .collect();
3466 f.run(&args);
3467
3468 let mut seen = Vec::new();
3469 let mut cursor = "0".to_owned();
3470 loop {
3471 let reply = f.run(&[b"SSCAN", b"s", cursor.as_bytes()]);
3472 let (next, got) = split_scan(&reply);
3473 seen.extend(got);
3474 cursor = next;
3475 if cursor == "0" {
3476 break;
3477 }
3478 }
3479 seen.sort();
3480 seen.dedup();
3481 assert_eq!(seen.len(), 300, "a walk saw a member twice or missed one");
3482
3483 f.run(&[b"SADD", b"small", b"a", b"b", b"c"]);
3486 let (cursor, got) = split_scan(&f.run(&[b"SSCAN", b"small", b"0", b"COUNT", b"1"]));
3487 assert_eq!(cursor, "0");
3488 assert_eq!(got.len(), 3);
3489 assert_eq!(f.run(&[b"SSCAN", b"nope", b"0"]), "*2\r\n$1\r\n0\r\n*0\r\n");
3491 }
3492
3493 #[test]
3494 fn a_scan_takes_match_and_count_and_refuses_anything_else() {
3495 let mut f = Fixture::new();
3496 f.run(&[b"SADD", b"s", b"aa", b"ab", b"ba", b"12", b"13"]);
3497
3498 let (_, got) = split_scan(&f.run(&[b"SSCAN", b"s", b"0", b"MATCH", b"a*"]));
3499 let mut got = got;
3500 got.sort();
3501 assert_eq!(got, ["aa", "ab"]);
3502
3503 let (_, got) = split_scan(&f.run(&[b"SSCAN", b"s", b"0", b"MATCH", b"1?"]));
3506 let mut got = got;
3507 got.sort();
3508 assert_eq!(got, ["12", "13"]);
3509
3510 assert_eq!(f.run(&[b"SSCAN", b"s", b"abc"]), "-ERR invalid cursor\r\n");
3511 assert_eq!(f.run(&[b"SSCAN", b"s", b"-1"]), "-ERR invalid cursor\r\n");
3512 assert_eq!(
3513 f.run(&[b"SSCAN", b"s", b"0", b"NOPE", b"1"]),
3514 "-ERR syntax error\r\n"
3515 );
3516 assert_eq!(
3519 f.run(&[b"SSCAN", b"s", b"0", b"COUNT", b"0"]),
3520 "-ERR syntax error\r\n"
3521 );
3522 }
3523
3524 #[test]
3525 fn moving_a_member_takes_it_off_one_set_and_puts_it_on_another() {
3526 let mut f = Fixture::new();
3527 f.run(&[b"SADD", b"src", b"a", b"b"]);
3528 f.run(&[b"SADD", b"dst", b"c"]);
3529
3530 assert_eq!(f.run(&[b"SMOVE", b"src", b"dst", b"a"]), ":1\r\n");
3531 assert_eq!(f.run(&[b"SISMEMBER", b"src", b"a"]), ":0\r\n");
3532 assert_eq!(f.run(&[b"SISMEMBER", b"dst", b"a"]), ":1\r\n");
3533 assert_eq!(f.run(&[b"SMOVE", b"src", b"dst", b"zz"]), ":0\r\n");
3535 assert_eq!(f.run(&[b"SCARD", b"dst"]), ":2\r\n");
3536
3537 assert_eq!(f.run(&[b"SMOVE", b"src", b"fresh", b"b"]), ":1\r\n");
3540 assert_eq!(f.run(&[b"EXISTS", b"src"]), ":0\r\n");
3541 assert_eq!(f.run(&[b"SMEMBERS", b"fresh"]), "*1\r\n$1\r\nb\r\n");
3542 }
3543
3544 #[test]
3545 fn moving_checks_the_types_in_the_order_redis_checks_them() {
3546 let mut f = Fixture::new();
3550 f.run(&[b"SET", b"str", b"v"]);
3551 f.run(&[b"SADD", b"set", b"a"]);
3552
3553 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
3554 assert_eq!(f.run(&[b"SMOVE", b"nope", b"str", b"a"]), ":0\r\n");
3555 assert_eq!(f.run(&[b"SMOVE", b"str", b"set", b"a"]), wrong);
3556 assert_eq!(f.run(&[b"SMOVE", b"set", b"str", b"a"]), wrong);
3557 assert_eq!(f.run(&[b"SPOP", b"str"]), wrong);
3558 assert_eq!(f.run(&[b"SRANDMEMBER", b"str"]), wrong);
3559 assert_eq!(f.run(&[b"SSCAN", b"str", b"0"]), wrong);
3560 assert_eq!(
3561 f.run(&[b"SISMEMBER", b"set", b"a"]),
3562 ":1\r\n",
3563 "and none of that moved anything"
3564 );
3565 }
3566
3567 #[test]
3568 fn a_scan_leaves_nothing_half_written_when_its_arguments_are_wrong() {
3569 let mut f = Fixture::new();
3572 f.run(&[b"SADD", b"s", b"a"]);
3573 for bad in [
3574 &[b"SSCAN".as_slice(), b"s", b"abc"][..],
3575 &[b"SSCAN".as_slice(), b"s", b"0", b"COUNT", b"nope"][..],
3576 &[b"SSCAN".as_slice(), b"s", b"0", b"MATCH"][..],
3577 ] {
3578 let reply = f.run(bad);
3579 assert!(reply.starts_with("-ERR"), "got {reply}");
3580 assert!(!reply.contains('*'), "an array header went out in front");
3581 }
3582 }
3583
3584 #[test]
3585 fn a_hash_writes_reads_and_deletes_its_fields() {
3586 let mut f = Fixture::new();
3587 assert_eq!(f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]), ":2\r\n");
3588 assert_eq!(f.run(&[b"HSET", b"h", b"a", b"9"]), ":0\r\n", "a was there");
3589 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n9\r\n");
3590 assert_eq!(f.run(&[b"HGET", b"h", b"nope"]), "$-1\r\n");
3591 assert_eq!(f.run(&[b"HGET", b"nokey", b"a"]), "$-1\r\n");
3592 assert_eq!(f.run(&[b"HLEN", b"h"]), ":2\r\n");
3593 assert_eq!(f.run(&[b"HEXISTS", b"h", b"a"]), ":1\r\n");
3594 assert_eq!(f.run(&[b"HEXISTS", b"h", b"nope"]), ":0\r\n");
3595 assert_eq!(f.run(&[b"HSTRLEN", b"h", b"a"]), ":1\r\n");
3596 assert_eq!(f.run(&[b"HSTRLEN", b"h", b"nope"]), ":0\r\n");
3597
3598 assert_eq!(f.run(&[b"HGET", b"h", b"2"]), "$-1\r\n");
3601
3602 assert_eq!(f.run(&[b"HDEL", b"h", b"a", b"nope"]), ":1\r\n");
3603 assert_eq!(f.run(&[b"HDEL", b"h", b"b"]), ":1\r\n");
3604 assert_eq!(
3605 f.run(&[b"EXISTS", b"h"]),
3606 ":0\r\n",
3607 "and losing the last field lost the key"
3608 );
3609 }
3610
3611 #[test]
3612 fn hgetall_answers_a_map_on_resp3_and_the_same_pairs_flat_on_resp2() {
3613 let mut f = Fixture::new();
3614 f.run(&[b"HSET", b"h", b"a", b"1"]);
3615 assert_eq!(f.run(&[b"HGETALL", b"h"]), "*2\r\n$1\r\na\r\n$1\r\n1\r\n");
3616 assert_eq!(f.run(&[b"HGETALL", b"nokey"]), "*0\r\n");
3617 assert_eq!(f.run(&[b"HKEYS", b"h"]), "*1\r\n$1\r\na\r\n");
3618 assert_eq!(f.run(&[b"HVALS", b"h"]), "*1\r\n$1\r\n1\r\n");
3619 assert_eq!(f.run(&[b"HKEYS", b"nokey"]), "*0\r\n");
3620
3621 f.run(&[b"HELLO", b"3"]);
3622 assert_eq!(f.run(&[b"HGETALL", b"h"]), "%1\r\n$1\r\na\r\n$1\r\n1\r\n");
3623 assert_eq!(
3624 f.run(&[b"HGETALL", b"nokey"]),
3625 "%0\r\n",
3626 "a missing key is the empty hash and never a nil"
3627 );
3628 assert_eq!(
3629 f.run(&[b"HKEYS", b"h"]),
3630 "*1\r\n$1\r\na\r\n",
3631 "and the two that answer one side stay arrays"
3632 );
3633 }
3634
3635 #[test]
3636 fn hmget_answers_once_per_field_and_hmset_answers_ok() {
3637 let mut f = Fixture::new();
3638 assert_eq!(f.run(&[b"HMSET", b"h", b"a", b"1", b"c", b"3"]), "+OK\r\n");
3639 assert_eq!(
3640 f.run(&[b"HMGET", b"h", b"a", b"b", b"c"]),
3641 "*3\r\n$1\r\n1\r\n$-1\r\n$1\r\n3\r\n",
3642 "the reply is positional, so b is a nil and not a gap"
3643 );
3644 assert_eq!(
3645 f.run(&[b"HMGET", b"nokey", b"a", b"b"]),
3646 "*2\r\n$-1\r\n$-1\r\n",
3647 "and a missing key is all nils rather than an empty array"
3648 );
3649
3650 assert_eq!(f.run(&[b"HSETNX", b"h", b"a", b"9"]), ":0\r\n");
3651 assert_eq!(f.run(&[b"HSETNX", b"h", b"z", b"9"]), ":1\r\n");
3652 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
3653 }
3654
3655 #[test]
3656 fn a_hash_counts_up_and_says_so_when_it_cannot() {
3657 let mut f = Fixture::new();
3658 assert_eq!(f.run(&[b"HINCRBY", b"h", b"n", b"5"]), ":5\r\n");
3659 assert_eq!(f.run(&[b"HINCRBY", b"h", b"n", b"-7"]), ":-2\r\n");
3660 assert_eq!(f.run(&[b"HGET", b"h", b"n"]), "$2\r\n-2\r\n");
3661 assert_eq!(
3662 f.run(&[b"HINCRBYFLOAT", b"h", b"f", b"10.5"]),
3663 "$4\r\n10.5\r\n",
3664 "a bulk string and not a double, on both protocols"
3665 );
3666
3667 f.run(&[b"HSET", b"h", b"s", b"words"]);
3668 let bad = f.run(&[b"HINCRBY", b"h", b"s", b"1"]);
3669 assert!(
3670 bad.starts_with("-ERR hash value is not an integer"),
3671 "{bad}"
3672 );
3673 let bad = f.run(&[b"HINCRBY", b"h", b"n", b"nope"]);
3674 assert!(
3675 bad.starts_with("-ERR value is not an integer"),
3676 "a bad argument is not yet a hash value, {bad}"
3677 );
3678 assert_eq!(
3679 f.run(&[b"HGET", b"h", b"s"]),
3680 "$5\r\nwords\r\n",
3681 "and neither of them wrote anything"
3682 );
3683 }
3684
3685 #[test]
3686 fn a_hash_scan_walks_every_pair_once_and_novalues_drops_half_of_it() {
3687 let mut f = Fixture::new();
3688 for i in 0..500 {
3689 let field = format!("field-{i}");
3690 let value = format!("value-{i}");
3691 f.run(&[b"HSET", b"h", field.as_bytes(), value.as_bytes()]);
3692 }
3693
3694 let mut seen: Vec<String> = Vec::new();
3695 let mut cursor = "0".to_owned();
3696 loop {
3697 let reply = f.run(&[b"HSCAN", b"h", cursor.as_bytes(), b"COUNT", b"32"]);
3698 let (next, items) = scan_reply(&reply);
3699 assert_eq!(items.len() % 2, 0, "a pair went out half written");
3700 for pair in items.chunks(2) {
3701 assert_eq!(
3702 pair[0].strip_prefix("field-"),
3703 pair[1].strip_prefix("value-"),
3704 "a field came back with someone else's value"
3705 );
3706 seen.push(pair[0].clone());
3707 }
3708 cursor = next;
3709 if cursor == "0" {
3710 break;
3711 }
3712 }
3713 seen.sort();
3714 seen.dedup();
3715 assert_eq!(seen.len(), 500, "every field once and only once");
3716
3717 let (_, items) = scan_reply(&f.run(&[b"HSCAN", b"h", b"0", b"NOVALUES", b"COUNT", b"32"]));
3718 assert!(
3719 items.iter().all(|s| s.starts_with("field-")),
3720 "NOVALUES still sent the values"
3721 );
3722
3723 let (_, one) = scan_reply(&f.run(&[
3724 b"HSCAN",
3725 b"h",
3726 b"0",
3727 b"MATCH",
3728 b"field-499",
3729 b"COUNT",
3730 b"1000",
3731 ]));
3732 assert_eq!(one, ["field-499", "value-499"], "MATCH is on the field");
3733 }
3734
3735 #[test]
3736 fn hrandfield_draws_what_it_was_asked_for_and_nests_values_on_resp3() {
3737 let mut f = Fixture::new();
3738 f.run(&[b"HSET", b"h", b"a", b"1"]);
3739 assert_eq!(f.run(&[b"HRANDFIELD", b"h"]), "$1\r\na\r\n");
3740 assert_eq!(f.run(&[b"HRANDFIELD", b"nokey"]), "$-1\r\n");
3741 assert_eq!(f.run(&[b"HRANDFIELD", b"nokey", b"3"]), "*0\r\n");
3742 assert_eq!(
3743 f.run(&[b"HRANDFIELD", b"h", b"3"]),
3744 "*1\r\n$1\r\na\r\n",
3745 "a positive count is capped at the size of the hash"
3746 );
3747 assert_eq!(
3748 f.run(&[b"HRANDFIELD", b"h", b"-3"]),
3749 "*3\r\n$1\r\na\r\n$1\r\na\r\n$1\r\na\r\n",
3750 "and a negative one repeats itself"
3751 );
3752 assert_eq!(
3753 f.run(&[b"HRANDFIELD", b"h", b"1", b"WITHVALUES"]),
3754 "*2\r\n$1\r\na\r\n$1\r\n1\r\n",
3755 "flat on RESP2"
3756 );
3757
3758 f.run(&[b"HELLO", b"3"]);
3759 assert_eq!(
3760 f.run(&[b"HRANDFIELD", b"h", b"1", b"WITHVALUES"]),
3761 "*1\r\n*2\r\n$1\r\na\r\n$1\r\n1\r\n",
3762 "and nested on RESP3, but still an array and never a map"
3763 );
3764 }
3765
3766 #[test]
3767 fn every_hash_command_says_wrongtype_and_writes_nothing() {
3768 let mut f = Fixture::new();
3769 f.run(&[b"SET", b"str", b"v"]);
3770 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
3771
3772 for cmd in [
3773 &[b"HSET".as_slice(), b"str", b"f", b"v"][..],
3774 &[b"HMSET".as_slice(), b"str", b"f", b"v"][..],
3775 &[b"HSETNX".as_slice(), b"str", b"f", b"v"][..],
3776 &[b"HGET".as_slice(), b"str", b"f"][..],
3777 &[b"HMGET".as_slice(), b"str", b"f"][..],
3778 &[b"HDEL".as_slice(), b"str", b"f"][..],
3779 &[b"HLEN".as_slice(), b"str"][..],
3780 &[b"HEXISTS".as_slice(), b"str", b"f"][..],
3781 &[b"HSTRLEN".as_slice(), b"str", b"f"][..],
3782 &[b"HGETALL".as_slice(), b"str"][..],
3783 &[b"HKEYS".as_slice(), b"str"][..],
3784 &[b"HVALS".as_slice(), b"str"][..],
3785 &[b"HINCRBY".as_slice(), b"str", b"f", b"1"][..],
3786 &[b"HINCRBYFLOAT".as_slice(), b"str", b"f", b"1"][..],
3787 &[b"HRANDFIELD".as_slice(), b"str"][..],
3788 &[b"HRANDFIELD".as_slice(), b"str", b"2"][..],
3789 &[b"HSCAN".as_slice(), b"str", b"0"][..],
3790 ] {
3791 let reply = f.run(cmd);
3792 assert_eq!(reply, wrong, "{:?}", cmd[0]);
3793 }
3794 assert_eq!(
3795 f.run(&[b"GET", b"str"]),
3796 "$1\r\nv\r\n",
3797 "and none of them touched the value"
3798 );
3799 }
3800
3801 #[test]
3802 fn a_hash_scan_leaves_nothing_half_written_when_its_arguments_are_wrong() {
3803 let mut f = Fixture::new();
3804 f.run(&[b"HSET", b"h", b"f", b"v"]);
3805 for bad in [
3806 &[b"HSCAN".as_slice(), b"h", b"abc"][..],
3807 &[b"HSCAN".as_slice(), b"h", b"0", b"COUNT", b"nope"][..],
3808 &[b"HSCAN".as_slice(), b"h", b"0", b"COUNT", b"0"][..],
3809 &[b"HSCAN".as_slice(), b"h", b"0", b"MATCH"][..],
3810 ] {
3811 let reply = f.run(bad);
3812 assert!(reply.starts_with("-ERR"), "got {reply}");
3813 assert!(!reply.contains('*'), "an array header went out in front");
3814 }
3815 }
3816
3817 #[test]
3818 fn a_field_deadline_goes_on_and_comes_back_in_all_four_units() {
3819 let mut f = Fixture::new();
3820 f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
3821 assert_eq!(
3822 f.run(&[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"a"]),
3823 "*1\r\n:1\r\n"
3824 );
3825 assert_eq!(
3826 f.run(&[b"HTTL", b"h", b"FIELDS", b"3", b"a", b"b", b"nope"]),
3827 "*3\r\n:100\r\n:-1\r\n:-2\r\n",
3828 "one answer per field, and the two sentinels are TTL's own"
3829 );
3830
3831 let ms = int_reply(&f.run(&[b"HPTTL", b"h", b"FIELDS", b"1", b"a"]));
3834 assert!((99_000..=100_000).contains(&ms), "got {ms}");
3835 let at = int_reply(&f.run(&[b"HEXPIRETIME", b"h", b"FIELDS", b"1", b"a"]));
3836 let at_ms = int_reply(&f.run(&[b"HPEXPIRETIME", b"h", b"FIELDS", b"1", b"a"]));
3837 assert_eq!(at, at_ms.div_euclid(1000) + i64::from(at_ms % 1000 != 0));
3838 assert!(at_ms > 1_700_000_000_000, "an absolute moment, got {at_ms}");
3839
3840 assert_eq!(
3841 f.run(&[b"HPERSIST", b"h", b"FIELDS", b"3", b"a", b"b", b"nope"]),
3842 "*3\r\n:1\r\n:-1\r\n:-2\r\n",
3843 "one for the deadline taken off, and it does not say what it was"
3844 );
3845 assert_eq!(
3846 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
3847 "*1\r\n:-1\r\n"
3848 );
3849 assert_eq!(
3850 f.run(&[b"HGET", b"h", b"a"]),
3851 "$1\r\n1\r\n",
3852 "and the field is still there with the value it had"
3853 );
3854 }
3855
3856 #[test]
3857 fn a_deadline_that_has_already_gone_deletes_the_field_now() {
3858 let mut f = Fixture::new();
3859 f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
3860 assert_eq!(
3861 f.run(&[b"HEXPIREAT", b"h", b"1", b"FIELDS", b"1", b"a"]),
3862 "*1\r\n:2\r\n",
3863 "two, and not one, because nothing was stored"
3864 );
3865 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$-1\r\n");
3866 assert_eq!(f.run(&[b"HLEN", b"h"]), ":1\r\n");
3867
3868 assert_eq!(
3869 f.run(&[b"HPEXPIREAT", b"h", b"1", b"FIELDS", b"1", b"b"]),
3870 "*1\r\n:2\r\n"
3871 );
3872 assert_eq!(
3873 f.run(&[b"EXISTS", b"h"]),
3874 ":0\r\n",
3875 "and the last field going took the key with it"
3876 );
3877
3878 f.run(&[b"HSET", b"h", b"a", b"1"]);
3881 assert_eq!(
3882 f.run(&[b"HEXPIRE", b"h", b"0", b"FIELDS", b"1", b"a"]),
3883 "*1\r\n:2\r\n"
3884 );
3885 }
3886
3887 #[test]
3888 fn a_field_is_gone_once_its_moment_passes() {
3889 let mut f = Fixture::new();
3890 f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
3891 assert_eq!(
3892 f.run(&[b"HPEXPIRE", b"h", b"20", b"FIELDS", b"1", b"a"]),
3893 "*1\r\n:1\r\n"
3894 );
3895 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n", "not yet");
3896
3897 f.server.db(0).clock_mut().advance(60);
3901 assert_eq!(f.run(&[b"HLEN", b"h"]), ":1\r\n");
3902 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$-1\r\n");
3903 assert_eq!(
3904 f.run(&[b"HGETALL", b"h"]),
3905 "*2\r\n$1\r\nb\r\n$1\r\n2\r\n",
3906 "and the walks do not hand back a field that has expired"
3907 );
3908 }
3909
3910 #[test]
3911 fn a_missing_key_answers_the_no_field_sentinel_for_every_field() {
3912 let mut f = Fixture::new();
3913 for cmd in [
3914 &[
3915 b"HEXPIRE".as_slice(),
3916 b"nokey",
3917 b"100",
3918 b"FIELDS",
3919 b"2",
3920 b"a",
3921 b"b",
3922 ][..],
3923 &[b"HTTL".as_slice(), b"nokey", b"FIELDS", b"2", b"a", b"b"][..],
3924 &[b"HPTTL".as_slice(), b"nokey", b"FIELDS", b"2", b"a", b"b"][..],
3925 &[
3926 b"HEXPIRETIME".as_slice(),
3927 b"nokey",
3928 b"FIELDS",
3929 b"2",
3930 b"a",
3931 b"b",
3932 ][..],
3933 &[
3934 b"HPERSIST".as_slice(),
3935 b"nokey",
3936 b"FIELDS",
3937 b"2",
3938 b"a",
3939 b"b",
3940 ][..],
3941 ] {
3942 assert_eq!(f.run(cmd), "*2\r\n:-2\r\n:-2\r\n", "{:?}", cmd[0]);
3943 }
3944 }
3945
3946 #[test]
3947 fn writing_a_field_clears_the_deadline_that_was_on_it() {
3948 let mut f = Fixture::new();
3949 f.run(&[b"HSET", b"h", b"a", b"1"]);
3950 f.run(&[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"a"]);
3951 f.run(&[b"HSET", b"h", b"a", b"2"]);
3952 assert_eq!(
3953 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
3954 "*1\r\n:-1\r\n",
3955 "Redis has done this since 7.4, and it is why HGETEX exists"
3956 );
3957 }
3958
3959 #[test]
3960 fn the_four_conditions_reach_the_store_the_way_they_were_written() {
3961 let mut f = Fixture::new();
3962 f.run(&[b"HSET", b"h", b"a", b"1"]);
3963 assert_eq!(
3964 f.run(&[b"HEXPIRE", b"h", b"100", b"XX", b"FIELDS", b"1", b"a"]),
3965 "*1\r\n:0\r\n",
3966 "XX on a field with no deadline changes nothing"
3967 );
3968 assert_eq!(
3969 f.run(&[b"HEXPIRE", b"h", b"100", b"NX", b"FIELDS", b"1", b"a"]),
3970 "*1\r\n:1\r\n"
3971 );
3972 assert_eq!(
3973 f.run(&[b"HEXPIRE", b"h", b"200", b"NX", b"FIELDS", b"1", b"a"]),
3974 "*1\r\n:0\r\n",
3975 "and NX will not move one that is already there"
3976 );
3977 assert_eq!(
3978 f.run(&[b"HEXPIRE", b"h", b"50", b"GT", b"FIELDS", b"1", b"a"]),
3979 "*1\r\n:0\r\n"
3980 );
3981 assert_eq!(
3982 f.run(&[b"HEXPIRE", b"h", b"500", b"GT", b"FIELDS", b"1", b"a"]),
3983 "*1\r\n:1\r\n"
3984 );
3985 assert_eq!(
3986 f.run(&[b"HEXPIRE", b"h", b"50", b"LT", b"FIELDS", b"1", b"a"]),
3987 "*1\r\n:1\r\n"
3988 );
3989 assert_eq!(
3990 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
3991 "*1\r\n:50\r\n"
3992 );
3993 }
3994
3995 #[test]
3996 fn the_field_ttl_family_leaves_nothing_half_written_on_a_bad_argument() {
3997 let mut f = Fixture::new();
3998 f.run(&[b"HSET", b"h", b"a", b"1"]);
3999 for (bad, want) in [
4000 (
4001 &[b"HEXPIRE".as_slice(), b"h", b"-1", b"FIELDS", b"1", b"a"][..],
4002 "-ERR invalid expire time, must be >= 0",
4003 ),
4004 (
4005 &[
4006 b"HEXPIRE".as_slice(),
4007 b"h",
4008 b"9999999999999999",
4009 b"FIELDS",
4010 b"1",
4011 b"a",
4012 ][..],
4013 "-ERR invalid expire time in 'hexpire' command",
4014 ),
4015 (
4016 &[b"HEXPIRE".as_slice(), b"h", b"100", b"FIELD", b"1", b"a"][..],
4017 "-ERR wrong number of arguments for 'hexpire' command",
4018 ),
4019 (
4020 &[b"HEXPIRE".as_slice(), b"h", b"100", b"FIELDS", b"0", b"a"][..],
4021 "-ERR Parameter `numFields` should be greater than 0",
4022 ),
4023 (
4024 &[b"HEXPIRE".as_slice(), b"h", b"100", b"FIELDS", b"2", b"a"][..],
4025 "-ERR wrong number of arguments",
4026 ),
4027 (
4028 &[b"HTTL".as_slice(), b"h", b"FIELDS", b"3", b"a", b"b"][..],
4029 "-ERR wrong number of arguments",
4030 ),
4031 ] {
4032 let reply = f.run(bad);
4033 assert!(reply.starts_with(want), "wanted {want}, got {reply}");
4034 assert!(!reply.contains('*'), "an array header went out in front");
4035 }
4036 assert_eq!(
4037 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
4038 "*1\r\n:-1\r\n",
4039 "and not one of them put a deadline on anything"
4040 );
4041 }
4042
4043 #[test]
4044 fn every_field_ttl_command_says_wrongtype_and_writes_nothing() {
4045 let mut f = Fixture::new();
4046 f.run(&[b"SET", b"str", b"v"]);
4047 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
4048
4049 for cmd in [
4050 &[b"HEXPIRE".as_slice(), b"str", b"100", b"FIELDS", b"1", b"f"][..],
4051 &[
4052 b"HPEXPIRE".as_slice(),
4053 b"str",
4054 b"100",
4055 b"FIELDS",
4056 b"1",
4057 b"f",
4058 ][..],
4059 &[
4060 b"HEXPIREAT".as_slice(),
4061 b"str",
4062 b"9999999999",
4063 b"FIELDS",
4064 b"1",
4065 b"f",
4066 ][..],
4067 &[
4068 b"HPEXPIREAT".as_slice(),
4069 b"str",
4070 b"9999999999999",
4071 b"FIELDS",
4072 b"1",
4073 b"f",
4074 ][..],
4075 &[b"HTTL".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
4076 &[b"HPTTL".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
4077 &[b"HEXPIRETIME".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
4078 &[b"HPEXPIRETIME".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
4079 &[b"HPERSIST".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
4080 ] {
4081 assert_eq!(f.run(cmd), wrong, "{:?}", cmd[0]);
4082 }
4083 assert_eq!(
4084 f.run(&[b"GET", b"str"]),
4085 "$1\r\nv\r\n",
4086 "and none of them touched the value"
4087 );
4088 }
4089
4090 #[test]
4091 fn hgetdel_hands_the_value_out_and_then_takes_the_field() {
4092 let mut f = Fixture::new();
4093 f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
4094 assert_eq!(
4095 f.run(&[b"HGETDEL", b"h", b"FIELDS", b"2", b"a", b"nope"]),
4096 "*2\r\n$1\r\n1\r\n$-1\r\n",
4097 "positional, so the field that was not there is a nil in its place"
4098 );
4099 assert_eq!(f.run(&[b"HLEN", b"h"]), ":1\r\n");
4100 assert_eq!(
4101 f.run(&[b"HGETDEL", b"nokey", b"FIELDS", b"1", b"a"]),
4102 "*1\r\n$-1\r\n"
4103 );
4104 assert_eq!(
4105 f.run(&[b"HGETDEL", b"h", b"FIELDS", b"1", b"b"]),
4106 "*1\r\n$1\r\n2\r\n"
4107 );
4108 assert_eq!(
4109 f.run(&[b"EXISTS", b"h"]),
4110 ":0\r\n",
4111 "and the last field took the key"
4112 );
4113 }
4114
4115 #[test]
4116 fn hgetex_reads_and_moves_the_deadline_in_one_command() {
4117 let mut f = Fixture::new();
4118 f.run(&[b"HSET", b"h", b"a", b"1"]);
4119 assert_eq!(
4120 f.run(&[b"HGETEX", b"h", b"FIELDS", b"1", b"a"]),
4121 "*1\r\n$1\r\n1\r\n"
4122 );
4123 assert_eq!(
4124 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
4125 "*1\r\n:-1\r\n",
4126 "no option means leave it alone, which is the one place this is not GETEX"
4127 );
4128
4129 f.run(&[b"HGETEX", b"h", b"EX", b"100", b"FIELDS", b"1", b"a"]);
4130 assert_eq!(
4131 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
4132 "*1\r\n:100\r\n"
4133 );
4134 f.run(&[b"HGETEX", b"h", b"FIELDS", b"1", b"a"]);
4135 assert_eq!(
4136 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
4137 "*1\r\n:100\r\n",
4138 "and a plain read really does leave it alone"
4139 );
4140 assert_eq!(
4141 f.run(&[b"HGETEX", b"h", b"PERSIST", b"FIELDS", b"1", b"a"]),
4142 "*1\r\n$1\r\n1\r\n"
4143 );
4144 assert_eq!(
4145 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
4146 "*1\r\n:-1\r\n"
4147 );
4148
4149 assert_eq!(
4150 f.run(&[b"HGETEX", b"h", b"EXAT", b"1", b"FIELDS", b"1", b"a"]),
4151 "*1\r\n$1\r\n1\r\n",
4152 "the value goes out before the deadline that has already gone is applied"
4153 );
4154 assert_eq!(f.run(&[b"EXISTS", b"h"]), ":0\r\n");
4155 assert_eq!(
4156 f.run(&[b"HGETEX", b"nokey", b"EX", b"100", b"FIELDS", b"1", b"a"]),
4157 "*1\r\n$-1\r\n"
4158 );
4159 }
4160
4161 #[test]
4162 fn hsetex_writes_all_of_it_or_none_of_it() {
4163 let mut f = Fixture::new();
4164 assert_eq!(
4165 f.run(&[b"HSETEX", b"h", b"FIELDS", b"1", b"a", b"1"]),
4166 ":1\r\n"
4167 );
4168 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
4169 assert_eq!(
4170 f.run(&[
4171 b"HSETEX", b"h", b"FNX", b"FIELDS", b"2", b"a", b"9", b"new", b"9"
4172 ]),
4173 ":0\r\n",
4174 "FNX wants every field named to be missing"
4175 );
4176 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
4177 assert_eq!(
4178 f.run(&[b"HEXISTS", b"h", b"new"]),
4179 ":0\r\n",
4180 "and none of the list was written"
4181 );
4182 assert_eq!(
4183 f.run(&[
4184 b"HSETEX", b"h", b"FXX", b"FIELDS", b"2", b"a", b"9", b"nope", b"9"
4185 ]),
4186 ":0\r\n",
4187 "and FXX wants every one of them to be there"
4188 );
4189 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
4190 assert_eq!(
4191 f.run(&[b"HSETEX", b"h", b"FXX", b"FIELDS", b"1", b"a", b"9"]),
4192 ":1\r\n"
4193 );
4194 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n9\r\n");
4195
4196 assert_eq!(
4197 f.run(&[b"HSETEX", b"gone", b"FXX", b"FIELDS", b"1", b"a", b"1"]),
4198 ":0\r\n"
4199 );
4200 assert_eq!(
4201 f.run(&[b"EXISTS", b"gone"]),
4202 ":0\r\n",
4203 "a key with no fields cannot meet FXX and is not created trying"
4204 );
4205 }
4206
4207 #[test]
4208 fn hsetex_clears_the_deadline_unless_it_is_told_to_keep_it() {
4209 let mut f = Fixture::new();
4210 f.run(&[b"HSETEX", b"h", b"EX", b"100", b"FIELDS", b"1", b"a", b"1"]);
4211 assert_eq!(
4212 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
4213 "*1\r\n:100\r\n"
4214 );
4215
4216 f.run(&[b"HSETEX", b"h", b"KEEPTTL", b"FIELDS", b"1", b"a", b"2"]);
4217 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n2\r\n");
4218 assert_eq!(
4219 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
4220 "*1\r\n:100\r\n",
4221 "KEEPTTL put back what the write cleared"
4222 );
4223
4224 f.run(&[b"HSETEX", b"h", b"FIELDS", b"1", b"a", b"3"]);
4225 assert_eq!(
4226 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
4227 "*1\r\n:-1\r\n",
4228 "and without it a write clears the deadline the way HSET does"
4229 );
4230
4231 assert_eq!(
4234 f.run(&[
4235 b"HSETEX", b"h", b"PX", b"100000", b"FXX", b"FIELDS", b"1", b"a", b"4"
4236 ]),
4237 ":1\r\n"
4238 );
4239 assert_eq!(
4240 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
4241 "*1\r\n:100\r\n"
4242 );
4243
4244 assert_eq!(
4245 f.run(&[b"HSETEX", b"h", b"EXAT", b"1", b"FIELDS", b"1", b"a", b"5"]),
4246 ":1\r\n",
4247 "written, and not the separate code the HEXPIRE family has for this"
4248 );
4249 assert_eq!(
4250 f.run(&[b"EXISTS", b"h"]),
4251 ":0\r\n",
4252 "and storing it and then removing it emptied the hash"
4253 );
4254 }
4255
4256 #[test]
4257 fn the_last_three_hash_commands_word_their_mistakes_their_own_way() {
4258 let mut f = Fixture::new();
4259 f.run(&[b"HSET", b"h", b"a", b"1"]);
4260 for (bad, want) in [
4261 (
4263 &[b"HGETDEL".as_slice(), b"h", b"FIELDS", b"0", b"a"][..],
4264 "-ERR Number of fields must be a positive integer",
4265 ),
4266 (
4267 &[b"HGETDEL".as_slice(), b"h", b"FIELDS", b"2", b"a"][..],
4268 "-ERR The `numfields` parameter must match the number of arguments",
4269 ),
4270 (
4271 &[b"HGETDEL".as_slice(), b"h", b"FIELD", b"1", b"a"][..],
4272 "-ERR Mandatory argument FIELDS is missing or not at the right position",
4273 ),
4274 (
4276 &[b"HGETEX".as_slice(), b"h", b"FIELDS", b"0", b"a"][..],
4277 "-ERR invalid number of fields",
4278 ),
4279 (
4280 &[b"HGETEX".as_slice(), b"h", b"FIELDS", b"2", b"a"][..],
4281 "-ERR wrong number of arguments",
4282 ),
4283 (
4284 &[b"HGETEX".as_slice(), b"h", b"FIELD", b"1", b"a"][..],
4285 "-ERR unknown argument: FIELD",
4286 ),
4287 (
4288 &[
4289 b"HGETEX".as_slice(),
4290 b"h",
4291 b"KEEPTTL",
4292 b"FIELDS",
4293 b"1",
4294 b"a",
4295 ][..],
4296 "-ERR unknown argument: KEEPTTL",
4297 ),
4298 (
4299 &[
4300 b"HGETEX".as_slice(),
4301 b"h",
4302 b"EX",
4303 b"100",
4304 b"PERSIST",
4305 b"FIELDS",
4306 b"1",
4307 b"a",
4308 ][..],
4309 "-ERR Only one of EX, PX, EXAT, PXAT or PERSIST arguments can be specified",
4310 ),
4311 (
4312 &[
4313 b"HSETEX".as_slice(),
4314 b"h",
4315 b"EX",
4316 b"1",
4317 b"KEEPTTL",
4318 b"FIELDS",
4319 b"1",
4320 b"a",
4321 b"1",
4322 ][..],
4323 "-ERR Only one of EX, PX, EXAT, PXAT or KEEPTTL arguments can be specified",
4324 ),
4325 (
4326 &[
4327 b"HSETEX".as_slice(),
4328 b"h",
4329 b"FNX",
4330 b"FXX",
4331 b"FIELDS",
4332 b"1",
4333 b"a",
4334 b"1",
4335 ][..],
4336 "-ERR Only one of FXX or FNX arguments can be specified",
4337 ),
4338 (
4339 &[
4340 b"HSETEX".as_slice(),
4341 b"h",
4342 b"FIELDS",
4343 b"2",
4344 b"a",
4345 b"1",
4346 b"b",
4347 ][..],
4348 "-ERR wrong number of arguments",
4349 ),
4350 (
4351 &[
4352 b"HGETEX".as_slice(),
4353 b"h",
4354 b"EX",
4355 b"-1",
4356 b"FIELDS",
4357 b"1",
4358 b"a",
4359 ][..],
4360 "-ERR invalid expire time, must be >= 0",
4361 ),
4362 (
4363 &[
4364 b"HGETEX".as_slice(),
4365 b"h",
4366 b"PXAT",
4367 b"99999999999999",
4368 b"FIELDS",
4369 b"1",
4370 b"a",
4371 ][..],
4372 "-ERR invalid expire time in 'hgetex' command",
4373 ),
4374 (
4375 &[
4376 b"HSETEX".as_slice(),
4377 b"h",
4378 b"EX",
4379 b"abc",
4380 b"FIELDS",
4381 b"1",
4382 b"a",
4383 b"1",
4384 ][..],
4385 "-ERR value is not an integer or out of range",
4386 ),
4387 ] {
4388 let reply = f.run(bad);
4389 assert!(reply.starts_with(want), "wanted {want}, got {reply}");
4390 assert!(!reply.contains('*'), "an array header went out in front");
4391 }
4392 assert_eq!(
4393 f.run(&[b"HGET", b"h", b"a"]),
4394 "$1\r\n1\r\n",
4395 "and not one of them wrote anything"
4396 );
4397 assert_eq!(
4398 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
4399 "*1\r\n:-1\r\n"
4400 );
4401 }
4402
4403 #[test]
4404 fn the_last_three_hash_commands_say_wrongtype_and_write_nothing() {
4405 let mut f = Fixture::new();
4406 f.run(&[b"SET", b"str", b"v"]);
4407 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
4408 for cmd in [
4409 &[b"HGETDEL".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
4410 &[b"HGETEX".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
4411 &[
4412 b"HGETEX".as_slice(),
4413 b"str",
4414 b"EX",
4415 b"100",
4416 b"FIELDS",
4417 b"1",
4418 b"f",
4419 ][..],
4420 &[b"HSETEX".as_slice(), b"str", b"FIELDS", b"1", b"f", b"v"][..],
4421 ] {
4422 assert_eq!(f.run(cmd), wrong, "{:?}", cmd[0]);
4423 }
4424 assert_eq!(f.run(&[b"GET", b"str"]), "$1\r\nv\r\n");
4425 }
4426
4427 fn int(reply: &str) -> i64 {
4433 let body = reply
4434 .strip_prefix(':')
4435 .and_then(|s| s.strip_suffix("\r\n"))
4436 .unwrap_or_else(|| panic!("wanted an integer, got {reply}"));
4437 body.parse().expect("an integer")
4438 }
4439
4440 fn int_reply(reply: &str) -> i64 {
4441 let body = reply
4442 .strip_prefix("*1\r\n:")
4443 .and_then(|s| s.strip_suffix("\r\n"))
4444 .unwrap_or_else(|| panic!("wanted one integer, got {reply}"));
4445 body.parse().expect("an integer")
4446 }
4447
4448 fn scan_reply(reply: &str) -> (String, Vec<String>) {
4450 let mut lines = reply.split("\r\n");
4451 assert_eq!(lines.next(), Some("*2"), "got {reply}");
4452 lines.next().expect("the cursor header");
4453 let cursor = lines.next().expect("a cursor").to_owned();
4454 let header = lines.next().expect("an item count");
4455 let n: usize = header[1..].parse().expect("a count");
4456 let mut items = Vec::with_capacity(n);
4457 for _ in 0..n {
4458 lines.next().expect("an item header");
4459 items.push(lines.next().expect("an item").to_owned());
4460 }
4461 (cursor, items)
4462 }
4463
4464 fn sorted(reply: &str) -> Vec<String> {
4467 let mut lines = reply.split("\r\n");
4468 let header = lines.next().expect("a header");
4469 assert!(
4470 header.starts_with('*') || header.starts_with('~'),
4471 "got {reply}"
4472 );
4473 let n: usize = header[1..].parse().expect("a member count");
4474 let mut got = Vec::with_capacity(n);
4475 for _ in 0..n {
4476 lines.next().expect("a member header");
4477 got.push(lines.next().expect("a member").to_owned());
4478 }
4479 got.sort();
4480 got
4481 }
4482
4483 #[test]
4484 fn the_algebra_answers_what_the_sets_share_and_do_not() {
4485 let mut f = Fixture::new();
4486 f.run(&[b"SADD", b"a", b"1", b"2", b"3"]);
4487 f.run(&[b"SADD", b"b", b"2", b"3", b"4"]);
4488 f.run(&[b"SADD", b"c", b"3", b"4", b"5"]);
4489
4490 assert_eq!(sorted(&f.run(&[b"SINTER", b"a", b"b", b"c"])), ["3"]);
4491 assert_eq!(
4492 sorted(&f.run(&[b"SUNION", b"a", b"b", b"c"])),
4493 ["1", "2", "3", "4", "5"]
4494 );
4495 assert_eq!(sorted(&f.run(&[b"SDIFF", b"a", b"b"])), ["1"]);
4496 assert_eq!(sorted(&f.run(&[b"SINTER", b"a"])), ["1", "2", "3"]);
4497
4498 assert_eq!(f.run(&[b"SINTER", b"a", b"nope"]), "*0\r\n");
4501 assert_eq!(sorted(&f.run(&[b"SUNION", b"a", b"nope"])), ["1", "2", "3"]);
4502 assert_eq!(f.run(&[b"SDIFF", b"nope", b"a"]), "*0\r\n");
4503 assert_eq!(f.run(&[b"DBSIZE"]), ":3\r\n", "and none of it made a key");
4504 }
4505
4506 #[test]
4507 fn the_algebra_answers_a_set_on_resp3_and_an_array_on_resp2() {
4508 let mut f = Fixture::new();
4509 f.run(&[b"SADD", b"a", b"x"]);
4510 assert_eq!(f.run(&[b"SINTER", b"a"]), "*1\r\n$1\r\nx\r\n");
4511 assert_eq!(f.run(&[b"SUNION", b"a"]), "*1\r\n$1\r\nx\r\n");
4512 assert_eq!(f.run(&[b"SDIFF", b"a"]), "*1\r\n$1\r\nx\r\n");
4513
4514 f.run(&[b"HELLO", b"3"]);
4515 assert_eq!(f.run(&[b"SINTER", b"a"]), "~1\r\n$1\r\nx\r\n");
4516 assert_eq!(f.run(&[b"SUNION", b"a"]), "~1\r\n$1\r\nx\r\n");
4517 assert_eq!(f.run(&[b"SDIFF", b"a"]), "~1\r\n$1\r\nx\r\n");
4518 assert_eq!(f.run(&[b"SINTER", b"nope"]), "~0\r\n");
4519 }
4520
4521 #[test]
4522 fn a_store_form_writes_a_key_and_answers_how_big_it_is() {
4523 let mut f = Fixture::new();
4524 f.run(&[b"SADD", b"a", b"1", b"2", b"3"]);
4525 f.run(&[b"SADD", b"b", b"2", b"3", b"4"]);
4526
4527 assert_eq!(f.run(&[b"SINTERSTORE", b"d", b"a", b"b"]), ":2\r\n");
4528 assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"d"])), ["2", "3"]);
4529 assert_eq!(f.run(&[b"SUNIONSTORE", b"d", b"a", b"b"]), ":4\r\n");
4530 assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"d"])), ["1", "2", "3", "4"]);
4531 assert_eq!(f.run(&[b"SDIFFSTORE", b"d", b"a", b"b"]), ":1\r\n");
4532 assert_eq!(f.run(&[b"SMEMBERS", b"d"]), "*1\r\n$1\r\n1\r\n");
4533
4534 assert_eq!(f.run(&[b"SDIFFSTORE", b"d", b"a", b"a"]), ":0\r\n");
4537 assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
4538 assert_eq!(f.run(&[b"SINTERSTORE", b"a", b"a", b"b"]), ":2\r\n");
4539 assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"a"])), ["2", "3"]);
4540
4541 f.run(&[b"SET", b"str", b"v"]);
4544 assert_eq!(f.run(&[b"SUNIONSTORE", b"str", b"b"]), ":3\r\n");
4545 assert_eq!(f.run(&[b"TYPE", b"str"]), "+set\r\n");
4546 }
4547
4548 #[test]
4549 fn sintercard_counts_without_building_and_stops_at_a_limit() {
4550 let mut f = Fixture::new();
4551 f.run(&[b"SADD", b"a", b"1", b"2", b"3", b"4"]);
4552 f.run(&[b"SADD", b"b", b"2", b"3", b"4", b"5"]);
4553
4554 assert_eq!(f.run(&[b"SINTERCARD", b"2", b"a", b"b"]), ":3\r\n");
4555 assert_eq!(
4556 f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"LIMIT", b"2"]),
4557 ":2\r\n"
4558 );
4559 assert_eq!(
4560 f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"LIMIT", b"0"]),
4561 ":3\r\n",
4562 "a limit of zero is no limit"
4563 );
4564 assert_eq!(f.run(&[b"SINTERCARD", b"1", b"a"]), ":4\r\n");
4565 assert_eq!(f.run(&[b"SINTERCARD", b"2", b"a", b"nope"]), ":0\r\n");
4566
4567 assert_eq!(
4569 f.run(&[b"SINTERCARD", b"0", b"a"]),
4570 "-ERR numkeys should be greater than 0\r\n"
4571 );
4572 assert_eq!(
4573 f.run(&[b"SINTERCARD", b"abc", b"a"]),
4574 "-ERR numkeys should be greater than 0\r\n"
4575 );
4576 assert_eq!(
4577 f.run(&[b"SINTERCARD", b"3", b"a", b"b"]),
4578 "-ERR Number of keys can't be greater than number of args\r\n"
4579 );
4580 assert_eq!(
4581 f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"LIMIT", b"-1"]),
4582 "-ERR LIMIT can't be negative\r\n"
4583 );
4584 assert_eq!(
4585 f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"NOPE", b"1"]),
4586 "-ERR syntax error\r\n"
4587 );
4588 f.run(&[b"SADD", b"LIMIT", b"2"]);
4590 assert_eq!(f.run(&[b"SINTERCARD", b"2", b"a", b"LIMIT"]), ":1\r\n");
4591 }
4592
4593 #[test]
4594 fn the_algebra_answers_wrongtype_before_it_writes_anything() {
4595 let mut f = Fixture::new();
4596 f.run(&[b"SADD", b"a", b"1"]);
4597 f.run(&[b"SADD", b"d", b"old"]);
4598 f.run(&[b"SET", b"str", b"v"]);
4599
4600 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
4601 for bad in [
4602 &[b"SINTER".as_slice(), b"a", b"str"][..],
4603 &[b"SUNION".as_slice(), b"str"][..],
4604 &[b"SDIFF".as_slice(), b"a", b"str"][..],
4605 &[b"SINTERCARD".as_slice(), b"2", b"a", b"str"][..],
4606 &[b"SINTERSTORE".as_slice(), b"d", b"a", b"str"][..],
4607 &[b"SUNIONSTORE".as_slice(), b"d", b"str"][..],
4608 &[b"SDIFFSTORE".as_slice(), b"d", b"a", b"str"][..],
4609 ] {
4610 let reply = f.run(bad);
4611 assert_eq!(reply, wrong, "for {:?}", bad[0]);
4612 }
4613 assert_eq!(
4614 f.run(&[b"SMEMBERS", b"d"]),
4615 "*1\r\n$3\r\nold\r\n",
4616 "and the destination was left alone every time"
4617 );
4618 }
4619
4620 #[test]
4623 fn churning_sets_does_not_grow_the_server() {
4624 let mut f = Fixture::new();
4625 let members: Vec<Vec<u8>> = (0..200).map(|i| format!("m{i}").into_bytes()).collect();
4626 let args: Vec<&[u8]> = std::iter::once(&b"SADD"[..])
4627 .chain(std::iter::once(&b"s"[..]))
4628 .chain(members.iter().map(Vec::as_slice))
4629 .collect();
4630
4631 f.run(&args);
4632 f.run(&[b"DEL", b"s"]);
4633 f.server.compact_step();
4634 let after_first = f.server.memory_bytes();
4635
4636 for _ in 0..200 {
4637 f.run(&args);
4638 f.run(&[b"DEL", b"s"]);
4639 f.server.compact_step();
4640 }
4641 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
4642 assert!(
4643 f.server.memory_bytes() <= after_first * 2,
4644 "held {} after two hundred passes against {after_first} after one",
4645 f.server.memory_bytes()
4646 );
4647 }
4648
4649 fn bulks(parts: &[&str]) -> String {
4652 let mut s = format!("*{}\r\n", parts.len());
4653 for p in parts {
4654 s.push_str(&format!("${}\r\n{p}\r\n", p.len()));
4655 }
4656 s
4657 }
4658
4659 #[test]
4660 fn a_list_is_pushed_from_both_ends_and_the_left_one_reverses() {
4661 let mut f = Fixture::new();
4662 assert_eq!(f.run(&[b"LPUSH", b"k", b"a", b"b", b"c"]), ":3\r\n");
4666 assert_eq!(
4667 f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
4668 bulks(&["c", "b", "a"])
4669 );
4670 assert_eq!(f.run(&[b"RPUSH", b"k", b"d"]), ":4\r\n");
4671 assert_eq!(f.run(&[b"LLEN", b"k"]), ":4\r\n");
4672 assert_eq!(f.run(&[b"LPOP", b"k"]), "$1\r\nc\r\n");
4673 assert_eq!(f.run(&[b"RPOP", b"k"]), "$1\r\nd\r\n");
4674 assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["b", "a"]));
4675 assert_eq!(f.run(&[b"TYPE", b"k"]), "+list\r\n");
4676 }
4677
4678 #[test]
4679 fn the_x_pushes_refuse_to_bring_a_list_back_to_life() {
4680 let mut f = Fixture::new();
4681 assert_eq!(f.run(&[b"LPUSHX", b"k", b"a"]), ":0\r\n");
4682 assert_eq!(f.run(&[b"RPUSHX", b"k", b"a"]), ":0\r\n");
4683 assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
4684 f.run(&[b"RPUSH", b"k", b"a"]);
4685 assert_eq!(f.run(&[b"LPUSHX", b"k", b"z"]), ":2\r\n");
4686 assert_eq!(f.run(&[b"RPUSHX", b"k", b"y"]), ":3\r\n");
4687 assert_eq!(
4688 f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
4689 bulks(&["z", "a", "y"])
4690 );
4691 }
4692
4693 #[test]
4696 fn an_empty_pop_is_a_different_nothing_with_a_count_and_without() {
4697 let mut f = Fixture::new();
4698 assert_eq!(f.run(&[b"LPOP", b"nope"]), "$-1\r\n");
4699 assert_eq!(f.run(&[b"LPOP", b"nope", b"2"]), "*-1\r\n");
4700 assert_eq!(f.run(&[b"RPOP", b"nope"]), "$-1\r\n");
4701 assert_eq!(f.run(&[b"RPOP", b"nope", b"2"]), "*-1\r\n");
4702 f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
4703 assert_eq!(f.run(&[b"LPOP", b"k", b"0"]), "*0\r\n");
4706 assert_eq!(f.run(&[b"LPOP", b"k", b"1"]), bulks(&["a"]));
4707 assert_eq!(f.run(&[b"RPOP", b"k", b"9"]), bulks(&["c", "b"]));
4709 assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
4710 }
4711
4712 #[test]
4713 fn a_pop_count_has_its_own_sentence_and_a_third_argument_is_an_arity_error() {
4714 let mut f = Fixture::new();
4715 f.run(&[b"RPUSH", b"k", b"a"]);
4716 let range = "-ERR value is out of range, must be positive\r\n";
4717 assert_eq!(f.run(&[b"LPOP", b"k", b"-1"]), range);
4718 assert_eq!(f.run(&[b"LPOP", b"k", b"abc"]), range);
4719 assert_eq!(f.run(&[b"RPOP", b"k", b"-1"]), range);
4720 assert_eq!(
4723 f.run(&[b"LPOP", b"k", b"1", b"2"]),
4724 "-ERR wrong number of arguments for 'lpop' command\r\n"
4725 );
4726 assert_eq!(f.run(&[b"LLEN", b"k"]), ":1\r\n");
4727 }
4728
4729 #[test]
4730 fn a_range_takes_negative_ends_and_clamps_the_ones_that_run_off() {
4731 let mut f = Fixture::new();
4732 f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
4733 assert_eq!(
4734 f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
4735 bulks(&["a", "b", "c"])
4736 );
4737 assert_eq!(f.run(&[b"LRANGE", b"k", b"-2", b"-1"]), bulks(&["b", "c"]));
4738 assert_eq!(f.run(&[b"LRANGE", b"k", b"1", b"1"]), bulks(&["b"]));
4739 assert_eq!(f.run(&[b"LRANGE", b"k", b"5", b"10"]), "*0\r\n");
4740 assert_eq!(f.run(&[b"LRANGE", b"k", b"2", b"1"]), "*0\r\n");
4741 assert_eq!(
4742 f.run(&[b"LRANGE", b"k", b"-100", b"100"]),
4743 bulks(&["a", "b", "c"])
4744 );
4745 assert_eq!(f.run(&[b"LRANGE", b"nope", b"0", b"-1"]), "*0\r\n");
4748 assert_eq!(
4749 f.run(&[b"LRANGE", b"k", b"a", b"b"]),
4750 "-ERR value is not an integer or out of range\r\n"
4751 );
4752 }
4753
4754 #[test]
4755 fn an_index_reads_and_writes_from_whichever_end_is_nearer() {
4756 let mut f = Fixture::new();
4757 f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
4758 assert_eq!(f.run(&[b"LINDEX", b"k", b"0"]), "$1\r\na\r\n");
4759 assert_eq!(f.run(&[b"LINDEX", b"k", b"-1"]), "$1\r\nc\r\n");
4760 assert_eq!(f.run(&[b"LINDEX", b"k", b"99"]), "$-1\r\n");
4761 assert_eq!(f.run(&[b"LINDEX", b"nope", b"0"]), "$-1\r\n");
4762 assert_eq!(f.run(&[b"LSET", b"k", b"-1", b"z"]), "+OK\r\n");
4763 assert_eq!(
4764 f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
4765 bulks(&["a", "b", "z"])
4766 );
4767 assert_eq!(
4770 f.run(&[b"LSET", b"k", b"99", b"z"]),
4771 "-ERR index out of range\r\n"
4772 );
4773 assert_eq!(
4774 f.run(&[b"LSET", b"nope", b"0", b"z"]),
4775 "-ERR no such key\r\n"
4776 );
4777 }
4778
4779 #[test]
4780 fn linsert_says_three_things_with_one_signed_number() {
4781 let mut f = Fixture::new();
4782 assert_eq!(
4785 f.run(&[b"LINSERT", b"nope", b"BEFORE", b"a", b"x"]),
4786 ":0\r\n"
4787 );
4788 f.run(&[b"RPUSH", b"k", b"a", b"b"]);
4789 assert_eq!(f.run(&[b"LINSERT", b"k", b"before", b"a", b"X"]), ":3\r\n");
4790 assert_eq!(f.run(&[b"LINSERT", b"k", b"AFTER", b"b", b"Y"]), ":4\r\n");
4791 assert_eq!(
4792 f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
4793 bulks(&["X", "a", "b", "Y"])
4794 );
4795 assert_eq!(
4796 f.run(&[b"LINSERT", b"k", b"BEFORE", b"zz", b"x"]),
4797 ":-1\r\n"
4798 );
4799 assert_eq!(
4800 f.run(&[b"LINSERT", b"k", b"SIDEWAYS", b"a", b"x"]),
4801 "-ERR syntax error\r\n"
4802 );
4803 }
4804
4805 #[test]
4806 fn lrem_counts_in_three_directions_and_takes_the_key_when_it_empties() {
4807 let mut f = Fixture::new();
4808 f.run(&[b"RPUSH", b"k", b"a", b"b", b"a", b"c", b"a"]);
4809 assert_eq!(f.run(&[b"LREM", b"k", b"2", b"a"]), ":2\r\n");
4810 assert_eq!(
4811 f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
4812 bulks(&["b", "c", "a"])
4813 );
4814 assert_eq!(f.run(&[b"LREM", b"k", b"-1", b"a"]), ":1\r\n");
4815 assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["b", "c"]));
4816 assert_eq!(f.run(&[b"LREM", b"k", b"0", b"b"]), ":1\r\n");
4817 assert_eq!(f.run(&[b"LREM", b"k", b"0", b"c"]), ":1\r\n");
4818 assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
4819 assert_eq!(f.run(&[b"LREM", b"nope", b"0", b"a"]), ":0\r\n");
4820 }
4821
4822 #[test]
4823 fn ltrim_keeps_a_window_and_an_empty_one_deletes_the_key() {
4824 let mut f = Fixture::new();
4825 f.run(&[b"RPUSH", b"k", b"a", b"b", b"c", b"d"]);
4826 assert_eq!(f.run(&[b"LTRIM", b"k", b"1", b"-2"]), "+OK\r\n");
4827 assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["b", "c"]));
4828 assert_eq!(f.run(&[b"LTRIM", b"k", b"1", b"0"]), "+OK\r\n");
4831 assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
4832 assert_eq!(f.run(&[b"LTRIM", b"nope", b"0", b"-1"]), "+OK\r\n");
4833 }
4834
4835 #[test]
4836 fn lpos_walks_from_either_end_and_stops_where_it_is_told() {
4837 let mut f = Fixture::new();
4838 f.run(&[b"RPUSH", b"p", b"a", b"b", b"c", b"a", b"b", b"c", b"a"]);
4839 assert_eq!(f.run(&[b"LPOS", b"p", b"a"]), ":0\r\n");
4840 assert_eq!(f.run(&[b"LPOS", b"p", b"a", b"RANK", b"-1"]), ":6\r\n");
4841 assert_eq!(f.run(&[b"LPOS", b"p", b"a", b"RANK", b"2"]), ":3\r\n");
4842 assert_eq!(
4843 f.run(&[b"LPOS", b"p", b"a", b"COUNT", b"2"]),
4844 "*2\r\n:0\r\n:3\r\n"
4845 );
4846 assert_eq!(
4847 f.run(&[b"LPOS", b"p", b"a", b"RANK", b"-1", b"COUNT", b"0"]),
4848 "*3\r\n:6\r\n:3\r\n:0\r\n"
4849 );
4850 assert_eq!(
4853 f.run(&[b"LPOS", b"p", b"a", b"COUNT", b"0", b"MAXLEN", b"3"]),
4854 "*1\r\n:0\r\n"
4855 );
4856 assert_eq!(f.run(&[b"LPOS", b"p", b"zz"]), "$-1\r\n");
4859 assert_eq!(f.run(&[b"LPOS", b"p", b"zz", b"COUNT", b"0"]), "*0\r\n");
4860 assert_eq!(f.run(&[b"LPOS", b"nope", b"a"]), "$-1\r\n");
4861 assert_eq!(f.run(&[b"LPOS", b"nope", b"a", b"COUNT", b"2"]), "*0\r\n");
4862 }
4863
4864 #[test]
4865 fn lpos_words_its_three_mistakes_the_way_redis_does() {
4866 let mut f = Fixture::new();
4867 f.run(&[b"RPUSH", b"p", b"a"]);
4868 assert_eq!(
4871 f.run(&[b"LPOS", b"p", b"a", b"RANK", b"0"]),
4872 "-ERR RANK can't be zero: use 1 to start from the first match, 2 from the second ... or use negative to start from the end of the list\r\n"
4873 );
4874 assert_eq!(
4875 f.run(&[b"LPOS", b"p", b"a", b"COUNT", b"-1"]),
4876 "-ERR COUNT can't be negative\r\n"
4877 );
4878 assert_eq!(
4879 f.run(&[b"LPOS", b"p", b"a", b"MAXLEN", b"-1"]),
4880 "-ERR MAXLEN can't be negative\r\n"
4881 );
4882 assert_eq!(
4883 f.run(&[b"LPOS", b"p", b"a", b"RANK"]),
4884 "-ERR syntax error\r\n"
4885 );
4886 assert_eq!(
4887 f.run(&[b"LPOS", b"p", b"a", b"FOO", b"1"]),
4888 "-ERR syntax error\r\n"
4889 );
4890 }
4891
4892 #[test]
4893 fn a_move_takes_from_one_end_and_gives_to_another_even_on_one_key() {
4894 let mut f = Fixture::new();
4895 f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
4896 assert_eq!(f.run(&[b"RPOPLPUSH", b"k", b"d"]), "$1\r\nc\r\n");
4897 assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["a", "b"]));
4898 assert_eq!(f.run(&[b"LRANGE", b"d", b"0", b"-1"]), bulks(&["c"]));
4899 assert_eq!(
4900 f.run(&[b"LMOVE", b"k", b"d", b"LEFT", b"RIGHT"]),
4901 "$1\r\na\r\n"
4902 );
4903 assert_eq!(f.run(&[b"LRANGE", b"d", b"0", b"-1"]), bulks(&["c", "a"]));
4904 f.run(&[b"DEL", b"r"]);
4907 f.run(&[b"RPUSH", b"r", b"1", b"2", b"3"]);
4908 assert_eq!(f.run(&[b"RPOPLPUSH", b"r", b"r"]), "$1\r\n3\r\n");
4909 assert_eq!(
4910 f.run(&[b"LRANGE", b"r", b"0", b"-1"]),
4911 bulks(&["3", "1", "2"])
4912 );
4913 assert_eq!(
4914 f.run(&[b"LMOVE", b"nope", b"d", b"LEFT", b"LEFT"]),
4915 "$-1\r\n"
4916 );
4917 assert_eq!(
4918 f.run(&[b"LMOVE", b"r", b"d", b"LEFT", b"SIDEWAYS"]),
4919 "-ERR syntax error\r\n"
4920 );
4921 }
4922
4923 #[test]
4924 fn a_move_checks_the_destination_before_it_takes_anything() {
4925 let mut f = Fixture::new();
4926 f.run(&[b"RPUSH", b"k", b"a", b"b"]);
4927 f.run(&[b"SET", b"str", b"v"]);
4928 assert_eq!(
4929 f.run(&[b"LMOVE", b"k", b"str", b"LEFT", b"LEFT"]),
4930 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
4931 );
4932 assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["a", "b"]));
4934 }
4935
4936 #[test]
4937 fn lmpop_answers_from_the_first_key_that_has_anything() {
4938 let mut f = Fixture::new();
4939 f.run(&[b"RPUSH", b"b", b"1", b"2", b"3"]);
4940 assert_eq!(
4943 f.run(&[b"LMPOP", b"2", b"a", b"b", b"LEFT", b"COUNT", b"2"]),
4944 "*2\r\n$1\r\nb\r\n*2\r\n$1\r\n1\r\n$1\r\n2\r\n"
4945 );
4946 assert_eq!(
4947 f.run(&[b"LMPOP", b"2", b"a", b"b", b"RIGHT"]),
4948 "*2\r\n$1\r\nb\r\n*1\r\n$1\r\n3\r\n"
4949 );
4950 assert_eq!(f.run(&[b"EXISTS", b"b"]), ":0\r\n");
4951 assert_eq!(f.run(&[b"LMPOP", b"2", b"a", b"b", b"LEFT"]), "*-1\r\n");
4954 }
4955
4956 #[test]
4957 fn lmpop_has_its_own_words_for_a_count_and_for_a_key_count() {
4958 let mut f = Fixture::new();
4959 f.run(&[b"RPUSH", b"k", b"a"]);
4960 assert_eq!(
4961 f.run(&[b"LMPOP", b"0", b"k", b"LEFT"]),
4962 "-ERR numkeys should be greater than 0\r\n"
4963 );
4964 assert_eq!(
4965 f.run(&[b"LMPOP", b"-1", b"k", b"LEFT"]),
4966 "-ERR numkeys should be greater than 0\r\n"
4967 );
4968 assert_eq!(
4969 f.run(&[b"LMPOP", b"1", b"k", b"LEFT", b"COUNT", b"0"]),
4970 "-ERR count should be greater than 0\r\n"
4971 );
4972 assert_eq!(
4975 f.run(&[b"LMPOP", b"3", b"k", b"LEFT"]),
4976 "-ERR syntax error\r\n"
4977 );
4978 assert_eq!(
4979 f.run(&[b"LMPOP", b"1", b"k", b"LEFT", b"COUNT", b"1", b"x"]),
4980 "-ERR syntax error\r\n"
4981 );
4982 assert_eq!(
4983 f.run(&[b"LMPOP", b"1", b"k", b"LEFT", b"FOO", b"1"]),
4984 "-ERR syntax error\r\n"
4985 );
4986 assert_eq!(
4987 f.run(&[b"LMPOP", b"1", b"k", b"SIDEWAYS"]),
4988 "-ERR syntax error\r\n"
4989 );
4990 assert_eq!(f.run(&[b"LLEN", b"k"]), ":1\r\n");
4991 }
4992
4993 #[test]
4994 fn every_list_command_says_wrongtype_and_writes_nothing() {
4995 let mut f = Fixture::new();
4996 f.run(&[b"SET", b"str", b"v"]);
4997 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
4998 for cmd in [
4999 &[b"LPUSH".as_slice(), b"str", b"a"][..],
5000 &[b"RPUSH", b"str", b"a"],
5001 &[b"LPUSHX", b"str", b"a"],
5002 &[b"RPUSHX", b"str", b"a"],
5003 &[b"LPOP", b"str"],
5004 &[b"LPOP", b"str", b"2"],
5005 &[b"RPOP", b"str"],
5006 &[b"LLEN", b"str"],
5007 &[b"LRANGE", b"str", b"0", b"-1"],
5008 &[b"LINDEX", b"str", b"0"],
5009 &[b"LSET", b"str", b"0", b"a"],
5010 &[b"LINSERT", b"str", b"BEFORE", b"a", b"b"],
5011 &[b"LREM", b"str", b"0", b"a"],
5012 &[b"LTRIM", b"str", b"0", b"-1"],
5013 &[b"LPOS", b"str", b"a"],
5014 &[b"LPOS", b"str", b"a", b"COUNT", b"0"],
5015 &[b"RPOPLPUSH", b"str", b"d"],
5016 &[b"LMOVE", b"str", b"d", b"LEFT", b"LEFT"],
5017 &[b"LMPOP", b"1", b"str", b"LEFT"],
5018 ] {
5019 assert_eq!(f.run(cmd), wrong, "{:?}", String::from_utf8_lossy(cmd[0]));
5020 }
5021 assert_eq!(f.run(&[b"GET", b"str"]), "$1\r\nv\r\n");
5022 assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
5023 }
5024
5025 #[test]
5029 fn a_timeout_has_three_ways_of_being_wrong() {
5030 let mut f = Fixture::new();
5031 let not_float = "-ERR timeout is not a float or out of range\r\n";
5032 let range = "-ERR timeout is out of range\r\n";
5033 for (bad, want) in [
5034 (&[b"BLPOP".as_slice(), b"k", b"abc"][..], not_float),
5035 (&[b"BLPOP", b"k", b"nan"], not_float),
5036 (&[b"BLPOP", b"k", b""], not_float),
5037 (&[b"BLPOP", b"k", b" 1"], not_float),
5040 (&[b"BLPOP", b"k", b"1 "], not_float),
5041 (&[b"BLPOP", b"k", b"-1"], "-ERR timeout is negative\r\n"),
5042 (&[b"BLPOP", b"k", b"-0.1"], "-ERR timeout is negative\r\n"),
5043 (&[b"BLPOP", b"k", b"1e400"], range),
5046 (&[b"BLPOP", b"k", b"inf"], range),
5047 (&[b"BLPOP", b"k", b"9999999999999999"], range),
5048 (&[b"BRPOP", b"k", b"abc"], not_float),
5049 (
5050 &[b"BLMOVE", b"a", b"b", b"LEFT", b"RIGHT", b"abc"],
5051 not_float,
5052 ),
5053 (
5054 &[b"BRPOPLPUSH", b"a", b"b", b"-1"],
5055 "-ERR timeout is negative\r\n",
5056 ),
5057 (&[b"BLMPOP", b"abc", b"1", b"k", b"LEFT"], not_float),
5058 ] {
5059 assert_eq!(f.run(bad), want, "for {bad:?}");
5060 }
5061 }
5062
5063 #[test]
5066 fn a_zero_timeout_waits_and_the_smallest_positive_one_does_not() {
5067 let mut f = Fixture::new();
5068 for timeout in [b"0".as_slice(), b"0.0", b"-0.0"] {
5069 let (flow, out) = f.flow(&[b"BLPOP", b"k", timeout]);
5070 assert_eq!(flow, Flow::Block, "for {timeout:?}");
5071 assert!(out.is_empty(), "for {timeout:?}");
5072 }
5073 let (flow, out) = f.flow(&[b"BLPOP", b"k", b"0.0000001"]);
5077 assert_eq!(flow, Flow::Block);
5078 assert!(out.is_empty());
5079 }
5080
5081 #[test]
5082 fn a_blocking_command_that_can_be_answered_answers_like_the_one_it_wraps() {
5083 let mut f = Fixture::new();
5084 f.run(&[b"RPUSH", b"L", b"a", b"b", b"c", b"d", b"e"]);
5085
5086 assert_eq!(
5089 f.flow(&[b"BLPOP", b"nope", b"L", b"0"]),
5090 (Flow::Continue, "*2\r\n$1\r\nL\r\n$1\r\na\r\n".to_owned())
5091 );
5092 assert_eq!(
5093 f.run(&[b"BRPOP", b"L", b"0"]),
5094 "*2\r\n$1\r\nL\r\n$1\r\ne\r\n"
5095 );
5096 assert_eq!(
5097 f.run(&[
5098 b"BLMPOP", b"0", b"2", b"nope", b"L", b"LEFT", b"COUNT", b"2"
5099 ]),
5100 "*2\r\n$1\r\nL\r\n*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
5101 );
5102 assert_eq!(
5103 f.run(&[b"BLMOVE", b"L", b"D", b"LEFT", b"RIGHT", b"0"]),
5104 "$1\r\nd\r\n"
5105 );
5106 assert_eq!(
5107 f.run(&[b"EXISTS", b"L"]),
5108 ":0\r\n",
5109 "and the key went with it"
5110 );
5111 assert_eq!(f.run(&[b"LRANGE", b"D", b"0", b"-1"]), "*1\r\n$1\r\nd\r\n");
5112 f.run(&[b"RPUSH", b"D", b"x"]);
5115 assert_eq!(f.run(&[b"BRPOPLPUSH", b"D", b"D", b"0"]), "$1\r\nx\r\n");
5116 assert_eq!(
5117 f.run(&[b"LRANGE", b"D", b"0", b"-1"]),
5118 "*2\r\n$1\r\nx\r\n$1\r\nd\r\n"
5119 );
5120 }
5121
5122 #[test]
5123 fn blmpop_reads_its_count_and_its_key_count_the_way_lmpop_does() {
5124 let mut f = Fixture::new();
5125 f.run(&[b"RPUSH", b"k", b"a"]);
5126 for (bad, want) in [
5127 (
5128 &[b"BLMPOP".as_slice(), b"0", b"0", b"k", b"LEFT"][..],
5129 "-ERR numkeys should be greater than 0\r\n",
5130 ),
5131 (
5132 &[b"BLMPOP", b"0", b"-1", b"k", b"LEFT"],
5133 "-ERR numkeys should be greater than 0\r\n",
5134 ),
5135 (
5138 &[b"BLMPOP", b"0", b"2", b"k", b"LEFT"],
5139 "-ERR syntax error\r\n",
5140 ),
5141 (
5142 &[b"BLMPOP", b"0", b"1", b"k", b"SIDEWAYS"],
5143 "-ERR syntax error\r\n",
5144 ),
5145 (
5146 &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT"],
5147 "-ERR syntax error\r\n",
5148 ),
5149 (
5150 &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT", b"2", b"x"],
5151 "-ERR syntax error\r\n",
5152 ),
5153 (
5156 &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT", b"0"],
5157 "-ERR count should be greater than 0\r\n",
5158 ),
5159 (
5160 &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT", b"abc"],
5161 "-ERR count should be greater than 0\r\n",
5162 ),
5163 ] {
5164 assert_eq!(f.run(bad), want, "for {bad:?}");
5165 }
5166 assert_eq!(f.run(&[b"LLEN", b"k"]), ":1\r\n", "and none of them popped");
5167 }
5168
5169 #[test]
5170 fn a_blocking_move_reads_its_directions_before_its_timeout() {
5171 let mut f = Fixture::new();
5172 assert_eq!(
5175 f.run(&[b"BLMOVE", b"a", b"b", b"UP", b"DOWN", b"abc"]),
5176 "-ERR syntax error\r\n"
5177 );
5178 assert_eq!(
5179 f.run(&[b"BLMOVE", b"a", b"b", b"LEFT", b"DOWN", b"0.05"]),
5180 "-ERR syntax error\r\n"
5181 );
5182 }
5183
5184 #[test]
5187 fn a_blocking_command_errors_on_a_wrong_type_rather_than_waiting_on_it() {
5188 let mut f = Fixture::new();
5189 f.run(&[b"SET", b"S", b"v"]);
5190 f.run(&[b"RPUSH", b"D", b"x"]);
5191 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
5192
5193 assert_eq!(f.run(&[b"BLPOP", b"S", b"0"]), wrong);
5194 assert_eq!(f.run(&[b"BLPOP", b"E", b"S", b"0"]), wrong);
5197 assert_eq!(f.run(&[b"BRPOP", b"S", b"0"]), wrong);
5198 assert_eq!(f.run(&[b"BLMPOP", b"0", b"1", b"S", b"LEFT"]), wrong);
5199 assert_eq!(f.run(&[b"BRPOPLPUSH", b"S", b"D", b"0"]), wrong);
5200 assert_eq!(f.run(&[b"BRPOPLPUSH", b"D", b"S", b"0"]), wrong);
5203 assert_eq!(f.run(&[b"LRANGE", b"D", b"0", b"-1"]), "*1\r\n$1\r\nx\r\n");
5204
5205 assert_eq!(
5209 f.flow(&[b"BLMOVE", b"E", b"S", b"LEFT", b"RIGHT", b"0.1"])
5210 .0,
5211 Flow::Block
5212 );
5213 }
5214
5215 #[test]
5219 fn churning_lists_does_not_grow_the_server() {
5220 let mut f = Fixture::new();
5221 let vals: Vec<Vec<u8>> = (0..200).map(|i| format!("v{i}").into_bytes()).collect();
5222 let args: Vec<&[u8]> = [&b"RPUSH"[..], &b"k"[..]]
5223 .into_iter()
5224 .chain(vals.iter().map(Vec::as_slice))
5225 .collect();
5226
5227 f.run(&args);
5228 f.run(&[b"DEL", b"k"]);
5229 f.server.compact_step();
5230 let after_first = f.server.memory_bytes();
5231
5232 for _ in 0..200 {
5233 f.run(&args);
5234 f.run(&[b"LTRIM", b"k", b"1", b"0"]);
5235 f.server.compact_step();
5236 }
5237 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
5238 assert!(
5239 f.server.memory_bytes() <= after_first * 2,
5240 "held {} after two hundred passes against {after_first} after one",
5241 f.server.memory_bytes()
5242 );
5243 }
5244
5245 #[test]
5248 fn a_sorted_set_takes_scores_and_gives_them_back() {
5249 let mut f = Fixture::new();
5250 assert_eq!(f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b"]), ":2\r\n");
5251 assert_eq!(f.run(&[b"ZADD", b"z", b"1", b"a", b"3", b"c"]), ":1\r\n");
5252 assert_eq!(f.run(&[b"ZCARD", b"z"]), ":3\r\n");
5253 assert_eq!(f.run(&[b"ZSCORE", b"z", b"b"]), "$1\r\n2\r\n");
5254 assert_eq!(f.run(&[b"ZSCORE", b"z", b"nope"]), "$-1\r\n");
5255 assert_eq!(f.run(&[b"ZSCORE", b"nokey", b"b"]), "$-1\r\n");
5256 assert_eq!(
5257 f.run(&[b"ZMSCORE", b"z", b"a", b"nope", b"c"]),
5258 "*3\r\n$1\r\n1\r\n$-1\r\n$1\r\n3\r\n"
5259 );
5260 assert_eq!(f.run(&[b"ZREM", b"z", b"a", b"nope"]), ":1\r\n");
5261 assert_eq!(f.run(&[b"ZCARD", b"z"]), ":2\r\n");
5262 assert_eq!(f.run(&[b"ZREM", b"z", b"b", b"c"]), ":2\r\n");
5264 assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
5265 }
5266
5267 #[test]
5268 fn a_score_is_a_double_on_resp3_and_digits_on_resp2() {
5269 let mut f = Fixture::new();
5270 f.run(&[b"ZADD", b"z", b"1.5", b"a", b"inf", b"b", b"-inf", b"c"]);
5271 assert_eq!(f.run(&[b"ZSCORE", b"z", b"a"]), "$3\r\n1.5\r\n");
5272 assert_eq!(f.run(&[b"ZSCORE", b"z", b"b"]), "$3\r\ninf\r\n");
5273 assert_eq!(f.run(&[b"ZSCORE", b"z", b"c"]), "$4\r\n-inf\r\n");
5274
5275 f.out = Out::new(Proto::Resp3);
5276 assert_eq!(f.run(&[b"ZSCORE", b"z", b"a"]), ",1.5\r\n");
5277 assert_eq!(f.run(&[b"ZSCORE", b"z", b"b"]), ",inf\r\n");
5278 assert_eq!(f.run(&[b"ZSCORE", b"z", b"c"]), ",-inf\r\n");
5279 assert_eq!(f.run(&[b"ZSCORE", b"z", b"nope"]), "_\r\n");
5280 }
5281
5282 #[test]
5283 fn the_zadd_options_gate_what_gets_written() {
5284 let mut f = Fixture::new();
5285 f.run(&[b"ZADD", b"z", b"5", b"a"]);
5286 assert_eq!(f.run(&[b"ZADD", b"z", b"NX", b"9", b"a"]), ":0\r\n");
5288 assert_eq!(f.run(&[b"ZSCORE", b"z", b"a"]), "$1\r\n5\r\n");
5289 assert_eq!(f.run(&[b"ZADD", b"z", b"XX", b"9", b"new"]), ":0\r\n");
5290 assert_eq!(f.run(&[b"EXISTS", b"z"]), ":1\r\n");
5291 assert_eq!(f.run(&[b"ZADD", b"z", b"GT", b"CH", b"3", b"a"]), ":0\r\n");
5293 assert_eq!(f.run(&[b"ZADD", b"z", b"GT", b"CH", b"7", b"a"]), ":1\r\n");
5294 assert_eq!(f.run(&[b"ZADD", b"z", b"LT", b"CH", b"9", b"a"]), ":0\r\n");
5295 assert_eq!(f.run(&[b"ZADD", b"z", b"1", b"a", b"1", b"b"]), ":1\r\n");
5297 assert_eq!(
5298 f.run(&[b"ZADD", b"z", b"CH", b"2", b"a", b"2", b"c"]),
5299 ":2\r\n"
5300 );
5301 }
5302
5303 #[test]
5304 fn zadd_incr_answers_a_score_or_nothing_at_all() {
5305 let mut f = Fixture::new();
5306 assert_eq!(f.run(&[b"ZADD", b"z", b"INCR", b"5", b"m"]), "$1\r\n5\r\n");
5307 assert_eq!(f.run(&[b"ZADD", b"z", b"INCR", b"2", b"m"]), "$1\r\n7\r\n");
5308 assert_eq!(
5311 f.run(&[b"ZADD", b"z", b"NX", b"INCR", b"2", b"m"]),
5312 "$-1\r\n"
5313 );
5314 assert_eq!(
5315 f.run(&[b"ZADD", b"z", b"XX", b"INCR", b"2", b"gone"]),
5316 "$-1\r\n"
5317 );
5318 assert_eq!(
5319 f.run(&[b"ZADD", b"z", b"GT", b"INCR", b"-1", b"m"]),
5320 "$-1\r\n"
5321 );
5322 assert_eq!(
5323 f.run(&[b"ZADD", b"z", b"GT", b"INCR", b"1", b"m"]),
5324 "$1\r\n8\r\n"
5325 );
5326 assert_eq!(f.run(&[b"ZINCRBY", b"z", b"2", b"m"]), "$2\r\n10\r\n");
5327 assert_eq!(f.run(&[b"ZINCRBY", b"z", b"1", b"fresh"]), "$1\r\n1\r\n");
5328 }
5329
5330 #[test]
5331 fn the_two_infinities_will_not_be_added_together() {
5332 let mut f = Fixture::new();
5333 f.run(&[b"ZADD", b"z", b"inf", b"m"]);
5334 let nan = "-ERR resulting score is not a number (NaN)\r\n";
5335 assert_eq!(f.run(&[b"ZINCRBY", b"z", b"-inf", b"m"]), nan);
5336 assert_eq!(f.run(&[b"ZADD", b"z", b"INCR", b"-inf", b"m"]), nan);
5337 assert_eq!(f.run(&[b"ZSCORE", b"z", b"m"]), "$3\r\ninf\r\n");
5338 assert_eq!(f.run(&[b"ZINCRBY", b"gone", b"1", b"m"]), "$1\r\n1\r\n");
5340 }
5341
5342 #[test]
5343 fn zadd_says_its_mistakes_the_way_redis_says_them() {
5344 let mut f = Fixture::new();
5345 assert_eq!(
5348 f.run(&[b"ZADD", b"z", b"NX", b"XX"]),
5349 "-ERR syntax error\r\n"
5350 );
5351 assert_eq!(
5352 f.run(&[b"ZADD", b"z", b"NX", b"XX", b"1", b"a"]),
5353 "-ERR XX and NX options at the same time are not compatible\r\n"
5354 );
5355 let gtlt = "-ERR GT, LT, and/or NX options at the same time are not compatible\r\n";
5356 assert_eq!(f.run(&[b"ZADD", b"z", b"NX", b"GT", b"1", b"a"]), gtlt);
5357 assert_eq!(f.run(&[b"ZADD", b"z", b"GT", b"LT", b"1", b"a"]), gtlt);
5358 assert_eq!(
5359 f.run(&[b"ZADD", b"z", b"INCR", b"1", b"a", b"2", b"b"]),
5360 "-ERR INCR option supports a single increment-element pair\r\n"
5361 );
5362 assert_eq!(
5364 f.run(&[b"ZADD", b"z", b"1", b"a", b"2"]),
5365 "-ERR syntax error\r\n"
5366 );
5367 assert_eq!(
5369 f.run(&[b"ZADD", b"z", b"1", b"a", b"nonsense", b"b"]),
5370 "-ERR value is not a valid float\r\n"
5371 );
5372 assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
5373 }
5374
5375 #[test]
5376 fn a_rank_says_where_a_member_sits_from_either_end() {
5377 let mut f = Fixture::new();
5378 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
5379 assert_eq!(f.run(&[b"ZRANK", b"z", b"a"]), ":0\r\n");
5380 assert_eq!(f.run(&[b"ZRANK", b"z", b"c"]), ":2\r\n");
5381 assert_eq!(f.run(&[b"ZREVRANK", b"z", b"c"]), ":0\r\n");
5382 assert_eq!(f.run(&[b"ZREVRANK", b"z", b"a"]), ":2\r\n");
5383 assert_eq!(
5385 f.run(&[b"ZRANK", b"z", b"b", b"WITHSCORE"]),
5386 "*2\r\n:1\r\n$1\r\n2\r\n"
5387 );
5388 assert_eq!(f.run(&[b"ZRANK", b"z", b"nope"]), "$-1\r\n");
5389 assert_eq!(f.run(&[b"ZRANK", b"z", b"nope", b"WITHSCORE"]), "*-1\r\n");
5390 assert_eq!(f.run(&[b"ZRANK", b"nokey", b"a", b"WITHSCORE"]), "*-1\r\n");
5391 assert_eq!(
5394 f.run(&[b"ZRANK", b"z", b"b", b"bogus"]),
5395 "-ERR syntax error\r\n"
5396 );
5397 assert_eq!(
5398 f.run(&[b"ZREVRANK", b"z", b"b", b"WITHSCORE", b"more"]),
5399 "-ERR wrong number of arguments for 'zrevrank' command\r\n"
5400 );
5401 }
5402
5403 #[test]
5404 fn the_two_counts_read_their_two_kinds_of_bound() {
5405 let mut f = Fixture::new();
5406 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
5407 assert_eq!(f.run(&[b"ZCOUNT", b"z", b"-inf", b"+inf"]), ":3\r\n");
5408 assert_eq!(f.run(&[b"ZCOUNT", b"z", b"2", b"3"]), ":2\r\n");
5409 assert_eq!(f.run(&[b"ZCOUNT", b"z", b"(1", b"3"]), ":2\r\n");
5410 assert_eq!(f.run(&[b"ZCOUNT", b"z", b"(1", b"(3"]), ":1\r\n");
5411 assert_eq!(f.run(&[b"ZCOUNT", b"nokey", b"-inf", b"+inf"]), ":0\r\n");
5412 assert_eq!(
5413 f.run(&[b"ZCOUNT", b"z", b"bogus", b"3"]),
5414 "-ERR min or max is not a float\r\n"
5415 );
5416
5417 f.run(&[b"ZADD", b"l", b"0", b"a", b"0", b"b", b"0", b"c"]);
5418 assert_eq!(f.run(&[b"ZLEXCOUNT", b"l", b"-", b"+"]), ":3\r\n");
5419 assert_eq!(f.run(&[b"ZLEXCOUNT", b"l", b"[a", b"(c"]), ":2\r\n");
5420 assert_eq!(f.run(&[b"ZLEXCOUNT", b"l", b"(a", b"+"]), ":2\r\n");
5421 assert_eq!(
5424 f.run(&[b"ZLEXCOUNT", b"l", b"a", b"c"]),
5425 "-ERR min or max not valid string range item\r\n"
5426 );
5427 }
5428
5429 #[test]
5435 fn one_range_command_selects_by_rank_or_score_or_name() {
5436 let mut f = Fixture::new();
5437 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
5438 assert_eq!(
5439 f.run(&[b"ZRANGE", b"z", b"0", b"-1"]),
5440 "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
5441 );
5442 assert_eq!(
5443 f.run(&[b"ZRANGE", b"z", b"-2", b"-1"]),
5444 "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
5445 );
5446 assert_eq!(f.run(&[b"ZRANGE", b"z", b"5", b"9"]), "*0\r\n");
5447 assert_eq!(f.run(&[b"ZRANGE", b"nokey", b"0", b"-1"]), "*0\r\n");
5448 assert_eq!(
5451 f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"REV"]),
5452 "*3\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\na\r\n"
5453 );
5454 assert_eq!(
5455 f.run(&[b"ZRANGE", b"z", b"(1", b"+inf", b"BYSCORE"]),
5456 "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
5457 );
5458 assert_eq!(
5461 f.run(&[b"ZRANGE", b"z", b"+inf", b"(1", b"BYSCORE", b"REV"]),
5462 "*2\r\n$1\r\nc\r\n$1\r\nb\r\n"
5463 );
5464 assert_eq!(
5465 f.run(&[b"ZRANGE", b"z", b"-", b"+", b"BYLEX"]),
5466 "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
5467 );
5468 assert_eq!(
5469 f.run(&[b"ZRANGE", b"z", b"+", b"-", b"BYLEX", b"REV"]),
5470 "*3\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\na\r\n"
5471 );
5472 }
5473
5474 #[test]
5477 fn the_older_range_spellings_name_their_high_end_first() {
5478 let mut f = Fixture::new();
5479 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
5480 assert_eq!(
5481 f.run(&[b"ZREVRANGE", b"z", b"0", b"-1"]),
5482 "*3\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\na\r\n"
5483 );
5484 assert_eq!(
5485 f.run(&[b"ZREVRANGE", b"z", b"0", b"0", b"WITHSCORES"]),
5486 "*2\r\n$1\r\nc\r\n$1\r\n3\r\n"
5487 );
5488 assert_eq!(
5489 f.run(&[b"ZRANGEBYSCORE", b"z", b"(1", b"3"]),
5490 "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
5491 );
5492 assert_eq!(
5493 f.run(&[b"ZREVRANGEBYSCORE", b"z", b"3", b"(1"]),
5494 "*2\r\n$1\r\nc\r\n$1\r\nb\r\n"
5495 );
5496 assert_eq!(f.run(&[b"ZREVRANGEBYSCORE", b"z", b"(1", b"3"]), "*0\r\n");
5500 assert_eq!(
5501 f.run(&[b"ZRANGEBYLEX", b"z", b"[a", b"(c"]),
5502 "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
5503 );
5504 assert_eq!(
5505 f.run(&[b"ZREVRANGEBYLEX", b"z", b"(c", b"[a"]),
5506 "*2\r\n$1\r\nb\r\n$1\r\na\r\n"
5507 );
5508 for cmd in [
5511 &[b"ZREVRANGE".as_slice(), b"z", b"0", b"-1", b"BYSCORE"][..],
5512 &[b"ZRANGEBYSCORE", b"z", b"1", b"3", b"REV"],
5513 &[b"ZRANGEBYLEX", b"z", b"[a", b"[c", b"BYLEX"],
5514 ] {
5515 assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{:?}", cmd[0]);
5516 }
5517 }
5518
5519 #[test]
5522 fn limit_and_withscores_are_read_by_all_of_them_and_refused_afterwards() {
5523 let mut f = Fixture::new();
5524 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
5525 assert_eq!(
5526 f.run(&[
5527 b"ZRANGE", b"z", b"-inf", b"+inf", b"BYSCORE", b"LIMIT", b"1", b"1"
5528 ]),
5529 "*1\r\n$1\r\nb\r\n"
5530 );
5531 assert_eq!(
5533 f.run(&[
5534 b"ZRANGE", b"z", b"-inf", b"+inf", b"BYSCORE", b"LIMIT", b"-1", b"2"
5535 ]),
5536 "*0\r\n"
5537 );
5538 assert_eq!(
5539 f.run(&[
5540 b"ZRANGE", b"z", b"-inf", b"+inf", b"BYSCORE", b"LIMIT", b"0", b"-1"
5541 ]),
5542 "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
5543 );
5544 let both = "*4\r\n$1\r\na\r\n$1\r\n1\r\n$1\r\nb\r\n$1\r\n2\r\n";
5546 assert_eq!(
5547 f.run(&[
5548 b"ZRANGEBYSCORE",
5549 b"z",
5550 b"1",
5551 b"3",
5552 b"WITHSCORES",
5553 b"LIMIT",
5554 b"0",
5555 b"2"
5556 ]),
5557 both
5558 );
5559 assert_eq!(
5560 f.run(&[
5561 b"ZRANGEBYSCORE",
5562 b"z",
5563 b"1",
5564 b"3",
5565 b"LIMIT",
5566 b"0",
5567 b"2",
5568 b"WITHSCORES"
5569 ]),
5570 both
5571 );
5572 let needs_by = "-ERR syntax error, LIMIT is only supported in combination with either BYSCORE or BYLEX\r\n";
5575 assert_eq!(
5576 f.run(&[
5577 b"ZREVRANGE",
5578 b"z",
5579 b"0",
5580 b"-1",
5581 b"WITHSCORES",
5582 b"LIMIT",
5583 b"0",
5584 b"1"
5585 ]),
5586 needs_by
5587 );
5588 assert_eq!(
5589 f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"LIMIT", b"0", b"1"]),
5590 needs_by
5591 );
5592 let not_bylex = "-ERR syntax error, WITHSCORES not supported in combination with BYLEX\r\n";
5593 assert_eq!(
5594 f.run(&[b"ZRANGE", b"z", b"-", b"+", b"BYLEX", b"WITHSCORES"]),
5595 not_bylex
5596 );
5597 assert_eq!(
5598 f.run(&[b"ZRANGEBYLEX", b"z", b"[a", b"[c", b"WITHSCORES"]),
5599 not_bylex
5600 );
5601 for cmd in [
5604 &[
5605 b"ZRANGE".as_slice(),
5606 b"z",
5607 b"0",
5608 b"-1",
5609 b"BYSCORE",
5610 b"BYLEX",
5611 ][..],
5612 &[b"ZRANGE", b"z", b"0", b"-1", b"junk"],
5613 &[b"ZRANGEBYSCORE", b"z", b"1", b"3", b"LIMIT", b"0"],
5614 ] {
5615 assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
5616 }
5617 assert_eq!(
5618 f.run(&[b"ZRANGEBYSCORE", b"z", b"bad", b"3"]),
5619 "-ERR min or max is not a float\r\n"
5620 );
5621 assert_eq!(
5622 f.run(&[b"ZRANGEBYLEX", b"z", b"a", b"[c"]),
5623 "-ERR min or max not valid string range item\r\n"
5624 );
5625 assert_eq!(
5626 f.run(&[b"ZRANGEBYSCORE", b"z", b"1", b"3", b"LIMIT", b"a", b"2"]),
5627 "-ERR value is not an integer or out of range\r\n"
5628 );
5629 }
5630
5631 #[test]
5634 fn withscores_nests_on_resp3_and_flattens_on_resp2() {
5635 let mut f = Fixture::new();
5636 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
5637 assert_eq!(
5638 f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"WITHSCORES"]),
5639 "*6\r\n$1\r\na\r\n$1\r\n1\r\n$1\r\nb\r\n$1\r\n2\r\n$1\r\nc\r\n$1\r\n3\r\n"
5640 );
5641 f.out = Out::new(Proto::Resp3);
5642 assert_eq!(
5643 f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"WITHSCORES"]),
5644 "*3\r\n*2\r\n$1\r\na\r\n,1\r\n*2\r\n$1\r\nb\r\n,2\r\n*2\r\n$1\r\nc\r\n,3\r\n"
5645 );
5646 assert_eq!(
5647 f.run(&[b"ZRANGE", b"z", b"0", b"-1"]),
5648 "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
5649 );
5650 }
5651
5652 #[test]
5654 fn a_range_store_writes_the_window_into_another_key() {
5655 let mut f = Fixture::new();
5656 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
5657 assert_eq!(f.run(&[b"ZRANGESTORE", b"d", b"z", b"0", b"-1"]), ":3\r\n");
5658 assert_eq!(f.run(&[b"ZRANGESTORE", b"d", b"z", b"5", b"9"]), ":0\r\n");
5661 assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
5662 assert_eq!(
5663 f.run(&[b"ZRANGESTORE", b"d", b"z", b"(1", b"+inf", b"BYSCORE"]),
5664 ":2\r\n"
5665 );
5666 assert_eq!(
5667 f.run(&[b"ZRANGE", b"d", b"0", b"-1", b"WITHSCORES"]),
5668 "*4\r\n$1\r\nb\r\n$1\r\n2\r\n$1\r\nc\r\n$1\r\n3\r\n"
5669 );
5670 assert_eq!(f.run(&[b"ZRANGESTORE", b"z", b"z", b"1", b"2"]), ":2\r\n");
5673 assert_eq!(
5674 f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"WITHSCORES"]),
5675 "*4\r\n$1\r\nb\r\n$1\r\n2\r\n$1\r\nc\r\n$1\r\n3\r\n"
5676 );
5677 assert_eq!(
5680 f.run(&[b"ZRANGESTORE", b"d", b"z", b"0", b"-1", b"WITHSCORES"]),
5681 "-ERR syntax error\r\n"
5682 );
5683 }
5684
5685 #[test]
5688 fn the_three_removals_share_their_window_with_the_reads() {
5689 let mut f = Fixture::new();
5690 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
5691 assert_eq!(f.run(&[b"ZREMRANGEBYRANK", b"z", b"0", b"0"]), ":1\r\n");
5692 assert_eq!(
5693 f.run(&[b"ZRANGE", b"z", b"0", b"-1"]),
5694 "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
5695 );
5696 assert_eq!(
5697 f.run(&[b"ZREMRANGEBYSCORE", b"z", b"(2", b"+inf"]),
5698 ":1\r\n"
5699 );
5700 assert_eq!(f.run(&[b"ZRANGE", b"z", b"0", b"-1"]), "*1\r\n$1\r\nb\r\n");
5701 assert_eq!(f.run(&[b"ZREMRANGEBYLEX", b"z", b"-", b"+"]), ":1\r\n");
5703 assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
5704 assert_eq!(
5705 f.run(&[b"ZREMRANGEBYRANK", b"nokey", b"0", b"-1"]),
5706 ":0\r\n"
5707 );
5708 assert_eq!(
5709 f.run(&[b"ZREMRANGEBYRANK", b"z", b"0", b"x"]),
5710 "-ERR value is not an integer or out of range\r\n"
5711 );
5712 }
5713
5714 #[test]
5716 fn the_three_algebra_commands_combine_scores_and_order_the_answer_once() {
5717 let mut f = Fixture::new();
5718 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
5719 f.run(&[b"ZADD", b"y", b"10", b"b", b"20", b"d"]);
5720 assert_eq!(
5721 f.run(&[b"ZUNION", b"2", b"z", b"y"]),
5722 "*4\r\n$1\r\na\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\nd\r\n"
5723 );
5724 assert_eq!(
5727 f.run(&[b"ZUNION", b"2", b"z", b"y", b"WITHSCORES"]),
5728 "*8\r\n$1\r\na\r\n$1\r\n1\r\n$1\r\nc\r\n$1\r\n3\r\n$1\r\nb\r\n$2\r\n12\r\n$1\r\nd\r\n$2\r\n20\r\n"
5729 );
5730 assert_eq!(
5731 f.run(&[
5732 b"ZUNION",
5733 b"2",
5734 b"z",
5735 b"y",
5736 b"WEIGHTS",
5737 b"2",
5738 b"3",
5739 b"WITHSCORES"
5740 ]),
5741 "*8\r\n$1\r\na\r\n$1\r\n2\r\n$1\r\nc\r\n$1\r\n6\r\n$1\r\nb\r\n$2\r\n34\r\n$1\r\nd\r\n$2\r\n60\r\n"
5742 );
5743 assert_eq!(
5744 f.run(&[
5745 b"ZUNION",
5746 b"2",
5747 b"z",
5748 b"y",
5749 b"AGGREGATE",
5750 b"MIN",
5751 b"WITHSCORES"
5752 ]),
5753 "*8\r\n$1\r\na\r\n$1\r\n1\r\n$1\r\nb\r\n$1\r\n2\r\n$1\r\nc\r\n$1\r\n3\r\n$1\r\nd\r\n$2\r\n20\r\n"
5754 );
5755 assert_eq!(
5756 f.run(&[
5757 b"ZUNION",
5758 b"2",
5759 b"z",
5760 b"y",
5761 b"AGGREGATE",
5762 b"MAX",
5763 b"WITHSCORES"
5764 ]),
5765 "*8\r\n$1\r\na\r\n$1\r\n1\r\n$1\r\nc\r\n$1\r\n3\r\n$1\r\nb\r\n$2\r\n10\r\n$1\r\nd\r\n$2\r\n20\r\n"
5766 );
5767 assert_eq!(
5768 f.run(&[b"ZINTER", b"2", b"z", b"y", b"WITHSCORES"]),
5769 "*2\r\n$1\r\nb\r\n$2\r\n12\r\n"
5770 );
5771 assert_eq!(
5772 f.run(&[b"ZDIFF", b"2", b"z", b"y", b"WITHSCORES"]),
5773 "*4\r\n$1\r\na\r\n$1\r\n1\r\n$1\r\nc\r\n$1\r\n3\r\n"
5774 );
5775 assert_eq!(f.run(&[b"ZUNION", b"1", b"nokey"]), "*0\r\n");
5776 f.run(&[b"SADD", b"p", b"a", b"d"]);
5779 assert_eq!(
5780 f.run(&[b"ZUNION", b"2", b"z", b"p", b"WITHSCORES"]),
5781 "*8\r\n$1\r\nd\r\n$1\r\n1\r\n$1\r\na\r\n$1\r\n2\r\n$1\r\nb\r\n$1\r\n2\r\n$1\r\nc\r\n$1\r\n3\r\n"
5782 );
5783 for cmd in [
5786 &[
5787 b"ZDIFF".as_slice(),
5788 b"2",
5789 b"z",
5790 b"y",
5791 b"WEIGHTS",
5792 b"1",
5793 b"1",
5794 ][..],
5795 &[b"ZDIFF", b"2", b"z", b"y", b"AGGREGATE", b"MIN"],
5796 ] {
5797 assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
5798 }
5799 }
5800
5801 #[test]
5803 fn the_algebra_counts_its_keys_and_says_so_when_the_count_is_wrong() {
5804 let mut f = Fixture::new();
5805 f.run(&[b"ZADD", b"z", b"1", b"a"]);
5806 f.run(&[b"ZADD", b"y", b"2", b"b"]);
5807 assert_eq!(
5809 f.run(&[b"ZUNION", b"0", b"z"]),
5810 "-ERR at least 1 input key is needed for 'zunion' command\r\n"
5811 );
5812 assert_eq!(
5813 f.run(&[b"ZUNION", b"-1", b"z"]),
5814 "-ERR at least 1 input key is needed for 'zunion' command\r\n"
5815 );
5816 assert_eq!(
5817 f.run(&[b"ZINTERCARD", b"0", b"z"]),
5818 "-ERR at least 1 input key is needed for 'zintercard' command\r\n"
5819 );
5820 assert_eq!(
5823 f.run(&[b"ZUNION", b"3", b"z", b"y"]),
5824 "-ERR syntax error\r\n"
5825 );
5826 assert_eq!(
5827 f.run(&[b"ZUNION", b"x", b"z"]),
5828 "-ERR value is not an integer or out of range\r\n"
5829 );
5830 assert_eq!(
5833 f.run(&[b"ZUNION", b"2", b"z", b"y", b"WEIGHTS", b"1"]),
5834 "-ERR syntax error\r\n"
5835 );
5836 assert_eq!(
5837 f.run(&[b"ZUNION", b"2", b"z", b"y", b"WEIGHTS", b"a", b"b"]),
5838 "-ERR weight value is not a float\r\n"
5839 );
5840 assert_eq!(
5841 f.run(&[b"ZUNION", b"2", b"z", b"y", b"AGGREGATE", b"NOPE"]),
5842 "-ERR syntax error\r\n"
5843 );
5844 }
5845
5846 #[test]
5848 fn the_algebra_stores_answer_a_count_and_delete_an_empty_destination() {
5849 let mut f = Fixture::new();
5850 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
5851 f.run(&[b"ZADD", b"y", b"10", b"b", b"20", b"d"]);
5852 assert_eq!(f.run(&[b"ZUNIONSTORE", b"d", b"2", b"z", b"y"]), ":4\r\n");
5853 assert_eq!(
5854 f.run(&[b"ZRANGE", b"d", b"0", b"-1", b"WITHSCORES"]),
5855 "*8\r\n$1\r\na\r\n$1\r\n1\r\n$1\r\nc\r\n$1\r\n3\r\n$1\r\nb\r\n$2\r\n12\r\n$1\r\nd\r\n$2\r\n20\r\n"
5856 );
5857 assert_eq!(f.run(&[b"ZINTERSTORE", b"d", b"2", b"z", b"y"]), ":1\r\n");
5858 assert_eq!(f.run(&[b"ZDIFFSTORE", b"d", b"2", b"z", b"y"]), ":2\r\n");
5859 assert_eq!(
5862 f.run(&[b"ZINTERSTORE", b"d", b"2", b"z", b"nokey"]),
5863 ":0\r\n"
5864 );
5865 assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
5866 assert_eq!(f.run(&[b"ZUNIONSTORE", b"z", b"2", b"z", b"y"]), ":4\r\n");
5868 assert_eq!(f.run(&[b"ZCARD", b"z"]), ":4\r\n");
5869 for cmd in [
5870 &[
5871 b"ZUNIONSTORE".as_slice(),
5872 b"d",
5873 b"2",
5874 b"z",
5875 b"y",
5876 b"WITHSCORES",
5877 ][..],
5878 &[
5879 b"ZDIFFSTORE",
5880 b"d",
5881 b"2",
5882 b"z",
5883 b"y",
5884 b"WEIGHTS",
5885 b"1",
5886 b"1",
5887 ],
5888 ] {
5889 assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
5890 }
5891 }
5892
5893 #[test]
5895 fn intercard_counts_and_stops_at_its_limit() {
5896 let mut f = Fixture::new();
5897 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
5898 f.run(&[b"ZADD", b"y", b"10", b"b", b"20", b"c", b"30", b"d"]);
5899 assert_eq!(f.run(&[b"ZINTERCARD", b"2", b"z", b"y"]), ":2\r\n");
5900 assert_eq!(
5902 f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"0"]),
5903 ":2\r\n"
5904 );
5905 assert_eq!(
5906 f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"1"]),
5907 ":1\r\n"
5908 );
5909 let bad = "-ERR LIMIT can't be negative\r\n";
5912 assert_eq!(
5913 f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"-1"]),
5914 bad
5915 );
5916 assert_eq!(
5917 f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"x"]),
5918 bad
5919 );
5920 for cmd in [
5921 &[b"ZINTERCARD".as_slice(), b"3", b"z", b"y"][..],
5922 &[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT"],
5923 &[b"ZINTERCARD", b"2", b"z", b"y", b"junk", b"1"],
5924 ] {
5925 assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
5926 }
5927 }
5928
5929 #[test]
5931 fn a_draw_answers_one_member_or_an_array_of_them() {
5932 let mut f = Fixture::new();
5933 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
5934 assert_eq!(f.run(&[b"ZRANDMEMBER", b"nokey"]), "$-1\r\n");
5937 assert_eq!(f.run(&[b"ZRANDMEMBER", b"nokey", b"3"]), "*0\r\n");
5938 assert_eq!(f.run(&[b"ZRANDMEMBER", b"z", b"0"]), "*0\r\n");
5939 assert!(f.run(&[b"ZRANDMEMBER", b"z"]).starts_with("$1\r\n"));
5940 let all = f.run(&[b"ZRANDMEMBER", b"z", b"10"]);
5943 assert!(all.starts_with("*3\r\n"), "{all}");
5944 for m in ["a", "b", "c"] {
5945 assert!(all.contains(m), "{all}");
5946 }
5947 assert!(
5950 f.run(&[b"ZRANDMEMBER", b"z", b"-5"]).starts_with("*5\r\n"),
5951 "five draws with replacement"
5952 );
5953 assert!(
5954 f.run(&[b"ZRANDMEMBER", b"z", b"2", b"WITHSCORES"])
5955 .starts_with("*4\r\n"),
5956 "two pairs, flat on RESP2"
5957 );
5958 f.out = Out::new(Proto::Resp3);
5959 let got = f.run(&[b"ZRANDMEMBER", b"z", b"2", b"WITHSCORES"]);
5960 assert!(got.starts_with("*2\r\n*2\r\n"), "{got}");
5961 assert_eq!(f.run(&[b"ZRANDMEMBER", b"nokey"]), "_\r\n");
5962 f.out = Out::new(Proto::Resp2);
5963 assert_eq!(
5964 f.run(&[b"ZRANDMEMBER", b"z", b"2", b"junk"]),
5965 "-ERR syntax error\r\n"
5966 );
5967 assert_eq!(
5968 f.run(&[b"ZRANDMEMBER", b"z", b"x"]),
5969 "-ERR value is not an integer or out of range\r\n"
5970 );
5971 }
5972
5973 #[test]
5975 fn a_sorted_set_scan_answers_pairs_of_strings_on_both_protocols() {
5976 let mut f = Fixture::new();
5977 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
5978 let all = "*2\r\n$1\r\n0\r\n*6\r\n$1\r\na\r\n$1\r\n1\r\n$1\r\nb\r\n$1\r\n2\r\n$1\r\nc\r\n$1\r\n3\r\n";
5979 assert_eq!(f.run(&[b"ZSCAN", b"z", b"0"]), all);
5980 assert_eq!(f.run(&[b"ZSCAN", b"z", b"0", b"COUNT", b"10"]), all);
5981 assert_eq!(
5982 f.run(&[b"ZSCAN", b"z", b"0", b"MATCH", b"a*"]),
5983 "*2\r\n$1\r\n0\r\n*2\r\n$1\r\na\r\n$1\r\n1\r\n"
5984 );
5985 assert_eq!(
5986 f.run(&[b"ZSCAN", b"nokey", b"0"]),
5987 "*2\r\n$1\r\n0\r\n*0\r\n"
5988 );
5989 f.out = Out::new(Proto::Resp3);
5992 assert_eq!(f.run(&[b"ZSCAN", b"z", b"0"]), all);
5993 f.out = Out::new(Proto::Resp2);
5994 assert_eq!(
5995 f.run(&[b"ZSCAN", b"z", b"0", b"NOVALUES"]),
5996 "-ERR NOVALUES option can only be used in HSCAN\r\n"
5997 );
5998 assert_eq!(f.run(&[b"ZSCAN", b"z", b"-1"]), "-ERR invalid cursor\r\n");
5999 assert_eq!(
6000 f.run(&[b"ZSCAN", b"z", b"0", b"COUNT", b"0"]),
6001 "-ERR syntax error\r\n"
6002 );
6003 }
6004
6005 #[test]
6007 fn a_sorted_set_pop_changes_shape_when_it_is_given_a_count() {
6008 let mut f = Fixture::new();
6009 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
6010 assert_eq!(f.run(&[b"ZPOPMIN", b"z"]), "*2\r\n$1\r\na\r\n$1\r\n1\r\n");
6012 assert_eq!(f.run(&[b"ZPOPMAX", b"z"]), "*2\r\n$1\r\nc\r\n$1\r\n3\r\n");
6013 f.run(&[b"ZADD", b"z", b"1", b"a", b"3", b"c"]);
6014 assert_eq!(
6016 f.run(&[b"ZPOPMIN", b"z", b"2"]),
6017 "*4\r\n$1\r\na\r\n$1\r\n1\r\n$1\r\nb\r\n$1\r\n2\r\n"
6018 );
6019 assert_eq!(f.run(&[b"ZPOPMIN", b"nokey"]), "*0\r\n");
6022 assert_eq!(f.run(&[b"ZPOPMIN", b"nokey", b"2"]), "*0\r\n");
6023 assert_eq!(f.run(&[b"ZPOPMIN", b"z", b"0"]), "*0\r\n");
6024 assert_eq!(
6026 f.run(&[b"ZPOPMIN", b"z", b"9"]),
6027 "*2\r\n$1\r\nc\r\n$1\r\n3\r\n"
6028 );
6029 assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
6030
6031 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b"]);
6032 f.out = Out::new(Proto::Resp3);
6033 assert_eq!(f.run(&[b"ZPOPMIN", b"z"]), "*2\r\n$1\r\na\r\n,1\r\n");
6034 assert_eq!(
6035 f.run(&[b"ZPOPMIN", b"z", b"1"]),
6036 "*1\r\n*2\r\n$1\r\nb\r\n,2\r\n"
6037 );
6038 f.out = Out::new(Proto::Resp2);
6039 let bad = "-ERR value is out of range, must be positive\r\n";
6042 assert_eq!(f.run(&[b"ZPOPMIN", b"z", b"x"]), bad);
6043 assert_eq!(f.run(&[b"ZPOPMIN", b"z", b"-1"]), bad);
6044 assert_eq!(
6045 f.run(&[b"ZPOPMIN", b"z", b"1", b"2"]),
6046 "-ERR syntax error\r\n"
6047 );
6048 }
6049
6050 #[test]
6052 fn a_multi_key_pop_names_the_key_that_answered_and_nests_its_pairs() {
6053 let mut f = Fixture::new();
6054 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
6055 assert_eq!(
6056 f.run(&[b"ZMPOP", b"2", b"nokey", b"z", b"MIN"]),
6057 "*2\r\n$1\r\nz\r\n*1\r\n*2\r\n$1\r\na\r\n$1\r\n1\r\n"
6058 );
6059 assert_eq!(
6062 f.run(&[b"ZMPOP", b"1", b"z", b"MAX", b"COUNT", b"2"]),
6063 "*2\r\n$1\r\nz\r\n*2\r\n*2\r\n$1\r\nc\r\n$1\r\n3\r\n*2\r\n$1\r\nb\r\n$1\r\n2\r\n"
6064 );
6065 assert_eq!(f.run(&[b"ZMPOP", b"1", b"nokey", b"MIN"]), "*-1\r\n");
6067 f.out = Out::new(Proto::Resp3);
6068 assert_eq!(f.run(&[b"ZMPOP", b"1", b"nokey", b"MIN"]), "_\r\n");
6069 f.out = Out::new(Proto::Resp2);
6070 let numkeys = "-ERR numkeys should be greater than 0\r\n";
6071 for bad in [
6072 &[b"ZMPOP".as_slice(), b"0", b"z", b"MIN"][..],
6073 &[b"ZMPOP", b"-1", b"z", b"MIN"],
6074 &[b"ZMPOP", b"x", b"z", b"MIN"],
6075 ] {
6076 assert_eq!(f.run(bad), numkeys, "{:?}", bad[1]);
6077 }
6078 let count = "-ERR count should be greater than 0\r\n";
6079 for bad in [
6080 &[b"ZMPOP".as_slice(), b"1", b"z", b"MIN", b"COUNT", b"0"][..],
6081 &[b"ZMPOP", b"1", b"z", b"MIN", b"COUNT", b"-1"],
6082 &[b"ZMPOP", b"1", b"z", b"MIN", b"COUNT", b"x"],
6083 ] {
6084 assert_eq!(f.run(bad), count, "{:?}", bad[5]);
6085 }
6086 let syntax = "-ERR syntax error\r\n";
6087 for bad in [
6088 &[b"ZMPOP".as_slice(), b"2", b"z", b"MIN"][..],
6091 &[b"ZMPOP", b"1", b"z", b"SIDEWAYS"],
6092 &[b"ZMPOP", b"1", b"z", b"MIN", b"junk"],
6093 &[b"ZMPOP", b"1", b"z", b"MIN", b"COUNT", b"1", b"junk"],
6094 ] {
6095 assert_eq!(f.run(bad), syntax, "{bad:?}");
6096 }
6097 }
6098
6099 #[test]
6102 fn the_sorted_set_pops_that_wait_answer_like_the_ones_they_wrap() {
6103 let mut f = Fixture::new();
6104 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
6105 assert_eq!(
6106 f.flow(&[b"BZPOPMIN", b"nokey", b"z", b"0"]),
6107 (
6108 Flow::Continue,
6109 "*3\r\n$1\r\nz\r\n$1\r\na\r\n$1\r\n1\r\n".to_owned()
6110 )
6111 );
6112 assert_eq!(
6113 f.run(&[b"BZPOPMAX", b"z", b"0"]),
6114 "*3\r\n$1\r\nz\r\n$1\r\nc\r\n$1\r\n3\r\n"
6115 );
6116 f.run(&[b"ZADD", b"z", b"1", b"a", b"3", b"c"]);
6117 assert_eq!(
6118 f.run(&[
6119 b"BZMPOP", b"0", b"2", b"nokey", b"z", b"MIN", b"COUNT", b"2"
6120 ]),
6121 "*2\r\n$1\r\nz\r\n*2\r\n*2\r\n$1\r\na\r\n$1\r\n1\r\n*2\r\n$1\r\nb\r\n$1\r\n2\r\n"
6122 );
6123 f.out = Out::new(Proto::Resp3);
6124 assert_eq!(
6125 f.run(&[b"BZPOPMIN", b"z", b"0"]),
6126 "*3\r\n$1\r\nz\r\n$1\r\nc\r\n,3\r\n"
6127 );
6128 f.out = Out::new(Proto::Resp2);
6129 assert_eq!(
6131 f.flow(&[b"BZPOPMIN", b"z", b"0"]),
6132 (Flow::Block, String::new())
6133 );
6134 assert_eq!(
6135 f.flow(&[b"BZMPOP", b"0", b"1", b"z", b"MIN"]),
6136 (Flow::Block, String::new())
6137 );
6138 assert_eq!(
6141 f.run(&[b"BZMPOP", b"abc", b"0", b"z", b"MIN"]),
6142 "-ERR timeout is not a float or out of range\r\n"
6143 );
6144 assert_eq!(
6145 f.run(&[b"BZMPOP", b"0", b"0", b"z", b"MIN"]),
6146 "-ERR numkeys should be greater than 0\r\n"
6147 );
6148 assert_eq!(
6149 f.run(&[b"BZPOPMIN", b"z", b"-1"]),
6150 "-ERR timeout is negative\r\n"
6151 );
6152 }
6153
6154 #[test]
6158 fn a_parked_sorted_set_client_waits_for_a_member_and_not_for_a_key() {
6159 let mut f = Fixture::new();
6160 assert_eq!(f.flow(&[b"BZPOPMIN", b"z", b"0"]).0, Flow::Block);
6161 assert_eq!(f.server.waiters().len(), 1);
6162 f.run(&[b"SET", b"z", b"v"]);
6165 let mut out = Out::new(Proto::Resp2);
6166 assert!(!f.server.serve_waiter(0, 0, &mut out));
6167 assert!(out.as_slice().is_empty());
6168 f.run(&[b"DEL", b"z"]);
6169 f.run(&[b"ZADD", b"z", b"5", b"m"]);
6170 assert!(f.server.serve_waiter(0, 0, &mut out));
6171 assert_eq!(
6172 core::str::from_utf8(out.as_slice()).expect("ascii"),
6173 "*3\r\n$1\r\nz\r\n$1\r\nm\r\n$1\r\n5\r\n"
6174 );
6175 assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
6178 }
6179
6180 #[test]
6181 fn every_sorted_set_command_says_wrongtype_and_writes_nothing() {
6182 let mut f = Fixture::new();
6183 f.run(&[b"SET", b"s", b"v"]);
6184 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
6185 for cmd in [
6186 &[b"ZADD".as_slice(), b"s", b"1", b"a"][..],
6187 &[b"ZINCRBY", b"s", b"1", b"a"],
6188 &[b"ZCARD", b"s"],
6189 &[b"ZSCORE", b"s", b"a"],
6190 &[b"ZMSCORE", b"s", b"a"],
6191 &[b"ZREM", b"s", b"a"],
6192 &[b"ZRANK", b"s", b"a"],
6193 &[b"ZREVRANK", b"s", b"a"],
6194 &[b"ZCOUNT", b"s", b"1", b"2"],
6195 &[b"ZLEXCOUNT", b"s", b"-", b"+"],
6196 &[b"ZRANGE", b"s", b"0", b"-1"],
6197 &[b"ZREVRANGE", b"s", b"0", b"-1"],
6198 &[b"ZRANGEBYSCORE", b"s", b"1", b"2"],
6199 &[b"ZREVRANGEBYSCORE", b"s", b"2", b"1"],
6200 &[b"ZRANGEBYLEX", b"s", b"-", b"+"],
6201 &[b"ZREVRANGEBYLEX", b"s", b"+", b"-"],
6202 &[b"ZRANGESTORE", b"d", b"s", b"0", b"-1"],
6203 &[b"ZREMRANGEBYRANK", b"s", b"0", b"-1"],
6204 &[b"ZREMRANGEBYSCORE", b"s", b"1", b"2"],
6205 &[b"ZREMRANGEBYLEX", b"s", b"-", b"+"],
6206 &[b"ZUNION", b"1", b"s"],
6207 &[b"ZINTER", b"1", b"s"],
6208 &[b"ZDIFF", b"1", b"s"],
6209 &[b"ZUNIONSTORE", b"d", b"1", b"s"],
6210 &[b"ZINTERSTORE", b"d", b"1", b"s"],
6211 &[b"ZDIFFSTORE", b"d", b"1", b"s"],
6212 &[b"ZINTERCARD", b"1", b"s"],
6213 &[b"ZRANDMEMBER", b"s"],
6214 &[b"ZSCAN", b"s", b"0"],
6215 &[b"ZPOPMIN", b"s"],
6216 &[b"ZPOPMAX", b"s", b"2"],
6217 &[b"ZMPOP", b"1", b"s", b"MIN"],
6218 &[b"BZPOPMIN", b"s", b"0"],
6219 &[b"BZPOPMAX", b"s", b"0"],
6220 &[b"BZMPOP", b"0", b"1", b"s", b"MIN"],
6221 ] {
6222 assert_eq!(f.run(cmd), wrong, "{:?}", cmd[0]);
6223 }
6224 assert_eq!(f.run(&[b"GET", b"s"]), "$1\r\nv\r\n");
6225 }
6226
6227 #[test]
6231 fn churning_sorted_sets_does_not_grow_the_server() {
6232 let mut f = Fixture::new();
6233 let members: Vec<Vec<u8>> = (0..200).map(|i| format!("m{i}").into_bytes()).collect();
6234 let scores: Vec<Vec<u8>> = (0..200).map(|i| format!("{i}").into_bytes()).collect();
6235 let mut args: Vec<&[u8]> = vec![b"ZADD", b"z"];
6236 for i in 0..200 {
6237 args.push(&scores[i]);
6238 args.push(&members[i]);
6239 }
6240
6241 f.run(&args);
6242 f.run(&[b"DEL", b"z"]);
6243 f.server.compact_step();
6244 let after_first = f.server.memory_bytes();
6245
6246 for _ in 0..200 {
6247 f.run(&args);
6248 f.run(&[b"DEL", b"z"]);
6249 f.server.compact_step();
6250 }
6251 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
6252 assert!(
6253 f.server.memory_bytes() <= after_first * 2,
6254 "held {} after two hundred passes against {after_first} after one",
6255 f.server.memory_bytes()
6256 );
6257 }
6258
6259 #[test]
6262 fn an_array_writes_at_any_index_and_reads_back_what_it_sent() {
6263 let mut f = Fixture::new();
6264 assert_eq!(
6267 f.run(&[b"ARSET", b"a", b"1000", b"x", b"y", b"z"]),
6268 ":3\r\n"
6269 );
6270 assert_eq!(f.run(&[b"ARSET", b"a", b"1000", b"X", b"Y"]), ":0\r\n");
6271 assert_eq!(f.run(&[b"ARGET", b"a", b"1000"]), "$1\r\nX\r\n");
6272 assert_eq!(f.run(&[b"ARGET", b"a", b"1002"]), "$1\r\nz\r\n");
6273 assert_eq!(f.run(&[b"ARGET", b"a", b"999"]), "$-1\r\n");
6275 assert_eq!(f.run(&[b"ARGET", b"nope", b"0"]), "$-1\r\n");
6276 assert_eq!(
6277 f.run(&[b"ARMGET", b"a", b"1002", b"999", b"1000"]),
6278 "*3\r\n$1\r\nz\r\n$-1\r\n$1\r\nX\r\n"
6279 );
6280 assert_eq!(f.run(&[b"ARMSET", b"a", b"5", b"p", b"5", b"q"]), ":1\r\n");
6282 assert_eq!(f.run(&[b"ARGET", b"a", b"5"]), "$1\r\nq\r\n");
6283 }
6284
6285 #[test]
6288 fn the_length_is_the_high_water_mark_and_the_count_is_the_population() {
6289 let mut f = Fixture::new();
6290 assert_eq!(f.run(&[b"ARLEN", b"nope"]), ":0\r\n");
6291 assert_eq!(f.run(&[b"ARCOUNT", b"nope"]), ":0\r\n");
6292 f.run(&[b"ARMSET", b"a", b"0", b"x", b"9", b"y"]);
6293 assert_eq!(f.run(&[b"ARLEN", b"a"]), ":10\r\n");
6294 assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":2\r\n");
6295 assert_eq!(f.run(&[b"ARDEL", b"a", b"0"]), ":1\r\n");
6297 assert_eq!(f.run(&[b"ARLEN", b"a"]), ":10\r\n");
6298 assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":1\r\n");
6299
6300 f.run(&[b"ARSET", b"top", b"18446744073709551614", b"z"]);
6304 assert_eq!(f.run(&[b"ARLEN", b"top"]), ":18446744073709551615\r\n");
6305 assert_eq!(f.run(&[b"ARCOUNT", b"top"]), ":1\r\n");
6306 assert_eq!(
6309 f.run(&[b"ARSET", b"over", b"18446744073709551614", b"a", b"b"]),
6310 "-ERR array index overflow\r\n"
6311 );
6312 assert_eq!(f.run(&[b"EXISTS", b"over"]), ":0\r\n");
6313 }
6314
6315 #[test]
6318 fn a_range_read_answers_for_the_holes_too_and_is_capped_at_a_million() {
6319 let mut f = Fixture::new();
6320 f.run(&[b"ARSET", b"a", b"1", b"x"]);
6321 assert_eq!(
6322 f.run(&[b"ARGETRANGE", b"a", b"0", b"3"]),
6323 "*4\r\n$-1\r\n$1\r\nx\r\n$-1\r\n$-1\r\n"
6324 );
6325 assert_eq!(
6328 f.run(&[b"ARGETRANGE", b"a", b"3", b"0"]),
6329 "*4\r\n$-1\r\n$-1\r\n$1\r\nx\r\n$-1\r\n"
6330 );
6331 assert_eq!(
6333 f.run(&[b"ARGETRANGE", b"nope", b"0", b"1"]),
6334 "*2\r\n$-1\r\n$-1\r\n"
6335 );
6336 assert_eq!(
6340 f.run(&[b"ARGETRANGE", b"nope", b"0", b"18446744073709551614"]),
6341 "-ERR range exceeds maximum of 1000000 items\r\n"
6342 );
6343 }
6344
6345 #[test]
6348 fn a_bad_index_late_in_the_line_writes_none_of_the_earlier_ones() {
6349 let mut f = Fixture::new();
6350 assert_eq!(
6351 f.run(&[b"ARMSET", b"a", b"0", b"x", b"-1", b"y"]),
6352 "-ERR invalid array index\r\n"
6353 );
6354 assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
6355 f.run(&[b"ARSET", b"a", b"0", b"x", b"y", b"z"]);
6356 assert_eq!(
6357 f.run(&[b"ARDEL", b"a", b"0", b"01"]),
6358 "-ERR invalid array index\r\n"
6359 );
6360 assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":3\r\n");
6361 assert_eq!(
6364 f.run(&[b"ARGET", b"a", b"-1"]),
6365 "-ERR invalid array index\r\n"
6366 );
6367 assert_eq!(
6370 f.run(&[b"ARMSET", b"a", b"0", b"x", b"1"]),
6371 "-ERR wrong number of arguments for 'armset' command\r\n"
6372 );
6373 assert_eq!(
6374 f.run(&[b"ARDELRANGE", b"a", b"0", b"1", b"2"]),
6375 "-ERR wrong number of arguments for 'ardelrange' command\r\n"
6376 );
6377 }
6378
6379 #[test]
6380 fn a_range_delete_costs_the_elements_and_takes_the_key_when_it_empties() {
6381 let mut f = Fixture::new();
6382 f.run(&[b"ARSET", b"a", b"0", b"0", b"1", b"2", b"3", b"4"]);
6383 assert_eq!(f.run(&[b"ARDELRANGE", b"a", b"3", b"1"]), ":3\r\n");
6384 assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":2\r\n");
6385 assert_eq!(
6388 f.run(&[
6389 b"ARDELRANGE",
6390 b"a",
6391 b"100",
6392 b"200",
6393 b"0",
6394 b"18446744073709551614"
6395 ]),
6396 ":2\r\n"
6397 );
6398 assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
6399 assert_eq!(f.run(&[b"ARDELRANGE", b"nope", b"0", b"1"]), ":0\r\n");
6400 assert_eq!(f.run(&[b"ARDEL", b"nope", b"0"]), ":0\r\n");
6401 }
6402
6403 #[test]
6406 fn a_value_comes_back_byte_for_byte_however_it_was_packed() {
6407 let mut f = Fixture::new();
6408 let long = vec![b'v'; 200];
6409 f.run(&[
6410 b"ARMSET", b"a", b"0", b"42", b"1", b"007", b"2", b"3.5", b"3", b"3.14", b"4",
6411 b"short", b"5", &long, b"6", b"-0",
6412 ]);
6413 assert_eq!(
6417 f.run(&[b"ARGETRANGE", b"a", b"0", b"6"]),
6418 format!(
6419 "*7\r\n$2\r\n42\r\n$3\r\n007\r\n$3\r\n3.5\r\n$4\r\n3.14\r\n$5\r\nshort\r\n$200\r\n{}\r\n$2\r\n-0\r\n",
6420 String::from_utf8_lossy(&long)
6421 )
6422 );
6423 }
6424
6425 #[test]
6426 fn an_array_is_a_type_and_an_encoding_a_client_can_see() {
6427 let mut f = Fixture::new();
6428 f.run(&[b"ARSET", b"a", b"0", b"x"]);
6429 assert_eq!(f.run(&[b"TYPE", b"a"]), "+array\r\n");
6430 assert_eq!(
6431 f.run(&[b"OBJECT", b"ENCODING", b"a"]),
6432 "$12\r\nsliced-array\r\n"
6433 );
6434 assert_eq!(f.run(&[b"EXPIRE", b"a", b"100"]), ":1\r\n");
6436 assert_eq!(f.run(&[b"PERSIST", b"a"]), ":1\r\n");
6437 assert_eq!(f.run(&[b"COPY", b"a", b"b"]), ":1\r\n");
6438 assert_eq!(f.run(&[b"ARGET", b"b", b"0"]), "$1\r\nx\r\n");
6439 assert_eq!(f.run(&[b"RENAME", b"a", b"c"]), "+OK\r\n");
6440 assert_eq!(f.run(&[b"ARCOUNT", b"c"]), ":1\r\n");
6441 }
6442
6443 #[test]
6444 fn every_array_command_refuses_a_key_holding_something_else() {
6445 let mut f = Fixture::new();
6446 f.run(&[b"SET", b"s", b"v"]);
6447 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
6448 for cmd in [
6449 &[b"ARSET".as_ref(), b"s", b"0", b"x"][..],
6450 &[b"ARMSET".as_ref(), b"s", b"0", b"x"][..],
6451 &[b"ARGET".as_ref(), b"s", b"0"][..],
6452 &[b"ARMGET".as_ref(), b"s", b"0"][..],
6453 &[b"ARGETRANGE".as_ref(), b"s", b"0", b"1"][..],
6454 &[b"ARLEN".as_ref(), b"s"][..],
6455 &[b"ARCOUNT".as_ref(), b"s"][..],
6456 &[b"ARDEL".as_ref(), b"s", b"0"][..],
6457 &[b"ARDELRANGE".as_ref(), b"s", b"0", b"1"][..],
6458 &[b"ARINSERT".as_ref(), b"s", b"x"][..],
6459 &[b"ARRING".as_ref(), b"s", b"4", b"x"][..],
6460 &[b"ARNEXT".as_ref(), b"s"][..],
6461 &[b"ARSEEK".as_ref(), b"s", b"1"][..],
6462 &[b"ARLASTITEMS".as_ref(), b"s", b"1"][..],
6463 &[b"ARSCAN".as_ref(), b"s", b"0", b"1"][..],
6464 &[b"ARGREP".as_ref(), b"s", b"0", b"1", b"EXACT", b"v"][..],
6465 &[b"AROP".as_ref(), b"s", b"0", b"1", b"SUM"][..],
6466 &[b"ARINFO".as_ref(), b"s"][..],
6467 ] {
6468 assert_eq!(f.run(cmd), wrong, "{}", String::from_utf8_lossy(cmd[0]));
6469 }
6470 }
6471
6472 #[test]
6476 fn a_bad_index_reports_the_type_only_where_redis_reports_it() {
6477 let mut f = Fixture::new();
6478 f.run(&[b"SET", b"s", b"v"]);
6479 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
6480 let bad = "-ERR invalid array index\r\n";
6481 assert_eq!(f.run(&[b"ARGET", b"s", b"-1"]), wrong);
6482 assert_eq!(f.run(&[b"ARMGET", b"s", b"0", b"-1"]), wrong);
6483 assert_eq!(f.run(&[b"ARSET", b"s", b"-1", b"x"]), bad);
6484 assert_eq!(f.run(&[b"ARDEL", b"s", b"-1"]), bad);
6485 assert_eq!(f.run(&[b"ARSCAN", b"s", b"-1", b"0"]), bad);
6486 assert_eq!(f.run(&[b"ARGREP", b"s", b"-1", b"0", b"EXACT", b"v"]), bad);
6487 f.run(&[b"ARSET", b"a", b"0", b"x"]);
6489 assert_eq!(f.run(&[b"ARGET", b"a", b"-1"]), bad);
6490 assert_eq!(f.run(&[b"ARGET", b"nope", b"-1"]), bad);
6491 }
6492
6493 #[test]
6494 fn an_append_follows_a_cursor_the_client_can_move() {
6495 let mut f = Fixture::new();
6496 assert_eq!(f.run(&[b"ARNEXT", b"nope"]), ":0\r\n");
6497 assert_eq!(f.run(&[b"ARINSERT", b"a", b"x", b"y"]), ":1\r\n");
6498 assert_eq!(f.run(&[b"ARNEXT", b"a"]), ":2\r\n");
6499 assert_eq!(f.run(&[b"ARINSERT", b"a", b"z"]), ":2\r\n");
6500 assert_eq!(f.run(&[b"ARGET", b"a", b"2"]), "$1\r\nz\r\n");
6501
6502 assert_eq!(f.run(&[b"ARSEEK", b"nope", b"5"]), ":0\r\n");
6505 assert_eq!(f.run(&[b"EXISTS", b"nope"]), ":0\r\n");
6506 assert_eq!(f.run(&[b"ARSEEK", b"a", b"100"]), ":1\r\n");
6507 assert_eq!(f.run(&[b"ARNEXT", b"a"]), ":100\r\n");
6508 assert_eq!(f.run(&[b"ARINSERT", b"a", b"far"]), ":100\r\n");
6509 assert_eq!(f.run(&[b"ARSEEK", b"a", b"0"]), ":1\r\n");
6510 assert_eq!(f.run(&[b"ARNEXT", b"a"]), ":0\r\n");
6511
6512 assert_eq!(f.run(&[b"ARSEEK", b"a", b"18446744073709551615"]), ":1\r\n");
6515 assert_eq!(f.run(&[b"ARNEXT", b"a"]), "$-1\r\n");
6516 assert_eq!(
6517 f.run(&[b"ARINSERT", b"a", b"x"]),
6518 "-ERR insert index overflow\r\n"
6519 );
6520 assert_eq!(
6521 f.run(&[b"ARSET", b"a", b"18446744073709551615", b"x"]),
6522 "-ERR invalid array index\r\n"
6523 );
6524 }
6525
6526 #[test]
6527 fn a_ring_keeps_the_newest_and_renumbers_them_when_it_is_resized() {
6528 let mut f = Fixture::new();
6529 assert_eq!(f.run(&[b"ARRING", b"r", b"3", b"a", b"b", b"c"]), ":2\r\n");
6530 assert_eq!(f.run(&[b"ARRING", b"r", b"3", b"d", b"e"]), ":1\r\n");
6531 assert_eq!(f.run(&[b"ARLEN", b"r"]), ":3\r\n");
6532 assert_eq!(
6533 f.run(&[b"ARGETRANGE", b"r", b"0", b"2"]),
6534 "*3\r\n$1\r\nd\r\n$1\r\ne\r\n$1\r\nc\r\n"
6535 );
6536 assert_eq!(f.run(&[b"ARRING", b"r", b"5", b"f"]), ":3\r\n");
6539 assert_eq!(
6540 f.run(&[b"ARGETRANGE", b"r", b"0", b"3"]),
6541 "*4\r\n$1\r\nc\r\n$1\r\nd\r\n$1\r\ne\r\n$1\r\nf\r\n"
6542 );
6543 assert_eq!(
6546 f.run(&[b"ARRING", b"r", b"0", b"x"]),
6547 "-ERR size must be positive\r\n"
6548 );
6549 assert_eq!(
6550 f.run(&[b"ARRING", b"r", b"big", b"x"]),
6551 "-ERR invalid size\r\n"
6552 );
6553 }
6554
6555 #[test]
6556 fn the_last_items_walk_back_from_the_cursor_and_report_the_holes() {
6557 let mut f = Fixture::new();
6558 assert_eq!(f.run(&[b"ARLASTITEMS", b"nope", b"5"]), "*0\r\n");
6559 f.run(&[b"ARRING", b"r", b"4", b"a", b"b", b"c", b"d", b"e"]);
6560 assert_eq!(
6561 f.run(&[b"ARLASTITEMS", b"r", b"3"]),
6562 "*3\r\n$1\r\nc\r\n$1\r\nd\r\n$1\r\ne\r\n"
6563 );
6564 assert_eq!(
6565 f.run(&[b"ARLASTITEMS", b"r", b"3", b"rev"]),
6566 "*3\r\n$1\r\ne\r\n$1\r\nd\r\n$1\r\nc\r\n"
6567 );
6568 assert_eq!(
6569 f.run(&[b"ARLASTITEMS", b"r", b"99"]),
6570 "*4\r\n$1\r\nb\r\n$1\r\nc\r\n$1\r\nd\r\n$1\r\ne\r\n",
6571 "more than there is gets what there is"
6572 );
6573 assert_eq!(f.run(&[b"ARLASTITEMS", b"r", b"0", b"junk"]), "*0\r\n");
6576 assert_eq!(
6577 f.run(&[b"ARLASTITEMS", b"r", b"1", b"junk"]),
6578 "-ERR syntax error\r\n"
6579 );
6580 assert_eq!(
6581 f.run(&[b"ARLASTITEMS", b"r", b"nine"]),
6582 "-ERR invalid COUNT\r\n"
6583 );
6584
6585 f.run(&[b"ARMSET", b"h", b"0", b"x", b"2", b"z"]);
6588 assert_eq!(
6589 f.run(&[b"ARLASTITEMS", b"h", b"5"]),
6590 "*2\r\n$-1\r\n$1\r\nz\r\n"
6591 );
6592 }
6593
6594 #[test]
6595 fn a_scan_answers_pairs_for_what_is_there_and_skips_what_is_not() {
6596 let mut f = Fixture::new();
6597 assert_eq!(f.run(&[b"ARSCAN", b"nope", b"0", b"10"]), "*0\r\n");
6598 f.run(&[b"ARMSET", b"a", b"0", b"x", b"7", b"y", b"1000000", b"z"]);
6599 assert_eq!(
6602 f.run(&[b"ARSCAN", b"a", b"0", b"18446744073709551614"]),
6603 "*3\r\n*2\r\n:0\r\n$1\r\nx\r\n*2\r\n:7\r\n$1\r\ny\r\n*2\r\n:1000000\r\n$1\r\nz\r\n"
6604 );
6605 assert_eq!(
6606 f.run(&[
6607 b"ARSCAN",
6608 b"a",
6609 b"18446744073709551614",
6610 b"0",
6611 b"LIMIT",
6612 b"1"
6613 ]),
6614 "*1\r\n*2\r\n:1000000\r\n$1\r\nz\r\n"
6615 );
6616 assert_eq!(f.run(&[b"ARSCAN", b"a", b"1", b"6"]), "*0\r\n");
6617 assert_eq!(
6618 f.run(&[b"ARSCAN", b"a", b"0", b"10", b"LIMIT", b"0"]),
6619 "-ERR LIMIT must be positive\r\n"
6620 );
6621 assert_eq!(
6622 f.run(&[b"ARSCAN", b"a", b"0", b"10", b"NOPE", b"1"]),
6623 "-ERR syntax error\r\n"
6624 );
6625 assert_eq!(
6626 f.run(&[b"ARSCAN", b"a", b"0", b"10", b"LIMIT"]),
6627 "-ERR wrong number of arguments for 'arscan' command\r\n"
6628 );
6629 }
6630
6631 #[test]
6632 fn a_grep_answers_the_indexes_whose_elements_match() {
6633 let mut f = Fixture::new();
6634 assert_eq!(
6635 f.run(&[b"ARGREP", b"nope", b"0", b"10", b"EXACT", b"x"]),
6636 "*0\r\n"
6637 );
6638 f.run(&[b"ARSET", b"a", b"0", b"alpha", b"beta", b"gamma", b"ALPHA"]);
6639
6640 assert_eq!(
6643 f.run(&[b"ARGREP", b"a", b"-", b"+", b"GLOB", b"*a"]),
6644 "*3\r\n:0\r\n:1\r\n:2\r\n"
6645 );
6646 assert_eq!(
6647 f.run(&[b"ARGREP", b"a", b"+", b"-", b"GLOB", b"*a"]),
6648 "*3\r\n:2\r\n:1\r\n:0\r\n"
6649 );
6650 assert_eq!(
6651 f.run(&[b"ARGREP", b"a", b"1", b"2", b"GLOB", b"*a"]),
6652 "*2\r\n:1\r\n:2\r\n"
6653 );
6654
6655 assert_eq!(
6658 f.run(&[b"ARGREP", b"a", b"-", b"+", b"EXACT", b"alpha"]),
6659 "*1\r\n:0\r\n"
6660 );
6661 assert_eq!(
6662 f.run(&[b"ARGREP", b"a", b"-", b"+", b"EXACT", b"alpha", b"NOCASE"]),
6663 "*2\r\n:0\r\n:3\r\n"
6664 );
6665 assert_eq!(
6666 f.run(&[b"ARGREP", b"a", b"-", b"+", b"MATCH", b"mm"]),
6667 "*1\r\n:2\r\n"
6668 );
6669 assert_eq!(
6670 f.run(&[b"ARGREP", b"a", b"-", b"+", b"RE", b"^[bg]"]),
6671 "*2\r\n:1\r\n:2\r\n"
6672 );
6673
6674 let both: &[&[u8]] = &[
6677 b"ARGREP", b"a", b"-", b"+", b"EXACT", b"beta", b"MATCH", b"al",
6678 ];
6679 assert_eq!(f.run(both), "*2\r\n:0\r\n:1\r\n");
6680 assert_eq!(
6681 f.run(&[
6682 b"ARGREP", b"a", b"-", b"+", b"EXACT", b"beta", b"MATCH", b"al", b"AND"
6683 ]),
6684 "*0\r\n"
6685 );
6686 assert_eq!(
6687 f.run(&[
6688 b"ARGREP", b"a", b"-", b"+", b"EXACT", b"beta", b"MATCH", b"al", b"AND", b"OR"
6689 ]),
6690 "*2\r\n:0\r\n:1\r\n"
6691 );
6692
6693 assert_eq!(
6696 f.run(&[
6697 b"ARGREP",
6698 b"a",
6699 b"-",
6700 b"+",
6701 b"MATCH",
6702 b"a",
6703 b"WITHVALUES",
6704 b"LIMIT",
6705 b"2"
6706 ]),
6707 "*2\r\n*2\r\n:0\r\n$5\r\nalpha\r\n*2\r\n:1\r\n$4\r\nbeta\r\n"
6708 );
6709 assert_eq!(
6710 f.run(&[
6711 b"ARGREP", b"a", b"-", b"+", b"EXACT", b"ALPHA", b"LIMIT", b"1"
6712 ]),
6713 "*1\r\n:3\r\n"
6714 );
6715 }
6716
6717 #[test]
6719 fn a_grep_reports_a_broken_command_the_way_redis_does() {
6720 let mut f = Fixture::new();
6721 f.run(&[b"ARSET", b"a", b"0", b"alpha"]);
6722 let syntax = "-ERR syntax error\r\n";
6723
6724 assert_eq!(
6727 f.run(&[b"ARGREP", b"a", b"-1", b"0", b"NOPE", b"x"]),
6728 "-ERR invalid array index\r\n"
6729 );
6730 assert_eq!(f.run(&[b"ARGREP", b"a", b"0", b"1", b"NOPE", b"x"]), syntax);
6731 assert_eq!(
6733 f.run(&[b"ARGREP", b"a", b"0", b"1", b"NOCASE", b"EXACT"]),
6734 syntax
6735 );
6736 assert_eq!(
6737 f.run(&[b"ARGREP", b"a", b"0", b"1", b"EXACT", b"x", b"LIMIT"]),
6738 syntax
6739 );
6740 assert_eq!(
6741 f.run(&[b"ARGREP", b"a", b"0", b"1", b"NOCASE", b"WITHVALUES"]),
6742 syntax,
6743 "a command with no predicate in it at all"
6744 );
6745 assert_eq!(
6746 f.run(&[b"ARGREP", b"a", b"0", b"1", b"EXACT", b"x", b"LIMIT", b"0"]),
6747 "-ERR LIMIT must be positive\r\n"
6748 );
6749 assert_eq!(
6750 f.run(&[
6751 b"ARGREP", b"a", b"0", b"1", b"EXACT", b"x", b"LIMIT", b"nine"
6752 ]),
6753 "-ERR value is not an integer or out of range\r\n"
6754 );
6755 assert_eq!(
6756 f.run(&[b"ARGREP", b"a", b"0", b"1", b"RE", b""]),
6757 "-ERR regular expression is empty\r\n"
6758 );
6759 assert_eq!(
6760 f.run(&[b"ARGREP", b"a", b"0", b"1", b"RE", b"(a"]),
6761 "-ERR invalid regular expression: Missing ')'\r\n"
6762 );
6763 assert_eq!(
6764 f.run(&[b"ARGREP", b"a", b"0", b"1", b"RE", br"(a)\1"]),
6765 "-ERR regular expression backreferences are not supported\r\n"
6766 );
6767 let arity = "-ERR wrong number of arguments for 'argrep' command\r\n";
6770 assert_eq!(f.run(&[b"ARGREP", b"a", b"0", b"1", b"EXACT"]), arity);
6771 assert_eq!(f.run(&[b"ARGREP", b"a", b"0", b"1"]), arity);
6772 }
6773
6774 #[test]
6775 fn an_op_reduces_a_range_to_one_number() {
6776 let mut f = Fixture::new();
6777 f.run(&[b"ARSET", b"a", b"0", b"1", b"2.5", b"word", b"-4"]);
6778 assert_eq!(
6779 f.run(&[b"AROP", b"a", b"0", b"10", b"SUM"]),
6780 "$4\r\n-0.5\r\n"
6781 );
6782 assert_eq!(f.run(&[b"AROP", b"a", b"0", b"10", b"min"]), "$2\r\n-4\r\n");
6783 assert_eq!(
6784 f.run(&[b"AROP", b"a", b"0", b"10", b"MAX"]),
6785 "$3\r\n2.5\r\n"
6786 );
6787 assert_eq!(f.run(&[b"AROP", b"a", b"0", b"10", b"USED"]), ":4\r\n");
6788 assert_eq!(
6789 f.run(&[b"AROP", b"a", b"0", b"10", b"MATCH", b"word"]),
6790 ":1\r\n"
6791 );
6792 f.run(&[b"ARSET", b"t", b"0", b"0.1", b"0.2"]);
6795 assert_eq!(
6796 f.run(&[b"AROP", b"t", b"0", b"10", b"SUM"]),
6797 "$19\r\n0.30000000000000004\r\n"
6798 );
6799 assert_eq!(f.run(&[b"ZADD", b"z", b"0.3", b"m"]), ":1\r\n");
6800 assert_eq!(f.run(&[b"ZSCORE", b"z", b"m"]), "$3\r\n0.3\r\n");
6801
6802 f.run(&[b"ARSET", b"w", b"0", b"word"]);
6805 assert_eq!(f.run(&[b"AROP", b"w", b"0", b"10", b"SUM"]), "$-1\r\n");
6806 assert_eq!(f.run(&[b"AROP", b"nope", b"0", b"10", b"SUM"]), "$-1\r\n");
6807 assert_eq!(f.run(&[b"AROP", b"nope", b"0", b"10", b"USED"]), ":0\r\n");
6808
6809 assert_eq!(
6810 f.run(&[b"AROP", b"a", b"0", b"10", b"NOPE"]),
6811 "-ERR unknown operation\r\n"
6812 );
6813 assert_eq!(
6814 f.run(&[b"AROP", b"a", b"0", b"10", b"MATCH"]),
6815 "-ERR MATCH requires a value argument\r\n"
6816 );
6817 assert_eq!(
6818 f.run(&[b"AROP", b"a", b"0", b"10", b"SUM", b"extra"]),
6819 "-ERR wrong number of arguments for 'arop' command\r\n"
6820 );
6821 }
6822
6823 #[test]
6824 fn the_info_is_a_map_and_a_missing_key_is_an_error() {
6825 let mut f = Fixture::new();
6826 assert_eq!(f.run(&[b"ARINFO", b"nope"]), "-ERR no such key\r\n");
6827 f.run(&[b"ARINSERT", b"a", b"x", b"y"]);
6828 let short = f.run(&[b"ARINFO", b"a"]);
6829 assert!(
6830 short.starts_with("*14\r\n"),
6831 "seven pairs on RESP2: {short}"
6832 );
6833 assert!(short.contains("$5\r\ncount\r\n:2\r\n"), "{short}");
6834 assert!(
6835 short.contains("$17\r\nnext-insert-index\r\n:2\r\n"),
6836 "{short}"
6837 );
6838 assert!(short.contains("$10\r\nslice-size\r\n:4096\r\n"), "{short}");
6839 let full = f.run(&[b"ARINFO", b"a", b"full"]);
6840 assert!(full.starts_with("*24\r\n"), "twelve pairs: {full}");
6841 assert!(full.contains("$12\r\ndense-slices\r\n:0\r\n"), "{full}");
6844 assert!(full.contains("$13\r\nsparse-slices\r\n:1\r\n"), "{full}");
6845 assert!(
6846 full.contains("$14\r\navg-dense-size\r\n$1\r\n0\r\n"),
6847 "{full}"
6848 );
6849 assert_eq!(f.run(&[b"ARINFO", b"a", b"nope"]), "-ERR syntax error\r\n");
6850
6851 let mut g = Fixture::new();
6853 g.run(&[b"HELLO", b"3"]);
6854 g.run(&[b"ARINSERT", b"a", b"x"]);
6855 let map = g.run(&[b"ARINFO", b"a", b"FULL"]);
6856 assert!(map.starts_with("%12\r\n"), "{map}");
6857 assert!(map.contains("$5\r\ncount\r\n:1\r\n"), "{map}");
6858 assert!(map.contains("$14\r\navg-dense-size\r\n,0\r\n"), "{map}");
6859 }
6860
6861 #[test]
6862 fn a_double_on_the_wire_is_written_the_way_redis_writes_one() {
6863 let mut f = Fixture::new();
6864 for (score, want) in [
6867 ("3", "3"),
6868 ("3.5", "3.5"),
6869 ("0.3", "0.3"),
6870 ("1e30", "1e+30"),
6871 ("1e19", "1e+19"),
6872 ("1e-7", "1e-7"),
6873 ("0.000001", "0.000001"),
6874 ("4611686018427387904", "4611686018427387904"),
6875 ("-0", "-0"),
6876 ] {
6877 f.run(&[b"ZADD", b"z", score.as_bytes(), b"m"]);
6878 assert_eq!(
6879 f.run(&[b"ZSCORE", b"z", b"m"]),
6880 format!("${}\r\n{want}\r\n", want.len()),
6881 "score {score}"
6882 );
6883 }
6884
6885 let mut g = Fixture::new();
6888 g.run(&[b"HELLO", b"3"]);
6889 g.run(&[b"ZADD", b"z", b"1e30", b"m"]);
6890 assert_eq!(g.run(&[b"ZSCORE", b"z", b"m"]), ",1e+30\r\n");
6891 assert_eq!(
6896 g.run(&[b"INCRBYFLOAT", b"s", b"1e30"]),
6897 "$31\r\n1000000000000000000000000000000\r\n"
6898 );
6899 assert_eq!(g.run(&[b"INCRBYFLOAT", b"t", b"0.1"]), "$3\r\n0.1\r\n");
6900 assert_eq!(
6901 g.run(&[b"HINCRBYFLOAT", b"h", b"f", b"1e19"]),
6902 "$20\r\n10000000000000000000\r\n"
6903 );
6904 }
6905}