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
98#[derive(Debug, Clone, Copy, PartialEq, Eq)]
100pub enum Flow {
101 Continue,
103 Close,
105 Block,
112}
113
114#[derive(Debug, Clone, Copy, Default)]
120pub struct Stats {
121 pub clients: u64,
123 pub connections: u64,
125 pub commands: u64,
127}
128
129pub struct Server {
136 dbs: Vec<Keyspace>,
137 clock: Clock,
138 started_ms: u64,
139 next_db: usize,
142 dirty: u64,
152 conn_bytes: usize,
154 waiters: Waiters,
156 pub stats: Stats,
158}
159
160impl Server {
161 #[must_use]
163 pub fn new() -> Server {
164 let clock = Clock::system();
165 Server {
166 dbs: (0..DATABASES)
167 .map(|_| Keyspace::with_clock(clock))
168 .collect(),
169 clock,
170 started_ms: clock.now_ms(),
171 next_db: 0,
172 dirty: ALL_DATABASES,
173 conn_bytes: 0,
174 waiters: Waiters::default(),
175 stats: Stats::default(),
176 }
177 }
178
179 #[must_use]
181 pub fn with_clock(clock: Clock) -> Server {
182 Server {
183 dbs: (0..DATABASES)
184 .map(|_| Keyspace::with_clock(clock))
185 .collect(),
186 clock,
187 started_ms: clock.now_ms(),
188 next_db: 0,
189 dirty: ALL_DATABASES,
190 conn_bytes: 0,
191 waiters: Waiters::default(),
192 stats: Stats::default(),
193 }
194 }
195
196 pub fn db(&mut self, i: usize) -> &mut Keyspace {
204 self.dirty |= 1u64 << i;
207 &mut self.dbs[i]
208 }
209
210 #[must_use]
221 pub fn db_ref(&self, i: usize) -> &Keyspace {
222 &self.dbs[i]
223 }
224
225 pub fn refresh_clock(&mut self) {
231 self.clock.refresh();
232 let now = self.clock.now_ms();
233 for db in &mut self.dbs {
234 db.clock_mut().set(now);
235 }
236 }
237
238 pub fn set_clock_ms(&mut self, ms: u64) {
246 self.clock.set(ms);
247 for db in &mut self.dbs {
248 db.clock_mut().set(ms);
249 }
250 }
251
252 #[must_use]
254 pub fn uptime_secs(&self) -> u64 {
255 self.clock.now_ms().saturating_sub(self.started_ms) / 1000
256 }
257
258 #[must_use]
266 pub fn memory_bytes(&self) -> usize {
267 self.dbs.iter().map(Keyspace::memory_bytes).sum::<usize>() + self.conn_bytes
268 }
269
270 #[must_use]
276 pub fn dataset_bytes(&self) -> usize {
277 self.dbs
278 .iter()
279 .map(|db| db.map().arena().live_bytes() as usize)
280 .sum()
281 }
282
283 #[must_use]
285 pub fn arena_bytes(&self) -> usize {
286 self.dbs
287 .iter()
288 .map(|db| db.map().arena().reserved_bytes() as usize)
289 .sum()
290 }
291
292 #[must_use]
294 pub fn index_bytes(&self) -> usize {
295 self.dbs
296 .iter()
297 .map(|db| db.map().index().memory_bytes())
298 .sum()
299 }
300
301 #[must_use]
303 pub fn segment_count(&self) -> usize {
304 self.dbs
305 .iter()
306 .map(|db| db.map().arena().resident_segments())
307 .sum()
308 }
309
310 #[must_use]
312 pub const fn conn_bytes(&self) -> usize {
313 self.conn_bytes
314 }
315
316 pub fn note_conn_bytes(&mut self, delta: isize) {
324 self.conn_bytes = self.conn_bytes.saturating_add_signed(delta);
325 }
326
327 #[must_use]
329 pub fn expired_keys(&self) -> u64 {
330 self.dbs.iter().map(Keyspace::expired_keys).sum()
331 }
332
333 pub fn compact_step(&mut self) -> Option<usize> {
346 for turn in 0..self.dbs.len() {
347 let i = (self.next_db + turn) % self.dbs.len();
348 if self.dirty & (1 << i) == 0 {
352 continue;
353 }
354 if let Some(moved) = self.dbs[i].compact_step() {
355 self.next_db = (i + 1) % self.dbs.len();
356 return Some(moved);
357 }
358 self.dirty &= !(1u64 << i);
359 }
360 None
361 }
362}
363
364impl Default for Server {
365 fn default() -> Server {
366 Server::new()
367 }
368}
369
370pub struct Session {
372 db: usize,
373 id: u64,
374 name: Vec<u8>,
375}
376
377impl Session {
378 #[must_use]
380 pub fn new(id: u64) -> Session {
381 Session {
382 db: 0,
383 id,
384 name: Vec::new(),
385 }
386 }
387
388 #[must_use]
390 pub const fn id(&self) -> u64 {
391 self.id
392 }
393
394 #[must_use]
396 pub const fn db(&self) -> usize {
397 self.db
398 }
399
400 #[must_use]
402 pub fn name(&self) -> &[u8] {
403 &self.name
404 }
405
406 pub fn reset(&mut self) {
411 self.db = 0;
412 self.name.clear();
413 }
414
415 fn set_name(&mut self, name: &[u8]) {
417 yo_alloc::allow(|| {
418 self.name.clear();
419 self.name.extend_from_slice(name);
420 });
421 }
422}
423
424pub fn execute(server: &mut Server, session: &mut Session, args: Args<'_>, out: &mut Out) -> Flow {
429 if args.is_empty() {
432 return Flow::Continue;
433 }
434 server.stats.commands += 1;
435
436 let Some(spec) = lookup(args.name()) else {
437 write_error(out, &args::unknown_command(args));
438 return Flow::Continue;
439 };
440 if !arity_ok(spec, args.len()) {
441 write_error(out, &args::wrong_arity(spec.name));
442 return Flow::Continue;
443 }
444
445 server.dirty |= match spec.group {
452 "string" | "set" | "hash" | "list" | "zset" | "array" => 1u64 << session.db,
453 _ => ALL_DATABASES,
454 };
455
456 let mark = out.len();
457 let done = if spec.flags.contains(&"blocking") {
464 blocking::execute(server, session, spec, args, out)
465 } else {
466 match spec.group {
467 "string" => {
468 let db = session.db;
469 strings::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
470 }
471 "set" => {
472 let db = session.db;
473 sets::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
474 }
475 "hash" => {
476 let db = session.db;
477 hashes::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
478 }
479 "list" => {
480 let db = session.db;
481 lists::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
482 }
483 "zset" => {
484 let db = session.db;
485 zsets::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
486 }
487 "array" => {
488 let db = session.db;
489 arrays::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
490 }
491 "keyspace" => keyspace::execute(&mut server.dbs, session.db, spec, args, out)
494 .map(|()| Flow::Continue),
495 "scripting" => scripting::execute(spec, args, out).map(|()| Flow::Continue),
496 _ => server::execute(server, session, spec, args, out),
497 }
498 };
499 match done {
500 Ok(flow) => flow,
501 Err(e) => {
502 out.truncate(mark);
503 write_error(out, &e);
504 Flow::Continue
505 }
506 }
507}
508
509fn write_error(out: &mut Out, e: &Error) {
516 let prefix: &[u8] = match e.code() {
517 Code::WrongType => b"WRONGTYPE ",
518 _ => b"ERR ",
519 };
520 out.error_line(prefix, e.message().as_bytes());
521}
522
523#[cfg(test)]
524mod tests {
525 use super::*;
526 use crate::proto::{Limits, Proto};
527 use crate::request::Argv;
528
529 pub(crate) fn encode(parts: &[&[u8]]) -> Vec<u8> {
534 let mut wire = format!("*{}\r\n", parts.len()).into_bytes();
535 for p in parts {
536 wire.extend_from_slice(format!("${}\r\n", p.len()).as_bytes());
537 wire.extend_from_slice(p);
538 wire.extend_from_slice(b"\r\n");
539 }
540 wire
541 }
542
543 struct Fixture {
545 server: Server,
546 session: Session,
547 argv: Argv,
548 out: Out,
549 }
550
551 impl Fixture {
552 fn new() -> Fixture {
553 Fixture {
554 server: Server::new(),
555 session: Session::new(7),
556 argv: Argv::new(),
557 out: Out::new(Proto::Resp2),
558 }
559 }
560
561 fn run(&mut self, parts: &[&[u8]]) -> String {
563 self.flow(parts).1
564 }
565
566 fn flow(&mut self, parts: &[&[u8]]) -> (Flow, String) {
568 let wire = encode(parts);
569 self.argv.decode(&wire, &Limits::default()).unwrap();
570 self.out.clear();
571 let flow = execute(
572 &mut self.server,
573 &mut self.session,
574 Args::new(&self.argv, &wire),
575 &mut self.out,
576 );
577 (
578 flow,
579 String::from_utf8_lossy(self.out.as_slice()).into_owned(),
580 )
581 }
582 }
583
584 #[test]
588 fn rewriting_the_same_keys_does_not_grow_the_server() {
589 let mut f = Fixture::new();
590 let val = vec![b'v'; 1024];
591 let keys: Vec<Vec<u8>> = (0..64).map(|i| format!("key:{i}").into_bytes()).collect();
592
593 for k in &keys {
594 f.run(&[b"SET", k, &val]);
595 }
596 f.server.compact_step();
597 let after_first = f.server.memory_bytes();
598
599 for _ in 0..500 {
604 for k in &keys {
605 f.run(&[b"SET", k, &val]);
606 }
607 f.server.compact_step();
608 }
609
610 assert!(
611 f.server.memory_bytes() <= after_first * 2,
612 "held {} after five hundred passes against {after_first} after one",
613 f.server.memory_bytes()
614 );
615 assert_eq!(f.run(&[b"DBSIZE"]), format!(":{}\r\n", keys.len()));
616 assert_eq!(f.run(&[b"STRLEN", b"key:7"]), ":1024\r\n");
617 }
618
619 #[test]
633 fn a_database_nobody_started_on_is_still_collected() {
634 let mut f = Fixture::new();
635 assert_eq!(f.run(&[b"SELECT", b"9"]), "+OK\r\n");
636 let val = vec![b'v'; 1024];
637 let keys: Vec<Vec<u8>> = (0..64).map(|i| format!("key:{i}").into_bytes()).collect();
638
639 for k in &keys {
640 f.run(&[b"SET", k, &val]);
641 }
642 while f.server.compact_step().is_some() {}
643 assert_eq!(
644 f.server.dirty & (1 << 9),
645 0,
646 "database nine was drained and should not be asked again until it is written to"
647 );
648 let after_first = f.server.memory_bytes();
649
650 for _ in 0..500 {
651 for k in &keys {
652 f.run(&[b"SET", k, &val]);
653 }
654 f.server.compact_step();
655 }
656
657 assert!(
658 f.server.memory_bytes() <= after_first * 2,
659 "held {} after five hundred passes against {after_first} after one",
660 f.server.memory_bytes()
661 );
662 assert_eq!(f.run(&[b"DBSIZE"]), format!(":{}\r\n", keys.len()));
663 assert_eq!(f.run(&[b"STRLEN", b"key:7"]), ":1024\r\n");
664 f.run(&[b"SELECT", b"0"]);
666 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
667 }
668
669 #[test]
670 fn a_command_goes_from_bytes_to_bytes() {
671 let mut f = Fixture::new();
672 assert_eq!(f.run(&[b"SET", b"k", b"v"]), "+OK\r\n");
673 assert_eq!(f.run(&[b"GET", b"k"]), "$1\r\nv\r\n");
674 assert_eq!(f.run(&[b"GET", b"nosuch"]), "$-1\r\n");
675 assert_eq!(f.run(&[b"STRLEN", b"k"]), ":1\r\n");
676 assert_eq!(f.run(&[b"set", b"k", b"v2", b"xx"]), "+OK\r\n");
678 assert_eq!(f.run(&[b"GET", b"k"]), "$2\r\nv2\r\n");
679 }
680
681 #[test]
682 fn deleting_counts_keys_removed_and_existing_counts_arguments_matched() {
683 let mut f = Fixture::new();
684 f.run(&[b"MSET", b"a", b"1", b"b", b"2", b"c", b"3"]);
685 assert_eq!(f.run(&[b"EXISTS", b"a", b"a", b"nosuch"]), ":2\r\n");
688 assert_eq!(f.run(&[b"DEL", b"a", b"a", b"nosuch"]), ":1\r\n");
689 assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
690 assert_eq!(f.run(&[b"UNLINK", b"b", b"c"]), ":2\r\n");
692 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
693 }
694
695 #[test]
696 fn type_is_a_simple_string_and_says_none_for_a_key_that_is_not_there() {
697 let mut f = Fixture::new();
698 f.run(&[b"SET", b"k", b"v"]);
699 assert_eq!(f.run(&[b"TYPE", b"k"]), "+string\r\n");
702 assert_eq!(f.run(&[b"TYPE", b"nosuch"]), "+none\r\n");
703 }
704
705 #[test]
706 fn touch_counts_the_way_exists_counts() {
707 let mut f = Fixture::new();
708 f.run(&[b"MSET", b"a", b"1", b"b", b"2"]);
709 assert_eq!(f.run(&[b"TOUCH", b"a", b"b"]), ":2\r\n");
710 assert_eq!(
711 f.run(&[b"TOUCH", b"a", b"a"]),
712 ":2\r\n",
713 "twice counts twice"
714 );
715 assert_eq!(f.run(&[b"TOUCH", b"a", b"nosuch"]), ":1\r\n");
716 assert_eq!(f.run(&[b"TOUCH", b"nosuch"]), ":0\r\n");
717 }
718
719 #[test]
720 fn a_rename_moves_the_deadline_with_the_value_and_drops_the_one_it_lands_on() {
721 let mut f = Fixture::new();
722 f.run(&[b"SET", b"a", b"v1", b"EX", b"100"]);
723 f.run(&[b"SET", b"b", b"v2", b"EX", b"500"]);
724
725 assert_eq!(f.run(&[b"RENAME", b"a", b"b"]), "+OK\r\n");
726 assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv1\r\n");
727 assert_eq!(
728 f.run(&[b"TTL", b"b"]),
729 ":100\r\n",
730 "the source's and not b's"
731 );
732 assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
733 }
734
735 #[test]
736 fn a_rename_with_no_source_is_an_error_and_not_a_zero() {
737 let mut f = Fixture::new();
738 assert_eq!(f.run(&[b"RENAME", b"a", b"b"]), "-ERR no such key\r\n");
739 assert_eq!(f.run(&[b"RENAMENX", b"a", b"a"]), "-ERR no such key\r\n");
742 }
743
744 #[test]
745 fn renamenx_refuses_a_taken_name_including_the_one_it_already_has() {
746 let mut f = Fixture::new();
747 f.run(&[b"MSET", b"a", b"v1", b"b", b"v2"]);
748
749 assert_eq!(f.run(&[b"RENAMENX", b"a", b"b"]), ":0\r\n");
750 assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv2\r\n");
751 assert_eq!(f.run(&[b"RENAMENX", b"a", b"a"]), ":0\r\n");
754 assert_eq!(f.run(&[b"RENAME", b"a", b"a"]), "+OK\r\n");
755 assert_eq!(f.run(&[b"RENAMENX", b"a", b"c"]), ":1\r\n");
756 assert_eq!(f.run(&[b"GET", b"c"]), "$2\r\nv1\r\n");
757 }
758
759 #[test]
760 fn renaming_a_set_does_not_touch_a_member() {
761 let mut f = Fixture::new();
762 for i in 0..300 {
763 f.run(&[b"SADD", b"s", format!("m{i}").as_bytes()]);
764 }
765 let before = f.server.memory_bytes();
766
767 assert_eq!(f.run(&[b"RENAME", b"s", b"t"]), "+OK\r\n");
768 assert_eq!(f.run(&[b"SCARD", b"t"]), ":300\r\n");
769 assert_eq!(f.run(&[b"TYPE", b"t"]), "+set\r\n");
770 assert!(
771 f.server.memory_bytes().abs_diff(before) < 256,
772 "the members were copied: {} against {before}",
773 f.server.memory_bytes()
774 );
775 }
776
777 #[test]
778 fn a_copy_is_a_second_value_and_not_a_second_name() {
779 let mut f = Fixture::new();
780 f.run(&[b"SADD", b"s", b"m1", b"m2"]);
781
782 assert_eq!(f.run(&[b"COPY", b"s", b"t"]), ":1\r\n");
783 f.run(&[b"SADD", b"t", b"m3"]);
784 assert_eq!(f.run(&[b"SCARD", b"s"]), ":2\r\n", "the original is intact");
785 assert_eq!(f.run(&[b"SCARD", b"t"]), ":3\r\n");
786 }
787
788 #[test]
798 fn every_type_can_be_copied() {
799 let mut f = Fixture::new();
800 f.run(&[b"SET", b"str", b"v1"]);
801 f.run(&[b"SADD", b"set", b"m1"]);
802 f.run(&[b"HSET", b"hash", b"f", b"v"]);
803 f.run(&[b"RPUSH", b"list", b"a", b"b"]);
804 f.run(&[b"ZADD", b"zset", b"1", b"m1"]);
805
806 for name in [
807 &b"str"[..],
808 &b"set"[..],
809 &b"hash"[..],
810 &b"list"[..],
811 &b"zset"[..],
812 ] {
813 let dst = [name, b":copy"].concat();
814 assert_eq!(
815 f.run(&[b"COPY", name, &dst]),
816 ":1\r\n",
817 "copying {}",
818 String::from_utf8_lossy(name)
819 );
820 assert_eq!(f.run(&[b"TYPE", name]), f.run(&[b"TYPE", &dst]));
821 }
822
823 assert_eq!(f.run(&[b"LRANGE", b"list:copy", b"0", b"-1"]), {
824 let mut want = String::from("*2\r\n");
825 want.push_str("$1\r\na\r\n$1\r\nb\r\n");
826 want
827 });
828 assert_eq!(f.run(&[b"ZSCORE", b"zset:copy", b"m1"]), "$1\r\n1\r\n");
829
830 f.run(&[b"RPUSH", b"list:copy", b"c"]);
832 assert_eq!(f.run(&[b"LLEN", b"list"]), ":2\r\n");
833 assert_eq!(f.run(&[b"LLEN", b"list:copy"]), ":3\r\n");
834 }
835
836 #[test]
837 fn a_copy_refuses_a_taken_destination_until_it_is_told_it_can_have_it() {
838 let mut f = Fixture::new();
839 f.run(&[b"SET", b"a", b"v1", b"EX", b"100"]);
840 f.run(&[b"SET", b"b", b"v2"]);
841
842 assert_eq!(f.run(&[b"COPY", b"a", b"b"]), ":0\r\n");
843 assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv2\r\n");
844 assert_eq!(f.run(&[b"COPY", b"a", b"b", b"REPLACE"]), ":1\r\n");
845 assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv1\r\n");
846 assert_eq!(f.run(&[b"TTL", b"b"]), ":100\r\n", "the deadline came too");
847 assert_eq!(f.run(&[b"COPY", b"nosuch", b"z"]), ":0\r\n");
848 }
849
850 #[test]
851 fn a_copy_into_another_database_is_a_copy_and_onto_itself_there_is_too() {
852 let mut f = Fixture::new();
853 f.run(&[b"SET", b"a", b"v1"]);
854
855 assert_eq!(f.run(&[b"COPY", b"a", b"a", b"DB", b"1"]), ":1\r\n");
858 f.run(&[b"SELECT", b"1"]);
859 assert_eq!(f.run(&[b"GET", b"a"]), "$2\r\nv1\r\n");
860 assert_eq!(
861 f.run(&[b"COPY", b"a", b"a", b"DB", b"0"]),
862 ":0\r\n",
863 "taken"
864 );
865 assert_eq!(
866 f.run(&[b"COPY", b"a", b"a", b"DB", b"0", b"REPLACE"]),
867 ":1\r\n"
868 );
869 }
870
871 #[test]
872 fn copy_checks_its_options_before_it_looks_for_anything() {
873 let mut f = Fixture::new();
874 assert_eq!(
877 f.run(&[b"COPY", b"a", b"b", b"DB", b"99"]),
878 "-ERR DB index is out of range\r\n"
879 );
880 assert_eq!(
881 f.run(&[b"COPY", b"a", b"b", b"DB", b"-1"]),
882 "-ERR DB index is out of range\r\n"
883 );
884 assert_eq!(
885 f.run(&[b"COPY", b"a", b"b", b"DB", b"x"]),
886 "-ERR value is not an integer or out of range\r\n"
887 );
888 assert_eq!(
889 f.run(&[b"COPY", b"a", b"b", b"nonsense"]),
890 "-ERR syntax error\r\n"
891 );
892 assert_eq!(
893 f.run(&[b"COPY", b"a", b"a"]),
894 "-ERR source and destination objects are the same\r\n"
895 );
896 assert_eq!(
898 f.run(&[b"COPY", b"a", b"b", b"dB", b"1", b"rEpLaCe", b"db", b"2"]),
899 ":0\r\n"
900 );
901 }
902
903 #[test]
904 fn time_is_two_bulk_strings_and_moves() {
905 let mut f = Fixture::new();
906 let first = f.run(&[b"TIME"]);
907 assert!(first.starts_with("*2\r\n$"), "got {first}");
908 let parts: Vec<&str> = first.split("\r\n").collect();
909 let secs: i64 = parts[2].parse().expect("seconds as decimal text");
910 let micros: i64 = parts[4].parse().expect("microseconds as decimal text");
911 assert!(secs > 1_700_000_000, "a real wall clock, got {secs}");
912 assert!((0..1_000_000).contains(µs), "got {micros}");
913 assert_ne!(first, f.run(&[b"TIME"]));
917 }
918
919 #[test]
920 fn a_keyspace_scan_walks_every_key_once() {
921 let mut f = Fixture::new();
922 for i in 0..500 {
923 f.run(&[b"SET", format!("k{i}").as_bytes(), b"v"]);
924 }
925
926 let mut seen: Vec<String> = Vec::new();
927 let mut cursor = "0".to_owned();
928 let mut calls = 0;
929 loop {
930 let (next, keys) = scan_reply(&f.run(&[b"SCAN", cursor.as_bytes(), b"COUNT", b"32"]));
931 seen.extend(keys);
932 cursor = next;
933 calls += 1;
934 assert!(calls < 10_000, "the cursor is not advancing");
935 if cursor == "0" {
936 break;
937 }
938 }
939
940 seen.sort();
941 seen.dedup();
942 assert_eq!(seen.len(), 500, "every key once and only once");
943 assert!(calls > 1, "500 keys came back in one batch");
946 }
947
948 #[test]
949 fn a_scan_narrows_by_pattern_and_by_type() {
950 let mut f = Fixture::new();
951 f.run(&[b"SET", b"str", b"v"]);
952 f.run(&[b"SADD", b"members", b"a"]);
953 f.run(&[b"HSET", b"fields", b"f", b"v"]);
954
955 let all = |f: &mut Fixture, args: &[&[u8]]| {
956 let mut out: Vec<String> = Vec::new();
957 let mut cursor = "0".to_owned();
958 loop {
959 let mut line: Vec<&[u8]> = vec![b"SCAN", cursor.as_bytes()];
960 line.extend_from_slice(args);
961 let (next, keys) = scan_reply(&f.run(&line));
962 out.extend(keys);
963 cursor = next;
964 if cursor == "0" {
965 break;
966 }
967 }
968 out.sort();
969 out
970 };
971
972 assert_eq!(all(&mut f, &[]), ["fields", "members", "str"]);
973 assert_eq!(all(&mut f, &[b"MATCH", b"*e*"]), ["fields", "members"]);
974 assert_eq!(all(&mut f, &[b"TYPE", b"set"]), ["members"]);
975 assert_eq!(all(&mut f, &[b"TYPE", b"HASH"]), ["fields"]);
977 assert!(all(&mut f, &[b"TYPE", b"list"]).is_empty());
979 assert!(all(&mut f, &[b"TYPE", b"banana"]).is_empty());
980 assert!(all(&mut f, &[b"MATCH", b"str*", b"TYPE", b"set"]).is_empty());
982 }
983
984 #[test]
985 fn a_scan_says_what_is_wrong_with_it() {
986 let mut f = Fixture::new();
987 assert_eq!(f.run(&[b"SCAN", b"nope"]), "-ERR invalid cursor\r\n");
988 assert_eq!(f.run(&[b"SCAN", b"-1"]), "-ERR invalid cursor\r\n");
989 assert_eq!(f.run(&[b"SCAN", b"0", b"MATCH"]), "-ERR syntax error\r\n");
990 assert_eq!(
991 f.run(&[b"SCAN", b"0", b"COUNT", b"0"]),
992 "-ERR syntax error\r\n"
993 );
994 assert_eq!(
995 f.run(&[b"SCAN", b"0", b"COUNT", b"x"]),
996 "-ERR value is not an integer or out of range\r\n"
997 );
998 assert_eq!(
999 f.run(&[b"SCAN", b"0", b"WAT", b"1"]),
1000 "-ERR syntax error\r\n"
1001 );
1002 assert!(f.run(&[b"SCAN", b"18446744073709551615"]).starts_with("*2"));
1007 }
1008
1009 #[test]
1010 fn keys_and_randomkey_look_at_the_whole_database() {
1011 let mut f = Fixture::new();
1012 assert_eq!(f.run(&[b"KEYS", b"*"]), "*0\r\n");
1013 assert_eq!(f.run(&[b"RANDOMKEY"]), "$-1\r\n");
1014
1015 for name in ["one", "two", "three"] {
1016 f.run(&[b"SET", name.as_bytes(), b"v"]);
1017 }
1018 assert_eq!(sorted(&f.run(&[b"KEYS", b"*"])), ["one", "three", "two"]);
1019 assert_eq!(sorted(&f.run(&[b"KEYS", b"t*"])), ["three", "two"]);
1020 assert_eq!(f.run(&[b"KEYS", b"nothing"]), "*0\r\n");
1021
1022 for _ in 0..50 {
1023 let got = f.run(&[b"RANDOMKEY"]);
1024 assert!(
1025 ["$3\r\none\r\n", "$3\r\ntwo\r\n", "$5\r\nthree\r\n"].contains(&got.as_str()),
1026 "got {got}"
1027 );
1028 }
1029 }
1030
1031 #[test]
1032 fn a_walk_does_not_answer_keys_that_have_expired() {
1033 let mut f = Fixture::new();
1034 f.run(&[b"SET", b"alive", b"v"]);
1035 f.run(&[b"SET", b"dead", b"v", b"PX", b"1"]);
1036 f.server.db(0).clock_mut().advance(2);
1037 assert_eq!(
1038 f.run(&[b"DBSIZE"]),
1039 ":2\r\n",
1040 "nothing has collected it yet"
1041 );
1042
1043 assert_eq!(f.run(&[b"KEYS", b"*"]), "*1\r\n$5\r\nalive\r\n");
1044 let (_, keys) = scan_reply(&f.run(&[b"SCAN", b"0", b"COUNT", b"1000"]));
1045 assert_eq!(keys, ["alive"]);
1046 for _ in 0..20 {
1047 assert_eq!(f.run(&[b"RANDOMKEY"]), "$5\r\nalive\r\n");
1048 }
1049 assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
1052 }
1053
1054 #[test]
1055 fn a_key_deadline_goes_on_and_comes_back_in_all_four_units() {
1056 let mut f = Fixture::new();
1057 f.run(&[b"SET", b"k", b"v"]);
1058 assert_eq!(f.run(&[b"TTL", b"k"]), ":-1\r\n", "there and no deadline");
1059 assert_eq!(f.run(&[b"TTL", b"nosuch"]), ":-2\r\n", "not there at all");
1060
1061 assert_eq!(f.run(&[b"EXPIRE", b"k", b"100"]), ":1\r\n");
1062 assert_eq!(f.run(&[b"TTL", b"k"]), ":100\r\n");
1063 let ms = int(&f.run(&[b"PTTL", b"k"]));
1064 assert!((99_000..=100_000).contains(&ms), "got {ms}");
1065
1066 let at = int(&f.run(&[b"EXPIRETIME", b"k"]));
1068 let at_ms = int(&f.run(&[b"PEXPIRETIME", b"k"]));
1069 assert_eq!(at, (at_ms + 500) / 1000);
1070 assert!(at_ms > 1_700_000_000_000, "an absolute moment, got {at_ms}");
1071
1072 assert_eq!(f.run(&[b"PERSIST", b"k"]), ":1\r\n");
1073 assert_eq!(f.run(&[b"TTL", b"k"]), ":-1\r\n");
1074 assert_eq!(
1075 f.run(&[b"PERSIST", b"k"]),
1076 ":0\r\n",
1077 "nothing to take off the second time"
1078 );
1079 assert_eq!(f.run(&[b"PERSIST", b"nosuch"]), ":0\r\n");
1080 assert_eq!(
1081 f.run(&[b"GET", b"k"]),
1082 "$1\r\nv\r\n",
1083 "and the value went through all of that untouched"
1084 );
1085 }
1086
1087 #[test]
1088 fn every_type_can_be_given_a_deadline_and_it_is_the_same_deadline() {
1089 let mut f = Fixture::new();
1090 f.run(&[b"SET", b"str", b"v"]);
1091 f.run(&[b"SADD", b"set", b"a", b"b"]);
1092 f.run(&[b"HSET", b"hash", b"f", b"v"]);
1093
1094 for key in [b"str".as_slice(), b"set", b"hash"] {
1095 assert_eq!(f.run(&[b"EXPIRE", key, b"100"]), ":1\r\n");
1096 assert_eq!(f.run(&[b"TTL", key]), ":100\r\n");
1097 }
1098 assert_eq!(f.run(&[b"SCARD", b"set"]), ":2\r\n");
1101 assert_eq!(f.run(&[b"HGET", b"hash", b"f"]), "$1\r\nv\r\n");
1102 assert_eq!(f.run(&[b"GET", b"str"]), "$1\r\nv\r\n");
1103 }
1104
1105 #[test]
1106 fn a_deadline_that_has_already_gone_deletes_the_key_now() {
1107 let mut f = Fixture::new();
1108 for key in [b"a".as_slice(), b"b", b"c", b"d"] {
1109 f.run(&[b"SET", key, b"v"]);
1110 }
1111 assert_eq!(f.run(&[b"EXPIRE", b"a", b"0"]), ":1\r\n");
1115 assert_eq!(f.run(&[b"EXPIRE", b"b", b"-1"]), ":1\r\n");
1116 assert_eq!(f.run(&[b"EXPIREAT", b"c", b"1"]), ":1\r\n");
1117 assert_eq!(f.run(&[b"PEXPIREAT", b"d", b"1"]), ":1\r\n");
1118 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
1119 assert_eq!(
1120 f.run(&[b"EXPIRE", b"a", b"100"]),
1121 ":0\r\n",
1122 "and the key really went, so there is nothing to put a deadline on"
1123 );
1124 }
1125
1126 #[test]
1127 fn the_four_conditions_decide_whether_the_deadline_moves() {
1128 let mut f = Fixture::new();
1129 f.run(&[b"SET", b"k", b"v"]);
1130
1131 assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"XX"]), ":0\r\n");
1132 assert_eq!(f.run(&[b"TTL", b"k"]), ":-1\r\n", "and XX left it alone");
1133 assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"GT"]), ":0\r\n");
1134 assert_eq!(
1135 f.run(&[b"EXPIRE", b"k", b"100", b"LT"]),
1136 ":1\r\n",
1137 "no deadline reads as infinitely far away, so LT passes where GT fails"
1138 );
1139
1140 assert_eq!(f.run(&[b"EXPIRE", b"k", b"50", b"NX"]), ":0\r\n");
1141 assert_eq!(f.run(&[b"EXPIRE", b"k", b"50", b"GT"]), ":0\r\n");
1142 assert_eq!(f.run(&[b"TTL", b"k"]), ":100\r\n");
1143 assert_eq!(f.run(&[b"EXPIRE", b"k", b"50", b"LT"]), ":1\r\n");
1144 assert_eq!(f.run(&[b"EXPIRE", b"k", b"200", b"GT"]), ":1\r\n");
1145 assert_eq!(f.run(&[b"TTL", b"k"]), ":200\r\n");
1146
1147 assert_eq!(f.run(&[b"EXPIRE", b"k", b"0", b"NX"]), ":0\r\n");
1150 assert_eq!(f.run(&[b"EXISTS", b"k"]), ":1\r\n");
1151 assert_eq!(f.run(&[b"EXPIRE", b"k", b"0", b"XX"]), ":1\r\n");
1152 assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n", "and XX let it through");
1153 }
1154
1155 #[test]
1156 fn the_conditions_are_a_set_and_not_a_keyword() {
1157 let mut f = Fixture::new();
1158 f.run(&[b"SET", b"k", b"v"]);
1159
1160 assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"nx"]), ":1\r\n");
1161 assert_eq!(
1162 f.run(&[b"EXPIRE", b"k", b"100", b"nx", b"nx"]),
1163 ":0\r\n",
1164 "the same keyword twice means it once, and NX now has a deadline to fail on"
1165 );
1166
1167 assert_eq!(f.run(&[b"EXPIRE", b"k", b"200", b"xx", b"gt"]), ":1\r\n");
1170 assert_eq!(f.run(&[b"TTL", b"k"]), ":200\r\n");
1171 assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"gt", b"xx"]), ":0\r\n");
1172 assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"XX", b"LT"]), ":1\r\n");
1173 assert_eq!(f.run(&[b"TTL", b"k"]), ":100\r\n");
1174 f.run(&[b"PERSIST", b"k"]);
1175 assert_eq!(
1176 f.run(&[b"EXPIRE", b"k", b"100", b"XX", b"LT"]),
1177 ":0\r\n",
1178 "where LT on its own would have taken it"
1179 );
1180 assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"LT"]), ":1\r\n");
1181 }
1182
1183 #[test]
1184 fn a_key_is_gone_once_its_moment_passes() {
1185 let mut f = Fixture::new();
1186 f.run(&[b"SET", b"k", b"v"]);
1187 f.run(&[b"EXPIRE", b"k", b"100"]);
1188
1189 let at = int(&f.run(&[b"PEXPIRETIME", b"k"]));
1190 f.server.set_clock_ms(at as u64 + 1);
1191 assert_eq!(f.run(&[b"GET", b"k"]), "$-1\r\n");
1192 assert_eq!(f.run(&[b"TTL", b"k"]), ":-2\r\n");
1193 assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
1194 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
1195 }
1196
1197 #[test]
1198 fn the_expiry_commands_refuse_what_a_real_server_refuses() {
1199 let mut f = Fixture::new();
1200 f.run(&[b"SET", b"k", b"v"]);
1201 for (bad, want) in [
1202 (
1203 &[b"EXPIRE".as_slice(), b"k", b"soon"][..],
1204 "-ERR value is not an integer or out of range\r\n",
1205 ),
1206 (
1207 &[b"EXPIRE", b"k", b"100", b"MAYBE"],
1208 "-ERR Unsupported option MAYBE\r\n",
1209 ),
1210 (
1211 &[b"EXPIRE", b"k", b"100", b"NX", b"XX"],
1212 "-ERR NX and XX, GT or LT options at the same time are not compatible\r\n",
1213 ),
1214 (
1215 &[b"EXPIRE", b"k", b"100", b"NX", b"GT"],
1216 "-ERR NX and XX, GT or LT options at the same time are not compatible\r\n",
1217 ),
1218 (
1219 &[b"EXPIRE", b"k", b"100", b"GT", b"LT", b"GT"],
1220 "-ERR GT and LT options at the same time are not compatible\r\n",
1221 ),
1222 (
1225 &[b"EXPIRE", b"k", b"9223372036854775807"],
1226 "-ERR invalid expire time in 'expire' command\r\n",
1227 ),
1228 (
1229 &[b"EXPIREAT", b"k", b"9223372036854775807"],
1230 "-ERR invalid expire time in 'expireat' command\r\n",
1231 ),
1232 (
1233 &[b"PEXPIRE", b"k", b"9223372036854775807"],
1234 "-ERR invalid expire time in 'pexpire' command\r\n",
1235 ),
1236 ] {
1237 assert_eq!(f.run(bad), want, "for {bad:?}");
1238 }
1239 assert_eq!(
1240 f.run(&[b"TTL", b"k"]),
1241 ":-1\r\n",
1242 "and none of those put a deadline on anything"
1243 );
1244
1245 assert_eq!(
1249 f.run(&[b"PEXPIREAT", b"k", b"9223372036854775807"]),
1250 ":1\r\n"
1251 );
1252 assert_eq!(f.run(&[b"PEXPIRETIME", b"k"]), ":70368744177663\r\n");
1253 }
1254
1255 #[test]
1256 fn flushing_empties_this_database_or_every_one_of_them() {
1257 let mut f = Fixture::new();
1258 f.run(&[b"SELECT", b"0"]);
1259 f.run(&[b"MSET", b"a", b"1", b"b", b"2"]);
1260 f.run(&[b"SELECT", b"1"]);
1261 f.run(&[b"SET", b"c", b"3"]);
1262 assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
1263 assert_eq!(f.run(&[b"FLUSHDB", b"async"]), "+OK\r\n");
1266 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
1267 f.run(&[b"SELECT", b"0"]);
1269 assert_eq!(f.run(&[b"DBSIZE"]), ":2\r\n");
1270 assert_eq!(f.run(&[b"FLUSHALL", b"SYNC"]), "+OK\r\n");
1271 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
1272 f.run(&[b"SELECT", b"1"]);
1273 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
1274 assert_eq!(f.run(&[b"FLUSHALL", b"nope"]), "-ERR syntax error\r\n");
1277 assert_eq!(
1278 f.run(&[b"FLUSHDB", b"sync", b"sync"]),
1279 "-ERR syntax error\r\n"
1280 );
1281 }
1282
1283 #[test]
1284 fn the_script_cache_and_the_library_set_answer_for_being_empty() {
1285 let mut f = Fixture::new();
1286 assert_eq!(f.run(&[b"SCRIPT", b"FLUSH"]), "+OK\r\n");
1287 assert_eq!(f.run(&[b"SCRIPT", b"FLUSH", b"async"]), "+OK\r\n");
1288 assert_eq!(f.run(&[b"FUNCTION", b"FLUSH", b"SYNC"]), "+OK\r\n");
1289 assert_eq!(
1292 f.run(&[b"SCRIPT", b"EXISTS", b"aaaa", b"bbbb"]),
1293 "*2\r\n:0\r\n:0\r\n"
1294 );
1295 assert_eq!(f.run(&[b"FUNCTION", b"LIST"]), "*0\r\n");
1296 assert_eq!(
1297 f.run(&[b"FUNCTION", b"LIST", b"LIBRARYNAME", b"x", b"WITHCODE"]),
1298 "*0\r\n"
1299 );
1300 assert_eq!(
1301 f.run(&[b"FUNCTION", b"DELETE", b"nosuch"]),
1302 "-ERR Library not found\r\n"
1303 );
1304
1305 assert_eq!(
1308 f.run(&[b"SCRIPT", b"FLUSH", b"nope"]),
1309 "-ERR SCRIPT FLUSH only support SYNC|ASYNC option\r\n"
1310 );
1311 assert_eq!(
1312 f.run(&[b"FUNCTION", b"FLUSH", b"nope"]),
1313 "-ERR FUNCTION FLUSH only supports SYNC|ASYNC option\r\n"
1314 );
1315 assert_eq!(
1318 f.run(&[b"FUNCTION", b"FLUSH", b"sync", b"sync"]),
1319 "-ERR unknown subcommand or wrong number of arguments for 'flush'. Try FUNCTION HELP.\r\n"
1320 );
1321 assert_eq!(
1322 f.run(&[b"FUNCTION", b"LIST", b"bogus"]),
1323 "-ERR Unknown argument bogus\r\n"
1324 );
1325 assert_eq!(
1326 f.run(&[b"SCRIPT", b"EXISTS"]),
1327 "-ERR wrong number of arguments for 'script|exists' command\r\n"
1328 );
1329
1330 assert_eq!(
1333 f.run(&[b"SCRIPT", b"LOAD", b"return 1"]),
1334 "-ERR unknown subcommand 'LOAD'. Try SCRIPT HELP.\r\n"
1335 );
1336 assert_eq!(
1337 f.run(&[b"FUNCTION", b"STATS"]),
1338 "-ERR unknown subcommand 'STATS'. Try FUNCTION HELP.\r\n"
1339 );
1340 }
1341
1342 #[test]
1343 fn a_counter_is_an_integer_and_not_a_string_of_digits() {
1344 let mut f = Fixture::new();
1345 assert_eq!(f.run(&[b"INCR", b"c"]), ":1\r\n");
1346 assert_eq!(f.run(&[b"INCRBY", b"c", b"41"]), ":42\r\n");
1347 assert_eq!(f.run(&[b"DECRBY", b"c", b"2"]), ":40\r\n");
1348 assert_eq!(f.run(&[b"GET", b"c"]), "$2\r\n40\r\n");
1351 assert_eq!(f.run(&[b"INCRBYFLOAT", b"c", b"0.5"]), "$4\r\n40.5\r\n");
1352 f.run(&[b"SET", b"k", b"hello"]);
1355 assert_eq!(
1356 f.run(&[b"INCR", b"k"]),
1357 "-ERR value is not an integer or out of range\r\n"
1358 );
1359 assert_eq!(
1360 f.run(&[b"INCRBYFLOAT", b"c", b"inf"]),
1361 "-ERR increment would produce NaN or Infinity\r\n"
1362 );
1363 }
1364
1365 #[test]
1370 fn the_newer_commands_reply_in_the_shapes_a_real_server_sends() {
1371 let mut f = Fixture::new();
1372 assert_eq!(f.run(&[b"SET", b"k", b"hello"]), "+OK\r\n");
1373 assert_eq!(f.run(&[b"DIGEST", b"k"]), "$16\r\n9555e8555c62dcfd\r\n");
1376 assert_eq!(f.run(&[b"DIGEST", b"nosuch"]), "$-1\r\n");
1377 assert_eq!(f.run(&[b"MSETEX", b"1", b"a", b"1"]), ":1\r\n");
1378 assert_eq!(f.run(&[b"MSETEX", b"1", b"a", b"2", b"NX"]), ":0\r\n");
1379 assert_eq!(f.run(&[b"GET", b"a"]), "$1\r\n1\r\n");
1380 assert_eq!(f.run(&[b"INCREX", b"n"]), "*2\r\n:1\r\n:1\r\n");
1381 assert_eq!(
1382 f.run(&[b"INCREX", b"n", b"BYINT", b"5", b"UBOUND", b"3"]),
1383 "*2\r\n:1\r\n:0\r\n",
1384 "a refused increment reports the value it left alone and applied nothing"
1385 );
1386 assert_eq!(
1387 f.run(&[
1388 b"INCREX",
1389 b"n",
1390 b"BYINT",
1391 b"5",
1392 b"UBOUND",
1393 b"3",
1394 b"SATURATE"
1395 ]),
1396 "*2\r\n:3\r\n:2\r\n"
1397 );
1398 assert_eq!(f.run(&[b"DELEX", b"a", b"IFEQ", b"2"]), ":0\r\n");
1399 assert_eq!(f.run(&[b"DELEX", b"a", b"IFEQ", b"1"]), ":1\r\n");
1400 }
1401
1402 #[test]
1403 fn the_same_answers_come_out_in_resp3_spelling() {
1404 let mut f = Fixture::new();
1405 assert!(f.run(&[b"HELLO", b"3"]).starts_with("%7\r\n"));
1406 assert_eq!(f.run(&[b"GET", b"nosuch"]), "_\r\n");
1407 assert_eq!(
1410 f.run(&[b"INCREX", b"c", b"BYFLOAT", b"1.5"]),
1411 "*2\r\n,1.5\r\n,1.5\r\n"
1412 );
1413 assert_eq!(f.run(&[b"INCRBYFLOAT", b"f", b"2.5"]), "$3\r\n2.5\r\n");
1414 assert_eq!(f.run(&[b"RESET"]), "+RESET\r\n");
1417 assert_eq!(f.run(&[b"GET", b"nosuch"]), "$-1\r\n");
1418 }
1419
1420 #[test]
1421 fn a_command_nobody_has_heard_of_is_an_error_and_not_a_closed_socket() {
1422 let mut f = Fixture::new();
1423 let (flow, reply) = f.flow(&[b"NOPE", b"a", b"b"]);
1424 assert_eq!(flow, Flow::Continue);
1425 assert_eq!(
1426 reply,
1427 "-ERR unknown command 'NOPE', with args beginning with: 'a' 'b' \r\n"
1428 );
1429 let reply = f.run(&[b"NO\r\n+PONG\r\nPE"]);
1432 assert_eq!(reply.matches("\r\n").count(), 1);
1433 }
1434
1435 #[test]
1436 fn arity_is_checked_before_the_command_is() {
1437 let mut f = Fixture::new();
1438 assert_eq!(
1439 f.run(&[b"GET"]),
1440 "-ERR wrong number of arguments for 'get' command\r\n"
1441 );
1442 assert_eq!(
1443 f.run(&[b"MSET", b"k"]),
1444 "-ERR wrong number of arguments for 'mset' command\r\n"
1445 );
1446 assert_eq!(
1450 f.run(&[b"PING", b"a", b"b"]),
1451 "-ERR wrong number of arguments for 'ping' command\r\n"
1452 );
1453 assert_eq!(f.run(&[b"PING"]), "+PONG\r\n");
1454 assert_eq!(f.run(&[b"PING", b"hi"]), "$2\r\nhi\r\n");
1455 assert_eq!(
1457 f.run(&[b"DELEX", b"k", b"IFEQ"]),
1458 "-ERR wrong number of arguments for 'delex' command\r\n"
1459 );
1460 }
1461
1462 #[test]
1466 fn the_option_combinations_are_the_ones_a_real_server_accepts() {
1467 let mut f = Fixture::new();
1468 let syntax = "-ERR syntax error\r\n";
1469 assert_eq!(f.run(&[b"SET", b"k", b"v", b"NX", b"XX"]), syntax);
1470 assert_eq!(f.run(&[b"SET", b"k", b"v", b"NX", b"IFEQ", b"a"]), syntax);
1471 assert_eq!(
1472 f.run(&[b"SET", b"k", b"v", b"KEEPTTL", b"EX", b"5"]),
1473 syntax
1474 );
1475 assert_eq!(
1476 f.run(&[b"SET", b"k", b"v", b"EX", b"5", b"PX", b"5"]),
1477 syntax
1478 );
1479 assert_eq!(f.run(&[b"SET", b"k", b"v", b"PERSIST"]), syntax);
1480 assert_eq!(
1482 f.run(&[b"SET", b"k", b"v", b"EX", b"5", b"EX", b"100"]),
1483 "+OK\r\n"
1484 );
1485 assert_eq!(f.run(&[b"SET", b"k", b"v", b"XX", b"XX"]), "+OK\r\n");
1486 assert_eq!(f.run(&[b"SET", b"k", b"v", b"GET", b"GET"]), "$1\r\nv\r\n");
1487 assert_eq!(
1489 f.run(&[b"INCREX", b"n", b"BYINT", b"1", b"BYINT", b"2"]),
1490 syntax
1491 );
1492 assert_eq!(
1493 f.run(&[b"INCREX", b"n", b"ENX"]),
1494 "-ERR ENX flag requires an expiration\r\n"
1495 );
1496 assert_eq!(
1497 f.run(&[b"INCREX", b"n", b"UBOUND", b"abc"]),
1498 "-ERR UBOUND is not an integer or out of range\r\n"
1499 );
1500 assert_eq!(
1501 f.run(&[b"INCREX", b"n", b"LBOUND", b"10", b"UBOUND", b"5"]),
1502 "-ERR LBOUND can't be greater than UBOUND\r\n"
1503 );
1504 assert_eq!(
1505 f.run(&[b"LCS", b"a", b"b", b"LEN", b"IDX"]),
1506 "-ERR If you want both the length and indexes, please just use IDX.\r\n"
1507 );
1508 }
1509
1510 #[test]
1514 fn the_expiry_rules_are_redis_own() {
1515 let mut f = Fixture::new();
1516 let bad = "-ERR invalid expire time in 'set' command\r\n";
1517 assert_eq!(f.run(&[b"SET", b"k", b"v", b"EX", b"0"]), bad);
1518 assert_eq!(f.run(&[b"SET", b"k", b"v", b"EX", b"-1"]), bad);
1519 assert_eq!(f.run(&[b"SET", b"k", b"v", b"EXAT", b"0"]), bad);
1520 assert_eq!(
1521 f.run(&[b"SET", b"k", b"v", b"EX", b"9999999999999999"]),
1522 bad
1523 );
1524 assert_eq!(
1525 f.run(&[b"SET", b"k", b"v", b"PX", b"99999999999999999999"]),
1526 "-ERR value is not an integer or out of range\r\n"
1527 );
1528 assert_eq!(
1529 f.run(&[b"SETEX", b"k", b"0", b"v"]),
1530 "-ERR invalid expire time in 'setex' command\r\n"
1531 );
1532 assert_eq!(f.run(&[b"GETEX", b"nosuch", b"EX", b"0"]), "$-1\r\n");
1533 assert_eq!(f.run(&[b"GETEX", b"nosuch", b"EX", b"abc"]), "$-1\r\n");
1534 assert_eq!(
1535 f.run(&[b"GETEX", b"nosuch", b"KEEPTTL"]),
1536 "-ERR syntax error\r\n",
1537 "the option list is still checked before the key is looked up"
1538 );
1539 assert_eq!(f.run(&[b"SET", b"k", b"v"]), "+OK\r\n");
1541 assert_eq!(f.run(&[b"SET", b"k", b"v", b"EXAT", b"1"]), "+OK\r\n");
1542 assert_eq!(f.run(&[b"GET", b"k"]), "$-1\r\n");
1543 }
1544
1545 #[test]
1546 fn mset_takes_its_pairs_from_the_read_buffer() {
1547 let mut f = Fixture::new();
1548 assert_eq!(f.run(&[b"MSET", b"a", b"1", b"b", b"2"]), "+OK\r\n");
1549 assert_eq!(
1550 f.run(&[b"MGET", b"a", b"b", b"nosuch"]),
1551 "*3\r\n$1\r\n1\r\n$1\r\n2\r\n$-1\r\n"
1552 );
1553 assert_eq!(f.run(&[b"MSETNX", b"b", b"9", b"c", b"3"]), ":0\r\n");
1554 assert_eq!(f.run(&[b"MSETNX", b"c", b"3", b"d", b"4"]), ":1\r\n");
1555 assert_eq!(
1556 f.run(&[b"MSETEX", b"2", b"e", b"5"]),
1557 "-ERR wrong number of key-value pairs\r\n"
1558 );
1559 assert_eq!(
1560 f.run(&[b"MSETEX", b"0", b"e", b"5"]),
1561 "-ERR invalid numkeys value\r\n"
1562 );
1563 assert_eq!(
1564 f.run(&[b"MSETEX", b"abc", b"e", b"5"]),
1565 "-ERR invalid numkeys value\r\n"
1566 );
1567 }
1568
1569 #[test]
1570 fn lcs_answers_the_length_the_string_and_the_runs() {
1571 let mut f = Fixture::new();
1572 f.run(&[b"MSET", b"a", b"ohmytext", b"b", b"mynewtext"]);
1573 assert_eq!(f.run(&[b"LCS", b"a", b"b"]), "$6\r\nmytext\r\n");
1574 assert_eq!(f.run(&[b"LCS", b"a", b"b", b"LEN"]), ":6\r\n");
1575 assert_eq!(
1576 f.run(&[b"LCS", b"a", b"b", b"IDX", b"MINMATCHLEN", b"4"]),
1577 "*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"
1578 );
1579 assert_eq!(
1582 f.run(&[b"LCS", b"a", b"b", b"MINMATCHLEN", b"4", b"WITHMATCHLEN"]),
1583 "$6\r\nmytext\r\n"
1584 );
1585 }
1586
1587 #[test]
1588 fn select_moves_the_connection_and_the_databases_stay_apart() {
1589 let mut f = Fixture::new();
1590 f.run(&[b"SET", b"k", b"zero"]);
1591 assert_eq!(f.run(&[b"SELECT", b"4"]), "+OK\r\n");
1592 assert_eq!(f.run(&[b"GET", b"k"]), "$-1\r\n");
1593 f.run(&[b"SET", b"k", b"four"]);
1594 assert_eq!(f.run(&[b"SELECT", b"0"]), "+OK\r\n");
1595 assert_eq!(f.run(&[b"GET", b"k"]), "$4\r\nzero\r\n");
1596 assert_eq!(
1597 f.run(&[b"SELECT", b"99"]),
1598 "-ERR DB index is out of range\r\n"
1599 );
1600 assert_eq!(
1601 f.run(&[b"SELECT", b"-1"]),
1602 "-ERR DB index is out of range\r\n"
1603 );
1604 assert_eq!(
1605 f.run(&[b"SELECT", b"abc"]),
1606 "-ERR value is not an integer or out of range\r\n"
1607 );
1608 f.run(&[b"SELECT", b"4"]);
1610 f.run(&[b"RESET"]);
1611 assert_eq!(f.run(&[b"GET", b"k"]), "$4\r\nzero\r\n");
1612 }
1613
1614 #[test]
1615 fn hello_agrees_on_a_protocol_and_refuses_the_ones_that_do_not_exist() {
1616 let mut f = Fixture::new();
1617 let reply = f.run(&[b"HELLO"]);
1618 assert!(reply.starts_with("*14\r\n"), "{reply}");
1619 assert!(reply.contains("$5\r\nredis\r\n"), "{reply}");
1620 assert!(reply.contains("$5\r\n8.8.0\r\n"), "{reply}");
1621 assert!(
1622 reply.contains(":7\r\n"),
1623 "the connection id is in there: {reply}"
1624 );
1625 assert_eq!(
1626 f.run(&[b"HELLO", b"4"]),
1627 "-NOPROTO unsupported protocol version\r\n"
1628 );
1629 assert_eq!(
1630 f.run(&[b"HELLO", b"abc"]),
1631 "-ERR Protocol version is not an integer or out of range\r\n"
1632 );
1633 assert_eq!(
1634 f.run(&[b"HELLO", b"3", b"SETNAME"]),
1635 "-ERR Syntax error in HELLO option 'SETNAME'\r\n"
1636 );
1637 assert!(
1638 f.run(&[b"HELLO", b"3", b"SETNAME", b"bob"])
1639 .starts_with("%7\r\n")
1640 );
1641 assert_eq!(f.session.name(), b"bob");
1642 f.run(&[b"RESET"]);
1643 assert_eq!(f.session.name(), b"");
1644 }
1645
1646 #[test]
1647 fn command_describes_this_server_in_the_shape_a_driver_reads() {
1648 let mut f = Fixture::new();
1649 let count = format!(":{}\r\n", COMMANDS.len());
1650 assert_eq!(f.run(&[b"COMMAND", b"COUNT"]), count);
1651 let info = f.run(&[b"COMMAND", b"INFO", b"get"]);
1652 assert_eq!(
1653 info,
1654 "*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\
1655 *3\r\n+@read\r\n+@string\r\n+@fast\r\n*0\r\n*0\r\n*0\r\n"
1656 );
1657 assert_eq!(f.run(&[b"COMMAND", b"INFO", b"nosuch"]), "*1\r\n$-1\r\n");
1659 assert_eq!(
1660 f.run(&[b"COMMAND", b"LIST", b"FILTERBY", b"PATTERN", b"getr*"]),
1661 "*1\r\n$8\r\ngetrange\r\n"
1662 );
1663 assert_eq!(
1664 f.run(&[b"COMMAND", b"NOPE"]),
1665 "-ERR unknown subcommand 'NOPE'. Try COMMAND HELP.\r\n"
1666 );
1667 }
1668
1669 #[test]
1673 fn command_getkeys_finds_the_keys_including_the_hidden_ones() {
1674 let mut f = Fixture::new();
1675 assert_eq!(
1676 f.run(&[b"COMMAND", b"GETKEYS", b"get", b"k"]),
1677 "*1\r\n$1\r\nk\r\n"
1678 );
1679 assert_eq!(
1680 f.run(&[b"COMMAND", b"GETKEYS", b"mset", b"a", b"1", b"b", b"2"]),
1681 "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
1682 );
1683 assert_eq!(
1684 f.run(&[
1685 b"COMMAND", b"GETKEYS", b"msetex", b"2", b"a", b"1", b"b", b"2"
1686 ]),
1687 "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
1688 );
1689 assert_eq!(
1690 f.run(&[b"COMMAND", b"GETKEYS", b"ping"]),
1691 "-ERR The command has no key arguments\r\n"
1692 );
1693 assert_eq!(
1694 f.run(&[b"COMMAND", b"GETKEYS", b"set"]),
1695 "-ERR Invalid number of arguments specified for command\r\n"
1696 );
1697 }
1698
1699 #[test]
1700 fn config_answers_what_it_can_and_refuses_what_it_cannot() {
1701 let mut f = Fixture::new();
1702 assert_eq!(
1703 f.run(&[b"CONFIG", b"GET", b"maxmemory"]),
1704 "*2\r\n$9\r\nmaxmemory\r\n$1\r\n0\r\n"
1705 );
1706 let both = f.run(&[b"CONFIG", b"GET", b"maxmemory*", b"maxmemory"]);
1709 assert!(both.starts_with("*4\r\n"), "{both}");
1710 assert_eq!(f.run(&[b"CONFIG", b"GET", b"nosuch"]), "*0\r\n");
1711 assert_eq!(f.run(&[b"CONFIG", b"SET", b"appendonly", b"no"]), "+OK\r\n");
1712 assert_eq!(
1713 f.run(&[b"CONFIG", b"SET", b"appendonly", b"yes"]),
1714 "-ERR CONFIG SET failed (possibly related to argument 'appendonly') - can't set immutable config\r\n"
1715 );
1716 assert_eq!(
1717 f.run(&[b"CONFIG", b"SET", b"nosuch", b"1"]),
1718 "-ERR Unknown option or number of arguments for CONFIG SET - 'nosuch'\r\n"
1719 );
1720 assert_eq!(
1721 f.run(&[b"CONFIG", b"GET"]),
1722 "-ERR wrong number of arguments for 'config|get' command\r\n"
1723 );
1724 assert_eq!(
1728 f.run(&[b"CONFIG", b"SET", b"appendonly"]),
1729 "-ERR wrong number of arguments for 'config|set' command\r\n"
1730 );
1731 assert_eq!(
1732 f.run(&[b"CONFIG", b"SET", b"appendonly", b"no", b"maxmemory"]),
1733 "-ERR syntax error\r\n"
1734 );
1735 assert_eq!(f.run(&[b"CONFIG", b"RESETSTAT"]), "+OK\r\n");
1736 assert_eq!(
1737 f.run(&[b"CONFIG", b"REWRITE"]),
1738 "-ERR The server is running without a config file\r\n"
1739 );
1740 }
1741
1742 #[test]
1743 fn object_says_which_rung_of_the_ladder_a_key_is_on() {
1744 let mut f = Fixture::new();
1745 f.run(&[b"SET", b"s", b"hello"]);
1746 f.run(&[b"SET", b"n", b"123"]);
1747 f.run(&[b"SADD", b"si", b"1", b"2", b"3"]);
1748 f.run(&[b"SADD", b"ss", b"a", b"b"]);
1749 f.run(&[b"HSET", b"h", b"f", b"v"]);
1750 for (key, want) in [
1751 (b"s".as_slice(), "embstr"),
1752 (b"n", "int"),
1753 (b"si", "intset"),
1754 (b"ss", "listpack"),
1755 (b"h", "listpack"),
1756 ] {
1757 let reply = f.run(&[b"OBJECT", b"ENCODING", key]);
1758 assert_eq!(reply, format!("${}\r\n{want}\r\n", want.len()));
1759 }
1760
1761 f.run(&[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"f"]);
1764 assert_eq!(
1765 f.run(&[b"OBJECT", b"ENCODING", b"h"]),
1766 "$10\r\nlistpackex\r\n"
1767 );
1768
1769 assert_eq!(f.run(&[b"OBJECT", b"REFCOUNT", b"s"]), ":1\r\n");
1770 assert_eq!(f.run(&[b"OBJECT", b"IDLETIME", b"s"]), ":0\r\n");
1771 assert!(f.run(&[b"OBJECT", b"HELP"]).starts_with("*14\r\n+OBJECT "));
1772 }
1773
1774 #[test]
1775 fn object_answers_nil_for_a_key_that_is_not_there() {
1776 let mut f = Fixture::new();
1777 for sub in [b"ENCODING".as_slice(), b"REFCOUNT", b"IDLETIME", b"FREQ"] {
1778 assert_eq!(
1779 f.run(&[b"OBJECT", sub, b"nokey"]),
1780 "$-1\r\n",
1781 "a nil and not an error, which is what 8.10.1 does"
1782 );
1783 }
1784 f.run(&[b"SET", b"s", b"v"]);
1787 assert!(
1788 f.run(&[b"OBJECT", b"FREQ", b"s"])
1789 .starts_with("-ERR An LFU maxmemory policy is not"),
1790 );
1791 assert_eq!(
1792 f.run(&[b"OBJECT", b"NOPE", b"s"]),
1793 "-ERR unknown subcommand 'NOPE'. Try OBJECT HELP.\r\n"
1794 );
1795 assert_eq!(
1796 f.run(&[b"OBJECT", b"ENCODING"]),
1797 "-ERR wrong number of arguments for 'object|encoding' command\r\n"
1798 );
1799 assert_eq!(
1800 f.run(&[b"OBJECT", b"ENCODING", b"s", b"extra"]),
1801 "-ERR wrong number of arguments for 'object|encoding' command\r\n"
1802 );
1803 assert_eq!(
1804 f.run(&[b"OBJECT"]),
1805 "-ERR wrong number of arguments for 'object' command\r\n"
1806 );
1807 }
1808
1809 #[test]
1810 fn config_moves_the_ladder_and_object_encoding_agrees() {
1811 let mut f = Fixture::new();
1812 assert_eq!(
1813 f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
1814 "*2\r\n$25\r\nhash-max-listpack-entries\r\n$3\r\n512\r\n",
1815 "512 and not the 128 everyone remembers, which is what 8.10.1 says"
1816 );
1817 assert_eq!(
1820 f.run(&[b"CONFIG", b"GET", b"hash-max-ziplist-entries"]),
1821 "*2\r\n$24\r\nhash-max-ziplist-entries\r\n$3\r\n512\r\n"
1822 );
1823 assert!(
1824 f.run(&[b"CONFIG", b"GET", b"hash-max-*"])
1825 .starts_with("*8\r\n")
1826 );
1827 assert!(
1828 f.run(&[b"CONFIG", b"GET", b"set-max-*"])
1829 .starts_with("*6\r\n")
1830 );
1831
1832 f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2", b"c", b"3"]);
1833 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"h"]), "$8\r\nlistpack\r\n");
1834
1835 assert_eq!(
1836 f.run(&[b"CONFIG", b"SET", b"hash-max-ziplist-entries", b"2"]),
1837 "+OK\r\n",
1838 "written under the old name and read back under the new one"
1839 );
1840 assert_eq!(
1841 f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
1842 "*2\r\n$25\r\nhash-max-listpack-entries\r\n$1\r\n2\r\n"
1843 );
1844 assert_eq!(
1845 f.run(&[b"OBJECT", b"ENCODING", b"h"]),
1846 "$8\r\nlistpack\r\n",
1847 "the hash that already exists is left exactly where it was"
1848 );
1849 f.run(&[b"HSET", b"h2", b"a", b"1", b"b", b"2", b"c", b"3"]);
1850 assert_eq!(
1851 f.run(&[b"OBJECT", b"ENCODING", b"h2"]),
1852 "$9\r\nhashtable\r\n",
1853 "and the next one built goes straight to a table"
1854 );
1855
1856 f.run(&[b"CONFIG", b"SET", b"set-max-intset-entries", b"2"]);
1858 f.run(&[b"SADD", b"s", b"1", b"2", b"3"]);
1859 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"s"]), "$8\r\nlistpack\r\n");
1860 f.run(&[b"CONFIG", b"SET", b"set-max-listpack-value", b"2"]);
1861 f.run(&[b"SADD", b"s2", b"abcdefgh"]);
1862 assert_eq!(
1863 f.run(&[b"OBJECT", b"ENCODING", b"s2"]),
1864 "$9\r\nhashtable\r\n"
1865 );
1866 }
1867
1868 #[test]
1869 fn config_set_takes_all_of_the_ladder_or_none_of_it() {
1870 let mut f = Fixture::new();
1871 assert_eq!(
1872 f.run(&[
1873 b"CONFIG",
1874 b"SET",
1875 b"hash-max-listpack-entries",
1876 b"7",
1877 b"set-max-listpack-entries",
1878 b"abc"
1879 ]),
1880 "-ERR CONFIG SET failed (possibly related to argument 'set-max-listpack-entries') - argument couldn't be parsed into an integer\r\n"
1881 );
1882 assert_eq!(
1883 f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
1884 "*2\r\n$25\r\nhash-max-listpack-entries\r\n$3\r\n512\r\n",
1885 "the pair in front of the bad one did not go in"
1886 );
1887 assert_eq!(
1890 f.run(&[b"CONFIG", b"SET", b"hash-max-ziplist-entries", b"abc"]),
1891 "-ERR CONFIG SET failed (possibly related to argument 'hash-max-ziplist-entries') - argument couldn't be parsed into an integer\r\n"
1892 );
1893 assert_eq!(
1894 f.run(&[b"CONFIG", b"SET", b"set-max-intset-entries", b"-1"]),
1895 "-ERR CONFIG SET failed (possibly related to argument 'set-max-intset-entries') - argument must be between 0 and 9223372036854775807 inclusive\r\n"
1896 );
1897 assert_eq!(
1900 f.run(&[
1901 b"CONFIG",
1902 b"SET",
1903 b"set-max-intset-entries",
1904 b"99999999999999999999"
1905 ]),
1906 "-ERR CONFIG SET failed (possibly related to argument 'set-max-intset-entries') - argument couldn't be parsed into an integer\r\n"
1907 );
1908 assert_eq!(
1909 f.run(&[
1910 b"CONFIG",
1911 b"SET",
1912 b"set-max-intset-entries",
1913 b"9223372036854775807"
1914 ]),
1915 "+OK\r\n"
1916 );
1917 }
1918
1919 #[test]
1920 fn a_setting_moved_on_one_database_moved_on_all_of_them() {
1921 let mut f = Fixture::new();
1922 f.run(&[b"CONFIG", b"SET", b"hash-max-listpack-entries", b"1"]);
1923 f.run(&[b"SELECT", b"3"]);
1924 f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
1925 assert_eq!(
1926 f.run(&[b"OBJECT", b"ENCODING", b"h"]),
1927 "$9\r\nhashtable\r\n",
1928 "these are one server wide number in Redis, whatever a Keyspace carries"
1929 );
1930 }
1931
1932 #[test]
1933 fn info_reports_the_numbers_it_can_stand_behind() {
1934 let mut f = Fixture::new();
1935 f.run(&[b"MSET", b"a", b"1", b"b", b"2"]);
1936 let all = f.run(&[b"INFO"]);
1937 assert!(all.contains("redis_version:8.8.0"), "{all}");
1938 assert!(
1939 all.contains(concat!("yo_version:", env!("CARGO_PKG_VERSION"))),
1940 "{all}"
1941 );
1942 assert!(all.contains("db0:keys=2,expires=0,avg_ttl=0"), "{all}");
1943 assert!(all.contains("role:master"), "{all}");
1944 let clients = f.run(&[b"INFO", b"clients"]);
1946 assert!(clients.contains("connected_clients:0"), "{clients}");
1947 assert!(!clients.contains("redis_version"), "{clients}");
1948 assert_eq!(f.run(&[b"INFO", b"nosuch"]), "$0\r\n\r\n");
1949 }
1950
1951 #[cfg(unix)]
1952 #[test]
1953 fn info_cpu_reports_processor_time_that_was_really_measured() {
1954 let mut f = Fixture::new();
1955 let cpu = f.run(&[b"INFO", b"cpu"]);
1956 assert!(cpu.contains("# CPU"), "{cpu}");
1957 assert!(cpu.contains("used_cpu_user:"), "{cpu}");
1959 assert!(cpu.contains("used_cpu_sys:"), "{cpu}");
1960 assert!(cpu.contains("used_cpu_user_children:0.000000"), "{cpu}");
1961 assert!(!cpu.contains("redis_version"), "{cpu}");
1962
1963 let before = used_cpu_user(&cpu);
1967 let mut n = 0u64;
1968 let mut rounds = 0;
1969 while used_cpu_user(&f.run(&[b"INFO", b"cpu"])) <= before {
1970 for i in 0..1_000_000u64 {
1971 n = n.wrapping_add(i.wrapping_mul(i));
1972 }
1973 rounds += 1;
1974 assert!(rounds < 1_000, "cpu time never moved, n is {n}");
1978 }
1979 }
1980
1981 #[cfg(unix)]
1983 fn used_cpu_user(info: &str) -> f64 {
1984 info.lines()
1985 .find_map(|l| l.strip_prefix("used_cpu_user:"))
1986 .expect("no used_cpu_user in the reply")
1987 .trim()
1988 .parse()
1989 .expect("used_cpu_user is not a number")
1990 }
1991
1992 #[test]
1998 fn a_command_that_fails_leaves_nothing_half_written() {
1999 let mut f = Fixture::new();
2000 let reply = f.run(&[b"SETRANGE", b"k", b"-1", b"x"]);
2001 assert_eq!(reply, "-ERR offset is out of range\r\n");
2002 assert!(!reply.contains(':'), "no integer went out in front of it");
2003 }
2004
2005 #[test]
2006 fn quit_answers_first_and_closes_after() {
2007 let mut f = Fixture::new();
2008 let (flow, reply) = f.flow(&[b"QUIT"]);
2009 assert_eq!(reply, "+OK\r\n");
2010 assert_eq!(flow, Flow::Close);
2011 }
2012
2013 #[test]
2014 fn the_command_counter_counts_every_command_including_the_bad_ones() {
2015 let mut f = Fixture::new();
2016 f.run(&[b"PING"]);
2017 f.run(&[b"NOPE"]);
2018 f.run(&[b"GET"]);
2019 assert_eq!(f.server.stats.commands, 3);
2020 }
2021
2022 #[test]
2023 fn a_set_goes_from_bytes_to_bytes() {
2024 let mut f = Fixture::new();
2025 assert_eq!(f.run(&[b"SADD", b"s", b"a", b"b", b"c"]), ":3\r\n");
2026 assert_eq!(f.run(&[b"SADD", b"s", b"b", b"d"]), ":1\r\n");
2027 assert_eq!(f.run(&[b"SCARD", b"s"]), ":4\r\n");
2028 assert_eq!(f.run(&[b"SISMEMBER", b"s", b"a"]), ":1\r\n");
2029 assert_eq!(f.run(&[b"SISMEMBER", b"s", b"z"]), ":0\r\n");
2030 assert_eq!(f.run(&[b"TYPE", b"s"]), "+set\r\n");
2031 assert_eq!(
2032 f.run(&[b"SMISMEMBER", b"s", b"a", b"z", b"d"]),
2033 "*3\r\n:1\r\n:0\r\n:1\r\n"
2034 );
2035 assert_eq!(f.run(&[b"SREM", b"s", b"a", b"z"]), ":1\r\n");
2036 assert_eq!(f.run(&[b"SCARD", b"s"]), ":3\r\n");
2037 }
2038
2039 #[test]
2040 fn a_set_command_at_a_key_that_is_not_there_answers_empty() {
2041 let mut f = Fixture::new();
2042 assert_eq!(f.run(&[b"SCARD", b"nope"]), ":0\r\n");
2043 assert_eq!(f.run(&[b"SISMEMBER", b"nope", b"a"]), ":0\r\n");
2044 assert_eq!(f.run(&[b"SREM", b"nope", b"a"]), ":0\r\n");
2045 assert_eq!(f.run(&[b"SMEMBERS", b"nope"]), "*0\r\n");
2046 assert_eq!(
2047 f.run(&[b"SMISMEMBER", b"nope", b"a", b"b"]),
2048 "*2\r\n:0\r\n:0\r\n"
2049 );
2050 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n", "and made nothing");
2051 }
2052
2053 #[test]
2054 fn smembers_answers_a_set_on_resp3_and_an_array_on_resp2() {
2055 let mut f = Fixture::new();
2059 f.run(&[b"SADD", b"s", b"one"]);
2060 assert_eq!(f.run(&[b"SMEMBERS", b"s"]), "*1\r\n$3\r\none\r\n");
2061
2062 f.run(&[b"HELLO", b"3"]);
2063 assert_eq!(f.run(&[b"SMEMBERS", b"s"]), "~1\r\n$3\r\none\r\n");
2064 }
2065
2066 #[test]
2067 fn an_integer_member_comes_back_as_the_digits_it_never_stored() {
2068 let mut f = Fixture::new();
2071 f.run(&[b"SADD", b"s", b"42"]);
2072 assert_eq!(f.run(&[b"SMEMBERS", b"s"]), "*1\r\n$2\r\n42\r\n");
2073 assert_eq!(f.run(&[b"SISMEMBER", b"s", b"42"]), ":1\r\n");
2074 assert_eq!(
2075 f.run(&[b"SISMEMBER", b"s", b"042"]),
2076 ":0\r\n",
2077 "the member is the bytes and not the number they parse to"
2078 );
2079 }
2080
2081 #[test]
2082 fn the_wrong_command_at_the_wrong_type_says_so_both_ways() {
2083 let mut f = Fixture::new();
2084 f.run(&[b"SET", b"str", b"v"]);
2085 f.run(&[b"SADD", b"set", b"a"]);
2086
2087 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
2088 assert_eq!(f.run(&[b"SADD", b"str", b"a"]), wrong);
2089 assert_eq!(f.run(&[b"SCARD", b"str"]), wrong);
2090 assert_eq!(f.run(&[b"SMEMBERS", b"str"]), wrong);
2091 assert_eq!(f.run(&[b"SMISMEMBER", b"str", b"a"]), wrong);
2092 assert_eq!(f.run(&[b"GET", b"set"]), wrong);
2093 assert_eq!(f.run(&[b"APPEND", b"set", b"x"]), wrong);
2094 assert_eq!(f.run(&[b"INCR", b"set"]), wrong);
2095 assert_eq!(f.run(&[b"STRLEN", b"set"]), wrong);
2096
2097 assert_eq!(
2100 f.run(&[b"MGET", b"str", b"set", b"nope"]),
2101 "*3\r\n$1\r\nv\r\n$-1\r\n$-1\r\n"
2102 );
2103 assert_eq!(f.run(&[b"SET", b"set", b"now a string"]), "+OK\r\n");
2105 assert_eq!(f.run(&[b"TYPE", b"set"]), "+string\r\n");
2106 }
2107
2108 #[test]
2109 fn a_wrongtype_leaves_nothing_half_written() {
2110 let mut f = Fixture::new();
2114 f.run(&[b"SET", b"k", b"v"]);
2115 let reply = f.run(&[b"SMISMEMBER", b"k", b"a", b"b"]);
2116 assert!(reply.starts_with("-WRONGTYPE"), "got {reply}");
2117 assert!(!reply.contains('*'), "an array header went out in front");
2118 }
2119
2120 #[test]
2121 fn emptying_a_set_takes_the_key_with_it() {
2122 let mut f = Fixture::new();
2123 f.run(&[b"SADD", b"s", b"a", b"b"]);
2124 assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
2125 assert_eq!(f.run(&[b"SREM", b"s", b"a", b"b"]), ":2\r\n");
2126 assert_eq!(f.run(&[b"EXISTS", b"s"]), ":0\r\n");
2127 assert_eq!(f.run(&[b"TYPE", b"s"]), "+none\r\n");
2128 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
2129 }
2130
2131 fn split_scan(reply: &str) -> (String, Vec<String>) {
2137 let mut lines = reply.split("\r\n");
2138 assert_eq!(lines.next(), Some("*2"), "got {reply}");
2139 lines.next().expect("the cursor header");
2140 let cursor = lines.next().expect("the cursor").to_owned();
2141 let header = lines.next().expect("the member header");
2142 let n: usize = header[1..].parse().expect("a member count");
2143 let mut members = Vec::with_capacity(n);
2144 for _ in 0..n {
2145 lines.next().expect("a member header");
2146 members.push(lines.next().expect("a member").to_owned());
2147 }
2148 (cursor, members)
2149 }
2150
2151 #[test]
2152 fn popping_takes_a_member_off_the_set_and_hands_it_back() {
2153 let mut f = Fixture::new();
2154 f.run(&[b"SADD", b"s", b"a", b"b", b"c", b"d"]);
2155
2156 let one = f.run(&[b"SPOP", b"s"]);
2157 assert!(
2158 ["$1\r\na\r\n", "$1\r\nb\r\n", "$1\r\nc\r\n", "$1\r\nd\r\n"].contains(&one.as_str()),
2159 "got {one}"
2160 );
2161 assert_eq!(f.run(&[b"SCARD", b"s"]), ":3\r\n");
2162
2163 let (_, rest) = ("", f.run(&[b"SPOP", b"s", b"3"]));
2165 assert!(rest.starts_with("*3\r\n"), "got {rest}");
2166 assert_eq!(f.run(&[b"EXISTS", b"s"]), ":0\r\n");
2167 assert_eq!(f.run(&[b"SPOP", b"s"]), "$-1\r\n");
2169 assert_eq!(f.run(&[b"SPOP", b"s", b"2"]), "*0\r\n");
2170 }
2171
2172 #[test]
2173 fn the_two_draws_disagree_about_the_reply_type_and_they_are_right_to() {
2174 let mut f = Fixture::new();
2179 f.run(&[b"HELLO", b"3"]);
2180 f.run(&[b"SADD", b"s", b"a", b"b", b"c"]);
2181
2182 assert!(f.run(&[b"SPOP", b"s", b"2"]).starts_with("~2\r\n"));
2183 assert!(f.run(&[b"SRANDMEMBER", b"s", b"1"]).starts_with("*1\r\n"));
2185
2186 f.run(&[b"SADD", b"one", b"z"]);
2190 assert_eq!(
2191 f.run(&[b"SRANDMEMBER", b"one", b"-3"]),
2192 "*3\r\n$1\r\nz\r\n$1\r\nz\r\n$1\r\nz\r\n"
2193 );
2194 }
2195
2196 #[test]
2197 fn drawing_a_member_removes_nothing_and_says_nil_at_a_missing_key() {
2198 let mut f = Fixture::new();
2199 f.run(&[b"SADD", b"s", b"only"]);
2200 assert_eq!(f.run(&[b"SRANDMEMBER", b"s"]), "$4\r\nonly\r\n");
2201 assert_eq!(f.run(&[b"SRANDMEMBER", b"s"]), "$4\r\nonly\r\n");
2202 assert_eq!(f.run(&[b"SCARD", b"s"]), ":1\r\n");
2203
2204 assert_eq!(f.run(&[b"SRANDMEMBER", b"nope"]), "$-1\r\n");
2205 assert_eq!(f.run(&[b"SRANDMEMBER", b"nope", b"3"]), "*0\r\n");
2208 assert_eq!(f.run(&[b"SRANDMEMBER", b"nope", b"-3"]), "*0\r\n");
2209 assert_eq!(f.run(&[b"SRANDMEMBER", b"s", b"9"]), "*1\r\n$4\r\nonly\r\n");
2211 }
2212
2213 #[test]
2214 fn a_pop_count_that_is_not_a_positive_number_says_so() {
2215 let mut f = Fixture::new();
2216 f.run(&[b"SADD", b"s", b"a"]);
2217 let bad = "-ERR value is out of range, must be positive\r\n";
2218 assert_eq!(f.run(&[b"SPOP", b"s", b"-1"]), bad);
2219 assert_eq!(f.run(&[b"SPOP", b"s", b"abc"]), bad);
2220 assert_eq!(f.run(&[b"SCARD", b"s"]), ":1\r\n", "and took nothing");
2221 assert_eq!(f.run(&[b"SPOP", b"s", b"0"]), "*0\r\n");
2223 assert_eq!(f.run(&[b"EXISTS", b"s"]), ":1\r\n");
2224 }
2225
2226 #[test]
2227 fn a_scan_walks_a_set_of_any_size_exactly_once() {
2228 let mut f = Fixture::new();
2229 let members: Vec<Vec<u8>> = (0..300).map(|i| format!("m{i}").into_bytes()).collect();
2230 let args: Vec<&[u8]> = [&b"SADD"[..], &b"s"[..]]
2231 .into_iter()
2232 .chain(members.iter().map(Vec::as_slice))
2233 .collect();
2234 f.run(&args);
2235
2236 let mut seen = Vec::new();
2237 let mut cursor = "0".to_owned();
2238 loop {
2239 let reply = f.run(&[b"SSCAN", b"s", cursor.as_bytes()]);
2240 let (next, got) = split_scan(&reply);
2241 seen.extend(got);
2242 cursor = next;
2243 if cursor == "0" {
2244 break;
2245 }
2246 }
2247 seen.sort();
2248 seen.dedup();
2249 assert_eq!(seen.len(), 300, "a walk saw a member twice or missed one");
2250
2251 f.run(&[b"SADD", b"small", b"a", b"b", b"c"]);
2254 let (cursor, got) = split_scan(&f.run(&[b"SSCAN", b"small", b"0", b"COUNT", b"1"]));
2255 assert_eq!(cursor, "0");
2256 assert_eq!(got.len(), 3);
2257 assert_eq!(f.run(&[b"SSCAN", b"nope", b"0"]), "*2\r\n$1\r\n0\r\n*0\r\n");
2259 }
2260
2261 #[test]
2262 fn a_scan_takes_match_and_count_and_refuses_anything_else() {
2263 let mut f = Fixture::new();
2264 f.run(&[b"SADD", b"s", b"aa", b"ab", b"ba", b"12", b"13"]);
2265
2266 let (_, got) = split_scan(&f.run(&[b"SSCAN", b"s", b"0", b"MATCH", b"a*"]));
2267 let mut got = got;
2268 got.sort();
2269 assert_eq!(got, ["aa", "ab"]);
2270
2271 let (_, got) = split_scan(&f.run(&[b"SSCAN", b"s", b"0", b"MATCH", b"1?"]));
2274 let mut got = got;
2275 got.sort();
2276 assert_eq!(got, ["12", "13"]);
2277
2278 assert_eq!(f.run(&[b"SSCAN", b"s", b"abc"]), "-ERR invalid cursor\r\n");
2279 assert_eq!(f.run(&[b"SSCAN", b"s", b"-1"]), "-ERR invalid cursor\r\n");
2280 assert_eq!(
2281 f.run(&[b"SSCAN", b"s", b"0", b"NOPE", b"1"]),
2282 "-ERR syntax error\r\n"
2283 );
2284 assert_eq!(
2287 f.run(&[b"SSCAN", b"s", b"0", b"COUNT", b"0"]),
2288 "-ERR syntax error\r\n"
2289 );
2290 }
2291
2292 #[test]
2293 fn moving_a_member_takes_it_off_one_set_and_puts_it_on_another() {
2294 let mut f = Fixture::new();
2295 f.run(&[b"SADD", b"src", b"a", b"b"]);
2296 f.run(&[b"SADD", b"dst", b"c"]);
2297
2298 assert_eq!(f.run(&[b"SMOVE", b"src", b"dst", b"a"]), ":1\r\n");
2299 assert_eq!(f.run(&[b"SISMEMBER", b"src", b"a"]), ":0\r\n");
2300 assert_eq!(f.run(&[b"SISMEMBER", b"dst", b"a"]), ":1\r\n");
2301 assert_eq!(f.run(&[b"SMOVE", b"src", b"dst", b"zz"]), ":0\r\n");
2303 assert_eq!(f.run(&[b"SCARD", b"dst"]), ":2\r\n");
2304
2305 assert_eq!(f.run(&[b"SMOVE", b"src", b"fresh", b"b"]), ":1\r\n");
2308 assert_eq!(f.run(&[b"EXISTS", b"src"]), ":0\r\n");
2309 assert_eq!(f.run(&[b"SMEMBERS", b"fresh"]), "*1\r\n$1\r\nb\r\n");
2310 }
2311
2312 #[test]
2313 fn moving_checks_the_types_in_the_order_redis_checks_them() {
2314 let mut f = Fixture::new();
2318 f.run(&[b"SET", b"str", b"v"]);
2319 f.run(&[b"SADD", b"set", b"a"]);
2320
2321 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
2322 assert_eq!(f.run(&[b"SMOVE", b"nope", b"str", b"a"]), ":0\r\n");
2323 assert_eq!(f.run(&[b"SMOVE", b"str", b"set", b"a"]), wrong);
2324 assert_eq!(f.run(&[b"SMOVE", b"set", b"str", b"a"]), wrong);
2325 assert_eq!(f.run(&[b"SPOP", b"str"]), wrong);
2326 assert_eq!(f.run(&[b"SRANDMEMBER", b"str"]), wrong);
2327 assert_eq!(f.run(&[b"SSCAN", b"str", b"0"]), wrong);
2328 assert_eq!(
2329 f.run(&[b"SISMEMBER", b"set", b"a"]),
2330 ":1\r\n",
2331 "and none of that moved anything"
2332 );
2333 }
2334
2335 #[test]
2336 fn a_scan_leaves_nothing_half_written_when_its_arguments_are_wrong() {
2337 let mut f = Fixture::new();
2340 f.run(&[b"SADD", b"s", b"a"]);
2341 for bad in [
2342 &[b"SSCAN".as_slice(), b"s", b"abc"][..],
2343 &[b"SSCAN".as_slice(), b"s", b"0", b"COUNT", b"nope"][..],
2344 &[b"SSCAN".as_slice(), b"s", b"0", b"MATCH"][..],
2345 ] {
2346 let reply = f.run(bad);
2347 assert!(reply.starts_with("-ERR"), "got {reply}");
2348 assert!(!reply.contains('*'), "an array header went out in front");
2349 }
2350 }
2351
2352 #[test]
2353 fn a_hash_writes_reads_and_deletes_its_fields() {
2354 let mut f = Fixture::new();
2355 assert_eq!(f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]), ":2\r\n");
2356 assert_eq!(f.run(&[b"HSET", b"h", b"a", b"9"]), ":0\r\n", "a was there");
2357 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n9\r\n");
2358 assert_eq!(f.run(&[b"HGET", b"h", b"nope"]), "$-1\r\n");
2359 assert_eq!(f.run(&[b"HGET", b"nokey", b"a"]), "$-1\r\n");
2360 assert_eq!(f.run(&[b"HLEN", b"h"]), ":2\r\n");
2361 assert_eq!(f.run(&[b"HEXISTS", b"h", b"a"]), ":1\r\n");
2362 assert_eq!(f.run(&[b"HEXISTS", b"h", b"nope"]), ":0\r\n");
2363 assert_eq!(f.run(&[b"HSTRLEN", b"h", b"a"]), ":1\r\n");
2364 assert_eq!(f.run(&[b"HSTRLEN", b"h", b"nope"]), ":0\r\n");
2365
2366 assert_eq!(f.run(&[b"HGET", b"h", b"2"]), "$-1\r\n");
2369
2370 assert_eq!(f.run(&[b"HDEL", b"h", b"a", b"nope"]), ":1\r\n");
2371 assert_eq!(f.run(&[b"HDEL", b"h", b"b"]), ":1\r\n");
2372 assert_eq!(
2373 f.run(&[b"EXISTS", b"h"]),
2374 ":0\r\n",
2375 "and losing the last field lost the key"
2376 );
2377 }
2378
2379 #[test]
2380 fn hgetall_answers_a_map_on_resp3_and_the_same_pairs_flat_on_resp2() {
2381 let mut f = Fixture::new();
2382 f.run(&[b"HSET", b"h", b"a", b"1"]);
2383 assert_eq!(f.run(&[b"HGETALL", b"h"]), "*2\r\n$1\r\na\r\n$1\r\n1\r\n");
2384 assert_eq!(f.run(&[b"HGETALL", b"nokey"]), "*0\r\n");
2385 assert_eq!(f.run(&[b"HKEYS", b"h"]), "*1\r\n$1\r\na\r\n");
2386 assert_eq!(f.run(&[b"HVALS", b"h"]), "*1\r\n$1\r\n1\r\n");
2387 assert_eq!(f.run(&[b"HKEYS", b"nokey"]), "*0\r\n");
2388
2389 f.run(&[b"HELLO", b"3"]);
2390 assert_eq!(f.run(&[b"HGETALL", b"h"]), "%1\r\n$1\r\na\r\n$1\r\n1\r\n");
2391 assert_eq!(
2392 f.run(&[b"HGETALL", b"nokey"]),
2393 "%0\r\n",
2394 "a missing key is the empty hash and never a nil"
2395 );
2396 assert_eq!(
2397 f.run(&[b"HKEYS", b"h"]),
2398 "*1\r\n$1\r\na\r\n",
2399 "and the two that answer one side stay arrays"
2400 );
2401 }
2402
2403 #[test]
2404 fn hmget_answers_once_per_field_and_hmset_answers_ok() {
2405 let mut f = Fixture::new();
2406 assert_eq!(f.run(&[b"HMSET", b"h", b"a", b"1", b"c", b"3"]), "+OK\r\n");
2407 assert_eq!(
2408 f.run(&[b"HMGET", b"h", b"a", b"b", b"c"]),
2409 "*3\r\n$1\r\n1\r\n$-1\r\n$1\r\n3\r\n",
2410 "the reply is positional, so b is a nil and not a gap"
2411 );
2412 assert_eq!(
2413 f.run(&[b"HMGET", b"nokey", b"a", b"b"]),
2414 "*2\r\n$-1\r\n$-1\r\n",
2415 "and a missing key is all nils rather than an empty array"
2416 );
2417
2418 assert_eq!(f.run(&[b"HSETNX", b"h", b"a", b"9"]), ":0\r\n");
2419 assert_eq!(f.run(&[b"HSETNX", b"h", b"z", b"9"]), ":1\r\n");
2420 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
2421 }
2422
2423 #[test]
2424 fn a_hash_counts_up_and_says_so_when_it_cannot() {
2425 let mut f = Fixture::new();
2426 assert_eq!(f.run(&[b"HINCRBY", b"h", b"n", b"5"]), ":5\r\n");
2427 assert_eq!(f.run(&[b"HINCRBY", b"h", b"n", b"-7"]), ":-2\r\n");
2428 assert_eq!(f.run(&[b"HGET", b"h", b"n"]), "$2\r\n-2\r\n");
2429 assert_eq!(
2430 f.run(&[b"HINCRBYFLOAT", b"h", b"f", b"10.5"]),
2431 "$4\r\n10.5\r\n",
2432 "a bulk string and not a double, on both protocols"
2433 );
2434
2435 f.run(&[b"HSET", b"h", b"s", b"words"]);
2436 let bad = f.run(&[b"HINCRBY", b"h", b"s", b"1"]);
2437 assert!(
2438 bad.starts_with("-ERR hash value is not an integer"),
2439 "{bad}"
2440 );
2441 let bad = f.run(&[b"HINCRBY", b"h", b"n", b"nope"]);
2442 assert!(
2443 bad.starts_with("-ERR value is not an integer"),
2444 "a bad argument is not yet a hash value, {bad}"
2445 );
2446 assert_eq!(
2447 f.run(&[b"HGET", b"h", b"s"]),
2448 "$5\r\nwords\r\n",
2449 "and neither of them wrote anything"
2450 );
2451 }
2452
2453 #[test]
2454 fn a_hash_scan_walks_every_pair_once_and_novalues_drops_half_of_it() {
2455 let mut f = Fixture::new();
2456 for i in 0..500 {
2457 let field = format!("field-{i}");
2458 let value = format!("value-{i}");
2459 f.run(&[b"HSET", b"h", field.as_bytes(), value.as_bytes()]);
2460 }
2461
2462 let mut seen: Vec<String> = Vec::new();
2463 let mut cursor = "0".to_owned();
2464 loop {
2465 let reply = f.run(&[b"HSCAN", b"h", cursor.as_bytes(), b"COUNT", b"32"]);
2466 let (next, items) = scan_reply(&reply);
2467 assert_eq!(items.len() % 2, 0, "a pair went out half written");
2468 for pair in items.chunks(2) {
2469 assert_eq!(
2470 pair[0].strip_prefix("field-"),
2471 pair[1].strip_prefix("value-"),
2472 "a field came back with someone else's value"
2473 );
2474 seen.push(pair[0].clone());
2475 }
2476 cursor = next;
2477 if cursor == "0" {
2478 break;
2479 }
2480 }
2481 seen.sort();
2482 seen.dedup();
2483 assert_eq!(seen.len(), 500, "every field once and only once");
2484
2485 let (_, items) = scan_reply(&f.run(&[b"HSCAN", b"h", b"0", b"NOVALUES", b"COUNT", b"32"]));
2486 assert!(
2487 items.iter().all(|s| s.starts_with("field-")),
2488 "NOVALUES still sent the values"
2489 );
2490
2491 let (_, one) = scan_reply(&f.run(&[
2492 b"HSCAN",
2493 b"h",
2494 b"0",
2495 b"MATCH",
2496 b"field-499",
2497 b"COUNT",
2498 b"1000",
2499 ]));
2500 assert_eq!(one, ["field-499", "value-499"], "MATCH is on the field");
2501 }
2502
2503 #[test]
2504 fn hrandfield_draws_what_it_was_asked_for_and_nests_values_on_resp3() {
2505 let mut f = Fixture::new();
2506 f.run(&[b"HSET", b"h", b"a", b"1"]);
2507 assert_eq!(f.run(&[b"HRANDFIELD", b"h"]), "$1\r\na\r\n");
2508 assert_eq!(f.run(&[b"HRANDFIELD", b"nokey"]), "$-1\r\n");
2509 assert_eq!(f.run(&[b"HRANDFIELD", b"nokey", b"3"]), "*0\r\n");
2510 assert_eq!(
2511 f.run(&[b"HRANDFIELD", b"h", b"3"]),
2512 "*1\r\n$1\r\na\r\n",
2513 "a positive count is capped at the size of the hash"
2514 );
2515 assert_eq!(
2516 f.run(&[b"HRANDFIELD", b"h", b"-3"]),
2517 "*3\r\n$1\r\na\r\n$1\r\na\r\n$1\r\na\r\n",
2518 "and a negative one repeats itself"
2519 );
2520 assert_eq!(
2521 f.run(&[b"HRANDFIELD", b"h", b"1", b"WITHVALUES"]),
2522 "*2\r\n$1\r\na\r\n$1\r\n1\r\n",
2523 "flat on RESP2"
2524 );
2525
2526 f.run(&[b"HELLO", b"3"]);
2527 assert_eq!(
2528 f.run(&[b"HRANDFIELD", b"h", b"1", b"WITHVALUES"]),
2529 "*1\r\n*2\r\n$1\r\na\r\n$1\r\n1\r\n",
2530 "and nested on RESP3, but still an array and never a map"
2531 );
2532 }
2533
2534 #[test]
2535 fn every_hash_command_says_wrongtype_and_writes_nothing() {
2536 let mut f = Fixture::new();
2537 f.run(&[b"SET", b"str", b"v"]);
2538 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
2539
2540 for cmd in [
2541 &[b"HSET".as_slice(), b"str", b"f", b"v"][..],
2542 &[b"HMSET".as_slice(), b"str", b"f", b"v"][..],
2543 &[b"HSETNX".as_slice(), b"str", b"f", b"v"][..],
2544 &[b"HGET".as_slice(), b"str", b"f"][..],
2545 &[b"HMGET".as_slice(), b"str", b"f"][..],
2546 &[b"HDEL".as_slice(), b"str", b"f"][..],
2547 &[b"HLEN".as_slice(), b"str"][..],
2548 &[b"HEXISTS".as_slice(), b"str", b"f"][..],
2549 &[b"HSTRLEN".as_slice(), b"str", b"f"][..],
2550 &[b"HGETALL".as_slice(), b"str"][..],
2551 &[b"HKEYS".as_slice(), b"str"][..],
2552 &[b"HVALS".as_slice(), b"str"][..],
2553 &[b"HINCRBY".as_slice(), b"str", b"f", b"1"][..],
2554 &[b"HINCRBYFLOAT".as_slice(), b"str", b"f", b"1"][..],
2555 &[b"HRANDFIELD".as_slice(), b"str"][..],
2556 &[b"HRANDFIELD".as_slice(), b"str", b"2"][..],
2557 &[b"HSCAN".as_slice(), b"str", b"0"][..],
2558 ] {
2559 let reply = f.run(cmd);
2560 assert_eq!(reply, wrong, "{:?}", cmd[0]);
2561 }
2562 assert_eq!(
2563 f.run(&[b"GET", b"str"]),
2564 "$1\r\nv\r\n",
2565 "and none of them touched the value"
2566 );
2567 }
2568
2569 #[test]
2570 fn a_hash_scan_leaves_nothing_half_written_when_its_arguments_are_wrong() {
2571 let mut f = Fixture::new();
2572 f.run(&[b"HSET", b"h", b"f", b"v"]);
2573 for bad in [
2574 &[b"HSCAN".as_slice(), b"h", b"abc"][..],
2575 &[b"HSCAN".as_slice(), b"h", b"0", b"COUNT", b"nope"][..],
2576 &[b"HSCAN".as_slice(), b"h", b"0", b"COUNT", b"0"][..],
2577 &[b"HSCAN".as_slice(), b"h", b"0", b"MATCH"][..],
2578 ] {
2579 let reply = f.run(bad);
2580 assert!(reply.starts_with("-ERR"), "got {reply}");
2581 assert!(!reply.contains('*'), "an array header went out in front");
2582 }
2583 }
2584
2585 #[test]
2586 fn a_field_deadline_goes_on_and_comes_back_in_all_four_units() {
2587 let mut f = Fixture::new();
2588 f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
2589 assert_eq!(
2590 f.run(&[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"a"]),
2591 "*1\r\n:1\r\n"
2592 );
2593 assert_eq!(
2594 f.run(&[b"HTTL", b"h", b"FIELDS", b"3", b"a", b"b", b"nope"]),
2595 "*3\r\n:100\r\n:-1\r\n:-2\r\n",
2596 "one answer per field, and the two sentinels are TTL's own"
2597 );
2598
2599 let ms = int_reply(&f.run(&[b"HPTTL", b"h", b"FIELDS", b"1", b"a"]));
2602 assert!((99_000..=100_000).contains(&ms), "got {ms}");
2603 let at = int_reply(&f.run(&[b"HEXPIRETIME", b"h", b"FIELDS", b"1", b"a"]));
2604 let at_ms = int_reply(&f.run(&[b"HPEXPIRETIME", b"h", b"FIELDS", b"1", b"a"]));
2605 assert_eq!(at, at_ms.div_euclid(1000) + i64::from(at_ms % 1000 != 0));
2606 assert!(at_ms > 1_700_000_000_000, "an absolute moment, got {at_ms}");
2607
2608 assert_eq!(
2609 f.run(&[b"HPERSIST", b"h", b"FIELDS", b"3", b"a", b"b", b"nope"]),
2610 "*3\r\n:1\r\n:-1\r\n:-2\r\n",
2611 "one for the deadline taken off, and it does not say what it was"
2612 );
2613 assert_eq!(
2614 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
2615 "*1\r\n:-1\r\n"
2616 );
2617 assert_eq!(
2618 f.run(&[b"HGET", b"h", b"a"]),
2619 "$1\r\n1\r\n",
2620 "and the field is still there with the value it had"
2621 );
2622 }
2623
2624 #[test]
2625 fn a_deadline_that_has_already_gone_deletes_the_field_now() {
2626 let mut f = Fixture::new();
2627 f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
2628 assert_eq!(
2629 f.run(&[b"HEXPIREAT", b"h", b"1", b"FIELDS", b"1", b"a"]),
2630 "*1\r\n:2\r\n",
2631 "two, and not one, because nothing was stored"
2632 );
2633 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$-1\r\n");
2634 assert_eq!(f.run(&[b"HLEN", b"h"]), ":1\r\n");
2635
2636 assert_eq!(
2637 f.run(&[b"HPEXPIREAT", b"h", b"1", b"FIELDS", b"1", b"b"]),
2638 "*1\r\n:2\r\n"
2639 );
2640 assert_eq!(
2641 f.run(&[b"EXISTS", b"h"]),
2642 ":0\r\n",
2643 "and the last field going took the key with it"
2644 );
2645
2646 f.run(&[b"HSET", b"h", b"a", b"1"]);
2649 assert_eq!(
2650 f.run(&[b"HEXPIRE", b"h", b"0", b"FIELDS", b"1", b"a"]),
2651 "*1\r\n:2\r\n"
2652 );
2653 }
2654
2655 #[test]
2656 fn a_field_is_gone_once_its_moment_passes() {
2657 let mut f = Fixture::new();
2658 f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
2659 assert_eq!(
2660 f.run(&[b"HPEXPIRE", b"h", b"20", b"FIELDS", b"1", b"a"]),
2661 "*1\r\n:1\r\n"
2662 );
2663 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n", "not yet");
2664
2665 f.server.db(0).clock_mut().advance(60);
2669 assert_eq!(f.run(&[b"HLEN", b"h"]), ":1\r\n");
2670 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$-1\r\n");
2671 assert_eq!(
2672 f.run(&[b"HGETALL", b"h"]),
2673 "*2\r\n$1\r\nb\r\n$1\r\n2\r\n",
2674 "and the walks do not hand back a field that has expired"
2675 );
2676 }
2677
2678 #[test]
2679 fn a_missing_key_answers_the_no_field_sentinel_for_every_field() {
2680 let mut f = Fixture::new();
2681 for cmd in [
2682 &[
2683 b"HEXPIRE".as_slice(),
2684 b"nokey",
2685 b"100",
2686 b"FIELDS",
2687 b"2",
2688 b"a",
2689 b"b",
2690 ][..],
2691 &[b"HTTL".as_slice(), b"nokey", b"FIELDS", b"2", b"a", b"b"][..],
2692 &[b"HPTTL".as_slice(), b"nokey", b"FIELDS", b"2", b"a", b"b"][..],
2693 &[
2694 b"HEXPIRETIME".as_slice(),
2695 b"nokey",
2696 b"FIELDS",
2697 b"2",
2698 b"a",
2699 b"b",
2700 ][..],
2701 &[
2702 b"HPERSIST".as_slice(),
2703 b"nokey",
2704 b"FIELDS",
2705 b"2",
2706 b"a",
2707 b"b",
2708 ][..],
2709 ] {
2710 assert_eq!(f.run(cmd), "*2\r\n:-2\r\n:-2\r\n", "{:?}", cmd[0]);
2711 }
2712 }
2713
2714 #[test]
2715 fn writing_a_field_clears_the_deadline_that_was_on_it() {
2716 let mut f = Fixture::new();
2717 f.run(&[b"HSET", b"h", b"a", b"1"]);
2718 f.run(&[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"a"]);
2719 f.run(&[b"HSET", b"h", b"a", b"2"]);
2720 assert_eq!(
2721 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
2722 "*1\r\n:-1\r\n",
2723 "Redis has done this since 7.4, and it is why HGETEX exists"
2724 );
2725 }
2726
2727 #[test]
2728 fn the_four_conditions_reach_the_store_the_way_they_were_written() {
2729 let mut f = Fixture::new();
2730 f.run(&[b"HSET", b"h", b"a", b"1"]);
2731 assert_eq!(
2732 f.run(&[b"HEXPIRE", b"h", b"100", b"XX", b"FIELDS", b"1", b"a"]),
2733 "*1\r\n:0\r\n",
2734 "XX on a field with no deadline changes nothing"
2735 );
2736 assert_eq!(
2737 f.run(&[b"HEXPIRE", b"h", b"100", b"NX", b"FIELDS", b"1", b"a"]),
2738 "*1\r\n:1\r\n"
2739 );
2740 assert_eq!(
2741 f.run(&[b"HEXPIRE", b"h", b"200", b"NX", b"FIELDS", b"1", b"a"]),
2742 "*1\r\n:0\r\n",
2743 "and NX will not move one that is already there"
2744 );
2745 assert_eq!(
2746 f.run(&[b"HEXPIRE", b"h", b"50", b"GT", b"FIELDS", b"1", b"a"]),
2747 "*1\r\n:0\r\n"
2748 );
2749 assert_eq!(
2750 f.run(&[b"HEXPIRE", b"h", b"500", b"GT", b"FIELDS", b"1", b"a"]),
2751 "*1\r\n:1\r\n"
2752 );
2753 assert_eq!(
2754 f.run(&[b"HEXPIRE", b"h", b"50", b"LT", b"FIELDS", b"1", b"a"]),
2755 "*1\r\n:1\r\n"
2756 );
2757 assert_eq!(
2758 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
2759 "*1\r\n:50\r\n"
2760 );
2761 }
2762
2763 #[test]
2764 fn the_field_ttl_family_leaves_nothing_half_written_on_a_bad_argument() {
2765 let mut f = Fixture::new();
2766 f.run(&[b"HSET", b"h", b"a", b"1"]);
2767 for (bad, want) in [
2768 (
2769 &[b"HEXPIRE".as_slice(), b"h", b"-1", b"FIELDS", b"1", b"a"][..],
2770 "-ERR invalid expire time, must be >= 0",
2771 ),
2772 (
2773 &[
2774 b"HEXPIRE".as_slice(),
2775 b"h",
2776 b"9999999999999999",
2777 b"FIELDS",
2778 b"1",
2779 b"a",
2780 ][..],
2781 "-ERR invalid expire time in 'hexpire' command",
2782 ),
2783 (
2784 &[b"HEXPIRE".as_slice(), b"h", b"100", b"FIELD", b"1", b"a"][..],
2785 "-ERR wrong number of arguments for 'hexpire' command",
2786 ),
2787 (
2788 &[b"HEXPIRE".as_slice(), b"h", b"100", b"FIELDS", b"0", b"a"][..],
2789 "-ERR Parameter `numFields` should be greater than 0",
2790 ),
2791 (
2792 &[b"HEXPIRE".as_slice(), b"h", b"100", b"FIELDS", b"2", b"a"][..],
2793 "-ERR wrong number of arguments",
2794 ),
2795 (
2796 &[b"HTTL".as_slice(), b"h", b"FIELDS", b"3", b"a", b"b"][..],
2797 "-ERR wrong number of arguments",
2798 ),
2799 ] {
2800 let reply = f.run(bad);
2801 assert!(reply.starts_with(want), "wanted {want}, got {reply}");
2802 assert!(!reply.contains('*'), "an array header went out in front");
2803 }
2804 assert_eq!(
2805 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
2806 "*1\r\n:-1\r\n",
2807 "and not one of them put a deadline on anything"
2808 );
2809 }
2810
2811 #[test]
2812 fn every_field_ttl_command_says_wrongtype_and_writes_nothing() {
2813 let mut f = Fixture::new();
2814 f.run(&[b"SET", b"str", b"v"]);
2815 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
2816
2817 for cmd in [
2818 &[b"HEXPIRE".as_slice(), b"str", b"100", b"FIELDS", b"1", b"f"][..],
2819 &[
2820 b"HPEXPIRE".as_slice(),
2821 b"str",
2822 b"100",
2823 b"FIELDS",
2824 b"1",
2825 b"f",
2826 ][..],
2827 &[
2828 b"HEXPIREAT".as_slice(),
2829 b"str",
2830 b"9999999999",
2831 b"FIELDS",
2832 b"1",
2833 b"f",
2834 ][..],
2835 &[
2836 b"HPEXPIREAT".as_slice(),
2837 b"str",
2838 b"9999999999999",
2839 b"FIELDS",
2840 b"1",
2841 b"f",
2842 ][..],
2843 &[b"HTTL".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
2844 &[b"HPTTL".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
2845 &[b"HEXPIRETIME".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
2846 &[b"HPEXPIRETIME".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
2847 &[b"HPERSIST".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
2848 ] {
2849 assert_eq!(f.run(cmd), wrong, "{:?}", cmd[0]);
2850 }
2851 assert_eq!(
2852 f.run(&[b"GET", b"str"]),
2853 "$1\r\nv\r\n",
2854 "and none of them touched the value"
2855 );
2856 }
2857
2858 #[test]
2859 fn hgetdel_hands_the_value_out_and_then_takes_the_field() {
2860 let mut f = Fixture::new();
2861 f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
2862 assert_eq!(
2863 f.run(&[b"HGETDEL", b"h", b"FIELDS", b"2", b"a", b"nope"]),
2864 "*2\r\n$1\r\n1\r\n$-1\r\n",
2865 "positional, so the field that was not there is a nil in its place"
2866 );
2867 assert_eq!(f.run(&[b"HLEN", b"h"]), ":1\r\n");
2868 assert_eq!(
2869 f.run(&[b"HGETDEL", b"nokey", b"FIELDS", b"1", b"a"]),
2870 "*1\r\n$-1\r\n"
2871 );
2872 assert_eq!(
2873 f.run(&[b"HGETDEL", b"h", b"FIELDS", b"1", b"b"]),
2874 "*1\r\n$1\r\n2\r\n"
2875 );
2876 assert_eq!(
2877 f.run(&[b"EXISTS", b"h"]),
2878 ":0\r\n",
2879 "and the last field took the key"
2880 );
2881 }
2882
2883 #[test]
2884 fn hgetex_reads_and_moves_the_deadline_in_one_command() {
2885 let mut f = Fixture::new();
2886 f.run(&[b"HSET", b"h", b"a", b"1"]);
2887 assert_eq!(
2888 f.run(&[b"HGETEX", b"h", b"FIELDS", b"1", b"a"]),
2889 "*1\r\n$1\r\n1\r\n"
2890 );
2891 assert_eq!(
2892 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
2893 "*1\r\n:-1\r\n",
2894 "no option means leave it alone, which is the one place this is not GETEX"
2895 );
2896
2897 f.run(&[b"HGETEX", b"h", b"EX", b"100", b"FIELDS", b"1", b"a"]);
2898 assert_eq!(
2899 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
2900 "*1\r\n:100\r\n"
2901 );
2902 f.run(&[b"HGETEX", b"h", b"FIELDS", b"1", b"a"]);
2903 assert_eq!(
2904 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
2905 "*1\r\n:100\r\n",
2906 "and a plain read really does leave it alone"
2907 );
2908 assert_eq!(
2909 f.run(&[b"HGETEX", b"h", b"PERSIST", b"FIELDS", b"1", b"a"]),
2910 "*1\r\n$1\r\n1\r\n"
2911 );
2912 assert_eq!(
2913 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
2914 "*1\r\n:-1\r\n"
2915 );
2916
2917 assert_eq!(
2918 f.run(&[b"HGETEX", b"h", b"EXAT", b"1", b"FIELDS", b"1", b"a"]),
2919 "*1\r\n$1\r\n1\r\n",
2920 "the value goes out before the deadline that has already gone is applied"
2921 );
2922 assert_eq!(f.run(&[b"EXISTS", b"h"]), ":0\r\n");
2923 assert_eq!(
2924 f.run(&[b"HGETEX", b"nokey", b"EX", b"100", b"FIELDS", b"1", b"a"]),
2925 "*1\r\n$-1\r\n"
2926 );
2927 }
2928
2929 #[test]
2930 fn hsetex_writes_all_of_it_or_none_of_it() {
2931 let mut f = Fixture::new();
2932 assert_eq!(
2933 f.run(&[b"HSETEX", b"h", b"FIELDS", b"1", b"a", b"1"]),
2934 ":1\r\n"
2935 );
2936 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
2937 assert_eq!(
2938 f.run(&[
2939 b"HSETEX", b"h", b"FNX", b"FIELDS", b"2", b"a", b"9", b"new", b"9"
2940 ]),
2941 ":0\r\n",
2942 "FNX wants every field named to be missing"
2943 );
2944 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
2945 assert_eq!(
2946 f.run(&[b"HEXISTS", b"h", b"new"]),
2947 ":0\r\n",
2948 "and none of the list was written"
2949 );
2950 assert_eq!(
2951 f.run(&[
2952 b"HSETEX", b"h", b"FXX", b"FIELDS", b"2", b"a", b"9", b"nope", b"9"
2953 ]),
2954 ":0\r\n",
2955 "and FXX wants every one of them to be there"
2956 );
2957 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
2958 assert_eq!(
2959 f.run(&[b"HSETEX", b"h", b"FXX", b"FIELDS", b"1", b"a", b"9"]),
2960 ":1\r\n"
2961 );
2962 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n9\r\n");
2963
2964 assert_eq!(
2965 f.run(&[b"HSETEX", b"gone", b"FXX", b"FIELDS", b"1", b"a", b"1"]),
2966 ":0\r\n"
2967 );
2968 assert_eq!(
2969 f.run(&[b"EXISTS", b"gone"]),
2970 ":0\r\n",
2971 "a key with no fields cannot meet FXX and is not created trying"
2972 );
2973 }
2974
2975 #[test]
2976 fn hsetex_clears_the_deadline_unless_it_is_told_to_keep_it() {
2977 let mut f = Fixture::new();
2978 f.run(&[b"HSETEX", b"h", b"EX", b"100", b"FIELDS", b"1", b"a", b"1"]);
2979 assert_eq!(
2980 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
2981 "*1\r\n:100\r\n"
2982 );
2983
2984 f.run(&[b"HSETEX", b"h", b"KEEPTTL", b"FIELDS", b"1", b"a", b"2"]);
2985 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n2\r\n");
2986 assert_eq!(
2987 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
2988 "*1\r\n:100\r\n",
2989 "KEEPTTL put back what the write cleared"
2990 );
2991
2992 f.run(&[b"HSETEX", b"h", b"FIELDS", b"1", b"a", b"3"]);
2993 assert_eq!(
2994 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
2995 "*1\r\n:-1\r\n",
2996 "and without it a write clears the deadline the way HSET does"
2997 );
2998
2999 assert_eq!(
3002 f.run(&[
3003 b"HSETEX", b"h", b"PX", b"100000", b"FXX", b"FIELDS", b"1", b"a", b"4"
3004 ]),
3005 ":1\r\n"
3006 );
3007 assert_eq!(
3008 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
3009 "*1\r\n:100\r\n"
3010 );
3011
3012 assert_eq!(
3013 f.run(&[b"HSETEX", b"h", b"EXAT", b"1", b"FIELDS", b"1", b"a", b"5"]),
3014 ":1\r\n",
3015 "written, and not the separate code the HEXPIRE family has for this"
3016 );
3017 assert_eq!(
3018 f.run(&[b"EXISTS", b"h"]),
3019 ":0\r\n",
3020 "and storing it and then removing it emptied the hash"
3021 );
3022 }
3023
3024 #[test]
3025 fn the_last_three_hash_commands_word_their_mistakes_their_own_way() {
3026 let mut f = Fixture::new();
3027 f.run(&[b"HSET", b"h", b"a", b"1"]);
3028 for (bad, want) in [
3029 (
3031 &[b"HGETDEL".as_slice(), b"h", b"FIELDS", b"0", b"a"][..],
3032 "-ERR Number of fields must be a positive integer",
3033 ),
3034 (
3035 &[b"HGETDEL".as_slice(), b"h", b"FIELDS", b"2", b"a"][..],
3036 "-ERR The `numfields` parameter must match the number of arguments",
3037 ),
3038 (
3039 &[b"HGETDEL".as_slice(), b"h", b"FIELD", b"1", b"a"][..],
3040 "-ERR Mandatory argument FIELDS is missing or not at the right position",
3041 ),
3042 (
3044 &[b"HGETEX".as_slice(), b"h", b"FIELDS", b"0", b"a"][..],
3045 "-ERR invalid number of fields",
3046 ),
3047 (
3048 &[b"HGETEX".as_slice(), b"h", b"FIELDS", b"2", b"a"][..],
3049 "-ERR wrong number of arguments",
3050 ),
3051 (
3052 &[b"HGETEX".as_slice(), b"h", b"FIELD", b"1", b"a"][..],
3053 "-ERR unknown argument: FIELD",
3054 ),
3055 (
3056 &[
3057 b"HGETEX".as_slice(),
3058 b"h",
3059 b"KEEPTTL",
3060 b"FIELDS",
3061 b"1",
3062 b"a",
3063 ][..],
3064 "-ERR unknown argument: KEEPTTL",
3065 ),
3066 (
3067 &[
3068 b"HGETEX".as_slice(),
3069 b"h",
3070 b"EX",
3071 b"100",
3072 b"PERSIST",
3073 b"FIELDS",
3074 b"1",
3075 b"a",
3076 ][..],
3077 "-ERR Only one of EX, PX, EXAT, PXAT or PERSIST arguments can be specified",
3078 ),
3079 (
3080 &[
3081 b"HSETEX".as_slice(),
3082 b"h",
3083 b"EX",
3084 b"1",
3085 b"KEEPTTL",
3086 b"FIELDS",
3087 b"1",
3088 b"a",
3089 b"1",
3090 ][..],
3091 "-ERR Only one of EX, PX, EXAT, PXAT or KEEPTTL arguments can be specified",
3092 ),
3093 (
3094 &[
3095 b"HSETEX".as_slice(),
3096 b"h",
3097 b"FNX",
3098 b"FXX",
3099 b"FIELDS",
3100 b"1",
3101 b"a",
3102 b"1",
3103 ][..],
3104 "-ERR Only one of FXX or FNX arguments can be specified",
3105 ),
3106 (
3107 &[
3108 b"HSETEX".as_slice(),
3109 b"h",
3110 b"FIELDS",
3111 b"2",
3112 b"a",
3113 b"1",
3114 b"b",
3115 ][..],
3116 "-ERR wrong number of arguments",
3117 ),
3118 (
3119 &[
3120 b"HGETEX".as_slice(),
3121 b"h",
3122 b"EX",
3123 b"-1",
3124 b"FIELDS",
3125 b"1",
3126 b"a",
3127 ][..],
3128 "-ERR invalid expire time, must be >= 0",
3129 ),
3130 (
3131 &[
3132 b"HGETEX".as_slice(),
3133 b"h",
3134 b"PXAT",
3135 b"99999999999999",
3136 b"FIELDS",
3137 b"1",
3138 b"a",
3139 ][..],
3140 "-ERR invalid expire time in 'hgetex' command",
3141 ),
3142 (
3143 &[
3144 b"HSETEX".as_slice(),
3145 b"h",
3146 b"EX",
3147 b"abc",
3148 b"FIELDS",
3149 b"1",
3150 b"a",
3151 b"1",
3152 ][..],
3153 "-ERR value is not an integer or out of range",
3154 ),
3155 ] {
3156 let reply = f.run(bad);
3157 assert!(reply.starts_with(want), "wanted {want}, got {reply}");
3158 assert!(!reply.contains('*'), "an array header went out in front");
3159 }
3160 assert_eq!(
3161 f.run(&[b"HGET", b"h", b"a"]),
3162 "$1\r\n1\r\n",
3163 "and not one of them wrote anything"
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 }
3170
3171 #[test]
3172 fn the_last_three_hash_commands_say_wrongtype_and_write_nothing() {
3173 let mut f = Fixture::new();
3174 f.run(&[b"SET", b"str", b"v"]);
3175 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
3176 for cmd in [
3177 &[b"HGETDEL".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
3178 &[b"HGETEX".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
3179 &[
3180 b"HGETEX".as_slice(),
3181 b"str",
3182 b"EX",
3183 b"100",
3184 b"FIELDS",
3185 b"1",
3186 b"f",
3187 ][..],
3188 &[b"HSETEX".as_slice(), b"str", b"FIELDS", b"1", b"f", b"v"][..],
3189 ] {
3190 assert_eq!(f.run(cmd), wrong, "{:?}", cmd[0]);
3191 }
3192 assert_eq!(f.run(&[b"GET", b"str"]), "$1\r\nv\r\n");
3193 }
3194
3195 fn int(reply: &str) -> i64 {
3201 let body = reply
3202 .strip_prefix(':')
3203 .and_then(|s| s.strip_suffix("\r\n"))
3204 .unwrap_or_else(|| panic!("wanted an integer, got {reply}"));
3205 body.parse().expect("an integer")
3206 }
3207
3208 fn int_reply(reply: &str) -> i64 {
3209 let body = reply
3210 .strip_prefix("*1\r\n:")
3211 .and_then(|s| s.strip_suffix("\r\n"))
3212 .unwrap_or_else(|| panic!("wanted one integer, got {reply}"));
3213 body.parse().expect("an integer")
3214 }
3215
3216 fn scan_reply(reply: &str) -> (String, Vec<String>) {
3218 let mut lines = reply.split("\r\n");
3219 assert_eq!(lines.next(), Some("*2"), "got {reply}");
3220 lines.next().expect("the cursor header");
3221 let cursor = lines.next().expect("a cursor").to_owned();
3222 let header = lines.next().expect("an item count");
3223 let n: usize = header[1..].parse().expect("a count");
3224 let mut items = Vec::with_capacity(n);
3225 for _ in 0..n {
3226 lines.next().expect("an item header");
3227 items.push(lines.next().expect("an item").to_owned());
3228 }
3229 (cursor, items)
3230 }
3231
3232 fn sorted(reply: &str) -> Vec<String> {
3235 let mut lines = reply.split("\r\n");
3236 let header = lines.next().expect("a header");
3237 assert!(
3238 header.starts_with('*') || header.starts_with('~'),
3239 "got {reply}"
3240 );
3241 let n: usize = header[1..].parse().expect("a member count");
3242 let mut got = Vec::with_capacity(n);
3243 for _ in 0..n {
3244 lines.next().expect("a member header");
3245 got.push(lines.next().expect("a member").to_owned());
3246 }
3247 got.sort();
3248 got
3249 }
3250
3251 #[test]
3252 fn the_algebra_answers_what_the_sets_share_and_do_not() {
3253 let mut f = Fixture::new();
3254 f.run(&[b"SADD", b"a", b"1", b"2", b"3"]);
3255 f.run(&[b"SADD", b"b", b"2", b"3", b"4"]);
3256 f.run(&[b"SADD", b"c", b"3", b"4", b"5"]);
3257
3258 assert_eq!(sorted(&f.run(&[b"SINTER", b"a", b"b", b"c"])), ["3"]);
3259 assert_eq!(
3260 sorted(&f.run(&[b"SUNION", b"a", b"b", b"c"])),
3261 ["1", "2", "3", "4", "5"]
3262 );
3263 assert_eq!(sorted(&f.run(&[b"SDIFF", b"a", b"b"])), ["1"]);
3264 assert_eq!(sorted(&f.run(&[b"SINTER", b"a"])), ["1", "2", "3"]);
3265
3266 assert_eq!(f.run(&[b"SINTER", b"a", b"nope"]), "*0\r\n");
3269 assert_eq!(sorted(&f.run(&[b"SUNION", b"a", b"nope"])), ["1", "2", "3"]);
3270 assert_eq!(f.run(&[b"SDIFF", b"nope", b"a"]), "*0\r\n");
3271 assert_eq!(f.run(&[b"DBSIZE"]), ":3\r\n", "and none of it made a key");
3272 }
3273
3274 #[test]
3275 fn the_algebra_answers_a_set_on_resp3_and_an_array_on_resp2() {
3276 let mut f = Fixture::new();
3277 f.run(&[b"SADD", b"a", b"x"]);
3278 assert_eq!(f.run(&[b"SINTER", b"a"]), "*1\r\n$1\r\nx\r\n");
3279 assert_eq!(f.run(&[b"SUNION", b"a"]), "*1\r\n$1\r\nx\r\n");
3280 assert_eq!(f.run(&[b"SDIFF", b"a"]), "*1\r\n$1\r\nx\r\n");
3281
3282 f.run(&[b"HELLO", b"3"]);
3283 assert_eq!(f.run(&[b"SINTER", b"a"]), "~1\r\n$1\r\nx\r\n");
3284 assert_eq!(f.run(&[b"SUNION", b"a"]), "~1\r\n$1\r\nx\r\n");
3285 assert_eq!(f.run(&[b"SDIFF", b"a"]), "~1\r\n$1\r\nx\r\n");
3286 assert_eq!(f.run(&[b"SINTER", b"nope"]), "~0\r\n");
3287 }
3288
3289 #[test]
3290 fn a_store_form_writes_a_key_and_answers_how_big_it_is() {
3291 let mut f = Fixture::new();
3292 f.run(&[b"SADD", b"a", b"1", b"2", b"3"]);
3293 f.run(&[b"SADD", b"b", b"2", b"3", b"4"]);
3294
3295 assert_eq!(f.run(&[b"SINTERSTORE", b"d", b"a", b"b"]), ":2\r\n");
3296 assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"d"])), ["2", "3"]);
3297 assert_eq!(f.run(&[b"SUNIONSTORE", b"d", b"a", b"b"]), ":4\r\n");
3298 assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"d"])), ["1", "2", "3", "4"]);
3299 assert_eq!(f.run(&[b"SDIFFSTORE", b"d", b"a", b"b"]), ":1\r\n");
3300 assert_eq!(f.run(&[b"SMEMBERS", b"d"]), "*1\r\n$1\r\n1\r\n");
3301
3302 assert_eq!(f.run(&[b"SDIFFSTORE", b"d", b"a", b"a"]), ":0\r\n");
3305 assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
3306 assert_eq!(f.run(&[b"SINTERSTORE", b"a", b"a", b"b"]), ":2\r\n");
3307 assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"a"])), ["2", "3"]);
3308
3309 f.run(&[b"SET", b"str", b"v"]);
3312 assert_eq!(f.run(&[b"SUNIONSTORE", b"str", b"b"]), ":3\r\n");
3313 assert_eq!(f.run(&[b"TYPE", b"str"]), "+set\r\n");
3314 }
3315
3316 #[test]
3317 fn sintercard_counts_without_building_and_stops_at_a_limit() {
3318 let mut f = Fixture::new();
3319 f.run(&[b"SADD", b"a", b"1", b"2", b"3", b"4"]);
3320 f.run(&[b"SADD", b"b", b"2", b"3", b"4", b"5"]);
3321
3322 assert_eq!(f.run(&[b"SINTERCARD", b"2", b"a", b"b"]), ":3\r\n");
3323 assert_eq!(
3324 f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"LIMIT", b"2"]),
3325 ":2\r\n"
3326 );
3327 assert_eq!(
3328 f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"LIMIT", b"0"]),
3329 ":3\r\n",
3330 "a limit of zero is no limit"
3331 );
3332 assert_eq!(f.run(&[b"SINTERCARD", b"1", b"a"]), ":4\r\n");
3333 assert_eq!(f.run(&[b"SINTERCARD", b"2", b"a", b"nope"]), ":0\r\n");
3334
3335 assert_eq!(
3337 f.run(&[b"SINTERCARD", b"0", b"a"]),
3338 "-ERR numkeys should be greater than 0\r\n"
3339 );
3340 assert_eq!(
3341 f.run(&[b"SINTERCARD", b"abc", b"a"]),
3342 "-ERR numkeys should be greater than 0\r\n"
3343 );
3344 assert_eq!(
3345 f.run(&[b"SINTERCARD", b"3", b"a", b"b"]),
3346 "-ERR Number of keys can't be greater than number of args\r\n"
3347 );
3348 assert_eq!(
3349 f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"LIMIT", b"-1"]),
3350 "-ERR LIMIT can't be negative\r\n"
3351 );
3352 assert_eq!(
3353 f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"NOPE", b"1"]),
3354 "-ERR syntax error\r\n"
3355 );
3356 f.run(&[b"SADD", b"LIMIT", b"2"]);
3358 assert_eq!(f.run(&[b"SINTERCARD", b"2", b"a", b"LIMIT"]), ":1\r\n");
3359 }
3360
3361 #[test]
3362 fn the_algebra_answers_wrongtype_before_it_writes_anything() {
3363 let mut f = Fixture::new();
3364 f.run(&[b"SADD", b"a", b"1"]);
3365 f.run(&[b"SADD", b"d", b"old"]);
3366 f.run(&[b"SET", b"str", b"v"]);
3367
3368 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
3369 for bad in [
3370 &[b"SINTER".as_slice(), b"a", b"str"][..],
3371 &[b"SUNION".as_slice(), b"str"][..],
3372 &[b"SDIFF".as_slice(), b"a", b"str"][..],
3373 &[b"SINTERCARD".as_slice(), b"2", b"a", b"str"][..],
3374 &[b"SINTERSTORE".as_slice(), b"d", b"a", b"str"][..],
3375 &[b"SUNIONSTORE".as_slice(), b"d", b"str"][..],
3376 &[b"SDIFFSTORE".as_slice(), b"d", b"a", b"str"][..],
3377 ] {
3378 let reply = f.run(bad);
3379 assert_eq!(reply, wrong, "for {:?}", bad[0]);
3380 }
3381 assert_eq!(
3382 f.run(&[b"SMEMBERS", b"d"]),
3383 "*1\r\n$3\r\nold\r\n",
3384 "and the destination was left alone every time"
3385 );
3386 }
3387
3388 #[test]
3391 fn churning_sets_does_not_grow_the_server() {
3392 let mut f = Fixture::new();
3393 let members: Vec<Vec<u8>> = (0..200).map(|i| format!("m{i}").into_bytes()).collect();
3394 let args: Vec<&[u8]> = std::iter::once(&b"SADD"[..])
3395 .chain(std::iter::once(&b"s"[..]))
3396 .chain(members.iter().map(Vec::as_slice))
3397 .collect();
3398
3399 f.run(&args);
3400 f.run(&[b"DEL", b"s"]);
3401 f.server.compact_step();
3402 let after_first = f.server.memory_bytes();
3403
3404 for _ in 0..200 {
3405 f.run(&args);
3406 f.run(&[b"DEL", b"s"]);
3407 f.server.compact_step();
3408 }
3409 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
3410 assert!(
3411 f.server.memory_bytes() <= after_first * 2,
3412 "held {} after two hundred passes against {after_first} after one",
3413 f.server.memory_bytes()
3414 );
3415 }
3416
3417 fn bulks(parts: &[&str]) -> String {
3420 let mut s = format!("*{}\r\n", parts.len());
3421 for p in parts {
3422 s.push_str(&format!("${}\r\n{p}\r\n", p.len()));
3423 }
3424 s
3425 }
3426
3427 #[test]
3428 fn a_list_is_pushed_from_both_ends_and_the_left_one_reverses() {
3429 let mut f = Fixture::new();
3430 assert_eq!(f.run(&[b"LPUSH", b"k", b"a", b"b", b"c"]), ":3\r\n");
3434 assert_eq!(
3435 f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
3436 bulks(&["c", "b", "a"])
3437 );
3438 assert_eq!(f.run(&[b"RPUSH", b"k", b"d"]), ":4\r\n");
3439 assert_eq!(f.run(&[b"LLEN", b"k"]), ":4\r\n");
3440 assert_eq!(f.run(&[b"LPOP", b"k"]), "$1\r\nc\r\n");
3441 assert_eq!(f.run(&[b"RPOP", b"k"]), "$1\r\nd\r\n");
3442 assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["b", "a"]));
3443 assert_eq!(f.run(&[b"TYPE", b"k"]), "+list\r\n");
3444 }
3445
3446 #[test]
3447 fn the_x_pushes_refuse_to_bring_a_list_back_to_life() {
3448 let mut f = Fixture::new();
3449 assert_eq!(f.run(&[b"LPUSHX", b"k", b"a"]), ":0\r\n");
3450 assert_eq!(f.run(&[b"RPUSHX", b"k", b"a"]), ":0\r\n");
3451 assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
3452 f.run(&[b"RPUSH", b"k", b"a"]);
3453 assert_eq!(f.run(&[b"LPUSHX", b"k", b"z"]), ":2\r\n");
3454 assert_eq!(f.run(&[b"RPUSHX", b"k", b"y"]), ":3\r\n");
3455 assert_eq!(
3456 f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
3457 bulks(&["z", "a", "y"])
3458 );
3459 }
3460
3461 #[test]
3464 fn an_empty_pop_is_a_different_nothing_with_a_count_and_without() {
3465 let mut f = Fixture::new();
3466 assert_eq!(f.run(&[b"LPOP", b"nope"]), "$-1\r\n");
3467 assert_eq!(f.run(&[b"LPOP", b"nope", b"2"]), "*-1\r\n");
3468 assert_eq!(f.run(&[b"RPOP", b"nope"]), "$-1\r\n");
3469 assert_eq!(f.run(&[b"RPOP", b"nope", b"2"]), "*-1\r\n");
3470 f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
3471 assert_eq!(f.run(&[b"LPOP", b"k", b"0"]), "*0\r\n");
3474 assert_eq!(f.run(&[b"LPOP", b"k", b"1"]), bulks(&["a"]));
3475 assert_eq!(f.run(&[b"RPOP", b"k", b"9"]), bulks(&["c", "b"]));
3477 assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
3478 }
3479
3480 #[test]
3481 fn a_pop_count_has_its_own_sentence_and_a_third_argument_is_an_arity_error() {
3482 let mut f = Fixture::new();
3483 f.run(&[b"RPUSH", b"k", b"a"]);
3484 let range = "-ERR value is out of range, must be positive\r\n";
3485 assert_eq!(f.run(&[b"LPOP", b"k", b"-1"]), range);
3486 assert_eq!(f.run(&[b"LPOP", b"k", b"abc"]), range);
3487 assert_eq!(f.run(&[b"RPOP", b"k", b"-1"]), range);
3488 assert_eq!(
3491 f.run(&[b"LPOP", b"k", b"1", b"2"]),
3492 "-ERR wrong number of arguments for 'lpop' command\r\n"
3493 );
3494 assert_eq!(f.run(&[b"LLEN", b"k"]), ":1\r\n");
3495 }
3496
3497 #[test]
3498 fn a_range_takes_negative_ends_and_clamps_the_ones_that_run_off() {
3499 let mut f = Fixture::new();
3500 f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
3501 assert_eq!(
3502 f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
3503 bulks(&["a", "b", "c"])
3504 );
3505 assert_eq!(f.run(&[b"LRANGE", b"k", b"-2", b"-1"]), bulks(&["b", "c"]));
3506 assert_eq!(f.run(&[b"LRANGE", b"k", b"1", b"1"]), bulks(&["b"]));
3507 assert_eq!(f.run(&[b"LRANGE", b"k", b"5", b"10"]), "*0\r\n");
3508 assert_eq!(f.run(&[b"LRANGE", b"k", b"2", b"1"]), "*0\r\n");
3509 assert_eq!(
3510 f.run(&[b"LRANGE", b"k", b"-100", b"100"]),
3511 bulks(&["a", "b", "c"])
3512 );
3513 assert_eq!(f.run(&[b"LRANGE", b"nope", b"0", b"-1"]), "*0\r\n");
3516 assert_eq!(
3517 f.run(&[b"LRANGE", b"k", b"a", b"b"]),
3518 "-ERR value is not an integer or out of range\r\n"
3519 );
3520 }
3521
3522 #[test]
3523 fn an_index_reads_and_writes_from_whichever_end_is_nearer() {
3524 let mut f = Fixture::new();
3525 f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
3526 assert_eq!(f.run(&[b"LINDEX", b"k", b"0"]), "$1\r\na\r\n");
3527 assert_eq!(f.run(&[b"LINDEX", b"k", b"-1"]), "$1\r\nc\r\n");
3528 assert_eq!(f.run(&[b"LINDEX", b"k", b"99"]), "$-1\r\n");
3529 assert_eq!(f.run(&[b"LINDEX", b"nope", b"0"]), "$-1\r\n");
3530 assert_eq!(f.run(&[b"LSET", b"k", b"-1", b"z"]), "+OK\r\n");
3531 assert_eq!(
3532 f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
3533 bulks(&["a", "b", "z"])
3534 );
3535 assert_eq!(
3538 f.run(&[b"LSET", b"k", b"99", b"z"]),
3539 "-ERR index out of range\r\n"
3540 );
3541 assert_eq!(
3542 f.run(&[b"LSET", b"nope", b"0", b"z"]),
3543 "-ERR no such key\r\n"
3544 );
3545 }
3546
3547 #[test]
3548 fn linsert_says_three_things_with_one_signed_number() {
3549 let mut f = Fixture::new();
3550 assert_eq!(
3553 f.run(&[b"LINSERT", b"nope", b"BEFORE", b"a", b"x"]),
3554 ":0\r\n"
3555 );
3556 f.run(&[b"RPUSH", b"k", b"a", b"b"]);
3557 assert_eq!(f.run(&[b"LINSERT", b"k", b"before", b"a", b"X"]), ":3\r\n");
3558 assert_eq!(f.run(&[b"LINSERT", b"k", b"AFTER", b"b", b"Y"]), ":4\r\n");
3559 assert_eq!(
3560 f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
3561 bulks(&["X", "a", "b", "Y"])
3562 );
3563 assert_eq!(
3564 f.run(&[b"LINSERT", b"k", b"BEFORE", b"zz", b"x"]),
3565 ":-1\r\n"
3566 );
3567 assert_eq!(
3568 f.run(&[b"LINSERT", b"k", b"SIDEWAYS", b"a", b"x"]),
3569 "-ERR syntax error\r\n"
3570 );
3571 }
3572
3573 #[test]
3574 fn lrem_counts_in_three_directions_and_takes_the_key_when_it_empties() {
3575 let mut f = Fixture::new();
3576 f.run(&[b"RPUSH", b"k", b"a", b"b", b"a", b"c", b"a"]);
3577 assert_eq!(f.run(&[b"LREM", b"k", b"2", b"a"]), ":2\r\n");
3578 assert_eq!(
3579 f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
3580 bulks(&["b", "c", "a"])
3581 );
3582 assert_eq!(f.run(&[b"LREM", b"k", b"-1", b"a"]), ":1\r\n");
3583 assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["b", "c"]));
3584 assert_eq!(f.run(&[b"LREM", b"k", b"0", b"b"]), ":1\r\n");
3585 assert_eq!(f.run(&[b"LREM", b"k", b"0", b"c"]), ":1\r\n");
3586 assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
3587 assert_eq!(f.run(&[b"LREM", b"nope", b"0", b"a"]), ":0\r\n");
3588 }
3589
3590 #[test]
3591 fn ltrim_keeps_a_window_and_an_empty_one_deletes_the_key() {
3592 let mut f = Fixture::new();
3593 f.run(&[b"RPUSH", b"k", b"a", b"b", b"c", b"d"]);
3594 assert_eq!(f.run(&[b"LTRIM", b"k", b"1", b"-2"]), "+OK\r\n");
3595 assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["b", "c"]));
3596 assert_eq!(f.run(&[b"LTRIM", b"k", b"1", b"0"]), "+OK\r\n");
3599 assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
3600 assert_eq!(f.run(&[b"LTRIM", b"nope", b"0", b"-1"]), "+OK\r\n");
3601 }
3602
3603 #[test]
3604 fn lpos_walks_from_either_end_and_stops_where_it_is_told() {
3605 let mut f = Fixture::new();
3606 f.run(&[b"RPUSH", b"p", b"a", b"b", b"c", b"a", b"b", b"c", b"a"]);
3607 assert_eq!(f.run(&[b"LPOS", b"p", b"a"]), ":0\r\n");
3608 assert_eq!(f.run(&[b"LPOS", b"p", b"a", b"RANK", b"-1"]), ":6\r\n");
3609 assert_eq!(f.run(&[b"LPOS", b"p", b"a", b"RANK", b"2"]), ":3\r\n");
3610 assert_eq!(
3611 f.run(&[b"LPOS", b"p", b"a", b"COUNT", b"2"]),
3612 "*2\r\n:0\r\n:3\r\n"
3613 );
3614 assert_eq!(
3615 f.run(&[b"LPOS", b"p", b"a", b"RANK", b"-1", b"COUNT", b"0"]),
3616 "*3\r\n:6\r\n:3\r\n:0\r\n"
3617 );
3618 assert_eq!(
3621 f.run(&[b"LPOS", b"p", b"a", b"COUNT", b"0", b"MAXLEN", b"3"]),
3622 "*1\r\n:0\r\n"
3623 );
3624 assert_eq!(f.run(&[b"LPOS", b"p", b"zz"]), "$-1\r\n");
3627 assert_eq!(f.run(&[b"LPOS", b"p", b"zz", b"COUNT", b"0"]), "*0\r\n");
3628 assert_eq!(f.run(&[b"LPOS", b"nope", b"a"]), "$-1\r\n");
3629 assert_eq!(f.run(&[b"LPOS", b"nope", b"a", b"COUNT", b"2"]), "*0\r\n");
3630 }
3631
3632 #[test]
3633 fn lpos_words_its_three_mistakes_the_way_redis_does() {
3634 let mut f = Fixture::new();
3635 f.run(&[b"RPUSH", b"p", b"a"]);
3636 assert_eq!(
3639 f.run(&[b"LPOS", b"p", b"a", b"RANK", b"0"]),
3640 "-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"
3641 );
3642 assert_eq!(
3643 f.run(&[b"LPOS", b"p", b"a", b"COUNT", b"-1"]),
3644 "-ERR COUNT can't be negative\r\n"
3645 );
3646 assert_eq!(
3647 f.run(&[b"LPOS", b"p", b"a", b"MAXLEN", b"-1"]),
3648 "-ERR MAXLEN can't be negative\r\n"
3649 );
3650 assert_eq!(
3651 f.run(&[b"LPOS", b"p", b"a", b"RANK"]),
3652 "-ERR syntax error\r\n"
3653 );
3654 assert_eq!(
3655 f.run(&[b"LPOS", b"p", b"a", b"FOO", b"1"]),
3656 "-ERR syntax error\r\n"
3657 );
3658 }
3659
3660 #[test]
3661 fn a_move_takes_from_one_end_and_gives_to_another_even_on_one_key() {
3662 let mut f = Fixture::new();
3663 f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
3664 assert_eq!(f.run(&[b"RPOPLPUSH", b"k", b"d"]), "$1\r\nc\r\n");
3665 assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["a", "b"]));
3666 assert_eq!(f.run(&[b"LRANGE", b"d", b"0", b"-1"]), bulks(&["c"]));
3667 assert_eq!(
3668 f.run(&[b"LMOVE", b"k", b"d", b"LEFT", b"RIGHT"]),
3669 "$1\r\na\r\n"
3670 );
3671 assert_eq!(f.run(&[b"LRANGE", b"d", b"0", b"-1"]), bulks(&["c", "a"]));
3672 f.run(&[b"DEL", b"r"]);
3675 f.run(&[b"RPUSH", b"r", b"1", b"2", b"3"]);
3676 assert_eq!(f.run(&[b"RPOPLPUSH", b"r", b"r"]), "$1\r\n3\r\n");
3677 assert_eq!(
3678 f.run(&[b"LRANGE", b"r", b"0", b"-1"]),
3679 bulks(&["3", "1", "2"])
3680 );
3681 assert_eq!(
3682 f.run(&[b"LMOVE", b"nope", b"d", b"LEFT", b"LEFT"]),
3683 "$-1\r\n"
3684 );
3685 assert_eq!(
3686 f.run(&[b"LMOVE", b"r", b"d", b"LEFT", b"SIDEWAYS"]),
3687 "-ERR syntax error\r\n"
3688 );
3689 }
3690
3691 #[test]
3692 fn a_move_checks_the_destination_before_it_takes_anything() {
3693 let mut f = Fixture::new();
3694 f.run(&[b"RPUSH", b"k", b"a", b"b"]);
3695 f.run(&[b"SET", b"str", b"v"]);
3696 assert_eq!(
3697 f.run(&[b"LMOVE", b"k", b"str", b"LEFT", b"LEFT"]),
3698 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
3699 );
3700 assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["a", "b"]));
3702 }
3703
3704 #[test]
3705 fn lmpop_answers_from_the_first_key_that_has_anything() {
3706 let mut f = Fixture::new();
3707 f.run(&[b"RPUSH", b"b", b"1", b"2", b"3"]);
3708 assert_eq!(
3711 f.run(&[b"LMPOP", b"2", b"a", b"b", b"LEFT", b"COUNT", b"2"]),
3712 "*2\r\n$1\r\nb\r\n*2\r\n$1\r\n1\r\n$1\r\n2\r\n"
3713 );
3714 assert_eq!(
3715 f.run(&[b"LMPOP", b"2", b"a", b"b", b"RIGHT"]),
3716 "*2\r\n$1\r\nb\r\n*1\r\n$1\r\n3\r\n"
3717 );
3718 assert_eq!(f.run(&[b"EXISTS", b"b"]), ":0\r\n");
3719 assert_eq!(f.run(&[b"LMPOP", b"2", b"a", b"b", b"LEFT"]), "*-1\r\n");
3722 }
3723
3724 #[test]
3725 fn lmpop_has_its_own_words_for_a_count_and_for_a_key_count() {
3726 let mut f = Fixture::new();
3727 f.run(&[b"RPUSH", b"k", b"a"]);
3728 assert_eq!(
3729 f.run(&[b"LMPOP", b"0", b"k", b"LEFT"]),
3730 "-ERR numkeys should be greater than 0\r\n"
3731 );
3732 assert_eq!(
3733 f.run(&[b"LMPOP", b"-1", b"k", b"LEFT"]),
3734 "-ERR numkeys should be greater than 0\r\n"
3735 );
3736 assert_eq!(
3737 f.run(&[b"LMPOP", b"1", b"k", b"LEFT", b"COUNT", b"0"]),
3738 "-ERR count should be greater than 0\r\n"
3739 );
3740 assert_eq!(
3743 f.run(&[b"LMPOP", b"3", b"k", b"LEFT"]),
3744 "-ERR syntax error\r\n"
3745 );
3746 assert_eq!(
3747 f.run(&[b"LMPOP", b"1", b"k", b"LEFT", b"COUNT", b"1", b"x"]),
3748 "-ERR syntax error\r\n"
3749 );
3750 assert_eq!(
3751 f.run(&[b"LMPOP", b"1", b"k", b"LEFT", b"FOO", b"1"]),
3752 "-ERR syntax error\r\n"
3753 );
3754 assert_eq!(
3755 f.run(&[b"LMPOP", b"1", b"k", b"SIDEWAYS"]),
3756 "-ERR syntax error\r\n"
3757 );
3758 assert_eq!(f.run(&[b"LLEN", b"k"]), ":1\r\n");
3759 }
3760
3761 #[test]
3762 fn every_list_command_says_wrongtype_and_writes_nothing() {
3763 let mut f = Fixture::new();
3764 f.run(&[b"SET", b"str", b"v"]);
3765 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
3766 for cmd in [
3767 &[b"LPUSH".as_slice(), b"str", b"a"][..],
3768 &[b"RPUSH", b"str", b"a"],
3769 &[b"LPUSHX", b"str", b"a"],
3770 &[b"RPUSHX", b"str", b"a"],
3771 &[b"LPOP", b"str"],
3772 &[b"LPOP", b"str", b"2"],
3773 &[b"RPOP", b"str"],
3774 &[b"LLEN", b"str"],
3775 &[b"LRANGE", b"str", b"0", b"-1"],
3776 &[b"LINDEX", b"str", b"0"],
3777 &[b"LSET", b"str", b"0", b"a"],
3778 &[b"LINSERT", b"str", b"BEFORE", b"a", b"b"],
3779 &[b"LREM", b"str", b"0", b"a"],
3780 &[b"LTRIM", b"str", b"0", b"-1"],
3781 &[b"LPOS", b"str", b"a"],
3782 &[b"LPOS", b"str", b"a", b"COUNT", b"0"],
3783 &[b"RPOPLPUSH", b"str", b"d"],
3784 &[b"LMOVE", b"str", b"d", b"LEFT", b"LEFT"],
3785 &[b"LMPOP", b"1", b"str", b"LEFT"],
3786 ] {
3787 assert_eq!(f.run(cmd), wrong, "{:?}", String::from_utf8_lossy(cmd[0]));
3788 }
3789 assert_eq!(f.run(&[b"GET", b"str"]), "$1\r\nv\r\n");
3790 assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
3791 }
3792
3793 #[test]
3797 fn a_timeout_has_three_ways_of_being_wrong() {
3798 let mut f = Fixture::new();
3799 let not_float = "-ERR timeout is not a float or out of range\r\n";
3800 let range = "-ERR timeout is out of range\r\n";
3801 for (bad, want) in [
3802 (&[b"BLPOP".as_slice(), b"k", b"abc"][..], not_float),
3803 (&[b"BLPOP", b"k", b"nan"], not_float),
3804 (&[b"BLPOP", b"k", b""], not_float),
3805 (&[b"BLPOP", b"k", b" 1"], not_float),
3808 (&[b"BLPOP", b"k", b"1 "], not_float),
3809 (&[b"BLPOP", b"k", b"-1"], "-ERR timeout is negative\r\n"),
3810 (&[b"BLPOP", b"k", b"-0.1"], "-ERR timeout is negative\r\n"),
3811 (&[b"BLPOP", b"k", b"1e400"], range),
3814 (&[b"BLPOP", b"k", b"inf"], range),
3815 (&[b"BLPOP", b"k", b"9999999999999999"], range),
3816 (&[b"BRPOP", b"k", b"abc"], not_float),
3817 (
3818 &[b"BLMOVE", b"a", b"b", b"LEFT", b"RIGHT", b"abc"],
3819 not_float,
3820 ),
3821 (
3822 &[b"BRPOPLPUSH", b"a", b"b", b"-1"],
3823 "-ERR timeout is negative\r\n",
3824 ),
3825 (&[b"BLMPOP", b"abc", b"1", b"k", b"LEFT"], not_float),
3826 ] {
3827 assert_eq!(f.run(bad), want, "for {bad:?}");
3828 }
3829 }
3830
3831 #[test]
3834 fn a_zero_timeout_waits_and_the_smallest_positive_one_does_not() {
3835 let mut f = Fixture::new();
3836 for timeout in [b"0".as_slice(), b"0.0", b"-0.0"] {
3837 let (flow, out) = f.flow(&[b"BLPOP", b"k", timeout]);
3838 assert_eq!(flow, Flow::Block, "for {timeout:?}");
3839 assert!(out.is_empty(), "for {timeout:?}");
3840 }
3841 let (flow, out) = f.flow(&[b"BLPOP", b"k", b"0.0000001"]);
3845 assert_eq!(flow, Flow::Block);
3846 assert!(out.is_empty());
3847 }
3848
3849 #[test]
3850 fn a_blocking_command_that_can_be_answered_answers_like_the_one_it_wraps() {
3851 let mut f = Fixture::new();
3852 f.run(&[b"RPUSH", b"L", b"a", b"b", b"c", b"d", b"e"]);
3853
3854 assert_eq!(
3857 f.flow(&[b"BLPOP", b"nope", b"L", b"0"]),
3858 (Flow::Continue, "*2\r\n$1\r\nL\r\n$1\r\na\r\n".to_owned())
3859 );
3860 assert_eq!(
3861 f.run(&[b"BRPOP", b"L", b"0"]),
3862 "*2\r\n$1\r\nL\r\n$1\r\ne\r\n"
3863 );
3864 assert_eq!(
3865 f.run(&[
3866 b"BLMPOP", b"0", b"2", b"nope", b"L", b"LEFT", b"COUNT", b"2"
3867 ]),
3868 "*2\r\n$1\r\nL\r\n*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
3869 );
3870 assert_eq!(
3871 f.run(&[b"BLMOVE", b"L", b"D", b"LEFT", b"RIGHT", b"0"]),
3872 "$1\r\nd\r\n"
3873 );
3874 assert_eq!(
3875 f.run(&[b"EXISTS", b"L"]),
3876 ":0\r\n",
3877 "and the key went with it"
3878 );
3879 assert_eq!(f.run(&[b"LRANGE", b"D", b"0", b"-1"]), "*1\r\n$1\r\nd\r\n");
3880 f.run(&[b"RPUSH", b"D", b"x"]);
3883 assert_eq!(f.run(&[b"BRPOPLPUSH", b"D", b"D", b"0"]), "$1\r\nx\r\n");
3884 assert_eq!(
3885 f.run(&[b"LRANGE", b"D", b"0", b"-1"]),
3886 "*2\r\n$1\r\nx\r\n$1\r\nd\r\n"
3887 );
3888 }
3889
3890 #[test]
3891 fn blmpop_reads_its_count_and_its_key_count_the_way_lmpop_does() {
3892 let mut f = Fixture::new();
3893 f.run(&[b"RPUSH", b"k", b"a"]);
3894 for (bad, want) in [
3895 (
3896 &[b"BLMPOP".as_slice(), b"0", b"0", b"k", b"LEFT"][..],
3897 "-ERR numkeys should be greater than 0\r\n",
3898 ),
3899 (
3900 &[b"BLMPOP", b"0", b"-1", b"k", b"LEFT"],
3901 "-ERR numkeys should be greater than 0\r\n",
3902 ),
3903 (
3906 &[b"BLMPOP", b"0", b"2", b"k", b"LEFT"],
3907 "-ERR syntax error\r\n",
3908 ),
3909 (
3910 &[b"BLMPOP", b"0", b"1", b"k", b"SIDEWAYS"],
3911 "-ERR syntax error\r\n",
3912 ),
3913 (
3914 &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT"],
3915 "-ERR syntax error\r\n",
3916 ),
3917 (
3918 &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT", b"2", b"x"],
3919 "-ERR syntax error\r\n",
3920 ),
3921 (
3924 &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT", b"0"],
3925 "-ERR count should be greater than 0\r\n",
3926 ),
3927 (
3928 &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT", b"abc"],
3929 "-ERR count should be greater than 0\r\n",
3930 ),
3931 ] {
3932 assert_eq!(f.run(bad), want, "for {bad:?}");
3933 }
3934 assert_eq!(f.run(&[b"LLEN", b"k"]), ":1\r\n", "and none of them popped");
3935 }
3936
3937 #[test]
3938 fn a_blocking_move_reads_its_directions_before_its_timeout() {
3939 let mut f = Fixture::new();
3940 assert_eq!(
3943 f.run(&[b"BLMOVE", b"a", b"b", b"UP", b"DOWN", b"abc"]),
3944 "-ERR syntax error\r\n"
3945 );
3946 assert_eq!(
3947 f.run(&[b"BLMOVE", b"a", b"b", b"LEFT", b"DOWN", b"0.05"]),
3948 "-ERR syntax error\r\n"
3949 );
3950 }
3951
3952 #[test]
3955 fn a_blocking_command_errors_on_a_wrong_type_rather_than_waiting_on_it() {
3956 let mut f = Fixture::new();
3957 f.run(&[b"SET", b"S", b"v"]);
3958 f.run(&[b"RPUSH", b"D", b"x"]);
3959 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
3960
3961 assert_eq!(f.run(&[b"BLPOP", b"S", b"0"]), wrong);
3962 assert_eq!(f.run(&[b"BLPOP", b"E", b"S", b"0"]), wrong);
3965 assert_eq!(f.run(&[b"BRPOP", b"S", b"0"]), wrong);
3966 assert_eq!(f.run(&[b"BLMPOP", b"0", b"1", b"S", b"LEFT"]), wrong);
3967 assert_eq!(f.run(&[b"BRPOPLPUSH", b"S", b"D", b"0"]), wrong);
3968 assert_eq!(f.run(&[b"BRPOPLPUSH", b"D", b"S", b"0"]), wrong);
3971 assert_eq!(f.run(&[b"LRANGE", b"D", b"0", b"-1"]), "*1\r\n$1\r\nx\r\n");
3972
3973 assert_eq!(
3977 f.flow(&[b"BLMOVE", b"E", b"S", b"LEFT", b"RIGHT", b"0.1"])
3978 .0,
3979 Flow::Block
3980 );
3981 }
3982
3983 #[test]
3987 fn churning_lists_does_not_grow_the_server() {
3988 let mut f = Fixture::new();
3989 let vals: Vec<Vec<u8>> = (0..200).map(|i| format!("v{i}").into_bytes()).collect();
3990 let args: Vec<&[u8]> = [&b"RPUSH"[..], &b"k"[..]]
3991 .into_iter()
3992 .chain(vals.iter().map(Vec::as_slice))
3993 .collect();
3994
3995 f.run(&args);
3996 f.run(&[b"DEL", b"k"]);
3997 f.server.compact_step();
3998 let after_first = f.server.memory_bytes();
3999
4000 for _ in 0..200 {
4001 f.run(&args);
4002 f.run(&[b"LTRIM", b"k", b"1", b"0"]);
4003 f.server.compact_step();
4004 }
4005 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
4006 assert!(
4007 f.server.memory_bytes() <= after_first * 2,
4008 "held {} after two hundred passes against {after_first} after one",
4009 f.server.memory_bytes()
4010 );
4011 }
4012
4013 #[test]
4016 fn a_sorted_set_takes_scores_and_gives_them_back() {
4017 let mut f = Fixture::new();
4018 assert_eq!(f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b"]), ":2\r\n");
4019 assert_eq!(f.run(&[b"ZADD", b"z", b"1", b"a", b"3", b"c"]), ":1\r\n");
4020 assert_eq!(f.run(&[b"ZCARD", b"z"]), ":3\r\n");
4021 assert_eq!(f.run(&[b"ZSCORE", b"z", b"b"]), "$1\r\n2\r\n");
4022 assert_eq!(f.run(&[b"ZSCORE", b"z", b"nope"]), "$-1\r\n");
4023 assert_eq!(f.run(&[b"ZSCORE", b"nokey", b"b"]), "$-1\r\n");
4024 assert_eq!(
4025 f.run(&[b"ZMSCORE", b"z", b"a", b"nope", b"c"]),
4026 "*3\r\n$1\r\n1\r\n$-1\r\n$1\r\n3\r\n"
4027 );
4028 assert_eq!(f.run(&[b"ZREM", b"z", b"a", b"nope"]), ":1\r\n");
4029 assert_eq!(f.run(&[b"ZCARD", b"z"]), ":2\r\n");
4030 assert_eq!(f.run(&[b"ZREM", b"z", b"b", b"c"]), ":2\r\n");
4032 assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
4033 }
4034
4035 #[test]
4036 fn a_score_is_a_double_on_resp3_and_digits_on_resp2() {
4037 let mut f = Fixture::new();
4038 f.run(&[b"ZADD", b"z", b"1.5", b"a", b"inf", b"b", b"-inf", b"c"]);
4039 assert_eq!(f.run(&[b"ZSCORE", b"z", b"a"]), "$3\r\n1.5\r\n");
4040 assert_eq!(f.run(&[b"ZSCORE", b"z", b"b"]), "$3\r\ninf\r\n");
4041 assert_eq!(f.run(&[b"ZSCORE", b"z", b"c"]), "$4\r\n-inf\r\n");
4042
4043 f.out = Out::new(Proto::Resp3);
4044 assert_eq!(f.run(&[b"ZSCORE", b"z", b"a"]), ",1.5\r\n");
4045 assert_eq!(f.run(&[b"ZSCORE", b"z", b"b"]), ",inf\r\n");
4046 assert_eq!(f.run(&[b"ZSCORE", b"z", b"c"]), ",-inf\r\n");
4047 assert_eq!(f.run(&[b"ZSCORE", b"z", b"nope"]), "_\r\n");
4048 }
4049
4050 #[test]
4051 fn the_zadd_options_gate_what_gets_written() {
4052 let mut f = Fixture::new();
4053 f.run(&[b"ZADD", b"z", b"5", b"a"]);
4054 assert_eq!(f.run(&[b"ZADD", b"z", b"NX", b"9", b"a"]), ":0\r\n");
4056 assert_eq!(f.run(&[b"ZSCORE", b"z", b"a"]), "$1\r\n5\r\n");
4057 assert_eq!(f.run(&[b"ZADD", b"z", b"XX", b"9", b"new"]), ":0\r\n");
4058 assert_eq!(f.run(&[b"EXISTS", b"z"]), ":1\r\n");
4059 assert_eq!(f.run(&[b"ZADD", b"z", b"GT", b"CH", b"3", b"a"]), ":0\r\n");
4061 assert_eq!(f.run(&[b"ZADD", b"z", b"GT", b"CH", b"7", b"a"]), ":1\r\n");
4062 assert_eq!(f.run(&[b"ZADD", b"z", b"LT", b"CH", b"9", b"a"]), ":0\r\n");
4063 assert_eq!(f.run(&[b"ZADD", b"z", b"1", b"a", b"1", b"b"]), ":1\r\n");
4065 assert_eq!(
4066 f.run(&[b"ZADD", b"z", b"CH", b"2", b"a", b"2", b"c"]),
4067 ":2\r\n"
4068 );
4069 }
4070
4071 #[test]
4072 fn zadd_incr_answers_a_score_or_nothing_at_all() {
4073 let mut f = Fixture::new();
4074 assert_eq!(f.run(&[b"ZADD", b"z", b"INCR", b"5", b"m"]), "$1\r\n5\r\n");
4075 assert_eq!(f.run(&[b"ZADD", b"z", b"INCR", b"2", b"m"]), "$1\r\n7\r\n");
4076 assert_eq!(
4079 f.run(&[b"ZADD", b"z", b"NX", b"INCR", b"2", b"m"]),
4080 "$-1\r\n"
4081 );
4082 assert_eq!(
4083 f.run(&[b"ZADD", b"z", b"XX", b"INCR", b"2", b"gone"]),
4084 "$-1\r\n"
4085 );
4086 assert_eq!(
4087 f.run(&[b"ZADD", b"z", b"GT", b"INCR", b"-1", b"m"]),
4088 "$-1\r\n"
4089 );
4090 assert_eq!(
4091 f.run(&[b"ZADD", b"z", b"GT", b"INCR", b"1", b"m"]),
4092 "$1\r\n8\r\n"
4093 );
4094 assert_eq!(f.run(&[b"ZINCRBY", b"z", b"2", b"m"]), "$2\r\n10\r\n");
4095 assert_eq!(f.run(&[b"ZINCRBY", b"z", b"1", b"fresh"]), "$1\r\n1\r\n");
4096 }
4097
4098 #[test]
4099 fn the_two_infinities_will_not_be_added_together() {
4100 let mut f = Fixture::new();
4101 f.run(&[b"ZADD", b"z", b"inf", b"m"]);
4102 let nan = "-ERR resulting score is not a number (NaN)\r\n";
4103 assert_eq!(f.run(&[b"ZINCRBY", b"z", b"-inf", b"m"]), nan);
4104 assert_eq!(f.run(&[b"ZADD", b"z", b"INCR", b"-inf", b"m"]), nan);
4105 assert_eq!(f.run(&[b"ZSCORE", b"z", b"m"]), "$3\r\ninf\r\n");
4106 assert_eq!(f.run(&[b"ZINCRBY", b"gone", b"1", b"m"]), "$1\r\n1\r\n");
4108 }
4109
4110 #[test]
4111 fn zadd_says_its_mistakes_the_way_redis_says_them() {
4112 let mut f = Fixture::new();
4113 assert_eq!(
4116 f.run(&[b"ZADD", b"z", b"NX", b"XX"]),
4117 "-ERR syntax error\r\n"
4118 );
4119 assert_eq!(
4120 f.run(&[b"ZADD", b"z", b"NX", b"XX", b"1", b"a"]),
4121 "-ERR XX and NX options at the same time are not compatible\r\n"
4122 );
4123 let gtlt = "-ERR GT, LT, and/or NX options at the same time are not compatible\r\n";
4124 assert_eq!(f.run(&[b"ZADD", b"z", b"NX", b"GT", b"1", b"a"]), gtlt);
4125 assert_eq!(f.run(&[b"ZADD", b"z", b"GT", b"LT", b"1", b"a"]), gtlt);
4126 assert_eq!(
4127 f.run(&[b"ZADD", b"z", b"INCR", b"1", b"a", b"2", b"b"]),
4128 "-ERR INCR option supports a single increment-element pair\r\n"
4129 );
4130 assert_eq!(
4132 f.run(&[b"ZADD", b"z", b"1", b"a", b"2"]),
4133 "-ERR syntax error\r\n"
4134 );
4135 assert_eq!(
4137 f.run(&[b"ZADD", b"z", b"1", b"a", b"nonsense", b"b"]),
4138 "-ERR value is not a valid float\r\n"
4139 );
4140 assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
4141 }
4142
4143 #[test]
4144 fn a_rank_says_where_a_member_sits_from_either_end() {
4145 let mut f = Fixture::new();
4146 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
4147 assert_eq!(f.run(&[b"ZRANK", b"z", b"a"]), ":0\r\n");
4148 assert_eq!(f.run(&[b"ZRANK", b"z", b"c"]), ":2\r\n");
4149 assert_eq!(f.run(&[b"ZREVRANK", b"z", b"c"]), ":0\r\n");
4150 assert_eq!(f.run(&[b"ZREVRANK", b"z", b"a"]), ":2\r\n");
4151 assert_eq!(
4153 f.run(&[b"ZRANK", b"z", b"b", b"WITHSCORE"]),
4154 "*2\r\n:1\r\n$1\r\n2\r\n"
4155 );
4156 assert_eq!(f.run(&[b"ZRANK", b"z", b"nope"]), "$-1\r\n");
4157 assert_eq!(f.run(&[b"ZRANK", b"z", b"nope", b"WITHSCORE"]), "*-1\r\n");
4158 assert_eq!(f.run(&[b"ZRANK", b"nokey", b"a", b"WITHSCORE"]), "*-1\r\n");
4159 assert_eq!(
4162 f.run(&[b"ZRANK", b"z", b"b", b"bogus"]),
4163 "-ERR syntax error\r\n"
4164 );
4165 assert_eq!(
4166 f.run(&[b"ZREVRANK", b"z", b"b", b"WITHSCORE", b"more"]),
4167 "-ERR wrong number of arguments for 'zrevrank' command\r\n"
4168 );
4169 }
4170
4171 #[test]
4172 fn the_two_counts_read_their_two_kinds_of_bound() {
4173 let mut f = Fixture::new();
4174 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
4175 assert_eq!(f.run(&[b"ZCOUNT", b"z", b"-inf", b"+inf"]), ":3\r\n");
4176 assert_eq!(f.run(&[b"ZCOUNT", b"z", b"2", b"3"]), ":2\r\n");
4177 assert_eq!(f.run(&[b"ZCOUNT", b"z", b"(1", b"3"]), ":2\r\n");
4178 assert_eq!(f.run(&[b"ZCOUNT", b"z", b"(1", b"(3"]), ":1\r\n");
4179 assert_eq!(f.run(&[b"ZCOUNT", b"nokey", b"-inf", b"+inf"]), ":0\r\n");
4180 assert_eq!(
4181 f.run(&[b"ZCOUNT", b"z", b"bogus", b"3"]),
4182 "-ERR min or max is not a float\r\n"
4183 );
4184
4185 f.run(&[b"ZADD", b"l", b"0", b"a", b"0", b"b", b"0", b"c"]);
4186 assert_eq!(f.run(&[b"ZLEXCOUNT", b"l", b"-", b"+"]), ":3\r\n");
4187 assert_eq!(f.run(&[b"ZLEXCOUNT", b"l", b"[a", b"(c"]), ":2\r\n");
4188 assert_eq!(f.run(&[b"ZLEXCOUNT", b"l", b"(a", b"+"]), ":2\r\n");
4189 assert_eq!(
4192 f.run(&[b"ZLEXCOUNT", b"l", b"a", b"c"]),
4193 "-ERR min or max not valid string range item\r\n"
4194 );
4195 }
4196
4197 #[test]
4203 fn one_range_command_selects_by_rank_or_score_or_name() {
4204 let mut f = Fixture::new();
4205 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
4206 assert_eq!(
4207 f.run(&[b"ZRANGE", b"z", b"0", b"-1"]),
4208 "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
4209 );
4210 assert_eq!(
4211 f.run(&[b"ZRANGE", b"z", b"-2", b"-1"]),
4212 "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
4213 );
4214 assert_eq!(f.run(&[b"ZRANGE", b"z", b"5", b"9"]), "*0\r\n");
4215 assert_eq!(f.run(&[b"ZRANGE", b"nokey", b"0", b"-1"]), "*0\r\n");
4216 assert_eq!(
4219 f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"REV"]),
4220 "*3\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\na\r\n"
4221 );
4222 assert_eq!(
4223 f.run(&[b"ZRANGE", b"z", b"(1", b"+inf", b"BYSCORE"]),
4224 "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
4225 );
4226 assert_eq!(
4229 f.run(&[b"ZRANGE", b"z", b"+inf", b"(1", b"BYSCORE", b"REV"]),
4230 "*2\r\n$1\r\nc\r\n$1\r\nb\r\n"
4231 );
4232 assert_eq!(
4233 f.run(&[b"ZRANGE", b"z", b"-", b"+", b"BYLEX"]),
4234 "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
4235 );
4236 assert_eq!(
4237 f.run(&[b"ZRANGE", b"z", b"+", b"-", b"BYLEX", b"REV"]),
4238 "*3\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\na\r\n"
4239 );
4240 }
4241
4242 #[test]
4245 fn the_older_range_spellings_name_their_high_end_first() {
4246 let mut f = Fixture::new();
4247 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
4248 assert_eq!(
4249 f.run(&[b"ZREVRANGE", b"z", b"0", b"-1"]),
4250 "*3\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\na\r\n"
4251 );
4252 assert_eq!(
4253 f.run(&[b"ZREVRANGE", b"z", b"0", b"0", b"WITHSCORES"]),
4254 "*2\r\n$1\r\nc\r\n$1\r\n3\r\n"
4255 );
4256 assert_eq!(
4257 f.run(&[b"ZRANGEBYSCORE", b"z", b"(1", b"3"]),
4258 "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
4259 );
4260 assert_eq!(
4261 f.run(&[b"ZREVRANGEBYSCORE", b"z", b"3", b"(1"]),
4262 "*2\r\n$1\r\nc\r\n$1\r\nb\r\n"
4263 );
4264 assert_eq!(f.run(&[b"ZREVRANGEBYSCORE", b"z", b"(1", b"3"]), "*0\r\n");
4268 assert_eq!(
4269 f.run(&[b"ZRANGEBYLEX", b"z", b"[a", b"(c"]),
4270 "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
4271 );
4272 assert_eq!(
4273 f.run(&[b"ZREVRANGEBYLEX", b"z", b"(c", b"[a"]),
4274 "*2\r\n$1\r\nb\r\n$1\r\na\r\n"
4275 );
4276 for cmd in [
4279 &[b"ZREVRANGE".as_slice(), b"z", b"0", b"-1", b"BYSCORE"][..],
4280 &[b"ZRANGEBYSCORE", b"z", b"1", b"3", b"REV"],
4281 &[b"ZRANGEBYLEX", b"z", b"[a", b"[c", b"BYLEX"],
4282 ] {
4283 assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{:?}", cmd[0]);
4284 }
4285 }
4286
4287 #[test]
4290 fn limit_and_withscores_are_read_by_all_of_them_and_refused_afterwards() {
4291 let mut f = Fixture::new();
4292 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
4293 assert_eq!(
4294 f.run(&[
4295 b"ZRANGE", b"z", b"-inf", b"+inf", b"BYSCORE", b"LIMIT", b"1", b"1"
4296 ]),
4297 "*1\r\n$1\r\nb\r\n"
4298 );
4299 assert_eq!(
4301 f.run(&[
4302 b"ZRANGE", b"z", b"-inf", b"+inf", b"BYSCORE", b"LIMIT", b"-1", b"2"
4303 ]),
4304 "*0\r\n"
4305 );
4306 assert_eq!(
4307 f.run(&[
4308 b"ZRANGE", b"z", b"-inf", b"+inf", b"BYSCORE", b"LIMIT", b"0", b"-1"
4309 ]),
4310 "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
4311 );
4312 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";
4314 assert_eq!(
4315 f.run(&[
4316 b"ZRANGEBYSCORE",
4317 b"z",
4318 b"1",
4319 b"3",
4320 b"WITHSCORES",
4321 b"LIMIT",
4322 b"0",
4323 b"2"
4324 ]),
4325 both
4326 );
4327 assert_eq!(
4328 f.run(&[
4329 b"ZRANGEBYSCORE",
4330 b"z",
4331 b"1",
4332 b"3",
4333 b"LIMIT",
4334 b"0",
4335 b"2",
4336 b"WITHSCORES"
4337 ]),
4338 both
4339 );
4340 let needs_by = "-ERR syntax error, LIMIT is only supported in combination with either BYSCORE or BYLEX\r\n";
4343 assert_eq!(
4344 f.run(&[
4345 b"ZREVRANGE",
4346 b"z",
4347 b"0",
4348 b"-1",
4349 b"WITHSCORES",
4350 b"LIMIT",
4351 b"0",
4352 b"1"
4353 ]),
4354 needs_by
4355 );
4356 assert_eq!(
4357 f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"LIMIT", b"0", b"1"]),
4358 needs_by
4359 );
4360 let not_bylex = "-ERR syntax error, WITHSCORES not supported in combination with BYLEX\r\n";
4361 assert_eq!(
4362 f.run(&[b"ZRANGE", b"z", b"-", b"+", b"BYLEX", b"WITHSCORES"]),
4363 not_bylex
4364 );
4365 assert_eq!(
4366 f.run(&[b"ZRANGEBYLEX", b"z", b"[a", b"[c", b"WITHSCORES"]),
4367 not_bylex
4368 );
4369 for cmd in [
4372 &[
4373 b"ZRANGE".as_slice(),
4374 b"z",
4375 b"0",
4376 b"-1",
4377 b"BYSCORE",
4378 b"BYLEX",
4379 ][..],
4380 &[b"ZRANGE", b"z", b"0", b"-1", b"junk"],
4381 &[b"ZRANGEBYSCORE", b"z", b"1", b"3", b"LIMIT", b"0"],
4382 ] {
4383 assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
4384 }
4385 assert_eq!(
4386 f.run(&[b"ZRANGEBYSCORE", b"z", b"bad", b"3"]),
4387 "-ERR min or max is not a float\r\n"
4388 );
4389 assert_eq!(
4390 f.run(&[b"ZRANGEBYLEX", b"z", b"a", b"[c"]),
4391 "-ERR min or max not valid string range item\r\n"
4392 );
4393 assert_eq!(
4394 f.run(&[b"ZRANGEBYSCORE", b"z", b"1", b"3", b"LIMIT", b"a", b"2"]),
4395 "-ERR value is not an integer or out of range\r\n"
4396 );
4397 }
4398
4399 #[test]
4402 fn withscores_nests_on_resp3_and_flattens_on_resp2() {
4403 let mut f = Fixture::new();
4404 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
4405 assert_eq!(
4406 f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"WITHSCORES"]),
4407 "*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"
4408 );
4409 f.out = Out::new(Proto::Resp3);
4410 assert_eq!(
4411 f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"WITHSCORES"]),
4412 "*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"
4413 );
4414 assert_eq!(
4415 f.run(&[b"ZRANGE", b"z", b"0", b"-1"]),
4416 "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
4417 );
4418 }
4419
4420 #[test]
4422 fn a_range_store_writes_the_window_into_another_key() {
4423 let mut f = Fixture::new();
4424 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
4425 assert_eq!(f.run(&[b"ZRANGESTORE", b"d", b"z", b"0", b"-1"]), ":3\r\n");
4426 assert_eq!(f.run(&[b"ZRANGESTORE", b"d", b"z", b"5", b"9"]), ":0\r\n");
4429 assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
4430 assert_eq!(
4431 f.run(&[b"ZRANGESTORE", b"d", b"z", b"(1", b"+inf", b"BYSCORE"]),
4432 ":2\r\n"
4433 );
4434 assert_eq!(
4435 f.run(&[b"ZRANGE", b"d", b"0", b"-1", b"WITHSCORES"]),
4436 "*4\r\n$1\r\nb\r\n$1\r\n2\r\n$1\r\nc\r\n$1\r\n3\r\n"
4437 );
4438 assert_eq!(f.run(&[b"ZRANGESTORE", b"z", b"z", b"1", b"2"]), ":2\r\n");
4441 assert_eq!(
4442 f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"WITHSCORES"]),
4443 "*4\r\n$1\r\nb\r\n$1\r\n2\r\n$1\r\nc\r\n$1\r\n3\r\n"
4444 );
4445 assert_eq!(
4448 f.run(&[b"ZRANGESTORE", b"d", b"z", b"0", b"-1", b"WITHSCORES"]),
4449 "-ERR syntax error\r\n"
4450 );
4451 }
4452
4453 #[test]
4456 fn the_three_removals_share_their_window_with_the_reads() {
4457 let mut f = Fixture::new();
4458 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
4459 assert_eq!(f.run(&[b"ZREMRANGEBYRANK", b"z", b"0", b"0"]), ":1\r\n");
4460 assert_eq!(
4461 f.run(&[b"ZRANGE", b"z", b"0", b"-1"]),
4462 "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
4463 );
4464 assert_eq!(
4465 f.run(&[b"ZREMRANGEBYSCORE", b"z", b"(2", b"+inf"]),
4466 ":1\r\n"
4467 );
4468 assert_eq!(f.run(&[b"ZRANGE", b"z", b"0", b"-1"]), "*1\r\n$1\r\nb\r\n");
4469 assert_eq!(f.run(&[b"ZREMRANGEBYLEX", b"z", b"-", b"+"]), ":1\r\n");
4471 assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
4472 assert_eq!(
4473 f.run(&[b"ZREMRANGEBYRANK", b"nokey", b"0", b"-1"]),
4474 ":0\r\n"
4475 );
4476 assert_eq!(
4477 f.run(&[b"ZREMRANGEBYRANK", b"z", b"0", b"x"]),
4478 "-ERR value is not an integer or out of range\r\n"
4479 );
4480 }
4481
4482 #[test]
4484 fn the_three_algebra_commands_combine_scores_and_order_the_answer_once() {
4485 let mut f = Fixture::new();
4486 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
4487 f.run(&[b"ZADD", b"y", b"10", b"b", b"20", b"d"]);
4488 assert_eq!(
4489 f.run(&[b"ZUNION", b"2", b"z", b"y"]),
4490 "*4\r\n$1\r\na\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\nd\r\n"
4491 );
4492 assert_eq!(
4495 f.run(&[b"ZUNION", b"2", b"z", b"y", b"WITHSCORES"]),
4496 "*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"
4497 );
4498 assert_eq!(
4499 f.run(&[
4500 b"ZUNION",
4501 b"2",
4502 b"z",
4503 b"y",
4504 b"WEIGHTS",
4505 b"2",
4506 b"3",
4507 b"WITHSCORES"
4508 ]),
4509 "*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"
4510 );
4511 assert_eq!(
4512 f.run(&[
4513 b"ZUNION",
4514 b"2",
4515 b"z",
4516 b"y",
4517 b"AGGREGATE",
4518 b"MIN",
4519 b"WITHSCORES"
4520 ]),
4521 "*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"
4522 );
4523 assert_eq!(
4524 f.run(&[
4525 b"ZUNION",
4526 b"2",
4527 b"z",
4528 b"y",
4529 b"AGGREGATE",
4530 b"MAX",
4531 b"WITHSCORES"
4532 ]),
4533 "*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"
4534 );
4535 assert_eq!(
4536 f.run(&[b"ZINTER", b"2", b"z", b"y", b"WITHSCORES"]),
4537 "*2\r\n$1\r\nb\r\n$2\r\n12\r\n"
4538 );
4539 assert_eq!(
4540 f.run(&[b"ZDIFF", b"2", b"z", b"y", b"WITHSCORES"]),
4541 "*4\r\n$1\r\na\r\n$1\r\n1\r\n$1\r\nc\r\n$1\r\n3\r\n"
4542 );
4543 assert_eq!(f.run(&[b"ZUNION", b"1", b"nokey"]), "*0\r\n");
4544 f.run(&[b"SADD", b"p", b"a", b"d"]);
4547 assert_eq!(
4548 f.run(&[b"ZUNION", b"2", b"z", b"p", b"WITHSCORES"]),
4549 "*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"
4550 );
4551 for cmd in [
4554 &[
4555 b"ZDIFF".as_slice(),
4556 b"2",
4557 b"z",
4558 b"y",
4559 b"WEIGHTS",
4560 b"1",
4561 b"1",
4562 ][..],
4563 &[b"ZDIFF", b"2", b"z", b"y", b"AGGREGATE", b"MIN"],
4564 ] {
4565 assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
4566 }
4567 }
4568
4569 #[test]
4571 fn the_algebra_counts_its_keys_and_says_so_when_the_count_is_wrong() {
4572 let mut f = Fixture::new();
4573 f.run(&[b"ZADD", b"z", b"1", b"a"]);
4574 f.run(&[b"ZADD", b"y", b"2", b"b"]);
4575 assert_eq!(
4577 f.run(&[b"ZUNION", b"0", b"z"]),
4578 "-ERR at least 1 input key is needed for 'zunion' command\r\n"
4579 );
4580 assert_eq!(
4581 f.run(&[b"ZUNION", b"-1", b"z"]),
4582 "-ERR at least 1 input key is needed for 'zunion' command\r\n"
4583 );
4584 assert_eq!(
4585 f.run(&[b"ZINTERCARD", b"0", b"z"]),
4586 "-ERR at least 1 input key is needed for 'zintercard' command\r\n"
4587 );
4588 assert_eq!(
4591 f.run(&[b"ZUNION", b"3", b"z", b"y"]),
4592 "-ERR syntax error\r\n"
4593 );
4594 assert_eq!(
4595 f.run(&[b"ZUNION", b"x", b"z"]),
4596 "-ERR value is not an integer or out of range\r\n"
4597 );
4598 assert_eq!(
4601 f.run(&[b"ZUNION", b"2", b"z", b"y", b"WEIGHTS", b"1"]),
4602 "-ERR syntax error\r\n"
4603 );
4604 assert_eq!(
4605 f.run(&[b"ZUNION", b"2", b"z", b"y", b"WEIGHTS", b"a", b"b"]),
4606 "-ERR weight value is not a float\r\n"
4607 );
4608 assert_eq!(
4609 f.run(&[b"ZUNION", b"2", b"z", b"y", b"AGGREGATE", b"NOPE"]),
4610 "-ERR syntax error\r\n"
4611 );
4612 }
4613
4614 #[test]
4616 fn the_algebra_stores_answer_a_count_and_delete_an_empty_destination() {
4617 let mut f = Fixture::new();
4618 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
4619 f.run(&[b"ZADD", b"y", b"10", b"b", b"20", b"d"]);
4620 assert_eq!(f.run(&[b"ZUNIONSTORE", b"d", b"2", b"z", b"y"]), ":4\r\n");
4621 assert_eq!(
4622 f.run(&[b"ZRANGE", b"d", b"0", b"-1", b"WITHSCORES"]),
4623 "*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"
4624 );
4625 assert_eq!(f.run(&[b"ZINTERSTORE", b"d", b"2", b"z", b"y"]), ":1\r\n");
4626 assert_eq!(f.run(&[b"ZDIFFSTORE", b"d", b"2", b"z", b"y"]), ":2\r\n");
4627 assert_eq!(
4630 f.run(&[b"ZINTERSTORE", b"d", b"2", b"z", b"nokey"]),
4631 ":0\r\n"
4632 );
4633 assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
4634 assert_eq!(f.run(&[b"ZUNIONSTORE", b"z", b"2", b"z", b"y"]), ":4\r\n");
4636 assert_eq!(f.run(&[b"ZCARD", b"z"]), ":4\r\n");
4637 for cmd in [
4638 &[
4639 b"ZUNIONSTORE".as_slice(),
4640 b"d",
4641 b"2",
4642 b"z",
4643 b"y",
4644 b"WITHSCORES",
4645 ][..],
4646 &[
4647 b"ZDIFFSTORE",
4648 b"d",
4649 b"2",
4650 b"z",
4651 b"y",
4652 b"WEIGHTS",
4653 b"1",
4654 b"1",
4655 ],
4656 ] {
4657 assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
4658 }
4659 }
4660
4661 #[test]
4663 fn intercard_counts_and_stops_at_its_limit() {
4664 let mut f = Fixture::new();
4665 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
4666 f.run(&[b"ZADD", b"y", b"10", b"b", b"20", b"c", b"30", b"d"]);
4667 assert_eq!(f.run(&[b"ZINTERCARD", b"2", b"z", b"y"]), ":2\r\n");
4668 assert_eq!(
4670 f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"0"]),
4671 ":2\r\n"
4672 );
4673 assert_eq!(
4674 f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"1"]),
4675 ":1\r\n"
4676 );
4677 let bad = "-ERR LIMIT can't be negative\r\n";
4680 assert_eq!(
4681 f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"-1"]),
4682 bad
4683 );
4684 assert_eq!(
4685 f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"x"]),
4686 bad
4687 );
4688 for cmd in [
4689 &[b"ZINTERCARD".as_slice(), b"3", b"z", b"y"][..],
4690 &[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT"],
4691 &[b"ZINTERCARD", b"2", b"z", b"y", b"junk", b"1"],
4692 ] {
4693 assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
4694 }
4695 }
4696
4697 #[test]
4699 fn a_draw_answers_one_member_or_an_array_of_them() {
4700 let mut f = Fixture::new();
4701 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
4702 assert_eq!(f.run(&[b"ZRANDMEMBER", b"nokey"]), "$-1\r\n");
4705 assert_eq!(f.run(&[b"ZRANDMEMBER", b"nokey", b"3"]), "*0\r\n");
4706 assert_eq!(f.run(&[b"ZRANDMEMBER", b"z", b"0"]), "*0\r\n");
4707 assert!(f.run(&[b"ZRANDMEMBER", b"z"]).starts_with("$1\r\n"));
4708 let all = f.run(&[b"ZRANDMEMBER", b"z", b"10"]);
4711 assert!(all.starts_with("*3\r\n"), "{all}");
4712 for m in ["a", "b", "c"] {
4713 assert!(all.contains(m), "{all}");
4714 }
4715 assert!(
4718 f.run(&[b"ZRANDMEMBER", b"z", b"-5"]).starts_with("*5\r\n"),
4719 "five draws with replacement"
4720 );
4721 assert!(
4722 f.run(&[b"ZRANDMEMBER", b"z", b"2", b"WITHSCORES"])
4723 .starts_with("*4\r\n"),
4724 "two pairs, flat on RESP2"
4725 );
4726 f.out = Out::new(Proto::Resp3);
4727 let got = f.run(&[b"ZRANDMEMBER", b"z", b"2", b"WITHSCORES"]);
4728 assert!(got.starts_with("*2\r\n*2\r\n"), "{got}");
4729 assert_eq!(f.run(&[b"ZRANDMEMBER", b"nokey"]), "_\r\n");
4730 f.out = Out::new(Proto::Resp2);
4731 assert_eq!(
4732 f.run(&[b"ZRANDMEMBER", b"z", b"2", b"junk"]),
4733 "-ERR syntax error\r\n"
4734 );
4735 assert_eq!(
4736 f.run(&[b"ZRANDMEMBER", b"z", b"x"]),
4737 "-ERR value is not an integer or out of range\r\n"
4738 );
4739 }
4740
4741 #[test]
4743 fn a_sorted_set_scan_answers_pairs_of_strings_on_both_protocols() {
4744 let mut f = Fixture::new();
4745 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
4746 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";
4747 assert_eq!(f.run(&[b"ZSCAN", b"z", b"0"]), all);
4748 assert_eq!(f.run(&[b"ZSCAN", b"z", b"0", b"COUNT", b"10"]), all);
4749 assert_eq!(
4750 f.run(&[b"ZSCAN", b"z", b"0", b"MATCH", b"a*"]),
4751 "*2\r\n$1\r\n0\r\n*2\r\n$1\r\na\r\n$1\r\n1\r\n"
4752 );
4753 assert_eq!(
4754 f.run(&[b"ZSCAN", b"nokey", b"0"]),
4755 "*2\r\n$1\r\n0\r\n*0\r\n"
4756 );
4757 f.out = Out::new(Proto::Resp3);
4760 assert_eq!(f.run(&[b"ZSCAN", b"z", b"0"]), all);
4761 f.out = Out::new(Proto::Resp2);
4762 assert_eq!(
4763 f.run(&[b"ZSCAN", b"z", b"0", b"NOVALUES"]),
4764 "-ERR NOVALUES option can only be used in HSCAN\r\n"
4765 );
4766 assert_eq!(f.run(&[b"ZSCAN", b"z", b"-1"]), "-ERR invalid cursor\r\n");
4767 assert_eq!(
4768 f.run(&[b"ZSCAN", b"z", b"0", b"COUNT", b"0"]),
4769 "-ERR syntax error\r\n"
4770 );
4771 }
4772
4773 #[test]
4775 fn a_sorted_set_pop_changes_shape_when_it_is_given_a_count() {
4776 let mut f = Fixture::new();
4777 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
4778 assert_eq!(f.run(&[b"ZPOPMIN", b"z"]), "*2\r\n$1\r\na\r\n$1\r\n1\r\n");
4780 assert_eq!(f.run(&[b"ZPOPMAX", b"z"]), "*2\r\n$1\r\nc\r\n$1\r\n3\r\n");
4781 f.run(&[b"ZADD", b"z", b"1", b"a", b"3", b"c"]);
4782 assert_eq!(
4784 f.run(&[b"ZPOPMIN", b"z", b"2"]),
4785 "*4\r\n$1\r\na\r\n$1\r\n1\r\n$1\r\nb\r\n$1\r\n2\r\n"
4786 );
4787 assert_eq!(f.run(&[b"ZPOPMIN", b"nokey"]), "*0\r\n");
4790 assert_eq!(f.run(&[b"ZPOPMIN", b"nokey", b"2"]), "*0\r\n");
4791 assert_eq!(f.run(&[b"ZPOPMIN", b"z", b"0"]), "*0\r\n");
4792 assert_eq!(
4794 f.run(&[b"ZPOPMIN", b"z", b"9"]),
4795 "*2\r\n$1\r\nc\r\n$1\r\n3\r\n"
4796 );
4797 assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
4798
4799 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b"]);
4800 f.out = Out::new(Proto::Resp3);
4801 assert_eq!(f.run(&[b"ZPOPMIN", b"z"]), "*2\r\n$1\r\na\r\n,1\r\n");
4802 assert_eq!(
4803 f.run(&[b"ZPOPMIN", b"z", b"1"]),
4804 "*1\r\n*2\r\n$1\r\nb\r\n,2\r\n"
4805 );
4806 f.out = Out::new(Proto::Resp2);
4807 let bad = "-ERR value is out of range, must be positive\r\n";
4810 assert_eq!(f.run(&[b"ZPOPMIN", b"z", b"x"]), bad);
4811 assert_eq!(f.run(&[b"ZPOPMIN", b"z", b"-1"]), bad);
4812 assert_eq!(
4813 f.run(&[b"ZPOPMIN", b"z", b"1", b"2"]),
4814 "-ERR syntax error\r\n"
4815 );
4816 }
4817
4818 #[test]
4820 fn a_multi_key_pop_names_the_key_that_answered_and_nests_its_pairs() {
4821 let mut f = Fixture::new();
4822 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
4823 assert_eq!(
4824 f.run(&[b"ZMPOP", b"2", b"nokey", b"z", b"MIN"]),
4825 "*2\r\n$1\r\nz\r\n*1\r\n*2\r\n$1\r\na\r\n$1\r\n1\r\n"
4826 );
4827 assert_eq!(
4830 f.run(&[b"ZMPOP", b"1", b"z", b"MAX", b"COUNT", b"2"]),
4831 "*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"
4832 );
4833 assert_eq!(f.run(&[b"ZMPOP", b"1", b"nokey", b"MIN"]), "*-1\r\n");
4835 f.out = Out::new(Proto::Resp3);
4836 assert_eq!(f.run(&[b"ZMPOP", b"1", b"nokey", b"MIN"]), "_\r\n");
4837 f.out = Out::new(Proto::Resp2);
4838 let numkeys = "-ERR numkeys should be greater than 0\r\n";
4839 for bad in [
4840 &[b"ZMPOP".as_slice(), b"0", b"z", b"MIN"][..],
4841 &[b"ZMPOP", b"-1", b"z", b"MIN"],
4842 &[b"ZMPOP", b"x", b"z", b"MIN"],
4843 ] {
4844 assert_eq!(f.run(bad), numkeys, "{:?}", bad[1]);
4845 }
4846 let count = "-ERR count should be greater than 0\r\n";
4847 for bad in [
4848 &[b"ZMPOP".as_slice(), b"1", b"z", b"MIN", b"COUNT", b"0"][..],
4849 &[b"ZMPOP", b"1", b"z", b"MIN", b"COUNT", b"-1"],
4850 &[b"ZMPOP", b"1", b"z", b"MIN", b"COUNT", b"x"],
4851 ] {
4852 assert_eq!(f.run(bad), count, "{:?}", bad[5]);
4853 }
4854 let syntax = "-ERR syntax error\r\n";
4855 for bad in [
4856 &[b"ZMPOP".as_slice(), b"2", b"z", b"MIN"][..],
4859 &[b"ZMPOP", b"1", b"z", b"SIDEWAYS"],
4860 &[b"ZMPOP", b"1", b"z", b"MIN", b"junk"],
4861 &[b"ZMPOP", b"1", b"z", b"MIN", b"COUNT", b"1", b"junk"],
4862 ] {
4863 assert_eq!(f.run(bad), syntax, "{bad:?}");
4864 }
4865 }
4866
4867 #[test]
4870 fn the_sorted_set_pops_that_wait_answer_like_the_ones_they_wrap() {
4871 let mut f = Fixture::new();
4872 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
4873 assert_eq!(
4874 f.flow(&[b"BZPOPMIN", b"nokey", b"z", b"0"]),
4875 (
4876 Flow::Continue,
4877 "*3\r\n$1\r\nz\r\n$1\r\na\r\n$1\r\n1\r\n".to_owned()
4878 )
4879 );
4880 assert_eq!(
4881 f.run(&[b"BZPOPMAX", b"z", b"0"]),
4882 "*3\r\n$1\r\nz\r\n$1\r\nc\r\n$1\r\n3\r\n"
4883 );
4884 f.run(&[b"ZADD", b"z", b"1", b"a", b"3", b"c"]);
4885 assert_eq!(
4886 f.run(&[
4887 b"BZMPOP", b"0", b"2", b"nokey", b"z", b"MIN", b"COUNT", b"2"
4888 ]),
4889 "*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"
4890 );
4891 f.out = Out::new(Proto::Resp3);
4892 assert_eq!(
4893 f.run(&[b"BZPOPMIN", b"z", b"0"]),
4894 "*3\r\n$1\r\nz\r\n$1\r\nc\r\n,3\r\n"
4895 );
4896 f.out = Out::new(Proto::Resp2);
4897 assert_eq!(
4899 f.flow(&[b"BZPOPMIN", b"z", b"0"]),
4900 (Flow::Block, String::new())
4901 );
4902 assert_eq!(
4903 f.flow(&[b"BZMPOP", b"0", b"1", b"z", b"MIN"]),
4904 (Flow::Block, String::new())
4905 );
4906 assert_eq!(
4909 f.run(&[b"BZMPOP", b"abc", b"0", b"z", b"MIN"]),
4910 "-ERR timeout is not a float or out of range\r\n"
4911 );
4912 assert_eq!(
4913 f.run(&[b"BZMPOP", b"0", b"0", b"z", b"MIN"]),
4914 "-ERR numkeys should be greater than 0\r\n"
4915 );
4916 assert_eq!(
4917 f.run(&[b"BZPOPMIN", b"z", b"-1"]),
4918 "-ERR timeout is negative\r\n"
4919 );
4920 }
4921
4922 #[test]
4926 fn a_parked_sorted_set_client_waits_for_a_member_and_not_for_a_key() {
4927 let mut f = Fixture::new();
4928 assert_eq!(f.flow(&[b"BZPOPMIN", b"z", b"0"]).0, Flow::Block);
4929 assert_eq!(f.server.waiters().len(), 1);
4930 f.run(&[b"SET", b"z", b"v"]);
4933 let mut out = Out::new(Proto::Resp2);
4934 assert!(!f.server.serve_waiter(0, 0, &mut out));
4935 assert!(out.as_slice().is_empty());
4936 f.run(&[b"DEL", b"z"]);
4937 f.run(&[b"ZADD", b"z", b"5", b"m"]);
4938 assert!(f.server.serve_waiter(0, 0, &mut out));
4939 assert_eq!(
4940 core::str::from_utf8(out.as_slice()).expect("ascii"),
4941 "*3\r\n$1\r\nz\r\n$1\r\nm\r\n$1\r\n5\r\n"
4942 );
4943 assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
4946 }
4947
4948 #[test]
4949 fn every_sorted_set_command_says_wrongtype_and_writes_nothing() {
4950 let mut f = Fixture::new();
4951 f.run(&[b"SET", b"s", b"v"]);
4952 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
4953 for cmd in [
4954 &[b"ZADD".as_slice(), b"s", b"1", b"a"][..],
4955 &[b"ZINCRBY", b"s", b"1", b"a"],
4956 &[b"ZCARD", b"s"],
4957 &[b"ZSCORE", b"s", b"a"],
4958 &[b"ZMSCORE", b"s", b"a"],
4959 &[b"ZREM", b"s", b"a"],
4960 &[b"ZRANK", b"s", b"a"],
4961 &[b"ZREVRANK", b"s", b"a"],
4962 &[b"ZCOUNT", b"s", b"1", b"2"],
4963 &[b"ZLEXCOUNT", b"s", b"-", b"+"],
4964 &[b"ZRANGE", b"s", b"0", b"-1"],
4965 &[b"ZREVRANGE", b"s", b"0", b"-1"],
4966 &[b"ZRANGEBYSCORE", b"s", b"1", b"2"],
4967 &[b"ZREVRANGEBYSCORE", b"s", b"2", b"1"],
4968 &[b"ZRANGEBYLEX", b"s", b"-", b"+"],
4969 &[b"ZREVRANGEBYLEX", b"s", b"+", b"-"],
4970 &[b"ZRANGESTORE", b"d", b"s", b"0", b"-1"],
4971 &[b"ZREMRANGEBYRANK", b"s", b"0", b"-1"],
4972 &[b"ZREMRANGEBYSCORE", b"s", b"1", b"2"],
4973 &[b"ZREMRANGEBYLEX", b"s", b"-", b"+"],
4974 &[b"ZUNION", b"1", b"s"],
4975 &[b"ZINTER", b"1", b"s"],
4976 &[b"ZDIFF", b"1", b"s"],
4977 &[b"ZUNIONSTORE", b"d", b"1", b"s"],
4978 &[b"ZINTERSTORE", b"d", b"1", b"s"],
4979 &[b"ZDIFFSTORE", b"d", b"1", b"s"],
4980 &[b"ZINTERCARD", b"1", b"s"],
4981 &[b"ZRANDMEMBER", b"s"],
4982 &[b"ZSCAN", b"s", b"0"],
4983 &[b"ZPOPMIN", b"s"],
4984 &[b"ZPOPMAX", b"s", b"2"],
4985 &[b"ZMPOP", b"1", b"s", b"MIN"],
4986 &[b"BZPOPMIN", b"s", b"0"],
4987 &[b"BZPOPMAX", b"s", b"0"],
4988 &[b"BZMPOP", b"0", b"1", b"s", b"MIN"],
4989 ] {
4990 assert_eq!(f.run(cmd), wrong, "{:?}", cmd[0]);
4991 }
4992 assert_eq!(f.run(&[b"GET", b"s"]), "$1\r\nv\r\n");
4993 }
4994
4995 #[test]
4999 fn churning_sorted_sets_does_not_grow_the_server() {
5000 let mut f = Fixture::new();
5001 let members: Vec<Vec<u8>> = (0..200).map(|i| format!("m{i}").into_bytes()).collect();
5002 let scores: Vec<Vec<u8>> = (0..200).map(|i| format!("{i}").into_bytes()).collect();
5003 let mut args: Vec<&[u8]> = vec![b"ZADD", b"z"];
5004 for i in 0..200 {
5005 args.push(&scores[i]);
5006 args.push(&members[i]);
5007 }
5008
5009 f.run(&args);
5010 f.run(&[b"DEL", b"z"]);
5011 f.server.compact_step();
5012 let after_first = f.server.memory_bytes();
5013
5014 for _ in 0..200 {
5015 f.run(&args);
5016 f.run(&[b"DEL", b"z"]);
5017 f.server.compact_step();
5018 }
5019 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
5020 assert!(
5021 f.server.memory_bytes() <= after_first * 2,
5022 "held {} after two hundred passes against {after_first} after one",
5023 f.server.memory_bytes()
5024 );
5025 }
5026
5027 #[test]
5030 fn an_array_writes_at_any_index_and_reads_back_what_it_sent() {
5031 let mut f = Fixture::new();
5032 assert_eq!(
5035 f.run(&[b"ARSET", b"a", b"1000", b"x", b"y", b"z"]),
5036 ":3\r\n"
5037 );
5038 assert_eq!(f.run(&[b"ARSET", b"a", b"1000", b"X", b"Y"]), ":0\r\n");
5039 assert_eq!(f.run(&[b"ARGET", b"a", b"1000"]), "$1\r\nX\r\n");
5040 assert_eq!(f.run(&[b"ARGET", b"a", b"1002"]), "$1\r\nz\r\n");
5041 assert_eq!(f.run(&[b"ARGET", b"a", b"999"]), "$-1\r\n");
5043 assert_eq!(f.run(&[b"ARGET", b"nope", b"0"]), "$-1\r\n");
5044 assert_eq!(
5045 f.run(&[b"ARMGET", b"a", b"1002", b"999", b"1000"]),
5046 "*3\r\n$1\r\nz\r\n$-1\r\n$1\r\nX\r\n"
5047 );
5048 assert_eq!(f.run(&[b"ARMSET", b"a", b"5", b"p", b"5", b"q"]), ":1\r\n");
5050 assert_eq!(f.run(&[b"ARGET", b"a", b"5"]), "$1\r\nq\r\n");
5051 }
5052
5053 #[test]
5056 fn the_length_is_the_high_water_mark_and_the_count_is_the_population() {
5057 let mut f = Fixture::new();
5058 assert_eq!(f.run(&[b"ARLEN", b"nope"]), ":0\r\n");
5059 assert_eq!(f.run(&[b"ARCOUNT", b"nope"]), ":0\r\n");
5060 f.run(&[b"ARMSET", b"a", b"0", b"x", b"9", b"y"]);
5061 assert_eq!(f.run(&[b"ARLEN", b"a"]), ":10\r\n");
5062 assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":2\r\n");
5063 assert_eq!(f.run(&[b"ARDEL", b"a", b"0"]), ":1\r\n");
5065 assert_eq!(f.run(&[b"ARLEN", b"a"]), ":10\r\n");
5066 assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":1\r\n");
5067
5068 f.run(&[b"ARSET", b"top", b"18446744073709551614", b"z"]);
5072 assert_eq!(f.run(&[b"ARLEN", b"top"]), ":18446744073709551615\r\n");
5073 assert_eq!(f.run(&[b"ARCOUNT", b"top"]), ":1\r\n");
5074 assert_eq!(
5077 f.run(&[b"ARSET", b"over", b"18446744073709551614", b"a", b"b"]),
5078 "-ERR array index overflow\r\n"
5079 );
5080 assert_eq!(f.run(&[b"EXISTS", b"over"]), ":0\r\n");
5081 }
5082
5083 #[test]
5086 fn a_range_read_answers_for_the_holes_too_and_is_capped_at_a_million() {
5087 let mut f = Fixture::new();
5088 f.run(&[b"ARSET", b"a", b"1", b"x"]);
5089 assert_eq!(
5090 f.run(&[b"ARGETRANGE", b"a", b"0", b"3"]),
5091 "*4\r\n$-1\r\n$1\r\nx\r\n$-1\r\n$-1\r\n"
5092 );
5093 assert_eq!(
5096 f.run(&[b"ARGETRANGE", b"a", b"3", b"0"]),
5097 "*4\r\n$-1\r\n$-1\r\n$1\r\nx\r\n$-1\r\n"
5098 );
5099 assert_eq!(
5101 f.run(&[b"ARGETRANGE", b"nope", b"0", b"1"]),
5102 "*2\r\n$-1\r\n$-1\r\n"
5103 );
5104 assert_eq!(
5108 f.run(&[b"ARGETRANGE", b"nope", b"0", b"18446744073709551614"]),
5109 "-ERR range exceeds maximum of 1000000 items\r\n"
5110 );
5111 }
5112
5113 #[test]
5116 fn a_bad_index_late_in_the_line_writes_none_of_the_earlier_ones() {
5117 let mut f = Fixture::new();
5118 assert_eq!(
5119 f.run(&[b"ARMSET", b"a", b"0", b"x", b"-1", b"y"]),
5120 "-ERR invalid array index\r\n"
5121 );
5122 assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
5123 f.run(&[b"ARSET", b"a", b"0", b"x", b"y", b"z"]);
5124 assert_eq!(
5125 f.run(&[b"ARDEL", b"a", b"0", b"01"]),
5126 "-ERR invalid array index\r\n"
5127 );
5128 assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":3\r\n");
5129 assert_eq!(
5132 f.run(&[b"ARGET", b"a", b"-1"]),
5133 "-ERR invalid array index\r\n"
5134 );
5135 assert_eq!(
5138 f.run(&[b"ARMSET", b"a", b"0", b"x", b"1"]),
5139 "-ERR wrong number of arguments for 'armset' command\r\n"
5140 );
5141 assert_eq!(
5142 f.run(&[b"ARDELRANGE", b"a", b"0", b"1", b"2"]),
5143 "-ERR wrong number of arguments for 'ardelrange' command\r\n"
5144 );
5145 }
5146
5147 #[test]
5148 fn a_range_delete_costs_the_elements_and_takes_the_key_when_it_empties() {
5149 let mut f = Fixture::new();
5150 f.run(&[b"ARSET", b"a", b"0", b"0", b"1", b"2", b"3", b"4"]);
5151 assert_eq!(f.run(&[b"ARDELRANGE", b"a", b"3", b"1"]), ":3\r\n");
5152 assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":2\r\n");
5153 assert_eq!(
5156 f.run(&[
5157 b"ARDELRANGE",
5158 b"a",
5159 b"100",
5160 b"200",
5161 b"0",
5162 b"18446744073709551614"
5163 ]),
5164 ":2\r\n"
5165 );
5166 assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
5167 assert_eq!(f.run(&[b"ARDELRANGE", b"nope", b"0", b"1"]), ":0\r\n");
5168 assert_eq!(f.run(&[b"ARDEL", b"nope", b"0"]), ":0\r\n");
5169 }
5170
5171 #[test]
5174 fn a_value_comes_back_byte_for_byte_however_it_was_packed() {
5175 let mut f = Fixture::new();
5176 let long = vec![b'v'; 200];
5177 f.run(&[
5178 b"ARMSET", b"a", b"0", b"42", b"1", b"007", b"2", b"3.5", b"3", b"3.14", b"4",
5179 b"short", b"5", &long, b"6", b"-0",
5180 ]);
5181 assert_eq!(
5185 f.run(&[b"ARGETRANGE", b"a", b"0", b"6"]),
5186 format!(
5187 "*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",
5188 String::from_utf8_lossy(&long)
5189 )
5190 );
5191 }
5192
5193 #[test]
5194 fn an_array_is_a_type_and_an_encoding_a_client_can_see() {
5195 let mut f = Fixture::new();
5196 f.run(&[b"ARSET", b"a", b"0", b"x"]);
5197 assert_eq!(f.run(&[b"TYPE", b"a"]), "+array\r\n");
5198 assert_eq!(
5199 f.run(&[b"OBJECT", b"ENCODING", b"a"]),
5200 "$12\r\nsliced-array\r\n"
5201 );
5202 assert_eq!(f.run(&[b"EXPIRE", b"a", b"100"]), ":1\r\n");
5204 assert_eq!(f.run(&[b"PERSIST", b"a"]), ":1\r\n");
5205 assert_eq!(f.run(&[b"COPY", b"a", b"b"]), ":1\r\n");
5206 assert_eq!(f.run(&[b"ARGET", b"b", b"0"]), "$1\r\nx\r\n");
5207 assert_eq!(f.run(&[b"RENAME", b"a", b"c"]), "+OK\r\n");
5208 assert_eq!(f.run(&[b"ARCOUNT", b"c"]), ":1\r\n");
5209 }
5210
5211 #[test]
5212 fn every_array_command_refuses_a_key_holding_something_else() {
5213 let mut f = Fixture::new();
5214 f.run(&[b"SET", b"s", b"v"]);
5215 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
5216 for cmd in [
5217 &[b"ARSET".as_ref(), b"s", b"0", b"x"][..],
5218 &[b"ARMSET".as_ref(), b"s", b"0", b"x"][..],
5219 &[b"ARGET".as_ref(), b"s", b"0"][..],
5220 &[b"ARMGET".as_ref(), b"s", b"0"][..],
5221 &[b"ARGETRANGE".as_ref(), b"s", b"0", b"1"][..],
5222 &[b"ARLEN".as_ref(), b"s"][..],
5223 &[b"ARCOUNT".as_ref(), b"s"][..],
5224 &[b"ARDEL".as_ref(), b"s", b"0"][..],
5225 &[b"ARDELRANGE".as_ref(), b"s", b"0", b"1"][..],
5226 &[b"ARINSERT".as_ref(), b"s", b"x"][..],
5227 &[b"ARRING".as_ref(), b"s", b"4", b"x"][..],
5228 &[b"ARNEXT".as_ref(), b"s"][..],
5229 &[b"ARSEEK".as_ref(), b"s", b"1"][..],
5230 &[b"ARLASTITEMS".as_ref(), b"s", b"1"][..],
5231 &[b"ARSCAN".as_ref(), b"s", b"0", b"1"][..],
5232 &[b"ARGREP".as_ref(), b"s", b"0", b"1", b"EXACT", b"v"][..],
5233 &[b"AROP".as_ref(), b"s", b"0", b"1", b"SUM"][..],
5234 &[b"ARINFO".as_ref(), b"s"][..],
5235 ] {
5236 assert_eq!(f.run(cmd), wrong, "{}", String::from_utf8_lossy(cmd[0]));
5237 }
5238 }
5239
5240 #[test]
5244 fn a_bad_index_reports_the_type_only_where_redis_reports_it() {
5245 let mut f = Fixture::new();
5246 f.run(&[b"SET", b"s", b"v"]);
5247 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
5248 let bad = "-ERR invalid array index\r\n";
5249 assert_eq!(f.run(&[b"ARGET", b"s", b"-1"]), wrong);
5250 assert_eq!(f.run(&[b"ARMGET", b"s", b"0", b"-1"]), wrong);
5251 assert_eq!(f.run(&[b"ARSET", b"s", b"-1", b"x"]), bad);
5252 assert_eq!(f.run(&[b"ARDEL", b"s", b"-1"]), bad);
5253 assert_eq!(f.run(&[b"ARSCAN", b"s", b"-1", b"0"]), bad);
5254 assert_eq!(f.run(&[b"ARGREP", b"s", b"-1", b"0", b"EXACT", b"v"]), bad);
5255 f.run(&[b"ARSET", b"a", b"0", b"x"]);
5257 assert_eq!(f.run(&[b"ARGET", b"a", b"-1"]), bad);
5258 assert_eq!(f.run(&[b"ARGET", b"nope", b"-1"]), bad);
5259 }
5260
5261 #[test]
5262 fn an_append_follows_a_cursor_the_client_can_move() {
5263 let mut f = Fixture::new();
5264 assert_eq!(f.run(&[b"ARNEXT", b"nope"]), ":0\r\n");
5265 assert_eq!(f.run(&[b"ARINSERT", b"a", b"x", b"y"]), ":1\r\n");
5266 assert_eq!(f.run(&[b"ARNEXT", b"a"]), ":2\r\n");
5267 assert_eq!(f.run(&[b"ARINSERT", b"a", b"z"]), ":2\r\n");
5268 assert_eq!(f.run(&[b"ARGET", b"a", b"2"]), "$1\r\nz\r\n");
5269
5270 assert_eq!(f.run(&[b"ARSEEK", b"nope", b"5"]), ":0\r\n");
5273 assert_eq!(f.run(&[b"EXISTS", b"nope"]), ":0\r\n");
5274 assert_eq!(f.run(&[b"ARSEEK", b"a", b"100"]), ":1\r\n");
5275 assert_eq!(f.run(&[b"ARNEXT", b"a"]), ":100\r\n");
5276 assert_eq!(f.run(&[b"ARINSERT", b"a", b"far"]), ":100\r\n");
5277 assert_eq!(f.run(&[b"ARSEEK", b"a", b"0"]), ":1\r\n");
5278 assert_eq!(f.run(&[b"ARNEXT", b"a"]), ":0\r\n");
5279
5280 assert_eq!(f.run(&[b"ARSEEK", b"a", b"18446744073709551615"]), ":1\r\n");
5283 assert_eq!(f.run(&[b"ARNEXT", b"a"]), "$-1\r\n");
5284 assert_eq!(
5285 f.run(&[b"ARINSERT", b"a", b"x"]),
5286 "-ERR insert index overflow\r\n"
5287 );
5288 assert_eq!(
5289 f.run(&[b"ARSET", b"a", b"18446744073709551615", b"x"]),
5290 "-ERR invalid array index\r\n"
5291 );
5292 }
5293
5294 #[test]
5295 fn a_ring_keeps_the_newest_and_renumbers_them_when_it_is_resized() {
5296 let mut f = Fixture::new();
5297 assert_eq!(f.run(&[b"ARRING", b"r", b"3", b"a", b"b", b"c"]), ":2\r\n");
5298 assert_eq!(f.run(&[b"ARRING", b"r", b"3", b"d", b"e"]), ":1\r\n");
5299 assert_eq!(f.run(&[b"ARLEN", b"r"]), ":3\r\n");
5300 assert_eq!(
5301 f.run(&[b"ARGETRANGE", b"r", b"0", b"2"]),
5302 "*3\r\n$1\r\nd\r\n$1\r\ne\r\n$1\r\nc\r\n"
5303 );
5304 assert_eq!(f.run(&[b"ARRING", b"r", b"5", b"f"]), ":3\r\n");
5307 assert_eq!(
5308 f.run(&[b"ARGETRANGE", b"r", b"0", b"3"]),
5309 "*4\r\n$1\r\nc\r\n$1\r\nd\r\n$1\r\ne\r\n$1\r\nf\r\n"
5310 );
5311 assert_eq!(
5314 f.run(&[b"ARRING", b"r", b"0", b"x"]),
5315 "-ERR size must be positive\r\n"
5316 );
5317 assert_eq!(
5318 f.run(&[b"ARRING", b"r", b"big", b"x"]),
5319 "-ERR invalid size\r\n"
5320 );
5321 }
5322
5323 #[test]
5324 fn the_last_items_walk_back_from_the_cursor_and_report_the_holes() {
5325 let mut f = Fixture::new();
5326 assert_eq!(f.run(&[b"ARLASTITEMS", b"nope", b"5"]), "*0\r\n");
5327 f.run(&[b"ARRING", b"r", b"4", b"a", b"b", b"c", b"d", b"e"]);
5328 assert_eq!(
5329 f.run(&[b"ARLASTITEMS", b"r", b"3"]),
5330 "*3\r\n$1\r\nc\r\n$1\r\nd\r\n$1\r\ne\r\n"
5331 );
5332 assert_eq!(
5333 f.run(&[b"ARLASTITEMS", b"r", b"3", b"rev"]),
5334 "*3\r\n$1\r\ne\r\n$1\r\nd\r\n$1\r\nc\r\n"
5335 );
5336 assert_eq!(
5337 f.run(&[b"ARLASTITEMS", b"r", b"99"]),
5338 "*4\r\n$1\r\nb\r\n$1\r\nc\r\n$1\r\nd\r\n$1\r\ne\r\n",
5339 "more than there is gets what there is"
5340 );
5341 assert_eq!(f.run(&[b"ARLASTITEMS", b"r", b"0", b"junk"]), "*0\r\n");
5344 assert_eq!(
5345 f.run(&[b"ARLASTITEMS", b"r", b"1", b"junk"]),
5346 "-ERR syntax error\r\n"
5347 );
5348 assert_eq!(
5349 f.run(&[b"ARLASTITEMS", b"r", b"nine"]),
5350 "-ERR invalid COUNT\r\n"
5351 );
5352
5353 f.run(&[b"ARMSET", b"h", b"0", b"x", b"2", b"z"]);
5356 assert_eq!(
5357 f.run(&[b"ARLASTITEMS", b"h", b"5"]),
5358 "*2\r\n$-1\r\n$1\r\nz\r\n"
5359 );
5360 }
5361
5362 #[test]
5363 fn a_scan_answers_pairs_for_what_is_there_and_skips_what_is_not() {
5364 let mut f = Fixture::new();
5365 assert_eq!(f.run(&[b"ARSCAN", b"nope", b"0", b"10"]), "*0\r\n");
5366 f.run(&[b"ARMSET", b"a", b"0", b"x", b"7", b"y", b"1000000", b"z"]);
5367 assert_eq!(
5370 f.run(&[b"ARSCAN", b"a", b"0", b"18446744073709551614"]),
5371 "*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"
5372 );
5373 assert_eq!(
5374 f.run(&[
5375 b"ARSCAN",
5376 b"a",
5377 b"18446744073709551614",
5378 b"0",
5379 b"LIMIT",
5380 b"1"
5381 ]),
5382 "*1\r\n*2\r\n:1000000\r\n$1\r\nz\r\n"
5383 );
5384 assert_eq!(f.run(&[b"ARSCAN", b"a", b"1", b"6"]), "*0\r\n");
5385 assert_eq!(
5386 f.run(&[b"ARSCAN", b"a", b"0", b"10", b"LIMIT", b"0"]),
5387 "-ERR LIMIT must be positive\r\n"
5388 );
5389 assert_eq!(
5390 f.run(&[b"ARSCAN", b"a", b"0", b"10", b"NOPE", b"1"]),
5391 "-ERR syntax error\r\n"
5392 );
5393 assert_eq!(
5394 f.run(&[b"ARSCAN", b"a", b"0", b"10", b"LIMIT"]),
5395 "-ERR wrong number of arguments for 'arscan' command\r\n"
5396 );
5397 }
5398
5399 #[test]
5400 fn a_grep_answers_the_indexes_whose_elements_match() {
5401 let mut f = Fixture::new();
5402 assert_eq!(
5403 f.run(&[b"ARGREP", b"nope", b"0", b"10", b"EXACT", b"x"]),
5404 "*0\r\n"
5405 );
5406 f.run(&[b"ARSET", b"a", b"0", b"alpha", b"beta", b"gamma", b"ALPHA"]);
5407
5408 assert_eq!(
5411 f.run(&[b"ARGREP", b"a", b"-", b"+", b"GLOB", b"*a"]),
5412 "*3\r\n:0\r\n:1\r\n:2\r\n"
5413 );
5414 assert_eq!(
5415 f.run(&[b"ARGREP", b"a", b"+", b"-", b"GLOB", b"*a"]),
5416 "*3\r\n:2\r\n:1\r\n:0\r\n"
5417 );
5418 assert_eq!(
5419 f.run(&[b"ARGREP", b"a", b"1", b"2", b"GLOB", b"*a"]),
5420 "*2\r\n:1\r\n:2\r\n"
5421 );
5422
5423 assert_eq!(
5426 f.run(&[b"ARGREP", b"a", b"-", b"+", b"EXACT", b"alpha"]),
5427 "*1\r\n:0\r\n"
5428 );
5429 assert_eq!(
5430 f.run(&[b"ARGREP", b"a", b"-", b"+", b"EXACT", b"alpha", b"NOCASE"]),
5431 "*2\r\n:0\r\n:3\r\n"
5432 );
5433 assert_eq!(
5434 f.run(&[b"ARGREP", b"a", b"-", b"+", b"MATCH", b"mm"]),
5435 "*1\r\n:2\r\n"
5436 );
5437 assert_eq!(
5438 f.run(&[b"ARGREP", b"a", b"-", b"+", b"RE", b"^[bg]"]),
5439 "*2\r\n:1\r\n:2\r\n"
5440 );
5441
5442 let both: &[&[u8]] = &[
5445 b"ARGREP", b"a", b"-", b"+", b"EXACT", b"beta", b"MATCH", b"al",
5446 ];
5447 assert_eq!(f.run(both), "*2\r\n:0\r\n:1\r\n");
5448 assert_eq!(
5449 f.run(&[
5450 b"ARGREP", b"a", b"-", b"+", b"EXACT", b"beta", b"MATCH", b"al", b"AND"
5451 ]),
5452 "*0\r\n"
5453 );
5454 assert_eq!(
5455 f.run(&[
5456 b"ARGREP", b"a", b"-", b"+", b"EXACT", b"beta", b"MATCH", b"al", b"AND", b"OR"
5457 ]),
5458 "*2\r\n:0\r\n:1\r\n"
5459 );
5460
5461 assert_eq!(
5464 f.run(&[
5465 b"ARGREP",
5466 b"a",
5467 b"-",
5468 b"+",
5469 b"MATCH",
5470 b"a",
5471 b"WITHVALUES",
5472 b"LIMIT",
5473 b"2"
5474 ]),
5475 "*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"
5476 );
5477 assert_eq!(
5478 f.run(&[
5479 b"ARGREP", b"a", b"-", b"+", b"EXACT", b"ALPHA", b"LIMIT", b"1"
5480 ]),
5481 "*1\r\n:3\r\n"
5482 );
5483 }
5484
5485 #[test]
5487 fn a_grep_reports_a_broken_command_the_way_redis_does() {
5488 let mut f = Fixture::new();
5489 f.run(&[b"ARSET", b"a", b"0", b"alpha"]);
5490 let syntax = "-ERR syntax error\r\n";
5491
5492 assert_eq!(
5495 f.run(&[b"ARGREP", b"a", b"-1", b"0", b"NOPE", b"x"]),
5496 "-ERR invalid array index\r\n"
5497 );
5498 assert_eq!(f.run(&[b"ARGREP", b"a", b"0", b"1", b"NOPE", b"x"]), syntax);
5499 assert_eq!(
5501 f.run(&[b"ARGREP", b"a", b"0", b"1", b"NOCASE", b"EXACT"]),
5502 syntax
5503 );
5504 assert_eq!(
5505 f.run(&[b"ARGREP", b"a", b"0", b"1", b"EXACT", b"x", b"LIMIT"]),
5506 syntax
5507 );
5508 assert_eq!(
5509 f.run(&[b"ARGREP", b"a", b"0", b"1", b"NOCASE", b"WITHVALUES"]),
5510 syntax,
5511 "a command with no predicate in it at all"
5512 );
5513 assert_eq!(
5514 f.run(&[b"ARGREP", b"a", b"0", b"1", b"EXACT", b"x", b"LIMIT", b"0"]),
5515 "-ERR LIMIT must be positive\r\n"
5516 );
5517 assert_eq!(
5518 f.run(&[
5519 b"ARGREP", b"a", b"0", b"1", b"EXACT", b"x", b"LIMIT", b"nine"
5520 ]),
5521 "-ERR value is not an integer or out of range\r\n"
5522 );
5523 assert_eq!(
5524 f.run(&[b"ARGREP", b"a", b"0", b"1", b"RE", b""]),
5525 "-ERR regular expression is empty\r\n"
5526 );
5527 assert_eq!(
5528 f.run(&[b"ARGREP", b"a", b"0", b"1", b"RE", b"(a"]),
5529 "-ERR invalid regular expression: Missing ')'\r\n"
5530 );
5531 assert_eq!(
5532 f.run(&[b"ARGREP", b"a", b"0", b"1", b"RE", br"(a)\1"]),
5533 "-ERR regular expression backreferences are not supported\r\n"
5534 );
5535 let arity = "-ERR wrong number of arguments for 'argrep' command\r\n";
5538 assert_eq!(f.run(&[b"ARGREP", b"a", b"0", b"1", b"EXACT"]), arity);
5539 assert_eq!(f.run(&[b"ARGREP", b"a", b"0", b"1"]), arity);
5540 }
5541
5542 #[test]
5543 fn an_op_reduces_a_range_to_one_number() {
5544 let mut f = Fixture::new();
5545 f.run(&[b"ARSET", b"a", b"0", b"1", b"2.5", b"word", b"-4"]);
5546 assert_eq!(
5547 f.run(&[b"AROP", b"a", b"0", b"10", b"SUM"]),
5548 "$4\r\n-0.5\r\n"
5549 );
5550 assert_eq!(f.run(&[b"AROP", b"a", b"0", b"10", b"min"]), "$2\r\n-4\r\n");
5551 assert_eq!(
5552 f.run(&[b"AROP", b"a", b"0", b"10", b"MAX"]),
5553 "$3\r\n2.5\r\n"
5554 );
5555 assert_eq!(f.run(&[b"AROP", b"a", b"0", b"10", b"USED"]), ":4\r\n");
5556 assert_eq!(
5557 f.run(&[b"AROP", b"a", b"0", b"10", b"MATCH", b"word"]),
5558 ":1\r\n"
5559 );
5560 f.run(&[b"ARSET", b"t", b"0", b"0.1", b"0.2"]);
5563 assert_eq!(
5564 f.run(&[b"AROP", b"t", b"0", b"10", b"SUM"]),
5565 "$19\r\n0.30000000000000004\r\n"
5566 );
5567 assert_eq!(f.run(&[b"ZADD", b"z", b"0.3", b"m"]), ":1\r\n");
5568 assert_eq!(f.run(&[b"ZSCORE", b"z", b"m"]), "$3\r\n0.3\r\n");
5569
5570 f.run(&[b"ARSET", b"w", b"0", b"word"]);
5573 assert_eq!(f.run(&[b"AROP", b"w", b"0", b"10", b"SUM"]), "$-1\r\n");
5574 assert_eq!(f.run(&[b"AROP", b"nope", b"0", b"10", b"SUM"]), "$-1\r\n");
5575 assert_eq!(f.run(&[b"AROP", b"nope", b"0", b"10", b"USED"]), ":0\r\n");
5576
5577 assert_eq!(
5578 f.run(&[b"AROP", b"a", b"0", b"10", b"NOPE"]),
5579 "-ERR unknown operation\r\n"
5580 );
5581 assert_eq!(
5582 f.run(&[b"AROP", b"a", b"0", b"10", b"MATCH"]),
5583 "-ERR MATCH requires a value argument\r\n"
5584 );
5585 assert_eq!(
5586 f.run(&[b"AROP", b"a", b"0", b"10", b"SUM", b"extra"]),
5587 "-ERR wrong number of arguments for 'arop' command\r\n"
5588 );
5589 }
5590
5591 #[test]
5592 fn the_info_is_a_map_and_a_missing_key_is_an_error() {
5593 let mut f = Fixture::new();
5594 assert_eq!(f.run(&[b"ARINFO", b"nope"]), "-ERR no such key\r\n");
5595 f.run(&[b"ARINSERT", b"a", b"x", b"y"]);
5596 let short = f.run(&[b"ARINFO", b"a"]);
5597 assert!(
5598 short.starts_with("*14\r\n"),
5599 "seven pairs on RESP2: {short}"
5600 );
5601 assert!(short.contains("$5\r\ncount\r\n:2\r\n"), "{short}");
5602 assert!(
5603 short.contains("$17\r\nnext-insert-index\r\n:2\r\n"),
5604 "{short}"
5605 );
5606 assert!(short.contains("$10\r\nslice-size\r\n:4096\r\n"), "{short}");
5607 let full = f.run(&[b"ARINFO", b"a", b"full"]);
5608 assert!(full.starts_with("*24\r\n"), "twelve pairs: {full}");
5609 assert!(full.contains("$12\r\ndense-slices\r\n:0\r\n"), "{full}");
5612 assert!(full.contains("$13\r\nsparse-slices\r\n:1\r\n"), "{full}");
5613 assert!(
5614 full.contains("$14\r\navg-dense-size\r\n$1\r\n0\r\n"),
5615 "{full}"
5616 );
5617 assert_eq!(f.run(&[b"ARINFO", b"a", b"nope"]), "-ERR syntax error\r\n");
5618
5619 let mut g = Fixture::new();
5621 g.run(&[b"HELLO", b"3"]);
5622 g.run(&[b"ARINSERT", b"a", b"x"]);
5623 let map = g.run(&[b"ARINFO", b"a", b"FULL"]);
5624 assert!(map.starts_with("%12\r\n"), "{map}");
5625 assert!(map.contains("$5\r\ncount\r\n:1\r\n"), "{map}");
5626 assert!(map.contains("$14\r\navg-dense-size\r\n,0\r\n"), "{map}");
5627 }
5628
5629 #[test]
5630 fn a_double_on_the_wire_is_written_the_way_redis_writes_one() {
5631 let mut f = Fixture::new();
5632 for (score, want) in [
5635 ("3", "3"),
5636 ("3.5", "3.5"),
5637 ("0.3", "0.3"),
5638 ("1e30", "1e+30"),
5639 ("1e19", "1e+19"),
5640 ("1e-7", "1e-7"),
5641 ("0.000001", "0.000001"),
5642 ("4611686018427387904", "4611686018427387904"),
5643 ("-0", "-0"),
5644 ] {
5645 f.run(&[b"ZADD", b"z", score.as_bytes(), b"m"]);
5646 assert_eq!(
5647 f.run(&[b"ZSCORE", b"z", b"m"]),
5648 format!("${}\r\n{want}\r\n", want.len()),
5649 "score {score}"
5650 );
5651 }
5652
5653 let mut g = Fixture::new();
5656 g.run(&[b"HELLO", b"3"]);
5657 g.run(&[b"ZADD", b"z", b"1e30", b"m"]);
5658 assert_eq!(g.run(&[b"ZSCORE", b"z", b"m"]), ",1e+30\r\n");
5659 assert_eq!(
5664 g.run(&[b"INCRBYFLOAT", b"s", b"1e30"]),
5665 "$31\r\n1000000000000000000000000000000\r\n"
5666 );
5667 assert_eq!(g.run(&[b"INCRBYFLOAT", b"t", b"0.1"]), "$3\r\n0.1\r\n");
5668 assert_eq!(
5669 g.run(&[b"HINCRBYFLOAT", b"h", b"f", b"1e19"]),
5670 "$20\r\n10000000000000000000\r\n"
5671 );
5672 }
5673}