1mod args;
55mod arrays;
56mod bits;
57mod blocking;
58mod cpu;
59mod geo;
60mod graph;
61mod hashes;
62mod hll;
63mod keyspace;
64mod lists;
65mod migrate;
66mod scan;
67mod scripting;
68mod server;
69mod sets;
70mod streams;
71mod strings;
72pub mod table;
73mod zsets;
74
75pub use args::Args;
76pub use blocking::{Parked, Waiters};
77pub use server::parse_memory;
78pub use table::{COMMANDS, Spec, arity_ok, lookup};
79
80use crate::reply::Out;
81use yo_common::{Code, Error};
82use yo_kv::cold::Blocks;
83use yo_kv::{Clock, Keyspace};
84
85pub const DATABASES: usize = 16;
92
93const ALL_DATABASES: u64 = if DATABASES == 64 {
100 u64::MAX
101} else {
102 (1u64 << DATABASES) - 1
103};
104const _: () = assert!(DATABASES <= 64);
105
106const EVICT_BUDGET: usize = 64;
118
119const OOM: &[u8] = b"command not allowed when used memory > 'maxmemory'.";
124
125#[derive(Debug, Clone, Copy, PartialEq, Eq)]
127pub enum Flow {
128 Continue,
130 Close,
132 Block,
139}
140
141#[derive(Debug, Clone, Copy, Default)]
147pub struct Stats {
148 pub clients: u64,
150 pub connections: u64,
152 pub commands: u64,
154}
155
156#[derive(Debug, Clone, Copy, Default)]
165pub struct CommandStat {
166 pub calls: u64,
168 pub rejected: u64,
171 pub failed: u64,
173}
174
175impl CommandStat {
176 const fn seen(&self) -> bool {
182 self.calls != 0 || self.rejected != 0 || self.failed != 0
183 }
184}
185
186struct CommandStats(Box<[CommandStat]>);
193
194impl Default for CommandStats {
195 fn default() -> CommandStats {
196 CommandStats(vec![CommandStat::default(); table::count()].into_boxed_slice())
197 }
198}
199
200impl CommandStats {
201 fn at(&mut self, spec: &'static Spec) -> &mut CommandStat {
203 &mut self.0[table::index_of(spec)]
204 }
205}
206
207pub type StoreSource = dyn FnMut(usize) -> Option<Box<dyn Blocks>>;
213
214pub struct Server {
221 dbs: Vec<Keyspace>,
222 clock: Clock,
223 started_ms: u64,
224 next_db: usize,
227 dirty: u64,
237 conn_bytes: usize,
239 maxmemory: u64,
244 store: Option<Box<StoreSource>>,
255 maxstore: Option<u64>,
271 used: usize,
283 evict_db: usize,
289 expire_db: usize,
296 expire_ms: u64,
299 waiters: Waiters,
301 peers: migrate::Peers,
306 pub stats: Stats,
308 cmdstats: CommandStats,
310}
311
312impl Server {
313 #[must_use]
315 pub fn new() -> Server {
316 let clock = Clock::system();
317 Server {
318 dbs: (0..DATABASES)
319 .map(|_| Keyspace::with_clock(clock))
320 .collect(),
321 clock,
322 started_ms: clock.now_ms(),
323 next_db: 0,
324 dirty: ALL_DATABASES,
325 conn_bytes: 0,
326 maxmemory: 0,
327 store: None,
328 maxstore: None,
329 used: 0,
330 evict_db: 0,
331 expire_db: 0,
332 expire_ms: 0,
333 waiters: Waiters::default(),
334 peers: migrate::Peers::default(),
335 stats: Stats::default(),
336 cmdstats: CommandStats::default(),
337 }
338 }
339
340 #[must_use]
342 pub fn with_clock(clock: Clock) -> Server {
343 Server {
344 dbs: (0..DATABASES)
345 .map(|_| Keyspace::with_clock(clock))
346 .collect(),
347 clock,
348 started_ms: clock.now_ms(),
349 next_db: 0,
350 dirty: ALL_DATABASES,
351 conn_bytes: 0,
352 maxmemory: 0,
353 store: None,
354 maxstore: None,
355 used: 0,
356 evict_db: 0,
357 expire_db: 0,
358 expire_ms: 0,
359 waiters: Waiters::default(),
360 peers: migrate::Peers::default(),
361 stats: Stats::default(),
362 cmdstats: CommandStats::default(),
363 }
364 }
365
366 pub fn db(&mut self, i: usize) -> &mut Keyspace {
374 self.dirty |= 1u64 << i;
377 &mut self.dbs[i]
378 }
379
380 #[must_use]
391 pub fn db_ref(&self, i: usize) -> &Keyspace {
392 &self.dbs[i]
393 }
394
395 pub fn refresh_clock(&mut self) {
401 self.clock.refresh();
402 let now = self.clock.now_ms();
403 for db in &mut self.dbs {
404 db.clock_mut().set(now);
405 }
406 }
407
408 pub fn set_clock_ms(&mut self, ms: u64) {
416 self.clock.set(ms);
417 for db in &mut self.dbs {
418 db.clock_mut().set(ms);
419 }
420 }
421
422 #[must_use]
424 pub fn uptime_secs(&self) -> u64 {
425 self.clock.now_ms().saturating_sub(self.started_ms) / 1000
426 }
427
428 #[must_use]
436 pub fn memory_bytes(&self) -> usize {
437 self.dbs.iter().map(Keyspace::memory_bytes).sum::<usize>() + self.conn_bytes
438 }
439
440 #[must_use]
446 pub fn dataset_bytes(&self) -> usize {
447 self.dbs
448 .iter()
449 .map(|db| db.map().arena().live_bytes() as usize)
450 .sum()
451 }
452
453 #[must_use]
455 pub fn arena_bytes(&self) -> usize {
456 self.dbs
457 .iter()
458 .map(|db| db.map().arena().reserved_bytes() as usize)
459 .sum()
460 }
461
462 #[must_use]
464 pub fn index_bytes(&self) -> usize {
465 self.dbs
466 .iter()
467 .map(|db| db.map().index().memory_bytes())
468 .sum()
469 }
470
471 #[must_use]
473 pub fn segment_count(&self) -> usize {
474 self.dbs
475 .iter()
476 .map(|db| db.map().arena().resident_segments())
477 .sum()
478 }
479
480 #[must_use]
482 pub const fn conn_bytes(&self) -> usize {
483 self.conn_bytes
484 }
485
486 pub fn note_conn_bytes(&mut self, delta: isize) {
494 self.conn_bytes = self.conn_bytes.saturating_add_signed(delta);
495 }
496
497 #[must_use]
499 pub fn expired_keys(&self) -> u64 {
500 self.dbs.iter().map(Keyspace::expired_keys).sum()
501 }
502
503 #[must_use]
505 pub fn evicted_keys(&self) -> u64 {
506 self.dbs.iter().map(Keyspace::evicted_keys).sum()
507 }
508
509 pub fn command_stats(&self) -> impl Iterator<Item = (&'static str, CommandStat)> {
515 self.cmdstats
516 .0
517 .iter()
518 .enumerate()
519 .filter(|(_, row)| row.seen())
520 .map(|(at, row)| (table::name_at(at), *row))
521 }
522
523 #[must_use]
525 pub const fn maxmemory(&self) -> u64 {
526 self.maxmemory
527 }
528
529 pub fn set_maxmemory(&mut self, bytes: u64) {
543 self.maxmemory = bytes;
544 for db in &mut self.dbs {
545 db.track_memory(bytes != 0);
546 }
547 self.used = self.settled_memory();
548 }
549
550 pub fn set_store_source(
560 &mut self,
561 source: impl FnMut(usize) -> Option<Box<dyn Blocks>> + 'static,
562 ) {
563 self.store = Some(Box::new(source));
564 }
565
566 #[must_use]
568 pub const fn has_store_source(&self) -> bool {
569 self.store.is_some()
570 }
571
572 fn attach_store(&mut self, at: usize) {
579 if self.dbs[at].store_bytes().is_some() {
580 return;
581 }
582 let Some(source) = self.store.as_mut() else {
583 return;
584 };
585 if let Some(blocks) = source(at) {
586 self.dbs[at].attach(blocks);
587 }
588 }
589
590 #[must_use]
592 pub const fn maxstore(&self) -> Option<u64> {
593 self.maxstore
594 }
595
596 pub const fn set_maxstore(&mut self, bytes: Option<u64>) {
602 self.maxstore = bytes;
603 }
604
605 #[must_use]
611 pub fn store_bytes(&self) -> u64 {
612 self.dbs.iter().filter_map(Keyspace::store_bytes).sum()
613 }
614
615 #[must_use]
630 pub fn cold_stats(&self) -> yo_kv::tier::Stats {
631 let mut total = yo_kv::tier::Stats::default();
632 for db in &self.dbs {
633 let Some(tier) = db.tier() else { continue };
634 let s = tier.stats();
635 total.demoted += s.demoted;
636 total.promoted += s.promoted;
637 total.faults += s.faults;
638 total.served += s.served;
639 total.bytes_out += s.bytes_out;
640 total.bytes_in += s.bytes_in;
641 }
642 total
643 }
644
645 #[must_use]
652 pub fn regime(&self) -> &'static str {
653 if (0..self.dbs.len()).any(|at| self.migrates(at)) {
654 "migrate"
655 } else {
656 "evict"
657 }
658 }
659
660 fn migrates(&self, at: usize) -> bool {
672 if self.maxstore == Some(0) {
673 return false;
674 }
675 match self.dbs[at].store_bytes() {
676 Some(held) => self.maxstore.is_none_or(|cap| held < cap),
677 None => self.store.is_some(),
681 }
682 }
683
684 pub fn refresh_memory(&mut self) {
689 if self.maxmemory != 0 {
690 self.used = self.settled_memory();
691 }
692 }
693
694 fn settled_memory(&mut self) -> usize {
701 self.dbs
702 .iter_mut()
703 .map(Keyspace::settled_memory_bytes)
704 .sum::<usize>()
705 + self.conn_bytes
706 }
707
708 pub fn make_room(&mut self) -> bool {
742 if self.maxmemory == 0 || self.used as u64 <= self.maxmemory {
743 return true;
744 }
745 self.used = self.settled_memory();
750 let mut budget = EVICT_BUDGET;
751 while self.used as u64 > self.maxmemory {
752 let over = self.used - self.maxmemory as usize;
753 if !self.relieve_step(over) {
754 return false;
755 }
756 self.compact_hard_step();
757 self.used = self.settled_memory();
758 budget -= 1;
759 if budget == 0 {
760 break;
761 }
762 }
763 true
764 }
765
766 fn relieve_step(&mut self, over: usize) -> bool {
783 for turn in 0..self.dbs.len() {
784 let i = (self.evict_db + turn) % self.dbs.len();
785 let gave = if !self.dbs[i].is_empty() && self.migrates(i) {
788 self.attach_store(i);
789 self.dbs[i]
794 .relieve(over)
795 .is_ok_and(yo_kv::tier::Relief::made_room)
796 } else {
797 self.dbs[i].evict_one()
798 };
799 if gave {
800 self.evict_db = (i + 1) % self.dbs.len();
801 self.dirty |= 1u64 << i;
802 return true;
803 }
804 }
805 false
806 }
807
808 pub fn expire_slice(&mut self, budget: usize) -> usize {
823 let now = self.clock.now_ms();
824 if now == self.expire_ms {
825 return 0;
826 }
827 self.expire_ms = now;
828 self.expire_step(budget)
829 }
830
831 pub fn expire_step(&mut self, budget: usize) -> usize {
847 let mut spent = 0;
848 for turn in 0..self.dbs.len() {
849 if spent >= budget {
850 break;
851 }
852 let i = (self.expire_db + turn) % self.dbs.len();
853 let c = self.dbs[i].expire_cycle(budget - spent);
854 spent += c.examined;
855 if c.expired > 0 {
856 self.expire_db = (i + 1) % self.dbs.len();
857 self.dirty |= 1u64 << i;
858 }
859 }
860 spent
861 }
862
863 fn compact_hard_step(&mut self) -> Option<usize> {
869 for turn in 0..self.dbs.len() {
870 let i = (self.next_db + turn) % self.dbs.len();
871 if let Some(moved) = self.dbs[i].compact_hard() {
872 self.next_db = (i + 1) % self.dbs.len();
873 return Some(moved);
874 }
875 }
876 None
877 }
878
879 pub fn compact_step(&mut self) -> Option<usize> {
892 for turn in 0..self.dbs.len() {
893 let i = (self.next_db + turn) % self.dbs.len();
894 if self.dirty & (1 << i) == 0 {
898 continue;
899 }
900 if let Some(moved) = self.dbs[i].compact_step() {
901 self.next_db = (i + 1) % self.dbs.len();
902 return Some(moved);
903 }
904 self.dirty &= !(1u64 << i);
905 }
906 None
907 }
908}
909
910impl Default for Server {
911 fn default() -> Server {
912 Server::new()
913 }
914}
915
916pub struct Session {
918 db: usize,
919 id: u64,
920 name: Vec<u8>,
921}
922
923impl Session {
924 #[must_use]
926 pub fn new(id: u64) -> Session {
927 Session {
928 db: 0,
929 id,
930 name: Vec::new(),
931 }
932 }
933
934 #[must_use]
936 pub const fn id(&self) -> u64 {
937 self.id
938 }
939
940 #[must_use]
942 pub const fn db(&self) -> usize {
943 self.db
944 }
945
946 #[must_use]
948 pub fn name(&self) -> &[u8] {
949 &self.name
950 }
951
952 pub fn reset(&mut self) {
957 self.db = 0;
958 self.name.clear();
959 }
960
961 fn set_name(&mut self, name: &[u8]) {
963 yo_alloc::allow(|| {
964 self.name.clear();
965 self.name.extend_from_slice(name);
966 });
967 }
968}
969
970pub fn execute(server: &mut Server, session: &mut Session, args: Args<'_>, out: &mut Out) -> Flow {
975 if args.is_empty() {
978 return Flow::Continue;
979 }
980 resolved(server, session, lookup(args.name()), args, out)
981}
982
983pub fn resolved(
995 server: &mut Server,
996 session: &mut Session,
997 spec: Option<&'static Spec>,
998 args: Args<'_>,
999 out: &mut Out,
1000) -> Flow {
1001 if args.is_empty() {
1002 return Flow::Continue;
1003 }
1004 server.stats.commands += 1;
1005
1006 let Some(spec) = spec else {
1007 write_error(out, &args::unknown_command(args));
1008 return Flow::Continue;
1009 };
1010 if !arity_ok(spec, args.len()) {
1011 server.cmdstats.at(spec).rejected += 1;
1012 write_error(out, &args::wrong_arity(spec.name));
1013 return Flow::Continue;
1014 }
1015
1016 if server.maxmemory != 0 && !server.make_room() && spec.flags.contains(&"denyoom") {
1026 server.cmdstats.at(spec).rejected += 1;
1027 out.error_line(b"OOM ", OOM);
1028 return Flow::Continue;
1029 }
1030
1031 server.dirty |= match spec.group {
1038 "string" | "bitmap" | "hyperloglog" | "geo" | "set" | "hash" | "list" | "zset"
1039 | "array" | "stream" => 1u64 << session.db,
1040 _ => ALL_DATABASES,
1041 };
1042
1043 let mark = out.len();
1044 let done = if spec.flags.contains(&"blocking") {
1051 blocking::execute(server, session, spec, args, out)
1052 } else {
1053 match spec.group {
1054 "string" => {
1055 let db = session.db;
1056 strings::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1057 }
1058 "bitmap" => {
1062 let db = session.db;
1063 bits::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1064 }
1065 "hyperloglog" => {
1068 let db = session.db;
1069 hll::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1070 }
1071 "set" => {
1072 let db = session.db;
1073 sets::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1074 }
1075 "hash" => {
1076 let db = session.db;
1077 hashes::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1078 }
1079 "list" => {
1080 let db = session.db;
1081 lists::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1082 }
1083 "zset" => {
1084 let db = session.db;
1085 zsets::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1086 }
1087 "geo" => {
1091 let db = session.db;
1092 geo::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1093 }
1094 "array" => {
1095 let db = session.db;
1096 arrays::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1097 }
1098 "graph" => {
1099 let db = session.db;
1100 graph::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1101 }
1102 "stream" => {
1107 let db = session.db;
1108 let now = server.now_ms();
1109 streams::execute(&mut server.dbs[db], spec, args, now, out).map(|()| Flow::Continue)
1110 }
1111 "keyspace" if spec.name == "migrate" => {
1115 migrate::execute(server, session.db, args, out).map(|()| Flow::Continue)
1116 }
1117 "keyspace" => keyspace::execute(&mut server.dbs, session.db, spec, args, out)
1120 .map(|()| Flow::Continue),
1121 "scripting" => scripting::execute(spec, args, out).map(|()| Flow::Continue),
1122 _ => server::execute(server, session, spec, args, out),
1123 }
1124 };
1125 let flow = match done {
1126 Ok(flow) => flow,
1127 Err(e) => {
1128 out.truncate(mark);
1129 write_error(out, &e);
1130 Flow::Continue
1131 }
1132 };
1133
1134 let row = server.cmdstats.at(spec);
1145 row.calls += 1;
1146 if matches!(out.as_slice().get(mark), Some(b'-' | b'!')) {
1147 row.failed += 1;
1148 }
1149 flow
1150}
1151
1152fn write_error(out: &mut Out, e: &Error) {
1162 let prefix: &[u8] = match e.code() {
1163 Code::WrongType => b"WRONGTYPE ",
1164 Code::Corrupt => b"INVALIDOBJ ",
1168 _ => b"ERR ",
1169 };
1170 out.error_line(prefix, e.message().as_bytes());
1171}
1172
1173#[cfg(test)]
1174mod tests {
1175 use super::*;
1176 use crate::proto::{Limits, Proto};
1177 use crate::request::Argv;
1178
1179 pub(crate) fn encode(parts: &[&[u8]]) -> Vec<u8> {
1184 let mut wire = format!("*{}\r\n", parts.len()).into_bytes();
1185 for p in parts {
1186 wire.extend_from_slice(format!("${}\r\n", p.len()).as_bytes());
1187 wire.extend_from_slice(p);
1188 wire.extend_from_slice(b"\r\n");
1189 }
1190 wire
1191 }
1192
1193 struct Fixture {
1195 server: Server,
1196 session: Session,
1197 argv: Argv,
1198 out: Out,
1199 }
1200
1201 impl Fixture {
1202 fn new() -> Fixture {
1203 Fixture {
1204 server: Server::new(),
1205 session: Session::new(7),
1206 argv: Argv::new(),
1207 out: Out::new(Proto::Resp2),
1208 }
1209 }
1210
1211 fn run(&mut self, parts: &[&[u8]]) -> String {
1213 self.flow(parts).1
1214 }
1215
1216 fn raw(&mut self, parts: &[&[u8]]) -> Vec<u8> {
1222 let wire = encode(parts);
1223 self.argv.decode(&wire, &Limits::default()).unwrap();
1224 self.out.clear();
1225 execute(
1226 &mut self.server,
1227 &mut self.session,
1228 Args::new(&self.argv, &wire),
1229 &mut self.out,
1230 );
1231 self.out.as_slice().to_vec()
1232 }
1233
1234 fn advance(&mut self, ms: u64) {
1236 for db in 0..DATABASES {
1237 self.server.db(db).clock_mut().advance(ms);
1238 }
1239 }
1240
1241 fn flow(&mut self, parts: &[&[u8]]) -> (Flow, String) {
1243 let wire = encode(parts);
1244 self.argv.decode(&wire, &Limits::default()).unwrap();
1245 self.out.clear();
1246 let flow = execute(
1247 &mut self.server,
1248 &mut self.session,
1249 Args::new(&self.argv, &wire),
1250 &mut self.out,
1251 );
1252 (
1253 flow,
1254 String::from_utf8_lossy(self.out.as_slice()).into_owned(),
1255 )
1256 }
1257 }
1258
1259 #[test]
1263 fn rewriting_the_same_keys_does_not_grow_the_server() {
1264 let mut f = Fixture::new();
1265 let val = vec![b'v'; 1024];
1266 let keys: Vec<Vec<u8>> = (0..64).map(|i| format!("key:{i}").into_bytes()).collect();
1267
1268 for k in &keys {
1269 f.run(&[b"SET", k, &val]);
1270 }
1271 f.server.compact_step();
1272 let after_first = f.server.memory_bytes();
1273
1274 for _ in 0..500 {
1279 for k in &keys {
1280 f.run(&[b"SET", k, &val]);
1281 }
1282 f.server.compact_step();
1283 }
1284
1285 assert!(
1286 f.server.memory_bytes() <= after_first * 2,
1287 "held {} after five hundred passes against {after_first} after one",
1288 f.server.memory_bytes()
1289 );
1290 assert_eq!(f.run(&[b"DBSIZE"]), format!(":{}\r\n", keys.len()));
1291 assert_eq!(f.run(&[b"STRLEN", b"key:7"]), ":1024\r\n");
1292 }
1293
1294 #[test]
1308 fn a_database_nobody_started_on_is_still_collected() {
1309 let mut f = Fixture::new();
1310 assert_eq!(f.run(&[b"SELECT", b"9"]), "+OK\r\n");
1311 let val = vec![b'v'; 1024];
1312 let keys: Vec<Vec<u8>> = (0..64).map(|i| format!("key:{i}").into_bytes()).collect();
1313
1314 for k in &keys {
1315 f.run(&[b"SET", k, &val]);
1316 }
1317 while f.server.compact_step().is_some() {}
1318 assert_eq!(
1319 f.server.dirty & (1 << 9),
1320 0,
1321 "database nine was drained and should not be asked again until it is written to"
1322 );
1323 let after_first = f.server.memory_bytes();
1324
1325 for _ in 0..500 {
1326 for k in &keys {
1327 f.run(&[b"SET", k, &val]);
1328 }
1329 f.server.compact_step();
1330 }
1331
1332 assert!(
1333 f.server.memory_bytes() <= after_first * 2,
1334 "held {} after five hundred passes against {after_first} after one",
1335 f.server.memory_bytes()
1336 );
1337 assert_eq!(f.run(&[b"DBSIZE"]), format!(":{}\r\n", keys.len()));
1338 assert_eq!(f.run(&[b"STRLEN", b"key:7"]), ":1024\r\n");
1339 f.run(&[b"SELECT", b"0"]);
1341 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
1342 }
1343
1344 #[test]
1345 fn a_command_goes_from_bytes_to_bytes() {
1346 let mut f = Fixture::new();
1347 assert_eq!(f.run(&[b"SET", b"k", b"v"]), "+OK\r\n");
1348 assert_eq!(f.run(&[b"GET", b"k"]), "$1\r\nv\r\n");
1349 assert_eq!(f.run(&[b"GET", b"nosuch"]), "$-1\r\n");
1350 assert_eq!(f.run(&[b"STRLEN", b"k"]), ":1\r\n");
1351 assert_eq!(f.run(&[b"set", b"k", b"v2", b"xx"]), "+OK\r\n");
1353 assert_eq!(f.run(&[b"GET", b"k"]), "$2\r\nv2\r\n");
1354 }
1355
1356 #[test]
1357 fn deleting_counts_keys_removed_and_existing_counts_arguments_matched() {
1358 let mut f = Fixture::new();
1359 f.run(&[b"MSET", b"a", b"1", b"b", b"2", b"c", b"3"]);
1360 assert_eq!(f.run(&[b"EXISTS", b"a", b"a", b"nosuch"]), ":2\r\n");
1363 assert_eq!(f.run(&[b"DEL", b"a", b"a", b"nosuch"]), ":1\r\n");
1364 assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
1365 assert_eq!(f.run(&[b"UNLINK", b"b", b"c"]), ":2\r\n");
1367 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
1368 }
1369
1370 #[test]
1371 fn type_is_a_simple_string_and_says_none_for_a_key_that_is_not_there() {
1372 let mut f = Fixture::new();
1373 f.run(&[b"SET", b"k", b"v"]);
1374 assert_eq!(f.run(&[b"TYPE", b"k"]), "+string\r\n");
1377 assert_eq!(f.run(&[b"TYPE", b"nosuch"]), "+none\r\n");
1378 }
1379
1380 #[test]
1381 fn touch_counts_the_way_exists_counts() {
1382 let mut f = Fixture::new();
1383 f.run(&[b"MSET", b"a", b"1", b"b", b"2"]);
1384 assert_eq!(f.run(&[b"TOUCH", b"a", b"b"]), ":2\r\n");
1385 assert_eq!(
1386 f.run(&[b"TOUCH", b"a", b"a"]),
1387 ":2\r\n",
1388 "twice counts twice"
1389 );
1390 assert_eq!(f.run(&[b"TOUCH", b"a", b"nosuch"]), ":1\r\n");
1391 assert_eq!(f.run(&[b"TOUCH", b"nosuch"]), ":0\r\n");
1392 }
1393
1394 #[test]
1395 fn a_rename_moves_the_deadline_with_the_value_and_drops_the_one_it_lands_on() {
1396 let mut f = Fixture::new();
1397 f.run(&[b"SET", b"a", b"v1", b"EX", b"100"]);
1398 f.run(&[b"SET", b"b", b"v2", b"EX", b"500"]);
1399
1400 assert_eq!(f.run(&[b"RENAME", b"a", b"b"]), "+OK\r\n");
1401 assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv1\r\n");
1402 assert_eq!(
1403 f.run(&[b"TTL", b"b"]),
1404 ":100\r\n",
1405 "the source's and not b's"
1406 );
1407 assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
1408 }
1409
1410 #[test]
1411 fn a_rename_with_no_source_is_an_error_and_not_a_zero() {
1412 let mut f = Fixture::new();
1413 assert_eq!(f.run(&[b"RENAME", b"a", b"b"]), "-ERR no such key\r\n");
1414 assert_eq!(f.run(&[b"RENAMENX", b"a", b"a"]), "-ERR no such key\r\n");
1417 }
1418
1419 #[test]
1420 fn renamenx_refuses_a_taken_name_including_the_one_it_already_has() {
1421 let mut f = Fixture::new();
1422 f.run(&[b"MSET", b"a", b"v1", b"b", b"v2"]);
1423
1424 assert_eq!(f.run(&[b"RENAMENX", b"a", b"b"]), ":0\r\n");
1425 assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv2\r\n");
1426 assert_eq!(f.run(&[b"RENAMENX", b"a", b"a"]), ":0\r\n");
1429 assert_eq!(f.run(&[b"RENAME", b"a", b"a"]), "+OK\r\n");
1430 assert_eq!(f.run(&[b"RENAMENX", b"a", b"c"]), ":1\r\n");
1431 assert_eq!(f.run(&[b"GET", b"c"]), "$2\r\nv1\r\n");
1432 }
1433
1434 #[test]
1435 fn renaming_a_set_does_not_touch_a_member() {
1436 let mut f = Fixture::new();
1437 for i in 0..300 {
1438 f.run(&[b"SADD", b"s", format!("m{i}").as_bytes()]);
1439 }
1440 let before = f.server.memory_bytes();
1441
1442 assert_eq!(f.run(&[b"RENAME", b"s", b"t"]), "+OK\r\n");
1443 assert_eq!(f.run(&[b"SCARD", b"t"]), ":300\r\n");
1444 assert_eq!(f.run(&[b"TYPE", b"t"]), "+set\r\n");
1445 assert!(
1446 f.server.memory_bytes().abs_diff(before) < 256,
1447 "the members were copied: {} against {before}",
1448 f.server.memory_bytes()
1449 );
1450 }
1451
1452 #[test]
1453 fn a_copy_is_a_second_value_and_not_a_second_name() {
1454 let mut f = Fixture::new();
1455 f.run(&[b"SADD", b"s", b"m1", b"m2"]);
1456
1457 assert_eq!(f.run(&[b"COPY", b"s", b"t"]), ":1\r\n");
1458 f.run(&[b"SADD", b"t", b"m3"]);
1459 assert_eq!(f.run(&[b"SCARD", b"s"]), ":2\r\n", "the original is intact");
1460 assert_eq!(f.run(&[b"SCARD", b"t"]), ":3\r\n");
1461 }
1462
1463 #[test]
1473 fn every_type_can_be_copied() {
1474 let mut f = Fixture::new();
1475 f.run(&[b"SET", b"str", b"v1"]);
1476 f.run(&[b"SADD", b"set", b"m1"]);
1477 f.run(&[b"HSET", b"hash", b"f", b"v"]);
1478 f.run(&[b"RPUSH", b"list", b"a", b"b"]);
1479 f.run(&[b"ZADD", b"zset", b"1", b"m1"]);
1480
1481 for name in [
1482 &b"str"[..],
1483 &b"set"[..],
1484 &b"hash"[..],
1485 &b"list"[..],
1486 &b"zset"[..],
1487 ] {
1488 let dst = [name, b":copy"].concat();
1489 assert_eq!(
1490 f.run(&[b"COPY", name, &dst]),
1491 ":1\r\n",
1492 "copying {}",
1493 String::from_utf8_lossy(name)
1494 );
1495 assert_eq!(f.run(&[b"TYPE", name]), f.run(&[b"TYPE", &dst]));
1496 }
1497
1498 assert_eq!(f.run(&[b"LRANGE", b"list:copy", b"0", b"-1"]), {
1499 let mut want = String::from("*2\r\n");
1500 want.push_str("$1\r\na\r\n$1\r\nb\r\n");
1501 want
1502 });
1503 assert_eq!(f.run(&[b"ZSCORE", b"zset:copy", b"m1"]), "$1\r\n1\r\n");
1504
1505 f.run(&[b"RPUSH", b"list:copy", b"c"]);
1507 assert_eq!(f.run(&[b"LLEN", b"list"]), ":2\r\n");
1508 assert_eq!(f.run(&[b"LLEN", b"list:copy"]), ":3\r\n");
1509 }
1510
1511 #[test]
1512 fn a_copy_refuses_a_taken_destination_until_it_is_told_it_can_have_it() {
1513 let mut f = Fixture::new();
1514 f.run(&[b"SET", b"a", b"v1", b"EX", b"100"]);
1515 f.run(&[b"SET", b"b", b"v2"]);
1516
1517 assert_eq!(f.run(&[b"COPY", b"a", b"b"]), ":0\r\n");
1518 assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv2\r\n");
1519 assert_eq!(f.run(&[b"COPY", b"a", b"b", b"REPLACE"]), ":1\r\n");
1520 assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv1\r\n");
1521 assert_eq!(f.run(&[b"TTL", b"b"]), ":100\r\n", "the deadline came too");
1522 assert_eq!(f.run(&[b"COPY", b"nosuch", b"z"]), ":0\r\n");
1523 }
1524
1525 #[test]
1526 fn a_copy_into_another_database_is_a_copy_and_onto_itself_there_is_too() {
1527 let mut f = Fixture::new();
1528 f.run(&[b"SET", b"a", b"v1"]);
1529
1530 assert_eq!(f.run(&[b"COPY", b"a", b"a", b"DB", b"1"]), ":1\r\n");
1533 f.run(&[b"SELECT", b"1"]);
1534 assert_eq!(f.run(&[b"GET", b"a"]), "$2\r\nv1\r\n");
1535 assert_eq!(
1536 f.run(&[b"COPY", b"a", b"a", b"DB", b"0"]),
1537 ":0\r\n",
1538 "taken"
1539 );
1540 assert_eq!(
1541 f.run(&[b"COPY", b"a", b"a", b"DB", b"0", b"REPLACE"]),
1542 ":1\r\n"
1543 );
1544 }
1545
1546 #[test]
1547 fn sort_takes_its_options_in_any_order_and_the_last_one_wins() {
1548 let mut f = Fixture::new();
1549 f.run(&[b"RPUSH", b"l", b"3", b"1", b"2"]);
1550 assert_eq!(
1551 f.run(&[b"SORT", b"l"]),
1552 "*3\r\n$1\r\n1\r\n$1\r\n2\r\n$1\r\n3\r\n"
1553 );
1554 assert_eq!(
1556 f.run(&[b"SORT", b"l", b"DESC", b"asc"]),
1557 "*3\r\n$1\r\n1\r\n$1\r\n2\r\n$1\r\n3\r\n"
1558 );
1559 assert_eq!(
1560 f.run(&[b"sort", b"l", b"LIMIT", b"1", b"1", b"DESC"]),
1561 "*1\r\n$1\r\n2\r\n"
1562 );
1563 }
1564
1565 #[test]
1566 fn sort_reads_a_key_per_element_for_by_and_for_get() {
1567 let mut f = Fixture::new();
1568 f.run(&[b"RPUSH", b"l", b"a", b"b"]);
1569 f.run(&[b"MSET", b"w_a", b"2", b"w_b", b"1", b"d_b", b"bee"]);
1570 assert_eq!(
1573 f.run(&[b"SORT", b"l", b"BY", b"w_*", b"GET", b"#", b"GET", b"d_*"]),
1574 "*4\r\n$1\r\nb\r\n$3\r\nbee\r\n$1\r\na\r\n$-1\r\n"
1575 );
1576 }
1577
1578 #[test]
1579 fn sort_store_writes_a_list_and_answers_its_length() {
1580 let mut f = Fixture::new();
1581 f.run(&[b"RPUSH", b"l", b"3", b"1", b"2"]);
1582 assert_eq!(f.run(&[b"SORT", b"l", b"STORE", b"out"]), ":3\r\n");
1583 assert_eq!(f.run(&[b"TYPE", b"out"]), "+list\r\n");
1584 assert_eq!(
1585 f.run(&[b"LRANGE", b"out", b"0", b"-1"]),
1586 "*3\r\n$1\r\n1\r\n$1\r\n2\r\n$1\r\n3\r\n"
1587 );
1588 assert_eq!(f.run(&[b"SORT", b"missing", b"STORE", b"out"]), ":0\r\n");
1591 assert_eq!(f.run(&[b"EXISTS", b"out"]), ":0\r\n");
1592 }
1593
1594 #[test]
1595 fn sort_ro_does_not_know_the_word_store() {
1596 let mut f = Fixture::new();
1597 f.run(&[b"RPUSH", b"l", b"2", b"1"]);
1598 assert_eq!(f.run(&[b"SORT_RO", b"l"]), "*2\r\n$1\r\n1\r\n$1\r\n2\r\n");
1599 assert_eq!(
1600 f.run(&[b"SORT_RO", b"l", b"STORE", b"d"]),
1601 "-ERR syntax error\r\n"
1602 );
1603 assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
1604 }
1605
1606 #[test]
1607 fn sort_refuses_what_it_cannot_sort() {
1608 let mut f = Fixture::new();
1609 assert_eq!(f.run(&[b"SORT", b"nosuchkey"]), "*0\r\n");
1610 f.run(&[b"SET", b"s", b"x"]);
1611 assert_eq!(
1612 f.run(&[b"SORT", b"s"]),
1613 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
1614 );
1615 f.run(&[b"RPUSH", b"words", b"one", b"two"]);
1616 assert_eq!(
1617 f.run(&[b"SORT", b"words"]),
1618 "-ERR One or more scores can't be converted into double\r\n"
1619 );
1620 assert_eq!(
1621 f.run(&[b"SORT", b"words", b"ALPHA"]),
1622 "*2\r\n$3\r\none\r\n$3\r\ntwo\r\n"
1623 );
1624 assert_eq!(f.run(&[b"SORT", b"words", b"BY"]), "-ERR syntax error\r\n");
1625 }
1626
1627 #[test]
1628 fn move_takes_the_key_out_of_one_database_and_puts_it_in_another() {
1629 let mut f = Fixture::new();
1630 assert_eq!(f.run(&[b"RPUSH", b"l", b"a", b"b"]), ":2\r\n");
1631 assert_eq!(f.run(&[b"MOVE", b"l", b"1"]), ":1\r\n");
1632 assert_eq!(f.run(&[b"EXISTS", b"l"]), ":0\r\n");
1633 assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
1634 assert_eq!(
1635 f.run(&[b"LRANGE", b"l", b"0", b"-1"]),
1636 "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
1637 );
1638 assert_eq!(f.run(&[b"MOVE", b"l", b"0"]), ":1\r\n");
1641 assert_eq!(f.run(&[b"EXISTS", b"l"]), ":0\r\n");
1642 }
1643
1644 #[test]
1645 fn move_answers_zero_when_either_end_says_no() {
1646 let mut f = Fixture::new();
1647 assert_eq!(f.run(&[b"MOVE", b"nope", b"1"]), ":0\r\n");
1648 assert_eq!(f.run(&[b"SET", b"a", b"here"]), "+OK\r\n");
1649 assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
1650 assert_eq!(f.run(&[b"SET", b"a", b"there"]), "+OK\r\n");
1651 assert_eq!(f.run(&[b"SELECT", b"0"]), "+OK\r\n");
1652 assert_eq!(f.run(&[b"MOVE", b"a", b"1"]), ":0\r\n");
1655 assert_eq!(f.run(&[b"GET", b"a"]), "$4\r\nhere\r\n");
1656 assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
1657 assert_eq!(f.run(&[b"GET", b"a"]), "$5\r\nthere\r\n");
1658 }
1659
1660 #[test]
1661 fn move_refuses_a_database_that_is_not_one_and_the_one_it_is_on() {
1662 let mut f = Fixture::new();
1663 assert_eq!(
1664 f.run(&[b"MOVE", b"a", b"0"]),
1665 "-ERR source and destination objects are the same\r\n"
1666 );
1667 assert_eq!(
1668 f.run(&[b"MOVE", b"a", b"99"]),
1669 "-ERR DB index is out of range\r\n"
1670 );
1671 assert_eq!(
1672 f.run(&[b"MOVE", b"a", b"-1"]),
1673 "-ERR DB index is out of range\r\n"
1674 );
1675 assert_eq!(
1676 f.run(&[b"MOVE", b"a", b"x"]),
1677 "-ERR value is not an integer or out of range\r\n"
1678 );
1679 }
1680
1681 #[test]
1682 fn swapdb_swaps_what_two_connections_would_see() {
1683 let mut f = Fixture::new();
1684 assert_eq!(f.run(&[b"SET", b"k", b"zero"]), "+OK\r\n");
1685 assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
1686 assert_eq!(f.run(&[b"SET", b"k", b"one"]), "+OK\r\n");
1687 assert_eq!(f.run(&[b"SELECT", b"0"]), "+OK\r\n");
1688
1689 assert_eq!(f.run(&[b"SWAPDB", b"0", b"1"]), "+OK\r\n");
1690 assert_eq!(f.run(&[b"GET", b"k"]), "$3\r\none\r\n");
1692 assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
1693 assert_eq!(f.run(&[b"GET", b"k"]), "$4\r\nzero\r\n");
1694 assert_eq!(f.run(&[b"SWAPDB", b"1", b"1"]), "+OK\r\n");
1696 assert_eq!(f.run(&[b"GET", b"k"]), "$4\r\nzero\r\n");
1697 }
1698
1699 #[test]
1700 fn swapdb_says_which_index_it_could_not_read() {
1701 let mut f = Fixture::new();
1702 assert_eq!(
1703 f.run(&[b"SWAPDB", b"x", b"1"]),
1704 "-ERR invalid first DB index\r\n"
1705 );
1706 assert_eq!(
1707 f.run(&[b"SWAPDB", b"0", b"y"]),
1708 "-ERR invalid second DB index\r\n"
1709 );
1710 assert_eq!(
1714 f.run(&[b"SWAPDB", b"99999999999999", b"1"]),
1715 "-ERR invalid first DB index\r\n"
1716 );
1717 assert_eq!(
1718 f.run(&[b"SWAPDB", b"0", b"99"]),
1719 "-ERR DB index is out of range\r\n"
1720 );
1721 assert_eq!(
1722 f.run(&[b"SWAPDB", b"-1", b"0"]),
1723 "-ERR DB index is out of range\r\n"
1724 );
1725 }
1726
1727 #[test]
1728 fn wait_answers_zero_replicas_without_waiting() {
1729 let mut f = Fixture::new();
1730 assert_eq!(f.run(&[b"SET", b"a", b"v"]), "+OK\r\n");
1731 assert_eq!(f.run(&[b"WAIT", b"0", b"0"]), ":0\r\n");
1732 assert_eq!(f.run(&[b"WAIT", b"3", b"1000"]), ":0\r\n");
1735 assert_eq!(f.run(&[b"WAIT", b"-1", b"0"]), ":0\r\n");
1738 assert_eq!(
1739 f.run(&[b"WAIT", b"x", b"0"]),
1740 "-ERR value is not an integer or out of range\r\n"
1741 );
1742 assert_eq!(
1743 f.run(&[b"WAIT", b"0", b"-1"]),
1744 "-ERR timeout is negative\r\n"
1745 );
1746 assert_eq!(
1747 f.run(&[b"WAIT", b"0", b"1.5"]),
1748 "-ERR timeout is not an integer or out of range\r\n"
1749 );
1750 }
1751
1752 #[test]
1753 fn waitaof_answers_two_zeroes_and_refuses_a_local_wait() {
1754 let mut f = Fixture::new();
1755 assert_eq!(f.run(&[b"WAITAOF", b"0", b"0", b"0"]), "*2\r\n:0\r\n:0\r\n");
1756 assert_eq!(
1757 f.run(&[b"WAITAOF", b"1", b"0", b"0"]),
1758 "-ERR WAITAOF cannot be used when numlocal is set but appendonly is disabled.\r\n"
1759 );
1760 assert_eq!(
1761 f.run(&[b"WAITAOF", b"2", b"0", b"0"]),
1762 "-ERR value is out of range, value must between 0 and 1\r\n"
1763 );
1764 assert_eq!(
1765 f.run(&[b"WAITAOF", b"0", b"-1", b"0"]),
1766 "-ERR value is out of range, must be positive\r\n"
1767 );
1768 assert_eq!(
1771 f.run(&[b"WAITAOF", b"1", b"0", b"-5"]),
1772 "-ERR timeout is negative\r\n"
1773 );
1774 }
1775
1776 fn payload(reply: &[u8]) -> Vec<u8> {
1780 let head = reply.windows(2).position(|w| w == b"\r\n").unwrap();
1781 reply[head + 2..reply.len() - 2].to_vec()
1782 }
1783
1784 #[test]
1785 fn a_value_survives_a_dump_and_a_restore() {
1786 let mut f = Fixture::new();
1787 f.run(&[b"SET", b"s", b"hello"]);
1788 f.run(&[b"RPUSH", b"l", b"a", b"b", b"c"]);
1789 f.run(&[b"SADD", b"t", b"1", b"2", b"3"]);
1790 f.run(&[b"SADD", b"u", b"x", b"y"]);
1791 f.run(&[b"HSET", b"h", b"f", b"1", b"g", b"2"]);
1792 f.run(&[b"ZADD", b"z", b"1.5", b"a", b"2.5", b"b"]);
1793
1794 for key in [&b"s"[..], b"l", b"t", b"u", b"h", b"z"] {
1795 let mut copy = key.to_vec();
1796 copy.push(b'2');
1797 let bytes = payload(&f.raw(&[b"DUMP", key]));
1798 assert_eq!(f.run(&[b"RESTORE", ©, b"0", &bytes]), "+OK\r\n");
1799 assert_eq!(f.run(&[b"TYPE", ©]), f.run(&[b"TYPE", key]));
1800 }
1801
1802 assert_eq!(f.run(&[b"GET", b"s2"]), "$5\r\nhello\r\n");
1803 assert_eq!(
1804 f.run(&[b"LRANGE", b"l2", b"0", b"-1"]),
1805 "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
1806 );
1807 assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"t2"])), ["1", "2", "3"]);
1808 assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"u2"])), ["x", "y"]);
1809 assert_eq!(f.run(&[b"HGET", b"h2", b"g"]), "$1\r\n2\r\n");
1810 assert_eq!(f.run(&[b"ZSCORE", b"z2", b"b"]), "$3\r\n2.5\r\n");
1811 assert_eq!(
1814 f.run(&[b"OBJECT", b"ENCODING", b"t2"]),
1815 f.run(&[b"OBJECT", b"ENCODING", b"t"])
1816 );
1817 }
1818
1819 #[test]
1820 fn a_dumped_hash_keeps_its_field_deadlines() {
1821 let mut f = Fixture::new();
1822 f.run(&[b"HSET", b"h", b"keep", b"1", b"go", b"2"]);
1823 assert_eq!(
1824 f.run(&[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"go"]),
1825 "*1\r\n:1\r\n"
1826 );
1827 let bytes = payload(&f.raw(&[b"DUMP", b"h"]));
1828 assert_eq!(f.run(&[b"RESTORE", b"h2", b"0", &bytes]), "+OK\r\n");
1829 assert_eq!(
1830 f.run(&[b"HTTL", b"h2", b"FIELDS", b"2", b"keep", b"go"]),
1831 "*2\r\n:-1\r\n:100\r\n"
1832 );
1833 }
1834
1835 #[test]
1836 fn dump_leaves_the_deadline_behind_and_restore_is_given_a_new_one() {
1837 let mut f = Fixture::new();
1838 f.run(&[b"SET", b"a", b"v", b"EX", b"100"]);
1839 let bytes = payload(&f.raw(&[b"DUMP", b"a"]));
1840 assert_eq!(f.run(&[b"RESTORE", b"b", b"0", &bytes]), "+OK\r\n");
1841 assert_eq!(f.run(&[b"TTL", b"b"]), ":-1\r\n");
1842 assert_eq!(f.run(&[b"RESTORE", b"c", b"5000", &bytes]), "+OK\r\n");
1843 assert_eq!(f.run(&[b"TTL", b"c"]), ":5\r\n");
1844 assert_eq!(
1847 f.run(&[b"RESTORE", b"d", b"1", &bytes, b"ABSTTL"]),
1848 "+OK\r\n"
1849 );
1850 assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
1851 }
1852
1853 #[test]
1854 fn dump_answers_nothing_for_a_key_that_is_not_there() {
1855 let mut f = Fixture::new();
1856 assert_eq!(f.run(&[b"DUMP", b"nope"]), "$-1\r\n");
1857 f.run(&[b"SET", b"gone", b"v", b"PX", b"10"]);
1858 f.advance(50);
1859 assert_eq!(f.run(&[b"DUMP", b"gone"]), "$-1\r\n");
1860 }
1861
1862 #[test]
1863 fn restore_refuses_a_key_that_is_there_unless_it_is_told_to_replace() {
1864 let mut f = Fixture::new();
1865 f.run(&[b"SET", b"a", b"first"]);
1866 f.run(&[b"SET", b"b", b"second"]);
1867 let bytes = payload(&f.raw(&[b"DUMP", b"b"]));
1868 assert_eq!(
1869 f.run(&[b"RESTORE", b"a", b"0", &bytes]),
1870 "-BUSYKEY Target key name already exists.\r\n"
1871 );
1872 assert_eq!(f.run(&[b"GET", b"a"]), "$5\r\nfirst\r\n");
1873 assert_eq!(
1874 f.run(&[b"RESTORE", b"a", b"0", &bytes, b"REPLACE"]),
1875 "+OK\r\n"
1876 );
1877 assert_eq!(f.run(&[b"GET", b"a"]), "$6\r\nsecond\r\n");
1878 }
1879
1880 #[test]
1884 fn restore_asks_about_the_key_before_it_looks_at_the_bytes() {
1885 let mut f = Fixture::new();
1886 f.run(&[b"SET", b"a", b"v"]);
1887 assert_eq!(
1888 f.run(&[b"RESTORE", b"a", b"0", b"rubbish"]),
1889 "-BUSYKEY Target key name already exists.\r\n"
1890 );
1891 assert_eq!(
1894 f.run(&[b"RESTORE", b"a", b"0", b"rubbish", b"FREQ", b"300"]),
1895 "-ERR Invalid FREQ value, must be >= 0 and <= 255\r\n"
1896 );
1897 }
1898
1899 #[test]
1900 fn restore_can_tell_a_bad_footer_from_bad_bytes() {
1901 let mut f = Fixture::new();
1902 f.run(&[b"SET", b"a", b"hello"]);
1903 let good = payload(&f.raw(&[b"DUMP", b"a"]));
1904
1905 let mut flipped = good.clone();
1906 flipped[2] ^= 0x40;
1907 assert_eq!(
1908 f.run(&[b"RESTORE", b"b", b"0", &flipped]),
1909 "-ERR DUMP payload version or checksum are wrong\r\n"
1910 );
1911 assert_eq!(
1912 f.run(&[b"RESTORE", b"b", b"0", b"short"]),
1913 "-ERR DUMP payload version or checksum are wrong\r\n"
1914 );
1915 let mut truncated = good[..1].to_vec();
1919 truncated.extend_from_slice(&good[good.len() - 10..good.len() - 8]);
1920 let crc = yo_common::crc::crc64(0, &truncated);
1921 truncated.extend_from_slice(&crc.to_le_bytes());
1922 assert_eq!(
1923 f.run(&[b"RESTORE", b"b", b"0", &truncated]),
1924 "-ERR Bad data format\r\n"
1925 );
1926 assert_eq!(f.run(&[b"EXISTS", b"b"]), ":0\r\n");
1927 }
1928
1929 #[test]
1930 fn restore_checks_the_three_numbers_a_client_can_get_wrong() {
1931 let mut f = Fixture::new();
1932 f.run(&[b"SET", b"a", b"v"]);
1933 let bytes = payload(&f.raw(&[b"DUMP", b"a"]));
1934 assert_eq!(
1935 f.run(&[b"RESTORE", b"b", b"-1", &bytes]),
1936 "-ERR Invalid TTL value, must be >= 0\r\n"
1937 );
1938 assert_eq!(
1939 f.run(&[b"RESTORE", b"b", b"0", &bytes, b"IDLETIME", b"-1"]),
1940 "-ERR Invalid IDLETIME value, must be >= 0\r\n"
1941 );
1942 assert_eq!(
1943 f.run(&[b"RESTORE", b"b", b"0", &bytes, b"FREQ", b"256"]),
1944 "-ERR Invalid FREQ value, must be >= 0 and <= 255\r\n"
1945 );
1946 assert_eq!(
1948 f.run(&[b"RESTORE", b"b", b"0", &bytes, b"IDLETIME", b"90"]),
1949 "+OK\r\n"
1950 );
1951 assert_eq!(
1952 f.run(&[b"RESTORE", b"c", b"0", &bytes, b"FREQ", b"200", b"REPLACE"]),
1953 "+OK\r\n"
1954 );
1955 }
1956
1957 #[test]
1961 fn restore_takes_idletime_or_freq_and_not_both() {
1962 let mut f = Fixture::new();
1963 f.run(&[b"SET", b"a", b"v"]);
1964 let bytes = payload(&f.raw(&[b"DUMP", b"a"]));
1965 assert_eq!(
1966 f.run(&[
1967 b"RESTORE",
1968 b"b",
1969 b"0",
1970 &bytes,
1971 b"IDLETIME",
1972 b"1",
1973 b"FREQ",
1974 b"2"
1975 ]),
1976 "-ERR syntax error\r\n"
1977 );
1978 assert_eq!(
1979 f.run(&[
1980 b"RESTORE",
1981 b"b",
1982 b"0",
1983 &bytes,
1984 b"FREQ",
1985 b"2",
1986 b"IDLETIME",
1987 b"1"
1988 ]),
1989 "-ERR syntax error\r\n"
1990 );
1991 assert_eq!(
1992 f.run(&[b"RESTORE", b"b", b"0", &bytes, b"FREQ"]),
1993 "-ERR syntax error\r\n"
1994 );
1995 assert_eq!(
1996 f.run(&[b"RESTORE", b"b", b"0", &bytes, b"NOSUCH"]),
1997 "-ERR syntax error\r\n"
1998 );
1999 }
2000
2001 #[test]
2002 fn copy_checks_its_options_before_it_looks_for_anything() {
2003 let mut f = Fixture::new();
2004 assert_eq!(
2007 f.run(&[b"COPY", b"a", b"b", b"DB", b"99"]),
2008 "-ERR DB index is out of range\r\n"
2009 );
2010 assert_eq!(
2011 f.run(&[b"COPY", b"a", b"b", b"DB", b"-1"]),
2012 "-ERR DB index is out of range\r\n"
2013 );
2014 assert_eq!(
2015 f.run(&[b"COPY", b"a", b"b", b"DB", b"x"]),
2016 "-ERR value is not an integer or out of range\r\n"
2017 );
2018 assert_eq!(
2019 f.run(&[b"COPY", b"a", b"b", b"nonsense"]),
2020 "-ERR syntax error\r\n"
2021 );
2022 assert_eq!(
2023 f.run(&[b"COPY", b"a", b"a"]),
2024 "-ERR source and destination objects are the same\r\n"
2025 );
2026 assert_eq!(
2028 f.run(&[b"COPY", b"a", b"b", b"dB", b"1", b"rEpLaCe", b"db", b"2"]),
2029 ":0\r\n"
2030 );
2031 }
2032
2033 #[test]
2034 fn time_is_two_bulk_strings_and_moves() {
2035 let mut f = Fixture::new();
2036 let first = f.run(&[b"TIME"]);
2037 assert!(first.starts_with("*2\r\n$"), "got {first}");
2038 let parts: Vec<&str> = first.split("\r\n").collect();
2039 let secs: i64 = parts[2].parse().expect("seconds as decimal text");
2040 let micros: i64 = parts[4].parse().expect("microseconds as decimal text");
2041 assert!(secs > 1_700_000_000, "a real wall clock, got {secs}");
2042 assert!((0..1_000_000).contains(µs), "got {micros}");
2043 assert_ne!(first, f.run(&[b"TIME"]));
2047 }
2048
2049 #[test]
2050 fn a_keyspace_scan_walks_every_key_once() {
2051 let mut f = Fixture::new();
2052 for i in 0..500 {
2053 f.run(&[b"SET", format!("k{i}").as_bytes(), b"v"]);
2054 }
2055
2056 let mut seen: Vec<String> = Vec::new();
2057 let mut cursor = "0".to_owned();
2058 let mut calls = 0;
2059 loop {
2060 let (next, keys) = scan_reply(&f.run(&[b"SCAN", cursor.as_bytes(), b"COUNT", b"32"]));
2061 seen.extend(keys);
2062 cursor = next;
2063 calls += 1;
2064 assert!(calls < 10_000, "the cursor is not advancing");
2065 if cursor == "0" {
2066 break;
2067 }
2068 }
2069
2070 seen.sort();
2071 seen.dedup();
2072 assert_eq!(seen.len(), 500, "every key once and only once");
2073 assert!(calls > 1, "500 keys came back in one batch");
2076 }
2077
2078 #[test]
2079 fn a_scan_narrows_by_pattern_and_by_type() {
2080 let mut f = Fixture::new();
2081 f.run(&[b"SET", b"str", b"v"]);
2082 f.run(&[b"SADD", b"members", b"a"]);
2083 f.run(&[b"HSET", b"fields", b"f", b"v"]);
2084
2085 let all = |f: &mut Fixture, args: &[&[u8]]| {
2086 let mut out: Vec<String> = Vec::new();
2087 let mut cursor = "0".to_owned();
2088 loop {
2089 let mut line: Vec<&[u8]> = vec![b"SCAN", cursor.as_bytes()];
2090 line.extend_from_slice(args);
2091 let (next, keys) = scan_reply(&f.run(&line));
2092 out.extend(keys);
2093 cursor = next;
2094 if cursor == "0" {
2095 break;
2096 }
2097 }
2098 out.sort();
2099 out
2100 };
2101
2102 assert_eq!(all(&mut f, &[]), ["fields", "members", "str"]);
2103 assert_eq!(all(&mut f, &[b"MATCH", b"*e*"]), ["fields", "members"]);
2104 assert_eq!(all(&mut f, &[b"TYPE", b"set"]), ["members"]);
2105 assert_eq!(all(&mut f, &[b"TYPE", b"HASH"]), ["fields"]);
2107 assert!(all(&mut f, &[b"TYPE", b"list"]).is_empty());
2109 assert!(all(&mut f, &[b"TYPE", b"banana"]).is_empty());
2110 assert!(all(&mut f, &[b"MATCH", b"str*", b"TYPE", b"set"]).is_empty());
2112 }
2113
2114 #[test]
2115 fn a_scan_says_what_is_wrong_with_it() {
2116 let mut f = Fixture::new();
2117 assert_eq!(f.run(&[b"SCAN", b"nope"]), "-ERR invalid cursor\r\n");
2118 assert_eq!(f.run(&[b"SCAN", b"-1"]), "-ERR invalid cursor\r\n");
2119 assert_eq!(f.run(&[b"SCAN", b"0", b"MATCH"]), "-ERR syntax error\r\n");
2120 assert_eq!(
2121 f.run(&[b"SCAN", b"0", b"COUNT", b"0"]),
2122 "-ERR syntax error\r\n"
2123 );
2124 assert_eq!(
2125 f.run(&[b"SCAN", b"0", b"COUNT", b"x"]),
2126 "-ERR value is not an integer or out of range\r\n"
2127 );
2128 assert_eq!(
2129 f.run(&[b"SCAN", b"0", b"WAT", b"1"]),
2130 "-ERR syntax error\r\n"
2131 );
2132 assert!(f.run(&[b"SCAN", b"18446744073709551615"]).starts_with("*2"));
2137 }
2138
2139 #[test]
2140 fn keys_and_randomkey_look_at_the_whole_database() {
2141 let mut f = Fixture::new();
2142 assert_eq!(f.run(&[b"KEYS", b"*"]), "*0\r\n");
2143 assert_eq!(f.run(&[b"RANDOMKEY"]), "$-1\r\n");
2144
2145 for name in ["one", "two", "three"] {
2146 f.run(&[b"SET", name.as_bytes(), b"v"]);
2147 }
2148 assert_eq!(sorted(&f.run(&[b"KEYS", b"*"])), ["one", "three", "two"]);
2149 assert_eq!(sorted(&f.run(&[b"KEYS", b"t*"])), ["three", "two"]);
2150 assert_eq!(f.run(&[b"KEYS", b"nothing"]), "*0\r\n");
2151
2152 for _ in 0..50 {
2153 let got = f.run(&[b"RANDOMKEY"]);
2154 assert!(
2155 ["$3\r\none\r\n", "$3\r\ntwo\r\n", "$5\r\nthree\r\n"].contains(&got.as_str()),
2156 "got {got}"
2157 );
2158 }
2159 }
2160
2161 #[test]
2162 fn a_walk_does_not_answer_keys_that_have_expired() {
2163 let mut f = Fixture::new();
2164 f.run(&[b"SET", b"alive", b"v"]);
2165 f.run(&[b"SET", b"dead", b"v", b"PX", b"1"]);
2166 f.server.db(0).clock_mut().advance(2);
2167 assert_eq!(
2168 f.run(&[b"DBSIZE"]),
2169 ":2\r\n",
2170 "nothing has collected it yet"
2171 );
2172
2173 assert_eq!(f.run(&[b"KEYS", b"*"]), "*1\r\n$5\r\nalive\r\n");
2174 let (_, keys) = scan_reply(&f.run(&[b"SCAN", b"0", b"COUNT", b"1000"]));
2175 assert_eq!(keys, ["alive"]);
2176 for _ in 0..20 {
2177 assert_eq!(f.run(&[b"RANDOMKEY"]), "$5\r\nalive\r\n");
2178 }
2179 assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
2182 }
2183
2184 #[test]
2185 fn a_key_deadline_goes_on_and_comes_back_in_all_four_units() {
2186 let mut f = Fixture::new();
2187 f.run(&[b"SET", b"k", b"v"]);
2188 assert_eq!(f.run(&[b"TTL", b"k"]), ":-1\r\n", "there and no deadline");
2189 assert_eq!(f.run(&[b"TTL", b"nosuch"]), ":-2\r\n", "not there at all");
2190
2191 assert_eq!(f.run(&[b"EXPIRE", b"k", b"100"]), ":1\r\n");
2192 assert_eq!(f.run(&[b"TTL", b"k"]), ":100\r\n");
2193 let ms = int(&f.run(&[b"PTTL", b"k"]));
2194 assert!((99_000..=100_000).contains(&ms), "got {ms}");
2195
2196 let at = int(&f.run(&[b"EXPIRETIME", b"k"]));
2198 let at_ms = int(&f.run(&[b"PEXPIRETIME", b"k"]));
2199 assert_eq!(at, (at_ms + 500) / 1000);
2200 assert!(at_ms > 1_700_000_000_000, "an absolute moment, got {at_ms}");
2201
2202 assert_eq!(f.run(&[b"PERSIST", b"k"]), ":1\r\n");
2203 assert_eq!(f.run(&[b"TTL", b"k"]), ":-1\r\n");
2204 assert_eq!(
2205 f.run(&[b"PERSIST", b"k"]),
2206 ":0\r\n",
2207 "nothing to take off the second time"
2208 );
2209 assert_eq!(f.run(&[b"PERSIST", b"nosuch"]), ":0\r\n");
2210 assert_eq!(
2211 f.run(&[b"GET", b"k"]),
2212 "$1\r\nv\r\n",
2213 "and the value went through all of that untouched"
2214 );
2215 }
2216
2217 #[test]
2218 fn every_type_can_be_given_a_deadline_and_it_is_the_same_deadline() {
2219 let mut f = Fixture::new();
2220 f.run(&[b"SET", b"str", b"v"]);
2221 f.run(&[b"SADD", b"set", b"a", b"b"]);
2222 f.run(&[b"HSET", b"hash", b"f", b"v"]);
2223
2224 for key in [b"str".as_slice(), b"set", b"hash"] {
2225 assert_eq!(f.run(&[b"EXPIRE", key, b"100"]), ":1\r\n");
2226 assert_eq!(f.run(&[b"TTL", key]), ":100\r\n");
2227 }
2228 assert_eq!(f.run(&[b"SCARD", b"set"]), ":2\r\n");
2231 assert_eq!(f.run(&[b"HGET", b"hash", b"f"]), "$1\r\nv\r\n");
2232 assert_eq!(f.run(&[b"GET", b"str"]), "$1\r\nv\r\n");
2233 }
2234
2235 #[test]
2236 fn a_deadline_that_has_already_gone_deletes_the_key_now() {
2237 let mut f = Fixture::new();
2238 for key in [b"a".as_slice(), b"b", b"c", b"d"] {
2239 f.run(&[b"SET", key, b"v"]);
2240 }
2241 assert_eq!(f.run(&[b"EXPIRE", b"a", b"0"]), ":1\r\n");
2245 assert_eq!(f.run(&[b"EXPIRE", b"b", b"-1"]), ":1\r\n");
2246 assert_eq!(f.run(&[b"EXPIREAT", b"c", b"1"]), ":1\r\n");
2247 assert_eq!(f.run(&[b"PEXPIREAT", b"d", b"1"]), ":1\r\n");
2248 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
2249 assert_eq!(
2250 f.run(&[b"EXPIRE", b"a", b"100"]),
2251 ":0\r\n",
2252 "and the key really went, so there is nothing to put a deadline on"
2253 );
2254 }
2255
2256 #[test]
2257 fn the_four_conditions_decide_whether_the_deadline_moves() {
2258 let mut f = Fixture::new();
2259 f.run(&[b"SET", b"k", b"v"]);
2260
2261 assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"XX"]), ":0\r\n");
2262 assert_eq!(f.run(&[b"TTL", b"k"]), ":-1\r\n", "and XX left it alone");
2263 assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"GT"]), ":0\r\n");
2264 assert_eq!(
2265 f.run(&[b"EXPIRE", b"k", b"100", b"LT"]),
2266 ":1\r\n",
2267 "no deadline reads as infinitely far away, so LT passes where GT fails"
2268 );
2269
2270 assert_eq!(f.run(&[b"EXPIRE", b"k", b"50", b"NX"]), ":0\r\n");
2271 assert_eq!(f.run(&[b"EXPIRE", b"k", b"50", b"GT"]), ":0\r\n");
2272 assert_eq!(f.run(&[b"TTL", b"k"]), ":100\r\n");
2273 assert_eq!(f.run(&[b"EXPIRE", b"k", b"50", b"LT"]), ":1\r\n");
2274 assert_eq!(f.run(&[b"EXPIRE", b"k", b"200", b"GT"]), ":1\r\n");
2275 assert_eq!(f.run(&[b"TTL", b"k"]), ":200\r\n");
2276
2277 assert_eq!(f.run(&[b"EXPIRE", b"k", b"0", b"NX"]), ":0\r\n");
2280 assert_eq!(f.run(&[b"EXISTS", b"k"]), ":1\r\n");
2281 assert_eq!(f.run(&[b"EXPIRE", b"k", b"0", b"XX"]), ":1\r\n");
2282 assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n", "and XX let it through");
2283 }
2284
2285 #[test]
2286 fn the_conditions_are_a_set_and_not_a_keyword() {
2287 let mut f = Fixture::new();
2288 f.run(&[b"SET", b"k", b"v"]);
2289
2290 assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"nx"]), ":1\r\n");
2291 assert_eq!(
2292 f.run(&[b"EXPIRE", b"k", b"100", b"nx", b"nx"]),
2293 ":0\r\n",
2294 "the same keyword twice means it once, and NX now has a deadline to fail on"
2295 );
2296
2297 assert_eq!(f.run(&[b"EXPIRE", b"k", b"200", b"xx", b"gt"]), ":1\r\n");
2300 assert_eq!(f.run(&[b"TTL", b"k"]), ":200\r\n");
2301 assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"gt", b"xx"]), ":0\r\n");
2302 assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"XX", b"LT"]), ":1\r\n");
2303 assert_eq!(f.run(&[b"TTL", b"k"]), ":100\r\n");
2304 f.run(&[b"PERSIST", b"k"]);
2305 assert_eq!(
2306 f.run(&[b"EXPIRE", b"k", b"100", b"XX", b"LT"]),
2307 ":0\r\n",
2308 "where LT on its own would have taken it"
2309 );
2310 assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"LT"]), ":1\r\n");
2311 }
2312
2313 #[test]
2314 fn a_key_is_gone_once_its_moment_passes() {
2315 let mut f = Fixture::new();
2316 f.run(&[b"SET", b"k", b"v"]);
2317 f.run(&[b"EXPIRE", b"k", b"100"]);
2318
2319 let at = int(&f.run(&[b"PEXPIRETIME", b"k"]));
2320 f.server.set_clock_ms(at as u64 + 1);
2321 assert_eq!(f.run(&[b"GET", b"k"]), "$-1\r\n");
2322 assert_eq!(f.run(&[b"TTL", b"k"]), ":-2\r\n");
2323 assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
2324 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
2325 }
2326
2327 #[test]
2328 fn the_expiry_commands_refuse_what_a_real_server_refuses() {
2329 let mut f = Fixture::new();
2330 f.run(&[b"SET", b"k", b"v"]);
2331 for (bad, want) in [
2332 (
2333 &[b"EXPIRE".as_slice(), b"k", b"soon"][..],
2334 "-ERR value is not an integer or out of range\r\n",
2335 ),
2336 (
2337 &[b"EXPIRE", b"k", b"100", b"MAYBE"],
2338 "-ERR Unsupported option MAYBE\r\n",
2339 ),
2340 (
2341 &[b"EXPIRE", b"k", b"100", b"NX", b"XX"],
2342 "-ERR NX and XX, GT or LT options at the same time are not compatible\r\n",
2343 ),
2344 (
2345 &[b"EXPIRE", b"k", b"100", b"NX", b"GT"],
2346 "-ERR NX and XX, GT or LT options at the same time are not compatible\r\n",
2347 ),
2348 (
2349 &[b"EXPIRE", b"k", b"100", b"GT", b"LT", b"GT"],
2350 "-ERR GT and LT options at the same time are not compatible\r\n",
2351 ),
2352 (
2355 &[b"EXPIRE", b"k", b"9223372036854775807"],
2356 "-ERR invalid expire time in 'expire' command\r\n",
2357 ),
2358 (
2359 &[b"EXPIREAT", b"k", b"9223372036854775807"],
2360 "-ERR invalid expire time in 'expireat' command\r\n",
2361 ),
2362 (
2363 &[b"PEXPIRE", b"k", b"9223372036854775807"],
2364 "-ERR invalid expire time in 'pexpire' command\r\n",
2365 ),
2366 ] {
2367 assert_eq!(f.run(bad), want, "for {bad:?}");
2368 }
2369 assert_eq!(
2370 f.run(&[b"TTL", b"k"]),
2371 ":-1\r\n",
2372 "and none of those put a deadline on anything"
2373 );
2374
2375 assert_eq!(
2379 f.run(&[b"PEXPIREAT", b"k", b"9223372036854775807"]),
2380 ":1\r\n"
2381 );
2382 assert_eq!(f.run(&[b"PEXPIRETIME", b"k"]), ":70368744177663\r\n");
2383 }
2384
2385 #[test]
2386 fn flushing_empties_this_database_or_every_one_of_them() {
2387 let mut f = Fixture::new();
2388 f.run(&[b"SELECT", b"0"]);
2389 f.run(&[b"MSET", b"a", b"1", b"b", b"2"]);
2390 f.run(&[b"SELECT", b"1"]);
2391 f.run(&[b"SET", b"c", b"3"]);
2392 assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
2393 assert_eq!(f.run(&[b"FLUSHDB", b"async"]), "+OK\r\n");
2396 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
2397 f.run(&[b"SELECT", b"0"]);
2399 assert_eq!(f.run(&[b"DBSIZE"]), ":2\r\n");
2400 assert_eq!(f.run(&[b"FLUSHALL", b"SYNC"]), "+OK\r\n");
2401 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
2402 f.run(&[b"SELECT", b"1"]);
2403 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
2404 assert_eq!(f.run(&[b"FLUSHALL", b"nope"]), "-ERR syntax error\r\n");
2407 assert_eq!(
2408 f.run(&[b"FLUSHDB", b"sync", b"sync"]),
2409 "-ERR syntax error\r\n"
2410 );
2411 }
2412
2413 #[test]
2414 fn the_script_cache_and_the_library_set_answer_for_being_empty() {
2415 let mut f = Fixture::new();
2416 assert_eq!(f.run(&[b"SCRIPT", b"FLUSH"]), "+OK\r\n");
2417 assert_eq!(f.run(&[b"SCRIPT", b"FLUSH", b"async"]), "+OK\r\n");
2418 assert_eq!(f.run(&[b"FUNCTION", b"FLUSH", b"SYNC"]), "+OK\r\n");
2419 assert_eq!(
2422 f.run(&[b"SCRIPT", b"EXISTS", b"aaaa", b"bbbb"]),
2423 "*2\r\n:0\r\n:0\r\n"
2424 );
2425 assert_eq!(f.run(&[b"FUNCTION", b"LIST"]), "*0\r\n");
2426 assert_eq!(
2427 f.run(&[b"FUNCTION", b"LIST", b"LIBRARYNAME", b"x", b"WITHCODE"]),
2428 "*0\r\n"
2429 );
2430 assert_eq!(
2431 f.run(&[b"FUNCTION", b"DELETE", b"nosuch"]),
2432 "-ERR Library not found\r\n"
2433 );
2434
2435 assert_eq!(
2438 f.run(&[b"SCRIPT", b"FLUSH", b"nope"]),
2439 "-ERR SCRIPT FLUSH only support SYNC|ASYNC option\r\n"
2440 );
2441 assert_eq!(
2442 f.run(&[b"FUNCTION", b"FLUSH", b"nope"]),
2443 "-ERR FUNCTION FLUSH only supports SYNC|ASYNC option\r\n"
2444 );
2445 assert_eq!(
2448 f.run(&[b"FUNCTION", b"FLUSH", b"sync", b"sync"]),
2449 "-ERR unknown subcommand or wrong number of arguments for 'flush'. Try FUNCTION HELP.\r\n"
2450 );
2451 assert_eq!(
2452 f.run(&[b"FUNCTION", b"LIST", b"bogus"]),
2453 "-ERR Unknown argument bogus\r\n"
2454 );
2455 assert_eq!(
2456 f.run(&[b"SCRIPT", b"EXISTS"]),
2457 "-ERR wrong number of arguments for 'script|exists' command\r\n"
2458 );
2459
2460 assert_eq!(
2463 f.run(&[b"SCRIPT", b"LOAD", b"return 1"]),
2464 "-ERR unknown subcommand 'LOAD'. Try SCRIPT HELP.\r\n"
2465 );
2466 assert_eq!(
2467 f.run(&[b"FUNCTION", b"STATS"]),
2468 "-ERR unknown subcommand 'STATS'. Try FUNCTION HELP.\r\n"
2469 );
2470 }
2471
2472 #[test]
2473 fn a_counter_is_an_integer_and_not_a_string_of_digits() {
2474 let mut f = Fixture::new();
2475 assert_eq!(f.run(&[b"INCR", b"c"]), ":1\r\n");
2476 assert_eq!(f.run(&[b"INCRBY", b"c", b"41"]), ":42\r\n");
2477 assert_eq!(f.run(&[b"DECRBY", b"c", b"2"]), ":40\r\n");
2478 assert_eq!(f.run(&[b"GET", b"c"]), "$2\r\n40\r\n");
2481 assert_eq!(f.run(&[b"INCRBYFLOAT", b"c", b"0.5"]), "$4\r\n40.5\r\n");
2482 f.run(&[b"SET", b"k", b"hello"]);
2485 assert_eq!(
2486 f.run(&[b"INCR", b"k"]),
2487 "-ERR value is not an integer or out of range\r\n"
2488 );
2489 assert_eq!(
2490 f.run(&[b"INCRBYFLOAT", b"c", b"inf"]),
2491 "-ERR increment would produce NaN or Infinity\r\n"
2492 );
2493 }
2494
2495 #[test]
2500 fn the_newer_commands_reply_in_the_shapes_a_real_server_sends() {
2501 let mut f = Fixture::new();
2502 assert_eq!(f.run(&[b"SET", b"k", b"hello"]), "+OK\r\n");
2503 assert_eq!(f.run(&[b"DIGEST", b"k"]), "$16\r\n9555e8555c62dcfd\r\n");
2506 assert_eq!(f.run(&[b"DIGEST", b"nosuch"]), "$-1\r\n");
2507 assert_eq!(f.run(&[b"MSETEX", b"1", b"a", b"1"]), ":1\r\n");
2508 assert_eq!(f.run(&[b"MSETEX", b"1", b"a", b"2", b"NX"]), ":0\r\n");
2509 assert_eq!(f.run(&[b"GET", b"a"]), "$1\r\n1\r\n");
2510 assert_eq!(f.run(&[b"INCREX", b"n"]), "*2\r\n:1\r\n:1\r\n");
2511 assert_eq!(
2512 f.run(&[b"INCREX", b"n", b"BYINT", b"5", b"UBOUND", b"3"]),
2513 "*2\r\n:1\r\n:0\r\n",
2514 "a refused increment reports the value it left alone and applied nothing"
2515 );
2516 assert_eq!(
2517 f.run(&[
2518 b"INCREX",
2519 b"n",
2520 b"BYINT",
2521 b"5",
2522 b"UBOUND",
2523 b"3",
2524 b"SATURATE"
2525 ]),
2526 "*2\r\n:3\r\n:2\r\n"
2527 );
2528 assert_eq!(f.run(&[b"DELEX", b"a", b"IFEQ", b"2"]), ":0\r\n");
2529 assert_eq!(f.run(&[b"DELEX", b"a", b"IFEQ", b"1"]), ":1\r\n");
2530 }
2531
2532 #[test]
2533 fn the_same_answers_come_out_in_resp3_spelling() {
2534 let mut f = Fixture::new();
2535 assert!(f.run(&[b"HELLO", b"3"]).starts_with("%7\r\n"));
2536 assert_eq!(f.run(&[b"GET", b"nosuch"]), "_\r\n");
2537 assert_eq!(
2540 f.run(&[b"INCREX", b"c", b"BYFLOAT", b"1.5"]),
2541 "*2\r\n,1.5\r\n,1.5\r\n"
2542 );
2543 assert_eq!(f.run(&[b"INCRBYFLOAT", b"f", b"2.5"]), "$3\r\n2.5\r\n");
2544 assert_eq!(f.run(&[b"RESET"]), "+RESET\r\n");
2547 assert_eq!(f.run(&[b"GET", b"nosuch"]), "$-1\r\n");
2548 }
2549
2550 #[test]
2551 fn a_command_nobody_has_heard_of_is_an_error_and_not_a_closed_socket() {
2552 let mut f = Fixture::new();
2553 let (flow, reply) = f.flow(&[b"NOPE", b"a", b"b"]);
2554 assert_eq!(flow, Flow::Continue);
2555 assert_eq!(
2556 reply,
2557 "-ERR unknown command 'NOPE', with args beginning with: 'a' 'b' \r\n"
2558 );
2559 let reply = f.run(&[b"NO\r\n+PONG\r\nPE"]);
2562 assert_eq!(reply.matches("\r\n").count(), 1);
2563 }
2564
2565 #[test]
2566 fn arity_is_checked_before_the_command_is() {
2567 let mut f = Fixture::new();
2568 assert_eq!(
2569 f.run(&[b"GET"]),
2570 "-ERR wrong number of arguments for 'get' command\r\n"
2571 );
2572 assert_eq!(
2573 f.run(&[b"MSET", b"k"]),
2574 "-ERR wrong number of arguments for 'mset' command\r\n"
2575 );
2576 assert_eq!(
2580 f.run(&[b"PING", b"a", b"b"]),
2581 "-ERR wrong number of arguments for 'ping' command\r\n"
2582 );
2583 assert_eq!(f.run(&[b"PING"]), "+PONG\r\n");
2584 assert_eq!(f.run(&[b"PING", b"hi"]), "$2\r\nhi\r\n");
2585 assert_eq!(
2587 f.run(&[b"DELEX", b"k", b"IFEQ"]),
2588 "-ERR wrong number of arguments for 'delex' command\r\n"
2589 );
2590 }
2591
2592 #[test]
2596 fn the_option_combinations_are_the_ones_a_real_server_accepts() {
2597 let mut f = Fixture::new();
2598 let syntax = "-ERR syntax error\r\n";
2599 assert_eq!(f.run(&[b"SET", b"k", b"v", b"NX", b"XX"]), syntax);
2600 assert_eq!(f.run(&[b"SET", b"k", b"v", b"NX", b"IFEQ", b"a"]), syntax);
2601 assert_eq!(
2602 f.run(&[b"SET", b"k", b"v", b"KEEPTTL", b"EX", b"5"]),
2603 syntax
2604 );
2605 assert_eq!(
2606 f.run(&[b"SET", b"k", b"v", b"EX", b"5", b"PX", b"5"]),
2607 syntax
2608 );
2609 assert_eq!(f.run(&[b"SET", b"k", b"v", b"PERSIST"]), syntax);
2610 assert_eq!(
2612 f.run(&[b"SET", b"k", b"v", b"EX", b"5", b"EX", b"100"]),
2613 "+OK\r\n"
2614 );
2615 assert_eq!(f.run(&[b"SET", b"k", b"v", b"XX", b"XX"]), "+OK\r\n");
2616 assert_eq!(f.run(&[b"SET", b"k", b"v", b"GET", b"GET"]), "$1\r\nv\r\n");
2617 assert_eq!(
2619 f.run(&[b"INCREX", b"n", b"BYINT", b"1", b"BYINT", b"2"]),
2620 syntax
2621 );
2622 assert_eq!(
2623 f.run(&[b"INCREX", b"n", b"ENX"]),
2624 "-ERR ENX flag requires an expiration\r\n"
2625 );
2626 assert_eq!(
2627 f.run(&[b"INCREX", b"n", b"UBOUND", b"abc"]),
2628 "-ERR UBOUND is not an integer or out of range\r\n"
2629 );
2630 assert_eq!(
2631 f.run(&[b"INCREX", b"n", b"LBOUND", b"10", b"UBOUND", b"5"]),
2632 "-ERR LBOUND can't be greater than UBOUND\r\n"
2633 );
2634 assert_eq!(
2635 f.run(&[b"LCS", b"a", b"b", b"LEN", b"IDX"]),
2636 "-ERR If you want both the length and indexes, please just use IDX.\r\n"
2637 );
2638 }
2639
2640 #[test]
2644 fn the_expiry_rules_are_redis_own() {
2645 let mut f = Fixture::new();
2646 let bad = "-ERR invalid expire time in 'set' command\r\n";
2647 assert_eq!(f.run(&[b"SET", b"k", b"v", b"EX", b"0"]), bad);
2648 assert_eq!(f.run(&[b"SET", b"k", b"v", b"EX", b"-1"]), bad);
2649 assert_eq!(f.run(&[b"SET", b"k", b"v", b"EXAT", b"0"]), bad);
2650 assert_eq!(
2651 f.run(&[b"SET", b"k", b"v", b"EX", b"9999999999999999"]),
2652 bad
2653 );
2654 assert_eq!(
2655 f.run(&[b"SET", b"k", b"v", b"PX", b"99999999999999999999"]),
2656 "-ERR value is not an integer or out of range\r\n"
2657 );
2658 assert_eq!(
2659 f.run(&[b"SETEX", b"k", b"0", b"v"]),
2660 "-ERR invalid expire time in 'setex' command\r\n"
2661 );
2662 assert_eq!(f.run(&[b"GETEX", b"nosuch", b"EX", b"0"]), "$-1\r\n");
2663 assert_eq!(f.run(&[b"GETEX", b"nosuch", b"EX", b"abc"]), "$-1\r\n");
2664 assert_eq!(
2665 f.run(&[b"GETEX", b"nosuch", b"KEEPTTL"]),
2666 "-ERR syntax error\r\n",
2667 "the option list is still checked before the key is looked up"
2668 );
2669 assert_eq!(f.run(&[b"SET", b"k", b"v"]), "+OK\r\n");
2671 assert_eq!(f.run(&[b"SET", b"k", b"v", b"EXAT", b"1"]), "+OK\r\n");
2672 assert_eq!(f.run(&[b"GET", b"k"]), "$-1\r\n");
2673 }
2674
2675 #[test]
2676 fn mset_takes_its_pairs_from_the_read_buffer() {
2677 let mut f = Fixture::new();
2678 assert_eq!(f.run(&[b"MSET", b"a", b"1", b"b", b"2"]), "+OK\r\n");
2679 assert_eq!(
2680 f.run(&[b"MGET", b"a", b"b", b"nosuch"]),
2681 "*3\r\n$1\r\n1\r\n$1\r\n2\r\n$-1\r\n"
2682 );
2683 assert_eq!(f.run(&[b"MSETNX", b"b", b"9", b"c", b"3"]), ":0\r\n");
2684 assert_eq!(f.run(&[b"MSETNX", b"c", b"3", b"d", b"4"]), ":1\r\n");
2685 assert_eq!(
2686 f.run(&[b"MSETEX", b"2", b"e", b"5"]),
2687 "-ERR wrong number of key-value pairs\r\n"
2688 );
2689 assert_eq!(
2690 f.run(&[b"MSETEX", b"0", b"e", b"5"]),
2691 "-ERR invalid numkeys value\r\n"
2692 );
2693 assert_eq!(
2694 f.run(&[b"MSETEX", b"abc", b"e", b"5"]),
2695 "-ERR invalid numkeys value\r\n"
2696 );
2697 }
2698
2699 #[test]
2700 fn lcs_answers_the_length_the_string_and_the_runs() {
2701 let mut f = Fixture::new();
2702 f.run(&[b"MSET", b"a", b"ohmytext", b"b", b"mynewtext"]);
2703 assert_eq!(f.run(&[b"LCS", b"a", b"b"]), "$6\r\nmytext\r\n");
2704 assert_eq!(f.run(&[b"LCS", b"a", b"b", b"LEN"]), ":6\r\n");
2705 assert_eq!(
2706 f.run(&[b"LCS", b"a", b"b", b"IDX", b"MINMATCHLEN", b"4"]),
2707 "*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"
2708 );
2709 assert_eq!(
2712 f.run(&[b"LCS", b"a", b"b", b"MINMATCHLEN", b"4", b"WITHMATCHLEN"]),
2713 "$6\r\nmytext\r\n"
2714 );
2715 }
2716
2717 #[test]
2718 fn select_moves_the_connection_and_the_databases_stay_apart() {
2719 let mut f = Fixture::new();
2720 f.run(&[b"SET", b"k", b"zero"]);
2721 assert_eq!(f.run(&[b"SELECT", b"4"]), "+OK\r\n");
2722 assert_eq!(f.run(&[b"GET", b"k"]), "$-1\r\n");
2723 f.run(&[b"SET", b"k", b"four"]);
2724 assert_eq!(f.run(&[b"SELECT", b"0"]), "+OK\r\n");
2725 assert_eq!(f.run(&[b"GET", b"k"]), "$4\r\nzero\r\n");
2726 assert_eq!(
2727 f.run(&[b"SELECT", b"99"]),
2728 "-ERR DB index is out of range\r\n"
2729 );
2730 assert_eq!(
2731 f.run(&[b"SELECT", b"-1"]),
2732 "-ERR DB index is out of range\r\n"
2733 );
2734 assert_eq!(
2735 f.run(&[b"SELECT", b"abc"]),
2736 "-ERR value is not an integer or out of range\r\n"
2737 );
2738 f.run(&[b"SELECT", b"4"]);
2740 f.run(&[b"RESET"]);
2741 assert_eq!(f.run(&[b"GET", b"k"]), "$4\r\nzero\r\n");
2742 }
2743
2744 #[test]
2745 fn hello_agrees_on_a_protocol_and_refuses_the_ones_that_do_not_exist() {
2746 let mut f = Fixture::new();
2747 let reply = f.run(&[b"HELLO"]);
2748 assert!(reply.starts_with("*14\r\n"), "{reply}");
2749 assert!(reply.contains("$5\r\nredis\r\n"), "{reply}");
2750 assert!(reply.contains("$5\r\n8.8.0\r\n"), "{reply}");
2751 assert!(
2752 reply.contains(":7\r\n"),
2753 "the connection id is in there: {reply}"
2754 );
2755 assert_eq!(
2756 f.run(&[b"HELLO", b"4"]),
2757 "-NOPROTO unsupported protocol version\r\n"
2758 );
2759 assert_eq!(
2760 f.run(&[b"HELLO", b"abc"]),
2761 "-ERR Protocol version is not an integer or out of range\r\n"
2762 );
2763 assert_eq!(
2764 f.run(&[b"HELLO", b"3", b"SETNAME"]),
2765 "-ERR Syntax error in HELLO option 'SETNAME'\r\n"
2766 );
2767 assert!(
2768 f.run(&[b"HELLO", b"3", b"SETNAME", b"bob"])
2769 .starts_with("%7\r\n")
2770 );
2771 assert_eq!(f.session.name(), b"bob");
2772 f.run(&[b"RESET"]);
2773 assert_eq!(f.session.name(), b"");
2774 }
2775
2776 #[test]
2777 fn command_describes_this_server_in_the_shape_a_driver_reads() {
2778 let mut f = Fixture::new();
2779 let count = format!(":{}\r\n", COMMANDS.len());
2780 assert_eq!(f.run(&[b"COMMAND", b"COUNT"]), count);
2781 let info = f.run(&[b"COMMAND", b"INFO", b"get"]);
2782 assert_eq!(
2783 info,
2784 "*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\
2785 *3\r\n+@read\r\n+@string\r\n+@fast\r\n*0\r\n*0\r\n*0\r\n"
2786 );
2787 assert_eq!(f.run(&[b"COMMAND", b"INFO", b"nosuch"]), "*1\r\n$-1\r\n");
2789 assert_eq!(
2790 f.run(&[b"COMMAND", b"LIST", b"FILTERBY", b"PATTERN", b"getr*"]),
2791 "*1\r\n$8\r\ngetrange\r\n"
2792 );
2793 assert_eq!(
2794 f.run(&[b"COMMAND", b"NOPE"]),
2795 "-ERR unknown subcommand 'NOPE'. Try COMMAND HELP.\r\n"
2796 );
2797 }
2798
2799 #[test]
2803 fn command_getkeys_finds_the_keys_including_the_hidden_ones() {
2804 let mut f = Fixture::new();
2805 assert_eq!(
2806 f.run(&[b"COMMAND", b"GETKEYS", b"get", b"k"]),
2807 "*1\r\n$1\r\nk\r\n"
2808 );
2809 assert_eq!(
2810 f.run(&[b"COMMAND", b"GETKEYS", b"mset", b"a", b"1", b"b", b"2"]),
2811 "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
2812 );
2813 assert_eq!(
2814 f.run(&[
2815 b"COMMAND", b"GETKEYS", b"msetex", b"2", b"a", b"1", b"b", b"2"
2816 ]),
2817 "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
2818 );
2819 assert_eq!(
2820 f.run(&[b"COMMAND", b"GETKEYS", b"ping"]),
2821 "-ERR The command has no key arguments\r\n"
2822 );
2823 assert_eq!(
2824 f.run(&[b"COMMAND", b"GETKEYS", b"set"]),
2825 "-ERR Invalid number of arguments specified for command\r\n"
2826 );
2827 }
2828
2829 #[test]
2830 fn config_answers_what_it_can_and_refuses_what_it_cannot() {
2831 let mut f = Fixture::new();
2832 assert_eq!(
2833 f.run(&[b"CONFIG", b"GET", b"maxmemory"]),
2834 "*2\r\n$9\r\nmaxmemory\r\n$1\r\n0\r\n"
2835 );
2836 let both = f.run(&[b"CONFIG", b"GET", b"maxmemory*", b"maxmemory"]);
2839 assert!(both.starts_with("*6\r\n"), "{both}");
2840 assert_eq!(f.run(&[b"CONFIG", b"GET", b"nosuch"]), "*0\r\n");
2841 assert_eq!(f.run(&[b"CONFIG", b"SET", b"appendonly", b"no"]), "+OK\r\n");
2842 assert_eq!(
2843 f.run(&[b"CONFIG", b"SET", b"appendonly", b"yes"]),
2844 "-ERR CONFIG SET failed (possibly related to argument 'appendonly') - can't set immutable config\r\n"
2845 );
2846 assert_eq!(
2847 f.run(&[b"CONFIG", b"SET", b"nosuch", b"1"]),
2848 "-ERR Unknown option or number of arguments for CONFIG SET - 'nosuch'\r\n"
2849 );
2850 assert_eq!(
2851 f.run(&[b"CONFIG", b"GET"]),
2852 "-ERR wrong number of arguments for 'config|get' command\r\n"
2853 );
2854 assert_eq!(
2858 f.run(&[b"CONFIG", b"SET", b"appendonly"]),
2859 "-ERR wrong number of arguments for 'config|set' command\r\n"
2860 );
2861 assert_eq!(
2862 f.run(&[b"CONFIG", b"SET", b"appendonly", b"no", b"maxmemory"]),
2863 "-ERR syntax error\r\n"
2864 );
2865 assert_eq!(f.run(&[b"CONFIG", b"RESETSTAT"]), "+OK\r\n");
2866 assert_eq!(
2867 f.run(&[b"CONFIG", b"REWRITE"]),
2868 "-ERR The server is running without a config file\r\n"
2869 );
2870 }
2871
2872 #[test]
2873 fn the_eviction_policy_reads_back_what_was_written_to_it() {
2874 let mut f = Fixture::new();
2875 assert_eq!(
2876 f.run(&[b"CONFIG", b"GET", b"maxmemory-policy"]),
2877 "*2\r\n$16\r\nmaxmemory-policy\r\n$10\r\nnoeviction\r\n"
2878 );
2879 assert_eq!(
2880 f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"AllKeys-LFU"]),
2881 "+OK\r\n",
2882 "the name is matched without regard to case, like every other one"
2883 );
2884 assert_eq!(
2885 f.run(&[b"CONFIG", b"GET", b"maxmemory-policy"]),
2886 "*2\r\n$16\r\nmaxmemory-policy\r\n$11\r\nallkeys-lfu\r\n"
2887 );
2888 assert!(
2890 f.run(&[b"INFO", b"memory"])
2891 .contains("maxmemory_policy:allkeys-lfu"),
2892 "INFO and CONFIG disagree about the policy"
2893 );
2894 assert_eq!(
2898 f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"garbage"]),
2899 "-ERR CONFIG SET failed (possibly related to argument 'maxmemory-policy') - argument(s) must be one of the following: volatile-lru, volatile-lfu, volatile-random, volatile-ttl, volatile-lrm, allkeys-lru, allkeys-lfu, allkeys-random, allkeys-lrm, noeviction\r\n"
2900 );
2901 assert_eq!(
2904 f.run(&[b"CONFIG", b"GET", b"maxmemory-policy"]),
2905 "*2\r\n$16\r\nmaxmemory-policy\r\n$11\r\nallkeys-lfu\r\n"
2906 );
2907 f.run(&[
2908 b"CONFIG",
2909 b"SET",
2910 b"hash-max-listpack-entries",
2911 b"7",
2912 b"maxmemory-policy",
2913 b"nonsense",
2914 ]);
2915 assert_eq!(
2916 f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
2917 "*2\r\n$25\r\nhash-max-listpack-entries\r\n$3\r\n512\r\n"
2918 );
2919 }
2920
2921 #[test]
2922 fn the_three_eviction_numbers_read_back_too() {
2923 let mut f = Fixture::new();
2924 for (name, default, set) in [
2925 ("maxmemory-samples", "5", "12"),
2926 ("lfu-log-factor", "10", "3"),
2927 ("lfu-decay-time", "1", "60"),
2928 ] {
2929 let get = || {
2930 format!(
2931 "*2\r\n${}\r\n{name}\r\n${}\r\n{default}\r\n",
2932 name.len(),
2933 default.len()
2934 )
2935 };
2936 assert_eq!(f.run(&[b"CONFIG", b"GET", name.as_bytes()]), get());
2937 assert_eq!(
2938 f.run(&[b"CONFIG", b"SET", name.as_bytes(), set.as_bytes()]),
2939 "+OK\r\n"
2940 );
2941 assert_eq!(
2942 f.run(&[b"CONFIG", b"GET", name.as_bytes()]),
2943 format!(
2944 "*2\r\n${}\r\n{name}\r\n${}\r\n{set}\r\n",
2945 name.len(),
2946 set.len()
2947 )
2948 );
2949 assert_eq!(
2952 f.run(&[b"CONFIG", b"SET", name.as_bytes(), b"soon"]),
2953 format!(
2954 "-ERR CONFIG SET failed (possibly related to argument '{name}') - argument couldn't be parsed into an integer\r\n"
2955 )
2956 );
2957 }
2958 }
2959
2960 #[test]
2961 fn the_memory_limit_reads_back_in_bytes_whatever_the_unit_was() {
2962 let mut f = Fixture::new();
2963 assert_eq!(
2964 f.run(&[b"CONFIG", b"GET", b"maxmemory"]),
2965 "*2\r\n$9\r\nmaxmemory\r\n$1\r\n0\r\n",
2966 "no limit is the default"
2967 );
2968 for (typed, bytes) in [
2971 (&b"1024"[..], "1024"),
2972 (b"1k", "1000"),
2973 (b"1kb", "1024"),
2974 (b"1M", "1000000"),
2975 (b"1Mb", "1048576"),
2976 (b"1gb", "1073741824"),
2977 (b"100mb", "104857600"),
2978 ] {
2979 assert_eq!(f.run(&[b"CONFIG", b"SET", b"maxmemory", typed]), "+OK\r\n");
2980 assert_eq!(
2981 f.run(&[b"CONFIG", b"GET", b"maxmemory"]),
2982 format!("*2\r\n$9\r\nmaxmemory\r\n${}\r\n{bytes}\r\n", bytes.len()),
2983 "set {}",
2984 String::from_utf8_lossy(typed)
2985 );
2986 }
2987 assert!(
2988 f.run(&[b"INFO", b"memory"]).contains("maxmemory:104857600"),
2989 "the report agrees with the setting"
2990 );
2991
2992 for bad in [&b"1tb"[..], b"-1", b"", b"lots"] {
2995 assert_eq!(
2996 f.run(&[b"CONFIG", b"SET", b"maxmemory", bad]),
2997 "-ERR CONFIG SET failed (possibly related to argument 'maxmemory') - argument must be a memory value\r\n",
2998 "refused {}",
2999 String::from_utf8_lossy(bad)
3000 );
3001 }
3002 assert!(
3003 f.run(&[b"INFO", b"memory"]).contains("maxmemory:104857600"),
3004 "and the refusal left the old one alone"
3005 );
3006 }
3007
3008 #[test]
3009 fn a_write_is_refused_when_there_is_no_room_and_nothing_to_evict() {
3010 let mut f = Fixture::new();
3011 f.run(&[b"SET", b"here", b"already"]);
3012 f.run(&[b"CONFIG", b"SET", b"maxmemory", b"1"]);
3016 assert_eq!(
3017 f.run(&[b"SET", b"k", b"v"]),
3018 "-OOM command not allowed when used memory > 'maxmemory'.\r\n"
3019 );
3020 assert_eq!(
3021 f.run(&[b"LPUSH", b"l", b"v"]),
3022 "-OOM command not allowed when used memory > 'maxmemory'.\r\n"
3023 );
3024 assert_eq!(f.run(&[b"GET", b"here"]), "$7\r\nalready\r\n");
3026 assert_eq!(f.run(&[b"DEL", b"here"]), ":1\r\n");
3027 assert!(f.run(&[b"INFO", b"stats"]).contains("evicted_keys:0"));
3028
3029 f.run(&[b"CONFIG", b"SET", b"maxmemory", b"0"]);
3031 assert_eq!(f.run(&[b"SET", b"k", b"v"]), "+OK\r\n");
3032 }
3033
3034 #[test]
3035 fn an_allkeys_policy_makes_room_instead_of_refusing() {
3036 let mut f = Fixture::new();
3037 let val = vec![b'v'; 256];
3038 for i in 0..24000u32 {
3039 let k = format!("key:{i:08}");
3040 f.run(&[b"SET", k.as_bytes(), &val]);
3041 }
3042 let full = f.server.memory_bytes();
3043 assert!(
3044 full > 3 * 1024 * 1024,
3045 "the arena is several segments: {full}"
3046 );
3047
3048 let limit = full - 2 * 1024 * 1024;
3052 f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lru"]);
3053 f.run(&[
3054 b"CONFIG",
3055 b"SET",
3056 b"maxmemory",
3057 limit.to_string().as_bytes(),
3058 ]);
3059
3060 for i in 0..2000u32 {
3064 let k = format!("new:{i:08}");
3065 assert_eq!(
3066 f.run(&[b"SET", k.as_bytes(), &val]),
3067 "+OK\r\n",
3068 "write {i} was refused"
3069 );
3070 f.server.refresh_memory();
3071 if f.server.memory_bytes() <= limit {
3072 break;
3073 }
3074 }
3075 assert!(
3076 f.server.memory_bytes() <= limit,
3077 "it never got under: {} against {limit}",
3078 f.server.memory_bytes()
3079 );
3080 let info = f.run(&[b"INFO", b"stats"]);
3081 assert!(!info.contains("evicted_keys:0"), "{info}");
3082 assert!(
3083 f.run(&[b"DBSIZE"]) != ":0\r\n",
3084 "and it did not empty the database to get there"
3085 );
3086 }
3087
3088 #[test]
3089 fn the_running_total_and_the_walk_agree_on_a_mixed_keyspace() {
3090 let mut f = Fixture::new();
3097 f.run(&[b"CONFIG", b"SET", b"maxmemory", b"1gb"]);
3098 let big = vec![b'v'; 200];
3099
3100 for i in 0..400u32 {
3101 let n = i.to_string();
3102 let n = n.as_bytes();
3103 f.run(&[b"SADD", b"s", n]);
3104 f.run(&[b"SADD", b"s2", &big]);
3105 f.run(&[b"HSET", b"h", n, &big]);
3106 f.run(&[b"RPUSH", b"l", &big]);
3107 f.run(&[b"ZADD", b"z", n, n]);
3108 f.run(&[b"ARSET", b"a", n, &big]);
3109 if i % 7 == 0 {
3110 f.run(&[b"SREM", b"s", n]);
3111 f.run(&[b"HDEL", b"h", n]);
3112 f.run(&[b"LPOP", b"l"]);
3113 f.run(&[b"ZREM", b"z", n]);
3114 f.run(&[b"ARDEL", b"a", n]);
3115 }
3116 if i % 53 == 0 {
3117 f.run(&[b"DEL", b"s2"]);
3120 }
3121 assert_eq!(
3122 f.server.settled_memory(),
3123 f.server.memory_bytes(),
3124 "after round {i}"
3125 );
3126 }
3127
3128 assert_eq!(f.run(&[b"DBSIZE"]), ":6\r\n");
3131 assert!(
3132 f.server.memory_bytes() > 512 * 1024,
3133 "{}",
3134 f.server.memory_bytes()
3135 );
3136
3137 f.run(&[b"FLUSHALL"]);
3139 assert_eq!(f.server.settled_memory(), f.server.memory_bytes());
3140 }
3141
3142 #[test]
3143 fn taking_the_limit_away_stops_the_counting_and_putting_it_back_starts_again() {
3144 let mut f = Fixture::new();
3149 for i in 0..200u32 {
3150 let n = i.to_string();
3151 f.run(&[b"SADD", b"s", n.as_bytes()]);
3152 f.run(&[b"HSET", b"h", n.as_bytes(), b"value"]);
3153 }
3154 f.run(&[b"CONFIG", b"SET", b"maxmemory", b"1gb"]);
3155 assert_eq!(f.server.settled_memory(), f.server.memory_bytes());
3156
3157 f.run(&[b"CONFIG", b"SET", b"maxmemory", b"0"]);
3158 for i in 200..400u32 {
3159 let n = i.to_string();
3160 f.run(&[b"SADD", b"s", n.as_bytes()]);
3161 }
3162 f.run(&[b"CONFIG", b"SET", b"maxmemory", b"1gb"]);
3163 assert_eq!(
3164 f.server.settled_memory(),
3165 f.server.memory_bytes(),
3166 "the writes it was not watching are in the number it started from"
3167 );
3168 }
3169
3170 #[test]
3171 fn evicted_keys_and_expired_keys_are_different_numbers() {
3172 let mut f = Fixture::new();
3173 f.run(&[b"SET", b"gone", b"v", b"PX", b"1"]);
3176 f.server.db(0).clock_mut().advance(20);
3177 f.run(&[b"GET", b"gone"]);
3178 let info = f.run(&[b"INFO", b"stats"]);
3179 assert!(info.contains("expired_keys:1"), "{info}");
3180 assert!(info.contains("evicted_keys:0"), "{info}");
3181 }
3182
3183 #[test]
3184 fn the_object_subcommands_follow_the_policy() {
3185 let mut f = Fixture::new();
3186 f.run(&[b"SET", b"s", b"v"]);
3187 assert_eq!(f.run(&[b"OBJECT", b"IDLETIME", b"s"]), ":0\r\n");
3191 assert!(
3192 f.run(&[b"OBJECT", b"FREQ", b"s"])
3193 .starts_with("-ERR An LFU maxmemory policy is not selected"),
3194 );
3195
3196 f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lfu"]);
3197 assert!(
3198 f.run(&[b"OBJECT", b"IDLETIME", b"s"])
3199 .starts_with("-ERR An LFU maxmemory policy is selected"),
3200 );
3201 assert!(
3206 f.run(&[b"OBJECT", b"FREQ", b"s"]).starts_with(':'),
3207 "FREQ should answer under an LFU policy"
3208 );
3209 }
3210
3211 #[test]
3212 fn object_says_which_rung_of_the_ladder_a_key_is_on() {
3213 let mut f = Fixture::new();
3214 f.run(&[b"SET", b"s", b"hello"]);
3215 f.run(&[b"SET", b"n", b"123"]);
3216 f.run(&[b"SADD", b"si", b"1", b"2", b"3"]);
3217 f.run(&[b"SADD", b"ss", b"a", b"b"]);
3218 f.run(&[b"HSET", b"h", b"f", b"v"]);
3219 for (key, want) in [
3220 (b"s".as_slice(), "embstr"),
3221 (b"n", "int"),
3222 (b"si", "intset"),
3223 (b"ss", "listpack"),
3224 (b"h", "listpack"),
3225 ] {
3226 let reply = f.run(&[b"OBJECT", b"ENCODING", key]);
3227 assert_eq!(reply, format!("${}\r\n{want}\r\n", want.len()));
3228 }
3229
3230 f.run(&[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"f"]);
3233 assert_eq!(
3234 f.run(&[b"OBJECT", b"ENCODING", b"h"]),
3235 "$10\r\nlistpackex\r\n"
3236 );
3237
3238 assert_eq!(f.run(&[b"OBJECT", b"REFCOUNT", b"s"]), ":1\r\n");
3239 assert_eq!(f.run(&[b"OBJECT", b"IDLETIME", b"s"]), ":0\r\n");
3240 assert!(f.run(&[b"OBJECT", b"HELP"]).starts_with("*14\r\n+OBJECT "));
3241 }
3242
3243 #[test]
3244 fn object_answers_nil_for_a_key_that_is_not_there() {
3245 let mut f = Fixture::new();
3246 for sub in [b"ENCODING".as_slice(), b"REFCOUNT", b"IDLETIME", b"FREQ"] {
3247 assert_eq!(
3248 f.run(&[b"OBJECT", sub, b"nokey"]),
3249 "$-1\r\n",
3250 "a nil and not an error, which is what 8.10.1 does"
3251 );
3252 }
3253 f.run(&[b"SET", b"s", b"v"]);
3256 assert!(
3257 f.run(&[b"OBJECT", b"FREQ", b"s"])
3258 .starts_with("-ERR An LFU maxmemory policy is not"),
3259 );
3260 assert_eq!(
3261 f.run(&[b"OBJECT", b"NOPE", b"s"]),
3262 "-ERR unknown subcommand 'NOPE'. Try OBJECT HELP.\r\n"
3263 );
3264 assert_eq!(
3265 f.run(&[b"OBJECT", b"ENCODING"]),
3266 "-ERR wrong number of arguments for 'object|encoding' command\r\n"
3267 );
3268 assert_eq!(
3269 f.run(&[b"OBJECT", b"ENCODING", b"s", b"extra"]),
3270 "-ERR wrong number of arguments for 'object|encoding' command\r\n"
3271 );
3272 assert_eq!(
3273 f.run(&[b"OBJECT"]),
3274 "-ERR wrong number of arguments for 'object' command\r\n"
3275 );
3276 }
3277
3278 #[test]
3279 fn config_moves_the_ladder_and_object_encoding_agrees() {
3280 let mut f = Fixture::new();
3281 assert_eq!(
3282 f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
3283 "*2\r\n$25\r\nhash-max-listpack-entries\r\n$3\r\n512\r\n",
3284 "512 and not the 128 everyone remembers, which is what 8.10.1 says"
3285 );
3286 assert_eq!(
3289 f.run(&[b"CONFIG", b"GET", b"hash-max-ziplist-entries"]),
3290 "*2\r\n$24\r\nhash-max-ziplist-entries\r\n$3\r\n512\r\n"
3291 );
3292 assert!(
3293 f.run(&[b"CONFIG", b"GET", b"hash-max-*"])
3294 .starts_with("*8\r\n")
3295 );
3296 assert!(
3297 f.run(&[b"CONFIG", b"GET", b"set-max-*"])
3298 .starts_with("*6\r\n")
3299 );
3300
3301 f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2", b"c", b"3"]);
3302 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"h"]), "$8\r\nlistpack\r\n");
3303
3304 assert_eq!(
3305 f.run(&[b"CONFIG", b"SET", b"hash-max-ziplist-entries", b"2"]),
3306 "+OK\r\n",
3307 "written under the old name and read back under the new one"
3308 );
3309 assert_eq!(
3310 f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
3311 "*2\r\n$25\r\nhash-max-listpack-entries\r\n$1\r\n2\r\n"
3312 );
3313 assert_eq!(
3314 f.run(&[b"OBJECT", b"ENCODING", b"h"]),
3315 "$8\r\nlistpack\r\n",
3316 "the hash that already exists is left exactly where it was"
3317 );
3318 f.run(&[b"HSET", b"h2", b"a", b"1", b"b", b"2", b"c", b"3"]);
3319 assert_eq!(
3320 f.run(&[b"OBJECT", b"ENCODING", b"h2"]),
3321 "$9\r\nhashtable\r\n",
3322 "and the next one built goes straight to a table"
3323 );
3324
3325 f.run(&[b"CONFIG", b"SET", b"set-max-intset-entries", b"2"]);
3327 f.run(&[b"SADD", b"s", b"1", b"2", b"3"]);
3328 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"s"]), "$8\r\nlistpack\r\n");
3329 f.run(&[b"CONFIG", b"SET", b"set-max-listpack-value", b"2"]);
3330 f.run(&[b"SADD", b"s2", b"abcdefgh"]);
3331 assert_eq!(
3332 f.run(&[b"OBJECT", b"ENCODING", b"s2"]),
3333 "$9\r\nhashtable\r\n"
3334 );
3335 }
3336
3337 #[test]
3338 fn config_set_takes_all_of_the_ladder_or_none_of_it() {
3339 let mut f = Fixture::new();
3340 assert_eq!(
3341 f.run(&[
3342 b"CONFIG",
3343 b"SET",
3344 b"hash-max-listpack-entries",
3345 b"7",
3346 b"set-max-listpack-entries",
3347 b"abc"
3348 ]),
3349 "-ERR CONFIG SET failed (possibly related to argument 'set-max-listpack-entries') - argument couldn't be parsed into an integer\r\n"
3350 );
3351 assert_eq!(
3352 f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
3353 "*2\r\n$25\r\nhash-max-listpack-entries\r\n$3\r\n512\r\n",
3354 "the pair in front of the bad one did not go in"
3355 );
3356 assert_eq!(
3359 f.run(&[b"CONFIG", b"SET", b"hash-max-ziplist-entries", b"abc"]),
3360 "-ERR CONFIG SET failed (possibly related to argument 'hash-max-ziplist-entries') - argument couldn't be parsed into an integer\r\n"
3361 );
3362 assert_eq!(
3363 f.run(&[b"CONFIG", b"SET", b"set-max-intset-entries", b"-1"]),
3364 "-ERR CONFIG SET failed (possibly related to argument 'set-max-intset-entries') - argument must be between 0 and 9223372036854775807 inclusive\r\n"
3365 );
3366 assert_eq!(
3369 f.run(&[
3370 b"CONFIG",
3371 b"SET",
3372 b"set-max-intset-entries",
3373 b"99999999999999999999"
3374 ]),
3375 "-ERR CONFIG SET failed (possibly related to argument 'set-max-intset-entries') - argument couldn't be parsed into an integer\r\n"
3376 );
3377 assert_eq!(
3378 f.run(&[
3379 b"CONFIG",
3380 b"SET",
3381 b"set-max-intset-entries",
3382 b"9223372036854775807"
3383 ]),
3384 "+OK\r\n"
3385 );
3386 }
3387
3388 #[test]
3389 fn a_setting_moved_on_one_database_moved_on_all_of_them() {
3390 let mut f = Fixture::new();
3391 f.run(&[b"CONFIG", b"SET", b"hash-max-listpack-entries", b"1"]);
3392 f.run(&[b"SELECT", b"3"]);
3393 f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
3394 assert_eq!(
3395 f.run(&[b"OBJECT", b"ENCODING", b"h"]),
3396 "$9\r\nhashtable\r\n",
3397 "these are one server wide number in Redis, whatever a Keyspace carries"
3398 );
3399 }
3400
3401 #[test]
3402 fn info_reports_the_numbers_it_can_stand_behind() {
3403 let mut f = Fixture::new();
3404 f.run(&[b"MSET", b"a", b"1", b"b", b"2"]);
3405 let all = f.run(&[b"INFO"]);
3406 assert!(all.contains("redis_version:8.8.0"), "{all}");
3407 assert!(
3408 all.contains(concat!("yo_version:", env!("CARGO_PKG_VERSION"))),
3409 "{all}"
3410 );
3411 assert!(all.contains("db0:keys=2,expires=0,avg_ttl=0"), "{all}");
3412 assert!(all.contains("role:master"), "{all}");
3413 let clients = f.run(&[b"INFO", b"clients"]);
3415 assert!(clients.contains("connected_clients:0"), "{clients}");
3416 assert!(!clients.contains("redis_version"), "{clients}");
3417 assert_eq!(f.run(&[b"INFO", b"nosuch"]), "$0\r\n\r\n");
3418 }
3419
3420 #[test]
3427 fn commandstats_is_asked_for_and_replication_is_not() {
3428 let mut f = Fixture::new();
3429 for arg in ["", "all", "default", "everything"] {
3430 let info = if arg.is_empty() {
3431 f.run(&[b"INFO"])
3432 } else {
3433 f.run(&[b"INFO", arg.as_bytes()])
3434 };
3435 assert!(info.contains("redis_version"), "{arg}: {info}");
3436 assert!(info.contains("used_cpu_user"), "{arg}: {info}");
3437 assert!(info.contains("used_memory"), "{arg}: {info}");
3438 assert!(!info.contains("sentinel_tilt"), "{arg}: {info}");
3439 let asked = arg == "all" || arg == "everything";
3440 assert_eq!(
3441 info.contains("rejected_calls"),
3442 asked,
3443 "{arg} should{} carry the command counters: {info}",
3444 if asked { "" } else { " not" }
3445 );
3446 }
3447
3448 let cpu = f.run(&[b"INFO", b"cpu"]);
3449 assert!(cpu.contains("used_cpu_user"), "{cpu}");
3450 assert!(!cpu.contains("used_memory"), "{cpu}");
3451
3452 let stats = f.run(&[b"INFO", b"commandSTATS"]);
3455 assert!(!stats.contains("used_memory"), "{stats}");
3456 assert!(stats.contains("rejected_calls"), "{stats}");
3457
3458 let pair = f.run(&[b"INFO", b"cpu", b"sentinel"]);
3460 assert!(pair.contains("used_cpu_user"), "{pair}");
3461 assert!(!pair.contains("master_repl_offset"), "{pair}");
3462
3463 let with_all = f.run(&[b"INFO", b"cpu", b"all"]);
3464 assert!(with_all.contains("used_memory"), "{with_all}");
3465 assert!(with_all.contains("master_repl_offset"), "{with_all}");
3466 assert!(with_all.contains("rejected_calls"), "{with_all}");
3467 assert_eq!(
3469 with_all.matches("used_cpu_user_children").count(),
3470 1,
3471 "{with_all}"
3472 );
3473
3474 let with_default = f.run(&[b"INFO", b"cpu", b"default"]);
3475 assert!(with_default.contains("used_memory"), "{with_default}");
3476 assert!(
3477 with_default.contains("master_repl_offset"),
3478 "{with_default}"
3479 );
3480 assert!(!with_default.contains("rejected_calls"), "{with_default}");
3481 assert_eq!(
3482 with_default.matches("used_cpu_user_children").count(),
3483 1,
3484 "{with_default}"
3485 );
3486 }
3487
3488 #[test]
3497 fn info_memory_reports_the_cap_and_the_quarter_of_it_that_gets_used() {
3498 let mut f = Fixture::new();
3499 let info = f.run(&[b"INFO", b"memory"]);
3500 for field in [
3501 "total_system_memory:",
3502 "mem_cgroup_limit:",
3503 "mem_limit:",
3504 "mem_budget:",
3505 ] {
3506 assert!(info.contains(field), "no {field} in {info}");
3507 }
3508
3509 let field = |name: &str| -> u64 {
3510 info.lines()
3511 .find_map(|l| l.strip_prefix(name))
3512 .unwrap_or_else(|| panic!("no {name} in {info}"))
3513 .trim()
3514 .parse()
3515 .unwrap_or_else(|_| panic!("{name} is not a number in {info}"))
3516 };
3517 let limit = field("mem_limit:");
3518 assert_eq!(field("mem_budget:"), limit / 4, "{info}");
3519 if limit != 0 {
3522 let host = field("total_system_memory:");
3523 let cgroup = field("mem_cgroup_limit:");
3524 assert!(
3525 limit == host || limit == cgroup,
3526 "the limit came from neither number: {info}"
3527 );
3528 }
3529 }
3530
3531 #[test]
3540 fn a_command_counts_what_it_did_separately_from_what_it_refused() {
3541 let mut f = Fixture::new();
3542 f.run(&[b"SET", b"k", b"v"]);
3543 f.run(&[b"SET", b"k", b"w"]);
3544 f.run(&[b"LPUSH", b"k", b"x"]);
3546 f.run(&[b"LPUSH", b"k"]);
3548
3549 let stats = f.run(&[b"INFO", b"commandstats"]);
3550 assert!(
3551 stats.contains("cmdstat_set:calls=2,rejected_calls=0,failed_calls=0"),
3552 "{stats}"
3553 );
3554 assert!(
3555 stats.contains("cmdstat_lpush:calls=1,rejected_calls=1,failed_calls=1"),
3556 "{stats}"
3557 );
3558 assert!(
3559 !stats.contains("cmdstat_zadd"),
3560 "a command nobody has sent has no row: {stats}"
3561 );
3562 }
3563
3564 #[test]
3568 fn the_active_sweep_reclaims_keys_no_client_comes_back_for() {
3569 let mut f = Fixture::new();
3570 for i in 0..3_000u32 {
3571 f.run(&[b"SET", format!("d{i}").as_bytes(), b"v", b"PX", b"50"]);
3572 }
3573 for i in 0..1_000u32 {
3574 f.run(&[b"SET", format!("k{i}").as_bytes(), b"v"]);
3575 }
3576 assert_eq!(f.run(&[b"DBSIZE"]), ":4000\r\n");
3577 f.advance(100);
3578 assert_eq!(
3579 f.run(&[b"DBSIZE"]),
3580 ":4000\r\n",
3581 "DBSIZE counts records and nothing has read past the dead ones yet"
3582 );
3583
3584 let mut spent = 0;
3586 for _ in 0..2_000 {
3587 spent += f.server.expire_step(4096);
3588 if f.run(&[b"DBSIZE"]) == ":1000\r\n" {
3589 break;
3590 }
3591 }
3592 assert_eq!(f.run(&[b"DBSIZE"]), ":1000\r\n", "spent {spent} looks");
3593 assert!(f.run(&[b"INFO", b"stats"]).contains("expired_keys:3000"));
3594 for i in 0..1_000u32 {
3595 assert_eq!(
3596 f.run(&[b"GET", format!("k{i}").as_bytes()]),
3597 "$1\r\nv\r\n",
3598 "it took a key that had no deadline"
3599 );
3600 }
3601 }
3602
3603 #[test]
3604 fn a_sweep_of_a_server_with_no_deadlines_anywhere_costs_nothing() {
3605 let mut f = Fixture::new();
3606 for i in 0..2_000u32 {
3607 f.run(&[b"SET", format!("k{i}").as_bytes(), b"v"]);
3608 }
3609 assert_eq!(f.server.expire_step(4096), 0);
3610 f.run(&[b"SELECT", b"3"]);
3612 f.run(&[b"SET", b"x", b"v", b"PX", b"50"]);
3613 f.advance(100);
3614 for _ in 0..64 {
3615 f.server.expire_step(4096);
3616 }
3617 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
3618 f.run(&[b"SELECT", b"0"]);
3619 assert_eq!(f.run(&[b"DBSIZE"]), ":2000\r\n");
3620 assert_eq!(f.server.expire_step(4096), 0, "and it is quiet again");
3621 }
3622
3623 #[test]
3626 fn the_sweep_the_loop_calls_runs_at_most_once_a_millisecond() {
3627 let mut f = Fixture::new();
3628 for i in 0..500u32 {
3629 f.run(&[b"SET", format!("d{i}").as_bytes(), b"v", b"PX", b"50"]);
3630 }
3631 f.advance(100);
3632 let at = f.server.db(0).clock().now_ms();
3633 f.server.set_clock_ms(at);
3634 assert!(f.server.expire_slice(8) > 0, "the first one works");
3638 for _ in 0..1_000 {
3639 assert_eq!(
3640 f.server.expire_slice(8),
3641 0,
3642 "the millisecond has not moved and neither should this"
3643 );
3644 }
3645 assert!(
3646 f.server.db(0).expires() > 400,
3647 "there is plenty left to take"
3648 );
3649 f.server.set_clock_ms(at + 1);
3650 assert!(f.server.expire_slice(8) > 0, "and then it goes again");
3651 }
3652
3653 #[test]
3656 fn info_keyspace_counts_the_keys_that_have_a_deadline() {
3657 let mut f = Fixture::new();
3658 f.run(&[b"MSET", b"a", b"1", b"b", b"2", b"c", b"3"]);
3659 assert!(
3660 f.run(&[b"INFO", b"keyspace"])
3661 .contains("db0:keys=3,expires=0"),
3662 "none of them has one yet"
3663 );
3664 f.run(&[b"EXPIRE", b"a", b"1000"]);
3665 f.run(&[b"EXPIRE", b"b", b"1000"]);
3666 let two = f.run(&[b"INFO", b"keyspace"]);
3667 assert!(two.contains("db0:keys=3,expires=2"), "{two}");
3668 f.run(&[b"PERSIST", b"a"]);
3669 f.run(&[b"DEL", b"b"]);
3670 let none = f.run(&[b"INFO", b"keyspace"]);
3671 assert!(none.contains("db0:keys=2,expires=0"), "{none}");
3672
3673 f.run(&[b"SELECT", b"1"]);
3675 f.run(&[b"SET", b"x", b"1", b"EX", b"1000"]);
3676 let both = f.run(&[b"INFO", b"keyspace"]);
3677 assert!(both.contains("db0:keys=2,expires=0"), "{both}");
3678 assert!(both.contains("db1:keys=1,expires=1"), "{both}");
3679 }
3680
3681 #[cfg(unix)]
3682 #[test]
3683 fn info_cpu_reports_processor_time_that_was_really_measured() {
3684 let mut f = Fixture::new();
3685 let cpu = f.run(&[b"INFO", b"cpu"]);
3686 assert!(cpu.contains("# CPU"), "{cpu}");
3687 assert!(cpu.contains("used_cpu_user:"), "{cpu}");
3689 assert!(cpu.contains("used_cpu_sys:"), "{cpu}");
3690 assert!(cpu.contains("used_cpu_user_children:0.000000"), "{cpu}");
3691 assert!(!cpu.contains("redis_version"), "{cpu}");
3692
3693 let before = used_cpu_user(&cpu);
3697 let mut n = 0u64;
3698 let mut rounds = 0;
3699 while used_cpu_user(&f.run(&[b"INFO", b"cpu"])) <= before {
3700 for i in 0..1_000_000u64 {
3701 n = n.wrapping_add(i.wrapping_mul(i));
3702 }
3703 rounds += 1;
3704 assert!(rounds < 1_000, "cpu time never moved, n is {n}");
3708 }
3709 }
3710
3711 #[cfg(unix)]
3713 fn used_cpu_user(info: &str) -> f64 {
3714 info.lines()
3715 .find_map(|l| l.strip_prefix("used_cpu_user:"))
3716 .expect("no used_cpu_user in the reply")
3717 .trim()
3718 .parse()
3719 .expect("used_cpu_user is not a number")
3720 }
3721
3722 #[test]
3728 fn a_command_that_fails_leaves_nothing_half_written() {
3729 let mut f = Fixture::new();
3730 let reply = f.run(&[b"SETRANGE", b"k", b"-1", b"x"]);
3731 assert_eq!(reply, "-ERR offset is out of range\r\n");
3732 assert!(!reply.contains(':'), "no integer went out in front of it");
3733 }
3734
3735 #[test]
3736 fn quit_answers_first_and_closes_after() {
3737 let mut f = Fixture::new();
3738 let (flow, reply) = f.flow(&[b"QUIT"]);
3739 assert_eq!(reply, "+OK\r\n");
3740 assert_eq!(flow, Flow::Close);
3741 }
3742
3743 #[test]
3744 fn the_command_counter_counts_every_command_including_the_bad_ones() {
3745 let mut f = Fixture::new();
3746 f.run(&[b"PING"]);
3747 f.run(&[b"NOPE"]);
3748 f.run(&[b"GET"]);
3749 assert_eq!(f.server.stats.commands, 3);
3750 }
3751
3752 #[test]
3753 fn a_set_goes_from_bytes_to_bytes() {
3754 let mut f = Fixture::new();
3755 assert_eq!(f.run(&[b"SADD", b"s", b"a", b"b", b"c"]), ":3\r\n");
3756 assert_eq!(f.run(&[b"SADD", b"s", b"b", b"d"]), ":1\r\n");
3757 assert_eq!(f.run(&[b"SCARD", b"s"]), ":4\r\n");
3758 assert_eq!(f.run(&[b"SISMEMBER", b"s", b"a"]), ":1\r\n");
3759 assert_eq!(f.run(&[b"SISMEMBER", b"s", b"z"]), ":0\r\n");
3760 assert_eq!(f.run(&[b"TYPE", b"s"]), "+set\r\n");
3761 assert_eq!(
3762 f.run(&[b"SMISMEMBER", b"s", b"a", b"z", b"d"]),
3763 "*3\r\n:1\r\n:0\r\n:1\r\n"
3764 );
3765 assert_eq!(f.run(&[b"SREM", b"s", b"a", b"z"]), ":1\r\n");
3766 assert_eq!(f.run(&[b"SCARD", b"s"]), ":3\r\n");
3767 }
3768
3769 #[test]
3770 fn a_set_command_at_a_key_that_is_not_there_answers_empty() {
3771 let mut f = Fixture::new();
3772 assert_eq!(f.run(&[b"SCARD", b"nope"]), ":0\r\n");
3773 assert_eq!(f.run(&[b"SISMEMBER", b"nope", b"a"]), ":0\r\n");
3774 assert_eq!(f.run(&[b"SREM", b"nope", b"a"]), ":0\r\n");
3775 assert_eq!(f.run(&[b"SMEMBERS", b"nope"]), "*0\r\n");
3776 assert_eq!(
3777 f.run(&[b"SMISMEMBER", b"nope", b"a", b"b"]),
3778 "*2\r\n:0\r\n:0\r\n"
3779 );
3780 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n", "and made nothing");
3781 }
3782
3783 #[test]
3784 fn smembers_answers_a_set_on_resp3_and_an_array_on_resp2() {
3785 let mut f = Fixture::new();
3789 f.run(&[b"SADD", b"s", b"one"]);
3790 assert_eq!(f.run(&[b"SMEMBERS", b"s"]), "*1\r\n$3\r\none\r\n");
3791
3792 f.run(&[b"HELLO", b"3"]);
3793 assert_eq!(f.run(&[b"SMEMBERS", b"s"]), "~1\r\n$3\r\none\r\n");
3794 }
3795
3796 #[test]
3797 fn an_integer_member_comes_back_as_the_digits_it_never_stored() {
3798 let mut f = Fixture::new();
3801 f.run(&[b"SADD", b"s", b"42"]);
3802 assert_eq!(f.run(&[b"SMEMBERS", b"s"]), "*1\r\n$2\r\n42\r\n");
3803 assert_eq!(f.run(&[b"SISMEMBER", b"s", b"42"]), ":1\r\n");
3804 assert_eq!(
3805 f.run(&[b"SISMEMBER", b"s", b"042"]),
3806 ":0\r\n",
3807 "the member is the bytes and not the number they parse to"
3808 );
3809 }
3810
3811 #[test]
3812 fn the_wrong_command_at_the_wrong_type_says_so_both_ways() {
3813 let mut f = Fixture::new();
3814 f.run(&[b"SET", b"str", b"v"]);
3815 f.run(&[b"SADD", b"set", b"a"]);
3816
3817 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
3818 assert_eq!(f.run(&[b"SADD", b"str", b"a"]), wrong);
3819 assert_eq!(f.run(&[b"SCARD", b"str"]), wrong);
3820 assert_eq!(f.run(&[b"SMEMBERS", b"str"]), wrong);
3821 assert_eq!(f.run(&[b"SMISMEMBER", b"str", b"a"]), wrong);
3822 assert_eq!(f.run(&[b"GET", b"set"]), wrong);
3823 assert_eq!(f.run(&[b"APPEND", b"set", b"x"]), wrong);
3824 assert_eq!(f.run(&[b"INCR", b"set"]), wrong);
3825 assert_eq!(f.run(&[b"STRLEN", b"set"]), wrong);
3826
3827 assert_eq!(
3830 f.run(&[b"MGET", b"str", b"set", b"nope"]),
3831 "*3\r\n$1\r\nv\r\n$-1\r\n$-1\r\n"
3832 );
3833 assert_eq!(f.run(&[b"SET", b"set", b"now a string"]), "+OK\r\n");
3835 assert_eq!(f.run(&[b"TYPE", b"set"]), "+string\r\n");
3836 }
3837
3838 #[test]
3839 fn a_wrongtype_leaves_nothing_half_written() {
3840 let mut f = Fixture::new();
3844 f.run(&[b"SET", b"k", b"v"]);
3845 let reply = f.run(&[b"SMISMEMBER", b"k", b"a", b"b"]);
3846 assert!(reply.starts_with("-WRONGTYPE"), "got {reply}");
3847 assert!(!reply.contains('*'), "an array header went out in front");
3848 }
3849
3850 #[test]
3851 fn emptying_a_set_takes_the_key_with_it() {
3852 let mut f = Fixture::new();
3853 f.run(&[b"SADD", b"s", b"a", b"b"]);
3854 assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
3855 assert_eq!(f.run(&[b"SREM", b"s", b"a", b"b"]), ":2\r\n");
3856 assert_eq!(f.run(&[b"EXISTS", b"s"]), ":0\r\n");
3857 assert_eq!(f.run(&[b"TYPE", b"s"]), "+none\r\n");
3858 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
3859 }
3860
3861 fn split_scan(reply: &str) -> (String, Vec<String>) {
3867 let mut lines = reply.split("\r\n");
3868 assert_eq!(lines.next(), Some("*2"), "got {reply}");
3869 lines.next().expect("the cursor header");
3870 let cursor = lines.next().expect("the cursor").to_owned();
3871 let header = lines.next().expect("the member header");
3872 let n: usize = header[1..].parse().expect("a member count");
3873 let mut members = Vec::with_capacity(n);
3874 for _ in 0..n {
3875 lines.next().expect("a member header");
3876 members.push(lines.next().expect("a member").to_owned());
3877 }
3878 (cursor, members)
3879 }
3880
3881 #[test]
3882 fn popping_takes_a_member_off_the_set_and_hands_it_back() {
3883 let mut f = Fixture::new();
3884 f.run(&[b"SADD", b"s", b"a", b"b", b"c", b"d"]);
3885
3886 let one = f.run(&[b"SPOP", b"s"]);
3887 assert!(
3888 ["$1\r\na\r\n", "$1\r\nb\r\n", "$1\r\nc\r\n", "$1\r\nd\r\n"].contains(&one.as_str()),
3889 "got {one}"
3890 );
3891 assert_eq!(f.run(&[b"SCARD", b"s"]), ":3\r\n");
3892
3893 let (_, rest) = ("", f.run(&[b"SPOP", b"s", b"3"]));
3895 assert!(rest.starts_with("*3\r\n"), "got {rest}");
3896 assert_eq!(f.run(&[b"EXISTS", b"s"]), ":0\r\n");
3897 assert_eq!(f.run(&[b"SPOP", b"s"]), "$-1\r\n");
3899 assert_eq!(f.run(&[b"SPOP", b"s", b"2"]), "*0\r\n");
3900 }
3901
3902 #[test]
3903 fn the_two_draws_disagree_about_the_reply_type_and_they_are_right_to() {
3904 let mut f = Fixture::new();
3909 f.run(&[b"HELLO", b"3"]);
3910 f.run(&[b"SADD", b"s", b"a", b"b", b"c"]);
3911
3912 assert!(f.run(&[b"SPOP", b"s", b"2"]).starts_with("~2\r\n"));
3913 assert!(f.run(&[b"SRANDMEMBER", b"s", b"1"]).starts_with("*1\r\n"));
3915
3916 f.run(&[b"SADD", b"one", b"z"]);
3920 assert_eq!(
3921 f.run(&[b"SRANDMEMBER", b"one", b"-3"]),
3922 "*3\r\n$1\r\nz\r\n$1\r\nz\r\n$1\r\nz\r\n"
3923 );
3924 }
3925
3926 #[test]
3927 fn drawing_a_member_removes_nothing_and_says_nil_at_a_missing_key() {
3928 let mut f = Fixture::new();
3929 f.run(&[b"SADD", b"s", b"only"]);
3930 assert_eq!(f.run(&[b"SRANDMEMBER", b"s"]), "$4\r\nonly\r\n");
3931 assert_eq!(f.run(&[b"SRANDMEMBER", b"s"]), "$4\r\nonly\r\n");
3932 assert_eq!(f.run(&[b"SCARD", b"s"]), ":1\r\n");
3933
3934 assert_eq!(f.run(&[b"SRANDMEMBER", b"nope"]), "$-1\r\n");
3935 assert_eq!(f.run(&[b"SRANDMEMBER", b"nope", b"3"]), "*0\r\n");
3938 assert_eq!(f.run(&[b"SRANDMEMBER", b"nope", b"-3"]), "*0\r\n");
3939 assert_eq!(f.run(&[b"SRANDMEMBER", b"s", b"9"]), "*1\r\n$4\r\nonly\r\n");
3941 }
3942
3943 #[test]
3944 fn a_pop_count_that_is_not_a_positive_number_says_so() {
3945 let mut f = Fixture::new();
3946 f.run(&[b"SADD", b"s", b"a"]);
3947 let bad = "-ERR value is out of range, must be positive\r\n";
3948 assert_eq!(f.run(&[b"SPOP", b"s", b"-1"]), bad);
3949 assert_eq!(f.run(&[b"SPOP", b"s", b"abc"]), bad);
3950 assert_eq!(f.run(&[b"SCARD", b"s"]), ":1\r\n", "and took nothing");
3951 assert_eq!(f.run(&[b"SPOP", b"s", b"0"]), "*0\r\n");
3953 assert_eq!(f.run(&[b"EXISTS", b"s"]), ":1\r\n");
3954 }
3955
3956 #[test]
3957 fn a_scan_walks_a_set_of_any_size_exactly_once() {
3958 let mut f = Fixture::new();
3959 let members: Vec<Vec<u8>> = (0..300).map(|i| format!("m{i}").into_bytes()).collect();
3960 let args: Vec<&[u8]> = [&b"SADD"[..], &b"s"[..]]
3961 .into_iter()
3962 .chain(members.iter().map(Vec::as_slice))
3963 .collect();
3964 f.run(&args);
3965
3966 let mut seen = Vec::new();
3967 let mut cursor = "0".to_owned();
3968 loop {
3969 let reply = f.run(&[b"SSCAN", b"s", cursor.as_bytes()]);
3970 let (next, got) = split_scan(&reply);
3971 seen.extend(got);
3972 cursor = next;
3973 if cursor == "0" {
3974 break;
3975 }
3976 }
3977 seen.sort();
3978 seen.dedup();
3979 assert_eq!(seen.len(), 300, "a walk saw a member twice or missed one");
3980
3981 f.run(&[b"SADD", b"small", b"a", b"b", b"c"]);
3984 let (cursor, got) = split_scan(&f.run(&[b"SSCAN", b"small", b"0", b"COUNT", b"1"]));
3985 assert_eq!(cursor, "0");
3986 assert_eq!(got.len(), 3);
3987 assert_eq!(f.run(&[b"SSCAN", b"nope", b"0"]), "*2\r\n$1\r\n0\r\n*0\r\n");
3989 }
3990
3991 #[test]
3992 fn a_scan_takes_match_and_count_and_refuses_anything_else() {
3993 let mut f = Fixture::new();
3994 f.run(&[b"SADD", b"s", b"aa", b"ab", b"ba", b"12", b"13"]);
3995
3996 let (_, got) = split_scan(&f.run(&[b"SSCAN", b"s", b"0", b"MATCH", b"a*"]));
3997 let mut got = got;
3998 got.sort();
3999 assert_eq!(got, ["aa", "ab"]);
4000
4001 let (_, got) = split_scan(&f.run(&[b"SSCAN", b"s", b"0", b"MATCH", b"1?"]));
4004 let mut got = got;
4005 got.sort();
4006 assert_eq!(got, ["12", "13"]);
4007
4008 assert_eq!(f.run(&[b"SSCAN", b"s", b"abc"]), "-ERR invalid cursor\r\n");
4009 assert_eq!(f.run(&[b"SSCAN", b"s", b"-1"]), "-ERR invalid cursor\r\n");
4010 assert_eq!(
4011 f.run(&[b"SSCAN", b"s", b"0", b"NOPE", b"1"]),
4012 "-ERR syntax error\r\n"
4013 );
4014 assert_eq!(
4017 f.run(&[b"SSCAN", b"s", b"0", b"COUNT", b"0"]),
4018 "-ERR syntax error\r\n"
4019 );
4020 }
4021
4022 #[test]
4023 fn moving_a_member_takes_it_off_one_set_and_puts_it_on_another() {
4024 let mut f = Fixture::new();
4025 f.run(&[b"SADD", b"src", b"a", b"b"]);
4026 f.run(&[b"SADD", b"dst", b"c"]);
4027
4028 assert_eq!(f.run(&[b"SMOVE", b"src", b"dst", b"a"]), ":1\r\n");
4029 assert_eq!(f.run(&[b"SISMEMBER", b"src", b"a"]), ":0\r\n");
4030 assert_eq!(f.run(&[b"SISMEMBER", b"dst", b"a"]), ":1\r\n");
4031 assert_eq!(f.run(&[b"SMOVE", b"src", b"dst", b"zz"]), ":0\r\n");
4033 assert_eq!(f.run(&[b"SCARD", b"dst"]), ":2\r\n");
4034
4035 assert_eq!(f.run(&[b"SMOVE", b"src", b"fresh", b"b"]), ":1\r\n");
4038 assert_eq!(f.run(&[b"EXISTS", b"src"]), ":0\r\n");
4039 assert_eq!(f.run(&[b"SMEMBERS", b"fresh"]), "*1\r\n$1\r\nb\r\n");
4040 }
4041
4042 #[test]
4043 fn moving_checks_the_types_in_the_order_redis_checks_them() {
4044 let mut f = Fixture::new();
4048 f.run(&[b"SET", b"str", b"v"]);
4049 f.run(&[b"SADD", b"set", b"a"]);
4050
4051 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
4052 assert_eq!(f.run(&[b"SMOVE", b"nope", b"str", b"a"]), ":0\r\n");
4053 assert_eq!(f.run(&[b"SMOVE", b"str", b"set", b"a"]), wrong);
4054 assert_eq!(f.run(&[b"SMOVE", b"set", b"str", b"a"]), wrong);
4055 assert_eq!(f.run(&[b"SPOP", b"str"]), wrong);
4056 assert_eq!(f.run(&[b"SRANDMEMBER", b"str"]), wrong);
4057 assert_eq!(f.run(&[b"SSCAN", b"str", b"0"]), wrong);
4058 assert_eq!(
4059 f.run(&[b"SISMEMBER", b"set", b"a"]),
4060 ":1\r\n",
4061 "and none of that moved anything"
4062 );
4063 }
4064
4065 #[test]
4066 fn a_scan_leaves_nothing_half_written_when_its_arguments_are_wrong() {
4067 let mut f = Fixture::new();
4070 f.run(&[b"SADD", b"s", b"a"]);
4071 for bad in [
4072 &[b"SSCAN".as_slice(), b"s", b"abc"][..],
4073 &[b"SSCAN".as_slice(), b"s", b"0", b"COUNT", b"nope"][..],
4074 &[b"SSCAN".as_slice(), b"s", b"0", b"MATCH"][..],
4075 ] {
4076 let reply = f.run(bad);
4077 assert!(reply.starts_with("-ERR"), "got {reply}");
4078 assert!(!reply.contains('*'), "an array header went out in front");
4079 }
4080 }
4081
4082 #[test]
4083 fn a_hash_writes_reads_and_deletes_its_fields() {
4084 let mut f = Fixture::new();
4085 assert_eq!(f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]), ":2\r\n");
4086 assert_eq!(f.run(&[b"HSET", b"h", b"a", b"9"]), ":0\r\n", "a was there");
4087 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n9\r\n");
4088 assert_eq!(f.run(&[b"HGET", b"h", b"nope"]), "$-1\r\n");
4089 assert_eq!(f.run(&[b"HGET", b"nokey", b"a"]), "$-1\r\n");
4090 assert_eq!(f.run(&[b"HLEN", b"h"]), ":2\r\n");
4091 assert_eq!(f.run(&[b"HEXISTS", b"h", b"a"]), ":1\r\n");
4092 assert_eq!(f.run(&[b"HEXISTS", b"h", b"nope"]), ":0\r\n");
4093 assert_eq!(f.run(&[b"HSTRLEN", b"h", b"a"]), ":1\r\n");
4094 assert_eq!(f.run(&[b"HSTRLEN", b"h", b"nope"]), ":0\r\n");
4095
4096 assert_eq!(f.run(&[b"HGET", b"h", b"2"]), "$-1\r\n");
4099
4100 assert_eq!(f.run(&[b"HDEL", b"h", b"a", b"nope"]), ":1\r\n");
4101 assert_eq!(f.run(&[b"HDEL", b"h", b"b"]), ":1\r\n");
4102 assert_eq!(
4103 f.run(&[b"EXISTS", b"h"]),
4104 ":0\r\n",
4105 "and losing the last field lost the key"
4106 );
4107 }
4108
4109 #[test]
4110 fn hgetall_answers_a_map_on_resp3_and_the_same_pairs_flat_on_resp2() {
4111 let mut f = Fixture::new();
4112 f.run(&[b"HSET", b"h", b"a", b"1"]);
4113 assert_eq!(f.run(&[b"HGETALL", b"h"]), "*2\r\n$1\r\na\r\n$1\r\n1\r\n");
4114 assert_eq!(f.run(&[b"HGETALL", b"nokey"]), "*0\r\n");
4115 assert_eq!(f.run(&[b"HKEYS", b"h"]), "*1\r\n$1\r\na\r\n");
4116 assert_eq!(f.run(&[b"HVALS", b"h"]), "*1\r\n$1\r\n1\r\n");
4117 assert_eq!(f.run(&[b"HKEYS", b"nokey"]), "*0\r\n");
4118
4119 f.run(&[b"HELLO", b"3"]);
4120 assert_eq!(f.run(&[b"HGETALL", b"h"]), "%1\r\n$1\r\na\r\n$1\r\n1\r\n");
4121 assert_eq!(
4122 f.run(&[b"HGETALL", b"nokey"]),
4123 "%0\r\n",
4124 "a missing key is the empty hash and never a nil"
4125 );
4126 assert_eq!(
4127 f.run(&[b"HKEYS", b"h"]),
4128 "*1\r\n$1\r\na\r\n",
4129 "and the two that answer one side stay arrays"
4130 );
4131 }
4132
4133 #[test]
4134 fn hmget_answers_once_per_field_and_hmset_answers_ok() {
4135 let mut f = Fixture::new();
4136 assert_eq!(f.run(&[b"HMSET", b"h", b"a", b"1", b"c", b"3"]), "+OK\r\n");
4137 assert_eq!(
4138 f.run(&[b"HMGET", b"h", b"a", b"b", b"c"]),
4139 "*3\r\n$1\r\n1\r\n$-1\r\n$1\r\n3\r\n",
4140 "the reply is positional, so b is a nil and not a gap"
4141 );
4142 assert_eq!(
4143 f.run(&[b"HMGET", b"nokey", b"a", b"b"]),
4144 "*2\r\n$-1\r\n$-1\r\n",
4145 "and a missing key is all nils rather than an empty array"
4146 );
4147
4148 assert_eq!(f.run(&[b"HSETNX", b"h", b"a", b"9"]), ":0\r\n");
4149 assert_eq!(f.run(&[b"HSETNX", b"h", b"z", b"9"]), ":1\r\n");
4150 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
4151 }
4152
4153 #[test]
4154 fn a_hash_counts_up_and_says_so_when_it_cannot() {
4155 let mut f = Fixture::new();
4156 assert_eq!(f.run(&[b"HINCRBY", b"h", b"n", b"5"]), ":5\r\n");
4157 assert_eq!(f.run(&[b"HINCRBY", b"h", b"n", b"-7"]), ":-2\r\n");
4158 assert_eq!(f.run(&[b"HGET", b"h", b"n"]), "$2\r\n-2\r\n");
4159 assert_eq!(
4160 f.run(&[b"HINCRBYFLOAT", b"h", b"f", b"10.5"]),
4161 "$4\r\n10.5\r\n",
4162 "a bulk string and not a double, on both protocols"
4163 );
4164
4165 f.run(&[b"HSET", b"h", b"s", b"words"]);
4166 let bad = f.run(&[b"HINCRBY", b"h", b"s", b"1"]);
4167 assert!(
4168 bad.starts_with("-ERR hash value is not an integer"),
4169 "{bad}"
4170 );
4171 let bad = f.run(&[b"HINCRBY", b"h", b"n", b"nope"]);
4172 assert!(
4173 bad.starts_with("-ERR value is not an integer"),
4174 "a bad argument is not yet a hash value, {bad}"
4175 );
4176 assert_eq!(
4177 f.run(&[b"HGET", b"h", b"s"]),
4178 "$5\r\nwords\r\n",
4179 "and neither of them wrote anything"
4180 );
4181 }
4182
4183 #[test]
4184 fn a_hash_scan_walks_every_pair_once_and_novalues_drops_half_of_it() {
4185 let mut f = Fixture::new();
4186 for i in 0..500 {
4187 let field = format!("field-{i}");
4188 let value = format!("value-{i}");
4189 f.run(&[b"HSET", b"h", field.as_bytes(), value.as_bytes()]);
4190 }
4191
4192 let mut seen: Vec<String> = Vec::new();
4193 let mut cursor = "0".to_owned();
4194 loop {
4195 let reply = f.run(&[b"HSCAN", b"h", cursor.as_bytes(), b"COUNT", b"32"]);
4196 let (next, items) = scan_reply(&reply);
4197 assert_eq!(items.len() % 2, 0, "a pair went out half written");
4198 for pair in items.chunks(2) {
4199 assert_eq!(
4200 pair[0].strip_prefix("field-"),
4201 pair[1].strip_prefix("value-"),
4202 "a field came back with someone else's value"
4203 );
4204 seen.push(pair[0].clone());
4205 }
4206 cursor = next;
4207 if cursor == "0" {
4208 break;
4209 }
4210 }
4211 seen.sort();
4212 seen.dedup();
4213 assert_eq!(seen.len(), 500, "every field once and only once");
4214
4215 let (_, items) = scan_reply(&f.run(&[b"HSCAN", b"h", b"0", b"NOVALUES", b"COUNT", b"32"]));
4216 assert!(
4217 items.iter().all(|s| s.starts_with("field-")),
4218 "NOVALUES still sent the values"
4219 );
4220
4221 let (_, one) = scan_reply(&f.run(&[
4222 b"HSCAN",
4223 b"h",
4224 b"0",
4225 b"MATCH",
4226 b"field-499",
4227 b"COUNT",
4228 b"1000",
4229 ]));
4230 assert_eq!(one, ["field-499", "value-499"], "MATCH is on the field");
4231 }
4232
4233 #[test]
4234 fn hrandfield_draws_what_it_was_asked_for_and_nests_values_on_resp3() {
4235 let mut f = Fixture::new();
4236 f.run(&[b"HSET", b"h", b"a", b"1"]);
4237 assert_eq!(f.run(&[b"HRANDFIELD", b"h"]), "$1\r\na\r\n");
4238 assert_eq!(f.run(&[b"HRANDFIELD", b"nokey"]), "$-1\r\n");
4239 assert_eq!(f.run(&[b"HRANDFIELD", b"nokey", b"3"]), "*0\r\n");
4240 assert_eq!(
4241 f.run(&[b"HRANDFIELD", b"h", b"3"]),
4242 "*1\r\n$1\r\na\r\n",
4243 "a positive count is capped at the size of the hash"
4244 );
4245 assert_eq!(
4246 f.run(&[b"HRANDFIELD", b"h", b"-3"]),
4247 "*3\r\n$1\r\na\r\n$1\r\na\r\n$1\r\na\r\n",
4248 "and a negative one repeats itself"
4249 );
4250 assert_eq!(
4251 f.run(&[b"HRANDFIELD", b"h", b"1", b"WITHVALUES"]),
4252 "*2\r\n$1\r\na\r\n$1\r\n1\r\n",
4253 "flat on RESP2"
4254 );
4255
4256 f.run(&[b"HELLO", b"3"]);
4257 assert_eq!(
4258 f.run(&[b"HRANDFIELD", b"h", b"1", b"WITHVALUES"]),
4259 "*1\r\n*2\r\n$1\r\na\r\n$1\r\n1\r\n",
4260 "and nested on RESP3, but still an array and never a map"
4261 );
4262 }
4263
4264 #[test]
4265 fn every_hash_command_says_wrongtype_and_writes_nothing() {
4266 let mut f = Fixture::new();
4267 f.run(&[b"SET", b"str", b"v"]);
4268 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
4269
4270 for cmd in [
4271 &[b"HSET".as_slice(), b"str", b"f", b"v"][..],
4272 &[b"HMSET".as_slice(), b"str", b"f", b"v"][..],
4273 &[b"HSETNX".as_slice(), b"str", b"f", b"v"][..],
4274 &[b"HGET".as_slice(), b"str", b"f"][..],
4275 &[b"HMGET".as_slice(), b"str", b"f"][..],
4276 &[b"HDEL".as_slice(), b"str", b"f"][..],
4277 &[b"HLEN".as_slice(), b"str"][..],
4278 &[b"HEXISTS".as_slice(), b"str", b"f"][..],
4279 &[b"HSTRLEN".as_slice(), b"str", b"f"][..],
4280 &[b"HGETALL".as_slice(), b"str"][..],
4281 &[b"HKEYS".as_slice(), b"str"][..],
4282 &[b"HVALS".as_slice(), b"str"][..],
4283 &[b"HINCRBY".as_slice(), b"str", b"f", b"1"][..],
4284 &[b"HINCRBYFLOAT".as_slice(), b"str", b"f", b"1"][..],
4285 &[b"HRANDFIELD".as_slice(), b"str"][..],
4286 &[b"HRANDFIELD".as_slice(), b"str", b"2"][..],
4287 &[b"HSCAN".as_slice(), b"str", b"0"][..],
4288 ] {
4289 let reply = f.run(cmd);
4290 assert_eq!(reply, wrong, "{:?}", cmd[0]);
4291 }
4292 assert_eq!(
4293 f.run(&[b"GET", b"str"]),
4294 "$1\r\nv\r\n",
4295 "and none of them touched the value"
4296 );
4297 }
4298
4299 #[test]
4300 fn a_hash_scan_leaves_nothing_half_written_when_its_arguments_are_wrong() {
4301 let mut f = Fixture::new();
4302 f.run(&[b"HSET", b"h", b"f", b"v"]);
4303 for bad in [
4304 &[b"HSCAN".as_slice(), b"h", b"abc"][..],
4305 &[b"HSCAN".as_slice(), b"h", b"0", b"COUNT", b"nope"][..],
4306 &[b"HSCAN".as_slice(), b"h", b"0", b"COUNT", b"0"][..],
4307 &[b"HSCAN".as_slice(), b"h", b"0", b"MATCH"][..],
4308 ] {
4309 let reply = f.run(bad);
4310 assert!(reply.starts_with("-ERR"), "got {reply}");
4311 assert!(!reply.contains('*'), "an array header went out in front");
4312 }
4313 }
4314
4315 #[test]
4316 fn a_field_deadline_goes_on_and_comes_back_in_all_four_units() {
4317 let mut f = Fixture::new();
4318 f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
4319 assert_eq!(
4320 f.run(&[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"a"]),
4321 "*1\r\n:1\r\n"
4322 );
4323 assert_eq!(
4324 f.run(&[b"HTTL", b"h", b"FIELDS", b"3", b"a", b"b", b"nope"]),
4325 "*3\r\n:100\r\n:-1\r\n:-2\r\n",
4326 "one answer per field, and the two sentinels are TTL's own"
4327 );
4328
4329 let ms = int_reply(&f.run(&[b"HPTTL", b"h", b"FIELDS", b"1", b"a"]));
4332 assert!((99_000..=100_000).contains(&ms), "got {ms}");
4333 let at = int_reply(&f.run(&[b"HEXPIRETIME", b"h", b"FIELDS", b"1", b"a"]));
4334 let at_ms = int_reply(&f.run(&[b"HPEXPIRETIME", b"h", b"FIELDS", b"1", b"a"]));
4335 assert_eq!(at, at_ms.div_euclid(1000) + i64::from(at_ms % 1000 != 0));
4336 assert!(at_ms > 1_700_000_000_000, "an absolute moment, got {at_ms}");
4337
4338 assert_eq!(
4339 f.run(&[b"HPERSIST", b"h", b"FIELDS", b"3", b"a", b"b", b"nope"]),
4340 "*3\r\n:1\r\n:-1\r\n:-2\r\n",
4341 "one for the deadline taken off, and it does not say what it was"
4342 );
4343 assert_eq!(
4344 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
4345 "*1\r\n:-1\r\n"
4346 );
4347 assert_eq!(
4348 f.run(&[b"HGET", b"h", b"a"]),
4349 "$1\r\n1\r\n",
4350 "and the field is still there with the value it had"
4351 );
4352 }
4353
4354 #[test]
4355 fn a_deadline_that_has_already_gone_deletes_the_field_now() {
4356 let mut f = Fixture::new();
4357 f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
4358 assert_eq!(
4359 f.run(&[b"HEXPIREAT", b"h", b"1", b"FIELDS", b"1", b"a"]),
4360 "*1\r\n:2\r\n",
4361 "two, and not one, because nothing was stored"
4362 );
4363 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$-1\r\n");
4364 assert_eq!(f.run(&[b"HLEN", b"h"]), ":1\r\n");
4365
4366 assert_eq!(
4367 f.run(&[b"HPEXPIREAT", b"h", b"1", b"FIELDS", b"1", b"b"]),
4368 "*1\r\n:2\r\n"
4369 );
4370 assert_eq!(
4371 f.run(&[b"EXISTS", b"h"]),
4372 ":0\r\n",
4373 "and the last field going took the key with it"
4374 );
4375
4376 f.run(&[b"HSET", b"h", b"a", b"1"]);
4379 assert_eq!(
4380 f.run(&[b"HEXPIRE", b"h", b"0", b"FIELDS", b"1", b"a"]),
4381 "*1\r\n:2\r\n"
4382 );
4383 }
4384
4385 #[test]
4386 fn a_field_is_gone_once_its_moment_passes() {
4387 let mut f = Fixture::new();
4388 f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
4389 assert_eq!(
4390 f.run(&[b"HPEXPIRE", b"h", b"20", b"FIELDS", b"1", b"a"]),
4391 "*1\r\n:1\r\n"
4392 );
4393 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n", "not yet");
4394
4395 f.server.db(0).clock_mut().advance(60);
4399 assert_eq!(f.run(&[b"HLEN", b"h"]), ":1\r\n");
4400 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$-1\r\n");
4401 assert_eq!(
4402 f.run(&[b"HGETALL", b"h"]),
4403 "*2\r\n$1\r\nb\r\n$1\r\n2\r\n",
4404 "and the walks do not hand back a field that has expired"
4405 );
4406 }
4407
4408 #[test]
4409 fn a_missing_key_answers_the_no_field_sentinel_for_every_field() {
4410 let mut f = Fixture::new();
4411 for cmd in [
4412 &[
4413 b"HEXPIRE".as_slice(),
4414 b"nokey",
4415 b"100",
4416 b"FIELDS",
4417 b"2",
4418 b"a",
4419 b"b",
4420 ][..],
4421 &[b"HTTL".as_slice(), b"nokey", b"FIELDS", b"2", b"a", b"b"][..],
4422 &[b"HPTTL".as_slice(), b"nokey", b"FIELDS", b"2", b"a", b"b"][..],
4423 &[
4424 b"HEXPIRETIME".as_slice(),
4425 b"nokey",
4426 b"FIELDS",
4427 b"2",
4428 b"a",
4429 b"b",
4430 ][..],
4431 &[
4432 b"HPERSIST".as_slice(),
4433 b"nokey",
4434 b"FIELDS",
4435 b"2",
4436 b"a",
4437 b"b",
4438 ][..],
4439 ] {
4440 assert_eq!(f.run(cmd), "*2\r\n:-2\r\n:-2\r\n", "{:?}", cmd[0]);
4441 }
4442 }
4443
4444 #[test]
4445 fn writing_a_field_clears_the_deadline_that_was_on_it() {
4446 let mut f = Fixture::new();
4447 f.run(&[b"HSET", b"h", b"a", b"1"]);
4448 f.run(&[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"a"]);
4449 f.run(&[b"HSET", b"h", b"a", b"2"]);
4450 assert_eq!(
4451 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
4452 "*1\r\n:-1\r\n",
4453 "Redis has done this since 7.4, and it is why HGETEX exists"
4454 );
4455 }
4456
4457 #[test]
4458 fn the_four_conditions_reach_the_store_the_way_they_were_written() {
4459 let mut f = Fixture::new();
4460 f.run(&[b"HSET", b"h", b"a", b"1"]);
4461 assert_eq!(
4462 f.run(&[b"HEXPIRE", b"h", b"100", b"XX", b"FIELDS", b"1", b"a"]),
4463 "*1\r\n:0\r\n",
4464 "XX on a field with no deadline changes nothing"
4465 );
4466 assert_eq!(
4467 f.run(&[b"HEXPIRE", b"h", b"100", b"NX", b"FIELDS", b"1", b"a"]),
4468 "*1\r\n:1\r\n"
4469 );
4470 assert_eq!(
4471 f.run(&[b"HEXPIRE", b"h", b"200", b"NX", b"FIELDS", b"1", b"a"]),
4472 "*1\r\n:0\r\n",
4473 "and NX will not move one that is already there"
4474 );
4475 assert_eq!(
4476 f.run(&[b"HEXPIRE", b"h", b"50", b"GT", b"FIELDS", b"1", b"a"]),
4477 "*1\r\n:0\r\n"
4478 );
4479 assert_eq!(
4480 f.run(&[b"HEXPIRE", b"h", b"500", b"GT", b"FIELDS", b"1", b"a"]),
4481 "*1\r\n:1\r\n"
4482 );
4483 assert_eq!(
4484 f.run(&[b"HEXPIRE", b"h", b"50", b"LT", b"FIELDS", b"1", b"a"]),
4485 "*1\r\n:1\r\n"
4486 );
4487 assert_eq!(
4488 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
4489 "*1\r\n:50\r\n"
4490 );
4491 }
4492
4493 #[test]
4494 fn the_field_ttl_family_leaves_nothing_half_written_on_a_bad_argument() {
4495 let mut f = Fixture::new();
4496 f.run(&[b"HSET", b"h", b"a", b"1"]);
4497 for (bad, want) in [
4498 (
4499 &[b"HEXPIRE".as_slice(), b"h", b"-1", b"FIELDS", b"1", b"a"][..],
4500 "-ERR invalid expire time, must be >= 0",
4501 ),
4502 (
4503 &[
4504 b"HEXPIRE".as_slice(),
4505 b"h",
4506 b"9999999999999999",
4507 b"FIELDS",
4508 b"1",
4509 b"a",
4510 ][..],
4511 "-ERR invalid expire time in 'hexpire' command",
4512 ),
4513 (
4514 &[b"HEXPIRE".as_slice(), b"h", b"100", b"FIELD", b"1", b"a"][..],
4515 "-ERR wrong number of arguments for 'hexpire' command",
4516 ),
4517 (
4518 &[b"HEXPIRE".as_slice(), b"h", b"100", b"FIELDS", b"0", b"a"][..],
4519 "-ERR Parameter `numFields` should be greater than 0",
4520 ),
4521 (
4522 &[b"HEXPIRE".as_slice(), b"h", b"100", b"FIELDS", b"2", b"a"][..],
4523 "-ERR wrong number of arguments",
4524 ),
4525 (
4526 &[b"HTTL".as_slice(), b"h", b"FIELDS", b"3", b"a", b"b"][..],
4527 "-ERR wrong number of arguments",
4528 ),
4529 ] {
4530 let reply = f.run(bad);
4531 assert!(reply.starts_with(want), "wanted {want}, got {reply}");
4532 assert!(!reply.contains('*'), "an array header went out in front");
4533 }
4534 assert_eq!(
4535 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
4536 "*1\r\n:-1\r\n",
4537 "and not one of them put a deadline on anything"
4538 );
4539 }
4540
4541 #[test]
4542 fn every_field_ttl_command_says_wrongtype_and_writes_nothing() {
4543 let mut f = Fixture::new();
4544 f.run(&[b"SET", b"str", b"v"]);
4545 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
4546
4547 for cmd in [
4548 &[b"HEXPIRE".as_slice(), b"str", b"100", b"FIELDS", b"1", b"f"][..],
4549 &[
4550 b"HPEXPIRE".as_slice(),
4551 b"str",
4552 b"100",
4553 b"FIELDS",
4554 b"1",
4555 b"f",
4556 ][..],
4557 &[
4558 b"HEXPIREAT".as_slice(),
4559 b"str",
4560 b"9999999999",
4561 b"FIELDS",
4562 b"1",
4563 b"f",
4564 ][..],
4565 &[
4566 b"HPEXPIREAT".as_slice(),
4567 b"str",
4568 b"9999999999999",
4569 b"FIELDS",
4570 b"1",
4571 b"f",
4572 ][..],
4573 &[b"HTTL".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
4574 &[b"HPTTL".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
4575 &[b"HEXPIRETIME".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
4576 &[b"HPEXPIRETIME".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
4577 &[b"HPERSIST".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
4578 ] {
4579 assert_eq!(f.run(cmd), wrong, "{:?}", cmd[0]);
4580 }
4581 assert_eq!(
4582 f.run(&[b"GET", b"str"]),
4583 "$1\r\nv\r\n",
4584 "and none of them touched the value"
4585 );
4586 }
4587
4588 #[test]
4589 fn hgetdel_hands_the_value_out_and_then_takes_the_field() {
4590 let mut f = Fixture::new();
4591 f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
4592 assert_eq!(
4593 f.run(&[b"HGETDEL", b"h", b"FIELDS", b"2", b"a", b"nope"]),
4594 "*2\r\n$1\r\n1\r\n$-1\r\n",
4595 "positional, so the field that was not there is a nil in its place"
4596 );
4597 assert_eq!(f.run(&[b"HLEN", b"h"]), ":1\r\n");
4598 assert_eq!(
4599 f.run(&[b"HGETDEL", b"nokey", b"FIELDS", b"1", b"a"]),
4600 "*1\r\n$-1\r\n"
4601 );
4602 assert_eq!(
4603 f.run(&[b"HGETDEL", b"h", b"FIELDS", b"1", b"b"]),
4604 "*1\r\n$1\r\n2\r\n"
4605 );
4606 assert_eq!(
4607 f.run(&[b"EXISTS", b"h"]),
4608 ":0\r\n",
4609 "and the last field took the key"
4610 );
4611 }
4612
4613 #[test]
4614 fn hgetex_reads_and_moves_the_deadline_in_one_command() {
4615 let mut f = Fixture::new();
4616 f.run(&[b"HSET", b"h", b"a", b"1"]);
4617 assert_eq!(
4618 f.run(&[b"HGETEX", b"h", b"FIELDS", b"1", b"a"]),
4619 "*1\r\n$1\r\n1\r\n"
4620 );
4621 assert_eq!(
4622 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
4623 "*1\r\n:-1\r\n",
4624 "no option means leave it alone, which is the one place this is not GETEX"
4625 );
4626
4627 f.run(&[b"HGETEX", b"h", b"EX", b"100", b"FIELDS", b"1", b"a"]);
4628 assert_eq!(
4629 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
4630 "*1\r\n:100\r\n"
4631 );
4632 f.run(&[b"HGETEX", b"h", b"FIELDS", b"1", b"a"]);
4633 assert_eq!(
4634 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
4635 "*1\r\n:100\r\n",
4636 "and a plain read really does leave it alone"
4637 );
4638 assert_eq!(
4639 f.run(&[b"HGETEX", b"h", b"PERSIST", b"FIELDS", b"1", b"a"]),
4640 "*1\r\n$1\r\n1\r\n"
4641 );
4642 assert_eq!(
4643 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
4644 "*1\r\n:-1\r\n"
4645 );
4646
4647 assert_eq!(
4648 f.run(&[b"HGETEX", b"h", b"EXAT", b"1", b"FIELDS", b"1", b"a"]),
4649 "*1\r\n$1\r\n1\r\n",
4650 "the value goes out before the deadline that has already gone is applied"
4651 );
4652 assert_eq!(f.run(&[b"EXISTS", b"h"]), ":0\r\n");
4653 assert_eq!(
4654 f.run(&[b"HGETEX", b"nokey", b"EX", b"100", b"FIELDS", b"1", b"a"]),
4655 "*1\r\n$-1\r\n"
4656 );
4657 }
4658
4659 #[test]
4660 fn hsetex_writes_all_of_it_or_none_of_it() {
4661 let mut f = Fixture::new();
4662 assert_eq!(
4663 f.run(&[b"HSETEX", b"h", b"FIELDS", b"1", b"a", b"1"]),
4664 ":1\r\n"
4665 );
4666 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
4667 assert_eq!(
4668 f.run(&[
4669 b"HSETEX", b"h", b"FNX", b"FIELDS", b"2", b"a", b"9", b"new", b"9"
4670 ]),
4671 ":0\r\n",
4672 "FNX wants every field named to be missing"
4673 );
4674 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
4675 assert_eq!(
4676 f.run(&[b"HEXISTS", b"h", b"new"]),
4677 ":0\r\n",
4678 "and none of the list was written"
4679 );
4680 assert_eq!(
4681 f.run(&[
4682 b"HSETEX", b"h", b"FXX", b"FIELDS", b"2", b"a", b"9", b"nope", b"9"
4683 ]),
4684 ":0\r\n",
4685 "and FXX wants every one of them to be there"
4686 );
4687 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
4688 assert_eq!(
4689 f.run(&[b"HSETEX", b"h", b"FXX", b"FIELDS", b"1", b"a", b"9"]),
4690 ":1\r\n"
4691 );
4692 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n9\r\n");
4693
4694 assert_eq!(
4695 f.run(&[b"HSETEX", b"gone", b"FXX", b"FIELDS", b"1", b"a", b"1"]),
4696 ":0\r\n"
4697 );
4698 assert_eq!(
4699 f.run(&[b"EXISTS", b"gone"]),
4700 ":0\r\n",
4701 "a key with no fields cannot meet FXX and is not created trying"
4702 );
4703 }
4704
4705 #[test]
4706 fn hsetex_clears_the_deadline_unless_it_is_told_to_keep_it() {
4707 let mut f = Fixture::new();
4708 f.run(&[b"HSETEX", b"h", b"EX", b"100", b"FIELDS", b"1", b"a", b"1"]);
4709 assert_eq!(
4710 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
4711 "*1\r\n:100\r\n"
4712 );
4713
4714 f.run(&[b"HSETEX", b"h", b"KEEPTTL", b"FIELDS", b"1", b"a", b"2"]);
4715 assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n2\r\n");
4716 assert_eq!(
4717 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
4718 "*1\r\n:100\r\n",
4719 "KEEPTTL put back what the write cleared"
4720 );
4721
4722 f.run(&[b"HSETEX", b"h", b"FIELDS", b"1", b"a", b"3"]);
4723 assert_eq!(
4724 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
4725 "*1\r\n:-1\r\n",
4726 "and without it a write clears the deadline the way HSET does"
4727 );
4728
4729 assert_eq!(
4732 f.run(&[
4733 b"HSETEX", b"h", b"PX", b"100000", b"FXX", b"FIELDS", b"1", b"a", b"4"
4734 ]),
4735 ":1\r\n"
4736 );
4737 assert_eq!(
4738 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
4739 "*1\r\n:100\r\n"
4740 );
4741
4742 assert_eq!(
4743 f.run(&[b"HSETEX", b"h", b"EXAT", b"1", b"FIELDS", b"1", b"a", b"5"]),
4744 ":1\r\n",
4745 "written, and not the separate code the HEXPIRE family has for this"
4746 );
4747 assert_eq!(
4748 f.run(&[b"EXISTS", b"h"]),
4749 ":0\r\n",
4750 "and storing it and then removing it emptied the hash"
4751 );
4752 }
4753
4754 #[test]
4755 fn the_last_three_hash_commands_word_their_mistakes_their_own_way() {
4756 let mut f = Fixture::new();
4757 f.run(&[b"HSET", b"h", b"a", b"1"]);
4758 for (bad, want) in [
4759 (
4761 &[b"HGETDEL".as_slice(), b"h", b"FIELDS", b"0", b"a"][..],
4762 "-ERR Number of fields must be a positive integer",
4763 ),
4764 (
4765 &[b"HGETDEL".as_slice(), b"h", b"FIELDS", b"2", b"a"][..],
4766 "-ERR The `numfields` parameter must match the number of arguments",
4767 ),
4768 (
4769 &[b"HGETDEL".as_slice(), b"h", b"FIELD", b"1", b"a"][..],
4770 "-ERR Mandatory argument FIELDS is missing or not at the right position",
4771 ),
4772 (
4774 &[b"HGETEX".as_slice(), b"h", b"FIELDS", b"0", b"a"][..],
4775 "-ERR invalid number of fields",
4776 ),
4777 (
4778 &[b"HGETEX".as_slice(), b"h", b"FIELDS", b"2", b"a"][..],
4779 "-ERR wrong number of arguments",
4780 ),
4781 (
4782 &[b"HGETEX".as_slice(), b"h", b"FIELD", b"1", b"a"][..],
4783 "-ERR unknown argument: FIELD",
4784 ),
4785 (
4786 &[
4787 b"HGETEX".as_slice(),
4788 b"h",
4789 b"KEEPTTL",
4790 b"FIELDS",
4791 b"1",
4792 b"a",
4793 ][..],
4794 "-ERR unknown argument: KEEPTTL",
4795 ),
4796 (
4797 &[
4798 b"HGETEX".as_slice(),
4799 b"h",
4800 b"EX",
4801 b"100",
4802 b"PERSIST",
4803 b"FIELDS",
4804 b"1",
4805 b"a",
4806 ][..],
4807 "-ERR Only one of EX, PX, EXAT, PXAT or PERSIST arguments can be specified",
4808 ),
4809 (
4810 &[
4811 b"HSETEX".as_slice(),
4812 b"h",
4813 b"EX",
4814 b"1",
4815 b"KEEPTTL",
4816 b"FIELDS",
4817 b"1",
4818 b"a",
4819 b"1",
4820 ][..],
4821 "-ERR Only one of EX, PX, EXAT, PXAT or KEEPTTL arguments can be specified",
4822 ),
4823 (
4824 &[
4825 b"HSETEX".as_slice(),
4826 b"h",
4827 b"FNX",
4828 b"FXX",
4829 b"FIELDS",
4830 b"1",
4831 b"a",
4832 b"1",
4833 ][..],
4834 "-ERR Only one of FXX or FNX arguments can be specified",
4835 ),
4836 (
4837 &[
4838 b"HSETEX".as_slice(),
4839 b"h",
4840 b"FIELDS",
4841 b"2",
4842 b"a",
4843 b"1",
4844 b"b",
4845 ][..],
4846 "-ERR wrong number of arguments",
4847 ),
4848 (
4849 &[
4850 b"HGETEX".as_slice(),
4851 b"h",
4852 b"EX",
4853 b"-1",
4854 b"FIELDS",
4855 b"1",
4856 b"a",
4857 ][..],
4858 "-ERR invalid expire time, must be >= 0",
4859 ),
4860 (
4861 &[
4862 b"HGETEX".as_slice(),
4863 b"h",
4864 b"PXAT",
4865 b"99999999999999",
4866 b"FIELDS",
4867 b"1",
4868 b"a",
4869 ][..],
4870 "-ERR invalid expire time in 'hgetex' command",
4871 ),
4872 (
4873 &[
4874 b"HSETEX".as_slice(),
4875 b"h",
4876 b"EX",
4877 b"abc",
4878 b"FIELDS",
4879 b"1",
4880 b"a",
4881 b"1",
4882 ][..],
4883 "-ERR value is not an integer or out of range",
4884 ),
4885 ] {
4886 let reply = f.run(bad);
4887 assert!(reply.starts_with(want), "wanted {want}, got {reply}");
4888 assert!(!reply.contains('*'), "an array header went out in front");
4889 }
4890 assert_eq!(
4891 f.run(&[b"HGET", b"h", b"a"]),
4892 "$1\r\n1\r\n",
4893 "and not one of them wrote anything"
4894 );
4895 assert_eq!(
4896 f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
4897 "*1\r\n:-1\r\n"
4898 );
4899 }
4900
4901 #[test]
4902 fn the_last_three_hash_commands_say_wrongtype_and_write_nothing() {
4903 let mut f = Fixture::new();
4904 f.run(&[b"SET", b"str", b"v"]);
4905 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
4906 for cmd in [
4907 &[b"HGETDEL".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
4908 &[b"HGETEX".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
4909 &[
4910 b"HGETEX".as_slice(),
4911 b"str",
4912 b"EX",
4913 b"100",
4914 b"FIELDS",
4915 b"1",
4916 b"f",
4917 ][..],
4918 &[b"HSETEX".as_slice(), b"str", b"FIELDS", b"1", b"f", b"v"][..],
4919 ] {
4920 assert_eq!(f.run(cmd), wrong, "{:?}", cmd[0]);
4921 }
4922 assert_eq!(f.run(&[b"GET", b"str"]), "$1\r\nv\r\n");
4923 }
4924
4925 fn int(reply: &str) -> i64 {
4931 let body = reply
4932 .strip_prefix(':')
4933 .and_then(|s| s.strip_suffix("\r\n"))
4934 .unwrap_or_else(|| panic!("wanted an integer, got {reply}"));
4935 body.parse().expect("an integer")
4936 }
4937
4938 fn int_reply(reply: &str) -> i64 {
4939 let body = reply
4940 .strip_prefix("*1\r\n:")
4941 .and_then(|s| s.strip_suffix("\r\n"))
4942 .unwrap_or_else(|| panic!("wanted one integer, got {reply}"));
4943 body.parse().expect("an integer")
4944 }
4945
4946 fn scan_reply(reply: &str) -> (String, Vec<String>) {
4948 let mut lines = reply.split("\r\n");
4949 assert_eq!(lines.next(), Some("*2"), "got {reply}");
4950 lines.next().expect("the cursor header");
4951 let cursor = lines.next().expect("a cursor").to_owned();
4952 let header = lines.next().expect("an item count");
4953 let n: usize = header[1..].parse().expect("a count");
4954 let mut items = Vec::with_capacity(n);
4955 for _ in 0..n {
4956 lines.next().expect("an item header");
4957 items.push(lines.next().expect("an item").to_owned());
4958 }
4959 (cursor, items)
4960 }
4961
4962 fn sorted(reply: &str) -> Vec<String> {
4965 let mut lines = reply.split("\r\n");
4966 let header = lines.next().expect("a header");
4967 assert!(
4968 header.starts_with('*') || header.starts_with('~'),
4969 "got {reply}"
4970 );
4971 let n: usize = header[1..].parse().expect("a member count");
4972 let mut got = Vec::with_capacity(n);
4973 for _ in 0..n {
4974 lines.next().expect("a member header");
4975 got.push(lines.next().expect("a member").to_owned());
4976 }
4977 got.sort();
4978 got
4979 }
4980
4981 #[test]
4982 fn the_algebra_answers_what_the_sets_share_and_do_not() {
4983 let mut f = Fixture::new();
4984 f.run(&[b"SADD", b"a", b"1", b"2", b"3"]);
4985 f.run(&[b"SADD", b"b", b"2", b"3", b"4"]);
4986 f.run(&[b"SADD", b"c", b"3", b"4", b"5"]);
4987
4988 assert_eq!(sorted(&f.run(&[b"SINTER", b"a", b"b", b"c"])), ["3"]);
4989 assert_eq!(
4990 sorted(&f.run(&[b"SUNION", b"a", b"b", b"c"])),
4991 ["1", "2", "3", "4", "5"]
4992 );
4993 assert_eq!(sorted(&f.run(&[b"SDIFF", b"a", b"b"])), ["1"]);
4994 assert_eq!(sorted(&f.run(&[b"SINTER", b"a"])), ["1", "2", "3"]);
4995
4996 assert_eq!(f.run(&[b"SINTER", b"a", b"nope"]), "*0\r\n");
4999 assert_eq!(sorted(&f.run(&[b"SUNION", b"a", b"nope"])), ["1", "2", "3"]);
5000 assert_eq!(f.run(&[b"SDIFF", b"nope", b"a"]), "*0\r\n");
5001 assert_eq!(f.run(&[b"DBSIZE"]), ":3\r\n", "and none of it made a key");
5002 }
5003
5004 #[test]
5005 fn the_algebra_answers_a_set_on_resp3_and_an_array_on_resp2() {
5006 let mut f = Fixture::new();
5007 f.run(&[b"SADD", b"a", b"x"]);
5008 assert_eq!(f.run(&[b"SINTER", b"a"]), "*1\r\n$1\r\nx\r\n");
5009 assert_eq!(f.run(&[b"SUNION", b"a"]), "*1\r\n$1\r\nx\r\n");
5010 assert_eq!(f.run(&[b"SDIFF", b"a"]), "*1\r\n$1\r\nx\r\n");
5011
5012 f.run(&[b"HELLO", b"3"]);
5013 assert_eq!(f.run(&[b"SINTER", b"a"]), "~1\r\n$1\r\nx\r\n");
5014 assert_eq!(f.run(&[b"SUNION", b"a"]), "~1\r\n$1\r\nx\r\n");
5015 assert_eq!(f.run(&[b"SDIFF", b"a"]), "~1\r\n$1\r\nx\r\n");
5016 assert_eq!(f.run(&[b"SINTER", b"nope"]), "~0\r\n");
5017 }
5018
5019 #[test]
5020 fn a_store_form_writes_a_key_and_answers_how_big_it_is() {
5021 let mut f = Fixture::new();
5022 f.run(&[b"SADD", b"a", b"1", b"2", b"3"]);
5023 f.run(&[b"SADD", b"b", b"2", b"3", b"4"]);
5024
5025 assert_eq!(f.run(&[b"SINTERSTORE", b"d", b"a", b"b"]), ":2\r\n");
5026 assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"d"])), ["2", "3"]);
5027 assert_eq!(f.run(&[b"SUNIONSTORE", b"d", b"a", b"b"]), ":4\r\n");
5028 assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"d"])), ["1", "2", "3", "4"]);
5029 assert_eq!(f.run(&[b"SDIFFSTORE", b"d", b"a", b"b"]), ":1\r\n");
5030 assert_eq!(f.run(&[b"SMEMBERS", b"d"]), "*1\r\n$1\r\n1\r\n");
5031
5032 assert_eq!(f.run(&[b"SDIFFSTORE", b"d", b"a", b"a"]), ":0\r\n");
5035 assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
5036 assert_eq!(f.run(&[b"SINTERSTORE", b"a", b"a", b"b"]), ":2\r\n");
5037 assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"a"])), ["2", "3"]);
5038
5039 f.run(&[b"SET", b"str", b"v"]);
5042 assert_eq!(f.run(&[b"SUNIONSTORE", b"str", b"b"]), ":3\r\n");
5043 assert_eq!(f.run(&[b"TYPE", b"str"]), "+set\r\n");
5044 }
5045
5046 #[test]
5047 fn sintercard_counts_without_building_and_stops_at_a_limit() {
5048 let mut f = Fixture::new();
5049 f.run(&[b"SADD", b"a", b"1", b"2", b"3", b"4"]);
5050 f.run(&[b"SADD", b"b", b"2", b"3", b"4", b"5"]);
5051
5052 assert_eq!(f.run(&[b"SINTERCARD", b"2", b"a", b"b"]), ":3\r\n");
5053 assert_eq!(
5054 f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"LIMIT", b"2"]),
5055 ":2\r\n"
5056 );
5057 assert_eq!(
5058 f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"LIMIT", b"0"]),
5059 ":3\r\n",
5060 "a limit of zero is no limit"
5061 );
5062 assert_eq!(f.run(&[b"SINTERCARD", b"1", b"a"]), ":4\r\n");
5063 assert_eq!(f.run(&[b"SINTERCARD", b"2", b"a", b"nope"]), ":0\r\n");
5064
5065 assert_eq!(
5067 f.run(&[b"SINTERCARD", b"0", b"a"]),
5068 "-ERR numkeys should be greater than 0\r\n"
5069 );
5070 assert_eq!(
5071 f.run(&[b"SINTERCARD", b"abc", b"a"]),
5072 "-ERR numkeys should be greater than 0\r\n"
5073 );
5074 assert_eq!(
5075 f.run(&[b"SINTERCARD", b"3", b"a", b"b"]),
5076 "-ERR Number of keys can't be greater than number of args\r\n"
5077 );
5078 assert_eq!(
5079 f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"LIMIT", b"-1"]),
5080 "-ERR LIMIT can't be negative\r\n"
5081 );
5082 assert_eq!(
5083 f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"NOPE", b"1"]),
5084 "-ERR syntax error\r\n"
5085 );
5086 f.run(&[b"SADD", b"LIMIT", b"2"]);
5088 assert_eq!(f.run(&[b"SINTERCARD", b"2", b"a", b"LIMIT"]), ":1\r\n");
5089 }
5090
5091 #[test]
5092 fn the_algebra_answers_wrongtype_before_it_writes_anything() {
5093 let mut f = Fixture::new();
5094 f.run(&[b"SADD", b"a", b"1"]);
5095 f.run(&[b"SADD", b"d", b"old"]);
5096 f.run(&[b"SET", b"str", b"v"]);
5097
5098 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
5099 for bad in [
5100 &[b"SINTER".as_slice(), b"a", b"str"][..],
5101 &[b"SUNION".as_slice(), b"str"][..],
5102 &[b"SDIFF".as_slice(), b"a", b"str"][..],
5103 &[b"SINTERCARD".as_slice(), b"2", b"a", b"str"][..],
5104 &[b"SINTERSTORE".as_slice(), b"d", b"a", b"str"][..],
5105 &[b"SUNIONSTORE".as_slice(), b"d", b"str"][..],
5106 &[b"SDIFFSTORE".as_slice(), b"d", b"a", b"str"][..],
5107 ] {
5108 let reply = f.run(bad);
5109 assert_eq!(reply, wrong, "for {:?}", bad[0]);
5110 }
5111 assert_eq!(
5112 f.run(&[b"SMEMBERS", b"d"]),
5113 "*1\r\n$3\r\nold\r\n",
5114 "and the destination was left alone every time"
5115 );
5116 }
5117
5118 #[test]
5121 fn churning_sets_does_not_grow_the_server() {
5122 let mut f = Fixture::new();
5123 let members: Vec<Vec<u8>> = (0..200).map(|i| format!("m{i}").into_bytes()).collect();
5124 let args: Vec<&[u8]> = std::iter::once(&b"SADD"[..])
5125 .chain(std::iter::once(&b"s"[..]))
5126 .chain(members.iter().map(Vec::as_slice))
5127 .collect();
5128
5129 f.run(&args);
5130 f.run(&[b"DEL", b"s"]);
5131 f.server.compact_step();
5132 let after_first = f.server.memory_bytes();
5133
5134 for _ in 0..200 {
5135 f.run(&args);
5136 f.run(&[b"DEL", b"s"]);
5137 f.server.compact_step();
5138 }
5139 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
5140 assert!(
5141 f.server.memory_bytes() <= after_first * 2,
5142 "held {} after two hundred passes against {after_first} after one",
5143 f.server.memory_bytes()
5144 );
5145 }
5146
5147 #[test]
5155 fn a_bit_is_written_and_read_back_and_a_write_unpacks_an_int() {
5156 let mut f = Fixture::new();
5157 assert_eq!(f.run(&[b"SETBIT", b"k", b"7", b"1"]), ":0\r\n");
5158 assert_eq!(f.run(&[b"GET", b"k"]), "$1\r\n\u{1}\r\n");
5159 assert_eq!(f.run(&[b"GETBIT", b"k", b"7"]), ":1\r\n");
5160 assert_eq!(f.run(&[b"GETBIT", b"k", b"6"]), ":0\r\n");
5161 assert_eq!(f.run(&[b"GETBIT", b"k", b"100"]), ":0\r\n");
5162 assert_eq!(f.run(&[b"SETBIT", b"k", b"7", b"0"]), ":1\r\n");
5163
5164 assert_eq!(f.run(&[b"SETBIT", b"nk", b"0", b"0"]), ":0\r\n");
5166 assert_eq!(f.run(&[b"STRLEN", b"nk"]), ":1\r\n");
5167 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"nk"]), "$3\r\nraw\r\n");
5168
5169 f.run(&[b"SET", b"num", b"12345"]);
5170 assert_eq!(f.run(&[b"GETBIT", b"num", b"1"]), ":0\r\n");
5171 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"num"]), "$3\r\nint\r\n");
5172 assert_eq!(f.run(&[b"SETBIT", b"num", b"1", b"1"]), ":0\r\n");
5173 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"num"]), "$3\r\nraw\r\n");
5174 assert_eq!(f.run(&[b"GET", b"num"]), "$5\r\nq2345\r\n");
5175 }
5176
5177 #[test]
5182 fn bits_are_counted_over_a_range_of_bytes_or_of_bits() {
5183 let mut f = Fixture::new();
5184 f.run(&[b"SET", b"mykey", b"foobar"]);
5185 assert_eq!(f.run(&[b"BITCOUNT", b"mykey"]), ":26\r\n");
5186 assert_eq!(f.run(&[b"BITCOUNT", b"mykey", b"0", b"0"]), ":4\r\n");
5187 assert_eq!(f.run(&[b"BITCOUNT", b"mykey", b"1", b"1"]), ":6\r\n");
5188 assert_eq!(
5189 f.run(&[b"BITCOUNT", b"mykey", b"1", b"1", b"BYTE"]),
5190 ":6\r\n"
5191 );
5192 assert_eq!(
5193 f.run(&[b"BITCOUNT", b"mykey", b"0", b"-5", b"BIT"]),
5194 ":25\r\n"
5195 );
5196 assert_eq!(
5197 f.run(&[b"BITCOUNT", b"mykey", b"5", b"30", b"BIT"]),
5198 ":17\r\n"
5199 );
5200 assert_eq!(f.run(&[b"BITCOUNT", b"nokey"]), ":0\r\n");
5201
5202 assert_eq!(f.run(&[b"BITCOUNT", b"mykey", b"10", b"20"]), ":0\r\n");
5205
5206 assert_eq!(
5208 f.run(&[b"BITCOUNT", b"mykey", b"0"]),
5209 "-ERR syntax error\r\n"
5210 );
5211 assert_eq!(
5212 f.run(&[b"BITCOUNT", b"mykey", b"0", b"1", b"NIB"]),
5213 "-ERR syntax error\r\n"
5214 );
5215 }
5216
5217 #[test]
5223 fn a_search_for_a_nought_past_the_end_answers_the_length_in_bits() {
5224 let mut f = Fixture::new();
5225 f.run(&[b"SET", b"ones", b"\xff\xff\xff"]);
5226 assert_eq!(f.run(&[b"BITPOS", b"ones", b"0"]), ":24\r\n");
5227 assert_eq!(f.run(&[b"BITPOS", b"ones", b"0", b"0"]), ":24\r\n");
5228 assert_eq!(f.run(&[b"BITPOS", b"ones", b"0", b"0", b"-1"]), ":-1\r\n");
5229 assert_eq!(f.run(&[b"BITPOS", b"ones", b"0", b"0", b"3"]), ":-1\r\n");
5230 assert_eq!(f.run(&[b"BITPOS", b"ones", b"1"]), ":0\r\n");
5231
5232 f.run(&[b"SET", b"mid", b"\x00\xff\xf0"]);
5233 assert_eq!(f.run(&[b"BITPOS", b"mid", b"1", b"0"]), ":8\r\n");
5234 assert_eq!(f.run(&[b"BITPOS", b"mid", b"1", b"2"]), ":16\r\n");
5235 assert_eq!(
5236 f.run(&[b"BITPOS", b"mid", b"1", b"0", b"-1", b"BIT"]),
5237 ":8\r\n"
5238 );
5239
5240 assert_eq!(f.run(&[b"BITPOS", b"gone", b"1"]), ":-1\r\n");
5243 assert_eq!(f.run(&[b"BITPOS", b"gone", b"0"]), ":0\r\n");
5244 }
5245
5246 #[test]
5248 fn the_eight_combinations_write_what_a_real_server_writes() {
5249 let mut f = Fixture::new();
5250 f.run(&[b"SET", b"a", b"abc"]);
5251 f.run(&[b"SET", b"b", b"abd"]);
5252 let cases: &[(&[u8], &str)] = &[
5253 (b"AND", "ab`"),
5254 (b"OR", "abg"),
5255 (b"XOR", "\u{0}\u{0}\u{7}"),
5256 (b"DIFF", "\u{0}\u{0}\u{3}"),
5257 (b"DIFF1", "\u{0}\u{0}\u{4}"),
5258 (b"ANDOR", "ab`"),
5259 (b"ONE", "\u{0}\u{0}\u{7}"),
5260 ];
5261 for (op, want) in cases {
5262 assert_eq!(f.run(&[b"BITOP", op, b"d", b"a", b"b"]), ":3\r\n", "{op:?}");
5263 assert_eq!(
5264 f.run(&[b"GET", b"d"]),
5265 format!("$3\r\n{want}\r\n"),
5266 "{op:?}"
5267 );
5268 }
5269 assert_eq!(f.run(&[b"BITOP", b"NOT", b"d", b"a"]), ":3\r\n");
5271 assert_eq!(f.raw(&[b"GET", b"d"]), b"$3\r\n\x9e\x9d\x9c\r\n".to_vec());
5272
5273 assert_eq!(f.run(&[b"BITOP", b"AND", b"d", b"a", b"gone"]), ":3\r\n");
5276 assert_eq!(f.run(&[b"GET", b"d"]), "$3\r\n\u{0}\u{0}\u{0}\r\n");
5277
5278 f.run(&[b"SET", b"dest", b"x"]);
5281 assert_eq!(f.run(&[b"BITOP", b"AND", b"dest", b"g1", b"g2"]), ":0\r\n");
5282 assert_eq!(f.run(&[b"EXISTS", b"dest"]), ":0\r\n");
5283 }
5284
5285 #[test]
5287 fn bitop_names_the_operation_in_its_own_complaints() {
5288 let mut f = Fixture::new();
5289 f.run(&[b"SET", b"a", b"abc"]);
5290 assert_eq!(
5291 f.run(&[b"BITOP", b"nope", b"d", b"a"]),
5292 "-ERR syntax error\r\n"
5293 );
5294 assert_eq!(
5295 f.run(&[b"BITOP", b"NOT", b"d", b"a", b"a"]),
5296 "-ERR BITOP NOT must be called with a single source key.\r\n"
5297 );
5298 for op in [&b"DIFF"[..], b"DIFF1", b"ANDOR"] {
5299 assert_eq!(
5300 f.run(&[b"BITOP", op, b"d", b"a"]),
5301 format!(
5302 "-ERR BITOP {} must be called with at least two source keys.\r\n",
5303 String::from_utf8_lossy(op)
5304 )
5305 );
5306 }
5307 f.run(&[b"LPUSH", b"l", b"x"]);
5308 assert_eq!(
5309 f.run(&[b"BITOP", b"AND", b"d", b"a", b"l"]),
5310 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
5311 );
5312 }
5313
5314 #[test]
5316 fn bitfield_reads_and_writes_packed_fields() {
5317 let mut f = Fixture::new();
5318 assert_eq!(f.run(&[b"BITFIELD", b"bf"]), "*0\r\n");
5319 assert_eq!(f.run(&[b"EXISTS", b"bf"]), ":0\r\n");
5320
5321 assert_eq!(
5322 f.run(&[
5323 b"BITFIELD",
5324 b"bf",
5325 b"INCRBY",
5326 b"u2",
5327 b"100",
5328 b"1",
5329 b"GET",
5330 b"u4",
5331 b"0"
5332 ]),
5333 "*2\r\n:1\r\n:0\r\n"
5334 );
5335 assert_eq!(f.run(&[b"STRLEN", b"bf"]), ":13\r\n");
5338
5339 assert_eq!(
5341 f.run(&[
5342 b"BITFIELD",
5343 b"bf",
5344 b"SET",
5345 b"u8",
5346 b"#0",
5347 b"255",
5348 b"GET",
5349 b"u8",
5350 b"#0"
5351 ]),
5352 "*2\r\n:0\r\n:255\r\n"
5353 );
5354
5355 assert_eq!(
5356 f.run(&[
5357 b"BITFIELD",
5358 b"bf",
5359 b"OVERFLOW",
5360 b"SAT",
5361 b"INCRBY",
5362 b"i8",
5363 b"0",
5364 b"120",
5365 b"INCRBY",
5366 b"i8",
5367 b"0",
5368 b"120"
5369 ]),
5370 "*2\r\n:119\r\n:127\r\n"
5371 );
5372 assert_eq!(
5373 f.run(&[
5374 b"BITFIELD",
5375 b"bf2",
5376 b"OVERFLOW",
5377 b"FAIL",
5378 b"INCRBY",
5379 b"u2",
5380 b"0",
5381 b"5"
5382 ]),
5383 "*1\r\n$-1\r\n"
5384 );
5385 assert_eq!(
5386 f.run(&[
5387 b"BITFIELD",
5388 b"bf3",
5389 b"OVERFLOW",
5390 b"WRAP",
5391 b"INCRBY",
5392 b"u2",
5393 b"0",
5394 b"5"
5395 ]),
5396 "*1\r\n:1\r\n"
5397 );
5398 assert_eq!(
5399 f.run(&[b"BITFIELD", b"bf3", b"GET", b"i64", b"0"]),
5400 "*1\r\n:4611686018427387904\r\n"
5401 );
5402 }
5403
5404 #[test]
5410 fn a_bad_bitfield_subcommand_leaves_the_key_alone() {
5411 let mut f = Fixture::new();
5412 let bad_type = "-ERR Invalid bitfield type. Use something like i16 u8. Note that u64 is not supported but i64 is.\r\n";
5413 assert_eq!(
5414 f.run(&[
5415 b"BITFIELD",
5416 b"bad",
5417 b"SET",
5418 b"u8",
5419 b"0",
5420 b"1",
5421 b"GET",
5422 b"u99",
5423 b"0"
5424 ]),
5425 bad_type
5426 );
5427 assert_eq!(f.run(&[b"EXISTS", b"bad"]), ":0\r\n");
5428 assert_eq!(
5429 f.run(&[b"BITFIELD", b"bad", b"GET", b"u64", b"0"]),
5430 bad_type
5431 );
5432 assert_eq!(
5433 f.run(&[b"BITFIELD", b"bad", b"GET"]),
5434 "-ERR syntax error\r\n"
5435 );
5436 assert_eq!(
5437 f.run(&[b"BITFIELD", b"bad", b"NOPE", b"u8", b"0"]),
5438 "-ERR syntax error\r\n"
5439 );
5440 assert_eq!(
5441 f.run(&[b"BITFIELD", b"bad", b"OVERFLOW"]),
5442 "-ERR syntax error\r\n"
5443 );
5444 assert_eq!(
5445 f.run(&[
5446 b"BITFIELD",
5447 b"bad",
5448 b"OVERFLOW",
5449 b"NOPE",
5450 b"GET",
5451 b"u8",
5452 b"0"
5453 ]),
5454 "-ERR Invalid OVERFLOW type specified\r\n"
5455 );
5456 assert_eq!(
5457 f.run(&[b"BITFIELD", b"bad", b"SET", b"u8", b"0", b"notanum"]),
5458 "-ERR value is not an integer or out of range\r\n"
5459 );
5460 for at in [&b"#-1"[..], b"abc"] {
5461 assert_eq!(
5462 f.run(&[b"BITFIELD", b"bad", b"GET", b"u8", at]),
5463 "-ERR bit offset is not an integer or out of range\r\n"
5464 );
5465 }
5466 }
5467
5468 #[test]
5470 fn bitfield_ro_answers_gets_and_refuses_the_rest() {
5471 let mut f = Fixture::new();
5472 f.run(&[b"SET", b"n", b"123"]);
5473 assert_eq!(
5474 f.run(&[b"BITFIELD_RO", b"n", b"GET", b"u8", b"0"]),
5475 "*1\r\n:49\r\n"
5476 );
5477 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"n"]), "$3\r\nint\r\n");
5479
5480 assert_eq!(
5482 f.run(&[
5483 b"BITFIELD_RO",
5484 b"n",
5485 b"OVERFLOW",
5486 b"SAT",
5487 b"GET",
5488 b"u8",
5489 b"0"
5490 ]),
5491 "*1\r\n:49\r\n"
5492 );
5493 for sub in [&b"SET"[..], b"INCRBY"] {
5494 assert_eq!(
5495 f.run(&[b"BITFIELD_RO", b"n", sub, b"u8", b"0", b"1"]),
5496 "-ERR BITFIELD_RO only supports the GET subcommand\r\n"
5497 );
5498 }
5499
5500 assert_eq!(
5501 f.run(&[b"BITFIELD_RO", b"gone", b"GET", b"u8", b"100"]),
5502 "*1\r\n:0\r\n"
5503 );
5504 assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
5505 }
5506
5507 #[test]
5509 fn an_offset_off_the_end_of_the_world_is_refused() {
5510 let mut f = Fixture::new();
5511 let bad = "-ERR bit offset is not an integer or out of range\r\n";
5512 for arg in [&b"abc"[..], b"-1", b"4294967296"] {
5513 assert_eq!(f.run(&[b"SETBIT", b"k", arg, b"1"]), bad);
5514 assert_eq!(f.run(&[b"GETBIT", b"k", arg]), bad);
5515 }
5516 for arg in [&b"2"[..], b"-1"] {
5517 assert_eq!(
5518 f.run(&[b"BITPOS", b"k", arg]),
5519 "-ERR The bit argument must be 1 or 0.\r\n"
5520 );
5521 }
5522 assert_eq!(
5523 f.run(&[b"BITPOS", b"k", b"abc"]),
5524 "-ERR value is not an integer or out of range\r\n"
5525 );
5526 assert_eq!(
5527 f.run(&[b"BITPOS", b"k", b"0", b"5", b"BIT"]),
5528 "-ERR value is not an integer or out of range\r\n"
5529 );
5530 let bad_bit = "-ERR bit is not an integer or out of range\r\n";
5531 assert_eq!(f.run(&[b"SETBIT", b"k", b"0", b"2"]), bad_bit);
5532 assert_eq!(f.run(&[b"SETBIT", b"k", b"0", b"abc"]), bad_bit);
5533 }
5534
5535 #[test]
5537 fn every_bitmap_command_says_wrongtype() {
5538 let mut f = Fixture::new();
5539 f.run(&[b"LPUSH", b"l", b"x"]);
5540 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
5541 let cases: &[&[&[u8]]] = &[
5542 &[b"SETBIT", b"l", b"0", b"1"],
5543 &[b"GETBIT", b"l", b"0"],
5544 &[b"BITCOUNT", b"l"],
5545 &[b"BITPOS", b"l", b"1"],
5546 &[b"BITOP", b"AND", b"d", b"l"],
5547 &[b"BITFIELD", b"l", b"GET", b"u8", b"0"],
5548 &[b"BITFIELD_RO", b"l", b"GET", b"u8", b"0"],
5549 ];
5550 for case in cases {
5551 assert_eq!(f.run(case), wrong, "{:?}", case[0]);
5552 }
5553 }
5554
5555 #[test]
5558 fn a_sketch_is_added_to_and_counted() {
5559 let mut f = Fixture::new();
5560 assert_eq!(f.run(&[b"PFADD", b"h"]), ":1\r\n");
5562 assert_eq!(f.run(&[b"PFADD", b"h"]), ":0\r\n");
5563 assert_eq!(f.run(&[b"PFCOUNT", b"h"]), ":0\r\n");
5564 assert_eq!(f.run(&[b"STRLEN", b"h"]), ":18\r\n");
5565 assert_eq!(f.run(&[b"TYPE", b"h"]), "+string\r\n");
5568 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"h"]), "$3\r\nraw\r\n");
5569
5570 assert_eq!(f.run(&[b"PFADD", b"h", b"a", b"b", b"c"]), ":1\r\n");
5571 assert_eq!(f.run(&[b"PFADD", b"h", b"a"]), ":0\r\n");
5572 assert_eq!(f.run(&[b"PFCOUNT", b"h"]), ":3\r\n");
5573 }
5574
5575 #[test]
5576 fn the_bytes_of_a_sketch_are_the_ones_a_real_server_writes() {
5577 let mut f = Fixture::new();
5578 f.run(&[b"PFADD", b"h", b"a", b"b", b"c"]);
5579 let want = b"HYLL\x01\0\0\0\0\0\0\0\0\0\0\x80\x60\xf3\x80\x50\xb1\x84\x4b\xfb\x80\x42\x5a";
5581 let mut reply = b"$27\r\n".to_vec();
5582 reply.extend_from_slice(want);
5583 reply.extend_from_slice(b"\r\n");
5584 assert_eq!(f.raw(&[b"GET", b"h"]), reply);
5585 }
5586
5587 #[test]
5588 fn counting_several_keys_counts_their_union() {
5589 let mut f = Fixture::new();
5590 f.run(&[b"PFADD", b"a", b"x", b"y"]);
5591 f.run(&[b"PFADD", b"b", b"y", b"z"]);
5592 assert_eq!(f.run(&[b"PFCOUNT", b"a"]), ":2\r\n");
5593 assert_eq!(f.run(&[b"PFCOUNT", b"a", b"b"]), ":3\r\n");
5594 assert_eq!(f.run(&[b"PFCOUNT", b"gone"]), ":0\r\n");
5597 assert_eq!(f.run(&[b"PFCOUNT", b"a", b"gone"]), ":2\r\n");
5598 assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
5599 }
5600
5601 #[test]
5602 fn a_merge_keeps_what_the_destination_had() {
5603 let mut f = Fixture::new();
5604 f.run(&[b"PFADD", b"a", b"x", b"y"]);
5605 f.run(&[b"PFADD", b"b", b"z"]);
5606 assert_eq!(f.run(&[b"PFMERGE", b"d", b"a", b"b"]), "+OK\r\n");
5607 assert_eq!(f.run(&[b"PFCOUNT", b"d"]), ":3\r\n");
5608 f.run(&[b"PFADD", b"c", b"w"]);
5610 assert_eq!(f.run(&[b"PFMERGE", b"d", b"c"]), "+OK\r\n");
5611 assert_eq!(f.run(&[b"PFCOUNT", b"d"]), ":4\r\n");
5612 assert_eq!(f.run(&[b"PFMERGE", b"fresh"]), "+OK\r\n");
5615 assert_eq!(f.run(&[b"PFCOUNT", b"fresh"]), ":0\r\n");
5616 }
5617
5618 #[test]
5619 fn the_debug_forms_answer_four_different_shapes() {
5620 let mut f = Fixture::new();
5621 f.run(&[b"PFADD", b"h", b"a", b"b", b"c"]);
5622 assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"h"]), "+sparse\r\n");
5623 assert_eq!(
5624 f.run(&[b"PFDEBUG", b"DECODE", b"h"]),
5625 "$44\r\nZ:8436 v:1,1 Z:4274 v:2,1 Z:3068 v:1,1 Z:603\r\n"
5626 );
5627 assert_eq!(f.run(&[b"PFDEBUG", b"TODENSE", b"h"]), ":1\r\n");
5628 assert_eq!(f.run(&[b"PFDEBUG", b"TODENSE", b"h"]), ":0\r\n");
5629 assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"h"]), "+dense\r\n");
5630 assert_eq!(f.run(&[b"STRLEN", b"h"]), ":12304\r\n");
5631 assert_eq!(f.run(&[b"PFCOUNT", b"h"]), ":3\r\n");
5632 assert_eq!(
5634 f.run(&[b"PFDEBUG", b"DECODE", b"h"]),
5635 "-ERR HLL encoding is not sparse\r\n"
5636 );
5637
5638 let reply = f.run(&[b"PFDEBUG", b"GETREG", b"h"]);
5640 assert!(reply.starts_with("*16384\r\n"), "{}", &reply[..16]);
5641 assert_eq!(reply.matches(":0\r\n").count(), 16381);
5642 assert_eq!(reply.matches(":1\r\n").count(), 2);
5643 assert_eq!(reply.matches(":2\r\n").count(), 1);
5644
5645 assert_eq!(f.run(&[b"PFSELFTEST"]), "+OK\r\n");
5646 }
5647
5648 #[test]
5649 fn a_string_that_is_not_a_sketch_is_refused_with_its_own_sentence() {
5650 let mut f = Fixture::new();
5651 f.run(&[b"SET", b"plain", b"not a sketch"]);
5652 let not_hll = "-WRONGTYPE Key is not a valid HyperLogLog string value.\r\n";
5653 assert_eq!(f.run(&[b"PFADD", b"plain", b"a"]), not_hll);
5654 assert_eq!(f.run(&[b"PFCOUNT", b"plain"]), not_hll);
5655 assert_eq!(f.run(&[b"PFMERGE", b"plain"]), not_hll);
5656 assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"plain"]), not_hll);
5657
5658 f.run(&[b"RPUSH", b"l", b"x"]);
5661 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
5662 assert_eq!(f.run(&[b"PFADD", b"l", b"a"]), wrong);
5663 assert_eq!(f.run(&[b"PFCOUNT", b"l"]), wrong);
5664 assert_eq!(f.run(&[b"PFMERGE", b"dest", b"l"]), wrong);
5665 assert_eq!(f.run(&[b"EXISTS", b"dest"]), ":0\r\n");
5666 assert_eq!(f.run(&[b"PFDEBUG", b"GETREG", b"l"]), wrong);
5667 }
5668
5669 #[test]
5670 fn pfdebug_has_its_own_complaints() {
5671 let mut f = Fixture::new();
5672 f.run(&[b"PFADD", b"h", b"a"]);
5673 assert_eq!(
5676 f.run(&[b"PFDEBUG", b"NOPE", b"h"]),
5677 "-ERR Unknown PFDEBUG subcommand 'NOPE'\r\n"
5678 );
5679 let gone = "-ERR The specified key does not exist\r\n";
5681 assert_eq!(f.run(&[b"PFDEBUG", b"GETREG", b"missing"]), gone);
5682 assert_eq!(f.run(&[b"PFDEBUG", b"DECODE", b"missing"]), gone);
5683 assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"missing"]), gone);
5684 assert_eq!(f.run(&[b"PFDEBUG", b"TODENSE", b"missing"]), gone);
5685 assert_eq!(
5686 f.run(&[b"PFDEBUG"]),
5687 "-ERR wrong number of arguments for 'pfdebug' command\r\n"
5688 );
5689 assert_eq!(
5690 f.run(&[b"PFSELFTEST", b"x"]),
5691 "-ERR wrong number of arguments for 'pfselftest' command\r\n"
5692 );
5693 }
5694
5695 #[test]
5696 fn a_sketch_whose_opcodes_do_not_add_up_says_so() {
5697 let mut f = Fixture::new();
5698 f.run(&[b"PFADD", b"h", b"a", b"b", b"c"]);
5699 let reply = f.raw(&[b"GET", b"h"]);
5702 let short = reply[5..reply.len() - 3].to_vec();
5703 f.run(&[b"SET", b"h", &short]);
5704 assert_eq!(
5705 f.run(&[b"PFCOUNT", b"h"]),
5706 "-INVALIDOBJ Corrupted HLL object detected\r\n"
5707 );
5708 }
5709
5710 #[test]
5711 fn a_sketch_survives_a_dump_and_a_restore_in_both_encodings() {
5712 let mut f = Fixture::new();
5713 f.run(&[b"PFADD", b"small", b"a", b"b", b"c"]);
5716 for i in 0..10_000u32 {
5717 let ele = format!("e{i}");
5718 f.run(&[b"PFADD", b"big", ele.as_bytes()]);
5719 }
5720 assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"small"]), "+sparse\r\n");
5721 assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"big"]), "+dense\r\n");
5722
5723 for key in [&b"small"[..], b"big"] {
5724 let mut copy = key.to_vec();
5725 copy.push(b'2');
5726 let bytes = payload(&f.raw(&[b"DUMP", key]));
5727 assert_eq!(f.run(&[b"RESTORE", ©, b"0", &bytes]), "+OK\r\n");
5728 assert_eq!(f.raw(&[b"GET", ©]), f.raw(&[b"GET", key]));
5731 assert_eq!(
5732 f.run(&[b"PFDEBUG", b"ENCODING", ©]),
5733 f.run(&[b"PFDEBUG", b"ENCODING", key])
5734 );
5735 assert_eq!(f.run(&[b"PFCOUNT", ©]), f.run(&[b"PFCOUNT", key]));
5736 }
5737 assert_eq!(f.run(&[b"PFCOUNT", b"small2"]), ":3\r\n");
5738 assert_eq!(f.run(&[b"STRLEN", b"big2"]), ":12304\r\n");
5739 }
5740
5741 fn bulks(parts: &[&str]) -> String {
5744 let mut s = format!("*{}\r\n", parts.len());
5745 for p in parts {
5746 s.push_str(&format!("${}\r\n{p}\r\n", p.len()));
5747 }
5748 s
5749 }
5750
5751 #[test]
5752 fn a_list_is_pushed_from_both_ends_and_the_left_one_reverses() {
5753 let mut f = Fixture::new();
5754 assert_eq!(f.run(&[b"LPUSH", b"k", b"a", b"b", b"c"]), ":3\r\n");
5758 assert_eq!(
5759 f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
5760 bulks(&["c", "b", "a"])
5761 );
5762 assert_eq!(f.run(&[b"RPUSH", b"k", b"d"]), ":4\r\n");
5763 assert_eq!(f.run(&[b"LLEN", b"k"]), ":4\r\n");
5764 assert_eq!(f.run(&[b"LPOP", b"k"]), "$1\r\nc\r\n");
5765 assert_eq!(f.run(&[b"RPOP", b"k"]), "$1\r\nd\r\n");
5766 assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["b", "a"]));
5767 assert_eq!(f.run(&[b"TYPE", b"k"]), "+list\r\n");
5768 }
5769
5770 #[test]
5771 fn the_x_pushes_refuse_to_bring_a_list_back_to_life() {
5772 let mut f = Fixture::new();
5773 assert_eq!(f.run(&[b"LPUSHX", b"k", b"a"]), ":0\r\n");
5774 assert_eq!(f.run(&[b"RPUSHX", b"k", b"a"]), ":0\r\n");
5775 assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
5776 f.run(&[b"RPUSH", b"k", b"a"]);
5777 assert_eq!(f.run(&[b"LPUSHX", b"k", b"z"]), ":2\r\n");
5778 assert_eq!(f.run(&[b"RPUSHX", b"k", b"y"]), ":3\r\n");
5779 assert_eq!(
5780 f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
5781 bulks(&["z", "a", "y"])
5782 );
5783 }
5784
5785 #[test]
5788 fn an_empty_pop_is_a_different_nothing_with_a_count_and_without() {
5789 let mut f = Fixture::new();
5790 assert_eq!(f.run(&[b"LPOP", b"nope"]), "$-1\r\n");
5791 assert_eq!(f.run(&[b"LPOP", b"nope", b"2"]), "*-1\r\n");
5792 assert_eq!(f.run(&[b"RPOP", b"nope"]), "$-1\r\n");
5793 assert_eq!(f.run(&[b"RPOP", b"nope", b"2"]), "*-1\r\n");
5794 f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
5795 assert_eq!(f.run(&[b"LPOP", b"k", b"0"]), "*0\r\n");
5798 assert_eq!(f.run(&[b"LPOP", b"k", b"1"]), bulks(&["a"]));
5799 assert_eq!(f.run(&[b"RPOP", b"k", b"9"]), bulks(&["c", "b"]));
5801 assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
5802 }
5803
5804 #[test]
5805 fn a_pop_count_has_its_own_sentence_and_a_third_argument_is_an_arity_error() {
5806 let mut f = Fixture::new();
5807 f.run(&[b"RPUSH", b"k", b"a"]);
5808 let range = "-ERR value is out of range, must be positive\r\n";
5809 assert_eq!(f.run(&[b"LPOP", b"k", b"-1"]), range);
5810 assert_eq!(f.run(&[b"LPOP", b"k", b"abc"]), range);
5811 assert_eq!(f.run(&[b"RPOP", b"k", b"-1"]), range);
5812 assert_eq!(
5815 f.run(&[b"LPOP", b"k", b"1", b"2"]),
5816 "-ERR wrong number of arguments for 'lpop' command\r\n"
5817 );
5818 assert_eq!(f.run(&[b"LLEN", b"k"]), ":1\r\n");
5819 }
5820
5821 #[test]
5822 fn a_range_takes_negative_ends_and_clamps_the_ones_that_run_off() {
5823 let mut f = Fixture::new();
5824 f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
5825 assert_eq!(
5826 f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
5827 bulks(&["a", "b", "c"])
5828 );
5829 assert_eq!(f.run(&[b"LRANGE", b"k", b"-2", b"-1"]), bulks(&["b", "c"]));
5830 assert_eq!(f.run(&[b"LRANGE", b"k", b"1", b"1"]), bulks(&["b"]));
5831 assert_eq!(f.run(&[b"LRANGE", b"k", b"5", b"10"]), "*0\r\n");
5832 assert_eq!(f.run(&[b"LRANGE", b"k", b"2", b"1"]), "*0\r\n");
5833 assert_eq!(
5834 f.run(&[b"LRANGE", b"k", b"-100", b"100"]),
5835 bulks(&["a", "b", "c"])
5836 );
5837 assert_eq!(f.run(&[b"LRANGE", b"nope", b"0", b"-1"]), "*0\r\n");
5840 assert_eq!(
5841 f.run(&[b"LRANGE", b"k", b"a", b"b"]),
5842 "-ERR value is not an integer or out of range\r\n"
5843 );
5844 }
5845
5846 #[test]
5847 fn an_index_reads_and_writes_from_whichever_end_is_nearer() {
5848 let mut f = Fixture::new();
5849 f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
5850 assert_eq!(f.run(&[b"LINDEX", b"k", b"0"]), "$1\r\na\r\n");
5851 assert_eq!(f.run(&[b"LINDEX", b"k", b"-1"]), "$1\r\nc\r\n");
5852 assert_eq!(f.run(&[b"LINDEX", b"k", b"99"]), "$-1\r\n");
5853 assert_eq!(f.run(&[b"LINDEX", b"nope", b"0"]), "$-1\r\n");
5854 assert_eq!(f.run(&[b"LSET", b"k", b"-1", b"z"]), "+OK\r\n");
5855 assert_eq!(
5856 f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
5857 bulks(&["a", "b", "z"])
5858 );
5859 assert_eq!(
5862 f.run(&[b"LSET", b"k", b"99", b"z"]),
5863 "-ERR index out of range\r\n"
5864 );
5865 assert_eq!(
5866 f.run(&[b"LSET", b"nope", b"0", b"z"]),
5867 "-ERR no such key\r\n"
5868 );
5869 }
5870
5871 #[test]
5872 fn linsert_says_three_things_with_one_signed_number() {
5873 let mut f = Fixture::new();
5874 assert_eq!(
5877 f.run(&[b"LINSERT", b"nope", b"BEFORE", b"a", b"x"]),
5878 ":0\r\n"
5879 );
5880 f.run(&[b"RPUSH", b"k", b"a", b"b"]);
5881 assert_eq!(f.run(&[b"LINSERT", b"k", b"before", b"a", b"X"]), ":3\r\n");
5882 assert_eq!(f.run(&[b"LINSERT", b"k", b"AFTER", b"b", b"Y"]), ":4\r\n");
5883 assert_eq!(
5884 f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
5885 bulks(&["X", "a", "b", "Y"])
5886 );
5887 assert_eq!(
5888 f.run(&[b"LINSERT", b"k", b"BEFORE", b"zz", b"x"]),
5889 ":-1\r\n"
5890 );
5891 assert_eq!(
5892 f.run(&[b"LINSERT", b"k", b"SIDEWAYS", b"a", b"x"]),
5893 "-ERR syntax error\r\n"
5894 );
5895 }
5896
5897 #[test]
5898 fn lrem_counts_in_three_directions_and_takes_the_key_when_it_empties() {
5899 let mut f = Fixture::new();
5900 f.run(&[b"RPUSH", b"k", b"a", b"b", b"a", b"c", b"a"]);
5901 assert_eq!(f.run(&[b"LREM", b"k", b"2", b"a"]), ":2\r\n");
5902 assert_eq!(
5903 f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
5904 bulks(&["b", "c", "a"])
5905 );
5906 assert_eq!(f.run(&[b"LREM", b"k", b"-1", b"a"]), ":1\r\n");
5907 assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["b", "c"]));
5908 assert_eq!(f.run(&[b"LREM", b"k", b"0", b"b"]), ":1\r\n");
5909 assert_eq!(f.run(&[b"LREM", b"k", b"0", b"c"]), ":1\r\n");
5910 assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
5911 assert_eq!(f.run(&[b"LREM", b"nope", b"0", b"a"]), ":0\r\n");
5912 }
5913
5914 #[test]
5915 fn ltrim_keeps_a_window_and_an_empty_one_deletes_the_key() {
5916 let mut f = Fixture::new();
5917 f.run(&[b"RPUSH", b"k", b"a", b"b", b"c", b"d"]);
5918 assert_eq!(f.run(&[b"LTRIM", b"k", b"1", b"-2"]), "+OK\r\n");
5919 assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["b", "c"]));
5920 assert_eq!(f.run(&[b"LTRIM", b"k", b"1", b"0"]), "+OK\r\n");
5923 assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
5924 assert_eq!(f.run(&[b"LTRIM", b"nope", b"0", b"-1"]), "+OK\r\n");
5925 }
5926
5927 #[test]
5928 fn lpos_walks_from_either_end_and_stops_where_it_is_told() {
5929 let mut f = Fixture::new();
5930 f.run(&[b"RPUSH", b"p", b"a", b"b", b"c", b"a", b"b", b"c", b"a"]);
5931 assert_eq!(f.run(&[b"LPOS", b"p", b"a"]), ":0\r\n");
5932 assert_eq!(f.run(&[b"LPOS", b"p", b"a", b"RANK", b"-1"]), ":6\r\n");
5933 assert_eq!(f.run(&[b"LPOS", b"p", b"a", b"RANK", b"2"]), ":3\r\n");
5934 assert_eq!(
5935 f.run(&[b"LPOS", b"p", b"a", b"COUNT", b"2"]),
5936 "*2\r\n:0\r\n:3\r\n"
5937 );
5938 assert_eq!(
5939 f.run(&[b"LPOS", b"p", b"a", b"RANK", b"-1", b"COUNT", b"0"]),
5940 "*3\r\n:6\r\n:3\r\n:0\r\n"
5941 );
5942 assert_eq!(
5945 f.run(&[b"LPOS", b"p", b"a", b"COUNT", b"0", b"MAXLEN", b"3"]),
5946 "*1\r\n:0\r\n"
5947 );
5948 assert_eq!(f.run(&[b"LPOS", b"p", b"zz"]), "$-1\r\n");
5951 assert_eq!(f.run(&[b"LPOS", b"p", b"zz", b"COUNT", b"0"]), "*0\r\n");
5952 assert_eq!(f.run(&[b"LPOS", b"nope", b"a"]), "$-1\r\n");
5953 assert_eq!(f.run(&[b"LPOS", b"nope", b"a", b"COUNT", b"2"]), "*0\r\n");
5954 }
5955
5956 #[test]
5957 fn lpos_words_its_three_mistakes_the_way_redis_does() {
5958 let mut f = Fixture::new();
5959 f.run(&[b"RPUSH", b"p", b"a"]);
5960 assert_eq!(
5963 f.run(&[b"LPOS", b"p", b"a", b"RANK", b"0"]),
5964 "-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"
5965 );
5966 assert_eq!(
5967 f.run(&[b"LPOS", b"p", b"a", b"COUNT", b"-1"]),
5968 "-ERR COUNT can't be negative\r\n"
5969 );
5970 assert_eq!(
5971 f.run(&[b"LPOS", b"p", b"a", b"MAXLEN", b"-1"]),
5972 "-ERR MAXLEN can't be negative\r\n"
5973 );
5974 assert_eq!(
5975 f.run(&[b"LPOS", b"p", b"a", b"RANK"]),
5976 "-ERR syntax error\r\n"
5977 );
5978 assert_eq!(
5979 f.run(&[b"LPOS", b"p", b"a", b"FOO", b"1"]),
5980 "-ERR syntax error\r\n"
5981 );
5982 }
5983
5984 #[test]
5985 fn a_move_takes_from_one_end_and_gives_to_another_even_on_one_key() {
5986 let mut f = Fixture::new();
5987 f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
5988 assert_eq!(f.run(&[b"RPOPLPUSH", b"k", b"d"]), "$1\r\nc\r\n");
5989 assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["a", "b"]));
5990 assert_eq!(f.run(&[b"LRANGE", b"d", b"0", b"-1"]), bulks(&["c"]));
5991 assert_eq!(
5992 f.run(&[b"LMOVE", b"k", b"d", b"LEFT", b"RIGHT"]),
5993 "$1\r\na\r\n"
5994 );
5995 assert_eq!(f.run(&[b"LRANGE", b"d", b"0", b"-1"]), bulks(&["c", "a"]));
5996 f.run(&[b"DEL", b"r"]);
5999 f.run(&[b"RPUSH", b"r", b"1", b"2", b"3"]);
6000 assert_eq!(f.run(&[b"RPOPLPUSH", b"r", b"r"]), "$1\r\n3\r\n");
6001 assert_eq!(
6002 f.run(&[b"LRANGE", b"r", b"0", b"-1"]),
6003 bulks(&["3", "1", "2"])
6004 );
6005 assert_eq!(
6006 f.run(&[b"LMOVE", b"nope", b"d", b"LEFT", b"LEFT"]),
6007 "$-1\r\n"
6008 );
6009 assert_eq!(
6010 f.run(&[b"LMOVE", b"r", b"d", b"LEFT", b"SIDEWAYS"]),
6011 "-ERR syntax error\r\n"
6012 );
6013 }
6014
6015 #[test]
6016 fn a_move_checks_the_destination_before_it_takes_anything() {
6017 let mut f = Fixture::new();
6018 f.run(&[b"RPUSH", b"k", b"a", b"b"]);
6019 f.run(&[b"SET", b"str", b"v"]);
6020 assert_eq!(
6021 f.run(&[b"LMOVE", b"k", b"str", b"LEFT", b"LEFT"]),
6022 "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
6023 );
6024 assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["a", "b"]));
6026 }
6027
6028 #[test]
6029 fn lmpop_answers_from_the_first_key_that_has_anything() {
6030 let mut f = Fixture::new();
6031 f.run(&[b"RPUSH", b"b", b"1", b"2", b"3"]);
6032 assert_eq!(
6035 f.run(&[b"LMPOP", b"2", b"a", b"b", b"LEFT", b"COUNT", b"2"]),
6036 "*2\r\n$1\r\nb\r\n*2\r\n$1\r\n1\r\n$1\r\n2\r\n"
6037 );
6038 assert_eq!(
6039 f.run(&[b"LMPOP", b"2", b"a", b"b", b"RIGHT"]),
6040 "*2\r\n$1\r\nb\r\n*1\r\n$1\r\n3\r\n"
6041 );
6042 assert_eq!(f.run(&[b"EXISTS", b"b"]), ":0\r\n");
6043 assert_eq!(f.run(&[b"LMPOP", b"2", b"a", b"b", b"LEFT"]), "*-1\r\n");
6046 }
6047
6048 #[test]
6049 fn lmpop_has_its_own_words_for_a_count_and_for_a_key_count() {
6050 let mut f = Fixture::new();
6051 f.run(&[b"RPUSH", b"k", b"a"]);
6052 assert_eq!(
6053 f.run(&[b"LMPOP", b"0", b"k", b"LEFT"]),
6054 "-ERR numkeys should be greater than 0\r\n"
6055 );
6056 assert_eq!(
6057 f.run(&[b"LMPOP", b"-1", b"k", b"LEFT"]),
6058 "-ERR numkeys should be greater than 0\r\n"
6059 );
6060 assert_eq!(
6061 f.run(&[b"LMPOP", b"1", b"k", b"LEFT", b"COUNT", b"0"]),
6062 "-ERR count should be greater than 0\r\n"
6063 );
6064 assert_eq!(
6067 f.run(&[b"LMPOP", b"3", b"k", b"LEFT"]),
6068 "-ERR syntax error\r\n"
6069 );
6070 assert_eq!(
6071 f.run(&[b"LMPOP", b"1", b"k", b"LEFT", b"COUNT", b"1", b"x"]),
6072 "-ERR syntax error\r\n"
6073 );
6074 assert_eq!(
6075 f.run(&[b"LMPOP", b"1", b"k", b"LEFT", b"FOO", b"1"]),
6076 "-ERR syntax error\r\n"
6077 );
6078 assert_eq!(
6079 f.run(&[b"LMPOP", b"1", b"k", b"SIDEWAYS"]),
6080 "-ERR syntax error\r\n"
6081 );
6082 assert_eq!(f.run(&[b"LLEN", b"k"]), ":1\r\n");
6083 }
6084
6085 #[test]
6086 fn every_list_command_says_wrongtype_and_writes_nothing() {
6087 let mut f = Fixture::new();
6088 f.run(&[b"SET", b"str", b"v"]);
6089 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
6090 for cmd in [
6091 &[b"LPUSH".as_slice(), b"str", b"a"][..],
6092 &[b"RPUSH", b"str", b"a"],
6093 &[b"LPUSHX", b"str", b"a"],
6094 &[b"RPUSHX", b"str", b"a"],
6095 &[b"LPOP", b"str"],
6096 &[b"LPOP", b"str", b"2"],
6097 &[b"RPOP", b"str"],
6098 &[b"LLEN", b"str"],
6099 &[b"LRANGE", b"str", b"0", b"-1"],
6100 &[b"LINDEX", b"str", b"0"],
6101 &[b"LSET", b"str", b"0", b"a"],
6102 &[b"LINSERT", b"str", b"BEFORE", b"a", b"b"],
6103 &[b"LREM", b"str", b"0", b"a"],
6104 &[b"LTRIM", b"str", b"0", b"-1"],
6105 &[b"LPOS", b"str", b"a"],
6106 &[b"LPOS", b"str", b"a", b"COUNT", b"0"],
6107 &[b"RPOPLPUSH", b"str", b"d"],
6108 &[b"LMOVE", b"str", b"d", b"LEFT", b"LEFT"],
6109 &[b"LMPOP", b"1", b"str", b"LEFT"],
6110 ] {
6111 assert_eq!(f.run(cmd), wrong, "{:?}", String::from_utf8_lossy(cmd[0]));
6112 }
6113 assert_eq!(f.run(&[b"GET", b"str"]), "$1\r\nv\r\n");
6114 assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
6115 }
6116
6117 #[test]
6121 fn a_timeout_has_three_ways_of_being_wrong() {
6122 let mut f = Fixture::new();
6123 let not_float = "-ERR timeout is not a float or out of range\r\n";
6124 let range = "-ERR timeout is out of range\r\n";
6125 for (bad, want) in [
6126 (&[b"BLPOP".as_slice(), b"k", b"abc"][..], not_float),
6127 (&[b"BLPOP", b"k", b"nan"], not_float),
6128 (&[b"BLPOP", b"k", b""], not_float),
6129 (&[b"BLPOP", b"k", b" 1"], not_float),
6132 (&[b"BLPOP", b"k", b"1 "], not_float),
6133 (&[b"BLPOP", b"k", b"-1"], "-ERR timeout is negative\r\n"),
6134 (&[b"BLPOP", b"k", b"-0.1"], "-ERR timeout is negative\r\n"),
6135 (&[b"BLPOP", b"k", b"1e400"], range),
6138 (&[b"BLPOP", b"k", b"inf"], range),
6139 (&[b"BLPOP", b"k", b"9999999999999999"], range),
6140 (&[b"BRPOP", b"k", b"abc"], not_float),
6141 (
6142 &[b"BLMOVE", b"a", b"b", b"LEFT", b"RIGHT", b"abc"],
6143 not_float,
6144 ),
6145 (
6146 &[b"BRPOPLPUSH", b"a", b"b", b"-1"],
6147 "-ERR timeout is negative\r\n",
6148 ),
6149 (&[b"BLMPOP", b"abc", b"1", b"k", b"LEFT"], not_float),
6150 ] {
6151 assert_eq!(f.run(bad), want, "for {bad:?}");
6152 }
6153 }
6154
6155 #[test]
6158 fn a_zero_timeout_waits_and_the_smallest_positive_one_does_not() {
6159 let mut f = Fixture::new();
6160 for timeout in [b"0".as_slice(), b"0.0", b"-0.0"] {
6161 let (flow, out) = f.flow(&[b"BLPOP", b"k", timeout]);
6162 assert_eq!(flow, Flow::Block, "for {timeout:?}");
6163 assert!(out.is_empty(), "for {timeout:?}");
6164 }
6165 let (flow, out) = f.flow(&[b"BLPOP", b"k", b"0.0000001"]);
6169 assert_eq!(flow, Flow::Block);
6170 assert!(out.is_empty());
6171 }
6172
6173 #[test]
6174 fn a_blocking_command_that_can_be_answered_answers_like_the_one_it_wraps() {
6175 let mut f = Fixture::new();
6176 f.run(&[b"RPUSH", b"L", b"a", b"b", b"c", b"d", b"e"]);
6177
6178 assert_eq!(
6181 f.flow(&[b"BLPOP", b"nope", b"L", b"0"]),
6182 (Flow::Continue, "*2\r\n$1\r\nL\r\n$1\r\na\r\n".to_owned())
6183 );
6184 assert_eq!(
6185 f.run(&[b"BRPOP", b"L", b"0"]),
6186 "*2\r\n$1\r\nL\r\n$1\r\ne\r\n"
6187 );
6188 assert_eq!(
6189 f.run(&[
6190 b"BLMPOP", b"0", b"2", b"nope", b"L", b"LEFT", b"COUNT", b"2"
6191 ]),
6192 "*2\r\n$1\r\nL\r\n*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
6193 );
6194 assert_eq!(
6195 f.run(&[b"BLMOVE", b"L", b"D", b"LEFT", b"RIGHT", b"0"]),
6196 "$1\r\nd\r\n"
6197 );
6198 assert_eq!(
6199 f.run(&[b"EXISTS", b"L"]),
6200 ":0\r\n",
6201 "and the key went with it"
6202 );
6203 assert_eq!(f.run(&[b"LRANGE", b"D", b"0", b"-1"]), "*1\r\n$1\r\nd\r\n");
6204 f.run(&[b"RPUSH", b"D", b"x"]);
6207 assert_eq!(f.run(&[b"BRPOPLPUSH", b"D", b"D", b"0"]), "$1\r\nx\r\n");
6208 assert_eq!(
6209 f.run(&[b"LRANGE", b"D", b"0", b"-1"]),
6210 "*2\r\n$1\r\nx\r\n$1\r\nd\r\n"
6211 );
6212 }
6213
6214 #[test]
6215 fn blmpop_reads_its_count_and_its_key_count_the_way_lmpop_does() {
6216 let mut f = Fixture::new();
6217 f.run(&[b"RPUSH", b"k", b"a"]);
6218 for (bad, want) in [
6219 (
6220 &[b"BLMPOP".as_slice(), b"0", b"0", b"k", b"LEFT"][..],
6221 "-ERR numkeys should be greater than 0\r\n",
6222 ),
6223 (
6224 &[b"BLMPOP", b"0", b"-1", b"k", b"LEFT"],
6225 "-ERR numkeys should be greater than 0\r\n",
6226 ),
6227 (
6230 &[b"BLMPOP", b"0", b"2", b"k", b"LEFT"],
6231 "-ERR syntax error\r\n",
6232 ),
6233 (
6234 &[b"BLMPOP", b"0", b"1", b"k", b"SIDEWAYS"],
6235 "-ERR syntax error\r\n",
6236 ),
6237 (
6238 &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT"],
6239 "-ERR syntax error\r\n",
6240 ),
6241 (
6242 &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT", b"2", b"x"],
6243 "-ERR syntax error\r\n",
6244 ),
6245 (
6248 &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT", b"0"],
6249 "-ERR count should be greater than 0\r\n",
6250 ),
6251 (
6252 &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT", b"abc"],
6253 "-ERR count should be greater than 0\r\n",
6254 ),
6255 ] {
6256 assert_eq!(f.run(bad), want, "for {bad:?}");
6257 }
6258 assert_eq!(f.run(&[b"LLEN", b"k"]), ":1\r\n", "and none of them popped");
6259 }
6260
6261 #[test]
6262 fn a_blocking_move_reads_its_directions_before_its_timeout() {
6263 let mut f = Fixture::new();
6264 assert_eq!(
6267 f.run(&[b"BLMOVE", b"a", b"b", b"UP", b"DOWN", b"abc"]),
6268 "-ERR syntax error\r\n"
6269 );
6270 assert_eq!(
6271 f.run(&[b"BLMOVE", b"a", b"b", b"LEFT", b"DOWN", b"0.05"]),
6272 "-ERR syntax error\r\n"
6273 );
6274 }
6275
6276 #[test]
6279 fn a_blocking_command_errors_on_a_wrong_type_rather_than_waiting_on_it() {
6280 let mut f = Fixture::new();
6281 f.run(&[b"SET", b"S", b"v"]);
6282 f.run(&[b"RPUSH", b"D", b"x"]);
6283 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
6284
6285 assert_eq!(f.run(&[b"BLPOP", b"S", b"0"]), wrong);
6286 assert_eq!(f.run(&[b"BLPOP", b"E", b"S", b"0"]), wrong);
6289 assert_eq!(f.run(&[b"BRPOP", b"S", b"0"]), wrong);
6290 assert_eq!(f.run(&[b"BLMPOP", b"0", b"1", b"S", b"LEFT"]), wrong);
6291 assert_eq!(f.run(&[b"BRPOPLPUSH", b"S", b"D", b"0"]), wrong);
6292 assert_eq!(f.run(&[b"BRPOPLPUSH", b"D", b"S", b"0"]), wrong);
6295 assert_eq!(f.run(&[b"LRANGE", b"D", b"0", b"-1"]), "*1\r\n$1\r\nx\r\n");
6296
6297 assert_eq!(
6301 f.flow(&[b"BLMOVE", b"E", b"S", b"LEFT", b"RIGHT", b"0.1"])
6302 .0,
6303 Flow::Block
6304 );
6305 }
6306
6307 #[test]
6311 fn churning_lists_does_not_grow_the_server() {
6312 let mut f = Fixture::new();
6313 let vals: Vec<Vec<u8>> = (0..200).map(|i| format!("v{i}").into_bytes()).collect();
6314 let args: Vec<&[u8]> = [&b"RPUSH"[..], &b"k"[..]]
6315 .into_iter()
6316 .chain(vals.iter().map(Vec::as_slice))
6317 .collect();
6318
6319 f.run(&args);
6320 f.run(&[b"DEL", b"k"]);
6321 f.server.compact_step();
6322 let after_first = f.server.memory_bytes();
6323
6324 for _ in 0..200 {
6325 f.run(&args);
6326 f.run(&[b"LTRIM", b"k", b"1", b"0"]);
6327 f.server.compact_step();
6328 }
6329 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
6330 assert!(
6331 f.server.memory_bytes() <= after_first * 2,
6332 "held {} after two hundred passes against {after_first} after one",
6333 f.server.memory_bytes()
6334 );
6335 }
6336
6337 #[test]
6340 fn a_sorted_set_takes_scores_and_gives_them_back() {
6341 let mut f = Fixture::new();
6342 assert_eq!(f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b"]), ":2\r\n");
6343 assert_eq!(f.run(&[b"ZADD", b"z", b"1", b"a", b"3", b"c"]), ":1\r\n");
6344 assert_eq!(f.run(&[b"ZCARD", b"z"]), ":3\r\n");
6345 assert_eq!(f.run(&[b"ZSCORE", b"z", b"b"]), "$1\r\n2\r\n");
6346 assert_eq!(f.run(&[b"ZSCORE", b"z", b"nope"]), "$-1\r\n");
6347 assert_eq!(f.run(&[b"ZSCORE", b"nokey", b"b"]), "$-1\r\n");
6348 assert_eq!(
6349 f.run(&[b"ZMSCORE", b"z", b"a", b"nope", b"c"]),
6350 "*3\r\n$1\r\n1\r\n$-1\r\n$1\r\n3\r\n"
6351 );
6352 assert_eq!(f.run(&[b"ZREM", b"z", b"a", b"nope"]), ":1\r\n");
6353 assert_eq!(f.run(&[b"ZCARD", b"z"]), ":2\r\n");
6354 assert_eq!(f.run(&[b"ZREM", b"z", b"b", b"c"]), ":2\r\n");
6356 assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
6357 }
6358
6359 #[test]
6360 fn a_score_is_a_double_on_resp3_and_digits_on_resp2() {
6361 let mut f = Fixture::new();
6362 f.run(&[b"ZADD", b"z", b"1.5", b"a", b"inf", b"b", b"-inf", b"c"]);
6363 assert_eq!(f.run(&[b"ZSCORE", b"z", b"a"]), "$3\r\n1.5\r\n");
6364 assert_eq!(f.run(&[b"ZSCORE", b"z", b"b"]), "$3\r\ninf\r\n");
6365 assert_eq!(f.run(&[b"ZSCORE", b"z", b"c"]), "$4\r\n-inf\r\n");
6366
6367 f.out = Out::new(Proto::Resp3);
6368 assert_eq!(f.run(&[b"ZSCORE", b"z", b"a"]), ",1.5\r\n");
6369 assert_eq!(f.run(&[b"ZSCORE", b"z", b"b"]), ",inf\r\n");
6370 assert_eq!(f.run(&[b"ZSCORE", b"z", b"c"]), ",-inf\r\n");
6371 assert_eq!(f.run(&[b"ZSCORE", b"z", b"nope"]), "_\r\n");
6372 }
6373
6374 #[test]
6375 fn the_zadd_options_gate_what_gets_written() {
6376 let mut f = Fixture::new();
6377 f.run(&[b"ZADD", b"z", b"5", b"a"]);
6378 assert_eq!(f.run(&[b"ZADD", b"z", b"NX", b"9", b"a"]), ":0\r\n");
6380 assert_eq!(f.run(&[b"ZSCORE", b"z", b"a"]), "$1\r\n5\r\n");
6381 assert_eq!(f.run(&[b"ZADD", b"z", b"XX", b"9", b"new"]), ":0\r\n");
6382 assert_eq!(f.run(&[b"EXISTS", b"z"]), ":1\r\n");
6383 assert_eq!(f.run(&[b"ZADD", b"z", b"GT", b"CH", b"3", b"a"]), ":0\r\n");
6385 assert_eq!(f.run(&[b"ZADD", b"z", b"GT", b"CH", b"7", b"a"]), ":1\r\n");
6386 assert_eq!(f.run(&[b"ZADD", b"z", b"LT", b"CH", b"9", b"a"]), ":0\r\n");
6387 assert_eq!(f.run(&[b"ZADD", b"z", b"1", b"a", b"1", b"b"]), ":1\r\n");
6389 assert_eq!(
6390 f.run(&[b"ZADD", b"z", b"CH", b"2", b"a", b"2", b"c"]),
6391 ":2\r\n"
6392 );
6393 }
6394
6395 #[test]
6396 fn zadd_incr_answers_a_score_or_nothing_at_all() {
6397 let mut f = Fixture::new();
6398 assert_eq!(f.run(&[b"ZADD", b"z", b"INCR", b"5", b"m"]), "$1\r\n5\r\n");
6399 assert_eq!(f.run(&[b"ZADD", b"z", b"INCR", b"2", b"m"]), "$1\r\n7\r\n");
6400 assert_eq!(
6403 f.run(&[b"ZADD", b"z", b"NX", b"INCR", b"2", b"m"]),
6404 "$-1\r\n"
6405 );
6406 assert_eq!(
6407 f.run(&[b"ZADD", b"z", b"XX", b"INCR", b"2", b"gone"]),
6408 "$-1\r\n"
6409 );
6410 assert_eq!(
6411 f.run(&[b"ZADD", b"z", b"GT", b"INCR", b"-1", b"m"]),
6412 "$-1\r\n"
6413 );
6414 assert_eq!(
6415 f.run(&[b"ZADD", b"z", b"GT", b"INCR", b"1", b"m"]),
6416 "$1\r\n8\r\n"
6417 );
6418 assert_eq!(f.run(&[b"ZINCRBY", b"z", b"2", b"m"]), "$2\r\n10\r\n");
6419 assert_eq!(f.run(&[b"ZINCRBY", b"z", b"1", b"fresh"]), "$1\r\n1\r\n");
6420 }
6421
6422 #[test]
6423 fn the_two_infinities_will_not_be_added_together() {
6424 let mut f = Fixture::new();
6425 f.run(&[b"ZADD", b"z", b"inf", b"m"]);
6426 let nan = "-ERR resulting score is not a number (NaN)\r\n";
6427 assert_eq!(f.run(&[b"ZINCRBY", b"z", b"-inf", b"m"]), nan);
6428 assert_eq!(f.run(&[b"ZADD", b"z", b"INCR", b"-inf", b"m"]), nan);
6429 assert_eq!(f.run(&[b"ZSCORE", b"z", b"m"]), "$3\r\ninf\r\n");
6430 assert_eq!(f.run(&[b"ZINCRBY", b"gone", b"1", b"m"]), "$1\r\n1\r\n");
6432 }
6433
6434 #[test]
6435 fn zadd_says_its_mistakes_the_way_redis_says_them() {
6436 let mut f = Fixture::new();
6437 assert_eq!(
6440 f.run(&[b"ZADD", b"z", b"NX", b"XX"]),
6441 "-ERR syntax error\r\n"
6442 );
6443 assert_eq!(
6444 f.run(&[b"ZADD", b"z", b"NX", b"XX", b"1", b"a"]),
6445 "-ERR XX and NX options at the same time are not compatible\r\n"
6446 );
6447 let gtlt = "-ERR GT, LT, and/or NX options at the same time are not compatible\r\n";
6448 assert_eq!(f.run(&[b"ZADD", b"z", b"NX", b"GT", b"1", b"a"]), gtlt);
6449 assert_eq!(f.run(&[b"ZADD", b"z", b"GT", b"LT", b"1", b"a"]), gtlt);
6450 assert_eq!(
6451 f.run(&[b"ZADD", b"z", b"INCR", b"1", b"a", b"2", b"b"]),
6452 "-ERR INCR option supports a single increment-element pair\r\n"
6453 );
6454 assert_eq!(
6456 f.run(&[b"ZADD", b"z", b"1", b"a", b"2"]),
6457 "-ERR syntax error\r\n"
6458 );
6459 assert_eq!(
6461 f.run(&[b"ZADD", b"z", b"1", b"a", b"nonsense", b"b"]),
6462 "-ERR value is not a valid float\r\n"
6463 );
6464 assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
6465 }
6466
6467 #[test]
6468 fn a_rank_says_where_a_member_sits_from_either_end() {
6469 let mut f = Fixture::new();
6470 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
6471 assert_eq!(f.run(&[b"ZRANK", b"z", b"a"]), ":0\r\n");
6472 assert_eq!(f.run(&[b"ZRANK", b"z", b"c"]), ":2\r\n");
6473 assert_eq!(f.run(&[b"ZREVRANK", b"z", b"c"]), ":0\r\n");
6474 assert_eq!(f.run(&[b"ZREVRANK", b"z", b"a"]), ":2\r\n");
6475 assert_eq!(
6477 f.run(&[b"ZRANK", b"z", b"b", b"WITHSCORE"]),
6478 "*2\r\n:1\r\n$1\r\n2\r\n"
6479 );
6480 assert_eq!(f.run(&[b"ZRANK", b"z", b"nope"]), "$-1\r\n");
6481 assert_eq!(f.run(&[b"ZRANK", b"z", b"nope", b"WITHSCORE"]), "*-1\r\n");
6482 assert_eq!(f.run(&[b"ZRANK", b"nokey", b"a", b"WITHSCORE"]), "*-1\r\n");
6483 assert_eq!(
6486 f.run(&[b"ZRANK", b"z", b"b", b"bogus"]),
6487 "-ERR syntax error\r\n"
6488 );
6489 assert_eq!(
6490 f.run(&[b"ZREVRANK", b"z", b"b", b"WITHSCORE", b"more"]),
6491 "-ERR wrong number of arguments for 'zrevrank' command\r\n"
6492 );
6493 }
6494
6495 #[test]
6496 fn the_two_counts_read_their_two_kinds_of_bound() {
6497 let mut f = Fixture::new();
6498 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
6499 assert_eq!(f.run(&[b"ZCOUNT", b"z", b"-inf", b"+inf"]), ":3\r\n");
6500 assert_eq!(f.run(&[b"ZCOUNT", b"z", b"2", b"3"]), ":2\r\n");
6501 assert_eq!(f.run(&[b"ZCOUNT", b"z", b"(1", b"3"]), ":2\r\n");
6502 assert_eq!(f.run(&[b"ZCOUNT", b"z", b"(1", b"(3"]), ":1\r\n");
6503 assert_eq!(f.run(&[b"ZCOUNT", b"nokey", b"-inf", b"+inf"]), ":0\r\n");
6504 assert_eq!(
6505 f.run(&[b"ZCOUNT", b"z", b"bogus", b"3"]),
6506 "-ERR min or max is not a float\r\n"
6507 );
6508
6509 f.run(&[b"ZADD", b"l", b"0", b"a", b"0", b"b", b"0", b"c"]);
6510 assert_eq!(f.run(&[b"ZLEXCOUNT", b"l", b"-", b"+"]), ":3\r\n");
6511 assert_eq!(f.run(&[b"ZLEXCOUNT", b"l", b"[a", b"(c"]), ":2\r\n");
6512 assert_eq!(f.run(&[b"ZLEXCOUNT", b"l", b"(a", b"+"]), ":2\r\n");
6513 assert_eq!(
6516 f.run(&[b"ZLEXCOUNT", b"l", b"a", b"c"]),
6517 "-ERR min or max not valid string range item\r\n"
6518 );
6519 }
6520
6521 #[test]
6527 fn one_range_command_selects_by_rank_or_score_or_name() {
6528 let mut f = Fixture::new();
6529 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
6530 assert_eq!(
6531 f.run(&[b"ZRANGE", b"z", b"0", b"-1"]),
6532 "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
6533 );
6534 assert_eq!(
6535 f.run(&[b"ZRANGE", b"z", b"-2", b"-1"]),
6536 "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
6537 );
6538 assert_eq!(f.run(&[b"ZRANGE", b"z", b"5", b"9"]), "*0\r\n");
6539 assert_eq!(f.run(&[b"ZRANGE", b"nokey", b"0", b"-1"]), "*0\r\n");
6540 assert_eq!(
6543 f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"REV"]),
6544 "*3\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\na\r\n"
6545 );
6546 assert_eq!(
6547 f.run(&[b"ZRANGE", b"z", b"(1", b"+inf", b"BYSCORE"]),
6548 "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
6549 );
6550 assert_eq!(
6553 f.run(&[b"ZRANGE", b"z", b"+inf", b"(1", b"BYSCORE", b"REV"]),
6554 "*2\r\n$1\r\nc\r\n$1\r\nb\r\n"
6555 );
6556 assert_eq!(
6557 f.run(&[b"ZRANGE", b"z", b"-", b"+", b"BYLEX"]),
6558 "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
6559 );
6560 assert_eq!(
6561 f.run(&[b"ZRANGE", b"z", b"+", b"-", b"BYLEX", b"REV"]),
6562 "*3\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\na\r\n"
6563 );
6564 }
6565
6566 #[test]
6569 fn the_older_range_spellings_name_their_high_end_first() {
6570 let mut f = Fixture::new();
6571 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
6572 assert_eq!(
6573 f.run(&[b"ZREVRANGE", b"z", b"0", b"-1"]),
6574 "*3\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\na\r\n"
6575 );
6576 assert_eq!(
6577 f.run(&[b"ZREVRANGE", b"z", b"0", b"0", b"WITHSCORES"]),
6578 "*2\r\n$1\r\nc\r\n$1\r\n3\r\n"
6579 );
6580 assert_eq!(
6581 f.run(&[b"ZRANGEBYSCORE", b"z", b"(1", b"3"]),
6582 "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
6583 );
6584 assert_eq!(
6585 f.run(&[b"ZREVRANGEBYSCORE", b"z", b"3", b"(1"]),
6586 "*2\r\n$1\r\nc\r\n$1\r\nb\r\n"
6587 );
6588 assert_eq!(f.run(&[b"ZREVRANGEBYSCORE", b"z", b"(1", b"3"]), "*0\r\n");
6592 assert_eq!(
6593 f.run(&[b"ZRANGEBYLEX", b"z", b"[a", b"(c"]),
6594 "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
6595 );
6596 assert_eq!(
6597 f.run(&[b"ZREVRANGEBYLEX", b"z", b"(c", b"[a"]),
6598 "*2\r\n$1\r\nb\r\n$1\r\na\r\n"
6599 );
6600 for cmd in [
6603 &[b"ZREVRANGE".as_slice(), b"z", b"0", b"-1", b"BYSCORE"][..],
6604 &[b"ZRANGEBYSCORE", b"z", b"1", b"3", b"REV"],
6605 &[b"ZRANGEBYLEX", b"z", b"[a", b"[c", b"BYLEX"],
6606 ] {
6607 assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{:?}", cmd[0]);
6608 }
6609 }
6610
6611 #[test]
6614 fn limit_and_withscores_are_read_by_all_of_them_and_refused_afterwards() {
6615 let mut f = Fixture::new();
6616 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
6617 assert_eq!(
6618 f.run(&[
6619 b"ZRANGE", b"z", b"-inf", b"+inf", b"BYSCORE", b"LIMIT", b"1", b"1"
6620 ]),
6621 "*1\r\n$1\r\nb\r\n"
6622 );
6623 assert_eq!(
6625 f.run(&[
6626 b"ZRANGE", b"z", b"-inf", b"+inf", b"BYSCORE", b"LIMIT", b"-1", b"2"
6627 ]),
6628 "*0\r\n"
6629 );
6630 assert_eq!(
6631 f.run(&[
6632 b"ZRANGE", b"z", b"-inf", b"+inf", b"BYSCORE", b"LIMIT", b"0", b"-1"
6633 ]),
6634 "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
6635 );
6636 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";
6638 assert_eq!(
6639 f.run(&[
6640 b"ZRANGEBYSCORE",
6641 b"z",
6642 b"1",
6643 b"3",
6644 b"WITHSCORES",
6645 b"LIMIT",
6646 b"0",
6647 b"2"
6648 ]),
6649 both
6650 );
6651 assert_eq!(
6652 f.run(&[
6653 b"ZRANGEBYSCORE",
6654 b"z",
6655 b"1",
6656 b"3",
6657 b"LIMIT",
6658 b"0",
6659 b"2",
6660 b"WITHSCORES"
6661 ]),
6662 both
6663 );
6664 let needs_by = "-ERR syntax error, LIMIT is only supported in combination with either BYSCORE or BYLEX\r\n";
6667 assert_eq!(
6668 f.run(&[
6669 b"ZREVRANGE",
6670 b"z",
6671 b"0",
6672 b"-1",
6673 b"WITHSCORES",
6674 b"LIMIT",
6675 b"0",
6676 b"1"
6677 ]),
6678 needs_by
6679 );
6680 assert_eq!(
6681 f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"LIMIT", b"0", b"1"]),
6682 needs_by
6683 );
6684 let not_bylex = "-ERR syntax error, WITHSCORES not supported in combination with BYLEX\r\n";
6685 assert_eq!(
6686 f.run(&[b"ZRANGE", b"z", b"-", b"+", b"BYLEX", b"WITHSCORES"]),
6687 not_bylex
6688 );
6689 assert_eq!(
6690 f.run(&[b"ZRANGEBYLEX", b"z", b"[a", b"[c", b"WITHSCORES"]),
6691 not_bylex
6692 );
6693 for cmd in [
6696 &[
6697 b"ZRANGE".as_slice(),
6698 b"z",
6699 b"0",
6700 b"-1",
6701 b"BYSCORE",
6702 b"BYLEX",
6703 ][..],
6704 &[b"ZRANGE", b"z", b"0", b"-1", b"junk"],
6705 &[b"ZRANGEBYSCORE", b"z", b"1", b"3", b"LIMIT", b"0"],
6706 ] {
6707 assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
6708 }
6709 assert_eq!(
6710 f.run(&[b"ZRANGEBYSCORE", b"z", b"bad", b"3"]),
6711 "-ERR min or max is not a float\r\n"
6712 );
6713 assert_eq!(
6714 f.run(&[b"ZRANGEBYLEX", b"z", b"a", b"[c"]),
6715 "-ERR min or max not valid string range item\r\n"
6716 );
6717 assert_eq!(
6718 f.run(&[b"ZRANGEBYSCORE", b"z", b"1", b"3", b"LIMIT", b"a", b"2"]),
6719 "-ERR value is not an integer or out of range\r\n"
6720 );
6721 }
6722
6723 #[test]
6726 fn withscores_nests_on_resp3_and_flattens_on_resp2() {
6727 let mut f = Fixture::new();
6728 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
6729 assert_eq!(
6730 f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"WITHSCORES"]),
6731 "*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"
6732 );
6733 f.out = Out::new(Proto::Resp3);
6734 assert_eq!(
6735 f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"WITHSCORES"]),
6736 "*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"
6737 );
6738 assert_eq!(
6739 f.run(&[b"ZRANGE", b"z", b"0", b"-1"]),
6740 "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
6741 );
6742 }
6743
6744 #[test]
6746 fn a_range_store_writes_the_window_into_another_key() {
6747 let mut f = Fixture::new();
6748 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
6749 assert_eq!(f.run(&[b"ZRANGESTORE", b"d", b"z", b"0", b"-1"]), ":3\r\n");
6750 assert_eq!(f.run(&[b"ZRANGESTORE", b"d", b"z", b"5", b"9"]), ":0\r\n");
6753 assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
6754 assert_eq!(
6755 f.run(&[b"ZRANGESTORE", b"d", b"z", b"(1", b"+inf", b"BYSCORE"]),
6756 ":2\r\n"
6757 );
6758 assert_eq!(
6759 f.run(&[b"ZRANGE", b"d", b"0", b"-1", b"WITHSCORES"]),
6760 "*4\r\n$1\r\nb\r\n$1\r\n2\r\n$1\r\nc\r\n$1\r\n3\r\n"
6761 );
6762 assert_eq!(f.run(&[b"ZRANGESTORE", b"z", b"z", b"1", b"2"]), ":2\r\n");
6765 assert_eq!(
6766 f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"WITHSCORES"]),
6767 "*4\r\n$1\r\nb\r\n$1\r\n2\r\n$1\r\nc\r\n$1\r\n3\r\n"
6768 );
6769 assert_eq!(
6772 f.run(&[b"ZRANGESTORE", b"d", b"z", b"0", b"-1", b"WITHSCORES"]),
6773 "-ERR syntax error\r\n"
6774 );
6775 }
6776
6777 #[test]
6780 fn the_three_removals_share_their_window_with_the_reads() {
6781 let mut f = Fixture::new();
6782 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
6783 assert_eq!(f.run(&[b"ZREMRANGEBYRANK", b"z", b"0", b"0"]), ":1\r\n");
6784 assert_eq!(
6785 f.run(&[b"ZRANGE", b"z", b"0", b"-1"]),
6786 "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
6787 );
6788 assert_eq!(
6789 f.run(&[b"ZREMRANGEBYSCORE", b"z", b"(2", b"+inf"]),
6790 ":1\r\n"
6791 );
6792 assert_eq!(f.run(&[b"ZRANGE", b"z", b"0", b"-1"]), "*1\r\n$1\r\nb\r\n");
6793 assert_eq!(f.run(&[b"ZREMRANGEBYLEX", b"z", b"-", b"+"]), ":1\r\n");
6795 assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
6796 assert_eq!(
6797 f.run(&[b"ZREMRANGEBYRANK", b"nokey", b"0", b"-1"]),
6798 ":0\r\n"
6799 );
6800 assert_eq!(
6801 f.run(&[b"ZREMRANGEBYRANK", b"z", b"0", b"x"]),
6802 "-ERR value is not an integer or out of range\r\n"
6803 );
6804 }
6805
6806 #[test]
6808 fn the_three_algebra_commands_combine_scores_and_order_the_answer_once() {
6809 let mut f = Fixture::new();
6810 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
6811 f.run(&[b"ZADD", b"y", b"10", b"b", b"20", b"d"]);
6812 assert_eq!(
6813 f.run(&[b"ZUNION", b"2", b"z", b"y"]),
6814 "*4\r\n$1\r\na\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\nd\r\n"
6815 );
6816 assert_eq!(
6819 f.run(&[b"ZUNION", b"2", b"z", b"y", b"WITHSCORES"]),
6820 "*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"
6821 );
6822 assert_eq!(
6823 f.run(&[
6824 b"ZUNION",
6825 b"2",
6826 b"z",
6827 b"y",
6828 b"WEIGHTS",
6829 b"2",
6830 b"3",
6831 b"WITHSCORES"
6832 ]),
6833 "*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"
6834 );
6835 assert_eq!(
6836 f.run(&[
6837 b"ZUNION",
6838 b"2",
6839 b"z",
6840 b"y",
6841 b"AGGREGATE",
6842 b"MIN",
6843 b"WITHSCORES"
6844 ]),
6845 "*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"
6846 );
6847 assert_eq!(
6848 f.run(&[
6849 b"ZUNION",
6850 b"2",
6851 b"z",
6852 b"y",
6853 b"AGGREGATE",
6854 b"MAX",
6855 b"WITHSCORES"
6856 ]),
6857 "*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"
6858 );
6859 assert_eq!(
6860 f.run(&[b"ZINTER", b"2", b"z", b"y", b"WITHSCORES"]),
6861 "*2\r\n$1\r\nb\r\n$2\r\n12\r\n"
6862 );
6863 assert_eq!(
6864 f.run(&[b"ZDIFF", b"2", b"z", b"y", b"WITHSCORES"]),
6865 "*4\r\n$1\r\na\r\n$1\r\n1\r\n$1\r\nc\r\n$1\r\n3\r\n"
6866 );
6867 assert_eq!(f.run(&[b"ZUNION", b"1", b"nokey"]), "*0\r\n");
6868 f.run(&[b"SADD", b"p", b"a", b"d"]);
6871 assert_eq!(
6872 f.run(&[b"ZUNION", b"2", b"z", b"p", b"WITHSCORES"]),
6873 "*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"
6874 );
6875 for cmd in [
6878 &[
6879 b"ZDIFF".as_slice(),
6880 b"2",
6881 b"z",
6882 b"y",
6883 b"WEIGHTS",
6884 b"1",
6885 b"1",
6886 ][..],
6887 &[b"ZDIFF", b"2", b"z", b"y", b"AGGREGATE", b"MIN"],
6888 ] {
6889 assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
6890 }
6891 }
6892
6893 #[test]
6895 fn the_algebra_counts_its_keys_and_says_so_when_the_count_is_wrong() {
6896 let mut f = Fixture::new();
6897 f.run(&[b"ZADD", b"z", b"1", b"a"]);
6898 f.run(&[b"ZADD", b"y", b"2", b"b"]);
6899 assert_eq!(
6901 f.run(&[b"ZUNION", b"0", b"z"]),
6902 "-ERR at least 1 input key is needed for 'zunion' command\r\n"
6903 );
6904 assert_eq!(
6905 f.run(&[b"ZUNION", b"-1", b"z"]),
6906 "-ERR at least 1 input key is needed for 'zunion' command\r\n"
6907 );
6908 assert_eq!(
6909 f.run(&[b"ZINTERCARD", b"0", b"z"]),
6910 "-ERR at least 1 input key is needed for 'zintercard' command\r\n"
6911 );
6912 assert_eq!(
6915 f.run(&[b"ZUNION", b"3", b"z", b"y"]),
6916 "-ERR syntax error\r\n"
6917 );
6918 assert_eq!(
6919 f.run(&[b"ZUNION", b"x", b"z"]),
6920 "-ERR value is not an integer or out of range\r\n"
6921 );
6922 assert_eq!(
6925 f.run(&[b"ZUNION", b"2", b"z", b"y", b"WEIGHTS", b"1"]),
6926 "-ERR syntax error\r\n"
6927 );
6928 assert_eq!(
6929 f.run(&[b"ZUNION", b"2", b"z", b"y", b"WEIGHTS", b"a", b"b"]),
6930 "-ERR weight value is not a float\r\n"
6931 );
6932 assert_eq!(
6933 f.run(&[b"ZUNION", b"2", b"z", b"y", b"AGGREGATE", b"NOPE"]),
6934 "-ERR syntax error\r\n"
6935 );
6936 }
6937
6938 #[test]
6940 fn the_algebra_stores_answer_a_count_and_delete_an_empty_destination() {
6941 let mut f = Fixture::new();
6942 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
6943 f.run(&[b"ZADD", b"y", b"10", b"b", b"20", b"d"]);
6944 assert_eq!(f.run(&[b"ZUNIONSTORE", b"d", b"2", b"z", b"y"]), ":4\r\n");
6945 assert_eq!(
6946 f.run(&[b"ZRANGE", b"d", b"0", b"-1", b"WITHSCORES"]),
6947 "*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"
6948 );
6949 assert_eq!(f.run(&[b"ZINTERSTORE", b"d", b"2", b"z", b"y"]), ":1\r\n");
6950 assert_eq!(f.run(&[b"ZDIFFSTORE", b"d", b"2", b"z", b"y"]), ":2\r\n");
6951 assert_eq!(
6954 f.run(&[b"ZINTERSTORE", b"d", b"2", b"z", b"nokey"]),
6955 ":0\r\n"
6956 );
6957 assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
6958 assert_eq!(f.run(&[b"ZUNIONSTORE", b"z", b"2", b"z", b"y"]), ":4\r\n");
6960 assert_eq!(f.run(&[b"ZCARD", b"z"]), ":4\r\n");
6961 for cmd in [
6962 &[
6963 b"ZUNIONSTORE".as_slice(),
6964 b"d",
6965 b"2",
6966 b"z",
6967 b"y",
6968 b"WITHSCORES",
6969 ][..],
6970 &[
6971 b"ZDIFFSTORE",
6972 b"d",
6973 b"2",
6974 b"z",
6975 b"y",
6976 b"WEIGHTS",
6977 b"1",
6978 b"1",
6979 ],
6980 ] {
6981 assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
6982 }
6983 }
6984
6985 #[test]
6987 fn intercard_counts_and_stops_at_its_limit() {
6988 let mut f = Fixture::new();
6989 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
6990 f.run(&[b"ZADD", b"y", b"10", b"b", b"20", b"c", b"30", b"d"]);
6991 assert_eq!(f.run(&[b"ZINTERCARD", b"2", b"z", b"y"]), ":2\r\n");
6992 assert_eq!(
6994 f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"0"]),
6995 ":2\r\n"
6996 );
6997 assert_eq!(
6998 f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"1"]),
6999 ":1\r\n"
7000 );
7001 let bad = "-ERR LIMIT can't be negative\r\n";
7004 assert_eq!(
7005 f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"-1"]),
7006 bad
7007 );
7008 assert_eq!(
7009 f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"x"]),
7010 bad
7011 );
7012 for cmd in [
7013 &[b"ZINTERCARD".as_slice(), b"3", b"z", b"y"][..],
7014 &[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT"],
7015 &[b"ZINTERCARD", b"2", b"z", b"y", b"junk", b"1"],
7016 ] {
7017 assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
7018 }
7019 }
7020
7021 #[test]
7023 fn a_draw_answers_one_member_or_an_array_of_them() {
7024 let mut f = Fixture::new();
7025 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
7026 assert_eq!(f.run(&[b"ZRANDMEMBER", b"nokey"]), "$-1\r\n");
7029 assert_eq!(f.run(&[b"ZRANDMEMBER", b"nokey", b"3"]), "*0\r\n");
7030 assert_eq!(f.run(&[b"ZRANDMEMBER", b"z", b"0"]), "*0\r\n");
7031 assert!(f.run(&[b"ZRANDMEMBER", b"z"]).starts_with("$1\r\n"));
7032 let all = f.run(&[b"ZRANDMEMBER", b"z", b"10"]);
7035 assert!(all.starts_with("*3\r\n"), "{all}");
7036 for m in ["a", "b", "c"] {
7037 assert!(all.contains(m), "{all}");
7038 }
7039 assert!(
7042 f.run(&[b"ZRANDMEMBER", b"z", b"-5"]).starts_with("*5\r\n"),
7043 "five draws with replacement"
7044 );
7045 assert!(
7046 f.run(&[b"ZRANDMEMBER", b"z", b"2", b"WITHSCORES"])
7047 .starts_with("*4\r\n"),
7048 "two pairs, flat on RESP2"
7049 );
7050 f.out = Out::new(Proto::Resp3);
7051 let got = f.run(&[b"ZRANDMEMBER", b"z", b"2", b"WITHSCORES"]);
7052 assert!(got.starts_with("*2\r\n*2\r\n"), "{got}");
7053 assert_eq!(f.run(&[b"ZRANDMEMBER", b"nokey"]), "_\r\n");
7054 f.out = Out::new(Proto::Resp2);
7055 assert_eq!(
7056 f.run(&[b"ZRANDMEMBER", b"z", b"2", b"junk"]),
7057 "-ERR syntax error\r\n"
7058 );
7059 assert_eq!(
7060 f.run(&[b"ZRANDMEMBER", b"z", b"x"]),
7061 "-ERR value is not an integer or out of range\r\n"
7062 );
7063 }
7064
7065 #[test]
7067 fn a_sorted_set_scan_answers_pairs_of_strings_on_both_protocols() {
7068 let mut f = Fixture::new();
7069 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
7070 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";
7071 assert_eq!(f.run(&[b"ZSCAN", b"z", b"0"]), all);
7072 assert_eq!(f.run(&[b"ZSCAN", b"z", b"0", b"COUNT", b"10"]), all);
7073 assert_eq!(
7074 f.run(&[b"ZSCAN", b"z", b"0", b"MATCH", b"a*"]),
7075 "*2\r\n$1\r\n0\r\n*2\r\n$1\r\na\r\n$1\r\n1\r\n"
7076 );
7077 assert_eq!(
7078 f.run(&[b"ZSCAN", b"nokey", b"0"]),
7079 "*2\r\n$1\r\n0\r\n*0\r\n"
7080 );
7081 f.out = Out::new(Proto::Resp3);
7084 assert_eq!(f.run(&[b"ZSCAN", b"z", b"0"]), all);
7085 f.out = Out::new(Proto::Resp2);
7086 assert_eq!(
7087 f.run(&[b"ZSCAN", b"z", b"0", b"NOVALUES"]),
7088 "-ERR NOVALUES option can only be used in HSCAN\r\n"
7089 );
7090 assert_eq!(f.run(&[b"ZSCAN", b"z", b"-1"]), "-ERR invalid cursor\r\n");
7091 assert_eq!(
7092 f.run(&[b"ZSCAN", b"z", b"0", b"COUNT", b"0"]),
7093 "-ERR syntax error\r\n"
7094 );
7095 }
7096
7097 #[test]
7099 fn a_sorted_set_pop_changes_shape_when_it_is_given_a_count() {
7100 let mut f = Fixture::new();
7101 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
7102 assert_eq!(f.run(&[b"ZPOPMIN", b"z"]), "*2\r\n$1\r\na\r\n$1\r\n1\r\n");
7104 assert_eq!(f.run(&[b"ZPOPMAX", b"z"]), "*2\r\n$1\r\nc\r\n$1\r\n3\r\n");
7105 f.run(&[b"ZADD", b"z", b"1", b"a", b"3", b"c"]);
7106 assert_eq!(
7108 f.run(&[b"ZPOPMIN", b"z", b"2"]),
7109 "*4\r\n$1\r\na\r\n$1\r\n1\r\n$1\r\nb\r\n$1\r\n2\r\n"
7110 );
7111 assert_eq!(f.run(&[b"ZPOPMIN", b"nokey"]), "*0\r\n");
7114 assert_eq!(f.run(&[b"ZPOPMIN", b"nokey", b"2"]), "*0\r\n");
7115 assert_eq!(f.run(&[b"ZPOPMIN", b"z", b"0"]), "*0\r\n");
7116 assert_eq!(
7118 f.run(&[b"ZPOPMIN", b"z", b"9"]),
7119 "*2\r\n$1\r\nc\r\n$1\r\n3\r\n"
7120 );
7121 assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
7122
7123 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b"]);
7124 f.out = Out::new(Proto::Resp3);
7125 assert_eq!(f.run(&[b"ZPOPMIN", b"z"]), "*2\r\n$1\r\na\r\n,1\r\n");
7126 assert_eq!(
7127 f.run(&[b"ZPOPMIN", b"z", b"1"]),
7128 "*1\r\n*2\r\n$1\r\nb\r\n,2\r\n"
7129 );
7130 f.out = Out::new(Proto::Resp2);
7131 let bad = "-ERR value is out of range, must be positive\r\n";
7134 assert_eq!(f.run(&[b"ZPOPMIN", b"z", b"x"]), bad);
7135 assert_eq!(f.run(&[b"ZPOPMIN", b"z", b"-1"]), bad);
7136 assert_eq!(
7137 f.run(&[b"ZPOPMIN", b"z", b"1", b"2"]),
7138 "-ERR syntax error\r\n"
7139 );
7140 }
7141
7142 #[test]
7144 fn a_multi_key_pop_names_the_key_that_answered_and_nests_its_pairs() {
7145 let mut f = Fixture::new();
7146 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
7147 assert_eq!(
7148 f.run(&[b"ZMPOP", b"2", b"nokey", b"z", b"MIN"]),
7149 "*2\r\n$1\r\nz\r\n*1\r\n*2\r\n$1\r\na\r\n$1\r\n1\r\n"
7150 );
7151 assert_eq!(
7154 f.run(&[b"ZMPOP", b"1", b"z", b"MAX", b"COUNT", b"2"]),
7155 "*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"
7156 );
7157 assert_eq!(f.run(&[b"ZMPOP", b"1", b"nokey", b"MIN"]), "*-1\r\n");
7159 f.out = Out::new(Proto::Resp3);
7160 assert_eq!(f.run(&[b"ZMPOP", b"1", b"nokey", b"MIN"]), "_\r\n");
7161 f.out = Out::new(Proto::Resp2);
7162 let numkeys = "-ERR numkeys should be greater than 0\r\n";
7163 for bad in [
7164 &[b"ZMPOP".as_slice(), b"0", b"z", b"MIN"][..],
7165 &[b"ZMPOP", b"-1", b"z", b"MIN"],
7166 &[b"ZMPOP", b"x", b"z", b"MIN"],
7167 ] {
7168 assert_eq!(f.run(bad), numkeys, "{:?}", bad[1]);
7169 }
7170 let count = "-ERR count should be greater than 0\r\n";
7171 for bad in [
7172 &[b"ZMPOP".as_slice(), b"1", b"z", b"MIN", b"COUNT", b"0"][..],
7173 &[b"ZMPOP", b"1", b"z", b"MIN", b"COUNT", b"-1"],
7174 &[b"ZMPOP", b"1", b"z", b"MIN", b"COUNT", b"x"],
7175 ] {
7176 assert_eq!(f.run(bad), count, "{:?}", bad[5]);
7177 }
7178 let syntax = "-ERR syntax error\r\n";
7179 for bad in [
7180 &[b"ZMPOP".as_slice(), b"2", b"z", b"MIN"][..],
7183 &[b"ZMPOP", b"1", b"z", b"SIDEWAYS"],
7184 &[b"ZMPOP", b"1", b"z", b"MIN", b"junk"],
7185 &[b"ZMPOP", b"1", b"z", b"MIN", b"COUNT", b"1", b"junk"],
7186 ] {
7187 assert_eq!(f.run(bad), syntax, "{bad:?}");
7188 }
7189 }
7190
7191 #[test]
7194 fn the_sorted_set_pops_that_wait_answer_like_the_ones_they_wrap() {
7195 let mut f = Fixture::new();
7196 f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
7197 assert_eq!(
7198 f.flow(&[b"BZPOPMIN", b"nokey", b"z", b"0"]),
7199 (
7200 Flow::Continue,
7201 "*3\r\n$1\r\nz\r\n$1\r\na\r\n$1\r\n1\r\n".to_owned()
7202 )
7203 );
7204 assert_eq!(
7205 f.run(&[b"BZPOPMAX", b"z", b"0"]),
7206 "*3\r\n$1\r\nz\r\n$1\r\nc\r\n$1\r\n3\r\n"
7207 );
7208 f.run(&[b"ZADD", b"z", b"1", b"a", b"3", b"c"]);
7209 assert_eq!(
7210 f.run(&[
7211 b"BZMPOP", b"0", b"2", b"nokey", b"z", b"MIN", b"COUNT", b"2"
7212 ]),
7213 "*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"
7214 );
7215 f.out = Out::new(Proto::Resp3);
7216 assert_eq!(
7217 f.run(&[b"BZPOPMIN", b"z", b"0"]),
7218 "*3\r\n$1\r\nz\r\n$1\r\nc\r\n,3\r\n"
7219 );
7220 f.out = Out::new(Proto::Resp2);
7221 assert_eq!(
7223 f.flow(&[b"BZPOPMIN", b"z", b"0"]),
7224 (Flow::Block, String::new())
7225 );
7226 assert_eq!(
7227 f.flow(&[b"BZMPOP", b"0", b"1", b"z", b"MIN"]),
7228 (Flow::Block, String::new())
7229 );
7230 assert_eq!(
7233 f.run(&[b"BZMPOP", b"abc", b"0", b"z", b"MIN"]),
7234 "-ERR timeout is not a float or out of range\r\n"
7235 );
7236 assert_eq!(
7237 f.run(&[b"BZMPOP", b"0", b"0", b"z", b"MIN"]),
7238 "-ERR numkeys should be greater than 0\r\n"
7239 );
7240 assert_eq!(
7241 f.run(&[b"BZPOPMIN", b"z", b"-1"]),
7242 "-ERR timeout is negative\r\n"
7243 );
7244 }
7245
7246 #[test]
7250 fn a_parked_sorted_set_client_waits_for_a_member_and_not_for_a_key() {
7251 let mut f = Fixture::new();
7252 assert_eq!(f.flow(&[b"BZPOPMIN", b"z", b"0"]).0, Flow::Block);
7253 assert_eq!(f.server.waiters().len(), 1);
7254 f.run(&[b"SET", b"z", b"v"]);
7257 let mut out = Out::new(Proto::Resp2);
7258 assert!(!f.server.serve_waiter(0, 0, &mut out));
7259 assert!(out.as_slice().is_empty());
7260 f.run(&[b"DEL", b"z"]);
7261 f.run(&[b"ZADD", b"z", b"5", b"m"]);
7262 assert!(f.server.serve_waiter(0, 0, &mut out));
7263 assert_eq!(
7264 core::str::from_utf8(out.as_slice()).expect("ascii"),
7265 "*3\r\n$1\r\nz\r\n$1\r\nm\r\n$1\r\n5\r\n"
7266 );
7267 assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
7270 }
7271
7272 #[test]
7273 fn every_sorted_set_command_says_wrongtype_and_writes_nothing() {
7274 let mut f = Fixture::new();
7275 f.run(&[b"SET", b"s", b"v"]);
7276 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
7277 for cmd in [
7278 &[b"ZADD".as_slice(), b"s", b"1", b"a"][..],
7279 &[b"ZINCRBY", b"s", b"1", b"a"],
7280 &[b"ZCARD", b"s"],
7281 &[b"ZSCORE", b"s", b"a"],
7282 &[b"ZMSCORE", b"s", b"a"],
7283 &[b"ZREM", b"s", b"a"],
7284 &[b"ZRANK", b"s", b"a"],
7285 &[b"ZREVRANK", b"s", b"a"],
7286 &[b"ZCOUNT", b"s", b"1", b"2"],
7287 &[b"ZLEXCOUNT", b"s", b"-", b"+"],
7288 &[b"ZRANGE", b"s", b"0", b"-1"],
7289 &[b"ZREVRANGE", b"s", b"0", b"-1"],
7290 &[b"ZRANGEBYSCORE", b"s", b"1", b"2"],
7291 &[b"ZREVRANGEBYSCORE", b"s", b"2", b"1"],
7292 &[b"ZRANGEBYLEX", b"s", b"-", b"+"],
7293 &[b"ZREVRANGEBYLEX", b"s", b"+", b"-"],
7294 &[b"ZRANGESTORE", b"d", b"s", b"0", b"-1"],
7295 &[b"ZREMRANGEBYRANK", b"s", b"0", b"-1"],
7296 &[b"ZREMRANGEBYSCORE", b"s", b"1", b"2"],
7297 &[b"ZREMRANGEBYLEX", b"s", b"-", b"+"],
7298 &[b"ZUNION", b"1", b"s"],
7299 &[b"ZINTER", b"1", b"s"],
7300 &[b"ZDIFF", b"1", b"s"],
7301 &[b"ZUNIONSTORE", b"d", b"1", b"s"],
7302 &[b"ZINTERSTORE", b"d", b"1", b"s"],
7303 &[b"ZDIFFSTORE", b"d", b"1", b"s"],
7304 &[b"ZINTERCARD", b"1", b"s"],
7305 &[b"ZRANDMEMBER", b"s"],
7306 &[b"ZSCAN", b"s", b"0"],
7307 &[b"ZPOPMIN", b"s"],
7308 &[b"ZPOPMAX", b"s", b"2"],
7309 &[b"ZMPOP", b"1", b"s", b"MIN"],
7310 &[b"BZPOPMIN", b"s", b"0"],
7311 &[b"BZPOPMAX", b"s", b"0"],
7312 &[b"BZMPOP", b"0", b"1", b"s", b"MIN"],
7313 ] {
7314 assert_eq!(f.run(cmd), wrong, "{:?}", cmd[0]);
7315 }
7316 assert_eq!(f.run(&[b"GET", b"s"]), "$1\r\nv\r\n");
7317 }
7318
7319 #[test]
7323 fn churning_sorted_sets_does_not_grow_the_server() {
7324 let mut f = Fixture::new();
7325 let members: Vec<Vec<u8>> = (0..200).map(|i| format!("m{i}").into_bytes()).collect();
7326 let scores: Vec<Vec<u8>> = (0..200).map(|i| format!("{i}").into_bytes()).collect();
7327 let mut args: Vec<&[u8]> = vec![b"ZADD", b"z"];
7328 for i in 0..200 {
7329 args.push(&scores[i]);
7330 args.push(&members[i]);
7331 }
7332
7333 f.run(&args);
7334 f.run(&[b"DEL", b"z"]);
7335 f.server.compact_step();
7336 let after_first = f.server.memory_bytes();
7337
7338 for _ in 0..200 {
7339 f.run(&args);
7340 f.run(&[b"DEL", b"z"]);
7341 f.server.compact_step();
7342 }
7343 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
7344 assert!(
7345 f.server.memory_bytes() <= after_first * 2,
7346 "held {} after two hundred passes against {after_first} after one",
7347 f.server.memory_bytes()
7348 );
7349 }
7350
7351 fn sicily(f: &mut Fixture) {
7359 f.run(&[
7360 b"GEOADD",
7361 b"Sicily",
7362 b"13.361389",
7363 b"38.115556",
7364 b"Palermo",
7365 b"15.087269",
7366 b"37.502669",
7367 b"Catania",
7368 ]);
7369 f.run(&[
7370 b"GEOADD",
7371 b"Sicily",
7372 b"13.583333",
7373 b"37.316667",
7374 b"Agrigento",
7375 ]);
7376 }
7377
7378 #[test]
7379 fn places_go_in_as_scores_and_come_back_as_positions() {
7380 let mut f = Fixture::new();
7381 assert_eq!(
7382 f.run(&[
7383 b"GEOADD",
7384 b"Sicily",
7385 b"13.361389",
7386 b"38.115556",
7387 b"Palermo",
7388 b"15.087269",
7389 b"37.502669",
7390 b"Catania"
7391 ]),
7392 ":2\r\n"
7393 );
7394 assert_eq!(f.run(&[b"TYPE", b"Sicily"]), "+zset\r\n");
7398 assert_eq!(
7399 f.run(&[b"ZSCORE", b"Sicily", b"Palermo"]),
7400 "$16\r\n3479099956230698\r\n"
7401 );
7402 assert_eq!(
7403 f.run(&[b"GEOPOS", b"Sicily", b"Palermo", b"NonExisting"]),
7404 "*2\r\n*2\r\n$18\r\n13.361389338970184\r\n$16\r\n38.1155563954963\r\n*-1\r\n"
7405 );
7406 assert_eq!(
7407 f.run(&[
7408 b"GEOHASH",
7409 b"Sicily",
7410 b"Palermo",
7411 b"Catania",
7412 b"NonExisting"
7413 ]),
7414 "*3\r\n$11\r\nsqc8b49rny0\r\n$11\r\nsqdtr74hyu0\r\n$-1\r\n"
7415 );
7416 assert_eq!(f.run(&[b"GEOPOS", b"nokey", b"a"]), "*1\r\n*-1\r\n");
7420 assert_eq!(f.run(&[b"GEOHASH", b"nokey", b"a"]), "*1\r\n$-1\r\n");
7421 }
7422
7423 #[test]
7424 fn a_distance_comes_back_with_four_places_in_whatever_unit_was_asked_for() {
7425 let mut f = Fixture::new();
7426 sicily(&mut f);
7427 assert_eq!(
7428 f.run(&[b"GEODIST", b"Sicily", b"Palermo", b"Catania"]),
7429 "$11\r\n166274.1516\r\n"
7430 );
7431 assert_eq!(
7432 f.run(&[b"GEODIST", b"Sicily", b"Palermo", b"Catania", b"km"]),
7433 "$8\r\n166.2742\r\n"
7434 );
7435 assert_eq!(
7436 f.run(&[b"GEODIST", b"Sicily", b"Palermo", b"Catania", b"mi"]),
7437 "$8\r\n103.3182\r\n"
7438 );
7439 assert_eq!(
7443 f.run(&[b"GEODIST", b"Sicily", b"Palermo", b"Foo"]),
7444 "$-1\r\n"
7445 );
7446 assert_eq!(f.run(&[b"GEODIST", b"nokey", b"a", b"b"]), "$-1\r\n");
7447 assert_eq!(
7448 f.run(&[b"GEODIST", b"nokey", b"a", b"b", b"parsecs"]),
7449 "-ERR unsupported unit provided. please use M, KM, FT, MI\r\n"
7450 );
7451 assert_eq!(
7452 f.run(&[b"GEODIST", b"Sicily", b"a", b"b", b"km", b"extra"]),
7453 "-ERR syntax error\r\n"
7454 );
7455 }
7456
7457 #[test]
7458 fn a_search_finds_what_is_inside_it_nearest_first() {
7459 let mut f = Fixture::new();
7460 sicily(&mut f);
7461 let all = "*3\r\n$7\r\nCatania\r\n$9\r\nAgrigento\r\n$7\r\nPalermo\r\n";
7462 assert_eq!(
7463 f.run(&[
7464 b"GEOSEARCH",
7465 b"Sicily",
7466 b"FROMLONLAT",
7467 b"15",
7468 b"37",
7469 b"BYRADIUS",
7470 b"200",
7471 b"km",
7472 b"ASC"
7473 ]),
7474 all
7475 );
7476 assert_eq!(
7479 f.run(&[b"GEORADIUS", b"Sicily", b"15", b"37", b"200", b"km", b"ASC"]),
7480 all
7481 );
7482 assert_eq!(
7483 f.run(&[
7484 b"GEORADIUS_RO",
7485 b"Sicily",
7486 b"15",
7487 b"37",
7488 b"200",
7489 b"km",
7490 b"ASC"
7491 ]),
7492 all
7493 );
7494 assert_eq!(
7497 f.run(&[
7498 b"GEORADIUS",
7499 b"Sicily",
7500 b"15",
7501 b"37",
7502 b"200",
7503 b"km",
7504 b"DESC",
7505 b"COUNT",
7506 b"1"
7507 ]),
7508 "*1\r\n$7\r\nPalermo\r\n"
7509 );
7510 assert_eq!(
7511 f.run(&[
7512 b"GEORADIUS",
7513 b"Sicily",
7514 b"15",
7515 b"37",
7516 b"200",
7517 b"km",
7518 b"COUNT",
7519 b"1"
7520 ]),
7521 "*1\r\n$7\r\nCatania\r\n"
7522 );
7523 let empty = "*0\r\n";
7526 assert_eq!(
7527 f.run(&[
7528 b"GEOSEARCH",
7529 b"Sicily",
7530 b"FROMLONLAT",
7531 b"15",
7532 b"37",
7533 b"BYRADIUS",
7534 b"1",
7535 b"km"
7536 ]),
7537 empty
7538 );
7539 assert_eq!(
7540 f.run(&[
7541 b"GEOSEARCH",
7542 b"nokey",
7543 b"FROMLONLAT",
7544 b"15",
7545 b"37",
7546 b"BYRADIUS",
7547 b"1",
7548 b"km"
7549 ]),
7550 empty
7551 );
7552 assert_eq!(
7553 f.run(&[b"GEORADIUSBYMEMBER", b"nokey", b"m", b"1", b"km"]),
7554 empty
7555 );
7556 }
7557
7558 #[test]
7559 fn a_search_centred_on_a_member_starts_from_where_that_member_is() {
7560 let mut f = Fixture::new();
7561 sicily(&mut f);
7562 assert_eq!(
7563 f.run(&[b"GEORADIUSBYMEMBER", b"Sicily", b"Agrigento", b"100", b"km"]),
7564 "*2\r\n$9\r\nAgrigento\r\n$7\r\nPalermo\r\n"
7565 );
7566 let with_dist = "*2\r\n*2\r\n$9\r\nAgrigento\r\n$6\r\n0.0000\r\n*2\r\n$7\r\nPalermo\r\n$7\r\n90.9778\r\n";
7569 assert_eq!(
7570 f.run(&[
7571 b"GEORADIUSBYMEMBER_RO",
7572 b"Sicily",
7573 b"Agrigento",
7574 b"100",
7575 b"km",
7576 b"WITHDIST"
7577 ]),
7578 with_dist
7579 );
7580 assert_eq!(
7581 f.run(&[
7582 b"GEOSEARCH",
7583 b"Sicily",
7584 b"FROMMEMBER",
7585 b"Agrigento",
7586 b"BYRADIUS",
7587 b"100",
7588 b"km",
7589 b"ASC",
7590 b"WITHDIST"
7591 ]),
7592 with_dist
7593 );
7594 assert_eq!(
7595 f.run(&[b"GEORADIUSBYMEMBER", b"Sicily", b"Nowhere", b"100", b"km"]),
7596 "-ERR could not decode requested zset member\r\n"
7597 );
7598 }
7599
7600 #[test]
7601 fn a_box_search_reports_the_distance_the_hash_and_the_coordinates() {
7602 let mut f = Fixture::new();
7603 sicily(&mut f);
7604 assert_eq!(
7608 f.run(&[
7609 b"GEOSEARCH",
7610 b"Sicily",
7611 b"FROMLONLAT",
7612 b"15",
7613 b"37",
7614 b"BYBOX",
7615 b"400",
7616 b"400",
7617 b"km",
7618 b"ASC",
7619 b"WITHCOORD",
7620 b"WITHDIST",
7621 b"WITHHASH"
7622 ]),
7623 "*3\r\n*4\r\n$7\r\nCatania\r\n$7\r\n56.4413\r\n:3479447370796909\r\n*2\r\n\
7624 $18\r\n15.087267458438873\r\n$17\r\n37.50266842333162\r\n\
7625 *4\r\n$9\r\nAgrigento\r\n$8\r\n130.4235\r\n:3479030013248308\r\n*2\r\n\
7626 $18\r\n13.583331406116486\r\n$18\r\n37.316668049938166\r\n\
7627 *4\r\n$7\r\nPalermo\r\n$8\r\n190.4424\r\n:3479099956230698\r\n*2\r\n\
7628 $18\r\n13.361389338970184\r\n$16\r\n38.1155563954963\r\n"
7629 );
7630 }
7631
7632 #[test]
7633 fn a_store_writes_the_hashes_and_a_storedist_writes_the_distances() {
7634 let mut f = Fixture::new();
7635 sicily(&mut f);
7636 let hashes = "*6\r\n$9\r\nAgrigento\r\n$16\r\n3479030013248308\r\n\
7637 $7\r\nPalermo\r\n$16\r\n3479099956230698\r\n\
7638 $7\r\nCatania\r\n$16\r\n3479447370796909\r\n";
7639 assert_eq!(
7640 f.run(&[
7641 b"GEOSEARCHSTORE",
7642 b"dst",
7643 b"Sicily",
7644 b"FROMLONLAT",
7645 b"15",
7646 b"37",
7647 b"BYRADIUS",
7648 b"200",
7649 b"km",
7650 b"ASC"
7651 ]),
7652 ":3\r\n"
7653 );
7654 assert_eq!(
7655 f.run(&[b"ZRANGE", b"dst", b"0", b"-1", b"WITHSCORES"]),
7656 hashes
7657 );
7658 assert_eq!(
7661 f.run(&[
7662 b"GEORADIUS",
7663 b"Sicily",
7664 b"15",
7665 b"37",
7666 b"200",
7667 b"km",
7668 b"STORE",
7669 b"dst3"
7670 ]),
7671 ":3\r\n"
7672 );
7673 assert_eq!(
7674 f.run(&[b"ZRANGE", b"dst3", b"0", b"-1", b"WITHSCORES"]),
7675 hashes
7676 );
7677 assert_eq!(
7686 f.run(&[
7687 b"GEOSEARCHSTORE",
7688 b"dst2",
7689 b"Sicily",
7690 b"FROMLONLAT",
7691 b"15",
7692 b"37",
7693 b"BYRADIUS",
7694 b"200",
7695 b"km",
7696 b"ASC",
7697 b"STOREDIST"
7698 ]),
7699 ":3\r\n"
7700 );
7701 for (member, want) in [
7702 ("Catania", 56.441_257_870_158_19),
7703 ("Agrigento", 130.423_487_067_147_14),
7704 ("Palermo", 190.442_429_847_757_92),
7705 ] {
7706 let reply = f.run(&[b"ZSCORE", b"dst2", member.as_bytes()]);
7707 let got: f64 = reply
7708 .trim_start_matches(|c: char| c != '\n')
7709 .trim()
7710 .parse()
7711 .unwrap_or_else(|_| panic!("{member} scored {reply:?}"));
7712 assert!(
7713 (got - want).abs() < 1e-9,
7714 "{member} scored {got} not {want}"
7715 );
7716 }
7717 assert_eq!(
7720 f.run(&[b"ZRANGE", b"dst2", b"0", b"-1"]),
7721 "*3\r\n$7\r\nCatania\r\n$9\r\nAgrigento\r\n$7\r\nPalermo\r\n"
7722 );
7723 assert_eq!(
7727 f.run(&[
7728 b"GEOSEARCHSTORE",
7729 b"dst",
7730 b"nokey",
7731 b"FROMLONLAT",
7732 b"15",
7733 b"37",
7734 b"BYRADIUS",
7735 b"200",
7736 b"km"
7737 ]),
7738 ":0\r\n"
7739 );
7740 assert_eq!(f.run(&[b"EXISTS", b"dst"]), ":0\r\n");
7741 }
7742
7743 #[test]
7744 fn the_gates_on_geoadd_are_the_ones_zadd_has() {
7745 let mut f = Fixture::new();
7746 sicily(&mut f);
7747 assert_eq!(
7750 f.run(&[
7751 b"GEOADD",
7752 b"Sicily",
7753 b"XX",
7754 b"CH",
7755 b"13.361389",
7756 b"38.115556",
7757 b"Palermo"
7758 ]),
7759 ":0\r\n"
7760 );
7761 assert_eq!(
7762 f.run(&[
7763 b"GEOADD",
7764 b"Sicily",
7765 b"NX",
7766 b"13.361389",
7767 b"38.9",
7768 b"Palermo"
7769 ]),
7770 ":0\r\n"
7771 );
7772 assert_eq!(
7773 f.run(&[
7774 b"GEOADD",
7775 b"Sicily",
7776 b"CH",
7777 b"13.361389",
7778 b"38.9",
7779 b"Palermo"
7780 ]),
7781 ":1\r\n"
7782 );
7783 assert_eq!(
7786 f.run(&[
7787 b"GEOADD",
7788 b"new",
7789 b"13.361389",
7790 b"38.115556",
7791 b"here",
7792 b"181",
7793 b"38",
7794 b"there"
7795 ]),
7796 "-ERR invalid longitude,latitude pair 181.000000,38.000000\r\n"
7797 );
7798 assert_eq!(f.run(&[b"EXISTS", b"new"]), ":0\r\n");
7799 assert_eq!(
7800 f.run(&[b"GEOADD", b"new", b"x", b"38", b"here"]),
7801 "-ERR value is not a valid float\r\n"
7802 );
7803 assert_eq!(
7806 f.run(&[b"GEOADD", b"new", b"13", b"38", b"here", b"and"]),
7807 "-ERR syntax error\r\n"
7808 );
7809 assert_eq!(
7810 f.run(&[b"GEOADD", b"new", b"NX", b"XX", b"CH"]),
7811 "-ERR syntax error\r\n"
7812 );
7813 assert_eq!(
7814 f.run(&[b"GEOADD", b"new", b"CH", b"CH", b"CH", b"CH"]),
7815 "-ERR syntax error\r\n"
7816 );
7817 assert_eq!(
7818 f.run(&[b"GEOADD", b"new", b"NX", b"CH"]),
7819 "-ERR wrong number of arguments for 'geoadd' command\r\n"
7820 );
7821 }
7822
7823 #[test]
7826 fn every_way_a_search_can_be_written_wrong_has_its_own_sentence() {
7827 let mut f = Fixture::new();
7828 sicily(&mut f);
7829 let cases: &[(&[&[u8]], &str)] = &[
7830 (
7831 &[b"GEORADIUS", b"Sicily", b"15", b"37", b"x", b"km"],
7832 "-ERR need numeric radius\r\n",
7833 ),
7834 (
7835 &[b"GEORADIUS", b"Sicily", b"15", b"37", b"-1", b"km"],
7836 "-ERR radius cannot be negative\r\n",
7837 ),
7838 (
7839 &[b"GEORADIUS", b"Sicily", b"15", b"37", b"1", b"parsecs"],
7840 "-ERR unsupported unit provided. please use M, KM, FT, MI\r\n",
7841 ),
7842 (
7843 &[b"GEORADIUS", b"Sicily", b"181", b"37", b"1", b"km"],
7844 "-ERR invalid longitude,latitude pair 181.000000,37.000000\r\n",
7845 ),
7846 (
7847 &[
7848 b"GEOSEARCH",
7849 b"Sicily",
7850 b"FROMLONLAT",
7851 b"15",
7852 b"37",
7853 b"BYBOX",
7854 b"x",
7855 b"1",
7856 b"km",
7857 ],
7858 "-ERR need numeric width\r\n",
7859 ),
7860 (
7861 &[
7862 b"GEOSEARCH",
7863 b"Sicily",
7864 b"FROMLONLAT",
7865 b"15",
7866 b"37",
7867 b"BYBOX",
7868 b"1",
7869 b"y",
7870 b"km",
7871 ],
7872 "-ERR need numeric height\r\n",
7873 ),
7874 (
7875 &[
7876 b"GEOSEARCH",
7877 b"Sicily",
7878 b"FROMLONLAT",
7879 b"15",
7880 b"37",
7881 b"BYBOX",
7882 b"-1",
7883 b"1",
7884 b"km",
7885 ],
7886 "-ERR height or width cannot be negative\r\n",
7887 ),
7888 (
7889 &[
7890 b"GEOSEARCH",
7891 b"Sicily",
7892 b"FROMLONLAT",
7893 b"15",
7894 b"37",
7895 b"BYRADIUS",
7896 b"1",
7897 b"km",
7898 b"ANY",
7899 ],
7900 "-ERR the ANY argument requires COUNT argument\r\n",
7901 ),
7902 (
7903 &[
7904 b"GEOSEARCH",
7905 b"Sicily",
7906 b"FROMLONLAT",
7907 b"15",
7908 b"37",
7909 b"BYRADIUS",
7910 b"1",
7911 b"km",
7912 b"COUNT",
7913 b"0",
7914 ],
7915 "-ERR COUNT must be > 0\r\n",
7916 ),
7917 (
7918 &[
7919 b"GEOSEARCH",
7920 b"Sicily",
7921 b"BYRADIUS",
7922 b"1",
7923 b"km",
7924 b"BYBOX",
7925 b"1",
7926 b"1",
7927 b"km",
7928 ],
7929 "-ERR syntax error\r\n",
7930 ),
7931 (
7932 &[
7933 b"GEOSEARCH",
7934 b"Sicily",
7935 b"FROMMEMBER",
7936 b"Palermo",
7937 b"FROMLONLAT",
7938 b"1",
7939 b"2",
7940 b"BYRADIUS",
7941 b"1",
7942 b"km",
7943 ],
7944 "-ERR syntax error\r\n",
7945 ),
7946 (
7949 &[
7950 b"geosearch",
7951 b"Sicily",
7952 b"BYRADIUS",
7953 b"1",
7954 b"km",
7955 b"ASC",
7956 b"WITHDIST",
7957 ],
7958 "-ERR exactly one of FROMMEMBER or FROMLONLAT can be specified for geosearch\r\n",
7959 ),
7960 (
7961 &[
7962 b"GEOSEARCH",
7963 b"Sicily",
7964 b"FROMLONLAT",
7965 b"15",
7966 b"37",
7967 b"ASC",
7968 b"WITHDIST",
7969 ],
7970 "-ERR exactly one of BYRADIUS and BYBOX can be specified for GEOSEARCH\r\n",
7971 ),
7972 (
7975 &[
7976 b"GEOSEARCHSTORE",
7977 b"d",
7978 b"Sicily",
7979 b"FROMLONLAT",
7980 b"15",
7981 b"37",
7982 b"BYRADIUS",
7983 b"1",
7984 b"km",
7985 b"WITHCOORD",
7986 ],
7987 "-ERR GEOSEARCHSTORE is not compatible with WITHDIST, WITHHASH and WITHCOORD options\r\n",
7988 ),
7989 (
7990 &[
7991 b"GEORADIUS",
7992 b"Sicily",
7993 b"15",
7994 b"37",
7995 b"1",
7996 b"km",
7997 b"WITHDIST",
7998 b"STORE",
7999 b"d",
8000 ],
8001 "-ERR STORE option in GEORADIUS is not compatible with WITHDIST, WITHHASH and WITHCOORD options\r\n",
8002 ),
8003 (
8006 &[
8007 b"GEORADIUS_RO",
8008 b"Sicily",
8009 b"15",
8010 b"37",
8011 b"1",
8012 b"km",
8013 b"STORE",
8014 b"d",
8015 ],
8016 "-ERR syntax error\r\n",
8017 ),
8018 (
8019 &[
8020 b"GEOSEARCH",
8021 b"Sicily",
8022 b"FROMLONLAT",
8023 b"15",
8024 b"37",
8025 b"BYRADIUS",
8026 b"1",
8027 b"km",
8028 b"STOREDIST",
8029 ],
8030 "-ERR syntax error\r\n",
8031 ),
8032 ];
8033 for (parts, want) in cases {
8034 assert_eq!(&f.run(parts), want, "{:?}", parts[0]);
8035 }
8036 }
8037
8038 #[test]
8041 fn every_geo_command_says_wrongtype() {
8042 let mut f = Fixture::new();
8043 f.run(&[b"SET", b"s", b"v"]);
8044 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
8045 let cases: &[&[&[u8]]] = &[
8046 &[b"GEOADD", b"s", b"13", b"38", b"m"],
8047 &[b"GEOPOS", b"s", b"m"],
8048 &[b"GEOHASH", b"s", b"m"],
8049 &[b"GEODIST", b"s", b"a", b"b"],
8050 &[
8051 b"GEOSEARCH",
8052 b"s",
8053 b"FROMLONLAT",
8054 b"15",
8055 b"37",
8056 b"BYRADIUS",
8057 b"1",
8058 b"km",
8059 ],
8060 &[
8061 b"GEOSEARCHSTORE",
8062 b"d",
8063 b"s",
8064 b"FROMLONLAT",
8065 b"15",
8066 b"37",
8067 b"BYRADIUS",
8068 b"1",
8069 b"km",
8070 ],
8071 &[b"GEORADIUS", b"s", b"15", b"37", b"1", b"km"],
8072 &[b"GEORADIUS_RO", b"s", b"15", b"37", b"1", b"km"],
8073 &[b"GEORADIUSBYMEMBER", b"s", b"m", b"1", b"km"],
8074 &[b"GEORADIUSBYMEMBER_RO", b"s", b"m", b"1", b"km"],
8075 ];
8076 for case in cases {
8077 assert_eq!(f.run(case), wrong, "{:?}", case[0]);
8078 }
8079 assert_eq!(
8082 f.run(&[b"GEORADIUS", b"s", b"15", b"37", b"x", b"km"]),
8083 wrong
8084 );
8085 }
8086
8087 #[test]
8090 fn an_array_writes_at_any_index_and_reads_back_what_it_sent() {
8091 let mut f = Fixture::new();
8092 assert_eq!(
8095 f.run(&[b"ARSET", b"a", b"1000", b"x", b"y", b"z"]),
8096 ":3\r\n"
8097 );
8098 assert_eq!(f.run(&[b"ARSET", b"a", b"1000", b"X", b"Y"]), ":0\r\n");
8099 assert_eq!(f.run(&[b"ARGET", b"a", b"1000"]), "$1\r\nX\r\n");
8100 assert_eq!(f.run(&[b"ARGET", b"a", b"1002"]), "$1\r\nz\r\n");
8101 assert_eq!(f.run(&[b"ARGET", b"a", b"999"]), "$-1\r\n");
8103 assert_eq!(f.run(&[b"ARGET", b"nope", b"0"]), "$-1\r\n");
8104 assert_eq!(
8105 f.run(&[b"ARMGET", b"a", b"1002", b"999", b"1000"]),
8106 "*3\r\n$1\r\nz\r\n$-1\r\n$1\r\nX\r\n"
8107 );
8108 assert_eq!(f.run(&[b"ARMSET", b"a", b"5", b"p", b"5", b"q"]), ":1\r\n");
8110 assert_eq!(f.run(&[b"ARGET", b"a", b"5"]), "$1\r\nq\r\n");
8111 }
8112
8113 #[test]
8116 fn the_length_is_the_high_water_mark_and_the_count_is_the_population() {
8117 let mut f = Fixture::new();
8118 assert_eq!(f.run(&[b"ARLEN", b"nope"]), ":0\r\n");
8119 assert_eq!(f.run(&[b"ARCOUNT", b"nope"]), ":0\r\n");
8120 f.run(&[b"ARMSET", b"a", b"0", b"x", b"9", b"y"]);
8121 assert_eq!(f.run(&[b"ARLEN", b"a"]), ":10\r\n");
8122 assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":2\r\n");
8123 assert_eq!(f.run(&[b"ARDEL", b"a", b"0"]), ":1\r\n");
8125 assert_eq!(f.run(&[b"ARLEN", b"a"]), ":10\r\n");
8126 assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":1\r\n");
8127
8128 f.run(&[b"ARSET", b"top", b"18446744073709551614", b"z"]);
8132 assert_eq!(f.run(&[b"ARLEN", b"top"]), ":18446744073709551615\r\n");
8133 assert_eq!(f.run(&[b"ARCOUNT", b"top"]), ":1\r\n");
8134 assert_eq!(
8137 f.run(&[b"ARSET", b"over", b"18446744073709551614", b"a", b"b"]),
8138 "-ERR array index overflow\r\n"
8139 );
8140 assert_eq!(f.run(&[b"EXISTS", b"over"]), ":0\r\n");
8141 }
8142
8143 #[test]
8146 fn a_range_read_answers_for_the_holes_too_and_is_capped_at_a_million() {
8147 let mut f = Fixture::new();
8148 f.run(&[b"ARSET", b"a", b"1", b"x"]);
8149 assert_eq!(
8150 f.run(&[b"ARGETRANGE", b"a", b"0", b"3"]),
8151 "*4\r\n$-1\r\n$1\r\nx\r\n$-1\r\n$-1\r\n"
8152 );
8153 assert_eq!(
8156 f.run(&[b"ARGETRANGE", b"a", b"3", b"0"]),
8157 "*4\r\n$-1\r\n$-1\r\n$1\r\nx\r\n$-1\r\n"
8158 );
8159 assert_eq!(
8161 f.run(&[b"ARGETRANGE", b"nope", b"0", b"1"]),
8162 "*2\r\n$-1\r\n$-1\r\n"
8163 );
8164 assert_eq!(
8168 f.run(&[b"ARGETRANGE", b"nope", b"0", b"18446744073709551614"]),
8169 "-ERR range exceeds maximum of 1000000 items\r\n"
8170 );
8171 }
8172
8173 #[test]
8176 fn a_bad_index_late_in_the_line_writes_none_of_the_earlier_ones() {
8177 let mut f = Fixture::new();
8178 assert_eq!(
8179 f.run(&[b"ARMSET", b"a", b"0", b"x", b"-1", b"y"]),
8180 "-ERR invalid array index\r\n"
8181 );
8182 assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
8183 f.run(&[b"ARSET", b"a", b"0", b"x", b"y", b"z"]);
8184 assert_eq!(
8185 f.run(&[b"ARDEL", b"a", b"0", b"01"]),
8186 "-ERR invalid array index\r\n"
8187 );
8188 assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":3\r\n");
8189 assert_eq!(
8192 f.run(&[b"ARGET", b"a", b"-1"]),
8193 "-ERR invalid array index\r\n"
8194 );
8195 assert_eq!(
8198 f.run(&[b"ARMSET", b"a", b"0", b"x", b"1"]),
8199 "-ERR wrong number of arguments for 'armset' command\r\n"
8200 );
8201 assert_eq!(
8202 f.run(&[b"ARDELRANGE", b"a", b"0", b"1", b"2"]),
8203 "-ERR wrong number of arguments for 'ardelrange' command\r\n"
8204 );
8205 }
8206
8207 #[test]
8208 fn a_range_delete_costs_the_elements_and_takes_the_key_when_it_empties() {
8209 let mut f = Fixture::new();
8210 f.run(&[b"ARSET", b"a", b"0", b"0", b"1", b"2", b"3", b"4"]);
8211 assert_eq!(f.run(&[b"ARDELRANGE", b"a", b"3", b"1"]), ":3\r\n");
8212 assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":2\r\n");
8213 assert_eq!(
8216 f.run(&[
8217 b"ARDELRANGE",
8218 b"a",
8219 b"100",
8220 b"200",
8221 b"0",
8222 b"18446744073709551614"
8223 ]),
8224 ":2\r\n"
8225 );
8226 assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
8227 assert_eq!(f.run(&[b"ARDELRANGE", b"nope", b"0", b"1"]), ":0\r\n");
8228 assert_eq!(f.run(&[b"ARDEL", b"nope", b"0"]), ":0\r\n");
8229 }
8230
8231 #[test]
8234 fn a_value_comes_back_byte_for_byte_however_it_was_packed() {
8235 let mut f = Fixture::new();
8236 let long = vec![b'v'; 200];
8237 f.run(&[
8238 b"ARMSET", b"a", b"0", b"42", b"1", b"007", b"2", b"3.5", b"3", b"3.14", b"4",
8239 b"short", b"5", &long, b"6", b"-0",
8240 ]);
8241 assert_eq!(
8245 f.run(&[b"ARGETRANGE", b"a", b"0", b"6"]),
8246 format!(
8247 "*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",
8248 String::from_utf8_lossy(&long)
8249 )
8250 );
8251 }
8252
8253 #[test]
8254 fn an_array_is_a_type_and_an_encoding_a_client_can_see() {
8255 let mut f = Fixture::new();
8256 f.run(&[b"ARSET", b"a", b"0", b"x"]);
8257 assert_eq!(f.run(&[b"TYPE", b"a"]), "+array\r\n");
8258 assert_eq!(
8259 f.run(&[b"OBJECT", b"ENCODING", b"a"]),
8260 "$12\r\nsliced-array\r\n"
8261 );
8262 assert_eq!(f.run(&[b"EXPIRE", b"a", b"100"]), ":1\r\n");
8264 assert_eq!(f.run(&[b"PERSIST", b"a"]), ":1\r\n");
8265 assert_eq!(f.run(&[b"COPY", b"a", b"b"]), ":1\r\n");
8266 assert_eq!(f.run(&[b"ARGET", b"b", b"0"]), "$1\r\nx\r\n");
8267 assert_eq!(f.run(&[b"RENAME", b"a", b"c"]), "+OK\r\n");
8268 assert_eq!(f.run(&[b"ARCOUNT", b"c"]), ":1\r\n");
8269 }
8270
8271 #[test]
8272 fn every_array_command_refuses_a_key_holding_something_else() {
8273 let mut f = Fixture::new();
8274 f.run(&[b"SET", b"s", b"v"]);
8275 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
8276 for cmd in [
8277 &[b"ARSET".as_ref(), b"s", b"0", b"x"][..],
8278 &[b"ARMSET".as_ref(), b"s", b"0", b"x"][..],
8279 &[b"ARGET".as_ref(), b"s", b"0"][..],
8280 &[b"ARMGET".as_ref(), b"s", b"0"][..],
8281 &[b"ARGETRANGE".as_ref(), b"s", b"0", b"1"][..],
8282 &[b"ARLEN".as_ref(), b"s"][..],
8283 &[b"ARCOUNT".as_ref(), b"s"][..],
8284 &[b"ARDEL".as_ref(), b"s", b"0"][..],
8285 &[b"ARDELRANGE".as_ref(), b"s", b"0", b"1"][..],
8286 &[b"ARINSERT".as_ref(), b"s", b"x"][..],
8287 &[b"ARRING".as_ref(), b"s", b"4", b"x"][..],
8288 &[b"ARNEXT".as_ref(), b"s"][..],
8289 &[b"ARSEEK".as_ref(), b"s", b"1"][..],
8290 &[b"ARLASTITEMS".as_ref(), b"s", b"1"][..],
8291 &[b"ARSCAN".as_ref(), b"s", b"0", b"1"][..],
8292 &[b"ARGREP".as_ref(), b"s", b"0", b"1", b"EXACT", b"v"][..],
8293 &[b"AROP".as_ref(), b"s", b"0", b"1", b"SUM"][..],
8294 &[b"ARINFO".as_ref(), b"s"][..],
8295 ] {
8296 assert_eq!(f.run(cmd), wrong, "{}", String::from_utf8_lossy(cmd[0]));
8297 }
8298 }
8299
8300 #[test]
8304 fn a_bad_index_reports_the_type_only_where_redis_reports_it() {
8305 let mut f = Fixture::new();
8306 f.run(&[b"SET", b"s", b"v"]);
8307 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
8308 let bad = "-ERR invalid array index\r\n";
8309 assert_eq!(f.run(&[b"ARGET", b"s", b"-1"]), wrong);
8310 assert_eq!(f.run(&[b"ARMGET", b"s", b"0", b"-1"]), wrong);
8311 assert_eq!(f.run(&[b"ARSET", b"s", b"-1", b"x"]), bad);
8312 assert_eq!(f.run(&[b"ARDEL", b"s", b"-1"]), bad);
8313 assert_eq!(f.run(&[b"ARSCAN", b"s", b"-1", b"0"]), bad);
8314 assert_eq!(f.run(&[b"ARGREP", b"s", b"-1", b"0", b"EXACT", b"v"]), bad);
8315 f.run(&[b"ARSET", b"a", b"0", b"x"]);
8317 assert_eq!(f.run(&[b"ARGET", b"a", b"-1"]), bad);
8318 assert_eq!(f.run(&[b"ARGET", b"nope", b"-1"]), bad);
8319 }
8320
8321 #[test]
8322 fn an_append_follows_a_cursor_the_client_can_move() {
8323 let mut f = Fixture::new();
8324 assert_eq!(f.run(&[b"ARNEXT", b"nope"]), ":0\r\n");
8325 assert_eq!(f.run(&[b"ARINSERT", b"a", b"x", b"y"]), ":1\r\n");
8326 assert_eq!(f.run(&[b"ARNEXT", b"a"]), ":2\r\n");
8327 assert_eq!(f.run(&[b"ARINSERT", b"a", b"z"]), ":2\r\n");
8328 assert_eq!(f.run(&[b"ARGET", b"a", b"2"]), "$1\r\nz\r\n");
8329
8330 assert_eq!(f.run(&[b"ARSEEK", b"nope", b"5"]), ":0\r\n");
8333 assert_eq!(f.run(&[b"EXISTS", b"nope"]), ":0\r\n");
8334 assert_eq!(f.run(&[b"ARSEEK", b"a", b"100"]), ":1\r\n");
8335 assert_eq!(f.run(&[b"ARNEXT", b"a"]), ":100\r\n");
8336 assert_eq!(f.run(&[b"ARINSERT", b"a", b"far"]), ":100\r\n");
8337 assert_eq!(f.run(&[b"ARSEEK", b"a", b"0"]), ":1\r\n");
8338 assert_eq!(f.run(&[b"ARNEXT", b"a"]), ":0\r\n");
8339
8340 assert_eq!(f.run(&[b"ARSEEK", b"a", b"18446744073709551615"]), ":1\r\n");
8343 assert_eq!(f.run(&[b"ARNEXT", b"a"]), "$-1\r\n");
8344 assert_eq!(
8345 f.run(&[b"ARINSERT", b"a", b"x"]),
8346 "-ERR insert index overflow\r\n"
8347 );
8348 assert_eq!(
8349 f.run(&[b"ARSET", b"a", b"18446744073709551615", b"x"]),
8350 "-ERR invalid array index\r\n"
8351 );
8352 }
8353
8354 #[test]
8355 fn a_ring_keeps_the_newest_and_renumbers_them_when_it_is_resized() {
8356 let mut f = Fixture::new();
8357 assert_eq!(f.run(&[b"ARRING", b"r", b"3", b"a", b"b", b"c"]), ":2\r\n");
8358 assert_eq!(f.run(&[b"ARRING", b"r", b"3", b"d", b"e"]), ":1\r\n");
8359 assert_eq!(f.run(&[b"ARLEN", b"r"]), ":3\r\n");
8360 assert_eq!(
8361 f.run(&[b"ARGETRANGE", b"r", b"0", b"2"]),
8362 "*3\r\n$1\r\nd\r\n$1\r\ne\r\n$1\r\nc\r\n"
8363 );
8364 assert_eq!(f.run(&[b"ARRING", b"r", b"5", b"f"]), ":3\r\n");
8367 assert_eq!(
8368 f.run(&[b"ARGETRANGE", b"r", b"0", b"3"]),
8369 "*4\r\n$1\r\nc\r\n$1\r\nd\r\n$1\r\ne\r\n$1\r\nf\r\n"
8370 );
8371 assert_eq!(
8374 f.run(&[b"ARRING", b"r", b"0", b"x"]),
8375 "-ERR size must be positive\r\n"
8376 );
8377 assert_eq!(
8378 f.run(&[b"ARRING", b"r", b"big", b"x"]),
8379 "-ERR invalid size\r\n"
8380 );
8381 }
8382
8383 #[test]
8384 fn the_last_items_walk_back_from_the_cursor_and_report_the_holes() {
8385 let mut f = Fixture::new();
8386 assert_eq!(f.run(&[b"ARLASTITEMS", b"nope", b"5"]), "*0\r\n");
8387 f.run(&[b"ARRING", b"r", b"4", b"a", b"b", b"c", b"d", b"e"]);
8388 assert_eq!(
8389 f.run(&[b"ARLASTITEMS", b"r", b"3"]),
8390 "*3\r\n$1\r\nc\r\n$1\r\nd\r\n$1\r\ne\r\n"
8391 );
8392 assert_eq!(
8393 f.run(&[b"ARLASTITEMS", b"r", b"3", b"rev"]),
8394 "*3\r\n$1\r\ne\r\n$1\r\nd\r\n$1\r\nc\r\n"
8395 );
8396 assert_eq!(
8397 f.run(&[b"ARLASTITEMS", b"r", b"99"]),
8398 "*4\r\n$1\r\nb\r\n$1\r\nc\r\n$1\r\nd\r\n$1\r\ne\r\n",
8399 "more than there is gets what there is"
8400 );
8401 assert_eq!(f.run(&[b"ARLASTITEMS", b"r", b"0", b"junk"]), "*0\r\n");
8404 assert_eq!(
8405 f.run(&[b"ARLASTITEMS", b"r", b"1", b"junk"]),
8406 "-ERR syntax error\r\n"
8407 );
8408 assert_eq!(
8409 f.run(&[b"ARLASTITEMS", b"r", b"nine"]),
8410 "-ERR invalid COUNT\r\n"
8411 );
8412
8413 f.run(&[b"ARMSET", b"h", b"0", b"x", b"2", b"z"]);
8416 assert_eq!(
8417 f.run(&[b"ARLASTITEMS", b"h", b"5"]),
8418 "*2\r\n$-1\r\n$1\r\nz\r\n"
8419 );
8420 }
8421
8422 #[test]
8423 fn a_scan_answers_pairs_for_what_is_there_and_skips_what_is_not() {
8424 let mut f = Fixture::new();
8425 assert_eq!(f.run(&[b"ARSCAN", b"nope", b"0", b"10"]), "*0\r\n");
8426 f.run(&[b"ARMSET", b"a", b"0", b"x", b"7", b"y", b"1000000", b"z"]);
8427 assert_eq!(
8430 f.run(&[b"ARSCAN", b"a", b"0", b"18446744073709551614"]),
8431 "*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"
8432 );
8433 assert_eq!(
8434 f.run(&[
8435 b"ARSCAN",
8436 b"a",
8437 b"18446744073709551614",
8438 b"0",
8439 b"LIMIT",
8440 b"1"
8441 ]),
8442 "*1\r\n*2\r\n:1000000\r\n$1\r\nz\r\n"
8443 );
8444 assert_eq!(f.run(&[b"ARSCAN", b"a", b"1", b"6"]), "*0\r\n");
8445 assert_eq!(
8446 f.run(&[b"ARSCAN", b"a", b"0", b"10", b"LIMIT", b"0"]),
8447 "-ERR LIMIT must be positive\r\n"
8448 );
8449 assert_eq!(
8450 f.run(&[b"ARSCAN", b"a", b"0", b"10", b"NOPE", b"1"]),
8451 "-ERR syntax error\r\n"
8452 );
8453 assert_eq!(
8454 f.run(&[b"ARSCAN", b"a", b"0", b"10", b"LIMIT"]),
8455 "-ERR wrong number of arguments for 'arscan' command\r\n"
8456 );
8457 }
8458
8459 #[test]
8460 fn a_grep_answers_the_indexes_whose_elements_match() {
8461 let mut f = Fixture::new();
8462 assert_eq!(
8463 f.run(&[b"ARGREP", b"nope", b"0", b"10", b"EXACT", b"x"]),
8464 "*0\r\n"
8465 );
8466 f.run(&[b"ARSET", b"a", b"0", b"alpha", b"beta", b"gamma", b"ALPHA"]);
8467
8468 assert_eq!(
8471 f.run(&[b"ARGREP", b"a", b"-", b"+", b"GLOB", b"*a"]),
8472 "*3\r\n:0\r\n:1\r\n:2\r\n"
8473 );
8474 assert_eq!(
8475 f.run(&[b"ARGREP", b"a", b"+", b"-", b"GLOB", b"*a"]),
8476 "*3\r\n:2\r\n:1\r\n:0\r\n"
8477 );
8478 assert_eq!(
8479 f.run(&[b"ARGREP", b"a", b"1", b"2", b"GLOB", b"*a"]),
8480 "*2\r\n:1\r\n:2\r\n"
8481 );
8482
8483 assert_eq!(
8486 f.run(&[b"ARGREP", b"a", b"-", b"+", b"EXACT", b"alpha"]),
8487 "*1\r\n:0\r\n"
8488 );
8489 assert_eq!(
8490 f.run(&[b"ARGREP", b"a", b"-", b"+", b"EXACT", b"alpha", b"NOCASE"]),
8491 "*2\r\n:0\r\n:3\r\n"
8492 );
8493 assert_eq!(
8494 f.run(&[b"ARGREP", b"a", b"-", b"+", b"MATCH", b"mm"]),
8495 "*1\r\n:2\r\n"
8496 );
8497 assert_eq!(
8498 f.run(&[b"ARGREP", b"a", b"-", b"+", b"RE", b"^[bg]"]),
8499 "*2\r\n:1\r\n:2\r\n"
8500 );
8501
8502 let both: &[&[u8]] = &[
8505 b"ARGREP", b"a", b"-", b"+", b"EXACT", b"beta", b"MATCH", b"al",
8506 ];
8507 assert_eq!(f.run(both), "*2\r\n:0\r\n:1\r\n");
8508 assert_eq!(
8509 f.run(&[
8510 b"ARGREP", b"a", b"-", b"+", b"EXACT", b"beta", b"MATCH", b"al", b"AND"
8511 ]),
8512 "*0\r\n"
8513 );
8514 assert_eq!(
8515 f.run(&[
8516 b"ARGREP", b"a", b"-", b"+", b"EXACT", b"beta", b"MATCH", b"al", b"AND", b"OR"
8517 ]),
8518 "*2\r\n:0\r\n:1\r\n"
8519 );
8520
8521 assert_eq!(
8524 f.run(&[
8525 b"ARGREP",
8526 b"a",
8527 b"-",
8528 b"+",
8529 b"MATCH",
8530 b"a",
8531 b"WITHVALUES",
8532 b"LIMIT",
8533 b"2"
8534 ]),
8535 "*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"
8536 );
8537 assert_eq!(
8538 f.run(&[
8539 b"ARGREP", b"a", b"-", b"+", b"EXACT", b"ALPHA", b"LIMIT", b"1"
8540 ]),
8541 "*1\r\n:3\r\n"
8542 );
8543 }
8544
8545 #[test]
8547 fn a_grep_reports_a_broken_command_the_way_redis_does() {
8548 let mut f = Fixture::new();
8549 f.run(&[b"ARSET", b"a", b"0", b"alpha"]);
8550 let syntax = "-ERR syntax error\r\n";
8551
8552 assert_eq!(
8555 f.run(&[b"ARGREP", b"a", b"-1", b"0", b"NOPE", b"x"]),
8556 "-ERR invalid array index\r\n"
8557 );
8558 assert_eq!(f.run(&[b"ARGREP", b"a", b"0", b"1", b"NOPE", b"x"]), syntax);
8559 assert_eq!(
8561 f.run(&[b"ARGREP", b"a", b"0", b"1", b"NOCASE", b"EXACT"]),
8562 syntax
8563 );
8564 assert_eq!(
8565 f.run(&[b"ARGREP", b"a", b"0", b"1", b"EXACT", b"x", b"LIMIT"]),
8566 syntax
8567 );
8568 assert_eq!(
8569 f.run(&[b"ARGREP", b"a", b"0", b"1", b"NOCASE", b"WITHVALUES"]),
8570 syntax,
8571 "a command with no predicate in it at all"
8572 );
8573 assert_eq!(
8574 f.run(&[b"ARGREP", b"a", b"0", b"1", b"EXACT", b"x", b"LIMIT", b"0"]),
8575 "-ERR LIMIT must be positive\r\n"
8576 );
8577 assert_eq!(
8578 f.run(&[
8579 b"ARGREP", b"a", b"0", b"1", b"EXACT", b"x", b"LIMIT", b"nine"
8580 ]),
8581 "-ERR value is not an integer or out of range\r\n"
8582 );
8583 assert_eq!(
8584 f.run(&[b"ARGREP", b"a", b"0", b"1", b"RE", b""]),
8585 "-ERR regular expression is empty\r\n"
8586 );
8587 assert_eq!(
8588 f.run(&[b"ARGREP", b"a", b"0", b"1", b"RE", b"(a"]),
8589 "-ERR invalid regular expression: Missing ')'\r\n"
8590 );
8591 assert_eq!(
8592 f.run(&[b"ARGREP", b"a", b"0", b"1", b"RE", br"(a)\1"]),
8593 "-ERR regular expression backreferences are not supported\r\n"
8594 );
8595 let arity = "-ERR wrong number of arguments for 'argrep' command\r\n";
8598 assert_eq!(f.run(&[b"ARGREP", b"a", b"0", b"1", b"EXACT"]), arity);
8599 assert_eq!(f.run(&[b"ARGREP", b"a", b"0", b"1"]), arity);
8600 }
8601
8602 #[test]
8603 fn an_op_reduces_a_range_to_one_number() {
8604 let mut f = Fixture::new();
8605 f.run(&[b"ARSET", b"a", b"0", b"1", b"2.5", b"word", b"-4"]);
8606 assert_eq!(
8607 f.run(&[b"AROP", b"a", b"0", b"10", b"SUM"]),
8608 "$4\r\n-0.5\r\n"
8609 );
8610 assert_eq!(f.run(&[b"AROP", b"a", b"0", b"10", b"min"]), "$2\r\n-4\r\n");
8611 assert_eq!(
8612 f.run(&[b"AROP", b"a", b"0", b"10", b"MAX"]),
8613 "$3\r\n2.5\r\n"
8614 );
8615 assert_eq!(f.run(&[b"AROP", b"a", b"0", b"10", b"USED"]), ":4\r\n");
8616 assert_eq!(
8617 f.run(&[b"AROP", b"a", b"0", b"10", b"MATCH", b"word"]),
8618 ":1\r\n"
8619 );
8620 f.run(&[b"ARSET", b"t", b"0", b"0.1", b"0.2"]);
8623 assert_eq!(
8624 f.run(&[b"AROP", b"t", b"0", b"10", b"SUM"]),
8625 "$19\r\n0.30000000000000004\r\n"
8626 );
8627 assert_eq!(f.run(&[b"ZADD", b"z", b"0.3", b"m"]), ":1\r\n");
8628 assert_eq!(f.run(&[b"ZSCORE", b"z", b"m"]), "$3\r\n0.3\r\n");
8629
8630 f.run(&[b"ARSET", b"w", b"0", b"word"]);
8633 assert_eq!(f.run(&[b"AROP", b"w", b"0", b"10", b"SUM"]), "$-1\r\n");
8634 assert_eq!(f.run(&[b"AROP", b"nope", b"0", b"10", b"SUM"]), "$-1\r\n");
8635 assert_eq!(f.run(&[b"AROP", b"nope", b"0", b"10", b"USED"]), ":0\r\n");
8636
8637 assert_eq!(
8638 f.run(&[b"AROP", b"a", b"0", b"10", b"NOPE"]),
8639 "-ERR unknown operation\r\n"
8640 );
8641 assert_eq!(
8642 f.run(&[b"AROP", b"a", b"0", b"10", b"MATCH"]),
8643 "-ERR MATCH requires a value argument\r\n"
8644 );
8645 assert_eq!(
8646 f.run(&[b"AROP", b"a", b"0", b"10", b"SUM", b"extra"]),
8647 "-ERR wrong number of arguments for 'arop' command\r\n"
8648 );
8649 }
8650
8651 #[test]
8652 fn the_info_is_a_map_and_a_missing_key_is_an_error() {
8653 let mut f = Fixture::new();
8654 assert_eq!(f.run(&[b"ARINFO", b"nope"]), "-ERR no such key\r\n");
8655 f.run(&[b"ARINSERT", b"a", b"x", b"y"]);
8656 let short = f.run(&[b"ARINFO", b"a"]);
8657 assert!(
8658 short.starts_with("*14\r\n"),
8659 "seven pairs on RESP2: {short}"
8660 );
8661 assert!(short.contains("$5\r\ncount\r\n:2\r\n"), "{short}");
8662 assert!(
8663 short.contains("$17\r\nnext-insert-index\r\n:2\r\n"),
8664 "{short}"
8665 );
8666 assert!(short.contains("$10\r\nslice-size\r\n:4096\r\n"), "{short}");
8667 let full = f.run(&[b"ARINFO", b"a", b"full"]);
8668 assert!(full.starts_with("*24\r\n"), "twelve pairs: {full}");
8669 assert!(full.contains("$12\r\ndense-slices\r\n:0\r\n"), "{full}");
8672 assert!(full.contains("$13\r\nsparse-slices\r\n:1\r\n"), "{full}");
8673 assert!(
8674 full.contains("$14\r\navg-dense-size\r\n$1\r\n0\r\n"),
8675 "{full}"
8676 );
8677 assert_eq!(f.run(&[b"ARINFO", b"a", b"nope"]), "-ERR syntax error\r\n");
8678
8679 let mut g = Fixture::new();
8681 g.run(&[b"HELLO", b"3"]);
8682 g.run(&[b"ARINSERT", b"a", b"x"]);
8683 let map = g.run(&[b"ARINFO", b"a", b"FULL"]);
8684 assert!(map.starts_with("%12\r\n"), "{map}");
8685 assert!(map.contains("$5\r\ncount\r\n:1\r\n"), "{map}");
8686 assert!(map.contains("$14\r\navg-dense-size\r\n,0\r\n"), "{map}");
8687 }
8688
8689 #[test]
8690 fn a_double_on_the_wire_is_written_the_way_redis_writes_one() {
8691 let mut f = Fixture::new();
8692 for (score, want) in [
8695 ("3", "3"),
8696 ("3.5", "3.5"),
8697 ("0.3", "0.3"),
8698 ("1e30", "1e+30"),
8699 ("1e19", "1e+19"),
8700 ("1e-7", "1e-7"),
8701 ("0.000001", "0.000001"),
8702 ("4611686018427387904", "4611686018427387904"),
8703 ("-0", "-0"),
8704 ] {
8705 f.run(&[b"ZADD", b"z", score.as_bytes(), b"m"]);
8706 assert_eq!(
8707 f.run(&[b"ZSCORE", b"z", b"m"]),
8708 format!("${}\r\n{want}\r\n", want.len()),
8709 "score {score}"
8710 );
8711 }
8712
8713 let mut g = Fixture::new();
8716 g.run(&[b"HELLO", b"3"]);
8717 g.run(&[b"ZADD", b"z", b"1e30", b"m"]);
8718 assert_eq!(g.run(&[b"ZSCORE", b"z", b"m"]), ",1e+30\r\n");
8719 assert_eq!(
8724 g.run(&[b"INCRBYFLOAT", b"s", b"1e30"]),
8725 "$31\r\n1000000000000000000000000000000\r\n"
8726 );
8727 assert_eq!(g.run(&[b"INCRBYFLOAT", b"t", b"0.1"]), "$3\r\n0.1\r\n");
8728 assert_eq!(
8729 g.run(&[b"HINCRBYFLOAT", b"h", b"f", b"1e19"]),
8730 "$20\r\n10000000000000000000\r\n"
8731 );
8732 }
8733
8734 #[test]
8737 fn a_node_comes_back_with_the_fields_it_went_in_with() {
8738 let mut f = Fixture::new();
8739 assert_eq!(
8740 f.run(&[
8741 b"G.NADD", b"social", b"ada", b"name", b"Ada", b"born", b"1815"
8742 ]),
8743 ":1\r\n"
8744 );
8745 assert_eq!(
8751 f.run(&[b"G.NGET", b"social", b"ada"]),
8752 "*4\r\n$4\r\nborn\r\n$4\r\n1815\r\n$4\r\nname\r\n$3\r\nAda\r\n"
8753 );
8754 assert_eq!(
8757 f.run(&[b"G.NADD", b"social", b"ada", b"name", b"Ada Lovelace"]),
8758 ":0\r\n"
8759 );
8760 assert_eq!(
8761 f.run(&[b"G.NGET", b"social", b"ada"]),
8762 "*2\r\n$4\r\nname\r\n$12\r\nAda Lovelace\r\n"
8763 );
8764 assert_eq!(f.run(&[b"G.NADD", b"social", b"grace"]), ":1\r\n");
8767 assert_eq!(f.run(&[b"G.NGET", b"social", b"grace"]), "*0\r\n");
8768 assert_eq!(f.run(&[b"G.NGET", b"social", b"nobody"]), "$-1\r\n");
8769 assert_eq!(f.run(&[b"G.NGET", b"nokey", b"ada"]), "$-1\r\n");
8770
8771 assert_eq!(
8774 f.run(&[b"G.NADD", b"fresh", b"n", b"lonely"]),
8775 "-ERR syntax error\r\n"
8776 );
8777 assert_eq!(f.run(&[b"EXISTS", b"fresh"]), ":0\r\n");
8778
8779 let mut g = Fixture::new();
8781 g.run(&[b"HELLO", b"3"]);
8782 g.run(&[b"G.NADD", b"social", b"ada", b"name", b"Ada"]);
8783 assert_eq!(
8784 g.run(&[b"G.NGET", b"social", b"ada"]),
8785 "%1\r\n$4\r\nname\r\n$3\r\nAda\r\n"
8786 );
8787 }
8788
8789 #[test]
8790 fn an_edge_creates_the_ends_it_needs() {
8791 let mut f = Fixture::new();
8792 assert_eq!(
8793 f.run(&[
8794 b"G.EADD", b"social", b"ada", b"grace", b"FOLLOWS", b"since", b"1843"
8795 ]),
8796 ":1\r\n"
8797 );
8798 assert_eq!(f.run(&[b"G.NGET", b"social", b"ada"]), "*0\r\n");
8800 assert_eq!(f.run(&[b"G.NGET", b"social", b"grace"]), "*0\r\n");
8801 assert_eq!(
8802 f.run(&[b"G.OUT", b"social", b"ada", b"FOLLOWS"]),
8803 "*2\r\n$1\r\n0\r\n*1\r\n$5\r\ngrace\r\n"
8804 );
8805 assert_eq!(
8806 f.run(&[b"G.IN", b"social", b"grace", b"FOLLOWS"]),
8807 "*2\r\n$1\r\n0\r\n*1\r\n$3\r\nada\r\n"
8808 );
8809 assert_eq!(
8812 f.run(&[
8813 b"G.EADD", b"social", b"ada", b"grace", b"FOLLOWS", b"since", b"1844"
8814 ]),
8815 ":0\r\n"
8816 );
8817 assert_eq!(f.run(&[b"G.DEG", b"social", b"ada", b"FOLLOWS"]), ":1\r\n");
8818 assert_eq!(
8820 f.run(&[b"G.EADD", b"social", b"ada", b"grace", b"WORKS_WITH"]),
8821 ":1\r\n"
8822 );
8823 assert_eq!(
8824 f.run(&[b"G.DEG", b"social", b"ada", b"WORKS_WITH"]),
8825 ":1\r\n"
8826 );
8827
8828 assert_eq!(
8829 f.run(&[b"G.EDEL", b"social", b"ada", b"grace", b"FOLLOWS"]),
8830 ":1\r\n"
8831 );
8832 assert_eq!(
8833 f.run(&[b"G.EDEL", b"social", b"ada", b"grace", b"FOLLOWS"]),
8834 ":0\r\n"
8835 );
8836 assert_eq!(
8839 f.run(&[b"G.EDEL", b"social", b"ada", b"grace", b"NEVER"]),
8840 ":0\r\n"
8841 );
8842 assert_eq!(
8843 f.run(&[b"G.EDEL", b"social", b"ada", b"nobody", b"FOLLOWS"]),
8844 ":0\r\n"
8845 );
8846 assert_eq!(
8847 f.run(&[b"G.EDEL", b"nokey", b"ada", b"grace", b"FOLLOWS"]),
8848 ":0\r\n"
8849 );
8850 }
8851
8852 #[test]
8855 fn a_hop_answers_a_cursor_and_a_page() {
8856 let mut f = Fixture::new();
8857 for i in 0..25u32 {
8858 let dst = format!("n{i}");
8859 f.run(&[b"G.EADD", b"social", b"hub", dst.as_bytes(), b"FOLLOWS"]);
8860 }
8861 let first = f.run(&[b"G.OUT", b"social", b"hub", b"FOLLOWS"]);
8863 assert!(first.starts_with("*2\r\n$2\r\n10\r\n*10\r\n"), "{first}");
8864
8865 let mut seen = 0;
8866 let mut cursor = String::from("0");
8867 loop {
8868 let page = f.run(&[
8869 b"G.OUT",
8870 b"social",
8871 b"hub",
8872 b"FOLLOWS",
8873 b"COUNT",
8874 b"7",
8875 b"CURSOR",
8876 cursor.as_bytes(),
8877 ]);
8878 let (head, rest) = page.split_once("\r\n*").expect("a cursor and a page");
8879 cursor = head
8880 .rsplit("\r\n")
8881 .next()
8882 .expect("the cursor line")
8883 .to_string();
8884 seen += rest
8885 .split_once("\r\n")
8886 .expect("the page length")
8887 .0
8888 .parse::<usize>()
8889 .expect("a length");
8890 if cursor == "0" {
8891 break;
8892 }
8893 }
8894 assert_eq!(seen, 25, "every neighbour once across the pages");
8895
8896 assert_eq!(
8899 f.run(&[b"G.OUT", b"social", b"hub", b"FOLLOWS", b"CURSOR", b"900"]),
8900 "*2\r\n$1\r\n0\r\n*0\r\n"
8901 );
8902 assert_eq!(
8903 f.run(&[b"G.OUT", b"social", b"hub", b"NEVER"]),
8904 "*2\r\n$1\r\n0\r\n*0\r\n"
8905 );
8906 assert_eq!(
8907 f.run(&[b"G.OUT", b"nokey", b"hub", b"FOLLOWS"]),
8908 "*2\r\n$1\r\n0\r\n*0\r\n"
8909 );
8910 assert_eq!(
8911 f.run(&[b"G.OUT", b"social", b"hub", b"FOLLOWS", b"COUNT", b"0"]),
8912 "-ERR COUNT must be a positive integer\r\n"
8913 );
8914 assert_eq!(
8915 f.run(&[b"G.OUT", b"social", b"hub", b"FOLLOWS", b"NOPE", b"1"]),
8916 "-ERR syntax error\r\n"
8917 );
8918 }
8919
8920 #[test]
8921 fn a_degree_counts_one_way_or_both() {
8922 let mut f = Fixture::new();
8923 f.run(&[b"G.EADD", b"social", b"a", b"b", b"F"]);
8924 f.run(&[b"G.EADD", b"social", b"a", b"c", b"F"]);
8925 f.run(&[b"G.EADD", b"social", b"d", b"a", b"F"]);
8926 assert_eq!(f.run(&[b"G.DEG", b"social", b"a", b"F"]), ":2\r\n");
8927 assert_eq!(f.run(&[b"G.DEG", b"social", b"a", b"F", b"OUT"]), ":2\r\n");
8928 assert_eq!(f.run(&[b"G.DEG", b"social", b"a", b"F", b"IN"]), ":1\r\n");
8929 assert_eq!(f.run(&[b"G.DEG", b"social", b"a", b"F", b"BOTH"]), ":3\r\n");
8930 assert_eq!(f.run(&[b"G.DEG", b"social", b"nobody", b"F"]), ":0\r\n");
8931 assert_eq!(f.run(&[b"G.DEG", b"social", b"a", b"NEVER"]), ":0\r\n");
8932 assert_eq!(f.run(&[b"G.DEG", b"nokey", b"a", b"F"]), ":0\r\n");
8933 assert_eq!(
8934 f.run(&[b"G.DEG", b"social", b"a", b"F", b"SIDEWAYS"]),
8935 "-ERR syntax error\r\n"
8936 );
8937 }
8938
8939 #[test]
8942 fn a_walk_reaches_each_node_once_however_many_ways_there_are() {
8943 let mut f = Fixture::new();
8944 for (src, dst) in [
8945 ("ada", "grace"),
8946 ("ada", "alan"),
8947 ("grace", "edsger"),
8948 ("alan", "edsger"),
8949 ("edsger", "barbara"),
8950 ] {
8951 f.run(&[b"G.EADD", b"social", src.as_bytes(), dst.as_bytes(), b"F"]);
8952 }
8953 assert_eq!(
8956 f.run(&[b"G.NEIGH", b"social", b"ada", b"F"]),
8957 "*3\r\n$5\r\ngrace\r\n$4\r\nalan\r\n$6\r\nedsger\r\n"
8958 );
8959 assert_eq!(
8960 f.run(&[b"G.NEIGH", b"social", b"ada", b"F", b"DEPTH", b"1"]),
8961 "*2\r\n$5\r\ngrace\r\n$4\r\nalan\r\n"
8962 );
8963 let deep = f.run(&[b"G.NEIGH", b"social", b"ada", b"F", b"DEPTH", b"9"]);
8964 assert!(deep.starts_with("*4\r\n"), "the whole component: {deep}");
8965 assert!(deep.contains("$7\r\nbarbara\r\n"), "{deep}");
8966 assert_eq!(
8968 f.run(&[b"G.NEIGH", b"social", b"ada", b"F", b"COUNT", b"1"]),
8969 "*1\r\n$5\r\ngrace\r\n"
8970 );
8971 assert_eq!(f.run(&[b"G.NEIGH", b"social", b"barbara", b"F"]), "*0\r\n");
8973 assert_eq!(f.run(&[b"G.NEIGH", b"social", b"ada", b"NEVER"]), "*0\r\n");
8974 assert_eq!(f.run(&[b"G.NEIGH", b"nokey", b"ada", b"F"]), "*0\r\n");
8975 assert_eq!(
8976 f.run(&[b"G.NEIGH", b"social", b"ada", b"F", b"DEPTH", b"0"]),
8977 "-ERR DEPTH must be a positive integer\r\n"
8978 );
8979 assert_eq!(
8980 f.run(&[b"G.NEIGH", b"social", b"ada", b"F", b"NOPE", b"1"]),
8981 "-ERR syntax error\r\n"
8982 );
8983 }
8984
8985 #[test]
8988 fn a_path_is_the_shortest_one_and_goes_over_any_label() {
8989 let mut f = Fixture::new();
8990 for i in 0..6u32 {
8993 let src = format!("n{i}");
8994 let dst = format!("n{}", i + 1);
8995 f.run(&[b"G.EADD", b"road", src.as_bytes(), dst.as_bytes(), b"STEP"]);
8996 }
8997 assert_eq!(
8998 f.run(&[b"G.PATH", b"road", b"n0", b"n6"]),
8999 "*7\r\n$2\r\nn0\r\n$2\r\nn1\r\n$2\r\nn2\r\n$2\r\nn3\r\n$2\r\nn4\r\n$2\r\nn5\r\n$2\r\nn6\r\n"
9000 );
9001 f.run(&[b"G.EADD", b"road", b"n0", b"n5", b"JUMP"]);
9002 assert_eq!(
9003 f.run(&[b"G.PATH", b"road", b"n0", b"n6"]),
9004 "*3\r\n$2\r\nn0\r\n$2\r\nn5\r\n$2\r\nn6\r\n"
9005 );
9006 assert_eq!(
9009 f.run(&[b"G.PATH", b"road", b"n2", b"n2"]),
9010 "*1\r\n$2\r\nn2\r\n"
9011 );
9012 assert_eq!(
9013 f.run(&[b"G.PATH", b"road", b"n0", b"n6", b"MAXDEPTH", b"1"]),
9014 "*0\r\n"
9015 );
9016 assert_eq!(f.run(&[b"G.PATH", b"road", b"n6", b"n0"]), "*0\r\n");
9018 f.run(&[b"G.NADD", b"road", b"island"]);
9021 assert_eq!(f.run(&[b"G.PATH", b"road", b"n0", b"island"]), "*0\r\n");
9022 assert_eq!(f.run(&[b"G.PATH", b"road", b"n0", b"nobody"]), "*0\r\n");
9023 assert_eq!(f.run(&[b"G.PATH", b"nokey", b"n0", b"n6"]), "*0\r\n");
9024 assert_eq!(
9025 f.run(&[b"G.PATH", b"road", b"n0", b"n6", b"NOPE", b"3"]),
9026 "-ERR syntax error\r\n"
9027 );
9028 }
9029
9030 #[test]
9034 fn the_keyspace_sees_a_graph_key_like_any_other() {
9035 let mut f = Fixture::new();
9036 f.run(&[b"G.EADD", b"social", b"ada", b"grace", b"F"]);
9037 assert_eq!(f.run(&[b"TYPE", b"social"]), "+graph\r\n");
9038 assert_eq!(
9039 f.run(&[b"OBJECT", b"ENCODING", b"social"]),
9040 "$9\r\nadjacency\r\n"
9041 );
9042 assert_eq!(f.run(&[b"EXISTS", b"social"]), ":1\r\n");
9043 assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
9044 assert_eq!(f.run(&[b"KEYS", b"*"]), "*1\r\n$6\r\nsocial\r\n");
9045 let held = f.server.memory_bytes();
9049 for i in 0..200u32 {
9050 let dst = format!("n{i}");
9051 f.run(&[b"G.EADD", b"big", b"hub", dst.as_bytes(), b"F"]);
9052 }
9053 assert!(
9054 f.server.memory_bytes() > held,
9055 "two hundred edges cost something: {held} then {}",
9056 f.server.memory_bytes()
9057 );
9058 f.run(&[b"DEL", b"big"]);
9059
9060 assert_eq!(f.run(&[b"EXPIRE", b"social", b"100"]), ":1\r\n");
9063 assert_eq!(f.run(&[b"PERSIST", b"social"]), ":1\r\n");
9064 assert_eq!(f.run(&[b"RENAME", b"social", b"net"]), "+OK\r\n");
9065 assert_eq!(f.run(&[b"MOVE", b"net", b"1"]), ":1\r\n");
9066 assert_eq!(f.run(&[b"EXISTS", b"net"]), ":0\r\n");
9067 f.run(&[b"SELECT", b"1"]);
9068 assert_eq!(f.run(&[b"G.DEG", b"net", b"ada", b"F"]), ":1\r\n");
9069
9070 assert_eq!(f.run(&[b"DEL", b"net"]), ":1\r\n");
9071 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
9072 f.run(&[b"G.NADD", b"g", b"n"]);
9073 assert_eq!(f.run(&[b"FLUSHDB"]), "+OK\r\n");
9074 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
9075 }
9076
9077 #[test]
9080 fn a_graph_cannot_be_copied_or_dumped() {
9081 let mut f = Fixture::new();
9082 f.run(&[b"G.NADD", b"social", b"ada"]);
9083 assert_eq!(
9084 f.run(&[b"COPY", b"social", b"other"]),
9085 "-ERR COPY is not supported for a graph\r\n"
9086 );
9087 assert_eq!(
9088 f.run(&[b"COPY", b"social", b"other", b"DB", b"1"]),
9089 "-ERR COPY is not supported for a graph\r\n"
9090 );
9091 assert_eq!(
9092 f.run(&[b"DUMP", b"social"]),
9093 "-ERR DUMP is not supported for a graph\r\n"
9094 );
9095 assert_eq!(f.run(&[b"EXISTS", b"social", b"other"]), ":1\r\n");
9097 }
9098
9099 #[test]
9102 fn a_graph_and_a_string_are_the_wrong_type_for_each_other() {
9103 let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
9104 let mut f = Fixture::new();
9105 f.run(&[b"G.NADD", b"social", b"ada"]);
9106 assert_eq!(f.run(&[b"GET", b"social"]), wrong);
9107 assert_eq!(f.run(&[b"LPUSH", b"social", b"x"]), wrong);
9108 assert_eq!(f.run(&[b"SADD", b"social", b"x"]), wrong);
9109
9110 f.run(&[b"SET", b"str", b"v"]);
9111 for cmd in [
9112 vec![b"G.NADD".as_ref(), b"str", b"n"],
9113 vec![b"G.NGET".as_ref(), b"str", b"n"],
9114 vec![b"G.NDEL".as_ref(), b"str", b"n"],
9115 vec![b"G.EADD".as_ref(), b"str", b"a", b"b", b"F"],
9116 vec![b"G.EDEL".as_ref(), b"str", b"a", b"b", b"F"],
9117 vec![b"G.OUT".as_ref(), b"str", b"a", b"F"],
9118 vec![b"G.IN".as_ref(), b"str", b"a", b"F"],
9119 vec![b"G.DEG".as_ref(), b"str", b"a", b"F"],
9120 vec![b"G.NEIGH".as_ref(), b"str", b"a", b"F"],
9121 vec![b"G.PATH".as_ref(), b"str", b"a", b"b"],
9122 ] {
9123 assert_eq!(f.run(&cmd), wrong, "{:?}", cmd[0]);
9124 }
9125 }
9126
9127 #[test]
9130 fn a_graph_goes_when_its_last_node_does() {
9131 let mut f = Fixture::new();
9132 f.run(&[
9133 b"G.EADD", b"social", b"ada", b"grace", b"F", b"since", b"1843",
9134 ]);
9135 assert_eq!(f.run(&[b"G.NDEL", b"social", b"ada"]), ":1\r\n");
9136 assert_eq!(f.run(&[b"G.NGET", b"social", b"ada"]), "$-1\r\n");
9138 assert_eq!(
9139 f.run(&[b"G.DEG", b"social", b"grace", b"F", b"IN"]),
9140 ":0\r\n"
9141 );
9142 assert_eq!(f.run(&[b"G.NDEL", b"social", b"ada"]), ":0\r\n");
9143 assert_eq!(f.run(&[b"EXISTS", b"social"]), ":1\r\n");
9144
9145 assert_eq!(f.run(&[b"G.NDEL", b"social", b"grace"]), ":1\r\n");
9146 assert_eq!(f.run(&[b"EXISTS", b"social"]), ":0\r\n");
9147 assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
9148 assert_eq!(f.run(&[b"G.NDEL", b"nokey", b"ada"]), ":0\r\n");
9149
9150 f.run(&[b"G.NADD", b"social", b"first"]);
9153 f.run(&[b"G.NADD", b"social", b"second"]);
9154 f.run(&[b"G.NDEL", b"social", b"first"]);
9155 f.run(&[b"G.EADD", b"social", b"third", b"second", b"F"]);
9156 assert_eq!(
9157 f.run(&[b"G.OUT", b"social", b"third", b"F"]),
9158 "*2\r\n$1\r\n0\r\n*1\r\n$6\r\nsecond\r\n"
9159 );
9160 }
9161
9162 #[test]
9165 fn xadd_ids_only_ever_go_up() {
9166 let mut f = Fixture::new();
9167 assert_eq!(f.run(&[b"XADD", b"s", b"5", b"a", b"1"]), "$3\r\n5-0\r\n");
9169 assert_eq!(f.run(&[b"XADD", b"s", b"5-*", b"a", b"2"]), "$3\r\n5-1\r\n");
9171 assert_eq!(f.run(&[b"XADD", b"s", b"5-*", b"a", b"3"]), "$3\r\n5-2\r\n");
9172 assert_eq!(f.run(&[b"XADD", b"s", b"6-9", b"a", b"4"]), "$3\r\n6-9\r\n");
9173 assert_eq!(f.run(&[b"XLEN", b"s"]), ":4\r\n");
9174
9175 assert!(
9176 f.run(&[b"XADD", b"s", b"6-9", b"a", b"5"])
9177 .contains("equal or smaller")
9178 );
9179 assert!(
9180 f.run(&[b"XADD", b"s", b"0-0", b"a", b"5"])
9181 .contains("must be greater than 0-0")
9182 );
9183 assert!(
9184 f.run(&[b"XADD", b"s", b"nonsense", b"a", b"5"])
9185 .contains("Invalid stream ID")
9186 );
9187 assert!(
9190 f.run(&[b"XADD", b"s", b"*", b"a"])
9191 .contains("wrong number of arguments")
9192 );
9193
9194 assert_eq!(
9197 f.run(&[b"XADD", b"gone", b"NOMKSTREAM", b"*", b"a", b"1"]),
9198 "$-1\r\n"
9199 );
9200 assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
9201 assert_eq!(f.run(&[b"TYPE", b"s"]), "+stream\r\n");
9202 assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"s"]), "$6\r\nstream\r\n");
9203 }
9204
9205 #[test]
9208 fn trimming_reads_its_options_the_way_redis_does() {
9209 let mut f = Fixture::new();
9210 for i in 1..=10u32 {
9211 f.run(&[b"XADD", b"s", format!("{i}-1").as_bytes(), b"a", b"1"]);
9212 }
9213 assert_eq!(f.run(&[b"XTRIM", b"s", b"MAXLEN", b"4"]), ":6\r\n");
9214 assert_eq!(f.run(&[b"XLEN", b"s"]), ":4\r\n");
9215 assert_eq!(f.run(&[b"XTRIM", b"s", b"MINID", b"9"]), ":2\r\n");
9216 assert_eq!(f.run(&[b"XLEN", b"s"]), ":2\r\n");
9217
9218 assert!(
9222 f.run(&[b"XTRIM", b"s", b"MAXLEN", b"~"])
9223 .contains("not an integer")
9224 );
9225 assert!(
9226 f.run(&[b"XTRIM", b"s", b"MAXLEN", b"-1"])
9227 .contains("MAXLEN argument must be >= 0")
9228 );
9229 assert!(
9232 f.run(&[b"XTRIM", b"s", b"LIMIT", b"5"])
9233 .contains("without specifying a trimming strategy")
9234 );
9235 assert!(
9236 f.run(&[b"XTRIM", b"s", b"MAXLEN", b"5", b"LIMIT", b"5"])
9237 .contains("without the special ~ option")
9238 );
9239 assert!(
9240 f.run(&[b"XTRIM", b"s", b"MAXLEN", b"5", b"MINID", b"5"])
9241 .contains("at the same time are not compatible")
9242 );
9243 assert!(
9245 f.run(&[b"XTRIM", b"s", b"NOMKSTREAM", b"MAXLEN", b"5"])
9246 .contains("syntax error")
9247 );
9248 assert_eq!(f.run(&[b"XTRIM", b"missing", b"MAXLEN", b"5"]), ":0\r\n");
9249 }
9250
9251 #[test]
9253 fn xrange_looks_the_key_up_before_it_reads_the_count() {
9254 let mut f = Fixture::new();
9255 f.run(&[b"XADD", b"s", b"5-1", b"a", b"1"]);
9256 f.run(&[b"XADD", b"s", b"6-1", b"b", b"2"]);
9257
9258 assert_eq!(
9259 f.run(&[b"XRANGE", b"s", b"-", b"+"]),
9260 "*2\r\n*2\r\n$3\r\n5-1\r\n*2\r\n$1\r\na\r\n$1\r\n1\r\n\
9261 *2\r\n$3\r\n6-1\r\n*2\r\n$1\r\nb\r\n$1\r\n2\r\n"
9262 );
9263 assert_eq!(
9264 f.run(&[b"XREVRANGE", b"s", b"+", b"-", b"COUNT", b"1"]),
9265 "*1\r\n*2\r\n$3\r\n6-1\r\n*2\r\n$1\r\nb\r\n$1\r\n2\r\n"
9266 );
9267 assert_eq!(
9271 f.run(&[b"XRANGE", b"s", b"-", b"(6"]),
9272 "*2\r\n*2\r\n$3\r\n5-1\r\n*2\r\n$1\r\na\r\n$1\r\n1\r\n\
9273 *2\r\n$3\r\n6-1\r\n*2\r\n$1\r\nb\r\n$1\r\n2\r\n"
9274 );
9275 assert_eq!(
9276 f.run(&[b"XRANGE", b"s", b"(5-1", b"+"]),
9277 "*1\r\n*2\r\n$3\r\n6-1\r\n*2\r\n$1\r\nb\r\n$1\r\n2\r\n"
9278 );
9279 assert!(
9280 f.run(&[b"XRANGE", b"s", b"(-", b"+"])
9281 .contains("Invalid stream ID")
9282 );
9283
9284 assert_eq!(
9288 f.run(&[b"XRANGE", b"missing", b"-", b"+", b"COUNT", b"0"]),
9289 "*0\r\n"
9290 );
9291 assert_eq!(
9292 f.run(&[b"XRANGE", b"s", b"-", b"+", b"COUNT", b"0"]),
9293 "*-1\r\n"
9294 );
9295 f.run(&[b"SET", b"str", b"v"]);
9296 assert!(
9297 f.run(&[b"XRANGE", b"str", b"-", b"+", b"COUNT", b"0"])
9298 .starts_with("-WRONGTYPE")
9299 );
9300 assert_eq!(
9302 f.run(&[b"XRANGE", b"s", b"-", b"+", b"COUNT", b"2", b"COUNT", b"1"]),
9303 "*1\r\n*2\r\n$3\r\n5-1\r\n*2\r\n$1\r\na\r\n$1\r\n1\r\n"
9304 );
9305 }
9306
9307 #[test]
9309 fn a_bad_id_late_in_the_list_stops_the_whole_command() {
9310 let mut f = Fixture::new();
9311 f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
9312 f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
9313 assert!(
9314 f.run(&[b"XDEL", b"s", b"1-1", b"nonsense"])
9315 .contains("Invalid stream ID")
9316 );
9317 assert_eq!(f.run(&[b"XLEN", b"s"]), ":2\r\n");
9318 assert_eq!(f.run(&[b"XDEL", b"s", b"1-1", b"9-9"]), ":1\r\n");
9319 assert_eq!(f.run(&[b"XLEN", b"s"]), ":1\r\n");
9320 assert_eq!(f.run(&[b"XDEL", b"missing", b"1-1"]), ":0\r\n");
9321 assert_eq!(f.run(&[b"XACK", b"missing", b"g", b"1-1"]), ":0\r\n");
9322 }
9323
9324 #[test]
9326 fn xgroup_has_an_arity_per_subcommand() {
9327 let mut f = Fixture::new();
9328 assert!(
9329 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"$"])
9330 .contains("requires the key")
9331 );
9332 assert_eq!(
9333 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"$", b"MKSTREAM"]),
9334 "+OK\r\n"
9335 );
9336 assert!(
9339 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"$"])
9340 .starts_with("-BUSYGROUP")
9341 );
9342 assert_eq!(
9343 f.run(&[b"XGROUP", b"CREATECONSUMER", b"s", b"g", b"c"]),
9344 ":1\r\n"
9345 );
9346 assert_eq!(
9347 f.run(&[b"XGROUP", b"CREATECONSUMER", b"s", b"g", b"c"]),
9348 ":0\r\n"
9349 );
9350 assert_eq!(
9351 f.run(&[b"XGROUP", b"DELCONSUMER", b"s", b"g", b"c"]),
9352 ":0\r\n"
9353 );
9354
9355 let short = f.run(&[b"XGROUP", b"DESTROY", b"s"]);
9357 assert!(
9358 short.contains("wrong number of arguments for 'xgroup|destroy' command"),
9359 "{short}"
9360 );
9361 let odd = f.run(&[b"XGROUP", b"SETID", b"s", b"g", b"0", b"ENTRIESREAD"]);
9363 assert!(
9364 odd.contains("unknown subcommand or wrong number of arguments for 'SETID'"),
9365 "{odd}"
9366 );
9367 assert!(
9368 f.run(&[b"XGROUP", b"NOSUCH", b"s"])
9369 .contains("Try XGROUP HELP")
9370 );
9371
9372 assert_eq!(f.run(&[b"XGROUP", b"SETID", b"s", b"g", b"0"]), "+OK\r\n");
9373 assert!(
9374 f.run(&[b"XGROUP", b"SETID", b"s", b"nogroup", b"0"])
9375 .starts_with("-NOGROUP")
9376 );
9377 assert_eq!(f.run(&[b"XGROUP", b"DESTROY", b"s", b"g"]), ":1\r\n");
9378 assert_eq!(f.run(&[b"XGROUP", b"DESTROY", b"s", b"g"]), ":0\r\n");
9379 assert!(
9380 f.run(&[b"XGROUP", b"DESTROY", b"missing", b"g"])
9381 .contains("requires the key")
9382 );
9383 }
9384
9385 #[test]
9387 fn xreadgroup_hands_out_and_xack_takes_back() {
9388 let mut f = Fixture::new();
9389 f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
9390 f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
9391 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
9392
9393 let first = f.run(&[
9394 b"XREADGROUP",
9395 b"GROUP",
9396 b"g",
9397 b"c1",
9398 b"COUNT",
9399 b"1",
9400 b"STREAMS",
9401 b"s",
9402 b">",
9403 ]);
9404 assert_eq!(
9405 first,
9406 "*1\r\n*2\r\n$1\r\ns\r\n*1\r\n*2\r\n$3\r\n1-1\r\n*2\r\n$1\r\na\r\n$1\r\n1\r\n"
9407 );
9408 assert_eq!(
9411 f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c2", b"STREAMS", b"s", b"0"]),
9412 "*1\r\n*2\r\n$1\r\ns\r\n*0\r\n"
9413 );
9414 assert_eq!(
9415 f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b"0"]),
9416 "*1\r\n*2\r\n$1\r\ns\r\n*1\r\n*2\r\n$3\r\n1-1\r\n*2\r\n$1\r\na\r\n$1\r\n1\r\n"
9417 );
9418
9419 assert_eq!(
9420 f.run(&[b"XPENDING", b"s", b"g"]),
9421 "*4\r\n:1\r\n$3\r\n1-1\r\n$3\r\n1-1\r\n*1\r\n*2\r\n$2\r\nc1\r\n$1\r\n1\r\n"
9422 );
9423 assert_eq!(f.run(&[b"XACK", b"s", b"g", b"1-1"]), ":1\r\n");
9424 assert_eq!(f.run(&[b"XACK", b"s", b"g", b"1-1"]), ":0\r\n");
9425 assert_eq!(
9427 f.run(&[b"XPENDING", b"s", b"g"]),
9428 "*4\r\n:0\r\n$-1\r\n$-1\r\n*-1\r\n"
9429 );
9430
9431 f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"]);
9434 f.run(&[b"XDEL", b"s", b"2-1"]);
9435 assert_eq!(
9436 f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b"0"]),
9437 "*1\r\n*2\r\n$1\r\ns\r\n*1\r\n*2\r\n$3\r\n2-1\r\n$-1\r\n"
9438 );
9439
9440 assert!(
9443 f.run(&[
9444 b"XREADGROUP",
9445 b"GROUP",
9446 b"nope",
9447 b"c",
9448 b"STREAMS",
9449 b"s",
9450 b"+"
9451 ])
9452 .starts_with("-NOGROUP")
9453 );
9454 assert!(
9455 f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c", b"STREAMS", b"s", b"$"])
9456 .contains("meaningless in the context of XREADGROUP")
9457 );
9458 assert!(
9459 f.run(&[b"XREAD", b"GROUP", b"g", b"c", b"STREAMS", b"s", b"0"])
9460 .contains("only supported by XREADGROUP")
9461 );
9462 assert!(
9463 f.run(&[
9464 b"XREADGROUP",
9465 b"GROUP",
9466 b"g",
9467 b"c",
9468 b"STREAMS",
9469 b"s",
9470 b"a",
9471 b"b"
9472 ])
9473 .contains("Unbalanced 'xreadgroup' list of streams")
9474 );
9475 }
9476
9477 #[test]
9480 fn xread_with_no_block_writes_the_null_itself() {
9481 let mut f = Fixture::new();
9482 f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
9483 assert_eq!(
9484 f.run(&[b"XREAD", b"STREAMS", b"s", b"0"]),
9485 "*1\r\n*2\r\n$1\r\ns\r\n*1\r\n*2\r\n$3\r\n1-1\r\n*2\r\n$1\r\na\r\n$1\r\n1\r\n"
9486 );
9487 assert_eq!(f.run(&[b"XREAD", b"STREAMS", b"s", b"1-1"]), "*-1\r\n");
9490 assert_eq!(f.run(&[b"XREAD", b"STREAMS", b"missing", b"0"]), "*-1\r\n");
9491 f.run(&[b"XADD", b"other", b"1-1", b"b", b"2"]);
9492 assert_eq!(
9493 f.run(&[b"XREAD", b"STREAMS", b"s", b"other", b"1-1", b"0"]),
9494 "*1\r\n*2\r\n$5\r\nother\r\n*1\r\n*2\r\n$3\r\n1-1\r\n*2\r\n$1\r\nb\r\n$1\r\n2\r\n"
9495 );
9496 assert_eq!(f.run(&[b"XREAD", b"STREAMS", b"s", b"$"]), "*-1\r\n");
9498 assert_eq!(
9500 f.run(&[b"XREAD", b"COUNT", b"5", b"STREAMS", b"s", b"+"]),
9501 "*1\r\n*2\r\n$1\r\ns\r\n*1\r\n*2\r\n$3\r\n1-1\r\n*2\r\n$1\r\na\r\n$1\r\n1\r\n"
9502 );
9503 assert_eq!(
9506 f.run(&[b"XREAD", b"COUNT", b"0", b"STREAMS", b"s", b"0"]),
9507 "*1\r\n*2\r\n$1\r\ns\r\n*1\r\n*2\r\n$3\r\n1-1\r\n*2\r\n$1\r\na\r\n$1\r\n1\r\n"
9508 );
9509 assert!(
9511 f.run(&[b"XREAD", b"BLOCK", b"0.5", b"STREAMS", b"s", b"$"])
9512 .contains("not an integer")
9513 );
9514 assert!(
9515 f.run(&[b"XREAD", b"BLOCK", b"-1", b"STREAMS", b"s", b"$"])
9516 .contains("timeout is negative")
9517 );
9518 assert!(
9519 f.run(&[b"XREAD", b"STREAMS", b"s", b"other", b"0"])
9520 .contains("Unbalanced 'xread' list of streams")
9521 );
9522 }
9523
9524 #[test]
9526 fn a_blocked_xread_wakes_on_the_next_entry() {
9527 let mut f = Fixture::new();
9528 f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
9529 let (flow, reply) = f.flow(&[b"XREAD", b"BLOCK", b"0", b"STREAMS", b"s", b"$"]);
9530 assert_eq!(flow, Flow::Block);
9531 assert!(reply.is_empty());
9532
9533 let (flow, _) = f.flow(&[b"XREAD", b"BLOCK", b"0", b"STREAMS", b"s", b"$"]);
9536 assert_eq!(flow, Flow::Block);
9537 assert_eq!(f.server.waiters().len(), 2);
9538
9539 f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
9540 let want = "*1\r\n*2\r\n$1\r\ns\r\n*1\r\n*2\r\n$3\r\n2-1\r\n*2\r\n$1\r\na\r\n$1\r\n2\r\n";
9541 for at in 0..2 {
9542 let mut out = Out::new(Proto::Resp2);
9543 assert!(f.server.serve_waiter(at, 0, &mut out));
9544 assert_eq!(core::str::from_utf8(out.as_slice()).expect("ascii"), want);
9545 }
9546
9547 f.server.waiters_mut().forget(7);
9550 let (flow, _) = f.flow(&[b"XREAD", b"BLOCK", b"50", b"STREAMS", b"s", b"$"]);
9551 assert_eq!(flow, Flow::Block);
9552 let mut out = Out::new(Proto::Resp2);
9553 assert!(!f.server.serve_waiter(0, 0, &mut out));
9554 assert!(out.as_slice().is_empty());
9555 assert!(f.server.serve_waiter(0, u64::MAX, &mut out));
9556 assert_eq!(
9557 core::str::from_utf8(out.as_slice()).expect("ascii"),
9558 "*-1\r\n"
9559 );
9560 }
9561
9562 #[test]
9564 fn losing_a_group_while_blocked_is_the_ordinary_sentence() {
9565 let mut f = Fixture::new();
9566 f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
9567 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"$"]);
9568 let (flow, _) = f.flow(&[
9569 b"XREADGROUP",
9570 b"GROUP",
9571 b"g",
9572 b"c",
9573 b"BLOCK",
9574 b"0",
9575 b"STREAMS",
9576 b"s",
9577 b">",
9578 ]);
9579 assert_eq!(flow, Flow::Block);
9580
9581 f.run(&[b"XGROUP", b"DESTROY", b"s", b"g"]);
9582 let mut out = Out::new(Proto::Resp2);
9583 assert!(f.server.serve_waiter(0, 0, &mut out));
9584 assert_eq!(
9587 core::str::from_utf8(out.as_slice()).expect("ascii"),
9588 "-NOGROUP No such key 's' or consumer group 'g' in XREADGROUP with GROUP option\r\n"
9589 );
9590 }
9591
9592 #[test]
9594 fn xclaim_reads_ids_until_one_will_not_parse() {
9595 let mut f = Fixture::new();
9596 f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
9597 f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
9598 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
9599 f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"]);
9600
9601 assert!(
9604 f.run(&[b"XCLAIM", b"s", b"g", b"c2", b"0", b"-"])
9605 .contains("Unrecognized XCLAIM option '-'")
9606 );
9607 assert_eq!(
9608 f.run(&[b"XCLAIM", b"s", b"g", b"c2", b"0", b"1-1", b"JUSTID"]),
9609 "*1\r\n$3\r\n1-1\r\n"
9610 );
9611 f.run(&[b"XDEL", b"s", b"2-1"]);
9614 assert_eq!(
9615 f.run(&[b"XCLAIM", b"s", b"g", b"c2", b"0", b"2-1"]),
9616 "*0\r\n"
9617 );
9618 assert!(
9619 f.run(&[b"XPENDING", b"s", b"g"])
9620 .starts_with("*4\r\n:1\r\n")
9621 );
9622 assert!(
9623 f.run(&[b"XCLAIM", b"s", b"nope", b"c", b"0", b"1-1"])
9624 .starts_with("-NOGROUP")
9625 );
9626 assert!(
9627 f.run(&[b"XCLAIM", b"s", b"g", b"c", b"nan", b"1-1"])
9628 .contains("Invalid min-idle-time argument for XCLAIM")
9629 );
9630 }
9631
9632 #[test]
9634 fn xautoclaim_reports_what_it_dropped() {
9635 let mut f = Fixture::new();
9636 f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
9637 f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
9638 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
9639 f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"]);
9640 f.run(&[b"XDEL", b"s", b"1-1"]);
9641
9642 assert_eq!(
9645 f.run(&[b"XAUTOCLAIM", b"s", b"g", b"c2", b"0", b"-", b"JUSTID"]),
9646 "*3\r\n$3\r\n0-0\r\n*1\r\n$3\r\n2-1\r\n*1\r\n$3\r\n1-1\r\n"
9647 );
9648 assert!(
9649 f.run(&[b"XAUTOCLAIM", b"s", b"g", b"c2", b"0", b"-", b"COUNT", b"0"])
9650 .contains("COUNT must be > 0")
9651 );
9652 assert!(
9653 f.run(&[b"XAUTOCLAIM", b"s", b"nope", b"c", b"0", b"-"])
9654 .starts_with("-NOGROUP")
9655 );
9656 }
9657
9658 #[test]
9660 fn xdelex_answers_one_integer_an_id() {
9661 let mut f = Fixture::new();
9662 for i in 1..=4 {
9663 f.run(&[b"XADD", b"s", format!("{i}-1").as_bytes(), b"a", b"1"]);
9664 }
9665 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
9666 f.run(&[
9667 b"XREADGROUP",
9668 b"GROUP",
9669 b"g",
9670 b"c",
9671 b"COUNT",
9672 b"2",
9673 b"STREAMS",
9674 b"s",
9675 b">",
9676 ]);
9677
9678 assert_eq!(
9680 f.run(&[b"XDELEX", b"s", b"IDS", b"2", b"1-1", b"9-9"]),
9681 "*2\r\n:1\r\n:-1\r\n"
9682 );
9683 assert!(
9686 f.run(&[b"XPENDING", b"s", b"g"])
9687 .starts_with("*4\r\n:2\r\n")
9688 );
9689 assert_eq!(
9691 f.run(&[b"XDELEX", b"s", b"DELREF", b"IDS", b"1", b"2-1"]),
9692 "*1\r\n:1\r\n"
9693 );
9694 assert_eq!(
9696 f.run(&[b"XPENDING", b"s", b"g"]),
9697 "*4\r\n:1\r\n$3\r\n1-1\r\n$3\r\n1-1\r\n*1\r\n*2\r\n$1\r\nc\r\n$1\r\n1\r\n"
9698 );
9699
9700 assert_eq!(
9704 f.run(&[b"XDELEX", b"s", b"ACKED", b"IDS", b"2", b"3-1", b"4-1"]),
9705 "*2\r\n:2\r\n:2\r\n"
9706 );
9707
9708 assert_eq!(
9710 f.run(&[b"XDELEX", b"nope", b"IDS", b"2", b"bad", b"worse"]),
9711 "*2\r\n:-1\r\n:-1\r\n"
9712 );
9713 assert!(
9715 f.run(&[b"XDELEX", b"s", b"IDS", b"2", b"3-1", b"bad"])
9716 .starts_with("-ERR Invalid stream ID")
9717 );
9718 assert_eq!(f.run(&[b"XLEN", b"s"]), ":2\r\n");
9719
9720 assert!(
9721 f.run(&[b"XDELEX", b"s", b"IDS", b"0", b"1-1"])
9722 .contains("Number of IDs must be a positive integer")
9723 );
9724 assert!(
9725 f.run(&[b"XDELEX", b"s", b"IDS", b"2", b"1-1"])
9726 .contains("The `numids` parameter must match the number of arguments")
9727 );
9728 assert!(
9731 f.run(&[b"XDELEX", b"s", b"KEEPREF", b"DELREF", b"IDS", b"1", b"1-1"])
9732 .starts_with("-ERR syntax error")
9733 );
9734 assert!(
9735 f.run(&[b"XDELEX", b"s", b"IDS", b"1", b"1-1", b"2-1"])
9736 .starts_with("-ERR syntax error")
9737 );
9738 f.run(&[b"SET", b"str", b"v"]);
9740 assert!(
9741 f.run(&[b"XDELEX", b"str", b"BOGUS", b"IDS", b"0", b"1-1"])
9742 .starts_with("-WRONGTYPE")
9743 );
9744 }
9745
9746 #[test]
9748 fn xackdel_reports_what_the_group_was_holding() {
9749 let mut f = Fixture::new();
9750 for i in 1..=3 {
9751 f.run(&[b"XADD", b"s", format!("{i}-1").as_bytes(), b"a", b"1"]);
9752 }
9753 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
9754 f.run(&[
9755 b"XREADGROUP",
9756 b"GROUP",
9757 b"g",
9758 b"c",
9759 b"COUNT",
9760 b"1",
9761 b"STREAMS",
9762 b"s",
9763 b">",
9764 ]);
9765
9766 assert_eq!(
9770 f.run(&[b"XACKDEL", b"s", b"g", b"IDS", b"2", b"1-1", b"2-1"]),
9771 "*2\r\n:1\r\n:-1\r\n"
9772 );
9773 assert_eq!(f.run(&[b"XLEN", b"s"]), ":2\r\n");
9774
9775 assert_eq!(
9777 f.run(&[b"XACKDEL", b"s", b"nope", b"IDS", b"1", b"2-1"]),
9778 "*1\r\n:-1\r\n"
9779 );
9780 assert_eq!(
9781 f.run(&[b"XACKDEL", b"nope", b"g", b"IDS", b"1", b"2-1"]),
9782 "*1\r\n:-1\r\n"
9783 );
9784
9785 f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c", b"STREAMS", b"s", b">"]);
9788 f.run(&[b"XGROUP", b"CREATE", b"s", b"g2", b"0"]);
9789 assert_eq!(
9790 f.run(&[b"XACKDEL", b"s", b"g", b"ACKED", b"IDS", b"1", b"2-1"]),
9791 "*1\r\n:2\r\n"
9792 );
9793 assert_eq!(
9794 f.run(&[b"XPENDING", b"s", b"g"]),
9795 "*4\r\n:1\r\n$3\r\n3-1\r\n$3\r\n3-1\r\n*1\r\n*2\r\n$1\r\nc\r\n$1\r\n1\r\n"
9796 );
9797 assert_eq!(f.run(&[b"XLEN", b"s"]), ":2\r\n");
9798 }
9799
9800 #[test]
9802 fn xnack_releases_an_entry_for_the_next_claim() {
9803 let mut f = Fixture::new();
9804 f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
9805 f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
9806 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
9807 f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"]);
9808 f.run(&[b"XCLAIM", b"s", b"g", b"c1", b"0", b"1-1", b"2-1"]);
9811
9812 assert_eq!(
9813 f.run(&[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"1-1"]),
9814 ":1\r\n"
9815 );
9816 assert_eq!(
9820 f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"]),
9821 "*2\r\n*4\r\n$3\r\n1-1\r\n$0\r\n\r\n:-1\r\n:2\r\n*4\r\n$3\r\n2-1\r\n$2\r\nc1\r\n:0\r\n:2\r\n"
9822 );
9823 assert_eq!(
9825 f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10", b"c1"]),
9826 "*1\r\n*4\r\n$3\r\n2-1\r\n$2\r\nc1\r\n:0\r\n:2\r\n"
9827 );
9828 assert_eq!(
9830 f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c2", b"STREAMS", b"s", b">"]),
9831 "*-1\r\n"
9832 );
9833 assert_eq!(
9835 f.run(&[
9836 b"XAUTOCLAIM",
9837 b"s",
9838 b"g",
9839 b"c2",
9840 b"99999999",
9841 b"-",
9842 b"JUSTID"
9843 ]),
9844 "*3\r\n$3\r\n0-0\r\n*1\r\n$3\r\n1-1\r\n*0\r\n"
9845 );
9846
9847 f.run(&[b"XNACK", b"s", b"g", b"SILENT", b"IDS", b"1", b"1-1"]);
9851 assert!(
9852 f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"])
9853 .contains(":-1\r\n:1\r\n")
9854 );
9855 f.run(&[b"XNACK", b"s", b"g", b"SILENT", b"IDS", b"1", b"1-1"]);
9857 f.run(&[b"XNACK", b"s", b"g", b"SILENT", b"IDS", b"1", b"1-1"]);
9858 assert!(
9859 f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"])
9860 .contains(":-1\r\n:0\r\n")
9861 );
9862 f.run(&[b"XNACK", b"s", b"g", b"FATAL", b"IDS", b"1", b"1-1"]);
9864 assert!(
9865 f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"])
9866 .contains(":9223372036854775807\r\n")
9867 );
9868 f.run(&[
9869 b"XNACK",
9870 b"s",
9871 b"g",
9872 b"FATAL",
9873 b"IDS",
9874 b"1",
9875 b"1-1",
9876 b"RETRYCOUNT",
9877 b"3",
9878 ]);
9879 assert!(
9880 f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"])
9881 .contains(":-1\r\n:3\r\n")
9882 );
9883
9884 f.run(&[b"XACK", b"s", b"g", b"2-1"]);
9888 assert_eq!(
9889 f.run(&[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"2-1"]),
9890 ":0\r\n"
9891 );
9892 assert_eq!(
9893 f.run(&[
9894 b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"2-1", b"FORCE"
9895 ]),
9896 ":1\r\n"
9897 );
9898 assert!(
9899 f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"])
9900 .contains(":-1\r\n:0\r\n")
9901 );
9902 assert_eq!(
9904 f.run(&[
9905 b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"9-9", b"FORCE"
9906 ]),
9907 ":0\r\n"
9908 );
9909
9910 assert_eq!(
9913 f.run(&[b"XNACK", b"s", b"nope", b"BOGUS", b"IDS", b"1", b"1-1"]),
9914 "-NOGROUP No such key 's' or consumer group 'nope'\r\n"
9915 );
9916 assert!(
9917 f.run(&[b"XNACK", b"s", b"g", b"BOGUS", b"IDS", b"1", b"1-1"])
9918 .starts_with("-ERR")
9919 );
9920 assert!(
9922 f.run(&[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"0", b"1-1"])
9923 .contains("numids must be a positive integer")
9924 );
9925 assert!(
9926 f.run(&[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"2", b"1-1"])
9927 .contains("number of IDs doesn't match numids")
9928 );
9929 assert!(
9932 f.run(&[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"1-1", b"2-1"])
9933 .contains("Unrecognized XNACK option '2-1'")
9934 );
9935 }
9936
9937 #[test]
9939 fn xinfo_reports_the_stream_the_groups_and_the_consumers() {
9940 let mut f = Fixture::new();
9941 f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
9942 f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
9943 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
9944 f.run(&[
9945 b"XREADGROUP",
9946 b"GROUP",
9947 b"g",
9948 b"c1",
9949 b"COUNT",
9950 b"1",
9951 b"STREAMS",
9952 b"s",
9953 b">",
9954 ]);
9955
9956 let info = f.run(&[b"XINFO", b"STREAM", b"s"]);
9957 assert!(info.starts_with("*20\r\n"), "{info}");
9960 assert!(info.contains("$6\r\nlength\r\n:2\r\n"), "{info}");
9961 assert!(
9962 info.contains("$17\r\nlast-generated-id\r\n$3\r\n2-1\r\n"),
9963 "{info}"
9964 );
9965 assert!(info.contains("$13\r\nentries-added\r\n:2\r\n"), "{info}");
9966 assert!(info.contains("$6\r\ngroups\r\n:1\r\n"), "{info}");
9967
9968 let groups = f.run(&[b"XINFO", b"GROUPS", b"s"]);
9969 assert!(groups.starts_with("*1\r\n*12\r\n"), "{groups}");
9970 assert!(groups.contains("$9\r\nconsumers\r\n:1\r\n"), "{groups}");
9971 assert!(groups.contains("$7\r\npending\r\n:1\r\n"), "{groups}");
9972 assert!(groups.contains("$3\r\nlag\r\n:1\r\n"), "{groups}");
9973
9974 f.run(&[b"XGROUP", b"CREATECONSUMER", b"s", b"g", b"c2"]);
9978 let consumers = f.run(&[b"XINFO", b"CONSUMERS", b"s", b"g"]);
9979 assert!(consumers.starts_with("*2\r\n"), "{consumers}");
9980 assert!(
9981 consumers.contains("$8\r\ninactive\r\n:-1\r\n"),
9982 "{consumers}"
9983 );
9984 let c1 = consumers.find("c1").unwrap();
9986 let c2 = consumers.find("c2").unwrap();
9987 assert!(c1 < c2, "{consumers}");
9988
9989 let full = f.run(&[b"XINFO", b"STREAM", b"s", b"FULL"]);
9990 assert!(full.starts_with("*18\r\n"), "{full}");
9991 assert!(full.contains("$12\r\nnacked-count\r\n:0\r\n"), "{full}");
9992 assert!(full.contains("$11\r\nactive-time\r\n"), "{full}");
9993
9994 assert!(
9995 f.run(&[b"XINFO", b"STREAM", b"missing"])
9996 .contains("no such key")
9997 );
9998 assert!(
9999 f.run(&[b"XINFO", b"GROUPS", b"missing"])
10000 .contains("no such key")
10001 );
10002 assert!(
10003 f.run(&[b"XINFO", b"CONSUMERS", b"s", b"nope"])
10004 .starts_with("-NOGROUP")
10005 );
10006 assert!(
10007 f.run(&[b"XINFO", b"NOSUCH", b"s"])
10008 .contains("Try XINFO HELP")
10009 );
10010 assert!(f.run(&[b"XINFO", b"HELP"]).contains("XINFO <subcommand>"));
10011 assert!(f.run(&[b"XGROUP", b"HELP"]).contains("XGROUP <subcommand>"));
10012 }
10013
10014 #[test]
10016 fn xpending_takes_the_consumer_only_when_the_count_comes_out_right() {
10017 let mut f = Fixture::new();
10018 f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
10019 f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
10020 f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"]);
10021
10022 let list = f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"]);
10023 assert_eq!(list, "*1\r\n*4\r\n$3\r\n1-1\r\n$2\r\nc1\r\n:0\r\n:1\r\n");
10024 assert_eq!(
10025 f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10", b"c1"]),
10026 "*1\r\n*4\r\n$3\r\n1-1\r\n$2\r\nc1\r\n:0\r\n:1\r\n"
10027 );
10028 assert_eq!(
10030 f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10", b"nope"]),
10031 "*0\r\n"
10032 );
10033 assert_eq!(
10034 f.run(&[b"XPENDING", b"s", b"g", b"IDLE", b"0", b"-", b"+", b"10"]),
10035 list
10036 );
10037 assert!(
10039 f.run(&[b"XPENDING", b"s", b"g", b"IDLE", b"0"])
10040 .contains("syntax error")
10041 );
10042 assert!(
10043 f.run(&[b"XPENDING", b"s", b"g", b"-", b"+"])
10044 .contains("syntax error")
10045 );
10046 assert_eq!(
10047 f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"-1"]),
10048 "*0\r\n"
10049 );
10050 assert!(
10051 f.run(&[b"XPENDING", b"missing", b"g"])
10052 .starts_with("-NOGROUP")
10053 );
10054 }
10055
10056 #[test]
10058 fn xsetid_will_not_go_below_what_is_there() {
10059 let mut f = Fixture::new();
10060 f.run(&[b"XADD", b"s", b"5-5", b"a", b"1"]);
10061 assert_eq!(f.run(&[b"XSETID", b"s", b"9-9"]), "+OK\r\n");
10062 assert_eq!(
10063 f.run(&[
10064 b"XSETID",
10065 b"s",
10066 b"10-1",
10067 b"ENTRIESADDED",
10068 b"7",
10069 b"MAXDELETEDID",
10070 b"9-1"
10071 ]),
10072 "+OK\r\n"
10073 );
10074 let info = f.run(&[b"XINFO", b"STREAM", b"s"]);
10075 assert!(info.contains("$13\r\nentries-added\r\n:7\r\n"), "{info}");
10076 assert!(
10077 info.contains("$20\r\nmax-deleted-entry-id\r\n$3\r\n9-1\r\n"),
10078 "{info}"
10079 );
10080
10081 assert!(
10082 f.run(&[b"XSETID", b"s", b"1-1"])
10083 .contains("smaller than the target stream top item")
10084 );
10085 assert!(
10086 f.run(&[b"XSETID", b"s", b"10-1", b"ENTRIESADDED", b"-1"])
10087 .contains("entries_added must be positive")
10088 );
10089 assert!(
10090 f.run(&[b"XSETID", b"missing", b"1-1"])
10091 .contains("no such key")
10092 );
10093 }
10094
10095 #[test]
10097 fn xread_answers_a_map_on_resp3_and_the_fields_stay_flat() {
10098 let mut f = Fixture::new();
10099 f.run(&[b"HELLO", b"3"]);
10100 f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
10101 assert_eq!(
10104 f.run(&[b"XREAD", b"STREAMS", b"s", b"0"]),
10105 "%1\r\n$1\r\ns\r\n*1\r\n*2\r\n$3\r\n1-1\r\n*2\r\n$1\r\na\r\n$1\r\n1\r\n"
10106 );
10107 assert_eq!(
10110 f.run(&[b"XRANGE", b"s", b"-", b"+"]),
10111 "*1\r\n*2\r\n$3\r\n1-1\r\n*2\r\n$1\r\na\r\n$1\r\n1\r\n"
10112 );
10113 assert_eq!(f.run(&[b"XREAD", b"STREAMS", b"s", b"1-1"]), "_\r\n");
10114 }
10115
10116 struct Mem {
10122 blobs: Vec<Vec<u8>>,
10123 }
10124
10125 impl yo_kv::cold::Blocks for Mem {
10126 fn put(&mut self, bytes: &[u8]) -> yo_common::Result<yo_common::Addr> {
10127 self.blobs.push(bytes.to_vec());
10128 Ok(yo_common::Addr::new(
10129 yo_common::Space::Log,
10130 (self.blobs.len() - 1) as u64,
10131 ))
10132 }
10133
10134 fn get(&self, at: yo_common::Addr) -> yo_common::Result<&[u8]> {
10135 self.blobs
10136 .get(at.offset() as usize)
10137 .map(Vec::as_slice)
10138 .ok_or_else(|| {
10139 yo_common::Error::new(yo_common::Code::Corrupt, "no chunk at that address")
10140 })
10141 }
10142
10143 fn bytes(&self) -> u64 {
10144 self.blobs.iter().map(|b| b.len() as u64).sum()
10145 }
10146 }
10147
10148 fn filled(attach: bool) -> (Fixture, usize) {
10152 let mut f = Fixture::new();
10153 if attach {
10154 f.server.db(0).attach(Box::new(Mem { blobs: Vec::new() }));
10155 }
10156 let val = vec![b'v'; 256];
10157 for i in 0..24000u32 {
10158 let k = format!("key:{i:08}");
10159 f.run(&[b"SET", k.as_bytes(), &val]);
10160 }
10161 let full = f.server.memory_bytes();
10162 assert!(full > 3 * 1024 * 1024, "the arena is several segments");
10163 (f, full)
10164 }
10165
10166 fn press(f: &mut Fixture, limit: usize) {
10172 let val = vec![b'v'; 256];
10173 for i in 0..3000u32 {
10174 let k = format!("new:{i:08}");
10175 assert_eq!(
10176 f.run(&[b"SET", k.as_bytes(), &val]),
10177 "+OK\r\n",
10178 "write {i} was refused"
10179 );
10180 f.server.refresh_memory();
10181 if f.server.memory_bytes() <= limit {
10182 return;
10183 }
10184 }
10185 panic!(
10186 "it never got under: {} against {limit}",
10187 f.server.memory_bytes()
10188 );
10189 }
10190
10191 #[test]
10192 fn the_storage_limit_reads_back_and_minus_one_is_no_limit() {
10193 let mut f = Fixture::new();
10194 assert_eq!(
10195 f.run(&[b"CONFIG", b"GET", b"maxstore"]),
10196 "*2\r\n$8\r\nmaxstore\r\n$2\r\n-1\r\n",
10197 "no limit is the default"
10198 );
10199 for (typed, bytes) in [
10202 (&b"0"[..], "0"),
10203 (b"1024", "1024"),
10204 (b"1k", "1000"),
10205 (b"1gb", "1073741824"),
10206 (b"-1", "-1"),
10207 ] {
10208 assert_eq!(f.run(&[b"CONFIG", b"SET", b"maxstore", typed]), "+OK\r\n");
10209 assert_eq!(
10210 f.run(&[b"CONFIG", b"GET", b"maxstore"]),
10211 format!("*2\r\n$8\r\nmaxstore\r\n${}\r\n{bytes}\r\n", bytes.len()),
10212 "set {}",
10213 String::from_utf8_lossy(typed)
10214 );
10215 }
10216 for bad in [&b"1tb"[..], b"-2", b"", b"lots"] {
10217 assert_eq!(
10218 f.run(&[b"CONFIG", b"SET", b"maxstore", bad]),
10219 "-ERR CONFIG SET failed (possibly related to argument 'maxstore') - argument must be a memory value or -1\r\n",
10220 "refused {}",
10221 String::from_utf8_lossy(bad)
10222 );
10223 }
10224 let info = f.run(&[b"INFO", b"memory"]);
10226 assert!(info.contains("maxstore:-1"), "{info}");
10227 assert!(info.contains("yo_memory_regime:evict"), "{info}");
10228 assert!(info.contains("yo_store_bytes:0"), "{info}");
10229 }
10230
10231 #[test]
10232 fn a_memory_limit_moves_values_to_the_file_instead_of_dropping_keys() {
10233 let (mut f, full) = filled(true);
10237 let keys = f.run(&[b"DBSIZE"]);
10238 assert!(
10239 f.run(&[b"INFO", b"memory"])
10240 .contains("yo_memory_regime:migrate"),
10241 "a database with somewhere to put values migrates"
10242 );
10243
10244 let limit = full - 2 * 1024 * 1024;
10245 f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lru"]);
10246 f.run(&[
10247 b"CONFIG",
10248 b"SET",
10249 b"maxmemory",
10250 limit.to_string().as_bytes(),
10251 ]);
10252 press(&mut f, limit);
10253
10254 assert!(
10255 f.run(&[b"INFO", b"stats"]).contains("evicted_keys:0"),
10256 "nothing was thrown away"
10257 );
10258 let after: usize = f.run(&[b"DBSIZE"])[1..]
10259 .trim_end()
10260 .parse()
10261 .expect("a count");
10262 let before: usize = keys[1..].trim_end().parse().expect("a count");
10263 assert!(after > before, "the keys that came in are all still here");
10264 assert!(
10265 f.server.store_bytes() > 0,
10266 "and what came out of memory went to the file"
10267 );
10268 let val = format!("$256\r\n{}\r\n", "v".repeat(256));
10271 assert_eq!(f.run(&[b"GET", b"key:00000000"]), val);
10272 assert_eq!(f.run(&[b"GET", b"key:00023999"]), val);
10273 }
10274
10275 #[test]
10276 fn a_storage_limit_of_zero_restores_redis_behaviour_exactly() {
10277 let (mut f, full) = filled(true);
10281 f.run(&[b"CONFIG", b"SET", b"maxstore", b"0"]);
10282 assert!(
10283 f.run(&[b"INFO", b"memory"])
10284 .contains("yo_memory_regime:evict"),
10285 "nothing may go to the file"
10286 );
10287
10288 let limit = full - 2 * 1024 * 1024;
10289 f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lru"]);
10290 f.run(&[
10291 b"CONFIG",
10292 b"SET",
10293 b"maxmemory",
10294 limit.to_string().as_bytes(),
10295 ]);
10296 press(&mut f, limit);
10297
10298 assert!(
10299 !f.run(&[b"INFO", b"stats"]).contains("evicted_keys:0"),
10300 "keys were thrown away, which is what was asked for"
10301 );
10302 assert_eq!(f.server.store_bytes(), 0, "and the file was never written");
10303 }
10304
10305 #[test]
10306 fn a_full_file_goes_back_to_evicting() {
10307 let (mut f, full) = filled(true);
10311 f.run(&[b"CONFIG", b"SET", b"maxstore", b"64kb"]);
10312 let limit = full - 2 * 1024 * 1024;
10313 f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lru"]);
10314 f.run(&[
10315 b"CONFIG",
10316 b"SET",
10317 b"maxmemory",
10318 limit.to_string().as_bytes(),
10319 ]);
10320 press(&mut f, limit);
10321
10322 assert!(f.server.store_bytes() >= 64 * 1024, "the file filled up");
10323 assert!(
10324 !f.run(&[b"INFO", b"stats"]).contains("evicted_keys:0"),
10325 "and then it started evicting"
10326 );
10327 assert!(
10328 f.run(&[b"INFO", b"memory"])
10329 .contains("yo_memory_regime:evict"),
10330 "and it says so"
10331 );
10332 }
10333}