1mod args;
55mod arrays;
56mod blocking;
57mod cpu;
58mod hashes;
59mod keyspace;
60mod lists;
61mod scan;
62mod scripting;
63mod server;
64mod sets;
65mod strings;
66pub mod table;
67mod zsets;
68
69pub use args::Args;
70pub use blocking::{Parked, Waiters};
71pub use table::{COMMANDS, Spec, arity_ok, lookup};
72
73use crate::reply::Out;
74use yo_common::{Code, Error};
75use yo_kv::{Clock, Keyspace};
76
77pub const DATABASES: usize = 16;
84
85const ALL_DATABASES: u64 = if DATABASES == 64 {
92 u64::MAX
93} else {
94 (1u64 << DATABASES) - 1
95};
96const _: () = assert!(DATABASES <= 64);
97
98const EVICT_BUDGET: usize = 64;
110
111const OOM: &[u8] = b"command not allowed when used memory > 'maxmemory'.";
116
117#[derive(Debug, Clone, Copy, PartialEq, Eq)]
119pub enum Flow {
120 Continue,
122 Close,
124 Block,
131}
132
133#[derive(Debug, Clone, Copy, Default)]
139pub struct Stats {
140 pub clients: u64,
142 pub connections: u64,
144 pub commands: u64,
146}
147
148pub struct Server {
155 dbs: Vec<Keyspace>,
156 clock: Clock,
157 started_ms: u64,
158 next_db: usize,
161 dirty: u64,
171 conn_bytes: usize,
173 maxmemory: u64,
178 used: usize,
190 evict_db: usize,
196 expire_db: usize,
203 expire_ms: u64,
206 waiters: Waiters,
208 pub stats: Stats,
210}
211
212impl Server {
213 #[must_use]
215 pub fn new() -> Server {
216 let clock = Clock::system();
217 Server {
218 dbs: (0..DATABASES)
219 .map(|_| Keyspace::with_clock(clock))
220 .collect(),
221 clock,
222 started_ms: clock.now_ms(),
223 next_db: 0,
224 dirty: ALL_DATABASES,
225 conn_bytes: 0,
226 maxmemory: 0,
227 used: 0,
228 evict_db: 0,
229 expire_db: 0,
230 expire_ms: 0,
231 waiters: Waiters::default(),
232 stats: Stats::default(),
233 }
234 }
235
236 #[must_use]
238 pub fn with_clock(clock: Clock) -> Server {
239 Server {
240 dbs: (0..DATABASES)
241 .map(|_| Keyspace::with_clock(clock))
242 .collect(),
243 clock,
244 started_ms: clock.now_ms(),
245 next_db: 0,
246 dirty: ALL_DATABASES,
247 conn_bytes: 0,
248 maxmemory: 0,
249 used: 0,
250 evict_db: 0,
251 expire_db: 0,
252 expire_ms: 0,
253 waiters: Waiters::default(),
254 stats: Stats::default(),
255 }
256 }
257
258 pub fn db(&mut self, i: usize) -> &mut Keyspace {
266 self.dirty |= 1u64 << i;
269 &mut self.dbs[i]
270 }
271
272 #[must_use]
283 pub fn db_ref(&self, i: usize) -> &Keyspace {
284 &self.dbs[i]
285 }
286
287 pub fn refresh_clock(&mut self) {
293 self.clock.refresh();
294 let now = self.clock.now_ms();
295 for db in &mut self.dbs {
296 db.clock_mut().set(now);
297 }
298 }
299
300 pub fn set_clock_ms(&mut self, ms: u64) {
308 self.clock.set(ms);
309 for db in &mut self.dbs {
310 db.clock_mut().set(ms);
311 }
312 }
313
314 #[must_use]
316 pub fn uptime_secs(&self) -> u64 {
317 self.clock.now_ms().saturating_sub(self.started_ms) / 1000
318 }
319
320 #[must_use]
328 pub fn memory_bytes(&self) -> usize {
329 self.dbs.iter().map(Keyspace::memory_bytes).sum::<usize>() + self.conn_bytes
330 }
331
332 #[must_use]
338 pub fn dataset_bytes(&self) -> usize {
339 self.dbs
340 .iter()
341 .map(|db| db.map().arena().live_bytes() as usize)
342 .sum()
343 }
344
345 #[must_use]
347 pub fn arena_bytes(&self) -> usize {
348 self.dbs
349 .iter()
350 .map(|db| db.map().arena().reserved_bytes() as usize)
351 .sum()
352 }
353
354 #[must_use]
356 pub fn index_bytes(&self) -> usize {
357 self.dbs
358 .iter()
359 .map(|db| db.map().index().memory_bytes())
360 .sum()
361 }
362
363 #[must_use]
365 pub fn segment_count(&self) -> usize {
366 self.dbs
367 .iter()
368 .map(|db| db.map().arena().resident_segments())
369 .sum()
370 }
371
372 #[must_use]
374 pub const fn conn_bytes(&self) -> usize {
375 self.conn_bytes
376 }
377
378 pub fn note_conn_bytes(&mut self, delta: isize) {
386 self.conn_bytes = self.conn_bytes.saturating_add_signed(delta);
387 }
388
389 #[must_use]
391 pub fn expired_keys(&self) -> u64 {
392 self.dbs.iter().map(Keyspace::expired_keys).sum()
393 }
394
395 #[must_use]
397 pub fn evicted_keys(&self) -> u64 {
398 self.dbs.iter().map(Keyspace::evicted_keys).sum()
399 }
400
401 #[must_use]
403 pub const fn maxmemory(&self) -> u64 {
404 self.maxmemory
405 }
406
407 pub fn set_maxmemory(&mut self, bytes: u64) {
421 self.maxmemory = bytes;
422 for db in &mut self.dbs {
423 db.track_memory(bytes != 0);
424 }
425 self.used = self.settled_memory();
426 }
427
428 pub fn refresh_memory(&mut self) {
433 if self.maxmemory != 0 {
434 self.used = self.settled_memory();
435 }
436 }
437
438 fn settled_memory(&mut self) -> usize {
445 self.dbs
446 .iter_mut()
447 .map(Keyspace::settled_memory_bytes)
448 .sum::<usize>()
449 + self.conn_bytes
450 }
451
452 pub fn make_room(&mut self) -> bool {
486 if self.maxmemory == 0 || self.used as u64 <= self.maxmemory {
487 return true;
488 }
489 self.used = self.settled_memory();
494 let mut budget = EVICT_BUDGET;
495 while self.used as u64 > self.maxmemory {
496 if !self.evict_step() {
497 return false;
498 }
499 self.compact_hard_step();
500 self.used = self.settled_memory();
501 budget -= 1;
502 if budget == 0 {
503 break;
504 }
505 }
506 true
507 }
508
509 fn evict_step(&mut self) -> bool {
516 for turn in 0..self.dbs.len() {
517 let i = (self.evict_db + turn) % self.dbs.len();
518 if self.dbs[i].evict_one() {
519 self.evict_db = (i + 1) % self.dbs.len();
520 self.dirty |= 1u64 << i;
521 return true;
522 }
523 }
524 false
525 }
526
527 pub fn expire_slice(&mut self, budget: usize) -> usize {
542 let now = self.clock.now_ms();
543 if now == self.expire_ms {
544 return 0;
545 }
546 self.expire_ms = now;
547 self.expire_step(budget)
548 }
549
550 pub fn expire_step(&mut self, budget: usize) -> usize {
566 let mut spent = 0;
567 for turn in 0..self.dbs.len() {
568 if spent >= budget {
569 break;
570 }
571 let i = (self.expire_db + turn) % self.dbs.len();
572 let c = self.dbs[i].expire_cycle(budget - spent);
573 spent += c.examined;
574 if c.expired > 0 {
575 self.expire_db = (i + 1) % self.dbs.len();
576 self.dirty |= 1u64 << i;
577 }
578 }
579 spent
580 }
581
582 fn compact_hard_step(&mut self) -> Option<usize> {
588 for turn in 0..self.dbs.len() {
589 let i = (self.next_db + turn) % self.dbs.len();
590 if let Some(moved) = self.dbs[i].compact_hard() {
591 self.next_db = (i + 1) % self.dbs.len();
592 return Some(moved);
593 }
594 }
595 None
596 }
597
598 pub fn compact_step(&mut self) -> Option<usize> {
611 for turn in 0..self.dbs.len() {
612 let i = (self.next_db + turn) % self.dbs.len();
613 if self.dirty & (1 << i) == 0 {
617 continue;
618 }
619 if let Some(moved) = self.dbs[i].compact_step() {
620 self.next_db = (i + 1) % self.dbs.len();
621 return Some(moved);
622 }
623 self.dirty &= !(1u64 << i);
624 }
625 None
626 }
627}
628
629impl Default for Server {
630 fn default() -> Server {
631 Server::new()
632 }
633}
634
635pub struct Session {
637 db: usize,
638 id: u64,
639 name: Vec<u8>,
640}
641
642impl Session {
643 #[must_use]
645 pub fn new(id: u64) -> Session {
646 Session {
647 db: 0,
648 id,
649 name: Vec::new(),
650 }
651 }
652
653 #[must_use]
655 pub const fn id(&self) -> u64 {
656 self.id
657 }
658
659 #[must_use]
661 pub const fn db(&self) -> usize {
662 self.db
663 }
664
665 #[must_use]
667 pub fn name(&self) -> &[u8] {
668 &self.name
669 }
670
671 pub fn reset(&mut self) {
676 self.db = 0;
677 self.name.clear();
678 }
679
680 fn set_name(&mut self, name: &[u8]) {
682 yo_alloc::allow(|| {
683 self.name.clear();
684 self.name.extend_from_slice(name);
685 });
686 }
687}
688
689pub fn execute(server: &mut Server, session: &mut Session, args: Args<'_>, out: &mut Out) -> Flow {
694 if args.is_empty() {
697 return Flow::Continue;
698 }
699 server.stats.commands += 1;
700
701 let Some(spec) = lookup(args.name()) else {
702 write_error(out, &args::unknown_command(args));
703 return Flow::Continue;
704 };
705 if !arity_ok(spec, args.len()) {
706 write_error(out, &args::wrong_arity(spec.name));
707 return Flow::Continue;
708 }
709
710 if server.maxmemory != 0 && !server.make_room() && spec.flags.contains(&"denyoom") {
720 out.error_line(b"OOM ", OOM);
721 return Flow::Continue;
722 }
723
724 server.dirty |= match spec.group {
731 "string" | "set" | "hash" | "list" | "zset" | "array" => 1u64 << session.db,
732 _ => ALL_DATABASES,
733 };
734
735 let mark = out.len();
736 let done = if spec.flags.contains(&"blocking") {
743 blocking::execute(server, session, spec, args, out)
744 } else {
745 match spec.group {
746 "string" => {
747 let db = session.db;
748 strings::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
749 }
750 "set" => {
751 let db = session.db;
752 sets::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
753 }
754 "hash" => {
755 let db = session.db;
756 hashes::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
757 }
758 "list" => {
759 let db = session.db;
760 lists::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
761 }
762 "zset" => {
763 let db = session.db;
764 zsets::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
765 }
766 "array" => {
767 let db = session.db;
768 arrays::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
769 }
770 "keyspace" => keyspace::execute(&mut server.dbs, session.db, spec, args, out)
773 .map(|()| Flow::Continue),
774 "scripting" => scripting::execute(spec, args, out).map(|()| Flow::Continue),
775 _ => server::execute(server, session, spec, args, out),
776 }
777 };
778 match done {
779 Ok(flow) => flow,
780 Err(e) => {
781 out.truncate(mark);
782 write_error(out, &e);
783 Flow::Continue
784 }
785 }
786}
787
788fn write_error(out: &mut Out, e: &Error) {
798 let prefix: &[u8] = match e.code() {
799 Code::WrongType => b"WRONGTYPE ",
800 _ => b"ERR ",
801 };
802 out.error_line(prefix, e.message().as_bytes());
803}
804
805#[cfg(test)]
806mod tests {
807 use super::*;
808 use crate::proto::{Limits, Proto};
809 use crate::request::Argv;
810
811 pub(crate) fn encode(parts: &[&[u8]]) -> Vec<u8> {
816 let mut wire = format!("*{}\r\n", parts.len()).into_bytes();
817 for p in parts {
818 wire.extend_from_slice(format!("${}\r\n", p.len()).as_bytes());
819 wire.extend_from_slice(p);
820 wire.extend_from_slice(b"\r\n");
821 }
822 wire
823 }
824
825 struct Fixture {
827 server: Server,
828 session: Session,
829 argv: Argv,
830 out: Out,
831 }
832
833 impl Fixture {
834 fn new() -> Fixture {
835 Fixture {
836 server: Server::new(),
837 session: Session::new(7),
838 argv: Argv::new(),
839 out: Out::new(Proto::Resp2),
840 }
841 }
842
843 fn run(&mut self, parts: &[&[u8]]) -> String {
845 self.flow(parts).1
846 }
847
848 fn advance(&mut self, ms: u64) {
850 for db in 0..DATABASES {
851 self.server.db(db).clock_mut().advance(ms);
852 }
853 }
854
855 fn flow(&mut self, parts: &[&[u8]]) -> (Flow, String) {
857 let wire = encode(parts);
858 self.argv.decode(&wire, &Limits::default()).unwrap();
859 self.out.clear();
860 let flow = execute(
861 &mut self.server,
862 &mut self.session,
863 Args::new(&self.argv, &wire),
864 &mut self.out,
865 );
866 (
867 flow,
868 String::from_utf8_lossy(self.out.as_slice()).into_owned(),
869 )
870 }
871 }
872
873 #[test]
877 fn rewriting_the_same_keys_does_not_grow_the_server() {
878 let mut f = Fixture::new();
879 let val = vec![b'v'; 1024];
880 let keys: Vec<Vec<u8>> = (0..64).map(|i| format!("key:{i}").into_bytes()).collect();
881
882 for k in &keys {
883 f.run(&[b"SET", k, &val]);
884 }
885 f.server.compact_step();
886 let after_first = f.server.memory_bytes();
887
888 for _ in 0..500 {
893 for k in &keys {
894 f.run(&[b"SET", k, &val]);
895 }
896 f.server.compact_step();
897 }
898
899 assert!(
900 f.server.memory_bytes() <= after_first * 2,
901 "held {} after five hundred passes against {after_first} after one",
902 f.server.memory_bytes()
903 );
904 assert_eq!(f.run(&[b"DBSIZE"]), format!(":{}\r\n", keys.len()));
905 assert_eq!(f.run(&[b"STRLEN", b"key:7"]), ":1024\r\n");
906 }
907
908 #[test]
922 fn a_database_nobody_started_on_is_still_collected() {
923 let mut f = Fixture::new();
924 assert_eq!(f.run(&[b"SELECT", b"9"]), "+OK\r\n");
925 let val = vec![b'v'; 1024];
926 let keys: Vec<Vec<u8>> = (0..64).map(|i| format!("key:{i}").into_bytes()).collect();
927
928 for k in &keys {
929 f.run(&[b"SET", k, &val]);
930 }
931 while f.server.compact_step().is_some() {}
932 assert_eq!(
933 f.server.dirty & (1 << 9),
934 0,
935 "database nine was drained and should not be asked again until it is written to"
936 );
937 let after_first = f.server.memory_bytes();
938
939 for _ in 0..500 {
940 for k in &keys {
941 f.run(&[b"SET", k, &val]);
942 }
943 f.server.compact_step();
944 }
945
946 assert!(
947 f.server.memory_bytes() <= after_first * 2,
948 "held {} after five hundred passes against {after_first} after one",
949 f.server.memory_bytes()
950 );
951 assert_eq!(f.run(&[b"DBSIZE"]), format!(":{}\r\n", keys.len()));
952 assert_eq!(f.run(&[b"STRLEN", b"key:7"]), ":1024\r\n");
953 f.run(&[b"SELECT", b"0"]);
955 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
956 }
957
958 #[test]
959 fn a_command_goes_from_bytes_to_bytes() {
960 let mut f = Fixture::new();
961 assert_eq!(f.run(&[b"SET", b"k", b"v"]), "+OK\r\n");
962 assert_eq!(f.run(&[b"GET", b"k"]), "$1\r\nv\r\n");
963 assert_eq!(f.run(&[b"GET", b"nosuch"]), "$-1\r\n");
964 assert_eq!(f.run(&[b"STRLEN", b"k"]), ":1\r\n");
965 assert_eq!(f.run(&[b"set", b"k", b"v2", b"xx"]), "+OK\r\n");
967 assert_eq!(f.run(&[b"GET", b"k"]), "$2\r\nv2\r\n");
968 }
969
970 #[test]
971 fn deleting_counts_keys_removed_and_existing_counts_arguments_matched() {
972 let mut f = Fixture::new();
973 f.run(&[b"MSET", b"a", b"1", b"b", b"2", b"c", b"3"]);
974 assert_eq!(f.run(&[b"EXISTS", b"a", b"a", b"nosuch"]), ":2\r\n");
977 assert_eq!(f.run(&[b"DEL", b"a", b"a", b"nosuch"]), ":1\r\n");
978 assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
979 assert_eq!(f.run(&[b"UNLINK", b"b", b"c"]), ":2\r\n");
981 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
982 }
983
984 #[test]
985 fn type_is_a_simple_string_and_says_none_for_a_key_that_is_not_there() {
986 let mut f = Fixture::new();
987 f.run(&[b"SET", b"k", b"v"]);
988 assert_eq!(f.run(&[b"TYPE", b"k"]), "+string\r\n");
991 assert_eq!(f.run(&[b"TYPE", b"nosuch"]), "+none\r\n");
992 }
993
994 #[test]
995 fn touch_counts_the_way_exists_counts() {
996 let mut f = Fixture::new();
997 f.run(&[b"MSET", b"a", b"1", b"b", b"2"]);
998 assert_eq!(f.run(&[b"TOUCH", b"a", b"b"]), ":2\r\n");
999 assert_eq!(
1000 f.run(&[b"TOUCH", b"a", b"a"]),
1001 ":2\r\n",
1002 "twice counts twice"
1003 );
1004 assert_eq!(f.run(&[b"TOUCH", b"a", b"nosuch"]), ":1\r\n");
1005 assert_eq!(f.run(&[b"TOUCH", b"nosuch"]), ":0\r\n");
1006 }
1007
1008 #[test]
1009 fn a_rename_moves_the_deadline_with_the_value_and_drops_the_one_it_lands_on() {
1010 let mut f = Fixture::new();
1011 f.run(&[b"SET", b"a", b"v1", b"EX", b"100"]);
1012 f.run(&[b"SET", b"b", b"v2", b"EX", b"500"]);
1013
1014 assert_eq!(f.run(&[b"RENAME", b"a", b"b"]), "+OK\r\n");
1015 assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv1\r\n");
1016 assert_eq!(
1017 f.run(&[b"TTL", b"b"]),
1018 ":100\r\n",
1019 "the source's and not b's"
1020 );
1021 assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
1022 }
1023
1024 #[test]
1025 fn a_rename_with_no_source_is_an_error_and_not_a_zero() {
1026 let mut f = Fixture::new();
1027 assert_eq!(f.run(&[b"RENAME", b"a", b"b"]), "-ERR no such key\r\n");
1028 assert_eq!(f.run(&[b"RENAMENX", b"a", b"a"]), "-ERR no such key\r\n");
1031 }
1032
1033 #[test]
1034 fn renamenx_refuses_a_taken_name_including_the_one_it_already_has() {
1035 let mut f = Fixture::new();
1036 f.run(&[b"MSET", b"a", b"v1", b"b", b"v2"]);
1037
1038 assert_eq!(f.run(&[b"RENAMENX", b"a", b"b"]), ":0\r\n");
1039 assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv2\r\n");
1040 assert_eq!(f.run(&[b"RENAMENX", b"a", b"a"]), ":0\r\n");
1043 assert_eq!(f.run(&[b"RENAME", b"a", b"a"]), "+OK\r\n");
1044 assert_eq!(f.run(&[b"RENAMENX", b"a", b"c"]), ":1\r\n");
1045 assert_eq!(f.run(&[b"GET", b"c"]), "$2\r\nv1\r\n");
1046 }
1047
1048 #[test]
1049 fn renaming_a_set_does_not_touch_a_member() {
1050 let mut f = Fixture::new();
1051 for i in 0..300 {
1052 f.run(&[b"SADD", b"s", format!("m{i}").as_bytes()]);
1053 }
1054 let before = f.server.memory_bytes();
1055
1056 assert_eq!(f.run(&[b"RENAME", b"s", b"t"]), "+OK\r\n");
1057 assert_eq!(f.run(&[b"SCARD", b"t"]), ":300\r\n");
1058 assert_eq!(f.run(&[b"TYPE", b"t"]), "+set\r\n");
1059 assert!(
1060 f.server.memory_bytes().abs_diff(before) < 256,
1061 "the members were copied: {} against {before}",
1062 f.server.memory_bytes()
1063 );
1064 }
1065
1066 #[test]
1067 fn a_copy_is_a_second_value_and_not_a_second_name() {
1068 let mut f = Fixture::new();
1069 f.run(&[b"SADD", b"s", b"m1", b"m2"]);
1070
1071 assert_eq!(f.run(&[b"COPY", b"s", b"t"]), ":1\r\n");
1072 f.run(&[b"SADD", b"t", b"m3"]);
1073 assert_eq!(f.run(&[b"SCARD", b"s"]), ":2\r\n", "the original is intact");
1074 assert_eq!(f.run(&[b"SCARD", b"t"]), ":3\r\n");
1075 }
1076
1077 #[test]
1087 fn every_type_can_be_copied() {
1088 let mut f = Fixture::new();
1089 f.run(&[b"SET", b"str", b"v1"]);
1090 f.run(&[b"SADD", b"set", b"m1"]);
1091 f.run(&[b"HSET", b"hash", b"f", b"v"]);
1092 f.run(&[b"RPUSH", b"list", b"a", b"b"]);
1093 f.run(&[b"ZADD", b"zset", b"1", b"m1"]);
1094
1095 for name in [
1096 &b"str"[..],
1097 &b"set"[..],
1098 &b"hash"[..],
1099 &b"list"[..],
1100 &b"zset"[..],
1101 ] {
1102 let dst = [name, b":copy"].concat();
1103 assert_eq!(
1104 f.run(&[b"COPY", name, &dst]),
1105 ":1\r\n",
1106 "copying {}",
1107 String::from_utf8_lossy(name)
1108 );
1109 assert_eq!(f.run(&[b"TYPE", name]), f.run(&[b"TYPE", &dst]));
1110 }
1111
1112 assert_eq!(f.run(&[b"LRANGE", b"list:copy", b"0", b"-1"]), {
1113 let mut want = String::from("*2\r\n");
1114 want.push_str("$1\r\na\r\n$1\r\nb\r\n");
1115 want
1116 });
1117 assert_eq!(f.run(&[b"ZSCORE", b"zset:copy", b"m1"]), "$1\r\n1\r\n");
1118
1119 f.run(&[b"RPUSH", b"list:copy", b"c"]);
1121 assert_eq!(f.run(&[b"LLEN", b"list"]), ":2\r\n");
1122 assert_eq!(f.run(&[b"LLEN", b"list:copy"]), ":3\r\n");
1123 }
1124
1125 #[test]
1126 fn a_copy_refuses_a_taken_destination_until_it_is_told_it_can_have_it() {
1127 let mut f = Fixture::new();
1128 f.run(&[b"SET", b"a", b"v1", b"EX", b"100"]);
1129 f.run(&[b"SET", b"b", b"v2"]);
1130
1131 assert_eq!(f.run(&[b"COPY", b"a", b"b"]), ":0\r\n");
1132 assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv2\r\n");
1133 assert_eq!(f.run(&[b"COPY", b"a", b"b", b"REPLACE"]), ":1\r\n");
1134 assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv1\r\n");
1135 assert_eq!(f.run(&[b"TTL", b"b"]), ":100\r\n", "the deadline came too");
1136 assert_eq!(f.run(&[b"COPY", b"nosuch", b"z"]), ":0\r\n");
1137 }
1138
1139 #[test]
1140 fn a_copy_into_another_database_is_a_copy_and_onto_itself_there_is_too() {
1141 let mut f = Fixture::new();
1142 f.run(&[b"SET", b"a", b"v1"]);
1143
1144 assert_eq!(f.run(&[b"COPY", b"a", b"a", b"DB", b"1"]), ":1\r\n");
1147 f.run(&[b"SELECT", b"1"]);
1148 assert_eq!(f.run(&[b"GET", b"a"]), "$2\r\nv1\r\n");
1149 assert_eq!(
1150 f.run(&[b"COPY", b"a", b"a", b"DB", b"0"]),
1151 ":0\r\n",
1152 "taken"
1153 );
1154 assert_eq!(
1155 f.run(&[b"COPY", b"a", b"a", b"DB", b"0", b"REPLACE"]),
1156 ":1\r\n"
1157 );
1158 }
1159
1160 #[test]
1161 fn copy_checks_its_options_before_it_looks_for_anything() {
1162 let mut f = Fixture::new();
1163 assert_eq!(
1166 f.run(&[b"COPY", b"a", b"b", b"DB", b"99"]),
1167 "-ERR DB index is out of range\r\n"
1168 );
1169 assert_eq!(
1170 f.run(&[b"COPY", b"a", b"b", b"DB", b"-1"]),
1171 "-ERR DB index is out of range\r\n"
1172 );
1173 assert_eq!(
1174 f.run(&[b"COPY", b"a", b"b", b"DB", b"x"]),
1175 "-ERR value is not an integer or out of range\r\n"
1176 );
1177 assert_eq!(
1178 f.run(&[b"COPY", b"a", b"b", b"nonsense"]),
1179 "-ERR syntax error\r\n"
1180 );
1181 assert_eq!(
1182 f.run(&[b"COPY", b"a", b"a"]),
1183 "-ERR source and destination objects are the same\r\n"
1184 );
1185 assert_eq!(
1187 f.run(&[b"COPY", b"a", b"b", b"dB", b"1", b"rEpLaCe", b"db", b"2"]),
1188 ":0\r\n"
1189 );
1190 }
1191
1192 #[test]
1193 fn time_is_two_bulk_strings_and_moves() {
1194 let mut f = Fixture::new();
1195 let first = f.run(&[b"TIME"]);
1196 assert!(first.starts_with("*2\r\n$"), "got {first}");
1197 let parts: Vec<&str> = first.split("\r\n").collect();
1198 let secs: i64 = parts[2].parse().expect("seconds as decimal text");
1199 let micros: i64 = parts[4].parse().expect("microseconds as decimal text");
1200 assert!(secs > 1_700_000_000, "a real wall clock, got {secs}");
1201 assert!((0..1_000_000).contains(µs), "got {micros}");
1202 assert_ne!(first, f.run(&[b"TIME"]));
1206 }
1207
1208 #[test]
1209 fn a_keyspace_scan_walks_every_key_once() {
1210 let mut f = Fixture::new();
1211 for i in 0..500 {
1212 f.run(&[b"SET", format!("k{i}").as_bytes(), b"v"]);
1213 }
1214
1215 let mut seen: Vec<String> = Vec::new();
1216 let mut cursor = "0".to_owned();
1217 let mut calls = 0;
1218 loop {
1219 let (next, keys) = scan_reply(&f.run(&[b"SCAN", cursor.as_bytes(), b"COUNT", b"32"]));
1220 seen.extend(keys);
1221 cursor = next;
1222 calls += 1;
1223 assert!(calls < 10_000, "the cursor is not advancing");
1224 if cursor == "0" {
1225 break;
1226 }
1227 }
1228
1229 seen.sort();
1230 seen.dedup();
1231 assert_eq!(seen.len(), 500, "every key once and only once");
1232 assert!(calls > 1, "500 keys came back in one batch");
1235 }
1236
1237 #[test]
1238 fn a_scan_narrows_by_pattern_and_by_type() {
1239 let mut f = Fixture::new();
1240 f.run(&[b"SET", b"str", b"v"]);
1241 f.run(&[b"SADD", b"members", b"a"]);
1242 f.run(&[b"HSET", b"fields", b"f", b"v"]);
1243
1244 let all = |f: &mut Fixture, args: &[&[u8]]| {
1245 let mut out: Vec<String> = Vec::new();
1246 let mut cursor = "0".to_owned();
1247 loop {
1248 let mut line: Vec<&[u8]> = vec![b"SCAN", cursor.as_bytes()];
1249 line.extend_from_slice(args);
1250 let (next, keys) = scan_reply(&f.run(&line));
1251 out.extend(keys);
1252 cursor = next;
1253 if cursor == "0" {
1254 break;
1255 }
1256 }
1257 out.sort();
1258 out
1259 };
1260
1261 assert_eq!(all(&mut f, &[]), ["fields", "members", "str"]);
1262 assert_eq!(all(&mut f, &[b"MATCH", b"*e*"]), ["fields", "members"]);
1263 assert_eq!(all(&mut f, &[b"TYPE", b"set"]), ["members"]);
1264 assert_eq!(all(&mut f, &[b"TYPE", b"HASH"]), ["fields"]);
1266 assert!(all(&mut f, &[b"TYPE", b"list"]).is_empty());
1268 assert!(all(&mut f, &[b"TYPE", b"banana"]).is_empty());
1269 assert!(all(&mut f, &[b"MATCH", b"str*", b"TYPE", b"set"]).is_empty());
1271 }
1272
1273 #[test]
1274 fn a_scan_says_what_is_wrong_with_it() {
1275 let mut f = Fixture::new();
1276 assert_eq!(f.run(&[b"SCAN", b"nope"]), "-ERR invalid cursor\r\n");
1277 assert_eq!(f.run(&[b"SCAN", b"-1"]), "-ERR invalid cursor\r\n");
1278 assert_eq!(f.run(&[b"SCAN", b"0", b"MATCH"]), "-ERR syntax error\r\n");
1279 assert_eq!(
1280 f.run(&[b"SCAN", b"0", b"COUNT", b"0"]),
1281 "-ERR syntax error\r\n"
1282 );
1283 assert_eq!(
1284 f.run(&[b"SCAN", b"0", b"COUNT", b"x"]),
1285 "-ERR value is not an integer or out of range\r\n"
1286 );
1287 assert_eq!(
1288 f.run(&[b"SCAN", b"0", b"WAT", b"1"]),
1289 "-ERR syntax error\r\n"
1290 );
1291 assert!(f.run(&[b"SCAN", b"18446744073709551615"]).starts_with("*2"));
1296 }
1297
1298 #[test]
1299 fn keys_and_randomkey_look_at_the_whole_database() {
1300 let mut f = Fixture::new();
1301 assert_eq!(f.run(&[b"KEYS", b"*"]), "*0\r\n");
1302 assert_eq!(f.run(&[b"RANDOMKEY"]), "$-1\r\n");
1303
1304 for name in ["one", "two", "three"] {
1305 f.run(&[b"SET", name.as_bytes(), b"v"]);
1306 }
1307 assert_eq!(sorted(&f.run(&[b"KEYS", b"*"])), ["one", "three", "two"]);
1308 assert_eq!(sorted(&f.run(&[b"KEYS", b"t*"])), ["three", "two"]);
1309 assert_eq!(f.run(&[b"KEYS", b"nothing"]), "*0\r\n");
1310
1311 for _ in 0..50 {
1312 let got = f.run(&[b"RANDOMKEY"]);
1313 assert!(
1314 ["$3\r\none\r\n", "$3\r\ntwo\r\n", "$5\r\nthree\r\n"].contains(&got.as_str()),
1315 "got {got}"
1316 );
1317 }
1318 }
1319
1320 #[test]
1321 fn a_walk_does_not_answer_keys_that_have_expired() {
1322 let mut f = Fixture::new();
1323 f.run(&[b"SET", b"alive", b"v"]);
1324 f.run(&[b"SET", b"dead", b"v", b"PX", b"1"]);
1325 f.server.db(0).clock_mut().advance(2);
1326 assert_eq!(
1327 f.run(&[b"DBSIZE"]),
1328 ":2\r\n",
1329 "nothing has collected it yet"
1330 );
1331
1332 assert_eq!(f.run(&[b"KEYS", b"*"]), "*1\r\n$5\r\nalive\r\n");
1333 let (_, keys) = scan_reply(&f.run(&[b"SCAN", b"0", b"COUNT", b"1000"]));
1334 assert_eq!(keys, ["alive"]);
1335 for _ in 0..20 {
1336 assert_eq!(f.run(&[b"RANDOMKEY"]), "$5\r\nalive\r\n");
1337 }
1338 assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
1341 }
1342
1343 #[test]
1344 fn a_key_deadline_goes_on_and_comes_back_in_all_four_units() {
1345 let mut f = Fixture::new();
1346 f.run(&[b"SET", b"k", b"v"]);
1347 assert_eq!(f.run(&[b"TTL", b"k"]), ":-1\r\n", "there and no deadline");
1348 assert_eq!(f.run(&[b"TTL", b"nosuch"]), ":-2\r\n", "not there at all");
1349
1350 assert_eq!(f.run(&[b"EXPIRE", b"k", b"100"]), ":1\r\n");
1351 assert_eq!(f.run(&[b"TTL", b"k"]), ":100\r\n");
1352 let ms = int(&f.run(&[b"PTTL", b"k"]));
1353 assert!((99_000..=100_000).contains(&ms), "got {ms}");
1354
1355 let at = int(&f.run(&[b"EXPIRETIME", b"k"]));
1357 let at_ms = int(&f.run(&[b"PEXPIRETIME", b"k"]));
1358 assert_eq!(at, (at_ms + 500) / 1000);
1359 assert!(at_ms > 1_700_000_000_000, "an absolute moment, got {at_ms}");
1360
1361 assert_eq!(f.run(&[b"PERSIST", b"k"]), ":1\r\n");
1362 assert_eq!(f.run(&[b"TTL", b"k"]), ":-1\r\n");
1363 assert_eq!(
1364 f.run(&[b"PERSIST", b"k"]),
1365 ":0\r\n",
1366 "nothing to take off the second time"
1367 );
1368 assert_eq!(f.run(&[b"PERSIST", b"nosuch"]), ":0\r\n");
1369 assert_eq!(
1370 f.run(&[b"GET", b"k"]),
1371 "$1\r\nv\r\n",
1372 "and the value went through all of that untouched"
1373 );
1374 }
1375
1376 #[test]
1377 fn every_type_can_be_given_a_deadline_and_it_is_the_same_deadline() {
1378 let mut f = Fixture::new();
1379 f.run(&[b"SET", b"str", b"v"]);
1380 f.run(&[b"SADD", b"set", b"a", b"b"]);
1381 f.run(&[b"HSET", b"hash", b"f", b"v"]);
1382
1383 for key in [b"str".as_slice(), b"set", b"hash"] {
1384 assert_eq!(f.run(&[b"EXPIRE", key, b"100"]), ":1\r\n");
1385 assert_eq!(f.run(&[b"TTL", key]), ":100\r\n");
1386 }
1387 assert_eq!(f.run(&[b"SCARD", b"set"]), ":2\r\n");
1390 assert_eq!(f.run(&[b"HGET", b"hash", b"f"]), "$1\r\nv\r\n");
1391 assert_eq!(f.run(&[b"GET", b"str"]), "$1\r\nv\r\n");
1392 }
1393
1394 #[test]
1395 fn a_deadline_that_has_already_gone_deletes_the_key_now() {
1396 let mut f = Fixture::new();
1397 for key in [b"a".as_slice(), b"b", b"c", b"d"] {
1398 f.run(&[b"SET", key, b"v"]);
1399 }
1400 assert_eq!(f.run(&[b"EXPIRE", b"a", b"0"]), ":1\r\n");
1404 assert_eq!(f.run(&[b"EXPIRE", b"b", b"-1"]), ":1\r\n");
1405 assert_eq!(f.run(&[b"EXPIREAT", b"c", b"1"]), ":1\r\n");
1406 assert_eq!(f.run(&[b"PEXPIREAT", b"d", b"1"]), ":1\r\n");
1407 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
1408 assert_eq!(
1409 f.run(&[b"EXPIRE", b"a", b"100"]),
1410 ":0\r\n",
1411 "and the key really went, so there is nothing to put a deadline on"
1412 );
1413 }
1414
1415 #[test]
1416 fn the_four_conditions_decide_whether_the_deadline_moves() {
1417 let mut f = Fixture::new();
1418 f.run(&[b"SET", b"k", b"v"]);
1419
1420 assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"XX"]), ":0\r\n");
1421 assert_eq!(f.run(&[b"TTL", b"k"]), ":-1\r\n", "and XX left it alone");
1422 assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"GT"]), ":0\r\n");
1423 assert_eq!(
1424 f.run(&[b"EXPIRE", b"k", b"100", b"LT"]),
1425 ":1\r\n",
1426 "no deadline reads as infinitely far away, so LT passes where GT fails"
1427 );
1428
1429 assert_eq!(f.run(&[b"EXPIRE", b"k", b"50", b"NX"]), ":0\r\n");
1430 assert_eq!(f.run(&[b"EXPIRE", b"k", b"50", b"GT"]), ":0\r\n");
1431 assert_eq!(f.run(&[b"TTL", b"k"]), ":100\r\n");
1432 assert_eq!(f.run(&[b"EXPIRE", b"k", b"50", b"LT"]), ":1\r\n");
1433 assert_eq!(f.run(&[b"EXPIRE", b"k", b"200", b"GT"]), ":1\r\n");
1434 assert_eq!(f.run(&[b"TTL", b"k"]), ":200\r\n");
1435
1436 assert_eq!(f.run(&[b"EXPIRE", b"k", b"0", b"NX"]), ":0\r\n");
1439 assert_eq!(f.run(&[b"EXISTS", b"k"]), ":1\r\n");
1440 assert_eq!(f.run(&[b"EXPIRE", b"k", b"0", b"XX"]), ":1\r\n");
1441 assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n", "and XX let it through");
1442 }
1443
1444 #[test]
1445 fn the_conditions_are_a_set_and_not_a_keyword() {
1446 let mut f = Fixture::new();
1447 f.run(&[b"SET", b"k", b"v"]);
1448
1449 assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"nx"]), ":1\r\n");
1450 assert_eq!(
1451 f.run(&[b"EXPIRE", b"k", b"100", b"nx", b"nx"]),
1452 ":0\r\n",
1453 "the same keyword twice means it once, and NX now has a deadline to fail on"
1454 );
1455
1456 assert_eq!(f.run(&[b"EXPIRE", b"k", b"200", b"xx", b"gt"]), ":1\r\n");
1459 assert_eq!(f.run(&[b"TTL", b"k"]), ":200\r\n");
1460 assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"gt", b"xx"]), ":0\r\n");
1461 assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"XX", b"LT"]), ":1\r\n");
1462 assert_eq!(f.run(&[b"TTL", b"k"]), ":100\r\n");
1463 f.run(&[b"PERSIST", b"k"]);
1464 assert_eq!(
1465 f.run(&[b"EXPIRE", b"k", b"100", b"XX", b"LT"]),
1466 ":0\r\n",
1467 "where LT on its own would have taken it"
1468 );
1469 assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"LT"]), ":1\r\n");
1470 }
1471
1472 #[test]
1473 fn a_key_is_gone_once_its_moment_passes() {
1474 let mut f = Fixture::new();
1475 f.run(&[b"SET", b"k", b"v"]);
1476 f.run(&[b"EXPIRE", b"k", b"100"]);
1477
1478 let at = int(&f.run(&[b"PEXPIRETIME", b"k"]));
1479 f.server.set_clock_ms(at as u64 + 1);
1480 assert_eq!(f.run(&[b"GET", b"k"]), "$-1\r\n");
1481 assert_eq!(f.run(&[b"TTL", b"k"]), ":-2\r\n");
1482 assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
1483 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
1484 }
1485
1486 #[test]
1487 fn the_expiry_commands_refuse_what_a_real_server_refuses() {
1488 let mut f = Fixture::new();
1489 f.run(&[b"SET", b"k", b"v"]);
1490 for (bad, want) in [
1491 (
1492 &[b"EXPIRE".as_slice(), b"k", b"soon"][..],
1493 "-ERR value is not an integer or out of range\r\n",
1494 ),
1495 (
1496 &[b"EXPIRE", b"k", b"100", b"MAYBE"],
1497 "-ERR Unsupported option MAYBE\r\n",
1498 ),
1499 (
1500 &[b"EXPIRE", b"k", b"100", b"NX", b"XX"],
1501 "-ERR NX and XX, GT or LT options at the same time are not compatible\r\n",
1502 ),
1503 (
1504 &[b"EXPIRE", b"k", b"100", b"NX", b"GT"],
1505 "-ERR NX and XX, GT or LT options at the same time are not compatible\r\n",
1506 ),
1507 (
1508 &[b"EXPIRE", b"k", b"100", b"GT", b"LT", b"GT"],
1509 "-ERR GT and LT options at the same time are not compatible\r\n",
1510 ),
1511 (
1514 &[b"EXPIRE", b"k", b"9223372036854775807"],
1515 "-ERR invalid expire time in 'expire' command\r\n",
1516 ),
1517 (
1518 &[b"EXPIREAT", b"k", b"9223372036854775807"],
1519 "-ERR invalid expire time in 'expireat' command\r\n",
1520 ),
1521 (
1522 &[b"PEXPIRE", b"k", b"9223372036854775807"],
1523 "-ERR invalid expire time in 'pexpire' command\r\n",
1524 ),
1525 ] {
1526 assert_eq!(f.run(bad), want, "for {bad:?}");
1527 }
1528 assert_eq!(
1529 f.run(&[b"TTL", b"k"]),
1530 ":-1\r\n",
1531 "and none of those put a deadline on anything"
1532 );
1533
1534 assert_eq!(
1538 f.run(&[b"PEXPIREAT", b"k", b"9223372036854775807"]),
1539 ":1\r\n"
1540 );
1541 assert_eq!(f.run(&[b"PEXPIRETIME", b"k"]), ":70368744177663\r\n");
1542 }
1543
1544 #[test]
1545 fn flushing_empties_this_database_or_every_one_of_them() {
1546 let mut f = Fixture::new();
1547 f.run(&[b"SELECT", b"0"]);
1548 f.run(&[b"MSET", b"a", b"1", b"b", b"2"]);
1549 f.run(&[b"SELECT", b"1"]);
1550 f.run(&[b"SET", b"c", b"3"]);
1551 assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
1552 assert_eq!(f.run(&[b"FLUSHDB", b"async"]), "+OK\r\n");
1555 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
1556 f.run(&[b"SELECT", b"0"]);
1558 assert_eq!(f.run(&[b"DBSIZE"]), ":2\r\n");
1559 assert_eq!(f.run(&[b"FLUSHALL", b"SYNC"]), "+OK\r\n");
1560 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
1561 f.run(&[b"SELECT", b"1"]);
1562 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
1563 assert_eq!(f.run(&[b"FLUSHALL", b"nope"]), "-ERR syntax error\r\n");
1566 assert_eq!(
1567 f.run(&[b"FLUSHDB", b"sync", b"sync"]),
1568 "-ERR syntax error\r\n"
1569 );
1570 }
1571
1572 #[test]
1573 fn the_script_cache_and_the_library_set_answer_for_being_empty() {
1574 let mut f = Fixture::new();
1575 assert_eq!(f.run(&[b"SCRIPT", b"FLUSH"]), "+OK\r\n");
1576 assert_eq!(f.run(&[b"SCRIPT", b"FLUSH", b"async"]), "+OK\r\n");
1577 assert_eq!(f.run(&[b"FUNCTION", b"FLUSH", b"SYNC"]), "+OK\r\n");
1578 assert_eq!(
1581 f.run(&[b"SCRIPT", b"EXISTS", b"aaaa", b"bbbb"]),
1582 "*2\r\n:0\r\n:0\r\n"
1583 );
1584 assert_eq!(f.run(&[b"FUNCTION", b"LIST"]), "*0\r\n");
1585 assert_eq!(
1586 f.run(&[b"FUNCTION", b"LIST", b"LIBRARYNAME", b"x", b"WITHCODE"]),
1587 "*0\r\n"
1588 );
1589 assert_eq!(
1590 f.run(&[b"FUNCTION", b"DELETE", b"nosuch"]),
1591 "-ERR Library not found\r\n"
1592 );
1593
1594 assert_eq!(
1597 f.run(&[b"SCRIPT", b"FLUSH", b"nope"]),
1598 "-ERR SCRIPT FLUSH only support SYNC|ASYNC option\r\n"
1599 );
1600 assert_eq!(
1601 f.run(&[b"FUNCTION", b"FLUSH", b"nope"]),
1602 "-ERR FUNCTION FLUSH only supports SYNC|ASYNC option\r\n"
1603 );
1604 assert_eq!(
1607 f.run(&[b"FUNCTION", b"FLUSH", b"sync", b"sync"]),
1608 "-ERR unknown subcommand or wrong number of arguments for 'flush'. Try FUNCTION HELP.\r\n"
1609 );
1610 assert_eq!(
1611 f.run(&[b"FUNCTION", b"LIST", b"bogus"]),
1612 "-ERR Unknown argument bogus\r\n"
1613 );
1614 assert_eq!(
1615 f.run(&[b"SCRIPT", b"EXISTS"]),
1616 "-ERR wrong number of arguments for 'script|exists' command\r\n"
1617 );
1618
1619 assert_eq!(
1622 f.run(&[b"SCRIPT", b"LOAD", b"return 1"]),
1623 "-ERR unknown subcommand 'LOAD'. Try SCRIPT HELP.\r\n"
1624 );
1625 assert_eq!(
1626 f.run(&[b"FUNCTION", b"STATS"]),
1627 "-ERR unknown subcommand 'STATS'. Try FUNCTION HELP.\r\n"
1628 );
1629 }
1630
1631 #[test]
1632 fn a_counter_is_an_integer_and_not_a_string_of_digits() {
1633 let mut f = Fixture::new();
1634 assert_eq!(f.run(&[b"INCR", b"c"]), ":1\r\n");
1635 assert_eq!(f.run(&[b"INCRBY", b"c", b"41"]), ":42\r\n");
1636 assert_eq!(f.run(&[b"DECRBY", b"c", b"2"]), ":40\r\n");
1637 assert_eq!(f.run(&[b"GET", b"c"]), "$2\r\n40\r\n");
1640 assert_eq!(f.run(&[b"INCRBYFLOAT", b"c", b"0.5"]), "$4\r\n40.5\r\n");
1641 f.run(&[b"SET", b"k", b"hello"]);
1644 assert_eq!(
1645 f.run(&[b"INCR", b"k"]),
1646 "-ERR value is not an integer or out of range\r\n"
1647 );
1648 assert_eq!(
1649 f.run(&[b"INCRBYFLOAT", b"c", b"inf"]),
1650 "-ERR increment would produce NaN or Infinity\r\n"
1651 );
1652 }
1653
1654 #[test]
1659 fn the_newer_commands_reply_in_the_shapes_a_real_server_sends() {
1660 let mut f = Fixture::new();
1661 assert_eq!(f.run(&[b"SET", b"k", b"hello"]), "+OK\r\n");
1662 assert_eq!(f.run(&[b"DIGEST", b"k"]), "$16\r\n9555e8555c62dcfd\r\n");
1665 assert_eq!(f.run(&[b"DIGEST", b"nosuch"]), "$-1\r\n");
1666 assert_eq!(f.run(&[b"MSETEX", b"1", b"a", b"1"]), ":1\r\n");
1667 assert_eq!(f.run(&[b"MSETEX", b"1", b"a", b"2", b"NX"]), ":0\r\n");
1668 assert_eq!(f.run(&[b"GET", b"a"]), "$1\r\n1\r\n");
1669 assert_eq!(f.run(&[b"INCREX", b"n"]), "*2\r\n:1\r\n:1\r\n");
1670 assert_eq!(
1671 f.run(&[b"INCREX", b"n", b"BYINT", b"5", b"UBOUND", b"3"]),
1672 "*2\r\n:1\r\n:0\r\n",
1673 "a refused increment reports the value it left alone and applied nothing"
1674 );
1675 assert_eq!(
1676 f.run(&[
1677 b"INCREX",
1678 b"n",
1679 b"BYINT",
1680 b"5",
1681 b"UBOUND",
1682 b"3",
1683 b"SATURATE"
1684 ]),
1685 "*2\r\n:3\r\n:2\r\n"
1686 );
1687 assert_eq!(f.run(&[b"DELEX", b"a", b"IFEQ", b"2"]), ":0\r\n");
1688 assert_eq!(f.run(&[b"DELEX", b"a", b"IFEQ", b"1"]), ":1\r\n");
1689 }
1690
1691 #[test]
1692 fn the_same_answers_come_out_in_resp3_spelling() {
1693 let mut f = Fixture::new();
1694 assert!(f.run(&[b"HELLO", b"3"]).starts_with("%7\r\n"));
1695 assert_eq!(f.run(&[b"GET", b"nosuch"]), "_\r\n");
1696 assert_eq!(
1699 f.run(&[b"INCREX", b"c", b"BYFLOAT", b"1.5"]),
1700 "*2\r\n,1.5\r\n,1.5\r\n"
1701 );
1702 assert_eq!(f.run(&[b"INCRBYFLOAT", b"f", b"2.5"]), "$3\r\n2.5\r\n");
1703 assert_eq!(f.run(&[b"RESET"]), "+RESET\r\n");
1706 assert_eq!(f.run(&[b"GET", b"nosuch"]), "$-1\r\n");
1707 }
1708
1709 #[test]
1710 fn a_command_nobody_has_heard_of_is_an_error_and_not_a_closed_socket() {
1711 let mut f = Fixture::new();
1712 let (flow, reply) = f.flow(&[b"NOPE", b"a", b"b"]);
1713 assert_eq!(flow, Flow::Continue);
1714 assert_eq!(
1715 reply,
1716 "-ERR unknown command 'NOPE', with args beginning with: 'a' 'b' \r\n"
1717 );
1718 let reply = f.run(&[b"NO\r\n+PONG\r\nPE"]);
1721 assert_eq!(reply.matches("\r\n").count(), 1);
1722 }
1723
1724 #[test]
1725 fn arity_is_checked_before_the_command_is() {
1726 let mut f = Fixture::new();
1727 assert_eq!(
1728 f.run(&[b"GET"]),
1729 "-ERR wrong number of arguments for 'get' command\r\n"
1730 );
1731 assert_eq!(
1732 f.run(&[b"MSET", b"k"]),
1733 "-ERR wrong number of arguments for 'mset' command\r\n"
1734 );
1735 assert_eq!(
1739 f.run(&[b"PING", b"a", b"b"]),
1740 "-ERR wrong number of arguments for 'ping' command\r\n"
1741 );
1742 assert_eq!(f.run(&[b"PING"]), "+PONG\r\n");
1743 assert_eq!(f.run(&[b"PING", b"hi"]), "$2\r\nhi\r\n");
1744 assert_eq!(
1746 f.run(&[b"DELEX", b"k", b"IFEQ"]),
1747 "-ERR wrong number of arguments for 'delex' command\r\n"
1748 );
1749 }
1750
1751 #[test]
1755 fn the_option_combinations_are_the_ones_a_real_server_accepts() {
1756 let mut f = Fixture::new();
1757 let syntax = "-ERR syntax error\r\n";
1758 assert_eq!(f.run(&[b"SET", b"k", b"v", b"NX", b"XX"]), syntax);
1759 assert_eq!(f.run(&[b"SET", b"k", b"v", b"NX", b"IFEQ", b"a"]), syntax);
1760 assert_eq!(
1761 f.run(&[b"SET", b"k", b"v", b"KEEPTTL", b"EX", b"5"]),
1762 syntax
1763 );
1764 assert_eq!(
1765 f.run(&[b"SET", b"k", b"v", b"EX", b"5", b"PX", b"5"]),
1766 syntax
1767 );
1768 assert_eq!(f.run(&[b"SET", b"k", b"v", b"PERSIST"]), syntax);
1769 assert_eq!(
1771 f.run(&[b"SET", b"k", b"v", b"EX", b"5", b"EX", b"100"]),
1772 "+OK\r\n"
1773 );
1774 assert_eq!(f.run(&[b"SET", b"k", b"v", b"XX", b"XX"]), "+OK\r\n");
1775 assert_eq!(f.run(&[b"SET", b"k", b"v", b"GET", b"GET"]), "$1\r\nv\r\n");
1776 assert_eq!(
1778 f.run(&[b"INCREX", b"n", b"BYINT", b"1", b"BYINT", b"2"]),
1779 syntax
1780 );
1781 assert_eq!(
1782 f.run(&[b"INCREX", b"n", b"ENX"]),
1783 "-ERR ENX flag requires an expiration\r\n"
1784 );
1785 assert_eq!(
1786 f.run(&[b"INCREX", b"n", b"UBOUND", b"abc"]),
1787 "-ERR UBOUND is not an integer or out of range\r\n"
1788 );
1789 assert_eq!(
1790 f.run(&[b"INCREX", b"n", b"LBOUND", b"10", b"UBOUND", b"5"]),
1791 "-ERR LBOUND can't be greater than UBOUND\r\n"
1792 );
1793 assert_eq!(
1794 f.run(&[b"LCS", b"a", b"b", b"LEN", b"IDX"]),
1795 "-ERR If you want both the length and indexes, please just use IDX.\r\n"
1796 );
1797 }
1798
1799 #[test]
1803 fn the_expiry_rules_are_redis_own() {
1804 let mut f = Fixture::new();
1805 let bad = "-ERR invalid expire time in 'set' command\r\n";
1806 assert_eq!(f.run(&[b"SET", b"k", b"v", b"EX", b"0"]), bad);
1807 assert_eq!(f.run(&[b"SET", b"k", b"v", b"EX", b"-1"]), bad);
1808 assert_eq!(f.run(&[b"SET", b"k", b"v", b"EXAT", b"0"]), bad);
1809 assert_eq!(
1810 f.run(&[b"SET", b"k", b"v", b"EX", b"9999999999999999"]),
1811 bad
1812 );
1813 assert_eq!(
1814 f.run(&[b"SET", b"k", b"v", b"PX", b"99999999999999999999"]),
1815 "-ERR value is not an integer or out of range\r\n"
1816 );
1817 assert_eq!(
1818 f.run(&[b"SETEX", b"k", b"0", b"v"]),
1819 "-ERR invalid expire time in 'setex' command\r\n"
1820 );
1821 assert_eq!(f.run(&[b"GETEX", b"nosuch", b"EX", b"0"]), "$-1\r\n");
1822 assert_eq!(f.run(&[b"GETEX", b"nosuch", b"EX", b"abc"]), "$-1\r\n");
1823 assert_eq!(
1824 f.run(&[b"GETEX", b"nosuch", b"KEEPTTL"]),
1825 "-ERR syntax error\r\n",
1826 "the option list is still checked before the key is looked up"
1827 );
1828 assert_eq!(f.run(&[b"SET", b"k", b"v"]), "+OK\r\n");
1830 assert_eq!(f.run(&[b"SET", b"k", b"v", b"EXAT", b"1"]), "+OK\r\n");
1831 assert_eq!(f.run(&[b"GET", b"k"]), "$-1\r\n");
1832 }
1833
1834 #[test]
1835 fn mset_takes_its_pairs_from_the_read_buffer() {
1836 let mut f = Fixture::new();
1837 assert_eq!(f.run(&[b"MSET", b"a", b"1", b"b", b"2"]), "+OK\r\n");
1838 assert_eq!(
1839 f.run(&[b"MGET", b"a", b"b", b"nosuch"]),
1840 "*3\r\n$1\r\n1\r\n$1\r\n2\r\n$-1\r\n"
1841 );
1842 assert_eq!(f.run(&[b"MSETNX", b"b", b"9", b"c", b"3"]), ":0\r\n");
1843 assert_eq!(f.run(&[b"MSETNX", b"c", b"3", b"d", b"4"]), ":1\r\n");
1844 assert_eq!(
1845 f.run(&[b"MSETEX", b"2", b"e", b"5"]),
1846 "-ERR wrong number of key-value pairs\r\n"
1847 );
1848 assert_eq!(
1849 f.run(&[b"MSETEX", b"0", b"e", b"5"]),
1850 "-ERR invalid numkeys value\r\n"
1851 );
1852 assert_eq!(
1853 f.run(&[b"MSETEX", b"abc", b"e", b"5"]),
1854 "-ERR invalid numkeys value\r\n"
1855 );
1856 }
1857
1858 #[test]
1859 fn lcs_answers_the_length_the_string_and_the_runs() {
1860 let mut f = Fixture::new();
1861 f.run(&[b"MSET", b"a", b"ohmytext", b"b", b"mynewtext"]);
1862 assert_eq!(f.run(&[b"LCS", b"a", b"b"]), "$6\r\nmytext\r\n");
1863 assert_eq!(f.run(&[b"LCS", b"a", b"b", b"LEN"]), ":6\r\n");
1864 assert_eq!(
1865 f.run(&[b"LCS", b"a", b"b", b"IDX", b"MINMATCHLEN", b"4"]),
1866 "*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"
1867 );
1868 assert_eq!(
1871 f.run(&[b"LCS", b"a", b"b", b"MINMATCHLEN", b"4", b"WITHMATCHLEN"]),
1872 "$6\r\nmytext\r\n"
1873 );
1874 }
1875
1876 #[test]
1877 fn select_moves_the_connection_and_the_databases_stay_apart() {
1878 let mut f = Fixture::new();
1879 f.run(&[b"SET", b"k", b"zero"]);
1880 assert_eq!(f.run(&[b"SELECT", b"4"]), "+OK\r\n");
1881 assert_eq!(f.run(&[b"GET", b"k"]), "$-1\r\n");
1882 f.run(&[b"SET", b"k", b"four"]);
1883 assert_eq!(f.run(&[b"SELECT", b"0"]), "+OK\r\n");
1884 assert_eq!(f.run(&[b"GET", b"k"]), "$4\r\nzero\r\n");
1885 assert_eq!(
1886 f.run(&[b"SELECT", b"99"]),
1887 "-ERR DB index is out of range\r\n"
1888 );
1889 assert_eq!(
1890 f.run(&[b"SELECT", b"-1"]),
1891 "-ERR DB index is out of range\r\n"
1892 );
1893 assert_eq!(
1894 f.run(&[b"SELECT", b"abc"]),
1895 "-ERR value is not an integer or out of range\r\n"
1896 );
1897 f.run(&[b"SELECT", b"4"]);
1899 f.run(&[b"RESET"]);
1900 assert_eq!(f.run(&[b"GET", b"k"]), "$4\r\nzero\r\n");
1901 }
1902
1903 #[test]
1904 fn hello_agrees_on_a_protocol_and_refuses_the_ones_that_do_not_exist() {
1905 let mut f = Fixture::new();
1906 let reply = f.run(&[b"HELLO"]);
1907 assert!(reply.starts_with("*14\r\n"), "{reply}");
1908 assert!(reply.contains("$5\r\nredis\r\n"), "{reply}");
1909 assert!(reply.contains("$5\r\n8.8.0\r\n"), "{reply}");
1910 assert!(
1911 reply.contains(":7\r\n"),
1912 "the connection id is in there: {reply}"
1913 );
1914 assert_eq!(
1915 f.run(&[b"HELLO", b"4"]),
1916 "-NOPROTO unsupported protocol version\r\n"
1917 );
1918 assert_eq!(
1919 f.run(&[b"HELLO", b"abc"]),
1920 "-ERR Protocol version is not an integer or out of range\r\n"
1921 );
1922 assert_eq!(
1923 f.run(&[b"HELLO", b"3", b"SETNAME"]),
1924 "-ERR Syntax error in HELLO option 'SETNAME'\r\n"
1925 );
1926 assert!(
1927 f.run(&[b"HELLO", b"3", b"SETNAME", b"bob"])
1928 .starts_with("%7\r\n")
1929 );
1930 assert_eq!(f.session.name(), b"bob");
1931 f.run(&[b"RESET"]);
1932 assert_eq!(f.session.name(), b"");
1933 }
1934
1935 #[test]
1936 fn command_describes_this_server_in_the_shape_a_driver_reads() {
1937 let mut f = Fixture::new();
1938 let count = format!(":{}\r\n", COMMANDS.len());
1939 assert_eq!(f.run(&[b"COMMAND", b"COUNT"]), count);
1940 let info = f.run(&[b"COMMAND", b"INFO", b"get"]);
1941 assert_eq!(
1942 info,
1943 "*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\
1944 *3\r\n+@read\r\n+@string\r\n+@fast\r\n*0\r\n*0\r\n*0\r\n"
1945 );
1946 assert_eq!(f.run(&[b"COMMAND", b"INFO", b"nosuch"]), "*1\r\n$-1\r\n");
1948 assert_eq!(
1949 f.run(&[b"COMMAND", b"LIST", b"FILTERBY", b"PATTERN", b"getr*"]),
1950 "*1\r\n$8\r\ngetrange\r\n"
1951 );
1952 assert_eq!(
1953 f.run(&[b"COMMAND", b"NOPE"]),
1954 "-ERR unknown subcommand 'NOPE'. Try COMMAND HELP.\r\n"
1955 );
1956 }
1957
1958 #[test]
1962 fn command_getkeys_finds_the_keys_including_the_hidden_ones() {
1963 let mut f = Fixture::new();
1964 assert_eq!(
1965 f.run(&[b"COMMAND", b"GETKEYS", b"get", b"k"]),
1966 "*1\r\n$1\r\nk\r\n"
1967 );
1968 assert_eq!(
1969 f.run(&[b"COMMAND", b"GETKEYS", b"mset", b"a", b"1", b"b", b"2"]),
1970 "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
1971 );
1972 assert_eq!(
1973 f.run(&[
1974 b"COMMAND", b"GETKEYS", b"msetex", b"2", b"a", b"1", b"b", b"2"
1975 ]),
1976 "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
1977 );
1978 assert_eq!(
1979 f.run(&[b"COMMAND", b"GETKEYS", b"ping"]),
1980 "-ERR The command has no key arguments\r\n"
1981 );
1982 assert_eq!(
1983 f.run(&[b"COMMAND", b"GETKEYS", b"set"]),
1984 "-ERR Invalid number of arguments specified for command\r\n"
1985 );
1986 }
1987
1988 #[test]
1989 fn config_answers_what_it_can_and_refuses_what_it_cannot() {
1990 let mut f = Fixture::new();
1991 assert_eq!(
1992 f.run(&[b"CONFIG", b"GET", b"maxmemory"]),
1993 "*2\r\n$9\r\nmaxmemory\r\n$1\r\n0\r\n"
1994 );
1995 let both = f.run(&[b"CONFIG", b"GET", b"maxmemory*", b"maxmemory"]);
1998 assert!(both.starts_with("*6\r\n"), "{both}");
1999 assert_eq!(f.run(&[b"CONFIG", b"GET", b"nosuch"]), "*0\r\n");
2000 assert_eq!(f.run(&[b"CONFIG", b"SET", b"appendonly", b"no"]), "+OK\r\n");
2001 assert_eq!(
2002 f.run(&[b"CONFIG", b"SET", b"appendonly", b"yes"]),
2003 "-ERR CONFIG SET failed (possibly related to argument 'appendonly') - can't set immutable config\r\n"
2004 );
2005 assert_eq!(
2006 f.run(&[b"CONFIG", b"SET", b"nosuch", b"1"]),
2007 "-ERR Unknown option or number of arguments for CONFIG SET - 'nosuch'\r\n"
2008 );
2009 assert_eq!(
2010 f.run(&[b"CONFIG", b"GET"]),
2011 "-ERR wrong number of arguments for 'config|get' command\r\n"
2012 );
2013 assert_eq!(
2017 f.run(&[b"CONFIG", b"SET", b"appendonly"]),
2018 "-ERR wrong number of arguments for 'config|set' command\r\n"
2019 );
2020 assert_eq!(
2021 f.run(&[b"CONFIG", b"SET", b"appendonly", b"no", b"maxmemory"]),
2022 "-ERR syntax error\r\n"
2023 );
2024 assert_eq!(f.run(&[b"CONFIG", b"RESETSTAT"]), "+OK\r\n");
2025 assert_eq!(
2026 f.run(&[b"CONFIG", b"REWRITE"]),
2027 "-ERR The server is running without a config file\r\n"
2028 );
2029 }
2030
2031 #[test]
2032 fn the_eviction_policy_reads_back_what_was_written_to_it() {
2033 let mut f = Fixture::new();
2034 assert_eq!(
2035 f.run(&[b"CONFIG", b"GET", b"maxmemory-policy"]),
2036 "*2\r\n$16\r\nmaxmemory-policy\r\n$10\r\nnoeviction\r\n"
2037 );
2038 assert_eq!(
2039 f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"AllKeys-LFU"]),
2040 "+OK\r\n",
2041 "the name is matched without regard to case, like every other one"
2042 );
2043 assert_eq!(
2044 f.run(&[b"CONFIG", b"GET", b"maxmemory-policy"]),
2045 "*2\r\n$16\r\nmaxmemory-policy\r\n$11\r\nallkeys-lfu\r\n"
2046 );
2047 assert!(
2049 f.run(&[b"INFO", b"memory"])
2050 .contains("maxmemory_policy:allkeys-lfu"),
2051 "INFO and CONFIG disagree about the policy"
2052 );
2053 assert_eq!(
2057 f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"garbage"]),
2058 "-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"
2059 );
2060 assert_eq!(
2063 f.run(&[b"CONFIG", b"GET", b"maxmemory-policy"]),
2064 "*2\r\n$16\r\nmaxmemory-policy\r\n$11\r\nallkeys-lfu\r\n"
2065 );
2066 f.run(&[
2067 b"CONFIG",
2068 b"SET",
2069 b"hash-max-listpack-entries",
2070 b"7",
2071 b"maxmemory-policy",
2072 b"nonsense",
2073 ]);
2074 assert_eq!(
2075 f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
2076 "*2\r\n$25\r\nhash-max-listpack-entries\r\n$3\r\n512\r\n"
2077 );
2078 }
2079
2080 #[test]
2081 fn the_three_eviction_numbers_read_back_too() {
2082 let mut f = Fixture::new();
2083 for (name, default, set) in [
2084 ("maxmemory-samples", "5", "12"),
2085 ("lfu-log-factor", "10", "3"),
2086 ("lfu-decay-time", "1", "60"),
2087 ] {
2088 let get = || {
2089 format!(
2090 "*2\r\n${}\r\n{name}\r\n${}\r\n{default}\r\n",
2091 name.len(),
2092 default.len()
2093 )
2094 };
2095 assert_eq!(f.run(&[b"CONFIG", b"GET", name.as_bytes()]), get());
2096 assert_eq!(
2097 f.run(&[b"CONFIG", b"SET", name.as_bytes(), set.as_bytes()]),
2098 "+OK\r\n"
2099 );
2100 assert_eq!(
2101 f.run(&[b"CONFIG", b"GET", name.as_bytes()]),
2102 format!(
2103 "*2\r\n${}\r\n{name}\r\n${}\r\n{set}\r\n",
2104 name.len(),
2105 set.len()
2106 )
2107 );
2108 assert_eq!(
2111 f.run(&[b"CONFIG", b"SET", name.as_bytes(), b"soon"]),
2112 format!(
2113 "-ERR CONFIG SET failed (possibly related to argument '{name}') - argument couldn't be parsed into an integer\r\n"
2114 )
2115 );
2116 }
2117 }
2118
2119 #[test]
2120 fn the_memory_limit_reads_back_in_bytes_whatever_the_unit_was() {
2121 let mut f = Fixture::new();
2122 assert_eq!(
2123 f.run(&[b"CONFIG", b"GET", b"maxmemory"]),
2124 "*2\r\n$9\r\nmaxmemory\r\n$1\r\n0\r\n",
2125 "no limit is the default"
2126 );
2127 for (typed, bytes) in [
2130 (&b"1024"[..], "1024"),
2131 (b"1k", "1000"),
2132 (b"1kb", "1024"),
2133 (b"1M", "1000000"),
2134 (b"1Mb", "1048576"),
2135 (b"1gb", "1073741824"),
2136 (b"100mb", "104857600"),
2137 ] {
2138 assert_eq!(f.run(&[b"CONFIG", b"SET", b"maxmemory", typed]), "+OK\r\n");
2139 assert_eq!(
2140 f.run(&[b"CONFIG", b"GET", b"maxmemory"]),
2141 format!("*2\r\n$9\r\nmaxmemory\r\n${}\r\n{bytes}\r\n", bytes.len()),
2142 "set {}",
2143 String::from_utf8_lossy(typed)
2144 );
2145 }
2146 assert!(
2147 f.run(&[b"INFO", b"memory"]).contains("maxmemory:104857600"),
2148 "the report agrees with the setting"
2149 );
2150
2151 for bad in [&b"1tb"[..], b"-1", b"", b"lots"] {
2154 assert_eq!(
2155 f.run(&[b"CONFIG", b"SET", b"maxmemory", bad]),
2156 "-ERR CONFIG SET failed (possibly related to argument 'maxmemory') - argument must be a memory value\r\n",
2157 "refused {}",
2158 String::from_utf8_lossy(bad)
2159 );
2160 }
2161 assert!(
2162 f.run(&[b"INFO", b"memory"]).contains("maxmemory:104857600"),
2163 "and the refusal left the old one alone"
2164 );
2165 }
2166
2167 #[test]
2168 fn a_write_is_refused_when_there_is_no_room_and_nothing_to_evict() {
2169 let mut f = Fixture::new();
2170 f.run(&[b"SET", b"here", b"already"]);
2171 f.run(&[b"CONFIG", b"SET", b"maxmemory", b"1"]);
2175 assert_eq!(
2176 f.run(&[b"SET", b"k", b"v"]),
2177 "-OOM command not allowed when used memory > 'maxmemory'.\r\n"
2178 );
2179 assert_eq!(
2180 f.run(&[b"LPUSH", b"l", b"v"]),
2181 "-OOM command not allowed when used memory > 'maxmemory'.\r\n"
2182 );
2183 assert_eq!(f.run(&[b"GET", b"here"]), "$7\r\nalready\r\n");
2185 assert_eq!(f.run(&[b"DEL", b"here"]), ":1\r\n");
2186 assert!(f.run(&[b"INFO", b"stats"]).contains("evicted_keys:0"));
2187
2188 f.run(&[b"CONFIG", b"SET", b"maxmemory", b"0"]);
2190 assert_eq!(f.run(&[b"SET", b"k", b"v"]), "+OK\r\n");
2191 }
2192
2193 #[test]
2194 fn an_allkeys_policy_makes_room_instead_of_refusing() {
2195 let mut f = Fixture::new();
2196 let val = vec![b'v'; 256];
2197 for i in 0..24000u32 {
2198 let k = format!("key:{i:08}");
2199 f.run(&[b"SET", k.as_bytes(), &val]);
2200 }
2201 let full = f.server.memory_bytes();
2202 assert!(
2203 full > 3 * 1024 * 1024,
2204 "the arena is several segments: {full}"
2205 );
2206
2207 let limit = full - 2 * 1024 * 1024;
2211 f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lru"]);
2212 f.run(&[
2213 b"CONFIG",
2214 b"SET",
2215 b"maxmemory",
2216 limit.to_string().as_bytes(),
2217 ]);
2218
2219 for i in 0..2000u32 {
2223 let k = format!("new:{i:08}");
2224 assert_eq!(
2225 f.run(&[b"SET", k.as_bytes(), &val]),
2226 "+OK\r\n",
2227 "write {i} was refused"
2228 );
2229 f.server.refresh_memory();
2230 if f.server.memory_bytes() <= limit {
2231 break;
2232 }
2233 }
2234 assert!(
2235 f.server.memory_bytes() <= limit,
2236 "it never got under: {} against {limit}",
2237 f.server.memory_bytes()
2238 );
2239 let info = f.run(&[b"INFO", b"stats"]);
2240 assert!(!info.contains("evicted_keys:0"), "{info}");
2241 assert!(
2242 f.run(&[b"DBSIZE"]) != ":0\r\n",
2243 "and it did not empty the database to get there"
2244 );
2245 }
2246
2247 #[test]
2248 fn the_running_total_and_the_walk_agree_on_a_mixed_keyspace() {
2249 let mut f = Fixture::new();
2256 f.run(&[b"CONFIG", b"SET", b"maxmemory", b"1gb"]);
2257 let big = vec![b'v'; 200];
2258
2259 for i in 0..400u32 {
2260 let n = i.to_string();
2261 let n = n.as_bytes();
2262 f.run(&[b"SADD", b"s", n]);
2263 f.run(&[b"SADD", b"s2", &big]);
2264 f.run(&[b"HSET", b"h", n, &big]);
2265 f.run(&[b"RPUSH", b"l", &big]);
2266 f.run(&[b"ZADD", b"z", n, n]);
2267 f.run(&[b"ARSET", b"a", n, &big]);
2268 if i % 7 == 0 {
2269 f.run(&[b"SREM", b"s", n]);
2270 f.run(&[b"HDEL", b"h", n]);
2271 f.run(&[b"LPOP", b"l"]);
2272 f.run(&[b"ZREM", b"z", n]);
2273 f.run(&[b"ARDEL", b"a", n]);
2274 }
2275 if i % 53 == 0 {
2276 f.run(&[b"DEL", b"s2"]);
2279 }
2280 assert_eq!(
2281 f.server.settled_memory(),
2282 f.server.memory_bytes(),
2283 "after round {i}"
2284 );
2285 }
2286
2287 assert_eq!(f.run(&[b"DBSIZE"]), ":6\r\n");
2290 assert!(
2291 f.server.memory_bytes() > 512 * 1024,
2292 "{}",
2293 f.server.memory_bytes()
2294 );
2295
2296 f.run(&[b"FLUSHALL"]);
2298 assert_eq!(f.server.settled_memory(), f.server.memory_bytes());
2299 }
2300
2301 #[test]
2302 fn taking_the_limit_away_stops_the_counting_and_putting_it_back_starts_again() {
2303 let mut f = Fixture::new();
2308 for i in 0..200u32 {
2309 let n = i.to_string();
2310 f.run(&[b"SADD", b"s", n.as_bytes()]);
2311 f.run(&[b"HSET", b"h", n.as_bytes(), b"value"]);
2312 }
2313 f.run(&[b"CONFIG", b"SET", b"maxmemory", b"1gb"]);
2314 assert_eq!(f.server.settled_memory(), f.server.memory_bytes());
2315
2316 f.run(&[b"CONFIG", b"SET", b"maxmemory", b"0"]);
2317 for i in 200..400u32 {
2318 let n = i.to_string();
2319 f.run(&[b"SADD", b"s", n.as_bytes()]);
2320 }
2321 f.run(&[b"CONFIG", b"SET", b"maxmemory", b"1gb"]);
2322 assert_eq!(
2323 f.server.settled_memory(),
2324 f.server.memory_bytes(),
2325 "the writes it was not watching are in the number it started from"
2326 );
2327 }
2328
2329 #[test]
2330 fn evicted_keys_and_expired_keys_are_different_numbers() {
2331 let mut f = Fixture::new();
2332 f.run(&[b"SET", b"gone", b"v", b"PX", b"1"]);
2335 f.server.db(0).clock_mut().advance(20);
2336 f.run(&[b"GET", b"gone"]);
2337 let info = f.run(&[b"INFO", b"stats"]);
2338 assert!(info.contains("expired_keys:1"), "{info}");
2339 assert!(info.contains("evicted_keys:0"), "{info}");
2340 }
2341
2342 #[test]
2343 fn the_object_subcommands_follow_the_policy() {
2344 let mut f = Fixture::new();
2345 f.run(&[b"SET", b"s", b"v"]);
2346 assert_eq!(f.run(&[b"OBJECT", b"IDLETIME", b"s"]), ":0\r\n");
2350 assert!(
2351 f.run(&[b"OBJECT", b"FREQ", b"s"])
2352 .starts_with("-ERR An LFU maxmemory policy is not selected"),
2353 );
2354
2355 f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lfu"]);
2356 assert!(
2357 f.run(&[b"OBJECT", b"IDLETIME", b"s"])
2358 .starts_with("-ERR An LFU maxmemory policy is selected"),
2359 );
2360 assert!(
2365 f.run(&[b"OBJECT", b"FREQ", b"s"]).starts_with(':'),
2366 "FREQ should answer under an LFU policy"
2367 );
2368 }
2369
2370 #[test]
2371 fn object_says_which_rung_of_the_ladder_a_key_is_on() {
2372 let mut f = Fixture::new();
2373 f.run(&[b"SET", b"s", b"hello"]);
2374 f.run(&[b"SET", b"n", b"123"]);
2375 f.run(&[b"SADD", b"si", b"1", b"2", b"3"]);
2376 f.run(&[b"SADD", b"ss", b"a", b"b"]);
2377 f.run(&[b"HSET", b"h", b"f", b"v"]);
2378 for (key, want) in [
2379 (b"s".as_slice(), "embstr"),
2380 (b"n", "int"),
2381 (b"si", "intset"),
2382 (b"ss", "listpack"),
2383 (b"h", "listpack"),
2384 ] {
2385 let reply = f.run(&[b"OBJECT", b"ENCODING", key]);
2386 assert_eq!(reply, format!("${}\r\n{want}\r\n", want.len()));
2387 }
2388
2389 f.run(&[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"f"]);
2392 assert_eq!(
2393 f.run(&[b"OBJECT", b"ENCODING", b"h"]),
2394 "$10\r\nlistpackex\r\n"
2395 );
2396
2397 assert_eq!(f.run(&[b"OBJECT", b"REFCOUNT", b"s"]), ":1\r\n");
2398 assert_eq!(f.run(&[b"OBJECT", b"IDLETIME", b"s"]), ":0\r\n");
2399 assert!(f.run(&[b"OBJECT", b"HELP"]).starts_with("*14\r\n+OBJECT "));
2400 }
2401
2402 #[test]
2403 fn object_answers_nil_for_a_key_that_is_not_there() {
2404 let mut f = Fixture::new();
2405 for sub in [b"ENCODING".as_slice(), b"REFCOUNT", b"IDLETIME", b"FREQ"] {
2406 assert_eq!(
2407 f.run(&[b"OBJECT", sub, b"nokey"]),
2408 "$-1\r\n",
2409 "a nil and not an error, which is what 8.10.1 does"
2410 );
2411 }
2412 f.run(&[b"SET", b"s", b"v"]);
2415 assert!(
2416 f.run(&[b"OBJECT", b"FREQ", b"s"])
2417 .starts_with("-ERR An LFU maxmemory policy is not"),
2418 );
2419 assert_eq!(
2420 f.run(&[b"OBJECT", b"NOPE", b"s"]),
2421 "-ERR unknown subcommand 'NOPE'. Try OBJECT HELP.\r\n"
2422 );
2423 assert_eq!(
2424 f.run(&[b"OBJECT", b"ENCODING"]),
2425 "-ERR wrong number of arguments for 'object|encoding' command\r\n"
2426 );
2427 assert_eq!(
2428 f.run(&[b"OBJECT", b"ENCODING", b"s", b"extra"]),
2429 "-ERR wrong number of arguments for 'object|encoding' command\r\n"
2430 );
2431 assert_eq!(
2432 f.run(&[b"OBJECT"]),
2433 "-ERR wrong number of arguments for 'object' command\r\n"
2434 );
2435 }
2436
2437 #[test]
2438 fn config_moves_the_ladder_and_object_encoding_agrees() {
2439 let mut f = Fixture::new();
2440 assert_eq!(
2441 f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
2442 "*2\r\n$25\r\nhash-max-listpack-entries\r\n$3\r\n512\r\n",
2443 "512 and not the 128 everyone remembers, which is what 8.10.1 says"
2444 );
2445 assert_eq!(
2448 f.run(&[b"CONFIG", b"GET", b"hash-max-ziplist-entries"]),
2449 "*2\r\n$24\r\nhash-max-ziplist-entries\r\n$3\r\n512\r\n"
2450 );
2451 assert!(
2452 f.run(&[b"CONFIG", b"GET", b"hash-max-*"])
2453 .starts_with("*8\r\n")
2454 );
2455 assert!(
2456 f.run(&[b"CONFIG", b"GET", b"set-max-*"])
2457 .starts_with("*6\r\n")
2458 );
2459
2460 f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2", b"c", b"3"]);
2461 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"h"]), "$8\r\nlistpack\r\n");
2462
2463 assert_eq!(
2464 f.run(&[b"CONFIG", b"SET", b"hash-max-ziplist-entries", b"2"]),
2465 "+OK\r\n",
2466 "written under the old name and read back under the new one"
2467 );
2468 assert_eq!(
2469 f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
2470 "*2\r\n$25\r\nhash-max-listpack-entries\r\n$1\r\n2\r\n"
2471 );
2472 assert_eq!(
2473 f.run(&[b"OBJECT", b"ENCODING", b"h"]),
2474 "$8\r\nlistpack\r\n",
2475 "the hash that already exists is left exactly where it was"
2476 );
2477 f.run(&[b"HSET", b"h2", b"a", b"1", b"b", b"2", b"c", b"3"]);
2478 assert_eq!(
2479 f.run(&[b"OBJECT", b"ENCODING", b"h2"]),
2480 "$9\r\nhashtable\r\n",
2481 "and the next one built goes straight to a table"
2482 );
2483
2484 f.run(&[b"CONFIG", b"SET", b"set-max-intset-entries", b"2"]);
2486 f.run(&[b"SADD", b"s", b"1", b"2", b"3"]);
2487 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"s"]), "$8\r\nlistpack\r\n");
2488 f.run(&[b"CONFIG", b"SET", b"set-max-listpack-value", b"2"]);
2489 f.run(&[b"SADD", b"s2", b"abcdefgh"]);
2490 assert_eq!(
2491 f.run(&[b"OBJECT", b"ENCODING", b"s2"]),
2492 "$9\r\nhashtable\r\n"
2493 );
2494 }
2495
2496 #[test]
2497 fn config_set_takes_all_of_the_ladder_or_none_of_it() {
2498 let mut f = Fixture::new();
2499 assert_eq!(
2500 f.run(&[
2501 b"CONFIG",
2502 b"SET",
2503 b"hash-max-listpack-entries",
2504 b"7",
2505 b"set-max-listpack-entries",
2506 b"abc"
2507 ]),
2508 "-ERR CONFIG SET failed (possibly related to argument 'set-max-listpack-entries') - argument couldn't be parsed into an integer\r\n"
2509 );
2510 assert_eq!(
2511 f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
2512 "*2\r\n$25\r\nhash-max-listpack-entries\r\n$3\r\n512\r\n",
2513 "the pair in front of the bad one did not go in"
2514 );
2515 assert_eq!(
2518 f.run(&[b"CONFIG", b"SET", b"hash-max-ziplist-entries", b"abc"]),
2519 "-ERR CONFIG SET failed (possibly related to argument 'hash-max-ziplist-entries') - argument couldn't be parsed into an integer\r\n"
2520 );
2521 assert_eq!(
2522 f.run(&[b"CONFIG", b"SET", b"set-max-intset-entries", b"-1"]),
2523 "-ERR CONFIG SET failed (possibly related to argument 'set-max-intset-entries') - argument must be between 0 and 9223372036854775807 inclusive\r\n"
2524 );
2525 assert_eq!(
2528 f.run(&[
2529 b"CONFIG",
2530 b"SET",
2531 b"set-max-intset-entries",
2532 b"99999999999999999999"
2533 ]),
2534 "-ERR CONFIG SET failed (possibly related to argument 'set-max-intset-entries') - argument couldn't be parsed into an integer\r\n"
2535 );
2536 assert_eq!(
2537 f.run(&[
2538 b"CONFIG",
2539 b"SET",
2540 b"set-max-intset-entries",
2541 b"9223372036854775807"
2542 ]),
2543 "+OK\r\n"
2544 );
2545 }
2546
2547 #[test]
2548 fn a_setting_moved_on_one_database_moved_on_all_of_them() {
2549 let mut f = Fixture::new();
2550 f.run(&[b"CONFIG", b"SET", b"hash-max-listpack-entries", b"1"]);
2551 f.run(&[b"SELECT", b"3"]);
2552 f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
2553 assert_eq!(
2554 f.run(&[b"OBJECT", b"ENCODING", b"h"]),
2555 "$9\r\nhashtable\r\n",
2556 "these are one server wide number in Redis, whatever a Keyspace carries"
2557 );
2558 }
2559
2560 #[test]
2561 fn info_reports_the_numbers_it_can_stand_behind() {
2562 let mut f = Fixture::new();
2563 f.run(&[b"MSET", b"a", b"1", b"b", b"2"]);
2564 let all = f.run(&[b"INFO"]);
2565 assert!(all.contains("redis_version:8.8.0"), "{all}");
2566 assert!(
2567 all.contains(concat!("yo_version:", env!("CARGO_PKG_VERSION"))),
2568 "{all}"
2569 );
2570 assert!(all.contains("db0:keys=2,expires=0,avg_ttl=0"), "{all}");
2571 assert!(all.contains("role:master"), "{all}");
2572 let clients = f.run(&[b"INFO", b"clients"]);
2574 assert!(clients.contains("connected_clients:0"), "{clients}");
2575 assert!(!clients.contains("redis_version"), "{clients}");
2576 assert_eq!(f.run(&[b"INFO", b"nosuch"]), "$0\r\n\r\n");
2577 }
2578
2579 #[test]
2583 fn the_active_sweep_reclaims_keys_no_client_comes_back_for() {
2584 let mut f = Fixture::new();
2585 for i in 0..3_000u32 {
2586 f.run(&[b"SET", format!("d{i}").as_bytes(), b"v", b"PX", b"50"]);
2587 }
2588 for i in 0..1_000u32 {
2589 f.run(&[b"SET", format!("k{i}").as_bytes(), b"v"]);
2590 }
2591 assert_eq!(f.run(&[b"DBSIZE"]), ":4000\r\n");
2592 f.advance(100);
2593 assert_eq!(
2594 f.run(&[b"DBSIZE"]),
2595 ":4000\r\n",
2596 "DBSIZE counts records and nothing has read past the dead ones yet"
2597 );
2598
2599 let mut spent = 0;
2601 for _ in 0..2_000 {
2602 spent += f.server.expire_step(4096);
2603 if f.run(&[b"DBSIZE"]) == ":1000\r\n" {
2604 break;
2605 }
2606 }
2607 assert_eq!(f.run(&[b"DBSIZE"]), ":1000\r\n", "spent {spent} looks");
2608 assert!(f.run(&[b"INFO", b"stats"]).contains("expired_keys:3000"));
2609 for i in 0..1_000u32 {
2610 assert_eq!(
2611 f.run(&[b"GET", format!("k{i}").as_bytes()]),
2612 "$1\r\nv\r\n",
2613 "it took a key that had no deadline"
2614 );
2615 }
2616 }
2617
2618 #[test]
2619 fn a_sweep_of_a_server_with_no_deadlines_anywhere_costs_nothing() {
2620 let mut f = Fixture::new();
2621 for i in 0..2_000u32 {
2622 f.run(&[b"SET", format!("k{i}").as_bytes(), b"v"]);
2623 }
2624 assert_eq!(f.server.expire_step(4096), 0);
2625 f.run(&[b"SELECT", b"3"]);
2627 f.run(&[b"SET", b"x", b"v", b"PX", b"50"]);
2628 f.advance(100);
2629 for _ in 0..64 {
2630 f.server.expire_step(4096);
2631 }
2632 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
2633 f.run(&[b"SELECT", b"0"]);
2634 assert_eq!(f.run(&[b"DBSIZE"]), ":2000\r\n");
2635 assert_eq!(f.server.expire_step(4096), 0, "and it is quiet again");
2636 }
2637
2638 #[test]
2641 fn the_sweep_the_loop_calls_runs_at_most_once_a_millisecond() {
2642 let mut f = Fixture::new();
2643 for i in 0..500u32 {
2644 f.run(&[b"SET", format!("d{i}").as_bytes(), b"v", b"PX", b"50"]);
2645 }
2646 f.advance(100);
2647 let at = f.server.db(0).clock().now_ms();
2648 f.server.set_clock_ms(at);
2649 assert!(f.server.expire_slice(8) > 0, "the first one works");
2653 for _ in 0..1_000 {
2654 assert_eq!(
2655 f.server.expire_slice(8),
2656 0,
2657 "the millisecond has not moved and neither should this"
2658 );
2659 }
2660 assert!(
2661 f.server.db(0).expires() > 400,
2662 "there is plenty left to take"
2663 );
2664 f.server.set_clock_ms(at + 1);
2665 assert!(f.server.expire_slice(8) > 0, "and then it goes again");
2666 }
2667
2668 #[test]
2671 fn info_keyspace_counts_the_keys_that_have_a_deadline() {
2672 let mut f = Fixture::new();
2673 f.run(&[b"MSET", b"a", b"1", b"b", b"2", b"c", b"3"]);
2674 assert!(
2675 f.run(&[b"INFO", b"keyspace"])
2676 .contains("db0:keys=3,expires=0"),
2677 "none of them has one yet"
2678 );
2679 f.run(&[b"EXPIRE", b"a", b"1000"]);
2680 f.run(&[b"EXPIRE", b"b", b"1000"]);
2681 let two = f.run(&[b"INFO", b"keyspace"]);
2682 assert!(two.contains("db0:keys=3,expires=2"), "{two}");
2683 f.run(&[b"PERSIST", b"a"]);
2684 f.run(&[b"DEL", b"b"]);
2685 let none = f.run(&[b"INFO", b"keyspace"]);
2686 assert!(none.contains("db0:keys=2,expires=0"), "{none}");
2687
2688 f.run(&[b"SELECT", b"1"]);
2690 f.run(&[b"SET", b"x", b"1", b"EX", b"1000"]);
2691 let both = f.run(&[b"INFO", b"keyspace"]);
2692 assert!(both.contains("db0:keys=2,expires=0"), "{both}");
2693 assert!(both.contains("db1:keys=1,expires=1"), "{both}");
2694 }
2695
2696 #[cfg(unix)]
2697 #[test]
2698 fn info_cpu_reports_processor_time_that_was_really_measured() {
2699 let mut f = Fixture::new();
2700 let cpu = f.run(&[b"INFO", b"cpu"]);
2701 assert!(cpu.contains("# CPU"), "{cpu}");
2702 assert!(cpu.contains("used_cpu_user:"), "{cpu}");
2704 assert!(cpu.contains("used_cpu_sys:"), "{cpu}");
2705 assert!(cpu.contains("used_cpu_user_children:0.000000"), "{cpu}");
2706 assert!(!cpu.contains("redis_version"), "{cpu}");
2707
2708 let before = used_cpu_user(&cpu);
2712 let mut n = 0u64;
2713 let mut rounds = 0;
2714 while used_cpu_user(&f.run(&[b"INFO", b"cpu"])) <= before {
2715 for i in 0..1_000_000u64 {
2716 n = n.wrapping_add(i.wrapping_mul(i));
2717 }
2718 rounds += 1;
2719 assert!(rounds < 1_000, "cpu time never moved, n is {n}");
2723 }
2724 }
2725
2726 #[cfg(unix)]
2728 fn used_cpu_user(info: &str) -> f64 {
2729 info.lines()
2730 .find_map(|l| l.strip_prefix("used_cpu_user:"))
2731 .expect("no used_cpu_user in the reply")
2732 .trim()
2733 .parse()
2734 .expect("used_cpu_user is not a number")
2735 }
2736
2737 #[test]
2743 fn a_command_that_fails_leaves_nothing_half_written() {
2744 let mut f = Fixture::new();
2745 let reply = f.run(&[b"SETRANGE", b"k", b"-1", b"x"]);
2746 assert_eq!(reply, "-ERR offset is out of range\r\n");
2747 assert!(!reply.contains(':'), "no integer went out in front of it");
2748 }
2749
2750 #[test]
2751 fn quit_answers_first_and_closes_after() {
2752 let mut f = Fixture::new();
2753 let (flow, reply) = f.flow(&[b"QUIT"]);
2754 assert_eq!(reply, "+OK\r\n");
2755 assert_eq!(flow, Flow::Close);
2756 }
2757
2758 #[test]
2759 fn the_command_counter_counts_every_command_including_the_bad_ones() {
2760 let mut f = Fixture::new();
2761 f.run(&[b"PING"]);
2762 f.run(&[b"NOPE"]);
2763 f.run(&[b"GET"]);
2764 assert_eq!(f.server.stats.commands, 3);
2765 }
2766
2767 #[test]
2768 fn a_set_goes_from_bytes_to_bytes() {
2769 let mut f = Fixture::new();
2770 assert_eq!(f.run(&[b"SADD", b"s", b"a", b"b", b"c"]), ":3\r\n");
2771 assert_eq!(f.run(&[b"SADD", b"s", b"b", b"d"]), ":1\r\n");
2772 assert_eq!(f.run(&[b"SCARD", b"s"]), ":4\r\n");
2773 assert_eq!(f.run(&[b"SISMEMBER", b"s", b"a"]), ":1\r\n");
2774 assert_eq!(f.run(&[b"SISMEMBER", b"s", b"z"]), ":0\r\n");
2775 assert_eq!(f.run(&[b"TYPE", b"s"]), "+set\r\n");
2776 assert_eq!(
2777 f.run(&[b"SMISMEMBER", b"s", b"a", b"z", b"d"]),
2778 "*3\r\n:1\r\n:0\r\n:1\r\n"
2779 );
2780 assert_eq!(f.run(&[b"SREM", b"s", b"a", b"z"]), ":1\r\n");
2781 assert_eq!(f.run(&[b"SCARD", b"s"]), ":3\r\n");
2782 }
2783
2784 #[test]
2785 fn a_set_command_at_a_key_that_is_not_there_answers_empty() {
2786 let mut f = Fixture::new();
2787 assert_eq!(f.run(&[b"SCARD", b"nope"]), ":0\r\n");
2788 assert_eq!(f.run(&[b"SISMEMBER", b"nope", b"a"]), ":0\r\n");
2789 assert_eq!(f.run(&[b"SREM", b"nope", b"a"]), ":0\r\n");
2790 assert_eq!(f.run(&[b"SMEMBERS", b"nope"]), "*0\r\n");
2791 assert_eq!(
2792 f.run(&[b"SMISMEMBER", b"nope", b"a", b"b"]),
2793 "*2\r\n:0\r\n:0\r\n"
2794 );
2795 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n", "and made nothing");
2796 }
2797
2798 #[test]
2799 fn smembers_answers_a_set_on_resp3_and_an_array_on_resp2() {
2800 let mut f = Fixture::new();
2804 f.run(&[b"SADD", b"s", b"one"]);
2805 assert_eq!(f.run(&[b"SMEMBERS", b"s"]), "*1\r\n$3\r\none\r\n");
2806
2807 f.run(&[b"HELLO", b"3"]);
2808 assert_eq!(f.run(&[b"SMEMBERS", b"s"]), "~1\r\n$3\r\none\r\n");
2809 }
2810
2811 #[test]
2812 fn an_integer_member_comes_back_as_the_digits_it_never_stored() {
2813 let mut f = Fixture::new();
2816 f.run(&[b"SADD", b"s", b"42"]);
2817 assert_eq!(f.run(&[b"SMEMBERS", b"s"]), "*1\r\n$2\r\n42\r\n");
2818 assert_eq!(f.run(&[b"SISMEMBER", b"s", b"42"]), ":1\r\n");
2819 assert_eq!(
2820 f.run(&[b"SISMEMBER", b"s", b"042"]),
2821 ":0\r\n",
2822 "the member is the bytes and not the number they parse to"
2823 );
2824 }
2825
2826 #[test]
2827 fn the_wrong_command_at_the_wrong_type_says_so_both_ways() {
2828 let mut f = Fixture::new();
2829 f.run(&[b"SET", b"str", b"v"]);
2830 f.run(&[b"SADD", b"set", b"a"]);
2831
2832 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
2833 assert_eq!(f.run(&[b"SADD", b"str", b"a"]), wrong);
2834 assert_eq!(f.run(&[b"SCARD", b"str"]), wrong);
2835 assert_eq!(f.run(&[b"SMEMBERS", b"str"]), wrong);
2836 assert_eq!(f.run(&[b"SMISMEMBER", b"str", b"a"]), wrong);
2837 assert_eq!(f.run(&[b"GET", b"set"]), wrong);
2838 assert_eq!(f.run(&[b"APPEND", b"set", b"x"]), wrong);
2839 assert_eq!(f.run(&[b"INCR", b"set"]), wrong);
2840 assert_eq!(f.run(&[b"STRLEN", b"set"]), wrong);
2841
2842 assert_eq!(
2845 f.run(&[b"MGET", b"str", b"set", b"nope"]),
2846 "*3\r\n$1\r\nv\r\n$-1\r\n$-1\r\n"
2847 );
2848 assert_eq!(f.run(&[b"SET", b"set", b"now a string"]), "+OK\r\n");
2850 assert_eq!(f.run(&[b"TYPE", b"set"]), "+string\r\n");
2851 }
2852
2853 #[test]
2854 fn a_wrongtype_leaves_nothing_half_written() {
2855 let mut f = Fixture::new();
2859 f.run(&[b"SET", b"k", b"v"]);
2860 let reply = f.run(&[b"SMISMEMBER", b"k", b"a", b"b"]);
2861 assert!(reply.starts_with("-WRONGTYPE"), "got {reply}");
2862 assert!(!reply.contains('*'), "an array header went out in front");
2863 }
2864
2865 #[test]
2866 fn emptying_a_set_takes_the_key_with_it() {
2867 let mut f = Fixture::new();
2868 f.run(&[b"SADD", b"s", b"a", b"b"]);
2869 assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
2870 assert_eq!(f.run(&[b"SREM", b"s", b"a", b"b"]), ":2\r\n");
2871 assert_eq!(f.run(&[b"EXISTS", b"s"]), ":0\r\n");
2872 assert_eq!(f.run(&[b"TYPE", b"s"]), "+none\r\n");
2873 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
2874 }
2875
2876 fn split_scan(reply: &str) -> (String, Vec<String>) {
2882 let mut lines = reply.split("\r\n");
2883 assert_eq!(lines.next(), Some("*2"), "got {reply}");
2884 lines.next().expect("the cursor header");
2885 let cursor = lines.next().expect("the cursor").to_owned();
2886 let header = lines.next().expect("the member header");
2887 let n: usize = header[1..].parse().expect("a member count");
2888 let mut members = Vec::with_capacity(n);
2889 for _ in 0..n {
2890 lines.next().expect("a member header");
2891 members.push(lines.next().expect("a member").to_owned());
2892 }
2893 (cursor, members)
2894 }
2895
2896 #[test]
2897 fn popping_takes_a_member_off_the_set_and_hands_it_back() {
2898 let mut f = Fixture::new();
2899 f.run(&[b"SADD", b"s", b"a", b"b", b"c", b"d"]);
2900
2901 let one = f.run(&[b"SPOP", b"s"]);
2902 assert!(
2903 ["$1\r\na\r\n", "$1\r\nb\r\n", "$1\r\nc\r\n", "$1\r\nd\r\n"].contains(&one.as_str()),
2904 "got {one}"
2905 );
2906 assert_eq!(f.run(&[b"SCARD", b"s"]), ":3\r\n");
2907
2908 let (_, rest) = ("", f.run(&[b"SPOP", b"s", b"3"]));
2910 assert!(rest.starts_with("*3\r\n"), "got {rest}");
2911 assert_eq!(f.run(&[b"EXISTS", b"s"]), ":0\r\n");
2912 assert_eq!(f.run(&[b"SPOP", b"s"]), "$-1\r\n");
2914 assert_eq!(f.run(&[b"SPOP", b"s", b"2"]), "*0\r\n");
2915 }
2916
2917 #[test]
2918 fn the_two_draws_disagree_about_the_reply_type_and_they_are_right_to() {
2919 let mut f = Fixture::new();
2924 f.run(&[b"HELLO", b"3"]);
2925 f.run(&[b"SADD", b"s", b"a", b"b", b"c"]);
2926
2927 assert!(f.run(&[b"SPOP", b"s", b"2"]).starts_with("~2\r\n"));
2928 assert!(f.run(&[b"SRANDMEMBER", b"s", b"1"]).starts_with("*1\r\n"));
2930
2931 f.run(&[b"SADD", b"one", b"z"]);
2935 assert_eq!(
2936 f.run(&[b"SRANDMEMBER", b"one", b"-3"]),
2937 "*3\r\n$1\r\nz\r\n$1\r\nz\r\n$1\r\nz\r\n"
2938 );
2939 }
2940
2941 #[test]
2942 fn drawing_a_member_removes_nothing_and_says_nil_at_a_missing_key() {
2943 let mut f = Fixture::new();
2944 f.run(&[b"SADD", b"s", b"only"]);
2945 assert_eq!(f.run(&[b"SRANDMEMBER", b"s"]), "$4\r\nonly\r\n");
2946 assert_eq!(f.run(&[b"SRANDMEMBER", b"s"]), "$4\r\nonly\r\n");
2947 assert_eq!(f.run(&[b"SCARD", b"s"]), ":1\r\n");
2948
2949 assert_eq!(f.run(&[b"SRANDMEMBER", b"nope"]), "$-1\r\n");
2950 assert_eq!(f.run(&[b"SRANDMEMBER", b"nope", b"3"]), "*0\r\n");
2953 assert_eq!(f.run(&[b"SRANDMEMBER", b"nope", b"-3"]), "*0\r\n");
2954 assert_eq!(f.run(&[b"SRANDMEMBER", b"s", b"9"]), "*1\r\n$4\r\nonly\r\n");
2956 }
2957
2958 #[test]
2959 fn a_pop_count_that_is_not_a_positive_number_says_so() {
2960 let mut f = Fixture::new();
2961 f.run(&[b"SADD", b"s", b"a"]);
2962 let bad = "-ERR value is out of range, must be positive\r\n";
2963 assert_eq!(f.run(&[b"SPOP", b"s", b"-1"]), bad);
2964 assert_eq!(f.run(&[b"SPOP", b"s", b"abc"]), bad);
2965 assert_eq!(f.run(&[b"SCARD", b"s"]), ":1\r\n", "and took nothing");
2966 assert_eq!(f.run(&[b"SPOP", b"s", b"0"]), "*0\r\n");
2968 assert_eq!(f.run(&[b"EXISTS", b"s"]), ":1\r\n");
2969 }
2970
2971 #[test]
2972 fn a_scan_walks_a_set_of_any_size_exactly_once() {
2973 let mut f = Fixture::new();
2974 let members: Vec<Vec<u8>> = (0..300).map(|i| format!("m{i}").into_bytes()).collect();
2975 let args: Vec<&[u8]> = [&b"SADD"[..], &b"s"[..]]
2976 .into_iter()
2977 .chain(members.iter().map(Vec::as_slice))
2978 .collect();
2979 f.run(&args);
2980
2981 let mut seen = Vec::new();
2982 let mut cursor = "0".to_owned();
2983 loop {
2984 let reply = f.run(&[b"SSCAN", b"s", cursor.as_bytes()]);
2985 let (next, got) = split_scan(&reply);
2986 seen.extend(got);
2987 cursor = next;
2988 if cursor == "0" {
2989 break;
2990 }
2991 }
2992 seen.sort();
2993 seen.dedup();
2994 assert_eq!(seen.len(), 300, "a walk saw a member twice or missed one");
2995
2996 f.run(&[b"SADD", b"small", b"a", b"b", b"c"]);
2999 let (cursor, got) = split_scan(&f.run(&[b"SSCAN", b"small", b"0", b"COUNT", b"1"]));
3000 assert_eq!(cursor, "0");
3001 assert_eq!(got.len(), 3);
3002 assert_eq!(f.run(&[b"SSCAN", b"nope", b"0"]), "*2\r\n$1\r\n0\r\n*0\r\n");
3004 }
3005
3006 #[test]
3007 fn a_scan_takes_match_and_count_and_refuses_anything_else() {
3008 let mut f = Fixture::new();
3009 f.run(&[b"SADD", b"s", b"aa", b"ab", b"ba", b"12", b"13"]);
3010
3011 let (_, got) = split_scan(&f.run(&[b"SSCAN", b"s", b"0", b"MATCH", b"a*"]));
3012 let mut got = got;
3013 got.sort();
3014 assert_eq!(got, ["aa", "ab"]);
3015
3016 let (_, got) = split_scan(&f.run(&[b"SSCAN", b"s", b"0", b"MATCH", b"1?"]));
3019 let mut got = got;
3020 got.sort();
3021 assert_eq!(got, ["12", "13"]);
3022
3023 assert_eq!(f.run(&[b"SSCAN", b"s", b"abc"]), "-ERR invalid cursor\r\n");
3024 assert_eq!(f.run(&[b"SSCAN", b"s", b"-1"]), "-ERR invalid cursor\r\n");
3025 assert_eq!(
3026 f.run(&[b"SSCAN", b"s", b"0", b"NOPE", b"1"]),
3027 "-ERR syntax error\r\n"
3028 );
3029 assert_eq!(
3032 f.run(&[b"SSCAN", b"s", b"0", b"COUNT", b"0"]),
3033 "-ERR syntax error\r\n"
3034 );
3035 }
3036
3037 #[test]
3038 fn moving_a_member_takes_it_off_one_set_and_puts_it_on_another() {
3039 let mut f = Fixture::new();
3040 f.run(&[b"SADD", b"src", b"a", b"b"]);
3041 f.run(&[b"SADD", b"dst", b"c"]);
3042
3043 assert_eq!(f.run(&[b"SMOVE", b"src", b"dst", b"a"]), ":1\r\n");
3044 assert_eq!(f.run(&[b"SISMEMBER", b"src", b"a"]), ":0\r\n");
3045 assert_eq!(f.run(&[b"SISMEMBER", b"dst", b"a"]), ":1\r\n");
3046 assert_eq!(f.run(&[b"SMOVE", b"src", b"dst", b"zz"]), ":0\r\n");
3048 assert_eq!(f.run(&[b"SCARD", b"dst"]), ":2\r\n");
3049
3050 assert_eq!(f.run(&[b"SMOVE", b"src", b"fresh", b"b"]), ":1\r\n");
3053 assert_eq!(f.run(&[b"EXISTS", b"src"]), ":0\r\n");
3054 assert_eq!(f.run(&[b"SMEMBERS", b"fresh"]), "*1\r\n$1\r\nb\r\n");
3055 }
3056
3057 #[test]
3058 fn moving_checks_the_types_in_the_order_redis_checks_them() {
3059 let mut f = Fixture::new();
3063 f.run(&[b"SET", b"str", b"v"]);
3064 f.run(&[b"SADD", b"set", b"a"]);
3065
3066 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
3067 assert_eq!(f.run(&[b"SMOVE", b"nope", b"str", b"a"]), ":0\r\n");
3068 assert_eq!(f.run(&[b"SMOVE", b"str", b"set", b"a"]), wrong);
3069 assert_eq!(f.run(&[b"SMOVE", b"set", b"str", b"a"]), wrong);
3070 assert_eq!(f.run(&[b"SPOP", b"str"]), wrong);
3071 assert_eq!(f.run(&[b"SRANDMEMBER", b"str"]), wrong);
3072 assert_eq!(f.run(&[b"SSCAN", b"str", b"0"]), wrong);
3073 assert_eq!(
3074 f.run(&[b"SISMEMBER", b"set", b"a"]),
3075 ":1\r\n",
3076 "and none of that moved anything"
3077 );
3078 }
3079
3080 #[test]
3081 fn a_scan_leaves_nothing_half_written_when_its_arguments_are_wrong() {
3082 let mut f = Fixture::new();
3085 f.run(&[b"SADD", b"s", b"a"]);
3086 for bad in [
3087 &[b"SSCAN".as_slice(), b"s", b"abc"][..],
3088 &[b"SSCAN".as_slice(), b"s", b"0", b"COUNT", b"nope"][..],
3089 &[b"SSCAN".as_slice(), b"s", b"0", b"MATCH"][..],
3090 ] {
3091 let reply = f.run(bad);
3092 assert!(reply.starts_with("-ERR"), "got {reply}");
3093 assert!(!reply.contains('*'), "an array header went out in front");
3094 }
3095 }
3096
3097 #[test]
3098 fn a_hash_writes_reads_and_deletes_its_fields() {
3099 let mut f = Fixture::new();
3100 assert_eq!(f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]), ":2\r\n");
3101 assert_eq!(f.run(&[b"HSET", b"h", b"a", b"9"]), ":0\r\n", "a was there");
3102 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n9\r\n");
3103 assert_eq!(f.run(&[b"HGET", b"h", b"nope"]), "$-1\r\n");
3104 assert_eq!(f.run(&[b"HGET", b"nokey", b"a"]), "$-1\r\n");
3105 assert_eq!(f.run(&[b"HLEN", b"h"]), ":2\r\n");
3106 assert_eq!(f.run(&[b"HEXISTS", b"h", b"a"]), ":1\r\n");
3107 assert_eq!(f.run(&[b"HEXISTS", b"h", b"nope"]), ":0\r\n");
3108 assert_eq!(f.run(&[b"HSTRLEN", b"h", b"a"]), ":1\r\n");
3109 assert_eq!(f.run(&[b"HSTRLEN", b"h", b"nope"]), ":0\r\n");
3110
3111 assert_eq!(f.run(&[b"HGET", b"h", b"2"]), "$-1\r\n");
3114
3115 assert_eq!(f.run(&[b"HDEL", b"h", b"a", b"nope"]), ":1\r\n");
3116 assert_eq!(f.run(&[b"HDEL", b"h", b"b"]), ":1\r\n");
3117 assert_eq!(
3118 f.run(&[b"EXISTS", b"h"]),
3119 ":0\r\n",
3120 "and losing the last field lost the key"
3121 );
3122 }
3123
3124 #[test]
3125 fn hgetall_answers_a_map_on_resp3_and_the_same_pairs_flat_on_resp2() {
3126 let mut f = Fixture::new();
3127 f.run(&[b"HSET", b"h", b"a", b"1"]);
3128 assert_eq!(f.run(&[b"HGETALL", b"h"]), "*2\r\n$1\r\na\r\n$1\r\n1\r\n");
3129 assert_eq!(f.run(&[b"HGETALL", b"nokey"]), "*0\r\n");
3130 assert_eq!(f.run(&[b"HKEYS", b"h"]), "*1\r\n$1\r\na\r\n");
3131 assert_eq!(f.run(&[b"HVALS", b"h"]), "*1\r\n$1\r\n1\r\n");
3132 assert_eq!(f.run(&[b"HKEYS", b"nokey"]), "*0\r\n");
3133
3134 f.run(&[b"HELLO", b"3"]);
3135 assert_eq!(f.run(&[b"HGETALL", b"h"]), "%1\r\n$1\r\na\r\n$1\r\n1\r\n");
3136 assert_eq!(
3137 f.run(&[b"HGETALL", b"nokey"]),
3138 "%0\r\n",
3139 "a missing key is the empty hash and never a nil"
3140 );
3141 assert_eq!(
3142 f.run(&[b"HKEYS", b"h"]),
3143 "*1\r\n$1\r\na\r\n",
3144 "and the two that answer one side stay arrays"
3145 );
3146 }
3147
3148 #[test]
3149 fn hmget_answers_once_per_field_and_hmset_answers_ok() {
3150 let mut f = Fixture::new();
3151 assert_eq!(f.run(&[b"HMSET", b"h", b"a", b"1", b"c", b"3"]), "+OK\r\n");
3152 assert_eq!(
3153 f.run(&[b"HMGET", b"h", b"a", b"b", b"c"]),
3154 "*3\r\n$1\r\n1\r\n$-1\r\n$1\r\n3\r\n",
3155 "the reply is positional, so b is a nil and not a gap"
3156 );
3157 assert_eq!(
3158 f.run(&[b"HMGET", b"nokey", b"a", b"b"]),
3159 "*2\r\n$-1\r\n$-1\r\n",
3160 "and a missing key is all nils rather than an empty array"
3161 );
3162
3163 assert_eq!(f.run(&[b"HSETNX", b"h", b"a", b"9"]), ":0\r\n");
3164 assert_eq!(f.run(&[b"HSETNX", b"h", b"z", b"9"]), ":1\r\n");
3165 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
3166 }
3167
3168 #[test]
3169 fn a_hash_counts_up_and_says_so_when_it_cannot() {
3170 let mut f = Fixture::new();
3171 assert_eq!(f.run(&[b"HINCRBY", b"h", b"n", b"5"]), ":5\r\n");
3172 assert_eq!(f.run(&[b"HINCRBY", b"h", b"n", b"-7"]), ":-2\r\n");
3173 assert_eq!(f.run(&[b"HGET", b"h", b"n"]), "$2\r\n-2\r\n");
3174 assert_eq!(
3175 f.run(&[b"HINCRBYFLOAT", b"h", b"f", b"10.5"]),
3176 "$4\r\n10.5\r\n",
3177 "a bulk string and not a double, on both protocols"
3178 );
3179
3180 f.run(&[b"HSET", b"h", b"s", b"words"]);
3181 let bad = f.run(&[b"HINCRBY", b"h", b"s", b"1"]);
3182 assert!(
3183 bad.starts_with("-ERR hash value is not an integer"),
3184 "{bad}"
3185 );
3186 let bad = f.run(&[b"HINCRBY", b"h", b"n", b"nope"]);
3187 assert!(
3188 bad.starts_with("-ERR value is not an integer"),
3189 "a bad argument is not yet a hash value, {bad}"
3190 );
3191 assert_eq!(
3192 f.run(&[b"HGET", b"h", b"s"]),
3193 "$5\r\nwords\r\n",
3194 "and neither of them wrote anything"
3195 );
3196 }
3197
3198 #[test]
3199 fn a_hash_scan_walks_every_pair_once_and_novalues_drops_half_of_it() {
3200 let mut f = Fixture::new();
3201 for i in 0..500 {
3202 let field = format!("field-{i}");
3203 let value = format!("value-{i}");
3204 f.run(&[b"HSET", b"h", field.as_bytes(), value.as_bytes()]);
3205 }
3206
3207 let mut seen: Vec<String> = Vec::new();
3208 let mut cursor = "0".to_owned();
3209 loop {
3210 let reply = f.run(&[b"HSCAN", b"h", cursor.as_bytes(), b"COUNT", b"32"]);
3211 let (next, items) = scan_reply(&reply);
3212 assert_eq!(items.len() % 2, 0, "a pair went out half written");
3213 for pair in items.chunks(2) {
3214 assert_eq!(
3215 pair[0].strip_prefix("field-"),
3216 pair[1].strip_prefix("value-"),
3217 "a field came back with someone else's value"
3218 );
3219 seen.push(pair[0].clone());
3220 }
3221 cursor = next;
3222 if cursor == "0" {
3223 break;
3224 }
3225 }
3226 seen.sort();
3227 seen.dedup();
3228 assert_eq!(seen.len(), 500, "every field once and only once");
3229
3230 let (_, items) = scan_reply(&f.run(&[b"HSCAN", b"h", b"0", b"NOVALUES", b"COUNT", b"32"]));
3231 assert!(
3232 items.iter().all(|s| s.starts_with("field-")),
3233 "NOVALUES still sent the values"
3234 );
3235
3236 let (_, one) = scan_reply(&f.run(&[
3237 b"HSCAN",
3238 b"h",
3239 b"0",
3240 b"MATCH",
3241 b"field-499",
3242 b"COUNT",
3243 b"1000",
3244 ]));
3245 assert_eq!(one, ["field-499", "value-499"], "MATCH is on the field");
3246 }
3247
3248 #[test]
3249 fn hrandfield_draws_what_it_was_asked_for_and_nests_values_on_resp3() {
3250 let mut f = Fixture::new();
3251 f.run(&[b"HSET", b"h", b"a", b"1"]);
3252 assert_eq!(f.run(&[b"HRANDFIELD", b"h"]), "$1\r\na\r\n");
3253 assert_eq!(f.run(&[b"HRANDFIELD", b"nokey"]), "$-1\r\n");
3254 assert_eq!(f.run(&[b"HRANDFIELD", b"nokey", b"3"]), "*0\r\n");
3255 assert_eq!(
3256 f.run(&[b"HRANDFIELD", b"h", b"3"]),
3257 "*1\r\n$1\r\na\r\n",
3258 "a positive count is capped at the size of the hash"
3259 );
3260 assert_eq!(
3261 f.run(&[b"HRANDFIELD", b"h", b"-3"]),
3262 "*3\r\n$1\r\na\r\n$1\r\na\r\n$1\r\na\r\n",
3263 "and a negative one repeats itself"
3264 );
3265 assert_eq!(
3266 f.run(&[b"HRANDFIELD", b"h", b"1", b"WITHVALUES"]),
3267 "*2\r\n$1\r\na\r\n$1\r\n1\r\n",
3268 "flat on RESP2"
3269 );
3270
3271 f.run(&[b"HELLO", b"3"]);
3272 assert_eq!(
3273 f.run(&[b"HRANDFIELD", b"h", b"1", b"WITHVALUES"]),
3274 "*1\r\n*2\r\n$1\r\na\r\n$1\r\n1\r\n",
3275 "and nested on RESP3, but still an array and never a map"
3276 );
3277 }
3278
3279 #[test]
3280 fn every_hash_command_says_wrongtype_and_writes_nothing() {
3281 let mut f = Fixture::new();
3282 f.run(&[b"SET", b"str", b"v"]);
3283 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
3284
3285 for cmd in [
3286 &[b"HSET".as_slice(), b"str", b"f", b"v"][..],
3287 &[b"HMSET".as_slice(), b"str", b"f", b"v"][..],
3288 &[b"HSETNX".as_slice(), b"str", b"f", b"v"][..],
3289 &[b"HGET".as_slice(), b"str", b"f"][..],
3290 &[b"HMGET".as_slice(), b"str", b"f"][..],
3291 &[b"HDEL".as_slice(), b"str", b"f"][..],
3292 &[b"HLEN".as_slice(), b"str"][..],
3293 &[b"HEXISTS".as_slice(), b"str", b"f"][..],
3294 &[b"HSTRLEN".as_slice(), b"str", b"f"][..],
3295 &[b"HGETALL".as_slice(), b"str"][..],
3296 &[b"HKEYS".as_slice(), b"str"][..],
3297 &[b"HVALS".as_slice(), b"str"][..],
3298 &[b"HINCRBY".as_slice(), b"str", b"f", b"1"][..],
3299 &[b"HINCRBYFLOAT".as_slice(), b"str", b"f", b"1"][..],
3300 &[b"HRANDFIELD".as_slice(), b"str"][..],
3301 &[b"HRANDFIELD".as_slice(), b"str", b"2"][..],
3302 &[b"HSCAN".as_slice(), b"str", b"0"][..],
3303 ] {
3304 let reply = f.run(cmd);
3305 assert_eq!(reply, wrong, "{:?}", cmd[0]);
3306 }
3307 assert_eq!(
3308 f.run(&[b"GET", b"str"]),
3309 "$1\r\nv\r\n",
3310 "and none of them touched the value"
3311 );
3312 }
3313
3314 #[test]
3315 fn a_hash_scan_leaves_nothing_half_written_when_its_arguments_are_wrong() {
3316 let mut f = Fixture::new();
3317 f.run(&[b"HSET", b"h", b"f", b"v"]);
3318 for bad in [
3319 &[b"HSCAN".as_slice(), b"h", b"abc"][..],
3320 &[b"HSCAN".as_slice(), b"h", b"0", b"COUNT", b"nope"][..],
3321 &[b"HSCAN".as_slice(), b"h", b"0", b"COUNT", b"0"][..],
3322 &[b"HSCAN".as_slice(), b"h", b"0", b"MATCH"][..],
3323 ] {
3324 let reply = f.run(bad);
3325 assert!(reply.starts_with("-ERR"), "got {reply}");
3326 assert!(!reply.contains('*'), "an array header went out in front");
3327 }
3328 }
3329
3330 #[test]
3331 fn a_field_deadline_goes_on_and_comes_back_in_all_four_units() {
3332 let mut f = Fixture::new();
3333 f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
3334 assert_eq!(
3335 f.run(&[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"a"]),
3336 "*1\r\n:1\r\n"
3337 );
3338 assert_eq!(
3339 f.run(&[b"HTTL", b"h", b"FIELDS", b"3", b"a", b"b", b"nope"]),
3340 "*3\r\n:100\r\n:-1\r\n:-2\r\n",
3341 "one answer per field, and the two sentinels are TTL's own"
3342 );
3343
3344 let ms = int_reply(&f.run(&[b"HPTTL", b"h", b"FIELDS", b"1", b"a"]));
3347 assert!((99_000..=100_000).contains(&ms), "got {ms}");
3348 let at = int_reply(&f.run(&[b"HEXPIRETIME", b"h", b"FIELDS", b"1", b"a"]));
3349 let at_ms = int_reply(&f.run(&[b"HPEXPIRETIME", b"h", b"FIELDS", b"1", b"a"]));
3350 assert_eq!(at, at_ms.div_euclid(1000) + i64::from(at_ms % 1000 != 0));
3351 assert!(at_ms > 1_700_000_000_000, "an absolute moment, got {at_ms}");
3352
3353 assert_eq!(
3354 f.run(&[b"HPERSIST", b"h", b"FIELDS", b"3", b"a", b"b", b"nope"]),
3355 "*3\r\n:1\r\n:-1\r\n:-2\r\n",
3356 "one for the deadline taken off, and it does not say what it was"
3357 );
3358 assert_eq!(
3359 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
3360 "*1\r\n:-1\r\n"
3361 );
3362 assert_eq!(
3363 f.run(&[b"HGET", b"h", b"a"]),
3364 "$1\r\n1\r\n",
3365 "and the field is still there with the value it had"
3366 );
3367 }
3368
3369 #[test]
3370 fn a_deadline_that_has_already_gone_deletes_the_field_now() {
3371 let mut f = Fixture::new();
3372 f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
3373 assert_eq!(
3374 f.run(&[b"HEXPIREAT", b"h", b"1", b"FIELDS", b"1", b"a"]),
3375 "*1\r\n:2\r\n",
3376 "two, and not one, because nothing was stored"
3377 );
3378 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$-1\r\n");
3379 assert_eq!(f.run(&[b"HLEN", b"h"]), ":1\r\n");
3380
3381 assert_eq!(
3382 f.run(&[b"HPEXPIREAT", b"h", b"1", b"FIELDS", b"1", b"b"]),
3383 "*1\r\n:2\r\n"
3384 );
3385 assert_eq!(
3386 f.run(&[b"EXISTS", b"h"]),
3387 ":0\r\n",
3388 "and the last field going took the key with it"
3389 );
3390
3391 f.run(&[b"HSET", b"h", b"a", b"1"]);
3394 assert_eq!(
3395 f.run(&[b"HEXPIRE", b"h", b"0", b"FIELDS", b"1", b"a"]),
3396 "*1\r\n:2\r\n"
3397 );
3398 }
3399
3400 #[test]
3401 fn a_field_is_gone_once_its_moment_passes() {
3402 let mut f = Fixture::new();
3403 f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
3404 assert_eq!(
3405 f.run(&[b"HPEXPIRE", b"h", b"20", b"FIELDS", b"1", b"a"]),
3406 "*1\r\n:1\r\n"
3407 );
3408 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n", "not yet");
3409
3410 f.server.db(0).clock_mut().advance(60);
3414 assert_eq!(f.run(&[b"HLEN", b"h"]), ":1\r\n");
3415 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$-1\r\n");
3416 assert_eq!(
3417 f.run(&[b"HGETALL", b"h"]),
3418 "*2\r\n$1\r\nb\r\n$1\r\n2\r\n",
3419 "and the walks do not hand back a field that has expired"
3420 );
3421 }
3422
3423 #[test]
3424 fn a_missing_key_answers_the_no_field_sentinel_for_every_field() {
3425 let mut f = Fixture::new();
3426 for cmd in [
3427 &[
3428 b"HEXPIRE".as_slice(),
3429 b"nokey",
3430 b"100",
3431 b"FIELDS",
3432 b"2",
3433 b"a",
3434 b"b",
3435 ][..],
3436 &[b"HTTL".as_slice(), b"nokey", b"FIELDS", b"2", b"a", b"b"][..],
3437 &[b"HPTTL".as_slice(), b"nokey", b"FIELDS", b"2", b"a", b"b"][..],
3438 &[
3439 b"HEXPIRETIME".as_slice(),
3440 b"nokey",
3441 b"FIELDS",
3442 b"2",
3443 b"a",
3444 b"b",
3445 ][..],
3446 &[
3447 b"HPERSIST".as_slice(),
3448 b"nokey",
3449 b"FIELDS",
3450 b"2",
3451 b"a",
3452 b"b",
3453 ][..],
3454 ] {
3455 assert_eq!(f.run(cmd), "*2\r\n:-2\r\n:-2\r\n", "{:?}", cmd[0]);
3456 }
3457 }
3458
3459 #[test]
3460 fn writing_a_field_clears_the_deadline_that_was_on_it() {
3461 let mut f = Fixture::new();
3462 f.run(&[b"HSET", b"h", b"a", b"1"]);
3463 f.run(&[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"a"]);
3464 f.run(&[b"HSET", b"h", b"a", b"2"]);
3465 assert_eq!(
3466 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
3467 "*1\r\n:-1\r\n",
3468 "Redis has done this since 7.4, and it is why HGETEX exists"
3469 );
3470 }
3471
3472 #[test]
3473 fn the_four_conditions_reach_the_store_the_way_they_were_written() {
3474 let mut f = Fixture::new();
3475 f.run(&[b"HSET", b"h", b"a", b"1"]);
3476 assert_eq!(
3477 f.run(&[b"HEXPIRE", b"h", b"100", b"XX", b"FIELDS", b"1", b"a"]),
3478 "*1\r\n:0\r\n",
3479 "XX on a field with no deadline changes nothing"
3480 );
3481 assert_eq!(
3482 f.run(&[b"HEXPIRE", b"h", b"100", b"NX", b"FIELDS", b"1", b"a"]),
3483 "*1\r\n:1\r\n"
3484 );
3485 assert_eq!(
3486 f.run(&[b"HEXPIRE", b"h", b"200", b"NX", b"FIELDS", b"1", b"a"]),
3487 "*1\r\n:0\r\n",
3488 "and NX will not move one that is already there"
3489 );
3490 assert_eq!(
3491 f.run(&[b"HEXPIRE", b"h", b"50", b"GT", b"FIELDS", b"1", b"a"]),
3492 "*1\r\n:0\r\n"
3493 );
3494 assert_eq!(
3495 f.run(&[b"HEXPIRE", b"h", b"500", b"GT", b"FIELDS", b"1", b"a"]),
3496 "*1\r\n:1\r\n"
3497 );
3498 assert_eq!(
3499 f.run(&[b"HEXPIRE", b"h", b"50", b"LT", b"FIELDS", b"1", b"a"]),
3500 "*1\r\n:1\r\n"
3501 );
3502 assert_eq!(
3503 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
3504 "*1\r\n:50\r\n"
3505 );
3506 }
3507
3508 #[test]
3509 fn the_field_ttl_family_leaves_nothing_half_written_on_a_bad_argument() {
3510 let mut f = Fixture::new();
3511 f.run(&[b"HSET", b"h", b"a", b"1"]);
3512 for (bad, want) in [
3513 (
3514 &[b"HEXPIRE".as_slice(), b"h", b"-1", b"FIELDS", b"1", b"a"][..],
3515 "-ERR invalid expire time, must be >= 0",
3516 ),
3517 (
3518 &[
3519 b"HEXPIRE".as_slice(),
3520 b"h",
3521 b"9999999999999999",
3522 b"FIELDS",
3523 b"1",
3524 b"a",
3525 ][..],
3526 "-ERR invalid expire time in 'hexpire' command",
3527 ),
3528 (
3529 &[b"HEXPIRE".as_slice(), b"h", b"100", b"FIELD", b"1", b"a"][..],
3530 "-ERR wrong number of arguments for 'hexpire' command",
3531 ),
3532 (
3533 &[b"HEXPIRE".as_slice(), b"h", b"100", b"FIELDS", b"0", b"a"][..],
3534 "-ERR Parameter `numFields` should be greater than 0",
3535 ),
3536 (
3537 &[b"HEXPIRE".as_slice(), b"h", b"100", b"FIELDS", b"2", b"a"][..],
3538 "-ERR wrong number of arguments",
3539 ),
3540 (
3541 &[b"HTTL".as_slice(), b"h", b"FIELDS", b"3", b"a", b"b"][..],
3542 "-ERR wrong number of arguments",
3543 ),
3544 ] {
3545 let reply = f.run(bad);
3546 assert!(reply.starts_with(want), "wanted {want}, got {reply}");
3547 assert!(!reply.contains('*'), "an array header went out in front");
3548 }
3549 assert_eq!(
3550 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
3551 "*1\r\n:-1\r\n",
3552 "and not one of them put a deadline on anything"
3553 );
3554 }
3555
3556 #[test]
3557 fn every_field_ttl_command_says_wrongtype_and_writes_nothing() {
3558 let mut f = Fixture::new();
3559 f.run(&[b"SET", b"str", b"v"]);
3560 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
3561
3562 for cmd in [
3563 &[b"HEXPIRE".as_slice(), b"str", b"100", b"FIELDS", b"1", b"f"][..],
3564 &[
3565 b"HPEXPIRE".as_slice(),
3566 b"str",
3567 b"100",
3568 b"FIELDS",
3569 b"1",
3570 b"f",
3571 ][..],
3572 &[
3573 b"HEXPIREAT".as_slice(),
3574 b"str",
3575 b"9999999999",
3576 b"FIELDS",
3577 b"1",
3578 b"f",
3579 ][..],
3580 &[
3581 b"HPEXPIREAT".as_slice(),
3582 b"str",
3583 b"9999999999999",
3584 b"FIELDS",
3585 b"1",
3586 b"f",
3587 ][..],
3588 &[b"HTTL".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
3589 &[b"HPTTL".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
3590 &[b"HEXPIRETIME".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
3591 &[b"HPEXPIRETIME".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
3592 &[b"HPERSIST".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
3593 ] {
3594 assert_eq!(f.run(cmd), wrong, "{:?}", cmd[0]);
3595 }
3596 assert_eq!(
3597 f.run(&[b"GET", b"str"]),
3598 "$1\r\nv\r\n",
3599 "and none of them touched the value"
3600 );
3601 }
3602
3603 #[test]
3604 fn hgetdel_hands_the_value_out_and_then_takes_the_field() {
3605 let mut f = Fixture::new();
3606 f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
3607 assert_eq!(
3608 f.run(&[b"HGETDEL", b"h", b"FIELDS", b"2", b"a", b"nope"]),
3609 "*2\r\n$1\r\n1\r\n$-1\r\n",
3610 "positional, so the field that was not there is a nil in its place"
3611 );
3612 assert_eq!(f.run(&[b"HLEN", b"h"]), ":1\r\n");
3613 assert_eq!(
3614 f.run(&[b"HGETDEL", b"nokey", b"FIELDS", b"1", b"a"]),
3615 "*1\r\n$-1\r\n"
3616 );
3617 assert_eq!(
3618 f.run(&[b"HGETDEL", b"h", b"FIELDS", b"1", b"b"]),
3619 "*1\r\n$1\r\n2\r\n"
3620 );
3621 assert_eq!(
3622 f.run(&[b"EXISTS", b"h"]),
3623 ":0\r\n",
3624 "and the last field took the key"
3625 );
3626 }
3627
3628 #[test]
3629 fn hgetex_reads_and_moves_the_deadline_in_one_command() {
3630 let mut f = Fixture::new();
3631 f.run(&[b"HSET", b"h", b"a", b"1"]);
3632 assert_eq!(
3633 f.run(&[b"HGETEX", b"h", b"FIELDS", b"1", b"a"]),
3634 "*1\r\n$1\r\n1\r\n"
3635 );
3636 assert_eq!(
3637 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
3638 "*1\r\n:-1\r\n",
3639 "no option means leave it alone, which is the one place this is not GETEX"
3640 );
3641
3642 f.run(&[b"HGETEX", b"h", b"EX", b"100", b"FIELDS", b"1", b"a"]);
3643 assert_eq!(
3644 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
3645 "*1\r\n:100\r\n"
3646 );
3647 f.run(&[b"HGETEX", b"h", b"FIELDS", b"1", b"a"]);
3648 assert_eq!(
3649 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
3650 "*1\r\n:100\r\n",
3651 "and a plain read really does leave it alone"
3652 );
3653 assert_eq!(
3654 f.run(&[b"HGETEX", b"h", b"PERSIST", b"FIELDS", b"1", b"a"]),
3655 "*1\r\n$1\r\n1\r\n"
3656 );
3657 assert_eq!(
3658 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
3659 "*1\r\n:-1\r\n"
3660 );
3661
3662 assert_eq!(
3663 f.run(&[b"HGETEX", b"h", b"EXAT", b"1", b"FIELDS", b"1", b"a"]),
3664 "*1\r\n$1\r\n1\r\n",
3665 "the value goes out before the deadline that has already gone is applied"
3666 );
3667 assert_eq!(f.run(&[b"EXISTS", b"h"]), ":0\r\n");
3668 assert_eq!(
3669 f.run(&[b"HGETEX", b"nokey", b"EX", b"100", b"FIELDS", b"1", b"a"]),
3670 "*1\r\n$-1\r\n"
3671 );
3672 }
3673
3674 #[test]
3675 fn hsetex_writes_all_of_it_or_none_of_it() {
3676 let mut f = Fixture::new();
3677 assert_eq!(
3678 f.run(&[b"HSETEX", b"h", b"FIELDS", b"1", b"a", b"1"]),
3679 ":1\r\n"
3680 );
3681 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
3682 assert_eq!(
3683 f.run(&[
3684 b"HSETEX", b"h", b"FNX", b"FIELDS", b"2", b"a", b"9", b"new", b"9"
3685 ]),
3686 ":0\r\n",
3687 "FNX wants every field named to be missing"
3688 );
3689 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
3690 assert_eq!(
3691 f.run(&[b"HEXISTS", b"h", b"new"]),
3692 ":0\r\n",
3693 "and none of the list was written"
3694 );
3695 assert_eq!(
3696 f.run(&[
3697 b"HSETEX", b"h", b"FXX", b"FIELDS", b"2", b"a", b"9", b"nope", b"9"
3698 ]),
3699 ":0\r\n",
3700 "and FXX wants every one of them to be there"
3701 );
3702 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
3703 assert_eq!(
3704 f.run(&[b"HSETEX", b"h", b"FXX", b"FIELDS", b"1", b"a", b"9"]),
3705 ":1\r\n"
3706 );
3707 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n9\r\n");
3708
3709 assert_eq!(
3710 f.run(&[b"HSETEX", b"gone", b"FXX", b"FIELDS", b"1", b"a", b"1"]),
3711 ":0\r\n"
3712 );
3713 assert_eq!(
3714 f.run(&[b"EXISTS", b"gone"]),
3715 ":0\r\n",
3716 "a key with no fields cannot meet FXX and is not created trying"
3717 );
3718 }
3719
3720 #[test]
3721 fn hsetex_clears_the_deadline_unless_it_is_told_to_keep_it() {
3722 let mut f = Fixture::new();
3723 f.run(&[b"HSETEX", b"h", b"EX", b"100", b"FIELDS", b"1", b"a", b"1"]);
3724 assert_eq!(
3725 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
3726 "*1\r\n:100\r\n"
3727 );
3728
3729 f.run(&[b"HSETEX", b"h", b"KEEPTTL", b"FIELDS", b"1", b"a", b"2"]);
3730 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n2\r\n");
3731 assert_eq!(
3732 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
3733 "*1\r\n:100\r\n",
3734 "KEEPTTL put back what the write cleared"
3735 );
3736
3737 f.run(&[b"HSETEX", b"h", b"FIELDS", b"1", b"a", b"3"]);
3738 assert_eq!(
3739 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
3740 "*1\r\n:-1\r\n",
3741 "and without it a write clears the deadline the way HSET does"
3742 );
3743
3744 assert_eq!(
3747 f.run(&[
3748 b"HSETEX", b"h", b"PX", b"100000", b"FXX", b"FIELDS", b"1", b"a", b"4"
3749 ]),
3750 ":1\r\n"
3751 );
3752 assert_eq!(
3753 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
3754 "*1\r\n:100\r\n"
3755 );
3756
3757 assert_eq!(
3758 f.run(&[b"HSETEX", b"h", b"EXAT", b"1", b"FIELDS", b"1", b"a", b"5"]),
3759 ":1\r\n",
3760 "written, and not the separate code the HEXPIRE family has for this"
3761 );
3762 assert_eq!(
3763 f.run(&[b"EXISTS", b"h"]),
3764 ":0\r\n",
3765 "and storing it and then removing it emptied the hash"
3766 );
3767 }
3768
3769 #[test]
3770 fn the_last_three_hash_commands_word_their_mistakes_their_own_way() {
3771 let mut f = Fixture::new();
3772 f.run(&[b"HSET", b"h", b"a", b"1"]);
3773 for (bad, want) in [
3774 (
3776 &[b"HGETDEL".as_slice(), b"h", b"FIELDS", b"0", b"a"][..],
3777 "-ERR Number of fields must be a positive integer",
3778 ),
3779 (
3780 &[b"HGETDEL".as_slice(), b"h", b"FIELDS", b"2", b"a"][..],
3781 "-ERR The `numfields` parameter must match the number of arguments",
3782 ),
3783 (
3784 &[b"HGETDEL".as_slice(), b"h", b"FIELD", b"1", b"a"][..],
3785 "-ERR Mandatory argument FIELDS is missing or not at the right position",
3786 ),
3787 (
3789 &[b"HGETEX".as_slice(), b"h", b"FIELDS", b"0", b"a"][..],
3790 "-ERR invalid number of fields",
3791 ),
3792 (
3793 &[b"HGETEX".as_slice(), b"h", b"FIELDS", b"2", b"a"][..],
3794 "-ERR wrong number of arguments",
3795 ),
3796 (
3797 &[b"HGETEX".as_slice(), b"h", b"FIELD", b"1", b"a"][..],
3798 "-ERR unknown argument: FIELD",
3799 ),
3800 (
3801 &[
3802 b"HGETEX".as_slice(),
3803 b"h",
3804 b"KEEPTTL",
3805 b"FIELDS",
3806 b"1",
3807 b"a",
3808 ][..],
3809 "-ERR unknown argument: KEEPTTL",
3810 ),
3811 (
3812 &[
3813 b"HGETEX".as_slice(),
3814 b"h",
3815 b"EX",
3816 b"100",
3817 b"PERSIST",
3818 b"FIELDS",
3819 b"1",
3820 b"a",
3821 ][..],
3822 "-ERR Only one of EX, PX, EXAT, PXAT or PERSIST arguments can be specified",
3823 ),
3824 (
3825 &[
3826 b"HSETEX".as_slice(),
3827 b"h",
3828 b"EX",
3829 b"1",
3830 b"KEEPTTL",
3831 b"FIELDS",
3832 b"1",
3833 b"a",
3834 b"1",
3835 ][..],
3836 "-ERR Only one of EX, PX, EXAT, PXAT or KEEPTTL arguments can be specified",
3837 ),
3838 (
3839 &[
3840 b"HSETEX".as_slice(),
3841 b"h",
3842 b"FNX",
3843 b"FXX",
3844 b"FIELDS",
3845 b"1",
3846 b"a",
3847 b"1",
3848 ][..],
3849 "-ERR Only one of FXX or FNX arguments can be specified",
3850 ),
3851 (
3852 &[
3853 b"HSETEX".as_slice(),
3854 b"h",
3855 b"FIELDS",
3856 b"2",
3857 b"a",
3858 b"1",
3859 b"b",
3860 ][..],
3861 "-ERR wrong number of arguments",
3862 ),
3863 (
3864 &[
3865 b"HGETEX".as_slice(),
3866 b"h",
3867 b"EX",
3868 b"-1",
3869 b"FIELDS",
3870 b"1",
3871 b"a",
3872 ][..],
3873 "-ERR invalid expire time, must be >= 0",
3874 ),
3875 (
3876 &[
3877 b"HGETEX".as_slice(),
3878 b"h",
3879 b"PXAT",
3880 b"99999999999999",
3881 b"FIELDS",
3882 b"1",
3883 b"a",
3884 ][..],
3885 "-ERR invalid expire time in 'hgetex' command",
3886 ),
3887 (
3888 &[
3889 b"HSETEX".as_slice(),
3890 b"h",
3891 b"EX",
3892 b"abc",
3893 b"FIELDS",
3894 b"1",
3895 b"a",
3896 b"1",
3897 ][..],
3898 "-ERR value is not an integer or out of range",
3899 ),
3900 ] {
3901 let reply = f.run(bad);
3902 assert!(reply.starts_with(want), "wanted {want}, got {reply}");
3903 assert!(!reply.contains('*'), "an array header went out in front");
3904 }
3905 assert_eq!(
3906 f.run(&[b"HGET", b"h", b"a"]),
3907 "$1\r\n1\r\n",
3908 "and not one of them wrote anything"
3909 );
3910 assert_eq!(
3911 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
3912 "*1\r\n:-1\r\n"
3913 );
3914 }
3915
3916 #[test]
3917 fn the_last_three_hash_commands_say_wrongtype_and_write_nothing() {
3918 let mut f = Fixture::new();
3919 f.run(&[b"SET", b"str", b"v"]);
3920 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
3921 for cmd in [
3922 &[b"HGETDEL".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
3923 &[b"HGETEX".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
3924 &[
3925 b"HGETEX".as_slice(),
3926 b"str",
3927 b"EX",
3928 b"100",
3929 b"FIELDS",
3930 b"1",
3931 b"f",
3932 ][..],
3933 &[b"HSETEX".as_slice(), b"str", b"FIELDS", b"1", b"f", b"v"][..],
3934 ] {
3935 assert_eq!(f.run(cmd), wrong, "{:?}", cmd[0]);
3936 }
3937 assert_eq!(f.run(&[b"GET", b"str"]), "$1\r\nv\r\n");
3938 }
3939
3940 fn int(reply: &str) -> i64 {
3946 let body = reply
3947 .strip_prefix(':')
3948 .and_then(|s| s.strip_suffix("\r\n"))
3949 .unwrap_or_else(|| panic!("wanted an integer, got {reply}"));
3950 body.parse().expect("an integer")
3951 }
3952
3953 fn int_reply(reply: &str) -> i64 {
3954 let body = reply
3955 .strip_prefix("*1\r\n:")
3956 .and_then(|s| s.strip_suffix("\r\n"))
3957 .unwrap_or_else(|| panic!("wanted one integer, got {reply}"));
3958 body.parse().expect("an integer")
3959 }
3960
3961 fn scan_reply(reply: &str) -> (String, Vec<String>) {
3963 let mut lines = reply.split("\r\n");
3964 assert_eq!(lines.next(), Some("*2"), "got {reply}");
3965 lines.next().expect("the cursor header");
3966 let cursor = lines.next().expect("a cursor").to_owned();
3967 let header = lines.next().expect("an item count");
3968 let n: usize = header[1..].parse().expect("a count");
3969 let mut items = Vec::with_capacity(n);
3970 for _ in 0..n {
3971 lines.next().expect("an item header");
3972 items.push(lines.next().expect("an item").to_owned());
3973 }
3974 (cursor, items)
3975 }
3976
3977 fn sorted(reply: &str) -> Vec<String> {
3980 let mut lines = reply.split("\r\n");
3981 let header = lines.next().expect("a header");
3982 assert!(
3983 header.starts_with('*') || header.starts_with('~'),
3984 "got {reply}"
3985 );
3986 let n: usize = header[1..].parse().expect("a member count");
3987 let mut got = Vec::with_capacity(n);
3988 for _ in 0..n {
3989 lines.next().expect("a member header");
3990 got.push(lines.next().expect("a member").to_owned());
3991 }
3992 got.sort();
3993 got
3994 }
3995
3996 #[test]
3997 fn the_algebra_answers_what_the_sets_share_and_do_not() {
3998 let mut f = Fixture::new();
3999 f.run(&[b"SADD", b"a", b"1", b"2", b"3"]);
4000 f.run(&[b"SADD", b"b", b"2", b"3", b"4"]);
4001 f.run(&[b"SADD", b"c", b"3", b"4", b"5"]);
4002
4003 assert_eq!(sorted(&f.run(&[b"SINTER", b"a", b"b", b"c"])), ["3"]);
4004 assert_eq!(
4005 sorted(&f.run(&[b"SUNION", b"a", b"b", b"c"])),
4006 ["1", "2", "3", "4", "5"]
4007 );
4008 assert_eq!(sorted(&f.run(&[b"SDIFF", b"a", b"b"])), ["1"]);
4009 assert_eq!(sorted(&f.run(&[b"SINTER", b"a"])), ["1", "2", "3"]);
4010
4011 assert_eq!(f.run(&[b"SINTER", b"a", b"nope"]), "*0\r\n");
4014 assert_eq!(sorted(&f.run(&[b"SUNION", b"a", b"nope"])), ["1", "2", "3"]);
4015 assert_eq!(f.run(&[b"SDIFF", b"nope", b"a"]), "*0\r\n");
4016 assert_eq!(f.run(&[b"DBSIZE"]), ":3\r\n", "and none of it made a key");
4017 }
4018
4019 #[test]
4020 fn the_algebra_answers_a_set_on_resp3_and_an_array_on_resp2() {
4021 let mut f = Fixture::new();
4022 f.run(&[b"SADD", b"a", b"x"]);
4023 assert_eq!(f.run(&[b"SINTER", b"a"]), "*1\r\n$1\r\nx\r\n");
4024 assert_eq!(f.run(&[b"SUNION", b"a"]), "*1\r\n$1\r\nx\r\n");
4025 assert_eq!(f.run(&[b"SDIFF", b"a"]), "*1\r\n$1\r\nx\r\n");
4026
4027 f.run(&[b"HELLO", b"3"]);
4028 assert_eq!(f.run(&[b"SINTER", b"a"]), "~1\r\n$1\r\nx\r\n");
4029 assert_eq!(f.run(&[b"SUNION", b"a"]), "~1\r\n$1\r\nx\r\n");
4030 assert_eq!(f.run(&[b"SDIFF", b"a"]), "~1\r\n$1\r\nx\r\n");
4031 assert_eq!(f.run(&[b"SINTER", b"nope"]), "~0\r\n");
4032 }
4033
4034 #[test]
4035 fn a_store_form_writes_a_key_and_answers_how_big_it_is() {
4036 let mut f = Fixture::new();
4037 f.run(&[b"SADD", b"a", b"1", b"2", b"3"]);
4038 f.run(&[b"SADD", b"b", b"2", b"3", b"4"]);
4039
4040 assert_eq!(f.run(&[b"SINTERSTORE", b"d", b"a", b"b"]), ":2\r\n");
4041 assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"d"])), ["2", "3"]);
4042 assert_eq!(f.run(&[b"SUNIONSTORE", b"d", b"a", b"b"]), ":4\r\n");
4043 assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"d"])), ["1", "2", "3", "4"]);
4044 assert_eq!(f.run(&[b"SDIFFSTORE", b"d", b"a", b"b"]), ":1\r\n");
4045 assert_eq!(f.run(&[b"SMEMBERS", b"d"]), "*1\r\n$1\r\n1\r\n");
4046
4047 assert_eq!(f.run(&[b"SDIFFSTORE", b"d", b"a", b"a"]), ":0\r\n");
4050 assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
4051 assert_eq!(f.run(&[b"SINTERSTORE", b"a", b"a", b"b"]), ":2\r\n");
4052 assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"a"])), ["2", "3"]);
4053
4054 f.run(&[b"SET", b"str", b"v"]);
4057 assert_eq!(f.run(&[b"SUNIONSTORE", b"str", b"b"]), ":3\r\n");
4058 assert_eq!(f.run(&[b"TYPE", b"str"]), "+set\r\n");
4059 }
4060
4061 #[test]
4062 fn sintercard_counts_without_building_and_stops_at_a_limit() {
4063 let mut f = Fixture::new();
4064 f.run(&[b"SADD", b"a", b"1", b"2", b"3", b"4"]);
4065 f.run(&[b"SADD", b"b", b"2", b"3", b"4", b"5"]);
4066
4067 assert_eq!(f.run(&[b"SINTERCARD", b"2", b"a", b"b"]), ":3\r\n");
4068 assert_eq!(
4069 f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"LIMIT", b"2"]),
4070 ":2\r\n"
4071 );
4072 assert_eq!(
4073 f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"LIMIT", b"0"]),
4074 ":3\r\n",
4075 "a limit of zero is no limit"
4076 );
4077 assert_eq!(f.run(&[b"SINTERCARD", b"1", b"a"]), ":4\r\n");
4078 assert_eq!(f.run(&[b"SINTERCARD", b"2", b"a", b"nope"]), ":0\r\n");
4079
4080 assert_eq!(
4082 f.run(&[b"SINTERCARD", b"0", b"a"]),
4083 "-ERR numkeys should be greater than 0\r\n"
4084 );
4085 assert_eq!(
4086 f.run(&[b"SINTERCARD", b"abc", b"a"]),
4087 "-ERR numkeys should be greater than 0\r\n"
4088 );
4089 assert_eq!(
4090 f.run(&[b"SINTERCARD", b"3", b"a", b"b"]),
4091 "-ERR Number of keys can't be greater than number of args\r\n"
4092 );
4093 assert_eq!(
4094 f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"LIMIT", b"-1"]),
4095 "-ERR LIMIT can't be negative\r\n"
4096 );
4097 assert_eq!(
4098 f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"NOPE", b"1"]),
4099 "-ERR syntax error\r\n"
4100 );
4101 f.run(&[b"SADD", b"LIMIT", b"2"]);
4103 assert_eq!(f.run(&[b"SINTERCARD", b"2", b"a", b"LIMIT"]), ":1\r\n");
4104 }
4105
4106 #[test]
4107 fn the_algebra_answers_wrongtype_before_it_writes_anything() {
4108 let mut f = Fixture::new();
4109 f.run(&[b"SADD", b"a", b"1"]);
4110 f.run(&[b"SADD", b"d", b"old"]);
4111 f.run(&[b"SET", b"str", b"v"]);
4112
4113 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
4114 for bad in [
4115 &[b"SINTER".as_slice(), b"a", b"str"][..],
4116 &[b"SUNION".as_slice(), b"str"][..],
4117 &[b"SDIFF".as_slice(), b"a", b"str"][..],
4118 &[b"SINTERCARD".as_slice(), b"2", b"a", b"str"][..],
4119 &[b"SINTERSTORE".as_slice(), b"d", b"a", b"str"][..],
4120 &[b"SUNIONSTORE".as_slice(), b"d", b"str"][..],
4121 &[b"SDIFFSTORE".as_slice(), b"d", b"a", b"str"][..],
4122 ] {
4123 let reply = f.run(bad);
4124 assert_eq!(reply, wrong, "for {:?}", bad[0]);
4125 }
4126 assert_eq!(
4127 f.run(&[b"SMEMBERS", b"d"]),
4128 "*1\r\n$3\r\nold\r\n",
4129 "and the destination was left alone every time"
4130 );
4131 }
4132
4133 #[test]
4136 fn churning_sets_does_not_grow_the_server() {
4137 let mut f = Fixture::new();
4138 let members: Vec<Vec<u8>> = (0..200).map(|i| format!("m{i}").into_bytes()).collect();
4139 let args: Vec<&[u8]> = std::iter::once(&b"SADD"[..])
4140 .chain(std::iter::once(&b"s"[..]))
4141 .chain(members.iter().map(Vec::as_slice))
4142 .collect();
4143
4144 f.run(&args);
4145 f.run(&[b"DEL", b"s"]);
4146 f.server.compact_step();
4147 let after_first = f.server.memory_bytes();
4148
4149 for _ in 0..200 {
4150 f.run(&args);
4151 f.run(&[b"DEL", b"s"]);
4152 f.server.compact_step();
4153 }
4154 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
4155 assert!(
4156 f.server.memory_bytes() <= after_first * 2,
4157 "held {} after two hundred passes against {after_first} after one",
4158 f.server.memory_bytes()
4159 );
4160 }
4161
4162 fn bulks(parts: &[&str]) -> String {
4165 let mut s = format!("*{}\r\n", parts.len());
4166 for p in parts {
4167 s.push_str(&format!("${}\r\n{p}\r\n", p.len()));
4168 }
4169 s
4170 }
4171
4172 #[test]
4173 fn a_list_is_pushed_from_both_ends_and_the_left_one_reverses() {
4174 let mut f = Fixture::new();
4175 assert_eq!(f.run(&[b"LPUSH", b"k", b"a", b"b", b"c"]), ":3\r\n");
4179 assert_eq!(
4180 f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
4181 bulks(&["c", "b", "a"])
4182 );
4183 assert_eq!(f.run(&[b"RPUSH", b"k", b"d"]), ":4\r\n");
4184 assert_eq!(f.run(&[b"LLEN", b"k"]), ":4\r\n");
4185 assert_eq!(f.run(&[b"LPOP", b"k"]), "$1\r\nc\r\n");
4186 assert_eq!(f.run(&[b"RPOP", b"k"]), "$1\r\nd\r\n");
4187 assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["b", "a"]));
4188 assert_eq!(f.run(&[b"TYPE", b"k"]), "+list\r\n");
4189 }
4190
4191 #[test]
4192 fn the_x_pushes_refuse_to_bring_a_list_back_to_life() {
4193 let mut f = Fixture::new();
4194 assert_eq!(f.run(&[b"LPUSHX", b"k", b"a"]), ":0\r\n");
4195 assert_eq!(f.run(&[b"RPUSHX", b"k", b"a"]), ":0\r\n");
4196 assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
4197 f.run(&[b"RPUSH", b"k", b"a"]);
4198 assert_eq!(f.run(&[b"LPUSHX", b"k", b"z"]), ":2\r\n");
4199 assert_eq!(f.run(&[b"RPUSHX", b"k", b"y"]), ":3\r\n");
4200 assert_eq!(
4201 f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
4202 bulks(&["z", "a", "y"])
4203 );
4204 }
4205
4206 #[test]
4209 fn an_empty_pop_is_a_different_nothing_with_a_count_and_without() {
4210 let mut f = Fixture::new();
4211 assert_eq!(f.run(&[b"LPOP", b"nope"]), "$-1\r\n");
4212 assert_eq!(f.run(&[b"LPOP", b"nope", b"2"]), "*-1\r\n");
4213 assert_eq!(f.run(&[b"RPOP", b"nope"]), "$-1\r\n");
4214 assert_eq!(f.run(&[b"RPOP", b"nope", b"2"]), "*-1\r\n");
4215 f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
4216 assert_eq!(f.run(&[b"LPOP", b"k", b"0"]), "*0\r\n");
4219 assert_eq!(f.run(&[b"LPOP", b"k", b"1"]), bulks(&["a"]));
4220 assert_eq!(f.run(&[b"RPOP", b"k", b"9"]), bulks(&["c", "b"]));
4222 assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
4223 }
4224
4225 #[test]
4226 fn a_pop_count_has_its_own_sentence_and_a_third_argument_is_an_arity_error() {
4227 let mut f = Fixture::new();
4228 f.run(&[b"RPUSH", b"k", b"a"]);
4229 let range = "-ERR value is out of range, must be positive\r\n";
4230 assert_eq!(f.run(&[b"LPOP", b"k", b"-1"]), range);
4231 assert_eq!(f.run(&[b"LPOP", b"k", b"abc"]), range);
4232 assert_eq!(f.run(&[b"RPOP", b"k", b"-1"]), range);
4233 assert_eq!(
4236 f.run(&[b"LPOP", b"k", b"1", b"2"]),
4237 "-ERR wrong number of arguments for 'lpop' command\r\n"
4238 );
4239 assert_eq!(f.run(&[b"LLEN", b"k"]), ":1\r\n");
4240 }
4241
4242 #[test]
4243 fn a_range_takes_negative_ends_and_clamps_the_ones_that_run_off() {
4244 let mut f = Fixture::new();
4245 f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
4246 assert_eq!(
4247 f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
4248 bulks(&["a", "b", "c"])
4249 );
4250 assert_eq!(f.run(&[b"LRANGE", b"k", b"-2", b"-1"]), bulks(&["b", "c"]));
4251 assert_eq!(f.run(&[b"LRANGE", b"k", b"1", b"1"]), bulks(&["b"]));
4252 assert_eq!(f.run(&[b"LRANGE", b"k", b"5", b"10"]), "*0\r\n");
4253 assert_eq!(f.run(&[b"LRANGE", b"k", b"2", b"1"]), "*0\r\n");
4254 assert_eq!(
4255 f.run(&[b"LRANGE", b"k", b"-100", b"100"]),
4256 bulks(&["a", "b", "c"])
4257 );
4258 assert_eq!(f.run(&[b"LRANGE", b"nope", b"0", b"-1"]), "*0\r\n");
4261 assert_eq!(
4262 f.run(&[b"LRANGE", b"k", b"a", b"b"]),
4263 "-ERR value is not an integer or out of range\r\n"
4264 );
4265 }
4266
4267 #[test]
4268 fn an_index_reads_and_writes_from_whichever_end_is_nearer() {
4269 let mut f = Fixture::new();
4270 f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
4271 assert_eq!(f.run(&[b"LINDEX", b"k", b"0"]), "$1\r\na\r\n");
4272 assert_eq!(f.run(&[b"LINDEX", b"k", b"-1"]), "$1\r\nc\r\n");
4273 assert_eq!(f.run(&[b"LINDEX", b"k", b"99"]), "$-1\r\n");
4274 assert_eq!(f.run(&[b"LINDEX", b"nope", b"0"]), "$-1\r\n");
4275 assert_eq!(f.run(&[b"LSET", b"k", b"-1", b"z"]), "+OK\r\n");
4276 assert_eq!(
4277 f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
4278 bulks(&["a", "b", "z"])
4279 );
4280 assert_eq!(
4283 f.run(&[b"LSET", b"k", b"99", b"z"]),
4284 "-ERR index out of range\r\n"
4285 );
4286 assert_eq!(
4287 f.run(&[b"LSET", b"nope", b"0", b"z"]),
4288 "-ERR no such key\r\n"
4289 );
4290 }
4291
4292 #[test]
4293 fn linsert_says_three_things_with_one_signed_number() {
4294 let mut f = Fixture::new();
4295 assert_eq!(
4298 f.run(&[b"LINSERT", b"nope", b"BEFORE", b"a", b"x"]),
4299 ":0\r\n"
4300 );
4301 f.run(&[b"RPUSH", b"k", b"a", b"b"]);
4302 assert_eq!(f.run(&[b"LINSERT", b"k", b"before", b"a", b"X"]), ":3\r\n");
4303 assert_eq!(f.run(&[b"LINSERT", b"k", b"AFTER", b"b", b"Y"]), ":4\r\n");
4304 assert_eq!(
4305 f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
4306 bulks(&["X", "a", "b", "Y"])
4307 );
4308 assert_eq!(
4309 f.run(&[b"LINSERT", b"k", b"BEFORE", b"zz", b"x"]),
4310 ":-1\r\n"
4311 );
4312 assert_eq!(
4313 f.run(&[b"LINSERT", b"k", b"SIDEWAYS", b"a", b"x"]),
4314 "-ERR syntax error\r\n"
4315 );
4316 }
4317
4318 #[test]
4319 fn lrem_counts_in_three_directions_and_takes_the_key_when_it_empties() {
4320 let mut f = Fixture::new();
4321 f.run(&[b"RPUSH", b"k", b"a", b"b", b"a", b"c", b"a"]);
4322 assert_eq!(f.run(&[b"LREM", b"k", b"2", b"a"]), ":2\r\n");
4323 assert_eq!(
4324 f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
4325 bulks(&["b", "c", "a"])
4326 );
4327 assert_eq!(f.run(&[b"LREM", b"k", b"-1", b"a"]), ":1\r\n");
4328 assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["b", "c"]));
4329 assert_eq!(f.run(&[b"LREM", b"k", b"0", b"b"]), ":1\r\n");
4330 assert_eq!(f.run(&[b"LREM", b"k", b"0", b"c"]), ":1\r\n");
4331 assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
4332 assert_eq!(f.run(&[b"LREM", b"nope", b"0", b"a"]), ":0\r\n");
4333 }
4334
4335 #[test]
4336 fn ltrim_keeps_a_window_and_an_empty_one_deletes_the_key() {
4337 let mut f = Fixture::new();
4338 f.run(&[b"RPUSH", b"k", b"a", b"b", b"c", b"d"]);
4339 assert_eq!(f.run(&[b"LTRIM", b"k", b"1", b"-2"]), "+OK\r\n");
4340 assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["b", "c"]));
4341 assert_eq!(f.run(&[b"LTRIM", b"k", b"1", b"0"]), "+OK\r\n");
4344 assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
4345 assert_eq!(f.run(&[b"LTRIM", b"nope", b"0", b"-1"]), "+OK\r\n");
4346 }
4347
4348 #[test]
4349 fn lpos_walks_from_either_end_and_stops_where_it_is_told() {
4350 let mut f = Fixture::new();
4351 f.run(&[b"RPUSH", b"p", b"a", b"b", b"c", b"a", b"b", b"c", b"a"]);
4352 assert_eq!(f.run(&[b"LPOS", b"p", b"a"]), ":0\r\n");
4353 assert_eq!(f.run(&[b"LPOS", b"p", b"a", b"RANK", b"-1"]), ":6\r\n");
4354 assert_eq!(f.run(&[b"LPOS", b"p", b"a", b"RANK", b"2"]), ":3\r\n");
4355 assert_eq!(
4356 f.run(&[b"LPOS", b"p", b"a", b"COUNT", b"2"]),
4357 "*2\r\n:0\r\n:3\r\n"
4358 );
4359 assert_eq!(
4360 f.run(&[b"LPOS", b"p", b"a", b"RANK", b"-1", b"COUNT", b"0"]),
4361 "*3\r\n:6\r\n:3\r\n:0\r\n"
4362 );
4363 assert_eq!(
4366 f.run(&[b"LPOS", b"p", b"a", b"COUNT", b"0", b"MAXLEN", b"3"]),
4367 "*1\r\n:0\r\n"
4368 );
4369 assert_eq!(f.run(&[b"LPOS", b"p", b"zz"]), "$-1\r\n");
4372 assert_eq!(f.run(&[b"LPOS", b"p", b"zz", b"COUNT", b"0"]), "*0\r\n");
4373 assert_eq!(f.run(&[b"LPOS", b"nope", b"a"]), "$-1\r\n");
4374 assert_eq!(f.run(&[b"LPOS", b"nope", b"a", b"COUNT", b"2"]), "*0\r\n");
4375 }
4376
4377 #[test]
4378 fn lpos_words_its_three_mistakes_the_way_redis_does() {
4379 let mut f = Fixture::new();
4380 f.run(&[b"RPUSH", b"p", b"a"]);
4381 assert_eq!(
4384 f.run(&[b"LPOS", b"p", b"a", b"RANK", b"0"]),
4385 "-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"
4386 );
4387 assert_eq!(
4388 f.run(&[b"LPOS", b"p", b"a", b"COUNT", b"-1"]),
4389 "-ERR COUNT can't be negative\r\n"
4390 );
4391 assert_eq!(
4392 f.run(&[b"LPOS", b"p", b"a", b"MAXLEN", b"-1"]),
4393 "-ERR MAXLEN can't be negative\r\n"
4394 );
4395 assert_eq!(
4396 f.run(&[b"LPOS", b"p", b"a", b"RANK"]),
4397 "-ERR syntax error\r\n"
4398 );
4399 assert_eq!(
4400 f.run(&[b"LPOS", b"p", b"a", b"FOO", b"1"]),
4401 "-ERR syntax error\r\n"
4402 );
4403 }
4404
4405 #[test]
4406 fn a_move_takes_from_one_end_and_gives_to_another_even_on_one_key() {
4407 let mut f = Fixture::new();
4408 f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
4409 assert_eq!(f.run(&[b"RPOPLPUSH", b"k", b"d"]), "$1\r\nc\r\n");
4410 assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["a", "b"]));
4411 assert_eq!(f.run(&[b"LRANGE", b"d", b"0", b"-1"]), bulks(&["c"]));
4412 assert_eq!(
4413 f.run(&[b"LMOVE", b"k", b"d", b"LEFT", b"RIGHT"]),
4414 "$1\r\na\r\n"
4415 );
4416 assert_eq!(f.run(&[b"LRANGE", b"d", b"0", b"-1"]), bulks(&["c", "a"]));
4417 f.run(&[b"DEL", b"r"]);
4420 f.run(&[b"RPUSH", b"r", b"1", b"2", b"3"]);
4421 assert_eq!(f.run(&[b"RPOPLPUSH", b"r", b"r"]), "$1\r\n3\r\n");
4422 assert_eq!(
4423 f.run(&[b"LRANGE", b"r", b"0", b"-1"]),
4424 bulks(&["3", "1", "2"])
4425 );
4426 assert_eq!(
4427 f.run(&[b"LMOVE", b"nope", b"d", b"LEFT", b"LEFT"]),
4428 "$-1\r\n"
4429 );
4430 assert_eq!(
4431 f.run(&[b"LMOVE", b"r", b"d", b"LEFT", b"SIDEWAYS"]),
4432 "-ERR syntax error\r\n"
4433 );
4434 }
4435
4436 #[test]
4437 fn a_move_checks_the_destination_before_it_takes_anything() {
4438 let mut f = Fixture::new();
4439 f.run(&[b"RPUSH", b"k", b"a", b"b"]);
4440 f.run(&[b"SET", b"str", b"v"]);
4441 assert_eq!(
4442 f.run(&[b"LMOVE", b"k", b"str", b"LEFT", b"LEFT"]),
4443 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
4444 );
4445 assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["a", "b"]));
4447 }
4448
4449 #[test]
4450 fn lmpop_answers_from_the_first_key_that_has_anything() {
4451 let mut f = Fixture::new();
4452 f.run(&[b"RPUSH", b"b", b"1", b"2", b"3"]);
4453 assert_eq!(
4456 f.run(&[b"LMPOP", b"2", b"a", b"b", b"LEFT", b"COUNT", b"2"]),
4457 "*2\r\n$1\r\nb\r\n*2\r\n$1\r\n1\r\n$1\r\n2\r\n"
4458 );
4459 assert_eq!(
4460 f.run(&[b"LMPOP", b"2", b"a", b"b", b"RIGHT"]),
4461 "*2\r\n$1\r\nb\r\n*1\r\n$1\r\n3\r\n"
4462 );
4463 assert_eq!(f.run(&[b"EXISTS", b"b"]), ":0\r\n");
4464 assert_eq!(f.run(&[b"LMPOP", b"2", b"a", b"b", b"LEFT"]), "*-1\r\n");
4467 }
4468
4469 #[test]
4470 fn lmpop_has_its_own_words_for_a_count_and_for_a_key_count() {
4471 let mut f = Fixture::new();
4472 f.run(&[b"RPUSH", b"k", b"a"]);
4473 assert_eq!(
4474 f.run(&[b"LMPOP", b"0", b"k", b"LEFT"]),
4475 "-ERR numkeys should be greater than 0\r\n"
4476 );
4477 assert_eq!(
4478 f.run(&[b"LMPOP", b"-1", b"k", b"LEFT"]),
4479 "-ERR numkeys should be greater than 0\r\n"
4480 );
4481 assert_eq!(
4482 f.run(&[b"LMPOP", b"1", b"k", b"LEFT", b"COUNT", b"0"]),
4483 "-ERR count should be greater than 0\r\n"
4484 );
4485 assert_eq!(
4488 f.run(&[b"LMPOP", b"3", b"k", b"LEFT"]),
4489 "-ERR syntax error\r\n"
4490 );
4491 assert_eq!(
4492 f.run(&[b"LMPOP", b"1", b"k", b"LEFT", b"COUNT", b"1", b"x"]),
4493 "-ERR syntax error\r\n"
4494 );
4495 assert_eq!(
4496 f.run(&[b"LMPOP", b"1", b"k", b"LEFT", b"FOO", b"1"]),
4497 "-ERR syntax error\r\n"
4498 );
4499 assert_eq!(
4500 f.run(&[b"LMPOP", b"1", b"k", b"SIDEWAYS"]),
4501 "-ERR syntax error\r\n"
4502 );
4503 assert_eq!(f.run(&[b"LLEN", b"k"]), ":1\r\n");
4504 }
4505
4506 #[test]
4507 fn every_list_command_says_wrongtype_and_writes_nothing() {
4508 let mut f = Fixture::new();
4509 f.run(&[b"SET", b"str", b"v"]);
4510 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
4511 for cmd in [
4512 &[b"LPUSH".as_slice(), b"str", b"a"][..],
4513 &[b"RPUSH", b"str", b"a"],
4514 &[b"LPUSHX", b"str", b"a"],
4515 &[b"RPUSHX", b"str", b"a"],
4516 &[b"LPOP", b"str"],
4517 &[b"LPOP", b"str", b"2"],
4518 &[b"RPOP", b"str"],
4519 &[b"LLEN", b"str"],
4520 &[b"LRANGE", b"str", b"0", b"-1"],
4521 &[b"LINDEX", b"str", b"0"],
4522 &[b"LSET", b"str", b"0", b"a"],
4523 &[b"LINSERT", b"str", b"BEFORE", b"a", b"b"],
4524 &[b"LREM", b"str", b"0", b"a"],
4525 &[b"LTRIM", b"str", b"0", b"-1"],
4526 &[b"LPOS", b"str", b"a"],
4527 &[b"LPOS", b"str", b"a", b"COUNT", b"0"],
4528 &[b"RPOPLPUSH", b"str", b"d"],
4529 &[b"LMOVE", b"str", b"d", b"LEFT", b"LEFT"],
4530 &[b"LMPOP", b"1", b"str", b"LEFT"],
4531 ] {
4532 assert_eq!(f.run(cmd), wrong, "{:?}", String::from_utf8_lossy(cmd[0]));
4533 }
4534 assert_eq!(f.run(&[b"GET", b"str"]), "$1\r\nv\r\n");
4535 assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
4536 }
4537
4538 #[test]
4542 fn a_timeout_has_three_ways_of_being_wrong() {
4543 let mut f = Fixture::new();
4544 let not_float = "-ERR timeout is not a float or out of range\r\n";
4545 let range = "-ERR timeout is out of range\r\n";
4546 for (bad, want) in [
4547 (&[b"BLPOP".as_slice(), b"k", b"abc"][..], not_float),
4548 (&[b"BLPOP", b"k", b"nan"], not_float),
4549 (&[b"BLPOP", b"k", b""], not_float),
4550 (&[b"BLPOP", b"k", b" 1"], not_float),
4553 (&[b"BLPOP", b"k", b"1 "], not_float),
4554 (&[b"BLPOP", b"k", b"-1"], "-ERR timeout is negative\r\n"),
4555 (&[b"BLPOP", b"k", b"-0.1"], "-ERR timeout is negative\r\n"),
4556 (&[b"BLPOP", b"k", b"1e400"], range),
4559 (&[b"BLPOP", b"k", b"inf"], range),
4560 (&[b"BLPOP", b"k", b"9999999999999999"], range),
4561 (&[b"BRPOP", b"k", b"abc"], not_float),
4562 (
4563 &[b"BLMOVE", b"a", b"b", b"LEFT", b"RIGHT", b"abc"],
4564 not_float,
4565 ),
4566 (
4567 &[b"BRPOPLPUSH", b"a", b"b", b"-1"],
4568 "-ERR timeout is negative\r\n",
4569 ),
4570 (&[b"BLMPOP", b"abc", b"1", b"k", b"LEFT"], not_float),
4571 ] {
4572 assert_eq!(f.run(bad), want, "for {bad:?}");
4573 }
4574 }
4575
4576 #[test]
4579 fn a_zero_timeout_waits_and_the_smallest_positive_one_does_not() {
4580 let mut f = Fixture::new();
4581 for timeout in [b"0".as_slice(), b"0.0", b"-0.0"] {
4582 let (flow, out) = f.flow(&[b"BLPOP", b"k", timeout]);
4583 assert_eq!(flow, Flow::Block, "for {timeout:?}");
4584 assert!(out.is_empty(), "for {timeout:?}");
4585 }
4586 let (flow, out) = f.flow(&[b"BLPOP", b"k", b"0.0000001"]);
4590 assert_eq!(flow, Flow::Block);
4591 assert!(out.is_empty());
4592 }
4593
4594 #[test]
4595 fn a_blocking_command_that_can_be_answered_answers_like_the_one_it_wraps() {
4596 let mut f = Fixture::new();
4597 f.run(&[b"RPUSH", b"L", b"a", b"b", b"c", b"d", b"e"]);
4598
4599 assert_eq!(
4602 f.flow(&[b"BLPOP", b"nope", b"L", b"0"]),
4603 (Flow::Continue, "*2\r\n$1\r\nL\r\n$1\r\na\r\n".to_owned())
4604 );
4605 assert_eq!(
4606 f.run(&[b"BRPOP", b"L", b"0"]),
4607 "*2\r\n$1\r\nL\r\n$1\r\ne\r\n"
4608 );
4609 assert_eq!(
4610 f.run(&[
4611 b"BLMPOP", b"0", b"2", b"nope", b"L", b"LEFT", b"COUNT", b"2"
4612 ]),
4613 "*2\r\n$1\r\nL\r\n*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
4614 );
4615 assert_eq!(
4616 f.run(&[b"BLMOVE", b"L", b"D", b"LEFT", b"RIGHT", b"0"]),
4617 "$1\r\nd\r\n"
4618 );
4619 assert_eq!(
4620 f.run(&[b"EXISTS", b"L"]),
4621 ":0\r\n",
4622 "and the key went with it"
4623 );
4624 assert_eq!(f.run(&[b"LRANGE", b"D", b"0", b"-1"]), "*1\r\n$1\r\nd\r\n");
4625 f.run(&[b"RPUSH", b"D", b"x"]);
4628 assert_eq!(f.run(&[b"BRPOPLPUSH", b"D", b"D", b"0"]), "$1\r\nx\r\n");
4629 assert_eq!(
4630 f.run(&[b"LRANGE", b"D", b"0", b"-1"]),
4631 "*2\r\n$1\r\nx\r\n$1\r\nd\r\n"
4632 );
4633 }
4634
4635 #[test]
4636 fn blmpop_reads_its_count_and_its_key_count_the_way_lmpop_does() {
4637 let mut f = Fixture::new();
4638 f.run(&[b"RPUSH", b"k", b"a"]);
4639 for (bad, want) in [
4640 (
4641 &[b"BLMPOP".as_slice(), b"0", b"0", b"k", b"LEFT"][..],
4642 "-ERR numkeys should be greater than 0\r\n",
4643 ),
4644 (
4645 &[b"BLMPOP", b"0", b"-1", b"k", b"LEFT"],
4646 "-ERR numkeys should be greater than 0\r\n",
4647 ),
4648 (
4651 &[b"BLMPOP", b"0", b"2", b"k", b"LEFT"],
4652 "-ERR syntax error\r\n",
4653 ),
4654 (
4655 &[b"BLMPOP", b"0", b"1", b"k", b"SIDEWAYS"],
4656 "-ERR syntax error\r\n",
4657 ),
4658 (
4659 &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT"],
4660 "-ERR syntax error\r\n",
4661 ),
4662 (
4663 &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT", b"2", b"x"],
4664 "-ERR syntax error\r\n",
4665 ),
4666 (
4669 &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT", b"0"],
4670 "-ERR count should be greater than 0\r\n",
4671 ),
4672 (
4673 &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT", b"abc"],
4674 "-ERR count should be greater than 0\r\n",
4675 ),
4676 ] {
4677 assert_eq!(f.run(bad), want, "for {bad:?}");
4678 }
4679 assert_eq!(f.run(&[b"LLEN", b"k"]), ":1\r\n", "and none of them popped");
4680 }
4681
4682 #[test]
4683 fn a_blocking_move_reads_its_directions_before_its_timeout() {
4684 let mut f = Fixture::new();
4685 assert_eq!(
4688 f.run(&[b"BLMOVE", b"a", b"b", b"UP", b"DOWN", b"abc"]),
4689 "-ERR syntax error\r\n"
4690 );
4691 assert_eq!(
4692 f.run(&[b"BLMOVE", b"a", b"b", b"LEFT", b"DOWN", b"0.05"]),
4693 "-ERR syntax error\r\n"
4694 );
4695 }
4696
4697 #[test]
4700 fn a_blocking_command_errors_on_a_wrong_type_rather_than_waiting_on_it() {
4701 let mut f = Fixture::new();
4702 f.run(&[b"SET", b"S", b"v"]);
4703 f.run(&[b"RPUSH", b"D", b"x"]);
4704 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
4705
4706 assert_eq!(f.run(&[b"BLPOP", b"S", b"0"]), wrong);
4707 assert_eq!(f.run(&[b"BLPOP", b"E", b"S", b"0"]), wrong);
4710 assert_eq!(f.run(&[b"BRPOP", b"S", b"0"]), wrong);
4711 assert_eq!(f.run(&[b"BLMPOP", b"0", b"1", b"S", b"LEFT"]), wrong);
4712 assert_eq!(f.run(&[b"BRPOPLPUSH", b"S", b"D", b"0"]), wrong);
4713 assert_eq!(f.run(&[b"BRPOPLPUSH", b"D", b"S", b"0"]), wrong);
4716 assert_eq!(f.run(&[b"LRANGE", b"D", b"0", b"-1"]), "*1\r\n$1\r\nx\r\n");
4717
4718 assert_eq!(
4722 f.flow(&[b"BLMOVE", b"E", b"S", b"LEFT", b"RIGHT", b"0.1"])
4723 .0,
4724 Flow::Block
4725 );
4726 }
4727
4728 #[test]
4732 fn churning_lists_does_not_grow_the_server() {
4733 let mut f = Fixture::new();
4734 let vals: Vec<Vec<u8>> = (0..200).map(|i| format!("v{i}").into_bytes()).collect();
4735 let args: Vec<&[u8]> = [&b"RPUSH"[..], &b"k"[..]]
4736 .into_iter()
4737 .chain(vals.iter().map(Vec::as_slice))
4738 .collect();
4739
4740 f.run(&args);
4741 f.run(&[b"DEL", b"k"]);
4742 f.server.compact_step();
4743 let after_first = f.server.memory_bytes();
4744
4745 for _ in 0..200 {
4746 f.run(&args);
4747 f.run(&[b"LTRIM", b"k", b"1", b"0"]);
4748 f.server.compact_step();
4749 }
4750 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
4751 assert!(
4752 f.server.memory_bytes() <= after_first * 2,
4753 "held {} after two hundred passes against {after_first} after one",
4754 f.server.memory_bytes()
4755 );
4756 }
4757
4758 #[test]
4761 fn a_sorted_set_takes_scores_and_gives_them_back() {
4762 let mut f = Fixture::new();
4763 assert_eq!(f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b"]), ":2\r\n");
4764 assert_eq!(f.run(&[b"ZADD", b"z", b"1", b"a", b"3", b"c"]), ":1\r\n");
4765 assert_eq!(f.run(&[b"ZCARD", b"z"]), ":3\r\n");
4766 assert_eq!(f.run(&[b"ZSCORE", b"z", b"b"]), "$1\r\n2\r\n");
4767 assert_eq!(f.run(&[b"ZSCORE", b"z", b"nope"]), "$-1\r\n");
4768 assert_eq!(f.run(&[b"ZSCORE", b"nokey", b"b"]), "$-1\r\n");
4769 assert_eq!(
4770 f.run(&[b"ZMSCORE", b"z", b"a", b"nope", b"c"]),
4771 "*3\r\n$1\r\n1\r\n$-1\r\n$1\r\n3\r\n"
4772 );
4773 assert_eq!(f.run(&[b"ZREM", b"z", b"a", b"nope"]), ":1\r\n");
4774 assert_eq!(f.run(&[b"ZCARD", b"z"]), ":2\r\n");
4775 assert_eq!(f.run(&[b"ZREM", b"z", b"b", b"c"]), ":2\r\n");
4777 assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
4778 }
4779
4780 #[test]
4781 fn a_score_is_a_double_on_resp3_and_digits_on_resp2() {
4782 let mut f = Fixture::new();
4783 f.run(&[b"ZADD", b"z", b"1.5", b"a", b"inf", b"b", b"-inf", b"c"]);
4784 assert_eq!(f.run(&[b"ZSCORE", b"z", b"a"]), "$3\r\n1.5\r\n");
4785 assert_eq!(f.run(&[b"ZSCORE", b"z", b"b"]), "$3\r\ninf\r\n");
4786 assert_eq!(f.run(&[b"ZSCORE", b"z", b"c"]), "$4\r\n-inf\r\n");
4787
4788 f.out = Out::new(Proto::Resp3);
4789 assert_eq!(f.run(&[b"ZSCORE", b"z", b"a"]), ",1.5\r\n");
4790 assert_eq!(f.run(&[b"ZSCORE", b"z", b"b"]), ",inf\r\n");
4791 assert_eq!(f.run(&[b"ZSCORE", b"z", b"c"]), ",-inf\r\n");
4792 assert_eq!(f.run(&[b"ZSCORE", b"z", b"nope"]), "_\r\n");
4793 }
4794
4795 #[test]
4796 fn the_zadd_options_gate_what_gets_written() {
4797 let mut f = Fixture::new();
4798 f.run(&[b"ZADD", b"z", b"5", b"a"]);
4799 assert_eq!(f.run(&[b"ZADD", b"z", b"NX", b"9", b"a"]), ":0\r\n");
4801 assert_eq!(f.run(&[b"ZSCORE", b"z", b"a"]), "$1\r\n5\r\n");
4802 assert_eq!(f.run(&[b"ZADD", b"z", b"XX", b"9", b"new"]), ":0\r\n");
4803 assert_eq!(f.run(&[b"EXISTS", b"z"]), ":1\r\n");
4804 assert_eq!(f.run(&[b"ZADD", b"z", b"GT", b"CH", b"3", b"a"]), ":0\r\n");
4806 assert_eq!(f.run(&[b"ZADD", b"z", b"GT", b"CH", b"7", b"a"]), ":1\r\n");
4807 assert_eq!(f.run(&[b"ZADD", b"z", b"LT", b"CH", b"9", b"a"]), ":0\r\n");
4808 assert_eq!(f.run(&[b"ZADD", b"z", b"1", b"a", b"1", b"b"]), ":1\r\n");
4810 assert_eq!(
4811 f.run(&[b"ZADD", b"z", b"CH", b"2", b"a", b"2", b"c"]),
4812 ":2\r\n"
4813 );
4814 }
4815
4816 #[test]
4817 fn zadd_incr_answers_a_score_or_nothing_at_all() {
4818 let mut f = Fixture::new();
4819 assert_eq!(f.run(&[b"ZADD", b"z", b"INCR", b"5", b"m"]), "$1\r\n5\r\n");
4820 assert_eq!(f.run(&[b"ZADD", b"z", b"INCR", b"2", b"m"]), "$1\r\n7\r\n");
4821 assert_eq!(
4824 f.run(&[b"ZADD", b"z", b"NX", b"INCR", b"2", b"m"]),
4825 "$-1\r\n"
4826 );
4827 assert_eq!(
4828 f.run(&[b"ZADD", b"z", b"XX", b"INCR", b"2", b"gone"]),
4829 "$-1\r\n"
4830 );
4831 assert_eq!(
4832 f.run(&[b"ZADD", b"z", b"GT", b"INCR", b"-1", b"m"]),
4833 "$-1\r\n"
4834 );
4835 assert_eq!(
4836 f.run(&[b"ZADD", b"z", b"GT", b"INCR", b"1", b"m"]),
4837 "$1\r\n8\r\n"
4838 );
4839 assert_eq!(f.run(&[b"ZINCRBY", b"z", b"2", b"m"]), "$2\r\n10\r\n");
4840 assert_eq!(f.run(&[b"ZINCRBY", b"z", b"1", b"fresh"]), "$1\r\n1\r\n");
4841 }
4842
4843 #[test]
4844 fn the_two_infinities_will_not_be_added_together() {
4845 let mut f = Fixture::new();
4846 f.run(&[b"ZADD", b"z", b"inf", b"m"]);
4847 let nan = "-ERR resulting score is not a number (NaN)\r\n";
4848 assert_eq!(f.run(&[b"ZINCRBY", b"z", b"-inf", b"m"]), nan);
4849 assert_eq!(f.run(&[b"ZADD", b"z", b"INCR", b"-inf", b"m"]), nan);
4850 assert_eq!(f.run(&[b"ZSCORE", b"z", b"m"]), "$3\r\ninf\r\n");
4851 assert_eq!(f.run(&[b"ZINCRBY", b"gone", b"1", b"m"]), "$1\r\n1\r\n");
4853 }
4854
4855 #[test]
4856 fn zadd_says_its_mistakes_the_way_redis_says_them() {
4857 let mut f = Fixture::new();
4858 assert_eq!(
4861 f.run(&[b"ZADD", b"z", b"NX", b"XX"]),
4862 "-ERR syntax error\r\n"
4863 );
4864 assert_eq!(
4865 f.run(&[b"ZADD", b"z", b"NX", b"XX", b"1", b"a"]),
4866 "-ERR XX and NX options at the same time are not compatible\r\n"
4867 );
4868 let gtlt = "-ERR GT, LT, and/or NX options at the same time are not compatible\r\n";
4869 assert_eq!(f.run(&[b"ZADD", b"z", b"NX", b"GT", b"1", b"a"]), gtlt);
4870 assert_eq!(f.run(&[b"ZADD", b"z", b"GT", b"LT", b"1", b"a"]), gtlt);
4871 assert_eq!(
4872 f.run(&[b"ZADD", b"z", b"INCR", b"1", b"a", b"2", b"b"]),
4873 "-ERR INCR option supports a single increment-element pair\r\n"
4874 );
4875 assert_eq!(
4877 f.run(&[b"ZADD", b"z", b"1", b"a", b"2"]),
4878 "-ERR syntax error\r\n"
4879 );
4880 assert_eq!(
4882 f.run(&[b"ZADD", b"z", b"1", b"a", b"nonsense", b"b"]),
4883 "-ERR value is not a valid float\r\n"
4884 );
4885 assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
4886 }
4887
4888 #[test]
4889 fn a_rank_says_where_a_member_sits_from_either_end() {
4890 let mut f = Fixture::new();
4891 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
4892 assert_eq!(f.run(&[b"ZRANK", b"z", b"a"]), ":0\r\n");
4893 assert_eq!(f.run(&[b"ZRANK", b"z", b"c"]), ":2\r\n");
4894 assert_eq!(f.run(&[b"ZREVRANK", b"z", b"c"]), ":0\r\n");
4895 assert_eq!(f.run(&[b"ZREVRANK", b"z", b"a"]), ":2\r\n");
4896 assert_eq!(
4898 f.run(&[b"ZRANK", b"z", b"b", b"WITHSCORE"]),
4899 "*2\r\n:1\r\n$1\r\n2\r\n"
4900 );
4901 assert_eq!(f.run(&[b"ZRANK", b"z", b"nope"]), "$-1\r\n");
4902 assert_eq!(f.run(&[b"ZRANK", b"z", b"nope", b"WITHSCORE"]), "*-1\r\n");
4903 assert_eq!(f.run(&[b"ZRANK", b"nokey", b"a", b"WITHSCORE"]), "*-1\r\n");
4904 assert_eq!(
4907 f.run(&[b"ZRANK", b"z", b"b", b"bogus"]),
4908 "-ERR syntax error\r\n"
4909 );
4910 assert_eq!(
4911 f.run(&[b"ZREVRANK", b"z", b"b", b"WITHSCORE", b"more"]),
4912 "-ERR wrong number of arguments for 'zrevrank' command\r\n"
4913 );
4914 }
4915
4916 #[test]
4917 fn the_two_counts_read_their_two_kinds_of_bound() {
4918 let mut f = Fixture::new();
4919 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
4920 assert_eq!(f.run(&[b"ZCOUNT", b"z", b"-inf", b"+inf"]), ":3\r\n");
4921 assert_eq!(f.run(&[b"ZCOUNT", b"z", b"2", b"3"]), ":2\r\n");
4922 assert_eq!(f.run(&[b"ZCOUNT", b"z", b"(1", b"3"]), ":2\r\n");
4923 assert_eq!(f.run(&[b"ZCOUNT", b"z", b"(1", b"(3"]), ":1\r\n");
4924 assert_eq!(f.run(&[b"ZCOUNT", b"nokey", b"-inf", b"+inf"]), ":0\r\n");
4925 assert_eq!(
4926 f.run(&[b"ZCOUNT", b"z", b"bogus", b"3"]),
4927 "-ERR min or max is not a float\r\n"
4928 );
4929
4930 f.run(&[b"ZADD", b"l", b"0", b"a", b"0", b"b", b"0", b"c"]);
4931 assert_eq!(f.run(&[b"ZLEXCOUNT", b"l", b"-", b"+"]), ":3\r\n");
4932 assert_eq!(f.run(&[b"ZLEXCOUNT", b"l", b"[a", b"(c"]), ":2\r\n");
4933 assert_eq!(f.run(&[b"ZLEXCOUNT", b"l", b"(a", b"+"]), ":2\r\n");
4934 assert_eq!(
4937 f.run(&[b"ZLEXCOUNT", b"l", b"a", b"c"]),
4938 "-ERR min or max not valid string range item\r\n"
4939 );
4940 }
4941
4942 #[test]
4948 fn one_range_command_selects_by_rank_or_score_or_name() {
4949 let mut f = Fixture::new();
4950 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
4951 assert_eq!(
4952 f.run(&[b"ZRANGE", b"z", b"0", b"-1"]),
4953 "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
4954 );
4955 assert_eq!(
4956 f.run(&[b"ZRANGE", b"z", b"-2", b"-1"]),
4957 "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
4958 );
4959 assert_eq!(f.run(&[b"ZRANGE", b"z", b"5", b"9"]), "*0\r\n");
4960 assert_eq!(f.run(&[b"ZRANGE", b"nokey", b"0", b"-1"]), "*0\r\n");
4961 assert_eq!(
4964 f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"REV"]),
4965 "*3\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\na\r\n"
4966 );
4967 assert_eq!(
4968 f.run(&[b"ZRANGE", b"z", b"(1", b"+inf", b"BYSCORE"]),
4969 "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
4970 );
4971 assert_eq!(
4974 f.run(&[b"ZRANGE", b"z", b"+inf", b"(1", b"BYSCORE", b"REV"]),
4975 "*2\r\n$1\r\nc\r\n$1\r\nb\r\n"
4976 );
4977 assert_eq!(
4978 f.run(&[b"ZRANGE", b"z", b"-", b"+", b"BYLEX"]),
4979 "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
4980 );
4981 assert_eq!(
4982 f.run(&[b"ZRANGE", b"z", b"+", b"-", b"BYLEX", b"REV"]),
4983 "*3\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\na\r\n"
4984 );
4985 }
4986
4987 #[test]
4990 fn the_older_range_spellings_name_their_high_end_first() {
4991 let mut f = Fixture::new();
4992 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
4993 assert_eq!(
4994 f.run(&[b"ZREVRANGE", b"z", b"0", b"-1"]),
4995 "*3\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\na\r\n"
4996 );
4997 assert_eq!(
4998 f.run(&[b"ZREVRANGE", b"z", b"0", b"0", b"WITHSCORES"]),
4999 "*2\r\n$1\r\nc\r\n$1\r\n3\r\n"
5000 );
5001 assert_eq!(
5002 f.run(&[b"ZRANGEBYSCORE", b"z", b"(1", b"3"]),
5003 "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
5004 );
5005 assert_eq!(
5006 f.run(&[b"ZREVRANGEBYSCORE", b"z", b"3", b"(1"]),
5007 "*2\r\n$1\r\nc\r\n$1\r\nb\r\n"
5008 );
5009 assert_eq!(f.run(&[b"ZREVRANGEBYSCORE", b"z", b"(1", b"3"]), "*0\r\n");
5013 assert_eq!(
5014 f.run(&[b"ZRANGEBYLEX", b"z", b"[a", b"(c"]),
5015 "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
5016 );
5017 assert_eq!(
5018 f.run(&[b"ZREVRANGEBYLEX", b"z", b"(c", b"[a"]),
5019 "*2\r\n$1\r\nb\r\n$1\r\na\r\n"
5020 );
5021 for cmd in [
5024 &[b"ZREVRANGE".as_slice(), b"z", b"0", b"-1", b"BYSCORE"][..],
5025 &[b"ZRANGEBYSCORE", b"z", b"1", b"3", b"REV"],
5026 &[b"ZRANGEBYLEX", b"z", b"[a", b"[c", b"BYLEX"],
5027 ] {
5028 assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{:?}", cmd[0]);
5029 }
5030 }
5031
5032 #[test]
5035 fn limit_and_withscores_are_read_by_all_of_them_and_refused_afterwards() {
5036 let mut f = Fixture::new();
5037 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
5038 assert_eq!(
5039 f.run(&[
5040 b"ZRANGE", b"z", b"-inf", b"+inf", b"BYSCORE", b"LIMIT", b"1", b"1"
5041 ]),
5042 "*1\r\n$1\r\nb\r\n"
5043 );
5044 assert_eq!(
5046 f.run(&[
5047 b"ZRANGE", b"z", b"-inf", b"+inf", b"BYSCORE", b"LIMIT", b"-1", b"2"
5048 ]),
5049 "*0\r\n"
5050 );
5051 assert_eq!(
5052 f.run(&[
5053 b"ZRANGE", b"z", b"-inf", b"+inf", b"BYSCORE", b"LIMIT", b"0", b"-1"
5054 ]),
5055 "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
5056 );
5057 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";
5059 assert_eq!(
5060 f.run(&[
5061 b"ZRANGEBYSCORE",
5062 b"z",
5063 b"1",
5064 b"3",
5065 b"WITHSCORES",
5066 b"LIMIT",
5067 b"0",
5068 b"2"
5069 ]),
5070 both
5071 );
5072 assert_eq!(
5073 f.run(&[
5074 b"ZRANGEBYSCORE",
5075 b"z",
5076 b"1",
5077 b"3",
5078 b"LIMIT",
5079 b"0",
5080 b"2",
5081 b"WITHSCORES"
5082 ]),
5083 both
5084 );
5085 let needs_by = "-ERR syntax error, LIMIT is only supported in combination with either BYSCORE or BYLEX\r\n";
5088 assert_eq!(
5089 f.run(&[
5090 b"ZREVRANGE",
5091 b"z",
5092 b"0",
5093 b"-1",
5094 b"WITHSCORES",
5095 b"LIMIT",
5096 b"0",
5097 b"1"
5098 ]),
5099 needs_by
5100 );
5101 assert_eq!(
5102 f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"LIMIT", b"0", b"1"]),
5103 needs_by
5104 );
5105 let not_bylex = "-ERR syntax error, WITHSCORES not supported in combination with BYLEX\r\n";
5106 assert_eq!(
5107 f.run(&[b"ZRANGE", b"z", b"-", b"+", b"BYLEX", b"WITHSCORES"]),
5108 not_bylex
5109 );
5110 assert_eq!(
5111 f.run(&[b"ZRANGEBYLEX", b"z", b"[a", b"[c", b"WITHSCORES"]),
5112 not_bylex
5113 );
5114 for cmd in [
5117 &[
5118 b"ZRANGE".as_slice(),
5119 b"z",
5120 b"0",
5121 b"-1",
5122 b"BYSCORE",
5123 b"BYLEX",
5124 ][..],
5125 &[b"ZRANGE", b"z", b"0", b"-1", b"junk"],
5126 &[b"ZRANGEBYSCORE", b"z", b"1", b"3", b"LIMIT", b"0"],
5127 ] {
5128 assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
5129 }
5130 assert_eq!(
5131 f.run(&[b"ZRANGEBYSCORE", b"z", b"bad", b"3"]),
5132 "-ERR min or max is not a float\r\n"
5133 );
5134 assert_eq!(
5135 f.run(&[b"ZRANGEBYLEX", b"z", b"a", b"[c"]),
5136 "-ERR min or max not valid string range item\r\n"
5137 );
5138 assert_eq!(
5139 f.run(&[b"ZRANGEBYSCORE", b"z", b"1", b"3", b"LIMIT", b"a", b"2"]),
5140 "-ERR value is not an integer or out of range\r\n"
5141 );
5142 }
5143
5144 #[test]
5147 fn withscores_nests_on_resp3_and_flattens_on_resp2() {
5148 let mut f = Fixture::new();
5149 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
5150 assert_eq!(
5151 f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"WITHSCORES"]),
5152 "*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"
5153 );
5154 f.out = Out::new(Proto::Resp3);
5155 assert_eq!(
5156 f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"WITHSCORES"]),
5157 "*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"
5158 );
5159 assert_eq!(
5160 f.run(&[b"ZRANGE", b"z", b"0", b"-1"]),
5161 "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
5162 );
5163 }
5164
5165 #[test]
5167 fn a_range_store_writes_the_window_into_another_key() {
5168 let mut f = Fixture::new();
5169 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
5170 assert_eq!(f.run(&[b"ZRANGESTORE", b"d", b"z", b"0", b"-1"]), ":3\r\n");
5171 assert_eq!(f.run(&[b"ZRANGESTORE", b"d", b"z", b"5", b"9"]), ":0\r\n");
5174 assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
5175 assert_eq!(
5176 f.run(&[b"ZRANGESTORE", b"d", b"z", b"(1", b"+inf", b"BYSCORE"]),
5177 ":2\r\n"
5178 );
5179 assert_eq!(
5180 f.run(&[b"ZRANGE", b"d", b"0", b"-1", b"WITHSCORES"]),
5181 "*4\r\n$1\r\nb\r\n$1\r\n2\r\n$1\r\nc\r\n$1\r\n3\r\n"
5182 );
5183 assert_eq!(f.run(&[b"ZRANGESTORE", b"z", b"z", b"1", b"2"]), ":2\r\n");
5186 assert_eq!(
5187 f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"WITHSCORES"]),
5188 "*4\r\n$1\r\nb\r\n$1\r\n2\r\n$1\r\nc\r\n$1\r\n3\r\n"
5189 );
5190 assert_eq!(
5193 f.run(&[b"ZRANGESTORE", b"d", b"z", b"0", b"-1", b"WITHSCORES"]),
5194 "-ERR syntax error\r\n"
5195 );
5196 }
5197
5198 #[test]
5201 fn the_three_removals_share_their_window_with_the_reads() {
5202 let mut f = Fixture::new();
5203 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
5204 assert_eq!(f.run(&[b"ZREMRANGEBYRANK", b"z", b"0", b"0"]), ":1\r\n");
5205 assert_eq!(
5206 f.run(&[b"ZRANGE", b"z", b"0", b"-1"]),
5207 "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
5208 );
5209 assert_eq!(
5210 f.run(&[b"ZREMRANGEBYSCORE", b"z", b"(2", b"+inf"]),
5211 ":1\r\n"
5212 );
5213 assert_eq!(f.run(&[b"ZRANGE", b"z", b"0", b"-1"]), "*1\r\n$1\r\nb\r\n");
5214 assert_eq!(f.run(&[b"ZREMRANGEBYLEX", b"z", b"-", b"+"]), ":1\r\n");
5216 assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
5217 assert_eq!(
5218 f.run(&[b"ZREMRANGEBYRANK", b"nokey", b"0", b"-1"]),
5219 ":0\r\n"
5220 );
5221 assert_eq!(
5222 f.run(&[b"ZREMRANGEBYRANK", b"z", b"0", b"x"]),
5223 "-ERR value is not an integer or out of range\r\n"
5224 );
5225 }
5226
5227 #[test]
5229 fn the_three_algebra_commands_combine_scores_and_order_the_answer_once() {
5230 let mut f = Fixture::new();
5231 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
5232 f.run(&[b"ZADD", b"y", b"10", b"b", b"20", b"d"]);
5233 assert_eq!(
5234 f.run(&[b"ZUNION", b"2", b"z", b"y"]),
5235 "*4\r\n$1\r\na\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\nd\r\n"
5236 );
5237 assert_eq!(
5240 f.run(&[b"ZUNION", b"2", b"z", b"y", b"WITHSCORES"]),
5241 "*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"
5242 );
5243 assert_eq!(
5244 f.run(&[
5245 b"ZUNION",
5246 b"2",
5247 b"z",
5248 b"y",
5249 b"WEIGHTS",
5250 b"2",
5251 b"3",
5252 b"WITHSCORES"
5253 ]),
5254 "*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"
5255 );
5256 assert_eq!(
5257 f.run(&[
5258 b"ZUNION",
5259 b"2",
5260 b"z",
5261 b"y",
5262 b"AGGREGATE",
5263 b"MIN",
5264 b"WITHSCORES"
5265 ]),
5266 "*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"
5267 );
5268 assert_eq!(
5269 f.run(&[
5270 b"ZUNION",
5271 b"2",
5272 b"z",
5273 b"y",
5274 b"AGGREGATE",
5275 b"MAX",
5276 b"WITHSCORES"
5277 ]),
5278 "*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"
5279 );
5280 assert_eq!(
5281 f.run(&[b"ZINTER", b"2", b"z", b"y", b"WITHSCORES"]),
5282 "*2\r\n$1\r\nb\r\n$2\r\n12\r\n"
5283 );
5284 assert_eq!(
5285 f.run(&[b"ZDIFF", b"2", b"z", b"y", b"WITHSCORES"]),
5286 "*4\r\n$1\r\na\r\n$1\r\n1\r\n$1\r\nc\r\n$1\r\n3\r\n"
5287 );
5288 assert_eq!(f.run(&[b"ZUNION", b"1", b"nokey"]), "*0\r\n");
5289 f.run(&[b"SADD", b"p", b"a", b"d"]);
5292 assert_eq!(
5293 f.run(&[b"ZUNION", b"2", b"z", b"p", b"WITHSCORES"]),
5294 "*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"
5295 );
5296 for cmd in [
5299 &[
5300 b"ZDIFF".as_slice(),
5301 b"2",
5302 b"z",
5303 b"y",
5304 b"WEIGHTS",
5305 b"1",
5306 b"1",
5307 ][..],
5308 &[b"ZDIFF", b"2", b"z", b"y", b"AGGREGATE", b"MIN"],
5309 ] {
5310 assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
5311 }
5312 }
5313
5314 #[test]
5316 fn the_algebra_counts_its_keys_and_says_so_when_the_count_is_wrong() {
5317 let mut f = Fixture::new();
5318 f.run(&[b"ZADD", b"z", b"1", b"a"]);
5319 f.run(&[b"ZADD", b"y", b"2", b"b"]);
5320 assert_eq!(
5322 f.run(&[b"ZUNION", b"0", b"z"]),
5323 "-ERR at least 1 input key is needed for 'zunion' command\r\n"
5324 );
5325 assert_eq!(
5326 f.run(&[b"ZUNION", b"-1", b"z"]),
5327 "-ERR at least 1 input key is needed for 'zunion' command\r\n"
5328 );
5329 assert_eq!(
5330 f.run(&[b"ZINTERCARD", b"0", b"z"]),
5331 "-ERR at least 1 input key is needed for 'zintercard' command\r\n"
5332 );
5333 assert_eq!(
5336 f.run(&[b"ZUNION", b"3", b"z", b"y"]),
5337 "-ERR syntax error\r\n"
5338 );
5339 assert_eq!(
5340 f.run(&[b"ZUNION", b"x", b"z"]),
5341 "-ERR value is not an integer or out of range\r\n"
5342 );
5343 assert_eq!(
5346 f.run(&[b"ZUNION", b"2", b"z", b"y", b"WEIGHTS", b"1"]),
5347 "-ERR syntax error\r\n"
5348 );
5349 assert_eq!(
5350 f.run(&[b"ZUNION", b"2", b"z", b"y", b"WEIGHTS", b"a", b"b"]),
5351 "-ERR weight value is not a float\r\n"
5352 );
5353 assert_eq!(
5354 f.run(&[b"ZUNION", b"2", b"z", b"y", b"AGGREGATE", b"NOPE"]),
5355 "-ERR syntax error\r\n"
5356 );
5357 }
5358
5359 #[test]
5361 fn the_algebra_stores_answer_a_count_and_delete_an_empty_destination() {
5362 let mut f = Fixture::new();
5363 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
5364 f.run(&[b"ZADD", b"y", b"10", b"b", b"20", b"d"]);
5365 assert_eq!(f.run(&[b"ZUNIONSTORE", b"d", b"2", b"z", b"y"]), ":4\r\n");
5366 assert_eq!(
5367 f.run(&[b"ZRANGE", b"d", b"0", b"-1", b"WITHSCORES"]),
5368 "*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"
5369 );
5370 assert_eq!(f.run(&[b"ZINTERSTORE", b"d", b"2", b"z", b"y"]), ":1\r\n");
5371 assert_eq!(f.run(&[b"ZDIFFSTORE", b"d", b"2", b"z", b"y"]), ":2\r\n");
5372 assert_eq!(
5375 f.run(&[b"ZINTERSTORE", b"d", b"2", b"z", b"nokey"]),
5376 ":0\r\n"
5377 );
5378 assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
5379 assert_eq!(f.run(&[b"ZUNIONSTORE", b"z", b"2", b"z", b"y"]), ":4\r\n");
5381 assert_eq!(f.run(&[b"ZCARD", b"z"]), ":4\r\n");
5382 for cmd in [
5383 &[
5384 b"ZUNIONSTORE".as_slice(),
5385 b"d",
5386 b"2",
5387 b"z",
5388 b"y",
5389 b"WITHSCORES",
5390 ][..],
5391 &[
5392 b"ZDIFFSTORE",
5393 b"d",
5394 b"2",
5395 b"z",
5396 b"y",
5397 b"WEIGHTS",
5398 b"1",
5399 b"1",
5400 ],
5401 ] {
5402 assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
5403 }
5404 }
5405
5406 #[test]
5408 fn intercard_counts_and_stops_at_its_limit() {
5409 let mut f = Fixture::new();
5410 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
5411 f.run(&[b"ZADD", b"y", b"10", b"b", b"20", b"c", b"30", b"d"]);
5412 assert_eq!(f.run(&[b"ZINTERCARD", b"2", b"z", b"y"]), ":2\r\n");
5413 assert_eq!(
5415 f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"0"]),
5416 ":2\r\n"
5417 );
5418 assert_eq!(
5419 f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"1"]),
5420 ":1\r\n"
5421 );
5422 let bad = "-ERR LIMIT can't be negative\r\n";
5425 assert_eq!(
5426 f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"-1"]),
5427 bad
5428 );
5429 assert_eq!(
5430 f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"x"]),
5431 bad
5432 );
5433 for cmd in [
5434 &[b"ZINTERCARD".as_slice(), b"3", b"z", b"y"][..],
5435 &[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT"],
5436 &[b"ZINTERCARD", b"2", b"z", b"y", b"junk", b"1"],
5437 ] {
5438 assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
5439 }
5440 }
5441
5442 #[test]
5444 fn a_draw_answers_one_member_or_an_array_of_them() {
5445 let mut f = Fixture::new();
5446 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
5447 assert_eq!(f.run(&[b"ZRANDMEMBER", b"nokey"]), "$-1\r\n");
5450 assert_eq!(f.run(&[b"ZRANDMEMBER", b"nokey", b"3"]), "*0\r\n");
5451 assert_eq!(f.run(&[b"ZRANDMEMBER", b"z", b"0"]), "*0\r\n");
5452 assert!(f.run(&[b"ZRANDMEMBER", b"z"]).starts_with("$1\r\n"));
5453 let all = f.run(&[b"ZRANDMEMBER", b"z", b"10"]);
5456 assert!(all.starts_with("*3\r\n"), "{all}");
5457 for m in ["a", "b", "c"] {
5458 assert!(all.contains(m), "{all}");
5459 }
5460 assert!(
5463 f.run(&[b"ZRANDMEMBER", b"z", b"-5"]).starts_with("*5\r\n"),
5464 "five draws with replacement"
5465 );
5466 assert!(
5467 f.run(&[b"ZRANDMEMBER", b"z", b"2", b"WITHSCORES"])
5468 .starts_with("*4\r\n"),
5469 "two pairs, flat on RESP2"
5470 );
5471 f.out = Out::new(Proto::Resp3);
5472 let got = f.run(&[b"ZRANDMEMBER", b"z", b"2", b"WITHSCORES"]);
5473 assert!(got.starts_with("*2\r\n*2\r\n"), "{got}");
5474 assert_eq!(f.run(&[b"ZRANDMEMBER", b"nokey"]), "_\r\n");
5475 f.out = Out::new(Proto::Resp2);
5476 assert_eq!(
5477 f.run(&[b"ZRANDMEMBER", b"z", b"2", b"junk"]),
5478 "-ERR syntax error\r\n"
5479 );
5480 assert_eq!(
5481 f.run(&[b"ZRANDMEMBER", b"z", b"x"]),
5482 "-ERR value is not an integer or out of range\r\n"
5483 );
5484 }
5485
5486 #[test]
5488 fn a_sorted_set_scan_answers_pairs_of_strings_on_both_protocols() {
5489 let mut f = Fixture::new();
5490 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
5491 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";
5492 assert_eq!(f.run(&[b"ZSCAN", b"z", b"0"]), all);
5493 assert_eq!(f.run(&[b"ZSCAN", b"z", b"0", b"COUNT", b"10"]), all);
5494 assert_eq!(
5495 f.run(&[b"ZSCAN", b"z", b"0", b"MATCH", b"a*"]),
5496 "*2\r\n$1\r\n0\r\n*2\r\n$1\r\na\r\n$1\r\n1\r\n"
5497 );
5498 assert_eq!(
5499 f.run(&[b"ZSCAN", b"nokey", b"0"]),
5500 "*2\r\n$1\r\n0\r\n*0\r\n"
5501 );
5502 f.out = Out::new(Proto::Resp3);
5505 assert_eq!(f.run(&[b"ZSCAN", b"z", b"0"]), all);
5506 f.out = Out::new(Proto::Resp2);
5507 assert_eq!(
5508 f.run(&[b"ZSCAN", b"z", b"0", b"NOVALUES"]),
5509 "-ERR NOVALUES option can only be used in HSCAN\r\n"
5510 );
5511 assert_eq!(f.run(&[b"ZSCAN", b"z", b"-1"]), "-ERR invalid cursor\r\n");
5512 assert_eq!(
5513 f.run(&[b"ZSCAN", b"z", b"0", b"COUNT", b"0"]),
5514 "-ERR syntax error\r\n"
5515 );
5516 }
5517
5518 #[test]
5520 fn a_sorted_set_pop_changes_shape_when_it_is_given_a_count() {
5521 let mut f = Fixture::new();
5522 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
5523 assert_eq!(f.run(&[b"ZPOPMIN", b"z"]), "*2\r\n$1\r\na\r\n$1\r\n1\r\n");
5525 assert_eq!(f.run(&[b"ZPOPMAX", b"z"]), "*2\r\n$1\r\nc\r\n$1\r\n3\r\n");
5526 f.run(&[b"ZADD", b"z", b"1", b"a", b"3", b"c"]);
5527 assert_eq!(
5529 f.run(&[b"ZPOPMIN", b"z", b"2"]),
5530 "*4\r\n$1\r\na\r\n$1\r\n1\r\n$1\r\nb\r\n$1\r\n2\r\n"
5531 );
5532 assert_eq!(f.run(&[b"ZPOPMIN", b"nokey"]), "*0\r\n");
5535 assert_eq!(f.run(&[b"ZPOPMIN", b"nokey", b"2"]), "*0\r\n");
5536 assert_eq!(f.run(&[b"ZPOPMIN", b"z", b"0"]), "*0\r\n");
5537 assert_eq!(
5539 f.run(&[b"ZPOPMIN", b"z", b"9"]),
5540 "*2\r\n$1\r\nc\r\n$1\r\n3\r\n"
5541 );
5542 assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
5543
5544 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b"]);
5545 f.out = Out::new(Proto::Resp3);
5546 assert_eq!(f.run(&[b"ZPOPMIN", b"z"]), "*2\r\n$1\r\na\r\n,1\r\n");
5547 assert_eq!(
5548 f.run(&[b"ZPOPMIN", b"z", b"1"]),
5549 "*1\r\n*2\r\n$1\r\nb\r\n,2\r\n"
5550 );
5551 f.out = Out::new(Proto::Resp2);
5552 let bad = "-ERR value is out of range, must be positive\r\n";
5555 assert_eq!(f.run(&[b"ZPOPMIN", b"z", b"x"]), bad);
5556 assert_eq!(f.run(&[b"ZPOPMIN", b"z", b"-1"]), bad);
5557 assert_eq!(
5558 f.run(&[b"ZPOPMIN", b"z", b"1", b"2"]),
5559 "-ERR syntax error\r\n"
5560 );
5561 }
5562
5563 #[test]
5565 fn a_multi_key_pop_names_the_key_that_answered_and_nests_its_pairs() {
5566 let mut f = Fixture::new();
5567 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
5568 assert_eq!(
5569 f.run(&[b"ZMPOP", b"2", b"nokey", b"z", b"MIN"]),
5570 "*2\r\n$1\r\nz\r\n*1\r\n*2\r\n$1\r\na\r\n$1\r\n1\r\n"
5571 );
5572 assert_eq!(
5575 f.run(&[b"ZMPOP", b"1", b"z", b"MAX", b"COUNT", b"2"]),
5576 "*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"
5577 );
5578 assert_eq!(f.run(&[b"ZMPOP", b"1", b"nokey", b"MIN"]), "*-1\r\n");
5580 f.out = Out::new(Proto::Resp3);
5581 assert_eq!(f.run(&[b"ZMPOP", b"1", b"nokey", b"MIN"]), "_\r\n");
5582 f.out = Out::new(Proto::Resp2);
5583 let numkeys = "-ERR numkeys should be greater than 0\r\n";
5584 for bad in [
5585 &[b"ZMPOP".as_slice(), b"0", b"z", b"MIN"][..],
5586 &[b"ZMPOP", b"-1", b"z", b"MIN"],
5587 &[b"ZMPOP", b"x", b"z", b"MIN"],
5588 ] {
5589 assert_eq!(f.run(bad), numkeys, "{:?}", bad[1]);
5590 }
5591 let count = "-ERR count should be greater than 0\r\n";
5592 for bad in [
5593 &[b"ZMPOP".as_slice(), b"1", b"z", b"MIN", b"COUNT", b"0"][..],
5594 &[b"ZMPOP", b"1", b"z", b"MIN", b"COUNT", b"-1"],
5595 &[b"ZMPOP", b"1", b"z", b"MIN", b"COUNT", b"x"],
5596 ] {
5597 assert_eq!(f.run(bad), count, "{:?}", bad[5]);
5598 }
5599 let syntax = "-ERR syntax error\r\n";
5600 for bad in [
5601 &[b"ZMPOP".as_slice(), b"2", b"z", b"MIN"][..],
5604 &[b"ZMPOP", b"1", b"z", b"SIDEWAYS"],
5605 &[b"ZMPOP", b"1", b"z", b"MIN", b"junk"],
5606 &[b"ZMPOP", b"1", b"z", b"MIN", b"COUNT", b"1", b"junk"],
5607 ] {
5608 assert_eq!(f.run(bad), syntax, "{bad:?}");
5609 }
5610 }
5611
5612 #[test]
5615 fn the_sorted_set_pops_that_wait_answer_like_the_ones_they_wrap() {
5616 let mut f = Fixture::new();
5617 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
5618 assert_eq!(
5619 f.flow(&[b"BZPOPMIN", b"nokey", b"z", b"0"]),
5620 (
5621 Flow::Continue,
5622 "*3\r\n$1\r\nz\r\n$1\r\na\r\n$1\r\n1\r\n".to_owned()
5623 )
5624 );
5625 assert_eq!(
5626 f.run(&[b"BZPOPMAX", b"z", b"0"]),
5627 "*3\r\n$1\r\nz\r\n$1\r\nc\r\n$1\r\n3\r\n"
5628 );
5629 f.run(&[b"ZADD", b"z", b"1", b"a", b"3", b"c"]);
5630 assert_eq!(
5631 f.run(&[
5632 b"BZMPOP", b"0", b"2", b"nokey", b"z", b"MIN", b"COUNT", b"2"
5633 ]),
5634 "*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"
5635 );
5636 f.out = Out::new(Proto::Resp3);
5637 assert_eq!(
5638 f.run(&[b"BZPOPMIN", b"z", b"0"]),
5639 "*3\r\n$1\r\nz\r\n$1\r\nc\r\n,3\r\n"
5640 );
5641 f.out = Out::new(Proto::Resp2);
5642 assert_eq!(
5644 f.flow(&[b"BZPOPMIN", b"z", b"0"]),
5645 (Flow::Block, String::new())
5646 );
5647 assert_eq!(
5648 f.flow(&[b"BZMPOP", b"0", b"1", b"z", b"MIN"]),
5649 (Flow::Block, String::new())
5650 );
5651 assert_eq!(
5654 f.run(&[b"BZMPOP", b"abc", b"0", b"z", b"MIN"]),
5655 "-ERR timeout is not a float or out of range\r\n"
5656 );
5657 assert_eq!(
5658 f.run(&[b"BZMPOP", b"0", b"0", b"z", b"MIN"]),
5659 "-ERR numkeys should be greater than 0\r\n"
5660 );
5661 assert_eq!(
5662 f.run(&[b"BZPOPMIN", b"z", b"-1"]),
5663 "-ERR timeout is negative\r\n"
5664 );
5665 }
5666
5667 #[test]
5671 fn a_parked_sorted_set_client_waits_for_a_member_and_not_for_a_key() {
5672 let mut f = Fixture::new();
5673 assert_eq!(f.flow(&[b"BZPOPMIN", b"z", b"0"]).0, Flow::Block);
5674 assert_eq!(f.server.waiters().len(), 1);
5675 f.run(&[b"SET", b"z", b"v"]);
5678 let mut out = Out::new(Proto::Resp2);
5679 assert!(!f.server.serve_waiter(0, 0, &mut out));
5680 assert!(out.as_slice().is_empty());
5681 f.run(&[b"DEL", b"z"]);
5682 f.run(&[b"ZADD", b"z", b"5", b"m"]);
5683 assert!(f.server.serve_waiter(0, 0, &mut out));
5684 assert_eq!(
5685 core::str::from_utf8(out.as_slice()).expect("ascii"),
5686 "*3\r\n$1\r\nz\r\n$1\r\nm\r\n$1\r\n5\r\n"
5687 );
5688 assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
5691 }
5692
5693 #[test]
5694 fn every_sorted_set_command_says_wrongtype_and_writes_nothing() {
5695 let mut f = Fixture::new();
5696 f.run(&[b"SET", b"s", b"v"]);
5697 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
5698 for cmd in [
5699 &[b"ZADD".as_slice(), b"s", b"1", b"a"][..],
5700 &[b"ZINCRBY", b"s", b"1", b"a"],
5701 &[b"ZCARD", b"s"],
5702 &[b"ZSCORE", b"s", b"a"],
5703 &[b"ZMSCORE", b"s", b"a"],
5704 &[b"ZREM", b"s", b"a"],
5705 &[b"ZRANK", b"s", b"a"],
5706 &[b"ZREVRANK", b"s", b"a"],
5707 &[b"ZCOUNT", b"s", b"1", b"2"],
5708 &[b"ZLEXCOUNT", b"s", b"-", b"+"],
5709 &[b"ZRANGE", b"s", b"0", b"-1"],
5710 &[b"ZREVRANGE", b"s", b"0", b"-1"],
5711 &[b"ZRANGEBYSCORE", b"s", b"1", b"2"],
5712 &[b"ZREVRANGEBYSCORE", b"s", b"2", b"1"],
5713 &[b"ZRANGEBYLEX", b"s", b"-", b"+"],
5714 &[b"ZREVRANGEBYLEX", b"s", b"+", b"-"],
5715 &[b"ZRANGESTORE", b"d", b"s", b"0", b"-1"],
5716 &[b"ZREMRANGEBYRANK", b"s", b"0", b"-1"],
5717 &[b"ZREMRANGEBYSCORE", b"s", b"1", b"2"],
5718 &[b"ZREMRANGEBYLEX", b"s", b"-", b"+"],
5719 &[b"ZUNION", b"1", b"s"],
5720 &[b"ZINTER", b"1", b"s"],
5721 &[b"ZDIFF", b"1", b"s"],
5722 &[b"ZUNIONSTORE", b"d", b"1", b"s"],
5723 &[b"ZINTERSTORE", b"d", b"1", b"s"],
5724 &[b"ZDIFFSTORE", b"d", b"1", b"s"],
5725 &[b"ZINTERCARD", b"1", b"s"],
5726 &[b"ZRANDMEMBER", b"s"],
5727 &[b"ZSCAN", b"s", b"0"],
5728 &[b"ZPOPMIN", b"s"],
5729 &[b"ZPOPMAX", b"s", b"2"],
5730 &[b"ZMPOP", b"1", b"s", b"MIN"],
5731 &[b"BZPOPMIN", b"s", b"0"],
5732 &[b"BZPOPMAX", b"s", b"0"],
5733 &[b"BZMPOP", b"0", b"1", b"s", b"MIN"],
5734 ] {
5735 assert_eq!(f.run(cmd), wrong, "{:?}", cmd[0]);
5736 }
5737 assert_eq!(f.run(&[b"GET", b"s"]), "$1\r\nv\r\n");
5738 }
5739
5740 #[test]
5744 fn churning_sorted_sets_does_not_grow_the_server() {
5745 let mut f = Fixture::new();
5746 let members: Vec<Vec<u8>> = (0..200).map(|i| format!("m{i}").into_bytes()).collect();
5747 let scores: Vec<Vec<u8>> = (0..200).map(|i| format!("{i}").into_bytes()).collect();
5748 let mut args: Vec<&[u8]> = vec![b"ZADD", b"z"];
5749 for i in 0..200 {
5750 args.push(&scores[i]);
5751 args.push(&members[i]);
5752 }
5753
5754 f.run(&args);
5755 f.run(&[b"DEL", b"z"]);
5756 f.server.compact_step();
5757 let after_first = f.server.memory_bytes();
5758
5759 for _ in 0..200 {
5760 f.run(&args);
5761 f.run(&[b"DEL", b"z"]);
5762 f.server.compact_step();
5763 }
5764 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
5765 assert!(
5766 f.server.memory_bytes() <= after_first * 2,
5767 "held {} after two hundred passes against {after_first} after one",
5768 f.server.memory_bytes()
5769 );
5770 }
5771
5772 #[test]
5775 fn an_array_writes_at_any_index_and_reads_back_what_it_sent() {
5776 let mut f = Fixture::new();
5777 assert_eq!(
5780 f.run(&[b"ARSET", b"a", b"1000", b"x", b"y", b"z"]),
5781 ":3\r\n"
5782 );
5783 assert_eq!(f.run(&[b"ARSET", b"a", b"1000", b"X", b"Y"]), ":0\r\n");
5784 assert_eq!(f.run(&[b"ARGET", b"a", b"1000"]), "$1\r\nX\r\n");
5785 assert_eq!(f.run(&[b"ARGET", b"a", b"1002"]), "$1\r\nz\r\n");
5786 assert_eq!(f.run(&[b"ARGET", b"a", b"999"]), "$-1\r\n");
5788 assert_eq!(f.run(&[b"ARGET", b"nope", b"0"]), "$-1\r\n");
5789 assert_eq!(
5790 f.run(&[b"ARMGET", b"a", b"1002", b"999", b"1000"]),
5791 "*3\r\n$1\r\nz\r\n$-1\r\n$1\r\nX\r\n"
5792 );
5793 assert_eq!(f.run(&[b"ARMSET", b"a", b"5", b"p", b"5", b"q"]), ":1\r\n");
5795 assert_eq!(f.run(&[b"ARGET", b"a", b"5"]), "$1\r\nq\r\n");
5796 }
5797
5798 #[test]
5801 fn the_length_is_the_high_water_mark_and_the_count_is_the_population() {
5802 let mut f = Fixture::new();
5803 assert_eq!(f.run(&[b"ARLEN", b"nope"]), ":0\r\n");
5804 assert_eq!(f.run(&[b"ARCOUNT", b"nope"]), ":0\r\n");
5805 f.run(&[b"ARMSET", b"a", b"0", b"x", b"9", b"y"]);
5806 assert_eq!(f.run(&[b"ARLEN", b"a"]), ":10\r\n");
5807 assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":2\r\n");
5808 assert_eq!(f.run(&[b"ARDEL", b"a", b"0"]), ":1\r\n");
5810 assert_eq!(f.run(&[b"ARLEN", b"a"]), ":10\r\n");
5811 assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":1\r\n");
5812
5813 f.run(&[b"ARSET", b"top", b"18446744073709551614", b"z"]);
5817 assert_eq!(f.run(&[b"ARLEN", b"top"]), ":18446744073709551615\r\n");
5818 assert_eq!(f.run(&[b"ARCOUNT", b"top"]), ":1\r\n");
5819 assert_eq!(
5822 f.run(&[b"ARSET", b"over", b"18446744073709551614", b"a", b"b"]),
5823 "-ERR array index overflow\r\n"
5824 );
5825 assert_eq!(f.run(&[b"EXISTS", b"over"]), ":0\r\n");
5826 }
5827
5828 #[test]
5831 fn a_range_read_answers_for_the_holes_too_and_is_capped_at_a_million() {
5832 let mut f = Fixture::new();
5833 f.run(&[b"ARSET", b"a", b"1", b"x"]);
5834 assert_eq!(
5835 f.run(&[b"ARGETRANGE", b"a", b"0", b"3"]),
5836 "*4\r\n$-1\r\n$1\r\nx\r\n$-1\r\n$-1\r\n"
5837 );
5838 assert_eq!(
5841 f.run(&[b"ARGETRANGE", b"a", b"3", b"0"]),
5842 "*4\r\n$-1\r\n$-1\r\n$1\r\nx\r\n$-1\r\n"
5843 );
5844 assert_eq!(
5846 f.run(&[b"ARGETRANGE", b"nope", b"0", b"1"]),
5847 "*2\r\n$-1\r\n$-1\r\n"
5848 );
5849 assert_eq!(
5853 f.run(&[b"ARGETRANGE", b"nope", b"0", b"18446744073709551614"]),
5854 "-ERR range exceeds maximum of 1000000 items\r\n"
5855 );
5856 }
5857
5858 #[test]
5861 fn a_bad_index_late_in_the_line_writes_none_of_the_earlier_ones() {
5862 let mut f = Fixture::new();
5863 assert_eq!(
5864 f.run(&[b"ARMSET", b"a", b"0", b"x", b"-1", b"y"]),
5865 "-ERR invalid array index\r\n"
5866 );
5867 assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
5868 f.run(&[b"ARSET", b"a", b"0", b"x", b"y", b"z"]);
5869 assert_eq!(
5870 f.run(&[b"ARDEL", b"a", b"0", b"01"]),
5871 "-ERR invalid array index\r\n"
5872 );
5873 assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":3\r\n");
5874 assert_eq!(
5877 f.run(&[b"ARGET", b"a", b"-1"]),
5878 "-ERR invalid array index\r\n"
5879 );
5880 assert_eq!(
5883 f.run(&[b"ARMSET", b"a", b"0", b"x", b"1"]),
5884 "-ERR wrong number of arguments for 'armset' command\r\n"
5885 );
5886 assert_eq!(
5887 f.run(&[b"ARDELRANGE", b"a", b"0", b"1", b"2"]),
5888 "-ERR wrong number of arguments for 'ardelrange' command\r\n"
5889 );
5890 }
5891
5892 #[test]
5893 fn a_range_delete_costs_the_elements_and_takes_the_key_when_it_empties() {
5894 let mut f = Fixture::new();
5895 f.run(&[b"ARSET", b"a", b"0", b"0", b"1", b"2", b"3", b"4"]);
5896 assert_eq!(f.run(&[b"ARDELRANGE", b"a", b"3", b"1"]), ":3\r\n");
5897 assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":2\r\n");
5898 assert_eq!(
5901 f.run(&[
5902 b"ARDELRANGE",
5903 b"a",
5904 b"100",
5905 b"200",
5906 b"0",
5907 b"18446744073709551614"
5908 ]),
5909 ":2\r\n"
5910 );
5911 assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
5912 assert_eq!(f.run(&[b"ARDELRANGE", b"nope", b"0", b"1"]), ":0\r\n");
5913 assert_eq!(f.run(&[b"ARDEL", b"nope", b"0"]), ":0\r\n");
5914 }
5915
5916 #[test]
5919 fn a_value_comes_back_byte_for_byte_however_it_was_packed() {
5920 let mut f = Fixture::new();
5921 let long = vec![b'v'; 200];
5922 f.run(&[
5923 b"ARMSET", b"a", b"0", b"42", b"1", b"007", b"2", b"3.5", b"3", b"3.14", b"4",
5924 b"short", b"5", &long, b"6", b"-0",
5925 ]);
5926 assert_eq!(
5930 f.run(&[b"ARGETRANGE", b"a", b"0", b"6"]),
5931 format!(
5932 "*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",
5933 String::from_utf8_lossy(&long)
5934 )
5935 );
5936 }
5937
5938 #[test]
5939 fn an_array_is_a_type_and_an_encoding_a_client_can_see() {
5940 let mut f = Fixture::new();
5941 f.run(&[b"ARSET", b"a", b"0", b"x"]);
5942 assert_eq!(f.run(&[b"TYPE", b"a"]), "+array\r\n");
5943 assert_eq!(
5944 f.run(&[b"OBJECT", b"ENCODING", b"a"]),
5945 "$12\r\nsliced-array\r\n"
5946 );
5947 assert_eq!(f.run(&[b"EXPIRE", b"a", b"100"]), ":1\r\n");
5949 assert_eq!(f.run(&[b"PERSIST", b"a"]), ":1\r\n");
5950 assert_eq!(f.run(&[b"COPY", b"a", b"b"]), ":1\r\n");
5951 assert_eq!(f.run(&[b"ARGET", b"b", b"0"]), "$1\r\nx\r\n");
5952 assert_eq!(f.run(&[b"RENAME", b"a", b"c"]), "+OK\r\n");
5953 assert_eq!(f.run(&[b"ARCOUNT", b"c"]), ":1\r\n");
5954 }
5955
5956 #[test]
5957 fn every_array_command_refuses_a_key_holding_something_else() {
5958 let mut f = Fixture::new();
5959 f.run(&[b"SET", b"s", b"v"]);
5960 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
5961 for cmd in [
5962 &[b"ARSET".as_ref(), b"s", b"0", b"x"][..],
5963 &[b"ARMSET".as_ref(), b"s", b"0", b"x"][..],
5964 &[b"ARGET".as_ref(), b"s", b"0"][..],
5965 &[b"ARMGET".as_ref(), b"s", b"0"][..],
5966 &[b"ARGETRANGE".as_ref(), b"s", b"0", b"1"][..],
5967 &[b"ARLEN".as_ref(), b"s"][..],
5968 &[b"ARCOUNT".as_ref(), b"s"][..],
5969 &[b"ARDEL".as_ref(), b"s", b"0"][..],
5970 &[b"ARDELRANGE".as_ref(), b"s", b"0", b"1"][..],
5971 &[b"ARINSERT".as_ref(), b"s", b"x"][..],
5972 &[b"ARRING".as_ref(), b"s", b"4", b"x"][..],
5973 &[b"ARNEXT".as_ref(), b"s"][..],
5974 &[b"ARSEEK".as_ref(), b"s", b"1"][..],
5975 &[b"ARLASTITEMS".as_ref(), b"s", b"1"][..],
5976 &[b"ARSCAN".as_ref(), b"s", b"0", b"1"][..],
5977 &[b"ARGREP".as_ref(), b"s", b"0", b"1", b"EXACT", b"v"][..],
5978 &[b"AROP".as_ref(), b"s", b"0", b"1", b"SUM"][..],
5979 &[b"ARINFO".as_ref(), b"s"][..],
5980 ] {
5981 assert_eq!(f.run(cmd), wrong, "{}", String::from_utf8_lossy(cmd[0]));
5982 }
5983 }
5984
5985 #[test]
5989 fn a_bad_index_reports_the_type_only_where_redis_reports_it() {
5990 let mut f = Fixture::new();
5991 f.run(&[b"SET", b"s", b"v"]);
5992 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
5993 let bad = "-ERR invalid array index\r\n";
5994 assert_eq!(f.run(&[b"ARGET", b"s", b"-1"]), wrong);
5995 assert_eq!(f.run(&[b"ARMGET", b"s", b"0", b"-1"]), wrong);
5996 assert_eq!(f.run(&[b"ARSET", b"s", b"-1", b"x"]), bad);
5997 assert_eq!(f.run(&[b"ARDEL", b"s", b"-1"]), bad);
5998 assert_eq!(f.run(&[b"ARSCAN", b"s", b"-1", b"0"]), bad);
5999 assert_eq!(f.run(&[b"ARGREP", b"s", b"-1", b"0", b"EXACT", b"v"]), bad);
6000 f.run(&[b"ARSET", b"a", b"0", b"x"]);
6002 assert_eq!(f.run(&[b"ARGET", b"a", b"-1"]), bad);
6003 assert_eq!(f.run(&[b"ARGET", b"nope", b"-1"]), bad);
6004 }
6005
6006 #[test]
6007 fn an_append_follows_a_cursor_the_client_can_move() {
6008 let mut f = Fixture::new();
6009 assert_eq!(f.run(&[b"ARNEXT", b"nope"]), ":0\r\n");
6010 assert_eq!(f.run(&[b"ARINSERT", b"a", b"x", b"y"]), ":1\r\n");
6011 assert_eq!(f.run(&[b"ARNEXT", b"a"]), ":2\r\n");
6012 assert_eq!(f.run(&[b"ARINSERT", b"a", b"z"]), ":2\r\n");
6013 assert_eq!(f.run(&[b"ARGET", b"a", b"2"]), "$1\r\nz\r\n");
6014
6015 assert_eq!(f.run(&[b"ARSEEK", b"nope", b"5"]), ":0\r\n");
6018 assert_eq!(f.run(&[b"EXISTS", b"nope"]), ":0\r\n");
6019 assert_eq!(f.run(&[b"ARSEEK", b"a", b"100"]), ":1\r\n");
6020 assert_eq!(f.run(&[b"ARNEXT", b"a"]), ":100\r\n");
6021 assert_eq!(f.run(&[b"ARINSERT", b"a", b"far"]), ":100\r\n");
6022 assert_eq!(f.run(&[b"ARSEEK", b"a", b"0"]), ":1\r\n");
6023 assert_eq!(f.run(&[b"ARNEXT", b"a"]), ":0\r\n");
6024
6025 assert_eq!(f.run(&[b"ARSEEK", b"a", b"18446744073709551615"]), ":1\r\n");
6028 assert_eq!(f.run(&[b"ARNEXT", b"a"]), "$-1\r\n");
6029 assert_eq!(
6030 f.run(&[b"ARINSERT", b"a", b"x"]),
6031 "-ERR insert index overflow\r\n"
6032 );
6033 assert_eq!(
6034 f.run(&[b"ARSET", b"a", b"18446744073709551615", b"x"]),
6035 "-ERR invalid array index\r\n"
6036 );
6037 }
6038
6039 #[test]
6040 fn a_ring_keeps_the_newest_and_renumbers_them_when_it_is_resized() {
6041 let mut f = Fixture::new();
6042 assert_eq!(f.run(&[b"ARRING", b"r", b"3", b"a", b"b", b"c"]), ":2\r\n");
6043 assert_eq!(f.run(&[b"ARRING", b"r", b"3", b"d", b"e"]), ":1\r\n");
6044 assert_eq!(f.run(&[b"ARLEN", b"r"]), ":3\r\n");
6045 assert_eq!(
6046 f.run(&[b"ARGETRANGE", b"r", b"0", b"2"]),
6047 "*3\r\n$1\r\nd\r\n$1\r\ne\r\n$1\r\nc\r\n"
6048 );
6049 assert_eq!(f.run(&[b"ARRING", b"r", b"5", b"f"]), ":3\r\n");
6052 assert_eq!(
6053 f.run(&[b"ARGETRANGE", b"r", b"0", b"3"]),
6054 "*4\r\n$1\r\nc\r\n$1\r\nd\r\n$1\r\ne\r\n$1\r\nf\r\n"
6055 );
6056 assert_eq!(
6059 f.run(&[b"ARRING", b"r", b"0", b"x"]),
6060 "-ERR size must be positive\r\n"
6061 );
6062 assert_eq!(
6063 f.run(&[b"ARRING", b"r", b"big", b"x"]),
6064 "-ERR invalid size\r\n"
6065 );
6066 }
6067
6068 #[test]
6069 fn the_last_items_walk_back_from_the_cursor_and_report_the_holes() {
6070 let mut f = Fixture::new();
6071 assert_eq!(f.run(&[b"ARLASTITEMS", b"nope", b"5"]), "*0\r\n");
6072 f.run(&[b"ARRING", b"r", b"4", b"a", b"b", b"c", b"d", b"e"]);
6073 assert_eq!(
6074 f.run(&[b"ARLASTITEMS", b"r", b"3"]),
6075 "*3\r\n$1\r\nc\r\n$1\r\nd\r\n$1\r\ne\r\n"
6076 );
6077 assert_eq!(
6078 f.run(&[b"ARLASTITEMS", b"r", b"3", b"rev"]),
6079 "*3\r\n$1\r\ne\r\n$1\r\nd\r\n$1\r\nc\r\n"
6080 );
6081 assert_eq!(
6082 f.run(&[b"ARLASTITEMS", b"r", b"99"]),
6083 "*4\r\n$1\r\nb\r\n$1\r\nc\r\n$1\r\nd\r\n$1\r\ne\r\n",
6084 "more than there is gets what there is"
6085 );
6086 assert_eq!(f.run(&[b"ARLASTITEMS", b"r", b"0", b"junk"]), "*0\r\n");
6089 assert_eq!(
6090 f.run(&[b"ARLASTITEMS", b"r", b"1", b"junk"]),
6091 "-ERR syntax error\r\n"
6092 );
6093 assert_eq!(
6094 f.run(&[b"ARLASTITEMS", b"r", b"nine"]),
6095 "-ERR invalid COUNT\r\n"
6096 );
6097
6098 f.run(&[b"ARMSET", b"h", b"0", b"x", b"2", b"z"]);
6101 assert_eq!(
6102 f.run(&[b"ARLASTITEMS", b"h", b"5"]),
6103 "*2\r\n$-1\r\n$1\r\nz\r\n"
6104 );
6105 }
6106
6107 #[test]
6108 fn a_scan_answers_pairs_for_what_is_there_and_skips_what_is_not() {
6109 let mut f = Fixture::new();
6110 assert_eq!(f.run(&[b"ARSCAN", b"nope", b"0", b"10"]), "*0\r\n");
6111 f.run(&[b"ARMSET", b"a", b"0", b"x", b"7", b"y", b"1000000", b"z"]);
6112 assert_eq!(
6115 f.run(&[b"ARSCAN", b"a", b"0", b"18446744073709551614"]),
6116 "*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"
6117 );
6118 assert_eq!(
6119 f.run(&[
6120 b"ARSCAN",
6121 b"a",
6122 b"18446744073709551614",
6123 b"0",
6124 b"LIMIT",
6125 b"1"
6126 ]),
6127 "*1\r\n*2\r\n:1000000\r\n$1\r\nz\r\n"
6128 );
6129 assert_eq!(f.run(&[b"ARSCAN", b"a", b"1", b"6"]), "*0\r\n");
6130 assert_eq!(
6131 f.run(&[b"ARSCAN", b"a", b"0", b"10", b"LIMIT", b"0"]),
6132 "-ERR LIMIT must be positive\r\n"
6133 );
6134 assert_eq!(
6135 f.run(&[b"ARSCAN", b"a", b"0", b"10", b"NOPE", b"1"]),
6136 "-ERR syntax error\r\n"
6137 );
6138 assert_eq!(
6139 f.run(&[b"ARSCAN", b"a", b"0", b"10", b"LIMIT"]),
6140 "-ERR wrong number of arguments for 'arscan' command\r\n"
6141 );
6142 }
6143
6144 #[test]
6145 fn a_grep_answers_the_indexes_whose_elements_match() {
6146 let mut f = Fixture::new();
6147 assert_eq!(
6148 f.run(&[b"ARGREP", b"nope", b"0", b"10", b"EXACT", b"x"]),
6149 "*0\r\n"
6150 );
6151 f.run(&[b"ARSET", b"a", b"0", b"alpha", b"beta", b"gamma", b"ALPHA"]);
6152
6153 assert_eq!(
6156 f.run(&[b"ARGREP", b"a", b"-", b"+", b"GLOB", b"*a"]),
6157 "*3\r\n:0\r\n:1\r\n:2\r\n"
6158 );
6159 assert_eq!(
6160 f.run(&[b"ARGREP", b"a", b"+", b"-", b"GLOB", b"*a"]),
6161 "*3\r\n:2\r\n:1\r\n:0\r\n"
6162 );
6163 assert_eq!(
6164 f.run(&[b"ARGREP", b"a", b"1", b"2", b"GLOB", b"*a"]),
6165 "*2\r\n:1\r\n:2\r\n"
6166 );
6167
6168 assert_eq!(
6171 f.run(&[b"ARGREP", b"a", b"-", b"+", b"EXACT", b"alpha"]),
6172 "*1\r\n:0\r\n"
6173 );
6174 assert_eq!(
6175 f.run(&[b"ARGREP", b"a", b"-", b"+", b"EXACT", b"alpha", b"NOCASE"]),
6176 "*2\r\n:0\r\n:3\r\n"
6177 );
6178 assert_eq!(
6179 f.run(&[b"ARGREP", b"a", b"-", b"+", b"MATCH", b"mm"]),
6180 "*1\r\n:2\r\n"
6181 );
6182 assert_eq!(
6183 f.run(&[b"ARGREP", b"a", b"-", b"+", b"RE", b"^[bg]"]),
6184 "*2\r\n:1\r\n:2\r\n"
6185 );
6186
6187 let both: &[&[u8]] = &[
6190 b"ARGREP", b"a", b"-", b"+", b"EXACT", b"beta", b"MATCH", b"al",
6191 ];
6192 assert_eq!(f.run(both), "*2\r\n:0\r\n:1\r\n");
6193 assert_eq!(
6194 f.run(&[
6195 b"ARGREP", b"a", b"-", b"+", b"EXACT", b"beta", b"MATCH", b"al", b"AND"
6196 ]),
6197 "*0\r\n"
6198 );
6199 assert_eq!(
6200 f.run(&[
6201 b"ARGREP", b"a", b"-", b"+", b"EXACT", b"beta", b"MATCH", b"al", b"AND", b"OR"
6202 ]),
6203 "*2\r\n:0\r\n:1\r\n"
6204 );
6205
6206 assert_eq!(
6209 f.run(&[
6210 b"ARGREP",
6211 b"a",
6212 b"-",
6213 b"+",
6214 b"MATCH",
6215 b"a",
6216 b"WITHVALUES",
6217 b"LIMIT",
6218 b"2"
6219 ]),
6220 "*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"
6221 );
6222 assert_eq!(
6223 f.run(&[
6224 b"ARGREP", b"a", b"-", b"+", b"EXACT", b"ALPHA", b"LIMIT", b"1"
6225 ]),
6226 "*1\r\n:3\r\n"
6227 );
6228 }
6229
6230 #[test]
6232 fn a_grep_reports_a_broken_command_the_way_redis_does() {
6233 let mut f = Fixture::new();
6234 f.run(&[b"ARSET", b"a", b"0", b"alpha"]);
6235 let syntax = "-ERR syntax error\r\n";
6236
6237 assert_eq!(
6240 f.run(&[b"ARGREP", b"a", b"-1", b"0", b"NOPE", b"x"]),
6241 "-ERR invalid array index\r\n"
6242 );
6243 assert_eq!(f.run(&[b"ARGREP", b"a", b"0", b"1", b"NOPE", b"x"]), syntax);
6244 assert_eq!(
6246 f.run(&[b"ARGREP", b"a", b"0", b"1", b"NOCASE", b"EXACT"]),
6247 syntax
6248 );
6249 assert_eq!(
6250 f.run(&[b"ARGREP", b"a", b"0", b"1", b"EXACT", b"x", b"LIMIT"]),
6251 syntax
6252 );
6253 assert_eq!(
6254 f.run(&[b"ARGREP", b"a", b"0", b"1", b"NOCASE", b"WITHVALUES"]),
6255 syntax,
6256 "a command with no predicate in it at all"
6257 );
6258 assert_eq!(
6259 f.run(&[b"ARGREP", b"a", b"0", b"1", b"EXACT", b"x", b"LIMIT", b"0"]),
6260 "-ERR LIMIT must be positive\r\n"
6261 );
6262 assert_eq!(
6263 f.run(&[
6264 b"ARGREP", b"a", b"0", b"1", b"EXACT", b"x", b"LIMIT", b"nine"
6265 ]),
6266 "-ERR value is not an integer or out of range\r\n"
6267 );
6268 assert_eq!(
6269 f.run(&[b"ARGREP", b"a", b"0", b"1", b"RE", b""]),
6270 "-ERR regular expression is empty\r\n"
6271 );
6272 assert_eq!(
6273 f.run(&[b"ARGREP", b"a", b"0", b"1", b"RE", b"(a"]),
6274 "-ERR invalid regular expression: Missing ')'\r\n"
6275 );
6276 assert_eq!(
6277 f.run(&[b"ARGREP", b"a", b"0", b"1", b"RE", br"(a)\1"]),
6278 "-ERR regular expression backreferences are not supported\r\n"
6279 );
6280 let arity = "-ERR wrong number of arguments for 'argrep' command\r\n";
6283 assert_eq!(f.run(&[b"ARGREP", b"a", b"0", b"1", b"EXACT"]), arity);
6284 assert_eq!(f.run(&[b"ARGREP", b"a", b"0", b"1"]), arity);
6285 }
6286
6287 #[test]
6288 fn an_op_reduces_a_range_to_one_number() {
6289 let mut f = Fixture::new();
6290 f.run(&[b"ARSET", b"a", b"0", b"1", b"2.5", b"word", b"-4"]);
6291 assert_eq!(
6292 f.run(&[b"AROP", b"a", b"0", b"10", b"SUM"]),
6293 "$4\r\n-0.5\r\n"
6294 );
6295 assert_eq!(f.run(&[b"AROP", b"a", b"0", b"10", b"min"]), "$2\r\n-4\r\n");
6296 assert_eq!(
6297 f.run(&[b"AROP", b"a", b"0", b"10", b"MAX"]),
6298 "$3\r\n2.5\r\n"
6299 );
6300 assert_eq!(f.run(&[b"AROP", b"a", b"0", b"10", b"USED"]), ":4\r\n");
6301 assert_eq!(
6302 f.run(&[b"AROP", b"a", b"0", b"10", b"MATCH", b"word"]),
6303 ":1\r\n"
6304 );
6305 f.run(&[b"ARSET", b"t", b"0", b"0.1", b"0.2"]);
6308 assert_eq!(
6309 f.run(&[b"AROP", b"t", b"0", b"10", b"SUM"]),
6310 "$19\r\n0.30000000000000004\r\n"
6311 );
6312 assert_eq!(f.run(&[b"ZADD", b"z", b"0.3", b"m"]), ":1\r\n");
6313 assert_eq!(f.run(&[b"ZSCORE", b"z", b"m"]), "$3\r\n0.3\r\n");
6314
6315 f.run(&[b"ARSET", b"w", b"0", b"word"]);
6318 assert_eq!(f.run(&[b"AROP", b"w", b"0", b"10", b"SUM"]), "$-1\r\n");
6319 assert_eq!(f.run(&[b"AROP", b"nope", b"0", b"10", b"SUM"]), "$-1\r\n");
6320 assert_eq!(f.run(&[b"AROP", b"nope", b"0", b"10", b"USED"]), ":0\r\n");
6321
6322 assert_eq!(
6323 f.run(&[b"AROP", b"a", b"0", b"10", b"NOPE"]),
6324 "-ERR unknown operation\r\n"
6325 );
6326 assert_eq!(
6327 f.run(&[b"AROP", b"a", b"0", b"10", b"MATCH"]),
6328 "-ERR MATCH requires a value argument\r\n"
6329 );
6330 assert_eq!(
6331 f.run(&[b"AROP", b"a", b"0", b"10", b"SUM", b"extra"]),
6332 "-ERR wrong number of arguments for 'arop' command\r\n"
6333 );
6334 }
6335
6336 #[test]
6337 fn the_info_is_a_map_and_a_missing_key_is_an_error() {
6338 let mut f = Fixture::new();
6339 assert_eq!(f.run(&[b"ARINFO", b"nope"]), "-ERR no such key\r\n");
6340 f.run(&[b"ARINSERT", b"a", b"x", b"y"]);
6341 let short = f.run(&[b"ARINFO", b"a"]);
6342 assert!(
6343 short.starts_with("*14\r\n"),
6344 "seven pairs on RESP2: {short}"
6345 );
6346 assert!(short.contains("$5\r\ncount\r\n:2\r\n"), "{short}");
6347 assert!(
6348 short.contains("$17\r\nnext-insert-index\r\n:2\r\n"),
6349 "{short}"
6350 );
6351 assert!(short.contains("$10\r\nslice-size\r\n:4096\r\n"), "{short}");
6352 let full = f.run(&[b"ARINFO", b"a", b"full"]);
6353 assert!(full.starts_with("*24\r\n"), "twelve pairs: {full}");
6354 assert!(full.contains("$12\r\ndense-slices\r\n:0\r\n"), "{full}");
6357 assert!(full.contains("$13\r\nsparse-slices\r\n:1\r\n"), "{full}");
6358 assert!(
6359 full.contains("$14\r\navg-dense-size\r\n$1\r\n0\r\n"),
6360 "{full}"
6361 );
6362 assert_eq!(f.run(&[b"ARINFO", b"a", b"nope"]), "-ERR syntax error\r\n");
6363
6364 let mut g = Fixture::new();
6366 g.run(&[b"HELLO", b"3"]);
6367 g.run(&[b"ARINSERT", b"a", b"x"]);
6368 let map = g.run(&[b"ARINFO", b"a", b"FULL"]);
6369 assert!(map.starts_with("%12\r\n"), "{map}");
6370 assert!(map.contains("$5\r\ncount\r\n:1\r\n"), "{map}");
6371 assert!(map.contains("$14\r\navg-dense-size\r\n,0\r\n"), "{map}");
6372 }
6373
6374 #[test]
6375 fn a_double_on_the_wire_is_written_the_way_redis_writes_one() {
6376 let mut f = Fixture::new();
6377 for (score, want) in [
6380 ("3", "3"),
6381 ("3.5", "3.5"),
6382 ("0.3", "0.3"),
6383 ("1e30", "1e+30"),
6384 ("1e19", "1e+19"),
6385 ("1e-7", "1e-7"),
6386 ("0.000001", "0.000001"),
6387 ("4611686018427387904", "4611686018427387904"),
6388 ("-0", "-0"),
6389 ] {
6390 f.run(&[b"ZADD", b"z", score.as_bytes(), b"m"]);
6391 assert_eq!(
6392 f.run(&[b"ZSCORE", b"z", b"m"]),
6393 format!("${}\r\n{want}\r\n", want.len()),
6394 "score {score}"
6395 );
6396 }
6397
6398 let mut g = Fixture::new();
6401 g.run(&[b"HELLO", b"3"]);
6402 g.run(&[b"ZADD", b"z", b"1e30", b"m"]);
6403 assert_eq!(g.run(&[b"ZSCORE", b"z", b"m"]), ",1e+30\r\n");
6404 assert_eq!(
6409 g.run(&[b"INCRBYFLOAT", b"s", b"1e30"]),
6410 "$31\r\n1000000000000000000000000000000\r\n"
6411 );
6412 assert_eq!(g.run(&[b"INCRBYFLOAT", b"t", b"0.1"]), "$3\r\n0.1\r\n");
6413 assert_eq!(
6414 g.run(&[b"HINCRBYFLOAT", b"h", b"f", b"1e19"]),
6415 "$20\r\n10000000000000000000\r\n"
6416 );
6417 }
6418}