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